SUBROUTINE SPRESP( STATUS ) *+ * Name: * SPRESP * Purpose: * Attach an ASTERIX spatial response structure to either a binned or * event dataset. * Language: * FORTRAN * Type of Module: * ADAM A-task * Invocation: * CALL SPRESP( STATUS ) * Arguments: * STATUS = INTEGER (Given and Returned) * The global status. * Description: * Attach a spatial response structure to a dataset. A spatial response * is stored as a function of X,Y offset from the source position, in * either pixel centred or vertex centred form, as a function of detector * position and/or energy. * Usage: * {routine_name} {parameter_usage} * ADAM Parameters: * INP = UNIV (Read) * Input dataset to which spatial response will be attached * RADIAL = LOGICAL (Read) * Is the psf simply a function of off-axis angle, rather than X and Y * PIXCENT = LOGICAL (Read) * Pixel centred, or vertex centred psf arrays * CUTOFF = REAL (Read) * Psf amplitude at which the psf is cut-off radially * RLIMIT = INTEGER (Read) * Limiting maximum radius of the psf * NEBIN = INTEGER (Read) * Number of samples in energy. * Examples: * {routine_example_text} * {routine_example_description} * Pitfalls: * {pitfall_description}... * Notes: * {routine_notes}... * Prior Requirements: * {routine_prior_requirements}... * Side Effects: * {routine_side_effects}... * Algorithm: * {algorithm_description}... * Accuracy: * {routine_accuracy} * Timing: * {routine_timing} * Implementation Status: * {routine_implementation_status} * External Routines Used: * {name_of_facility_or_package}: * {routine_used}... * Implementation Deficiencies: * {routine_deficiencies}... * References: * {routine_references}... * Keywords: * {routine_keywords}... * Copyright: * {routine_copyright} * Authors: * DJA: David J. Allan (ROSAT) * {enter_new_authors_here} * History: * 21 Apr 1993 V1.7-0 (DJA): * Original version. * 20 Jan 1994 V1.7-1 (DJA): * Fixed locator annulling bug. * 21 Jan 1994 V1.7-2 (DJA): * Fixed bug in compression algorithm. * 3 Mar 1994 V1.7-3 (DJA): * Write first 2 axis attributes. * 17 Aug 1994 V1.7-4 (DJA): * Reduces expanded dimensions to minimum required * 25 Nov 1994 V1.8-0 (DJA): * User interface now uses only USI. * 25 Apr 1995 V1.8-1 (DJA): * New data interfaces. * 31 Jul 1995 V2.0-0 (DJA): * Allow user to control energy resolution using NEBIN * 1 Aug 1995 V2.0-1 (DJA): * Fixed bug in rectangular mode where output units were * not written in radians * 3 Oct 1995 V2.0-2 (DJA): * Fixed another bug in rectangular mode, where Y was zero * going into PSF_2D_DATA * 16 Oct 1995 V2.0-3 (DJA): * Use new BDI_ routines to read and write data * 8 Jan 1996 V2.0-4 (DJA): * Fixed bug in energy axis copying * 13 May 1996 V2.0-5 (DJA): * Added perturbation option * {enter_changes_here} * Bugs: * {note_any_bugs_here} *- * Type Definitions: IMPLICIT NONE ! No implicit typing * Global Constants: INCLUDE 'SAE_PAR' ! Standard SAE constants INCLUDE 'DAT_PAR' ! Standard HDS constants INCLUDE 'PRM_PAR' ! Standard PRM constants INCLUDE 'ADI_PAR' ! Asterix ADI constants INCLUDE 'PSF_PAR' ! Asterix PSF constants INCLUDE 'MATH_PAR' ! Asterix MATH constants * Status: INTEGER STATUS ! Global status * External references: EXTERNAL CHR_LEN INTEGER CHR_LEN * Local Constants: CHARACTER*(DAT__SZNAM) SRESP ! Spatial response object name PARAMETER ( SRESP = 'SPATIAL_RESP' ) CHARACTER*30 VERSION PARAMETER ( VERSION = 'SPRESP Version 2.0-5' ) * Local Variables: CHARACTER*(DAT__SZLOC) ALOC ! Input ASTERIX structure CHARACTER*40 EAXLAB ! Energy axis label CHARACTER*(DAT__SZLOC) SLOC ! SPATIAL_RESP object CHARACTER*40 UNITS ! Axis units REAL CUTOFF ! Cutoff amplitude REAL DX, DY ! Psf data bin widths in radians REAL E, R, X, Y ! REAL EBASE, ESCALE ! Energy axis data REAL ELO, EHI ! Energy axis extrema REAL ERES ! Spectral resolution REAL MAXR ! Maximum off-axis angle REAL PXSCALE, PYSCALE REAL SPARR(2) ! Space array data REAL SRES ! Spatial resolution REAL TOR ! Units to radians conversion REAL XBASE, XSCALE ! X axis attributes REAL XLO, XHI ! X axis extrema REAL YBASE, YSCALE ! Y axis attributes REAL YLO, YHI ! Y axis extrema INTEGER BIID ! Output response id INTEGER CDIMS(4) ! Dimensions of response index INTEGER CNDIM ! Dimensionality of response i'x INTEGER CNELM ! Total no. elements in index INTEGER CP_PTR ! Cursor over spatial response INTEGER NDIM, DIMS(5) ! Response dimensions INTEGER EBIN ! Loop over energy axis INTEGER EPTR ! Ptr to energy bin centres INTEGER I ! Loop variable INTEGER IDIMS(ADI__MXDIM),INDIM ! Dimensions of input file INTEGER IDUM ! Dummy argument INTEGER IFID ! Input dataset id INTEGER IPSF ! PSF handle INTEGER NPSF ! Number of psfs in response INTEGER NUSED ! Length of compressed response INTEGER ONEBIN ! O/p # energy bins INTEGER PDPTR ! Perturbation data INTEGER PFID ! Perturbation file INTEGER PDIMS(2), PNDIM ! Perturb file shape INTEGER PNRAD ! Perturb polar bins INTEGER POPT ! Perturbation option INTEGER PSFSIZ ! Psf size in bytes INTEGER PWPTR ! Perturbation workspace INTEGER RBIN ! Loop over off-axis angle INTEGER RLIMIT ! Limiting psf radius INTEGER SI_PTR ! Spatial reponse index INTEGER SNELM ! Total # elements in response INTEGER SP_PTR ! Full spatial reponse INTEGER X_AX,Y_AX,E_AX,T_AX ! Axis numbers INTEGER XBIN, YBIN ! Loop over detector X, Y axes INTEGER XSMAX, YSMAX ! Extreme psf sizes LOGICAL EVDS ! Input is event dataset? LOGICAL H_OK ! Psf system do a hint? LOGICAL PERTURB ! Perturb psf? LOGICAL PIXCENT ! Pixel centred? LOGICAL RADIAL ! Psf is only function of R LOGICAL THERE ! Component exists? *. * Check inherited global status. IF ( STATUS .NE. SAI__OK ) RETURN * Initialise ASTERIX CALL AST_INIT() * Version annoucement CALL MSG_PRNT( VERSION ) * Get dataset CALL USI_ASSOC( 'INP', 'BinDS|EventDS', 'UPDATE', IFID, STATUS ) * Does ASTERIX structure exist? If not, create it CALL ADI1_LOCAST( IFID, .TRUE., ALOC, STATUS ) IF ( STATUS .NE. SAI__OK ) GOTO 99 * Remove existing SPATIAL_RESP object CALL DAT_THERE( ALOC, SRESP, THERE, STATUS ) IF ( THERE ) THEN CALL MSG_PRNT( 'Removing existing spatial response...' ) CALL DAT_ERASE( ALOC, SRESP, STATUS ) END IF * Introduce dataset to psf system CALL PSF_ASSOCI( IFID, IPSF, STATUS ) IF ( STATUS .NE. SAI__OK ) GOTO 99 * Create spatial response structure CALL DAT_NEW( ALOC, SRESP, 'EXTENSION', 0, 0, STATUS ) CALL DAT_FIND( ALOC, SRESP, SLOC, STATUS ) CALL DAT_NEW( SLOC, 'MORE', 'EXTENSION', 0, 0, STATUS ) * Import locator to make a file object CALL ADI1_MKFILE( SLOC, 'UPDATE', BIID, STATUS ) * Write SPRESP version id CALL HDX_PUTC( SLOC, 'VERSION', 1, VERSION, STATUS ) * Is RADIAL_SYMMETRY hint available from the psf? If it is use it as * the default for the RADIAL parameter CALL PSF_QHINT( IPSF, PSF_H_RADSYM, H_OK, RADIAL, STATUS ) IF ( H_OK ) THEN CALL USI_DEF0L( 'RADIAL', RADIAL, STATUS ) END IF * Assume radial symmetry CALL USI_GET0L( 'RADIAL', RADIAL, STATUS ) IF ( STATUS .NE. SAI__OK ) GOTO 99 CALL HDX_PUTL( SLOC, 'RADIAL', 1, RADIAL, STATUS ) IF ( RADIAL ) THEN NDIM = 4 ELSE NDIM = 5 END IF * Identify the axis numbers of the dataset CALL PSF_QAXES( IPSF, X_AX, Y_AX, E_AX, T_AX, STATUS ) * Energy information available in dataset, ie. is there an energy axis * for the binned dataset, or an energy list for the event dataset? * Spatial resolution required CALL BDI_AXGET0C( IFID, X_AX, 'Units', UNITS, STATUS ) CALL CONV_UNIT2R( UNITS, TOR, STATUS ) IF ( RADIAL ) THEN CALL USI_PROMT( 'SRES', 'Radial sampling of psf in '/ : /UNITS(:CHR_LEN(UNITS)), STATUS ) ELSE CALL USI_PROMT( 'SRES', 'X,Y sampling of psf in '/ : /UNITS(:CHR_LEN(UNITS)), STATUS ) END IF CALL USI_GET0R( 'SRES', SRES, STATUS ) IF ( STATUS .NE. SAI__OK ) GOTO 99 * Only require energy resolution if event dataset, otherwise we use * the full energy variation CALL ADI_DERVD( IFID, 'EventDS', EVDS, STATUS ) IF ( EVDS ) THEN CALL USI_GET0R( 'ERES', ERES, STATUS ) IF ( STATUS .NE. SAI__OK ) GOTO 99 ELSE * Get data dimensions CALL BDI_GETSHP( IFID, ADI__MXDIM, IDIMS, INDIM, STATUS ) * Map energy if defined IF ( E_AX .GT. 0 ) THEN * Get axis label CALL BDI_AXGET0C( IFID, E_AX, 'Label', EAXLAB, STATUS ) * Get number of output energy bins from user CALL USI_DEF0I( 'NEBIN', IDIMS(E_AX), STATUS ) CALL USI_GET0I( 'NEBIN', ONEBIN, STATUS ) IF ( STATUS .NE. SAI__OK ) GOTO 99 * Ensure number is less than or equal to the number of input energy bins ONEBIN = MIN( IDIMS(E_AX), ONEBIN ) * If user is happy with the default energy resolution, just map * the input axis IF ( ONEBIN .EQ. IDIMS(E_AX) ) THEN CALL BDI_AXMAPR( IFID, E_AX, 'Data', 'READ', EPTR, STATUS ) ELSE * Otherwise extract axis extrema and calculate base and scale CALL PSF_QAXEXT( IPSF, E_AX, ELO, EHI, STATUS ) ESCALE = (EHI-ELO) / REAL(ONEBIN) EBASE = ELO + ESCALE/2.0 END IF END IF * Extract X,Y pixel sizes CALL BDI_AXGET1R( IFID, X_AX, 'SpacedData', 2, SPARR, IDUM, : STATUS ) XBASE = SPARR(1) XSCALE = SPARR(2) CALL BDI_AXGET1R( IFID, Y_AX, 'SpacedData', 2, SPARR, IDUM, : STATUS ) YBASE = SPARR(1) YSCALE = SPARR(2) * Psf bin widths in radians DX = XSCALE * TOR DY = YSCALE * TOR END IF * Decide number of psfs in each dimension X,Y (or R), and E IF ( EVDS ) THEN END IF * Pixel centred or vertex centred array CALL USI_GET0L( 'PIXCENT', PIXCENT, STATUS ) CALL HDX_PUTL( SLOC, 'PIXCENT', 1, PIXCENT, STATUS ) * Fractional amplitude to cut-off CALL USI_GET0R( 'CUTOFF', CUTOFF, STATUS ) CALL HDX_PUTR( SLOC, 'CUTOFF', 1, CUTOFF, STATUS ) * Limiting radius CALL USI_GET0I( 'RLIMIT', RLIMIT, STATUS ) CALL HDX_PUTI( SLOC, 'RLIMIT', 1, RLIMIT, STATUS ) IF ( STATUS .NE. SAI__OK ) GOTO 99 * The fact that this a compressed response CALL HDX_PUTL( SLOC, 'COMPRESSED', 1, .TRUE., STATUS ) * Define dimensions of response DIMS(1) = RLIMIT*2 DIMS(2) = RLIMIT*2 IF ( PIXCENT ) THEN DIMS(1) = DIMS(1) + 1 DIMS(2) = DIMS(2) + 1 END IF XLO = XBASE - XSCALE XHI = XLO + XSCALE*(IDIMS(X_AX)+2) YLO = YBASE - YSCALE YHI = YLO + YSCALE*(IDIMS(Y_AX)+2) IF ( RADIAL ) THEN MAXR = SQRT( MAX(XLO*XLO+YLO*YLO,XLO*XLO+YHI*YHI, : XHI*XHI+YLO*YLO,XHI*XHI+YHI*YHI) ) DIMS(3) = INT(MAXR/SRES) + 1 CALL MSG_SETI( 'NR', DIMS(3) ) CALL MSG_PRNT( 'There will be ^NR radial bins' ) ELSE DIMS(3) = INT(ABS(XHI - XLO)/SRES) + 1 DIMS(4) = INT(ABS(YHI - YLO)/SRES) + 1 PXSCALE = SIGN(SRES,XSCALE) PYSCALE = SIGN(SRES,YSCALE) END IF IF ( E_AX .GT. 0 ) THEN DIMS(NDIM) = ONEBIN ELSE DIMS(NDIM) = 1 END IF CALL ARR_SUMDIM( NDIM, DIMS, SNELM ) * The spatial response is just a collection of NPSF 2D psfs NPSF = SNELM / (DIMS(1)*DIMS(2)) * Create BinDS object and link it to the response locator CALL BDI_LINK( 'BinDS', NDIM, DIMS, 'REAL', BIID, STATUS ) * Write some axes CALL BDI_PUT0C( BIID, 'Label', 'Psf amplitude', STATUS ) CALL BDI_PUT0C( BIID, 'Units', 'Integrated probability per pixel', : STATUS ) CALL BDI_AXPUT0C( BIID, 1, 'Label', 'X offset from source', : STATUS ) CALL BDI_AXPUT0C( BIID, 1, 'Units', UNITS, STATUS ) SPARR(1) = (0.0-DX*DIMS(1))/TOR SPARR(2) = DX/TOR CALL BDI_AXPUT1R( BIID, 1, 'SpacedData', 2, SPARR, STATUS ) CALL BDI_AXPUT0C( BIID, 2, 'Label', 'Y offset from source', : STATUS ) SPARR(1) = (0.0-DY*DIMS(2))/TOR SPARR(2) = DY/TOR CALL BDI_AXPUT1R( BIID, 2, 'SpacedData', 2, SPARR, STATUS ) CALL BDI_AXPUT0C( BIID, 2, 'Units', UNITS, STATUS ) IF ( RADIAL ) THEN CALL BDI_AXPUT0C( BIID, 3, 'Label', 'Off-axis angle', STATUS ) CALL BDI_AXPUT0C( BIID, 3, 'Units', 'radian', STATUS ) SPARR(1) = SRES*TOR/2.0 SPARR(2) = SRES*TOR CALL BDI_AXPUT1R( BIID, 3, 'SpacedData', 2, SPARR, STATUS ) ELSE CALL BDI_AXPUT0C( BIID, 3, 'Label', 'X_CORR', STATUS ) CALL BDI_AXPUT0C( BIID, 3, 'Units', 'radian', STATUS ) SPARR(1) = XBASE*TOR SPARR(2) = PXSCALE*TOR CALL BDI_AXPUT1R( BIID, 3, 'SpacedData', 2, SPARR, STATUS ) CALL BDI_AXPUT0C( BIID, 4, 'Label', 'Y_CORR', STATUS ) CALL BDI_AXPUT0C( BIID, 4, 'Units', 'radian', STATUS ) SPARR(1) = YBASE*TOR SPARR(2) = PYSCALE*TOR CALL BDI_AXPUT1R( BIID, 4, 'SpacedData', 2, SPARR, STATUS ) END IF * Copy energy axis if present IF ( (E_AX.GT.0) .AND. (ONEBIN .EQ. IDIMS(E_AX)) ) THEN CALL BDI_AXCOPY( IFID, E_AX, ' ', BIID, NDIM, STATUS ) ELSE IF ( E_AX .GT. 0 ) THEN CALL BDI_AXPUT0C( BIID, NDIM, 'Label', EAXLAB, STATUS ) CALL BDI_AXCOPY( IFID, E_AX, 'Units', BIID, NDIM, STATUS ) SPARR(1) = EBASE SPARR(2) = ESCALE CALL BDI_AXPUT1R( BIID, NDIM, 'SpacedData', 2, SPARR, STATUS ) ELSE CALL BDI_AXPUT0C( BIID, NDIM, 'Label', 'Energy bin number', : STATUS ) SPARR(1) = 1.0 SPARR(2) = 1.0 CALL BDI_AXPUT1R( BIID, NDIM, 'SpacedData', 2, SPARR, STATUS ) END IF * Perturb the psfs? CALL USI_GET0L( 'PERTURB', PERTURB, STATUS ) IF ( STATUS .NE. SAI__OK ) GOTO 99 IF ( PERTURB ) THEN * Get perturbation option CALL USI_GET0I( 'POPT', POPT, STATUS ) IF ( (POPT.LT.1) .OR. (POPT.GT.2) ) THEN STATUS = SAI__ERROR CALL ERR_REP( ' ', 'Illegal perturbation option', STATUS ) GOTO 99 END IF * Get perturbation control IF ( POPT .EQ. 1 ) THEN CALL USI_PROMT( 'PFILE', 'Enclosed energy redistribution '/ : /'function', STATUS ) ELSE IF ( POPT .EQ. 2 ) THEN CALL USI_PROMT( 'PFILE', 'Smoothing kernel', STATUS ) END IF CALL USI_ASSOC( 'PFILE', 'BinDS', 'READ', PFID, STATUS ) * Get perturbation data CALL BDI_GETSHP( PFID, POPT, PDIMS, PNDIM, STATUS ) IF ( POPT .EQ. 1 ) THEN CALL BDI_MAPR( PFID, 'Data', 'READ', PDPTR, STATUS ) PNRAD = DIMS(1)/2 + 1 ELSE IF ( POPT .EQ. 2 ) THEN IF ( ((PDIMS(1)/2)*2 .EQ. PDIMS(1)) .OR. : ((PDIMS(2)/2)*2 .EQ. PDIMS(2)) ) THEN STATUS = SAI__ERROR CALL ERR_REP( ' ', 'Both dimensions of smoothing mask '/ : /'should be odd', STATUS ) END IF CALL BDI_MAPR( PFID, 'Data', 'READ', PDPTR, STATUS ) CALL DYN_MAPR( 2, DIMS, PWPTR, STATUS ) END IF * No perturbation ELSE POPT = 0 END IF IF ( STATUS .NE. SAI__OK ) GOTO 99 * Map space for expanded spatial response structure CALL DYN_MAPR( NDIM, DIMS, SP_PTR, STATUS ) * Fill it with data PSFSIZ = DIMS(1)*DIMS(2)*VAL__NBR CP_PTR = SP_PTR * Loop over energy DO EBIN = 1, DIMS(NDIM) * Define energy band if appropriate IF ( E_AX .GT. 0 ) THEN * Extract EBIN'th energy bin IF ( ONEBIN .EQ. IDIMS(E_AX) ) THEN CALL ARR_ELEM1R( EPTR, ONEBIN, EBIN, E, STATUS ) ELSE E = EBASE + REAL(EBIN-1)*ESCALE END IF * Define this energy band CALL PSF_DEF( IPSF, 0.0D0, 0.0D0, NINT(E), NINT(E), 0, 0, : STATUS ) * Announce to user CALL MSG_SETR( 'ENERGY', E ) CALL MSG_SETC( 'AXLAB', EAXLAB ) CALL MSG_PRNT( 'Doing ^AXLAB band ^ENERGY' ) END IF * Radial symmetry? IF ( RADIAL ) THEN * Loop over radial bins DO RBIN = 1, DIMS(3) * This radius in radians R = (REAL(RBIN)-1.0) * SRES * TOR * Evaluate the psf CALL PSF_2D_DATA( IPSF, R, 0.0, 0.0, 0.0, DX, DY, .TRUE., : DIMS(1), DIMS(2), %VAL(CP_PTR), STATUS ) * Perturb it? IF ( POPT .EQ. 1 ) THEN CALL SPRESP_PERT_EN( PDIMS(1), %VAL(PDPTR), PNRAD, : DIMS(1), : DIMS(2), %VAL(CP_PTR), STATUS ) ELSE IF ( POPT .EQ. 2 ) THEN CALL SPRESP_PERT_SM( PDIMS(1), PDIMS(2), %VAL(PDPTR), : %VAL(PWPTR), DIMS(1), : DIMS(2), %VAL(CP_PTR), STATUS ) END IF * Advance to next psf CP_PTR = CP_PTR + PSFSIZ END DO * Cartesian grid ELSE * Loop over Y psf bins DO YBIN = 1, DIMS(3) * This Y position in radians Y = (YBASE + (REAL(YBIN)-1.0) * PYSCALE) * TOR * Loop over X psf bins DO XBIN = 1, DIMS(4) * This X position in radians X = (XBASE + (REAL(XBIN)-1.0) * PXSCALE) * TOR * Evaluate the psf CALL PSF_2D_DATA( IPSF, X, Y, 0.0, 0.0, DX, DY, .TRUE., : DIMS(1), DIMS(2), %VAL(CP_PTR), STATUS ) * Perturb it? IF ( POPT .EQ. 1 ) THEN CALL SPRESP_PERT_EN( PDIMS(1), %VAL(PDPTR), PNRAD, : DIMS(1), : DIMS(2), %VAL(CP_PTR), STATUS ) ELSE IF ( POPT .EQ. 2 ) THEN CALL SPRESP_PERT_SM( PDIMS(1), PDIMS(2), %VAL(PDPTR), : %VAL(PWPTR), DIMS(1), : DIMS(2), %VAL(CP_PTR), STATUS ) END IF * Advance to next psf CP_PTR = CP_PTR + PSFSIZ END DO END DO END IF END DO * The index for the compressed data has 1 dimension less than the full * spatial response. The first dimension has size 3 holding, 1) the * start point in the compressed file, 2) number of bins in X and 3) * number of bins in Y. CDIMS(1) = 3 DO I = 3, NDIM CDIMS(I-1) = DIMS(I) END DO CNDIM = NDIM - 1 CALL DAT_NEW( SLOC, 'INDEX', '_INTEGER', CNDIM, CDIMS, STATUS ) CALL CMP_MAPV( SLOC, 'INDEX', '_INTEGER', 'WRITE', SI_PTR, CNELM, : STATUS ) * Compress given CUTOFF. We process the array in memory sequential order * which means that we can overwrite the SP_PTR array. CALL SPRESP_COMP( DIMS(1), DIMS(2), NPSF, %VAL(SP_PTR), : CUTOFF, %VAL(SI_PTR), %VAL(SP_PTR), : NUSED, STATUS ) * Find maximum sizes used in compressed index. Adjust expanded dimensions so * fit the largest psf in the index. Doesn't save any disk space but saves * memory in the psf system when the response has to be uncompressed CALL SPRESP_SQSH( NPSF, %VAL(SI_PTR), XSMAX, YSMAX, STATUS ) DIMS(1) = XSMAX*2 DIMS(2) = YSMAX*2 IF ( PIXCENT ) THEN DIMS(1) = DIMS(1) + 1 DIMS(2) = DIMS(2) + 1 END IF * Write the expanded dimensions CALL HDX_PUTI( SLOC, 'DIMS', NDIM, DIMS, STATUS ) * Report compression factor CALL MSG_SETR( 'FAC', REAL(SNELM)/REAL(NUSED) ) CALL MSG_PRNT( 'Response compressed by a factor ^FAC' ) * Unmap the index CALL CMP_UNMAP( SLOC, 'INDEX', STATUS ) * Write compressed data to file CALL HDX_PUTR( SLOC, 'DATA_ARRAY', NUSED, %VAL(SP_PTR), STATUS ) * Add a bit of history CALL HSI_ADD( IFID, VERSION, STATUS ) * Tidy up 99 CALL AST_CLOSE() CALL AST_ERR( STATUS ) END SUBROUTINE SPRESP_COMP( NX, NY, NPSF, PDATA, CUTOFF, INDEX, : OUT, NOUT, STATUS ) *+ * Name: * SPRESP_COMP * Purpose: * Compress a spatial response by trimming those areas whose value falls * below CUTOFF times the maximum value in the psf. * Language: * FORTRAN * Arguments: * STATUS = INTEGER (Given and Returned) * The global status. * Description: * Attach a spatial response structure to a dataset. A spatial response * is stored as a function of X,Y offset from the source position, in * either pixel centred or vertex centred form, as a function of detector * position and/or energy. * Usage: * {routine_name} {parameter_usage} * Examples: * {routine_example_text} * {routine_example_description} * Pitfalls: * {pitfall_description}... * Notes: * {routine_notes}... * Prior Requirements: * {routine_prior_requirements}... * Side Effects: * {routine_side_effects}... * Algorithm: * {algorithm_description}... * Accuracy: * {routine_accuracy} * Timing: * {routine_timing} * Implementation Status: * {routine_implementation_status} * External Routines Used: * {name_of_facility_or_package}: * {routine_used}... * Implementation Deficiencies: * {routine_deficiencies}... * References: * {routine_references}... * Keywords: * {routine_keywords}... * Copyright: * {routine_copyright} * Authors: * DJA: David J. Allan (ROSAT) * {enter_new_authors_here} * History: * 21-Apr-93 (DJA): * V1.7-0 Original version. * {enter_changes_here} * Bugs: * {note_any_bugs_here} *- * Type Definitions: IMPLICIT NONE ! No implicit typing * Global Constants: INCLUDE 'SAE_PAR' ! Standard SAE constants INCLUDE 'PRM_PAR' ! Standard PRM constants * Status: INTEGER STATUS ! Global status * Given : INTEGER NX, NY ! Dimensions of psf data INTEGER NPSF ! Number of psfs REAL PDATA(NX,NY,NPSF) ! The psf data REAL CUTOFF ! Amplitude cut-off * Returned : INTEGER INDEX(3,NPSF) ! REAL OUT(*) ! Compressed output INTEGER NOUT ! Length of compressed output * Local Variables: REAL AMAX, ACUT INTEGER I, J, IPSF INTEGER NEMPTY ! Number of empty psfs INTEGER XMIN, XMAX, XOFF INTEGER YMIN, YMAX, YOFF *. * Check inherited global status. IF ( STATUS .NE. SAI__OK ) RETURN * Loop over psfs NOUT = 0 NEMPTY = 0 DO IPSF = 1, NPSF * Find maximum psf value AMAX = VAL__MINR DO J = 1, NY DO I = 1, NX IF ( PDATA(I,J,IPSF) .GT. AMAX ) AMAX = PDATA(I,J,IPSF) END DO END DO * Check in case psf is zero IF ( AMAX .GT. 0.0 ) THEN * This defines the cut-off amplitude ACUT = AMAX*CUTOFF * Find the bounds in the psf data which must be included because * the data is above the cut. XMAX = VAL__MINI YMAX = VAL__MINI XMIN = VAL__MAXI YMIN = VAL__MAXI DO J = 1, NY DO I = 1, NX IF ( PDATA(I,J,IPSF) .GT. ACUT ) THEN XMAX = MAX(XMAX,I) YMAX = MAX(YMAX,J) XMIN = MIN(XMIN,I) YMIN = MIN(YMIN,J) END IF END DO END DO * Coerce bounds to be symmetrical about psf centre INDEX(1,IPSF) = NOUT + 1 INDEX(2,IPSF) = (NX - 2*MIN(XMIN-1,NX-XMAX)) INDEX(3,IPSF) = (NY - 2*MIN(YMIN-1,NY-YMAX)) * Write this section of data to the output XOFF = (NX - INDEX(2,IPSF))/2 YOFF = (NY - INDEX(3,IPSF))/2 DO J = 1, INDEX(3,IPSF) DO I = 1, INDEX(2,IPSF) NOUT = NOUT + 1 OUT(NOUT) = PDATA(I+XOFF,J+YOFF,IPSF) END DO END DO * Store psf half-widths INDEX(2,IPSF) = INDEX(2,IPSF) / 2 INDEX(3,IPSF) = INDEX(3,IPSF) / 2 * Empty psf markers ELSE INDEX(1,IPSF) = -1 INDEX(2,IPSF) = 0 INDEX(3,IPSF) = 0 NEMPTY = NEMPTY + 1 END IF END DO * Warn user if any empty psfs IF ( NEMPTY .GT. 0 ) THEN CALL MSG_SETI( 'N', NEMPTY ) CALL MSG_PRNT( '** WARNING - There were ^N blank psfs in'/ : /' this response **' ) END IF END SUBROUTINE SPRESP_SQSH( NPSF, INDEX, XMAX, YMAX, STATUS ) *+ * Name: * SPRESP_SQSH * Purpose: * Scans a spatial response index to find the maximum required psf * extent. Can be used to adjust the expanded reponse dimensions. * Language: * FORTRAN * Arguments: * STATUS = INTEGER (Given and Returned) * The global status. * Description: * * Usage: * {routine_name} {parameter_usage} * Examples: * {routine_example_text} * {routine_example_description} * Pitfalls: * {pitfall_description}... * Notes: * {routine_notes}... * Prior Requirements: * {routine_prior_requirements}... * Side Effects: * {routine_side_effects}... * Algorithm: * {algorithm_description}... * Accuracy: * {routine_accuracy} * Timing: * {routine_timing} * Implementation Status: * {routine_implementation_status} * External Routines Used: * {name_of_facility_or_package}: * {routine_used}... * Implementation Deficiencies: * {routine_deficiencies}... * References: * {routine_references}... * Keywords: * {routine_keywords}... * Copyright: * {routine_copyright} * Authors: * DJA: David J. Allan (ROSAT) * {enter_new_authors_here} * History: * 21-Apr-93 (DJA): * V1.7-0 Original version. * {enter_changes_here} * Bugs: * {note_any_bugs_here} *- * Type Definitions: IMPLICIT NONE ! No implicit typing * Global Constants: INCLUDE 'SAE_PAR' ! Standard SAE constants INCLUDE 'PRM_PAR' ! Standard PRM constants * Status: INTEGER STATUS ! Global status * Given : INTEGER NPSF ! Number of psfs INTEGER INDEX(3,NPSF) ! Compressed index * Returned : INTEGER XMAX, YMAX ! Extreme sizes * Local Variables: INTEGER IPSF ! Loop over psfs *. * Check inherited global status. IF ( STATUS .NE. SAI__OK ) RETURN * Initialise XMAX = 0 YMAX = 0 * Loop over psfs DO IPSF = 1, NPSF * Update maxima XMAX = MAX( XMAX, INDEX(2,IPSF) ) YMAX = MAX( YMAX, INDEX(3,IPSF) ) END DO END SUBROUTINE SPRESP_PERT_SM( PNX, PNY, PDATA, PWORK, NX, NY, : PSF, STATUS ) *+ * Name: * SPRESP_PERT_SM * Purpose: * Perturbs a psf by smoothing it. The normalisation of the original psf * is NOT retained. * Language: * FORTRAN * Arguments: * STATUS = INTEGER (Given and Returned) * The global status. * Description: * * Usage: * {routine_name} {parameter_usage} * Examples: * {routine_example_text} * {routine_example_description} * Pitfalls: * {pitfall_description}... * Notes: * {routine_notes}... * Prior Requirements: * {routine_prior_requirements}... * Side Effects: * {routine_side_effects}... * Algorithm: * {algorithm_description}... * Accuracy: * {routine_accuracy} * Timing: * {routine_timing} * Implementation Status: * {routine_implementation_status} * External Routines Used: * {name_of_facility_or_package}: * {routine_used}... * Implementation Deficiencies: * {routine_deficiencies}... * References: * {routine_references}... * Keywords: * {routine_keywords}... * Copyright: * {routine_copyright} * Authors: * DJA: David J. Allan (ROSAT) * {enter_new_authors_here} * History: * 14 May 1996 (DJA): * Original version. * {enter_changes_here} * Bugs: * {note_any_bugs_here} *- * Type Definitions: IMPLICIT NONE ! No implicit typing * Global Constants: INCLUDE 'SAE_PAR' ! Standard SAE constants * Status: INTEGER STATUS ! Global status * Given : INTEGER PNX, PNY, NX, NY REAL PDATA(-PNX/2:PNX/2, : -PNY/2:PNY/2) REAL PWORK(NX,NY) * Given and Returned : REAL PSF(NX,NY) *. * Check inherited global status. IF ( STATUS .NE. SAI__OK ) RETURN * Make copy of psf, and zero the output CALL ARR_COP1R( NX*NY, PSF, PWORK, STATUS ) * Convolve the psf CALL PSF_CONVOLVE( NX, NY, PWORK, PNX, PNY, PDATA, PSF, STATUS ) END SUBROUTINE SPRESP_PERT_EN( PNBIN, PDATA, NRAD, NX, : NY, PSF, STATUS ) *+ * Name: * SPRESP_PERT_EN * Purpose: * Perturbs a psf by altering its radial profile. The normalisation * of the original psf is retained. * Language: * FORTRAN * Arguments: * STATUS = INTEGER (Given and Returned) * The global status. * Description: * Each point in the input is adjusted by a factor derived from the * input perturbation profile. * Usage: * {routine_name} {parameter_usage} * Examples: * {routine_example_text} * {routine_example_description} * Pitfalls: * {pitfall_description}... * Notes: * {routine_notes}... * Prior Requirements: * {routine_prior_requirements}... * Side Effects: * {routine_side_effects}... * Algorithm: * {algorithm_description}... * Accuracy: * {routine_accuracy} * Timing: * {routine_timing} * Implementation Status: * {routine_implementation_status} * External Routines Used: * {name_of_facility_or_package}: * {routine_used}... * Implementation Deficiencies: * {routine_deficiencies}... * References: * {routine_references}... * Keywords: * {routine_keywords}... * Copyright: * {routine_copyright} * Authors: * DJA: David J. Allan (ROSAT) * {enter_new_authors_here} * History: * 14 May 1996 (DJA): * Original version. * {enter_changes_here} * Bugs: * {note_any_bugs_here} *- * Type Definitions: IMPLICIT NONE ! No implicit typing * Global Constants: INCLUDE 'SAE_PAR' ! Standard SAE constants * Status: INTEGER STATUS ! Global status * Given : INTEGER PNBIN, NX, NY, NRAD REAL PDATA(PNBIN) * Given and Returned : REAL PSF(NX,NY) * Local Constants: INTEGER SAMPLE ! Oversampling PARAMETER ( SAMPLE = 3 ) * Local Variables: REAL SUM ! Psf normalisation REAL XP,YP,XOLD,YOLD,SCALE,NSUM INTEGER I,J,II,JJ,KK ! Loops over psf *. * Check inherited global status. IF ( STATUS .NE. SAI__OK ) RETURN * Find sum of input psf SUM = 0.0 DO J = 1, NY DO I = 1, NX SUM = SUM + PSF(I,J) END DO END DO * Scale the input psf NSUM = 0.0 DO J = 1, NY YOLD = REAL(J-1) - REAL(NY)/2.0 DO I = 1, NX XOLD = REAL(I-1) - REAL(NX)/2.0 * Find weighted rescale for this psf pixel SCALE = 0.0 DO JJ = 1, SAMPLE YP = YOLD + (REAL(JJ)-0.5)/REAL(SAMPLE) DO II = 1, SAMPLE XP = XOLD + (REAL(II)-0.5)/REAL(SAMPLE) * Find radial bin number in perturbation profile KK = INT(SQRT(XP*XP+YP*YP)*REAL(SAMPLE))/SAMPLE+1 * In valid range? IF ( (KK.GT.0) .AND. (KK.LE.PNBIN) ) THEN SCALE = SCALE + PDATA(KK) END IF END DO END DO * Adjust scale factor for number of samples SCALE = SCALE / (SAMPLE**2) * Scale the psf and accumulate new sum PSF(I,J) = PSF(I,J) * SCALE NSUM = NSUM + PSF(I,J) END DO END DO * Renormalise the psf DO J = 1, NY DO I = 1, NX PSF(I,J) = PSF(I,J) * SUM / NSUM END DO END DO END