SUBROUTINE AST2XSP( STATUS ) *+ * Name: * AST2XSP * Purpose: * Converts spectra to XANADU FITS spectral format * Language: * Starlink Fortran * Type of Module: * ASTERIX task * Invocation: * CALL AST2XSP( STATUS ) * Arguments: * STATUS = INTEGER (Given and Returned) * The global status. * Description: * Program converts an ASTERIX HDS spectrum file into a form * that can be read by XSPEC -(X-ray Spectral Analysis). * It outputs a pulse height analyser file (.PHA) and a * response file (.RMF). It currently only handles data for the * Exosat ME, ROSAT XRT, and ROSAT WFC instruments. * Usage: * AST2XSP hds_file [slice] xspec_file * Environment Parameters: * INP = CHARACTER (read) * Name of the input HDS spectrum * SLICE = INTEGER (read) * Number of spectrum in spectral set * AREA = REAL (read) * Geometrical area of detector * OUT = CHARACTER (read) * Filename root of the output XSPEC format file * 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: * OGIP CAL/GEN/92-002 : The Calibration Requirements for Spectral Analysis * Keywords: * astxsp, usage:public * Copyright: * Copyright (C) University of Birmingham, 1995 * Authors: * PAM: Paul McGale (XRA,University of Leicester). * RDS: Richard Saxton (ROSAT, University of Leicester) * DJA: David J. Allan (Jet-X, University of Birmingham) * {enter_new_authors_here} * History: * Feb 91 (PAM): * Original Version. * Apr 91 (RDS): * Adamised version * 7 Nov 91 (RMJ): * Allows binned up data from XRT to be handled (XMV::RMJ) * Jun 92 (RDS): * Takes a slice from a spectral_series and works on EXOSAT LE data * Aug 92 (RDS): * Fixed a bug in the slicing * Apr 93 (RDS,RMJ): * Solved a problem which occured when an energy had a * response in only one PH bin. * 24 Nov 94 V1.8-0 (DJA): * Now use USI for user interface (DJA) * 14 Jun 95 V1.8-1 (DJA): * Rewrite for XANADU FITS formats. No longer needs * linking against XANADU. * 6 Aug 95 V1.8-2 (DJA): * Don't write AREASCAL keyword. Handle spectra with bins more than * one channel wide properly. Removed ignoring channels 1..7 for * XRT data. * 14 Aug 95 V1.8-3 (DJA): * Reinstated AREASCAL and normalise response to take the factor out. * {enter_changes_here} * Bugs: * {note_any_bugs_here} *- * Type Definitions: IMPLICIT NONE ! No implicit typing * Global Constants: INCLUDE 'SAE_PAR' ! Standard SAE constants INCLUDE 'ADI_PAR' INCLUDE 'DAT_PAR' * Status: INTEGER STATUS ! Global status * External References: INTEGER CHR_LEN EXTERNAL CHR_LEN * Local Constants: INTEGER MAXCOL PARAMETER ( MAXCOL = 6 ) INTEGER MAXIG PARAMETER ( MAXIG = 2 ) CHARACTER*30 VERSION PARAMETER ( VERSION = 'AST2XSP Version 1.8-4' ) INTEGER XSP_OK PARAMETER ( XSP_OK = 0 ) INTEGER XSP_BAD PARAMETER ( XSP_BAD = 1 ) * Local Variables: CHARACTER*(DAT__SZLOC) LOCSRT, LOCTIME CHARACTER*40 DET ! Detector name CHARACTER*132 INFILE ! Input spectrum name CHARACTER*40 INSTMNT ! Instrument name CHARACTER*132 OUTPHA ! O/p spectrum name CHARACTER*132 OUTRSP ! O/p response name CHARACTER*8 TTYPE(MAXCOL) ! Column names CHARACTER*8 TFORM(MAXCOL) ! Column types CHARACTER*20 TUNIT(MAXCOL) ! Column units * Relate to HDS files. INTEGER DIMS(4),ODIMS(4),AMIN(4),AMAX(4),ORD(4) REAL EXTIME ! Exposure time REAL GEOMAREA ! Geometric area INTEGER DETID ! Detector configuration INTEGER E_AX ! Energy axis number INTEGER EAPTR ! Energy axis data INTEGER ERRCOL ! Error column number INTEGER I,J ! Loop variables INTEGER IDPTR ! I/p data INTEGER IEPTR ! I/p errors INTEGER IFID ! I/p file descriptor INTEGER IGSTART(MAXIG) ! Channels to ignore INTEGER IGEND(MAXIG) ! Start and end points INTEGER IQPTR ! I/p quality INTEGER KDOT ! Character index INTEGER LUN ! O/p logical unit INTEGER NDIM ! I/p dimensionality INTEGER NEDIM ! Not E_AX dim in 2D INTEGER NFIELDS ! # o/p fields INTEGER NLEV ! File trace info INTEGER OLEN ! Length of OUTPHA/RSP INTEGER OPHA ! Output PHA dataset INTEGER ORSP ! Output RMF dataset INTEGER NIGNORE ! # Regions to ignore INTEGER PIXID, PRJID, SYSID ! Astrometry details INTEGER QUALCOL ! Quality column number INTEGER RMFID, ARFID ! I/p response objects INTEGER SLICE ! Energy slice wanted INTEGER TIMID ! Timing info INTEGER WPTR ! Workspace pointer LOGICAL ERROK ! Errors present? LOGICAL EXPCOR ! Corrected spectrum? LOGICAL LVAL ! Spectral bin bad? LOGICAL OK ! General validity test LOGICAL QUALOK ! Quality present? LOGICAL TWOD ! I/p is 2-dimensional * Relate to XSPEC files. INTEGER INFOAR(4) DATA INFOAR/4*0/ * Relate to main program. CHARACTER*80 PATH *. * Check inherited global status. IF ( STATUS .NE. SAI__OK ) RETURN * Version id CALL MSG_PRNT( VERSION ) * Initialise ASTERIX CALL AST_INIT() * Get input file name CALL USI_ASSOC( 'INP', 'BinDS', 'READ', IFID, STATUS ) * Find name of input file CALL ADI_FTRACE( IFID, NLEV, PATH, INFILE, STATUS ) IF (STATUS .NE. SAI__OK) GOTO 99 * Get rid of the file extension KDOT = MAX(INDEX(INFILE,'.sdf'),INDEX(INFILE,'.SDF')) IF ( KDOT .GT. 0 ) THEN CALL USI_DEF0C( 'OUT', INFILE(:KDOT-1), STATUS ) END IF CALL USI_GET0C( 'OUT', OUTPHA, STATUS ) OLEN = CHR_LEN(OUTPHA) OUTPHA = OUTPHA(:OLEN)//'.pha' OUTRSP = OUTPHA(:OLEN)//'.rmf' OLEN = OLEN + 4 * Get hardware configuration, astrometry and timing data CALL DCI_GETID( IFID, DETID, STATUS ) CALL WCI_GETIDS( IFID, PIXID, PRJID, SYSID, STATUS ) CALL TCI_GETID( IFID, TIMID, STATUS ) * Get instrument name CALL ADI_CGET0C( DETID, 'Instrument', INSTMNT, STATUS ) IF (STATUS .NE. SAI__OK) THEN CALL ERR_ANNUL( STATUS ) CALL MSG_PRNT( 'Error reading instrument name' ) INSTMNT = 'unknown' CALL ADI_CPUT0C( DETID, 'Instrument', INSTMNT, STATUS ) END IF * Get detector name CALL ADI_CGET0C( DETID, 'Detector', DET, STATUS ) IF (STATUS .NE. SAI__OK) THEN CALL ERR_ANNUL( STATUS ) CALL MSG_PRNT( 'Error reading detector name, assumed unknown' ) DET = 'unknown' CALL ADI_CPUT0C( DETID, 'Detector', INSTMNT, STATUS ) END IF * Get data dimensions CALL BDI_CHK( IFID, 'Data', OK, STATUS ) CALL BDI_GETSHP( IFID, 2, DIMS, NDIM, STATUS ) IF (STATUS .NE. SAI__OK .OR. .NOT. OK) THEN CALL MSG_PRNT('Error reading data array') GOTO 99 END IF * If a 2-d XRT file assume that the other dimension is radial bins IF (NDIM .EQ. 2 .AND. INDEX(INSTMNT, 'XRT') .NE. 0) THEN * Find which axis is the PH axis CALL BDI0_FNDAXC( IFID, 'E', E_AX, STATUS ) IF ( STATUS .NE. SAI__OK ) GOTO 99 * Set the other axis IF (E_AX .EQ. 1) THEN NEDIM = 2 ELSE IF ( E_AX .EQ. 2 ) THEN NEDIM = 1 ELSE CALL MSG_PRNT( '** PH axis not found **' ) GOTO 99 END IF * Ask user which slice is required CALL MSG_SETI( 'NSPEC', DIMS(NEDIM) ) CALL MSG_PRNT( 'This file contains ^NSPEC spectra' ) CALL USI_GET0I( 'SLICE', SLICE, STATUS ) IF ( (STATUS .EQ. SAI__OK) .AND. : ((SLICE.LT.1) .OR. (SLICE.GT.DIMS(NEDIM))) ) THEN CALL MSG_SETI( 'S', SLICE ) CALL MSG_SETI( 'N', DIMS(NEDIM) ) STATUS = SAI__ERROR CALL ERR_REP( ' ', 'Invalid slice index ^S, should be 1..^N', : STATUS ) END IF IF (STATUS .NE. SAI__OK) GOTO 99 * DIMS(3)=1 DIMS(4)=1 TWOD = .TRUE. ELSE E_AX = 1 TWOD = .FALSE. SLICE = 0 END IF * Locate response elements CALL ERI_GETIDS( IFID, SLICE, RMFID, ARFID, STATUS ) * Get the base and scale values of the PHA axis C CALL BDI_MAPR( IFID, 'E_Axis_Data', 'READ', EAPTR, STATUS ) C IF (STATUS .NE. SAI__OK) THEN C CALL ERR_REP( ' ', 'Error reading spectral axis values', C : STATUS ) C GOTO 99 C END IF * Check if instrument is ROSAT XRT, WFC, EXOSAT ME or LE. NIGNORE = 0 GEOMAREA = -1.0 IF ( INDEX(INSTMNT, 'XRT') .GT. 0 ) THEN IF ( INDEX( DET,'PSPC') .NE. 0 ) THEN GEOMAREA = 1141.0 * Standard ignores for XRT/PSPC are channels 1-7 C NIGNORE = 1 C IGSTART(1) = 1 C IGEND(1) = 7 END IF C CALL DAT_FIND(LOCSRT, 'TIME', LOCTIME, STATUS) C* C IF (STATUS .NE. SAI__OK) THEN C CALL MSG_PRNT('Error reading times from SORt box') C GOTO 99 C ENDIF * EXOSAT ME ELSE IF (INDEX(INSTMNT, 'ME') .GT. 0 ) THEN GEOMAREA = 1676.71 NIGNORE = 1 IGSTART(1) = 1 IGEND(1) = 4 C CALL DAT_FIND(LOCSRT, 'TIME', LOCTIME, STATUS) * EXOSAT LE ELSE IF (INDEX(INSTMNT, 'LE') .GT. 0 ) THEN GEOMAREA = 90.5 * ROSAT WFC ELSE IF (INDEX(INSTMNT, 'WFC') .GT. 0 ) THEN GEOMAREA = 456.0 * HEXE ELSE IF (INDEX(INSTMNT, 'HEXE') .GT. 0 ) THEN GEOMAREA = 574.117 * TTM ELSE IF ( INDEX(INSTMNT, 'TTM') .GT. 0 ) THEN GEOMAREA = 200.0 END IF * Unrecognised instrument so prompt for area IF ( GEOMAREA .LT. 0.0 ) THEN CALL MSG_SETC( 'INS', INSTMNT ) CALL MSG_SETC( 'DET', DET ) CALL MSG_PRNT( 'Cannot derive geometric area for ^INS / ^DET' ) CALL USI_GET0R( 'AREA', GEOMAREA, STATUS ) END IF IF ( STATUS .NE. SAI__OK ) GOTO 99 * Store geometrical area in response CALL ADI_CPUT0R( RMFID, 'GeometricalArea', GEOMAREA, STATUS ) * Read in info from HDS file. * Have to get locator to array cell. C IF (INDEX(INSTMNT, 'WFC') .GT. 0 ) THEN C STRTIME = 1.0E6 C STPTIME = 1.1E6 * Read in info from HDS file first. CALL ADI_CGET0R( TIMID, 'Exposure', EXTIME, STATUS ) IF ( STATUS .NE. SAI__OK ) THEN CALL ERR_ANNUL( STATUS ) CALL MSG_PRNT( 'Unable to get exposure time from input, '/ : /'assuming exposure of 1 second' ) EXTIME = 1.0 END IF * Map the data array CALL BDI_MAPR( IFID, 'Data', 'READ', IDPTR, STATUS ) * Map error array if present CALL BDI_CHK( IFID, 'Variance', ERROK, STATUS ) IF ( ERROK ) THEN CALL BDI_MAPR( IFID, 'Error', 'READ', IEPTR, STATUS ) IF (STATUS .NE. SAI__OK) THEN CALL ERR_REP( ' ', 'Error reading errors from input', STATUS ) GOTO 99 END IF END IF * Map quality array if present CALL BDI_CHK( IFID, 'Quality', QUALOK, STATUS ) IF ( QUALOK ) THEN CALL BDI_MAPL( IFID, 'LogicalQuality', 'READ', IQPTR, STATUS ) IF ( STATUS .NE. SAI__OK ) THEN CALL ERR_REP( ' ', 'Error reading quality from input', : STATUS ) GOTO 99 END IF END IF * Extract slice if 2-D IF ( TWOD ) THEN * Set min and max values DO J = 1, 4 ORD(J) = J IF ( J .EQ. NEDIM ) THEN AMIN(J) = SLICE AMAX(J) = SLICE ODIMS(J)=1 ELSE AMIN(J) = 1 AMAX(J) = DIMS(J) ODIMS(J) = DIMS(J) END IF END DO ODIMS(E_AX) = DIMS(E_AX) * Copy the slice that you want into the output array CALL DYN_MAPR( 1, DIMS(E_AX), WPTR, STATUS ) CALL DTA_COPYSLICER( DIMS(1), DIMS(2), DIMS(3), DIMS(4), : %VAL(IDPTR), AMIN, AMAX, ORD, ODIMS(1), ODIMS(2), : ODIMS(3), ODIMS(4), %VAL(WPTR) ) IDPTR = WPTR CALL DYN_MAPR( 1, DIMS(E_AX), WPTR, STATUS ) CALL DTA_COPYSLICER( DIMS(1), DIMS(2), DIMS(3), DIMS(4), : %VAL(IEPTR), AMIN, AMAX, ORD, ODIMS(1), ODIMS(2), : ODIMS(3), ODIMS(4), %VAL(WPTR) ) IEPTR = WPTR END IF * Open new PHA file. Has a null primary header, into which we shove * most of the ancillary keywords CALL ADI_FCREAT( OUTPHA(:OLEN)//'%fits', ADI__NULLID, OPHA, : STATUS ) * Write loads'a'keywords CALL ADI2_PKEY0C( OPHA, ' ', 'CONTENT', 'SPECTRUM', : 'Spectrum file contains', STATUS ) CALL ADI2_PKEY0C( OPHA, ' ', 'CREATOR', VERSION, : 'Creator of this file', STATUS ) CALL ADI2_PKEY0R( OPHA, 'SPECTRUM', 'EXPOSURE', EXTIME, : 'Exposure time', STATUS ) CALL ADI2_PKEY0R( OPHA, 'SPECTRUM', 'AREASCAL', GEOMAREA, : 'Area scaling factor', STATUS ) CALL ADI2_PKEY0R( OPHA, 'SPECTRUM', 'BACKSCAL', 1.0, : 'Background scaling factor', STATUS ) CALL ADI2_PKEY0R( OPHA, 'SPECTRUM', 'CORRSCAL', 1.0, : 'Corrections scaling factor', STATUS ) * Number of raw detector channels CALL ADI2_PKEY0I( OPHA, 'SPECTRUM', 'DETCHANS', DIMS(E_AX), : 'Total number of raw detector channels', STATUS ) * Is the spectrum corrected? CALL PRF_GET( IFID, 'CORRECTED.EXPOSURE', EXPCOR, STATUS ) * Create table names, types and units TTYPE(1) = 'CHANNEL' TFORM(1) = 'I' CALL BDI_AXGET0C( IFID, E_AX, 'Units', TUNIT(1), STATUS ) IF ( EXPCOR ) THEN TTYPE(2) = 'RATE' TFORM(2) = 'E' ELSE TTYPE(2) = 'COUNTS' TFORM(2) = 'I' END IF CALL BDI_GET0C( IFID, 'Units', TUNIT(2), STATUS ) NFIELDS = 2 IF ( ERROK ) THEN NFIELDS = NFIELDS + 1 ERRCOL = NFIELDS TTYPE(NFIELDS) = 'STAT_ERR' TFORM(NFIELDS) = 'E' TUNIT(NFIELDS) = TUNIT(2) END IF IF ( QUALOK .OR. (NIGNORE.GT.0) ) THEN NFIELDS = NFIELDS + 1 QUALCOL = NFIELDS TTYPE(NFIELDS) = 'QUALITY' TFORM(NFIELDS) = 'I' TUNIT(NFIELDS) = ' ' END IF * Create extension for spectrum CALL ADI2_POGIPK( OPHA, 'SPECTRUM', 'SPECTRUM', : ' ', ' ', ' ', ' ', ' ', : STATUS ) CALL ADI2_PKEY0I( OPHA, 'SPECTRUM', 'TLMIN1', 1, : 'Lowest legal channel number', STATUS ) CALL ADI2_PKEY0I( OPHA, 'SPECTRUM', 'TLMAX1', DIMS(E_AX), : 'Highest legal channel number', STATUS ) CALL ADI2_PKEY0C( OPHA, 'SPECTRUM', 'RESPFILE', OUTRSP(:OLEN), : 'Redistribution matrix file (RMF)', STATUS ) CALL ADI2_PKEY0C( OPHA, 'SPECTRUM', 'PHAVERSN', '1992a', : 'OGIP classification of FITS format style', STATUS ) * Write astrometry, detector info and timing CALL WCI_PUTIDS( OPHA, PIXID, PRJID, SYSID, STATUS ) CALL DCI_PUTID( OPHA, DETID, STATUS ) CALL TCI_PUTID( OPHA, TIMID, STATUS ) CALL ADI2_DEFBTB( OPHA, 'SPECTRUM', DIMS(E_AX), NFIELDS, TTYPE, : TFORM, TUNIT, 0, STATUS ) * Write the spectrum CALL ADI2_GETLUN( OPHA, LUN, STATUS ) CALL DYN_MAPR( 1, DIMS(E_AX), EAPTR, STATUS ) CALL ARR_REG1R( 1.0, 1.0, DIMS(E_AX), %VAL(EAPTR), STATUS ) CALL FTPCLE( LUN, 1, 1, 1, DIMS(E_AX), %VAL(EAPTR), STATUS ) CALL FTPCLE( LUN, 2, 1, 1, DIMS(E_AX), %VAL(IDPTR), STATUS ) IF ( ERROK ) THEN CALL FTPCLE( LUN, ERRCOL, 1, 1, DIMS(E_AX), %VAL(IEPTR), : STATUS ) END IF * Write quality IF ( QUALOK ) THEN DO I = 1, DIMS(E_AX) CALL ARR_ELEM1L( IQPTR, DIMS(E_AX), I, LVAL, STATUS ) IF ( LVAL ) THEN CALL FTPCLJ( LUN, QUALCOL, I, 1, 1, XSP_OK, STATUS ) ELSE CALL FTPCLJ( LUN, QUALCOL, I, 1, 1, XSP_BAD, STATUS ) END IF END DO ELSE IF ( NIGNORE .GT. 0 ) THEN DO I = 1, DIMS(E_AX) CALL FTPCLJ( LUN, QUALCOL, I, 1, 1, XSP_OK, STATUS ) END DO END IF IF ( NIGNORE .GT. 0 ) THEN DO I = 1, NIGNORE DO J = IGSTART(I), IGEND(I) CALL FTPCLJ( LUN, QUALCOL, J, 1, 1, XSP_BAD, STATUS ) END DO END DO END IF * Close spectrum file CALL ADI_FCLOSE( OPHA, STATUS ) * Open the response file CALL ADI_FCREAT( OUTRSP(:OLEN)//'%fits', ADI__NULLID, ORSP, : STATUS ) CALL ADI2_PKEY0C( ORSP, ' ', 'CREATOR', VERSION, '*', STATUS ) CALL ADI2_PKEY0C( ORSP, ' ', 'CONTENT', 'MATRIX', : 'File contains response matrix', STATUS ) CALL ERI_PUTIDS( ORSP, RMFID, ARFID, STATUS ) CALL DCI_PUTID( ORSP, DETID, STATUS ) * Close the response file CALL ADI_FCLOSE( ORSP, STATUS ) * Tidy up 99 CALL AST_CLOSE() CALL AST_ERR( STATUS ) END