From 01e0ebfe59d9028b0246ec4a549bd7528ada94eb Mon Sep 17 00:00:00 2001 From: William Joye Date: Tue, 9 Jan 2018 14:06:55 -0500 Subject: update ast 8.6.2 --- ast/.gitignore | 65 + ast/AST_to_do | 23 + ast/COPYING | 674 + ast/COPYING.LESSER | 165 + ast/COPYING.LIB | 481 + ast/Ers.h | 245 + ast/GRF_PAR | 124 + ast/Makefile.am | 828 + ast/Notes | 105 + ast/STARLINK_BRANCHES | 46 + ast/addcopyright | 2 + ast/addlinks | 163 + ast/addversion.in | 10 + ast/ast-for-wcslib/astTester.c | 85 + ast/ast-for-wcslib/loader.c | 35 + ast/ast-for-wcslib/matrixmap.h | 7 + ast/ast-for-wcslib/plot.h | 2 + ast/ast-for-wcslib/wcslib-instructions | 112 + ast/ast.news | 1189 + ast/ast_cpp.in | 11 + ast/ast_dev | 86 + ast/ast_err.msg | 214 + ast/ast_link.in | 463 + ast/ast_link_adam.in | 406 + ast/ast_par.source | 733 + ast/ast_test.c | 115 + ast/ast_tester/README | 33 + ast/ast_tester/a20070718_00010_02_cube.ast | 339 + ast/ast_tester/a20070718_00010_02_cube.fits-wcs | 61 + ast/ast_tester/aitoff.attr | 1 + ast/ast_tester/aitoff.box | 1 + ast/ast_tester/aitoff.head | 13 + ast/ast_tester/ast_tester | 232 + ast/ast_tester/brad.map | 58 + ast/ast_tester/brad.simp | 49 + ast/ast_tester/car1.attr | 1 + ast/ast_tester/car1.box | 1 + ast/ast_tester/car1.fattr | 1 + ast/ast_tester/car1.head | 32 + ast/ast_tester/car2.attr | 1 + ast/ast_tester/car2.box | 1 + ast/ast_tester/car2.fattr | 1 + ast/ast_tester/car2.head | 32 + ast/ast_tester/car3.attr | 1 + ast/ast_tester/car3.box | 1 + ast/ast_tester/car3.head | 8 + ast/ast_tester/car4.attr | 0 ast/ast_tester/car4.box | 1 + ast/ast_tester/car4.fattr | 1 + ast/ast_tester/car4.head | 38 + ast/ast_tester/car5.attr | 0 ast/ast_tester/car5.box | 1 + ast/ast_tester/car5.head | 38 + ast/ast_tester/car6.attr | 0 ast/ast_tester/car6.box | 1 + ast/ast_tester/car6.head | 39 + ast/ast_tester/cobe.attr | 1 + ast/ast_tester/cobe.box | 1 + ast/ast_tester/cobe.head | 61 + ast/ast_tester/degen1.ast | 318 + ast/ast_tester/degen1.fits-wcs | 40 + ast/ast_tester/doplot | 53 + ast/ast_tester/dss.ast | 166 + ast/ast_tester/dss.dss | 58 + ast/ast_tester/dss.fits-dss | 108 + ast/ast_tester/dss.fits-wcs | 39 + ast/ast_tester/hpx.attr | 1 + ast/ast_tester/hpx.box | 1 + ast/ast_tester/hpx.head | 10 + ast/ast_tester/joye_car_headers/CAR_model.head | 47 + ast/ast_tester/joye_car_headers/CHIPASS_Equ.head | 39 + ast/ast_tester/joye_car_headers/car1.fattr | 1 + ast/ast_tester/joye_car_headers/car1.head | 32 + ast/ast_tester/joye_car_headers/car2.fattr | 1 + ast/ast_tester/joye_car_headers/car2.head | 32 + ast/ast_tester/joye_car_headers/car3.head | 8 + ast/ast_tester/joye_car_headers/car4.fattr | 1 + ast/ast_tester/joye_car_headers/car4.head | 38 + ast/ast_tester/joye_car_headers/car5.head | 38 + .../joye_car_headers/cmap_3years_GP_D2.head | 162 + ast/ast_tester/joye_car_headers/doit | 17 + ast/ast_tester/joye_car_headers/total_hi.head | 35 + ast/ast_tester/longslit.fits-pc | 18 + ast/ast_tester/longslit.fits-wcs | 22 + ast/ast_tester/makeplot | 47 + ast/ast_tester/maketest | 35 + ast/ast_tester/origin.attr | 1 + ast/ast_tester/origin.box | 1 + ast/ast_tester/origin.head | 18 + ast/ast_tester/plot3d-test1.ast | 340 + ast/ast_tester/plotter.f | 230 + ast/ast_tester/polco.attr | 1 + ast/ast_tester/polco.box | 1 + ast/ast_tester/polco.head | 86 + ast/ast_tester/polco2.attr | 1 + ast/ast_tester/polco2.box | 1 + ast/ast_tester/polco2.head | 86 + ast/ast_tester/regression.current | 7583 ++++ ast/ast_tester/regression.f | 1515 + ast/ast_tester/regression.out | 7630 ++++ ast/ast_tester/rigby.map | 240 + ast/ast_tester/rigby.simp | 201 + ast/ast_tester/scp.attr | 1 + ast/ast_tester/scp.box | 1 + ast/ast_tester/scp.head | 204 + ast/ast_tester/serpens.attr | 1 + ast/ast_tester/serpens.box | 1 + ast/ast_tester/serpens.head | 160 + ast/ast_tester/simplify.f | 123 + ast/ast_tester/sip.fits-wcs | 289 + ast/ast_tester/sip.head | 288 + ast/ast_tester/sparse.ast | 392 + ast/ast_tester/specflux.ast | 29 + ast/ast_tester/specflux.attr | 1 + ast/ast_tester/specflux.box | 2 + ast/ast_tester/specflux.head | 28 + ast/ast_tester/splittest1.ast | 185 + ast/ast_tester/stcschan-test1-doc3-props.ast | 126 + ast/ast_tester/stcschan-test1-doc3.ast | 272 + ast/ast_tester/testchannel.f | 88 + ast/ast_tester/testchebymap.f | 475 + ast/ast_tester/testcmpmap.f | 149 + ast/ast_tester/testconvert.c | 232 + ast/ast_tester/testerror.c | 44 + ast/ast_tester/testfitschan.f | 988 + ast/ast_tester/testfitstable.f | 590 + ast/ast_tester/testflux.f | 354 + ast/ast_tester/testframeset.f | 391 + ast/ast_tester/testkeymap.f | 1369 + ast/ast_tester/testlutmap.f | 177 + ast/ast_tester/testmapping.f | 85 + ast/ast_tester/testnormmap.f | 94 + ast/ast_tester/testobject.c | 120 + ast/ast_tester/testplot3d.f | 1357 + ast/ast_tester/testpolymap.f | 319 + ast/ast_tester/testrate.f | 344 + ast/ast_tester/testratemap.f | 148 + ast/ast_tester/testrebin.f | 4176 ++ ast/ast_tester/testrebinseq.f | 1580 + ast/ast_tester/testregions.f | 4032 ++ ast/ast_tester/testskyframe.f | 89 + ast/ast_tester/testspecflux.f | 331 + ast/ast_tester/testspecframe.f | 251 + ast/ast_tester/teststc.f | 1858 + ast/ast_tester/teststc_com | 13 + ast/ast_tester/teststc_eg1 | 71 + ast/ast_tester/teststc_eg10 | 18 + ast/ast_tester/teststc_eg2 | 114 + ast/ast_tester/teststc_eg3 | 113 + ast/ast_tester/teststc_eg4 | 55 + ast/ast_tester/teststc_eg5 | 109 + ast/ast_tester/teststc_eg6 | 49 + ast/ast_tester/teststc_eg7 | 52 + ast/ast_tester/teststc_eg8 | 52 + ast/ast_tester/teststc_eg9 | 49 + ast/ast_tester/teststcschan.f | 654 + ast/ast_tester/testswitchmap.f | 794 + ast/ast_tester/testtable.f | 537 + ast/ast_tester/testtime.f | 979 + ast/ast_tester/testtrangrid.f | 276 + ast/ast_tester/testunitnormmap.f | 324 + ast/ast_tester/testxmlchan.f | 246 + ast/ast_tester/testxmlchan_com | 13 + ast/ast_tester/testzoommap.f | 91 + ast/ast_tester/timeplot.attr | 1 + ast/ast_tester/timeplot.box | 1 + ast/ast_tester/timeplot.head | 34 + ast/ast_tester/timj.ast | 234 + ast/ast_tester/timj.fits-aips | 11 + ast/ast_tester/timj.fits-iraf | 13 + ast/ast_tester/timj.fits-pc | 16 + ast/ast_tester/timj.fits-wcs | 39 + ast/ast_tester/timj.native | 217 + ast/ast_tester/tnx.attr | 1 + ast/ast_tester/tnx.box | 1 + ast/ast_tester/tnx.head | 180 + ast/ast_tester/tsc.attr | 1 + ast/ast_tester/tsc.box | 1 + ast/ast_tester/tsc.head | 116 + ast/ast_tester/wcsconverter.f | 222 + ast/ast_tester/zpn.attr | 1 + ast/ast_tester/zpn.box | 1 + ast/ast_tester/zpn.head | 136 + ast/ast_tester/zpx.attr | 1 + ast/ast_tester/zpx.head | 153 + ast/astbad.c | 181 + ast/axis.c | 3500 ++ ast/axis.h | 625 + ast/bootstrap | 134 + ast/box.c | 5062 +++ ast/box.h | 234 + ast/builddocs.in | 146 + ast/buildhyperdocs | 11 + ast/c2f77.c | 125 + ast/c2f77.h | 166 + ast/cexpand | 32 + ast/cexpand.pl | 22 + ast/channel.c | 6458 +++ ast/channel.h | 687 + ast/chebymap.c | 2404 + ast/chebymap.h | 234 + ast/circle.c | 2900 ++ ast/circle.h | 241 + ast/cminpack/CopyrightMINPACK.txt | 52 + ast/cminpack/README.md | 128 + ast/cminpack/cminpack.h | 370 + ast/cminpack/cminpackP.h | 62 + ast/cminpack/dpmpar.c | 201 + ast/cminpack/enorm.c | 157 + ast/cminpack/lmder.c | 526 + ast/cminpack/lmder1.c | 167 + ast/cminpack/lmpar.c | 338 + ast/cminpack/qrfac.c | 285 + ast/cminpack/qrsolv.c | 218 + ast/cmpframe.c | 10846 +++++ ast/cmpframe.h | 428 + ast/cmpframe.pdf | Bin 0 -> 7432 bytes ast/cmpmap.c | 4739 ++ ast/cmpmap.h | 300 + ast/cmpregion.c | 5127 +++ ast/cmpregion.h | 251 + ast/complex.pdf | Bin 0 -> 15323 bytes ast/component.xml | 44 + ast/component.xml.in | 44 + ast/configure.ac | 243 + ast/devtools/make_simtest | 4 + ast/devtools/simtest.c | 864 + ast/doincludes | 22 + ast/dsbspecframe.c | 3266 ++ ast/dsbspecframe.h | 298 + ast/dssmap.c | 2283 + ast/dssmap.h | 401 + ast/ellipse.c | 3055 ++ ast/ellipse.h | 244 + ast/ems.h | 185 + ast/erfa.h | 72 + ast/erfa/INFO | 19 + ast/erfa/LICENSE | 53 + ast/erfa/Makefile.am | 47 + ast/erfa/README.rst | 93 + ast/erfa/RELEASE.rst | 180 + ast/erfa/a2af.c | 129 + ast/erfa/a2tf.c | 125 + ast/erfa/ab.c | 137 + ast/erfa/af2a.c | 116 + ast/erfa/anp.c | 91 + ast/erfa/anpm.c | 91 + ast/erfa/apcg.c | 181 + ast/erfa/apcg13.c | 184 + ast/erfa/apci.c | 190 + ast/erfa/apci13.c | 202 + ast/erfa/apco.c | 264 + ast/erfa/apco13.c | 287 + ast/erfa/apcs.c | 233 + ast/erfa/apcs13.c | 191 + ast/erfa/aper.c | 162 + ast/erfa/aper13.c | 181 + ast/erfa/apio.c | 213 + ast/erfa/apio13.c | 259 + ast/erfa/atci13.c | 159 + ast/erfa/atciq.c | 154 + ast/erfa/atciqn.c | 191 + ast/erfa/atciqz.c | 153 + ast/erfa/atco13.c | 243 + ast/erfa/atic13.c | 152 + ast/erfa/aticq.c | 199 + ast/erfa/aticqn.c | 237 + ast/erfa/atio13.c | 222 + ast/erfa/atioq.c | 243 + ast/erfa/atoc13.c | 233 + ast/erfa/atoi13.c | 228 + ast/erfa/atoiq.c | 260 + ast/erfa/bi00.c | 125 + ast/erfa/bp00.c | 181 + ast/erfa/bp06.c | 152 + ast/erfa/bpn2xy.c | 109 + ast/erfa/c2i00a.c | 148 + ast/erfa/c2i00b.c | 148 + ast/erfa/c2i06a.c | 145 + ast/erfa/c2ibpn.c | 151 + ast/erfa/c2ixy.c | 140 + ast/erfa/c2ixys.c | 132 + ast/erfa/c2s.c | 105 + ast/erfa/c2t00a.c | 163 + ast/erfa/c2t00b.c | 159 + ast/erfa/c2t06a.c | 161 + ast/erfa/c2tcio.c | 131 + ast/erfa/c2teqx.c | 131 + ast/erfa/c2tpe.c | 176 + ast/erfa/c2txy.c | 168 + ast/erfa/cal2jd.c | 148 + ast/erfa/cp.c | 89 + ast/erfa/cpv.c | 91 + ast/erfa/cr.c | 92 + ast/erfa/d2dtf.c | 245 + ast/erfa/d2tf.c | 169 + ast/erfa/dat.c | 306 + ast/erfa/dtdb.c | 1222 + ast/erfa/dtf2d.c | 212 + ast/erfa/eceq06.c | 141 + ast/erfa/ecm06.c | 144 + ast/erfa/ee00.c | 137 + ast/erfa/ee00a.c | 144 + ast/erfa/ee00b.c | 150 + ast/erfa/ee06a.c | 131 + ast/erfa/eect00.c | 291 + ast/erfa/eform.c | 155 + ast/erfa/eo06a.c | 140 + ast/erfa/eors.c | 117 + ast/erfa/epb.c | 100 + ast/erfa/epb2jd.c | 100 + ast/erfa/epj.c | 102 + ast/erfa/epj2jd.c | 100 + ast/erfa/epv00.c | 2598 ++ ast/erfa/eqec06.c | 142 + ast/erfa/eqeq94.c | 141 + ast/erfa/era00.c | 145 + ast/erfa/erfa.h | 517 + ast/erfa/erfam.h | 208 + ast/erfa/fad03.c | 112 + ast/erfa/fae03.c | 111 + ast/erfa/faf03.c | 115 + ast/erfa/faju03.c | 111 + ast/erfa/fal03.c | 112 + ast/erfa/falp03.c | 112 + ast/erfa/fama03.c | 111 + ast/erfa/fame03.c | 111 + ast/erfa/fane03.c | 108 + ast/erfa/faom03.c | 113 + ast/erfa/fapa03.c | 112 + ast/erfa/fasa03.c | 111 + ast/erfa/faur03.c | 108 + ast/erfa/fave03.c | 111 + ast/erfa/fk52h.c | 152 + ast/erfa/fk5hip.c | 135 + ast/erfa/fk5hz.c | 169 + ast/erfa/fw2m.c | 143 + ast/erfa/fw2xy.c | 130 + ast/erfa/g2icrs.c | 170 + ast/erfa/gc2gd.c | 143 + ast/erfa/gc2gde.c | 208 + ast/erfa/gd2gc.c | 142 + ast/erfa/gd2gce.c | 146 + ast/erfa/gmst00.c | 154 + ast/erfa/gmst06.c | 145 + ast/erfa/gmst82.c | 160 + ast/erfa/gst00a.c | 147 + ast/erfa/gst00b.c | 155 + ast/erfa/gst06.c | 149 + ast/erfa/gst06a.c | 140 + ast/erfa/gst94.c | 140 + ast/erfa/h2fk5.c | 157 + ast/erfa/hfk5z.c | 184 + ast/erfa/icrs2g.c | 170 + ast/erfa/ir.c | 92 + ast/erfa/jd2cal.c | 164 + ast/erfa/jdcalf.c | 170 + ast/erfa/ld.c | 161 + ast/erfa/ldn.c | 183 + ast/erfa/ldsun.c | 115 + ast/erfa/lteceq.c | 138 + ast/erfa/ltecm.c | 157 + ast/erfa/lteqec.c | 139 + ast/erfa/ltp.c | 140 + ast/erfa/ltpb.c | 133 + ast/erfa/ltpecl.c | 177 + ast/erfa/ltpequ.c | 177 + ast/erfa/num00a.c | 130 + ast/erfa/num00b.c | 130 + ast/erfa/num06a.c | 134 + ast/erfa/numat.c | 118 + ast/erfa/nut00a.c | 2056 + ast/erfa/nut00b.c | 381 + ast/erfa/nut06a.c | 162 + ast/erfa/nut80.c | 334 + ast/erfa/nutm80.c | 126 + ast/erfa/obl06.c | 127 + ast/erfa/obl80.c | 127 + ast/erfa/p06e.c | 330 + ast/erfa/p2pv.c | 92 + ast/erfa/p2s.c | 100 + ast/erfa/pap.c | 148 + ast/erfa/pas.c | 105 + ast/erfa/pb06.c | 153 + ast/erfa/pdp.c | 93 + ast/erfa/pfw06.c | 174 + ast/erfa/plan94.c | 523 + ast/erfa/pm.c | 85 + ast/erfa/pmat00.c | 127 + ast/erfa/pmat06.c | 131 + ast/erfa/pmat76.c | 150 + ast/erfa/pmp.c | 94 + ast/erfa/pmpx.c | 153 + ast/erfa/pmsafe.c | 206 + ast/erfa/pn.c | 118 + ast/erfa/pn00.c | 186 + ast/erfa/pn00a.c | 172 + ast/erfa/pn00b.c | 172 + ast/erfa/pn06.c | 196 + ast/erfa/pn06a.c | 162 + ast/erfa/pnm00a.c | 130 + ast/erfa/pnm00b.c | 130 + ast/erfa/pnm06a.c | 133 + ast/erfa/pnm80.c | 135 + ast/erfa/pom00.c | 124 + ast/erfa/ppp.c | 94 + ast/erfa/ppsp.c | 103 + ast/erfa/pr00.c | 151 + ast/erfa/prec76.c | 157 + ast/erfa/pv2p.c | 90 + ast/erfa/pv2s.c | 153 + ast/erfa/pvdpv.c | 111 + ast/erfa/pvm.c | 95 + ast/erfa/pvmpv.c | 96 + ast/erfa/pvppv.c | 96 + ast/erfa/pvstar.c | 216 + ast/erfa/pvtob.c | 162 + ast/erfa/pvu.c | 102 + ast/erfa/pvup.c | 97 + ast/erfa/pvxpv.c | 116 + ast/erfa/pxp.c | 103 + ast/erfa/refco.c | 262 + ast/erfa/rm2v.c | 120 + ast/erfa/rv2m.c | 127 + ast/erfa/rx.c | 119 + ast/erfa/rxp.c | 108 + ast/erfa/rxpv.c | 95 + ast/erfa/rxr.c | 108 + ast/erfa/ry.c | 119 + ast/erfa/rz.c | 119 + ast/erfa/s00.c | 380 + ast/erfa/s00a.c | 152 + ast/erfa/s00b.c | 152 + ast/erfa/s06.c | 377 + ast/erfa/s06a.c | 154 + ast/erfa/s2c.c | 94 + ast/erfa/s2p.c | 97 + ast/erfa/s2pv.c | 112 + ast/erfa/s2xpv.c | 96 + ast/erfa/sepp.c | 114 + ast/erfa/seps.c | 102 + ast/erfa/sp00.c | 127 + ast/erfa/starpm.c | 214 + ast/erfa/starpv.c | 273 + ast/erfa/sxp.c | 93 + ast/erfa/sxpv.c | 94 + ast/erfa/t_erfa_c.c | 9742 +++++ ast/erfa/taitt.c | 119 + ast/erfa/taiut1.c | 120 + ast/erfa/taiutc.c | 168 + ast/erfa/tcbtdb.c | 141 + ast/erfa/tcgtt.c | 118 + ast/erfa/tdbtcb.c | 146 + ast/erfa/tdbtt.c | 130 + ast/erfa/tf2a.c | 116 + ast/erfa/tf2d.c | 116 + ast/erfa/tr.c | 102 + ast/erfa/trxp.c | 102 + ast/erfa/trxpv.c | 102 + ast/erfa/tttai.c | 119 + ast/erfa/tttcg.c | 121 + ast/erfa/tttdb.c | 130 + ast/erfa/ttut1.c | 119 + ast/erfa/ut1tai.c | 120 + ast/erfa/ut1tt.c | 119 + ast/erfa/ut1utc.c | 202 + ast/erfa/utctai.c | 186 + ast/erfa/utcut1.c | 156 + ast/erfa/xy06.c | 2767 ++ ast/erfa/xys00a.c | 142 + ast/erfa/xys00b.c | 142 + ast/erfa/xys06a.c | 142 + ast/erfa/zp.c | 86 + ast/erfa/zpv.c | 88 + ast/erfa/zr.c | 92 + ast/erfa2ast.h | 248 + ast/erfam.h | 61 + ast/err.h | 66 + ast/err_drama.c | 122 + ast/err_ems.c | 108 + ast/err_null.c | 111 + ast/error.c | 1359 + ast/error.h | 362 + ast/f77.h.in | 1096 + ast/fbox.c | 110 + ast/fchannel.c | 473 + ast/fchebymap.c | 137 + ast/fcircle.c | 128 + ast/fcmpframe.c | 104 + ast/fcmpmap.c | 106 + ast/fcmpregion.c | 107 + ast/fdsbspecframe.c | 100 + ast/fdssmap.c | 75 + ast/fellipse.c | 136 + ast/ferror.c | 120 + ast/fetch | 5 + ast/ffitschan.c | 1023 + ast/ffitstable.c | 234 + ast/ffluxframe.c | 104 + ast/fframe.c | 514 + ast/fframeset.c | 214 + ast/fgrismmap.c | 99 + ast/finterval.c | 109 + ast/fintramap.c | 332 + ast/fitschan.c | 43747 +++++++++++++++++++ ast/fitschan.h | 933 + ast/fitstable.c | 3006 ++ ast/fitstable.h | 235 + ast/fkeymap.c | 1441 + ast/flutmap.c | 107 + ast/fluxframe.c | 4490 ++ ast/fluxframe.h | 267 + ast/fmapping.c | 771 + ast/fmathmap.c | 122 + ast/fmatrixmap.c | 110 + ast/fnormmap.c | 101 + ast/fnullregion.c | 104 + ast/fobject.c | 674 + ast/fpcdmap.c | 103 + ast/fpermmap.c | 110 + ast/fplot.c | 683 + ast/fplot3d.c | 107 + ast/fpointlist.c | 117 + ast/fpolygon.c | 226 + ast/fpolymap.c | 159 + ast/fprism.c | 105 + ast/frame.c | 16067 +++++++ ast/frame.f | 71 + ast/frame.h | 1459 + ast/frames.pdf | Bin 0 -> 7196 bytes ast/frameset.c | 13337 ++++++ ast/frameset.h | 714 + ast/frameset.pdf | Bin 0 -> 28443 bytes ast/fratemap.c | 106 + ast/fregion.c | 297 + ast/fronta.pdf | Bin 0 -> 22646 bytes ast/fronta_bw.pdf | Bin 0 -> 22635 bytes ast/frontb.pdf | Bin 0 -> 66281 bytes ast/frontb_bw.pdf | Bin 0 -> 66247 bytes ast/frontc.pdf | Bin 0 -> 43197 bytes ast/frontc_bw.pdf | Bin 0 -> 43186 bytes ast/fsalign.pdf | Bin 0 -> 51413 bytes ast/fsconvert.pdf | Bin 0 -> 18693 bytes ast/fselectormap.c | 115 + ast/fsexample.pdf | Bin 0 -> 17716 bytes ast/fshiftmap.c | 103 + ast/fskyframe.c | 112 + ast/fslamap.c | 122 + ast/fsmerge.pdf | Bin 0 -> 47115 bytes ast/fspecfluxframe.c | 104 + ast/fspecframe.c | 134 + ast/fspecmap.c | 124 + ast/fsphmap.c | 99 + ast/fsremap.pdf | Bin 0 -> 24970 bytes ast/fstc.c | 114 + ast/fstccatalogentrylocation.c | 117 + ast/fstcobsdatalocation.c | 117 + ast/fstcresourceprofile.c | 118 + ast/fstcschan.c | 131 + ast/fstcsearchlocation.c | 117 + ast/fswitchmap.c | 118 + ast/ftable.c | 330 + ast/ftemplateclass.c | 109 + ast/ftimeframe.c | 114 + ast/ftimemap.c | 122 + ast/ftranmap.c | 104 + ast/funitmap.c | 101 + ast/funitnormmap.c | 105 + ast/fwcsmap.c | 108 + ast/fwinmap.c | 110 + ast/fxmlchan.c | 130 + ast/fzoommap.c | 103 + ast/getatt | 163 + ast/getnewversion | 15 + ast/globals.c | 253 + ast/globals.h | 247 + ast/grf.h | 110 + ast/grf3d.c | 102 + ast/grf3d.h | 69 + ast/grf3d_pgplot.c | 3196 ++ ast/grf_2.0.c | 101 + ast/grf_3.2.c | 74 + ast/grf_5.6.c | 77 + ast/grf_null.c | 98 + ast/grf_pgplot.c | 1494 + ast/gridplot.pdf | Bin 0 -> 50536 bytes ast/gridplot_bw.pdf | Bin 0 -> 50506 bytes ast/grismmap.c | 2596 ++ ast/grismmap.h | 353 + ast/interval.c | 4686 ++ ast/interval.h | 236 + ast/intramap.c | 2942 ++ ast/intramap.h | 344 + ast/keymap.c | 10972 +++++ ast/keymap.h | 569 + ast/leap-seconds.png | Bin 0 -> 47101 bytes ast/loader.c | 199 + ast/loader.h | 49 + ast/lutmap.c | 2629 ++ ast/lutmap.h | 335 + ast/makeh | 313 + ast/mapping.c | 24692 +++++++++++ ast/mapping.h | 856 + ast/mapping.pdf | Bin 0 -> 8013 bytes ast/mathmap.c | 7421 ++++ ast/mathmap.h | 410 + ast/matrixmap.c | 5731 +++ ast/matrixmap.h | 318 + ast/memory.c | 5470 +++ ast/memory.h | 347 + ast/normmap.c | 1720 + ast/normmap.h | 217 + ast/nullregion.c | 2093 + ast/nullregion.h | 221 + ast/object.c | 8950 ++++ ast/object.f | 70 + ast/object.h.in | 1960 + ast/overgrid.pdf | Bin 0 -> 26963 bytes ast/overgrid_bw.pdf | Bin 0 -> 25865 bytes ast/pal.h | 582 + ast/pal/pal.h | 551 + ast/pal/pal1sofa.h | 142 + ast/pal/palAddet.c | 112 + ast/pal/palAmpqk.c | 159 + ast/pal/palCaldj.c | 99 + ast/pal/palDat.c | 95 + ast/pal/palDe2h.c | 142 + ast/pal/palDeuler.c | 141 + ast/pal/palDh2e.c | 133 + ast/pal/palDjcal.c | 97 + ast/pal/palDmat.c | 182 + ast/pal/palDrange.c | 77 + ast/pal/palDs2tp.c | 127 + ast/pal/palDtp2s.c | 95 + ast/pal/palDtps2c.c | 151 + ast/pal/palDtt.c | 77 + ast/pal/palEcmat.c | 82 + ast/pal/palEqgal.c | 118 + ast/pal/palEtrms.c | 106 + ast/pal/palEvp.c | 110 + ast/pal/palFk45z.c | 186 + ast/pal/palFk524.c | 259 + ast/pal/palFk54z.c | 113 + ast/pal/palGaleq.c | 118 + ast/pal/palGalsup.c | 116 + ast/pal/palGeoc.c | 83 + ast/pal/palMappa.c | 129 + ast/pal/palMapqkz.c | 150 + ast/pal/palOne2One.c | 1482 + ast/pal/palPrebn.c | 98 + ast/pal/palPrec.c | 107 + ast/pal/palPrenut.c | 111 + ast/pal/palPvobs.c | 108 + ast/pal/palRvgalc.c | 111 + ast/pal/palRvlg.c | 106 + ast/pal/palRvlsrd.c | 116 + ast/pal/palRvlsrk.c | 116 + ast/pal/palSubet.c | 112 + ast/pal/palSupgal.c | 116 + ast/pal/palmac.h | 136 + ast/pal2ast.h | 134 + ast/palwrap.c | 301 + ast/parallel.pdf | Bin 0 -> 10441 bytes ast/pcdmap.c | 3218 ++ ast/pcdmap.h | 357 + ast/permmap.c | 3204 ++ ast/permmap.h | 322 + ast/pg3d.h | 68 + ast/plot.c | 32074 ++++++++++++++ ast/plot.f | 71 + ast/plot.h | 1417 + ast/plot3d.c | 8587 ++++ ast/plot3d.h | 258 + ast/pointlist.c | 3407 ++ ast/pointlist.h | 239 + ast/pointset.c | 3285 ++ ast/pointset.h | 711 + ast/polygon.c | 7087 +++ ast/polygon.h | 353 + ast/polymap.c | 6107 +++ ast/polymap.h | 386 + ast/prepare_all | 41 + ast/prepare_docs | 16 + ast/prepare_hyperdocs | 37 + ast/prepare_release | 39 + ast/prism.c | 4448 ++ ast/prism.h | 238 + ast/proj.c | 4840 ++ ast/proj.h | 181 + ast/ratemap.c | 2011 + ast/ratemap.h | 276 + ast/region.c | 13502 ++++++ ast/region.h | 515 + ast/selectfc | 29 + ast/selectormap.c | 1838 + ast/selectormap.h | 277 + ast/series.pdf | Bin 0 -> 10149 bytes ast/shiftmap.c | 1617 + ast/shiftmap.h | 290 + ast/simpexamp.pdf | Bin 0 -> 7842 bytes ast/skyaxis.c | 5150 +++ ast/skyaxis.h | 428 + ast/skyframe.c | 12592 ++++++ ast/skyframe.h | 508 + ast/slamap.c | 5027 +++ ast/slamap.h | 330 + ast/specfluxframe.c | 2189 + ast/specfluxframe.h | 215 + ast/specframe.c | 7437 ++++ ast/specframe.h | 430 + ast/specmap.c | 4696 ++ ast/specmap.h | 282 + ast/sphmap.c | 2061 + ast/sphmap.h | 374 + ast/stc.c | 3703 ++ ast/stc.h | 240 + ast/stccatalogentrylocation.c | 804 + ast/stccatalogentrylocation.h | 223 + ast/stcobsdatalocation.c | 1051 + ast/stcobsdatalocation.h | 236 + ast/stcresourceprofile.c | 807 + ast/stcresourceprofile.h | 223 + ast/stcs-ex1.txt | 13 + ast/stcschan-demo1.c | 288 + ast/stcschan-demo2.c | 263 + ast/stcschan-demo3.c | 435 + ast/stcschan-demo4.c | 262 + ast/stcschan-demo5.c | 300 + ast/stcschan.c | 8732 ++++ ast/stcschan.h | 308 + ast/stcsearchlocation.c | 806 + ast/stcsearchlocation.h | 222 + ast/sun210_figures/cmpframe.pdf | Bin 0 -> 7432 bytes ast/sun210_figures/complex.pdf | Bin 0 -> 15323 bytes ast/sun210_figures/frames.pdf | Bin 0 -> 7196 bytes ast/sun210_figures/frameset.pdf | Bin 0 -> 28443 bytes ast/sun210_figures/fronta.pdf | Bin 0 -> 22394 bytes ast/sun210_figures/fronta_bw.pdf | Bin 0 -> 22635 bytes ast/sun210_figures/frontb.pdf | Bin 0 -> 57273 bytes ast/sun210_figures/frontb_bw.pdf | Bin 0 -> 66247 bytes ast/sun210_figures/frontc.pdf | Bin 0 -> 57629 bytes ast/sun210_figures/frontc_bw.pdf | Bin 0 -> 43186 bytes ast/sun210_figures/fsalign.pdf | Bin 0 -> 51413 bytes ast/sun210_figures/fsconvert.pdf | Bin 0 -> 18693 bytes ast/sun210_figures/fsexample.pdf | Bin 0 -> 17716 bytes ast/sun210_figures/fsmerge.pdf | Bin 0 -> 47115 bytes ast/sun210_figures/fsremap.pdf | Bin 0 -> 24970 bytes ast/sun210_figures/gridplot.pdf | Bin 0 -> 50536 bytes ast/sun210_figures/gridplot_bw.pdf | Bin 0 -> 20421 bytes ast/sun210_figures/mapping.pdf | Bin 0 -> 8013 bytes ast/sun210_figures/overgrid.pdf | Bin 0 -> 26963 bytes ast/sun210_figures/overgrid_bw.pdf | Bin 0 -> 25865 bytes ast/sun210_figures/parallel.pdf | Bin 0 -> 10441 bytes ast/sun210_figures/series.pdf | Bin 0 -> 10149 bytes ast/sun210_figures/simpexamp.pdf | Bin 0 -> 7842 bytes ast/sun211_figures/cmpframe.pdf | Bin 0 -> 7432 bytes ast/sun211_figures/complex.pdf | Bin 0 -> 15323 bytes ast/sun211_figures/frames.pdf | Bin 0 -> 7196 bytes ast/sun211_figures/frameset.pdf | Bin 0 -> 28443 bytes ast/sun211_figures/fronta.pdf | Bin 0 -> 22394 bytes ast/sun211_figures/fronta_bw.pdf | Bin 0 -> 22635 bytes ast/sun211_figures/frontb.pdf | Bin 0 -> 57273 bytes ast/sun211_figures/frontb_bw.pdf | Bin 0 -> 66247 bytes ast/sun211_figures/frontc.pdf | Bin 0 -> 57629 bytes ast/sun211_figures/frontc_bw.pdf | Bin 0 -> 43186 bytes ast/sun211_figures/fsalign.pdf | Bin 0 -> 51413 bytes ast/sun211_figures/fsconvert.pdf | Bin 0 -> 18693 bytes ast/sun211_figures/fsexample.pdf | Bin 0 -> 17716 bytes ast/sun211_figures/fsmerge.pdf | Bin 0 -> 47115 bytes ast/sun211_figures/fsremap.pdf | Bin 0 -> 24962 bytes ast/sun211_figures/gridplot.pdf | Bin 0 -> 50536 bytes ast/sun211_figures/gridplot_bw.pdf | Bin 0 -> 20421 bytes ast/sun211_figures/mapping.pdf | Bin 0 -> 8013 bytes ast/sun211_figures/overgrid.pdf | Bin 0 -> 26963 bytes ast/sun211_figures/overgrid_bw.pdf | Bin 0 -> 25865 bytes ast/sun211_figures/parallel.pdf | Bin 0 -> 10441 bytes ast/sun211_figures/series.pdf | Bin 0 -> 10149 bytes ast/sun211_figures/simpexamp.pdf | Bin 0 -> 7842 bytes ast/sun_master.tex | 21879 ++++++++++ ast/switchmap.c | 2875 ++ ast/switchmap.h | 289 + ast/table.c | 5246 +++ ast/table.h | 309 + ast/templateclass.README | 29 + ast/templateclass.c | 1483 + ast/templateclass.h | 216 + ast/timeframe.c | 7530 ++++ ast/timeframe.h | 324 + ast/timemap.c | 5330 +++ ast/timemap.h | 285 + ast/tpn.c | 393 + ast/tranmap.c | 2327 + ast/tranmap.h | 276 + ast/unit.c | 6218 +++ ast/unit.h | 83 + ast/unitmap.c | 1425 + ast/unitmap.h | 288 + ast/unitnormmap.c | 1666 + ast/unitnormmap.h | 299 + ast/version.h.in | 73 + ast/wcsmap.c | 6094 +++ ast/wcsmap.h | 591 + ast/wcsmath.h | 67 + ast/wcstrig.c | 189 + ast/wcstrig.h | 63 + ast/winmap.c | 4389 ++ ast/winmap.h | 300 + ast/xml.c | 7119 +++ ast/xml.h | 392 + ast/xmlchan.c | 14120 ++++++ ast/xmlchan.h | 300 + ast/zoommap.c | 2074 + ast/zoommap.h | 322 + 813 files changed, 624593 insertions(+) create mode 100644 ast/.gitignore create mode 100644 ast/AST_to_do create mode 100644 ast/COPYING create mode 100644 ast/COPYING.LESSER create mode 100644 ast/COPYING.LIB create mode 100644 ast/Ers.h create mode 100644 ast/GRF_PAR create mode 100644 ast/Makefile.am create mode 100644 ast/Notes create mode 100644 ast/STARLINK_BRANCHES create mode 100755 ast/addcopyright create mode 100755 ast/addlinks create mode 100644 ast/addversion.in create mode 100644 ast/ast-for-wcslib/astTester.c create mode 100644 ast/ast-for-wcslib/loader.c create mode 100644 ast/ast-for-wcslib/matrixmap.h create mode 100644 ast/ast-for-wcslib/plot.h create mode 100644 ast/ast-for-wcslib/wcslib-instructions create mode 100644 ast/ast.news create mode 100644 ast/ast_cpp.in create mode 100644 ast/ast_dev create mode 100644 ast/ast_err.msg create mode 100644 ast/ast_link.in create mode 100644 ast/ast_link_adam.in create mode 100644 ast/ast_par.source create mode 100644 ast/ast_test.c create mode 100644 ast/ast_tester/README create mode 100644 ast/ast_tester/a20070718_00010_02_cube.ast create mode 100644 ast/ast_tester/a20070718_00010_02_cube.fits-wcs create mode 100644 ast/ast_tester/aitoff.attr create mode 100644 ast/ast_tester/aitoff.box create mode 100644 ast/ast_tester/aitoff.head create mode 100755 ast/ast_tester/ast_tester create mode 100644 ast/ast_tester/brad.map create mode 100644 ast/ast_tester/brad.simp create mode 100644 ast/ast_tester/car1.attr create mode 100644 ast/ast_tester/car1.box create mode 100644 ast/ast_tester/car1.fattr create mode 100644 ast/ast_tester/car1.head create mode 100644 ast/ast_tester/car2.attr create mode 100644 ast/ast_tester/car2.box create mode 100644 ast/ast_tester/car2.fattr create mode 100644 ast/ast_tester/car2.head create mode 100644 ast/ast_tester/car3.attr create mode 100644 ast/ast_tester/car3.box create mode 100644 ast/ast_tester/car3.head create mode 100644 ast/ast_tester/car4.attr create mode 100644 ast/ast_tester/car4.box create mode 100644 ast/ast_tester/car4.fattr create mode 100644 ast/ast_tester/car4.head create mode 100644 ast/ast_tester/car5.attr create mode 100644 ast/ast_tester/car5.box create mode 100644 ast/ast_tester/car5.head create mode 100644 ast/ast_tester/car6.attr create mode 100644 ast/ast_tester/car6.box create mode 100644 ast/ast_tester/car6.head create mode 100644 ast/ast_tester/cobe.attr create mode 100644 ast/ast_tester/cobe.box create mode 100644 ast/ast_tester/cobe.head create mode 100644 ast/ast_tester/degen1.ast create mode 100644 ast/ast_tester/degen1.fits-wcs create mode 100755 ast/ast_tester/doplot create mode 100644 ast/ast_tester/dss.ast create mode 100644 ast/ast_tester/dss.dss create mode 100644 ast/ast_tester/dss.fits-dss create mode 100644 ast/ast_tester/dss.fits-wcs create mode 100644 ast/ast_tester/hpx.attr create mode 100644 ast/ast_tester/hpx.box create mode 100644 ast/ast_tester/hpx.head create mode 100644 ast/ast_tester/joye_car_headers/CAR_model.head create mode 100644 ast/ast_tester/joye_car_headers/CHIPASS_Equ.head create mode 100644 ast/ast_tester/joye_car_headers/car1.fattr create mode 100644 ast/ast_tester/joye_car_headers/car1.head create mode 100644 ast/ast_tester/joye_car_headers/car2.fattr create mode 100644 ast/ast_tester/joye_car_headers/car2.head create mode 100644 ast/ast_tester/joye_car_headers/car3.head create mode 100644 ast/ast_tester/joye_car_headers/car4.fattr create mode 100644 ast/ast_tester/joye_car_headers/car4.head create mode 100644 ast/ast_tester/joye_car_headers/car5.head create mode 100644 ast/ast_tester/joye_car_headers/cmap_3years_GP_D2.head create mode 100755 ast/ast_tester/joye_car_headers/doit create mode 100644 ast/ast_tester/joye_car_headers/total_hi.head create mode 100644 ast/ast_tester/longslit.fits-pc create mode 100644 ast/ast_tester/longslit.fits-wcs create mode 100755 ast/ast_tester/makeplot create mode 100755 ast/ast_tester/maketest create mode 100644 ast/ast_tester/origin.attr create mode 100644 ast/ast_tester/origin.box create mode 100644 ast/ast_tester/origin.head create mode 100644 ast/ast_tester/plot3d-test1.ast create mode 100644 ast/ast_tester/plotter.f create mode 100644 ast/ast_tester/polco.attr create mode 100644 ast/ast_tester/polco.box create mode 100644 ast/ast_tester/polco.head create mode 100644 ast/ast_tester/polco2.attr create mode 100644 ast/ast_tester/polco2.box create mode 100644 ast/ast_tester/polco2.head create mode 100644 ast/ast_tester/regression.current create mode 100644 ast/ast_tester/regression.f create mode 100644 ast/ast_tester/regression.out create mode 100644 ast/ast_tester/rigby.map create mode 100644 ast/ast_tester/rigby.simp create mode 100644 ast/ast_tester/scp.attr create mode 100644 ast/ast_tester/scp.box create mode 100644 ast/ast_tester/scp.head create mode 100644 ast/ast_tester/serpens.attr create mode 100644 ast/ast_tester/serpens.box create mode 100644 ast/ast_tester/serpens.head create mode 100644 ast/ast_tester/simplify.f create mode 100644 ast/ast_tester/sip.fits-wcs create mode 100644 ast/ast_tester/sip.head create mode 100644 ast/ast_tester/sparse.ast create mode 100644 ast/ast_tester/specflux.ast create mode 100644 ast/ast_tester/specflux.attr create mode 100644 ast/ast_tester/specflux.box create mode 100644 ast/ast_tester/specflux.head create mode 100644 ast/ast_tester/splittest1.ast create mode 100644 ast/ast_tester/stcschan-test1-doc3-props.ast create mode 100644 ast/ast_tester/stcschan-test1-doc3.ast create mode 100644 ast/ast_tester/testchannel.f create mode 100644 ast/ast_tester/testchebymap.f create mode 100644 ast/ast_tester/testcmpmap.f create mode 100644 ast/ast_tester/testconvert.c create mode 100644 ast/ast_tester/testerror.c create mode 100644 ast/ast_tester/testfitschan.f create mode 100644 ast/ast_tester/testfitstable.f create mode 100644 ast/ast_tester/testflux.f create mode 100644 ast/ast_tester/testframeset.f create mode 100644 ast/ast_tester/testkeymap.f create mode 100644 ast/ast_tester/testlutmap.f create mode 100644 ast/ast_tester/testmapping.f create mode 100644 ast/ast_tester/testnormmap.f create mode 100644 ast/ast_tester/testobject.c create mode 100644 ast/ast_tester/testplot3d.f create mode 100644 ast/ast_tester/testpolymap.f create mode 100644 ast/ast_tester/testrate.f create mode 100644 ast/ast_tester/testratemap.f create mode 100644 ast/ast_tester/testrebin.f create mode 100644 ast/ast_tester/testrebinseq.f create mode 100644 ast/ast_tester/testregions.f create mode 100644 ast/ast_tester/testskyframe.f create mode 100644 ast/ast_tester/testspecflux.f create mode 100644 ast/ast_tester/testspecframe.f create mode 100644 ast/ast_tester/teststc.f create mode 100644 ast/ast_tester/teststc_com create mode 100644 ast/ast_tester/teststc_eg1 create mode 100644 ast/ast_tester/teststc_eg10 create mode 100644 ast/ast_tester/teststc_eg2 create mode 100644 ast/ast_tester/teststc_eg3 create mode 100644 ast/ast_tester/teststc_eg4 create mode 100644 ast/ast_tester/teststc_eg5 create mode 100644 ast/ast_tester/teststc_eg6 create mode 100644 ast/ast_tester/teststc_eg7 create mode 100644 ast/ast_tester/teststc_eg8 create mode 100644 ast/ast_tester/teststc_eg9 create mode 100755 ast/ast_tester/teststcschan.f create mode 100644 ast/ast_tester/testswitchmap.f create mode 100644 ast/ast_tester/testtable.f create mode 100644 ast/ast_tester/testtime.f create mode 100644 ast/ast_tester/testtrangrid.f create mode 100644 ast/ast_tester/testunitnormmap.f create mode 100644 ast/ast_tester/testxmlchan.f create mode 100644 ast/ast_tester/testxmlchan_com create mode 100644 ast/ast_tester/testzoommap.f create mode 100644 ast/ast_tester/timeplot.attr create mode 100644 ast/ast_tester/timeplot.box create mode 100644 ast/ast_tester/timeplot.head create mode 100644 ast/ast_tester/timj.ast create mode 100644 ast/ast_tester/timj.fits-aips create mode 100644 ast/ast_tester/timj.fits-iraf create mode 100644 ast/ast_tester/timj.fits-pc create mode 100644 ast/ast_tester/timj.fits-wcs create mode 100644 ast/ast_tester/timj.native create mode 100644 ast/ast_tester/tnx.attr create mode 100644 ast/ast_tester/tnx.box create mode 100644 ast/ast_tester/tnx.head create mode 100644 ast/ast_tester/tsc.attr create mode 100644 ast/ast_tester/tsc.box create mode 100644 ast/ast_tester/tsc.head create mode 100644 ast/ast_tester/wcsconverter.f create mode 100644 ast/ast_tester/zpn.attr create mode 100644 ast/ast_tester/zpn.box create mode 100644 ast/ast_tester/zpn.head create mode 100644 ast/ast_tester/zpx.attr create mode 100644 ast/ast_tester/zpx.head create mode 100644 ast/astbad.c create mode 100644 ast/axis.c create mode 100644 ast/axis.h create mode 100755 ast/bootstrap create mode 100644 ast/box.c create mode 100644 ast/box.h create mode 100644 ast/builddocs.in create mode 100755 ast/buildhyperdocs create mode 100644 ast/c2f77.c create mode 100644 ast/c2f77.h create mode 100755 ast/cexpand create mode 100755 ast/cexpand.pl create mode 100644 ast/channel.c create mode 100644 ast/channel.h create mode 100644 ast/chebymap.c create mode 100644 ast/chebymap.h create mode 100644 ast/circle.c create mode 100644 ast/circle.h create mode 100644 ast/cminpack/CopyrightMINPACK.txt create mode 100644 ast/cminpack/README.md create mode 100644 ast/cminpack/cminpack.h create mode 100644 ast/cminpack/cminpackP.h create mode 100644 ast/cminpack/dpmpar.c create mode 100644 ast/cminpack/enorm.c create mode 100644 ast/cminpack/lmder.c create mode 100644 ast/cminpack/lmder1.c create mode 100644 ast/cminpack/lmpar.c create mode 100644 ast/cminpack/qrfac.c create mode 100644 ast/cminpack/qrsolv.c create mode 100644 ast/cmpframe.c create mode 100644 ast/cmpframe.h create mode 100644 ast/cmpframe.pdf create mode 100644 ast/cmpmap.c create mode 100644 ast/cmpmap.h create mode 100644 ast/cmpregion.c create mode 100644 ast/cmpregion.h create mode 100644 ast/complex.pdf create mode 100644 ast/component.xml create mode 100644 ast/component.xml.in create mode 100644 ast/configure.ac create mode 100644 ast/devtools/make_simtest create mode 100644 ast/devtools/simtest.c create mode 100755 ast/doincludes create mode 100644 ast/dsbspecframe.c create mode 100644 ast/dsbspecframe.h create mode 100644 ast/dssmap.c create mode 100644 ast/dssmap.h create mode 100644 ast/ellipse.c create mode 100644 ast/ellipse.h create mode 100644 ast/ems.h create mode 100644 ast/erfa.h create mode 100644 ast/erfa/INFO create mode 100644 ast/erfa/LICENSE create mode 100644 ast/erfa/Makefile.am create mode 100644 ast/erfa/README.rst create mode 100644 ast/erfa/RELEASE.rst create mode 100644 ast/erfa/a2af.c create mode 100644 ast/erfa/a2tf.c create mode 100644 ast/erfa/ab.c create mode 100644 ast/erfa/af2a.c create mode 100644 ast/erfa/anp.c create mode 100644 ast/erfa/anpm.c create mode 100644 ast/erfa/apcg.c create mode 100644 ast/erfa/apcg13.c create mode 100644 ast/erfa/apci.c create mode 100644 ast/erfa/apci13.c create mode 100644 ast/erfa/apco.c create mode 100644 ast/erfa/apco13.c create mode 100644 ast/erfa/apcs.c create mode 100644 ast/erfa/apcs13.c create mode 100644 ast/erfa/aper.c create mode 100644 ast/erfa/aper13.c create mode 100644 ast/erfa/apio.c create mode 100644 ast/erfa/apio13.c create mode 100644 ast/erfa/atci13.c create mode 100644 ast/erfa/atciq.c create mode 100644 ast/erfa/atciqn.c create mode 100644 ast/erfa/atciqz.c create mode 100644 ast/erfa/atco13.c create mode 100644 ast/erfa/atic13.c create mode 100644 ast/erfa/aticq.c create mode 100644 ast/erfa/aticqn.c create mode 100644 ast/erfa/atio13.c create mode 100644 ast/erfa/atioq.c create mode 100644 ast/erfa/atoc13.c create mode 100644 ast/erfa/atoi13.c create mode 100644 ast/erfa/atoiq.c create mode 100644 ast/erfa/bi00.c create mode 100644 ast/erfa/bp00.c create mode 100644 ast/erfa/bp06.c create mode 100644 ast/erfa/bpn2xy.c create mode 100644 ast/erfa/c2i00a.c create mode 100644 ast/erfa/c2i00b.c create mode 100644 ast/erfa/c2i06a.c create mode 100644 ast/erfa/c2ibpn.c create mode 100644 ast/erfa/c2ixy.c create mode 100644 ast/erfa/c2ixys.c create mode 100644 ast/erfa/c2s.c create mode 100644 ast/erfa/c2t00a.c create mode 100644 ast/erfa/c2t00b.c create mode 100644 ast/erfa/c2t06a.c create mode 100644 ast/erfa/c2tcio.c create mode 100644 ast/erfa/c2teqx.c create mode 100644 ast/erfa/c2tpe.c create mode 100644 ast/erfa/c2txy.c create mode 100644 ast/erfa/cal2jd.c create mode 100644 ast/erfa/cp.c create mode 100644 ast/erfa/cpv.c create mode 100644 ast/erfa/cr.c create mode 100644 ast/erfa/d2dtf.c create mode 100644 ast/erfa/d2tf.c create mode 100644 ast/erfa/dat.c create mode 100644 ast/erfa/dtdb.c create mode 100644 ast/erfa/dtf2d.c create mode 100644 ast/erfa/eceq06.c create mode 100644 ast/erfa/ecm06.c create mode 100644 ast/erfa/ee00.c create mode 100644 ast/erfa/ee00a.c create mode 100644 ast/erfa/ee00b.c create mode 100644 ast/erfa/ee06a.c create mode 100644 ast/erfa/eect00.c create mode 100644 ast/erfa/eform.c create mode 100644 ast/erfa/eo06a.c create mode 100644 ast/erfa/eors.c create mode 100644 ast/erfa/epb.c create mode 100644 ast/erfa/epb2jd.c create mode 100644 ast/erfa/epj.c create mode 100644 ast/erfa/epj2jd.c create mode 100644 ast/erfa/epv00.c create mode 100644 ast/erfa/eqec06.c create mode 100644 ast/erfa/eqeq94.c create mode 100644 ast/erfa/era00.c create mode 100644 ast/erfa/erfa.h create mode 100644 ast/erfa/erfam.h create mode 100644 ast/erfa/fad03.c create mode 100644 ast/erfa/fae03.c create mode 100644 ast/erfa/faf03.c create mode 100644 ast/erfa/faju03.c create mode 100644 ast/erfa/fal03.c create mode 100644 ast/erfa/falp03.c create mode 100644 ast/erfa/fama03.c create mode 100644 ast/erfa/fame03.c create mode 100644 ast/erfa/fane03.c create mode 100644 ast/erfa/faom03.c create mode 100644 ast/erfa/fapa03.c create mode 100644 ast/erfa/fasa03.c create mode 100644 ast/erfa/faur03.c create mode 100644 ast/erfa/fave03.c create mode 100644 ast/erfa/fk52h.c create mode 100644 ast/erfa/fk5hip.c create mode 100644 ast/erfa/fk5hz.c create mode 100644 ast/erfa/fw2m.c create mode 100644 ast/erfa/fw2xy.c create mode 100644 ast/erfa/g2icrs.c create mode 100644 ast/erfa/gc2gd.c create mode 100644 ast/erfa/gc2gde.c create mode 100644 ast/erfa/gd2gc.c create mode 100644 ast/erfa/gd2gce.c create mode 100644 ast/erfa/gmst00.c create mode 100644 ast/erfa/gmst06.c create mode 100644 ast/erfa/gmst82.c create mode 100644 ast/erfa/gst00a.c create mode 100644 ast/erfa/gst00b.c create mode 100644 ast/erfa/gst06.c create mode 100644 ast/erfa/gst06a.c create mode 100644 ast/erfa/gst94.c create mode 100644 ast/erfa/h2fk5.c create mode 100644 ast/erfa/hfk5z.c create mode 100644 ast/erfa/icrs2g.c create mode 100644 ast/erfa/ir.c create mode 100644 ast/erfa/jd2cal.c create mode 100644 ast/erfa/jdcalf.c create mode 100644 ast/erfa/ld.c create mode 100644 ast/erfa/ldn.c create mode 100644 ast/erfa/ldsun.c create mode 100644 ast/erfa/lteceq.c create mode 100644 ast/erfa/ltecm.c create mode 100644 ast/erfa/lteqec.c create mode 100644 ast/erfa/ltp.c create mode 100644 ast/erfa/ltpb.c create mode 100644 ast/erfa/ltpecl.c create mode 100644 ast/erfa/ltpequ.c create mode 100644 ast/erfa/num00a.c create mode 100644 ast/erfa/num00b.c create mode 100644 ast/erfa/num06a.c create mode 100644 ast/erfa/numat.c create mode 100644 ast/erfa/nut00a.c create mode 100644 ast/erfa/nut00b.c create mode 100644 ast/erfa/nut06a.c create mode 100644 ast/erfa/nut80.c create mode 100644 ast/erfa/nutm80.c create mode 100644 ast/erfa/obl06.c create mode 100644 ast/erfa/obl80.c create mode 100644 ast/erfa/p06e.c create mode 100644 ast/erfa/p2pv.c create mode 100644 ast/erfa/p2s.c create mode 100644 ast/erfa/pap.c create mode 100644 ast/erfa/pas.c create mode 100644 ast/erfa/pb06.c create mode 100644 ast/erfa/pdp.c create mode 100644 ast/erfa/pfw06.c create mode 100644 ast/erfa/plan94.c create mode 100644 ast/erfa/pm.c create mode 100644 ast/erfa/pmat00.c create mode 100644 ast/erfa/pmat06.c create mode 100644 ast/erfa/pmat76.c create mode 100644 ast/erfa/pmp.c create mode 100644 ast/erfa/pmpx.c create mode 100644 ast/erfa/pmsafe.c create mode 100644 ast/erfa/pn.c create mode 100644 ast/erfa/pn00.c create mode 100644 ast/erfa/pn00a.c create mode 100644 ast/erfa/pn00b.c create mode 100644 ast/erfa/pn06.c create mode 100644 ast/erfa/pn06a.c create mode 100644 ast/erfa/pnm00a.c create mode 100644 ast/erfa/pnm00b.c create mode 100644 ast/erfa/pnm06a.c create mode 100644 ast/erfa/pnm80.c create mode 100644 ast/erfa/pom00.c create mode 100644 ast/erfa/ppp.c create mode 100644 ast/erfa/ppsp.c create mode 100644 ast/erfa/pr00.c create mode 100644 ast/erfa/prec76.c create mode 100644 ast/erfa/pv2p.c create mode 100644 ast/erfa/pv2s.c create mode 100644 ast/erfa/pvdpv.c create mode 100644 ast/erfa/pvm.c create mode 100644 ast/erfa/pvmpv.c create mode 100644 ast/erfa/pvppv.c create mode 100644 ast/erfa/pvstar.c create mode 100644 ast/erfa/pvtob.c create mode 100644 ast/erfa/pvu.c create mode 100644 ast/erfa/pvup.c create mode 100644 ast/erfa/pvxpv.c create mode 100644 ast/erfa/pxp.c create mode 100644 ast/erfa/refco.c create mode 100644 ast/erfa/rm2v.c create mode 100644 ast/erfa/rv2m.c create mode 100644 ast/erfa/rx.c create mode 100644 ast/erfa/rxp.c create mode 100644 ast/erfa/rxpv.c create mode 100644 ast/erfa/rxr.c create mode 100644 ast/erfa/ry.c create mode 100644 ast/erfa/rz.c create mode 100644 ast/erfa/s00.c create mode 100644 ast/erfa/s00a.c create mode 100644 ast/erfa/s00b.c create mode 100644 ast/erfa/s06.c create mode 100644 ast/erfa/s06a.c create mode 100644 ast/erfa/s2c.c create mode 100644 ast/erfa/s2p.c create mode 100644 ast/erfa/s2pv.c create mode 100644 ast/erfa/s2xpv.c create mode 100644 ast/erfa/sepp.c create mode 100644 ast/erfa/seps.c create mode 100644 ast/erfa/sp00.c create mode 100644 ast/erfa/starpm.c create mode 100644 ast/erfa/starpv.c create mode 100644 ast/erfa/sxp.c create mode 100644 ast/erfa/sxpv.c create mode 100644 ast/erfa/t_erfa_c.c create mode 100644 ast/erfa/taitt.c create mode 100644 ast/erfa/taiut1.c create mode 100644 ast/erfa/taiutc.c create mode 100644 ast/erfa/tcbtdb.c create mode 100644 ast/erfa/tcgtt.c create mode 100644 ast/erfa/tdbtcb.c create mode 100644 ast/erfa/tdbtt.c create mode 100644 ast/erfa/tf2a.c create mode 100644 ast/erfa/tf2d.c create mode 100644 ast/erfa/tr.c create mode 100644 ast/erfa/trxp.c create mode 100644 ast/erfa/trxpv.c create mode 100644 ast/erfa/tttai.c create mode 100644 ast/erfa/tttcg.c create mode 100644 ast/erfa/tttdb.c create mode 100644 ast/erfa/ttut1.c create mode 100644 ast/erfa/ut1tai.c create mode 100644 ast/erfa/ut1tt.c create mode 100644 ast/erfa/ut1utc.c create mode 100644 ast/erfa/utctai.c create mode 100644 ast/erfa/utcut1.c create mode 100644 ast/erfa/xy06.c create mode 100644 ast/erfa/xys00a.c create mode 100644 ast/erfa/xys00b.c create mode 100644 ast/erfa/xys06a.c create mode 100644 ast/erfa/zp.c create mode 100644 ast/erfa/zpv.c create mode 100644 ast/erfa/zr.c create mode 100644 ast/erfa2ast.h create mode 100644 ast/erfam.h create mode 100644 ast/err.h create mode 100644 ast/err_drama.c create mode 100644 ast/err_ems.c create mode 100644 ast/err_null.c create mode 100644 ast/error.c create mode 100644 ast/error.h create mode 100644 ast/f77.h.in create mode 100644 ast/fbox.c create mode 100644 ast/fchannel.c create mode 100644 ast/fchebymap.c create mode 100644 ast/fcircle.c create mode 100644 ast/fcmpframe.c create mode 100644 ast/fcmpmap.c create mode 100644 ast/fcmpregion.c create mode 100644 ast/fdsbspecframe.c create mode 100644 ast/fdssmap.c create mode 100644 ast/fellipse.c create mode 100644 ast/ferror.c create mode 100755 ast/fetch create mode 100644 ast/ffitschan.c create mode 100644 ast/ffitstable.c create mode 100644 ast/ffluxframe.c create mode 100644 ast/fframe.c create mode 100644 ast/fframeset.c create mode 100644 ast/fgrismmap.c create mode 100644 ast/finterval.c create mode 100644 ast/fintramap.c create mode 100644 ast/fitschan.c create mode 100644 ast/fitschan.h create mode 100644 ast/fitstable.c create mode 100644 ast/fitstable.h create mode 100644 ast/fkeymap.c create mode 100644 ast/flutmap.c create mode 100644 ast/fluxframe.c create mode 100644 ast/fluxframe.h create mode 100644 ast/fmapping.c create mode 100644 ast/fmathmap.c create mode 100644 ast/fmatrixmap.c create mode 100644 ast/fnormmap.c create mode 100644 ast/fnullregion.c create mode 100644 ast/fobject.c create mode 100644 ast/fpcdmap.c create mode 100644 ast/fpermmap.c create mode 100644 ast/fplot.c create mode 100644 ast/fplot3d.c create mode 100644 ast/fpointlist.c create mode 100644 ast/fpolygon.c create mode 100644 ast/fpolymap.c create mode 100644 ast/fprism.c create mode 100644 ast/frame.c create mode 100644 ast/frame.f create mode 100644 ast/frame.h create mode 100644 ast/frames.pdf create mode 100644 ast/frameset.c create mode 100644 ast/frameset.h create mode 100644 ast/frameset.pdf create mode 100644 ast/fratemap.c create mode 100644 ast/fregion.c create mode 100644 ast/fronta.pdf create mode 100644 ast/fronta_bw.pdf create mode 100644 ast/frontb.pdf create mode 100644 ast/frontb_bw.pdf create mode 100644 ast/frontc.pdf create mode 100644 ast/frontc_bw.pdf create mode 100644 ast/fsalign.pdf create mode 100644 ast/fsconvert.pdf create mode 100644 ast/fselectormap.c create mode 100644 ast/fsexample.pdf create mode 100644 ast/fshiftmap.c create mode 100644 ast/fskyframe.c create mode 100644 ast/fslamap.c create mode 100644 ast/fsmerge.pdf create mode 100644 ast/fspecfluxframe.c create mode 100644 ast/fspecframe.c create mode 100644 ast/fspecmap.c create mode 100644 ast/fsphmap.c create mode 100644 ast/fsremap.pdf create mode 100644 ast/fstc.c create mode 100644 ast/fstccatalogentrylocation.c create mode 100644 ast/fstcobsdatalocation.c create mode 100644 ast/fstcresourceprofile.c create mode 100644 ast/fstcschan.c create mode 100644 ast/fstcsearchlocation.c create mode 100644 ast/fswitchmap.c create mode 100644 ast/ftable.c create mode 100644 ast/ftemplateclass.c create mode 100644 ast/ftimeframe.c create mode 100644 ast/ftimemap.c create mode 100644 ast/ftranmap.c create mode 100644 ast/funitmap.c create mode 100644 ast/funitnormmap.c create mode 100644 ast/fwcsmap.c create mode 100644 ast/fwinmap.c create mode 100644 ast/fxmlchan.c create mode 100644 ast/fzoommap.c create mode 100755 ast/getatt create mode 100644 ast/getnewversion create mode 100644 ast/globals.c create mode 100644 ast/globals.h create mode 100644 ast/grf.h create mode 100644 ast/grf3d.c create mode 100644 ast/grf3d.h create mode 100644 ast/grf3d_pgplot.c create mode 100644 ast/grf_2.0.c create mode 100644 ast/grf_3.2.c create mode 100644 ast/grf_5.6.c create mode 100644 ast/grf_null.c create mode 100644 ast/grf_pgplot.c create mode 100644 ast/gridplot.pdf create mode 100644 ast/gridplot_bw.pdf create mode 100644 ast/grismmap.c create mode 100644 ast/grismmap.h create mode 100644 ast/interval.c create mode 100644 ast/interval.h create mode 100644 ast/intramap.c create mode 100644 ast/intramap.h create mode 100644 ast/keymap.c create mode 100644 ast/keymap.h create mode 100644 ast/leap-seconds.png create mode 100644 ast/loader.c create mode 100644 ast/loader.h create mode 100644 ast/lutmap.c create mode 100644 ast/lutmap.h create mode 100644 ast/makeh create mode 100644 ast/mapping.c create mode 100644 ast/mapping.h create mode 100644 ast/mapping.pdf create mode 100644 ast/mathmap.c create mode 100644 ast/mathmap.h create mode 100644 ast/matrixmap.c create mode 100644 ast/matrixmap.h create mode 100644 ast/memory.c create mode 100644 ast/memory.h create mode 100644 ast/normmap.c create mode 100644 ast/normmap.h create mode 100644 ast/nullregion.c create mode 100644 ast/nullregion.h create mode 100644 ast/object.c create mode 100644 ast/object.f create mode 100644 ast/object.h.in create mode 100644 ast/overgrid.pdf create mode 100644 ast/overgrid_bw.pdf create mode 100644 ast/pal.h create mode 100644 ast/pal/pal.h create mode 100644 ast/pal/pal1sofa.h create mode 100644 ast/pal/palAddet.c create mode 100644 ast/pal/palAmpqk.c create mode 100644 ast/pal/palCaldj.c create mode 100644 ast/pal/palDat.c create mode 100644 ast/pal/palDe2h.c create mode 100644 ast/pal/palDeuler.c create mode 100644 ast/pal/palDh2e.c create mode 100644 ast/pal/palDjcal.c create mode 100644 ast/pal/palDmat.c create mode 100644 ast/pal/palDrange.c create mode 100644 ast/pal/palDs2tp.c create mode 100644 ast/pal/palDtp2s.c create mode 100644 ast/pal/palDtps2c.c create mode 100644 ast/pal/palDtt.c create mode 100644 ast/pal/palEcmat.c create mode 100644 ast/pal/palEqgal.c create mode 100644 ast/pal/palEtrms.c create mode 100644 ast/pal/palEvp.c create mode 100644 ast/pal/palFk45z.c create mode 100644 ast/pal/palFk524.c create mode 100644 ast/pal/palFk54z.c create mode 100644 ast/pal/palGaleq.c create mode 100644 ast/pal/palGalsup.c create mode 100644 ast/pal/palGeoc.c create mode 100644 ast/pal/palMappa.c create mode 100644 ast/pal/palMapqkz.c create mode 100644 ast/pal/palOne2One.c create mode 100644 ast/pal/palPrebn.c create mode 100644 ast/pal/palPrec.c create mode 100644 ast/pal/palPrenut.c create mode 100644 ast/pal/palPvobs.c create mode 100644 ast/pal/palRvgalc.c create mode 100644 ast/pal/palRvlg.c create mode 100644 ast/pal/palRvlsrd.c create mode 100644 ast/pal/palRvlsrk.c create mode 100644 ast/pal/palSubet.c create mode 100644 ast/pal/palSupgal.c create mode 100644 ast/pal/palmac.h create mode 100644 ast/pal2ast.h create mode 100644 ast/palwrap.c create mode 100644 ast/parallel.pdf create mode 100644 ast/pcdmap.c create mode 100644 ast/pcdmap.h create mode 100644 ast/permmap.c create mode 100644 ast/permmap.h create mode 100644 ast/pg3d.h create mode 100644 ast/plot.c create mode 100644 ast/plot.f create mode 100644 ast/plot.h create mode 100644 ast/plot3d.c create mode 100644 ast/plot3d.h create mode 100644 ast/pointlist.c create mode 100644 ast/pointlist.h create mode 100644 ast/pointset.c create mode 100644 ast/pointset.h create mode 100644 ast/polygon.c create mode 100644 ast/polygon.h create mode 100644 ast/polymap.c create mode 100644 ast/polymap.h create mode 100755 ast/prepare_all create mode 100755 ast/prepare_docs create mode 100755 ast/prepare_hyperdocs create mode 100755 ast/prepare_release create mode 100644 ast/prism.c create mode 100644 ast/prism.h create mode 100644 ast/proj.c create mode 100644 ast/proj.h create mode 100644 ast/ratemap.c create mode 100644 ast/ratemap.h create mode 100644 ast/region.c create mode 100644 ast/region.h create mode 100755 ast/selectfc create mode 100644 ast/selectormap.c create mode 100644 ast/selectormap.h create mode 100644 ast/series.pdf create mode 100644 ast/shiftmap.c create mode 100644 ast/shiftmap.h create mode 100644 ast/simpexamp.pdf create mode 100644 ast/skyaxis.c create mode 100644 ast/skyaxis.h create mode 100644 ast/skyframe.c create mode 100644 ast/skyframe.h create mode 100644 ast/slamap.c create mode 100644 ast/slamap.h create mode 100644 ast/specfluxframe.c create mode 100644 ast/specfluxframe.h create mode 100644 ast/specframe.c create mode 100644 ast/specframe.h create mode 100644 ast/specmap.c create mode 100644 ast/specmap.h create mode 100644 ast/sphmap.c create mode 100644 ast/sphmap.h create mode 100644 ast/stc.c create mode 100644 ast/stc.h create mode 100644 ast/stccatalogentrylocation.c create mode 100644 ast/stccatalogentrylocation.h create mode 100644 ast/stcobsdatalocation.c create mode 100644 ast/stcobsdatalocation.h create mode 100644 ast/stcresourceprofile.c create mode 100644 ast/stcresourceprofile.h create mode 100644 ast/stcs-ex1.txt create mode 100644 ast/stcschan-demo1.c create mode 100644 ast/stcschan-demo2.c create mode 100644 ast/stcschan-demo3.c create mode 100644 ast/stcschan-demo4.c create mode 100644 ast/stcschan-demo5.c create mode 100644 ast/stcschan.c create mode 100644 ast/stcschan.h create mode 100644 ast/stcsearchlocation.c create mode 100644 ast/stcsearchlocation.h create mode 100644 ast/sun210_figures/cmpframe.pdf create mode 100644 ast/sun210_figures/complex.pdf create mode 100644 ast/sun210_figures/frames.pdf create mode 100644 ast/sun210_figures/frameset.pdf create mode 100644 ast/sun210_figures/fronta.pdf create mode 100644 ast/sun210_figures/fronta_bw.pdf create mode 100644 ast/sun210_figures/frontb.pdf create mode 100644 ast/sun210_figures/frontb_bw.pdf create mode 100644 ast/sun210_figures/frontc.pdf create mode 100644 ast/sun210_figures/frontc_bw.pdf create mode 100644 ast/sun210_figures/fsalign.pdf create mode 100644 ast/sun210_figures/fsconvert.pdf create mode 100644 ast/sun210_figures/fsexample.pdf create mode 100644 ast/sun210_figures/fsmerge.pdf create mode 100644 ast/sun210_figures/fsremap.pdf create mode 100644 ast/sun210_figures/gridplot.pdf create mode 100644 ast/sun210_figures/gridplot_bw.pdf create mode 100644 ast/sun210_figures/mapping.pdf create mode 100644 ast/sun210_figures/overgrid.pdf create mode 100644 ast/sun210_figures/overgrid_bw.pdf create mode 100644 ast/sun210_figures/parallel.pdf create mode 100644 ast/sun210_figures/series.pdf create mode 100644 ast/sun210_figures/simpexamp.pdf create mode 100644 ast/sun211_figures/cmpframe.pdf create mode 100644 ast/sun211_figures/complex.pdf create mode 100644 ast/sun211_figures/frames.pdf create mode 100644 ast/sun211_figures/frameset.pdf create mode 100644 ast/sun211_figures/fronta.pdf create mode 100644 ast/sun211_figures/fronta_bw.pdf create mode 100644 ast/sun211_figures/frontb.pdf create mode 100644 ast/sun211_figures/frontb_bw.pdf create mode 100644 ast/sun211_figures/frontc.pdf create mode 100644 ast/sun211_figures/frontc_bw.pdf create mode 100644 ast/sun211_figures/fsalign.pdf create mode 100644 ast/sun211_figures/fsconvert.pdf create mode 100644 ast/sun211_figures/fsexample.pdf create mode 100644 ast/sun211_figures/fsmerge.pdf create mode 100644 ast/sun211_figures/fsremap.pdf create mode 100644 ast/sun211_figures/gridplot.pdf create mode 100644 ast/sun211_figures/gridplot_bw.pdf create mode 100644 ast/sun211_figures/mapping.pdf create mode 100644 ast/sun211_figures/overgrid.pdf create mode 100644 ast/sun211_figures/overgrid_bw.pdf create mode 100644 ast/sun211_figures/parallel.pdf create mode 100644 ast/sun211_figures/series.pdf create mode 100644 ast/sun211_figures/simpexamp.pdf create mode 100644 ast/sun_master.tex create mode 100644 ast/switchmap.c create mode 100644 ast/switchmap.h create mode 100644 ast/table.c create mode 100644 ast/table.h create mode 100644 ast/templateclass.README create mode 100644 ast/templateclass.c create mode 100644 ast/templateclass.h create mode 100644 ast/timeframe.c create mode 100644 ast/timeframe.h create mode 100644 ast/timemap.c create mode 100644 ast/timemap.h create mode 100644 ast/tpn.c create mode 100644 ast/tranmap.c create mode 100644 ast/tranmap.h create mode 100644 ast/unit.c create mode 100644 ast/unit.h create mode 100644 ast/unitmap.c create mode 100644 ast/unitmap.h create mode 100644 ast/unitnormmap.c create mode 100644 ast/unitnormmap.h create mode 100644 ast/version.h.in create mode 100644 ast/wcsmap.c create mode 100644 ast/wcsmap.h create mode 100644 ast/wcsmath.h create mode 100644 ast/wcstrig.c create mode 100644 ast/wcstrig.h create mode 100644 ast/winmap.c create mode 100644 ast/winmap.h create mode 100644 ast/xml.c create mode 100644 ast/xml.h create mode 100644 ast/xmlchan.c create mode 100644 ast/xmlchan.h create mode 100644 ast/zoommap.c create mode 100644 ast/zoommap.h diff --git a/ast/.gitignore b/ast/.gitignore new file mode 100644 index 0000000..742b687 --- /dev/null +++ b/ast/.gitignore @@ -0,0 +1,65 @@ +/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 +*.safe +*.lo +*.o +*.la +.deps/ +.libs/ +.bug +AST_ERR +Makefile +Makefile.in +aclocal.m4 +ast_err.h +ast_test.log +autom4te.cache/ +build-aux/ +cminpack/.deps/ +cminpack/.dirstamp +componentinfo.dtd +config.h +config.h.in +config.log +config.status +configure +experiments +fac_1521_err +libtool +stamp-h1 +starconf.status +sun210.htx/ +sun210.htx_tar +sun210.lof +sun210.log +sun210.out +sun210.toc +sun211.htx/ +sun211.htx_tar +sun211.lof +sun211.log +sun211.out +sun211.toc +test-suite.log +make.log +make.log.err +*~ +/ast_tester/*.ps +/ast_tester/fred.tmp +/ast_tester/fred.txt +/ast_tester/fred2.txt diff --git a/ast/AST_to_do b/ast/AST_to_do new file mode 100644 index 0000000..a1faf93 --- /dev/null +++ b/ast/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/ast/COPYING b/ast/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/ast/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/ast/COPYING.LESSER b/ast/COPYING.LESSER new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/ast/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/ast/COPYING.LIB b/ast/COPYING.LIB new file mode 100644 index 0000000..eb685a5 --- /dev/null +++ b/ast/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/ast/Ers.h b/ast/Ers.h new file mode 100644 index 0000000..a66fa82 --- /dev/null +++ b/ast/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/ast/GRF_PAR b/ast/GRF_PAR new file mode 100644 index 0000000..0c1d9aa --- /dev/null +++ b/ast/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/ast/Makefile.am b/ast/Makefile.am new file mode 100644 index 0000000..56096e7 --- /dev/null +++ b/ast/Makefile.am @@ -0,0 +1,828 @@ +## 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 \ + chebymap.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 \ + unitnormmap.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 \ + fchebymap.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 \ + funitnormmap.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 \ + chebymap.h \ + ratemap.h \ + normmap.h \ + shiftmap.h \ + slamap.h \ + specmap.h \ + sphmap.h \ + timemap.h \ + selectormap.h \ + switchmap.h \ + tranmap.h \ + unitmap.h \ + unitnormmap.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 + +# If we have no Fortran we are not building f77.h and we probably +# do not want a Fortran runtime. This requires that PGPLOT is disabled. +# We replace it with the stub GRF interface. An alternative would +# be to have the PGPLOT wrappers use a preprocessor symbol to build +# the pgplot to always error if used. +if !NOFORTRAN +GRF_PGPLOT_SOURCES = \ + grf_pgplot.c +GRF3D_PGPLOT_SOURCES = \ + grf3d_pgplot.c +else +GRF_PGPLOT_SOURCES = \ + grf_5.6.c +GRF3D_PGPLOT_SOURCES = \ + grf3d.c +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_SOURCES) + +# The graphics module that uses PGPLOT for 3D graphical output. +libast_pgplot3d_la_SOURCES = $(GRF3D_PGPLOT_SOURCES) + +# 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 + +test: install + cd ast_tester && STARLINK=@STARLINK@ PGPLOT_DIR=@STARLINK@/bin ./ast_tester -nd + +#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/ast/Notes b/ast/Notes new file mode 100644 index 0000000..bb4ac0e --- /dev/null +++ b/ast/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/ast/STARLINK_BRANCHES b/ast/STARLINK_BRANCHES new file mode 100644 index 0000000..975ab87 --- /dev/null +++ b/ast/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/ast/addcopyright b/ast/addcopyright new file mode 100755 index 0000000..4772023 --- /dev/null +++ b/ast/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/ast/addlinks b/ast/addlinks new file mode 100755 index 0000000..997dad6 --- /dev/null +++ b/ast/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/ast/addversion.in b/ast/addversion.in new file mode 100644 index 0000000..955a971 --- /dev/null +++ b/ast/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/ast-for-wcslib/astTester.c b/ast/ast-for-wcslib/astTester.c new file mode 100644 index 0000000..0cd6a92 --- /dev/null +++ b/ast/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/ast-for-wcslib/loader.c b/ast/ast-for-wcslib/loader.c new file mode 100644 index 0000000..d71c6fb --- /dev/null +++ b/ast/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/ast-for-wcslib/matrixmap.h b/ast/ast-for-wcslib/matrixmap.h new file mode 100644 index 0000000..d999180 --- /dev/null +++ b/ast/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/ast-for-wcslib/plot.h b/ast/ast-for-wcslib/plot.h new file mode 100644 index 0000000..1678ec9 --- /dev/null +++ b/ast/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/ast-for-wcslib/wcslib-instructions b/ast/ast-for-wcslib/wcslib-instructions new file mode 100644 index 0000000..82c4369 --- /dev/null +++ b/ast/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/ast.news b/ast/ast.news new file mode 100644 index 0000000..cbefd45 --- /dev/null +++ b/ast/ast.news @@ -0,0 +1,1189 @@ +AST Library +----------- + A new release (V8.6.2) 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 astWrite method of the FitsChan class can now create FITS-WCS headers +that include keyords describing focal plane distortion using the +conventions of the Spitzer SIP scheme. This is however only possible if +the SipOK attribute of the FitsChan is set to a non-zero value (which is +the default), and the FrameSet being written out contains an appropriate +PolyMap that conforms to the requirements of the SIP convention. + +- The behaviour of the astLinearApprox method of the Mapping class has +been changed in cases where the Mapping being approximated generates bad +(AST__BAD) values for one or more of its outputs. Previously, any such +Mapping would be deemed non-linear and no fit would be returned. Now, a +fit is returned, provided the other outputs of the Mapping are linear, +but the fit contains AST__BAD values for the coefficients describing the +bad Mapping output. + + +Main Changes in V8.6.1 +---------------------- + +- A new function call astCreatedAt is now available that returns the function +name, file path and line number at which an AST object was first created. + +- The number of digits used to format floating point values has been +increased in order to avoid loss of precision when converting from binary +to string and back to binary. This could cause very small changes in numerical +values returned by AST functions. + +- If a FrameSet is supplied as the "Map" argument to astAddFrame, it now +extracts and stores the base->current Mapping from the supplied FrameSet. +Previously, the entire FrameSet was stored as the Mapping. + + +Main Changes in V8.5.1 +---------------------- + +- A new class of Mapping called ChebyMap has been added. This is a +Mapping that implements Chebyshev polynomial transformations. + +- If the function that delivers error messages to the user (astPutErr) is +re-implemented, the new version can now be registered at run-time using +the new astSetPutErr function. Previously, the new version needed to be +linked into the application at build time. + +- A bug has been fixed in the PolyMap class that caused incorrect values +to be returned for the TranForward and TranInverse attributes if the PolyMap +has been inverted. + +- The KeyMap class has a new method called astMapGetC (AST_MAPGETC) which +returns a named entry as a single string. If the entry is a vector the +returned string is a comma-separated list of its elements, enclosed in +parentheses. + +- The Frame class now has a new attribute caled DTAI, which can be used +to specify the number of leap seconds at the moment represented by the +Frame's Epoch attribute. By default, the internal look-up table of leap +seconds contained within AST is used. The DTAI attribute allows old +versions of AST, which may not include the most recent leap seconds, to +be used with new data. + +- The TimeMap class has been changed so that some conversions now require +a "Dtai" value (i.e. the number of leap seconds) to be supplied by the +caller. If AST__BAD is supplied for "Dtai", the internal look-up table of +leap seconds contained withn AST will be used. The conversions affected +are those between TAI and UTC, and those between TT and TDB. + +Main Changes in V8.3.0 +---------------------- + +- The PAL library files included in the AST distribution have been updated +to PAL version 0.9.7. + +- Multiple identical NormMaps in series will now be simplified to a +single NormMap. + +- A NormMap that encapsulates a basic Frame will now be simplified to a +UnitMap. + +- The astTimeAdd (AST_TIMEADD) method of the TimeMap class now include an +extra argument that gives the number of values supplied in the arguments +array. Note, any existing code that uses this method will need to be +changed. + +- The astSlaAdd (AST_SLAADD) method of the SlaMap class now include an +extra argument that gives the number of values supplied in the arguments +array. Note, any existing code that uses this method will need to be +changed. + +- The astSpecAdd (AST_SPECADD) method of the SpecMap class now include an +extra argument that gives the number of values supplied in the arguments +array. Note, any existing code that uses this method will need to be +changed. + +- If the astMapRegion (AST_MAPREGION) method is used to map a Region into +a new Frame that has fewer axes than the original Region, and if the +inverse transformation of the supplied Mapping does not specify a value +for the missing axes, then those axes are removed entirely from the +Region. Previously they were retained, but supplied with bad values. This +affects the number of mesh points per axes for such Regions, and so +affects the accuracy of overlap determination. + + +Main Changes in V8.3.0 +---------------------- + +- A new method called astAxNorm has been added to the Frame class that +normalises an array of axis values. When used with SkyFrames, it allows +longitude values to be normalised into the shortest range. + +- A bug has been fixed in the Fortran include file AST_PAR that caused constants +related to PI to be defined as single rather than double precision. + +- A bug has been fixed in the astGetRegionBounds method that could +cause the wrong bounds to be returned for regions spanning a longitude = +zero singularity. + +Main Changes in V8.2.0 +---------------------- + +- A new class of Mapping called UnitNormMap has been added that converts a +vector to a unit vector relative to a specified centre, plus length. A +UnitNormMap has N inputs and N+1 outputs.The lower N output coordinates +represent a unit vector parallel to the supplied input vector, and the +(N+1)'th output coordinate is the length of the input vector. + +- 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/ast_cpp.in b/ast/ast_cpp.in new file mode 100644 index 0000000..c4fb206 --- /dev/null +++ b/ast/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/ast_dev b/ast/ast_dev new file mode 100644 index 0000000..a073e9a --- /dev/null +++ b/ast/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/ast_err.msg b/ast/ast_err.msg new file mode 100644 index 0000000..6e42091 --- /dev/null +++ b/ast/ast_err.msg @@ -0,0 +1,214 @@ +.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 + +! New codes introduced for V8.5.0 +NOBOX + +.END diff --git a/ast/ast_link.in b/ast/ast_link.in new file mode 100644 index 0000000..c5759e2 --- /dev/null +++ b/ast/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/ast_link_adam.in b/ast/ast_link_adam.in new file mode 100644 index 0000000..df93c6c --- /dev/null +++ b/ast/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/ast_par.source b/ast/ast_par.source new file mode 100644 index 0000000..fda7eac --- /dev/null +++ b/ast/ast_par.source @@ -0,0 +1,733 @@ +*+ +* 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. +* 26-OCT-2016 (DSB): +* Make angle constants double precision. +*- + +* 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.1415926535897932384626433832795028842D0 ) + + DOUBLE PRECISION AST__DPIBY2 + PARAMETER ( AST__DPIBY2 = 1.5707963267948966192313216916397514D0 ) + + DOUBLE PRECISION AST__DD2R + PARAMETER ( AST__DD2R = 0.017453292519943295769236907684886127D0 ) + + DOUBLE PRECISION AST__DR2D + PARAMETER ( AST__DR2D = 57.29577951308232087679815481410517033D0 ) + + 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_MAPGETC + 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 + +* UnitNormMap class. + INTEGER AST_UNITNORMMAP + LOGICAL AST_ISAUNITNORMMAP + +* ChebyMap class. + INTEGER AST_CHEBYMAP + LOGICAL AST_ISACHEBYMAP + INTEGER AST_CHEBYTRAN + diff --git a/ast/ast_test.c b/ast/ast_test.c new file mode 100644 index 0000000..61e948e --- /dev/null +++ b/ast/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/ast_tester/README b/ast/ast_tester/README new file mode 100644 index 0000000..8c50db6 --- /dev/null +++ b/ast/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/ast_tester/a20070718_00010_02_cube.ast b/ast/ast_tester/a20070718_00010_02_cube.ast new file mode 100644 index 0000000..f005938 --- /dev/null +++ b/ast/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/ast_tester/a20070718_00010_02_cube.fits-wcs b/ast/ast_tester/a20070718_00010_02_cube.fits-wcs new file mode 100644 index 0000000..428efaa --- /dev/null +++ b/ast/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/ast_tester/aitoff.attr b/ast/ast_tester/aitoff.attr new file mode 100644 index 0000000..85aa9de --- /dev/null +++ b/ast/ast_tester/aitoff.attr @@ -0,0 +1 @@ +Grid=1,tickall=0,border=1,tol=0.001 diff --git a/ast/ast_tester/aitoff.box b/ast/ast_tester/aitoff.box new file mode 100644 index 0000000..b4f303e --- /dev/null +++ b/ast/ast_tester/aitoff.box @@ -0,0 +1 @@ +10.0 -10.0 300.0 280.0 diff --git a/ast/ast_tester/aitoff.head b/ast/ast_tester/aitoff.head new file mode 100644 index 0000000..5f34de0 --- /dev/null +++ b/ast/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/ast_tester/ast_tester b/ast/ast_tester/ast_tester new file mode 100755 index 0000000..e626157 --- /dev/null +++ b/ast/ast_tester/ast_tester @@ -0,0 +1,232 @@ +#!/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_DIR - 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_DIR is used +# if not set. + +# Author: +# DSB: David Berry (JAC, Hawaii) +#- + +if( ! $?STARLINK_DIR ) then + setenv STARLINK_DIR /star +endif + +if( ! $?AST ) then + setenv AST $STARLINK_DIR +endif + +if( ! $?LDFLAGS ) then + setenv LDFLAGS "" +endif + +setenv PATH $AST/bin\:$STARLINK_DIR/bin\:$PATH + + +# Build the progs +#gfortran -fno-second-underscore -o regression regression.f -fno-range-check $LDFLAGS -I$AST/include -I$STARLINK_DIR/include -L$AST/lib -L$STARLINK_DIR/lib `ast_link -ems` `chr_link` +gfortran -fno-second-underscore -o plotter plotter.f -fno-range-check $LDFLAGS -I$AST/include -I$STARLINK_DIR/include -L$AST/lib -L$STARLINK_DIR/lib `ast_link -pgp -ems` `pgplot_link` +gfortran -fno-second-underscore -o wcsconverter wcsconverter.f -fno-range-check $LDFLAGS -I$AST/include -I$STARLINK_DIR/include -L$AST/lib -L$STARLINK_DIR/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_DIR/include -L$AST/lib -L$STARLINK_DIR/lib `ast_link -ems` `chr_link` `err_link` + +# Run the other test progs +echo "" + + +foreach prog (testmapping testchebymap testunitnormmap 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_DIR/include -L$AST/lib -L$STARLINK_DIR/lib `ast_link -ems` \ + `psx_link` `prm_link` `chr_link` `err_link` + +./$prog +\rm $prog + +end + + + +foreach prog (testobject testconvert testerror) + +gcc -o $prog $prog.c -I.. -DHAVE_CONFIG_H $LDFLAGS -L$STARLINK_DIR/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_DIR/bin/psmerge ) then + $STARLINK_DIR/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" \ + "degen1 ast fits-wcs cdmatrix=1" \ + "sip head fits-wcs cdmatrix=1,sipreplace=0" \ + "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/ast_tester/brad.map b/ast/ast_tester/brad.map new file mode 100644 index 0000000..d403fce --- /dev/null +++ b/ast/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/ast_tester/brad.simp b/ast/ast_tester/brad.simp new file mode 100644 index 0000000..ffa6e18 --- /dev/null +++ b/ast/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.2359877559829903e-06 # Scale factor for axis 2 + Scl3 = 5.2359877559829903e-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.5043200254440301 + L3 = 1.61332011222839 + L4 = 1.7223200798034699 + L5 = 1.83132004737854 + L6 = 1.94032001495361 + L7 = 2.0493202209472701 + L8 = 2.1583199501037602 + L9 = 2.2673201560974099 + L10 = 2.3763201236724898 + L11 = 2.4853200912475599 + 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/ast_tester/car1.attr b/ast/ast_tester/car1.attr new file mode 100644 index 0000000..f74084f --- /dev/null +++ b/ast/ast_tester/car1.attr @@ -0,0 +1 @@ +numlabgap=0.05 diff --git a/ast/ast_tester/car1.box b/ast/ast_tester/car1.box new file mode 100644 index 0000000..09670e6 --- /dev/null +++ b/ast/ast_tester/car1.box @@ -0,0 +1 @@ +0 0 2962 562 diff --git a/ast/ast_tester/car1.fattr b/ast/ast_tester/car1.fattr new file mode 100644 index 0000000..5f408d2 --- /dev/null +++ b/ast/ast_tester/car1.fattr @@ -0,0 +1 @@ +carlin=1 diff --git a/ast/ast_tester/car1.head b/ast/ast_tester/car1.head new file mode 100644 index 0000000..bec8d59 --- /dev/null +++ b/ast/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/ast_tester/car2.attr b/ast/ast_tester/car2.attr new file mode 100644 index 0000000..483f747 --- /dev/null +++ b/ast/ast_tester/car2.attr @@ -0,0 +1 @@ +numlabgap(2)=0.05,labelling=interior diff --git a/ast/ast_tester/car2.box b/ast/ast_tester/car2.box new file mode 100644 index 0000000..09670e6 --- /dev/null +++ b/ast/ast_tester/car2.box @@ -0,0 +1 @@ +0 0 2962 562 diff --git a/ast/ast_tester/car2.fattr b/ast/ast_tester/car2.fattr new file mode 100644 index 0000000..5f408d2 --- /dev/null +++ b/ast/ast_tester/car2.fattr @@ -0,0 +1 @@ +carlin=1 diff --git a/ast/ast_tester/car2.head b/ast/ast_tester/car2.head new file mode 100644 index 0000000..bec8d59 --- /dev/null +++ b/ast/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/ast_tester/car3.attr b/ast/ast_tester/car3.attr new file mode 100644 index 0000000..b80a7c0 --- /dev/null +++ b/ast/ast_tester/car3.attr @@ -0,0 +1 @@ +grid=1 diff --git a/ast/ast_tester/car3.box b/ast/ast_tester/car3.box new file mode 100644 index 0000000..54240c7 --- /dev/null +++ b/ast/ast_tester/car3.box @@ -0,0 +1 @@ +0 0 400 400 diff --git a/ast/ast_tester/car3.head b/ast/ast_tester/car3.head new file mode 100644 index 0000000..de6c76e --- /dev/null +++ b/ast/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/ast_tester/car4.attr b/ast/ast_tester/car4.attr new file mode 100644 index 0000000..e69de29 diff --git a/ast/ast_tester/car4.box b/ast/ast_tester/car4.box new file mode 100644 index 0000000..0393b0d --- /dev/null +++ b/ast/ast_tester/car4.box @@ -0,0 +1 @@ +0 0 951 1851 diff --git a/ast/ast_tester/car4.fattr b/ast/ast_tester/car4.fattr new file mode 100644 index 0000000..5f408d2 --- /dev/null +++ b/ast/ast_tester/car4.fattr @@ -0,0 +1 @@ +carlin=1 diff --git a/ast/ast_tester/car4.head b/ast/ast_tester/car4.head new file mode 100644 index 0000000..95cd97a --- /dev/null +++ b/ast/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/ast_tester/car5.attr b/ast/ast_tester/car5.attr new file mode 100644 index 0000000..e69de29 diff --git a/ast/ast_tester/car5.box b/ast/ast_tester/car5.box new file mode 100644 index 0000000..0393b0d --- /dev/null +++ b/ast/ast_tester/car5.box @@ -0,0 +1 @@ +0 0 951 1851 diff --git a/ast/ast_tester/car5.head b/ast/ast_tester/car5.head new file mode 100644 index 0000000..2be3a64 --- /dev/null +++ b/ast/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/ast_tester/car6.attr b/ast/ast_tester/car6.attr new file mode 100644 index 0000000..e69de29 diff --git a/ast/ast_tester/car6.box b/ast/ast_tester/car6.box new file mode 100644 index 0000000..e5f0493 --- /dev/null +++ b/ast/ast_tester/car6.box @@ -0,0 +1 @@ +0 0 5401 1741 diff --git a/ast/ast_tester/car6.head b/ast/ast_tester/car6.head new file mode 100644 index 0000000..5356fa5 --- /dev/null +++ b/ast/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/ast_tester/cobe.attr b/ast/ast_tester/cobe.attr new file mode 100644 index 0000000..c1f61e5 --- /dev/null +++ b/ast/ast_tester/cobe.attr @@ -0,0 +1 @@ +format(1)=gd,format(2)=gd,Grid=1,tickall=0,width(axes)=3 diff --git a/ast/ast_tester/cobe.box b/ast/ast_tester/cobe.box new file mode 100644 index 0000000..b4f303e --- /dev/null +++ b/ast/ast_tester/cobe.box @@ -0,0 +1 @@ +10.0 -10.0 300.0 280.0 diff --git a/ast/ast_tester/cobe.head b/ast/ast_tester/cobe.head new file mode 100644 index 0000000..fb6da34 --- /dev/null +++ b/ast/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/ast_tester/degen1.ast b/ast/ast_tester/degen1.ast new file mode 100644 index 0000000..202f149 --- /dev/null +++ b/ast/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/ast_tester/degen1.fits-wcs b/ast/ast_tester/degen1.fits-wcs new file mode 100644 index 0000000..6e5309d --- /dev/null +++ b/ast/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/ast_tester/doplot b/ast/ast_tester/doplot new file mode 100755 index 0000000..f7df1ce --- /dev/null +++ b/ast/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/ast_tester/dss.ast b/ast/ast_tester/dss.ast new file mode 100644 index 0000000..6c778c4 --- /dev/null +++ b/ast/ast_tester/dss.ast @@ -0,0 +1,166 @@ + 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.5707963267948966 # Lowest legal axis value +# Top2 = 1.5707963267948966 # 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.5707963267948966 # Lowest legal axis value +# Top2 = 1.5707963267948966 # 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.8272374655684112 # Ref. pos. RA 10:47:57.3 + SRef2 = -1.0518122540502668 # 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 = -0.3287946560728543 # Shift for axis 1 + Scl1 = -0.00044129690205585437 # Scale factor for axis 1 + Sft2 = 0.38797155568647818 # Shift for axis 2 + Scl2 = 0.00044129690205585437 # Scale factor 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 + 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.00037777813768480556 # Projection parameter 0 for axis 1 + PV1_1 = 0.018675372165510556 # Projection parameter 1 for axis 1 + PV1_2 = 1.4659181119170556e-05 # Projection parameter 2 for axis 1 + PV1_4 = -5.6541834490241662e-09 # Projection parameter 4 for axis 1 + PV1_5 = -1.6598619578175834e-10 # Projection parameter 5 for axis 1 + PV1_6 = 3.324645548432778e-09 # Projection parameter 6 for axis 1 + PV1_7 = 6.8029375162963896e-10 # Projection parameter 7 for axis 1 + PV1_8 = -1.0315391309210556e-11 # Projection parameter 8 for axis 1 + PV1_9 = 6.5770184096316667e-10 # Projection parameter 9 for axis 1 + PV1_10 = 4.6843790588691666e-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.00020734395690532499 # Projection parameter 0 for axis 2 + PV2_1 = 0.018675089806542779 # Projection parameter 1 for axis 2 + PV2_2 = -1.6578391725152224e-05 # Projection parameter 2 for axis 2 + PV2_4 = -5.1378767937980552e-09 # Projection parameter 4 for axis 2 + PV2_5 = -1.7623932712259446e-09 # Projection parameter 5 for axis 2 + PV2_6 = 2.7161547313251387e-10 # Projection parameter 6 for axis 2 + PV2_7 = 7.088907407099166e-10 # Projection parameter 7 for axis 2 + PV2_8 = 1.8432618513145277e-11 # Projection parameter 8 for axis 2 + PV2_9 = 6.8491061989569442e-10 # Projection parameter 9 for axis 2 + PV2_10 = 7.3325859634708332e-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.82577216035104439 # Forward matrix value + M1 = -0.30920332196760869 # Forward matrix value + M2 = -0.47169232013396639 # Forward matrix value + M3 = -0.26848851872737706 # Forward matrix value + M4 = -0.95099595460979502 # Forward matrix value + M5 = 0.153364303628268 # Forward matrix value + M6 = -0.49599824042101986 # Forward matrix value + M7 = 0 # Forward matrix value + M8 = -0.86832352582390171 # 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.8272374655684112 # Polar longitude (rad.s) + End SphMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End FrameSet diff --git a/ast/ast_tester/dss.dss b/ast/ast_tester/dss.dss new file mode 100644 index 0000000..c57a035 --- /dev/null +++ b/ast/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/ast_tester/dss.fits-dss b/ast/ast_tester/dss.fits-dss new file mode 100644 index 0000000..c728dc4 --- /dev/null +++ b/ast/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/ast_tester/dss.fits-wcs b/ast/ast_tester/dss.fits-wcs new file mode 100644 index 0000000..e9896db --- /dev/null +++ b/ast/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/ast_tester/hpx.attr b/ast/ast_tester/hpx.attr new file mode 100644 index 0000000..496403c --- /dev/null +++ b/ast/ast_tester/hpx.attr @@ -0,0 +1 @@ +border=1 diff --git a/ast/ast_tester/hpx.box b/ast/ast_tester/hpx.box new file mode 100644 index 0000000..8b52762 --- /dev/null +++ b/ast/ast_tester/hpx.box @@ -0,0 +1 @@ +0.5 0.5 300.5 200.5 diff --git a/ast/ast_tester/hpx.head b/ast/ast_tester/hpx.head new file mode 100644 index 0000000..9e82b8e --- /dev/null +++ b/ast/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/ast_tester/joye_car_headers/CAR_model.head b/ast/ast_tester/joye_car_headers/CAR_model.head new file mode 100644 index 0000000..82a7d4e --- /dev/null +++ b/ast/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/ast_tester/joye_car_headers/CHIPASS_Equ.head b/ast/ast_tester/joye_car_headers/CHIPASS_Equ.head new file mode 100644 index 0000000..5356fa5 --- /dev/null +++ b/ast/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/ast_tester/joye_car_headers/car1.fattr b/ast/ast_tester/joye_car_headers/car1.fattr new file mode 100644 index 0000000..5f408d2 --- /dev/null +++ b/ast/ast_tester/joye_car_headers/car1.fattr @@ -0,0 +1 @@ +carlin=1 diff --git a/ast/ast_tester/joye_car_headers/car1.head b/ast/ast_tester/joye_car_headers/car1.head new file mode 100644 index 0000000..bec8d59 --- /dev/null +++ b/ast/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/ast_tester/joye_car_headers/car2.fattr b/ast/ast_tester/joye_car_headers/car2.fattr new file mode 100644 index 0000000..5f408d2 --- /dev/null +++ b/ast/ast_tester/joye_car_headers/car2.fattr @@ -0,0 +1 @@ +carlin=1 diff --git a/ast/ast_tester/joye_car_headers/car2.head b/ast/ast_tester/joye_car_headers/car2.head new file mode 100644 index 0000000..bec8d59 --- /dev/null +++ b/ast/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/ast_tester/joye_car_headers/car3.head b/ast/ast_tester/joye_car_headers/car3.head new file mode 100644 index 0000000..de6c76e --- /dev/null +++ b/ast/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/ast_tester/joye_car_headers/car4.fattr b/ast/ast_tester/joye_car_headers/car4.fattr new file mode 100644 index 0000000..5f408d2 --- /dev/null +++ b/ast/ast_tester/joye_car_headers/car4.fattr @@ -0,0 +1 @@ +carlin=1 diff --git a/ast/ast_tester/joye_car_headers/car4.head b/ast/ast_tester/joye_car_headers/car4.head new file mode 100644 index 0000000..95cd97a --- /dev/null +++ b/ast/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/ast_tester/joye_car_headers/car5.head b/ast/ast_tester/joye_car_headers/car5.head new file mode 100644 index 0000000..2be3a64 --- /dev/null +++ b/ast/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/ast_tester/joye_car_headers/cmap_3years_GP_D2.head b/ast/ast_tester/joye_car_headers/cmap_3years_GP_D2.head new file mode 100644 index 0000000..d27e368 --- /dev/null +++ b/ast/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/ast_tester/joye_car_headers/doit b/ast/ast_tester/joye_car_headers/doit new file mode 100755 index 0000000..a78a590 --- /dev/null +++ b/ast/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/ast_tester/joye_car_headers/total_hi.head b/ast/ast_tester/joye_car_headers/total_hi.head new file mode 100644 index 0000000..2ed691c --- /dev/null +++ b/ast/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/ast_tester/longslit.fits-pc b/ast/ast_tester/longslit.fits-pc new file mode 100644 index 0000000..2adf05a --- /dev/null +++ b/ast/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/ast_tester/longslit.fits-wcs b/ast/ast_tester/longslit.fits-wcs new file mode 100644 index 0000000..cff2ca0 --- /dev/null +++ b/ast/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/ast_tester/makeplot b/ast/ast_tester/makeplot new file mode 100755 index 0000000..760fac9 --- /dev/null +++ b/ast/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/ast_tester/maketest b/ast/ast_tester/maketest new file mode 100755 index 0000000..5891121 --- /dev/null +++ b/ast/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/ast_tester/origin.attr b/ast/ast_tester/origin.attr new file mode 100644 index 0000000..277e359 --- /dev/null +++ b/ast/ast_tester/origin.attr @@ -0,0 +1 @@ +format(1)=ghms,format(2)=gdm,edge(2)=r,tickall=0,grid=1 diff --git a/ast/ast_tester/origin.box b/ast/ast_tester/origin.box new file mode 100644 index 0000000..9b5eb80 --- /dev/null +++ b/ast/ast_tester/origin.box @@ -0,0 +1 @@ +0 0 500 500 diff --git a/ast/ast_tester/origin.head b/ast/ast_tester/origin.head new file mode 100644 index 0000000..88ea783 --- /dev/null +++ b/ast/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/ast_tester/plot3d-test1.ast b/ast/ast_tester/plot3d-test1.ast new file mode 100644 index 0000000..271faf1 --- /dev/null +++ b/ast/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/ast_tester/plotter.f b/ast/ast_tester/plotter.f new file mode 100644 index 0000000..79bb8e1 --- /dev/null +++ b/ast/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/ast_tester/polco.attr b/ast/ast_tester/polco.attr new file mode 100644 index 0000000..c220287 --- /dev/null +++ b/ast/ast_tester/polco.attr @@ -0,0 +1 @@ +Grid=1,labelling=interior,bottom(1)=0 diff --git a/ast/ast_tester/polco.box b/ast/ast_tester/polco.box new file mode 100644 index 0000000..0661792 --- /dev/null +++ b/ast/ast_tester/polco.box @@ -0,0 +1 @@ +-300 -300 500 500 diff --git a/ast/ast_tester/polco.head b/ast/ast_tester/polco.head new file mode 100644 index 0000000..d953089 --- /dev/null +++ b/ast/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/ast_tester/polco2.attr b/ast/ast_tester/polco2.attr new file mode 100644 index 0000000..5019421 --- /dev/null +++ b/ast/ast_tester/polco2.attr @@ -0,0 +1 @@ +Grid=0,labelling=exterior,gap(1)=50 diff --git a/ast/ast_tester/polco2.box b/ast/ast_tester/polco2.box new file mode 100644 index 0000000..0661792 --- /dev/null +++ b/ast/ast_tester/polco2.box @@ -0,0 +1 @@ +-300 -300 500 500 diff --git a/ast/ast_tester/polco2.head b/ast/ast_tester/polco2.head new file mode 100644 index 0000000..d953089 --- /dev/null +++ b/ast/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/ast_tester/regression.current b/ast/ast_tester/regression.current new file mode 100644 index 0000000..788c2bc --- /dev/null +++ b/ast/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/ast_tester/regression.f b/ast/ast_tester/regression.f new file mode 100644 index 0000000..e9ca32e --- /dev/null +++ b/ast/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/ast_tester/regression.out b/ast/ast_tester/regression.out new file mode 100644 index 0000000..7fcaa9c --- /dev/null +++ b/ast/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/ast_tester/rigby.map b/ast/ast_tester/rigby.map new file mode 100644 index 0000000..6482149 --- /dev/null +++ b/ast/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/ast_tester/rigby.simp b/ast/ast_tester/rigby.simp new file mode 100644 index 0000000..fa78112 --- /dev/null +++ b/ast/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.1296825791877598 # Forward matrix value + M1 = 0.58230800408075301 # Forward matrix value + M2 = 0 # Forward matrix value + M3 = -5.8230800408075298 # Forward matrix value + M4 = 0.81296825791877603 # 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.1919848386815 # Shift for axis 1 + Scl1 = 0.99999999997788802 # Scale factor for axis 1 + Sft2 = 2408.2662394170707 # Shift for axis 2 + Scl2 = 0.99999999998530598 # Scale factor for axis 2 + Sft3 = 330416457339.85065 # Shift for axis 3 + Scl3 = 551361.36816875194 # 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.2823037047206903e-05 # Forward matrix value + M1 = -1.6868750268151298e-05 # Forward matrix value + M2 = -1.6868750268151298e-05 # Forward matrix value + M3 = 3.2823037047206903e-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.016039835492688701 # Forward matrix value + M1 = 0.96719204920834101 # Forward matrix value + M2 = 0.25353947153360201 # Forward matrix value + M3 = -0.061066031142155398 # Forward matrix value + M4 = 0.25404633425454198 # Forward matrix value + M5 = -0.96526234770261998 # Forward matrix value + M6 = -0.99800484143009704 # Forward matrix value + M7 = 0 # Forward matrix value + M8 = 0.063137441206365094 # 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.00039155961436864302 # Constant number 1 + End PermMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap diff --git a/ast/ast_tester/scp.attr b/ast/ast_tester/scp.attr new file mode 100644 index 0000000..54434e8 --- /dev/null +++ b/ast/ast_tester/scp.attr @@ -0,0 +1 @@ +Grid=1,labelling=int diff --git a/ast/ast_tester/scp.box b/ast/ast_tester/scp.box new file mode 100644 index 0000000..76f5ba8 --- /dev/null +++ b/ast/ast_tester/scp.box @@ -0,0 +1 @@ +1.0 1.0 1500.0 1300.0 diff --git a/ast/ast_tester/scp.head b/ast/ast_tester/scp.head new file mode 100644 index 0000000..f35c6f4 --- /dev/null +++ b/ast/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/ast_tester/serpens.attr b/ast/ast_tester/serpens.attr new file mode 100644 index 0000000..496403c --- /dev/null +++ b/ast/ast_tester/serpens.attr @@ -0,0 +1 @@ +border=1 diff --git a/ast/ast_tester/serpens.box b/ast/ast_tester/serpens.box new file mode 100644 index 0000000..f9576e8 --- /dev/null +++ b/ast/ast_tester/serpens.box @@ -0,0 +1 @@ +0.5 0.5 150.5 120.5 diff --git a/ast/ast_tester/serpens.head b/ast/ast_tester/serpens.head new file mode 100644 index 0000000..0775604 --- /dev/null +++ b/ast/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/ast_tester/simplify.f b/ast/ast_tester/simplify.f new file mode 100644 index 0000000..063ebd1 --- /dev/null +++ b/ast/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/ast_tester/sip.fits-wcs b/ast/ast_tester/sip.fits-wcs new file mode 100644 index 0000000..92d29ed --- /dev/null +++ b/ast/ast_tester/sip.fits-wcs @@ -0,0 +1,289 @@ +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.136 / [days] MJD at DCE start (,JD-2400000.5) +UTCS_OBS= 2.3635893E+8 / [sec] J2000 ephem. time at DCE start +SCLK_OBS= 8.675543E+8 / [sec] SCLK time (since 1/1/1980) at DCE start +SAMPTIME= 0.2 / [sec] Sample integration time +FRAMTIME= 12.0 / [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.0 / [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.48232 / [deg] RA at CRPIX1,CRPIX2 (using Pointing Recon +CRVAL2 = 47.175119 / [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 +CD1_1 = 0.00024975688 / Corrected CD matrix element with Pointing Recon +CD1_2 = 0.00023017781 / Corrected CD matrix element with Pointing Recon +CD2_1 = 0.00023042852 / Corrected CD matrix element with Pointing Recon +CD2_2 = -2.4996577E-4 / 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.0 / Reference pixel along axis 1 +CRPIX2 = 128.0 / Reference pixel along axis 2 +MJD-OBS = 51544.499 / Modified Julian Date of observation +DATE-OBS= '2000-01-01T11:58:55.816' / Date of observation +PXSCAL1 = -1.2233412 / [arcsec/pix] Scale for axis 1 at CRPIX1,CRPIX2 +PXSCAL2 = 1.2232836 / [arcsec/pix] Scale for axis 2 at CRPIX1,CRPIX2 +CRDER1 = 4.0250976E-5 / [deg] Uncertainty in CRVAL1 +CRDER2 = 3.4274613E-5 / [deg] Uncertainty in CRVAL2 +UNCRTPA = 0.00037878784 / [deg] Uncertainty in position angle +CSDRADEC= 4.7597178E-7 / [deg] Costandard deviation in RA and Dec +SIGRA = 0.033948714 / [arcsec] RMS dispersion of RA over DCE +SIGDEC = 0.063768266 / [arcsec] RMS dispersion of DEC over DCE +SIGPA = 1.3646427 / [arcsec] RMS dispersion of PA over DCE +PA = 137.35998 / [deg] Position angle of axis 2 (E of N) (was OR +RA_RQST = 202.48212 / [deg] Requested RA at CRPIX1, CRPIX2 +DEC_RQST= 47.175079 / [deg] Requested Dec at CRPIX1, CRPIX2 +PM_RA = 0.0 / [arcsec/yr] Proper Motion in RA (J2000) +PM_DEC = 0.0 / [arcsec/yr] Proper Motion in Dec (J200) +RMS_JIT = 0.007683034 / [arcsec] RMS jitter during DCE +RMS_JITY= 0.0051507034 / [arcsec] RMS jitter during DCE along Y +RMS_JITZ= 0.0057008128 / [arcsec] RMS jitter during DCE along Z +SIG_JTYZ= 0.0023780641 / [arcsec] Costadard deviation of jitter in YZ +PTGDIFF = 0.52510652 / [arcsec] Offset btwn actual and rqsted pntng +PTGDIFFX= 0.46966798 / [pixels] rqsted - actual pntng along axis 1 +PTGDIFFY= -0.2352852 / [pixels] rqsted - actual pntng along axis 2 +RA_REF = 202.47237 / [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.48232 / [deg] Refined RA +DECRFND = 47.175121 / [deg] Refined DEC +CT2RFND = 137.35905 / [deg] Refined CROT2 +ERARFND = 2.332304E-6 / [deg] Error in RARFND value +EDECRFND= 2.332411E-6 / [deg] Error in DECRFND value +ECT2RFND= 0.00054069905 / [deg] Error in CT2RFND value +NASTROM = 12 / Astrometric sources for absolute refinement +ORIG_RA = 202.48206 / [deg] Original RA from raw BPHF (without pointi +ORIG_DEC= 47.175072 / [deg] Original Dec from raw BPHF (without point +ORIGCD11= 0.00024975633 / [deg/pix] Original CD1_1 element (without point +ORIGCD12= 0.00023017843 / [deg/pix] Original CD1_2 element (without point +ORIGCD21= 0.00023042914 / [deg/pix] Original CD2_1 element (without point +ORIGCD22= -2.499652E-4 / [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-6 / distortion coefficient +A_0_3 = 3.7746E-9 / distortion coefficient +A_1_1 = 2.1886E-5 / distortion coefficient +A_1_2 = -1.6847E-7 / distortion coefficient +A_2_0 = -2.3863E-5 / distortion coefficient +A_2_1 = -8.561E-9 / distortion coefficient +A_3_0 = -1.4172E-7 / distortion coefficient +A_DMAX = 1.394 / [pixel] maximum correction +B_ORDER = 3 / polynomial order, axis 2, detector to sky +B_0_2 = 2.31E-5 / distortion coefficient +B_0_3 = -1.6168E-7 / distortion coefficient +B_1_1 = -2.4386E-5 / distortion coefficient +B_1_2 = -5.7813E-9 / distortion coefficient +B_2_0 = 2.1197E-6 / distortion coefficient +B_2_1 = -1.6583E-7 / distortion coefficient +B_3_0 = -2.0249E-8 / distortion coefficient +B_DMAX = 1.501 / [pixel] maximum correction +AP_ORDER= 3 / polynomial order, axis 1, sky to detector +AP_0_1 = -6.4275E-7 / distortion coefficient +AP_0_2 = -2.9425E-6 / distortion coefficient +AP_0_3 = -3.582E-9 / distortion coefficient +AP_1_0 = -1.4897E-5 / distortion coefficient +AP_1_1 = -2.225E-5 / distortion coefficient +AP_1_2 = 1.7195E-7 / distortion coefficient +AP_2_0 = 2.4146E-5 / distortion coefficient +AP_2_1 = 6.709E-9 / distortion coefficient +AP_3_0 = 1.4492E-7 / distortion coefficient +BP_ORDER= 3 / polynomial order, axis 2, sky to detector +BP_0_1 = -1.6588E-5 / distortion coefficient +BP_0_2 = -2.3424E-5 / distortion coefficient +BP_0_3 = 1.651E-7 / distortion coefficient +BP_1_0 = -2.6783E-6 / distortion coefficient +BP_1_1 = 2.4753E-5 / distortion coefficient +BP_1_2 = 3.8917E-9 / distortion coefficient +BP_2_0 = -2.151E-6 / distortion coefficient +BP_2_1 = 1.7E-7 / distortion coefficient +BP_3_0 = 2.0482E-8 / 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.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/ast_tester/sip.head b/ast/ast_tester/sip.head new file mode 100644 index 0000000..0e9e838 --- /dev/null +++ b/ast/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/ast_tester/sparse.ast b/ast/ast_tester/sparse.ast new file mode 100644 index 0000000..c28cb4d --- /dev/null +++ b/ast/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/ast_tester/specflux.ast b/ast/ast_tester/specflux.ast new file mode 100644 index 0000000..0da5fea --- /dev/null +++ b/ast/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/ast_tester/specflux.attr b/ast/ast_tester/specflux.attr new file mode 100644 index 0000000..e00d209 --- /dev/null +++ b/ast/ast_tester/specflux.attr @@ -0,0 +1 @@ +system=freq,logplot=1,loggap(1)=100 diff --git a/ast/ast_tester/specflux.box b/ast/ast_tester/specflux.box new file mode 100644 index 0000000..7c6f180 --- /dev/null +++ b/ast/ast_tester/specflux.box @@ -0,0 +1,2 @@ +1 3 1.0E-8 9 + diff --git a/ast/ast_tester/specflux.head b/ast/ast_tester/specflux.head new file mode 100644 index 0000000..ac4a035 --- /dev/null +++ b/ast/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/ast_tester/splittest1.ast b/ast/ast_tester/splittest1.ast new file mode 100644 index 0000000..9fb1e2f --- /dev/null +++ b/ast/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/ast_tester/stcschan-test1-doc3-props.ast b/ast/ast_tester/stcschan-test1-doc3-props.ast new file mode 100644 index 0000000..4ab41aa --- /dev/null +++ b/ast/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/ast_tester/stcschan-test1-doc3.ast b/ast/ast_tester/stcschan-test1-doc3.ast new file mode 100644 index 0000000..0758ca7 --- /dev/null +++ b/ast/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/ast_tester/testchannel.f b/ast/ast_tester/testchannel.f new file mode 100644 index 0000000..ac69b2c --- /dev/null +++ b/ast/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/ast_tester/testchebymap.f b/ast/ast_tester/testchebymap.f new file mode 100644 index 0000000..b0a0bba --- /dev/null +++ b/ast/ast_tester/testchebymap.f @@ -0,0 +1,475 @@ + program testchebymap + implicit none + + include 'SAE_PAR' + include 'AST_PAR' + include 'PRM_PAR' + + integer status, lstat, cm, cm2, cm3, i, j, nco + double precision lbnd( 2 ), ubnd( 2 ), dval, lb, ub, xl(2), xu(2) + double precision tlbnd( 2 ), tubnd( 2 ), dlbnd( 2 ), dubnd( 2 ) + + double precision coeffs_1( 4*3 ), xin( 5 ), xout( 5 ), xrec( 5 ), + : yrec( 5 ), coeffs_4(4*4 ), + : work( 5 ), coeffs_2( 2*3 ), coeffs_3( 4*5 ), + : yin(5), yout(5), xi, yi, xv, yv, y, a, x, + : cofs( 100 ) + + +C f(x) = 1.5*T0(x') - 1.0*T2(x') + 2.0*T3(x') - 1.3*T4(x') + data coeffs_1 /1.5D0, 1.0D0, 0.0D0, + : -1.0D0, 1.0D0, 2.0D0, + : 2.0D0, 1.0D0, 3.0D0, + : 1.3D0, 1.0D0, 4.0D0 / + +C f(x) = 1.0*T0(x') - 2.0*T1(x') + data coeffs_2 /1.0D0, 1.0D0, 0.0D0, + : -2.0D0, 1.0D0, 1.0D0 / + + +C fx(x,y) = 1.0*T0(x')*T0(y') - 2.0*T1(x')*T2(y') + T1(y') +C fy(x,y) = 1.5*T0(x')*T0(y') - 2.5*T1(x')*T2(y') + data coeffs_3 /1.0D0, 1.0D0, 0.0D0, 0.0D0, + : -2.0D0, 1.0D0, 1.0D0, 2.0D0, + : 1.0D0, 1.0D0, 0.0D0, 1.0D0, + : 1.5D0, 2.0D0, 0.0D0, 0.0D0, + : -2.5D0, 2.0D0, 1.0D0, 2.0D0/ + +C fx(x,y) = T1(x') + T1(y') +C fy(x,y) = T1(x') - T1(y') +C +C This has the property that the coeffs of the inverse transformation are +C equal to the coeffs of the forward transformation. + data coeffs_4 /1.0D0, 1.0D0, 1.0D0, 0.0D0, + : 1.0D0, 1.0D0, 0.0D0, 1.0D0, + : 1.0D0, 2.0D0, 1.0D0, 0.0D0, + : -1.0D0, 2.0D0, 0.0D0, 1.0D0 / + + + status = sai__ok + call ast_begin( status ) + +C One-dimensional ChebyMaps, order 1: a constant equal to 1.5 + lbnd( 1 ) = -1.0D0 + lbnd( 2 ) = -1.0D0 + ubnd( 1 ) = 1.0D0 + ubnd( 2 ) = 1.0D0 + + cm = ast_chebymap( 1, 1, 1, coeffs_1, 0, 0.0D0, lbnd, ubnd, 1.0D0, + : 1.0D0, ' ', status ) + + xin( 1 ) = -1.0D0 + xin( 2 ) = -0.5D0 + xin( 3 ) = 0.0D0 + xin( 4 ) = 0.5D0 + xin( 5 ) = 1.0D0 + + call ast_tran1( cm, 5, xin, .true., xout, status ) + do i = 1, 5 + if( xout( i ) .ne. 1.5D0 ) call stopit( 0, status ) + end do + +C One-dimensional ChebyMaps, order 3: 2.5 - 2*x*x + cm = ast_chebymap( 1, 1, 2, coeffs_1, 0, 0.0D0, lbnd, ubnd, 1.0D0, + : 1.0D0, ' ', status ) + call ast_tran1( cm, 5, xin, .true., xout, status ) + do i = 1, 5 + if( xout( i ) .ne. 2.5D0 - 2.0D0*xin( i )**2 ) + : call stopit( 1, status ) + end do + +C One-dimensional ChebyMaps, order 4: 2.5 - 6*x - 2*x*x + 8*x*x*x + cm = ast_chebymap( 1, 1, 3, coeffs_1, 0, 0.0D0, lbnd, ubnd, 1.0D0, + : 1.0D0, ' ', status ) + call ast_tran1( cm, 5, xin, .true., xout, status ) + do i = 1, 5 + dval = 2.5D0-6.0D0*xin(i)-2.0D0*xin(i)**2+8.0D0*xin(i)**3 + if( xout( i ) .ne. dval ) call stopit( 2, status ) + end do + +C One-dimensional ChebyMaps, order 5 + cm = ast_chebymap( 1, 1, 4, coeffs_1, 0, 0.0D0, lbnd, ubnd, 1.0D0, + : 1.0D0, ' ', status ) + call ast_tran1( cm, 5, xin, .true., xout, status ) + + do i = 1, 5 + work( 1 ) = 1.0D0 + work( 2 ) = xin( i ) + do j = 3, 5 + work( j ) = 2.0D0 * xin( i ) * work( j - 1 ) + : - work( j - 2 ) + end do + + if( 1.5D0*work(1) - 1.0D0*work(3) + + : 2.0D0*work(4) + 1.3D0*work(5) .ne. + : xout( i ) ) call stopit( 3, status ) + + end do + +C Check the IterInverse attribute is zero. + if( ast_getl( cm, 'IterInverse', status ) ) then + call stopit( 4, status ) + end if + +c The astPolyTran method on a 1-dimensional ChebyMaps, order 2: 1 - 2*x + cm = ast_chebymap( 1, 1, 2, coeffs_2, 0, 0.0D0, lbnd, ubnd, 1.0D0, + : 1.0D0, ' ', status ) + cm2 = ast_polytran( cm, .false., 0.01D0, 0.01D0, 5, lbnd, + : ubnd, status ) + if( cm2 .eq. AST__NULL ) then + call stopit( 5, status ) + else + xin( 1 ) = -1.0D0 + xin( 2 ) = -0.5D0 + xin( 3 ) = 0.0D0 + xin( 4 ) = 0.5D0 + xin( 5 ) = 1.0D0 + call ast_tran1( cm2, 5, xin, .true., xout, status ) + call ast_tran1( cm2, 5, xout, .false., xrec, status ) + do i = 1, 5 + if( abs( xrec(i) - xin(i) ) .gt. 1.0D-3*abs( xin(i) ) ) + : call stopit( 6, status ) + end do + + call ast_chebydomain( cm2, .false., dlbnd, dubnd, status ) + if( dlbnd(1) .ne. -1.0D0 ) + : call stopit( 501, status ) + if( dubnd(1) .ne. 3.0D0 ) + : call stopit( 502, status ) + + call ast_polycoeffs( cm2, .false., 100, cofs, nco, status ) + if( nco .ne. 1 ) + : call stopit( 503, status ) + + if( abs( cofs(1) + 1.0D0 ) .gt. 1.0D-10 ) + : call stopit( 504, status ) + if( abs( cofs(2) - 1.0D0 ) .gt. 1.0D-10 ) + : call stopit( 505, status ) + if( abs( cofs(3) - 1.0D0 ) .gt. 1.0D-10 ) + : call stopit( 506, status ) + + end if + +c The astPolyTran method on a 1-dimensional ChebyMaps, order 5. + lbnd( 1 ) = -100.0D0 + ubnd( 1 ) = 100.0D0 + cm = ast_chebymap( 1, 1, 4, coeffs_1, 0, 0.0D0, lbnd, ubnd, 1.0D0, + : 1.0D0, ' ', status ) + cm2 = ast_polytran( cm, .false., 0.01D0, 0.01D0, 10, -5.0D0, + : 50.0D0, status ) + + if( cm2 .eq. AST__NULL ) then + call stopit( 7, status ) + else + xin(1) = 0.0D0; + xin(2) = 10.0D0; + xin(3) = 20.0D0; + xin(4) = 30.0D0; + xin(5) = 40.0D0; + call ast_tran1( cm2, 5, xin, .true., xout, status ) + call ast_tran1( cm2, 5, xout, .false., xrec, status ) + + do i = 1, 5 + if( abs( xrec( i ) - xin( i ) ) .gt. 0.01D0 ) + : call stopit( 8, status ) + end do + end if + +c ast_equal and ast_copy + cm3 = ast_copy( cm2, status ) + if( .not. ast_equal( cm2, cm3, status ) ) then + call stopit( 9, status ) + end if + +c astDump and astLoadChebyMap + call checkdump( cm2, status ) + + +c Simple 2d ChebyMap. +C fx(x,y) = T1(x') + T1(y') +C fy(x,y) = T1(x') - T1(y') + + lbnd(1) = -1.0D0 + lbnd(2) = -1.0D0 + ubnd(1) = 1.0D0 + ubnd(2) = 1.0D0 + + cm = ast_chebymap( 2, 2, 4, coeffs_4, 0, 0.0D0, lbnd, ubnd, + : 1.0D0, 1.0D0, ' ', status ) + + cm2 = ast_copy( cm, status ) + call ast_invert( cm2, status ) + cm3 = ast_simplify( ast_cmpmap( cm, cm2, .TRUE., ' ', status ), + : status ) + if( .not. ast_isaunitmap( cm3, status ) ) then + call stopit( 1000, status ) + end if + + xin(1) = 0.5D0 + xin(2) = 0.0D0 + xin(3) = -0.5D0 + xin(4) = 0.0D0 + + yin(1) = 0.0D0 + yin(2) = 0.5D0 + yin(3) = 0.0D0 + yin(4) = -0.5D0 + + call ast_tran2( cm, 4, xin, yin, .true., xout, yout, status ) + do i = 1, 4 + xv = xin(i) + yin(i) + yv = xin(i) - yin(i) + + if( abs( xout(i) - xv ) .gt. 1.0D-6*abs(xv) .or. + : abs( yout(i) - yv ) .gt. 1.0D-6*abs(yv) ) then + call stopit( 101, status ) + end if + end do + + cm2 = ast_polytran( cm, .false., 0.01D0, 0.01D0, 10, lbnd, + : ubnd, status ) + + if( cm2 .eq. AST__NULL ) then + call stopit( 102, status ) + else + call ast_tran2( cm2, 4, xout, yout, .false., xrec, yrec, + : status ) + do i = 1, 4 + if( abs( xrec(i) - xin(i) ) .gt. 0.01D0 .or. + : abs( yrec(i) - yin(i) ) .gt. 0.01D0 ) then + call stopit( 103, status ) + end if + end do + end if + + call ast_polycoeffs( cm2, .false., 100, cofs, nco, status ) + if( nco .ne. 4 ) then + call stopit( 104, status ) + else + do i = 1, 16 + if( abs( cofs(i) - coeffs_4(i) ) .gt. 0.01D0 ) then + call stopit( 105, status ) + end if + end do + end if + + call ast_chebydomain( cm2, .false., dlbnd, dubnd, status ) + + if( dlbnd(1) .ne. -2.0D0 ) then + call stopit( 106, status ) + else if( dlbnd(2) .ne. -2.0D0 ) then + call stopit( 107, status ) + else if( dubnd(1) .ne. 2.0D0 ) then + call stopit( 108, status ) + else if( dubnd(2) .ne. 2.0D0 ) then + call stopit( 109, status ) + end if + +* 2-dimensional ChebyMaps: forward transformation + lbnd(1) = 0.0D0 + lbnd(2) = 0.0D0 + ubnd(1) = 10.0D0 + ubnd(2) = 10.0D0 + + cm = ast_chebymap( 2, 2, 5, coeffs_3, 0, 0.0D0, lbnd, ubnd, + : 1.0D0, 1.0D0, ' ', status ) + + xin(1) = 0.0D0 + xin(2) = 2.0D0 + xin(3) = 6.0D0 + xin(4) = 10.0D0 + + yin(1) = 2.0D0 + yin(2) = 5.0D0 + yin(3) = 8.0D0 + yin(4) = 0.0D0 + + call ast_tran2( cm, 4, xin, yin, .true., xout, yout, status ) + do i = 1, 4 + xi = 2.0D0*( xin(i) - lbnd(1) )/( ubnd(1) - lbnd(1) ) - 1.0D0 + yi = 2.0D0*( yin(i) - lbnd(2) )/( ubnd(2) - lbnd(2) ) - 1.0D0 + + xv = 1 - 2*xi*(2*yi**2 - 1) + yi + yv = 1.5 - 2.5*xi*(2*yi**2 - 1) + + if( abs( xout(i) - xv ) .gt. 1.0D-6*abs(xv) .or. + : abs( yout(i) - yv ) .gt. 1.0D-6*abs(yv) ) then + call stopit( 10, status ) + end if + end do + + +* 2-dimensional ChebyMaps: fitted inverse transformation + tlbnd(1) = 4.0D0 + tlbnd(2) = 4.0D0 + tubnd(1) = 6.0D0 + tubnd(2) = 6.0D0 + cm2 = ast_polytran( cm, .false., 0.01D0, 0.01D0, 10, tlbnd, + : tubnd, status ) + + if( cm2 .eq. AST__NULL ) then + call stopit( 11, status ) + else + xin(1) = 4.0D0 + xin(2) = 4.5D0 + xin(3) = 5.0D0 + xin(4) = 5.5D0 + + yin(1) = 6.0D0 + yin(2) = 5.5D0 + yin(3) = 5.0D0 + yin(4) = 4.5D0 + + call ast_tran2( cm2, 4, xin, yin, .true., xout, yout, status ) + call ast_tran2( cm2, 4, xout, yout, .false., xrec, yrec, + : status ) + do i = 1, 4 + if( abs( xrec(i) - xin(i) ) .gt. 0.01D0 .or. + : abs( yrec(i) - yin(i) ) .gt. 0.01D0 ) then + call stopit( 12, status ) + end if + end do + end if + + +* Test recovery of coeffs + call ast_polycoeffs( cm2, .true., 0, 0.0D0, nco, status ) + if( nco .ne. 5 ) then + call stopit( 13, status ) + endif + + call ast_polycoeffs( cm2, .true., 100, cofs, nco, status ) + if( nco .ne. 5 ) then + call stopit( 14, status ) + else + do i = 1, 20 + if( cofs( i ) .ne. coeffs_3( i ) ) then + call stopit( 15, status ) + end if + end do + endif + + call ast_polycoeffs( cm2, .false., 0, 0.0D0, nco, status ) + if( nco .ne. 9 ) then + call stopit( 16, status ) + endif + + call ast_polycoeffs( cm2, .false., 100, cofs, nco, status ) + + if( nco .ne. 9 ) then + call stopit( 17, status ) + else if( abs( cofs( 1 ) - 5.0000000000000018D0 ) .gt. + : 1.0E-6 ) then + call stopit( 18, status ) + else if( abs( cofs( 13 ) - 0.35096188953505458D0 ) .gt. + : 1.0E-6 ) then + call stopit( 19, status ) + else if( cofs( 15 ) .ne. 2.0D0 ) then + call stopit( 20, status ) + end if + +* Test recovery of domain bounding box + call ast_chebydomain( cm, .true., dlbnd, dubnd, status ) + + if( dlbnd(1) .ne. lbnd(1) ) then + call stopit( 21, status ) + else if( dlbnd(2) .ne. lbnd(2) ) then + call stopit( 22, status ) + else if( dubnd(1) .ne. ubnd(1) ) then + call stopit( 23, status ) + else if( dubnd(2) .ne. ubnd(2) ) then + call stopit( 24, status ) + end if + + call ast_chebydomain( cm, .false., dlbnd, dubnd, status ) + + if( dlbnd(1) .ne. -2.0D0 ) then + call stopit( 25, status ) + else if( dlbnd(2) .ne. -1.0D0 ) then + call stopit( 26, status ) + else if( dubnd(1) .ne. 4.0D0 ) then + call stopit( 27, status ) + else if( dubnd(2) .ne. 4.0D0 ) then + call stopit( 28, status ) + end if + + + call ast_chebydomain( cm2, .true., dlbnd, dubnd, status ) + + if( dlbnd(1) .ne. lbnd(1) ) then + call stopit( 29, status ) + else if( dlbnd(2) .ne. lbnd(2) ) then + call stopit( 30, status ) + else if( dubnd(1) .ne. ubnd(1) ) then + call stopit( 31, status ) + else if( dubnd(2) .ne. ubnd(2) ) then + call stopit( 32, status ) + end if + + call ast_chebydomain( cm2, .false., dlbnd, dubnd, status ) + + if( abs( dlbnd(1) - 0.432 ) .gt. 1.0D-6 ) then + call stopit( 33, status ) + else if( abs( dlbnd(2) - 1.000816 ) .gt. 1.0D-6 ) then + call stopit( 34, status ) + else if( abs( dubnd(1) - 1.568D0 ) .gt. 1.0D-6 ) then + call stopit( 35, status ) + else if( abs( dubnd(2) - 1.9991836D0 ) .gt. 1.0D-6 ) then + call stopit( 36, status ) + end if + + + call ast_end( status ) + call ast_activememory( 'testchebymap' ); + call ast_flushmemory( 1 ) + + if( status .eq. sai__ok ) then + write(*,*) 'All ChebyMap tests passed' + else + write(*,*) 'ChebyMap 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 + + + subroutine checkdump( obj, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer obj, status, ch, result + + if( status .ne. sai__ok ) return + + ch = ast_channel( AST_NULL, AST_NULL, ' ', status ) + + call ast_set( ch, 'SinkFile=fred.tmp', status ) + if( ast_write( ch, obj, status ) .ne. 1 ) then + call stopit( -1, status ) + end if + call ast_clear( ch, 'SinkFile', status ) + + call ast_set( ch, 'SourceFile=fred.tmp', status ) + result = ast_read( ch, status ) + if( result .eq. ast__null ) then + call stopit( -2, status ) + end if + call ast_clear( ch, 'SourceFile', status ) + + if( .not. ast_equal( result, obj, status ) ) then + call ast_show( obj, status ) + call ast_show( result, status ) + call stopit( -3, status ) + end if + + end diff --git a/ast/ast_tester/testcmpmap.f b/ast/ast_tester/testcmpmap.f new file mode 100644 index 0000000..bd3cad1 --- /dev/null +++ b/ast/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/ast_tester/testconvert.c b/ast/ast_tester/testconvert.c new file mode 100644 index 0000000..d28c318 --- /dev/null +++ b/ast/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" + +int 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/ast_tester/testerror.c b/ast/ast_tester/testerror.c new file mode 100644 index 0000000..d668879 --- /dev/null +++ b/ast/ast_tester/testerror.c @@ -0,0 +1,44 @@ +#include "ast.h" + +#define ERRVAL -1234 +static int flag; + +void myPutErr( int status_value, const char *message ); + +int main(){ + double a[2] = {0.0,0.0}; + +/* Initialise the flag that indicates if the error handler has been + called. */ + flag = 0; + +/* Register the error handler. */ + astSetPutErr( myPutErr ); + +/* Generate an error by making a ShiftMap with a negative number of axes. + The error handler will set the flag to a special value. */ + AstShiftMap *map = astShiftMap( -1, a, " " ); + +/* Clear the error status. */ + astClearStatus; + +/* Clear the error reporter so that the default error reporter is used. */ + astSetPutErr( NULL ); + +/* Report an error if the flag was not set to the correct value. */ + if( flag != ERRVAL ) { + astError( AST__INTER, "Error reporting function has not been " + "called." ); + } + + if( astOK ) { + printf(" All Error tests passed\n"); + } else { + printf("Error tests failed\n"); + } + +} + +void myPutErr( int status_value, const char *message ) { + flag = ERRVAL; +} diff --git a/ast/ast_tester/testfitschan.f b/ast/ast_tester/testfitschan.f new file mode 100644 index 0000000..f2816c3 --- /dev/null +++ b/ast/ast_tester/testfitschan.f @@ -0,0 +1,988 @@ + program testfitschan + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + integer status, fs, fc, i, val, iwcfrm, map + character cards(10)*80, card*80 + logical there + double precision xin, yin, xout, yout + + 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 ) + + if( .not. ast_getl( fc, 'SipOK', status ) ) then + call stopit( 776, ' ', status ) + end if + +* 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) = 'MYNAME = ' + cards(6) = 'CDELT1 = -0.01' + cards(7) = 'CDELT2 = 0.01' + cards(8) = 'CTYPE1 = ''RA---TAN''' + cards(9) = 'CTYPE2 = ''DEC--TAN''' + do i = 1, 9 + 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 + + call ast_seti( fc, 'Card', 5, status ) + if( ast_testfits( fc, '.', there, status ) ) then + call stopit( 779, ' ', status ) + else if( .not. there ) then + call stopit( 780, ' ', status ) + endif + + if( .not. ast_testfits( fc, 'CDELT1', there, status ) ) then + call stopit( 781, ' ', status ) + else if( .not. there ) then + call stopit( 782, ' ', status ) + endif + + if( ast_testfits( fc, 'ABCDEF', there, status ) ) then + call stopit( 783, ' ', status ) + else if( there ) then + call stopit( 784, ' ', status ) + endif + + call ast_seti( fc, 'Card', 10, status ) + if( ast_testfits( fc, '.', there, status ) ) then + call stopit( 785, ' ', status ) + else if( there ) then + call stopit( 786, ' ', status ) + endif + + card = ast_getc( fc, 'CardName', status ) + if( card .ne. ' ' ) call stopit( 787, ' ', status ) + +* 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. 9 ) then + write(*,*) ast_geti( fc, 'NCard', status ) + call stopit( 1000, ' ', status ) + endif + + if( ast_geti( fc, 'Nkey', status ) .ne. 9 ) 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, 'Iwc=1', 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 + +* Check the IWC Frame is present and check the reference point +* transforms to the origin of IWC. + if( ast_geti( fs, 'Nframe', status ) .ne. 3 ) then + call stopit( 301, 'Wrong number of Frames', status ) + endif + if( ast_geti( fs, 'Current', status ) .ne. 2 ) then + call stopit( 302, 'Wrong current Frame', status ) + endif + iwcfrm = ast_getframe( fs, 3, status ) + if( ast_getc( iwcfrm, 'Domain', status ) .ne. 'IWC' ) then + call stopit( 303, 'Wrong Domain in IWC Frame', status ) + endif + + map = ast_getmapping( fs, 1, 3, status ) + xin = 45.0D0 + yin = 45.0D0 + call ast_tran2( map, 1, xin, yin, .true., xout, yout, status ) + if( xout .ne. 0.0D0 .or. yout .ne. 0.0D0 ) then + call stopit( 304, 'Wrong IWC for CRPIX position', status ) + endif + + xin = xin + 1.0D0 + call ast_tran2( map, 1, xin, yin, .true., xout, yout, status ) + if( xout .ne. -0.01D0 .or. yout .ne. 0.0D0 ) then + call stopit( 305, 'Wrong IWC for offset CRPIX position', + : status ) + endif + + map = ast_getmapping( fs, 2, 3, status ) + xin = 45.0D0*AST__DD2R + yin = 89.9D0*AST__DD2R + call ast_tran2( map, 1, xin, yin, .true., xout, yout, status ) + if( abs( xout ) .gt. 1.0D-10 .or. abs( yout ) .gt. 1.0D-10 ) then + call stopit( 306, 'Wrong IWC for CRVAL position', status ) + endif + + +* 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_seti( fc, 'SipOK', 0, 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/ast_tester/testfitstable.f b/ast/ast_tester/testfitstable.f new file mode 100644 index 0000000..e85f065 --- /dev/null +++ b/ast/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/ast_tester/testflux.f b/ast/ast_tester/testflux.f new file mode 100644 index 0000000..f24629f --- /dev/null +++ b/ast/ast_tester/testflux.f @@ -0,0 +1,354 @@ + 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( abs( ast_getd( mp, 'Zoom', status ) - 1.0D-9 ) + : .gt. 1.0E-24 ) 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/ast_tester/testframeset.f b/ast/ast_tester/testframeset.f new file mode 100644 index 0000000..0c9c52e --- /dev/null +++ b/ast/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/ast_tester/testkeymap.f b/ast/ast_tester/testkeymap.f new file mode 100644 index 0000000..fcd5d5c --- /dev/null +++ b/ast/ast_tester/testkeymap.f @@ -0,0 +1,1369 @@ + 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_mapgetc( map2, 'Fredc', cval, l, status ) ) then + call stopit( status, 'Error 5b' ) + else if( l .ne. 5 ) then + write(*,*) l + call stopit( status, 'Error 6b' ) + else if( cval( :l ) .ne. 'Hello' ) then + write(*,*) cval( :l ) + call stopit( status, 'Error 7b' ) + 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 entire vector as a single string. + if( .not. ast_mapgetc( map2, 'Fredc', cval0, l, status ) ) then + call stopit( status, 'Error BB1' ) + else if( l .ne. 34 ) then + call stopit( status, 'Error BB2' ) + else if( cval0 .ne. '(Hello , , Hello )' ) then + call stopit( status, 'Error BB3' ) + 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/ast_tester/testlutmap.f b/ast/ast_tester/testlutmap.f new file mode 100644 index 0000000..e183375 --- /dev/null +++ b/ast/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/ast_tester/testmapping.f b/ast/ast_tester/testmapping.f new file mode 100644 index 0000000..a02dd5b --- /dev/null +++ b/ast/ast_tester/testmapping.f @@ -0,0 +1,85 @@ + program testmapping + implicit none + + include 'AST_PAR' + include 'SAE_PAR' + + integer status, pm + double precision coeff(20), fit(6), lbnd(2), ubnd(2) + + 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, + : 3.0, 1.0, 0.0, 2.0 / + + + + status = sai__ok + call err_mark( status ) + call ast_begin( status ) + + pm = ast_polymap( 2, 2, 4, coeff, 0, coeff, ' ', status ) + + lbnd( 1 ) = -1.0D0 + lbnd( 2 ) = -1.0D0 + ubnd( 1 ) = 1.0D0 + ubnd( 2 ) = 1.0D0 + if( ast_linearapprox(pm, lbnd, ubnd, 0.001D0, fit, status) ) then + if( fit(1) .ne. 1.0D0 .or. fit(2) .ne. 1.0D0 .or. + : fit(3) .ne. 2.0D0 .or. fit(4) .ne. 0.0D0 .or. + : fit(5) .ne. 0.0D0 .or. fit(6) .ne. 3.0D0 ) then + call stopit( status, 'Error 0' ) + end if + else + call stopit( status, 'Error 1' ) + end if + + coeff( 13 ) = AST__BAD + pm = ast_polymap( 2, 2, 4, coeff, 0, coeff, ' ', status ) + + if( ast_linearapprox(pm, lbnd, ubnd, 0.001D0, fit, status) ) then + if( fit(1) .ne. 1.0D0 .or. fit(2) .ne. AST__BAD .or. + : fit(3) .ne. 2.0D0 .or. fit(4) .ne. 0.0D0 .or. + : fit(5) .ne. AST__BAD .or. fit(6) .ne. AST__BAD ) then + call stopit( status, 'Error 2' ) + end if + else + call stopit( status, 'Error 3' ) + end if + + pm = ast_polymap( 2, 2, 5, coeff, 0, coeff, ' ', status ) + + if( ast_linearapprox(pm, lbnd, ubnd, 0.001D0, fit, status) ) then + write(*,*) fit + call stopit( status, 'Error 4' ) + end if + + + + + call ast_end( status ) + call err_rlse( status ) + + if( status .eq. sai__ok ) then + write(*,*) 'All Mapping tests passed' + else + write(*,*) 'Mapping 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/ast_tester/testnormmap.f b/ast/ast_tester/testnormmap.f new file mode 100644 index 0000000..2c57ee6 --- /dev/null +++ b/ast/ast_tester/testnormmap.f @@ -0,0 +1,94 @@ + 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) + + + + +* Test adjacent identical NormMaps are simplified to a single NormMap. + m2 = ast_cmpmap( ast_cmpmap( m, ast_copy( m, status ), .true., + : ' ', status ), + : ast_cmpmap( m, ast_copy( m, status ), .true., + : ' ', status ), .true., ' ', status ) + m3 = ast_simplify( m2, status ) + if( .not. ast_isanormmap( m3, status ) ) call stopit( 9, status ) + +* Test NormMap that encapsulate a basic Frame are simplified to a UnitMap. + m = ast_normmap( ast_frame(2, ' ', status ), ' ', status ) + m2 = ast_simplify( m, status ) + if( .not. ast_isaunitmap( m2, status ) ) call stopit( 10, 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/ast_tester/testobject.c b/ast/ast_tester/testobject.c new file mode 100644 index 0000000..c1c1972 --- /dev/null +++ b/ast/ast_tester/testobject.c @@ -0,0 +1,120 @@ +#include "ast.h" +#include +#include + +int main(){ + const char *routine; + const char *file; + int i; + int line; + char *pickle1; + char *pickle2; + AstSkyFrame *sf = astSkyFrame( " " ); + AstFrame *bf = astFrame( 2, "Domain=SKY" ); + AstFrameSet *fs = astConvert( bf, sf, " " ); + AstKeyMap *km; + void *p; + + 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" ); + } + + astCreatedAt( bf, &routine, &file, &line ); + if( ( !routine || strcmp( routine, "main" ) ) && astOK ) { + astError( AST__INTER, "Error 31\n" ); + } + if( ( !file || strcmp( file, "testobject.c" ) ) && astOK ) { + astError( AST__INTER, "Error 32\n" ); + } + if( line != 13 && astOK ) { + astError( AST__INTER, "Error 33 (line is %d)\n", line ); + } + + + km = astActiveObjects( NULL, 0, 0 ); + if( !km && astOK ) { + astError( AST__INTER, "Error 34\n" ); + } else { + int nkey = astMapSize( km ); + if( nkey != 3 && astOK ) { + astError( AST__INTER, "Error 35 (nkey is %d)\n", nkey ); + } + + astSetC( km, "SortBy", "KeyUp" ); + for( i=0; i < 3; i++ ){ + const char *key = astMapKey( km, i ); + if( i == 0 ) { + if( strcmp( key, "Frame" ) && astOK ) { + astError( AST__INTER, "Error 36 (key 0 is '%s')\n", key ); + } else if( astMapLength(km,key) != 1 && astOK ) { + astError( AST__INTER, "Error 361 (%d)\n", astMapLength(km,key) ); + } else if( ( !astMapGetElemP( km, key, 0, &p ) || ( p != bf ) ) && astOK ) { + astError( AST__INTER, "Error 362\n" ); + } else { + astCreatedAt( p, &routine, &file, &line ); + if( ( !routine || strcmp( routine, "main" ) ) && astOK ) { + astError( AST__INTER, "Error 363\n" ); + } + if( ( !file || strcmp( file, "testobject.c" ) ) && astOK ) { + astError( AST__INTER, "Error 364\n" ); + } + if( line != 13 && astOK ) { + astError( AST__INTER, "Error 365 (line is %d)\n", line ); + } + } + } else if( i == 1 ) { + if( strcmp( key, "FrameSet" ) && astOK ) { + astError( AST__INTER, "Error 37 (key 1 is '%s')\n", key ); + } else if( astMapLength(km,key) != 2 && astOK ) { + astError( AST__INTER, "Error 371 (%d)\n", astMapLength(km,key) ); + } else if( ( !astMapGetElemP( km, key, 1, &p ) || ( p != fs2 ) ) && astOK ) { + astError( AST__INTER, "Error 372\n" ); + } else { + astCreatedAt( p, &routine, &file, &line ); + if( ( !routine || strcmp( routine, "main" ) ) && astOK ) { + astError( AST__INTER, "Error 373\n" ); + } + if( ( !file || strcmp( file, "testobject.c" ) ) && astOK ) { + astError( AST__INTER, "Error 374\n" ); + } + if( line != 20 && astOK ) { + astError( AST__INTER, "Error 375 (line is %d)\n", line ); + } + } + } else { + if( strcmp( key, "SkyFrame" ) && astOK ) { + astError( AST__INTER, "Error 38 (key 2 is '%s')\n", key ); + } else if( astMapLength(km,key) != 1 && astOK ) { + astError( AST__INTER, "Error 381 (%d)\n", astMapLength(km,key) ); + } + } + } + } + km = astAnnul( km ); + + } 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/ast_tester/testplot3d.f b/ast/ast_tester/testplot3d.f new file mode 100644 index 0000000..66a6858 --- /dev/null +++ b/ast/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/ast_tester/testpolymap.f b/ast/ast_tester/testpolymap.f new file mode 100644 index 0000000..1251894 --- /dev/null +++ b/ast/ast_tester/testpolymap.f @@ -0,0 +1,319 @@ + program testpolymap + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'PRM_PAR' + + integer status, pm, pm2, i, maxord, nco + 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, + : cofs( 20 ) + + 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 ) + + call ast_polycoeffs( pm, .true., 0, 0.0D0, nco, status ) + if( nco .ne. 4 ) then + call stopit( -1, status ) + endif + + call ast_polycoeffs( pm, .false., 0, 0.0D0, nco, status ) + if( nco .ne. 0 ) then + call stopit( -2, status ) + endif + + call ast_polycoeffs( pm, .true., 20, cofs, nco, status ) + if( nco .ne. 4 ) then + call stopit( -3, status ) + else + do i = 1, 16 + if( cofs( i ) .ne. coeff( i ) ) then + call stopit( -4, status ) + end if + end do + endif + + 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 + + + + if( .not. ast_getl( pm, 'TranForward', status ) ) then + call stopit( 8001, status ) + else if( .not. ast_getl( pm, 'IterInverse', status ) ) then + call stopit( 8002, status ) + else if( .not. ast_getl( pm, 'TranInverse', status ) ) then + call stopit( 8003, status ) + endif + + call ast_setl( pm, 'IterInverse', .FALSE., status ) + + if( .not. ast_getl( pm, 'TranForward', status ) ) then + call stopit( 8004, status ) + else if( ast_getl( pm, 'IterInverse', status ) ) then + call stopit( 8005, status ) + else if( ast_getl( pm, 'TranInverse', status ) ) then + call stopit( 8006, status ) + endif + + call ast_invert( pm, status ) + + if( ast_getl( pm, 'TranForward', status ) ) then + call stopit( 8007, status ) + else if( ast_getl( pm, 'IterInverse', status ) ) then + call stopit( 8008, status ) + else if( .not. ast_getl( pm, 'TranInverse', status ) ) then + call stopit( 8009, status ) + endif + + call ast_setl( pm, 'IterInverse', .TRUE., status ) + + if( .not. ast_getl( pm, 'TranForward', status ) ) then + call stopit( 8010, status ) + else if( .not. ast_getl( pm, 'IterInverse', status ) ) then + call stopit( 8011, status ) + else if( .not. ast_getl( pm, 'TranInverse', status ) ) then + call stopit( 8012, status ) + endif + + call ast_invert( pm, status ) + + if( .not. ast_getl( pm, 'TranForward', status ) ) then + call stopit( 8013, status ) + else if( .not. ast_getl( pm, 'IterInverse', status ) ) then + call stopit( 8014, status ) + else if( .not. ast_getl( pm, 'TranInverse', status ) ) then + call stopit( 8015, status ) + endif + + + + + + + + + + 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/ast_tester/testrate.f b/ast/ast_tester/testrate.f new file mode 100644 index 0000000..4803294 --- /dev/null +++ b/ast/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/ast_tester/testratemap.f b/ast/ast_tester/testratemap.f new file mode 100644 index 0000000..720a4b9 --- /dev/null +++ b/ast/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/ast_tester/testrebin.f b/ast/ast_tester/testrebin.f new file mode 100644 index 0000000..5ec976f --- /dev/null +++ b/ast/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/ast_tester/testrebinseq.f b/ast/ast_tester/testrebinseq.f new file mode 100644 index 0000000..3ab4e43 --- /dev/null +++ b/ast/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/ast_tester/testregions.f b/ast/ast_tester/testregions.f new file mode 100644 index 0000000..201ab17 --- /dev/null +++ b/ast/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/ast_tester/testskyframe.f b/ast/ast_tester/testskyframe.f new file mode 100644 index 0000000..6359d63 --- /dev/null +++ b/ast/ast_tester/testskyframe.f @@ -0,0 +1,89 @@ + program testskyframe + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + integer status, sf1, sf2, fs + double precision vals(5) + + 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 + + + vals(1) = 6.1D0 + vals(2) = 6.15D0 + vals(3) = 6.2D0 + vals(4) = 6.25D0 + vals(5) = 6.3D0 + call ast_axnorm( sf1, 1, 0, 5, vals, status ) + if( vals(1) .ne. 6.1D0 .or. + : vals(2) .ne. 6.15D0 .or. + : vals(3) .ne. 6.2D0 .or. + : vals(4) .ne. 6.25D0 .or. + : vals(5) .ne. 6.3D0 - 2*AST__DPI ) then + call stopit( status, 'Error 5' ) + end if + + call ast_axnorm( sf1, 1, 1, 5, vals, status ) + if( vals(1) .ne. 6.1D0 - 2*AST__DPI .or. + : vals(2) .ne. 6.15D0 - 2*AST__DPI .or. + : vals(3) .ne. 6.2D0 - 2*AST__DPI .or. + : vals(4) .ne. 6.25D0 - 2*AST__DPI .or. + : vals(5) .ne. 6.3D0 - 2*AST__DPI ) then + call stopit( status, 'Error 6' ) + 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/ast_tester/testspecflux.f b/ast/ast_tester/testspecflux.f new file mode 100644 index 0000000..ba19984 --- /dev/null +++ b/ast/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/ast_tester/testspecframe.f b/ast/ast_tester/testspecframe.f new file mode 100644 index 0000000..4af2606 --- /dev/null +++ b/ast/ast_tester/testspecframe.f @@ -0,0 +1,251 @@ + 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( abs( ast_GetD( sf, 'SpecOrigin', status ) - rf*1.0D9 ) + : .gt. 0.1 ) 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 ) - rf*1.0D9 ) + : .gt. 0.1 ) 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/ast_tester/teststc.f b/ast/ast_tester/teststc.f new file mode 100644 index 0000000..47c3c02 --- /dev/null +++ b/ast/ast_tester/teststc.f @@ -0,0 +1,1858 @@ + 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' ) + +c if( abs( 86400.0D0*lbnd(3) + 4.9662776D-5 ) .gt. 0.1E-10 ) +c : 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' ) +c if( abs( 86400.0D0*ubnd(3) - 4.9662776D-5 ) .gt. 0.1E-10 ) +c : 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/ast_tester/teststc_com b/ast/ast_tester/teststc_com new file mode 100644 index 0000000..f9a7fb1 --- /dev/null +++ b/ast/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/ast_tester/teststc_eg1 b/ast/ast_tester/teststc_eg1 new file mode 100644 index 0000000..b1044c6 --- /dev/null +++ b/ast/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/ast_tester/teststc_eg10 b/ast/ast_tester/teststc_eg10 new file mode 100644 index 0000000..977fa57 --- /dev/null +++ b/ast/ast_tester/teststc_eg10 @@ -0,0 +1,18 @@ + + + + Space + + + + + + + + + 143.0 42.0 + 1.0 1.0 + + + + diff --git a/ast/ast_tester/teststc_eg2 b/ast/ast_tester/teststc_eg2 new file mode 100644 index 0000000..6665676 --- /dev/null +++ b/ast/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/ast_tester/teststc_eg3 b/ast/ast_tester/teststc_eg3 new file mode 100644 index 0000000..028d484 --- /dev/null +++ b/ast/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/ast_tester/teststc_eg4 b/ast/ast_tester/teststc_eg4 new file mode 100644 index 0000000..5b9d8ab --- /dev/null +++ b/ast/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/ast_tester/teststc_eg5 b/ast/ast_tester/teststc_eg5 new file mode 100644 index 0000000..97aad97 --- /dev/null +++ b/ast/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/ast_tester/teststc_eg6 b/ast/ast_tester/teststc_eg6 new file mode 100644 index 0000000..7a291f6 --- /dev/null +++ b/ast/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/ast_tester/teststc_eg7 b/ast/ast_tester/teststc_eg7 new file mode 100644 index 0000000..0106c85 --- /dev/null +++ b/ast/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/ast_tester/teststc_eg8 b/ast/ast_tester/teststc_eg8 new file mode 100644 index 0000000..17c6574 --- /dev/null +++ b/ast/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/ast_tester/teststc_eg9 b/ast/ast_tester/teststc_eg9 new file mode 100644 index 0000000..195adb7 --- /dev/null +++ b/ast/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/ast_tester/teststcschan.f b/ast/ast_tester/teststcschan.f new file mode 100755 index 0000000..928b301 --- /dev/null +++ b/ast/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/ast_tester/testswitchmap.f b/ast/ast_tester/testswitchmap.f new file mode 100644 index 0000000..401447b --- /dev/null +++ b/ast/ast_tester/testswitchmap.f @@ -0,0 +1,794 @@ + 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( abs( out(4,1) - rmout(2,1) ) .gt. + : 1.0D-6*abs( rmout(2,1) ) ) then + call stopit( 138, out(2,1), status ) + + else if( abs( out(4,2) - rmout(2,2) ) .gt. + : 1.0D-6*abs( 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/ast_tester/testtable.f b/ast/ast_tester/testtable.f new file mode 100644 index 0000000..907dd66 --- /dev/null +++ b/ast/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/ast_tester/testtime.f b/ast/ast_tester/testtime.f new file mode 100644 index 0000000..39c4b9a --- /dev/null +++ b/ast/ast_tester/testtime.f @@ -0,0 +1,979 @@ + program testtime + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'AST_ERR' + + character txt*40 + double precision xin, xout, xout2, 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 + + +* Test use of DTAI + tf1 = ast_timeframe( 'system=mjd,timescale=tai', status ) + tf2 = ast_timeframe( 'system=mjd,timescale=utc', status ) + + fs = ast_convert( tf1, tf2, ' ', status ) + + xin = 57844.0D0 + + if (fs .eq. AST__NULL ) then + call stopit( status, 'error 52' ) + else + call ast_tran1( fs, 1, xin, .true., xout, status) + if (abs(((xin - xout) * 86400.0D0) - 37.0D0) .gt. 1.0D-3) then + write(*,*) xout + call stopit( status, 'error 53' ) + endif + call checkdump( fs, 'CheckDump 2', status ) + end if + + call ast_setd( tf2, 'dtai', 40.0D0, status ) + + fs = ast_convert( tf1, tf2, ' ', status ) + + if (fs .eq. AST__NULL ) then + call stopit( status, 'error 54' ) + else + call ast_tran1( fs, 1, xin, .true., xout, status) + if (abs(((xin - xout) * 86400.0D0) - 40.0D0) .gt. 1.0D-3) then + write(*,*) xout + call stopit( status, 'error 55' ) + endif + call checkdump( fs, 'CheckDump 3', status ) + end if + + + + tf1 = ast_timeframe( 'system=mjd,timescale=tt', status ) + tf2 = ast_timeframe( 'system=mjd,timescale=tdb,dtai=37.0', + : status ) + + fs = ast_convert( tf1, tf2, ' ', status ) + + if (fs .eq. AST__NULL ) then + call stopit( status, 'error 56' ) + else + call ast_tran1( fs, 1, xin, .true., xout, status) + call checkdump( fs, 'CheckDump 4', status ) + end if + + call ast_clear( tf2, 'dtai', status ) + + fs = ast_convert( tf1, tf2, ' ', status ) + + if (fs .eq. AST__NULL ) then + call stopit( status, 'error 57' ) + else + call ast_tran1( fs, 1, xin, .true., xout2, status) + if( xout .ne. xout2 ) then + call stopit( status, 'error 58' ) + 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, map, + : map1, map2 + 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_isatimeframe( obj, status ) ) then + 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 ) .or. + : ast_getc( obj, 'Dtai', status ) .ne. + : ast_getc( result, 'Dtai', status ) ) then + call ast_Show( obj, status ) + call ast_Show( result, status ) + write(*,*) text + call stopit( status, 'Object has changed' ) + end if + else if( ast_isamapping( obj, status ) ) then + if( ast_isaframeset( obj, status ) ) then + map1 = ast_getmapping( obj, ast__base, ast__current, + : status ) + map2 = ast_getmapping( result, ast__base, ast__current, + : status ) + else + map1 = ast_clone( obj, status ) + map2 = ast_clone( result, status ) + end if + + call ast_invert( map2, status ) + map = ast_simplify( ast_cmpmap( map1, map2, .true., ' ', + : status ), + : status ) + if( .not. ast_isaunitmap( map, status ) ) then + write(*,*) text + call ast_show( map1, status ) + call ast_invert( map2, status ) + call ast_show( map2, status ) + + call stopit( status, 'Mapping has changed' ) + endif + 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/ast_tester/testtrangrid.f b/ast/ast_tester/testtrangrid.f new file mode 100644 index 0000000..b867291 --- /dev/null +++ b/ast/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/ast_tester/testunitnormmap.f b/ast/ast_tester/testunitnormmap.f new file mode 100644 index 0000000..4437f2c --- /dev/null +++ b/ast/ast_tester/testunitnormmap.f @@ -0,0 +1,324 @@ + program testunitnormmap + implicit none + + include 'AST_PAR' + include 'SAE_PAR' + + integer status, map, cmpmap, smap, invmap, nin, i + logical good, differ + double precision norm, frompos(3), centre(3), topos(4) + + data centre /-1.0D0, 1.0D0, 2.0D0 /, + : frompos /-22.0D0, 3.0D0, 0.5D0/ + + + status = sai__ok + call err_mark( status ) + call ast_begin( status ) + + do nin = 1, 3 + map = ast_unitnormmap( nin, centre, ' ', status ) + + if( .not. ast_isaunitnormmap( map, status ) ) + : call stopit( status, 'Error 1' ) + if( .not. ast_isamapping( map, status ) ) + : call stopit( status, 'Error 2' ) + if( ast_geti( map, 'Nin', status ) .ne. nin ) + : call stopit( status, 'Error 3' ) + if( ast_geti( map, 'Nout', status ) .ne. nin+1 ) + : call stopit( status, 'Error 4' ) + if( ast_getl( map, 'IsLinear', status ) ) + : call stopit( status, 'Error 5' ) + + call checkdump( map, status ) + + call checkroundtrip( map, frompos, good, status ) + if( .not. good ) call stopit( status, 'Error 6' ) + + call checkroundtrip( map, centre, good, status ) + if( .not. good ) call stopit( status, 'Error 6' ) + + invmap = ast_copy( map, status ) + call ast_invert( invmap, status ) + cmpmap = ast_cmpmap( map, invmap, .true., ' ', status ) + smap = ast_simplify( cmpmap, status ) + if( .not. ast_isaunitmap( smap, status ) ) + : call stopit( status, 'Error 7' ) + if( ast_geti( smap, 'Nin', status ) .ne. nin ) + : call stopit( status, 'Error 8' ) + + cmpmap = ast_cmpmap( invmap, map, .true., ' ', status ) + smap = ast_simplify( cmpmap, status ) + if( .not. ast_isaunitmap( smap, status ) ) + : call stopit( status, 'Error 9' ) + if( ast_geti( smap, 'Nin', status ) .ne. nin+1 ) + : call stopit( status, 'Error 10' ) + + + call ast_trann( map, 1, nin, 1, frompos, .true., nin+1, + : 1, topos, status ); + + norm = 0.0D0 + do i = 1, nin + norm = norm + (frompos(i)-centre(i))**2 + end do + norm = sqrt( norm ) + + if( differ( norm, topos(nin+1) ) ) + : call stopit( status, 'Error 11' ) + + do i = 1, nin + if( differ( norm*topos(i), frompos(i)-centre(i) ) ) + : call stopit( status, 'Error 12' ) + end do + + end do + + call testsimplify( status ) + + call ast_end( status ) + call err_rlse( status ) + +c call ast_activememory( 'testunitnormmap' ) + call ast_flushmemory( 1 ) + + if( status .eq. sai__ok ) then + write(*,*) 'All UnitNormMap tests passed' + else + write(*,*) 'UnitNormMap 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, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer obj, status, stat, ch, result, nin, i + logical differ + double precision in(3), out1(3), out2(3) + + data in /10.0D0, 5.0D0, -12.0D0 / + + if( status .ne. sai__ok ) return + + ch = ast_channel( AST_NULL, AST_NULL, 'SinkFile=testdmp', status ) + + if( ast_write( ch, obj, status ) .ne.1 ) then + call stopit( status, 'Cannot write supplied object to '// + : 'channel' ) + end if + + call ast_clear( ch, 'SinkFile', status ) + call ast_setc( ch, 'SourceFile', 'testdmp', status ) + result = ast_read( ch, status ) + + if( result .eq. ast__null ) then + call stopit( status, 'Cannot read object from channel' ) + end if + + if( .not. ast_isaunitnormmap( result, status ) ) then + call stopit( status, 'Object read from channel is not a '// + : 'UnitNormMap') + else if( ast_geti( result, 'Nin', status ) .ne. + : ast_geti( obj, 'Nin', status ) ) then + call stopit( status, 'UnitNormMap have different Nin values' ) + + else + nin = ast_geti( result, 'Nin', status ) + call ast_trann( result, 1, nin, 1, in, .TRUE., nin+1, 1, out1, + : status ); + call ast_trann( obj, 1, nin, 1, in, .TRUE., nin+1, 1, out2, + : status ); + + do i = 1, nin + if( differ( out2(i), out1(i) ) ) + : call stopit( status, 'Error - Recovered UnitNormMap '// + : 'differs from suppled UnitNormMap' ) + end do + + end if + + call ast_annul( result, status ) + + open( unit=1234, iostat=stat, file='testdmp', status='old') + if (stat == 0) close(1234, status='delete') + + end + + + + subroutine checkroundtrip( map, pos, good, status ) + + implicit none + + include 'SAE_PAR' + include 'AST_PAR' + + logical good, differ + integer status, map, mapinv, cmp, i, nin, nout + double precision pos(*), rtol, atol, out(7), in(7) + + good = .true. + + if( status .ne. sai__ok ) return + + nin = ast_geti( map, 'Nin', status ) + nout = ast_geti( map, 'Nout', status ) + call ast_trann( map, 1, nin, 1, pos, .TRUE., nout, 1, out, + : status ); + call ast_trann( map, 1, nout, 1, out, .FALSE., nin, 1, in, + : status ); + do i = 1, nin + if( differ( in(i), pos(i) ) ) good = .false. + end do + + mapinv = ast_copy( map, status ) + call ast_invert( mapinv, status ) + cmp = ast_cmpmap( map, mapinv, .TRUE., ' ', status ) + + call ast_trann( cmp, 1, nin, 1, pos, .true., nin, 1, in, + : status ); + do i = 1, nin + if( differ( in(i), pos(i) ) ) good = .false. + end do + + end + + + + subroutine testsimplify( status ) + implicit none + + include 'SAE_PAR' + include 'AST_PAR' + + integer status, unm1, unm1inv, unm2, unm2inv, shiftmap, + : winmap1, winmap2, i, j, k, nin, nout, cmpmap, + : cmpmap_simp + double precision centre1(3) + double precision centre2(3) + double precision shift(3) + double precision zeros(3) + double precision ones(3) + double precision a(3) + double precision testpoints(3,4) + double precision outpoints(3,4) + double precision outpoints_simp(3,4) + + integer map1(7), map2(7) + character class(7)*30 + logical differ + + data centre1 /2.0D0, -1.0D0, 0.0D0/, + : centre2 /-1.0D0, 6.0D0, 4.0D0/, + : shift /3.0D0, 7.0D0, -9.0D0/, + : zeros /0.0D0, 0.0D0, 0.0D0/, + : ones /1.0D0, 1.0D0, 1.0D0/, + : testpoints / 1.0D0, 3.0D0, -5.0D0, 2.0D0, 3.0D0, 99.0D0, + : -6.0D0, -5.0D0, -7.0D0, 30.0D0, 21.0D0, 37.0D0 / + + if( status .ne. sai__ok ) return + + unm1 = ast_unitnormmap( 3, centre1, ' ', status ) + unm1inv = ast_copy( unm1, status ) + call ast_setl( unm1inv, 'Invert', .TRUE., status ) + + unm2 = ast_unitnormmap( 3, centre2, ' ', status ) + unm2inv = ast_copy( unm2, status ) + call ast_setl( unm2inv, 'Invert', .TRUE., status ) + + shiftmap = ast_shiftmap(3, shift, ' ', status ) + + do i = 1, 3 + a(i) = ones(i) + shift(i) + end do + winmap1 = ast_winmap( 3, zeros, shift, ones, a, ' ', status ) + + do i = 1, 3 + a(i) = 2*ones(i) + shift(i) + end do + winmap2 = ast_winmap( 3, zeros, shift, ones, a, ' ', status ) + + map1(1) = unm1 + map2(1) = unm2inv + class(1) = 'WinMap' + + map1(2) = shiftmap + map2(2) = unm2 + class(2) = 'UnitNormMap' + + map1(3) = winmap1 + map2(3) = unm1 + class(3) = 'UnitNormMap' + + map1(4) = winmap2 + map2(4) = unm1 + class(4) = 'CmpMap' + + map1(5) = unm1inv + map2(5) = shiftmap + class(5) = 'UnitNormMap' + + map1(6) = unm1inv + map2(6) = winmap1 + class(6) = 'UnitNormMap' + + map1(7) = unm1inv + map2(7) = winmap2 + class(7) = 'CmpMap' + + do i = 1, 7 + cmpmap = ast_cmpmap( map1(i), map2(i), .true., ' ', status ) + cmpmap_simp = ast_simplify( cmpmap, status ) + + if( ast_getc( cmpmap_simp, 'Class', status ) .ne. class(i) ) + : call stopit( status, 'Simplify error 1' ) + + nin = ast_geti( cmpmap, 'Nin', status ) + if( nin .ne. ast_geti( cmpmap_simp, 'Nin', status ) ) + : call stopit( status, 'Simplify error 2' ) + + nout = ast_geti( cmpmap, 'Nout', status ) + if( nout .ne. ast_geti( cmpmap_simp, 'Nout', status ) ) + : call stopit( status, 'Simplify error 3' ) + + call ast_trann( cmpmap, 3, nin, 3, testpoints, .true., nout, + : 3, outpoints, status ) + call ast_trann( cmpmap_simp, 3, nin, 3, testpoints, .true., + : nout, 3, outpoints_simp, status ) + + do j = 1, nout + do k = 1, 3 + if( differ( outpoints(k,j), outpoints_simp(k,j) ) ) + : call stopit( status, 'Simplify error 4' ) + end do + end do + end do + + end + + + logical function differ( aa, bb ) + implicit none + double precision aa, bb, diff + differ = abs( (aa) - (bb) ) .gt. + : abs( 0.5D0*( (aa) + (bb) ) )*1.0D-14 + + end diff --git a/ast/ast_tester/testxmlchan.f b/ast/ast_tester/testxmlchan.f new file mode 100644 index 0000000..51e58ec --- /dev/null +++ b/ast/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/ast_tester/testxmlchan_com b/ast/ast_tester/testxmlchan_com new file mode 100644 index 0000000..3b7500e --- /dev/null +++ b/ast/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/ast_tester/testzoommap.f b/ast/ast_tester/testzoommap.f new file mode 100644 index 0000000..0c2afd7 --- /dev/null +++ b/ast/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/ast_tester/timeplot.attr b/ast/ast_tester/timeplot.attr new file mode 100644 index 0000000..5b43238 --- /dev/null +++ b/ast/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/ast_tester/timeplot.box b/ast/ast_tester/timeplot.box new file mode 100644 index 0000000..c2351b4 --- /dev/null +++ b/ast/ast_tester/timeplot.box @@ -0,0 +1 @@ +0.046 3.0 1.008 3.3 diff --git a/ast/ast_tester/timeplot.head b/ast/ast_tester/timeplot.head new file mode 100644 index 0000000..95ea8d5 --- /dev/null +++ b/ast/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/ast_tester/timj.ast b/ast/ast_tester/timj.ast new file mode 100644 index 0000000..ed011dd --- /dev/null +++ b/ast/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/ast_tester/timj.fits-aips b/ast/ast_tester/timj.fits-aips new file mode 100644 index 0000000..4fdd94b --- /dev/null +++ b/ast/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/ast_tester/timj.fits-iraf b/ast/ast_tester/timj.fits-iraf new file mode 100644 index 0000000..88c3d94 --- /dev/null +++ b/ast/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/ast_tester/timj.fits-pc b/ast/ast_tester/timj.fits-pc new file mode 100644 index 0000000..f3bfc6f --- /dev/null +++ b/ast/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/ast_tester/timj.fits-wcs b/ast/ast_tester/timj.fits-wcs new file mode 100644 index 0000000..f8b3e8a --- /dev/null +++ b/ast/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/ast_tester/timj.native b/ast/ast_tester/timj.native new file mode 100644 index 0000000..fb54315 --- /dev/null +++ b/ast/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/ast_tester/tnx.attr b/ast/ast_tester/tnx.attr new file mode 100644 index 0000000..e5498b5 --- /dev/null +++ b/ast/ast_tester/tnx.attr @@ -0,0 +1 @@ +Grid=1,textlabgap=0.1,border=0 diff --git a/ast/ast_tester/tnx.box b/ast/ast_tester/tnx.box new file mode 100644 index 0000000..f69daff --- /dev/null +++ b/ast/ast_tester/tnx.box @@ -0,0 +1 @@ +0.0 0.0 2048.0 4096.0 diff --git a/ast/ast_tester/tnx.head b/ast/ast_tester/tnx.head new file mode 100644 index 0000000..58c6aa0 --- /dev/null +++ b/ast/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/ast_tester/tsc.attr b/ast/ast_tester/tsc.attr new file mode 100644 index 0000000..496403c --- /dev/null +++ b/ast/ast_tester/tsc.attr @@ -0,0 +1 @@ +border=1 diff --git a/ast/ast_tester/tsc.box b/ast/ast_tester/tsc.box new file mode 100644 index 0000000..45cc65e --- /dev/null +++ b/ast/ast_tester/tsc.box @@ -0,0 +1 @@ +1 1 1600 1600 diff --git a/ast/ast_tester/tsc.head b/ast/ast_tester/tsc.head new file mode 100644 index 0000000..e41aefb --- /dev/null +++ b/ast/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/ast_tester/wcsconverter.f b/ast/ast_tester/wcsconverter.f new file mode 100644 index 0000000..9af2e30 --- /dev/null +++ b/ast/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/ast_tester/zpn.attr b/ast/ast_tester/zpn.attr new file mode 100644 index 0000000..d74badf --- /dev/null +++ b/ast/ast_tester/zpn.attr @@ -0,0 +1 @@ +tol=0.0001 diff --git a/ast/ast_tester/zpn.box b/ast/ast_tester/zpn.box new file mode 100644 index 0000000..ee14a3a --- /dev/null +++ b/ast/ast_tester/zpn.box @@ -0,0 +1 @@ +1 1 200.0 200.0 diff --git a/ast/ast_tester/zpn.head b/ast/ast_tester/zpn.head new file mode 100644 index 0000000..a5e90af --- /dev/null +++ b/ast/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/ast_tester/zpx.attr b/ast/ast_tester/zpx.attr new file mode 100644 index 0000000..f073d5c --- /dev/null +++ b/ast/ast_tester/zpx.attr @@ -0,0 +1 @@ +Grid=1,style(grid1)=2,size(textlab2)=2 diff --git a/ast/ast_tester/zpx.head b/ast/ast_tester/zpx.head new file mode 100644 index 0000000..a69353a --- /dev/null +++ b/ast/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/astbad.c b/ast/astbad.c new file mode 100644 index 0000000..d79cdc3 --- /dev/null +++ b/ast/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 * AST__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 + AST__DBL_DIG and IEEE_DIG. */ + for ( digits = ( AST__DBL_DIG > IEEE_DIG )?AST__DBL_DIG:IEEE_DIG; + digits <= ( 2 * AST__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/ast/axis.c b/ast/axis.c new file mode 100644 index 0000000..6f58b14 --- /dev/null +++ b/ast/axis.c @@ -0,0 +1,3500 @@ +/* +*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. +* 26-OCT-2016 (DSB): +* Added astAxisNormValues. +*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 AxisNormValues( AstAxis *, int, int, 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 ), + AST__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 ), + AST__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 void AxisNormValues( AstAxis *this, int oper, int nval, + double *values, int *status ){ +/* +*+ +* Name: +* astAxisNormValues + +* Purpose: +* Normalise an array of axis coordinate values. + +* Type: +* Public virtual function. + +* Synopsis: +* #include "axis.h" +* void astAxisNormValues( AstAxis *this, int oper, int nval, +* double *values ) + +* Class Membership: +* Axis method. + +* Description: +* This function modifies a supplied array of axis values so that +* they are normalised in the manner indicated by parameter "oper". +* +* For a simple axis, the supplied values are always returned +* unchanged regardless of the value of "oper". + +* Parameters: +* this +* Pointer to the Axis. +* oper +* Indicates the type of normalisation to be applied. If zero is +* supplied, the normalisation will be the same as that performed by +* function astAxisNorm. If 1 is supplied, the normalisation will be +* chosen automatically so that the resulting list has the smallest +* range. +* nval +* The number of points in the values array. +* values +* On entry, the axis values to be normalised. Modified on exit to +* hold the normalised values. +*- +*/ + +/* 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", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Bottom. */ +/* ------- */ + } else if ( !strcmp( attrib, "bottom" ) ) { + dval = astGetAxisBottom( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__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->AxisNormValues = AxisNormValues; + 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 ); +} +void astAxisNormValues_( AstAxis *this, int oper, int nval, double *values, + int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Axis,AxisNormValues))( this, oper, nval, values, status ); +} + + + + + + diff --git a/ast/axis.h b/ast/axis.h new file mode 100644 index 0000000..3b053c5 --- /dev/null +++ b/ast/axis.h @@ -0,0 +1,625 @@ +#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 (* AxisNormValues)( AstAxis *, int, int, 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 * ); +void astAxisNormValues_( AstAxis *, int, int, 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 astAxisNormValues(this,oper,nval,values) \ +astINVOKE(V,astAxisNormValues_(astCheckAxis(this),oper,nval,values,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/ast/bootstrap b/ast/bootstrap new file mode 100755 index 0000000..b1b79e9 --- /dev/null +++ b/ast/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/ast/box.c b/ast/box.c new file mode 100644 index 0000000..99aa780 --- /dev/null +++ b/ast/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/ast/box.h b/ast/box.h new file mode 100644 index 0000000..46dcf6c --- /dev/null +++ b/ast/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/ast/builddocs.in b/ast/builddocs.in new file mode 100644 index 0000000..5056ce0 --- /dev/null +++ b/ast/builddocs.in @@ -0,0 +1,146 @@ +#! /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}/chebymap.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}/unitnormmap.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/ast/buildhyperdocs b/ast/buildhyperdocs new file mode 100755 index 0000000..23a07e1 --- /dev/null +++ b/ast/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/ast/c2f77.c b/ast/c2f77.c new file mode 100644 index 0000000..1801a55 --- /dev/null +++ b/ast/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/ast/c2f77.h b/ast/c2f77.h new file mode 100644 index 0000000..c50edac --- /dev/null +++ b/ast/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/ast/cexpand b/ast/cexpand new file mode 100755 index 0000000..b40957a --- /dev/null +++ b/ast/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/ast/cexpand.pl b/ast/cexpand.pl new file mode 100755 index 0000000..0795c04 --- /dev/null +++ b/ast/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/ast/channel.c b/ast/channel.c new file mode 100644 index 0000000..9219502 --- /dev/null +++ b/ast/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", AST__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. + +* 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/ast/channel.h b/ast/channel.h new file mode 100644 index 0000000..1969ea3 --- /dev/null +++ b/ast/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/ast/chebymap.c b/ast/chebymap.c new file mode 100644 index 0000000..29f544b --- /dev/null +++ b/ast/chebymap.c @@ -0,0 +1,2404 @@ +/* To do: + + - what to do about input positions that fall outside the bounding box. + Have an attribute that can be used to select "set bad" or "extrapolate"? + + - what about overriding astRate ? + + - Providing an iterative inverse requires the Jacobian to be defined. + + for a PolyMap: if y = C.x^n then y' = n.C.x^n-1 + for a ChebyMap: if y = C.Tn(x) then y' = n.C.Un-1(x) + + where Un-1(x) is the Chebyshev polynomial of the second kind, degree + (n-1), evaluated at x. Since PolyMap.GetJacobian function uses PolyMaps + to express the Jacobian of a PolyMap, it would need to use ChebyMaps + to express the Jacobian of a ChebyMap. But this would mean that + ChebyMap needs to be able to represent Chebyshev polynomials of the + second type. In fact the set of powers associated with each coefficient + would need to indicate somehow whether to use type 1 or type 2 for + each power. Could use negative powers to indicate type 2, but PolyMap.StoreArrays + objects to negative powers. StoreArrays could just store them + without checking, and then call a virtual function to verify the powers + are OK. + + Simpler for the moment just to disable iterative inverses in + ChebyMap. + +*/ + + +/* +*class++ +* Name: +* ChebyMap + +* Purpose: +* Map coordinates using Chebyshev polynomial functions. + +* Constructor Function: +c astChebyMap +f AST_CHEBYMAP + +* Description: +* A ChebyMap is a form of Mapping which performs a Chebyshev polynomial +* transformation. Each output coordinate is a linear combination of +* Chebyshev polynomials of the first kind, of order zero up to a +* specified maximum order, evaluated at the input coordinates. The +* coefficients to be used in the linear combination are specified +* separately for each output coordinate. +* +* For a 1-dimensional ChebyMap, the forward transformation is defined +* as follows: +* +* f(x) = c0.T0(x') + c1.T1(x') + c2.T2(x') + ... +* +* where: +* - Tn(x') is the nth Chebyshev polynomial of the first kind: +* - T0(x') = 1 +* - T1(x') = x' +* - Tn+1(x') = 2.x'.Tn(x') + Tn-1(x') +* - x' is the inpux axis value, x, offset and scaled to the range +* [-1, 1] as x ranges over a specified bounding box, given when the +* ChebyMap is created. The input positions, x, supplied to the +* forward transformation must fall within the bounding box - bad +* axis values (AST__BAD) are generated for points outside the +* bounding box. +* +* For an N-dimensional ChebyMap, the forward transformation is a +* generalisation of the above form. Each output axis value is the sum +c of "ncoeff" +f of NCOEFF +* terms, where each term is the product of a single coefficient +* value and N factors of the form Tn(x'_i), where "x'_i" is the +* normalised value of the i'th input axis value. +* +* The forward and inverse transformations are defined independantly +* by separate sets of coefficients, supplied when the ChebyMap is +* created. If no coefficients are supplied to define the inverse +* transformation, the +c astPolyTran +f AST_POLYTRAN +* method of the parent PolyMap class can instead be used to create an +* inverse transformation. The inverse transformation so generated +* will be a Chebyshev polynomial with coefficients chosen to minimise +* the residuals left by a round trip (forward transformation followed +* by inverse transformation). + +* Inheritance: +* The ChebyMap class inherits from the PolyMap class. + +* Attributes: +* The ChebyMap class does not define any new attributes beyond those +* which are applicable to all PolyMaps. + +* Functions: +c In addition to those functions applicable to all PolyMap, the +c following functions may also be applied to all ChebyMaps: +f In addition to those routines applicable to all PolyMap, the +f following routines may also be applied to all ChebyMaps: +* +c - astChebyDomain: Get the bounds of the domain of the ChebyMap +f - AST_CHEBYDOMAIN: Get the bounds of the domain of the ChebyMap + +* Copyright: +* Copyright (C) 2017 East Asian Observatory. +* 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 (EAO) + +* History: +* 1-MAR-2017 (DSB): +* Original version. +* 30-MAR-2017 (DSB): +* Over-ride the astFitPoly1DInit and astFitPoly2DInit virtual +* functions inherited form the PolyMap class. +*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 ChebyMap + +/* 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 "polymap.h" /* Polynomial mappings (parent class) */ +#include "cmpmap.h" /* Compound mappings */ +#include "chebymap.h" /* Interface definition for this class */ +#include "unitmap.h" /* Unit mappings */ + +/* 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 int (* parent_getobjsize)( AstObject *, int * ); +static int (* parent_equal)( AstObject *, AstObject *, int * ); +static void (* parent_polypowers)( AstPolyMap *, double **, int, const int *, double **, int, int, int * ); +static AstPolyMap *(*parent_polytran)( AstPolyMap *, int, double, double, int, const double *, const double *, 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(ChebyMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(ChebyMap,Class_Init) +#define class_vtab astGLOBAL(ChebyMap,Class_Vtab) + +#include + + +#else + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstChebyMapVtab 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. */ +AstChebyMap *astChebyMapId_( int, int, int, const double[], int, const double[], + const double[], const double[], const double[], + const double[], const char *, ... ); + + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstPolyMap *PolyTran( AstPolyMap *, int, double, double, int, const double *, const double *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetIterInverse( AstPolyMap *, int * ); +static int GetObjSize( AstObject *, int * ); +static void ChebyDomain( AstChebyMap *, int, double *, double *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *obj, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void PolyPowers( AstPolyMap *, double **, int, const int *, double **, int, int, int *); +static void FitPoly1DInit( AstPolyMap *, int, double **, AstMinPackData *, double *, int *); +static void FitPoly2DInit( AstPolyMap *, int, double **, AstMinPackData *, double *, int *); + +/* Member functions. */ +/* ================= */ + +static void ChebyDomain( AstChebyMap *this, int forward, double *lbnd, + double *ubnd, int *status ){ +/* +*++ +* Name: +c astChebyDomain +f AST_CHEBYDOMAIN + +* Purpose: +* Returns the bounding box of the domain of a ChebyMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "chebymap.h" +c void astChebyDomain( AstChebyMap *this, int forward, double *lbnd, +c double *ubnd ) +f CALL AST_CHEBYDOMAIN( THIS, FORWARD, LBND, UBND, STATUS ) + +* Class Membership: +* ChebyMap method. + +* Description: +c This function +f This routine +* returns the upper and lower limits of the box defining the domain +* of either the forward or inverse transformation of a ChebyMap. These +* are the values that were supplied when the ChebyMap was created. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the ChebyMap. +c forward +f FORWARD = LOGICAL (Given) +c A non-zero +f A .TRUE. +* value indicates that the domain of the ChebyMap's +* forward transformation is to be returned, while a zero +* value indicates that the domain of the inverse transformation +* should be returned. +c lbnd +f LBND() = DOUBLE PRECISION (Returned) +c Pointer to an +f An +* array in which to return the lower axis bounds of the ChebyMap +* domain. The number of elements should be at least equal to the +* number of ChebyMap inputs (if +c "forward" is non-zero), or outputs (if "forward" is zero). +f FORWARD is .TRUE.), or outputs (if FORWARD is .FALSE.). +c ubnd +f UBND() = DOUBLE PRECISION (Returned) +c Pointer to an +f An +* array in which to return the upper axis bounds of the ChebyMap +* domain. The number of elements should be at least equal to the +* number of ChebyMap inputs (if +c "forward" is non-zero), or outputs (if "forward" is zero). +f FORWARD is .TRUE.), or outputs (if FORWARD is .FALSE.). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - If the requested transformation is undefined (i.e. no +* transformation coefficients were specified when the ChebyMap was +* created), this method returns a box determined using the +c astMapBox +f AST_MAPBOX +* method on the opposite transformation, if the opposite +* transformation is defined. +* - If the above procedure fails to determine a bounding box, the supplied +* arrays are filled with AST__BAD values but no error is reported. + +*-- +*/ + +/* Local Variables: */ + double *lbnd_o; + double *offset_o; + double *offset; + double *scale_o; + double *scale; + double *ubnd_o; + int fwd_o; + int iax; + int nax; + int nax_o; + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Get the scales and offsets to use, depending on the value of "forward" + and whether the ChebyMap has been inverted. */ + if( forward != astGetInvert( this ) ) { + scale = this->scale_f; + offset = this->offset_f; + nax = astGetNin( this ); + scale_o = this->scale_i; + offset_o = this->offset_i; + nax_o = astGetNout( this ); + fwd_o = 0; + } else { + scale = this->scale_i; + offset = this->offset_i; + nax = astGetNout( this ); + scale_o = this->scale_f; + offset_o = this->offset_f; + nax_o = astGetNin( this ); + fwd_o = 1; + } + +/* Check the domain is defined. */ + if( scale && offset ) { + for( iax = 0; iax < nax; iax++ ) { + if( scale[ iax ] != 0.0 ) { + lbnd[ iax ] = ( -1.0 - offset[ iax ] ) / scale[ iax ]; + ubnd[ iax ] = ( 1.0 - offset[ iax ] ) / scale[ iax ]; + } else { + lbnd[ iax ] = AST__BAD; + ubnd[ iax ] = AST__BAD; + } + } + +/* If the requested domain is not defined, see if it can be determined + by transforming the domain of the other transformation into the + requested input ot putput space. */ + } else if( scale_o && offset_o ){ + +/* Allocate memory to hold the bounding box in the other space (input or + output), and then store the bounding box values. */ + lbnd_o = astMalloc( nax_o*sizeof( *lbnd_o ) ); + ubnd_o = astMalloc( nax_o*sizeof( *ubnd_o ) ); + if( astOK ) { + for( iax = 0; iax < nax_o; iax++ ) { + if( scale_o[ iax ] != 0.0 ) { + lbnd_o[ iax ] = ( -1.0 - offset_o[ iax ] ) / scale_o[ iax ]; + ubnd_o[ iax ] = ( 1.0 - offset_o[ iax ] ) / scale_o[ iax ]; + } else { + lbnd_o[ iax ] = AST__BAD; + ubnd_o[ iax ] = AST__BAD; + } + } + +/* Loop round finding the bounds on each input axis of the requested + transformation. */ + for( iax = 0; iax < nax; iax++ ) { + astMapBox( this, lbnd_o, ubnd_o, fwd_o, iax, lbnd + iax, + ubnd + iax, NULL, NULL ); + } + +/* Free resources */ + lbnd_o = astFree( lbnd_o ); + ubnd_o = astFree( ubnd_o ); + } + + +/* If the domain of the other transformation is not defined, return bad values. */ + } else { + for( iax = 0; iax < nax; iax++ ) { + lbnd[ iax ] = AST__BAD; + ubnd[ iax ] = AST__BAD; + } + } +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two ChebyMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "chebymap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* ChebyMap member function (over-rides the astEqual protected +* method inherited from the astPolyMap class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two ChebyMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a ChebyMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the ChebyMaps 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: */ + AstChebyMap *that; + AstChebyMap *this; + int i; + int nin; + int nout; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the Equal method inherited from the parent PolyMap class. This + checks that the PolyMaps are equal. */ + result = (*parent_equal)( this_object, that_object, status ); + if( result ) { + +/* Obtain pointers to the two ChebyMap structures. */ + this = (AstChebyMap *) this_object; + that = (AstChebyMap *) that_object; + +/* Check the second object is a ChebyMap. We know the first is a + ChebyMap since we have arrived at this implementation of the virtual + function. */ + if( astIsAChebyMap( that ) ) { + +/* Get the number of axes in the input bounding box (the original input space). */ + nin = astGetInvert( this ) ? astGetNout( this ) : astGetNin( this ); + +/* Check the bounding box is the same for both ChebyMaps. */ + if( this->scale_f && that->scale_f && + this->offset_f && that->offset_f ) { + for( i = 0; i < nin && result; i++ ) { + if( !astEQUAL( this->scale_f[ i ], that->scale_f[ i ] ) || + !astEQUAL( this->offset_f[ i ], that->offset_f[ i ] )){ + result = 0; + } + } + } else if( this->scale_f || that->scale_f || + this->offset_f || that->offset_f ) { + result = 0; + } + +/* Get the number of axes in the output bounding box (the original output space). */ + nout = astGetInvert( this ) ? astGetNin( this ) : astGetNout( this ); + +/* Check the bounding box is the same for both ChebyMaps. */ + if( this->scale_i && that->scale_i && + this->offset_i && that->offset_i ) { + for( i = 0; i < nout && result; i++ ) { + if( !astEQUAL( this->scale_i[ i ], that->scale_i[ i ] ) || + !astEQUAL( this->offset_i[ i ], that->offset_i[ i ] )){ + result = 0; + } + } + } else if( this->scale_i || that->scale_i || + this->offset_i || that->offset_i ) { + result = 0; + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static void FitPoly1DInit( AstPolyMap *this_polymap, int forward, double **table, + AstMinPackData *data, double *scales, int *status ){ +/* +* Name: +* FitPoly1DInit + +* Purpose: +* Perform initialisation needed for FitPoly1D + +* Type: +* Private function. + +* Synopsis: +* #include "chebymap.h" +* void FitPoly1DInit( AstPolyMap *this, int forward, double **table, +* AstMinPackData *data, double *scales, int *status ) + +* Class Membership: +* ChebyMap member function (over-rides the astFitPoly1DInit protected +* method inherited from the PolyMap class). + +* Description: +* This function performs initialisation needed for FitPoly1D in the +* PolyMap class. + +* Parameters: +* this +* Pointer to the PolyMap. +* forward +* Non-zero if the forward transformation of "this" is being +* replaced. Zero if the inverse transformation of "this" is being +* replaced. +* 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. +* data +* Pointer to a structure holding information to pass the the +* service function invoked by the minimisation function. +* 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. The scales are modified on exit to +* take account of the scaling performed by the ChebyMap Transform +* method. +*/ + +/* Local Variables; */ + AstChebyMap *this; + double *px1; + double *pxp1; + double maxx; + double minx; + double off; + double pmax; + double pmin; + double scl; + double x1; + int k; + int w1; + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the ChebyMap structure. */ + this = (AstChebyMap *) this_polymap; + +/* Find the bounds of the supplied x1 values. */ + px1 = table[ 0 ]; + minx = *px1; + maxx = *px1; + px1++; + for( k = 1; k < data->nsamp; k++,px1++ ) { + if( *px1 > maxx ) { + maxx = *px1; + } else if( *px1 < minx ) { + minx = *px1; + } + } + +/* Transform the above limits from table values into PolyMap axis values. */ + pmax = maxx*scales[ 0 ]; + pmin = minx*scales[ 0 ]; + +/* Calculate the scale and offset that map the above bounds onto the range + [-1,+1], and store them in the ChebyMap. */ + if( pmax != pmin ) { + scl = 2.0/( pmax - pmin ); + off = -( pmax + pmin )/( pmax - pmin ); + } else if( astOK ){ + astError( AST__BADBX, "astPolyTran(%s): New bounding box has zero width " + "on axis 1.", status, astGetClass(this)); + } + + if( forward ) { + this->scale_f = (double *) astFree( this->scale_f ); + this->offset_f = (double *) astFree( this->offset_f ); + + this->scale_f = (double *) astMalloc( sizeof( double ) ); + this->offset_f = (double *) astMalloc( sizeof( double ) ); + if( astOK ) { + this->scale_f[ 0 ] = scl; + this->offset_f[ 0 ] = off; + } + } else { + this->scale_i = (double *) astFree( this->scale_i ); + this->offset_i = (double *) astFree( this->offset_i ); + + this->scale_i = (double *) astMalloc( sizeof( double ) ); + this->offset_i = (double *) astMalloc( sizeof( double ) ); + if( astOK ) { + this->scale_i[ 0 ] = scl; + this->offset_i[ 0 ] = off; + } + } + +/* Get pointers to the supplied x1 values. */ + px1 = table[ 0 ]; + +/* Get pointers to the location for the next "power" of x1. Here "X to + the power N" is a metaphor for Tn(x). */ + pxp1 = data->xp1; + +/* Loop round all samples. */ + for( k = 0; k < data->nsamp; k++ ) { + +/* Get the current x1 value, and scale it into the range [-1,+1]. */ + x1 = *(px1++)*scl*scales[0] + off; + +/* Find all the required "powers" of x1 and store them in the "xp1" + component of the data structure. */ + *(pxp1++) = 1.0; + *(pxp1++) = x1; + for( w1 = 2; w1 < data->order; w1++,pxp1++ ) { + pxp1[ 0 ] = 2.0*x1*pxp1[ -1 ] - pxp1[ -2 ]; + } + } + +/* The scaling representing by the scales[0] value will be performed by + the astTransform method of the ChebyMap class, so reset teh scales[0] + value to unity, to avoid the scaling being applied twice. */ + scales[ 0 ] = 1.0; + +} + +static void FitPoly2DInit( AstPolyMap *this_polymap, int forward, double **table, + AstMinPackData *data, double *scales, int *status ){ +/* +* Name: +* FitPoly2DInit + +* Purpose: +* Perform initialisation needed for FitPoly2D + +* Type: +* Private function. + +* Synopsis: +* #include "chebymap.h" +* void FitPoly2DInit( AstPolyMap *this, int forward, double **table, +* AstMinPackData *data, double *scales, int *status ) + +* Class Membership: +* ChebyMap member function (over-rides the astFitPoly2DInit protected +* method inherited from the PolyMap class). + +* Description: +* This function performs initialisation needed for FitPoly2D in the +* PolyMap class.. + +* Parameters: +* this +* Pointer to the PolyMap. +* forward +* Non-zero if the forward transformation of "this" is being +* replaced. Zero if the inverse transformation of "this" is being +* replaced. +* 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. +* data +* Pointer to a structure holding information to pass the the +* service function invoked by the minimisation function. +* 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. +*/ + +/* Local Variables; */ + AstChebyMap *this; + double *px1; + double *px2; + double *pxp1; + double *pxp2; + double maxx; + double maxy; + double minx; + double miny; + double off[ 2 ]; + double pxmax; + double pxmin; + double pymax; + double pymin; + double scl[ 2 ]; + double x1; + double x2; + int k; + int w1; + int w2; + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the ChebyMap structure. */ + this = (AstChebyMap *) this_polymap; + +/* Find the bounds of the supplied x1 and x2 values. */ + px1 = table[ 0 ]; + px2 = table[ 1 ]; + minx = *px1; + maxx = *px1; + miny = *px2; + maxy = *px2; + px1++; + px2++; + for( k = 1; k < data->nsamp; k++,px1++,px2++ ) { + if( *px1 > maxx ) { + maxx = *px1; + } else if( *px1 < minx ) { + minx = *px1; + } + if( *px2 > maxy ) { + maxy = *px2; + } else if( *px2 < miny ) { + miny = *px2; + } + } + +/* Transform the above limits from table values into PolyMap axis values. */ + pxmax = maxx*scales[ 0 ]; + pxmin = minx*scales[ 0 ]; + pymax = maxy*scales[ 1 ]; + pymin = miny*scales[ 1 ]; + +/* Calculate the scale and offset that map the above bounds onto the range + [-1,+1], and store them in the ChebyMap. */ + if( pxmax != pxmin && pymax != pymin ) { + scl[ 0 ] = 2.0/( pxmax - pxmin ); + off[ 0 ] = -( pxmax + pxmin )/( pxmax - pxmin ); + scl[ 1 ] = 2.0/( pymax - pymin ); + off[ 1 ] = -( pymax + pymin )/( pymax - pymin ); + } else if( astOK ){ + astError( AST__BADBX, "astPolyTran(%s): New bounding box has zero width " + "on or both axes.", status, astGetClass(this)); + } + + if( forward ) { + this->scale_f = (double *) astFree( this->scale_f ); + this->offset_f = (double *) astFree( this->offset_f ); + + this->scale_f = (double *) astMalloc( 2*sizeof( double ) ); + this->offset_f = (double *) astMalloc( 2*sizeof( double ) ); + if( astOK ) { + this->scale_f[ 0 ] = scl[ 0 ]; + this->offset_f[ 0 ] = off[ 0 ]; + this->scale_f[ 1 ] = scl[ 1 ]; + this->offset_f[ 1 ] = off[ 1 ]; + } + } else { + this->scale_i = (double *) astFree( this->scale_i ); + this->offset_i = (double *) astFree( this->offset_i ); + + this->scale_i = (double *) astMalloc( 2*sizeof( double ) ); + this->offset_i = (double *) astMalloc( 2*sizeof( double ) ); + if( astOK ) { + this->scale_i[ 0 ] = scl[ 0 ]; + this->offset_i[ 0 ] = off[ 0 ]; + this->scale_i[ 1 ] = scl[ 1 ]; + this->offset_i[ 1 ] = off[ 1 ]; + } + } + +/* 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 anmd x2. Here "X to + the power N" is a metaphor for Tn(x). */ + pxp1 = data->xp1; + pxp2 = data->xp2; + +/* Loop round all samples. */ + for( k = 0; k < data->nsamp; k++ ) { + +/* Get the current x1 and x2 values, and scale them into the range [-1,+1]. */ + x1 = *(px1++)*scl[0]*scales[0] + off[0]; + x2 = *(px2++)*scl[1]*scales[1] + off[1]; + +/* Find all the required "powers" of x1 and store them in the "xp1" + component of the data structure. */ + *(pxp1++) = 1.0; + *(pxp1++) = x1; + for( w1 = 2; w1 < data->order; w1++,pxp1++ ) { + pxp1[ 0 ] = 2.0*x1*pxp1[ -1 ] - pxp1[ -2 ]; + } + +/* Find all the required "powers" of x2 and store them in the "xp2" + component of the data structure. */ + *(pxp2++) = 1.0; + *(pxp2++) = x2; + for( w2 = 2; w2 < data->order; w2++,pxp2++ ) { + pxp2[ 0 ] = 2.0*x2*pxp2[ -1 ] - pxp2[ -2 ]; + } + } + +/* The scaling representing by the scales[0] and scales[1] values will be + performed by the astTransform method of the ChebyMap class, so reset the + scales[0] and scales[1] values to unity, to avoid the scaling being + applied twice. */ + scales[ 0 ] = 1.0; + scales[ 1 ] = 1.0; + +} + +static int GetIterInverse( AstPolyMap *this, int *status ) { +/* +* Name: +* GetIterInverse + +* Purpose: +* Return the value of the IterInverse attribute. + +* Type: +* Private function. + +* Synopsis: +* #include "polymap.h" +* int GetIterInverse( AstObject *this, int *status ) + +* Class Membership: +* ChebyMap member function (over-rides the astGetIterInverse protected +* method inherited from the parent PolyMap class). + +* Description: +* This function returns the value of the IterInverse attribute, which +* is always zero for a ChebyMap. + +* Parameters: +* this +* Pointer to the ChebyMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The IterInverse 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. +*/ + + return 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 "chebymap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* ChebyMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied ChebyMap, +* in bytes. + +* Parameters: +* this +* Pointer to the ChebyMap. +* 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: */ + AstChebyMap *this; + int nin; + int nout; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the ChebyMap structure. */ + this = (AstChebyMap *) this_object; + +/* Get the number of input and output axes. */ + nin = astGetInvert( this ) ? astGetNout( this ) : astGetNin( this ); + nout = astGetInvert( this ) ? astGetNin( this ) : astGetNout( this ); + +/* 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->scale_f ) result += nin*sizeof( *this->scale_f ); + if( this->offset_f ) result += nin*sizeof( *this->offset_f ); + if( this->scale_i ) result += nout*sizeof( *this->scale_i ); + if( this->offset_i ) result += nout*sizeof( *this->offset_i ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +void astInitChebyMapVtab_( AstChebyMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitChebyMapVtab + +* Purpose: +* Initialise a virtual function table for a ChebyMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "chebymap.h" +* void astInitChebyMapVtab( AstChebyMapVtab *vtab, const char *name ) + +* Class Membership: +* ChebyMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the ChebyMap 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 */ + AstPolyMapVtab *polymap; /* Pointer to PolyMap 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. */ + astInitPolyMapVtab( (AstPolyMapVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAChebyMap) 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 = &(((AstPolyMapVtab *) 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; + polymap = (AstPolyMapVtab *) vtab; + + polymap->GetIterInverse = GetIterInverse; + + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_polypowers = polymap->PolyPowers; + polymap->PolyPowers = PolyPowers; + + parent_polytran = polymap->PolyTran; + polymap->PolyTran = PolyTran; + + parent_equal = object->Equal; + object->Equal = Equal; + + polymap->FitPoly1DInit = FitPoly1DInit; + polymap->FitPoly2DInit = FitPoly2DInit; + +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + vtab->ChebyDomain = ChebyDomain; + +/* Declare the destructor and copy constructor. */ + astSetDelete( (AstObjectVtab *) vtab, Delete ); + astSetCopy( (AstObjectVtab *) vtab, Copy ); + +/* Declare the class dump function. */ + astSetDump( vtab, Dump, "ChebyMap", "Chebyshev 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 PolyPowers( AstPolyMap *this_polymap, double **work, int ncoord, + const int *mxpow, double **ptr, int point, int fwd, + int *status ){ +/* +* Name: +* PolyPowers + +* Purpose: +* Find the required powers of the input axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "chebymap.h" +* void PolyPowers( AstPolyMap *this, double **work, int ncoord, +* const int *mxpow, double **ptr, int point, +* int fwd, int *status ) + +* Class Membership: +* ChebyMap member function (over-rides the astPolyPowers protected +* method inherited from the PolyMap class). + +* Description: +* This function is used by astTransform to calculate the powers of +* the axis values for a single input position. In the case of +* sub-classes, the powers may not be simply powers of the supplied +* axis values but may be more complex quantities such as a Chebyshev +* polynomial of the required degree evaluated at the input axis values. + +* Parameters: +* this +* Pointer to the PolyMap. +* work +* An array of "ncoord" pointers, each pointing to an array of +* length "max(2,mxpow)". The required values are placed in this +* array on exit. +* ncoord +* The number of axes. +* mxpow +* Pointer to an array holding the maximum power required of each +* axis value. Should have "ncoord" elements. +* ptr +* An array of "ncoord" pointers, each pointing to an array holding +* the axis values. Each of these arrays of axis values must have +* at least "point+1" elements. +* point +* The zero based index of the point within "ptr" that holds the +* axis values to be exponentiated. +* fwd +* Do the supplied coefficients define the foward transformation of +* the PolyMap? +*/ + +/* Local Variables; */ + AstChebyMap *this; + double *scales; + double *offsets; + double *pwork; + double *t; + double x; + int coord; + int ip; + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the ChebyMap structure. */ + this = (AstChebyMap *) this_polymap; + +/* Either transformation of a ChebyMap (forward or inverse) can be + defined either as Chebyshev polynomial or as a standard polynomial. + Chebyshev polynomials always have non-NULL scale array pointers. + If the scale array pointer is NULL, then the transformation is a + standard polynomial. If the coefficients relate to a standard + polynomial, then invoke the astPolyPowers implementation of the parent + class (PolyMap). */ + if( (fwd && !this->scale_f) || (!fwd && !this->scale_i) ) { + (*parent_polypowers)( this_polymap, work, ncoord, mxpow, ptr, point, + fwd, status ); + +/* If the coefficients relate to a Chebyshev polynomial... */ + } else { + scales = fwd ? this->scale_f : this->scale_i; + offsets = fwd ? this->offset_f : this->offset_i; + +/* This method uses a Chebyshev polynomial of the first kind of degree "i" + evaluated at "x'" instead of "x raised to the power i". Here, "x'" is + the input axis value scaled and shifted into the range [-1,+1] on each + axis. Loop over all input axes. */ + for( coord = 0; coord < ncoord; coord++ ) { + +/* Get a pointer to the array in which the powers of the current axis + value are to be returned. */ + pwork = work[ coord ]; + +/* The Chebyshev function (type 1) of degree zero is always 1.0, regardless + of the value of x. */ + pwork[ 0 ] = 1.0; + +/* Get the input axis value. If it is bad, store bad values for all + remaining powers. */ + x = ptr[ coord ][ point ]; + if( x == AST__BAD ) { + for( ip = 1; ip <= mxpow[ coord ]; ip++ ) pwork[ ip ] = AST__BAD; + +/* Otherwise, apply the required scaling to the input */ + } else { + x = x*scales[ coord ] + offsets[ coord ]; + +/* Return bad values for input positions outside the bounding box + associated with the transformation. */ + if( fabs( x ) <= 1.0 ) { + +/* The Chebyshev function of degree one is equal to x. */ + t = pwork + 1; + *t = x; + +/* Form and store the remaining Chebyshev polynomial values at the input axis value. + Use the standard recurrence relation: Tn+1(x') = 2.x'.Tn(x') + Tn-1(x'). */ + for( ip = 2; ip <= mxpow[ coord ]; ip++,t++ ) { + t[ 1 ] = 2.0*x*t[ 0 ] - t[ -1 ]; + } + } else { + for( ip = 1; ip <= mxpow[ coord ]; ip++ ) pwork[ ip ] = AST__BAD; + } + } + } + } +} + +static AstPolyMap *PolyTran( AstPolyMap *this_polymap, int forward, double acc, + double maxacc, int maxorder, const double *lbnd, + const double *ubnd, int *status ){ +/* +* Name: +* PolyTran + +* Purpose: +* Fit a PolyMap inverse or forward transformation. + +* Type: +* Private function. + +* Synopsis: +* #include "polymap.h" +* AstPolyMap *PolyTran( AstPolyMap *this, int forward, double acc, +* double maxacc, int maxorder, const double *lbnd, +* const double *ubnd ) + +* Class Membership: +* ChebyMap member function (over-rides the astPolyTran method inherited +* from the PolyMap class). + +* 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 "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. +* +* The "forward" parameter specifies the transformation to be replaced. If +* it is non-zero, 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). +* +* If "forward" is zero (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 "maxacc". +* If it is not, an error is reported. + +* Parameters: +* this +* Pointer to the original Mapping. +* forward +* If non-zero, 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 +* Pointer to 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. The length of this array +* should equal the value of the PolyMap's Nin or Nout attribute, +* depending on "forward". If a NULL pointer is supplied, the lower +* bounds of the box supplied when the ChebyMap was constructed is +* used. +* ubnd +* Pointer to 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. The length of this array should equal the +* value of the PolyMap's Nin or Nout attribute, depending on "forward". +* If a NULL pointer is supplied, the upper bounds of the box supplied +* when the ChebyMap was constructed is used. + +* Returned Value: +* astPolyTran() +* A pointer to the new PolyMap. A NULL pointer will be returned if +* the fit fails to achieve the accuracy specified by "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 +* function is invoked with the AST error status set, or if it +* should fail for any reason. +*/ + +/* Local Variables: */ + AstChebyMap *this; + AstPolyMap *result; + const char *word; + double *offset; + double *scale; + double this_lbnd[ 2 ]; + double this_ubnd[ 2 ]; + int inverted; + int nax; + +/* Initialise. */ + result = NULL; + +/* Check the inherited status. */ + if ( !astOK ) return result; + +/* Get a pointer to the CHebyMap structure. */ + this = (AstChebyMap *) this_polymap; + +/* Select the ChebyMap scales and offsets to be used. */ + inverted = astGetInvert( this ); + if( ( inverted && !forward ) || ( !inverted && forward ) ) { + word = "inverse"; + scale = this->scale_i; + offset = this->offset_i; + nax = ((AstMapping *)this)->nout; + } else { + word = "forward"; + scale = this->scale_f; + offset = this->offset_f; + nax = ((AstMapping *)this)->nin; + } + +/* The scaled box for a Chebyshev polynomial spans [-1,+1] on each axis. + Create the corresponding unscaled box. If the user supplies any bounds, + use them in preference to the bounds in the ChebyMap. */ + if( lbnd ) { + this_lbnd[ 0 ] = lbnd[ 0 ]; + if( nax > 1 ) this_lbnd[ 1 ] = lbnd[ 1 ]; + + } else if( scale && offset ) { + this_lbnd[ 0 ] = ( -1.0 - offset[ 0 ] )/scale[ 0 ]; + if( nax > 1 ) this_lbnd[ 1 ] = ( -1.0 - offset[ 1 ] )/scale[ 1 ]; + + } else if( astOK ) { + astError( AST__NOBOX, "astPolyTran(%s): The %s transformation is " + "not a Chebyshev polynomial and therefore requires a " + "user-supplied bounding box. But no lower bounds were " + "supplied. ", status, astGetClass( this ), word ); + } + + + if( ubnd ) { + this_ubnd[ 0 ] = ubnd[ 0 ]; + if( nax > 1 ) this_ubnd[ 1 ] = ubnd[ 1 ]; + } else if( scale && offset ) { + this_ubnd[ 0 ] = ( 1.0 - offset[ 0 ] )/scale[ 0 ]; + if( nax > 1 ) this_ubnd[ 1 ] = ( 1.0 - offset[ 1 ] )/scale[ 1 ]; + } else if( astOK ) { + astError( AST__NOBOX, "astPolyTran(%s): The %s transformation is " + "not a Chebyshev polynomial and therefore requires a " + "user-supplied bounding box. But no upper bounds were " + "supplied. ", status, astGetClass( this ), word ); + } + +/* Invoke the parent astPolyMap method, using the bounding box selected + above. */ + result = (*parent_polytran)( this_polymap, forward, acc, maxacc, maxorder, + this_lbnd, this_ubnd, status ); + +/* Return the new ChebyMap. */ + 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 ChebyMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for ChebyMap 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 ChebyMap. +*/ + + +/* Local Variables: */ + AstChebyMap *in; /* Pointer to input ChebyMap */ + AstChebyMap *out; /* Pointer to output ChebyMap */ + 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 ChebyMaps. */ + in = (AstChebyMap *) objin; + out = (AstChebyMap *) 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->scale_f = NULL; + out->offset_f = NULL; + out->scale_i = NULL; + out->offset_i = NULL; + +/* Get the number of inputs and outputs of the uninverted Mapping. */ + nin = ( (AstMapping *) in )->nin; + nout = ( (AstMapping *) in )->nout; + +/* Copy the bounding box arrays. */ + if( in->scale_f ) out->scale_f = (double *) astStore( NULL, + (void *) in->scale_f, + sizeof( double )*nin ); + if( in->offset_f ) out->offset_f = (double *) astStore( NULL, + (void *) in->offset_f, + sizeof( double )*nin ); + if( in->scale_i ) out->scale_i = (double *) astStore( NULL, + (void *) in->scale_i, + sizeof( double )*nout ); + if( in->offset_i ) out->offset_i = (double *) astStore( NULL, + (void *) in->offset_i, + sizeof( double )*nout ); +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for ChebyMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for ChebyMap 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: */ + AstChebyMap *this; + +/* Obtain a pointer to the ChebyMap structure. */ + this = (AstChebyMap *) obj; + +/* Free the boundib box arrays. */ + this->scale_f = astFree( this->scale_f ); + this->offset_f = astFree( this->offset_f ); + this->scale_i = astFree( this->scale_i ); + this->offset_i = astFree( this->offset_i ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for ChebyMap 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 ChebyMap class to an output Channel. + +* Parameters: +* this +* Pointer to the ChebyMap 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: */ + AstChebyMap *this; /* Pointer to the ChebyMap structure */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + char comm[ 100 ]; /* Buffer for comment string */ + int i; /* Loop index */ + 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 ChebyMap structure. */ + this = (AstChebyMap *) 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 + ChebyMap class. */ + +/* The input axis scale factors. */ + if( this->scale_f ){ + for( i = 0; i < nin; i++ ){ + (void) sprintf( buff, "FSCL%d", i + 1 ); + (void) sprintf( comm, "Scale factor on input %d", i + 1 ); + astWriteDouble( channel, buff, 1, 1, (this->scale_f)[ i ], comm ); + } + } + +/* The input axis offsets. */ + if( this->offset_f ){ + for( i = 0; i < nin; i++ ){ + (void) sprintf( buff, "FOFF%d", i + 1 ); + (void) sprintf( comm, "Offset on input %d", i + 1 ); + astWriteDouble( channel, buff, 1, 1, (this->offset_f)[ i ], comm ); + } + } + +/* The output axis scale factors. */ + if( this->scale_i ){ + for( i = 0; i < nout; i++ ){ + (void) sprintf( buff, "ISCL%d", i + 1 ); + (void) sprintf( comm, "Scale factor on output %d", i + 1 ); + astWriteDouble( channel, buff, 1, 1, (this->scale_i)[ i ], comm ); + } + } + +/* The output axis offsets. */ + if( this->offset_i ){ + for( i = 0; i < nout; i++ ){ + (void) sprintf( buff, "IOFF%d", i + 1 ); + (void) sprintf( comm, "Offset on output %d", i + 1 ); + astWriteDouble( channel, buff, 1, 1, (this->offset_i)[ i ], comm ); + } + } + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAChebyMap and astCheckChebyMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(ChebyMap,Mapping) +astMAKE_CHECK(ChebyMap) + +AstChebyMap *astChebyMap_( int nin, int nout, int ncoeff_f, const double coeff_f[], + int ncoeff_i, const double coeff_i[], + const double lbnd_f[], const double ubnd_f[], + const double lbnd_i[], const double ubnd_i[], + const char *options, int *status, ...){ +/* +*++ +* Name: +c astChebyMap +f AST_CHEBYMAP + +* Purpose: +* Create a ChebyMap. + +* Type: +* Public function. + +* Synopsis: +c #include "chebymap.h" +c AstChebyMap *astChebyMap( int nin, int nout, int ncoeff_f, const double coeff_f[], +c int ncoeff_i, const double coeff_i[], +c const double lbnd_f[], const double ubnd_f[], +c const double lbnd_i[], const double ubnd_i[], +c const char *options, ... ) +f RESULT = AST_CHEBYMAP( NIN, NOUT, NCOEFF_F, COEFF_F, NCOEFF_I, COEFF_I, +f LBND_F, UBND_F, LBND_I, UBND_I, OPTIONS, STATUS ) + +* Class Membership: +* ChebyMap constructor. + +* Description: +* This function creates a new ChebyMap and optionally initialises +* its attributes. +* +* A ChebyMap is a form of Mapping which performs a Chebyshev polynomial +* transformation. Each output coordinate is a linear combination of +* Chebyshev polynomials of the first kind, of order zero up to a +* specified maximum order, evaluated at the input coordinates. The +* coefficients to be used in the linear combination are specified +* separately for each output coordinate. +* +* For a 1-dimensional ChebyMap, the forward transformation is defined +* as follows: +* +* f(x) = c0.T0(x') + c1.T1(x') + c2.T2(x') + ... +* +* where: +* - Tn(x') is the nth Chebyshev polynomial of the first kind: +* - T0(x') = 1 +* - T1(x') = x' +* - Tn+1(x') = 2.x'.Tn(x') + Tn-1(x') +* - x' is the inpux axis value, x, offset and scaled to the range +* [-1, 1] as x ranges over a specified bounding box, given when the +* ChebyMap is created. The input positions, x, supplied to the +* forward transformation must fall within the bounding box - bad +* axis values (AST__BAD) are generated for points outside the +* bounding box. +* +* For an N-dimensional ChebyMap, the forward transformation is a +* generalisation of the above form. Each output axis value is the sum +c of "ncoeff" +f of NCOEFF +* terms, where each term is the product of a single coefficient +* value and N factors of the form Tn(x'_i), where "x'_i" is the +* normalised value of the i'th input axis value. +* +* The forward and inverse transformations are defined independantly +* by separate sets of coefficients, supplied when the ChebyMap is +* created. If no coefficients are supplied to define the inverse +* transformation, the +c astPolyTran +f AST_POLYTRAN +* method of the parent PolyMap class can instead be used to create an +* inverse transformation. The inverse transformation so generated +* will be a Chebyshev polynomial with coefficients chosen to minimise +* the residuals left by a round trip (forward transformation followed +* by inverse 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 ChebyMap. 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 +* ChebyMap output which uses the coefficient within its defining +* expression (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 ChebyMap 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 the Chebyshev +* polynomial of power 1 evaluated at input coordinate 1, and the +* value of the Chebyshev polynomial of power 3 evaluated at input +* coordinate 2. 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 )" 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 ChebyMap. 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 lbnd_f +f LBND_F( * ) = DOUBLE PRECISION (Given) +* An array containing the lower bounds of the input bounding box within +* which the ChebyMap is defined. This argument is not used or +* accessed if +c ncoeff_f is zero, and so a NULL pointer may be supplied. +f NCOEFF_F is zero. +* If supplied, the array should contain +c "nin" elements. +f "NIN" elements. +c ubnd_f +f UBND_F( * ) = DOUBLE PRECISION (Given) +* An array containing the upper bounds of the input bounding box within +* which the ChebyMap is defined. This argument is not used or +* accessed if +c ncoeff_f is zero, and so a NULL pointer may be supplied. +f NCOEFF_F is zero. +* If supplied, the array should contain +c "nin" elements. +f "NIN" elements. +c lbnd_i +f LBND_I( * ) = DOUBLE PRECISION (Given) +* An array containing the lower bounds of the output bounding box within +* which the ChebyMap is defined. This argument is not used or +* accessed if +c ncoeff_i is zero, and so a NULL pointer may be supplied. +f NCOEFF_I is zero. +* If supplied, the array should contain +c "nout" elements. +f "NOUT" elements. +c ubnd_i +f UBND_I( * ) = DOUBLE PRECISION (Given) +* An array containing the upper bounds of the output bounding box within +* which the ChebyMap is defined. This argument is not used or +* accessed if +c ncoeff_i is zero, and so a NULL pointer may be supplied. +f NCOEFF_I is zero. +* If supplied, the array should contain +c "nout" elements. +f "NOUT" elements. +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 ChebyMap. 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 ChebyMap. 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 astChebyMap() +f AST_CHEBYMAP = INTEGER +* A pointer to the new ChebyMap. + +* 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 */ + AstChebyMap *new; /* Pointer to new ChebyMap */ + 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 ChebyMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitChebyMap( NULL, sizeof( AstChebyMap ), !class_init, + &class_vtab, "ChebyMap", nin, nout, + ncoeff_f, coeff_f, ncoeff_i, coeff_i, + lbnd_f, ubnd_f, lbnd_i, ubnd_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 ChebyMap'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 ChebyMap. */ + return new; +} + +AstChebyMap *astChebyMapId_( int nin, int nout, int ncoeff_f, const double coeff_f[], + int ncoeff_i, const double coeff_i[], + const double lbnd_f[], const double ubnd_f[], + const double lbnd_i[], const double ubnd_i[], + const char *options, ... ){ +/* +* Name: +* astChebyMapId_ + +* Purpose: +* Create a ChebyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "chebymap.h" +* AstChebyMap *astChebyMap( int nin, int nout, int ncoeff_f, const double coeff_f[], +* int ncoeff_i, const double coeff_i[], const +* double lbnd_f[], const double ubnd_f[], +* double lbnd_i[], const double ubnd_i[], +* const char *options, ... ) + +* Class Membership: +* ChebyMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astChebyMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astChebyMap_ 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 astChebyMap_ 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 astChebyMap_. + +* Returned Value: +* The ID value associated with the new ChebyMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstChebyMap *new; /* Pointer to new ChebyMap */ + 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 ChebyMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitChebyMap( NULL, sizeof( AstChebyMap ), !class_init, + &class_vtab, "ChebyMap", nin, nout, + ncoeff_f, coeff_f, ncoeff_i, coeff_i, + lbnd_f, ubnd_f, lbnd_i, ubnd_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 ChebyMap'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 ChebyMap. */ + return astMakeId( new ); +} + +AstChebyMap *astInitChebyMap_( void *mem, size_t size, int init, + AstChebyMapVtab *vtab, const char *name, + int nin, int nout, int ncoeff_f, const double coeff_f[], + int ncoeff_i, const double coeff_i[], + const double lbnd_f[], const double ubnd_f[], + const double lbnd_i[], const double ubnd_i[], + int *status ){ +/* +*+ +* Name: +* astInitChebyMap + +* Purpose: +* Initialise a ChebyMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "chebymap.h" +* AstChebyMap *astInitChebyMap( void *mem, size_t size, int init, +* AstChebyMapVtab *vtab, const char *name, +* int nin, int nout, int ncoeff_f, +* const double coeff_f[], int ncoeff_i, +* const double coeff_i[] +* const double lbnd_f[], const double ubnd_f[], +* const double lbnd_i[], const double ubnd_i[] ) + +* Class Membership: +* ChebyMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new ChebyMap object. It allocates memory (if necessary) to accommodate +* the ChebyMap plus any additional data associated with the derived class. +* It then initialises a ChebyMap 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 ChebyMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the ChebyMap is to be initialised. +* This must be of sufficient size to accommodate the ChebyMap data +* (sizeof(ChebyMap)) 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 ChebyMap (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 ChebyMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the ChebyMap'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 ChebyMap. +* 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 ChebyMap. 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 ChebyMap 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 ChebyMap 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 ChebyMap. 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. +* lbnd_f +* An array containing the lower bounds of the input bounding box within +* which the ChebyMap is defined. The array should contain "nin" elements. +* ubnd_f +* An array containing the upper bounds of the input bounding box within +* which the ChebyMap is defined. The array should contain "nin" elements. +* lbnd_i +* An array containing the lower bounds of the output bounding box within +* which the ChebyMap is defined. The array should contain "nout" elements. +* ubnd_i +* An array containing the upper bounds of the output bounding box within +* which the ChebyMap is defined. The array should contain "nout" elements. + +* Returned Value: +* A pointer to the new ChebyMap. + +* 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: */ + AstChebyMap *new; + int i; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitChebyMapVtab( vtab, name ); + +/* Initialise a PolyMap structure (the parent class) as the first component + within the ChebyMap structure, allocating memory if necessary. */ + new = (AstChebyMap *) astInitPolyMap( mem, size, 0, + (AstPolyMapVtab *) vtab, name, + nin, nout, ncoeff_f, coeff_f, + ncoeff_i, coeff_i ); + if ( astOK ) { + +/* Initialise the ChebyMap data. */ +/* ---------------------------- */ + +/* First initialise the pointers in case of errors. */ + new->scale_f = NULL; + new->offset_f = NULL; + new->scale_i = NULL; + new->offset_i = NULL; + +/* Calculate the scales and offsets that map the supplied input bounding box + onto the range [-1,+1] on each input axis, and store them. */ + if( ncoeff_f > 0 ) { + new->scale_f = (double *) astMalloc( sizeof( double )*nin ); + new->offset_f = (double *) astMalloc( sizeof( double )*nin ); + if( astOK ) { + for( i = 0; i < nin; i++ ) { + if( ubnd_f[ i ] != lbnd_f[ i ] ) { + new->scale_f[ i ] = 2.0/( ubnd_f[ i ] - lbnd_f[ i ] ); + new->offset_f[ i ] = -( ubnd_f[ i ] + lbnd_f[ i ] )/( ubnd_f[ i ] - lbnd_f[ i ] ); + } else if( astOK ){ + astError( AST__BADBX, "astInitChebyMap(%s): Input bounding box " + "has zero width on input axis %d.", status, name, i + 1 ); + break; + } + } + } + } + +/* Calculate the scales and offsets that map the supplied output bounding box + onto the range [-1,+1] on each output axis, and store them. */ + if( ncoeff_i > 0 ) { + new->scale_i = (double *) astMalloc( sizeof( double )*nout ); + new->offset_i = (double *) astMalloc( sizeof( double )*nout ); + if( astOK ) { + for( i = 0; i < nout; i++ ) { + if( ubnd_i[ i ] != lbnd_i[ i ] ) { + new->scale_i[ i ] = 2.0/( ubnd_i[ i ] - lbnd_i[ i ] ); + new->offset_i[ i ] = -( ubnd_i[ i ] + lbnd_i[ i ] )/( ubnd_i[ i ] - lbnd_i[ i ] ); + } else if( astOK ){ + astError( AST__BADBX, "astInitChebyMap(%s): Output bounding box " + "has zero width on output axis %d.", status, name, i + 1 ); + break; + } + } + } + } + +/* If an error occurred, clean up by deleting the new ChebyMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new ChebyMap. */ + return new; +} + +AstChebyMap *astLoadChebyMap_( void *mem, size_t size, + AstChebyMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadChebyMap + +* Purpose: +* Load a ChebyMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "chebymap.h" +* AstChebyMap *astLoadChebyMap( void *mem, size_t size, +* AstChebyMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* ChebyMap loader. + +* Description: +* This function is provided to load a new ChebyMap 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 +* ChebyMap 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 ChebyMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the ChebyMap is to be +* loaded. This must be of sufficient size to accommodate the +* ChebyMap data (sizeof(ChebyMap)) 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 ChebyMap (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 ChebyMap 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(AstChebyMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new ChebyMap. If this is NULL, a pointer +* to the (static) virtual function table for the ChebyMap 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 "ChebyMap" is used instead. + +* Returned Value: +* A pointer to the new ChebyMap. + +* 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: */ + AstChebyMap *new; /* Pointer to the new ChebyMap */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + int i; /* Loop index */ + int ngood; /* No. of non-bad values */ + 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 ChebyMap. In this case the + ChebyMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstChebyMap ); + vtab = &class_vtab; + name = "ChebyMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitChebyMapVtab( 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 ChebyMap. */ + new = astLoadPolyMap( mem, size, (AstPolyMapVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Get the number of inputs and outputs for the uninverted Mapping. */ + nin = ( (AstMapping *) new )->nin; + nout = ( (AstMapping *) new )->nout; + +/* Initialise values */ + new->scale_f = NULL; + new->offset_f = NULL; + new->scale_i = NULL; + new->offset_i = 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, "ChebyMap" ); + +/* Is the forward transformation defined? */ + if( ((AstPolyMap *) new)->ncoeff_f ) { + +/* Allocate memory to hold the scales and offsets for the input axes. */ + new->scale_f = astMalloc( sizeof( double )*(size_t) nin ); + new->offset_f = astMalloc( sizeof( double )*(size_t) nin ); + if( astOK ) { + +/* Get the scale factors. */ + ngood = 0; + for( i = 0; i < nin; i++ ){ + (void) sprintf( buff, "fscl%d", i + 1 ); + (new->scale_f)[ i ] = astReadDouble( channel, buff, AST__BAD ); + if( (new->scale_f)[ i ] != AST__BAD ) ngood++; + } + +/* Get the offsets of the bounding box. */ + for( i = 0; i < nin; i++ ){ + (void) sprintf( buff, "foff%d", i + 1 ); + (new->offset_f)[ i ] = astReadDouble( channel, buff, AST__BAD ); + if( (new->offset_f)[ i ] != AST__BAD ) ngood++; + } + +/* The scale and offset values should all be AST__BAD if the transformation + is a standard polynomial. Anull the scale and offset arrays to + indicate this. */ + if( ngood == 0 ) { + new->scale_f = astFree( new->scale_f ); + new->offset_f = astFree( new->offset_f ); + +/* Otherwise, there should be no bad values. */ + } else if( ngood != 2*nin && astOK ) { + astError( AST__OBJIN, "astLoadChebyMap: insufficient scale " + "and offset values for the forward transformation " + "in loaded ChebyMap.", status ); + } + } + } + +/* Is the inverse transformation defined? */ + if( ((AstPolyMap *) new)->ncoeff_i ) { + +/* Allocate memory to hold the scales and offsets for the output axes. */ + new->scale_i = astMalloc( sizeof( double )*(size_t) nout ); + new->offset_i = astMalloc( sizeof( double )*(size_t) nout ); + if( astOK ) { + +/* Get the scale factors. */ + ngood = 0; + for( i = 0; i < nout; i++ ){ + (void) sprintf( buff, "iscl%d", i + 1 ); + (new->scale_i)[ i ] = astReadDouble( channel, buff, AST__BAD ); + if( (new->scale_i)[ i ] != AST__BAD ) ngood++; + } + +/* Get the offsets of the bounding box. */ + for( i = 0; i < nout; i++ ){ + (void) sprintf( buff, "ioff%d", i + 1 ); + (new->offset_i)[ i ] = astReadDouble( channel, buff, AST__BAD ); + if( (new->offset_i)[ i ] != AST__BAD ) ngood++; + } + +/* The scale and offset values should all be AST__BAD if the transformation + is a standard polynomial. Anull the scale and offset arrays to + indicate this. */ + if( ngood == 0 ) { + new->scale_i = astFree( new->scale_i ); + new->offset_i = astFree( new->offset_i ); + +/* Otherwise, there should be no bad values. */ + } else if( ngood != 2*nout && astOK ) { + astError( AST__OBJIN, "astLoadChebyMap: insufficient scale " + "and offset values for the inverse transformation " + "in loaded ChebyMap.", status ); + } + } + } + +/* If an error occurred, clean up by deleting the new ChebyMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new ChebyMap 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 astChebyDomain_( AstChebyMap *this, int forward, double *lbnd, double *ubnd, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,ChebyMap,ChebyDomain))( this, forward, lbnd, ubnd, status ); +} + + diff --git a/ast/chebymap.h b/ast/chebymap.h new file mode 100644 index 0000000..fff75a6 --- /dev/null +++ b/ast/chebymap.h @@ -0,0 +1,234 @@ +#if !defined( CHEBYMAP_INCLUDED ) /* Include this file only once */ +#define CHEBYMAP_INCLUDED +/* +*+ +* Name: +* chebymap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the ChebyMap class. + +* Invocation: +* #include "chebymap.h" + +* Description: +* This include file defines the interface to the ChebyMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. + +* Inheritance: +* The ChebyMap class inherits from the PolyMap class. + +* Copyright: +* Copyright (C) 2017 East Asian Observatory. +* 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: +* 2-MAR-2017 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "polymap.h" /* Polynomial 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. */ +/* ================= */ +/* ChebyMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstChebyMap { + +/* Attributes inherited from the parent class. */ + AstPolyMap polymap; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + int cheby_f; /* Is fwd transformation a Cheby poly? */ + int cheby_i; /* Is inv transformation a Cheby poly? */ + double *scale_f; /* Pointer to array of input axis scales */ + double *offset_f; /* Pointer to array of input axis offsets */ + double *scale_i; /* Pointer to array of input axis scales */ + double *offset_i; /* Pointer to array of input axis offsets */ +} AstChebyMap; + +/* 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 AstChebyMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstPolyMapVtab polymap_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + void (* ChebyDomain)( AstChebyMap *, int, double *, double *, int * ); + +} AstChebyMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstChebyMapGlobals { + AstChebyMapVtab Class_Vtab; + int Class_Init; +} AstChebyMapGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitChebyMapGlobals_( AstChebyMapGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(ChebyMap) /* Check class membership */ +astPROTO_ISA(ChebyMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstChebyMap *astChebyMap_( int, int, int, const double[], int, const double[], + const double[], const double[], const double[], const double[], + const char *, int *, ...); +#else +AstChebyMap *astChebyMapId_( int, int, int, const double[], int, const double[], const double[], const double[], const double[], const double[], const char *, ... )__attribute__((format(printf,11,12))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstChebyMap *astInitChebyMap_( void *, size_t, int, AstChebyMapVtab *, + const char *, int, int, int, const double[], + int, const double[], const double[], const double[], + const double[], const double[], int * ); + +/* Vtab initialiser. */ +void astInitChebyMapVtab_( AstChebyMapVtab *, const char *, int * ); + +/* Loader. */ +AstChebyMap *astLoadChebyMap_( void *, size_t, AstChebyMapVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +void astChebyDomain_( AstChebyMap *, int, double *, double *, int * ); + +# 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 astCheckChebyMap(this) astINVOKE_CHECK(ChebyMap,this,0) +#define astVerifyChebyMap(this) astINVOKE_CHECK(ChebyMap,this,1) + +/* Test class membership. */ +#define astIsAChebyMap(this) astINVOKE_ISA(ChebyMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astChebyMap astINVOKE(F,astChebyMap_) +#else +#define astChebyMap astINVOKE(F,astChebyMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitChebyMap(mem,size,init,vtab,name,nin,nout,ncoeff_f,coeff_f,ncoeff_i,coeff_i,lbnd_f,ubnd_f,lbnd_i,ubnd_i) \ +astINVOKE(O,astInitChebyMap_(mem,size,init,vtab,name,nin,nout,ncoeff_f,coeff_f,ncoeff_i,coeff_i,lbnd_f,ubnd_f,lbnd_i,ubnd_i,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitChebyMapVtab(vtab,name) astINVOKE(V,astInitChebyMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadChebyMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadChebyMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckChebyMap to validate ChebyMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#define astChebyDomain(this,forward,lbnd,ubnd) \ +astINVOKE(V,astChebyDomain_(astCheckChebyMap(this),forward,lbnd,ubnd,STATUS_PTR)) + + +#if defined(astCLASS) /* Protected */ + +#endif +#endif + + + + + diff --git a/ast/circle.c b/ast/circle.c new file mode 100644 index 0000000..c795b98 --- /dev/null +++ b/ast/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/ast/circle.h b/ast/circle.h new file mode 100644 index 0000000..a69f8ea --- /dev/null +++ b/ast/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/ast/cminpack/CopyrightMINPACK.txt b/ast/cminpack/CopyrightMINPACK.txt new file mode 100644 index 0000000..ae7984d --- /dev/null +++ b/ast/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/ast/cminpack/README.md b/ast/cminpack/README.md new file mode 100644 index 0000000..66bbc6f --- /dev/null +++ b/ast/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/ast/cminpack/cminpack.h b/ast/cminpack/cminpack.h new file mode 100644 index 0000000..6d3f757 --- /dev/null +++ b/ast/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/ast/cminpack/cminpackP.h b/ast/cminpack/cminpackP.h new file mode 100644 index 0000000..4e8ba7b --- /dev/null +++ b/ast/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/ast/cminpack/dpmpar.c b/ast/cminpack/dpmpar.c new file mode 100644 index 0000000..81c6fcd --- /dev/null +++ b/ast/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/ast/cminpack/enorm.c b/ast/cminpack/enorm.c new file mode 100644 index 0000000..ad10824 --- /dev/null +++ b/ast/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/ast/cminpack/lmder.c b/ast/cminpack/lmder.c new file mode 100644 index 0000000..7f57428 --- /dev/null +++ b/ast/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/ast/cminpack/lmder1.c b/ast/cminpack/lmder1.c new file mode 100644 index 0000000..581462e --- /dev/null +++ b/ast/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/ast/cminpack/lmpar.c b/ast/cminpack/lmpar.c new file mode 100644 index 0000000..108e687 --- /dev/null +++ b/ast/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/ast/cminpack/qrfac.c b/ast/cminpack/qrfac.c new file mode 100644 index 0000000..1573772 --- /dev/null +++ b/ast/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/ast/cminpack/qrsolv.c b/ast/cminpack/qrsolv.c new file mode 100644 index 0000000..6ab9e98 --- /dev/null +++ b/ast/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/ast/cmpframe.c b/ast/cmpframe.c new file mode 100644 index 0000000..48e971a --- /dev/null +++ b/ast/cmpframe.c @@ -0,0 +1,10846 @@ +/* +*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. +* 11-JAN-2017 (GSB): +* Override astSetDtai, astGetDtai and astClearDtai. +*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_getdtai)( AstFrame *, 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_cleardtai)( AstFrame *, 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_setdtai)( AstFrame *, double, 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 GetDtai( AstFrame *, int * ); +static void ClearDtai( AstFrame *, int * ); +static void SetDtai( 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 ClearDtai( AstFrame *this_frame, int *status ) { +/* +* Name: +* ClearDtai + +* Purpose: +* Clear the value of the Dtai attribute for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void ClearDtai( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astClearDtai method +* inherited from the Frame class). + +* Description: +* This function clears the Dtai 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 Dtai value. */ + (*parent_cleardtai)( this_frame, status ); + +/* Now clear the Dtai attribute in the two component Frames. */ + astClearDtai( this->frame1 ); + astClearDtai( this->frame2 ); +} + +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 GetDtai( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetDtai + +* Purpose: +* Get a value for the Dtai attribute of a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* double GetDtai( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetDtai method +* inherited from the Frame class). + +* Description: +* This function returns a value for the Dtai attribute of a +* CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Dtai 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 Dtai attribute value has been set, invoke the parent method + to obtain it. */ + if ( astTestDtai( this ) ) { + result = (*parent_getdtai)( this_frame, status ); + +/* Otherwise, if the Dtai value is set in the first component Frame, + return it. */ + } else if( astTestDtai( this->frame1 ) ){ + result = astGetDtai( this->frame1 ); + +/* Otherwise, if the Dtai value is set in the second component Frame, + return it. */ + } else if( astTestDtai( this->frame2 ) ){ + result = astGetDtai( this->frame2 ); + +/* Otherwise, return the default Dtai value from the first component + Frame. */ + } else { + result = astGetDtai( this->frame1 ); + } + +/* 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_getdtai = frame->GetDtai; + frame->GetDtai = GetDtai; + + parent_setdtai = frame->SetDtai; + frame->SetDtai = SetDtai; + + parent_cleardtai = frame->ClearDtai; + frame->ClearDtai = ClearDtai; + + 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 SetDtai( AstFrame *this_frame, double val, int *status ) { +/* +* Name: +* SetDtai + +* Purpose: +* Set the value of the Dtai attribute for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void SetDtai( AstFrame *this, double val, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astSetDtai method +* inherited from the Frame class). + +* Description: +* This function sets the Dtai value in the component Frames as +* well as this CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* val +* New Dtai 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 Dtai value. */ + (*parent_setdtai)( this_frame, val, status ); + +/* Now set the Dtai attribute in the two component Frames. */ + astSetDtai( this->frame1, val ); + astSetDtai( this->frame2, val ); +} + +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/ast/cmpframe.h b/ast/cmpframe.h new file mode 100644 index 0000000..f692aa1 --- /dev/null +++ b/ast/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/ast/cmpframe.pdf b/ast/cmpframe.pdf new file mode 100644 index 0000000..8d81909 Binary files /dev/null and b/ast/cmpframe.pdf differ diff --git a/ast/cmpmap.c b/ast/cmpmap.c new file mode 100644 index 0000000..d0210ec --- /dev/null +++ b/ast/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 && astOK ) { + +/* 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/ast/cmpmap.h b/ast/cmpmap.h new file mode 100644 index 0000000..ed305bb --- /dev/null +++ b/ast/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/ast/cmpregion.c b/ast/cmpregion.c new file mode 100644 index 0000000..c3b5b07 --- /dev/null +++ b/ast/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/ast/cmpregion.h b/ast/cmpregion.h new file mode 100644 index 0000000..92d7898 --- /dev/null +++ b/ast/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/ast/complex.pdf b/ast/complex.pdf new file mode 100644 index 0000000..2d16739 Binary files /dev/null and b/ast/complex.pdf differ diff --git a/ast/component.xml b/ast/component.xml new file mode 100644 index 0000000..e133a00 --- /dev/null +++ b/ast/component.xml @@ -0,0 +1,44 @@ + + + + + 8.6.2 + libext/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/ast/component.xml.in b/ast/component.xml.in new file mode 100644 index 0000000..d59bb5a --- /dev/null +++ b/ast/component.xml.in @@ -0,0 +1,44 @@ + + + + + @PACKAGE_VERSION@ + libext/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/ast/configure.ac b/ast/configure.ac new file mode 100644 index 0000000..a923657 --- /dev/null +++ b/ast/configure.ac @@ -0,0 +1,243 @@ +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.6.2],[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) + + +# Decide how to get the name of the curently executing function. +# ------------------------------------------------------------- +AC_DEFUN([CIT_FUNCTIONSTRING], [ + set_function_name=no + + + + AC_MSG_CHECKING([whether C compiler defines __func__]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[]], [[const char* name = __func__;]])], + [AC_MSG_RESULT(yes) + set_function_name=yes + AC_DEFINE([FUNCTION_NAME], [__func__], [Variable holding current function name.])], + [AC_MSG_RESULT(no)]) + if test "$set_function_name" == no; then + AC_MSG_CHECKING([whether C compiler defines __FUNCTION__]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[]], [[const char* name = __FUNCTION__;]])], + [AC_MSG_RESULT(yes) + set_function_name=yes + AC_DEFINE([FUNCTION_NAME], [__FUNCTION__], [Variable holding current function name.])], + [AC_MSG_RESULT(no)]) + fi +]) + +CIT_FUNCTIONSTRING + +# 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/ast/devtools/make_simtest b/ast/devtools/make_simtest new file mode 100644 index 0000000..87412ed --- /dev/null +++ b/ast/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/ast/devtools/simtest.c b/ast/devtools/simtest.c new file mode 100644 index 0000000..e36634c --- /dev/null +++ b/ast/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/ast/doincludes b/ast/doincludes new file mode 100755 index 0000000..656b0ac --- /dev/null +++ b/ast/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/ast/dsbspecframe.c b/ast/dsbspecframe.c new file mode 100644 index 0000000..576f5b9 --- /dev/null +++ b/ast/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", AST__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", AST__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", AST__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/ast/dsbspecframe.h b/ast/dsbspecframe.h new file mode 100644 index 0000000..87617f5 --- /dev/null +++ b/ast/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/ast/dssmap.c b/ast/dssmap.c new file mode 100644 index 0000000..b869d57 --- /dev/null +++ b/ast/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/ast/dssmap.h b/ast/dssmap.h new file mode 100644 index 0000000..b7550de --- /dev/null +++ b/ast/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/ast/ellipse.c b/ast/ellipse.c new file mode 100644 index 0000000..1e23724 --- /dev/null +++ b/ast/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/ast/ellipse.h b/ast/ellipse.h new file mode 100644 index 0000000..cdd46f7 --- /dev/null +++ b/ast/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/ast/ems.h b/ast/ems.h new file mode 100644 index 0000000..5b6fef3 --- /dev/null +++ b/ast/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/ast/erfa.h b/ast/erfa.h new file mode 100644 index 0000000..1f98296 --- /dev/null +++ b/ast/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/ast/erfa/INFO b/ast/erfa/INFO new file mode 100644 index 0000000..0719bcc --- /dev/null +++ b/ast/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/ast/erfa/LICENSE b/ast/erfa/LICENSE new file mode 100644 index 0000000..8b3e92c --- /dev/null +++ b/ast/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/ast/erfa/Makefile.am b/ast/erfa/Makefile.am new file mode 100644 index 0000000..e0dce1b --- /dev/null +++ b/ast/erfa/Makefile.am @@ -0,0 +1,47 @@ +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 eceq06.c ee00a.c ee00.c eect00.c eo06a.c epb2jd.c epj2jd.c epv00.c \ +eqeq94.c ecm06.c ee00b.c ee06a.c eform.c eors.c epb.c epj.c eqec06.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 g2icrs.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 icrs2g.c ir.c jd2cal.c jdcalf.c ld.c \ +ldn.c ldsun.c lteceq.c ltecm.c lteqec.c ltpb.c ltp.c ltpecl.c ltpequ.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/ast/erfa/README.rst b/ast/erfa/README.rst new file mode 100644 index 0000000..418d138 --- /dev/null +++ b/ast/erfa/README.rst @@ -0,0 +1,93 @@ +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.3.0) is based on SOFA version "20160503_a", 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. + +Bugfixes +^^^^^^^^ + +ERFA includes smaller changes that may or may not eventually make it into SOFA, +addressing localized bugs or similar smaller issues: + +* ERFA 1.3.0 and SOFA "20160503_a" + + + There are no differences between ERFA 1.3.0 and SOFA "20160503_a". + +* ERFA 1.2.0 and SOFA "20150209_a" + + + Typos have been corrected in the documentation of atco13 and atio13 (see https://github.com/liberfa/erfa/issues/29). + +Note that issues identified in ERFA should generally also be reported upstream to SOFA at sofa@ukho.gov.uk. + +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/ast/erfa/RELEASE.rst b/ast/erfa/RELEASE.rst new file mode 100644 index 0000000..3726fdc --- /dev/null +++ b/ast/erfa/RELEASE.rst @@ -0,0 +1,180 @@ +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`. Add any new C files or header files added by SOFA + to ``src/Makefile.am``, as appropriate. 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/ast/erfa/a2af.c b/ast/erfa/a2af.c new file mode 100644 index 0000000..0101630 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/a2tf.c b/ast/erfa/a2tf.c new file mode 100644 index 0000000..ac3aa98 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/ab.c b/ast/erfa/ab.c new file mode 100644 index 0000000..5d56656 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/af2a.c b/ast/erfa/af2a.c new file mode 100644 index 0000000..b74dd75 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/anp.c b/ast/erfa/anp.c new file mode 100644 index 0000000..dcd678e --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/anpm.c b/ast/erfa/anpm.c new file mode 100644 index 0000000..bd1e059 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/apcg.c b/ast/erfa/apcg.c new file mode 100644 index 0000000..e49d4ae --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/apcg13.c b/ast/erfa/apcg13.c new file mode 100644 index 0000000..4157785 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/apci.c b/ast/erfa/apci.c new file mode 100644 index 0000000..74dbc3b --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/apci13.c b/ast/erfa/apci13.c new file mode 100644 index 0000000..c041652 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/apco.c b/ast/erfa/apco.c new file mode 100644 index 0000000..1b61ab8 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/apco13.c b/ast/erfa/apco13.c new file mode 100644 index 0000000..cdd4d4f --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/apcs.c b/ast/erfa/apcs.c new file mode 100644 index 0000000..0a34f14 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/apcs13.c b/ast/erfa/apcs13.c new file mode 100644 index 0000000..0eb2d72 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/aper.c b/ast/erfa/aper.c new file mode 100644 index 0000000..372c565 --- /dev/null +++ b/ast/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-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + astrom->eral = theta + astrom->along; + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/aper13.c b/ast/erfa/aper13.c new file mode 100644 index 0000000..e2b019c --- /dev/null +++ b/ast/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-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraAper(eraEra00(ut11,ut12), astrom); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/apio.c b/ast/erfa/apio.c new file mode 100644 index 0000000..ebc5569 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/apio13.c b/ast/erfa/apio13.c new file mode 100644 index 0000000..4544c9e --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/atci13.c b/ast/erfa/atci13.c new file mode 100644 index 0000000..40a3a04 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/atciq.c b/ast/erfa/atciq.c new file mode 100644 index 0000000..68947ba --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/atciqn.c b/ast/erfa/atciqn.c new file mode 100644 index 0000000..0ad89a0 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/atciqz.c b/ast/erfa/atciqz.c new file mode 100644 index 0000000..d141b91 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/atco13.c b/ast/erfa/atco13.c new file mode 100644 index 0000000..eaaabe8 --- /dev/null +++ b/ast/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 CIRS to observed +** +** Copyright (C) 2013-2016, 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-2016, 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/ast/erfa/atic13.c b/ast/erfa/atic13.c new file mode 100644 index 0000000..bd70ce5 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/aticq.c b/ast/erfa/aticq.c new file mode 100644 index 0000000..1e205b8 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/aticqn.c b/ast/erfa/aticqn.c new file mode 100644 index 0000000..d69119e --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/atio13.c b/ast/erfa/atio13.c new file mode 100644 index 0000000..6201776 --- /dev/null +++ b/ast/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 CIRS to observed +** +** Copyright (C) 2013-2016, 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-2016, 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/ast/erfa/atioq.c b/ast/erfa/atioq.c new file mode 100644 index 0000000..d8a1f7b --- /dev/null +++ b/ast/erfa/atioq.c @@ -0,0 +1,243 @@ +#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-2016, 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-2016, 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/ast/erfa/atoc13.c b/ast/erfa/atoc13.c new file mode 100644 index 0000000..8f0932c --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/atoi13.c b/ast/erfa/atoi13.c new file mode 100644 index 0000000..41f2632 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/atoiq.c b/ast/erfa/atoiq.c new file mode 100644 index 0000000..d5501a7 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/bi00.c b/ast/erfa/bi00.c new file mode 100644 index 0000000..9000221 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/bp00.c b/ast/erfa/bp00.c new file mode 100644 index 0000000..dd387ea --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/bp06.c b/ast/erfa/bp06.c new file mode 100644 index 0000000..b5a60c4 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/bpn2xy.c b/ast/erfa/bpn2xy.c new file mode 100644 index 0000000..769bd33 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/c2i00a.c b/ast/erfa/c2i00a.c new file mode 100644 index 0000000..a83b12b --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/c2i00b.c b/ast/erfa/c2i00b.c new file mode 100644 index 0000000..7969a34 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/c2i06a.c b/ast/erfa/c2i06a.c new file mode 100644 index 0000000..9fd6bfe --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/c2ibpn.c b/ast/erfa/c2ibpn.c new file mode 100644 index 0000000..62f34a2 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/c2ixy.c b/ast/erfa/c2ixy.c new file mode 100644 index 0000000..a488f20 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/c2ixys.c b/ast/erfa/c2ixys.c new file mode 100644 index 0000000..1c92687 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/c2s.c b/ast/erfa/c2s.c new file mode 100644 index 0000000..6f31a2b --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/c2t00a.c b/ast/erfa/c2t00a.c new file mode 100644 index 0000000..94439fa --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/c2t00b.c b/ast/erfa/c2t00b.c new file mode 100644 index 0000000..fe19504 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/c2t06a.c b/ast/erfa/c2t06a.c new file mode 100644 index 0000000..3b8b2ff --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/c2tcio.c b/ast/erfa/c2tcio.c new file mode 100644 index 0000000..2bdd004 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/c2teqx.c b/ast/erfa/c2teqx.c new file mode 100644 index 0000000..c86e76c --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/c2tpe.c b/ast/erfa/c2tpe.c new file mode 100644 index 0000000..c6e4bda --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/c2txy.c b/ast/erfa/c2txy.c new file mode 100644 index 0000000..198c7a8 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/cal2jd.c b/ast/erfa/cal2jd.c new file mode 100644 index 0000000..5d4d689 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/cp.c b/ast/erfa/cp.c new file mode 100644 index 0000000..cf3ab90 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/cpv.c b/ast/erfa/cpv.c new file mode 100644 index 0000000..05204f5 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/cr.c b/ast/erfa/cr.c new file mode 100644 index 0000000..721781d --- /dev/null +++ b/ast/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: +** c double[3][3] copy +** +** Called: +** eraCp copy p-vector +** +** Copyright (C) 2013-2016, 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-2016, 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/ast/erfa/d2dtf.c b/ast/erfa/d2dtf.c new file mode 100644 index 0000000..4476970 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/d2tf.c b/ast/erfa/d2tf.c new file mode 100644 index 0000000..3608f7f --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/dat.c b/ast/erfa/dat.c new file mode 100644 index 0000000..c3b1836 --- /dev/null +++ b/ast/erfa/dat.c @@ -0,0 +1,306 @@ +#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: 2016 December 31 : +** : : +** :__________________________________________: +** +** 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) +** -5 = internal error (Note 5) +** +** 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. The "internal error" status refers to a +** case that is impossible but causes some compilers to issue a +** warning. +** +** 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-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Release year for this version of eraDat */ + enum { IYV = 2016}; + +/* 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 }, + { 2015, 7, 36.0 }, + { 2017, 1, 37.0 } + }; + +/* Number of Delta(AT) changes */ + enum { NDAT = (int) (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; + } + +/* Prevent underflow warnings. */ + if (i < 0) return -5; + +/* 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-2016, 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/ast/erfa/dtdb.c b/ast/erfa/dtdb.c new file mode 100644 index 0000000..1ac4f43 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/dtf2d.c b/ast/erfa/dtf2d.c new file mode 100644 index 0000000..70d6b25 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/eceq06.c b/ast/erfa/eceq06.c new file mode 100644 index 0000000..e1ba72d --- /dev/null +++ b/ast/erfa/eceq06.c @@ -0,0 +1,141 @@ +#include "erfa.h" + +void eraEceq06(double date1, double date2, double dl, double db, + double *dr, double *dd) +/* +** - - - - - - - - - - +** e r a E c e q 0 6 +** - - - - - - - - - - +** +** Transformation from ecliptic coordinates (mean equinox and ecliptic +** of date) to ICRS RA,Dec, using the IAU 2006 precession model. +** +** Given: +** date1,date2 double TT as a 2-part Julian date (Note 1) +** dl,db double ecliptic longitude and latitude (radians) +** +** Returned: +** dr,dd double ICRS right ascension and declination (radians) +** +** 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) No assumptions are made about whether the coordinates represent +** starlight and embody astrometric effects such as parallax or +** aberration. +** +** 3) The transformation is approximately that from ecliptic longitude +** and latitude (mean equinox and ecliptic of date) to mean J2000.0 +** right ascension and declination, with only frame bias (always +** less than 25 mas) to disturb this classical picture. +** +** Called: +** eraS2c spherical coordinates to unit vector +** eraEcm06 J2000.0 to ecliptic rotation matrix, IAU 2006 +** eraTrxp product of transpose of r-matrix and p-vector +** eraC2s unit vector to spherical coordinates +** eraAnp normalize angle into range 0 to 2pi +** eraAnpm normalize angle into range +/- pi +** +** Copyright (C) 2013-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rm[3][3], v1[3], v2[3], a, b; + + +/* Spherical to Cartesian. */ + eraS2c(dl, db, v1); + +/* Rotation matrix, ICRS equatorial to ecliptic. */ + eraEcm06(date1, date2, rm); + +/* The transformation from ecliptic to ICRS. */ + eraTrxp(rm, v1, v2); + +/* Cartesian to spherical. */ + eraC2s(v2, &a, &b); + +/* Express in conventional ranges. */ + *dr = eraAnp(a); + *dd = eraAnpm(b); + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/ecm06.c b/ast/erfa/ecm06.c new file mode 100644 index 0000000..ce19ba2 --- /dev/null +++ b/ast/erfa/ecm06.c @@ -0,0 +1,144 @@ +#include "erfa.h" + +void eraEcm06(double date1, double date2, double rm[3][3]) +/* +** - - - - - - - - - +** e r a E c m 0 6 +** - - - - - - - - - +** +** ICRS equatorial to ecliptic rotation matrix, IAU 2006. +** +** Given: +** date1,date2 double TT as a 2-part Julian date (Note 1) +** +** Returned: +** rm double[3][3] ICRS to ecliptic rotation 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. +** +** 1) The matrix is in the sense +** +** E_ep = rm x P_ICRS, +** +** where P_ICRS is a vector with respect to ICRS right ascension +** and declination axes and E_ep is the same vector with respect to +** the (inertial) ecliptic and equinox of date. +** +** 2) P_ICRS is a free vector, merely a direction, typically of unit +** magnitude, and not bound to any particular spatial origin, such +** as the Earth, Sun or SSB. No assumptions are made about whether +** it represents starlight and embodies astrometric effects such as +** parallax or aberration. The transformation is approximately that +** between mean J2000.0 right ascension and declination and ecliptic +** longitude and latitude, with only frame bias (always less than +** 25 mas) to disturb this classical picture. +** +** Called: +** eraObl06 mean obliquity, IAU 2006 +** eraPmat06 PB matrix, IAU 2006 +** eraIr initialize r-matrix to identity +** eraRx rotate around X-axis +** eraRxr product of two r-matrices +** +** Copyright (C) 2013-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double ob, bp[3][3], e[3][3]; + + +/* Obliquity, IAU 2006. */ + ob = eraObl06(date1, date2); + +/* Precession-bias matrix, IAU 2006. */ + eraPmat06(date1, date2, bp); + +/* Equatorial of date to ecliptic matrix. */ + eraIr(e); + eraRx(ob, e); + +/* ICRS to ecliptic coordinates rotation matrix, IAU 2006. */ + eraRxr(e, bp, rm); + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/ee00.c b/ast/erfa/ee00.c new file mode 100644 index 0000000..de1b6b9 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/ee00a.c b/ast/erfa/ee00a.c new file mode 100644 index 0000000..90eade7 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/ee00b.c b/ast/erfa/ee00b.c new file mode 100644 index 0000000..2bed962 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/ee06a.c b/ast/erfa/ee06a.c new file mode 100644 index 0000000..c64a65f --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/eect00.c b/ast/erfa/eect00.c new file mode 100644 index 0000000..b8e4bf3 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/eform.c b/ast/erfa/eform.c new file mode 100644 index 0000000..0982ab4 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/eo06a.c b/ast/erfa/eo06a.c new file mode 100644 index 0000000..6fd27c9 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/eors.c b/ast/erfa/eors.c new file mode 100644 index 0000000..c1b5557 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/epb.c b/ast/erfa/epb.c new file mode 100644 index 0000000..3d06508 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/epb2jd.c b/ast/erfa/epb2jd.c new file mode 100644 index 0000000..4dca8c9 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/epj.c b/ast/erfa/epj.c new file mode 100644 index 0000000..059853d --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/epj2jd.c b/ast/erfa/epj2jd.c new file mode 100644 index 0000000..751eb15 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/epv00.c b/ast/erfa/epv00.c new file mode 100644 index 0000000..4f37b9c --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/eqec06.c b/ast/erfa/eqec06.c new file mode 100644 index 0000000..6a667c9 --- /dev/null +++ b/ast/erfa/eqec06.c @@ -0,0 +1,142 @@ +#include "erfa.h" + +void eraEqec06(double date1, double date2, double dr, double dd, + double *dl, double *db) +/* +** - - - - - - - - - - +** e r a E q e c 0 6 +** - - - - - - - - - - +** +** Transformation from ICRS equatorial coordinates to ecliptic +** coordinates (mean equinox and ecliptic of date) using IAU 2006 +** precession model. +** +** Given: +** date1,date2 double TT as a 2-part Julian date (Note 1) +** dr,dd double ICRS right ascension and declination (radians) +** +** Returned: +** dl,db double ecliptic longitude and latitude (radians) +** +** 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) No assumptions are made about whether the coordinates represent +** starlight and embody astrometric effects such as parallax or +** aberration. +** +** 3) The transformation is approximately that from mean J2000.0 right +** ascension and declination to ecliptic longitude and latitude +** (mean equinox and ecliptic of date), with only frame bias (always +** less than 25 mas) to disturb this classical picture. +** +** Called: +** eraS2c spherical coordinates to unit vector +** eraEcm06 J2000.0 to ecliptic rotation matrix, IAU 2006 +** eraRxp product of r-matrix and p-vector +** eraC2s unit vector to spherical coordinates +** eraAnp normalize angle into range 0 to 2pi +** eraAnpm normalize angle into range +/- pi +** +** Copyright (C) 2013-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rm[3][3], v1[3], v2[3], a, b; + + +/* Spherical to Cartesian. */ + eraS2c(dr, dd, v1); + +/* Rotation matrix, ICRS equatorial to ecliptic. */ + eraEcm06(date1, date2, rm); + +/* The transformation from ICRS to ecliptic. */ + eraRxp(rm, v1, v2); + +/* Cartesian to spherical. */ + eraC2s(v2, &a, &b); + +/* Express in conventional ranges. */ + *dl = eraAnp(a); + *db = eraAnpm(b); + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/eqeq94.c b/ast/erfa/eqeq94.c new file mode 100644 index 0000000..00076b1 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/era00.c b/ast/erfa/era00.c new file mode 100644 index 0000000..01e06f5 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/erfa.h b/ast/erfa/erfa.h new file mode 100644 index 0000000..c22f470 --- /dev/null +++ b/ast/erfa/erfa.h @@ -0,0 +1,517 @@ +#ifndef ERFAHDEF +#define ERFAHDEF + +/* +** - - - - - - - +** e r f a . h +** - - - - - - - +** +** Prototype function declarations for ERFA library. +** +** Copyright (C) 2013-2016, 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 tc, 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 tc, 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 tc, 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 tc, 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 tc, 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 tc, 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 pob[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 height, double xp, + double yp, double sp, double theta, double pv[2][3]); +void eraRefco(double phpa, double tc, 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 eraLtp(double epj, double rp[3][3]); +void eraLtpb(double epj, double rpb[3][3]); +void eraLtpecl(double epj, double vec[3]); +void eraLtpequ(double epj, double veq[3]); +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 date01, double date02, + double date11, double date12, + 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 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/EclipticCoordinates */ +void eraEceq06(double date1, double date2, double dl, double db, + double *dr, double *dd); +void eraEcm06(double date1, double date2, double rm[3][3]); +void eraEqec06(double date1, double date2, double dr, double dd, + double *dl, double *db); +void eraLteceq(double epj, double dl, double db, double *dr, double *dd); +void eraLtecm(double epj, double rm[3][3]); +void eraLteqec(double epj, double dr, double dd, double *dl, double *db); + +/* Astronomy/GalacticCoordinates */ +void eraG2icrs(double dl, double db, double *dr, double *dd); +void eraIcrs2g(double dr, double dd, double *dl, double *db); + +/* 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]); + +/* 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-2016, 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/ast/erfa/erfam.h b/ast/erfa/erfam.h new file mode 100644 index 0000000..750c4c2 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/fad03.c b/ast/erfa/fad03.c new file mode 100644 index 0000000..421f937 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/fae03.c b/ast/erfa/fae03.c new file mode 100644 index 0000000..e40b1c9 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/faf03.c b/ast/erfa/faf03.c new file mode 100644 index 0000000..aa7850a --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/faju03.c b/ast/erfa/faju03.c new file mode 100644 index 0000000..9b9298f --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/fal03.c b/ast/erfa/fal03.c new file mode 100644 index 0000000..1d80d20 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/falp03.c b/ast/erfa/falp03.c new file mode 100644 index 0000000..4080809 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/fama03.c b/ast/erfa/fama03.c new file mode 100644 index 0000000..21bff75 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/fame03.c b/ast/erfa/fame03.c new file mode 100644 index 0000000..c28ab0b --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/fane03.c b/ast/erfa/fane03.c new file mode 100644 index 0000000..682e7bf --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/faom03.c b/ast/erfa/faom03.c new file mode 100644 index 0000000..ff6b3e9 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/fapa03.c b/ast/erfa/fapa03.c new file mode 100644 index 0000000..aae2af4 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/fasa03.c b/ast/erfa/fasa03.c new file mode 100644 index 0000000..d5e8cbe --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/faur03.c b/ast/erfa/faur03.c new file mode 100644 index 0000000..cce0f83 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/fave03.c b/ast/erfa/fave03.c new file mode 100644 index 0000000..b91678c --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/fk52h.c b/ast/erfa/fk52h.c new file mode 100644 index 0000000..e169cbe --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/fk5hip.c b/ast/erfa/fk5hip.c new file mode 100644 index 0000000..5a05d4d --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/fk5hz.c b/ast/erfa/fk5hz.c new file mode 100644 index 0000000..4134f7c --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/fw2m.c b/ast/erfa/fw2m.c new file mode 100644 index 0000000..d1b73d2 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/fw2xy.c b/ast/erfa/fw2xy.c new file mode 100644 index 0000000..b4e0693 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/g2icrs.c b/ast/erfa/g2icrs.c new file mode 100644 index 0000000..4612726 --- /dev/null +++ b/ast/erfa/g2icrs.c @@ -0,0 +1,170 @@ +#include "erfa.h" + +void eraG2icrs ( double dl, double db, double *dr, double *dd ) +/* +** - - - - - - - - - - +** e r a G 2 i c r s +** - - - - - - - - - - +** +** Transformation from Galactic Coordinates to ICRS. +** +** Given: +** dl double galactic longitude (radians) +** db double galactic latitude (radians) +** +** Returned: +** dr double ICRS right ascension (radians) +** dd double ICRS declination (radians) +** +** Notes: +** +** 1) The IAU 1958 system of Galactic coordinates was defined with +** respect to the now obsolete reference system FK4 B1950.0. When +** interpreting the system in a modern context, several factors have +** to be taken into account: +** +** . The inclusion in FK4 positions of the E-terms of aberration. +** +** . The distortion of the FK4 proper motion system by differential +** Galactic rotation. +** +** . The use of the B1950.0 equinox rather than the now-standard +** J2000.0. +** +** . The frame bias between ICRS and the J2000.0 mean place system. +** +** The Hipparcos Catalogue (Perryman & ESA 1997) provides a rotation +** matrix that transforms directly between ICRS and Galactic +** coordinates with the above factors taken into account. The +** matrix is derived from three angles, namely the ICRS coordinates +** of the Galactic pole and the longitude of the ascending node of +** the galactic equator on the ICRS equator. They are given in +** degrees to five decimal places and for canonical purposes are +** regarded as exact. In the Hipparcos Catalogue the matrix +** elements are given to 10 decimal places (about 20 microarcsec). +** In the present ERFA function the matrix elements have been +** recomputed from the canonical three angles and are given to 30 +** decimal places. +** +** 2) The inverse transformation is performed by the function eraIcrs2g. +** +** Called: +** eraAnp normalize angle into range 0 to 2pi +** eraAnpm normalize angle into range +/- pi +** eraS2c spherical coordinates to unit vector +** eraTrxp product of transpose of r-matrix and p-vector +** eraC2s p-vector to spherical +** +** Reference: +** Perryman M.A.C. & ESA, 1997, ESA SP-1200, The Hipparcos and Tycho +** catalogues. Astrometric and photometric star catalogues +** derived from the ESA Hipparcos Space Astrometry Mission. ESA +** Publications Division, Noordwijk, Netherlands. +** +** Copyright (C) 2013-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double v1[3], v2[3]; + +/* +** L2,B2 system of galactic coordinates in the form presented in the +** Hipparcos Catalogue. In degrees: +** +** P = 192.85948 right ascension of the Galactic north pole in ICRS +** Q = 27.12825 declination of the Galactic north pole in ICRS +** R = 32.93192 longitude of the ascending node of the Galactic +** plane on the ICRS equator +** +** ICRS to galactic rotation matrix, obtained by computing +** R_3(-R) R_1(pi/2-Q) R_3(pi/2+P) to the full precision shown: +*/ + double r[3][3] = { { -0.054875560416215368492398900454, + -0.873437090234885048760383168409, + -0.483835015548713226831774175116 }, + { +0.494109427875583673525222371358, + -0.444829629960011178146614061616, + +0.746982244497218890527388004556 }, + { -0.867666149019004701181616534570, + -0.198076373431201528180486091412, + +0.455983776175066922272100478348 } }; + + +/* Spherical to Cartesian. */ + eraS2c(dl, db, v1); + +/* Galactic to ICRS. */ + eraTrxp(r, v1, v2); + +/* Cartesian to spherical. */ + eraC2s(v2, dr, dd); + +/* Express in conventional ranges. */ + *dr = eraAnp(*dr); + *dd = eraAnpm(*dd); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/gc2gd.c b/ast/erfa/gc2gd.c new file mode 100644 index 0000000..385e7d5 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/gc2gde.c b/ast/erfa/gc2gde.c new file mode 100644 index 0000000..43ed61d --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/gd2gc.c b/ast/erfa/gd2gc.c new file mode 100644 index 0000000..383c928 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/gd2gce.c b/ast/erfa/gd2gce.c new file mode 100644 index 0000000..52b9f0e --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/gmst00.c b/ast/erfa/gmst00.c new file mode 100644 index 0000000..6b598bb --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/gmst06.c b/ast/erfa/gmst06.c new file mode 100644 index 0000000..2fcb1a5 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/gmst82.c b/ast/erfa/gmst82.c new file mode 100644 index 0000000..9484b66 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/gst00a.c b/ast/erfa/gst00a.c new file mode 100644 index 0000000..e185576 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/gst00b.c b/ast/erfa/gst00b.c new file mode 100644 index 0000000..cfb5293 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/gst06.c b/ast/erfa/gst06.c new file mode 100644 index 0000000..c4c85df --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/gst06a.c b/ast/erfa/gst06a.c new file mode 100644 index 0000000..e80fd83 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/gst94.c b/ast/erfa/gst94.c new file mode 100644 index 0000000..112cf61 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/h2fk5.c b/ast/erfa/h2fk5.c new file mode 100644 index 0000000..ad7e9a6 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/hfk5z.c b/ast/erfa/hfk5z.c new file mode 100644 index 0000000..8824e96 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/icrs2g.c b/ast/erfa/icrs2g.c new file mode 100644 index 0000000..cf59118 --- /dev/null +++ b/ast/erfa/icrs2g.c @@ -0,0 +1,170 @@ +#include "erfa.h" + +void eraIcrs2g ( double dr, double dd, double *dl, double *db ) +/* +** - - - - - - - - - - +** e r a I c r s 2 g +** - - - - - - - - - - +** +** Transformation from ICRS to Galactic Coordinates. +** +** Given: +** dr double ICRS right ascension (radians) +** dd double ICRS declination (radians) +** +** Returned: +** dl double galactic longitude (radians) +** db double galactic latitude (radians) +** +** Notes: +** +** 1) The IAU 1958 system of Galactic coordinates was defined with +** respect to the now obsolete reference system FK4 B1950.0. When +** interpreting the system in a modern context, several factors have +** to be taken into account: +** +** . The inclusion in FK4 positions of the E-terms of aberration. +** +** . The distortion of the FK4 proper motion system by differential +** Galactic rotation. +** +** . The use of the B1950.0 equinox rather than the now-standard +** J2000.0. +** +** . The frame bias between ICRS and the J2000.0 mean place system. +** +** The Hipparcos Catalogue (Perryman & ESA 1997) provides a rotation +** matrix that transforms directly between ICRS and Galactic +** coordinates with the above factors taken into account. The +** matrix is derived from three angles, namely the ICRS coordinates +** of the Galactic pole and the longitude of the ascending node of +** the galactic equator on the ICRS equator. They are given in +** degrees to five decimal places and for canonical purposes are +** regarded as exact. In the Hipparcos Catalogue the matrix +** elements are given to 10 decimal places (about 20 microarcsec). +** In the present ERFA function the matrix elements have been +** recomputed from the canonical three angles and are given to 30 +** decimal places. +** +** 2) The inverse transformation is performed by the function eraG2icrs. +** +** Called: +** eraAnp normalize angle into range 0 to 2pi +** eraAnpm normalize angle into range +/- pi +** eraS2c spherical coordinates to unit vector +** eraRxp product of r-matrix and p-vector +** eraC2s p-vector to spherical +** +** Reference: +** Perryman M.A.C. & ESA, 1997, ESA SP-1200, The Hipparcos and Tycho +** catalogues. Astrometric and photometric star catalogues +** derived from the ESA Hipparcos Space Astrometry Mission. ESA +** Publications Division, Noordwijk, Netherlands. +** +** Copyright (C) 2013-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double v1[3], v2[3]; + +/* +** L2,B2 system of galactic coordinates in the form presented in the +** Hipparcos Catalogue. In degrees: +** +** P = 192.85948 right ascension of the Galactic north pole in ICRS +** Q = 27.12825 declination of the Galactic north pole in ICRS +** R = 32.93192 longitude of the ascending node of the Galactic +** plane on the ICRS equator +** +** ICRS to galactic rotation matrix, obtained by computing +** R_3(-R) R_1(pi/2-Q) R_3(pi/2+P) to the full precision shown: +*/ + double r[3][3] = { { -0.054875560416215368492398900454, + -0.873437090234885048760383168409, + -0.483835015548713226831774175116 }, + { +0.494109427875583673525222371358, + -0.444829629960011178146614061616, + +0.746982244497218890527388004556 }, + { -0.867666149019004701181616534570, + -0.198076373431201528180486091412, + +0.455983776175066922272100478348 } }; + + +/* Spherical to Cartesian. */ + eraS2c(dr, dd, v1); + +/* ICRS to Galactic. */ + eraRxp(r, v1, v2); + +/* Cartesian to spherical. */ + eraC2s(v2, dl, db); + +/* Express in conventional ranges. */ + *dl = eraAnp(*dl); + *db = eraAnpm(*db); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/ir.c b/ast/erfa/ir.c new file mode 100644 index 0000000..8c7cd1e --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/jd2cal.c b/ast/erfa/jd2cal.c new file mode 100644 index 0000000..abe2b8f --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/jdcalf.c b/ast/erfa/jdcalf.c new file mode 100644 index 0000000..85efb68 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/ld.c b/ast/erfa/ld.c new file mode 100644 index 0000000..c1569fd --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/ldn.c b/ast/erfa/ldn.c new file mode 100644 index 0000000..5ad0a50 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/ldsun.c b/ast/erfa/ldsun.c new file mode 100644 index 0000000..62ed117 --- /dev/null +++ b/ast/erfa/ldsun.c @@ -0,0 +1,115 @@ +#include "erfa.h" + +void eraLdsun(double p[3], double e[3], double em, double p1[3]) +/* +** - - - - - - - - - +** e r a L d s u n +** - - - - - - - - - +** +** Deflection of starlight by the Sun. +** +** Given: +** p double[3] direction from observer to star (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 star (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 a threshold value, falling to +** zero deflection for zero separation. The chosen threshold value +** is within the solar limb for all solar-system applications, and +** is about 5 arcminutes for the case of a terrestrial observer. +** +** 3) The arguments p and p1 can be the same array. +** +** Called: +** eraLd light deflection by a solar-system body +** +** Copyright (C) 2013-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double em2, dlim; + + +/* Deflection limiter (smaller for distant observers). */ + em2 = em*em; + if ( em2 < 1.0 ) em2 = 1.0; + dlim = 1e-6 / (em2 > 1.0 ? em2 : 1.0); + +/* Apply the deflection. */ + eraLd(1.0, p, p, e, em, dlim, p1); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/lteceq.c b/ast/erfa/lteceq.c new file mode 100644 index 0000000..9ba725d --- /dev/null +++ b/ast/erfa/lteceq.c @@ -0,0 +1,138 @@ +#include "erfa.h" + +void eraLteceq(double epj, double dl, double db, double *dr, double *dd) +/* +** - - - - - - - - - - +** e r a L t e c e q +** - - - - - - - - - - +** +** Transformation from ecliptic coordinates (mean equinox and ecliptic +** of date) to ICRS RA,Dec, using a long-term precession model. +** +** Given: +** epj double Julian epoch (TT) +** dl,db double ecliptic longitude and latitude (radians) +** +** Returned: +** dr,dd double ICRS right ascension and declination (radians) +** +** 1) No assumptions are made about whether the coordinates represent +** starlight and embody astrometric effects such as parallax or +** aberration. +** +** 2) The transformation is approximately that from ecliptic longitude +** and latitude (mean equinox and ecliptic of date) to mean J2000.0 +** right ascension and declination, with only frame bias (always +** less than 25 mas) to disturb this classical picture. +** +** 3) The Vondrak et al. (2011, 2012) 400 millennia precession model +** agrees with the IAU 2006 precession at J2000.0 and stays within +** 100 microarcseconds during the 20th and 21st centuries. It is +** accurate to a few arcseconds throughout the historical period, +** worsening to a few tenths of a degree at the end of the +** +/- 200,000 year time span. +** +** Called: +** eraS2c spherical coordinates to unit vector +** eraLtecm J2000.0 to ecliptic rotation matrix, long term +** eraTrxp product of transpose of r-matrix and p-vector +** eraC2s unit vector to spherical coordinates +** eraAnp normalize angle into range 0 to 2pi +** eraAnpm normalize angle into range +/- pi +** +** References: +** +** Vondrak, J., Capitaine, N. and Wallace, P., 2011, New precession +** expressions, valid for long time intervals, Astron.Astrophys. 534, +** A22 +** +** Vondrak, J., Capitaine, N. and Wallace, P., 2012, New precession +** expressions, valid for long time intervals (Corrigendum), +** Astron.Astrophys. 541, C1 +** +** Copyright (C) 2013-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rm[3][3], v1[3], v2[3], a, b; + + +/* Spherical to Cartesian. */ + eraS2c(dl, db, v1); + +/* Rotation matrix, ICRS equatorial to ecliptic. */ + eraLtecm(epj, rm); + +/* The transformation from ecliptic to ICRS. */ + eraTrxp(rm, v1, v2); + +/* Cartesian to spherical. */ + eraC2s(v2, &a, &b); + +/* Express in conventional ranges. */ + *dr = eraAnp(a); + *dd = eraAnpm(b); + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/ltecm.c b/ast/erfa/ltecm.c new file mode 100644 index 0000000..c0bf28b --- /dev/null +++ b/ast/erfa/ltecm.c @@ -0,0 +1,157 @@ +#include "erfa.h" + +void eraLtecm(double epj, double rm[3][3]) +/* +** - - - - - - - - - +** e r a L t e c m +** - - - - - - - - - +** +** ICRS equatorial to ecliptic rotation matrix, long-term. +** +** Given: +** epj double Julian epoch (TT) +** +** Returned: +** rm double[3][3] ICRS to ecliptic rotation matrix +** +** Notes: +** +** 1) The matrix is in the sense +** +** E_ep = rm x P_ICRS, +** +** where P_ICRS is a vector with respect to ICRS right ascension +** and declination axes and E_ep is the same vector with respect to +** the (inertial) ecliptic and equinox of epoch epj. +** +** 2) P_ICRS is a free vector, merely a direction, typically of unit +** magnitude, and not bound to any particular spatial origin, such +** as the Earth, Sun or SSB. No assumptions are made about whether +** it represents starlight and embodies astrometric effects such as +** parallax or aberration. The transformation is approximately that +** between mean J2000.0 right ascension and declination and ecliptic +** longitude and latitude, with only frame bias (always less than +** 25 mas) to disturb this classical picture. +** +** 3) The Vondrak et al. (2011, 2012) 400 millennia precession model +** agrees with the IAU 2006 precession at J2000.0 and stays within +** 100 microarcseconds during the 20th and 21st centuries. It is +** accurate to a few arcseconds throughout the historical period, +** worsening to a few tenths of a degree at the end of the +** +/- 200,000 year time span. +** +** Called: +** eraLtpequ equator pole, long term +** eraLtpecl ecliptic pole, long term +** eraPxp vector product +** eraPn normalize vector +** +** References: +** +** Vondrak, J., Capitaine, N. and Wallace, P., 2011, New precession +** expressions, valid for long time intervals, Astron.Astrophys. 534, +** A22 +** +** Vondrak, J., Capitaine, N. and Wallace, P., 2012, New precession +** expressions, valid for long time intervals (Corrigendum), +** Astron.Astrophys. 541, C1 +** +** Copyright (C) 2013-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Frame bias (IERS Conventions 2010, Eqs. 5.21 and 5.33) */ + const double dx = -0.016617 * ERFA_DAS2R, + de = -0.0068192 * ERFA_DAS2R, + dr = -0.0146 * ERFA_DAS2R; + + double p[3], z[3], w[3], s, x[3], y[3]; + + +/* Equator pole. */ + eraLtpequ(epj, p); + +/* Ecliptic pole (bottom row of equatorial to ecliptic matrix). */ + eraLtpecl(epj, z); + +/* Equinox (top row of matrix). */ + eraPxp(p, z, w); + eraPn(w, &s, x); + +/* Middle row of matrix. */ + eraPxp(z, x, y); + +/* Combine with frame bias. */ + rm[0][0] = x[0] - x[1]*dr + x[2]*dx; + rm[0][1] = x[0]*dr + x[1] + x[2]*de; + rm[0][2] = - x[0]*dx - x[1]*de + x[2]; + rm[1][0] = y[0] - y[1]*dr + y[2]*dx; + rm[1][1] = y[0]*dr + y[1] + y[2]*de; + rm[1][2] = - y[0]*dx - y[1]*de + y[2]; + rm[2][0] = z[0] - z[1]*dr + z[2]*dx; + rm[2][1] = z[0]*dr + z[1] + z[2]*de; + rm[2][2] = - z[0]*dx - z[1]*de + z[2]; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/lteqec.c b/ast/erfa/lteqec.c new file mode 100644 index 0000000..a93ec0f --- /dev/null +++ b/ast/erfa/lteqec.c @@ -0,0 +1,139 @@ +#include "erfa.h" + +void eraLteqec(double epj, double dr, double dd, double *dl, double *db) +/* +** - - - - - - - - - - +** e r a L t e q e c +** - - - - - - - - - - +** +** Transformation from ICRS equatorial coordinates to ecliptic +** coordinates (mean equinox and ecliptic of date) using a long-term +** precession model. +** +** Given: +** epj double Julian epoch (TT) +** dr,dd double ICRS right ascension and declination (radians) +** +** Returned: +** dl,db double ecliptic longitude and latitude (radians) +** +** 1) No assumptions are made about whether the coordinates represent +** starlight and embody astrometric effects such as parallax or +** aberration. +** +** 2) The transformation is approximately that from mean J2000.0 right +** ascension and declination to ecliptic longitude and latitude +** (mean equinox and ecliptic of date), with only frame bias (always +** less than 25 mas) to disturb this classical picture. +** +** 3) The Vondrak et al. (2011, 2012) 400 millennia precession model +** agrees with the IAU 2006 precession at J2000.0 and stays within +** 100 microarcseconds during the 20th and 21st centuries. It is +** accurate to a few arcseconds throughout the historical period, +** worsening to a few tenths of a degree at the end of the +** +/- 200,000 year time span. +** +** Called: +** eraS2c spherical coordinates to unit vector +** eraLtecm J2000.0 to ecliptic rotation matrix, long term +** eraRxp product of r-matrix and p-vector +** eraC2s unit vector to spherical coordinates +** eraAnp normalize angle into range 0 to 2pi +** eraAnpm normalize angle into range +/- pi +** +** References: +** +** Vondrak, J., Capitaine, N. and Wallace, P., 2011, New precession +** expressions, valid for long time intervals, Astron.Astrophys. 534, +** A22 +** +** Vondrak, J., Capitaine, N. and Wallace, P., 2012, New precession +** expressions, valid for long time intervals (Corrigendum), +** Astron.Astrophys. 541, C1 +** +** Copyright (C) 2013-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rm[3][3], v1[3], v2[3], a, b; + + +/* Spherical to Cartesian. */ + eraS2c(dr, dd, v1); + +/* Rotation matrix, ICRS equatorial to ecliptic. */ + eraLtecm(epj, rm); + +/* The transformation from ICRS to ecliptic. */ + eraRxp(rm, v1, v2); + +/* Cartesian to spherical. */ + eraC2s(v2, &a, &b); + +/* Express in conventional ranges. */ + *dl = eraAnp(a); + *db = eraAnpm(b); + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/ltp.c b/ast/erfa/ltp.c new file mode 100644 index 0000000..2b8249c --- /dev/null +++ b/ast/erfa/ltp.c @@ -0,0 +1,140 @@ +#include "erfa.h" + +void eraLtp(double epj, double rp[3][3]) +/* +** - - - - - - - +** e r a L t p +** - - - - - - - +** +** Long-term precession matrix. +** +** Given: +** epj double Julian epoch (TT) +** +** Returned: +** rp double[3][3] precession matrix, J2000.0 to date +** +** Notes: +** +** 1) The matrix is in the sense +** +** P_date = rp x P_J2000, +** +** where P_J2000 is a vector with respect to the J2000.0 mean +** equator and equinox and P_date is the same vector with respect to +** the equator and equinox of epoch epj. +** +** 2) The Vondrak et al. (2011, 2012) 400 millennia precession model +** agrees with the IAU 2006 precession at J2000.0 and stays within +** 100 microarcseconds during the 20th and 21st centuries. It is +** accurate to a few arcseconds throughout the historical period, +** worsening to a few tenths of a degree at the end of the +** +/- 200,000 year time span. +** +** Called: +** eraLtpequ equator pole, long term +** eraLtpecl ecliptic pole, long term +** eraPxp vector product +** eraPn normalize vector +** +** References: +** +** Vondrak, J., Capitaine, N. and Wallace, P., 2011, New precession +** expressions, valid for long time intervals, Astron.Astrophys. 534, +** A22 +** +** Vondrak, J., Capitaine, N. and Wallace, P., 2012, New precession +** expressions, valid for long time intervals (Corrigendum), +** Astron.Astrophys. 541, C1 +** +** Copyright (C) 2013-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int i; + double peqr[3], pecl[3], v[3], w, eqx[3]; + + +/* Equator pole (bottom row of matrix). */ + eraLtpequ(epj, peqr); + +/* Ecliptic pole. */ + eraLtpecl(epj, pecl); + +/* Equinox (top row of matrix). */ + eraPxp(peqr, pecl, v); + eraPn(v, &w, eqx); + +/* Middle row of matrix. */ + eraPxp(peqr, eqx, v); + +/* Assemble the matrix. */ + for ( i = 0; i < 3; i++ ) { + rp[0][i] = eqx[i]; + rp[1][i] = v[i]; + rp[2][i] = peqr[i]; + } + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/ltpb.c b/ast/erfa/ltpb.c new file mode 100644 index 0000000..6818f19 --- /dev/null +++ b/ast/erfa/ltpb.c @@ -0,0 +1,133 @@ +#include "erfa.h" + +void eraLtpb(double epj, double rpb[3][3]) +/* +** - - - - - - - - +** e r a L t p b +** - - - - - - - - +** +** Long-term precession matrix, including ICRS frame bias. +** +** Given: +** epj double Julian epoch (TT) +** +** Returned: +** rpb double[3][3] precession-bias matrix, J2000.0 to date +** +** Notes: +** +** 1) The matrix is in the sense +** +** P_date = rpb x P_ICRS, +** +** where P_ICRS is a vector in the Geocentric Celestial Reference +** System, and P_date is the vector with respect to the Celestial +** Intermediate Reference System at that date but with nutation +** neglected. +** +** 2) A first order frame bias formulation is used, of sub- +** microarcsecond accuracy compared with a full 3D rotation. +** +** 3) The Vondrak et al. (2011, 2012) 400 millennia precession model +** agrees with the IAU 2006 precession at J2000.0 and stays within +** 100 microarcseconds during the 20th and 21st centuries. It is +** accurate to a few arcseconds throughout the historical period, +** worsening to a few tenths of a degree at the end of the +** +/- 200,000 year time span. +** +** References: +** +** Vondrak, J., Capitaine, N. and Wallace, P., 2011, New precession +** expressions, valid for long time intervals, Astron.Astrophys. 534, +** A22 +** +** Vondrak, J., Capitaine, N. and Wallace, P., 2012, New precession +** expressions, valid for long time intervals (Corrigendum), +** Astron.Astrophys. 541, C1 +** +** Copyright (C) 2013-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Frame bias (IERS Conventions 2010, Eqs. 5.21 and 5.33) */ + const double dx = -0.016617 * ERFA_DAS2R, + de = -0.0068192 * ERFA_DAS2R, + dr = -0.0146 * ERFA_DAS2R; + + int i; + double rp[3][3]; + + +/* Precession matrix. */ + eraLtp(epj, rp); + +/* Apply the bias. */ + for ( i = 0; i < 3; i++ ) { + rpb[i][0] = rp[i][0] - rp[i][1]*dr + rp[i][2]*dx; + rpb[i][1] = rp[i][0]*dr + rp[i][1] + rp[i][2]*de; + rpb[i][2] = -rp[i][0]*dx - rp[i][1]*de + rp[i][2]; + } + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/ltpecl.c b/ast/erfa/ltpecl.c new file mode 100644 index 0000000..feb3d55 --- /dev/null +++ b/ast/erfa/ltpecl.c @@ -0,0 +1,177 @@ +#include "erfa.h" + +void eraLtpecl(double epj, double vec[3]) +/* +** - - - - - - - - - - +** e r a L t p e c l +** - - - - - - - - - - +** +** Long-term precession of the ecliptic. +** +** Given: +** epj double Julian epoch (TT) +** +** Returned: +** vec double[3] ecliptic pole unit vector +** +** Notes: +** +** 1) The returned vector is with respect to the J2000.0 mean equator +** and equinox. +** +** 2) The Vondrak et al. (2011, 2012) 400 millennia precession model +** agrees with the IAU 2006 precession at J2000.0 and stays within +** 100 microarcseconds during the 20th and 21st centuries. It is +** accurate to a few arcseconds throughout the historical period, +** worsening to a few tenths of a degree at the end of the +** +/- 200,000 year time span. +** +** References: +** +** Vondrak, J., Capitaine, N. and Wallace, P., 2011, New precession +** expressions, valid for long time intervals, Astron.Astrophys. 534, +** A22 +** +** Vondrak, J., Capitaine, N. and Wallace, P., 2012, New precession +** expressions, valid for long time intervals (Corrigendum), +** Astron.Astrophys. 541, C1 +** +** Copyright (C) 2013-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Obliquity at J2000.0 (radians). */ + static const double eps0 = 84381.406 * ERFA_DAS2R; + +/* Polynomial coefficients */ + enum { NPOL = 4 }; + static const double pqpol[2][NPOL] = { + { 5851.607687, + -0.1189000, + -0.00028913, + 0.000000101}, + {-1600.886300, + 1.1689818, + -0.00000020, + -0.000000437} + }; + +/* Periodic coefficients */ + static const double pqper[][5] = { + { 708.15,-5486.751211,-684.661560, 667.666730,-5523.863691}, + {2309.00, -17.127623,2446.283880,-2354.886252, -549.747450}, + {1620.00, -617.517403, 399.671049, -428.152441, -310.998056}, + { 492.20, 413.442940,-356.652376, 376.202861, 421.535876}, + {1183.00, 78.614193,-186.387003, 184.778874, -36.776172}, + { 622.00, -180.732815,-316.800070, 335.321713, -145.278396}, + { 882.00, -87.676083, 198.296701, -185.138669, -34.744450}, + { 547.00, 46.140315, 101.135679, -120.972830, 22.885731} + }; + static const int NPER = (int) ( sizeof pqper / 5 / sizeof (double) ); + +/* Miscellaneous */ + int i; + double t, p, q, w, a, s, c; + + +/* Centuries since J2000. */ + t = ( epj - 2000.0 ) / 100.0; + +/* Initialize P_A and Q_A accumulators. */ + p = 0.0; + q = 0.0; + +/* Periodic terms. */ + w = ERFA_D2PI*t; + for ( i = 0; i < NPER; i++ ) { + a = w/pqper[i][0]; + s = sin(a); + c = cos(a); + p += c*pqper[i][1] + s*pqper[i][3]; + q += c*pqper[i][2] + s*pqper[i][4]; + } + +/* Polynomial terms. */ + w = 1.0; + for ( i = 0; i < NPOL; i++ ) { + p += pqpol[0][i]*w; + q += pqpol[1][i]*w; + w *= t; + } + +/* P_A and Q_A (radians). */ + p *= ERFA_DAS2R; + q *= ERFA_DAS2R; + +/* Form the ecliptic pole vector. */ + w = 1.0 - p*p - q*q; + w = w < 0.0 ? 0.0 : sqrt(w); + s = sin(eps0); + c = cos(eps0); + vec[0] = p; + vec[1] = - q*c - w*s; + vec[2] = - q*s + w*c; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/ltpequ.c b/ast/erfa/ltpequ.c new file mode 100644 index 0000000..354ce32 --- /dev/null +++ b/ast/erfa/ltpequ.c @@ -0,0 +1,177 @@ +#include "erfa.h" + +void eraLtpequ(double epj, double veq[3]) +/* +** - - - - - - - - - - +** e r a L t p e q u +** - - - - - - - - - - +** +** Long-term precession of the equator. +** +** Given: +** epj double Julian epoch (TT) +** +** Returned: +** veq double[3] equator pole unit vector +** +** Notes: +** +** 1) The returned vector is with respect to the J2000.0 mean equator +** and equinox. +** +** 2) The Vondrak et al. (2011, 2012) 400 millennia precession model +** agrees with the IAU 2006 precession at J2000.0 and stays within +** 100 microarcseconds during the 20th and 21st centuries. It is +** accurate to a few arcseconds throughout the historical period, +** worsening to a few tenths of a degree at the end of the +** +/- 200,000 year time span. +** +** References: +** +** Vondrak, J., Capitaine, N. and Wallace, P., 2011, New precession +** expressions, valid for long time intervals, Astron.Astrophys. 534, +** A22 +** +** Vondrak, J., Capitaine, N. and Wallace, P., 2012, New precession +** expressions, valid for long time intervals (Corrigendum), +** Astron.Astrophys. 541, C1 +** +** Copyright (C) 2013-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Polynomial coefficients */ + enum { NPOL = 4 }; + static const double xypol[2][NPOL] = { + { 5453.282155, + 0.4252841, + -0.00037173, + -0.000000152}, + {-73750.930350, + -0.7675452, + -0.00018725, + 0.000000231} + }; + +/* Periodic coefficients */ + static const double xyper[][5] = { + { 256.75, -819.940624,75004.344875,81491.287984, 1558.515853}, + { 708.15,-8444.676815, 624.033993, 787.163481, 7774.939698}, + { 274.20, 2600.009459, 1251.136893, 1251.296102,-2219.534038}, + { 241.45, 2755.175630,-1102.212834,-1257.950837,-2523.969396}, + {2309.00, -167.659835,-2660.664980,-2966.799730, 247.850422}, + { 492.20, 871.855056, 699.291817, 639.744522, -846.485643}, + { 396.10, 44.769698, 153.167220, 131.600209,-1393.124055}, + { 288.90, -512.313065, -950.865637, -445.040117, 368.526116}, + { 231.10, -819.415595, 499.754645, 584.522874, 749.045012}, + {1610.00, -538.071099, -145.188210, -89.756563, 444.704518}, + { 620.00, -189.793622, 558.116553, 524.429630, 235.934465}, + { 157.87, -402.922932, -23.923029, -13.549067, 374.049623}, + { 220.30, 179.516345, -165.405086, -210.157124, -171.330180}, + {1200.00, -9.814756, 9.344131, -44.919798, -22.899655} + }; + static const int NPER = (int) ( sizeof xyper / 5 / sizeof (double) ); + +/* Miscellaneous */ + int i; + double t, x, y, w, a, s, c; + + +/* Centuries since J2000. */ + t = ( epj - 2000.0 ) / 100.0; + +/* Initialize X and Y accumulators. */ + x = 0.0; + y = 0.0; + +/* Periodic terms. */ + w = ERFA_D2PI * t; + for ( i = 0; i < NPER; i++ ) { + a = w / xyper[i][0]; + s = sin(a); + c = cos(a); + x += c*xyper[i][1] + s*xyper[i][3]; + y += c*xyper[i][2] + s*xyper[i][4]; + } + +/* Polynomial terms. */ + w = 1.0; + for ( i = 0; i < NPOL; i++ ) { + x += xypol[0][i]*w; + y += xypol[1][i]*w; + w *= t; + } + +/* X and Y (direction cosines). */ + x *= ERFA_DAS2R; + y *= ERFA_DAS2R; + +/* Form the equator pole vector. */ + veq[0] = x; + veq[1] = y; + w = 1.0 - x*x - y*y; + veq[2] = w < 0.0 ? 0.0 : sqrt(w); + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/num00a.c b/ast/erfa/num00a.c new file mode 100644 index 0000000..24964e3 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/num00b.c b/ast/erfa/num00b.c new file mode 100644 index 0000000..f006257 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/num06a.c b/ast/erfa/num06a.c new file mode 100644 index 0000000..a27d23a --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/numat.c b/ast/erfa/numat.c new file mode 100644 index 0000000..43a3b00 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/nut00a.c b/ast/erfa/nut00a.c new file mode 100644 index 0000000..4c4e89a --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/nut00b.c b/ast/erfa/nut00b.c new file mode 100644 index 0000000..775613f --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/nut06a.c b/ast/erfa/nut06a.c new file mode 100644 index 0000000..c4d945e --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/nut80.c b/ast/erfa/nut80.c new file mode 100644 index 0000000..5ffb23c --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/nutm80.c b/ast/erfa/nutm80.c new file mode 100644 index 0000000..c526da2 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/obl06.c b/ast/erfa/obl06.c new file mode 100644 index 0000000..9b93626 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/obl80.c b/ast/erfa/obl80.c new file mode 100644 index 0000000..1b5e288 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/p06e.c b/ast/erfa/p06e.c new file mode 100644 index 0000000..24f9f2b --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/p2pv.c b/ast/erfa/p2pv.c new file mode 100644 index 0000000..b5d2081 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/p2s.c b/ast/erfa/p2s.c new file mode 100644 index 0000000..14da5f0 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pap.c b/ast/erfa/pap.c new file mode 100644 index 0000000..afd8651 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pas.c b/ast/erfa/pas.c new file mode 100644 index 0000000..52b3264 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pb06.c b/ast/erfa/pb06.c new file mode 100644 index 0000000..7b14279 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pdp.c b/ast/erfa/pdp.c new file mode 100644 index 0000000..72f1cc0 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pfw06.c b/ast/erfa/pfw06.c new file mode 100644 index 0000000..b510f0a --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/plan94.c b/ast/erfa/plan94.c new file mode 100644 index 0000000..ef878dd --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pm.c b/ast/erfa/pm.c new file mode 100644 index 0000000..6b08fa7 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pmat00.c b/ast/erfa/pmat00.c new file mode 100644 index 0000000..e25315a --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pmat06.c b/ast/erfa/pmat06.c new file mode 100644 index 0000000..364211a --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pmat76.c b/ast/erfa/pmat76.c new file mode 100644 index 0000000..c38e6c4 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pmp.c b/ast/erfa/pmp.c new file mode 100644 index 0000000..e11c12e --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pmpx.c b/ast/erfa/pmpx.c new file mode 100644 index 0000000..034e8f0 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pmsafe.c b/ast/erfa/pmsafe.c new file mode 100644 index 0000000..02e5824 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pn.c b/ast/erfa/pn.c new file mode 100644 index 0000000..7bdebbb --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pn00.c b/ast/erfa/pn00.c new file mode 100644 index 0000000..f088649 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pn00a.c b/ast/erfa/pn00a.c new file mode 100644 index 0000000..3e47f0e --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pn00b.c b/ast/erfa/pn00b.c new file mode 100644 index 0000000..d93376f --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pn06.c b/ast/erfa/pn06.c new file mode 100644 index 0000000..63cf117 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pn06a.c b/ast/erfa/pn06a.c new file mode 100644 index 0000000..69948a3 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pnm00a.c b/ast/erfa/pnm00a.c new file mode 100644 index 0000000..9e70aa9 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pnm00b.c b/ast/erfa/pnm00b.c new file mode 100644 index 0000000..5989b85 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pnm06a.c b/ast/erfa/pnm06a.c new file mode 100644 index 0000000..f587721 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pnm80.c b/ast/erfa/pnm80.c new file mode 100644 index 0000000..a62f937 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pom00.c b/ast/erfa/pom00.c new file mode 100644 index 0000000..0f61496 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/ppp.c b/ast/erfa/ppp.c new file mode 100644 index 0000000..26cb957 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/ppsp.c b/ast/erfa/ppsp.c new file mode 100644 index 0000000..c58cbcb --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pr00.c b/ast/erfa/pr00.c new file mode 100644 index 0000000..0cdf7d3 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/prec76.c b/ast/erfa/prec76.c new file mode 100644 index 0000000..4c47985 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pv2p.c b/ast/erfa/pv2p.c new file mode 100644 index 0000000..b6ea164 --- /dev/null +++ b/ast/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-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraCp(pv[0], p); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/pv2s.c b/ast/erfa/pv2s.c new file mode 100644 index 0000000..58d86a9 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pvdpv.c b/ast/erfa/pvdpv.c new file mode 100644 index 0000000..103c308 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pvm.c b/ast/erfa/pvm.c new file mode 100644 index 0000000..fcb94c5 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pvmpv.c b/ast/erfa/pvmpv.c new file mode 100644 index 0000000..dda99ee --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pvppv.c b/ast/erfa/pvppv.c new file mode 100644 index 0000000..19c2875 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pvstar.c b/ast/erfa/pvstar.c new file mode 100644 index 0000000..3a4e00b --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pvtob.c b/ast/erfa/pvtob.c new file mode 100644 index 0000000..943a63c --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pvu.c b/ast/erfa/pvu.c new file mode 100644 index 0000000..34d9db2 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pvup.c b/ast/erfa/pvup.c new file mode 100644 index 0000000..7f50e6c --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pvxpv.c b/ast/erfa/pvxpv.c new file mode 100644 index 0000000..0d87479 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/pxp.c b/ast/erfa/pxp.c new file mode 100644 index 0000000..5b5a98b --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/refco.c b/ast/erfa/refco.c new file mode 100644 index 0000000..918d990 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/rm2v.c b/ast/erfa/rm2v.c new file mode 100644 index 0000000..2de16ce --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/rv2m.c b/ast/erfa/rv2m.c new file mode 100644 index 0000000..9fe3c4b --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/rx.c b/ast/erfa/rx.c new file mode 100644 index 0000000..df080d5 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/rxp.c b/ast/erfa/rxp.c new file mode 100644 index 0000000..f1dbe82 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/rxpv.c b/ast/erfa/rxpv.c new file mode 100644 index 0000000..f7bb53d --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/rxr.c b/ast/erfa/rxr.c new file mode 100644 index 0000000..ff0485b --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/ry.c b/ast/erfa/ry.c new file mode 100644 index 0000000..193d7a9 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/rz.c b/ast/erfa/rz.c new file mode 100644 index 0000000..58a26e7 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/s00.c b/ast/erfa/s00.c new file mode 100644 index 0000000..82d0a5f --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/s00a.c b/ast/erfa/s00a.c new file mode 100644 index 0000000..a65bb30 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/s00b.c b/ast/erfa/s00b.c new file mode 100644 index 0000000..370f95b --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/s06.c b/ast/erfa/s06.c new file mode 100644 index 0000000..21546c5 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/s06a.c b/ast/erfa/s06a.c new file mode 100644 index 0000000..575c3dd --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/s2c.c b/ast/erfa/s2c.c new file mode 100644 index 0000000..067cec1 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/s2p.c b/ast/erfa/s2p.c new file mode 100644 index 0000000..a9b02bb --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/s2pv.c b/ast/erfa/s2pv.c new file mode 100644 index 0000000..df6e1c7 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/s2xpv.c b/ast/erfa/s2xpv.c new file mode 100644 index 0000000..9b554da --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/sepp.c b/ast/erfa/sepp.c new file mode 100644 index 0000000..98f2671 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/seps.c b/ast/erfa/seps.c new file mode 100644 index 0000000..f97fc58 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/sp00.c b/ast/erfa/sp00.c new file mode 100644 index 0000000..7c57abd --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/starpm.c b/ast/erfa/starpm.c new file mode 100644 index 0000000..69b7708 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/starpv.c b/ast/erfa/starpv.c new file mode 100644 index 0000000..77173ed --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/sxp.c b/ast/erfa/sxp.c new file mode 100644 index 0000000..3bd311f --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/sxpv.c b/ast/erfa/sxpv.c new file mode 100644 index 0000000..6b497cf --- /dev/null +++ b/ast/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-2016, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraS2xpv(s, s, pv, spv); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2016, 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/ast/erfa/t_erfa_c.c b/ast/erfa/t_erfa_c.c new file mode 100644 index 0000000..e32ce1e --- /dev/null +++ b/ast/erfa/t_erfa_c.c @@ -0,0 +1,9742 @@ +#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: 2016 July 11 +** +*/ + +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: 2016 April 21 +*/ +{ + double a, f; /* absolute and fractional error */ + + + a = val - valok; + if (a != 0.0 && fabs(a) > fabs(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: 2016 July 11 +*/ +{ + 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); + + j = eraDat(2017, 9, 1, 0.0, &deltat); + + vvd(deltat, 37.0, 0.0, "eraDat", "d3", status); + viv(j, 0, "eraDat", "j3", 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_eceq06(int *status) +/* +** - - - - - +** t _ e c e q 0 6 +** - - - - - +** +** Test eraEceq06 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEceq06, vvd +** +** This revision: 2016 March 12 +*/ +{ + double date1, date2, dl, db, dr, dd; + + + date1 = 2456165.5; + date2 = 0.401182685; + dl = 5.1; + db = -0.9; + + eraEceq06(date1, date2, dl, db, &dr, &dd); + + vvd(dr, 5.533459733613627767, 1e-14, "eraEceq06", "dr", status); + vvd(dd, -1.246542932554480576, 1e-14, "eraEceq06", "dd", status); + +} + +static void t_ecm06(int *status) +/* +** - - - - - - - - +** t _ e c m 0 6 +** - - - - - - - - +** +** Test eraEcm06 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEcm06, vvd +** +** This revision: 2016 March 12 +*/ +{ + double date1, date2, rm[3][3]; + + + date1 = 2456165.5; + date2 = 0.401182685; + + eraEcm06(date1, date2, rm); + + vvd(rm[0][0], 0.9999952427708701137, 1e-14, + "eraEcm06", "rm11", status); + vvd(rm[0][1], -0.2829062057663042347e-2, 1e-14, + "eraEcm06", "rm12", status); + vvd(rm[0][2], -0.1229163741100017629e-2, 1e-14, + "eraEcm06", "rm13", status); + vvd(rm[1][0], 0.3084546876908653562e-2, 1e-14, + "eraEcm06", "rm21", status); + vvd(rm[1][1], 0.9174891871550392514, 1e-14, + "eraEcm06", "rm22", status); + vvd(rm[1][2], 0.3977487611849338124, 1e-14, + "eraEcm06", "rm23", status); + vvd(rm[2][0], 0.2488512951527405928e-5, 1e-14, + "eraEcm06", "rm31", status); + vvd(rm[2][1], -0.3977506604161195467, 1e-14, + "eraEcm06", "rm32", status); + vvd(rm[2][2], 0.9174935488232863071, 1e-14, + "eraEcm06", "rm33", 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: 2016 March 12 +*/ +{ + 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.3352810664747480720e-2, 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.3352810681182318935e-2, 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.3352779454167504862e-2, 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_eqec06(int *status) +/* +** - - - - - - - - - +** t _ e q e c 0 6 +** - - - - - - - - - +** +** Test eraEqec06 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEqec06, vvd +** +** This revision: 2016 March 12 +*/ +{ + double date1, date2, dr, dd, dl, db; + + + date1 = 1234.5; + date2 = 2440000.5; + dr = 1.234; + dd = 0.987; + + eraEqec06(date1, date2, dr, dd, &dl, &db); + + vvd(dl, 1.342509918994654619, 1e-14, "eraEqec06", "dl", status); + vvd(db, 0.5926215259704608132, 1e-14, "eraEqec06", "db", 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_g2icrs(int *status) +/* +** - - - - - - - - - +** t _ g 2 i c r s +** - - - - - - - - - +** +** Test eraG2icrs function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraG2icrs, vvd +** +** This revision: 2015 January 30 +*/ +{ + double dl, db, dr, dd; + + + dl = 5.5850536063818546461558105; + db = -0.7853981633974483096156608; + eraG2icrs (dl, db, &dr, &dd); + vvd(dr, 5.9338074302227188048671, 1e-14, "eraG2icrs", "R", status); + vvd(dd, -1.1784870613579944551541, 1e-14, "eraG2icrs", "D", 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: 2016 March 12 +*/ +{ + 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.9827937232473290680, 1e-14, "eraGc2gd", "e1", status); + vvd(p, 0.97160184819075459, 1e-14, "eraGc2gd", "p1", status); + vvd(h, 331.4172461426059892, 1e-8, "eraGc2gd", "h1", status); + + j = eraGc2gd(ERFA_GRS80, xyz, &e, &p, &h); + + viv(j, 0, "eraGc2gd", "j2", status); + vvd(e, 0.9827937232473290680, 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.9827937232473290680, 1e-14, "eraGc2gd", "e3", status); + vvd(p, 0.9716018181101511937, 1e-14, "eraGc2gd", "p3", status); + vvd(h, 333.2770726130318123, 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: 2016 March 12 +*/ +{ + 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.9827937232473290680, 1e-14, "eraGc2gde", "e", status); + vvd(p, 0.9716018377570411532, 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: 2016 March 12 +*/ +{ + 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", "1/1", status); + vvd(xyz[1], 233011.67223479203, 1e-7, "eraGd2gc", "2/1", status); + vvd(xyz[2], -3040909.4706983363, 1e-7, "eraGd2gc", "3/1", status); + + j = eraGd2gc(ERFA_GRS80, e, p, h, xyz); + + viv(j, 0, "eraGd2gc", "j2", status); + vvd(xyz[0], -5599000.5577260984, 1e-7, "eraGd2gc", "1/2", status); + vvd(xyz[1], 233011.6722356702949, 1e-7, "eraGd2gc", "2/2", status); + vvd(xyz[2], -3040909.4706095476, 1e-7, "eraGd2gc", "3/2", status); + + j = eraGd2gc(ERFA_WGS72, e, p, h, xyz); + + viv(j, 0, "eraGd2gc", "j3", status); + vvd(xyz[0], -5598998.7626301490, 1e-7, "eraGd2gc", "1/3", status); + vvd(xyz[1], 233011.5975297822211, 1e-7, "eraGd2gc", "2/3", status); + vvd(xyz[2], -3040908.6861467111, 1e-7, "eraGd2gc", "3/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: 2016 March 12 +*/ +{ + 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", "1", status); + vvd(xyz[1], 233011.6351463057189, 1e-7, "eraGd2gce", "2", status); + vvd(xyz[2], -3040909.0517314132, 1e-7, "eraGd2gce", "3", 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_icrs2g(int *status) +/* +** - - - - - - - - - +** t _ i c r s 2 g +** - - - - - - - - - +** +** Test eraIcrs2g function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraIcrs2g, vvd +** +** This revision: 2015 January 30 +*/ +{ + double dr, dd, dl, db; + + dr = 5.9338074302227188048671087; + dd = -1.1784870613579944551540570; + eraIcrs2g (dr, dd, &dl, &db); + vvd(dl, 5.5850536063818546461558, 1e-14, "eraIcrs2g", "L", status); + vvd(db, -0.7853981633974483096157, 1e-14, "eraIcrs2g", "B", 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_lteceq(int *status) +/* +** - - - - - - - - - +** t _ l t e c e q +** - - - - - - - - - +** +** Test eraLteceq function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraLteceq, vvd +** +** This revision: 2016 March 12 +*/ +{ + double epj, dl, db, dr, dd; + + + epj = 2500.0; + dl = 1.5; + db = 0.6; + + eraLteceq(epj, dl, db, &dr, &dd); + + vvd(dr, 1.275156021861921167, 1e-14, "eraLteceq", "dr", status); + vvd(dd, 0.9966573543519204791, 1e-14, "eraLteceq", "dd", status); + +} + +static void t_ltecm(int *status) +/* +** - - - - - - - - +** t _ l t e c m +** - - - - - - - - +** +** Test eraLtecm function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraLtecm, vvd +** +** This revision: 2016 March 12 +*/ +{ + double epj, rm[3][3]; + + + epj = -3000.0; + + eraLtecm(epj, rm); + + vvd(rm[0][0], 0.3564105644859788825, 1e-14, + "eraLtecm", "rm11", status); + vvd(rm[0][1], 0.8530575738617682284, 1e-14, + "eraLtecm", "rm12", status); + vvd(rm[0][2], 0.3811355207795060435, 1e-14, + "eraLtecm", "rm13", status); + vvd(rm[1][0], -0.9343283469640709942, 1e-14, + "eraLtecm", "rm21", status); + vvd(rm[1][1], 0.3247830597681745976, 1e-14, + "eraLtecm", "rm22", status); + vvd(rm[1][2], 0.1467872751535940865, 1e-14, + "eraLtecm", "rm23", status); + vvd(rm[2][0], 0.1431636191201167793e-2, 1e-14, + "eraLtecm", "rm31", status); + vvd(rm[2][1], -0.4084222566960599342, 1e-14, + "eraLtecm", "rm32", status); + vvd(rm[2][2], 0.9127919865189030899, 1e-14, + "eraLtecm", "rm33", status); + +} + +static void t_lteqec(int *status) +/* +** - - - - - - - - - +** t _ l t e q e c +** - - - - - - - - - +** +** Test eraLteqec function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraLteqec, vvd +** +** This revision: 2016 March 12 +*/ +{ + double epj, dr, dd, dl, db; + + + epj = -1500.0; + dr = 1.234; + dd = 0.987; + + eraLteqec(epj, dr, dd, &dl, &db); + + vvd(dl, 0.5039483649047114859, 1e-14, "eraLteqec", "dl", status); + vvd(db, 0.5848534459726224882, 1e-14, "eraLteqec", "db", status); + +} + +static void t_ltp(int *status) +/* +** - - - - - - +** t _ l t p +** - - - - - - +** +** Test eraLtp function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraLtp, vvd +** +** This revision: 2016 March 12 +*/ +{ + double epj, rp[3][3]; + + + epj = 1666.666; + + eraLtp(epj, rp); + + vvd(rp[0][0], 0.9967044141159213819, 1e-14, + "eraLtp", "rp11", status); + vvd(rp[0][1], 0.7437801893193210840e-1, 1e-14, + "eraLtp", "rp12", status); + vvd(rp[0][2], 0.3237624409345603401e-1, 1e-14, + "eraLtp", "rp13", status); + vvd(rp[1][0], -0.7437802731819618167e-1, 1e-14, + "eraLtp", "rp21", status); + vvd(rp[1][1], 0.9972293894454533070, 1e-14, + "eraLtp", "rp22", status); + vvd(rp[1][2], -0.1205768842723593346e-2, 1e-14, + "eraLtp", "rp23", status); + vvd(rp[2][0], -0.3237622482766575399e-1, 1e-14, + "eraLtp", "rp31", status); + vvd(rp[2][1], -0.1206286039697609008e-2, 1e-14, + "eraLtp", "rp32", status); + vvd(rp[2][2], 0.9994750246704010914, 1e-14, + "eraLtp", "rp33", status); + +} + +static void t_ltpb(int *status) +/* +** - - - - - - - +** t _ l t p b +** - - - - - - - +** +** Test eraLtpb function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraLtpb, vvd +** +** This revision: 2016 March 12 +*/ +{ + double epj, rpb[3][3]; + + + epj = 1666.666; + + eraLtpb(epj, rpb); + + vvd(rpb[0][0], 0.9967044167723271851, 1e-14, + "eraLtpb", "rpb11", status); + vvd(rpb[0][1], 0.7437794731203340345e-1, 1e-14, + "eraLtpb", "rpb12", status); + vvd(rpb[0][2], 0.3237632684841625547e-1, 1e-14, + "eraLtpb", "rpb13", status); + vvd(rpb[1][0], -0.7437795663437177152e-1, 1e-14, + "eraLtpb", "rpb21", status); + vvd(rpb[1][1], 0.9972293947500013666, 1e-14, + "eraLtpb", "rpb22", status); + vvd(rpb[1][2], -0.1205741865911243235e-2, 1e-14, + "eraLtpb", "rpb23", status); + vvd(rpb[2][0], -0.3237630543224664992e-1, 1e-14, + "eraLtpb", "rpb31", status); + vvd(rpb[2][1], -0.1206316791076485295e-2, 1e-14, + "eraLtpb", "rpb32", status); + vvd(rpb[2][2], 0.9994750220222438819, 1e-14, + "eraLtpb", "rpb33", status); + +} + +static void t_ltpecl(int *status) +/* +** - - - - - - - - - +** t _ l t p e c l +** - - - - - - - - - +** +** Test eraLtpecl function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraLtpecl, vvd +** +** This revision: 2016 March 12 +*/ +{ + double epj, vec[3]; + + + epj = -1500.0; + + eraLtpecl(epj, vec); + + vvd(vec[0], 0.4768625676477096525e-3, 1e-14, + "eraLtpecl", "vec1", status); + vvd(vec[1], -0.4052259533091875112, 1e-14, + "eraLtpecl", "vec2", status); + vvd(vec[2], 0.9142164401096448012, 1e-14, + "eraLtpecl", "vec3", status); + +} + +static void t_ltpequ(int *status) +/* +** - - - - - - - - - +** t _ l t p e q u +** - - - - - - - - - +** +** Test eraLtpequ function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraLtpequ, vvd +** +** This revision: 2016 March 12 +*/ +{ + double epj, veq[3]; + + + epj = -2500.0; + + eraLtpequ(epj, veq); + + vvd(veq[0], -0.3586652560237326659, 1e-14, + "eraLtpequ", "veq1", status); + vvd(veq[1], -0.1996978910771128475, 1e-14, + "eraLtpequ", "veq2", status); + vvd(veq[2], 0.9118552442250819624, 1e-14, + "eraLtpequ", "veq3", 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: 2016 March 12 +*/ +{ + 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_eceq06(&status); + t_ecm06(&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_eqec06(&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_g2icrs(&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_icrs2g(&status); + t_ir(&status); + t_jd2cal(&status); + t_jdcalf(&status); + t_ld(&status); + t_ldn(&status); + t_ldsun(&status); + t_lteceq(&status); + t_ltecm(&status); + t_lteqec(&status); + t_ltp(&status); + t_ltpb(&status); + t_ltpecl(&status); + t_ltpequ(&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-2016, 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/ast/erfa/taitt.c b/ast/erfa/taitt.c new file mode 100644 index 0000000..087399e --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/taiut1.c b/ast/erfa/taiut1.c new file mode 100644 index 0000000..4a9a485 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/taiutc.c b/ast/erfa/taiutc.c new file mode 100644 index 0000000..ce3a177 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/tcbtdb.c b/ast/erfa/tcbtdb.c new file mode 100644 index 0000000..007af2e --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/tcgtt.c b/ast/erfa/tcgtt.c new file mode 100644 index 0000000..0e463b4 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/tdbtcb.c b/ast/erfa/tdbtcb.c new file mode 100644 index 0000000..c12f807 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/tdbtt.c b/ast/erfa/tdbtt.c new file mode 100644 index 0000000..879ffe5 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/tf2a.c b/ast/erfa/tf2a.c new file mode 100644 index 0000000..678a33d --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/tf2d.c b/ast/erfa/tf2d.c new file mode 100644 index 0000000..3936bfc --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/tr.c b/ast/erfa/tr.c new file mode 100644 index 0000000..d49dc27 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/trxp.c b/ast/erfa/trxp.c new file mode 100644 index 0000000..eaba6f1 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/trxpv.c b/ast/erfa/trxpv.c new file mode 100644 index 0000000..ce6e5ae --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/tttai.c b/ast/erfa/tttai.c new file mode 100644 index 0000000..0392aee --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/tttcg.c b/ast/erfa/tttcg.c new file mode 100644 index 0000000..a0367dc --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/tttdb.c b/ast/erfa/tttdb.c new file mode 100644 index 0000000..2d59625 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/ttut1.c b/ast/erfa/ttut1.c new file mode 100644 index 0000000..b225f3a --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/ut1tai.c b/ast/erfa/ut1tai.c new file mode 100644 index 0000000..34a87c6 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/ut1tt.c b/ast/erfa/ut1tt.c new file mode 100644 index 0000000..56964e0 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/ut1utc.c b/ast/erfa/ut1utc.c new file mode 100644 index 0000000..6ec1b44 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/utctai.c b/ast/erfa/utctai.c new file mode 100644 index 0000000..2863867 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/utcut1.c b/ast/erfa/utcut1.c new file mode 100644 index 0000000..1ab4e94 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/xy06.c b/ast/erfa/xy06.c new file mode 100644 index 0000000..67079b4 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/xys00a.c b/ast/erfa/xys00a.c new file mode 100644 index 0000000..41a0723 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/xys00b.c b/ast/erfa/xys00b.c new file mode 100644 index 0000000..d2ebda7 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/xys06a.c b/ast/erfa/xys06a.c new file mode 100644 index 0000000..e116e15 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/zp.c b/ast/erfa/zp.c new file mode 100644 index 0000000..77a8e59 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/zpv.c b/ast/erfa/zpv.c new file mode 100644 index 0000000..ca176be --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa/zr.c b/ast/erfa/zr.c new file mode 100644 index 0000000..3a07ff8 --- /dev/null +++ b/ast/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-2016, 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-2016, 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/ast/erfa2ast.h b/ast/erfa2ast.h new file mode 100644 index 0000000..b19e0fc --- /dev/null +++ b/ast/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/ast/erfam.h b/ast/erfam.h new file mode 100644 index 0000000..5190b3e --- /dev/null +++ b/ast/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/ast/err.h b/ast/err.h new file mode 100644 index 0000000..9448d86 --- /dev/null +++ b/ast/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/ast/err_drama.c b/ast/err_drama.c new file mode 100644 index 0000000..643afda --- /dev/null +++ b/ast/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/ast/err_ems.c b/ast/err_ems.c new file mode 100644 index 0000000..63bcab6 --- /dev/null +++ b/ast/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/ast/err_null.c b/ast/err_null.c new file mode 100644 index 0000000..72ca9ef --- /dev/null +++ b/ast/err_null.c @@ -0,0 +1,111 @@ +/* +* 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 the astPutErr +* function defined within this module. You can then either link +* your program against the resulting library, or you can register +* the re-implemented astPutErr function at run-time using the +* astSetPutErr function defined in file error.c. + +* 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/ast/error.c b/ast/error.c new file mode 100644 index 0000000..a28bfff --- /dev/null +++ b/ast/error.c @@ -0,0 +1,1359 @@ +/* +* 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. Internally, AST +* indicates an error has occurred by calling function astError. This +* in turn delivers an apprioriate error message to the user by +* calling a function, astPutErr. The default version of astPutErr +* that comes with AST simply writes the message to standard output, +* but astPutErr can be re-implemented if required to deliver the +* message to some external underlying error system. The +* re-implemented function can either be linked into your application +* in place of the default version at build-time (see the options in +* the ast_link script), or registered at run-time using function +* astSetPutErr (defined within this module). See the file err_null.c +* included in the AST source distribution for details of how to +* re-implement astPutErr. +* +* 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) 2017 East Asian Observatory. +* 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. +* 28-FEB-2017 (DSB): +* Added facility for specifying the error handling function, +* astPutErr, at run-time via new function astSetPutErr, rather +* than at link-time. +* 17-OCT-2017 (DSB): +* Added astGetAt. +*/ + +/* 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 puterr astGLOBAL(Error,PutErr) +#define puterr_wrapper astGLOBAL(Error,PutErr_Wrapper) +#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 LOCK_MUTEX1 pthread_mutex_lock( &mutex1 ) +#define UNLOCK_MUTEX1 pthread_mutex_unlock( &mutex1 ) + +/* Define the initial values for the global data for this module. */ +#define GLOBAL_inits \ + globals->Reporting = 1; \ + globals->PutErr = NULL; \ + globals->PutErr_Wrapper = NULL; \ + 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? */ + +/* Function pointers */ +static AstPutErrFun puterr = NULL; /* Pointer to registered error handler */ +static AstPutErrFunWrapper puterr_wrapper = NULL; /* Pointer to + PutError wrapper */ + +/* Un-reported message stack */ +static char *message_stack[ AST__ERROR_MSTACK_SIZE ]; +static int mstack_size = 0; + +#define LOCK_MUTEX1 +#define UNLOCK_MUTEX1 + +#endif + + +/* Function prototypes. */ +/* ==================== */ +static void PutErr( int, const char * ); +static void CPutErrWrapper( AstPutErrFun, int, const char * ); +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 ) { + PutErr( astStatus, " " ); + for( j = 1; j < np; j++ ) { + sprintf( buf, "%d: %s", j, strings[j] ); + PutErr( astStatus, buf ); + } + free( strings ); + PutErr( astStatus, " " ); + +/* If not succesful, issue a warning. */ + } else { + PutErr( astStatus, "Cannot convert backtrace addresses into formatted strings" ); + } + +#else + PutErr( 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 ) PutErr( 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 ) { + PutErr( 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 ) { + PutErr( 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 ) { + PutErr( 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 ) { + PutErr( 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 astGetAt_( const char **routine, const char **file, int *line ){ +/* +*+ +* Name: +* astGetAt + +* Purpose: +* Return the current routine, file and line number context. + +* Type: +* Protected function. + +* Synopsis: +* #include "error.h" +* void astGetAt( const char **routine, const char **file, int *line ) + +* Description: +* This function returns pointers to two strings containing the +* names of a routine and a file, together with an integer line +* number. These values will have been stored previously by calling +* function astAt. Null values are returned if astAt has not been +* called. + +* Parameters: +* routine +* Address of a pointer to a null terminated C string containing +* a routine name (the string will reside in static memory). The +* pointer will be set to NULL on exit if no routine name has been +* specified usiung astAt. +* file +* Address of a pointer to a null terminated C string containing +* a file name (the string will reside in static memory). The +* pointer will be set to NULL on exit if no file name has been +* specified usiung astAt. +* line +* Address of an int in which to stopre the line number in the file. +* A line number of zero is returned if no line number has been +* stored using astAt. + +* Notes: +* - This function attempts to execute even if the global error status +* is set. +*- +*/ + +/* 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); + +/* Return the stored values */ + *routine = current_routine; + *file = current_file; + *line = current_line; +} + +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 +} + +static void CPutErrWrapper( AstPutErrFun fun, int status_value, + const char *message ) { +/* +* +* Name: +* CPutErrWrapper + +* Purpose: +* A wrapper to call a astPutErr error handling function written in C. + +* Type: +* Private function. + +* Synopsis: +* #include "error.h" +* void CPutErrWrapper( AstPutErrFun fun, int status_value, +* const char *message ) + +* Description: +* This function calls the supplied astPutErr function to deliver +* an error message, assuming the supplied function is written in C. + +* Parameters: +* fun +* Pointer to the user-supplied astPutErr function. It is called +* using C calling conventions +* 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. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* 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_MUTEX1; + +/* Invoke the astPutErr function registered using astSetPutErr. */ + if( fun ) ( *fun )( status_value, message ); + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX1; +} + +static void PutErr( int status_value, const char *message ) { +/* +* +* Name: +* PutErr + +* Purpose: +* Call the astPutErr error handling function. + +* Type: +* Private function. + +* Synopsis: +* #include "error.h" +* void PutErr( int status_value, const char *message ) + +* Description: +* This function calls the astPutErr function to deliver an error +* message, either calling the version registered using astSetPutErr, +* or the version in the linked error module. The linked version +* is used if no function has been registered for PutErr using +* astSetPutErr. + +* 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. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + int old_status; /* Old status value */ + int *status; /* Pointer to status value */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Use the astPutErr function registered using astSetPutErr (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( puterr && puterr_wrapper ) { + +/* We need to ensure the AST status is cleared before invoking the + external error handler, as it may use AST memory management functions, + will return without action if the AST status is non-zero. Remember the + current status value so that it can be re-instated afterwards. */ + status = astGetStatusPtr; + old_status = *status; + *status = 0; + + ( *puterr_wrapper )( puterr, status_value, message ); + + *status = old_status; + +/* Otherwise, use the function in the external error module, selected at + link-time using ast_link options.*/ + } else { + astPutErr( status_value, message ); + } +} + + +/* +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; +} + +void astSetPutErr_( AstPutErrFun fun, int *status ){ +/* +*++ +* Name: +c astSetPutErr +f AST_SETPUTERR + +* Purpose: +c Register an error handling function for use by the AST error model +f Register an error handling routine for use by the AST error model + +* Type: +* Public function. + +* Synopsis: +c #include "error.h" +c void astSetPutErr( void (*fun)(int,const char*) ) +f CALL AST_GRFSET( FUN, STATUS ) + +* Description: +* This function can be used to register an external function to be +* used to deliver an error message and (optionally) an accompanying +* status value to the user. +* +* If this function is not called prior to the first error occuring +* within AST, then the external error handling function selected at +* link-time (using the ast_link command) will be used. To use an +* alternative error handler, call this function before using any other +* AST functions, specifying the external error handling function to be +* used. This will register the function for future use. + +* Parameters: +c fun +f FUN = INTEGER FUNCTION (Given) +c A Pointer to the function to be used to handle errors. The interface +c for this function is described below. +f The name of the routine to be used to handle errors (the name +f should also appear in a Fortran EXTERNAL statement in the +f routine which invokes AST_SETPUTERR). +c Once a function has been provided, a NULL pointer can be supplied +c in a subsequent call to astSetPutErr to reset the function to the +c corresponding function selected at link-time. +f Once a routine has been provided, the "null" routine AST_NULL can +f be supplied in a subsequent call to astSetPutErr to reset the routine +f to the corresponding routine selected at link-time. AST_NULL is +f defined in the AST_PAR include file. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Function Interface: +* The supplied external function should deliver the supplied error message +* and (optionally) the supplied status value to the user or to some +* underlying error system. It requires the following interface: +* +c void PutErr( int status_value, const char *message ) +f SUBROUTINE PUTERR( STATUS_VALUE, MESSAGE ) +* +c - status_value - +f - STATUS_VALUE = INTEGER (Given) - +* The error status value. +c - message - Pointer to a null-terminated character string containing +c the error message to be delivered. +f - MESSAGE = CHARACTER * ( * ) (Given) - The error message to be delivered. + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + +/* Ensure that the thread-specific status block has been created and + initialised. */ + astGET_GLOBALS(NULL); + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store the pointer. */ + puterr = fun; + +/* In general, the interface to the PutErr 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 function with + an interface suited to the language in use. Call astPutErrWrapper to + store a wrapper to a suitable function which can invoke the supplied + 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. */ + astSetPutErrWrapper( CPutErrWrapper ); +} + +void astSetPutErrWrapper_( AstPutErrFunWrapper wrapper, int *status ){ +/* +*+ +* Name: +* astSetPutErrWrapper + +* Purpose: +* Register a wrapper for the error handling function. + +* Type: +* Public function. + +* Synopsis: +* #include "error.h" +* void astSetPutErrWrapper( AstPutErrFunWrapper wrapper ) + +* Description: +* This function must be used to register a wrapper for the external +* function to be used to deliver an error message and (optionally) +* an accompanying status value to the user. + +* Parameters: +* wrapper +* A pointer to the wrapper function to be used to handle errors. + +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + +/* Ensure that the thread-specific status block has been created and + initialised. */ + astGET_GLOBALS(NULL); + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store the pointer. */ + puterr_wrapper = wrapper; +} + +/* +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/ast/error.h b/ast/error.h new file mode 100644 index 0000000..af0e2ae --- /dev/null +++ b/ast/error.h @@ -0,0 +1,362 @@ +#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 + +#if HAVE_CONFIG_H +#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 routine, file and line number at which + the invocation occurs. These are included in public error reports and are + stored with public Object identifiers (from which they can be recoivered + using function astCreatedAt). This is only done for invocations from + outside of AST (i.e. public invocations). */ +#if defined(astCLASS) || defined(astFORTRAN77) +#define astERROR_INVOKE(function) (function) +#elif defined FUNCTION_NAME +#define astERROR_INVOKE(function) (astAt_(FUNCTION_NAME,__FILE__,__LINE__,0,astGetStatusPtr),(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 */ +/* ================ */ + +/* The interface for the PutErr functions to be passed as an argument + to astSetPutErr. */ +typedef void (* AstPutErrFun)( int, const char * ); + +/* The interface for the PutErr wrapper functions to be passed as an argument + to astSetPutErrWrapper. */ +typedef void (* AstPutErrFunWrapper)( AstPutErrFun, int, const char * ); + +/* 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? */ + +/* Foreign functions */ + AstPutErrFun PutErr; /* Pointer to registered error handler */ + AstPutErrFunWrapper PutErr_Wrapper; /* Pointer to wrapper for error handler */ + +/* 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 * ); +void astSetPutErr_( AstPutErrFun, int * ); + +#if defined(astCLASS) || defined(astFORTRAN77) /* Protected only */ +void astSetPutErrWrapper_( AstPutErrFunWrapper, int * ); +int astReporting_( int, int * ); +void astError_( int, const char *, int *, ... )__attribute__((format(printf,2,4))); +void astBacktrace_( int * ); +void astGetAt_( const char **, const char **, 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) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif + +#define astSetPutErr(fun) astSetPutErr_(fun,STATUS_PTR) + +#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) +#define astSetPutErrWrapper(fun) astSetPutErrWrapper_(fun,status) +#define astGetAt astGetAt_ + +#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) +#define astSetPutErrWrapper(fun) astSetPutErrWrapper_(fun,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/ast/f77.h.in b/ast/f77.h.in new file mode 100644 index 0000000..3a886fc --- /dev/null +++ b/ast/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/ast/fbox.c b/ast/fbox.c new file mode 100644 index 0000000..8dff34f --- /dev/null +++ b/ast/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/ast/fchannel.c b/ast/fchannel.c new file mode 100644 index 0000000..9f70315 --- /dev/null +++ b/ast/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/ast/fchebymap.c b/ast/fchebymap.c new file mode 100644 index 0000000..2b006ae --- /dev/null +++ b/ast/fchebymap.c @@ -0,0 +1,137 @@ +/* +*+ +* Name: +* fchebymap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST ChebyMap 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 ChebyMap class. + +* Routines Defined: +* AST_ISACHEBYMAP +* AST_CHEBYMAP + +* Copyright: +* Copyright (C) 201y East Asian Observatory. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General 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: +* 2-MAR-2017 (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 "chebymap.h" /* C interface to the ChebyMap class */ + +F77_LOGICAL_FUNCTION(ast_isachebymap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISACHEBYMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAChebyMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_chebymap)( INTEGER(NIN), + INTEGER(NOUT), + INTEGER(NCOEFF_F), + DOUBLE_ARRAY(COEFF_F), + INTEGER(NCOEFF_I), + DOUBLE_ARRAY(COEFF_I), + DOUBLE_ARRAY(LBND_F), + DOUBLE_ARRAY(UBND_F), + DOUBLE_ARRAY(LBND_I), + DOUBLE_ARRAY(UBND_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_DOUBLE_ARRAY(LBND_F) + GENPTR_DOUBLE_ARRAY(UBND_F) + GENPTR_DOUBLE_ARRAY(LBND_I) + GENPTR_DOUBLE_ARRAY(UBND_I) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_CHEBYMAP", 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( astChebyMap( *NIN, *NOUT, *NCOEFF_F, COEFF_F, *NCOEFF_I, + COEFF_I, LBND_F, UBND_F, LBND_I, UBND_I, "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + + +F77_SUBROUTINE(ast_chebydomain)( INTEGER(THIS), + INTEGER(FWD), + DOUBLE(LBND), + DOUBLE(UBND), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(FWD) + GENPTR_DOUBLE(XOUT) + GENPTR_DOUBLE(YOUT) + + astAt( "AST_CHEBYDOMAIN", NULL, 0 ); + astWatchSTATUS( + astChebyDomain( astI2P( *THIS ), *FWD, LBND, UBND ); + ) +} + diff --git a/ast/fcircle.c b/ast/fcircle.c new file mode 100644 index 0000000..b217a4f --- /dev/null +++ b/ast/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/ast/fcmpframe.c b/ast/fcmpframe.c new file mode 100644 index 0000000..8d99d3b --- /dev/null +++ b/ast/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/ast/fcmpmap.c b/ast/fcmpmap.c new file mode 100644 index 0000000..2888519 --- /dev/null +++ b/ast/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/ast/fcmpregion.c b/ast/fcmpregion.c new file mode 100644 index 0000000..f494d28 --- /dev/null +++ b/ast/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/ast/fdsbspecframe.c b/ast/fdsbspecframe.c new file mode 100644 index 0000000..f0ec5db --- /dev/null +++ b/ast/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/ast/fdssmap.c b/ast/fdssmap.c new file mode 100644 index 0000000..8e570de --- /dev/null +++ b/ast/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/ast/fellipse.c b/ast/fellipse.c new file mode 100644 index 0000000..2570a2c --- /dev/null +++ b/ast/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/ast/ferror.c b/ast/ferror.c new file mode 100644 index 0000000..7407b0f --- /dev/null +++ b/ast/ferror.c @@ -0,0 +1,120 @@ +/* +*+ +* 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 + +#define MXSTRLEN 80 /* String length at which truncation starts + within astPutErr */ +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "error.h" /* C interface to the Error module */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ + + +/* Prototypes for external functions. */ +/* ================================== */ +/* This is the null function defined by the FORTRAN interface in +fobject.c. */ +F77_SUBROUTINE(ast_null)( void ); + +static void FPutErrWrapper( AstPutErrFun, int, const char * ); + + +/* Wrapper functions */ +/* ================= */ +F77_SUBROUTINE(ast_setputerr)( AstPutErrFun FUN, INTEGER(STATUS) ) { + AstPutErrFun fun; + const char *class; /* Object class */ + const char *method; /* Current method */ + + 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 == (AstPutErrFun) F77_EXTERNAL_NAME(ast_null) ) { + fun = NULL; + } + +/* Store the function pointer in the error module. */ + astSetPutErr( fun ); + +/* The above call assumes that "fun" uses C calling conventions. Since in + fact "fun" uses Fortran calling conventions, we need to tell the error + module to call "fun" via a wrapper that converts strings etc from C to + Fortran. */ + astSetPutErrWrapper( FPutErrWrapper ); + ) +} + + +static void FPutErrWrapper( AstPutErrFun fun, int status_value, const char *message ){ + + DECLARE_CHARACTER(LMESSAGE,MXSTRLEN); + int fmessage_length; + + fmessage_length = strlen( message ); + if( fmessage_length > LMESSAGE_length ) fmessage_length = LMESSAGE_length; + astStringExport( message, LMESSAGE, fmessage_length ); + + ( *(void (*)( INTEGER(status_value), CHARACTER(LMESSAGE) + TRAIL(fmessage) ) ) fun)(INTEGER_ARG(&status_value), + CHARACTER_ARG(LMESSAGE) + TRAIL_ARG(fmessage)); + + +} + + diff --git a/ast/fetch b/ast/fetch new file mode 100755 index 0000000..1ca0578 --- /dev/null +++ b/ast/fetch @@ -0,0 +1,5 @@ + +here=${PWD} +cd ${AST_REF} +cp $@ ${here} +cd ${here} diff --git a/ast/ffitschan.c b/ast/ffitschan.c new file mode 100644 index 0000000..ef1b83e --- /dev/null +++ b/ast/ffitschan.c @@ -0,0 +1,1023 @@ +/* +*+ +* 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 ); + if( name && !strcmp( name, "." ) ) name = astFree( name ); \ + 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/ast/ffitstable.c b/ast/ffitstable.c new file mode 100644 index 0000000..9f2db37 --- /dev/null +++ b/ast/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/ast/ffluxframe.c b/ast/ffluxframe.c new file mode 100644 index 0000000..ab55e80 --- /dev/null +++ b/ast/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/ast/fframe.c b/ast/fframe.c new file mode 100644 index 0000000..6a71cc1 --- /dev/null +++ b/ast/fframe.c @@ -0,0 +1,514 @@ +/* +*+ +* 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. +* 26-OCT-2016 (DSB): +* Added method AST_AXNORM. +*/ + +/* 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; +} + +F77_SUBROUTINE(ast_axnorm)( INTEGER(THIS), + INTEGER(AXIS), + INTEGER(OPER), + INTEGER(NVAL), + DOUBLE(VALUES), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(AXIS) + GENPTR_INTEGER(OPER) + GENPTR_INTEGER(NVAL) + GENPTR_DOUBLE(VALUES) + + astAt( "AST_AXNORM", NULL, 0 ); + astWatchSTATUS( + astAxNorm( astI2P( *THIS ), *AXIS, *OPER, *NVAL, VALUES ); + ) +} diff --git a/ast/fframeset.c b/ast/fframeset.c new file mode 100644 index 0000000..1698847 --- /dev/null +++ b/ast/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/ast/fgrismmap.c b/ast/fgrismmap.c new file mode 100644 index 0000000..f26159c --- /dev/null +++ b/ast/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/ast/finterval.c b/ast/finterval.c new file mode 100644 index 0000000..7023868 --- /dev/null +++ b/ast/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/ast/fintramap.c b/ast/fintramap.c new file mode 100644 index 0000000..cdf3e4e --- /dev/null +++ b/ast/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/ast/fitschan.c b/ast/fitschan.c new file mode 100644 index 0000000..ae7c36e --- /dev/null +++ b/ast/fitschan.c @@ -0,0 +1,43747 @@ +/* +*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 +* - PolyTan: Use PVi_m keywords to define distorted TAN projection? +* - SipReplace: Replace SIP inverse transformation? +* - SipOK: Use Spitzer Space Telescope keywords to define distortion? +* - SipReplace: Replace SIP inverse transformation? +* - TabOK: Should the FITS "-TAB" algorithm be recognised? +* - 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 to 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. +* 28-JUN-2016 ((DSB): +* IsAipsSpectral: Trailing spaces in CTYPE values are insignificant. +* 17-MAR-2017 (DSB): +* Fix memory leak in MakeFitsFrameSet. +* 25-APR-2017 (DSB): +* When reading foreign WCS, retain the TIMESYS keyword by default. +* 28-APR-2017 (DSB): +* When reading a JCMT or UKIRT foreign header that contains a +* DTAI keyword, use it to set the Dtai attribute in the WCS Frame. +* 11-SEP-2017 (DSB): +* Allow a NULL keyword name to be supplied to astTestFits to +* indicate that the current card should be used (as is also done in +* astGetFits). +* 25-OCT-2017 (DSB): +* Added attribute FitsTol. +* 27-OCT-2017 (DSB): +* In RoundFString, only right justift the final string if a +* minimum field width is given. Otherwise, leave the unchanged +* characters in their original positions. Right justifying ccould +* cause very long strings to be returned. +* 6-NOV-2017 (DSB): +* In CelestialAxes, simplify the base->current mapping before +* attempting to split it. This can cause multiple WcsMaps to cancel out, +* which could otherwise prevent SplitMap from splitting the Mapping +* successfully. +* 7-NOV-2017 (DSB): +* If an IWC Frame is included in the FrameSet returned by astRead, +* ensure it comes between the pixel and sky frames in the mapping chain. +* Previously the order was PIXEL->SKY->IWC, Now it is PIXEL->IWC->SKY. +* The inter-Frame Mappings required by the new arrangment are simpler +* than for the old arrangement. +* 20-NOV-2017 (DSB) +* Added SipReplace attribute. +* 20-NOV-2017 (DSB) +* Added SipReplace attribute. +* 30-DEC-2017 (DSB): +* Add the SipOK attribute, and support for writing SIP headers. +*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 ***dtai; + 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 ClearSipReplace( AstFitsChan *, int * ); +static int GetSipReplace( AstFitsChan *, int * ); +static int TestSipReplace( AstFitsChan *, int * ); +static void SetSipReplace( 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 ClearSipOK( AstFitsChan *, int * ); +static int GetSipOK( AstFitsChan *, int * ); +static int TestSipOK( AstFitsChan *, int * ); +static void SetSipOK( 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 double GetFitsTol( AstFitsChan *, int * ); +static int TestFitsTol( AstFitsChan *, int * ); +static void ClearFitsTol( AstFitsChan *, int * ); +static void SetFitsTol( AstFitsChan *, double, 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 *SIPIntWorld( AstMapping *, int, int, char, FitsStore *, double *, int[2], double[2], double[4], const char *, const char *, int * ); +static AstMapping *SIPMapping( AstFitsChan *, 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 *, double, int, 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 nf; /* Number of Frames originally in fset */ + 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 ); + } + +/* Record the original number of Frames in the FrameSet. */ + nf = astGetNframe( fset ); + +/* Add in the Frame (which may be a FrameSet). */ + astAddFrame( fset, pixel, mapping, frame ); + +/* Ensure the WCS Frame is the current Frame within fset (it may not be if + the frame returned by WcsMapFrm is actually a FrameSet containing a IWC + Frame as well as a WCS Frame). The WCS Frame is always the first frame + in the frame/frameset returned by WcsMapFrm. */ + astSetCurrent( fset, nf + 1 ); + +/* 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 fitstol; /* Max departure from linearity, in pixels */ + 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 */ + int sipok; /* Should SIP headers be produced? */ + +/* 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 describe 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 ); + +/* Get the maximum departure from linearity, in pixels, for the pixiwcmap + mapping to be considered linear. */ + fitstol = astGetFitsTol( this ); + +/* See if SIP headers are to be produced. */ + sipok = astGetSipOK( this ); + +/* Now attempt to store values for the keywords describing the pixel->IWC + Mapping (CRPIX, CD, PC, CDELT). This tests that the iwcmap is linear. + It can also include keywords describing distortion in the form of SIP + headers, if appropriate. Zero is returned if the test fails. */ + ret = MakeIntWorld( pixiwcmap, wcsfrm, wperm, s, store, dim, fitstol, + sipok, 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 ); + } + +/* Store the TAI-UTC correction, if set. */ + if( astTestDtai( wcsfrm ) && s == ' ' ) { + SetItem( &(store->dtai), 0, 0, ' ', astGetDtai( wcsfrm ), 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 *map0; /* Unsimplified Pixel -> WCS mapping */ + AstMapping *map1; /* Pointer to pre-WcsMap Mapping */ + AstMapping *map3; /* Pointer to post-WcsMap Mapping */ + AstMapping *map; /* Simplified 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. */ + map0 = astGetMapping( fs, AST__BASE, AST__CURRENT ); + map = astSimplify( map0 ); + map0 = astAnnul( map0 ); + +/* 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 ); + +/* SipReplace */ +/* ---------- */ + } else if ( !strcmp( attrib, "sipreplace" ) ) { + astClearSipReplace( this ); + +/* FitsTol */ +/* ------- */ + } else if ( !strcmp( attrib, "fitstol" ) ) { + astClearFitsTol( this ); + +/* PolyTan */ +/* ------- */ + } else if ( !strcmp( attrib, "polytan" ) ) { + astClearPolyTan( this ); + +/* SipOK */ +/* ------- */ + } else if ( !strcmp( attrib, "sipok" ) ) { + astClearSipOK( 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", AST__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", AST__DBL_DIG, ( (double *) odata )[ 0 ] ); + CheckZero( cnvtype_text0, ( (double *) odata )[ 0 ], 0, status ); + (void) sprintf( cnvtype_text1, "%.*g", AST__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( this, 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->dtai = 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->dtai), 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->dtai = 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 */ + double dval; /* Double attribute value */ + 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; + } + +/* SipReplace */ +/* ---------- */ + } else if ( !strcmp( attrib, "sipreplace" ) ) { + ival = astGetSipReplace( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* FitsTol. */ +/* -------- */ + } else if ( !strcmp( attrib, "fitstol" ) ) { + dval = astGetFitsTol( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* PolyTan */ +/* ------- */ + } else if ( !strcmp( attrib, "polytan" ) ) { + ival = astGetPolyTan( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* SipOK */ +/* ------- */ + } else if ( !strcmp( attrib, "sipok" ) ) { + ival = astGetSipOK( 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", AST__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->ClearSipReplace = ClearSipReplace; + vtab->TestSipReplace = TestSipReplace; + vtab->SetSipReplace = SetSipReplace; + vtab->GetSipReplace = GetSipReplace; + vtab->ClearFitsTol = ClearFitsTol; + vtab->TestFitsTol = TestFitsTol; + vtab->SetFitsTol = SetFitsTol; + vtab->GetFitsTol = GetFitsTol; + vtab->ClearPolyTan = ClearPolyTan; + vtab->TestPolyTan = TestPolyTan; + vtab->SetPolyTan = SetPolyTan; + vtab->GetPolyTan = GetPolyTan; + vtab->ClearSipOK = ClearSipOK; + vtab->TestSipOK = TestSipOK; + vtab->SetSipOK = SetSipOK; + vtab->GetSipOK = GetSipOK; + 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 used length of the string is not 8, then it is not an AIPS spectral axis. */ + if( astChrLen( 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( !strncmp( ctype + 4, "-LSR", 4 ) ){ + *wspecsys = "LSRK"; + } else if( !strncmp( ctype + 4, "LSRK", 4 ) ){ + *wspecsys = "LSRK"; + } else if( !strncmp( ctype + 4, "-LSD", 4 ) ){ + *wspecsys = "LSRD"; + } else if( !strncmp( ctype + 4, "-HEL", 4 ) ){ + *wspecsys = "BARYCENT"; + } else if( !strncmp( ctype + 4, "-EAR", 4 ) || !strncmp( ctype + 4, "-GEO", 4 ) ){ + *wspecsys = "GEOCENTR"; + } else if( !strncmp( ctype + 4, "-OBS", 4 ) || !strncmp( ctype + 4, "-TOP", 4 ) ){ + *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 + AST__DBL_DIG*2 ]; + char invexp[ 12 + AST__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)", AST__DBL_DIG, crv, AST__DBL_DIG, crv ); + sprintf( invexp, "w=%.*g*log(s/%.*g)", AST__DBL_DIG, crv, AST__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 ); + (void) astAnnul( map ); + map = tmap; + } + remap = astAnnul( remap ); + smap = astAnnul( smap ); + +/* 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", AST__DBL_DIG, + AST__DR2D*astGetRefRA( specfrm ) ); + astPutFits( fc, card, 0 ); + sprintf( card, "CRVAL2 = %.*g", AST__DBL_DIG, + AST__DR2D*astGetRefDec( specfrm ) ); + astPutFits( fc, card, 0 ); + sprintf( card, "MJD-OBS = %.*g", AST__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, double fitstol, + int sipok, 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, double fitstol, +* int sipok, 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 +* supplied Mapping, which must be linear with an optional shift of +* origin, otherwise a value of zero is returned. The exception to +* this rule is that if the Mapping contains a PolyMap, and the "sipok" +* argument is non-zero, an attempt is made to create a set of SIP +* headers to describe the non-linear transformation. +* +* 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. +* fitstol +* The maximum departure from linearity that can be introduced by +* "cmap" on any axis for it to be considered linear. Expressed as +* a fraction of a grid pixel. +* sipok +* Flag indicating if SIP headers should be produced if there is +* a suitable PolyMap in the Mapping chain. +* 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; + AstMapping *sipmap; + AstPointSet *psetg; + AstPointSet *psetw; + double **fullmat; + double **partmat; + double **ptrw; + double **ptrg; + double *c; + double *cdelt; + double *cdmat; + double *colvec; + double *d; + double *g0; + double *g; + double *m; + double *mat; + double *tol; + double *w0; + double *y; + double cd; + double cd_sip[4]; + double crp; + double crpix_sip[2]; + double crv; + double cv; + double det; + double err; + double k; + double mxcv; + double skydiag0; + double skydiag1; + double val; + int *iw; + int *lin; + int *pperm; + int *skycol; + int havesip; + int i; + int ii; + int j; + int jax; + int jj; + int lonax; + int latax; + int nin; + int nout; + int nwcs; + int paxis; + int ret; + int sipax[2]; + int sing; + int skycol0; + int skycol1; + +/* Initialise */ + ret = 0; + +/* Check the inherited status. */ + if( !astOK ) 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( cmap ); + nout = astGetNout( cmap ); + if( nout < nin ) return ret; + +/* Simplify the supplied Mapping to reduce rounding errors when + transforming points. */ + map = astSimplify( cmap ); + +/* See if the WCS Frame contains a SkyFrame, and if so get the indices of + the Mapping outputs that correspond to the longitude and latitude + axes. */ + lonax = -1; + latax = -1; + for( i = 0; i < nout; i++ ) { + astPrimaryFrame( fr, i, &pfrm, &paxis ); + if( astIsASkyFrame( pfrm ) ) { + if( paxis == 0 ) { + lonax = i; + } else { + latax = i; + } + } + } + +/* If there is a pair of celestial axes in the WCS Frame, and if the + celestial axes can be described using SIP distortion, then put the + headers describing the SIP distortion into the FitsStore. This also + returns the CRPIX and CD values to use with the celestial axes. */ + havesip = 0; + if( sipok && lonax != -1 ){ + sipmap = SIPIntWorld( map, lonax, latax, s, store, dim, sipax, + crpix_sip, cd_sip, method, class, status ); + +/* If SIP headers were stored successfully, use the modified mapping from + now on. This is the same as "map" but does not include the PolyMap + from which the SIP headers were determined. This is done so that he + PolyMap does not break the linearity test performed below in order to + determine CRPIX and CD values for any other non-celestial axes. */ + if( sipmap ) { + (void) astAnnul( map ); + map = sipmap; + havesip = 1; + } + } + +/* 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 a + fraction "fitstol" 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 ] *= fitstol; + +/* 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 we are using SIP distortion, replace the values for the celestial + axes found above with the values found by SIPIntWorld. */ + if( havesip ) { + partmat[ sipax[0] ][ lonax ] = cd_sip[ 0 ]; + partmat[ sipax[1] ][ lonax ] = cd_sip[ 1 ]; + partmat[ sipax[0] ][ latax ] = cd_sip[ 2 ]; + partmat[ sipax[1] ][ latax ] = cd_sip[ 3 ]; + } + +/* 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 we are + using SIP distortion, replace the crpix values for the celestial + axes found above with the values found by SIPIntWorld. */ + if( j < nin ) { + crp = g0[ j ] - y[ j ]; + if( havesip ) { + if( j == sipax[0] ){ + crp = crpix_sip[0]; + } else if( j == sipax[1] ){ + crp = crpix_sip[1]; + } + } + +/* 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; + } + } + } + +/* If a minimum field width has been given, right justify the returned + string in the original field width. */ + if( 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 */ + double dval; /* Double attribute value */ + 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 ); + +/* SipReplace */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "sipreplace= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetSipReplace( this, ival ); + +/* FitsTol. */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "fitstol= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetFitsTol( this, dval ); + +/* PolyTan */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "polytan= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetPolyTan( this, ival ); + +/* SipOK */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "sipok= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetSipOK( 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 *SIPIntWorld( AstMapping *map, int lonax, int latax, + char s, FitsStore *store, double *dim, + int inaxes[2], double crpix[2], double cd[4], + const char *method, const char *class, + int *status ){ +/* +* Name: +* SIPIntWorld + +* Purpose: +* Create FITS header values which map grid into intermediate world +* coords for celestial axes that include SIP distortion. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* AstMapping *SIPIntWorld( AstMapping *map, int lonax, int latax, +* char s, FitsStore *store, double *dim, +* int inaxes[2], double crpix[2], double cd[4], +* const char *method, const char *class, +* int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function finds and returns values for the CRPIX and CDi_j +* keywords for sky axes that can be described using the SIP +* distortion scheme. These values are determined by examining the +* supplied pixel->IWCS Mapping. Values for SIP headers are also stored +* in the supplied FitsSTore. +* +* The celestial axes are first identified and the supplied Mapping +* split to create a (2-in,2-out) Mapping that describes them. This +* Mapping is then searched for a PolyMap. If found, the Mapping prior +* to the PolyMap is checked to ensure it is a simple shift of origin. +* The Mapping following the PolyMap is checked to ensure it is a +* linear transformation with no shift of origin. The PolyMap itself +* is checked to see if it conforms to the requirements of the SIP +* conventions. If any of these conditions are not met, NULL is +* returned as the function value. Otherwise, CRPIX values are created +* from the Mapping prior to the PolyMap, and CDi_j values from the +* Mapping following the PolyMap. The keywords describing the SIP +* distortion itself (the PolyMap) are stored in the supplied FitsStore. +* A Mapping is retuned that is identical to the supplied Mapping but +* without the PolyMap. + +* Parameters: +* map +* A pointer to a Mapping which transforms grid coordinates into +* intermediate world coordinates. +* lonax +* The zero-based index of the output of "map" corresponding to +* celestial longitude. +* latax +* The zero-based index of the output of "map" corresponding to +* celestial latitude. +* 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 SIP headers +* are stored. +* dim +* An array holding the image dimensions in pixels. AST__BAD can be +* supplied for any unknwon dimensions. +* inaxes +* Returned holding the indices of the two Mapping inputs that generate +* the returned "crpix" and "cd" values. +* crpix +* If SIP headers are stored successfully in the FitsStore, then +* this array is returned holding the CRPIX values. The first +* element refers to the Mapping input given by the first element +* of "inaxes". The second element refers to the Mapping input given +* by the second element of "inaxes". +* cd +* If SIP headers are stored successfully in the FitsStore, then +* this array is returned holding the CD values in the order +* (CDlonax_j1,CDlonax_j2,CDlatax_j1,CDlatax_j2). Where "lonax" and +* "latax" are the indices of the lon and lat Mapping outputs +* (note, these may be different to the corresponding FITS "i" axis +* indices), and "j1" and "j2" are the indices of the Mapping inputs +* returned in "inaxes" (i.e. j1 = inaxes[0] and j2 = inaxes[1]). +* 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 Mapping is returned if SIP headers have been stored in the +* FitsStore successfully. NULL is returned otherwise. The returned +* Mapping is a copy of the supplied mapping "map", but without the +* PolyMap. + +* Notes: +* - NULL is returned if an error occurs. +*/ + +/* Local Variables: */ + AstMapping **map_list; + AstMapping *map_upper; + AstMapping *map_lower; + AstMapping *result; + AstMapping *smap; + AstMapping *tmap; + AstMapping *tmap2; + AstMapping *tmap1; + AstPolyMap *polymap; + AstPermMap *pm; + const char *cval; + char buf[30]; + double ****item; + double *coeffs; + double *pc; + double *scales; + double *shifts; + double fit[ 6 ]; + double iwcxin; + double iwcyin; + double lbnd[ 2 ]; + double ubnd[ 2 ]; + double val; + int *inax1; + int *inax2; + int *inperm1; + int *inperm2; + int *invert_list; + int *outperm1; + int *outperm2; + int *outrem; + int fwd; + int i; + int icoeff; + int iin; + int imap; + int imap_pm; + int iout; + int ioutrem; + int jm; + int ncoeff; + int nin; + int nmap; + int nout; + int noutrem; + int ok; + int old_invert; + int outax[ 2 ]; + int aimax; + int ajmmax; + int bimax; + int bjmmax; + +/* Initialise */ + result = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get the number of inputs and outputs for the Mapping. */ + nin = astGetNin( map ); + nout = astGetNin( map ); + +/* Check both transformations are defined in the supplied Mapping. */ + if( astGetTranForward( map ) && astGetTranInverse( map ) ) { + +/* Attempt to split the supplied Mapping to generate a (2-input,2-output) + Mapping that goes from grid coords to celestial longitude and latitude. + Since we want to specify the retained output, we need to invert the + Mapping, because astMapSplit only allows us to specify the retained + inputs. */ + astInvert( map ); + outax[ 0 ] = lonax; + outax[ 1 ] = latax; + inax1 = astMapSplit( map, 2, outax, &tmap1 ); + astInvert( map ); + +/* Check the Mapping could be split, and that the mapping that generates + lonax/latax has exactly two inputs (use the NBout attribute since + "tmap1" is inverted). Then invert "tmap1" so that it is in the same + direction as the supplied mapping. */ + if( inax1 && tmap1 && astGetNout( tmap1 ) == 2 ) { + astInvert( tmap1 ); + inaxes[ 0 ] = inax1[ 0 ]; + inaxes[ 1 ] = inax1[ 1 ]; + +/* Search this list of Mappings for a PolyMap. First simplify it, then + expand it as a list of Mappings in series. Then look through the list + for a PolyMap. */ + polymap = NULL; + smap = astSimplify( tmap1 ); + nmap = 0; + map_list = NULL; + invert_list = NULL; + (void) astMapList( smap, 1, astGetInvert(smap), &nmap, &map_list, + &invert_list ); + for( imap = 0; imap < nmap; imap++ ) { + if( astIsAPolyMap( map_list[ imap ] ) ) { + imap_pm = imap; + polymap = astCopy( map_list[ imap ] ); + astSetInvert( polymap, invert_list[ imap ] ); + break; + } + } + +/* If a PolyMap is found, check it conforms to the requirements of the + SIP convention. */ + if( polymap ){ + if( astGetNin( polymap ) == 2 && astGetNout( polymap ) == 2 ){ + +/* Check that each Mapping before the PolyMap is a shift of origin, and + accumulate them into a single CmpMap. */ + map_lower = NULL; + ok = 1; + for( imap = 0; ok && imap < imap_pm; imap++ ) { + old_invert = astGetInvert( map_list[ imap ] ); + astSetInvert( map_list[ imap ], invert_list[ imap ] ); + + if( astGetNin( map_list[ imap ] ) != 2 ){ + ok = 0; + + } else if( astIsAWinMap( map_list[ imap ] ) ) { + astWinTerms( map_list[ imap ], &shifts, &scales ); + if( scales[ 0 ] != 1.0 || scales[ 1 ] != 1.0 ) ok = 0; + + } else if( !astIsAShiftMap( map_list[ imap ] ) && + !astIsAUnitMap( map_list[ imap ] ) ) { + ok = 0; + } + + if( map_lower ) { + tmap = (AstMapping *) astCmpMap( map_lower, map_list[ imap ], 1, " ", status ); + (void) astAnnul( map_lower ); + map_lower = tmap; + } else { + map_lower = astCopy( map_list[ imap ] ); + } + + astSetInvert( map_list[ imap ], old_invert ); + } + +/* Check that each Mapping after the PolyMap is a scaling or matrix, + and accumulate them into a single CmpMap. */ + map_upper = NULL; + for( imap = imap_pm + 1; ok && imap < nmap; imap++ ) { + old_invert = astGetInvert( map_list[ imap ] ); + astSetInvert( map_list[ imap ], invert_list[ imap ] ); + + if( astGetNin( map_list[ imap ] ) != 2 ){ + ok = 0; + + } else if( astIsAWinMap( map_list[ imap ] ) ) { + astWinTerms( map_list[ imap ], &shifts, &scales ); + if( shifts[ 0 ] != 0.0 || shifts[ 1 ] != 0.0 ) ok = 0; + + } else if( !astIsAMatrixMap( map_list[ imap ] ) && + !astIsAZoomMap( map_list[ imap ] ) && + !astIsAUnitMap( map_list[ imap ] ) ) { + ok = 0; + } + + if( map_upper ) { + tmap = (AstMapping *) astCmpMap( map_upper, map_list[ imap ], 1, " ", status ); + (void) astAnnul( map_upper ); + map_upper = tmap; + } else { + map_upper = astCopy( map_list[ imap ] ); + } + + astSetInvert( map_list[ imap ], old_invert ); + } + +/* Split the supplied Mapping to generate the Mapping that gives + any remaining non-celestial output axes. We only need to do this if + the supplied Mapping has any surplus inputs or outputs. */ + inax2 = NULL; + tmap2 = NULL; + outrem = NULL; + + if( nout > 2 ) { + noutrem = nout - 2; + outrem = astMalloc( noutrem*sizeof(int) ); + if( astOK ) { + ioutrem = 0; + for( iout = 0; iout < nout; iout++ ) { + if( iout != lonax && iout != latax ) outrem[ ioutrem++ ] = iout; + } + + astInvert( map ); + inax2 = astMapSplit( map, noutrem, outrem, &tmap2 ); + astInvert( map ); + if( tmap2 ) { + astInvert( tmap2 ); + } else { + ok = 0; + } + } + + } else if( nout != 2 || nin != 2 ) { + ok = 0; + } + +/* If the above tests were passed, transform the origin of IWC (the total map + output space) into grid coords. This gives CRPIX. */ + if( ok ) { + iwcxin = 0.0; + iwcyin = 0.0; + astTran2( smap, 1, &iwcxin, &iwcyin, 0, crpix, crpix + 1 ); + +/* Determine the CD matrix. We already know the upper Mapping is linear + because we have checked that it contains only linear atomic mappings. + So we can use fixed bounds for the fitting area safely. */ + lbnd[ 0 ] = -1.0; + lbnd[ 1 ] = -1.0; + ubnd[ 0 ] = 1.0; + ubnd[ 1 ] = 1.0; + if( !astLinearApprox( map_upper, lbnd, ubnd, 0.01, fit ) ) { + astError( AST__INTER, "%s(%s): SipIntWorld: Mapping " + "following PolyMap is not linear (internal " + "AST programming error).", status, method, + class ); + } + +/* Store the matrix elements in the required order. */ + cd[ 0 ] = fit[ 2 ]; + cd[ 1 ] = fit[ 3 ]; + cd[ 2 ] = fit[ 4 ]; + cd[ 3 ] = fit[ 5 ]; + +/* Store SIP headers describing first the forward then the inverse + transformation of the PolyMap in the FitsStore. Note, the axis indices + returned by astPolyCoeffs are 1-based. */ + for( fwd = 1; fwd >= 0; fwd-- ) { + if( ( fwd && astGetTranForward( polymap ) ) || + ( !fwd && astGetTranInverse( polymap ) ) ) { + astPolyCoeffs( polymap, fwd, 0, NULL, &ncoeff ); + coeffs = astMalloc( 4*ncoeff*sizeof(*coeffs) ); + if( astOK ) { + astPolyCoeffs( polymap, fwd, 4*ncoeff, coeffs, &ncoeff ); + +/* Find the maximum used power on each input axis. */ + aimax = 0; + ajmmax = 0; + bimax = 0; + bjmmax = 0; + pc = coeffs; + for( icoeff = 0; icoeff < ncoeff; icoeff++ ) { + if( inaxes[ 0 ] < inaxes [ 1 ] ) { + i = (int) ( pc[ 2 ] + 0.5 ); + jm = (int) ( pc[ 3 ] + 0.5 ); + if( pc[ 1 ] == 1 ) { + if( i > aimax ) aimax = i; + if( jm > ajmmax ) ajmmax = jm; + } else { + if( i > bimax ) bimax = i; + if( jm > bjmmax ) bjmmax = jm; + } + } else { + i = (int) ( pc[ 3 ] + 0.5 ); + jm = (int) ( pc[ 2 ] + 0.5 ); + if( pc[ 1 ] == 1 ) { + if( i > bimax ) bimax = i; + if( jm > bjmmax ) bjmmax = jm; + } else { + if( i > aimax ) aimax = i; + if( jm > ajmmax ) ajmmax = jm; + } + } + pc += 4; + } + +/* Initialise the arrays with bad values so that unused powers are not + included in the header. */ + + for( i = 0; i <= aimax; i++ ){ + for( jm = 0; jm <= ajmmax; jm++ ){ + SetItem( fwd? &(store->asip) : &(store->apsip), + i, jm, s, AST__BAD, status ); + } + } + + for( i = 0; i <= bimax; i++ ){ + for( jm = 0; jm <= bjmmax; jm++ ){ + SetItem( fwd? &(store->bsip) : &(store->bpsip), + i, jm, s, AST__BAD, status ); + } + } + +/* Over-write the bad values with real values for the powers that are + actually used. Reduce the coefficients of the linear terms by 1.0 + since the SIP distortion is an additive correction, rather than a direct + transformation. */ + pc = coeffs; + for( icoeff = 0; icoeff < ncoeff; icoeff++ ) { + if( inaxes[ 0 ] < inaxes [ 1 ] ) { + if( pc[ 1 ] == 1 ) { + item = fwd ? &(store->asip) : &(store->apsip); + } else { + item = fwd ? &(store->bsip) : &(store->bpsip); + } + i = (int) ( pc[ 2 ] + 0.5 ); + jm = (int) ( pc[ 3 ] + 0.5 ); + } else { + if( pc[ 1 ] == 1 ) { + item = fwd ? &(store->bsip) : &(store->bpsip); + } else { + item = fwd ? &(store->asip) : &(store->apsip); + } + i = (int) ( pc[ 3 ] + 0.5 ); + jm = (int) ( pc[ 2 ] + 0.5 ); + } + + val = pc[ 0 ]; + if( ( pc[ 1 ] == 1 && i == 1 && jm == 0 ) || + ( pc[ 1 ] == 2 && i == 0 && jm == 1 ) ){ + val -= 1.0; + } + if( val != 0.0 && val != AST__BAD ) { + SetItem( item, i, jm, s, val, status ); + } + pc += 4; + } + } + coeffs = astFree( coeffs ); + } + } + +/* Change the CTYPE value to indicate SIP distortion is in use. */ + cval = GetItemC( &(store->ctype), latax, 0, s, NULL, method, + class, status ); + if( cval ){ + strcpy( buf, cval ); + strcpy( buf + 8, "-SIP" ); + SetItemC( &(store->ctype), latax, 0, s, buf, status ); + } + + cval = GetItemC( &(store->ctype), lonax, 0, s, NULL, method, + class, status ); + if( cval ){ + strcpy( buf, cval ); + strcpy( buf + 8, "-SIP" ); + SetItemC( &(store->ctype), lonax, 0, s, buf, status ); + } + +/* Construct the returned Mapping. This is equivalent to the supplied + Mapping, but without the PolyMap. Use PermMaps at beginning and end to + take account of any axis permutations introduced by the operation of + astMapSplit. First put the 2D Mapping preceding the PolyMap in series + with the 2D Mapping following the PolyMap. */ + result = (AstMapping *) astCmpMap( map_lower, map_upper, 1, " ", status ); + +/* Now put the above Mapping in parallel with the mMapping that + transforms any additional axes. */ + if( tmap2 ) { + tmap = (AstMapping *) astCmpMap( result, tmap2, 0, " ", status ); + (void) astAnnul( result ); + result = tmap; + } + +/* Create a PermMap that permutes the outputs of the above Mapping back + into their original order. */ + inperm1 = astMalloc( nout*sizeof(int) ); + outperm1 = astMalloc( nout*sizeof(int) ); + inperm2 = astMalloc( nin*sizeof(int) ); + outperm2 = astMalloc( nin*sizeof(int) ); + if( astOK ) { + inperm1[ 0 ] = lonax; + inperm1[ 1 ] = latax; + outperm1[ lonax ] = 0; + outperm1[ latax ] = 1; + if( tmap2 ) { + for( iout = 0; iout < noutrem; iout++ ) { + inperm1[ iout + 2 ] = outrem[ iout ]; + outperm1[ outrem[ iout ] ] = iout + 2; + } + } + pm = astPermMap( nout, inperm1, nout, outperm1, + NULL, " ", status ); + +/* Put this PermMap in series with (following) the main Mapping created + above. */ + tmap = (AstMapping *) astCmpMap( result, pm, 1, " ", status ); + (void) astAnnul( result ); + pm = astAnnul( pm ); + result = tmap; + +/* Create a PermMap that permutes the inputs of the above Mapping back + into their original order. */ + outperm2[ 0 ] = inax1[ 0 ]; + outperm2[ 1 ] = inax1[ 1 ]; + inperm2[ inax1[ 0 ] ] = 0; + inperm2[ inax1[ 1 ] ] = 1; + + if( tmap2 ) { + for( iin = 0; iin < nin - 2; iin++ ) { + outperm2[ iin + 2 ] = inax2[ iin ]; + inperm2[ inax2[ iin ] ] = iin + 2; + } + } + + pm = astPermMap( nin, inperm2, nin, outperm2, + NULL, " ", status ); + +/* Put this PermMap in series with (preceding) the main Mapping created + above. */ + tmap = (AstMapping *) astCmpMap( pm, result, 1, " ", status ); + (void) astAnnul( result ); + pm = astAnnul( pm ); + result = tmap; + } + +/* Free resources. */ + inperm1 = astFree( inperm1 ); + inperm2 = astFree( inperm2 ); + outperm1 = astFree( outperm1 ); + outperm2 = astFree( outperm2 ); + } + + inax2 = astFree( inax2 ); + outrem = astFree( outrem ); + if( tmap2 ) tmap2 = astAnnul( tmap2 ); + if( map_lower ) map_lower = astAnnul( map_lower ); + if( map_upper ) map_upper = astAnnul( map_upper ); + } + + polymap = astAnnul( polymap ); + } + + for( imap = 0; imap < nmap; imap++ ) { + map_list[ imap ] = astAnnul( map_list[ imap ] ); + } + + invert_list = astFree( invert_list ); + map_list = astFree( map_list ); + inax1 = astFree( inax1 ); + smap = astAnnul( smap ); + } + if( tmap1 ) tmap1 = astAnnul( tmap1 ); + } + +/* Return the Mapping. */ + return result; +} + +static AstMapping *SIPMapping( AstFitsChan *this, 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( AstFitsChan *this, 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: +* this +* The FitsChan. +* 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. Only do this if it has not been disabled + using attribute SipReplace. */ + if( astGetSipReplace( this ) ) { + 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, AST__DBL_DIG, AST__DR2D*ptr2[ ilon ][ 0 ] ); + } else if( i == ilat ) { + sprintf( card, "CRVAL%d = %.*g", i + 1, AST__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 ); + +/* SipReplace. */ +/* ----------- */ + } else if ( !strcmp( attrib, "sipreplace" ) ) { + result = astTestSipReplace( this ); + +/* FitsTol. */ +/* -------- */ + } else if ( !strcmp( attrib, "fitstol" ) ) { + result = astTestFitsTol( this ); + +/* PolyTan */ +/* ------- */ + } else if ( !strcmp( attrib, "polytan" ) ) { + result = astTestPolyTan( this ); + +/* SipOK */ +/* ----- */ + } else if ( !strcmp( attrib, "sipok" ) ) { + result = astTestSipOK( 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. If +c NULL +f a single dot '.' +* is supplied, the current card is tested. +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 */ + int type; /* The card's type */ + +/* 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. */ + if( name ) { + (void) Split( this, name, &lname, &lvalue, &lcom, method, class, status ); + } else { + lname = NULL; + lvalue = NULL; + lcom = NULL; + } + +/* 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. No + need to do the search if the value of the current card is required. */ + if( !lname || SearchCard( this, lname, method, class, status ) ){ + +/* Get the card type. */ + type = CardType( this, status ); + +/* Check the card exists. */ + if( type != AST__NOTYPE ) { + +/* If the cards data type is not undefined, return 1. */ + if( CardType( this, status ) != AST__UNDEF ) ret = 1; + +/* Indicate the card has been found. */ + if( there ) *there = 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 */ + char *telescop; /* Pointer to TELESCOP keyword value */ + 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 DTAI keyword? Only handle if the telescope is JCMT + or UKIRT, since other telescopes may use DTAI for different purposes. */ + } else if( Match( keynam, "DTAI", 0, fld, &nfld, method, class, status ) ){ + if( GetValue( fc, "TELESCOP", AST__STRING, &telescop, 0, 0, method, + class, status ) && ( !strncmp( telescop, "JCMT", 4) + || !strncmp( telescop, "UKIRT", 5 ) ) ){ + mark = 0; + item = &(store->dtai); + 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 ) ){ + mark = 0; + 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 combuf[80]; /* Buffer for FITS card comment */ + char parprefix[4]; /* Prefix for projection parameter keywords */ + 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 */ + const char *order_kwd; /* Name for SIP max order keyword */ + double ****item; /* Address of FitsStore item to use */ + 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 order; /* Max SIP polynomial order */ + int p; /* Power of u or U */ + int pmax; /* Max power of u or U */ + int prj; /* Projection type */ + int q; /* Power of v or V */ + int qmax; /* Max power of v or V */ + 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 ); + } + + +/* Forward SIP distortion keywords. Loop over the two spatial axes. My reading + of the SIP paper is that the SIP distortion *must* be attached to the first + two pixel axes defined by the FITS header. */ + for( i = 0; i < 2; i++ ) { + +/* Get a pointer to the FitsStore item holding the values defining this + output. */ + if( i == 0 ) { + item = &(store->asip); + strcpy( parprefix, "A_" ); + order_kwd = "A_ORDER"; + } else { + item = &(store->bsip); + strcpy( parprefix, "B_" ); + order_kwd = "B_ORDER"; + } + +/* 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. */ + order = 0; + for( p = 0; p <= pmax; p++ ){ + for( q = 0; q <= qmax; q++ ){ + +/* Get the polynomial coefficient for this combination of powers. If it + is good, format the keyword name and store it in the header. */ + val = GetItem( item, p, q, s, NULL, method, class, status ); + if( val != AST__BAD ) { + SetValue( this, FormatKey( parprefix, p, q, s, status ), &val, + AST__FLOAT, "SIP forward distortion coeff", status ); + if( p + q > order ) order = p + q; + } + } + } + if( order > 0 ) SetValue( this, order_kwd, &order, AST__INT, + "SIP max order", status ); + } + +/* Inverse SIP distortion keywords. Loop over the two spatial axes. */ + for( i = 0; i < 2; i++ ) { + +/* Get a pointer to the FitsStore item holding the values defining this + output. */ + if( i == 0 ) { + item = &(store->apsip); + strcpy( parprefix, "AP_" ); + order_kwd = "AP_ORDER"; + } else { + item = &(store->bpsip); + strcpy( parprefix, "BP_" ); + order_kwd = "BP_ORDER"; + } + +/* 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. */ + order = 0; + for( p = 0; p <= pmax; p++ ){ + for( q = 0; q <= qmax; q++ ){ + +/* Get the polynomial coefficient for this combination of powers. If it + is good, format the keyword name and store it in the header. */ + val = GetItem( item, p, q, s, NULL, method, class, status ); + if( val != AST__BAD ) { + SetValue( this, FormatKey( parprefix, p, q, s, status ), &val, + AST__FLOAT, "SIP inverse distortion coeff", status ); + if( p + q > order ) order = p + q; + } + } + } + if( order > 0 ) SetValue( this, order_kwd, &order, AST__INT, + "SIP inverse max order", 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 Frame 1 +* is the base Frame and describes the required WCS system. Frame +* 2 is the current Frame and describes 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. If the Iwc attribute is non-zero, this +* will be the Mapping from pixel to IWC coordinates. Otherwise it +* will be the mapping from pixel to WCS coordinates. +*/ + +/* 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 dtai; /* TAI-UTC correction in seconds */ + 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 ); + +/* Set the DTAI value. */ + dtai = GetItem( &(store->dtai), 0, 0, s, NULL, method, class, status ); + if( dtai != AST__BAD ) astSetDtai( *frm, dtai ); + +/* The returned Frame is actually a FrameSet in which the base (first) Frame + is the required WCS Frame. The FrameSet contains one other Frame, + which is the Frame representing IWC and is always frame 2, the current + Frame. Create a FrameSet containing these two Frames. */ + if( astGetIwc( this ) ) { + fs = astFrameSet( *frm, "", status ); + astInvert( ret ); + map9 = (AstMapping *) astCmpMap( ret, map1, 1, "", status ); + astInvert( ret ); + map10 = astSimplify( map9 ); + astAddFrame( fs, AST__BASE, map10, iwcfrm ); + +/* Return this FrameSet instead of the Frame. */ + *frm = astAnnul( *frm ); + *frm = (AstFrame *) fs; + +/* Free resources */ + map9 = astAnnul( map9 ); + map10 = astAnnul( map10 ); + +/* Also modify the returned mapping so that it goes from pixel to iwc + instead of to wcs. */ + ret = astAnnul( ret ); + ret = astClone( map1 ); + } + +/* 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. ", AST__DBL_DIG, alphap*AST__DR2D, AST__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). */ + +/* +*att++ +* Name: +* FitsTol + +* Purpose: +* Maximum non-linearity allowed when exporting to FITS-WCS. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute is used when attempting to write a FrameSet to a +* FitsChan using a foreign encoding. It specifies the maximum +* departure from linearity allowed on any axis within the mapping +* from pixel coordinates to Intermediate World Coordinates. It is +* expressed in units of pixels. If an axis of the Mapping is found +* to deviate from linearity by more than this amount, the write +* operation fails. If the linearity test succeeds, a linear +* approximation to the mapping is used to determine the FITS keyword +* values to be placed in the FitsChan. +* +* The default value is one tenth of a pixel. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ +astMAKE_CLEAR(FitsChan,FitsTol,fitstol,-1.0) +astMAKE_GET(FitsChan,FitsTol,double,1,(this->fitstol==-1.0?0.1:this->fitstol)) +astMAKE_SET(FitsChan,FitsTol,double,fitstol,(astMAX(value,0.0))) +astMAKE_TEST(FitsChan,FitsTol,(this->fitstol!=-1.0)) + +/* +*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 )) + +/* SipReplace */ +/* ========== */ + +/* +*att++ +* Name: +* SipReplace + +* Purpose: +* Replace SIP inverse transformation? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute is a boolean value which specifies how SIP keywords +* should be handled when reading a FITS-WCS encoded header using the +c astRead +f AST_READ +* function. See +* http://irsa.ipac.caltech.edu/data/SPITZER/docs/files/spitzer/shupeADASS.pdf +* for more information about SIP headers. If SipReplace is non-zero, +* then any SIP keywords describing the inverse transformation (i.e. from +* WCS to pixel coordinates) are ignored. Instead a new inverse +* transformation is found by performing a fit to the forward +* transformation. The SipReplace attribute can be set to zero to prevent +* this happening. If SipReplace is zero, any SIP keywords describing the +* inverse transformation are used as supplied, rather than being +* replaced using a new fit. The default value is 1. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ +astMAKE_CLEAR(FitsChan,SipReplace,sipreplace,-1) +astMAKE_GET(FitsChan,SipReplace,int,1,(this->sipreplace == -1 ? 1 : this->sipreplace)) +astMAKE_SET(FitsChan,SipReplace,int,sipreplace,( value ? 1 : 0 )) +astMAKE_TEST(FitsChan,SipReplace,( this->sipreplace != -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, this +* scheme has now been added to the registry of FITS conventions +* (http://fits.gsfc.nasa.gov/registry/tpvwcs.html) and headers +* that use this convention are created by the SCAMP utility +* (http://www.astromatic.net/software/scamp) and the Dark Energy +* Camera at NOAO. +* +* 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)) + +/* SipOK */ +/* ===== */ + +/* +*att++ +* Name: +* SipOK + +* Purpose: +* Use Spitzer Space Telescope keywords to define distortion? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute is a boolean value which specifies whether to include +* support for the "SIP" scheme, which can be used to add distortion to +* basic FITS-WCS projections. This scheme was first defined by the +* Spitzer Space Telescope and is described in the following document: +* http://irsa.ipac.caltech.edu/data/SPITZER/docs/files/spitzer/shupeADASS.pdf +* The default for SipOK is 1. +* +* When using +c astRead +f AST_READ +* to read a FITS-WCS encoded header, a suitable PolyMap will always be +* included in the returned FrameSet if the header contains SIP +* keywords, regardless of the value of the SipOK attribute. The PolyMap +* will be immediately before the MatrixMap that corresponds to the FITS-WCS +* PC or CD matrix. +* +* When using +c astWrite +f AST_WRITE +* to write a FrameSet to a FITS-WCS encoded header, suitable SIP +* keywords will be included in the header if the FrameSet contains a +* PolyMap immediately before the MatrixMap that corresponds to the +* FITS-WCS PC or CD matrix, but only if the SipOK attribute is non-zero. +* If the FrameSet contains a PolyMap but SipOK is zero, then an attempt +* will be made to write out the FrameSet without SIP keywords using a +* linear approximation to the pixel-to-IWC mapping. If this fails +* because the Mapping exceeds the linearity requirement specified by +* attribute FitsTol, +c astWrite +f AST_WRITE +* will return zero, indicating that the FrameSet could not be written +* out. Note, SIP headers can only be produced for axes that form part +* of a SkyFrame. +* +* Note, the SIP distortion scheme is independent of the TPV/TPN +* distortion schemes (see attribute PolyTan). A FITS-WCS header could +* in principle, contain keywords for both schemes although this is unlikely. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ +astMAKE_CLEAR(FitsChan,SipOK,sipok,-INT_MAX) +astMAKE_SET(FitsChan,SipOK,int,sipok,value) +astMAKE_TEST(FitsChan,SipOK,( this->sipok != -INT_MAX )) +astMAKE_GET(FitsChan,SipOK,int,1,(this->sipok == -INT_MAX ? 1 : this->sipok)) + +/* 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,AST__DBL_DIG) +astMAKE_GET(FitsChan,FitsDigits,int,AST__DBL_DIG,this->fitsdigits) +astMAKE_SET(FitsChan,FitsDigits,int,fitsdigits,value) +astMAKE_TEST(FitsChan,FitsDigits,( this->fitsdigits != AST__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 */ + double dval; /* Double 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") ); + +/* SipReplace */ +/* ------ */ + set = TestSipReplace( this, status ); + ival = set ? GetSipReplace( this, status ) : astGetSipReplace( this ); + astWriteInt( channel, "SipReplace", set, 1, ival, "Replace SIP inverse coefficients?" ); + +/* FitsTol */ +/* ------- */ + set = TestFitsTol( this, status ); + dval = set ? GetFitsTol( this, status ) : astGetFitsTol( this ); + astWriteDouble( channel, "FitsTol", set, 1, dval, "[pixel] Max allowed " + "departure from linearity"); + +/* 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") ); + +/* SipOK */ +/* ----- */ + set = TestSipOK( this, status ); + ival = set ? GetSipOK( this, status ) : astGetSipOK( this ); + astWriteInt( channel, "SipOK", set, 0, ival, (ival ? "Use SIP distortion convention": "Ignore SIP keywords") ); + +/* 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->sipreplace = -1; + new->fitstol = -1.0; + new->polytan = -INT_MAX; + new->sipok = -INT_MAX; + new->iwc = -1; + new->clean = -1; + new->fitsdigits = AST__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", AST__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 ); + +/* SipReplace */ +/* ---------- */ + new->sipreplace = astReadInt( channel, "sipreplace", -1 ); + if ( TestSipReplace( new, status ) ) SetSipReplace( new, new->sipreplace, status ); + +/* FitsTol */ +/* ------- */ + new->fitstol = astReadDouble( channel, "fitstol", -1.0 ); + if ( TestFitsTol( new, status ) ) SetFitsTol( new, new->fitstol, status ); + +/* PolyTan */ +/* ------- */ + new->polytan = astReadInt( channel, "polytan", -1 ); + if ( TestPolyTan( new, status ) ) SetPolyTan( new, new->polytan, status ); + +/* SipOK */ +/* ----- */ + new->sipok = astReadInt( channel, "sipok", -1 ); + if ( TestSipOK( new, status ) ) SetSipOK( new, new->sipok, 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/ast/fitschan.h b/ast/fitschan.h new file mode 100644 index 0000000..c3e9329 --- /dev/null +++ b/ast/fitschan.h @@ -0,0 +1,933 @@ +#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. +* 20-NOV-2017 (DSB): +* Added SipReplace attribute. +*- +*/ + +/* 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 sipok; /* Use SIP distortion convention? */ + int carlin; /* Use linear CAR mappings? */ + int sipreplace; /* Replace SIP inverse coefficients? */ + double fitstol; /* Max departure from linearity, in pixels */ + 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 (* GetSipReplace)( AstFitsChan *, int * ); + int (* TestSipReplace)( AstFitsChan *, int * ); + void (* SetSipReplace)( AstFitsChan *, int, int * ); + void (* ClearSipReplace)( AstFitsChan *, int * ); + + double (* GetFitsTol)( AstFitsChan *, int * ); + int (* TestFitsTol)( AstFitsChan *, int * ); + void (* SetFitsTol)( AstFitsChan *, double, int * ); + void (* ClearFitsTol)( 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 (* GetSipOK)( AstFitsChan *, int * ); + int (* TestSipOK)( AstFitsChan *, int * ); + void (* SetSipOK)( AstFitsChan *, int, int * ); + void (* ClearSipOK)( 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 astGetSipReplace_( AstFitsChan *, int * ); + int astTestSipReplace_( AstFitsChan *, int * ); + void astSetSipReplace_( AstFitsChan *, int, int * ); + void astClearSipReplace_( AstFitsChan *, int * ); + + int astGetSipOK_( AstFitsChan *, int * ); + int astTestSipOK_( AstFitsChan *, int * ); + void astSetSipOK_( AstFitsChan *, int, int * ); + void astClearSipOK_( AstFitsChan *, int * ); + + int astGetCarLin_( AstFitsChan *, int * ); + int astTestCarLin_( AstFitsChan *, int * ); + void astSetCarLin_( AstFitsChan *, int, int * ); + void astClearCarLin_( AstFitsChan *, int * ); + + double astGetFitsTol_( AstFitsChan *, int * ); + int astTestFitsTol_( AstFitsChan *, int * ); + void astSetFitsTol_( AstFitsChan *, double, int * ); + void astClearFitsTol_( 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 astClearSipOK(this) \ +astINVOKE(V,astClearSipOK_(astCheckFitsChan(this),STATUS_PTR)) +#define astGetSipOK(this) \ +astINVOKE(V,astGetSipOK_(astCheckFitsChan(this),STATUS_PTR)) +#define astSetSipOK(this,value) \ +astINVOKE(V,astSetSipOK_(astCheckFitsChan(this),value,STATUS_PTR)) +#define astTestSipOK(this) \ +astINVOKE(V,astTestSipOK_(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 astClearSipReplace(this) \ +astINVOKE(V,astClearSipReplace_(astCheckFitsChan(this),STATUS_PTR)) +#define astGetSipReplace(this) \ +astINVOKE(V,astGetSipReplace_(astCheckFitsChan(this),STATUS_PTR)) +#define astSetSipReplace(this,siprep) \ +astINVOKE(V,astSetSipReplace_(astCheckFitsChan(this),siprep,STATUS_PTR)) +#define astTestSipReplace(this) \ +astINVOKE(V,astTestSipReplace_(astCheckFitsChan(this),STATUS_PTR)) + +#define astClearFitsTol(this) \ +astINVOKE(V,astClearFitsTol_(astCheckFitsChan(this),STATUS_PTR)) +#define astGetFitsTol(this) \ +astINVOKE(V,astGetFitsTol_(astCheckFitsChan(this),STATUS_PTR)) +#define astSetFitsTol(this,fitstl) \ +astINVOKE(V,astSetFitsTol_(astCheckFitsChan(this),fitstl,STATUS_PTR)) +#define astTestFitsTol(this) \ +astINVOKE(V,astTestFitsTol_(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/ast/fitstable.c b/ast/fitstable.c new file mode 100644 index 0000000..148431c --- /dev/null +++ b/ast/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/ast/fitstable.h b/ast/fitstable.h new file mode 100644 index 0000000..a2633b6 --- /dev/null +++ b/ast/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/ast/fkeymap.c b/ast/fkeymap.c new file mode 100644 index 0000000..2eea900 --- /dev/null +++ b/ast/fkeymap.c @@ -0,0 +1,1441 @@ +/* +*+ +* 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 ); + ) +} + +F77_LOGICAL_FUNCTION(ast_mapgetc)( 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_MAPGETC", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + value = NULL; + RESULT = astMapGetC( 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; +} + diff --git a/ast/flutmap.c b/ast/flutmap.c new file mode 100644 index 0000000..4ff5f0a --- /dev/null +++ b/ast/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/ast/fluxframe.c b/ast/fluxframe.c new file mode 100644 index 0000000..ee62fa3 --- /dev/null +++ b/ast/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", AST__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/ast/fluxframe.h b/ast/fluxframe.h new file mode 100644 index 0000000..c030142 --- /dev/null +++ b/ast/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/ast/fmapping.c b/ast/fmapping.c new file mode 100644 index 0000000..16f2b75 --- /dev/null +++ b/ast/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/ast/fmathmap.c b/ast/fmathmap.c new file mode 100644 index 0000000..88ac236 --- /dev/null +++ b/ast/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/ast/fmatrixmap.c b/ast/fmatrixmap.c new file mode 100644 index 0000000..8fff328 --- /dev/null +++ b/ast/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/ast/fnormmap.c b/ast/fnormmap.c new file mode 100644 index 0000000..f7fa127 --- /dev/null +++ b/ast/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/ast/fnullregion.c b/ast/fnullregion.c new file mode 100644 index 0000000..6e7d61f --- /dev/null +++ b/ast/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/ast/fobject.c b/ast/fobject.c new file mode 100644 index 0000000..c718986 --- /dev/null +++ b/ast/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/ast/fpcdmap.c b/ast/fpcdmap.c new file mode 100644 index 0000000..bd5a335 --- /dev/null +++ b/ast/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/ast/fpermmap.c b/ast/fpermmap.c new file mode 100644 index 0000000..d280958 --- /dev/null +++ b/ast/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/ast/fplot.c b/ast/fplot.c new file mode 100644 index 0000000..a5e0b47 --- /dev/null +++ b/ast/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/ast/fplot3d.c b/ast/fplot3d.c new file mode 100644 index 0000000..4246f50 --- /dev/null +++ b/ast/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/ast/fpointlist.c b/ast/fpointlist.c new file mode 100644 index 0000000..9644fdc --- /dev/null +++ b/ast/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/ast/fpolygon.c b/ast/fpolygon.c new file mode 100644 index 0000000..12247d7 --- /dev/null +++ b/ast/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/ast/fpolymap.c b/ast/fpolymap.c new file mode 100644 index 0000000..687e9f6 --- /dev/null +++ b/ast/fpolymap.c @@ -0,0 +1,159 @@ +/* +*+ +* 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; +} + +F77_SUBROUTINE(ast_polycoeffs)( INTEGER(THIS), + LOGICAL(FORWARD), + INTEGER(NEL), + DOUBLE_ARRAY(COEFFS), + INTEGER(NCOEFF), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_LOGICAL(FORWARD) + GENPTR_INTEGER(NEL) + GENPTR_DOUBLE_ARRAY(COEFFS) + GENPTR_INTEGER(NCOEFF) + + astAt( "AST_POLYCOEFFS", NULL, 0 ); + astWatchSTATUS( + astPolyCoeffs( astI2P( *THIS ), F77_ISTRUE( *FORWARD ), *NEL, COEFFS, NCOEFF ); + ) +} + + + diff --git a/ast/fprism.c b/ast/fprism.c new file mode 100644 index 0000000..7dc70b7 --- /dev/null +++ b/ast/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/ast/frame.c b/ast/frame.c new file mode 100644 index 0000000..bd70cdd --- /dev/null +++ b/ast/frame.c @@ -0,0 +1,16067 @@ +/* +*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 +* - Dtai: Difference between the TAI and UTC timescale +* - 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 - astAxNorm: Normalises an array of 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_AXNORM: Normalises an array of 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. +* 26-OCT-2016 (DSB): +* Added method astAxNorm. +* 11-JAN-2017 (GSB): +* Add Dtai 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 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 AxNorm( AstFrame *, int, int, int, double *, 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 GetDtai( AstFrame *, int * ); +static int TestDtai( AstFrame *, int * ); +static void ClearDtai( AstFrame *, int * ); +static void SetDtai( 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 void AxNorm( AstFrame *this, int axis, int oper, int nval, + double *values, int *status ){ +/* +*++ +* Name: +c astAxNorm +f AST_AXNORM + +* Purpose: +* Normalise an array of axis values. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c void astAxNorm( AstFrame *this, int axis, int oper, int nval, +c double *values, int *status ) +f CALL AST_AXNORM( THIS, AXIS, OPER, NVAL, VALUES, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function +f This routine +* modifies a supplied array of axis values so that they are normalised +* in the manner indicated by +c parameter "oper". +f argument OPER. +* +* No normalisation is possible for a simple Frame and so the supplied +* values are returned unchanged. However, this may not be the case for +* specialised sub-classes of Frame. For instance, a SkyFrame has a +* discontinuity at zero longitude and so a longitude value can be +* expressed in the range [-Pi,+PI] or the range [0,2*PI]. See the +* "Applicability:" section below for details. + +* 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 oper +f OPER = INTEGER (Given) +* Indicates the type of normalisation to be applied. If zero is +* supplied, the normalisation will be the same as that performed by +c function astNorm. +f routine AST_NORM. +* If 1 is supplied, the normalisation will be chosen automatically +* so that the resulting list has the smallest range. +c nval +f NVAL = INTEGER (Given) +* The number of points in the values array. +c values +f VALUES( NVAL ) = DOUBLE PRECISION (Given and Returned) +* On entry, the axis values to be normalised. Modified on exit to +* hold the normalised values. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* SkyFrame +c If "oper" +f If OPER +* is 0, longitude values are returned in the range [0,2*PI]. +c If "oper" +f If OPER +* is 1, longitude values are returned in either the range +* [0,2*PI] or [-PI,PI]. The choice is made so that that the +* resulting list has the smallest range. Latitude values are +* always returned in the range [-PI,PI]. +* All other classes of Frame +* The supplied axis values are returned unchanged. + +*-- + +* 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 */ + +/* 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, 1, "astAxNorm" ); + ax = astGetAxis( this, axis - 1 ); + +/* Validate ther "oper" value. */ + if( ( oper < 0 || oper > 1 ) && astOK ) { + astError( AST__OPRIN, "astAxNorm(%s): Invalid operation %d.", status, + astGetClass( this ), oper ); + } + +/* Use the AxisNormValues method associated with the Axis. */ + if( astOK ) astAxisNormValues( ax, oper, nval, values ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); +} + +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 ); + +/* Dtai */ +/* --- */ + } else if ( !strcmp( attrib, "dtai" ) ) { + astClearDtai( 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 > AST__DBL_DIG ) ? AST__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 ), AST__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", AST__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", AST__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", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Dtai. */ +/* ---- */ + } else if ( !strcmp( attrib, "dtai" ) ) { + dval = astGetDtai( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Dut1. */ +/* ---- */ + } else if ( !strcmp( attrib, "dut1" ) ) { + dval = astGetDut1( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__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->AxNorm = AxNorm; + 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->ClearDtai = ClearDtai; + vtab->GetDtai = GetDtai; + vtab->SetDtai = SetDtai; + vtab->TestDtai = TestDtai; + + 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(Dtai); + 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, AST__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, + AST__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 ); + +/* Dtai. */ +/* ---- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "dtai= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetDtai( 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 ); + +/* Dtai. */ +/* ---- */ + } else if ( !strcmp( attrib, "dtai" ) ) { + result = astTestDtai( 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: +* Dtai + +* Purpose: +* The TAI-UTC correction. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute specifies the difference between TAI and UTC (i.e. +* the number of leap seconds) at the moment corresponding to the +* Frame's Epoch value. The default value of AST__BAD causes the +* number of leap seconds to be determined from an internal look-up +* table, which is kept up-to-date manually by the AST development team. +* Therefore it is only necessary to assign a value to this attribute +* if the version of AST in use is so old that it does not include all +* leap seconds that occurred prior to the time represented by the +* Frame's Epoch value. + +* Applicability: +* Frame +* All Frames have this attribute. + +*att-- +*/ +/* The TAI-UTC correction, in seconds. Has a value of AST__BAD when not set. */ +astMAKE_CLEAR(Frame,Dtai,dtai,AST__BAD) +astMAKE_GET(Frame,Dtai,double,AST__BAD,(this->dtai)) +astMAKE_SET(Frame,Dtai,double,dtai,value) +astMAKE_TEST(Frame,Dtai,( this->dtai != AST__BAD )) + +/* +*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 change 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. +*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: +* Floating point. + +* 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)" ); + +/* Dtai*/ +/* ---- */ + set = TestDtai( this, status ); + dval = set ? GetDtai( this, status ) : astGetDtai( this ); + astWriteDouble( channel, "Dtai", set, 0, dval, "TAI-UTC in seconds" ); + +/* 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->dtai = 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 ); + +/* Dtai. */ +/* ---- */ + new->dtai = astReadDouble( channel, "dtai", AST__BAD ); + if ( TestDtai( new, status ) ) SetDtai( new, new->dtai, 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 ); +} +void astAxNorm_( AstFrame *this, int axis, int oper, int nval, double *values, + int *status ){ + if ( !astOK ) return; + return (**astMEMBER(this,Frame,AxNorm))( this, axis, oper, nval, values, 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/ast/frame.f b/ast/frame.f new file mode 100644 index 0000000..d88402d --- /dev/null +++ b/ast/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/ast/frame.h b/ast/frame.h new file mode 100644 index 0000000..91a628e --- /dev/null +++ b/ast/frame.h @@ -0,0 +1,1459 @@ +#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. +* 26-OCT-2016 (DSB): +* Added method astAxNorm. +* 11-JAN-2017 (GSB): +* Add Dtai 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 dtai; /* TAI-UTC in seconds */ + 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 * ); + void (* AxNorm)( AstFrame *, int, int, int, 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 (* GetDtai)( AstFrame *, int * ); + int (* TestDtai)( AstFrame *, int * ); + void (* ClearDtai)( AstFrame *, int * ); + void (* SetDtai)( 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 astAxNorm_( AstFrame *, int, int, int, double *, 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 astGetDtai_( AstFrame *, int * ); +int astTestDtai_( AstFrame *, int * ); +void astClearDtai_( AstFrame *, int * ); +void astSetDtai_( 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 astAxNorm(this,axis,oper,nval,values) \ +astINVOKE(V,astAxNorm_(astCheckFrame(this),axis,oper,nval,values,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 astClearDtai(this) \ +astINVOKE(V,astClearDtai_(astCheckFrame(this),STATUS_PTR)) +#define astGetDtai(this) \ +astINVOKE(V,astGetDtai_(astCheckFrame(this),STATUS_PTR)) +#define astSetDtai(this,value) \ +astINVOKE(V,astSetDtai_(astCheckFrame(this),value,STATUS_PTR)) +#define astTestDtai(this) \ +astINVOKE(V,astTestDtai_(astCheckFrame(this),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/ast/frames.pdf b/ast/frames.pdf new file mode 100644 index 0000000..6ac10e5 Binary files /dev/null and b/ast/frames.pdf differ diff --git a/ast/frameset.c b/ast/frameset.c new file mode 100644 index 0000000..d3bcc06 --- /dev/null +++ b/ast/frameset.c @@ -0,0 +1,13337 @@ +/* +*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 stored 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. +* 26-OCT-2016 (DSB): +* Override the AxNorm method. +* 07-APR-2017 (GSB): +* Override Dtai and Dut1 accessor methods. +* 07-NOV-2017 (GSB): +* In AddFrame, check to see if a FrameSet is supplied in place of +* a Mapping and if so, use the FrameSet's base -> current Mapping +* instead. +* 11-DEC-2017 (DSB): +* Added method astGetNode. +*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 GetNode( AstFrameSet *, int, int *, int *, AstMapping **, int *, 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 AxNorm( AstFrame *, int, int, int, double *, 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 GetDtai( AstFrame *, int * ); +static int TestDtai( AstFrame *, int * ); +static void ClearDtai( AstFrame *, int * ); +static void SetDtai( 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 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 *map0, + 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 *map; /* The supplied Mapping */ + 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; + +/* If a FrameSet was supplied instead of a Mapping, use the Mapping from + its base Frame to its current Frame. */ + if( astIsAFrameSet( map0 ) ) { + map = astGetMapping( map0, AST__BASE, AST__CURRENT ); + } else { + map = astClone( map0 ); + } + +/* 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; + } + } + } + } + +/* Annul the local pointer to the supplied Mapping. */ + map = astAnnul( map ); +} + +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 ) +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 void AxNorm( AstFrame *this_frame, int axis, int oper, int nval, + double *values, int *status ){ +/* +* Name: +* AxNorm + +* Purpose: +* Normalise an array of axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void AxNorm( AstFrame *this, int axis, int oper, int nval, +* double *values, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astAxNorm +* method inherited from the Frame class). + +* Description: +* This function modifies a supplied array of axis values so that +* they are normalised in the manner indicated by parameter "oper". +* +* No normalisation is possible for a simple Frame and so the supplied +* values are returned unchanged. However, this may not be the case for +* specialised sub-classes of Frame. For instance, a SkyFrame has a +* discontinuity at zero longitude and so a longitude value can be +* expressed in the range [-Pi,+PI] or the range [0,2*PI]. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The index of the axis to which the supplied values refer. The +* first axis has index 1. +* oper +* Indicates the type of normalisation to be applied. If zero is +* supplied, the normalisation will be the same as that performed by +* function astNorm. If 1 is supplied, the normalisation will be +* chosen automatically so that the resulting list has the smallest +* range. +* nval +* The number of points in the values array. +* values +* On entry, the axis values to be normalised. Modified on exit to +* hold the normalised values. +* 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. */ + (void) astValidateAxis( this, axis - 1, 1, "astAxNorm" ); + +/* Obtain a pointer to the FrameSet's current Frame and invoke the + astAxNorm method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + astAxNorm( fr, axis, oper, nval, values ); + fr = astAnnul( fr ); +} + +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 GetNode( AstFrameSet *this, int inode, int *nnodes, + int *iframe, AstMapping **map, int *parent, + int *status ) { +/* +*+ +* Name: +* astGetNode + +* Purpose: +* Get information about a single node in a FrameSet tree. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frameset.h" +* int astGetNode( AstFrameSet *this, int inode, int *nnodes, +* int *iframe, AstMapping **map, int *parent ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function returns information about a specified node in a +* FrameSet. It is documented as protected, because it should not +* be used in general purpose software since it depends on the internal +* details of the FrameSet class. However, it is in fact public so that +* it can be used in external software that needs to know about the +* internal structure of a FrameSet (for instance, a graphical FrameSet +* visualisation system). + +* Parameters: +* this +* Pointer to the FrameSet. +* inode +* The zero-based index of the required node. +* nnodes +* Address of an int returned holding the number of nodes defined +* in the FrameSet. +* iframe +* Address of an int returned holding the one-based index of the +* Frame associated with the node. AST__NOFRAME is returned if the +* node has no Frame. +* map +* Address of a Mapping pointer returned holding a pointer to a +* deep copy of the Mapping, if any, from the parent node to the +* requested node. NULL is returned if the node has no parent. +* parent +* Address of an int returned holding the zero-based index of the +* node from which the requested node is derived. -1 is returned if +* the requested node has no parent (i.e. is the root of the tree). + +* Returned Value: +* A non-zero value is returned if the "inode" value is within bounds. +* Otherwise, zero is returned. + +*- +*/ + +/* Local Variables: */ + int jframe; + int result; + +/* Initialise returned values. */ + *nnodes = 0; + *iframe = AST__NOFRAME; + *map = NULL; + *parent = -1; + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Return the number of nodes. */ + *nnodes = this->nnode; + +/* Check the index of the requested node. */ + if( inode >= 0 && inode < this->nnode ) { + +/* Get the index of the Frame - if any - associated with the node. */ + for( jframe = 0; jframe < this->nframe; jframe++ ) { + if( this->node[ jframe ] == inode ) { + *iframe = jframe + 1; + break; + } + } + +/* Get the Mapping - if any - associated with the node. The root node - + node zero - has no mapping or parent node. */ + if( inode > 0 ) { + *map = astCopy( this->map[ inode - 1 ] ); + if( astGetInvert( *map ) != this->invert[ inode - 1 ] ) { + astSetInvert( *map, this->invert[ inode - 1 ] ); + } + +/* The index of the parent node. */ + *parent = this->link[ inode - 1 ]; + } + +/* Indicate success. */ + result = 1; + } + + 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->GetNode = GetNode; + 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->AxNorm = AxNorm; + 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->GetDtai = GetDtai; + frame->SetDtai = SetDtai; + frame->TestDtai = TestDtai; + frame->ClearDtai = ClearDtai; + + frame->GetDut1 = GetDut1; + frame->SetDut1 = SetDut1; + frame->TestDut1 = TestDut1; + frame->ClearDut1 = ClearDut1; + + 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 ) +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 attribute 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 attribute 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 are its 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 astAddVariant +f AST_ADDVARIANT +* 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(Dtai,double) +MAKE_SET(Dtai,double) +MAKE_TEST(Dtai) +MAKE_CLEAR(Dtai) + +MAKE_GET(Dut1,double) +MAKE_SET(Dut1,double) +MAKE_TEST(Dut1) +MAKE_CLEAR(Dut1) + +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 ); +} +int astGetNode_( AstFrameSet *this, int inode, int *nnodes, + int *iframe, AstMapping **map, int *parent, + int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,FrameSet,GetNode))( this, inode, nnodes, + iframe, map, parent, 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 *, ... ); +int astGetNodeId_( AstFrameSet *, int, int *, int *, AstMapping **, int *, int *); + +/* 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 ); +} + +int astGetNodeId_( AstFrameSet *this, int inode, int *nnodes, + int *iframe, AstMapping **map, int *parent, + int *status ) { +/* +*+ +* Name: +* astGetNode + +* Purpose: +* Get information about a single node in a FrameSet tree. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frameset.h" +* int astGetNode( AstFrameSet *this, int inode, int *nnodes, +* int *iframe, AstMapping **map, int *parent ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function returns information about a specified node in a +* FrameSet. It is documented as protected, because it should not +* be used in general purpose software since it depends on the internal +* details of the FrameSet class. However, it is in fact public so that +* it can be used in external software that needs to know about the +* internal structure of a FrameSet (for instance, a graphical FrameSet +* visualisation system). + +* Parameters: +* this +* Pointer to the FrameSet. +* inode +* The zero-based index of the required node. +* nnodes +* Address of an int returned holding the number of nodes defined +* in the FrameSet. +* frame +* Address of a Frame pointer returned holding a pointer to a deep +* copy of the Frame, if any, associated with the node. NULL +* is returned if the node has no Frame. +* map +* Address of a Mapping pointer returned holding a pointer to a +* deep copy of the Mapping, if any, from the parent node to the +* requested node. NULL is returned if the node has no parent. +* parent +* Address of an int returned holding the zero-based index of the +* node from which the requested node is derived. -1 is returned if +* the requested node has no parent (i.e. is the root of the tree). + +* Returned Value: +* A non-zero value is returned if the "inode" value is within bounds. +* Otherwise, zero is returned. + +*- +*/ + +/* Local Variables: */ + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the normal astGetNode_ function. */ + result = astGetNode_( this, inode, nnodes, iframe, map, parent, status ); + +/* Return an ID value for the Mapping. */ + *map = astMakeId( *map ); + +/* Return the result. */ + return result; +} + + diff --git a/ast/frameset.h b/ast/frameset.h new file mode 100644 index 0000000..6629cfb --- /dev/null +++ b/ast/frameset.h @@ -0,0 +1,714 @@ +/* +*+ +* 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 * ); + int (* GetNode)( AstFrameSet *, int, int *, int *, AstMapping **, int *, 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 * ); +int astGetNodeId_( AstFrameSet *, int, int *, int *, AstMapping **, 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 * ); +int astGetNode_( AstFrameSet *, int, 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 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)) +#define astGetNode(this,inode,nnodes,iframe,map,parent) \ +astINVOKE(V,astGetNodeId_(astCheckFrameSet(this),inode,nnodes,iframe,map,parent,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/ast/frameset.pdf b/ast/frameset.pdf new file mode 100644 index 0000000..7730800 Binary files /dev/null and b/ast/frameset.pdf differ diff --git a/ast/fratemap.c b/ast/fratemap.c new file mode 100644 index 0000000..62588ab --- /dev/null +++ b/ast/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/ast/fregion.c b/ast/fregion.c new file mode 100644 index 0000000..2c3b4ef --- /dev/null +++ b/ast/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/ast/fronta.pdf b/ast/fronta.pdf new file mode 100644 index 0000000..fc0cd6e Binary files /dev/null and b/ast/fronta.pdf differ diff --git a/ast/fronta_bw.pdf b/ast/fronta_bw.pdf new file mode 100644 index 0000000..099405e Binary files /dev/null and b/ast/fronta_bw.pdf differ diff --git a/ast/frontb.pdf b/ast/frontb.pdf new file mode 100644 index 0000000..e01d2cf Binary files /dev/null and b/ast/frontb.pdf differ diff --git a/ast/frontb_bw.pdf b/ast/frontb_bw.pdf new file mode 100644 index 0000000..d5a0e67 Binary files /dev/null and b/ast/frontb_bw.pdf differ diff --git a/ast/frontc.pdf b/ast/frontc.pdf new file mode 100644 index 0000000..ec9228c Binary files /dev/null and b/ast/frontc.pdf differ diff --git a/ast/frontc_bw.pdf b/ast/frontc_bw.pdf new file mode 100644 index 0000000..d6dd1c8 Binary files /dev/null and b/ast/frontc_bw.pdf differ diff --git a/ast/fsalign.pdf b/ast/fsalign.pdf new file mode 100644 index 0000000..e1c0c97 Binary files /dev/null and b/ast/fsalign.pdf differ diff --git a/ast/fsconvert.pdf b/ast/fsconvert.pdf new file mode 100644 index 0000000..1665762 Binary files /dev/null and b/ast/fsconvert.pdf differ diff --git a/ast/fselectormap.c b/ast/fselectormap.c new file mode 100644 index 0000000..4ecc4a7 --- /dev/null +++ b/ast/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/ast/fsexample.pdf b/ast/fsexample.pdf new file mode 100644 index 0000000..4b7d1df Binary files /dev/null and b/ast/fsexample.pdf differ diff --git a/ast/fshiftmap.c b/ast/fshiftmap.c new file mode 100644 index 0000000..d876071 --- /dev/null +++ b/ast/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/ast/fskyframe.c b/ast/fskyframe.c new file mode 100644 index 0000000..ebdbaaa --- /dev/null +++ b/ast/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/ast/fslamap.c b/ast/fslamap.c new file mode 100644 index 0000000..0fa631b --- /dev/null +++ b/ast/fslamap.c @@ -0,0 +1,122 @@ +/* +*+ +* 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), + INTEGER(NARG), + DOUBLE_ARRAY(ARGS), + INTEGER(STATUS) + TRAIL(CVT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(CVT) + GENPTR_INTEGER(NARG) + GENPTR_DOUBLE_ARRAY(ARGS) + char *cvt; + + astAt( "AST_SLAADD", NULL, 0 ); + astWatchSTATUS( + cvt = astString( CVT, CVT_length ); + astSlaAdd( astI2P( *THIS ), cvt, *NARG, 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/ast/fsmerge.pdf b/ast/fsmerge.pdf new file mode 100644 index 0000000..33fe7dc Binary files /dev/null and b/ast/fsmerge.pdf differ diff --git a/ast/fspecfluxframe.c b/ast/fspecfluxframe.c new file mode 100644 index 0000000..fb1c218 --- /dev/null +++ b/ast/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/ast/fspecframe.c b/ast/fspecframe.c new file mode 100644 index 0000000..c45c7e3 --- /dev/null +++ b/ast/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/ast/fspecmap.c b/ast/fspecmap.c new file mode 100644 index 0000000..f96db73 --- /dev/null +++ b/ast/fspecmap.c @@ -0,0 +1,124 @@ +/* +*+ +* 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), + INTEGER(NARG), + DOUBLE_ARRAY(ARGS), + INTEGER(STATUS) + TRAIL(CVT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(CVT) + GENPTR_INTEGER(NARG) + GENPTR_DOUBLE_ARRAY(ARGS) + char *cvt; + + astAt( "AST_SPECADD", NULL, 0 ); + astWatchSTATUS( + cvt = astString( CVT, CVT_length ); + astSpecAdd( astI2P( *THIS ), cvt, *NARG, 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/ast/fsphmap.c b/ast/fsphmap.c new file mode 100644 index 0000000..439fec9 --- /dev/null +++ b/ast/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/ast/fsremap.pdf b/ast/fsremap.pdf new file mode 100644 index 0000000..3554420 Binary files /dev/null and b/ast/fsremap.pdf differ diff --git a/ast/fstc.c b/ast/fstc.c new file mode 100644 index 0000000..50c53bb --- /dev/null +++ b/ast/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/ast/fstccatalogentrylocation.c b/ast/fstccatalogentrylocation.c new file mode 100644 index 0000000..5c88aa0 --- /dev/null +++ b/ast/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/ast/fstcobsdatalocation.c b/ast/fstcobsdatalocation.c new file mode 100644 index 0000000..767ebb4 --- /dev/null +++ b/ast/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/ast/fstcresourceprofile.c b/ast/fstcresourceprofile.c new file mode 100644 index 0000000..04484f3 --- /dev/null +++ b/ast/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/ast/fstcschan.c b/ast/fstcschan.c new file mode 100644 index 0000000..9f47469 --- /dev/null +++ b/ast/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/ast/fstcsearchlocation.c b/ast/fstcsearchlocation.c new file mode 100644 index 0000000..aa74baf --- /dev/null +++ b/ast/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/ast/fswitchmap.c b/ast/fswitchmap.c new file mode 100644 index 0000000..0159420 --- /dev/null +++ b/ast/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/ast/ftable.c b/ast/ftable.c new file mode 100644 index 0000000..5656531 --- /dev/null +++ b/ast/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/ast/ftemplateclass.c b/ast/ftemplateclass.c new file mode 100644 index 0000000..28dca89 --- /dev/null +++ b/ast/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/ast/ftimeframe.c b/ast/ftimeframe.c new file mode 100644 index 0000000..f164c26 --- /dev/null +++ b/ast/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/ast/ftimemap.c b/ast/ftimemap.c new file mode 100644 index 0000000..a9929f2 --- /dev/null +++ b/ast/ftimemap.c @@ -0,0 +1,122 @@ +/* +*+ +* 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), + INTEGER(NARG), + DOUBLE_ARRAY(ARGS), + INTEGER(STATUS) + TRAIL(CVT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(CVT) + GENPTR_INTEGER(NARG) + GENPTR_DOUBLE_ARRAY(ARGS) + char *cvt; + + astAt( "AST_TIMEADD", NULL, 0 ); + astWatchSTATUS( + cvt = astString( CVT, CVT_length ); + astTimeAdd( astI2P( *THIS ), cvt, *NARG, 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/ast/ftranmap.c b/ast/ftranmap.c new file mode 100644 index 0000000..155439b --- /dev/null +++ b/ast/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/ast/funitmap.c b/ast/funitmap.c new file mode 100644 index 0000000..986a82d --- /dev/null +++ b/ast/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/ast/funitnormmap.c b/ast/funitnormmap.c new file mode 100644 index 0000000..8e88009 --- /dev/null +++ b/ast/funitnormmap.c @@ -0,0 +1,105 @@ +/* +*+ +* Name: +* fshiftmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST UnitNormMap 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 UnitNormMap class. + +* Routines Defined: +* AST_ISAUNITNORMMAP +* AST_UNITNORMMAP + +* Copyright: +* Copyright (C) 2016 AURA/LSST +* 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) +* TIMJ: Tim Jenness (LSST) + +* 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 "unitnormmap.h" /* C interface to the UnitNormMap class */ + +F77_LOGICAL_FUNCTION(ast_isaunitnormmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAUNITNORMMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAUnitNormMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_unitnormmap)( INTEGER(NCOORDS), + DOUBLE(CENTRE), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(NCOORDS) + GENPTR_DOUBLE(CENTRE) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_UNITNORMMAP", 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( astUnitNormMap( *NCOORDS, CENTRE, "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/ast/fwcsmap.c b/ast/fwcsmap.c new file mode 100644 index 0000000..0b1e3a3 --- /dev/null +++ b/ast/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/ast/fwinmap.c b/ast/fwinmap.c new file mode 100644 index 0000000..41338a0 --- /dev/null +++ b/ast/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/ast/fxmlchan.c b/ast/fxmlchan.c new file mode 100644 index 0000000..17efc50 --- /dev/null +++ b/ast/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/ast/fzoommap.c b/ast/fzoommap.c new file mode 100644 index 0000000..d2c8e48 --- /dev/null +++ b/ast/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/ast/getatt b/ast/getatt new file mode 100755 index 0000000..fb8bc78 --- /dev/null +++ b/ast/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/ast/getnewversion b/ast/getnewversion new file mode 100644 index 0000000..b05f6d0 --- /dev/null +++ b/ast/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/ast/globals.c b/ast/globals.c new file mode 100644 index 0000000..8f43a9a --- /dev/null +++ b/ast/globals.c @@ -0,0 +1,253 @@ +#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( ChebyMap ); + 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( UnitNormMap ); + 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/ast/globals.h b/ast/globals.h new file mode 100644 index 0000000..11fbd6b --- /dev/null +++ b/ast/globals.h @@ -0,0 +1,247 @@ +#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 "chebymap.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 "unitnormmap.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; + AstChebyMapGlobals ChebyMap; + 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; + AstUnitNormMapGlobals UnitNormMap; + 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/ast/grf.h b/ast/grf.h new file mode 100644 index 0000000..04294f3 --- /dev/null +++ b/ast/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/ast/grf3d.c b/ast/grf3d.c new file mode 100644 index 0000000..c0c1a33 --- /dev/null +++ b/ast/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/ast/grf3d.h b/ast/grf3d.h new file mode 100644 index 0000000..6ab7195 --- /dev/null +++ b/ast/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/ast/grf3d_pgplot.c b/ast/grf3d_pgplot.c new file mode 100644 index 0000000..ab25085 --- /dev/null +++ b/ast/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/ast/grf_2.0.c b/ast/grf_2.0.c new file mode 100644 index 0000000..170c191 --- /dev/null +++ b/ast/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/ast/grf_3.2.c b/ast/grf_3.2.c new file mode 100644 index 0000000..d6fc1a1 --- /dev/null +++ b/ast/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/ast/grf_5.6.c b/ast/grf_5.6.c new file mode 100644 index 0000000..3a77f58 --- /dev/null +++ b/ast/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/ast/grf_null.c b/ast/grf_null.c new file mode 100644 index 0000000..ef7a97e --- /dev/null +++ b/ast/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/ast/grf_pgplot.c b/ast/grf_pgplot.c new file mode 100644 index 0000000..d0b1193 --- /dev/null +++ b/ast/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/ast/gridplot.pdf b/ast/gridplot.pdf new file mode 100644 index 0000000..1ba8733 Binary files /dev/null and b/ast/gridplot.pdf differ diff --git a/ast/gridplot_bw.pdf b/ast/gridplot_bw.pdf new file mode 100644 index 0000000..c480928 Binary files /dev/null and b/ast/gridplot_bw.pdf differ diff --git a/ast/grismmap.c b/ast/grismmap.c new file mode 100644 index 0000000..bc615d3 --- /dev/null +++ b/ast/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", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + + } else if ( !strcmp( attrib, "grismnrp" ) ) { + dval = astGetGrismNRP( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + + } else if ( !strcmp( attrib, "grismwaver" ) ) { + dval = astGetGrismWaveR( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + + } else if ( !strcmp( attrib, "grismalpha" ) ) { + dval = astGetGrismAlpha( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + + } else if ( !strcmp( attrib, "grismg" ) ) { + dval = astGetGrismG( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + + } else if ( !strcmp( attrib, "grismm" ) ) { + dval = astGetGrismM( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + + } else if ( !strcmp( attrib, "grismeps" ) ) { + dval = astGetGrismEps( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + + } else if ( !strcmp( attrib, "grismtheta" ) ) { + dval = astGetGrismTheta( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__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/ast/grismmap.h b/ast/grismmap.h new file mode 100644 index 0000000..5655be0 --- /dev/null +++ b/ast/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/ast/interval.c b/ast/interval.c new file mode 100644 index 0000000..a25768d --- /dev/null +++ b/ast/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 to 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/ast/interval.h b/ast/interval.h new file mode 100644 index 0000000..3397e24 --- /dev/null +++ b/ast/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/ast/intramap.c b/ast/intramap.c new file mode 100644 index 0000000..67eebb2 --- /dev/null +++ b/ast/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/ast/intramap.h b/ast/intramap.h new file mode 100644 index 0000000..ac2d8cd --- /dev/null +++ b/ast/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/ast/keymap.c b/ast/keymap.c new file mode 100644 index 0000000..df722ca --- /dev/null +++ b/ast/keymap.c @@ -0,0 +1,10972 @@ +/* +*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 - astMapGetC: Get a scalar or vector entry as a single string. +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_MAPGETC: Get a scalar or vector entry as a single string. +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". +* 9-SEP-2016 (DSB): +* Guard against memory corruption that could occur after making +* 50 (AST__KEYMAP_CONVERTVALUE_MAX_STRINGS) calls to put a string +* into a KeyMap using astMapPutElemC. +* 16-MAR-2017 (DSB): +* Added astMapGetC. +*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 MapGetC( AstKeyMap *, const char *, const char **, 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. If the +* output value is a pointer to a string, it should not be modified +* by the caller in any way. Neither should it be freed by the caller. +* 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->MapGetC = MapGetC; + 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. The +c calling code should never attempt to free the returned pointer +c (for instance, using astFree). +* - 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; +} + +static int MapGetC( AstKeyMap *this, const char *key, const char **value, int *status ) { +/* +*++ +* Name: +c astMapGetC +f AST_MAPGETC + +* Purpose: +* Get a scalar or vector value from a KeyMap as a single string. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "ast.h" +c int astMapGetC( AstKeyMap *this, const char *key, const char **value ); +f RESULT = AST_MAPGETC( THIS, KEY, VALUE, L, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function gets a named value from a KeyMap as a single string. +* For scalar values it is equivalent to +c astMapGet0C. +f AST_MAPGET0C. +* If the value is a vector, the returned string is a comma-separated +* list of the vector elements, enclosed in parentheses. + +* 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 = CHARACTER * ( * ) (Returned) +c Address at which to return a pointer to the required string 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), then the contents of the supplied pointer +f AST_MAPPUTU), then the contents of the supplied string +* are unchanged on exit. +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 astMapGetC() +f AST_MAPGETC = 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. +c - The string pointer returned by astMapGetC 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. The +c calling code should never attempt to free the returned pointer +c (for instance, using astFree). +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + char *buf; /* Buffer for all string values */ + char *cvalue; /* Pointer to returned string */ + char *pb; /* Pointer to start of next section of buffer */ + int i; /* Loop count */ + int mxlen; /* Max length of any string in the vector */ + int nval; /* No. of elements in vector */ + int result; /* Returned flag */ + int nc; /* Current length of total string */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* 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; + } + +/* See how many elements are stored in the requested entry. */ + nval = astMapLength( this, key ); + +/* If the requested entry does not exist, return without further action. */ + if( nval == 0 ) { + result = 0; + +/* If the requested entry is a scalar, just call astMapGet0C to get the + required string. */ + } else if( nval == 1 ) { + result = astMapGet0C( this, key, value ); + +/* If the requested entry is a vector, use astMapGet1C to get all the + strings stored in dynamic memory. */ + } else { + +/* First get the maximum length of any one string stored in the vector. */ + mxlen = astMapLenC( this, key ); + +/* Allocate a buffer big enough to hold all elements assuming they are + all the maximum length. Include room for a terminating null on each + string. */ + buf = astMalloc( (mxlen + 1)*nval*sizeof( *buf ) ); + if( astOK ) { + +/* Store the strings in the buffer. */ + result = astMapGet1C( this, key, mxlen+1, nval, &nval, buf ); + if( result ) { + +/* Combine the individual strings into a single comma-delimited list, + enclosed in parentheses, and stored in dynamic memory. */ + nc = 0; + cvalue = astAppendString( NULL, &nc, "(" ); + pb = buf; + for( i = 0; i < nval; i++ ) { + if( i ) cvalue = astAppendString( cvalue, &nc, "," ); + cvalue = astAppendString( cvalue, &nc, pb ); + pb += mxlen + 1; + } + cvalue = astAppendString( cvalue, &nc, ")" ); + +/* Put a pointer to the total string into the next element of the "convertvalue_strings" + array, freeing any value already stored in the next element first. */ + if( cvalue && astOK ) { + (void) astFree( convertvalue_strings[ convertvalue_istr ] ); + convertvalue_strings[ convertvalue_istr ] = cvalue; + +/* 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( ++convertvalue_istr == ( AST__KEYMAP_CONVERTVALUE_MAX_STRINGS - 1 ) ) + convertvalue_istr = 0; + +/* Return a pointer to the striong now stored in the "convertvalue_strings" + array. */ + *value = cvalue; + } + } + } + +/* Free the space used to hold the values buffer. */ + buf = astFree( buf ); + } + +/* 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 that a character variable +* 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 ); \ +\ +/* For strings, the "raw" value is a copy of a pointer stored in the global \ + "convertvalue_strings" array. These pointers should never be freed other \ + than within the ConvertValue function (otherwise you can end up with \ + spurious "invalid pointer" errors). But the "raw" value will be freed \ + when as part of the KeyMap when the KeyMap is destroyed. So we replace \ + the "raw" value with a new copy. */ \ + } else if( raw_type == AST__STRINGTYPE ){ \ + char **cp = (char **) raw; \ + *cp = astStore( NULL, *cp, strlen( *cp ) + 1 ); \ + } \ + } \ + } \ +} + +/* 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_ + + +int astMapGetC_( AstKeyMap *this, const char *key, const char **value, int *status ){ \ + if ( !astOK ) return 0; \ + return (**astMEMBER(this,KeyMap,MapGetC))(this,key,value, status ); \ +} + + +#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/ast/keymap.h b/ast/keymap.h new file mode 100644 index 0000000..a0ece66 --- /dev/null +++ b/ast/keymap.h @@ -0,0 +1,569 @@ +#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 (* MapGetC)( AstKeyMap *, const char *, const char **, 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 astMapGetC_( AstKeyMap *, const char *, const char **, 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 astMapGetC(this,key,value) astINVOKE(V,astMapGetC_(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/ast/leap-seconds.png b/ast/leap-seconds.png new file mode 100644 index 0000000..bee2559 Binary files /dev/null and b/ast/leap-seconds.png differ diff --git a/ast/loader.c b/ast/loader.c new file mode 100644 index 0000000..82d477e --- /dev/null +++ b/ast/loader.c @@ -0,0 +1,199 @@ +#define astCLASS +#include "axis.h" +#include "box.h" +#include "channel.h" +#include "chebymap.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 "unitnormmap.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-2016 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) +* RO: Russell Owen (LSST) + +* 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. +* 20-APR-2016 (RO): +* Added the UnitNormMap 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(ChebyMap); + 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(UnitNormMap); + 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/ast/loader.h b/ast/loader.h new file mode 100644 index 0000000..6227a81 --- /dev/null +++ b/ast/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/ast/lutmap.c b/ast/lutmap.c new file mode 100644 index 0000000..23925ea --- /dev/null +++ b/ast/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", AST__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/ast/lutmap.h b/ast/lutmap.h new file mode 100644 index 0000000..68fa194 --- /dev/null +++ b/ast/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/ast/makeh b/ast/makeh new file mode 100644 index 0000000..c07d343 --- /dev/null +++ b/ast/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/ast/mapping.c b/ast/mapping.c new file mode 100644 index 0000000..73dc546 --- /dev/null +++ b/ast/mapping.c @@ -0,0 +1,24692 @@ +/* +*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): +* Do not 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). +* 16-JUN-2017 (DSB): +* If a simplification fails because the simplification process makes +* an inappropriate assumption about the supplied Mapping (e.g. that +* it has a defined inverse transformation) - thus causing an error to +* be reported, then clear the error status and return a clone of the +* unmodified supplied Mapping. Putting this check in the astSimplify_ +* wrapper function in the base Mapping class is much simpler and less +* error prone than performing tests on the appropriateness of the +* mapping in teh astMapMerge method of each and every mapping class. +* 9-JAN-2018 (DSB): +* Modify astLinearApprox so that a linear mapping in parallel with a +* mapping that generates bad values is considered linear. The returned +* coeffs for the bad outputs are set bad. +* +*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. +* - If a Mapping output is found to have a bad value (AST__BAD) at +* one or more of the test points used in the linearity test, then all +* the values in the returned fit that correspond to that output are +* set to AST__BAD. However, this does not affect the linearity tests +* on the other Mapping outputs - if they are all found to be linear +* then usable coefficients will be returned for them in the fit, and +* the function will return a +c non-zero value. +f .TRUE. value. +* Consequently, it may be necessary to check that the values in the +* returned fit are not AST__BAD before using them. If all Mapping +* outputs generate bad values, then +c zero is returned as the function value. +f .FALSE. is returned as the function value. +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 bad_output; /* Does the Mapping output generate bad values? */ + 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; + +/* Initialise the supplied array to hold bad values. */ + for( ii = 0; ii < nc; ii++ ) fit[ ii ] = AST__BAD; + +/* 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++ ) { + bad_output = 0; + 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. Mapping outputs + that are bad at one or more test points have bad values stored for the + corresponding coefficients in the returned fit, but do not invalidate + the linearity of the fit to other outputs. */ + if ( ( out1 == AST__BAD ) || ( out2 == AST__BAD ) ) { + bad_output = 1; + 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 ); + } + +/* Determine the average zero point from all dimensions. */ + if( !bad_output ) zero[ coord_out ] = z / (double) ( 2 * ndim_in ); + } + +/* The zero points of the above fit 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. Also, if all Mapping outputs generate bad values, flag + that we do not have a linear fit. */ + linear = 0; + ii = 0; + for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { + if( zero[ coord_out ] != AST__BAD ) { + linear = 1; + 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; + } + } else { + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + grad[ ii++ ] = AST__BAD; + } + } + } + } + +/* 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. */ +/* --------------------------------------------- */ +/* The linear fit obtained above, 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 store bad values for + the output's coefficients and pass on to the next output. */ + bad_output = 0; + ii = 0; + for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { + y = ptr_out_t[ coord_out ][ point ]; + if ( y == AST__BAD || zero[ coord_out ] == AST__BAD ) { + zero[ coord_out ] = AST__BAD; + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + grad[ ii++ ] = AST__BAD; + } + bad_output++; + 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; + } + +/* 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. Reduce the allowed tolerance in proproprtion to the number of + bad Mapping outputs that were found. */ + if ( sqrt( err ) > (tol*(ndim_out - bad_output))/ndim_out ) { + 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 ), AST__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 ), AST__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 ? ", " : "", + AST__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 ? ", " : "", + AST__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 ), AST__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 to 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 ), AST__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; + AstErrorContext error_context; + + if ( !astOK ) return NULL; + +/* If this Mapping has already been simplified, or if it cannot be + simplified (e.g. because it is a Frame) we just returned a clone + of the upplied pointer. */ + if( !astGetIsSimple( this ) && !astDoNotSimplify( this ) ) { + +/* Start a new error reporting context. This is done so that errors + caused by the siplification process attempting to do inappropriate things + with the supplied mapping can be caught. */ + astErrorBegin( &error_context ); + +/* Do the simplification. */ + result = (**astMEMBER(this,Mapping,Simplify))( this, status ); + +/* If a result was returned, indicate it has been simplified and so does + not need to be simplified again. */ + if( result ) { + result->flags |= AST__ISSIMPLE_FLAG; + +/* If the simplification process failed due to the supplied Mappings + being inappropriate (e.g. because it attempted to ue an undefined + transformation), clear the error status and return a clone of the + supplied Mapping. */ + } else if( astStatus == AST__NODEF || astStatus == AST__TRNND ){ + astClearStatus; + result = astClone( this ); + } + +/* End the error reporting context. */ + astErrorEnd( &error_context ); + +/* If the Mapping has already been simplified just return a clone. */ + } 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/ast/mapping.h b/ast/mapping.h new file mode 100644 index 0000000..7b63fa2 --- /dev/null +++ b/ast/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/ast/mapping.pdf b/ast/mapping.pdf new file mode 100644 index 0000000..95b5866 Binary files /dev/null and b/ast/mapping.pdf differ diff --git a/ast/mathmap.c b/ast/mathmap.c new file mode 100644 index 0000000..f3a21f9 --- /dev/null +++ b/ast/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 (AST__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) AST__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/ast/mathmap.h b/ast/mathmap.h new file mode 100644 index 0000000..e3b4004 --- /dev/null +++ b/ast/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/ast/matrixmap.c b/ast/matrixmap.c new file mode 100644 index 0000000..2e6420a --- /dev/null +++ b/ast/matrixmap.c @@ -0,0 +1,5731 @@ +/* +*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. +* 15-JUN-2017 (DSB): +* A diagonal MatrixMap in which the diagonal elements are all zero +* cannot be simplified to a ZoomMap, since ZoomMaps cannot have +* zero zoom factor. +* 16-JUN-2017 (DSB): +* Fix error checking bug in MtrMult - it was checking for the +* inverse transformation of "this" instead of the forward +* transformation of "a". +* 7-NOW-2017 (DSB): +* Allow a diagonal MatrixMap to merge with a WinMap. +*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 AstWinMap *MatWin2( AstMatrixMap *, AstWinMap *, int, int, int, 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, so long as the + diagonal elements are not all zero. */ + } 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 ] ){ + factor = (mm->i_matrix)[ 0 ]; + } else { + factor = (mm->f_matrix)[ 0 ]; + } + + if( factor != 0.0 ){ + map2 = (AstMapping *) astZoomMap( nin, factor, "", status ); + } + } + +/* If the MatrixMap is a full matrix but all off-diagonal 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 is diagonal it can also merge with a WinMap. */ + if( !nclass && mm->form == DIAGONAL) { + if( class1 && ( !strcmp( class1, "WinMap" ) ) ){ + nclass = class1; + i1 = where - 1; + i2 = where; + + } else if( class2 && ( !strcmp( class2, "WinMap" ) ) ){ + nclass = class2; + i1 = where; + i2 = where + 1; + + } + } + +/* If the MatrixMap can merge with one of its neighbours, create the merged + Mapping. */ + if( nclass ){ + + if( !strcmp( nclass, "MatrixMap" ) ){ + newmap = (AstMapping *) MatMat( ( *map_list )[ i1 ], ( *map_list )[ i2 ], + ( *invert_list )[ i1 ], ( *invert_list )[ i2 ], status ); + invert = 0; + + } else if( !strcmp( nclass, "ZoomMap" ) ){ + if( i1 == where ){ + newmap = (AstMapping *) MatZoom( (AstMatrixMap *)( *map_list )[ i1 ], + (AstZoomMap *)( *map_list )[ i2 ], + ( *invert_list )[ i1 ], ( *invert_list )[ i2 ], status ); + } else { + newmap = (AstMapping *) 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 ){ + newmap = (AstMapping *) MatPerm( (AstMatrixMap *)( *map_list )[ i1 ], + (AstPermMap *)( *map_list )[ i2 ], + ( *invert_list )[ i1 ], ( *invert_list )[ i2 ], 1, status ); + } else { + newmap = (AstMapping *) MatPerm( (AstMatrixMap *)( *map_list )[ i2 ], + (AstPermMap *)( *map_list )[ i1 ], + ( *invert_list )[ i2 ], ( *invert_list )[ i1 ], 0, status ); + } + invert = 0; + + } else if( !strcmp( nclass, "WinMap" ) ){ + if( i1 == where ){ + newmap = (AstMapping *) MatWin2( (AstMatrixMap *)( *map_list )[ i1 ], + (AstWinMap *)( *map_list )[ i2 ], + ( *invert_list )[ i1 ], ( *invert_list )[ i2 ], 1, status ); + } else { + newmap = (AstMapping *) MatWin2( (AstMatrixMap *)( *map_list )[ i2 ], + (AstWinMap *)( *map_list )[ i1 ], + ( *invert_list )[ i2 ], ( *invert_list )[ i1 ], 0, status ); + } + invert = 0; + + } else { + newmap = 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 ] = 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 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 AstWinMap *MatWin2( AstMatrixMap *mm, AstWinMap *wm, int minv, + int winv, int mat1, int *status ){ +/* +* Name: +* MatWin2 + +* Purpose: +* Create a WinMap by merging a diagonal MatrixMap and a WinMap. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* AstWinMap *MatWin2( AstMatrixMap *mm, AstWinMap *wm, int minv, +* int winv, int mat1, int *status ) + +* Class Membership: +* MatrixMap member function + +* Description: +* This function creates a new WinMap 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), in the order specified by +* "mat1". + +* Parameters: +* mm +* A pointer to the MatrixMap. Assumed to be diagonal. +* wm +* A pointer to the WinMap. +* 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. +* winv +* The invert flag to use with wm. +* mat1 +* If non-zero, then "mm" is applied first followed by "wm". Otherwise, +* "wm" 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 "winv" 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: */ + AstWinMap *result; /* Pointer to output WinMap */ + double *ina; /* Input corner A in new WinMap */ + double *inb; /* Input corner B in new WinMap */ + double *newscales; /* Scales for new WinMap */ + double *newshifts; /* Shifts for new WinMap */ + double *outa; /* Output corner A in new WinMap */ + double *outb; /* Output corner B in new WinMap */ + double *scales2; /* Pointer to extended WinMap scales array */ + double *scales; /* Pointer to WinMap scales array */ + double *shifts; /* Pointer to WinMap shifts array */ + int i; /* Axis index */ + int ncol; /* No. of columns in the MatrixMap */ + int nrow; /* No. of rows in the MatrixMap */ + int nt; /* Number of axes in WinMap */ + int old_minv; /* Original setting of MatrixMap Invert attribute */ + 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 attributes of both Mappings to the supplied + values. */ + old_minv = astGetInvert( mm ); + astSetInvert( mm, minv ); + + old_winv = astGetInvert( wm ); + astSetInvert( wm, winv ); + +/* Get the number of inputs (columns) and outputs (rows) for the MatrixMap. */ + ncol = astGetNin( mm ); + nrow = astGetNout( mm ); + +/* Get the scales and shifts implemented by the WinMap. These take into + account the current Invert attribute of the WinMap. */ + nt = astWinTerms( wm, &shifts, &scales ); + +/* First deal with cases where the MatrixMap is applied first. */ + if( mat1 ){ + +/* Sanity check. */ + if( nt != nrow ) { + if( astOK ) astError( AST__INTER, "astMapMerge(%s): WinMap has %d axes, " + "but MatrixMap has %d rows (internal AST programming " + "error).", status, astGetClass(mm), nt, nrow ); + + } else { + +/* Allocate the array to hold the scale terms for the new WinMap. */ + newscales = astMalloc( nrow*sizeof(double) ); + +/* Ensure that the original scales array is padded with sufficient zeros + to allow it to be transformed using the matrixmap. */ + scales2 = astCalloc( ncol, sizeof(double) ); + if( astOK ) memcpy( scales2, scales, + (ncoli_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/ast/matrixmap.h b/ast/matrixmap.h new file mode 100644 index 0000000..b24565a --- /dev/null +++ b/ast/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/ast/memory.c b/ast/memory.c new file mode 100644 index 0000000..2339e8c --- /dev/null +++ b/ast/memory.c @@ -0,0 +1,5470 @@ +/* +* 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. +* 17-MAR-2017 (DSB): +* Remove unnecessary checks that supplied size_t argument values +* are not less than zero - size_t is unsigned and so is never negative. +*/ + +/* 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); + +/* 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. */ + 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 ) { + +/* Obtain a pointer to the memory header. */ + 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/ast/memory.h b/ast/memory.h new file mode 100644 index 0000000..7a631fb --- /dev/null +++ b/ast/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/ast/normmap.c b/ast/normmap.c new file mode 100644 index 0000000..3739739 --- /dev/null +++ b/ast/normmap.c @@ -0,0 +1,1720 @@ +/* +*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 (so long as the Frames encapsulated by +* the two NormMaps are equal - i.e. have the same class and the same +* attribute values). This means, for instance, that if a CmpMap contains +* a NormMap, the CmpMap will still cancel with its own inverse. + +* 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. +* 8-NOV-2016 (DSB): +* - Allow multiple identical NormMaps in series to be simplified to +* a single NormMap. +* - Allow a NormMap that contains a basic Frame to be simplified +* to a UnitMap. +*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; + const char *class; + 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. Get its class. */ + smap = astSimplify( map->frame ); + class = astGetClass( smap ); + +/* 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; + +/* If the encapsulated Frame is a basic Frame, we can safely assume that + the astNorm method does nothing, and so we can replace the NormMap with + a UnitMap. */ + } else if( class && !strcmp( class, "Frame" ) ) { + nax = astGetNin( smap ); + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = (AstMapping *) astUnitMap( nax, "", status ); + result = where; + +/* The only other simplications which can be performed are a) to cancel a NormMap + with its own inverse in series, or b) to remove duplicated adjacent + NormMaps 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; + +/* Otherwise, see if the nominated NormMap is followed by one or more + identical NormMaps, in which case we can remove all but one of the + NormMaps. */ + } else { + +/* Loop round all subsequent Mappings until the end of the list is + reached, or a Mapping that is not a NormMap is reached. On each pass + we replace the NormMap with a UnitMap. */ + nax = astGetNin( map ); + cancel = where; + while( ++cancel < *nmap && + astIsANormMap( ( *map_list )[ cancel ] ) ) { + +/* Check the Invert flags are equal. */ + if( ( *invert_list )[ where ] == ( *invert_list )[ cancel ] ) { + +/* Check the Frames are equal .*/ + nmap2 = (AstNormMap *) ( *map_list )[ cancel ]; + if( astEqual( map->frame, nmap2->frame ) ) { + +/* Replace the later NormMap with a UnitMap. */ + (void) astAnnul( ( *map_list )[ cancel ] ); + ( *map_list )[ cancel ] = (AstMapping *) astUnitMap( nax, + "", status ); + +/* We return the index of the first modified Mapping in the list, so do + not update "result" if this is not the first change. */ + if( result == -1 ) result = cancel; + } + } + } + } + } + +/* 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 (so long as the Frames encapsulated by +* the two NormMaps are equal - i.e. have the same class and the same +* attribute values). This means, for instance, that if a CmpMap contains +* a NormMap, the CmpMap will still cancel with its own inverse. + +* 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/ast/normmap.h b/ast/normmap.h new file mode 100644 index 0000000..5336e56 --- /dev/null +++ b/ast/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/ast/nullregion.c b/ast/nullregion.c new file mode 100644 index 0000000..6ef9cfa --- /dev/null +++ b/ast/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/ast/nullregion.h b/ast/nullregion.h new file mode 100644 index 0000000..bcfc501 --- /dev/null +++ b/ast/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/ast/object.c b/ast/object.c new file mode 100644 index 0000000..2f8d869 --- /dev/null +++ b/ast/object.c @@ -0,0 +1,8950 @@ +/* +*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 - astCreatedAt: Returns information about where an object was created +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. +* 4-JUL-2017 (DSB): +* Within astLockId, perform the correct check that the supplied +* object handle is not locked by another thread. +* 17-SEP-2017 (DSB): +* Add function astCreatedAt. This increases the size of a Handle +* structure by 20 bytes. If this turns out to be problematic +* this facility could be controlled using a configure option. +*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 "keymap.h" /* Hash tables */ +#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 ); + +/* Use a null string if a NULL pointer was returned by Get. */ + if( !value ) 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 "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,AST__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 */ + +/* Information that records where the Handle was created. */ + const char *routine; /* Routine name */ + const char *file; /* File name */ + int line; /* Line number */ + +} 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. */ +/* ----------------------------- */ +AstKeyMap *astActiveObjects_( const char *class, int subclass, int current, + int *status ) { +/* +c++ +* Name: +* astActiveObjects + +* Purpose: +* Return pointers for all active Objects. + +* Type: +* Public function. + +* Synopsis: +* #include "object.h" +* AstKeyMap *astActiveObjects( const char *class, int subclass, +* int current ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a KeyMap holding currently active AST Object +* pointers. Each entry in the KeyMap will have a key that is an AST +* class name such as "FrameSet", "SkyFrame", "ZoomMap", etc. The +* value of the entry will be a 1-dimensional list of pointers for +* objects of the same class. Note, the returned pointers should NOT +* be annulled when they are no longer needed. +* +* The pointers to return in the KeyMap may be restricted either by +* class or by context level using the function arguments. + +* Parameters: +* class +* If NULL, then the returned KeyMap will contain pointers ofr +* Objects of all classes. If not NULL, then "class" should be a +* pointer to a null-terminated string holding the name of an AST +* class. The returned KeyMap will contain pointers only for the +* specified class. See also "subclass". +* subclass +* A Boolean flag indicating if all subclasses of the class +* specified by "class" should be included in the returned KeyMap. +* If zero, then subclass objects are not returned. Otherwise they +* are returned. The supplied "subclass" value is ignored if +* "class" is NULL. +* current +* A Boolean flag indicating if the returned list of pointers +* should be restricted to pointers issued within the current AST +* object context (see astBegin and astEnd). + +* Returned Value: +* astActiveObjects() +* A pointer to a new KeyMap holding the required object pointers. +* They KeyMap pointer should be annulled when it is no longer +* needed, but the object pointers within the KeyMap should not be +* annulled. A NULL pointer is returned if an error has occurred +* prior to calling this function. +* +* The values stored in the KeyMap should be accessed as generic C +* pointers using the KeyMap "P" data type (e.g. using function +* astMapGetlemP etc). + +* Notes: +* - This function will only return objects locked by the currently +* executing thread. +* - The KeyMap pointer returned by this function is not included in the +* list of active objects stored in the KeyMap. +* - Objects that were created using the Fortran interface will have a +* null "file" value and will have a routine name equal to the upper case +* Fortran routine that issued the pointer (e.g. "AST_CLONE", "AST_FRAME", +* etc). + +c-- +*/ + +/* Local Variables: */ + AstKeyMap *result; /* Returned KeyMap */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int i; /* Loop count */ + int ihandle; /* Offset of Handle to be annulled */ + AstObjectVtab *req_vtab; /* Vtab for requested class */ + Handle *handle; /* Pointer to current Handle */ + int generation_gap; /* Hereditary relationshp between two classes */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Create an empty KeyMap to hold the results. */ + result = astKeyMap( " ", status ); + +/* If we will need to check if each object is a subclass of a specified + class, we need a pointer to the VTAB descriibing the specified class. */ + req_vtab = NULL; + if( class && subclass ) { + +/* 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 requested class, + store a pointer to the vtab. */ + if( !strcmp( class, known_vtabs[ i ]->class ) ) { + req_vtab = known_vtabs[ i ]; + break; + } + } + } + +/* Get exclusive access to the handles array. */ + LOCK_MUTEX2; + +/* Get the index of the first Handle to check. If we are checking only the + current context level, then get this index from the appropriate element + of the active_handles array. Otherwise, we check the whole of the + handles array, starting at element zero. */ + if( current && active_handles ) { + ihandle = active_handles[ context_level ]; + } else { + ihandle = 0; + } + +/* Loop over the Handles array, starting from the above element. */ + handle = handles + ihandle; + for( ; ihandle < nhandles; ihandle++,handle++ ) { + +/* Skip Handles that have no associated object. */ + if( !handle->ptr ) continue; + +/* Skip handles that are in an unrequired context. */ + if( current && handle->context != context_level ) continue; + +#if defined(THREAD_SAFE) +/* Skip handles that are not locked for use by the current thread. */ + if( handle->thread != AST__THREAD_ID ) continue; +#endif + +/* If required, check that the current handle is for an object of the + specified class. */ + if( class ) { + +/* Skip the handle if no VTAB was found for the requested class. */ + if( !req_vtab ) continue; + +/* Get the generation gap between the current handle's object and the + specified class. */ + generation_gap = astClassCompare( astVTAB( handle->ptr ), req_vtab ); + +/* Skip the handle if it is not for the specified class or a subclass. */ + if( generation_gap < 0 || generation_gap == AST__COUSIN || + ( generation_gap > 0 && !subclass ) ) continue; + } + +/* Get the integer identifier associated with the handle, convert it to a + pointer and append to the end of the KeyMap entry describing the handle's + class. */ + astMapPutElemP( result, astGetClass( handle->ptr ), -1, + astI2P( handle->check ) ); + } + +/* Relinquish access to the handles array. */ + UNLOCK_MUTEX2; + +/* Return the KeyMap. */ + return result; +} + +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; +} + +void astCreatedAtId_( AstObject *this_id, const char **routine, + const char **file, int *line, int *status ){ +/* +c++ +* Name: +* astCreatedAt + +* Purpose: +* Return the routine, file and line number at which an Object was +* created. + +* Type: +* Public function. + +* Synopsis: +* #include "object.h" +* void astCreatedAt( AstObject *this, const char **routine, +* const char **file, int *line ) + +* Class Membership: +* Object method. + +* Description: +* This function returns pointers to two strings containing the +* name of the routine or function within which the object was created +* and the name of the source file containing that routine. It also +* returns the number of the line within the file at which the object +* was created. It is intended for use in debugging memory leaks etc. +* +* Precisely, the information returned identifies the point at which +* the Object's public identifier (i.e. the supplied pointer) was +* first issued. This may not correspond to the actual creation of the +* Object if the object is contained within some higher level Object. +* For instance, if the astGetFrame method is used to get a pointer to +* a Frame within a FrameSet, the information returned by this +* function if supplied with the Frame pointer would identify the call +* to astGetFrame, rather than the line at which the FrameSet created +* its internal copy of the Frame. Likewise, if this function is used +* to get information from an Object pointer returned by astClone, the +* information will describe the call to astClone, not the call that +* created the original Object. + +* Parameters: +* this +* Pointer to the Object. +* routine +* Address of a pointer to a null terminated C string in which to +* return the routine name (the string will reside in static memory). +* The pointer will be set to NULL on exit if no routine name is +* available. +* file +* Address of a pointer to a null terminated C string in which to +* return the file name (the string will reside in static memory). +* The pointer will be set to NULL on exit if no file name is +* available. +* line +* Address of an int in which to store the line number in the file. +* A line number of zero is returned if no line number is available. + +* Notes: +* - NULL pointers and a line number of zero are returned if an error +* has already occurred prior to calling this function. + +c-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int ihandle; /* Result to return */ + +/* Initialise */ + *routine = NULL; + *file = NULL; + *line = 0; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* 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 ) { + +/* Copy the required pointers etc to the supplied addresses. */ + *routine = handles[ ihandle ].routine; + *file = handles[ ihandle ].file; + *line = handles[ ihandle ].line; + } + +/* Unlock the mutex that guards access to the handles array */ + UNLOCK_MUTEX2; + +} + +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, 1, 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 associated with a different thread (no error + if the handle has no associated thread or is associated with the + current 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; + handles[ ihandle ].line = 0; + handles[ ihandle ].file = NULL; + handles[ ihandle ].routine = NULL; +#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 information that records where the Handle is created - routine + name, file name and line number. */ + astGetAt( &handles[ ihandle ].routine, + &handles[ ihandle ].file, + &handles[ ihandle ].line ); + +/* 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/ast/object.f b/ast/object.f new file mode 100644 index 0000000..34f92b2 --- /dev/null +++ b/ast/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/ast/object.h.in b/ast/object.h.in new file mode 100644 index 0000000..64720ff --- /dev/null +++ b/ast/object.h.in @@ -0,0 +1,1960 @@ +#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 + +/* Number of digits to use when formatting double precision floating point + values. DBL_DIG ensures no loss when round-tripping from text to + binary to text, but we want no loss when round-tripping from binary to + text to binary, so we use DBL_DECIMAL_DIG instead. If this is not + avaialable we use DBL_DIG but add on an extra 3 digits. */ +#ifdef DBL_DECIMAL_DIG + #define AST__DBL_DIG (DBL_DECIMAL_DIG) +#else + #define AST__DBL_DIG (DBL_DIG + 3) +#endif + +#ifdef FLT_DECIMAL_DIG + #define AST__FLT_DIG (FLT_DECIMAL_DIG) +#else + #define AST__FLT_DIG (FLT_DIG + 3) +#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; +struct KeyMap; + +/* 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 + +struct AstKeyMap *astActiveObjects_( const char *, int, int, int *); +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 astCreatedAtId_( AstObject *, const char **, const char **, int *, 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)) +#define astCreatedAt(this,routine,file,line) astINVOKE(V,astCreatedAtId_((AstObject *)this,routine,file,line,STATUS_PTR)) +#define astActiveObjects(class,subclass,current) astINVOKE(O,astActiveObjects_(class,subclass,current,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/ast/overgrid.pdf b/ast/overgrid.pdf new file mode 100644 index 0000000..21922e8 Binary files /dev/null and b/ast/overgrid.pdf differ diff --git a/ast/overgrid_bw.pdf b/ast/overgrid_bw.pdf new file mode 100644 index 0000000..457b75e Binary files /dev/null and b/ast/overgrid_bw.pdf differ diff --git a/ast/pal.h b/ast/pal.h new file mode 100644 index 0000000..31ff8c7 --- /dev/null +++ b/ast/pal.h @@ -0,0 +1,582 @@ +#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. +* 11-JAN-2017 (DSB): +* Update to PAL V0.9.7. +* {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, 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] ); + +int palVers ( char * verstring, size_t verlen ); + +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/ast/pal/pal.h b/ast/pal/pal.h new file mode 100644 index 0000000..a588168 --- /dev/null +++ b/ast/pal/pal.h @@ -0,0 +1,551 @@ +#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] ); + +int palVers ( char * verstring, size_t verlen ); + +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/ast/pal/pal1sofa.h b/ast/pal/pal1sofa.h new file mode 100644 index 0000000..11f7446 --- /dev/null +++ b/ast/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/ast/pal/palAddet.c b/ast/pal/palAddet.c new file mode 100644 index 0000000..ce3ddab --- /dev/null +++ b/ast/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/ast/pal/palAmpqk.c b/ast/pal/palAmpqk.c new file mode 100644 index 0000000..ce698b8 --- /dev/null +++ b/ast/pal/palAmpqk.c @@ -0,0 +1,159 @@ +/* +*+ +* 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) 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*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. + +* Note: +* Iterative techniques are used for the aberration and +* light deflection corrections so that the routines +* palAmp (or palAmpqk) and palMap (or palMapqk) are +* accurate inverses; even at the edge of the Sun's disc +* the discrepancy is only about 1 nanoarcsecond. + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness +* {enter_new_authors_here} + +* History: +* 2012-02-13 (PTW): +* Initial version. +* Adapted with permission from the Fortran SLALIB library. +* 2016-12-19 (TIMJ): +* Add in light deflection (was missed in the initial port). +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 2000 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* Copyright (C) 2016 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} +*- +*/ + +#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; + double gr2e, pde, pdep1, ehn[3], p[3]; + int i, j; + +/* Unpack some of the parameters */ + gr2e = amprms[7]; + ab1 = amprms[11]; + for( i = 0; i < 3; i++ ) { + ehn[i] = amprms[i + 4]; + 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]; + } + } + +/* Light deflection */ + for( i = 0; i < 3; i++ ) { + p[i] = p1[i]; + } + for( j = 0; j < 5; j++ ) { + pde = eraPdp( p, ehn ); + pdep1 = 1.0 + pde; + w = pdep1 - gr2e*pde; + for( i = 0; i < 3; i++ ) { + p[i] = (pdep1*p1[i] - gr2e*ehn[i])/w; + } + eraPn( p, &w, p2 ); + for( i = 0; i < 3; i++ ) { + p[i] = p2[i]; + } + } + +/* Mean RA,Dec */ + eraC2s( p, rm, dm ); + *rm = eraAnp( *rm ); +} diff --git a/ast/pal/palCaldj.c b/ast/pal/palCaldj.c new file mode 100644 index 0000000..03c6b97 --- /dev/null +++ b/ast/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/ast/pal/palDat.c b/ast/pal/palDat.c new file mode 100644 index 0000000..f869681 --- /dev/null +++ b/ast/pal/palDat.c @@ -0,0 +1,95 @@ +/* +*+ +* Name: +* palDat + +* Purpose: +* Return offset between UTC and TAI + +* 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/ast/pal/palDe2h.c b/ast/pal/palDe2h.c new file mode 100644 index 0000000..a250e9e --- /dev/null +++ b/ast/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/ast/pal/palDeuler.c b/ast/pal/palDeuler.c new file mode 100644 index 0000000..17e536b --- /dev/null +++ b/ast/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/ast/pal/palDh2e.c b/ast/pal/palDh2e.c new file mode 100644 index 0000000..65434b7 --- /dev/null +++ b/ast/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/ast/pal/palDjcal.c b/ast/pal/palDjcal.c new file mode 100644 index 0000000..b6aac9e --- /dev/null +++ b/ast/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/ast/pal/palDmat.c b/ast/pal/palDmat.c new file mode 100644 index 0000000..2948f92 --- /dev/null +++ b/ast/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/ast/pal/palDs2tp.c b/ast/pal/palDs2tp.c new file mode 100644 index 0000000..cbf582a --- /dev/null +++ b/ast/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/ast/pal/palDtp2s.c b/ast/pal/palDtp2s.c new file mode 100644 index 0000000..583a56d --- /dev/null +++ b/ast/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/ast/pal/palDtps2c.c b/ast/pal/palDtps2c.c new file mode 100644 index 0000000..ecb3090 --- /dev/null +++ b/ast/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/ast/pal/palDtt.c b/ast/pal/palDtt.c new file mode 100644 index 0000000..f6a3714 --- /dev/null +++ b/ast/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/ast/pal/palEcmat.c b/ast/pal/palEcmat.c new file mode 100644 index 0000000..e9d9aeb --- /dev/null +++ b/ast/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/ast/pal/palEqgal.c b/ast/pal/palEqgal.c new file mode 100644 index 0000000..9df3d09 --- /dev/null +++ b/ast/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/ast/pal/palEtrms.c b/ast/pal/palEtrms.c new file mode 100644 index 0000000..4484682 --- /dev/null +++ b/ast/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/ast/pal/palEvp.c b/ast/pal/palEvp.c new file mode 100644 index 0000000..b30a3a9 --- /dev/null +++ b/ast/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/ast/pal/palFk45z.c b/ast/pal/palFk45z.c new file mode 100644 index 0000000..643fd74 --- /dev/null +++ b/ast/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/ast/pal/palFk524.c b/ast/pal/palFk524.c new file mode 100644 index 0000000..47c3abb --- /dev/null +++ b/ast/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.0E-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/ast/pal/palFk54z.c b/ast/pal/palFk54z.c new file mode 100644 index 0000000..b902b0d --- /dev/null +++ b/ast/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/ast/pal/palGaleq.c b/ast/pal/palGaleq.c new file mode 100644 index 0000000..d0da1ee --- /dev/null +++ b/ast/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/ast/pal/palGalsup.c b/ast/pal/palGalsup.c new file mode 100644 index 0000000..e469fb7 --- /dev/null +++ b/ast/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/ast/pal/palGeoc.c b/ast/pal/palGeoc.c new file mode 100644 index 0000000..9954d92 --- /dev/null +++ b/ast/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/ast/pal/palMappa.c b/ast/pal/palMappa.c new file mode 100644 index 0000000..4e7ee64 --- /dev/null +++ b/ast/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) (Schwarzschild radius of Sun)/(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/ast/pal/palMapqkz.c b/ast/pal/palMapqkz.c new file mode 100644 index 0000000..44a2c79 --- /dev/null +++ b/ast/pal/palMapqkz.c @@ -0,0 +1,150 @@ +/* +*+ +* Name: +* palMapqkz + +* Purpose: +* Quick mean to apparent place (no proper motion or parallax). + +* 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) heliocentric direction of the Earth (unit vector) +* (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. + +* Notes: +* - The reference systems and timescales used are IAU 2006. +* - The mean place rm, dm and the vectors amprms[1-3] and amprms[4-6] +* are referred to the mean equinox and equator of the epoch +* specified when generating the precession/nutation matrix +* amprms[12-20]. In the call to palMappa (q.v.) normally used +* to populate amprms, this epoch is the first argument (eq). +* - The vector amprms(4-6) is referred to the mean equinox and +* equator of epoch eq. +* - Strictly speaking, the routine is not valid for solar-system +* sources, though the error will usually be extremely small. +* However, to prevent gross errors in the case where the +* position of the Sun is specified, the gravitational +* deflection term is restrained within about 920 arcsec of the +* centre of the Sun's disc. The term has a maximum value of +* about 1.85 arcsec at this radius, and decreases to zero as +* the centre of the disc is approached. + +* 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, p2[3], p3[3]; + double gr2e, pde, pdep1, ehn[3], p1[3]; + +/* Unpack scalar and vector parameters. */ + ab1 = amprms[11]; + gr2e = amprms[7]; + for( i = 0; i < 3; i++ ) { + abv[i] = amprms[i+8]; + ehn[i] = amprms[i+4]; + } + +/* Spherical to x,y,z. */ + eraS2c( rm, dm, p ); + +/* Light deflection (restrained within the Sun's disc) */ + pde = eraPdp( p, ehn ); + pdep1 = pde + 1.0; + w = gr2e / ( pdep1 > 1.0e-5 ? pdep1 : 1.0e-5 ); + for( i = 0; i < 3; i++) { + p1[i] = p[i] + w * ( ehn[i] - pde * p[i] ); + } + +/* Aberration. */ + p1dv = eraPdp( p1, abv ); + w = 1.0 + p1dv / ( ab1 + 1.0 ); + for( i = 0; i < 3; i++ ) { + p2[i] = ( ( ab1 * p1[i] ) + ( w * abv[i] ) ); + } + +/* Precession and nutation. */ + eraRxp( (double(*)[3]) &rms[12], p2, p3 ); + +/* Geocentric apparent RA,dec. */ + eraC2s( p3, ra, da ); + *ra = eraAnp( *ra ); +} diff --git a/ast/pal/palOne2One.c b/ast/pal/palOne2One.c new file mode 100644 index 0000000..a8d8072 --- /dev/null +++ b/ast/pal/palOne2One.c @@ -0,0 +1,1482 @@ +/* +*+ +* 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: +* Copyright (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" + +/* +*+ +* Name: +* palCldj + +* Purpose: +* Gregorian Calendar to Modified Julian Date + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palCldj( int iy, int im, int id, double *djm, int *j ); + +* Arguments: +* iy = int (Given) +* Year in Gregorian calendar +* im = int (Given) +* Month in Gregorian calendar +* id = int (Given) +* Day in Gregorian calendar +* djm = double * (Returned) +* Modified Julian Date (JD-2400000.5) for 0 hrs +* j = int * (Returned) +* status: 0 = OK, 1 = bad year (MJD not computed), +* 2 = bad month (MJD not computed), 3 = bad day (MJD computed). + +* Description: +* Gregorian calendar to Modified Julian Date. + +* Notes: +* - Uses eraCal2jd(). See SOFA/ERFA documentation for details. + +*- +*/ + +void palCldj ( int iy, int im, int id, double *djm, int *j ) { + double djm0; + *j = eraCal2jd( iy, im, id, &djm0, djm ); +} + +/* +*+ +* Name: +* palDbear + +* Purpose: +* Bearing (position angle) of one point on a sphere relative to another + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* pa = palDbear( double a1, double b1, double a2, double b2 ); + +* Arguments: +* a1 = double (Given) +* Longitude of point A (e.g. RA) in radians. +* a2 = double (Given) +* Latitude of point A (e.g. Dec) in radians. +* b1 = double (Given) +* Longitude of point B in radians. +* b2 = double (Given) +* Latitude of point B in radians. + +* Returned Value: +* The result is the bearing (position angle), in radians, of point +* A2,B2 as seen from point A1,B1. It is in the range +/- pi. If +* A2,B2 is due east of A1,B1 the bearing is +pi/2. Zero is returned +* if the two points are coincident. + +* Description: +* Bearing (position angle) of one point in a sphere relative to another. + +* Notes: +* - Uses eraPas(). See SOFA/ERFA documentation for details. + +*- +*/ + +double palDbear ( double a1, double b1, double a2, double b2 ) { + return eraPas( a1, b1, a2, b2 ); +} + +/* +*+ +* Name: +* palDaf2r + +* Purpose: +* Convert degrees, arcminutes, arcseconds to radians + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDaf2r( int ideg, int iamin, double asec, double *rad, int *j ); + +* Arguments: +* ideg = int (Given) +* Degrees. +* iamin = int (Given) +* Arcminutes. +* iasec = double (Given) +* Arcseconds. +* rad = double * (Returned) +* Angle in radians. +* j = int * (Returned) +* Status: 0 = OK, 1 = "ideg" out of range 0-359, +* 2 = "iamin" outside of range 0-59, +* 2 = "asec" outside range 0-59.99999 + +* Description: +* Convert degrees, arcminutes, arcseconds to radians. + +* Notes: +* - Uses eraAf2a(). See SOFA/ERFA documentation for details. + +*- +*/ + +/* 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 ); +} + +/* +*+ +* Name: +* palDav2m + +* Purpose: +* Form the rotation matrix corresponding to a given axial vector. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDav2m( double axvec[3], double rmat[3][3] ); + +* Arguments: +* axvec = double [3] (Given) +* Axial vector (radians) +* rmat = double [3][3] (Returned) +* Rotation matrix. + +* Description: +* A rotation matrix describes a rotation about some arbitrary axis, +* called the Euler axis. The "axial vector" supplied to this routine +* has the same direction as the Euler axis, and its magnitude is the +* amount of rotation in radians. + +* Notes: +* - Uses eraRv2m(). See SOFA/ERFA documentation for details. + +*- +*/ + +void palDav2m ( double axvec[3], double rmat[3][3] ) { + eraRv2m( axvec, rmat ); +} + +/* +*+ +* Name: +* palDcc2s + +* Purpose: +* Cartesian to spherical coordinates + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDcc2s( double v[3], double *a, double *b ); + +* Arguments: +* v = double [3] (Given) +* x, y, z vector. +* a = double * (Returned) +* Spherical coordinate (radians) +* b = double * (Returned) +* Spherical coordinate (radians) + +* Description: +* The spherical coordinates are longitude (+ve anticlockwise looking +* from the +ve latitude pole) and latitude. The Cartesian coordinates +* are right handed, with the x axis at zero longitude and latitude, and +* the z axis at the +ve latitude pole. + +* Notes: +* - Uses eraC2s(). See SOFA/ERFA documentation for details. + +*- +*/ + +void palDcc2s ( double v[3], double *a, double *b ) { + eraC2s( v, a, b ); +} + +/* +*+ +* Name: +* palDcs2c + +* Purpose: +* Spherical coordinates to direction cosines + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDcs2c( double a, double b, double v[3] ); + +* Arguments: +* a = double (Given) +* Spherical coordinate in radians (ra, long etc). +* b = double (Given) +* Spherical coordinate in radians (dec, lat etc). +* v = double [3] (Returned) +* x, y, z vector + +* Description: +* The spherical coordinates are longitude (+ve anticlockwise looking +* from the +ve latitude pole) and latitude. The Cartesian coordinates +* are right handed, with the x axis at zero longitude and latitude, and +* the z axis at the +ve latitude pole. + +* Notes: +* - Uses eraS2c(). See SOFA/ERFA documentation for details. + +*- +*/ + +void palDcs2c ( double a, double b, double v[3] ) { + eraS2c( a, b, v ); +} + +/* +*+ +* Name: +* palDd2tf + +* Purpose: +* Convert an interval in days into hours, minutes, seconds + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDd2tf( int ndp, double days, char *sign, int ihmsf[4] ); + +* Arguments: +* ndp = int (Given) +* Number of decimal places of seconds +* days = double (Given) +* Interval in days +* sign = char * (Returned) +* '+' or '-' (single character, not string) +* ihmsf = int [4] (Returned) +* Hours, minutes, seconds, fraction + +* Description: +* Convert and interval in days into hours, minutes, seconds. + +* Notes: +* - Uses eraD2tf(). See SOFA/ERFA documentation for details. + +*- +*/ + +void palDd2tf ( int ndp, double days, char *sign, int ihmsf[4] ) { + eraD2tf( ndp, days, sign, ihmsf ); +} + +/* +*+ +* Name: +* palDimxv + +* Purpose: +* Perform the 3-D backward unitary transformation + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDimxv( double dm[3][3], double va[3], double vb[3] ); + +* Arguments: +* dm = double [3][3] (Given) +* Matrix +* va = double [3] (Given) +* vector +* vb = double [3] (Returned) +* Result vector + +* Description: +* Perform the 3-D backward unitary transformation. + +* Notes: +* - Uses eraTrxp(). See SOFA/ERFA documentation for details. + +*- +*/ + +void palDimxv ( double dm[3][3], double va[3], double vb[3] ) { + eraTrxp( dm, va, vb ); +} + +/* +*+ +* Name: +* palDm2av + +* Purpose: +* From a rotation matrix, determine the corresponding axial vector + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDm2av( double rmat[3][3], double axvec[3] ); + +* Arguments: +* rmat = double [3][3] (Given) +* Rotation matrix +* axvec = double [3] (Returned) +* Axial vector (radians) + +* Description: +* A rotation matrix describes a rotation about some arbitrary axis, +* called the Euler axis. The "axial vector" returned by this routine +* has the same direction as the Euler axis, and its magnitude is the +* amount of rotation in radians. (The magnitude and direction can be +* separated by means of the routine palDvn.) + +* Notes: +* - Uses eraRm2v(). See SOFA/ERFA documentation for details. + +*- +*/ + +void palDm2av ( double rmat[3][3], double axvec[3] ) { + eraRm2v( rmat, axvec ); +} + +/* +*+ +* Name: +* palDjcl + +* Purpose: +* Modified Julian Date to Gregorian year, month, day and fraction of day + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDjcl( double djm, int *iy, int *im, int *id, double *fd, int *j ); + +* Arguments: +* djm = double (Given) +* modified Julian Date (JD-2400000.5) +* iy = int * (Returned) +* year +* im = int * (Returned) +* month +* id = int * (Returned) +* day +* fd = double * (Returned) +* Fraction of day. + +* Description: +* Modified Julian Date to Gregorian year, month, day and fraction of day. + +* Notes: +* - Uses eraJd2cal(). See SOFA/ERFA documentation for details. + +*- +*/ + +/* 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 ); +} + +/* +*+ +* Name: +* palDmxm + +* Purpose: +* Product of two 3x3 matrices + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDmxm( double a[3][3], double b[3][3], double c[3][3] ); + +* Arguments: +* a = double [3][3] (Given) +* Matrix +* b = double [3][3] (Given) +* Matrix +* c = double [3][3] (Returned) +* Matrix result + +* Description: +* Product of two 3x3 matrices. + +* Notes: +* - Uses eraRxr(). See SOFA/ERFA documentation for details. + +*- +*/ + +void palDmxm ( double a[3][3], double b[3][3], double c[3][3] ) { + eraRxr( a, b, c ); +} + +/* +*+ +* Name: +* palDmxv + +* Purpose: +* Performs the 3-D forward unitary transformation + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDmxv( double dm[3][3], double va[3], double vb[3] ); + +* Arguments: +* dm = double [3][3] (Given) +* matrix +* va = double [3] (Given) +* vector +* dp = double [3] (Returned) +* result vector + +* Description: +* Performs the 3-D forward unitary transformation. + +* Notes: +* - Uses eraRxp(). See SOFA/ERFA documentation for details. + +*- +*/ + +void palDmxv ( double dm[3][3], double va[3], double vb[3] ) { + eraRxp( dm, va, vb ); +} + +/* +*+ +* Name: +* palDpav + +* Purpose: +* Position angle of one celestial direction with respect to another + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* pa = palDpav( double v1[3], double v2[3] ); + +* Arguments: +* v1 = double [3] (Given) +* direction cosines of one point. +* v2 = double [3] (Given) +* direction cosines of the other point. + +* Returned Value: +* The result is the bearing (position angle), in radians, of point +* V2 with respect to point V1. It is in the range +/- pi. The +* sense is such that if V2 is a small distance east of V1, the +* bearing is about +pi/2. Zero is returned if the two points +* are coincident. + +* Description: +* Position angle of one celestial direction with respect to another. + +* Notes: +* - The coordinate frames correspond to RA,Dec, Long,Lat etc. +* - Uses eraPap(). See SOFA/ERFA documentation for details. + +*- +*/ + +double palDpav ( double v1[3], double v2[3] ) { + return eraPap( v1, v2 ); +} + +/* +*+ +* Name: +* palDr2af + +* Purpose: +* Convert an angle in radians to degrees, arcminutes, arcseconds + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDr2af( int ndp, double angle, char *sign, int idmsf[4] ); + +* Arguments: +* ndp = int (Given) +* number of decimal places of arcseconds +* angle = double (Given) +* angle in radians +* sign = char * (Returned) +* '+' or '-' (single character) +* idmsf = int [4] (Returned) +* Degrees, arcminutes, arcseconds, fraction + +* Description: +* Convert an angle in radians to degrees, arcminutes, arcseconds. + +* Notes: +* - Uses eraA2af(). See SOFA/ERFA documentation for details. + +*- +*/ + +void palDr2af ( int ndp, double angle, char *sign, int idmsf[4] ) { + eraA2af( ndp, angle, sign, idmsf ); +} + +/* +*+ +* Name: +* palDr2tf + +* Purpose: +* Convert an angle in radians to hours, minutes, seconds + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDr2tf ( int ndp, double angle, char *sign, int ihmsf[4] ); + +* Arguments: +* ndp = int (Given) +* number of decimal places of arcseconds +* angle = double (Given) +* angle in radians +* sign = char * (Returned) +* '+' or '-' (single character) +* idmsf = int [4] (Returned) +* Hours, minutes, seconds, fraction + +* Description: +* Convert an angle in radians to hours, minutes, seconds. + +* Notes: +* - Uses eraA2tf(). See SOFA/ERFA documentation for details. + +*- +*/ + +void palDr2tf( int ndp, double angle, char *sign, int ihmsf[4] ) { + eraA2tf( ndp, angle, sign, ihmsf ); +} + +/* +*+ +* Name: +* palDranrm + +* Purpose: +* Normalize angle into range 0-2 pi + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* norm = palDranrm( double angle ); + +* Arguments: +* angle = double (Given) +* angle in radians + +* Returned Value: +* Angle expressed in the range 0-2 pi + +* Description: +* Normalize angle into range 0-2 pi. + +* Notes: +* - Uses eraAnp(). See SOFA/ERFA documentation for details. + +*- +*/ + +double palDranrm ( double angle ) { + return eraAnp( angle ); +} + +/* +*+ +* Name: +* palDsep + +* Purpose: +* Angle between two points on a sphere + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* ang = palDsep( double a1, double b1, double a2, double b2 ); + +* Arguments: +* a1 = double (Given) +* Spherical coordinate of one point (radians) +* b1 = double (Given) +* Spherical coordinate of one point (radians) +* a2 = double (Given) +* Spherical coordinate of other point (radians) +* b2 = double (Given) +* Spherical coordinate of other point (radians) + +* Returned Value: +* Angle, in radians, between the two points. Always positive. + +* Description: +* Angle between two points on a sphere. + +* Notes: +* - The spherical coordinates are [RA,Dec], [Long,Lat] etc, in radians. +* - Uses eraSeps(). See SOFA/ERFA documentation for details. + +*- +*/ + +double palDsep ( double a1, double b1, double a2, double b2 ) { + return eraSeps( a1, b1, a2, b2 ); +} + +/* +*+ +* Name: +* palDsepv + +* Purpose: +* Angle between two vectors + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* ang = palDsepv( double v1[3], double v2[3] ); + +* Arguments: +* v1 = double [3] (Given) +* First vector +* v2 = double [3] (Given) +* Second vector + +* Returned Value: +* Angle, in radians, between the two points. Always positive. + +* Description: +* Angle between two vectors. + +* Notes: +* - Uses eraSepp(). See SOFA/ERFA documentation for details. + +*- +*/ + +double palDsepv ( double v1[3], double v2[3] ) { + return eraSepp( v1, v2 ); +} + +/* +*+ +* Name: +* palDtf2d + +* Purpose: +* Convert hours, minutes, seconds to days + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDtf2d( int ihour, int imin, double sec, double *days, int *j ); + +* Arguments: +* ihour = int (Given) +* Hours +* imin = int (Given) +* Minutes +* sec = double (Given) +* Seconds +* days = double * (Returned) +* Interval in days +* j = int * (Returned) +* status: 0 = ok, 1 = ihour outside range 0-23, +* 2 = imin outside range 0-59, 3 = sec outside range 0-59.999... + +* Description: +* Convert hours, minutes, seconds to days. + +* Notes: +* - Uses eraTf2d(). See SOFA/ERFA documentation for details. + +*- +*/ + +/* 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 ); +} + +/* +*+ +* Name: +* palDtf2r + +* Purpose: +* Convert hours, minutes, seconds to radians + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDtf2r( int ihour, int imin, double sec, double *rad, int *j ); + +* Arguments: +* ihour = int (Given) +* Hours +* imin = int (Given) +* Minutes +* sec = double (Given) +* Seconds +* days = double * (Returned) +* Angle in radians +* j = int * (Returned) +* status: 0 = ok, 1 = ihour outside range 0-23, +* 2 = imin outside range 0-59, 3 = sec outside range 0-59.999... + +* Description: +* Convert hours, minutes, seconds to radians. + +* Notes: +* - Uses eraTf2a(). See SOFA/ERFA documentation for details. + +*- +*/ + +/* 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 ); +} + +/* +*+ +* Name: +* palDvdv + +* Purpose: +* Scalar product of two 3-vectors + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* prod = palDvdv ( double va[3], double vb[3] ); + +* Arguments: +* va = double [3] (Given) +* First vector +* vb = double [3] (Given) +* Second vector + +* Returned Value: +* Scalar product va.vb + +* Description: +* Scalar product of two 3-vectors. + +* Notes: +* - Uses eraPdp(). See SOFA/ERFA documentation for details. + +*- +*/ + +double palDvdv ( double va[3], double vb[3] ) { + return eraPdp( va, vb ); +} + +/* +*+ +* Name: +* palDvn + +* Purpose: +* Normalizes a 3-vector also giving the modulus + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDvn( double v[3], double uv[3], double *vm ); + +* Arguments: +* v = double [3] (Given) +* vector +* uv = double [3] (Returned) +* unit vector in direction of "v" +* vm = double * (Returned) +* modulus of "v" + +* Description: +* Normalizes a 3-vector also giving the modulus. + +* Notes: +* - Uses eraPn(). See SOFA/ERFA documentation for details. + +*- +*/ + +/* Note that the arguments are flipped */ +void palDvn ( double v[3], double uv[3], double *vm ) { + eraPn( v, vm, uv ); +} + +/* +*+ +* Name: +* palDvxv + +* Purpose: +* Vector product of two 3-vectors + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDvxv( double va[3], double vb[3], double vc[3] ); + +* Arguments: +* va = double [3] (Given) +* First vector +* vb = double [3] (Given) +* Second vector +* vc = double [3] (Returned) +* Result vector + +* Description: +* Vector product of two 3-vectors. + +* Notes: +* - Uses eraPxp(). See SOFA/ERFA documentation for details. + +*- +*/ + +void palDvxv ( double va[3], double vb[3], double vc[3] ) { + eraPxp( va, vb, vc ); +} + +/* +*+ +* Name: +* palEpb + +* Purpose: +* Conversion of modified Julian Data to Besselian Epoch + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* epb = palEpb ( double date ); + +* Arguments: +* date = double (Given) +* Modified Julian Date (JD - 2400000.5) + +* Returned Value: +* Besselian epoch. + +* Description: +* Conversion of modified Julian Data to Besselian Epoch. + +* Notes: +* - Uses eraEpb(). See SOFA/ERFA documentation for details. + +*- +*/ + +/* Requires additional SLA MJD reference date */ +double palEpb ( double date ) { + return eraEpb( PAL__MJD0, date ); +} + +/* +*+ +* Name: +* palEpb2d + +* Purpose: +* Conversion of Besselian Epoch to Modified Julian Date + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* mjd = palEpb2d ( double epb ); + +* Arguments: +* epb = double (Given) +* Besselian Epoch + +* Returned Value: +* Modified Julian Date (JD - 2400000.5) + +* Description: +* Conversion of Besselian Epoch to Modified Julian Date. + +* Notes: +* - Uses eraEpb2jd(). See SOFA/ERFA documentation for details. + +*- +*/ + + +double palEpb2d ( double epb ) { + double djm0, djm; + eraEpb2jd( epb, &djm0, &djm ); + return djm; +} + +/* +*+ +* Name: +* palEpj + +* Purpose: +* Conversion of Modified Julian Date to Julian Epoch + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* epj = palEpj ( double date ); + +* Arguments: +* date = double (Given) +* Modified Julian Date (JD - 2400000.5) + +* Returned Value: +* The Julian Epoch. + +* Description: +* Conversion of Modified Julian Date to Julian Epoch. + +* Notes: +* - Uses eraEpj(). See SOFA/ERFA documentation for details. + +*- +*/ + +/* Requires additional SLA MJD reference date */ +double palEpj ( double date ) { + return eraEpj( PAL__MJD0, date ); +} + +/* +*+ +* Name: +* palEpj2d + +* Purpose: +* Conversion of Julian Epoch to Modified Julian Date + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* mjd = palEpj2d ( double epj ); + +* Arguments: +* epj = double (Given) +* Julian Epoch. + +* Returned Value: +* Modified Julian Date (JD - 2400000.5) + +* Description: +* Conversion of Julian Epoch to Modified Julian Date. + +* Notes: +* - Uses eraEpj2d(). See SOFA/ERFA documentation for details. + +*- +*/ +double palEpj2d ( double epj ) { + double djm0, djm; + eraEpj2jd( epj, &djm0, &djm ); + return djm; +} + +/* +*+ +* Name: +* palEqeqx + +* Purpose: +* Equation of the equinoxes (IAU 2000/2006) + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palEqeqx( double date ); + +* Arguments: +* date = double (Given) +* TT as Modified Julian Date (JD-400000.5) + +* Description: +* Equation of the equinoxes (IAU 2000/2006). + +* Notes: +* - Uses eraEe06a(). See SOFA/ERFA documentation for details. + +*- +*/ + +/* Requires additional SLA MJD reference date */ +double palEqeqx ( double date ) { + return eraEe06a( PAL__MJD0, date ); +} + +/* +*+ +* Name: +* palFk5hz + +* Purpose: +* Transform an FK5 (J2000) star position into the frame of the +* Hipparcos catalogue. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palFk5hz ( double r5, double d5, double epoch, +* double *rh, double *dh ); + +* Arguments: +* r5 = double (Given) +* FK5 RA (radians), equinox J2000, epoch "epoch" +* d5 = double (Given) +* FK5 dec (radians), equinox J2000, epoch "epoch" +* epoch = double (Given) +* Julian epoch +* rh = double * (Returned) +* RA (radians) +* dh = double * (Returned) +* Dec (radians) + +* Description: +* Transform an FK5 (J2000) star position into the frame of the +* Hipparcos catalogue. + +* Notes: +* - Assumes zero Hipparcos proper motion. +* - Uses eraEpj2jd() and eraFk5hz. +* See SOFA/ERFA documentation for details. + +*- +*/ + +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 ); +} + +/* +*+ +* Name: +* palGmst + +* Purpose: +* Greenwich mean sidereal time (consistent with IAU 2006 precession). + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* mst = palGmst ( double ut1 ); + +* Arguments: +* ut1 = double (Given) +* Universal time (UT1) expressed as modified Julian Date (JD-2400000.5) + +* Returned Value: +* Greenwich mean sidereal time + +* Description: +* Greenwich mean sidereal time (consistent with IAU 2006 precession). + +* Notes: +* - Uses eraGmst06(). See SOFA/ERFA documentation for details. + +*- +*/ + +/* 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 ); +} + +/* +*+ +* Name: +* palGmsta + +* Purpose: +* Greenwich mean sidereal time (consistent with IAU 2006 precession). + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* mst = palGmsta ( double date, double ut1 ); + +* Arguments: +* date = double (Given) +* UT1 date (MJD: integer part of JD-2400000.5) +* ut1 = double (Given) +* UT1 time (fraction of a day) + +* Returned Value: +* Greenwich mean sidereal time (in range 0 to 2 pi) + +* Description: +* Greenwich mean sidereal time (consistent with IAU 2006 precession). + +* Notes: +* - For best accuracy use eraGmst06() directly. +* - Uses eraGmst06(). See SOFA/ERFA documentation for details. + +*- +*/ + +/* 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 ); +} + +/* +*+ +* Name: +* palHfk5z + +* Purpose: +* Hipparcos star position to FK5 J2000 + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palHfk5z( double rh, double dh, double epoch, +* double *r5, double *d5, double *dr5, double *dd5 ); + +* Arguments: +* rh = double (Given) +* Hipparcos RA (radians) +* dh = double (Given) +* Hipparcos Dec (radians) +* epoch = double (Given) +* Julian epoch (TDB) +* r5 = double * (Returned) +* RA (radians, FK5, equinox J2000, epoch "epoch") +* d5 = double * (Returned) +* Dec (radians, FK5, equinox J2000, epoch "epoch") + +* Description: +* Transform a Hipparcos star position into FK5 J2000, assuming +* zero Hipparcos proper motion. + +* Notes: +* - Uses eraEpj2jd and eraHfk5z(). See SOFA/ERFA documentation for details. + +*- +*/ + +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 ); +} + +/* +*+ +* Name: +* palRefcoq + +* Purpose: +* Determine the constants A and B in the atmospheric refraction model + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palRefcoq( double tdk, double pmb, double rh, double wl, +* double *refa, double *refb ); + +* Arguments: +* tdk = double (Given) +* Ambient temperature at the observer (K) +* pmb = double (Given) +* Pressure at the observer (millibar) +* rh = double (Given) +* Relative humidity at the observer (range 0-1) +* wl = double (Given) +* Effective wavelength of the source (micrometre). +* Radio refraction is chosen by specifying wl > 100 micrometres. +* refa = double * (Returned) +* tan Z coefficient (radian) +* refb = double * (Returned) +* tan**3 Z coefficient (radian) + +* Description: +* Determine the constants A and B in the atmospheric refraction +* model dZ = A tan Z + B tan**3 Z. This is a fast alternative +* to the palRefco routine. +* +* 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. + +* Notes: +* - Uses eraRefco(). See SOFA/ERFA documentation for details. +* - Note that the SOFA/ERFA routine uses different order of +* of arguments and uses deg C rather than K. + +*- +*/ + +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/ast/pal/palPrebn.c b/ast/pal/palPrebn.c new file mode 100644 index 0000000..989ce59 --- /dev/null +++ b/ast/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/ast/pal/palPrec.c b/ast/pal/palPrec.c new file mode 100644 index 0000000..678770d --- /dev/null +++ b/ast/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/ast/pal/palPrenut.c b/ast/pal/palPrenut.c new file mode 100644 index 0000000..04e36f3 --- /dev/null +++ b/ast/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/ast/pal/palPvobs.c b/ast/pal/palPvobs.c new file mode 100644 index 0000000..763d202 --- /dev/null +++ b/ast/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/ast/pal/palRvgalc.c b/ast/pal/palRvgalc.c new file mode 100644 index 0000000..2f01576 --- /dev/null +++ b/ast/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/ast/pal/palRvlg.c b/ast/pal/palRvlg.c new file mode 100644 index 0000000..255801e --- /dev/null +++ b/ast/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/ast/pal/palRvlsrd.c b/ast/pal/palRvlsrd.c new file mode 100644 index 0000000..6302c4f --- /dev/null +++ b/ast/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 palRvlsrk. + +* 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/ast/pal/palRvlsrk.c b/ast/pal/palRvlsrk.c new file mode 100644 index 0000000..968188a --- /dev/null +++ b/ast/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 palRvlsrd. + +* 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/ast/pal/palSubet.c b/ast/pal/palSubet.c new file mode 100644 index 0000000..ada122a --- /dev/null +++ b/ast/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/ast/pal/palSupgal.c b/ast/pal/palSupgal.c new file mode 100644 index 0000000..1f4aeb2 --- /dev/null +++ b/ast/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/ast/pal/palmac.h b/ast/pal/palmac.h new file mode 100644 index 0000000..207b843 --- /dev/null +++ b/ast/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/ast/pal2ast.h b/ast/pal2ast.h new file mode 100644 index 0000000..83bab9a --- /dev/null +++ b/ast/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/ast/palwrap.c b/ast/palwrap.c new file mode 100644 index 0000000..ca9a38f --- /dev/null +++ b/ast/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/ast/parallel.pdf b/ast/parallel.pdf new file mode 100644 index 0000000..c02ef03 Binary files /dev/null and b/ast/parallel.pdf differ diff --git a/ast/pcdmap.c b/ast/pcdmap.c new file mode 100644 index 0000000..7c043d6 --- /dev/null +++ b/ast/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", AST__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", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* PcdCen. */ +/* ------- */ + } else if ( !strcmp( attrib, "pcdcen" ) ) { + dval = astGetPcdCen( this, 0 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__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/ast/pcdmap.h b/ast/pcdmap.h new file mode 100644 index 0000000..ca8fc24 --- /dev/null +++ b/ast/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/ast/permmap.c b/ast/permmap.c new file mode 100644 index 0000000..69b0962 --- /dev/null +++ b/ast/permmap.c @@ -0,0 +1,3204 @@ +/* +*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. +* 26-MAY-2016 (DSB): +* Allow the PermSplit attribute to be changed at any time. This is +* because it does not directly affect either the forward or inverse +* transformation of the PermMap. The FitsCHan class needs to be able +* to change it to determine when checking if the -TAB algorithm can +* be used. +*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, unlike most Mapping attributes, the value of this attribute +* may be changed at any time. This is because it does not change the +* nature of either the forward or inverse transformation of the Mapping. + +* Applicability: +* PermMap +* All PermMaps have this attribute. +*att- +*/ +astMAKE_CLEAR(PermMap,PermSplit,permsplit,-INT_MAX) +astMAKE_GET(PermMap,PermSplit,int,0,( this->permsplit != -INT_MAX ? + this->permsplit : 0 )) +astMAKE_SET(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/ast/permmap.h b/ast/permmap.h new file mode 100644 index 0000000..0115783 --- /dev/null +++ b/ast/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/ast/pg3d.h b/ast/pg3d.h new file mode 100644 index 0000000..e4dab63 --- /dev/null +++ b/ast/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/ast/plot.c b/ast/plot.c new file mode 100644 index 0000000..a701116 --- /dev/null +++ b/ast/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, AST__DBL_DIG, start[0], AST__DBL_DIG, start[1], AST__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 to 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 to 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 to 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", AST__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", AST__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", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Gap. */ +/* ---- */ + } else if ( !strcmp( attrib, "gap" ) ) { + dval = GetUsedGap( this, 0, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__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", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* LogGap. */ +/* ---- */ + } else if ( !strcmp( attrib, "loggap" ) ) { + dval = GetUsedLogGap( this, 0, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__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", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* NumLabGap. */ +/* -------- */ + } else if ( !strcmp( attrib, "numlabgap" ) ) { + dval = astGetNumLabGap( this, 0 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__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", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* TextLabGap. */ +/* ----------- */ + } else if ( !strcmp( attrib, "textlabgap" ) ) { + dval = astGetTextLabGap( this, 0 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__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", AST__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", AST__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", AST__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", AST__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", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* TitleGap. */ +/* --------- */ + } else if ( !strcmp( attrib, "titlegap" ) ) { + dval = astGetTitleGap( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* MajTickLen. */ +/* ----------- */ + } else if ( !strcmp( attrib, "majticklen" ) ) { + dval = GetUsedMajTickLen( this, 0, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__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", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* MinTickLen. */ +/* ----------- */ + } else if ( !strcmp( attrib, "minticklen" ) ) { + dval = astGetMinTickLen( this, 0 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__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", AST__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 to 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*AST__DBL_DIG ]; /* Forward log mapping expression */ + char invexp[ 28 + 2*AST__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", AST__DBL_DIG, a, AST__DBL_DIG, b ); + (void) sprintf( fwdexp, "b=pow(10,(g-%.*g)/%.*g)", AST__DBL_DIG, b, AST__DBL_DIG, a ); + + } else { + b = ( g2*log10( -b1 ) - g1*log10( -b2 ) )/c; + (void) sprintf( invexp, "g=%.*g*log10(-b)+%.*g", AST__DBL_DIG, a, AST__DBL_DIG, b ); + (void) sprintf( fwdexp, "b=-pow(10,(g-%.*g)/%.*g)", AST__DBL_DIG, b, AST__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/ast/plot.f b/ast/plot.f new file mode 100644 index 0000000..27e83b4 --- /dev/null +++ b/ast/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/ast/plot.h b/ast/plot.h new file mode 100644 index 0000000..6f702ab --- /dev/null +++ b/ast/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/ast/plot3d.c b/ast/plot3d.c new file mode 100644 index 0000000..1bed16c --- /dev/null +++ b/ast/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 to 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", AST__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/ast/plot3d.h b/ast/plot3d.h new file mode 100644 index 0000000..4826b44 --- /dev/null +++ b/ast/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/ast/pointlist.c b/ast/pointlist.c new file mode 100644 index 0000000..61117c2 --- /dev/null +++ b/ast/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/ast/pointlist.h b/ast/pointlist.h new file mode 100644 index 0000000..a2e35b7 --- /dev/null +++ b/ast/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/ast/pointset.c b/ast/pointset.c new file mode 100644 index 0000000..cdbdef6 --- /dev/null +++ b/ast/pointset.c @@ -0,0 +1,3285 @@ +/* +* 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. +* 24-MAY-2016 (DSB): +* Added astShowPoints. +*/ + +/* 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 SetNpoint( AstPointSet *, int, int * ); +static void SetPoints( AstPointSet *, double **, int * ); +static void SetSubPoints( AstPointSet *, int, int, AstPointSet *, int * ); +static void ShowPoints( 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->ShowPoints = ShowPoints; + + 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 void ShowPoints( AstPointSet *this, int *status ) { +/* +*+ +* Name: +* astShowPoints + +* Purpose: +* Display the contents of a PointSet on standard output as a table +* in TOPCAT ASCII format. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "pointset.h" +* void astShowPoints( AstPointSet *this ) + +* Class Membership: +* PointSet method. + +* Description: +* This function displays the contents of the supplied PointSet on +* standard output as a table in TOPCAT "ascii" format. The first row +* is a comment that defines the column (axis) names as AXIS1, AXIS2, +* etc. Each subsequent row contains the axis values for one point. +* Bad axis values are represented by the string "null". + +* Parameters: +* this +* Pointer to the first PointSet. +*- +*/ + +/* Local Variables: */ + double **ptr; + double *p; + 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 and the number + of points. */ + ptr = astGetPoints( this ); + nc = astGetNcoord( this ); + np = astGetNpoint( this ); + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* Display the header. */ + printf("# "); + for( ic = 0; ic < nc; ic++ ) { + printf("Axis%d ", ic + 1 ); + } + printf("\n"); + +/* Display each row. */ + for( ip = 0; ip < np; ip++,p++ ) { + for( ic = 0; ic < nc; ic++ ) { + if( ptr[ic][ip] != AST__BAD ) { + printf("%.*g ", AST__DBL_DIG, ptr[ic][ip] ); + } else { + printf("%*s ", -AST__DBL_DIG, "null"); + } + } + printf("\n"); + } + } +} + +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: */ + 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. */ + +/* 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 ); +} +void astShowPoints_( AstPointSet *this, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,PointSet,ShowPoints))( this, status ); +} + + + + + diff --git a/ast/pointset.h b/ast/pointset.h new file mode 100644 index 0000000..422a077 --- /dev/null +++ b/ast/pointset.h @@ -0,0 +1,711 @@ +#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 * ); + void (* ShowPoints)( 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 * ); +void astShowPoints_( 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)) +#define astShowPoints(this) \ +astINVOKE(V,astShowPoints_(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/ast/polygon.c b/ast/polygon.c new file mode 100644 index 0000000..1dfae39 --- /dev/null +++ b/ast/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/ast/polygon.h b/ast/polygon.h new file mode 100644 index 0000000..2cd46b6 --- /dev/null +++ b/ast/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/ast/polymap.c b/ast/polymap.c new file mode 100644 index 0000000..498000f --- /dev/null +++ b/ast/polymap.c @@ -0,0 +1,6107 @@ +/* +*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, the default behaviour is to use +* an iterative method to evaluate the inverse based only on the forward +* transformation (see attribute IterInverse). + +* 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 - astPolyCoeffs: Retrieve the coefficients of a PolyMap transformation +c - astPolyTran: Fit a PolyMap inverse or forward transformation +f - AST_POLYCOEFFS: Retrieve the coefficients of a PolyMap transformation +f - AST_POLYTRAN: Fit a PolyMap inverse or forward transformation + +* Copyright: +* Copyright (C) 2017 East Asian Observatory. +* 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. +* 11-NOV-2016 (DSB): +* - Fix bug in MapMerge that could cause a seg fault. It did not +* check that the PolyMap had a defined transformation before accessing +* the transformation's coefficient array. +* - Fix similar bugs in Equal that could cause seg faults. +* 15-MAR-2017 (DSB): +* - Change the GetTranForward and GetTranInverse functions so that they +* take into account the state of the Invert attribute. +* - Improve docs for the IterInverse attribute to explain that the +* inverse transformation replaced is always the original inverse +* transformation, as defined by the arguments supplied to the PolyMap +* constructor, regardless of the state of the Invert attribute. +* 17-MAR-2017 (DSB): +* - Add the astPolyCoeffs method. +* 30-MAR-2017 (DSB): +* Modify the astPolyTran method so that it can be used by the +* ChebyMap class to determine new transformations implemented as +* Chebyshev polynomials. +* 27-JUN-2017 (DSB): +* In SamplePoly1D/2D ensure the final sample on each axis does not +* go above the supplied upper bound. This can happen due to rounding +* error. This is important for ChebyMaps since points outside the +* bounds are set bad when transformed using a ChebyMap, causing NaNs +* to be generated in lmder1 (cminpack minimisation function). +* 3-JUL-2017 (DSB): +* Within FitPoly1D and FitPoly2D, use an initial guess that represents +* a unit mapping between normalised input and output values, rather +* than unnormalised PolyMap values. This is in case the PolyMap being +* fitted includes a change of scale (e.g. the PolyMap input is in "mm" +* but the output is in "rads" and includes some large scaling factor +* to do the conversion). +* 9-JAN-2018 (DSB): +* Correct Transform to take account of AST__BAD coeffs correctly. +* Previously bad coeffs could generate NaN output values. +*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 + + + +/* 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( AstPolyMap *, int, int, double, int, double **, double[2], int *, double *, int * ); +static double *FitPoly2D( AstPolyMap *, int, 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 PolyPowers( AstPolyMap *, double **, int, const int *, double **, int, int, int * ); +static void StoreArrays( AstPolyMap *, int, int, const double *, int * ); +static void PolyCoeffs( AstPolyMap *, int, int, double *, int *, int * ); +static void FitPoly1DInit( AstPolyMap *, int, double **, AstMinPackData *, double *, int *); +static void FitPoly2DInit( AstPolyMap *, int, double **, AstMinPackData *, 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; + int tmp; + +/* 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 ) ) { + +/* Assume they are the same until we find a difference. */ + result = 1; + +/* The "_f" and "_i" suffixes in the PolyMap structure array names refer + to the forward and inverse transformations of the original uninverted + PolyMap. So we need to ensure the "nout" and "nin" values also refer + to the uninverted values. So if the PolyMaps are inverted, swap nout + and nin. */ + if( astGetInvert( this ) ) { + tmp = nin; + nin = nout; + nout = tmp; + } + +/* Check properties of the forward transformation. */ + if( this->ncoeff_f && that->ncoeff_f ) { + for( i = 0; i < nout && result; i++ ) { + if( this->ncoeff_f[ i ] != that->ncoeff_f[ i ] ){ + result = 0; + } + } + } else if( this->ncoeff_f || that->ncoeff_f ) { + result = 0; + } + + if( this->mxpow_f && that->mxpow_f ) { + for( i = 0; i < nout && result; i++ ) { + if( this->mxpow_f[ i ] != that->mxpow_f[ i ] ){ + result = 0; + } + } + } else if( this->mxpow_f || that->mxpow_f ) { + 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; + } + } + } + } else if( this->coeff_f || that->coeff_f ) { + 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; + } + } + } + } + } else if( this->power_f || that->power_f ) { + result = 0; + } + +/* Check properties of the inverse transformation. */ + if( this->ncoeff_i && that->ncoeff_i ) { + for( i = 0; i < nout && result; i++ ) { + if( this->ncoeff_i[ i ] != that->ncoeff_i[ i ] ){ + result = 0; + } + } + } else if( this->ncoeff_i || that->ncoeff_i ) { + result = 0; + } + + if( this->mxpow_i && that->mxpow_i ) { + for( i = 0; i < nout && result; i++ ) { + if( this->mxpow_i[ i ] != that->mxpow_i[ i ] ){ + result = 0; + } + } + } else if( this->mxpow_i || that->mxpow_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; + } + } + } + } else if( this->coeff_f || that->coeff_f ) { + 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; + } + } + } + } + } else if( this->power_f || that->power_f ) { + 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( AstPolyMap *this, int forward, 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( AstPolyMap *this, int forward, 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, and returns the coefficients of a +* PolyMap to describe the fit. For the purposes of this function, +* the input to the fit is refered to as x1 and the output as y1. So +* the returned coefficients describe a PolyMap with forward +* transformation: +* +* y1 = P1( x1 ) + +* Parameters: +* this +* Pointer to the PolyMap. +* forward +* Non-zero if the forward transformation of "this" is being +* replaced. Zero if the inverse transformation of "this" is being +* replaced. +* nsamp +* The number of (x1,y1) positions in the supplied table. +* acc +* The required accuracy, expressed as a geodesic distance within +* the polynomials output space (not the normalised tabular 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 normalised and sampled +* values for x1 and y1 in that order. +* scales +* Array holding the scaling factors used to produced the normalised +* values in the two columns of the table. Multiplying the normalised +* table values by the scale factor produces input or output axis +* values for the returned PolyMap +* ncoeff +* Pointer to an int 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: */ + AstMinPackData data; + double *coeffs; + double *pc; + double *pr; + double *pxp1; + double *result; + double *work1; + double *work2; + double *work4; + double f1; + double f2; + double maxterm; + double term; + double tv; + 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 ) { + +/* Find all the required powers of x1 and store them in the "xp1" + component of the data structure. The required initialisation is done + differently for different subclasses of PolyMap, so we need to wrap + it up in a virtual function. */ + astFitPoly1DInit( this, forward, table, &data, scales ); + +/* The initial guess at the coefficient values represents a unit + transformation in (normalised) tabulated (x,y) values. Using normalised + values means that we are, effectively, including a guess at the linear + scaling factor between input and output of the PolyMap (e.g. the PolyMap + may have inputs in mm and outputs in radians). */ + for( k = 0; k < ncof; k++ ) coeffs[ k ] = 0.0; + coeffs[ 1 ] = 1.0; + +/* 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. The "work1" array holds the normalised Y + residuals at each tabulated point. */ + 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 void FitPoly1DInit( AstPolyMap *this, int forward, double **table, + AstMinPackData *data, double *scales, int *status ){ +/* +*+ +* Name: +* astFitPoly1DInit + +* Purpose: +* Perform initialisation needed for FitPoly1D + +* Type: +* Protected function. + +* Synopsis: +* #include "polymap.h" +* void astFitPoly1DInit( AstPolyMap *this, int forward, double **table, +* AstMinPackData *data, double *scales, +* int *status ) + +* Class Membership: +* PolyMap virtual function. + +* Description: +* This function performs initialisation needed for FitPoly1D. + +* Parameters: +* this +* Pointer to the PolyMap. +* forward +* Non-zero if the forward transformation of "this" is being +* replaced. Zero if the inverse transformation of "this" is being +* replaced. +* 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. +* data +* Pointer to a structure holding information to pass the the +* service function invoked by the minimisation function. +* 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. +*- +*/ + +/* Local Variables; */ + double *px1; + double *pxp1; + double tv; + double x1; + int k; + int w1; + +/* Check the local error status. */ + if ( !astOK ) return; + +/* 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 < data->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 < data->order; w1++ ) { + *(pxp1++) = tv; + tv *= x1; + } + } +} + +static double *FitPoly2D( AstPolyMap *this, int forward, 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( AstPolyMap *this, int forward, 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, and returns the coefficients +* of a PolyMap to describe the fit. For the purposes of this function, +* the inputs to the fit is refered to as (x1,x2) and the output as +* (y1,y2). So the returned coefficients describe a PolyMap with forward +* transformations: +* +* 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: +* this +* Pointer to the PolyMap. +* forward +* Non-zero if the forward transformation of "this" is being +* replaced. Zero if the inverse transformation of "this" is being +* replaced. +* 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 (not the normalised tabular 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 normalised and sampled +* values for x1, x2, y1 or y2 in that order. +* scales +* Array holding the scaling factors used to produced the normalised +* values in the four columns of the table. Multiplying the normalised +* table values by the scale factor produces input or output axis +* values for the returned PolyMap +* 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: */ + AstMinPackData data; + double *coeffs; + double *pc; + double *pr; + 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; + 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 ) { + +/* Find all the required powers of x1 and x2 and store them in the "xp1" + and "xp2" components of the data structure. The required initialisation + is done differently for different subclasses of PolyMap, so we need to + wrap it up in a virtual function. */ + astFitPoly2DInit( this, forward, table, &data, scales ); + +/* The initial guess at the coefficient values represents a unit + transformation in (normalised) tabulated (x,y) values. Using normalised + values means that we are, effectively, including a guess at the linear + scaling factor between input and output of the PolyMap (e.g. the PolyMap + may have inputs in mm and outputs in radians). */ + for( k = 0; k < 2*ncof; k++ ) coeffs[ k ] = 0.0; + coeffs[ 1 ] = 1.0; + coeffs[ 5 ] = 1.0; + +/* 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 FitPoly2DInit( AstPolyMap *this, int forward, double **table, + AstMinPackData *data, double *scales, int *status ){ +/* +*+ +* Name: +* astFitPoly2DInit + +* Purpose: +* Perform initialisation needed for FitPoly2D + +* Type: +* Protected function. + +* Synopsis: +* #include "polymap.h" +* void astFitPoly2DInit( AstPolyMap *this, int forward, double **table, +* AstMinPackData *data, double *scales, +* int *status ) + +* Class Membership: +* PolyMap virtual function. + +* Description: +* This function performs initialisation needed for FitPoly2D. + +* Parameters: +* this +* Pointer to the PolyMap. +* forward +* Non-zero if the forward transformation of "this" is being +* replaced. Zero if the inverse transformation of "this" is being +* replaced. +* 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. +* data +* Pointer to a structure holding information to pass the the +* service function invoked by the minimisation function. +* 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. +*- +*/ + +/* Local Variables; */ + double *px1; + double *px2; + double *pxp1; + double *pxp2; + double tv; + double x1; + double x2; + int k; + int w1; + int w2; + +/* Check the local error status. */ + if ( !astOK ) return; + +/* 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 < data->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 < data->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 < data->order; w2++ ) { + *(pxp2++) = tv; + tv *= x2; + } + } +} + +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", AST__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 */ + int result; /* The returned value */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the PolyMap. */ + map = (AstPolyMap *) this; + +/* First deal with cases where the PolyMap has not been inverted. */ + if( ! astGetInvert( this ) ) { + +/* The PolyMap has a defined forward transformation if one or more + coefficients values were supplied for the original forward + transformation. It is not possible to replace the original forward + transformation with an iterative algorithm. */ + result = map->ncoeff_f ? 1 : 0; + +/* Now deal with cases where the PolyMap has been inverted. */ + } else { + +/* The PolyMap has a defined forward transformation if one or more + coefficients values were supplied for the original inverse + transformation, or if the original inverse transformation is being + approximated using an iterative algorithm. */ + result = ( map->ncoeff_i || astGetIterInverse( map ) ) ? 1 : 0; + } + +/* Return the result. */ + return result; +} + +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 */ + int result; /* The returned value */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the PolyMap. */ + map = (AstPolyMap *) this; + +/* First deal with cases where the PolyMap has not been inverted. */ + if( ! astGetInvert( this ) ) { + +/* The PolyMap has a defined inverse transformation if one or more + coefficients values were supplied for the original inverse + transformation, or if the original inverse transformation is being + approximated using an iterative algorithm. */ + result = ( map->ncoeff_i || astGetIterInverse( map ) ) ? 1 : 0; + +/* Now deal with cases where the PolyMap has been inverted. */ + } else { + +/* The PolyMap has a defined inverse transformation if one or more + coefficients values were supplied for the original forward + transformation. It is not possible to replace the original forward + transformation with an iterative algorithm. */ + result = map->ncoeff_f ? 1 : 0; + } + +/* Return the result. */ + return result; +} + +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->PolyPowers = PolyPowers; + vtab->FitPoly1DInit = FitPoly1DInit; + vtab->FitPoly2DInit = FitPoly2DInit; + vtab->PolyTran = PolyTran; + vtab->PolyCoeffs = PolyCoeffs; + + 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: */ + AstMinPackData *data; + double *px1; + double *py; + const double *vp; + double *vr; + double res; + int k; + int w1; + +/* Get a pointer to the data structure. */ + data = (AstMinPackData *) 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: */ + AstMinPackData *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 = (AstMinPackData *) adata; + +/* Initialise a pointer to the current returned residual value. */ + vr = hx; + +/* Initialise 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-w2", 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: */ + AstMinPackData *data; + int k; + int w1; + +/* Get a pointer to the data structure. */ + data = (AstMinPackData *) 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: */ + AstMinPackData *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 = (AstMinPackData *) 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 nin; /* Number of input coordinates for nominated PolyMap */ + int nout; /* Number of output coordinates for nominated PolyMap */ + int ok; /* Are PolyMaps equivalent? */ + int oldinv0; /* Original Invert value in pmap0 */ + int oldinv1; /* Original Invert value in pmap1 */ + int result; /* Result value to return */ + +/* 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 ) { + +/* Temporarily set the Invert flag of the nominated PolyMap to the + required value, first saving the original value so that it can be + re-instated later. */ + oldinv0 = astGetInvert( pmap0 ); + astSetInvert( pmap0, ( *invert_list )[ where ] ); + +/* Get the number of inputs and outputs to the nominated PolyMap. */ + nin = astGetNin( pmap0 ); + nout = astGetNout( 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( ! astIsAPolyMap( ( *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; + +/* We use the astEqual method to check that the two PolyMaps are equal. + But at the moment they may not be equal because they may have + different Invert flags. Therefore, temporarily set the invert flag + of the neighbour so that it is the same as the nominated PolyMap, + first saving the original value so that it can be re-instated later. + Note, we have already checked that the two PolyMaps are used in opposite + directions within the CmpMap. */ + oldinv1 = astGetInvert( pmap1 ); + astSetInvert( pmap1, ( *invert_list )[ where ] ); + +/* Use astEqual to check that the coefficients etc are equal in the two + PolyMaps. */ + ok = astEqual( pmap0, pmap1 ); + +/* Re-instate the original value of the Invert flag in the neighbour. */ + astSetInvert( pmap1, oldinv1 ); + +/* Pass on to the next neighbour if the current neighbour differs from + the nominated PolyMap. */ + 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. */ + pmap0 = astAnnul( pmap0 ); + pmap1 = 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; + } + +/* If the nominated PolyMap was not replaced by a UnitMap, then + re-instate its original value for the Invert flag. */ + if( pmap0 ) astSetInvert( pmap0, oldinv0 ); + } + +/* 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 void PolyCoeffs( AstPolyMap *this, int forward, int nel, double *coeffs, + int *ncoeff, int *status ){ +/* +*++ +* Name: +c astPolyCoeffs +f AST_POLYCOEFFS + +* Purpose: +* Retrieve the coefficient values used by a PolyMap. + +* Type: +* Public function. + +* Synopsis: +c #include "polymap.h" +c void astPolyCoeffs( AstPolyMap *this, int forward, int nel, double *coeffs, +c int *ncoeff ) +f CALL AST_POLYCOEFFS( THIS, FORWARD, NEL, COEFFS, NCOEFF, STATUS ) + +* Class Membership: +* PolyMap method. + +* Description: +* This function returns the coefficient values used by either the +* forward or inverse transformation of a PolyMap, in the same form +* that they are supplied to the PolyMap constructor. +* +* Usually, you should call this method first with +c "nel" +f NEL +* set to zero to determine the number of coefficients used by the +* PolyMap. This allows you to allocate an array of the correct size to +* hold all coefficient data. You should then call this method a +* second time to get the coefficient data. + +* 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 coefficients of the forward PolyMap transformation are +* returned. Otherwise the inverse transformation coefficients are +* returned. +c nel +f NEL = INTEGER (Given) +* The length of the supplied +c "coeffs" +f COEFFS +* array. It should be at least "ncoeff*( nin + 2 )" if +c "foward" is non-zero, +f FORWARD is .TRUE., +* and "ncoeff*( nout + 2 )" otherwise, where "ncoeff" is the +* number of coefficients to be returned. If a value of zero +* is supplied, no coefficient values are returned, but the +* number of coefficients used by the transformation is still +* returned in +c "ncoeff". +f NCOEFF. +c coeffs +f COEFFS( NEL ) = DOUBLE PRECISION (Returned) +* An array in which to return the coefficients used by the +* requested transformation of the PolyMap. Ignored if +c "nel" is zero. +f NEL is zero. +* The coefficient data is returned in the form in which it is +* supplied to the PolyMap constructor. That is, each group of +* "2 + nin" or "2 + nout" adjacent elements describe a single +* coefficient of the forward or inverse transformation. See the +* PolyMap constructor documentation for further details. +* +* If the supplied array is too short to hold all the coefficients, +* trailing coefficients are excluded. If the supplied array is +* longer than needed to hold all the coefficients, trailing +* elements are filled with zeros. +c ncoeff +f NCOEFF = INTEGER (Returned) +* The number of coefficients used by the requested transformation. +* A value of zero is returned if the transformation does not +* have any defining polynomials. A value is returned for this argument +* even if +c "nel" is zero. +f NEL is zero. +f STATUS = INTEGER (Given and Returned) +f The global status. + +*-- +*/ + +/* Local Variables: */ + double **coeff; + int ***power; + int *nco; + int *pp; + int iax; + int icoeff; + int iel; + int ipoly; + int nax; + int npoly; + +/* Initialise */ + *ncoeff = 0; + +/* Check the inherited status. */ + if ( !astOK ) return; + +/* Fill any supplied array with zeros. */ + if( nel ) memset( coeffs, 0, nel*sizeof( *coeffs ) ); + +/* Get the values to use, taking account of whether the PolyMap has been + inverted or not. */ + if( forward != astGetInvert( this ) ){ + nco = this->ncoeff_f; + power = this->power_f; + coeff = this->coeff_f; + npoly = astGetNout( this ); + nax = astGetNin( this ); + } else { + nco = this->ncoeff_i; + power = this->power_i; + coeff = this->coeff_i; + npoly = astGetNin( this ); + nax = astGetNout( this ); + } + +/* Notheg to do if there are no coeffs. */ + if( nco && power && coeff ) { + +/* Initialise index of next value to store in returned array. */ + iel = 0; + +/* Loop round each 1D polynomial. */ + for( ipoly = 0; ipoly < npoly; ipoly++ ){ + +/* Loop round coefficients. */ + for( icoeff = 0; icoeff < nco[ ipoly ]; icoeff++ ) { + +/* Store the coefficient value in the next element of the returned array, + if the array is not already full. Increment the pointer to the next + element. */ + if( iel < nel ) coeffs[ iel++ ] = coeff[ipoly][icoeff]; + +/* Next store the integer index of the PolyMap input or output which uses + the coefficient within its defining polynomial (the first axis has + index 1). */ + if( iel < nel ) coeffs[ iel++ ] = ipoly + 1; + +/* The remaining elements of the group give the integer powers to use + with each input or output coordinate value. */ + pp = power[ipoly][icoeff]; + for( iax = 0; iax < nax; iax++,pp++ ) { + if( iel < nel ) coeffs[ iel++ ] = *pp; + } + } + +/* Increment the total number of coefficients used by the transformation. */ + *ncoeff += nco[ ipoly ]; + } + } +} + +static void PolyPowers( AstPolyMap *this, double **work, int ncoord, + const int *mxpow, double **ptr, int point, + int fwd, int *status ){ +/* +*+ +* Name: +* astPolyPowers + +* Purpose: +* Find the required powers of the input axis values. + +* Type: +* Protected function. + +* Synopsis: +* #include "polymap.h" +* void astPolyPowers( AstPolyMap *this, double **work, int ncoord, +* const int *mxpow, double **ptr, int point, +* int fwd ) + +* Class Membership: +* PolyMap virtual function. + +* Description: +* This function is used by astTransform to calculate the powers of +* the axis values for a single input position. In the case of +* sub-classes, the powers may not be simply powers of the supplied +* axis values but may be more complex quantities such as a Chebyshev +* polynomial of the required degree evaluated at the input axis values. + +* Parameters: +* this +* Pointer to the PolyMap. +* work +* An array of "ncoord" pointers, each pointing to an array of +* length "max(2,mxpow)". The required values are placed in this +* array on exit. +* ncoord +* The number of axes. +* mxpow +* Pointer to an array holding the maximum power required of each +* axis value. Should have "ncoord" elements. +* ptr +* An array of "ncoord" pointers, each pointing to an array holding +* the axis values. Each of these arrays of axis values must have +* at least "point+1" elements. +* point +* The zero based index of the point within "ptr" that holds the +* axis values to be exponentiated. +* fwd +* Do the supplied coefficients define the foward transformation of +* the PolyMap? +*- +*/ + +/* Local Variables; */ + double *pwork; + double x; + int coord; + int ip; + +/* Check the local error status. */ + if ( !astOK ) return; + +/* For the base PolyMap class, this method simply raises each input axis + value to the required power. Loop over all input axes. */ + for( coord = 0; coord < ncoord; coord++ ) { + +/* Get a pointer to the array in which the powers of the current axis + value are to be returned. */ + pwork = work[ coord ]; + +/* Anything to the power zero is 1.0. */ + pwork[ 0 ] = 1.0; + +/* Get the input axis value. If it is bad, store bad values for all + remaining powers. */ + x = ptr[ coord ][ point ]; + if( x == AST__BAD ) { + for( ip = 1; ip <= mxpow[ coord ]; ip++ ) pwork[ ip ] = AST__BAD; + +/* Otherwise, form and store the required powers of the input axis value. */ + } else { + for( ip = 1; ip <= mxpow[ coord ]; ip++ ) { + pwork[ ip ] = pwork[ ip - 1 ]*x; + } + } + } +} + +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, a NULL pointer is returned but no 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. + +* Applicability: +* PolyMap +* All PolyMaps have this method. +* ChebyMap +c The ChebyMap implementation of this method allows +c NULL pointers to be supplied for "lbnd" and/or "ubnd", +c in which case the corresponding bounds supplied when the ChebyMap +c was created are used. +* The returned PolyMap will be a ChebyMap, and the new transformation +* will be defined as a weighted sum of Chebyshev functions of the +* first kind. + +* 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 || !ubnd ) { + astError( AST__NODEF, "astPolyTran(%s): No upper and/or lower bounds " + "supplied.", status, astGetClass( this ) ); + } else 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. */ + 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( this, forward, 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( this, forward, 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. Rounding error may cause the + final point to be just over the upper bound, so we leave the final + point out of the loop and set it explicitly to the upper bound + afterwards. */ + val0 = lbnd; + p0 = ptr1[ 0 ]; + for( i = 0; i < npoint-1; i++ ) { + *(p0++) = val0; + val0 += delta0; + } + *p0 = ubnd; + +/* 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. Rounding error may cause the + final point on each axis to be just over the upper bound, so we leave + the final point out of the loop and set it explicitly to the upper bound + afterwards. */ + val0 = lbnd[ 0 ]; + p0 = ptr1[ 0 ]; + p1 = ptr1[ 1 ]; + for( i = 0; i < npoint - 1; i++ ) { + val1 = lbnd[ 1 ]; + for( j = 0; j < npoint-1; j++ ) { + *(p0++) = val0; + *(p1++) = val1; + val1 += delta1; + } + *(p0++) = val0; + *(p1++) = ubnd[ 1 ]; + val0 += delta0; + } + + + val1 = lbnd[ 1 ]; + for( j = 0; j < npoint-1; j++ ) { + *(p0++) = ubnd[ 0 ]; + *(p1++) = val1; + val1 += delta1; + } + *(p0++) = ubnd[ 0 ]; + *(p1++) = ubnd[ 1 ]; + + +/* Transform the input grid to get the output grid. */ + (void) astTransform( this, ps1, forward, ps2 ); + +/* Scale each pair of columns in turn. Use the same 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 outval; /* Output axis value */ + double term; /* Term to be added to output 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 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 original 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 original 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) ( astMAX( 2, 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. Note, using a virtual method here slows the PolyMap + Transform function down by about 5%, compared to doing the equivalent + calculations in-line. But we need some way to allow the ChebyMap class + to over-ride the calculation of the powers, so we must do something + like this. If the 5% slow-down is too much, it can be reduced down to + about 2% by replacing the invocation of the astPolyPowers_ interface + function with a direct call to the implementation function itself. + This involves replacing the astPolyPowers call below with this: + + (**astMEMBER(this,PolyMap,PolyPowers))( (AstPolyMap *) this, work, ncoord_in, + mxpow, ptr_in, point, forward, status ); + + The above could be wrapped up in an alternative implementation of the + astPolyPowers macro, so that it looks the same as the existing code. + In fact, this scheme could be more widely used to speed up invocation + of virtual functions within AST. The disadvantage is that the interface + functions for some virtual methods includes some extra processing, + over and above simply invoking the implementation function. + + Of course the other way to get rid of the 5% slow down, is to + revert to using in-line code below, and then replicate this entire + function in the ChebyMap class, making suitable changes to use + Chebyshev functions in place of simple powers. But that is bad + structuring... */ + astPolyPowers( this, work, ncoord_in, mxpow, ptr_in, point, + forward ); + +/* 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. */ + if( outval != AST__BAD ) 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 approximation method. +* +* The iterative inverse returns AST__BAD axis values at positions +* for which the inverse transformation is undefined. For instance, +* if the forward transformation is y = x*x, the iterative inverse +* will return x = AST__BAD at y = -1. If the inverse transformation +* is multiply defined, the position returned by the iterative inverse +* will be the position of the solution that is closest to the +* supplied position. For instance, using the above example, y = x*x, +* the iterative inverse will return x = +2 at y = 4, because x = +2 +* is the closest solution to 4 (the other solution is x = -2). + +* Applicability: +* PolyMap +* All PolyMaps have this attribute. +* ChebyMap +* The ChebyMap class does not currently provide an option for an +* iterative inverse, and so the IterInverse value is always zero. +* Setting or clearing the IterInverse attribute of a ChebyMap has +* no effect. + +* Notes: +* - The transformation replaced by the iterative algorithm is the +* transformation from the original PolyMap output space to the +* original PolyMap input space (i.e. the input and output spaces as +* defined by the arguments of the PolyMap constructor). This is still +* the case even if the PolyMap has subsequently been inverted. In +* other words if a PolyMap is created and then inverted, setting +* the IterInverse to a non-zero value will replace the forward +* transformation of the inverted PolyMap (i.e. the inverse +* transformation of the original PolyMap). It is not possible to +* replace the other transformation (i.e. from the original PolyMap +* input space to the original PolyMap output space) with an iterative +* algorithm. +* - If a PolyMap that has an iterative inverse transformation is +* subsequently inverted, the inverted PolyMap will have an iterative +* forward transformation. +* - 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 the 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, the default behaviour is to use +* an iterative method to evaluate the inverse based only on the forward +* transformation (see attribute IterInverse). + +* 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 +* default behaviour is to use an iterative method to evaluate the +* inverse based only on the forward transformation (see attribute +* IterInverse). +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. */ + +void astPolyPowers_( AstPolyMap *this, double **work, int ncoord, + const int *mxpow, double **ptr, int point, int fwd, + int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,PolyMap,PolyPowers))( this, work, ncoord, mxpow, ptr, + point, fwd, status ); +} + +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 ); +} + +void astPolyCoeffs_( AstPolyMap *this, int forward, int nel, double *array, + int *ncoeff, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,PolyMap,PolyCoeffs))( this, forward, nel, + array, ncoeff, status ); +} + +void astFitPoly1DInit_( AstPolyMap *this, int forward, double **table, + AstMinPackData *data, double *scales, + int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,PolyMap,FitPoly1DInit))( this, forward, table, data, scales, + status ); +} + + +void astFitPoly2DInit_( AstPolyMap *this, int forward, double **table, + AstMinPackData *data, double *scales, + int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,PolyMap,FitPoly2DInit))( this, forward, table, data, scales, + status ); +} + + + + diff --git a/ast/polymap.h b/ast/polymap.h new file mode 100644 index 0000000..bdfb319 --- /dev/null +++ b/ast/polymap.h @@ -0,0 +1,386 @@ +#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. */ +/* ----------------------- */ +#if defined(astCLASS) /* Protected */ + +/* Structure used to pass data to the Levenberg - Marquardt non-linear + minimization algorithm. */ +typedef struct AstMinPackData { + 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 */ +} AstMinPackData; + + +/* This structure contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +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 * ); + void (* PolyPowers)( AstPolyMap *, double **, int, const int *, double **, int, int, int * ); + void (* PolyCoeffs)( AstPolyMap *, int, int, double *, int *, int *); + void (* FitPoly1DInit)( AstPolyMap *, int, double **, AstMinPackData *, double *, int *); + void (* FitPoly2DInit)( AstPolyMap *, int, double **, AstMinPackData *, 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 * ); +void astPolyCoeffs_( AstPolyMap *, int, int, double *, int *, int *); + +# if defined(astCLASS) /* Protected */ + void astPolyPowers_( AstPolyMap *, double **, int, const int *, double **, int, int, int * ); + void astFitPoly1DInit_( AstPolyMap *, int, double **, AstMinPackData *, double *, int *); + void astFitPoly2DInit_( AstPolyMap *, int, double **, AstMinPackData *, double *, int *); + + 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)) + +#define astPolyCoeffs(this,forward,nel,coeffs,ncoeff) \ +astINVOKE(V,astPolyCoeffs_(astCheckPolyMap(this),forward,nel,coeffs,ncoeff,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ + +#define astPolyPowers(this,work,ncoord,mxpow,ptr,offset,fwd) \ + astINVOKE(V,astPolyPowers_(astCheckPolyMap(this),work,ncoord,mxpow,ptr,point,fwd,STATUS_PTR)) +#define astFitPoly1DInit(this,forward,table,data,scales) \ + astINVOKE(V,astFitPoly1DInit_(astCheckPolyMap(this),forward,table,data,scales,STATUS_PTR)) +#define astFitPoly2DInit(this,forward,table,data,scales) \ + astINVOKE(V,astFitPoly2DInit_(astCheckPolyMap(this),forward,table,data,scales,STATUS_PTR)) +#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/ast/prepare_all b/ast/prepare_all new file mode 100755 index 0000000..cda505d --- /dev/null +++ b/ast/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/ast/prepare_docs b/ast/prepare_docs new file mode 100755 index 0000000..8fc0358 --- /dev/null +++ b/ast/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/ast/prepare_hyperdocs b/ast/prepare_hyperdocs new file mode 100755 index 0000000..3d6e1e4 --- /dev/null +++ b/ast/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/ast/prepare_release b/ast/prepare_release new file mode 100755 index 0000000..84b8dff --- /dev/null +++ b/ast/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/ast/prism.c b/ast/prism.c new file mode 100644 index 0000000..f4e26c9 --- /dev/null +++ b/ast/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/ast/prism.h b/ast/prism.h new file mode 100644 index 0000000..ab14c77 --- /dev/null +++ b/ast/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/ast/proj.c b/ast/proj.c new file mode 100644 index 0000000..8b92c21 --- /dev/null +++ b/ast/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/ast/proj.h b/ast/proj.h new file mode 100644 index 0000000..61e4746 --- /dev/null +++ b/ast/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/ast/ratemap.c b/ast/ratemap.c new file mode 100644 index 0000000..1e343a6 --- /dev/null +++ b/ast/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/ast/ratemap.h b/ast/ratemap.h new file mode 100644 index 0000000..b26bbd6 --- /dev/null +++ b/ast/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/ast/region.c b/ast/region.c new file mode 100644 index 0000000..5148917 --- /dev/null +++ b/ast/region.c @@ -0,0 +1,13502 @@ +/* +*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. +* 26-OCT-2016 (DSB): +* - Override the astAxNorm method. +* - Use astAxNorm to fix a bug in astGetRegionBounds for cases where +* the Region cross a longitude=0 singularity. +* 11-NOV-2016 (DSB): +* When loading a Region, use the dimensionality of the base Frame +* of the FrameSet (rather than the current Frame as it used to be) +* to define the number of axes required in the PointSet. +* 1-DEC-2016 (DSB): +* Changed MapRegion to remove any unnecessary base frame axes in +* the returned Region. +*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 AxNorm( AstFrame *, int, int, int, double *, 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 void AxNorm( AstFrame *this_frame, int axis, int oper, int nval, + double *values, int *status ){ +/* +* Name: +* AxNorm + +* Purpose: +* Normalise an array of axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void AxNorm( AstFrame *this, int axis, int oper, int nval, +* double *values, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astAxNorm +* method inherited from the Frame class). + +* Description: +* This function modifies a supplied array of axis values so that +* they are normalised in the manner indicated by parameter "oper". +* +* No normalisation is possible for a simple Frame and so the supplied +* values are returned unchanged. However, this may not be the case for +* specialised sub-classes of Frame. For instance, a SkyFrame has a +* discontinuity at zero longitude and so a longitude value can be +* expressed in the range [-Pi,+PI] or the range [0,2*PI]. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The index of the axis to which the supplied values refer. The +* first axis has index 1. +* oper +* Indicates the type of normalisation to be applied. If zero is +* supplied, the normalisation will be the same as that performed by +* function astNorm. If 1 is supplied, the normalisation will be +* chosen automatically so that the resulting list has the smallest +* range. +* nval +* The number of points in the values array. +* values +* On entry, the axis values to be normalised. Modified on exit to +* hold the normalised values. +* 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. */ + (void) astValidateAxis( this, axis - 1, 1, "astAxNorm" ); + +/* Obtain a pointer to the Region's encapsulated Frame and invoke + the astAxNorm method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + astAxNorm( fr, axis, oper, nval, values ); + fr = astAnnul( fr ); +} + +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", AST__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->AxNorm = AxNorm; + 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 *tmap; + AstMapping *usemap; + AstMapping *map; + AstPointSet *ps1; + AstPointSet *pst; + AstPointSet *ps2; + AstRegion *usethis; + AstRegion *result; + double **ptr1; + double **ptr2; + int *axflags; + int *inax; + int *keep; + int *outax; + int i; + int icurr; + int j; + int nax1; + int nkept; + int nnew; + int nold; + int np; + int ntotal; + int ok; + +/* Initialise returned value. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Initialise local variables */ + axflags = NULL; + +/* 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 ) ); + } + + +/* Get the number of axes in the supplied Region. */ + nold = astGetNaxes( this ); + +/* Get the number of axes in the returned Region. */ + nnew = astGetNaxes( frame ); + +/* The forward transformation must not introduce any bad axis values. We + can only perform this test reliably if the supplied Region has no bad + axis values. */ + ps1 = this->points; + if( ps1 ) { + nax1 = astGetNcoord( ps1 ); + np = astGetNpoint( ps1 ); + ptr1 = astGetPoints( ps1 ); + if( ptr1 ) { + +/* Check the axis values defining the Region are good. */ + 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 ) { + +/* Transform the points defining the supplied Region into the current Frame + of the Region. */ + pst = astRegTransform( this, ps1, 1, NULL, NULL ); + +/* The use the supplied Mapping to transfom them into the new Frame. */ + ps2 = astTransform( map, pst, 1, NULL ); + +/* Test if any of these axis values are bad. */ + ptr2 = astGetPoints( ps2 ); + if( ptr2 ) { + for( i = 0; i < nnew && 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 ) ); + +/* If all axis values are good, use the inverse transformation of the + supplied Mapping to transform them back to the Frame of the supplied + Region. */ + } else { + pst = astAnnul( pst ); + pst = astTransform( map, ps2, 0, NULL ); + +/* Get a flag for each input of the supplied Mapping (i.e. each axis of + the supplied Region) which is non-zero if the inverse transformation + of the Mapping has produced any good axis values. */ + ptr1 = astGetPoints( pst ); + axflags = astCalloc( nold, sizeof(int) ); + if( astOK ) { + for( i = 0; i < nold; i++ ){ + for( j = 0; j < np; j++ ) { + if( ptr1[ i ][ j ] != AST__BAD ){ + axflags[ i ] = 1; + break; + } + } + } + } + } + } + ps2 = astAnnul( ps2 ); + pst = astAnnul( pst ); + } + } + } + +/* Assume we will be using the supplied Region (this) and Mapping (map). */ + usethis = astClone( this ); + usemap = astClone( map ); + +/* If the new Frame for the Region has fewer axes than the old Frame, see + if we can discard some base-frame axes. We only do this if the inverse + transformation would otherwise supply bad values for the unused axes. + Using bad axis values is not a good idea as some operations cannot be + performed if any bad values are supplied. Also having more axes than + needed is bad as it results in fewer mesh points per axis. */ + if( nnew < nold ) { + +/* First invert the Mapping since astMapSplit only allows selection of + inputs, and we want to select outputs. */ + astInvert( map ); + +/* Create an array holding the indices of the required inputs. */ + inax = astMalloc( nnew*sizeof(int) ); + if( astOK ) for( i = 0; i < nnew; i++ ) inax[i] = i; + +/* Attempt to split the mapping to extract a new mapping that omits any + unnecessary outputs (i.e. outputs that are indepenent of the selected + inputs). Check the Mapping was split successfully. */ + outax = astMapSplit( map, nnew, inax, &tmap ); + if( outax ) { + +/* Get the number of old axes that have been retained in the Mapping. */ + nkept = astGetNout( tmap ); + +/* Now we need to ensure that any unused axes for which the Mapping + creates non-bad values are retained (such values are significant + since they may determine whether the new Region is null or not). + We only need to do this if any of the outputs that were split off + above have been found to generate good values, as indicated by the + "axflags" array. Count the number of extra axes that need to be kept, + over and above those that are kept by the Mapping returned by + astMapSplit above. */ + ntotal = 0; + keep = NULL; + if( axflags ) { + keep = astMalloc( nold*sizeof(int) ); + if( astOK ) { + +/* Loop round each axis in the supplied Region. */ + for( i = 0; i < nold; i++ ) { + +/* Search the "outax" array to see if this axis was retained by astMapSplit. + If it was, append its index to the total list of axes to keep. */ + ok = 0; + for( j = 0; j < nkept; j++ ) { + if( outax[ j ] == i ) { + keep[ ntotal++ ] = i; + ok = 1; + break; + } + } + +/* If the axis was not retained by astMapSplit, but generates good axis + values, also append its index to the total list of axes to keep. */ + if( !ok && axflags[ i ] ) { + keep[ ntotal++ ] = i; + } + } + } + } + +/* If there are no extra axes to keep, then the Mapping returned by + astMapSplit above can be used as it is. */ + if( ntotal == nkept ) { + +/* The values in the "outax" array will hold the zero-based indices of + the original old axes that are retained by the new Mapping. We need to + create a copy of the supplied Region that includes only these axes. */ + usethis = astAnnul( usethis ); + usethis = astPickAxes( this, nkept, outax, NULL ); + +/* Use the temportary Mapping returned by astMapSplit in place of the + supplied Mapping (remember to invert to undo the inversion performed + above). */ + usemap = astAnnul( usemap ); + usemap = astClone( tmap ); + astInvert( usemap ); + +/* Free temporary resources. */ + tmap = astAnnul( tmap ); + outax = astFree( outax ); + +/* If we need to retain some extra axes because they generate good values + (even though they are independent of the new Frame axes)... */ + } else if( ntotal > nkept ){ + +/* We know the old Frame axes that we want to keep, so use astMapSplit + in the opposite direction - i.e. use it on the Mapping form old to + new. */ + astInvert( map ); + + tmap = astAnnul( tmap ); + outax = astFree( outax ); + + outax = astMapSplit( map, ntotal, keep, &tmap ); + if( outax ) { + + usethis = astAnnul( usethis ); + usethis = astPickAxes( this, ntotal, keep, NULL ); + + usemap = astAnnul( usemap ); + usemap = astClone( tmap ); + + tmap = astAnnul( tmap ); + outax = astFree( outax ); + + } + + astInvert( map ); + } + keep = astFree( keep ); + } + inax = astFree( inax ); + +/* Invert the Mapping again to bring it back to its original state. */ + astInvert( map ); + } + +/* Take a deep copy of the supplied Region. */ + result = astCopy( usethis ); + +/* 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, usemap, 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( result ); + +/* Free resources */ + usemap = astAnnul( usemap ); + usethis = astAnnul( usethis ); + map = astAnnul( map ); + frame = astAnnul( frame ); + axflags = astFree( axflags ); + +/* 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 **cptr; /* 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 ); + +/* There is a possibility that these points may span a singularity in the + coordinate system such as the RA=0 line in a SkyFrame. So ensure the + axis values are normalised into the shortest possible range. */ + frm = astGetFrame( this->frameset, AST__CURRENT ); + ncur = astGetNaxes( frm ); + + cptr = astGetPoints( cmesh ); + npos = astGetNpoint( cmesh ); + for( i = 0; i < ncur; i++ ) { + astAxNorm( frm, i+1, 1, npos, cptr[i] ); + } + +/* Get the axis bounds of this PointSet. */ + astBndPoints( cmesh, lbnd, ubnd ); + +/* There is again 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. */ + 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", AST__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 loader 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, or FrameSet base 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. Get the number + of axes spanning its base Frame ("Nin"). */ + } else { + new->frameset = astReadObject( channel, "frmset", NULL ); + if( new->frameset ) { + nax = astGetNin( 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/ast/region.h b/ast/region.h new file mode 100644 index 0000000..b6ffd4e --- /dev/null +++ b/ast/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/ast/selectfc b/ast/selectfc new file mode 100755 index 0000000..abf0dc7 --- /dev/null +++ b/ast/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/ast/selectormap.c b/ast/selectormap.c new file mode 100644 index 0000000..87a6f45 --- /dev/null +++ b/ast/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/ast/selectormap.h b/ast/selectormap.h new file mode 100644 index 0000000..36d83a5 --- /dev/null +++ b/ast/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/ast/series.pdf b/ast/series.pdf new file mode 100644 index 0000000..71d8624 Binary files /dev/null and b/ast/series.pdf differ diff --git a/ast/shiftmap.c b/ast/shiftmap.c new file mode 100644 index 0000000..eed6e1d --- /dev/null +++ b/ast/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/ast/shiftmap.h b/ast/shiftmap.h new file mode 100644 index 0000000..a9b1346 --- /dev/null +++ b/ast/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/ast/simpexamp.pdf b/ast/simpexamp.pdf new file mode 100644 index 0000000..b65f58e Binary files /dev/null and b/ast/simpexamp.pdf differ diff --git a/ast/skyaxis.c b/ast/skyaxis.c new file mode 100644 index 0000000..fd6d6f6 --- /dev/null +++ b/ast/skyaxis.c @@ -0,0 +1,5150 @@ +/* +*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.. +* 26-OCT-2016 (DSB): +* Override astAxisNormValues. +* 7-NOV-2016 (DSB): +* Ensure astAxisNormValues ignores bad axis values. +*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 AxisNormValues( AstAxis *, int, int, 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 void AxisNormValues( AstAxis *this_axis, int oper, int nval, + double *values, int *status ){ +/* +* Name: +* astAxisNormValues + +* Purpose: +* Normalise an array of axis coordinate values. + +* Type: +* Public virtual function. + +* Synopsis: +* #include "skyaxis.h" +* void astAxisNormValues( AstAxis *this, int oper, int nval, +* double *values ) + +* Class Membership: +* SkyAxis member function (over-rides the astAxisNormValues method +* inherited from the Axis class). + +* Description: +* This function modifies a supplied array of axis values so that +* they are normalised in the manner indicated by parameter "oper". +* +* For a SkyAxis, if "oper" is 0, longitude values are returned in +* the range [0,2*PI]. If "oper" is 1, longitude values are returned +* in either the range [0,2*PI] or [-PI,PI]. The choice is made so +* that the resulting list has the smallest range. Latitude values +* are always returned in the range [-PI,PI]. + +* Parameters: +* this +* Pointer to the Axis. +* oper +* Indicates the type of normalisation to be applied. If zero is +* supplied, the normalisation will be the same as that performed by +* function astAxisNorm. If 1 is supplied, the normalisation will be +* chosen automatically so that the resulting list has the smallest +* range. +* nval +* The number of points in the values array. +* values +* On entry, the axis values to be normalised. Modified on exit to +* hold the normalised values. + +* Notes: +* - Bad axis values, and axis values outside the range -1E6 to +1E6 +* radians (such as DBL_MAX and DBL_MIN) are returned unchanged. + +*/ + +/* Local macros */ +#define VAL_OK(v) ((v)!=AST__BAD&&fabs(v)<1.0E6) + +/* Local Variables: */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + double *pv; /* Pointer to next axis value */ + double hi; /* Max axis value after normalisation */ + double lo; /* Min axis value after normalisation */ + double mn1; /* Min value in range [-pi,0] */ + double mn2; /* Min value in range [0,pi] */ + double mn3; /* Min value in range [pi,2pi] */ + double mx1; /* Max value in range [-pi,0] */ + double mx2; /* Max value in range [0,pi] */ + double mx3; /* Max value in range [pi,2pi] */ + double range1; /* Range after normalising to [0,2*pi] */ + double range2; /* Range after normalising to [-pi,pi] */ + double twopi; /* Two PI */ + int centrezero; /* SkyAxis range centred on zero? */ + int i; /* Index of next axis value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_axis; + +/* Oper 0 - always normalise according to the value of the CentreZero + attribute (i.e. mimic AxisNorm). */ + if( oper == 0 ) { + +/* Determine if the SkyAxis range is centred on zero or PI. */ + centrezero = astGetAxisCentreZero( this ); + +/* Loop over axis values. */ + pv = values; + for( i = 0; i < nval; i++,pv++ ) { + +/* If the coordinate value is bad, or unusually large, then return it + unchanged. Otherwise, wrap the value into the appropriate range. */ + if( VAL_OK(*pv) ) *pv = centrezero ? palDrange( *pv ) : + palDranrm( *pv ); + } + +/* Oper 1 - choose a range that leaves most values unchanged. */ + } else if( oper == 1 ) { + +/* Normalise latitude axes into [-PI,+PI]. */ + if( astGetAxisIsLatitude( this ) ) { + pv = values; + for( i = 0; i < nval; i++,pv++ ) { + if( VAL_OK(*pv) ) *pv = palDrange( *pv ); + } + +/* Now deal with longitude axes. */ + } else { + +/* First ensure all values are in the range [-PI,2*PI] and find the max + and min value in each of the three ranges [-PI,0], [0,PI], [PI, 2PI]. */ + twopi = 2*AST__DPI; + mx1 = -DBL_MAX; + mn1 = DBL_MAX; + mx2 = -DBL_MAX; + mn2 = DBL_MAX; + mx3 = -DBL_MAX; + mn3 = DBL_MAX; + + pv = values; + for( i = 0; i < nval; i++,pv++ ) { + if( VAL_OK(*pv) ) { + while( *pv > twopi ) *pv -= twopi; + while( *pv < -AST__DPIBY2 ) *pv += twopi; + + if( *pv > AST__DPI ) { + mx3 = astMAX( mx3, *pv ); + mn3 = astMIN( mn3, *pv ); + } else if( *pv > 0 ) { + mx2 = astMAX( mx2, *pv ); + mn2 = astMIN( mn2, *pv ); + } else { + mx1 = astMAX( mx1, *pv ); + mn1 = astMIN( mn1, *pv ); + } + } + } + +/* What would the range be if we normalised into [0,2.PI] ? */ + if( mx1 != -DBL_MAX ) { + hi = astMAX( mx2, astMAX( mx1 + twopi, mx3) ); + lo = astMIN( mn2, astMIN( mn1 + twopi, mn3) ); + } else { + hi = astMAX( mx2, mx3 ); + lo = astMIN( mn2, mn3 ); + } + range1 = hi - lo; + +/* What would the range be if we normalised into [-PI,PI] ? */ + if( mn3 != -DBL_MAX ) { + hi = astMAX( mx2, astMAX( mx3 - twopi, mx1) ); + lo = astMIN( mn2, astMIN( mn3 - twopi, mn1) ); + } else { + hi = astMAX( mx2, mx1 ); + lo = astMIN( mn2, mn1 ); + } + range2 = hi - lo; + +/* If [-PI,PI] produces the smaller range, normalise into [-PI,PI]. */ + if( range2 < range1 ) { + pv = values; + for( i = 0; i < nval; i++,pv++ ) { + if( VAL_OK(*pv) ) *pv = palDrange( *pv ); + } + +/* Otherwise, normalise all into the range [0,2PI]. */ + } else { + pv = values; + for( i = 0; i < nval; i++,pv++ ) { + if( VAL_OK(*pv) ) *pv = palDranrm( *pv ); + } + } + } + +/* Report an error if the supplied operation is invalid. */ + } else if( astOK ) { + astError( AST__INTER, "astAxisNormValues: Invalid oper value %d " + "supplied (internal AST programming error).", status, oper ); + } + +#undef VAL_OK +} + +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->AxisNormValues = AxisNormValues; + 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/ast/skyaxis.h b/ast/skyaxis.h new file mode 100644 index 0000000..5c2f7ac --- /dev/null +++ b/ast/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/ast/skyframe.c b/ast/skyframe.c new file mode 100644 index 0000000..1c20839 --- /dev/null +++ b/ast/skyframe.c @@ -0,0 +1,12592 @@ +/* +*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. +* 3-FEB-2017 (GSB): +* Override astSetDtai and astClearDtai. +* 6-APR-2017 (GSB): +* Added dtai to AstSkyLastTable. +* 10-APR-2017 (GSB): +* Added macro to test floating point equality and used it for Dtai. +*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) + +/* Check for floating point equality (within the given tolerance), taking + bad values into account. */ +#define EQUAL(aa,bb,tol) (((aa)==AST__BAD)?(((bb)==AST__BAD)?1:0):(((bb)==AST__BAD)?0:(fabs((aa)-(bb))<=(tol)))) + +/* +* +* 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_cleardtai)( AstFrame *, 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_setdtai)( AstFrame *, double, 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, 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, double, int * ); +static double GetEpoch( AstFrame *, int * ); +static double GetEquinox( AstSkyFrame *, int * ); +static void SetCachedLAST( AstSkyFrame *, double, 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 ClearDtai( AstFrame *, 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 SetDtai( AstFrame *, double, 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, double dtai, + 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, double dtai, +* 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. +* dtai +* The TAI-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, dtai, + 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 ); + +/* Store the DTAI value. */ + if ( dtai == AST__BAD ) { + astClearDtai( tdbframe ); + astClearDtai( lastframe ); + } + else { + astSetDtai( tdbframe, dtai ); + astSetDtai( lastframe, dtai ); + } + +/* 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, dtai, + 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 ClearDtai( AstFrame *this, int *status ) { +/* +* Name: +* ClearDtai + +* Purpose: +* Clear the value of the Dtai attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void ClearDtai( AstFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astClearDtai method +* inherited from the Frame class). + +* Description: +* This function clears the Dtai 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 = astGetDtai( this ); + +/* Invoke the parent method to clear the Frame Dtai */ + (*parent_cleardtai)( this, status ); + +/* If the DTAI value has changed significantly, indicate that the LAST value + will need to be re-calculated when it is next needed. */ + if( ! EQUAL( orig, astGetDtai( this ), 1.0E-6 ) ) { + ( (AstSkyFrame *) this )->last = AST__BAD; + ( (AstSkyFrame *) this )->eplast = AST__BAD; + ( (AstSkyFrame *) this )->klast = AST__BAD; + } +} + +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 ), + AST__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", AST__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", AST__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", AST__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, + double dtai, 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, +* double dtai, 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. +* dtai +* The TAI-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 and/or DTAI value. */ + for( itable = 0; itable < nlast_tables; itable++ ) { + table = last_tables[ itable ]; + +/* See if the table refers to the given position, dut1 and dtai 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 && + EQUAL( table->dtai, dtai, 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 ), astGetDtai( 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_setdtai = frame->SetDtai; + frame->SetDtai = SetDtai; + parent_setdut1 = frame->SetDut1; + frame->SetDut1 = SetDut1; + + parent_cleardtai = frame->ClearDtai; + frame->ClearDtai = ClearDtai; + 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, 0, NULL ); + + #define TRANSFORM_1(cvt,arg0) \ + args[ 0 ] = arg0; \ + astSlaAdd( slamap, cvt, 1, args ); + + #define TRANSFORM_2(cvt,arg0,arg1) \ + args[ 0 ] = arg0; \ + args[ 1 ] = arg1; \ + astSlaAdd( slamap, cvt, 2, args ); + + #define TRANSFORM_3(cvt,arg0,arg1,arg2) \ + args[ 0 ] = arg0; \ + args[ 1 ] = arg1; \ + args[ 2 ] = arg2; \ + astSlaAdd( slamap, cvt, 3, args ); + + #define TRANSFORM_4(cvt,arg0,arg1,arg2,arg3) \ + args[ 0 ] = arg0; \ + args[ 1 ] = arg1; \ + args[ 2 ] = arg2; \ + args[ 3 ] = arg3; \ + astSlaAdd( slamap, cvt, 4, 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, double dtai, 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, double dtai, 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. +* dtai +* The TAI-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 and/or DTAI value. */ + for( itable = 0; itable < nlast_tables; itable++ ) { + table = last_tables[ itable ]; + +/* See if the table refers to the given position, dut1 and dtai 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 && + EQUAL( table->dtai, dtai, 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->dtai = dtai; + 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 SetDtai( AstFrame *this_frame, double val, int *status ) { +/* +* Name: +* SetDtai + +* Purpose: +* Set the value of the Dtai attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetDtai( AstFrame *this, double val, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astSetDtai method +* inherited from the Frame class). + +* Description: +* This function clears the Dtai value and updates the LAST value +* stored in the SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* val +* New Dtai 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 Dtai value. */ + orig = astGetDtai( this ); + +/* Invoke the parent method to set the Frame Dtai value. */ + (*parent_setdtai)( this_frame, val, status ); + +/* If the DTAI value has changed significantly, indicate that the LAST value + will need to be re-calculated when it is next needed. */ + if( ! EQUAL( orig, val, 1.0E-6 ) ) { + this->last = AST__BAD; + this->eplast = AST__BAD; + this->klast = AST__BAD; + } +} + +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 ), astGetDtai( 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( "Dtai", a, len ) ) { + set1 = astTestDtai( target ); + set2 = astTestDtai( result ); + desc = "TAI-UTC correction"; + + } 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 to 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, AST__DBL_DIG, dval); + } else { + astSet( new, "Epoch=J%.*g", status, AST__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/ast/skyframe.h b/ast/skyframe.h new file mode 100644 index 0000000..1f63451 --- /dev/null +++ b/ast/skyframe.h @@ -0,0 +1,508 @@ +#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. +* 6-APR-2017 (GSB): +* Added dtai to AstSkyLastTable. +*- +*/ + +/* 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, DUT1 and DTAI + 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 */ + double dtai; /* Dtai 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/ast/slamap.c b/ast/slamap.c new file mode 100644 index 0000000..345bbf0 --- /dev/null +++ b/ast/slamap.c @@ -0,0 +1,5027 @@ +/* +*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. +* 30-NOV-2016 (DSB): +* Added a "narg" argumeent to astSlaAdd. + +*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, 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 *, int, 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, int narg, + 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, int narg, +* 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. +* narg +* The number of argument values supplied in "args". +* 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 ); + } + +/* If the number of supplied arguments is incorrect, then report an error. */ + if ( astOK && nargs != narg ) { + astError( AST__TIMIN, "AddSlaCvt(%s): Invalid no. of arguments for SLALIB " + "coordinate conversion type %d - %d supplied, %d required.", + status, astGetClass( this ), (int) cvttype, narg, nargs ); + } + +/* 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 */ + 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 ], + narg[ 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, int narg, + 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, int narg, +c const double args[] ) +f CALL AST_SLAADD( THIS, CVT, NARG, 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 narg +f NARG = INTEGER (Given) +* The number of argument values supplied in the +c "args" array. +f ARGS array. +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, narg, 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, int narg, const double args[], int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,SlaMap,SlaAdd))( this, cvt, narg, args, status ); +} + +int astSlaIsEmpty_( AstSlaMap *this, int *status ) { + if ( !astOK ) return 1; + return (**astMEMBER(this,SlaMap,SlaIsEmpty))( this, status ); +} + diff --git a/ast/slamap.h b/ast/slamap.h new file mode 100644 index 0000000..e7280b8 --- /dev/null +++ b/ast/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 *, int, 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 *, int, 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,narg,args) \ +astINVOKE(V,astSlaAdd_(astCheckSlaMap(this),cvt,narg,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/ast/specfluxframe.c b/ast/specfluxframe.c new file mode 100644 index 0000000..ea00d49 --- /dev/null +++ b/ast/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/ast/specfluxframe.h b/ast/specfluxframe.h new file mode 100644 index 0000000..ff8a5e1 --- /dev/null +++ b/ast/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/ast/specframe.c b/ast/specframe.c new file mode 100644 index 0000000..482f1d8 --- /dev/null +++ b/ast/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", 0, NULL ); + + } else if( sys == AST__VOPTICAL ) { + astSpecAdd( specmap, "VOTOVL", 0, NULL ); + + } else if( sys == AST__REDSHIFT ) { + astSpecAdd( specmap, "ZOTOVL", 0, NULL ); + + } else if( sys == AST__BETA ) { + astSpecAdd( specmap, "BTTOVL", 0, NULL ); + } + +/* Add a conversion from velocity to frequency since SorConvert converts + frequencies. */ + rf = astGetRestFreq( this ); + astSpecAdd( specmap, "VLTOFR", 1, &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", 1, &rf ); + +/* Add a conversion from relativistic velocity to the required spectral + system, if needed. */ + if( newsys == AST__VRADIO ) { + astSpecAdd( specmap, "VLTOVR", 0, NULL ); + + } else if( newsys == AST__VOPTICAL ) { + astSpecAdd( specmap, "VLTOVO",0, NULL ); + + } else if( newsys == AST__REDSHIFT ) { + astSpecAdd( specmap, "VLTOZO",0, NULL ); + + } else if( newsys == AST__BETA ) { + astSpecAdd( specmap, "VLTOBT",0, 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", AST__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", AST__DBL_DIG, dval ); + result = getattrib_buff; + + } + +/* SpecOrigin. */ +/* ----------- */ + } else if ( !strcmp( attrib, "specorigin" ) ) { + dval = GetSpecOriginCur( this, status ); + if( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__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, 0, NULL ); + +#define TRANSFORM_1(cvt,arg0) \ + args[ 0 ] = arg0; \ + astSpecAdd( specmap, cvt, 1, 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, 2, args ); + +#define TRANSFORM_3(cvt,arg0,arg1,arg2) \ + args[ 0 ] = arg0; \ + args[ 1 ] = arg1; \ + args[ 2 ] = arg2; \ + astSpecAdd( specmap, cvt, 3, 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, 6, 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/ast/specframe.h b/ast/specframe.h new file mode 100644 index 0000000..34d8eac --- /dev/null +++ b/ast/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/ast/specmap.c b/ast/specmap.c new file mode 100644 index 0000000..48ada4e --- /dev/null +++ b/ast/specmap.c @@ -0,0 +1,4696 @@ +/* +*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. +* 1-DEC-2016 (DSB): +* Added a "narg" argumeent to astSpecAdd. + +*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, 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 *, int, 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, int narg, + 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, int narg, +* 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. +* narg +* The number of argument values supplied in "args". +* 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 ); + } + +/* If the number of supplied arguments is incorrect, then report an error. */ + if ( astOK && nargs != narg ) { + astError( AST__TIMIN, "AddSpecCvt(%s): Invalid no. of arguments for spectral " + "coordinate conversion type %d - %d supplied, %d required.", + status, astGetClass( this ), (int) cvttype, narg, nargs ); + } + +/* 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 *narg; /* Pointer to argument count */ + int *szarg; /* Pointer to argument array size */ + 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 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 ); + narg = 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 + nstep, 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 ]; + 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 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 ], + narg[ 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 ); + narg = astFree( narg ); + } + +/* 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, int narg, + 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, int narg, +c const double args[] ) +f CALL AST_SPECADD( THIS, CVT, NARG, 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 narg +f NARG = INTEGER (Given) +* The number of argument values supplied in the +c "args" array. +f ARGS array. +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, narg, 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, int narg, + const double args[], int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,SpecMap,SpecAdd))( this, cvt, narg, args, status ); +} + + + + diff --git a/ast/specmap.h b/ast/specmap.h new file mode 100644 index 0000000..d266ba1 --- /dev/null +++ b/ast/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 *, int, 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 *, int, 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,narg,args) \ +astINVOKE(V,astSpecAdd_(astCheckSpecMap(this),cvt,narg,args,STATUS_PTR)) + +#endif + + + + + diff --git a/ast/sphmap.c b/ast/sphmap.c new file mode 100644 index 0000000..bc4018c --- /dev/null +++ b/ast/sphmap.c @@ -0,0 +1,2061 @@ +/* +*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. +* 28-APR-2016 (DSB): +* Avoid modifying the attributes of the existing SphMap in +* MapMerge, since it may be in use in other contexts. Modify a +* copy instead. +*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", AST__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 */ + AstSphMap *sm; /* The new SphMap */ + 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 + get a copy of the first SphMap and change its PolarLong value. We use + a copy since the original may be in use in other contexts that could be + badly affected by the change. */ + sm = astCopy( ( *map_list )[ where ] ); + astSetPolarLong( sm, polarlong ); + +/* Annul the SphMap and the MatrixMap or ZoomMap. */ + (void) astAnnul( ( *map_list )[ where ] ); + (void) astAnnul( ( *map_list )[ where + 1 ] ); + +/* Store the modified SphMap in the slot left vacant by the annulled + MatrixMap or ZoomMap. */ + ( *map_list )[ where + 1 ] = (AstMapping *) sm; + ( *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/ast/sphmap.h b/ast/sphmap.h new file mode 100644 index 0000000..905703b --- /dev/null +++ b/ast/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/ast/stc.c b/ast/stc.c new file mode 100644 index 0000000..08e4392 --- /dev/null +++ b/ast/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/ast/stc.h b/ast/stc.h new file mode 100644 index 0000000..1fe0b1b --- /dev/null +++ b/ast/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/ast/stccatalogentrylocation.c b/ast/stccatalogentrylocation.c new file mode 100644 index 0000000..a1ea16f --- /dev/null +++ b/ast/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/ast/stccatalogentrylocation.h b/ast/stccatalogentrylocation.h new file mode 100644 index 0000000..f999e6a --- /dev/null +++ b/ast/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/ast/stcobsdatalocation.c b/ast/stcobsdatalocation.c new file mode 100644 index 0000000..777d29e --- /dev/null +++ b/ast/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/ast/stcobsdatalocation.h b/ast/stcobsdatalocation.h new file mode 100644 index 0000000..ad3b793 --- /dev/null +++ b/ast/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/ast/stcresourceprofile.c b/ast/stcresourceprofile.c new file mode 100644 index 0000000..4ae6cc0 --- /dev/null +++ b/ast/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/ast/stcresourceprofile.h b/ast/stcresourceprofile.h new file mode 100644 index 0000000..8157dbb --- /dev/null +++ b/ast/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/ast/stcs-ex1.txt b/ast/stcs-ex1.txt new file mode 100644 index 0000000..f46fb6a --- /dev/null +++ b/ast/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/ast/stcschan-demo1.c b/ast/stcschan-demo1.c new file mode 100644 index 0000000..171e78c --- /dev/null +++ b/ast/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/ast/stcschan-demo2.c b/ast/stcschan-demo2.c new file mode 100644 index 0000000..1967e0b --- /dev/null +++ b/ast/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/ast/stcschan-demo3.c b/ast/stcschan-demo3.c new file mode 100644 index 0000000..b7f4e6f --- /dev/null +++ b/ast/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/ast/stcschan-demo4.c b/ast/stcschan-demo4.c new file mode 100644 index 0000000..e45035f --- /dev/null +++ b/ast/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/ast/stcschan-demo5.c b/ast/stcschan-demo5.c new file mode 100644 index 0000000..787c9e6 --- /dev/null +++ b/ast/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/ast/stcschan.c b/ast/stcschan.c new file mode 100644 index 0000000..4b43524 --- /dev/null +++ b/ast/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/ast/stcschan.h b/ast/stcschan.h new file mode 100644 index 0000000..11de827 --- /dev/null +++ b/ast/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/ast/stcsearchlocation.c b/ast/stcsearchlocation.c new file mode 100644 index 0000000..aa6bd4e --- /dev/null +++ b/ast/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/ast/stcsearchlocation.h b/ast/stcsearchlocation.h new file mode 100644 index 0000000..c359711 --- /dev/null +++ b/ast/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/ast/sun210_figures/cmpframe.pdf b/ast/sun210_figures/cmpframe.pdf new file mode 100644 index 0000000..fd82293 Binary files /dev/null and b/ast/sun210_figures/cmpframe.pdf differ diff --git a/ast/sun210_figures/complex.pdf b/ast/sun210_figures/complex.pdf new file mode 100644 index 0000000..b743453 Binary files /dev/null and b/ast/sun210_figures/complex.pdf differ diff --git a/ast/sun210_figures/frames.pdf b/ast/sun210_figures/frames.pdf new file mode 100644 index 0000000..66d2b2f Binary files /dev/null and b/ast/sun210_figures/frames.pdf differ diff --git a/ast/sun210_figures/frameset.pdf b/ast/sun210_figures/frameset.pdf new file mode 100644 index 0000000..d5d9a1a Binary files /dev/null and b/ast/sun210_figures/frameset.pdf differ diff --git a/ast/sun210_figures/fronta.pdf b/ast/sun210_figures/fronta.pdf new file mode 100644 index 0000000..3433a3d Binary files /dev/null and b/ast/sun210_figures/fronta.pdf differ diff --git a/ast/sun210_figures/fronta_bw.pdf b/ast/sun210_figures/fronta_bw.pdf new file mode 100644 index 0000000..2a5ff7e Binary files /dev/null and b/ast/sun210_figures/fronta_bw.pdf differ diff --git a/ast/sun210_figures/frontb.pdf b/ast/sun210_figures/frontb.pdf new file mode 100644 index 0000000..16aeef3 Binary files /dev/null and b/ast/sun210_figures/frontb.pdf differ diff --git a/ast/sun210_figures/frontb_bw.pdf b/ast/sun210_figures/frontb_bw.pdf new file mode 100644 index 0000000..78fa1db Binary files /dev/null and b/ast/sun210_figures/frontb_bw.pdf differ diff --git a/ast/sun210_figures/frontc.pdf b/ast/sun210_figures/frontc.pdf new file mode 100644 index 0000000..69ab837 Binary files /dev/null and b/ast/sun210_figures/frontc.pdf differ diff --git a/ast/sun210_figures/frontc_bw.pdf b/ast/sun210_figures/frontc_bw.pdf new file mode 100644 index 0000000..17fce95 Binary files /dev/null and b/ast/sun210_figures/frontc_bw.pdf differ diff --git a/ast/sun210_figures/fsalign.pdf b/ast/sun210_figures/fsalign.pdf new file mode 100644 index 0000000..7d28fb7 Binary files /dev/null and b/ast/sun210_figures/fsalign.pdf differ diff --git a/ast/sun210_figures/fsconvert.pdf b/ast/sun210_figures/fsconvert.pdf new file mode 100644 index 0000000..5b07041 Binary files /dev/null and b/ast/sun210_figures/fsconvert.pdf differ diff --git a/ast/sun210_figures/fsexample.pdf b/ast/sun210_figures/fsexample.pdf new file mode 100644 index 0000000..1ba073a Binary files /dev/null and b/ast/sun210_figures/fsexample.pdf differ diff --git a/ast/sun210_figures/fsmerge.pdf b/ast/sun210_figures/fsmerge.pdf new file mode 100644 index 0000000..2098b15 Binary files /dev/null and b/ast/sun210_figures/fsmerge.pdf differ diff --git a/ast/sun210_figures/fsremap.pdf b/ast/sun210_figures/fsremap.pdf new file mode 100644 index 0000000..2967186 Binary files /dev/null and b/ast/sun210_figures/fsremap.pdf differ diff --git a/ast/sun210_figures/gridplot.pdf b/ast/sun210_figures/gridplot.pdf new file mode 100644 index 0000000..65953d1 Binary files /dev/null and b/ast/sun210_figures/gridplot.pdf differ diff --git a/ast/sun210_figures/gridplot_bw.pdf b/ast/sun210_figures/gridplot_bw.pdf new file mode 100644 index 0000000..67353bc Binary files /dev/null and b/ast/sun210_figures/gridplot_bw.pdf differ diff --git a/ast/sun210_figures/mapping.pdf b/ast/sun210_figures/mapping.pdf new file mode 100644 index 0000000..78c12b7 Binary files /dev/null and b/ast/sun210_figures/mapping.pdf differ diff --git a/ast/sun210_figures/overgrid.pdf b/ast/sun210_figures/overgrid.pdf new file mode 100644 index 0000000..82c65cf Binary files /dev/null and b/ast/sun210_figures/overgrid.pdf differ diff --git a/ast/sun210_figures/overgrid_bw.pdf b/ast/sun210_figures/overgrid_bw.pdf new file mode 100644 index 0000000..10d65ab Binary files /dev/null and b/ast/sun210_figures/overgrid_bw.pdf differ diff --git a/ast/sun210_figures/parallel.pdf b/ast/sun210_figures/parallel.pdf new file mode 100644 index 0000000..42ff646 Binary files /dev/null and b/ast/sun210_figures/parallel.pdf differ diff --git a/ast/sun210_figures/series.pdf b/ast/sun210_figures/series.pdf new file mode 100644 index 0000000..df2a23b Binary files /dev/null and b/ast/sun210_figures/series.pdf differ diff --git a/ast/sun210_figures/simpexamp.pdf b/ast/sun210_figures/simpexamp.pdf new file mode 100644 index 0000000..d63a5af Binary files /dev/null and b/ast/sun210_figures/simpexamp.pdf differ diff --git a/ast/sun211_figures/cmpframe.pdf b/ast/sun211_figures/cmpframe.pdf new file mode 100644 index 0000000..63ca4e1 Binary files /dev/null and b/ast/sun211_figures/cmpframe.pdf differ diff --git a/ast/sun211_figures/complex.pdf b/ast/sun211_figures/complex.pdf new file mode 100644 index 0000000..8e5bbb9 Binary files /dev/null and b/ast/sun211_figures/complex.pdf differ diff --git a/ast/sun211_figures/frames.pdf b/ast/sun211_figures/frames.pdf new file mode 100644 index 0000000..dbf5070 Binary files /dev/null and b/ast/sun211_figures/frames.pdf differ diff --git a/ast/sun211_figures/frameset.pdf b/ast/sun211_figures/frameset.pdf new file mode 100644 index 0000000..697fe38 Binary files /dev/null and b/ast/sun211_figures/frameset.pdf differ diff --git a/ast/sun211_figures/fronta.pdf b/ast/sun211_figures/fronta.pdf new file mode 100644 index 0000000..3433a3d Binary files /dev/null and b/ast/sun211_figures/fronta.pdf differ diff --git a/ast/sun211_figures/fronta_bw.pdf b/ast/sun211_figures/fronta_bw.pdf new file mode 100644 index 0000000..54cf906 Binary files /dev/null and b/ast/sun211_figures/fronta_bw.pdf differ diff --git a/ast/sun211_figures/frontb.pdf b/ast/sun211_figures/frontb.pdf new file mode 100644 index 0000000..16aeef3 Binary files /dev/null and b/ast/sun211_figures/frontb.pdf differ diff --git a/ast/sun211_figures/frontb_bw.pdf b/ast/sun211_figures/frontb_bw.pdf new file mode 100644 index 0000000..03df1c2 Binary files /dev/null and b/ast/sun211_figures/frontb_bw.pdf differ diff --git a/ast/sun211_figures/frontc.pdf b/ast/sun211_figures/frontc.pdf new file mode 100644 index 0000000..69ab837 Binary files /dev/null and b/ast/sun211_figures/frontc.pdf differ diff --git a/ast/sun211_figures/frontc_bw.pdf b/ast/sun211_figures/frontc_bw.pdf new file mode 100644 index 0000000..29fa5e1 Binary files /dev/null and b/ast/sun211_figures/frontc_bw.pdf differ diff --git a/ast/sun211_figures/fsalign.pdf b/ast/sun211_figures/fsalign.pdf new file mode 100644 index 0000000..e0b8c15 Binary files /dev/null and b/ast/sun211_figures/fsalign.pdf differ diff --git a/ast/sun211_figures/fsconvert.pdf b/ast/sun211_figures/fsconvert.pdf new file mode 100644 index 0000000..3950b3b Binary files /dev/null and b/ast/sun211_figures/fsconvert.pdf differ diff --git a/ast/sun211_figures/fsexample.pdf b/ast/sun211_figures/fsexample.pdf new file mode 100644 index 0000000..097114d Binary files /dev/null and b/ast/sun211_figures/fsexample.pdf differ diff --git a/ast/sun211_figures/fsmerge.pdf b/ast/sun211_figures/fsmerge.pdf new file mode 100644 index 0000000..5d6c198 Binary files /dev/null and b/ast/sun211_figures/fsmerge.pdf differ diff --git a/ast/sun211_figures/fsremap.pdf b/ast/sun211_figures/fsremap.pdf new file mode 100644 index 0000000..31772d3 Binary files /dev/null and b/ast/sun211_figures/fsremap.pdf differ diff --git a/ast/sun211_figures/gridplot.pdf b/ast/sun211_figures/gridplot.pdf new file mode 100644 index 0000000..c06b370 Binary files /dev/null and b/ast/sun211_figures/gridplot.pdf differ diff --git a/ast/sun211_figures/gridplot_bw.pdf b/ast/sun211_figures/gridplot_bw.pdf new file mode 100644 index 0000000..67353bc Binary files /dev/null and b/ast/sun211_figures/gridplot_bw.pdf differ diff --git a/ast/sun211_figures/mapping.pdf b/ast/sun211_figures/mapping.pdf new file mode 100644 index 0000000..8228188 Binary files /dev/null and b/ast/sun211_figures/mapping.pdf differ diff --git a/ast/sun211_figures/overgrid.pdf b/ast/sun211_figures/overgrid.pdf new file mode 100644 index 0000000..f78a965 Binary files /dev/null and b/ast/sun211_figures/overgrid.pdf differ diff --git a/ast/sun211_figures/overgrid_bw.pdf b/ast/sun211_figures/overgrid_bw.pdf new file mode 100644 index 0000000..0d972d4 Binary files /dev/null and b/ast/sun211_figures/overgrid_bw.pdf differ diff --git a/ast/sun211_figures/parallel.pdf b/ast/sun211_figures/parallel.pdf new file mode 100644 index 0000000..18f9911 Binary files /dev/null and b/ast/sun211_figures/parallel.pdf differ diff --git a/ast/sun211_figures/series.pdf b/ast/sun211_figures/series.pdf new file mode 100644 index 0000000..11656da Binary files /dev/null and b/ast/sun211_figures/series.pdf differ diff --git a/ast/sun211_figures/simpexamp.pdf b/ast/sun211_figures/simpexamp.pdf new file mode 100644 index 0000000..a685837 Binary files /dev/null and b/ast/sun211_figures/simpexamp.pdf differ diff --git a/ast/sun_master.tex b/ast/sun_master.tex new file mode 100644 index 0000000..cfa6c43 --- /dev/null +++ b/ast/sun_master.tex @@ -0,0 +1,21879 @@ +\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.28} +c- +f+ +\stardocnumber {210.28} +f- +\stardocauthors {R.F. Warren-Smith \& D.S. Berry} +\stardocdate {17th October 2017} +\stardoctitle {AST\linebreak% + A Library for Handling\linebreak% + World Coordinate Systems\linebreak% + in Astronomy} +\stardoccopyright {Copyright (C) 2017 East Asian Observatory} +\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 and +ChebyMap). + +\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 + ChebyMap + 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 + ChebyMap - N-dimensional Chebyshev 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 + UnitNormMap - Converts a vector to a unit vector plus length + 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. + +\item 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- + +\end{enumerate} + +\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 +occurred in the AST library between versions V8.1.0 and V8.2.0: + +\begin{enumerate} + +\item A new class of Mapping called UnitNormMap has been added that converts +a vector to a unit vector relative to a specified centre, plus length. A +UnitNormMap has N inputs and N+1 outputs.The lower N output coordinates +represent a unit vector parallel to the supplied input vector, and the +(N+1)'th output coordinate is the length of the input vector. + +\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} + + +\subsection{Changes Introduced in V8.3} +The following describes the most significant changes which +occurred in the AST library between versions V8.2.0 and V8.3.0: + +\begin{enumerate} + +c+ +\item A new method called astAxNorm +c- +f+ +\item A new method called AST\_AXNORM +f- +has been added to the Frame class that normalises an array of axis +values. When used with SkyFrames, it allows longitude values to be +normalised into the shortest range. + +f+ +\item A bug has been fixed in the Fortran include file AST\_PAR that caused constants +related to $\pi$ to be defined as single rather than double precision. +f- + +\item A bug has been fixed in the astGetRegionBounds method that could +cause the wrong bounds to be returned for regions spanning a longitude = +zero singularity. + +\end{enumerate} + +\subsection{Changes Introduced in V8.4} +The following describes the most significant changes which +occurred in the AST library between versions V8.3.0 and V8.4.0: + +\begin{enumerate} + +\item The PAL library files included in the AST distribution have been updated +to PAL version 0.9.7. + +\item Multiple identical NormMaps in series will now be simplified to a +single NormMap. + +\item A NormMap that encapsulates a basic Frame will now be simplified to a +UnitMap. + +f+ +\item The AST\_TIMEADD +f- +c+ +\item The astTimeAdd +c- +method of the TimeMap class now include an extra argument that gives the +number of values supplied in the arguments array. Note, any existing code +that uses this method will need to be changed. + +f+ +\item The AST\_SLAADD +f- +c+ +\item The astSlaAdd +c- +method of the SlaMap class now include an extra argument that gives the +number of values supplied in the arguments array. Note, any existing code +that uses this method will need to be changed. + +f+ +\item The AST\_SPECADD +f- +c+ +\item The astSpecAdd +c- +method of the SpecMap class now include an extra argument that gives the +number of values supplied in the arguments array. Note, any existing code +that uses this method will need to be changed. + +\item Multiple identical NormMaps in series will now be simplified to a +single NormMap. + +\item A NormMap that encapsulates a basic Frame will now be simplified to a +UnitMap. + +\item If the +c+ +astMapRegion +c- +f+ +AST\_MAPREGION +f- +method is used to map a Region into a new Frame that has fewer axes than +the original Region, and if the inverse transformation of the supplied +Mapping does not specify a value for the missing axes, then those axes +are removed entirely from the Region. Previously they were retained, but +automatically supplied with bad values. This affects the number of mesh +points per axes for such Regions, and so affects the accuracy of overlap +determination. + +\end{enumerate} + +\subsection{Changes Introduced in V8.5} +The following describes the most significant changes which +occurred in the AST library between versions V8.4.0 and V8.5.1: + +\begin{enumerate} + +\item - A new class of Mapping called ChebyMap has been added. This is a +Mapping that implements Chebyshev polynomial transformations. + +\item A bug has been fixed in the PolyMap class that caused incorrect values +to be returned for the TranForward and TranInverse attributes if the PolyMap +has been inverted. + +\item The KeyMap class has a new method called +c+ +astMapGetC +c- +f+ +AST\_MAPGETC +f- +which returns a named entry as a single string. If the entry is a vector +the returned string is a comma-separated list of its elements, enclosed +in parentheses. + +\item If the +c+ +function that delivers error messages to the user (astPutErr) +c- +f+ +routine that delivers error messages to the user (AST\_PUTERR) +f- +is re-implemented, the new version can now be registered at run-time using +the new +c+ +astSetPutErr function. +c- +f+ +AST\_SETPUTERR routine. +f- +Previously, the new version needed to be linked into the application at +build time. + + +\item The Frame class now has a new attribute caled DTAI, which can be used +to specify the number of leap seconds at the moment represented by the +Frame's Epoch attribute. By default, the internal look-up table of leap +seconds contained within AST is used. The DTAI attribute allows old +versions of AST, which may not include the most recent leap seconds, to +be used with new data. + +\item The TimeMap class has been changed so that some conversions now require +a ``Dtai'' value (\emph{i.e.} the number of leap seconds) to be supplied by the +caller. If AST\_\_BAD is supplied for ``Dtai'', the internal look-up table of +leap seconds contained withn AST will be used. The conversions affected +are those between TAI and UTC, and those between TT and TDB. + +\end{enumerate} + +\subsection{\xlabel{changes}\xlabel{list_of_most_recent_changes}Changes +Introduced in V8.6} +The following describes the most significant changes which have +occurred in the AST library between versions V8.5.1 and V8.6.2 (the +current version): + +\begin{enumerate} + +\item The behaviour of the astLinearApprox method of the Mapping class has +been changed in cases where the Mapping being approximated generates bad +(AST\_\_BAD) values for one or more of its outputs. Previously, any such +Mapping would be deemed non-linear and no fit would be returned. Now, a +fit is returned, provided the other outputs of the Mapping are linear, +but the fit contains AST\_\_BAD values for the coefficients describing the +bad Mapping output. + +\item The astWrite method of the FitsChan class can now create FITS-WCS headers +that include keyords describing focal plane distortion using the +conventions of the Spitzer SIP scheme. This is however only possible if +the SipOK attribute of the FitsChan is set to a non-zero value (which is +the default), and the FrameSet being written out contains an appropriate +PolyMap that conforms to the requirements of the SIP convention. + +\item A new function call astCreatedAt is now available that returns the +function name, file path and line number at which an AST object was first +created. Note, there is no Fortran equivalent to this new C function. + +\item The number of digits used to format floating point values has been +increased in order to avoid loss of precision when converting from binary +to string and back to binary. This could cause very small changes in numerical +values returned by AST functions. + +\item If a FrameSet is supplied as the ``map'' argument to astAddFrame, it now +extracts and stores the base->current Mapping from the supplied FrameSet. +Previously, the entire FrameSet was stored as the Mapping. + +\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/ast/switchmap.c b/ast/switchmap.c new file mode 100644 index 0000000..23dec4b --- /dev/null +++ b/ast/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/ast/switchmap.h b/ast/switchmap.h new file mode 100644 index 0000000..40c60a9 --- /dev/null +++ b/ast/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/ast/table.c b/ast/table.c new file mode 100644 index 0000000..0d283bc --- /dev/null +++ b/ast/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/ast/table.h b/ast/table.h new file mode 100644 index 0000000..f51690e --- /dev/null +++ b/ast/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/ast/templateclass.README b/ast/templateclass.README new file mode 100644 index 0000000..914834e --- /dev/null +++ b/ast/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/ast/templateclass.c b/ast/templateclass.c new file mode 100644 index 0000000..395f63c --- /dev/null +++ b/ast/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/ast/templateclass.h b/ast/templateclass.h new file mode 100644 index 0000000..070e84b --- /dev/null +++ b/ast/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/ast/timeframe.c b/ast/timeframe.c new file mode 100644 index 0000000..8d0906a --- /dev/null +++ b/ast/timeframe.c @@ -0,0 +1,7530 @@ +/* +*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. +* 5-APR-2017 (GSB): +* - Pass DTAI to astAddTime for UTCTOTAI and TAITOUTC conversions and +* check whether there is a relevant DTAI difference in +* MakeTimeMapping. +* 7-APR-2017 (GSB): +* - Add LT to macro defining scales depending on DTAI. +* 10-APR-2017 (GSB): +* - Added macro to test floating point equality and used it for Dtai. +* 27-APR-2017 (DSB): +* Conversions between TT and TDB now require DTAI as an argument. +*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 ) + +/* Timescales can be divided up into 3 groups such that conversion from a + timescale in one group to a timescale in any other group requires the + DTAI value, but conversion between timescales in the same group does not + require the DTAI value. Define a macro that returns the group number + (1, 2 or 3) for a specific timescale. */ +#define DTAI_SCALE(ts) \ + ( ( ts == AST__LMST || \ + ts == AST__LAST || \ + ts == AST__GMST || \ + ts == AST__UT1 || \ + ts == AST__UTC || \ + ts == AST__LT ) ? 1 : \ + ( ( ts == AST__TAI || ts == AST__TT ) ? 2 : 3 ) ) + +/* 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 + +/* Check for floating point equality (within the given tolerance), taking + bad values into account. */ +#define EQUAL(aa,bb,tol) (((aa)==AST__BAD)?(((bb)==AST__BAD)?1:0):(((bb)==AST__BAD)?0:(fabs((aa)-(bb))<=(tol)))) + +/* 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", 2, args ); + + } else if( newsys == AST__JEPOCH ) { + astTimeAdd( timemap, "MJDTOJEP", 2, args ); + + } else if( newsys == AST__BEPOCH ) { + astTimeAdd( timemap, "MJDTOBEP", 2, 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", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* LTOffset. */ +/* --------- */ + } else if ( !strcmp( attrib, "ltoffset" ) ) { + dval = astGetLTOffset( this ); + if( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__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[ 5 ]; + double args_lt[ 1 ]; + double args_ut[ 1 ]; + double args_tai[ 2 ]; + 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", 2, args ); + + } else if( sys1 == AST__JEPOCH ) { + astTimeAdd( timemap, "JEPTOMJD", 2, args ); + + } else if( sys1 == AST__BEPOCH ) { + astTimeAdd( timemap, "BEPTOMJD", 2, 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; + +/* Currently the only conversion that take 5 arguments require the DTAI + value as the 5th argument. */ + args[ 4 ] = this ? astGetDtai( this ) : AST__BAD; + +/* 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; + +/* The UTCTOTAI and TAITOUTC conversions require the input offset and DTAI. */ + args_tai[ 0 ] = args[ 0 ]; + args_tai[ 1 ] = this ? astGetDtai( this ) : AST__BAD; + +/* 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", 2, args_tai ); + + } else if( ts1 == AST__TT ) { + astTimeAdd( timemap, "TTTOTAI", 1, args ); + + } else if( ts1 == AST__TDB ) { + astTimeAdd( timemap, "TDBTOTT", 5, args ); + astTimeAdd( timemap, "TTTOTAI", 1, args ); + + } else if( ts1 == AST__TCG ) { + astTimeAdd( timemap, "TCGTOTT", 1, args ); + astTimeAdd( timemap, "TTTOTAI", 1, args ); + + } else if( ts1 == AST__LT ) { + astTimeAdd( timemap, "LTTOUTC", 1, args_lt ); + astTimeAdd( timemap, "UTCTOTAI", 2, args_tai ); + + } else if( ts1 == AST__TCB ) { + astTimeAdd( timemap, "TCBTOTDB", 1, args ); + astTimeAdd( timemap, "TDBTOTT", 5, args ); + astTimeAdd( timemap, "TTTOTAI", 1, args ); + + } else if( ts1 == AST__UT1 ) { + astTimeAdd( timemap, "UTTOUTC", 1, args_ut ); + astTimeAdd( timemap, "UTCTOTAI", 2, args_tai ); + + } else if( ts1 == AST__GMST ) { + astTimeAdd( timemap, "GMSTTOUT", 1, args ); + astTimeAdd( timemap, "UTTOUTC", 1, args_ut ); + astTimeAdd( timemap, "UTCTOTAI", 2, args_tai ); + + } else if( ts1 == AST__LAST ) { + astTimeAdd( timemap, "LASTTOLMST", 3, args ); + astTimeAdd( timemap, "LMSTTOGMST", 3, args ); + astTimeAdd( timemap, "GMSTTOUT", 1, args ); + astTimeAdd( timemap, "UTTOUTC", 1, args_ut ); + astTimeAdd( timemap, "UTCTOTAI", 2, args_tai ); + + } else if( ts1 == AST__LMST ) { + astTimeAdd( timemap, "LMSTTOGMST", 3, args ); + astTimeAdd( timemap, "GMSTTOUT", 1, args ); + astTimeAdd( timemap, "UTTOUTC", 1, args_ut ); + astTimeAdd( timemap, "UTCTOTAI", 2, args_tai ); + } + +/* Now add a conversion from TAI to the output timescale. */ + if( ts2 == AST__TAI ) { + + } else if( ts2 == AST__UTC ) { + astTimeAdd( timemap, "TAITOUTC", 2, args_tai ); + + } else if( ts2 == AST__TT ) { + astTimeAdd( timemap, "TAITOTT", 1, args ); + + } else if( ts2 == AST__TDB ) { + astTimeAdd( timemap, "TAITOTT", 1, args ); + astTimeAdd( timemap, "TTTOTDB", 5, args ); + + } else if( ts2 == AST__TCG ) { + astTimeAdd( timemap, "TAITOTT", 1, args ); + astTimeAdd( timemap, "TTTOTCG", 1, args ); + + } else if( ts2 == AST__TCB ) { + astTimeAdd( timemap, "TAITOTT", 1, args ); + astTimeAdd( timemap, "TTTOTDB", 5, args ); + astTimeAdd( timemap, "TDBTOTCB", 1, args ); + + } else if( ts2 == AST__UT1 ) { + astTimeAdd( timemap, "TAITOUTC", 2, args_tai ); + astTimeAdd( timemap, "UTCTOUT", 1, args_ut ); + + } else if( ts2 == AST__GMST ) { + astTimeAdd( timemap, "TAITOUTC", 2, args_tai ); + astTimeAdd( timemap, "UTCTOUT", 1, args_ut ); + astTimeAdd( timemap, "UTTOGMST", 1, args ); + + } else if( ts2 == AST__LAST ) { + astTimeAdd( timemap, "TAITOUTC", 2, args_tai ); + astTimeAdd( timemap, "UTCTOUT", 1, args_ut ); + astTimeAdd( timemap, "UTTOGMST", 1, args ); + astTimeAdd( timemap, "GMSTTOLMST", 3, args ); + astTimeAdd( timemap, "LMSTTOLAST", 3, args ); + + } else if( ts2 == AST__LMST ) { + astTimeAdd( timemap, "TAITOUTC", 2, args_tai ); + astTimeAdd( timemap, "UTCTOUT", 1, args_ut ); + astTimeAdd( timemap, "UTTOGMST", 1, args ); + astTimeAdd( timemap, "GMSTTOLMST", 3, args ); + + } else if( ts2 == AST__LT ) { + astTimeAdd( timemap, "TAITOUTC", 2, args_tai ); + astTimeAdd( timemap, "UTCTOLT", 1, 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", 2, args ); + + } else if( sys2 == AST__JD ) { + astTimeAdd( timemap, "MJDTOJD", 2, args ); + + } else if( sys2 == AST__JEPOCH ) { + astTimeAdd( timemap, "MJDTOJEP", 2, args ); + + } else if( sys2 == AST__BEPOCH ) { + astTimeAdd( timemap, "MJDTOBEP", 2, 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 ardtai; /* Align->result depends on Dtai? */ + 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 dtaidiff; /* Do target and result Dtai values 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 tadtai; /* Target->align depends on Dtai? */ + 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. Or if DTAI differs and is similarly relevant. */ + dut1diff = ( astGetDut1( target ) != astGetDut1( result ) ); + tadut = DUT1_SCALE( ts1 ) != DUT1_SCALE( align_ts ); + ardut = DUT1_SCALE( align_ts ) != DUT1_SCALE( ts2 ); + + dtaidiff = ! EQUAL( astGetDtai( target ), astGetDtai( result ), 1.0E-6 ); + tadtai = DTAI_SCALE( ts1 ) != DTAI_SCALE( align_ts ); + ardtai = DTAI_SCALE( align_ts ) != DTAI_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 ) ) && + ( !dtaidiff || !( tadtai || ardtai ) ) ) { + *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, dtai, dut1 + and ltoffset), then a Mapping from the alignment Frame to the results + Frame (using the result clock, dtai, 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", 2, args ); */ + astTimeAdd( timemap, "MJDTOMJD", 2, args ); + + } else if( oldsys == AST__JD ) { + astTimeAdd( timemap, "JDTOMJD", 2, args ); + + } else if( oldsys == AST__JEPOCH ) { + astTimeAdd( timemap, "JEPTOMJD", 2, args ); + + } else if( oldsys == AST__BEPOCH ) { + astTimeAdd( timemap, "BEPTOMJD", 2, 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( "Dtai", a, len ) ) { + set = astTestDtai( this ); + desc = "TAI-UTC correction"; + + } 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/ast/timeframe.h b/ast/timeframe.h new file mode 100644 index 0000000..4aae267 --- /dev/null +++ b/ast/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/ast/timemap.c b/ast/timemap.c new file mode 100644 index 0000000..7eef60b --- /dev/null +++ b/ast/timemap.c @@ -0,0 +1,5330 @@ +/* +*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. +* 1-SEP-2016 (DSB): +* Add 2017 January 1 leap second. +* 11-NOV-2016 (DSB): +* Add argument "narg" to astTimeAdd method. +* 5-APR-2017 (GSB): +* Add DTAI argument for TAITOUTC and UTCTOTAI. +* 28-APR-2017 (DSB): +* - Fix bug in MapMerge that prevented adjacent TAITOUTC and UTCTOTAI +* conversions cancelling out. +* - Add DTAI argument for TTTOTDB and TDBTOTT. +*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 7 + +/* 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 **order, 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, 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 *, int, 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, NULL, 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[ 5 ] = 0.001*r*AST__AU; + cvtargs[ 6 ] = 0.001*z*AST__AU; + break; + + case AST__TDBTOTT: + palGeoc( cvtargs[ 2 ], cvtargs[ 3 ], &r, &z ); + cvtargs[ 5 ] = 0.001*r*AST__AU; + cvtargs[ 6 ] = 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, int narg, + 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, int narg, 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. +* narg +* The number of argument values supplied in "args". +* 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, DTAI ) +* Convert a TAI MJD to a UTC MJD. +* AST__UTCTOTAI( MJDOFF, DTAI ) +* 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, DTAI ) +* Convert a TT MJD to a TDB MJD. +* AST__TDBTOTT( MJDOFF, OBSLON, OBSLAT, OBSALT, DTAI ) +* 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. +* - DTAI: The value of TAI-UTC (the value returned by astDat is used if +* DTAI is AST__BAD). +* - 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, + NULL, 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 ); + } + +/* If the number of supplied arguments is incorrect, then report an error. */ + if ( astOK && nargs != narg ) { + astError( AST__TIMIN, "AddTimeCvt(%s): Invalid no. of arguments for time " + "coordinate conversion type %d - %d supplied, %d required.", + status, astGetClass( this ), (int) cvttype, narg, nargs ); + } + +/* 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 **order, 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 **order, 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. +* order +* Ignored if NULL. If not NULL, it should be an address at which +* to return a pointer to a dynamically allocated int array (the +* returned array should be freed using astFree when no longer +* needed). The returned array will have "*szargs" elements. The +* index into the array is the index of the argument within the +* external representation of a TimeMap as produced by the Dump +* function. The value stored in each element is the index of +* the argument within the internal argument list. If there is +* no difference between the internal and external order of the +* arguments, a NULL pointer will be returned instead of a pointer +* to an int array. +* 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; + if( order ) *order = 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 = 2; + *szargs = 2; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "DTAI"; + break; + + case AST__UTCTOTAI: + *comment = "Convert UTC to TAI"; + result = "UTCTOTAI"; + *nargs = 2; + *szargs = 2; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "DTAI"; + 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 = 5; + *szargs = 7; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "Observer longitude"; + arg[ 2 ] = "Observer latitude"; + arg[ 3 ] = "Observer altitude"; + arg[ 4 ] = "DTAI"; + arg[ 5 ] = "Distance from earth spin axis"; + arg[ 6 ] = "Distance north of equatorial plane"; + +/* For backward compatibility, the order of arguments in the external + representation is different to the internal order. This became + necessary when the DTAI user-supplied argument was added. New user + supplied arguments need to be before the calculated arguments in + the internal list, but need to be added to the end of the external + argument list. This means new TimeMaps read by old software will ignore + the new user supplied argument. It also means that old TimeMaps read by + new software will use default values for the missing user supplied + argument. */ + if( order ) { + *order = astMalloc( 7*sizeof( **order ) ); + if( astOK ) { + (*order)[ 0 ] = 0; + (*order)[ 1 ] = 1; + (*order)[ 2 ] = 2; + (*order)[ 3 ] = 3; + (*order)[ 4 ] = 5; + (*order)[ 5 ] = 6; + (*order)[ 6 ] = 4; + } + } + break; + + case AST__TDBTOTT: + *comment = "Convert TDB to TT"; + result = "TDBTOTT"; + *nargs = 5; + *szargs = 7; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "Observer longitude"; + arg[ 2 ] = "Observer latitude"; + arg[ 3 ] = "Observer altitude"; + arg[ 4 ] = "DTAI"; + arg[ 5 ] = "Distance from earth spin axis"; + arg[ 6 ] = "Distance north of equatorial plane"; + if( order ) { + *order = astMalloc( 7*sizeof( **order ) ); + if( astOK ) { + (*order)[ 0 ] = 0; + (*order)[ 1 ] = 1; + (*order)[ 2 ] = 2; + (*order)[ 3 ] = 3; + (*order)[ 4 ] = 5; + (*order)[ 5 ] = 6; + (*order)[ 6 ] = 4; + } + } + 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: 2017 January 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 ) { + +/* 2017 January 1 */ + if ( in >= 57754.0 ) { + result = 37.0; + +/* 2015 July 1 */ + } else 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 { + + +/* 2017 January 1 */ + if ( in >= 57754.0 + 37.0/SPD ) { + result = -37.0; + +/* 2015 July 1 */ + } else 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 *narg; /* Pointer to argument count */ + int *szarg; /* Pointer to argument array size */ + 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 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 ); + narg = 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 + nstep, szarg + nstep, argdesc, + NULL, 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__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 + that are swapped by inversion. */ + } 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 two user-supplied arguments + that are NOT swapped by inversion. */ + } else if( ( PAIR_CVT2( AST__TAITOUTC, AST__UTCTOTAI ) ) && + 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__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 five 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 ] ) && + astEQUAL( cvtargs[ istep ][ 4 ], + cvtargs[ istep + 1 ][ 4 ] ) ) { + 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 ]; + 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 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 ], + narg[ 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 ); + narg = astFree( narg ); + } + +/* 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, int narg, + 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, int narg, +c const double args[] ) +f CALL AST_TIMEADD( THIS, CVT, NARG, 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 narg +f NARG = INTEGER (Given) +* The number of argument values supplied in the +c "args" array. +f ARGS array. +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,DTAI): Convert a TAI MJD to a UTC MJD. +* - "UTCTOTAI" (MJDOFF,DTAI): 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,DTAI): Convert a TT MJD to a TDB MJD. +* - "TDBTOTT" (MJDOFF,OBSLON,OBSLAT,OBSALT,DTAI): 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. +* - DTAI: The value of TAI-UTC (the value returned by astDat is used if +* DTAI is AST__BAD). +* - 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, narg, 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 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. */ + 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 ] += ( (args[ 1 ] == AST__BAD) + ? astDat( time[ point ] + args[ 0 ], 0 ) + : - args[ 1 ] )/SPD; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += ( (args[ 1 ] == AST__BAD) + ? astDat( time[ point ] + args[ 0 ], 1 ) + : args[ 1 ] )/SPD; + } + } + } + break; + +/* UTC to TAI. */ +/* ----------- */ + case AST__UTCTOTAI: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += ( (args[ 1 ] == AST__BAD) + ? astDat( time[ point ] + args[ 0 ], 1 ) + : args[ 1 ] )/SPD; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += ( (args[ 1 ] == AST__BAD) + ? astDat( time[ point ] + args[ 0 ], 0 ) + : - args[ 1 ] )/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. In fact, TAI is + probably a good enough approximation to UTC for the vast majority of + cases, but for completeness we handle the difference between TAI and + UTC (i.e. leap seconds) here. */ + 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 + ( (args[ 4 ] == AST__BAD) ? astDat( tai, 0 ) + : -args[ 4 ] )/SPD; + time[ point ] += Rcc( tt, utc, args[ 1 ], args[ 5 ], + args[ 6 ], 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 + ( (args[ 4 ] == AST__BAD) ? astDat( tai, 0 ) + : -args[ 4 ] )/SPD; + time[ point ] -= Rcc( tdb, utc, args[ 1 ], args[ 5 ], + args[ 6 ], 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. In fact, TAI is + probably a good enough approximation to UTC for the vast majority of + cases, but for completeness we handle the difference between TAI and + UTC (i.e. leap seconds) here. */ + 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 + ( (args[ 4 ] == AST__BAD) ? astDat( tai, 0 ) + : -args[ 4 ] )/SPD; + time[ point ] -= Rcc( tdb, utc, args[ 1 ], args[ 5 ], + args[ 6 ], 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 + ( (args[ 4 ] == AST__BAD) ? astDat( tai, 0 ) + : -args[ 4 ] )/SPD; + time[ point ] += Rcc( tt, utc, args[ 1 ], args[ 5 ], + args[ 6 ], 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; /* Internal index of argument */ + int icvt; /* Loop counter for conversion steps */ + int ival; /* Integer value */ + int jarg; /* External index of argument */ + int nargs; /* Number of user-supplied arguments */ + int *order; /* Order in which to store 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, &order, 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++ ) { + +/* The "iarg" value is the index of the argument within the external + argument list. Get the index of the argument within the internal + argument list. */ + jarg = order ? order[ iarg ] : iarg; + +/* Arguments. */ +/* ---------- */ +/* Create an appropriate keyword and write out the argument value, + accompanied by the descriptive comment obtained above. */ + if( this->cvtargs[ icvt ][ jarg ] != AST__BAD ) { + (void) sprintf( key, "Time%d%c", icvt + 1, ALPHABET[ iarg ] ); + astWriteDouble( channel, key, 1, 1, this->cvtargs[ icvt ][ jarg ], + argdesc[ jarg ] ); + } + } + +/* Free the order array. */ + order = astFree( order ); + +/* 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; /* Internal index of argument */ + int icvt; /* Loop counter for conversion steps */ + int jarg; /* External index of argument */ + int *order; /* Order in which to store arguments */ + 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, &order, status ); + new->cvtargs[ icvt ] = astMalloc( sizeof( double ) * + (size_t) szargs ); + +/* Read in data for each argument... */ + if ( astOK ) { + for ( iarg = 0; iarg < szargs; iarg++ ) { + +/* The "iarg" value is the index of the argument within the external + argument list. Get the index of the argument within the internal + argument list. */ + jarg = order ? order[ iarg ] : iarg; + +/* Arguments. */ +/* ---------- */ +/* Create an appropriate keyword and read each argument value. */ + (void) sprintf( key, "time%d%c", icvt + 1, ALPHABET[ iarg ] ); + new->cvtargs[ icvt ][ jarg ] = astReadDouble( channel, key, + AST__BAD ); + } + } + +/* Free the order array. */ + order = astFree( order ); + +/* 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, int narg, const double args[], + int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,TimeMap,TimeAdd))( this, cvt, narg, args, status ); +} + + + + diff --git a/ast/timemap.h b/ast/timemap.h new file mode 100644 index 0000000..fa24730 --- /dev/null +++ b/ast/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 *, int, 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 *, int, 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,narg,args) \ +astINVOKE(V,astTimeAdd_(astCheckTimeMap(this),cvt,narg,args,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ +#define astDat(in,forward) astDat_(in,forward,STATUS_PTR) +#endif +#endif + + + + + diff --git a/ast/tpn.c b/ast/tpn.c new file mode 100644 index 0000000..0d30503 --- /dev/null +++ b/ast/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/ast/tranmap.c b/ast/tranmap.c new file mode 100644 index 0000000..5abd75d --- /dev/null +++ b/ast/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/ast/tranmap.h b/ast/tranmap.h new file mode 100644 index 0000000..ae16f18 --- /dev/null +++ b/ast/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/ast/unit.c b/ast/unit.c new file mode 100644 index 0000000..0ce3184 --- /dev/null +++ b/ast/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", AST__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/ast/unit.h b/ast/unit.h new file mode 100644 index 0000000..3637f13 --- /dev/null +++ b/ast/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/ast/unitmap.c b/ast/unitmap.c new file mode 100644 index 0000000..a371031 --- /dev/null +++ b/ast/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/ast/unitmap.h b/ast/unitmap.h new file mode 100644 index 0000000..c808ed1 --- /dev/null +++ b/ast/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/ast/unitnormmap.c b/ast/unitnormmap.c new file mode 100644 index 0000000..c9e69c2 --- /dev/null +++ b/ast/unitnormmap.c @@ -0,0 +1,1666 @@ +/* +*class++ +* Name: +* UnitNormMap + +* Purpose: +* Convert a vector to a unit vector and its norm, relative to a specified centre. + +* Constructor Function: +c astUnitNormMap +f AST_UNITNORMMAP + +* Description: +* The forward transformation of a UnitNormMap subtracts the specified centre +* and then transforms the resulting vector to a unit vector and the vector norm. +* The output contains one more coordinate than the input: the initial +* Nin outputs are in the same order as the input; the final output is the norm. +* If the norm is 0, then the output of the forward transformation is AST__BAD +* for each component of the unit vector and 0 for the norm (the final value). +* +* The inverse transformation of a UnitNormMap multiplies each component +* of the provided vector by the provided norm and adds the specified centre. +* The output contains one fewer coordinate than the input: the initial Nin inputs +* are in the same order as the output; the final input is the norm. +* If the provided norm is 0 then the other input values are ignored, +* and the output vector is the centre. +* +* Example: if centre = [1, -1] then [5, 2] transforms to [4, 3] after subtracting the centre; +* the norm is 5, so the output is [0.8, 0.6, 5]. +* +* UnitNormMap enables radially symmetric transformations, as follows: +* - apply a UnitNormMap to produce a unit vector and norm (radius) +* - apply a one-dimensional mapping to the norm (radius), while passing the unit vector unchanged +* - apply the same UnitNormMap in the inverse direction to produce the result + +* Inheritance: +* The UnitNormMap class inherits from the Mapping class. + +* Attributes: +* The UnitNormMap class does not define any new attributes beyond those +* which are applicable to all Mappings. + +* Functions: +c The UnitNormMap class does not define any new functions beyond those +f The UnitNormMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 2016 University of Washington + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General 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: +* RO: Russell Owen (LSST) +* DSB: David S Berry (EAO) + +* History: +* 20-APR-2016 (RO): +* Original version. +* 17-MAR-2017 (DSB): +* Fix some memory leaks in MakeMergedMap. +*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 UnitNormMap + +/* 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.0E9*MAX((fabs(aa)+fabs(bb))*DBL_EPSILON,DBL_MIN)))) + +/* 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 "unitmap.h" /* Unit mappings */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "shiftmap.h" /* ShiftMap */ +#include "winmap.h" /* ShiftMap */ +#include "channel.h" /* I/O channels */ +#include "unitnormmap.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(UnitNormMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(UnitNormMap,Class_Init) +#define class_vtab astGLOBAL(UnitNormMap,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstUnitNormMapVtab 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. */ +AstUnitNormMap *astUnitNormMapId_( int, const double [], const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ + +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static int GetMappingType( AstMapping *, int * ); +static AstMapping * MakeMergedMap( AstMapping *, AstMapping *, 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 int GetIsLinear( AstMapping *, int * ); + +/* Member functions. */ +/* ================= */ +static int GetMappingType( AstMapping *map, int *status ) { +/* +* +* Name: +* GetMappingType + +* Purpose: +* Return a code describing the mapping type, to aid simplification. + +* Type: +* Private function. + +* Synopsis: +* #include "unitnormmap.h" +* int MakeMergedMap( AstMapping *map1, AstMapping *map2, int inv1, int inv2, int *status ) + +* Class Membership: +* UnitNormMap internal utility function. + +* Description: +* This function returns an integer code describing the type of a mapping, +* to help with merging two mappings. The codes are: +* 3 = WinMap +* 2 = ShiftMap +* 1 = UnitNormMap +* 0 = other + +* Parameters: +* map +* A pointer to the mapping +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the merged mapping, or NULL if the Mappings cannot be merged + +*/ + const char *class = astGetClass( map ); + int type = 0; + if( strcmp( class, "UnitNormMap" ) == 0 ) { + type = 1; + } else if( strcmp( class, "ShiftMap" ) == 0 ) { + type = 2; + } else if( strcmp( class, "WinMap" ) == 0){ + type = 3; + } + return type; +} + +static AstMapping * MakeMergedMap( AstMapping *map1, AstMapping *map2, int *status ){ +/* +* +* Name: +* MakeMergedMap + +* Purpose: +* Make a single Mapping that is equivalent to the merging of two other Mappings, +* one of which must be a UnitNormMap. + +* Type: +* Private function. + +* Synopsis: +* #include "unitnormmap.h" +* int MakeMergedMap( AstMapping *map1, AstMapping *map2, int inv1, int inv2, int *status ) + +* Class Membership: +* UnitNormMap internal utility function. + +* Description: +* This function returns a single Mapping that is the equivalent of the two supplied Mappings, +* if they can be merged. One of the pair must be a UnitNormMap. +* +* Supported merges, which must be in series: +* ShiftMap + UnitNormMap(forward) = UnitNormMap(forward) +* WinMap with unit scale + UnitNormMap(forward) = UnitNormMap(forward) +* UnitNormMap(inverted) + ShiftMap = UnitNormMap(inverted) +* UnitNormMap(inverted) + WinMap with unit scale = UnitNormMap(inverted) +* UnitNormMap(forward) + identical UnitNormMap(inverted) = UnitMap +* UnitNormMap(inverted) + identical UnitNormMap(forward) = UnitMap +* UnitNormMap(forward) + UnitNormMap(inverted) = ShiftMap +* +* +* Notes: +* - A UnitMap before or after a UnitNormMap = the UnitNormMap, but UnitMap takes care of that merge. +* - The code that checks for ShiftMap + UnitNormMap probably never runs, because ShiftMap is +* converted to WinMap during simplification before it gets here. + +* Parameters: +* map1 +* A pointer to the first mapping. +* map2 +* A pointer to the second mapping. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the merged mapping, or NULL if the Mappings cannot be merged + +*/ + +/* Check the global error status. */ + if( !astOK ) return NULL; + +/* Initialise returned mapping */ + AstMapping *retmap = NULL; + +/* Set type to 1=UnitNormMap, 2=ShiftMap, 0=other for both maps */ + int type1 = GetMappingType( map1, status ); + int type2 = GetMappingType( map2, status ); + if( !astOK ) return NULL; + + if( type1 == 0 || type2 == 0 ) return NULL; /* can only merge with UnitNormMap or ShiftMap */ + + if( !(type1 == 1 || type2 == 1) ) return NULL; /* one must be a UnitNormMap */ + + if( type1 == 2 ) { + if( astGetInvert( map2 )) return NULL; /* ShiftMap + UnitNormMap(inverted) not supported */ + +/* ShiftMap + UnitNormMap(forward) = UnitNormMap(forward) */ + AstShiftMap *shiftmap = (AstShiftMap *) map1; + AstUnitNormMap *unm = (AstUnitNormMap *) map2; + int nin = astGetNin( shiftmap ); + double shiftmult = astGetInvert( shiftmap ) ? -1 : 1; + double *newcentre = astMalloc( sizeof(double)*(size_t)nin ); + if( astOK ) { + int coord = 0; + for( coord = 0; coord < nin; coord++ ){ + newcentre[coord] = unm->centre[coord] - shiftmult*shiftmap->shift[coord]; + } + retmap = (AstMapping *) astUnitNormMap( nin, newcentre, "", status ); + newcentre = astFree( (void *) newcentre ); + } + } else if( type1 == 3 ) { + if( astGetInvert( map2 )) return NULL; /* WinMap + UnitNormMap(inverted) not supported */ + +/* WinMap with unit scale + UnitNormMap(forward) = UnitNormMap(forward); + Do not create a returned Mapping (but do free the newcentre memory) if WinMap + does not have unit scale */ + AstWinMap *winmap = (AstWinMap *) map1; + AstUnitNormMap *unm = (AstUnitNormMap *) map2; + int nin = astGetNin( winmap ); + double shiftmult = astGetInvert( winmap ) ? -1 : 1; + double *newcentre = astMalloc( sizeof(double)*(size_t)nin ); + if( astOK ) { + int coord = 0; + int ok = 1; + for( coord = 0; coord < nin; coord++ ){ + if( !EQUAL( winmap->b[coord], 1.0 )) ok = 0; + newcentre[coord] = unm->centre[coord] - shiftmult*winmap->a[coord]; + } + if( ok ) retmap = (AstMapping *) astUnitNormMap( nin, newcentre, "", status ); + newcentre = astFree( (void *) newcentre ); + } + } else if( type2 == 2 ) { + if( !astGetInvert( map1 )) return NULL; /* UnitNormMap(forward) + ShiftMap not supported */ + +/* UnitNormMap(inverted) + ShiftMap = UnitNormMap(inverted) */ + AstShiftMap *shiftmap = (AstShiftMap *) map2; + AstUnitNormMap *unm = (AstUnitNormMap *) map1; + int nin = astGetNin( shiftmap ); + double shiftmult = astGetInvert( shiftmap ) ? -1 : 1; + double *newcentre = astMalloc( sizeof(double)*(size_t)nin ); + if( astOK ) { + int coord = 0; + for( coord = 0; coord < nin; coord++ ){ + newcentre[coord] = unm->centre[coord] + shiftmult*shiftmap->shift[coord]; + } + retmap = (AstMapping *) astUnitNormMap( nin, newcentre, "Invert=1", status ); + newcentre = astFree( (void *) newcentre ); + } + } else if( type2 == 3 ) { + if( !astGetInvert( map1 )) return NULL; /* UnitNormMap(forward) + WinMap not supported */ + +/* UnitNormMap(inverted) + WinMap = UnitNormMap(inverted); + Do not create a returned Mapping - but do free the newcentre memory - if WinMap + does not have unit scale */ + AstWinMap *winmap = (AstWinMap *) map2; + AstUnitNormMap *unm = (AstUnitNormMap *) map1; + int nin = astGetNin( winmap ); + double shiftmult = astGetInvert( winmap ) ? -1 : 1; + double *newcentre = astMalloc( sizeof(double)*(size_t)nin ); + if( astOK ) { + int coord = 0; + int ok = 1; + for( coord = 0; coord < nin; coord++ ){ + if( !EQUAL( winmap->b[coord], 1.0 )) ok = 0; + newcentre[coord] = unm->centre[coord] + shiftmult*winmap->a[coord]; + } + if( ok ) retmap = (AstMapping *) astUnitNormMap( nin, newcentre, "Invert=1", status ); + newcentre = astFree( (void *) newcentre ); + } + } else { + if( !astGetInvert( map1 ) == !astGetInvert( map2 )) return NULL; /* UNMs must have opposite dir. */ + +/* Two UnitNormMaps in opposite directions */ + AstUnitNormMap *unm1 = (AstUnitNormMap *) map1; + AstUnitNormMap *unm2 = (AstUnitNormMap *) map2; + + int ctrlen = MIN( astGetNin( map1 ), astGetNin( map2 ) ); + int centres_equal = 1; + int i = 0; + for( i = 0; i < ctrlen; i++ ){ + if( !EQUAL( unm1->centre[i], unm2->centre[i] )){ + centres_equal = 0; + break; + } + } + if( centres_equal ) { + +/* Two UnitNormMap in opposite directions with identical centres = UnitMap */ + retmap = (AstMapping *) astUnitMap( astGetNin( map1 ), "", status ); + } else { + if( astGetInvert( map2 )) { + +/* UnitNormMap(forward) + UnitNormMap(inverted) = ShiftMap */ + int nin = astGetNin( map1 ); + double *shift = astMalloc( sizeof(double)*(size_t)nin ); + if( astOK ) { + int coord = 0; + for( coord = 0; coord < nin; coord++ ){ + shift[coord] = unm2->centre[coord] - unm1->centre[coord]; + } + retmap = (AstMapping *) astShiftMap( nin, shift, "", status ); + shift = astFree( (void *) shift ); + } + } + } + } + + return astOK ? retmap : NULL; +} + +static int GetIsLinear( AstMapping *this_mapping, int *status ){ +/* +* Name: +* GetIsLinear + +* Purpose: +* Return the value of the IsLinear attribute for a UnitNormMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void GetIsLinear( AstMapping *this, int *status ) + +* Class Membership: +* UnitNormMap 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 UnitNormMap. +* status +* Pointer to the inherited status variable. +*/ + return 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 "unitnormmap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* UnitNormMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied UnitNormMap, +* in bytes. + +* Parameters: +* this +* Pointer to the UnitNormMap. +* 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. +*/ + +/* Initialise. */ + int result = 0; + +/* Check the global error status. */ + if( !astOK ) return result; + +/* Obtain a pointers to the UnitNormMap structure. */ + AstUnitNormMap *this = (AstUnitNormMap *) 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->centre ); + +/* If an error occurred, clear the result value. */ + if( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +void astInitUnitNormMapVtab_( AstUnitNormMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitUnitNormMapVtab + +* Purpose: +* Initialise a virtual function table for a UnitNormMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "unitnormmap.h" +* void astInitUnitNormMapVtab( AstUnitNormMapVtab *vtab, const char *name ) + +* Class Membership: +* UnitNormMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the UnitNormMap 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). +*- +*/ + +/* Check the local error status. */ + if( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + 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 astIsAUnitNormMap) 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. */ + AstObjectVtab *object = (AstObjectVtab *) vtab; + AstMappingVtab *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. */ + mapping->MapMerge = MapMerge; + mapping->GetIsLinear = GetIsLinear; + +/* Declare the class dump, copy and delete functions.*/ + astSetDump( vtab, Dump, "UnitNormMap", "Compute unit vector and norm relative to a centre" ); + 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 UnitNormMap. + +* 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: +* UnitNormMap 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 UnitNormMap 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 UnitNormMap 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 UnitNormMap which is to be merged with +* its neighbours. This should be a cloned copy of the UnitNormMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* UnitNormMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated UnitNormMap 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. +*/ + +/* Initialise. */ + int result = -1; + +/* Check the global error status. */ + if( !astOK ) return result; + + if( series ) { + +/* In series */ +/* Try to merge UnitNormMap with either of its neighbours. */ + AstMapping *map1 = NULL; /* The first Mapping to merge */ + AstMapping *map2 = NULL; /* The second Mapping to merge */ + AstMapping *newmap = NULL; /* The merged Mapping */ + int i1 = MAX( where - 1, 0 ); + int i2 = i1 + 1; + for( ; i2 < *nmap; i1++, i2++ ){ + map1 = ( *map_list )[ i1 ]; + map2 = ( *map_list )[ i2 ]; + int wasinverted1 = astGetInvert( map1 ); + int wasinverted2 = astGetInvert( map2 ); + astSetInvert( map1, ( *invert_list )[ i1 ] ); + astSetInvert( map2, ( *invert_list )[ i2 ] ); + newmap = MakeMergedMap( map1, map2, status ); + astSetInvert( map1, wasinverted1 ); + astSetInvert( map2, wasinverted2 ); + if( newmap ) break; + } + + if( !newmap ) return -1; + if( !astOK ) { + astAnnul( newmap ); + return -1; + } + +/* Annul the first of the two Mappings, and replace it with the merged Mapping. + Also update the invert flag. */ + (void) astAnnul( map1 ); + ( *map_list )[ i1 ] = newmap; + ( *invert_list )[ i1 ] = astGetInvert( newmap ); + +/* Annul the second of the two Mappings, and shuffle down the rest of the list to fill the gap. */ + (void) astAnnul( map2 ); + int i = 0; + 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; + + } else { + +/* In parallel. */ +/* UnitNormMaps cannot combine in parallel with any other Mappings. */ + } + +/* Return the result. */ + return result; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a UnitNormMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "unitnormmap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* UnitNormMap member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a UnitNormMap 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 UnitNormMap. +* 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 UnitNormMap 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. +*/ + +/* Check the global error status. */ + if( !astOK ) return NULL; + +/* Obtain a pointer to the UnitNormMap. */ + AstUnitNormMap *map = (AstUnitNormMap *) 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. */ + AstPointSet *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. */ + int ncoord_in = astGetNcoord( in ); + int ncoord_out = astGetNcoord( result ); + int npoint = astGetNpoint(in); + double **ptr_in = astGetPoints(in); + double **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 UnitNormMap does not contain a centre. */ + if( !map->centre && astOK ){ + const char *class = astGetClass( this ); + astError( AST__BADSM, "astTransform(%s): The supplied %s does not " + "contain any centre information.", status, class, class ); + } + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if( astOK ){ + +/* If any centre coordinate is bad then set all outputs bad */ + int const ncoord_centre = forward ? ncoord_in : ncoord_out; + int coord_ctr = 0; + for( coord_ctr = 0; coord_ctr < ncoord_centre; coord_ctr++ ){ + if( (map->centre)[coord_ctr] == AST__BAD ){ + int coord_out = 0; + for( coord_out = 0; coord_out < ncoord_out; coord_out++ ){ + int point = 0; + for( point = 0; point < npoint; point++ ){ + ptr_out[point][coord_out] = AST__BAD; + } + } + return result; + } + } + + if( forward ){ + +/* Forward transformation: vector to unit vector, norm */ + int point = 0; + for( point = 0; point < npoint; point++ ){ + +/* Compute max_relin: the maximum absolute input value relative to centre */ + double max_relin = 0; + int coord_in = 0; + for( coord_in = 0; coord_in < ncoord_in; coord_in++ ){ + double in = ptr_in[coord_in][point]; + if( in == AST__BAD ){ + +/* An input coord is bad, so all outputs are bad */ + int coord_out = 0; + for( coord_out = 0; coord_out < ncoord_out; coord_out++ ){ + ptr_out[coord_out][point] = AST__BAD; + } + goto forward_next_point; + } + double abs_relin = fabs(in - map->centre[coord_in]); + if( abs_relin > max_relin ){ + max_relin = abs_relin; + } + } + + if( max_relin == 0 ){ + +/* The norm is 0, so the unit vector (first nin components of output) is unknown */ + int coord = 0; + for( coord = 0; coord < ncoord_out - 1; coord++ ){ + ptr_out[coord][point] = AST__BAD; + } + ptr_out[ncoord_out - 1][point] = 0; + continue; + } + +/* All is well; compute scaled_sum as the sum of (relin/max_relin)^2 for each input + where relin = in - centre (the scaling avoids overflow), + then compute norm = max_relin * sqrt(scaled_sum) and set all outputs */ + double scaled_sum = 0; + int coord = 0; + for( coord = 0; coord < ncoord_in; coord++ ){ + double scaled_in = (ptr_in[coord][point] - map->centre[coord])/max_relin; + scaled_sum += scaled_in*scaled_in; + } + double norm = max_relin*sqrt(scaled_sum); + for( coord = 0; coord < ncoord_in; coord++ ){ + ptr_out[coord][point] = (ptr_in[coord][point] - map->centre[coord])/norm; + } + ptr_out[ncoord_out - 1][point] = norm; + + forward_next_point: ; + } + } else { + +/* Inverse transformation: unit vector, norm -> vector */ + int point = 0; + for( point = 0; point < npoint; point++ ){ + double norm = ptr_in[ncoord_in-1][point]; + if( norm == AST__BAD ){ + +/* Norm is bad for this point; set all output coords bad */ + int coord = 0; + for( coord = 0; coord < ncoord_out; coord++ ){ + ptr_out[coord][point] = AST__BAD; + } + } else if( norm == 0 ){ + +/* Norm is 0 for this point; set the output to the centre */ + int coord = 0; + for( coord = 0; coord < ncoord_out; coord++ ){ + ptr_out[coord][point] = map->centre[coord]; + } + } else { + int coord = 0; + for( coord = 0; coord < ncoord_out; coord++ ){ + double in = ptr_in[coord][point]; + if( in == AST__BAD ){ + ptr_out[coord][point] = AST__BAD; + } else { + ptr_out[coord][point] = in*norm + map->centre[coord]; + } + } + } + } + } + } + +/* 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 UnitNormMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for UnitNormMap objects. + +* Parameters: +* objin +* Pointer to the UnitNormMap to be copied. +* objout +* Pointer to the UnitNormMap being constructed. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstUnitNormMap *out; /* Pointer to output UnitNormMap */ + AstUnitNormMap *in; /* Pointer to input UnitNormMap */ + int ncoord; /* No. of axes for the mapping */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Obtain a pointer to the input and output UnitNormMaps. */ + in= (AstUnitNormMap *) objin; + out = (AstUnitNormMap *) objout; + +/* Get the number of coordinates mapped by the UnitNormMap. */ + ncoord = astGetNin( in ); + +/* Allocate memory holding copies of the centre defining the mapping. */ + out->centre = (double *) astStore( NULL, (void *) in->centre, + sizeof(double)*(size_t)ncoord ); + +/* If an error occurred, free any allocated memory. */ + if( !astOK ) { + out->centre = (double *) astFree( (void *) out->centre ); + } + +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for UnitNormMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for UnitNormMap objects. + +* Parameters: +* obj +* Pointer to the UnitNormMap 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: */ + AstUnitNormMap *this; /* Pointer to UnitNormMap */ + +/* Obtain a pointer to the UnitNormMap structure. */ + this = (AstUnitNormMap *) obj; + +/* Free the memory holding the centre. */ + this->centre = (double *) astFree( (void *) this->centre ); + +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for UnitNormMap 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 UnitNormMap class to an output Channel. + +* Parameters: +* this +* Pointer to the UnitNormMap 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: */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + char comment[ COMMENT_LEN + 1 ]; /* Buffer for comment string */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Obtain a pointer to the UnitNormMap structure. */ + AstUnitNormMap *this = (AstUnitNormMap *) this_object; + +/* Get the number of coordinates to be mapped. */ + int ncoord = astGetNin( this ); + +/* Write out values representing the instance variables for the + UnitNormMap class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* The centre. */ + int axis = 0; + for( axis = 0; axis < ncoord; axis++ ){ + (void) sprintf( buff, "Ctr%d", axis + 1 ); + (void) sprintf( comment, "Centre for axis %d", axis + 1 ); + astWriteDouble( channel, buff, (this->centre)[ axis ] != 0.0, 0, + (this->centre)[ axis ], comment ); + } + +/* Undefine macros local to this function. */ +#undef COMMENT_LEN +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAUnitNormMap and astCheckUnitNormMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(UnitNormMap,Mapping) +astMAKE_CHECK(UnitNormMap) + +AstUnitNormMap *astUnitNormMap_( int ncoord, const double centre[], const char *options, int *status, ...) { +/* +*++ +* Name: +c astUnitNormMap +f AST_UNITNORMMAP + +* Purpose: +* Create a UnitNormMap. + +* Type: +* Public function. + +* Synopsis: +c #include "unitnormmap.h" +c AstUnitNormMap *astUnitNormMap( int ncoord, const double centre[], +c const char *options, ... ) +f RESULT = AST_UNITNORMMAP( NCOORD, CENTRE, OPTIONS, STATUS ) + +* Class Membership: +* UnitNormMap constructor. + +* Description: +* This function creates a new UnitNormMap and optionally initialises its +* attributes. +* +* The forward transformation of a UnitNormMap subtracts the specified centre +* and then transforms the resulting vector to a unit vector and the vector norm. +* The output contains one more coordinate than the input: the initial +* Nin outputs are in the same order as the input; the final output is the norm. +* If the norm is 0, then the output of the forward transformation is AST__BAD +* for each component of the unit vector and 0 for the norm (the final value). +* +* The inverse transformation of a UnitNormMap multiplies each component +* of the provided vector by the provided norm and adds the specified centre. +* The output contains one fewer coordinate than the input: the initial Nin inputs +* are in the same order as the output; the final input is the norm. +* If the provided norm is 0 then the other input values are ignored, +* and the output vector is the centre. +* +* Example: if centre = [1, -1] then [5, 2] transforms to [4, 3] after subtracting the centre; +* the norm is 5, so the output is [0.8, 0.6, 5]. +* +* UnitNormMap enables radially symmetric transformations, as follows: +* - apply a UnitNormMap to produce a unit vector and norm (radius) +* - apply a one-dimensional mapping to the norm (radius), while passing the unit vector unchanged +* - apply the same UnitNormMap in the inverse direction to produce the result + +* 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). Output will include one additional coordinate. +c centre +f CENTRE( NCOORD ) = DOUBLE PRECISION (Given) +* An array containing the values to be subtracted from the input +* coordinates before computing unit vector and norm. A separate +* value must 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 UnitNormMap. 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 UnitNormMap. 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 astUnitNormMap() +f AST_UNITNORMMAP = INTEGER +* A pointer to the new UnitNormMap. + +* 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". + +*-- +*/ + +/* Get a pointer to the thread specific global data structure. */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if( !astOK ) return NULL; + +/* Initialise the UnitNormMap, allocating memory and initialising the + virtual function table as well if necessary. */ + AstUnitNormMap *new = astInitUnitNormMap( NULL, sizeof( AstUnitNormMap ), !class_init, &class_vtab, + "UnitNormMap", ncoord, centre ); + +/* 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 UnitNormMap's attributes. */ + va_list args; + 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 UnitNormMap. */ + return new; +} + +AstUnitNormMap *astUnitNormMapId_( int ncoord, const double centre[], + const char *options, ... ) { +/* +* Name: +* astUnitNormMapId_ + +* Purpose: +* Create a UnitNormMap. + +* Type: +* Private function. + +* Synopsis: +* #include "unitnormmap.h" +* AstUnitNormMap *astUnitNormMapId_( int ncoord, const double centre[], +* const char *options, ... ) + +* Class Membership: +* UnitNormMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astUnitNormMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astUnitNormMap_ 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 astUnitNormMap_ 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 astUnitNormMap_. + +* Returned Value: +* The ID value associated with the new UnitNormMap. +*/ + + 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. */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if( !astOK ) return NULL; + +/* Initialise the UnitNormMap, allocating memory and initialising the + virtual function table as well if necessary. */ + AstUnitNormMap *new = astInitUnitNormMap( NULL, sizeof( AstUnitNormMap ), !class_init, &class_vtab, + "UnitNormMap", ncoord, centre ); + +/* 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 UnitNormMap's attributes. */ + va_list args; /* Variable argument list */ + 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 UnitNormMap. */ + return astMakeId( new ); +} + +AstUnitNormMap *astInitUnitNormMap_( void *mem, size_t size, int init, + AstUnitNormMapVtab *vtab, const char *name, + int ncoord, const double *centre, int *status ) { +/* +*+ +* Name: +* astInitUnitNormMap + +* Purpose: +* Initialise a UnitNormMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "unitnormmap.h" +* AstUnitNormMap *astInitUnitNormMap( void *mem, size_t size, int init, +* AstUnitNormMapVtab *vtab, const char *name, +* int ncoord, const double *centre ) + +* Class Membership: +* UnitNormMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new UnitNormMap object. It allocates memory (if necessary) to accommodate +* the UnitNormMap plus any additional data associated with the derived class. +* It then initialises a UnitNormMap 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 UnitNormMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the UnitNormMap is to be initialised. +* This must be of sufficient size to accommodate the UnitNormMap data +* (sizeof(UnitNormMap)) 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 UnitNormMap (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 UnitNormMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the UnitNormMap'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 UnitNormMap. +* 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. +* centre +* Pointer to an array of centres, one for each coordinate. + +* Returned Value: +* A pointer to the new UnitNormMap. + +* 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: */ + AstUnitNormMap *new; /* Pointer to new UnitNormMap */ + +/* Check the global status. */ + if( !astOK ) return NULL; + +/* Check centre */ + if( ncoord <= 0 ){ + astError( AST__BADSM, "The centre must have at least one axis", status ); + return NULL; + } + +/* If necessary, initialise the virtual function table. */ + if( init ) astInitUnitNormMapVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Initialise a Mapping structure (the parent class) as the first component + within the UnitNormMap structure, allocating memory if necessary. Specify that + the Mapping should be defined in both the forward and inverse directions. */ + new = (AstUnitNormMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + ncoord, ncoord+1, 1, 1 ); + + if( astOK ) { + +/* Initialise the UnitNormMap data. */ +/* ---------------------------- */ +/* Allocate memory to hold the centre for each axis. */ + new->centre = (double *) astMalloc( sizeof(double)*(size_t)ncoord ); + +/* Check the pointers can be used */ + if( astOK ){ + +/* Store the centre for each axis. */ + int axis = 0; + for( axis = 0; axis < ncoord; axis++ ){ + (new->centre)[ axis ] = centre ? centre[ axis ] : AST__BAD; + } + + } + +/* If an error occurred, clean up by deleting the new UnitNormMap. */ + if( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new UnitNormMap. */ + return new; +} + +AstUnitNormMap *astLoadUnitNormMap_( void *mem, size_t size, + AstUnitNormMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadUnitNormMap + +* Purpose: +* Load a UnitNormMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "unitnormmap.h" +* AstUnitNormMap *astLoadUnitNormMap( void *mem, size_t size, +* AstUnitNormMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* UnitNormMap loader. + +* Description: +* This function is provided to load a new UnitNormMap 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 +* UnitNormMap 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 UnitNormMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the UnitNormMap is to be +* loaded. This must be of sufficient size to accommodate the +* UnitNormMap data (sizeof(UnitNormMap)) 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 UnitNormMap (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 UnitNormMap 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(AstUnitNormMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new UnitNormMap. If this is NULL, a pointer +* to the (static) virtual function table for the UnitNormMap 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 "UnitNormMap" is used instead. + +* Returned Value: +* A pointer to the new UnitNormMap. + +* 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: */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + +/* Get a pointer to the thread specific global data structure. */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + astGET_GLOBALS(channel); + +/* Initialise. */ + AstUnitNormMap *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 UnitNormMap. In this case the + UnitNormMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if( !vtab ) { + size = sizeof( AstUnitNormMap ); + vtab = &class_vtab; + name = "UnitNormMap"; + +/* If required, initialise the virtual function table for this class. */ + if( !class_init ) { + astInitUnitNormMapVtab( 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 UnitNormMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if( astOK ) { + +/* Get the number of axis for the mapping. */ + int ncoord = astGetNin( (AstMapping *) new ); + +/* Allocate memory to hold the centre. */ + new->centre = (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, "UnitNormMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* The centre. */ + int axis = 0; + for( axis = 0; axis < ncoord; axis++ ){ + (void) sprintf( buff, "ctr%d", axis + 1 ); + (new->centre)[ axis ] = astReadDouble( channel, buff, 0.0 ); + } + } + +/* If an error occurred, clean up by deleting the new UnitNormMap. */ + if( !astOK ) new = astDelete( new ); + +/* Return the new UnitNormMap 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/ast/unitnormmap.h b/ast/unitnormmap.h new file mode 100644 index 0000000..c18ba14 --- /dev/null +++ b/ast/unitnormmap.h @@ -0,0 +1,299 @@ +#if !defined( UNITNORMMAP_INCLUDED ) /* Include this file only once */ +#define UNITNORMMAP_INCLUDED +/* +*+ +* Name: +* unitnormmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the UnitNormMap class. + +* Invocation: +* #include "unitnormmap.h" + +* Description: +* This include file defines the interface to the UnitNormMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* A UnitNormMap is a Mapping which, in the forward direction, +* subtracts the specified centre and then transforms the resulting vector +* to a unit vector and the vector norm. +* The forward direction outputs one more coordinate than is input. +* +* The inverse transformation of a UnitNormMap multiplies each component +* of the provided vector by the provided norm and adds the specified centre. +* The forward direction outputs one fewer coordinate than is input. +* +* Example: if centre = [1, -1] then [5, 2] transforms to [4, 3] after subtracting the centre; +* the norm is 5, so the output is [0.8, 0.6, 5] +* +* UnitNormMap is intended for applying radially symmetric distortions, as follows: +* - apply a UnitNormMap to produce a unit vector and norm (radius) +* - apply some one-dimensional mapping to the norm (radius), while passing the unit vector unchanged +* - apply the same UnitNormMap in the inverse direction to produce the result +* + +* Inheritance: +* The UnitNormMap 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 UnitNormMap. +* GetAttrib +* Get an attribute value for a UnitNormMap. +* SetAttrib +* Set an attribute value for a UnitNormMap. +* TestAttrib +* Test if an attribute value has been set for a UnitNormMap. +* astMapMerge +* Simplify a sequence of Mappings containing a UnitNormMap. +* astTransform +* Apply a UnitNormMap to transform a set of points. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* None. + +* Other Class Functions: +* Public: +* astIsAUnitNormMap +* Test class membership. +* astUnitNormMap +* Create a UnitNormMap. +* +* Protected: +* astCheckUnitNormMap +* Validate class membership. +* astInitUnitNormMap +* Initialise a UnitNormMap. +* astInitUnitNormMapVtab +* Initialise the virtual function table for the UnitNormMap class. +* astLoadUnitNormMap +* Load a UnitNormMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstUnitNormMap +* UnitNormMap object type. +* +* Protected: +* AstUnitNormMapVtab +* UnitNormMap 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) 2016 University of Washington + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General 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: +* RO: Russell Owen (LSST) + +* History: +* 20-APR-2016 (RO): +* 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. */ +/* ================= */ +/* UnitNormMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstUnitNormMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double *centre; /* Pointer to array of shifts */ + +} AstUnitNormMap; + +/* 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 AstUnitNormMapVtab { + +/* 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. */ + +} AstUnitNormMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstUnitNormMapGlobals { + AstUnitNormMapVtab Class_Vtab; + int Class_Init; +} AstUnitNormMapGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitUnitNormMapGlobals_( AstUnitNormMapGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(UnitNormMap) /* Check class membership */ +astPROTO_ISA(UnitNormMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstUnitNormMap *astUnitNormMap_( int, const double [], const char *, int *, ...); +#else +AstUnitNormMap *astUnitNormMapId_( int, const double [], const char *, ... )__attribute__((format(printf,3,4))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstUnitNormMap *astInitUnitNormMap_( void *, size_t, int, AstUnitNormMapVtab *, + const char *, int, const double *, int * ); + +/* Vtab initialiser. */ +void astInitUnitNormMapVtab_( AstUnitNormMapVtab *, const char *, int * ); + +/* Loader. */ +AstUnitNormMap *astLoadUnitNormMap_( void *, size_t, AstUnitNormMapVtab *, + 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 astCheckUnitNormMap(this) astINVOKE_CHECK(UnitNormMap,this,0) +#define astVerifyUnitNormMap(this) astINVOKE_CHECK(UnitNormMap,this,1) + +/* Test class membership. */ +#define astIsAUnitNormMap(this) astINVOKE_ISA(UnitNormMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astUnitNormMap astINVOKE(F,astUnitNormMap_) +#else +#define astUnitNormMap astINVOKE(F,astUnitNormMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define \ +astInitUnitNormMap(mem,size,init,vtab,name,ncoord,centre) \ +astINVOKE(O,astInitUnitNormMap_(mem,size,init,vtab,name,ncoord,centre,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitUnitNormMapVtab(vtab,name) astINVOKE(V,astInitUnitNormMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadUnitNormMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadUnitNormMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckUnitNormMap to validate UnitNormMap 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/ast/version.h.in b/ast/version.h.in new file mode 100644 index 0000000..49d6643 --- /dev/null +++ b/ast/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/ast/wcsmap.c b/ast/wcsmap.c new file mode 100644 index 0000000..6ba3811 --- /dev/null +++ b/ast/wcsmap.c @@ -0,0 +1,6094 @@ +/* +*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. +* 30-DEC-2017 (DSB): +* Improve merging of WcsMaps and PermMaps. +*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 *, AstWcsMap **, 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, AstWcsMap **newwcsmap, 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, AstWcsMap **newwcssmap ) + +* 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. +* newwcsmap +* Addresss of a location at which to return a pointer to the +* WcsMap that would be produced if the two Mappings were swapped. +* Returned holding NULL if the supplied Mappings cannot be swapped. + +* 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 *maps[2]; /* Pointer to Mappign list */ + 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 iwm; /* Index of WcsMap within "maps" */ + 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 */ + +/* Initialise */ + ret = 0; + *simpler = 0; + *newwcsmap = NULL; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* 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" ) ){ + iwm = 0; + wcs = (AstWcsMap *) map1; + nowcs = map2; + nowcs_class = class2; + } else { + iwm = 1; + 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 ] ); + +/* If the Mappings can swap, get the equivalent swapped mappings. */ + if( ret ) { + maps[ 0 ] = astClone( map1 ); + maps[ 1 ] = astClone( map2 ); + invert[ 0 ] = inv1; + invert[ 1 ] = inv2; + WcsPerm( maps, invert, iwm, status ); + +/* Return a pointer to the swapped WcsMap. */ + *newwcsmap = astClone( maps[ 1 - iwm ] ); + +/* Free resources */ + maps[ 0 ] = astAnnul( maps[ 0 ] ); + maps[ 1 ] = astAnnul( maps[ 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", AST__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", AST__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", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* NatLon */ +/* ====== */ + } else if ( !strcmp( attrib, "natlon" ) ) { + dval = astGetNatLon( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__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 */ + AstWcsMap *newwcsmap; /* The WcsMap after swapping */ + 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, + &newwcsmap, status ); + } else { + do2 = 0; + swaphi = 0; + newwcsmap = NULL; + } + +/* 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 ], + (AstMapping *) newwcsmap, astGetInvert(newwcsmap), 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; + } + } + } + + if( newwcsmap ) newwcsmap = astAnnul( newwcsmap ); + +/* 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, + &newwcsmap, status ); + } else { + do1 = 0; + swaplo = 0; + newwcsmap = NULL; + } + + nstep1 = -1; + if( swaplo ){ + for( i1 = where - 1; i1 >= 0; i1-- ){ + + if( CanMerge( ( *map_list )[ i1 ], ( *invert_list )[ i1 ], + (AstMapping *) newwcsmap, astGetInvert(newwcsmap), status ) ) { + nstep1 = where - 1 - i1; + break; + } + + nclass = astGetClass( ( *map_list )[ i1 ] ); + if( strcmp( nclass, "PermMap" ) ) { + break; + } + } + } + + if( newwcsmap ) newwcsmap = astAnnul( newwcsmap ); + +/* 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/ast/wcsmap.h b/ast/wcsmap.h new file mode 100644 index 0000000..16ec3a7 --- /dev/null +++ b/ast/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/ast/wcsmath.h b/ast/wcsmath.h new file mode 100644 index 0000000..1f8977a --- /dev/null +++ b/ast/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/ast/wcstrig.c b/ast/wcstrig.c new file mode 100644 index 0000000..d3ba400 --- /dev/null +++ b/ast/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/ast/wcstrig.h b/ast/wcstrig.h new file mode 100644 index 0000000..36463bb --- /dev/null +++ b/ast/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/ast/winmap.c b/ast/winmap.c new file mode 100644 index 0000000..5ba97d5 --- /dev/null +++ b/ast/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/ast/winmap.h b/ast/winmap.h new file mode 100644 index 0000000..fd05066 --- /dev/null +++ b/ast/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/ast/xml.c b/ast/xml.c new file mode 100644 index 0000000..f14cb22 --- /dev/null +++ b/ast/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 to 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/ast/xml.h b/ast/xml.h new file mode 100644 index 0000000..bbd0c89 --- /dev/null +++ b/ast/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/ast/xmlchan.c b/ast/xmlchan.c new file mode 100644 index 0000000..a03e252 --- /dev/null +++ b/ast/xmlchan.c @@ -0,0 +1,14120 @@ +/* +*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[ AST__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, AST__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", AST__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", AST__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", AST__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 ); + } + } + +/* 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 ); + } + } + +/* 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