Example #1
0
void smurf_calcqu( int *status ) {

    /* Local Variables: */
    AstFitsChan *fc;           /* Holds FITS headers for output NDFs */
    AstKeyMap *config;         /* Holds all cleaning parameters */
    AstKeyMap *dkpars;         /* Holds dark squid cleaning parameters */
    AstKeyMap *heateffmap = NULL; /* Heater efficiency data */
    AstKeyMap *sub_instruments;/* Indicates which instrument is being used */
    Grp *bgrp = NULL;          /* Group of base names for each chunk */
    Grp *igrp = NULL;          /* Group of input files */
    Grp *ogrp = NULL;          /* Group of output files  */
    Grp *sgrp = NULL;          /* Group of science files */
    HDSLoc *loci = NULL;       /* Locator for output I container file */
    HDSLoc *locq = NULL;       /* Locator for output Q container file */
    HDSLoc *locu = NULL;       /* Locator for output U container file */
    NdgProvenance *oprov;      /* Provenance to store in each output NDF */
    ThrWorkForce *wf;          /* Pointer to a pool of worker threads */
    char headval[ 81 ];        /* FITS header value */
    char ndfname[ 30 ];        /* Name of output Q or U NDF */
    char polcrd[ 81 ];         /* FITS 'POL_CRD' header value */
    char subarray[ 10 ];       /* Subarray name (e.g. "s4a", etc) */
    double angrot;             /* Angle from focal plane X axis to fixed analyser */
    double paoff;              /* WPLATE value corresponding to POL_ANG=0.0 */
    float arcerror;            /* Max acceptable error (arcsec) in one block */
    int block_end;             /* Index of last time slice in block */
    int block_start;           /* Index of first time slice in block */
    int dkclean;               /* Clean dark squids? */
    int fix;                   /* Fix the POL-2 triggering issue? */
    int iblock;                /* Index of current block */
    int iplace;                /* NDF placeholder for current block's I image */
    int ipolcrd;               /* Reference direction for waveplate angles */
    int maxsize;               /* Max no. of time slices in a block */
    int minsize;               /* Min no. of time slices in a block */
    int nc;                    /* Number of characters written to a string */
    int pasign;                /* +1 or -1 indicating sense of POL_ANG value */
    int qplace;                /* NDF placeholder for current block's Q image */
    int submean;               /* Subtract mean value from each time slice? */
    int uplace;                /* NDF placeholder for current block's U image */
    size_t ichunk;             /* Continuous chunk counter */
    size_t idx;                /* Subarray counter */
    size_t igroup;             /* Index for group of related input NDFs */
    size_t inidx;              /* Index into group of science input NDFs */
    size_t nchunk;             /* Number continuous chunks outside iter loop */
    size_t ssize;              /* Number of science files in input group */
    smfArray *concat = NULL;   /* Pointer to smfArray holding bolometer data */
    smfArray *darks = NULL;    /* dark frames */
    smfArray *dkarray = NULL;  /* Pointer to smfArray holding dark squid data */
    smfArray *flatramps = NULL;/* Flatfield ramps */
    smfData *data = NULL;      /* Concatenated data for one subarray */
    smfData *dkdata = NULL;    /* Concatenated dark squid data for one subarray */
    smfGroup *sgroup = NULL;   /* smfGroup corresponding to sgrp */

    /* Check inhereited status */
    if( *status != SAI__OK ) return;

    /* Start new AST and NDF contexts. */
    astBegin;
    ndfBegin();

    /* Find the number of cores/processors available and create a work force
       holding the same number of threads. */
    wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

    /* Get a group of input files */
    kpg1Rgndf( "IN", 0, 1, "  Give more NDFs...", &igrp, &ssize, status );

    /* Get a group containing just the files holding science data. */
    smf_find_science( igrp, &sgrp, 0, NULL, NULL, 1, 1, SMF__NULL, &darks,
                      &flatramps, &heateffmap, NULL, status );

    /* Check we have at least once science file. */
    ssize = grpGrpsz( sgrp, status );
    if( ssize == 0 ) {
        msgOutif( MSG__NORM, " ", "All supplied input frames were DARK.",
                  status );
    } else {

        /* See if a correction should be made for the POL2 triggering issue. */
        parGet0l( "FIX", &fix, status );

        /* Create HDS container files to hold the output NDFs. */
        datCreat( "OUTQ", "CALCQU", 0, 0, status );
        datCreat( "OUTU", "CALCQU", 0, 0, status );

        /* Associate the locators with the structures. */
        datAssoc( "OUTQ", "WRITE", &locq, status );
        datAssoc( "OUTU", "WRITE", &locu, status );

        /* The I images are optional. */
        if( *status == SAI__OK ) {
            datCreat( "OUTI", "CALCQU", 0, 0, status );
            datAssoc( "OUTI", "WRITE", &loci, status );
            if( *status == PAR__NULL ) {
                errAnnul( status );
                loci = NULL;
            }
        }

        /* Group the input files so that all files within a single group have the
           same wavelength and belong to the same subscan of the same observation.
           Also identify chunks of data that are contiguous in time, and
           determine to which such chunk each group belongs. All this information
           is returned in a smfGroup structure ("*sgroup"). */
        smf_grp_related( sgrp, ssize, 1, 1, 0, NULL, NULL, NULL,
                         NULL, &sgroup, &bgrp, NULL, status );

        /* Obtain the number of contiguous chunks. */
        if( *status == SAI__OK ) {
            nchunk = sgroup->chunk[ sgroup->ngroups - 1 ] + 1;
        } else {
            nchunk = 0;
        }

        /* Indicate we have not yet found a value for the ARCERROR parameter. */
        arcerror = 0.0;

        /* Loop over all contiguous chunks */
        for( ichunk = 0; ichunk < nchunk && *status == SAI__OK; ichunk++ ) {

            /* Display the chunk number. */
            if( nchunk > 1 ) {
                msgOutiff( MSG__VERB, "", "   Doing chunk %d of %d.",
                           status, (int) ichunk + 1, (int) nchunk );
            }

            /* Concatenate the data within this contiguous chunk. This produces a
               smfArray ("concat") containing a smfData for each subarray present in
               the chunk. Each smfData holds the concatenated data for a single
               subarray. */
            smf_concat_smfGroup( wf, NULL, sgroup, darks, NULL, flatramps,
                                 heateffmap, ichunk, 1, 1, NULL, 0, NULL, NULL,
                                 0, 0, 0, &concat, NULL, status );

            /* Get a KeyMap holding values for the configuration parameters. Since we
               sorted by wavelength when calling smf_grp_related, we know that all
               smfDatas in the current smfArray (i.e. chunk) will relate to the same
               wavelength. Therefore we can use the same parameters for all smfDatas in
               the current smfArray. */
            sub_instruments = smf_subinst_keymap( SMF__SUBINST_NONE,
                                                  concat->sdata[ 0 ], NULL,
                                                  0, status );
            config = kpg1Config( "CONFIG", "$SMURF_DIR/smurf_calcqu.def",
                                 sub_instruments, status );
            sub_instruments = astAnnul( sub_instruments );


            /* Get the CALCQU specific parameters. */
            if( !astMapGet0I( config, "PASIGN", &pasign ) ) pasign = 1;
            msgOutiff( MSG__VERB, "", "PASIGN=%d", status, pasign );
            if( !astMapGet0D( config, "PAOFF", &paoff ) ) paoff = 0.0;
            msgOutiff( MSG__VERB, "", "PAOFF=%g", status, paoff );
            if( !astMapGet0D( config, "ANGROT", &angrot ) ) angrot = 90.0;
            msgOutiff( MSG__VERB, "", "ANGROT=%g", status, angrot );
            if( !astMapGet0I( config, "SUBMEAN", &submean ) ) submean = 0;
            msgOutiff( MSG__VERB, "", "SUBMEAN=%d", status, submean );

            /* See if the dark squids should be cleaned. */
            if( !astMapGet0I( config, "DKCLEAN", &dkclean ) ) dkclean = 0;

            /* If required, clean the dark squids now since we might need to use them to
               clean the bolometer data. */
            if( dkclean ) {

                /* Create a smfArray containing the dark squid data. For each one, store
                   a pointer to the main header so that smf_clean_smfArray can get at the
                   JCMTState information. */
                dkarray = smf_create_smfArray( status );
                for( idx = 0; idx < concat->ndat && *status == SAI__OK; idx++ ) {
                    data = concat->sdata[ idx ];
                    if( data && data->da && data->da->dksquid ) {
                        dkdata = data->da->dksquid;
                        dkdata->hdr = data->hdr;
                        smf_addto_smfArray( dkarray, dkdata, status );
                    }
                }

                /* Clean the smfArray containing the dark squid data. Use the "CLEANDK.*"
                   parameters. */
                (void) astMapGet0A( config, "CLEANDK", &dkpars );
                smf_clean_smfArray( wf, dkarray, NULL, NULL, NULL, dkpars, status );
                dkpars = astAnnul( dkpars );

                /* Nullify the header pointers so that we don't accidentally close any. */
                if( dkarray ) {
                    for( idx = 0; idx < dkarray->ndat; idx++ ) {
                        dkdata = dkarray->sdata[ idx ];
                        dkdata->hdr = NULL;
                    }

                    /* Free the smfArray holding the dark squid data, but do not free the
                       individual smfDatas within it. */
                    dkarray->owndata = 0;
                    smf_close_related( &dkarray, status );
                }
            }

            /* Now clean the bolometer data */
            smf_clean_smfArray( wf, concat, NULL, NULL, NULL, config, status );

            /* If required correct for the POL2 triggering issue. */
            if( fix ) smf_fix_pol2( wf, concat, status );

            /* Loop round each sub-array in the current contiguous chunk of data. */
            for( idx = 0; idx < concat->ndat && *status == SAI__OK; idx++ ) {
                data = concat->sdata[ idx ];

                /* Find the name of the subarray that generated the data. */
                smf_find_subarray( data->hdr, subarray, sizeof(subarray), NULL,
                                   status );

                /* Display the sub-array. */
                if( concat->ndat > 1 ) {
                    msgOutiff( MSG__VERB, "", "   Doing sub-array %s.",
                               status, subarray );
                }

                /* Create an empty provenance structure. Each input NDF that contributes
                   to the current chunk and array will be added as an ancestor to this
                   structure, which will later be stored in each output NDF created for
                   this chunk and array. */
                oprov = ndgReadProv( NDF__NOID, "SMURF:CALCQU", status );

                /* Indicate we do not yet have any FITS headers for the output NDFs */
                fc = NULL;

                /* Indicate we do not yet know the coordinate reference frame for the
                   half-waveplate angle. */
                polcrd[ 0 ] = 0;
                ipolcrd = 0;

                /* Go through the smfGroup looking for groups of related input NDFs that
                   contribute to the current chunk. */
                for( igroup = 0; igroup < sgroup->ngroups; igroup++ ) {
                    if( sgroup->chunk[ igroup ] == ichunk ) {

                        /* Get the integer index into the GRP group (sgrp) that holds the input NDFs.
                           This index identifies the input NDF that provides the data for the current
                           chunk and subarray. This assumes that the order in which smf_concat_smfGroup
                           stores arrays in the "concat" smfArray matches the order in which
                           smf_grp_related stores arrays within the sgroup->subgroups. */
                        inidx = sgroup->subgroups[ igroup ][ idx ];

                        /* Add this input NDF as an ancestor into the output provenance structure. */
                        smf_accumulate_prov( NULL, sgrp, inidx, NDF__NOID,
                                             "SMURF:CALCQU", &oprov, status );

                        /* Merge the FITS headers from the current input NDF into the FitsChan
                           that holds headers for the output NDFs. The merging retains only those
                           headers which have the same value in all input NDFs. */
                        smf_fits_outhdr( data->hdr->fitshdr, &fc, status );

                        /* Get the polarimetry related FITS headers and check that all input NDFs
                           have usabie values. */
                        headval[ 0 ] = 0;
                        smf_getfitss( data->hdr, "POL_MODE", headval,
                                      sizeof(headval), status );
                        if( strcmp( headval, "CONSTANT" ) && *status == SAI__OK ) {
                            *status = SAI__ERROR;
                            grpMsg( "N", sgrp, inidx );
                            errRep( " ", "Input NDF ^N does not contain "
                                    "polarimetry data obtained with a spinning "
                                    "half-waveplate.", status );
                        }

                        headval[ 0 ] = 0;
                        smf_getfitss( data->hdr, "POLWAVIN", headval,
                                      sizeof(headval), status );
                        if( strcmp( headval, "Y" ) && *status == SAI__OK ) {
                            *status = SAI__ERROR;
                            grpMsg( "N", sgrp, inidx );
                            errRep( " ", "Half-waveplate was not in the beam for "
                                    "input NDF ^N.", status );
                        }

                        headval[ 0 ] = 0;
                        smf_getfitss( data->hdr, "POLANLIN", headval,
                                      sizeof(headval), status );
                        if( strcmp( headval, "Y" ) && *status == SAI__OK ) {
                            *status = SAI__ERROR;
                            grpMsg( "N", sgrp, inidx );
                            errRep( " ", "Analyser was not in the beam for input "
                                    "NDF ^N.", status );
                        }

                        if( polcrd[ 0 ] ) {
                            headval[ 0 ] = 0;
                            smf_getfitss( data->hdr, "POL_CRD", headval,
                                          sizeof(headval), status );
                            if( strcmp( headval, polcrd ) && *status == SAI__OK ) {
                                *status = SAI__ERROR;
                                errRep( " ", "Input NDFs have differing values for "
                                        "FITS header 'POL_CRD'.", status );
                            }

                        } else {
                            smf_getfitss( data->hdr, "POL_CRD", polcrd,
                                          sizeof(polcrd), status );
                            if( !strcmp( polcrd, "FPLANE" ) ) {
                                ipolcrd = 0;
                            } else if( !strcmp( polcrd, "AZEL" ) ) {
                                ipolcrd = 1;
                            } else if( !strcmp( polcrd, "TRACKING" ) ) {
                                ipolcrd = 2;
                            } else if( *status == SAI__OK ) {
                                *status = SAI__ERROR;
                                msgSetc( "N", data->file->name );
                                msgSetc( "V", polcrd );
                                errRep( " ", "Input NDF ^N contains unknown value "
                                        "'^V' for FITS header 'POL_CRD'.", status );
                            }
                        }
                    }
                }

                /* If not already done, get the maximum spatial drift (in arc-seconds) that
                   can be tolerated whilst creating a single I/Q/U image. The default value is
                   half the makemap default pixel size. Also get limits on the number of
                   time slices in any block. */
                if( arcerror == 0.0 ) {
                    parDef0d( "ARCERROR", 0.5*smf_calc_telres( data->hdr->fitshdr,
                              status ), status );
                    parGet0r( "ARCERROR", &arcerror, status );

                    parGet0i( "MAXSIZE", &maxsize, status );
                    parGet0i( "MINSIZE", &minsize, status );
                    if( maxsize > 0 && maxsize < minsize && *status == SAI__OK ) {
                        *status = SAI__ERROR;
                        errRepf( "", "Value of parameter MAXSIZE (%d) is less "
                                 "than value of parameter MINSIZE (%d)", status,
                                 maxsize, minsize );
                    }
                }

                /* The algorithm that calculates I, Q and U assumes that all samples for a
                   single bolometer measure flux from the same point on the sky. Due to
                   sky rotation, this will not be the case - each bolometer will drift
                   slowly across the sky. However, since the drift is (or should be)
                   slow we can apply the I/Q/U algorithm to blocks of contiguous data over
                   which the bolometers do not move significantly. We produce a separate
                   I, Q and U image for each such block. The first block starts at the first
                   time slice in the smfData. */
                block_start = 0;

                /* Find the time slice at which the corner bolometers have moved
                   a critical distance (given by parameter ARCERROR) from their
                   positions at the start of the block. Then back off some time slices
                   to ensure that the block holds an integral number of half-waveplate
                   rotations. */
                block_end = smf_block_end( data, block_start, ipolcrd, arcerror,
                                           maxsize, status );

                /* Loop round creating I/Q/U images for each block. Count them. */
                iblock = 0;
                while( block_end >= 0 && *status == SAI__OK ) {

                    /* Skip very short blocks. */
                    if( block_end - block_start > minsize ) {

                        /* Display the start and end of the block. */
                        msgOutiff( MSG__VERB, "", "   Doing time slice block %d "
                                   "-> %d", status, (int) block_start,
                                   (int) block_end );

                        /* Get the name for the Q NDF for this block. Start of with "Q" followed by
                           the block index. */
                        iblock++;
                        nc = sprintf( ndfname, "Q%d", iblock );

                        /* Append the subarray name to the NDF name. */
                        nc += sprintf( ndfname + nc, "_%s", subarray );

                        /* Append the chunk index to the NDF name. */
                        nc += sprintf( ndfname + nc, "_%d", (int) ichunk );

                        /* Get NDF placeholder for the Q NDF. The NDFs are created inside the
                           output container file. */
                        ndfPlace( locq, ndfname, &qplace, status );

                        /* The name of the U NDF is the same except the initial "Q" is changed to
                           "U". */
                        ndfname[ 0 ] = 'U';
                        ndfPlace( locu, ndfname, &uplace, status );

                        /* The name of the I NDF is the same except the initial "Q" is changed to
                           "I". */
                        if( loci ) {
                            ndfname[ 0 ] = 'I';
                            ndfPlace( loci, ndfname, &iplace, status );
                        } else {
                            iplace = NDF__NOPL;
                        }

                        /* Store the chunk and block numbers as FITS headers. */
                        atlPtfti( fc, "POLCHUNK", (int) ichunk, "Chunk index used by CALCQU", status );
                        atlPtfti( fc, "POLBLOCK", iblock, "Block index used by CALCQU", status );

                        /* Create the Q and U images for the current block of time slices from
                           the subarray given by "idx", storing them in the output container
                           file. */
                        smf_calc_iqu( wf, data, block_start, block_end, ipolcrd,
                                      qplace, uplace, iplace, oprov, fc,
                                      pasign, AST__DD2R*paoff, AST__DD2R*angrot,
                                      submean, status );

                        /* Warn about short blocks. */
                    } else {
                        msgOutiff( MSG__VERB, "", "   Skipping short block of %d "
                                   "time slices (parameter MINSIZE=%d).", status,
                                   block_end - block_start - 1, minsize );
                    }

                    /* The next block starts at the first time slice following the previous
                       block. */
                    block_start = block_end + 1;

                    /* Find the time slice at which the corner bolometers have moved
                       a critical distance (given by parameter ARCERROR) from their
                       positions at the start of the block. Then back off some time slices
                       to ensure that the block holds an integral number of half-waveplate
                       rotations. This returns -1 if all time slices have been used. */
                    block_end = smf_block_end( data, block_start, ipolcrd,
                                               arcerror, maxsize, status );
                }

                /* Free resources */
                oprov = ndgFreeProv( oprov, status );
                fc = astAnnul( fc );
            }
            config = astAnnul( config );

            /* Close the smfArray. */
            smf_close_related( &concat, status );
        }

        /* Annul the locators for the output container files. */
        datAnnul( &locq, status );
        datAnnul( &locu, status );
        if( loci ) datAnnul( &loci, status );

        /* The parameter system hangs onto a primary locator for each container
           file, so cancel the parameters to annul these locators. */
        datCancl( "OUTQ", status );
        datCancl( "OUTU", status );
        datCancl( "OUTI", status );
    }

    /* Free resources. */
    smf_close_related( &darks, status );
    smf_close_related( &flatramps, status );

    if( igrp ) grpDelet( &igrp, status);
    if( sgrp ) grpDelet( &sgrp, status);
    if( bgrp ) grpDelet( &bgrp, status );
    if( ogrp ) grpDelet( &ogrp, status );
    if( sgroup ) smf_close_smfGroup( &sgroup, status );
    if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status );

    /* End the NDF and AST contexts. */
    ndfEnd( status );
    astEnd;

    /* Issue a status indication.*/
    if( *status == SAI__OK ) {
        msgOutif( MSG__VERB, " ", "CALCQU succeeded.", status);
    } else {
        msgOutif( MSG__VERB, " ", "CALCQU failed.", status);
    }
}
Example #2
0
void smurf_fts2_split(int* status)
{
  if( *status != SAI__OK ) { return; }

  const double STAGE_LENGTH = 450.0;    /* mm */
  int LR                    = 0;        /* Treat as Low Resolution scan */
  Grp* gIn                  = NULL;     /* Input group */
  Grp* gOut                 = NULL;     /* Output group */
  Grp* gTmp                 = NULL;     /* Temporary group */
  smfData* inData           = NULL;     /* Pointer to input data */
  smfData* outData          = NULL;     /* Pointer to output data */
  double* outData_pntr      = NULL;     /* Pointer to output data values array */
  int nMirPos               = 0;        /* Number of frames where the mirror actually moves */
  int nStart                = 0;        /* Frame index where the mirror starts moving */
  int nStartNext            = 0;        /* Frame index where the mirror starts moving in the next scan */
  int nStop                 = 0;        /* Frame index where the mirror stops */
  int lrStart               = 0;        /* Frame index where low resolution mirror limit starts */
  int hrStop                = 0;        /* Frame index where high resolution mirror limit stops */
  int hrStart               = 0;        /* Frame index where high resolution mirror limit starts */
  int lrStop                = 0;        /* Frame index where low resolution mirror limit stops */
  int lrCentre              = 0;        /* Frame index at centre of low resolution mirror positions */
  int i                     = 0;        /* Counter */
  int j                     = 0;        /* Counter */
  int k                     = 0;        /* Counter */
  int n                     = 0;        /* Counter */
  double fNyquist           = 0.0;      /* Nyquist frequency */
  double dz                 = 0.0;      /* Step size in evenly spaced OPD grid */
  double* MIRPOS            = NULL;     /* Mirror positions */

  size_t nFiles             = 0;        /* Size of the input group */
  size_t nOutFiles          = 0;        /* Size of the output group */
  size_t fIndex             = 0;        /* File index */
  size_t nWidth             = 0;        /* Data cube width */
  size_t nHeight            = 0;        /* Data cube height */
  size_t nFrames            = 0;        /* Data cube depth in input file */
  size_t nFramesOut         = 0;        /* Data cube depth in output file */
  size_t nFramesOutPrev     = 0;        /* Data cube depth in previous output file */
  size_t hrFramesOut        = 0;        /* Data cube depth in high res output file */
  size_t hrFramesOutPrev    = 0;        /* Data cube depth in previous high res output file */
  size_t lrFramesOut        = 0;        /* Data cube depth in low res output file */
  size_t lrFramesOutPrev    = 0;        /* Data cube depth in previous low res output file */
  size_t nPixels            = 0;        /* Number of bolometers in the subarray */

  char object[SZFITSTR];
  char subarray[SZFITSTR];
  char obsID[SZFITSTR];
  char scanMode[SZFITSTR];

  double scanVel            = 0.0;      /* Mirror speed in mm/sec */
  double stepTime           = 0.0;      /* RTS step time, average sample rate */
  double minOPD             = 0;        /* OPD minimum */
  double maxOPD             = 0;        /* OPD maximum */
  double ZPD                = 0;
  double lrmmBandPass       = 0.0;      /* low res mm +/- offset from centre */
  int lrBandPassFrames      = 0;        /* Number of low res band pass frames from centre +/- length of lrmmBandPass */
  int nTmp                  = 0;
  int nMax                  = 0;
  int nOPD                  = 0;
  int bolIndex              = 0;
  int index                 = 0;
  int indexIn               = 0;
  int indexOut              = 0;
  int badPixel              = 0;
  int k0                    = 0;
  int indexZPD              = 0;
  int done                  = 0;        /* Track completion of extracting multiple scans */
  int outDataCount          = 0;        /* The number of output data files being written */

  double lenLeft,
         lenRight,
         minLenLeft,
         minLenRight,
         minLen,
         minZPD,
         maxZPD,
         midZPD             = 0.0;      /* Mirror position half side measures */
  int midZPDPos             = 0;        /* Middle ZPD position in mirror position array */

  double EPSILON            = 0.0;
  char fileName[SMF_PATH_MAX+1];
  char scanNumStr[5+1];                 /* String form of scan number of the input file */
  int scanNum               = 0;        /* Scan number of the input file */
  int conNum                = 0;        /* Concatenation number of the input file (left shifted scanNum) */
  int scanDir               = 0;        /* Scan direction: 1 -> back to front (positive), -1 -> front to back (negative) */
  JCMTState *allState       = NULL;     /* Temporary buffer for reduced header allState array data */


  /* Get Input, Output groups */
  kpg1Rgndf("IN", 0, 1, "", &gIn, &nFiles, status);
  kpg1Wgndf("OUT", gOut, nFiles, nFiles, "More output files expected!", &gOut, &nOutFiles, status);

  /* Read in ADAM parameters */
  parGet0d("BANDPASS", &lrmmBandPass, status);          /* Low res mm band +/- offset from centre */

  /* Treat as Low Resolution scan? */
  if(lrmmBandPass > 0) {
      LR = 1;
  }

  /* Eliminate the first record in the output group, since it will be replaced later */
  gTmp = grpCopy(gOut, 1, 1, 1, status);
  grpDelet(&gOut, status);
  gOut = gTmp;

  /* BEGIN NDF */
  ndfBegin();

  /* Loop through each input file */
  for(fIndex = 1; fIndex <= nFiles; fIndex++) {
    /* Open Observation file */
    smf_open_file(gIn, fIndex, "READ", 0, &inData, status);
    if(*status != SAI__OK) {
      *status = SAI__ERROR;
      errRep(FUNC_NAME, "Unable to open the source file!", status);
      goto CLEANUP;
    }

    smf_fits_getS(inData->hdr, "OBJECT", object, sizeof(object), status);
    smf_fits_getS(inData->hdr, "SUBARRAY", subarray, sizeof(subarray), status);
    smf_fits_getS(inData->hdr, "OBSID", obsID, sizeof(obsID), status);
    smf_fits_getS(inData->hdr, "FTS_MODE", scanMode, sizeof(scanMode), status);
    smf_fits_getD(inData->hdr, "SCANVEL", &scanVel, status);
    smf_fits_getD(inData->hdr, "STEPTIME", &stepTime, status);

    /* Nyquist frequency */
    fNyquist = 10.0 / (8.0 * scanVel * stepTime);
    dz = 1.0 / (2.0 * fNyquist);
    EPSILON = scanVel * stepTime / 2;

    /* Extract the scan number from the input file to be incremented in the output files */
    one_strlcpy(scanNumStr, &(inData->file->name[strlen(inData->file->name) - 8]),
               astMIN(SMF_PATH_MAX + 1, 5), status);
    if (*status == ONE__TRUNC) {
        errRep(FUNC_NAME, "Error extracting scanNumStr!", status);
        errAnnul(status);
    }

    /* Create a temporary base file name from input file name */
    one_strlcpy(fileName, inData->file->name,
                astMIN(SMF_PATH_MAX + 1, strlen(inData->file->name) - 7), status);
    if (*status == ONE__TRUNC) {
        errRep(FUNC_NAME, "Error extracting base fileName!", status);
        errAnnul(status);
    }
    scanNum = (int) one_strtod(scanNumStr, status);
    if (*status != SAI__OK) {
        errRep(FUNC_NAME, "Error extracting scanNum!", status);
        errAnnul(status);
    }

    /* Left shift scanNum to conNum as a prefix to make output scan number unique */
    if(scanNum < 100) {
      conNum = scanNum * 100;
    } else if(scanNum < 1000) {
      conNum = scanNum * 10;
    }

    /*printf("%s: Processing file: %s, having basename: %s and scanNumStr: %s, scanNum: %04d\n",
           TASK_NAME, inData->file->name, fileName, scanNumStr, scanNum);*/

    /* Data cube dimensions */
    nWidth  = inData->dims[0];
    nHeight = inData->dims[1];
    nFrames = inData->dims[2];
    nPixels = nWidth * nHeight;

    /* Mirror positions in mm */
    nTmp = nFrames;
    MIRPOS = astCalloc(nFrames, sizeof(*MIRPOS));
    fts2_getmirrorpositions(inData, MIRPOS, &nTmp, status); // (mm)
    if(*status != SAI__OK) {
      *status = SAI__ERROR;
      errRep( FUNC_NAME, "Unable to get the mirror positions!", status);
      goto CLEANUP;
    }

    nStart = -1;
    nStop = -1;
    nStartNext = 0;
    hrStart = -1;
    hrStop = -1;
    lrStart = -1;
    lrStop = -1;
    outDataCount = 0;
    done = 0;
    do {
        /* Find the next range of single scan mirror positions for which to extract corresponding NDF data */
        for(n=nStartNext; n<nFrames-1; n++){
            if(hrStart < 0 && fabs(MIRPOS[n+1] - MIRPOS[n]) >= EPSILON) {
                nStart = n;
                hrStart = n;
                /*printf("%s: Split nStart=%d\n", TASK_NAME, nStart);*/
            }
            if(hrStart >= 0 && hrStop < 0 && (fabs(MIRPOS[n+1] - MIRPOS[n]) < EPSILON || n+1 == nFrames-1)) {
                hrStop = n+1;
                hrFramesOutPrev = hrFramesOut;
                hrFramesOut = abs(hrStop - hrStart) + 1;
                outDataCount++;

                nStop = hrStop;
                nFramesOutPrev = hrFramesOutPrev;
                nFramesOut = hrFramesOut;

                /*printf("%s: Split: %d of %d frames found at hrStart=%d, hrStop=%d\n",
                       TASK_NAME, outDataCount, hrFramesOut, hrStart, hrStop);*/
                break;
            }
        }

        /* Determine scan direction */
        if(MIRPOS[hrStart] < MIRPOS[hrStop]) {
            scanDir = 1;    /* Positive */
        } else {
            scanDir = -1;   /* Negative */
        }

        /* Limit to specified mirror position range */
        if(LR) {
            /* Calculate how many frames correspond to the given +/- mm of LR bandpass */
            lrBandPassFrames = lrmmBandPass / dz;

            /* Find the centre of the current scan */
            lrCentre = floor((abs(hrStop-hrStart)+1)/2);

            /* Set low res start and stop values at corresponding frame offsets from centre */
            lrStart = lrCentre - lrBandPassFrames;
            lrStop = lrCentre + lrBandPassFrames;
            lrFramesOutPrev = lrFramesOut;
            lrFramesOut = abs(lrStop - lrStart) + 1;

            nStart = lrStart;
            nStop = lrStop;
            nFramesOutPrev = lrFramesOutPrev;
            nFramesOut = lrFramesOut;

            /*printf("%s: LR Split: %d of %d frames found at lrStart=%d, lrStop=%d\n",
                   TASK_NAME, outDataCount, lrFramesOut, lrStart, lrStop);*/
        }

        /* Check for end of data condition */
        if(hrStop < hrStart  || hrStop >= nFrames-1) {
            hrStop = nFrames-1;
            done = 1;
        }

        /* Output scan if there is a start and stop position found,
           and for the last scan if it's the only one
           and if it's not too short (compared to the previous one) */
        /*printf("%s: nStart=%d, nStop=%d, nFramesOutPrev=%d, nFramesOut=%d\n", TASK_NAME, nStart, nStop, nFramesOutPrev, nFramesOut);*/
        if(nStart >=0 && nStop > 0 &&
            (nFramesOutPrev == 0 ||
              (nFramesOutPrev > 0 && nFramesOut > 0 && (double)hrFramesOut/(double)hrFramesOutPrev >= 0.5))) {
            /* Copy single scan NDF data from input to output */
            outData = smf_deepcopy_smfData(inData, 0, SMF__NOCREATE_DATA | SMF__NOCREATE_FTS, 0, 0, status);
            outData->dtype   = SMF__DOUBLE;
            outData->ndims   = 3;
            outData->dims[0] = nWidth;
            outData->dims[1] = nHeight;
            outData->dims[2] = nFramesOut;
            outData_pntr = (double*) astMalloc((nPixels * nFramesOut) * sizeof(*outData_pntr));
            outData->pntr[0] = outData_pntr;
            outData->hdr->nframes = nFramesOut;

            for(i=0; i<nWidth; i++) {
                for(j=0; j<nHeight; j++) {
                    bolIndex = i + j * nWidth;
                    for(k=nStart; k<=nStop; k++) {
                        indexIn = bolIndex + k * nPixels;
                        indexOut = bolIndex + (k-nStart) * nPixels;
                        *((double*)(outData->pntr[0]) + indexOut) = *((double*)(inData->pntr[0]) + indexIn);
                    }
                }
            }

            /* Update the FITS headers */
            outData->fts = smf_create_smfFts(status);
            /* Update FITS component */
            smf_fits_updateD(outData->hdr, "FNYQUIST", fNyquist, "Nyquist frequency (cm^-1)", status);
            smf_fits_updateI(outData->hdr, "MIRSTART", 1, "Frame index in which the mirror starts moving", status);
            smf_fits_updateI(outData->hdr, "MIRSTOP", nFramesOut, "Frame index in which the mirror stops moving", status);
            smf_fits_updateI(outData->hdr, "SCANDIR", scanDir, "Scan direction", status);
            smf_fits_updateD(outData->hdr, "OPDMIN", 0.0, "Minimum OPD", status);
            smf_fits_updateD(outData->hdr, "OPDSTEP", 0.0, "OPD step size", status);

            /* Update the JCMTSTATE header */
            /* Reallocate outData header array memory to reduced size */
            allState = (JCMTState*) astRealloc(outData->hdr->allState, nFramesOut * sizeof(*(outData->hdr->allState)));
            if(*status == SAI__OK && allState) {
                outData->hdr->allState = allState;
            } else {
                errRepf(TASK_NAME, "Error reallocating allState JCMTState header", status);
                goto CLEANUP;
            }
            for(k=nStart; k<=nStop; k++) {
                /* Copy over JCMTstate */
                /*printf("%s: memcpy allState: %d to: %p from: %p size: %d\n",TASK_NAME, k,
                       (void *) &(outData->hdr->allState[k-nStart]), (void *) &(inData->hdr->allState[k]), sizeof(*(outData->hdr->allState)) );*/
                memcpy( (void *) &(outData->hdr->allState[k-nStart]), (void *) &(inData->hdr->allState[k]), sizeof(*(outData->hdr->allState)) );

                /*printf("%s: Scan: %d index: %d rts_num: %d\n", TASK_NAME, outDataCount, k-nStart, outData->hdr->allState[k-nStart].rts_num);*/
                /*printf("%s: Scan: %d index: %d fts_pos: %f\n", TASK_NAME, outDataCount, k-nStart, outData->hdr->allState[k-nStart].fts_pos);*/
            }

            /* Write output */
            /* Append unique suffix to fileName */
            /* This must be modified by the concatenation file scan number to improve uniqueness */
            n = one_snprintf(outData->file->name, SMF_PATH_MAX, "%s%04d_scn.sdf", status, fileName, conNum+outDataCount);
            /*printf("%s: Writing outData->file->name: %s\n", TASK_NAME, outData->file->name);*/
            if(n < 0 || n >= SMF_PATH_MAX) {
                errRepf(TASK_NAME, "Error creating outData->file->name", status);
                goto CLEANUP;
            }
            /* Update the list of output _scn file names */
            grpPut1(gOut, outData->file->name, 0, status);
            if(*status != SAI__OK) {
                errRepf(TASK_NAME, "Error saving outData file name", status);
                goto CLEANUP;
            }
            smf_write_smfData(outData, NULL, outData->file->name, gOut, fIndex, 0, MSG__VERB, 0, status);
            if(*status != SAI__OK) {
                errRepf(TASK_NAME, "Error writing outData file", status);
                goto CLEANUP;
            }
            smf_close_file(&outData, status);
            if(*status != SAI__OK) {
                errRepf(TASK_NAME, "Error closing outData file", status);
                goto CLEANUP;
            }
            if(*status != SAI__OK) {
                errRepf(TASK_NAME, "Error closing outData file", status);
                goto CLEANUP;
            }
        }/* else {
            if(!(nStart >=0 && nStop)) printf("%s: Output scan condition failed: nStart(%d) >= nStop(%d) is FALSE\n",TASK_NAME, nStart, nStop);
            if(!(nFramesOutPrev == 0 ||
              (nFramesOutPrev > 0 && nFramesOut > 0 && (double)nFramesOut/(double)nFramesOutPrev >= 0.5))) printf("%s: Output scan condition failed: nFramesOutPrev(%d) == 0 || (nFramesOutPrev(%d) > 0 && nFramesOut(%d) > 0 && nFramesOut/nFramesOutPrev (%f) >= 0.5) is FALSE\n", TASK_NAME, nFramesOutPrev, nFramesOutPrev, nFramesOut, (double)nFramesOut/(double)nFramesOutPrev);
        }*/

        /* Prepare for next iteration */
        nStartNext = hrStop + 1;
        hrStart = -1;
        hrStop = -1;

    } while (!done);


    /* Deallocate memory used by arrays */
    if(MIRPOS)  { MIRPOS    = astFree(MIRPOS); }

    /* Close the file */
    smf_close_file(&inData, status);

  }
  CLEANUP:
  /* Deallocate memory used by arrays */
  if(inData)  { smf_close_file(&inData, status); }
  if(outData) { smf_close_file(&outData, status); }

  /* END NDF */
  ndfEnd(status);

  /* Write out the list of output NDF names, annulling the error if a null
     parameter value is supplied. */
  if( *status == SAI__OK && gOut ) {
      grpList( "OUTFILES", 0, 0, NULL, gOut, status );
          if( *status == PAR__NULL ) {
              errRep(FUNC_NAME, "Error writing OUTFILES!", status);
              errAnnul( status );
          }
  }

  /* Delete groups */
  if(gIn)     { grpDelet(&gIn, status);  }
  if(gOut)    { grpDelet(&gOut, status); }
}
Example #3
0
void kpg1Fit1d( int lbnd, int ubnd, const double y[], const double x[],
                double *m, double *c, double *rms, int *status ){
/*
*  Name:
*     kpg1Fit1d

*  Purpose:
*     Fits a least-squares straight line to supplied data.

*  Language:
*     C.

*  Invocation:
*     void kpg1Fit1d( int lbnd, int ubnd, const double *y, const double *x,
*                     double *m, double *c, double *rms, int *status )

*  Description:
*     A straight line is fitted to the data supplied in X and Y, using
*     the least-squares criterion. Data points lying further than three
*     standard deviations from the the fitted line are rejected and the
*     gradient and intercept are updated to exclude the rejected points.
*     The returned values of M and C are the gradient and intercept of
*     the fit, so that y = M.x + C. The RMS residual of the Y data from
*     the fit, excluding rejected points, is returned in RMS.
*
*     An error is reported if there are less than two good data values
*     in Y, or if the X values cover a range of zero.

*  Arguments:
*     lbnd
*        The lower bound of the X and Y arrays.
*     ubnd
*        The upper bound of the X and Y arrays.
*     y
*        The Y data values. Any bad values are ignored.
*     x
*        The X positions corresponding to each Y data value.
*     m
*        Pointer to a double in which to return the gradient.
*     c
*        Pointer to a double in which to return the intercept.
*     rms
*        Pointer to a double in which to return the RMS residual
*        between the Y values and the fit.
*     status
*        The inherited status.

*  Notes:
*     - The "lbnd" and "ubnd" arguments serve only to determine the
*     number of elements in the supplied "x" and "y" arrays.

*  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 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:
*     DSB: David Berry (JAC, Hawaii)
*     {enter_new_authors_here}

*  History:
*     8-JAN-2010 (DSB):
*        Original version (transliterated form kpg1_fit1d.f).
*     30-SEP-2010 (DSB):
*        Do a single 3 sigma clip.
*     1-OCT-2010 (DSB):
*        - Re-form the expressions for *rms to avoid heavy rounding errors.
*        - Handle perfect straight lines.
*     5-JUL-2013 (DSB):
*        Account for rounding errors when checking for zero values.
*     5-OCT-2013 (DSB):
*        Do not use the gradent before it is calculated !! This would
*        have completely fouled up the calculation of the returned RMS.
*     {enter_changes_here}

*  Bugs:
*     {note_any_bugs_here}

*/

/* Local Variables: */
   double d1,d2,d3,d4,d5; /* Various difference values */
   double denom;     /* Denominator */
   double sx;        /* Sum of X values */
   double sxx;       /* Sum of X squared values */
   double sxy;       /* Sum of X*Y values */
   double sy;        /* Sum of Y values */
   double syy;       /* Sum of Y squared values */
   double fitval;    /* The value of the fitted line */
   double thresh;    /* Rejection threshold */
   double xval;      /* X value */
   double yval;      /* Y value */
   int i;            /* Loop count */
   int n;            /* No. of points in sums */
   int nel;          /* No. of elements in array */

/* Initialise */
   *m = VAL__BADD;
   *c = VAL__BADD;
   *rms = VAL__BADD;

/* Check inherited global status. */
   if( *status != SAI__OK ) return;

/* Initialise the running sums to zero. */
   sy = 0.0;
   sx = 0.0;
   sxy = 0.0;
   sxx = 0.0;
   syy = 0.0;
   n = 0;

/* Loop round finding the sums of all the necessary terms. */
   nel = ubnd - lbnd + 1;
   for( i = 0; i < nel; i++ ) {
      xval = x[ i ];
      yval = y[ i ];
      if( xval != VAL__BADD && yval != VAL__BADD ) {
         sy += yval;
         sx += xval;
         sxy += xval*yval;
         sxx += xval*xval;
         syy += yval*yval;
         n++;
      }
   }

/* Report an error if there are less than 2 good data values. */
   if( n == 0 ) {
     if( *status == SAI__OK ) {
       *status = SAI__ERROR;
       errRepf( " ", "kpg1Fit1d: Out of %d data values supplied none were good", status, nel );
     }
     return;

   } else if( n == 1 ) {
     if( *status == SAI__OK ) {
       *status = SAI__ERROR;
       errRepf( " ", "kpg1Fit1d: Out of %d data values supplied only 1 was good", status, nel );
     }
     return;
   }

/* Form the requied differences, checking for values that are so small
   that they are effectively zero. */
   denom =  n*sxx - sx*sx;
   if( fabs( denom ) <= 10*VAL__EPSD*(n*sxx + sx*sx) ) denom = 0.0;
   d1 =  n*sxy - sx*sy;
   if( fabs( d1 ) <= 10*VAL__EPSD*(n*sxy + sx*sy) ) d1 = 0.0;
   d2 = sxx*sy - sx*sxy;
   if( fabs( d2 ) <= 10*VAL__EPSD*( sxx*sy + sx*sxy ) ) d2 = 0.0;

/* Report an error if the denominator is zero. */
   if( denom == 0 ) {
     if( *status == SAI__OK ) {
       *status = SAI__ERROR;
       errRepf( " ", "kpg1Fit1d: All %d supplied X values are equal", status, nel );
     }
     return;
   }

/* Form the gradient. */
   if( *status == SAI__OK ) {
      *m =  d1/denom;

/* Form the intercept. */
      *c =  d2/denom;

/* Form the RMS residual. */
      d3 = syy - sy*sy/n;
      if( fabs( d3 ) <= 10*VAL__EPSD*( syy + sy*sy/n ) ) d3 = 0.0;
      d4 = sxy - sx*sy/n;
      if( fabs( d4 ) <= 10*VAL__EPSD*( sxy + sx*sy/n ) ) d4 = 0.0;
      d5 =  d3 - (*m)*d4;
      if( fabs( d5 ) <= 10*VAL__EPSD*( d3 + (*m)*d4 ) ) d5 = 0.0;
      *rms = d5/n;

/* Do not reject any points if the RMS is zero (e.g. if the supplied data
   is a perfect straight line). */
      if( *rms <= 0.0 ) {
         *rms = 0.0;

/* If the data is not a perfect straight line, rejected 3.sigma outliers
   and recalculate the fit. */
      } else {
         *rms = sqrt( *rms );

/* Get the rejection threshold. */
         thresh = 3.0*( *rms );

/* For each point that is tto far from the line, remove the point from
   the running sums */
         for( i = 0; i < nel; i++ ) {
            xval = x[ i ];
            yval = y[ i ];
            if( xval != VAL__BADD && yval != VAL__BADD ) {
               fitval = ( *m )*xval + ( *c );
               if( fabs( yval - fitval ) > thresh ) {
                  sy -= yval;
                  sx -= xval;
                  sxy -= xval*yval;
                  sxx -= xval*xval;
                  syy -= yval*yval;
                  n--;
               }
            }
         }

/* Report an error if there are less than 2 good data values. */
         if( n == 0 ) {
           if( *status == SAI__OK ) {
             *status = SAI__ERROR;
             errRep( " ", "kpg1Fit1d: No good data values supplied after sigma clipping", status );
           }
         } else if( n == 1 ) {
           if( *status == SAI__OK ) {
             *status = SAI__ERROR;
             errRep( " ", "kpg1Fit1d: Only 1 good data value found afte sigma clipping", status );
           }
         }

/* Form the denominator used to calculate the returned values. */
         denom =  n*sxx - sx*sx;

/* Report an error if the denominator is zero. */
         if( denom == 0 ) {
           if( *status == SAI__OK ) {
             *status = SAI__ERROR;
             errRep( " ", "kpg1Fit1d: All supplied X values are equal after sigma clipping", status );
           }
         }

/* Form the gradient. */
         if( *status == SAI__OK ) {
            *m =  ( n*sxy - sx*sy )/denom;

/* Form the intercept. */
            *c =  ( sxx*sy - sx*sxy ) /denom;

/* Form the RMS residual. */
            *rms = (syy - sy*sy/n - (*m)*(sxy - sx*sy/n))/n;
            if( *rms <= 0.0 ) {
               *rms = 0.0;
            } else {
               *rms = sqrt( *rms );
            }
         }
      }
   }
}
Example #4
0
static void smf1_rebinmap1( void *job_data_ptr, int *status ) {

    /* Local Variables: */
    SmfRebinMap1Data *pdata;
    dim_t di;                  /* data array index */
    dim_t vi;                  /* variance array index */
    double R;                  /* Another temp variable for variance calc */
    double delta;              /* Offset for weighted mean */
    double temp;               /* Temporary calculation */
    double thisweight;         /* The weight at this point */
    size_t ibolo;              /* Bolometer index */
    size_t ipix;               /* Map pixel index */
    size_t itime;              /* Time slice index */

    /* Check inherited status */
    if( *status != SAI__OK ) return;

    /* Get a pointer that can be used for accessing the required items in the
       supplied structure. */
    pdata = (SmfRebinMap1Data *) job_data_ptr;

    /* Map variance from spread of input values - quality checking version.
       ================================================================== */
    if( pdata->operation == 1 ) {

        /* Loop round all bolometers. */
        for( ibolo = 0; ibolo < pdata->nbolo; ibolo++ ) {

            /* Skip bad bolometers. */
            if( !( (pdata->qual)[ ibolo*(pdata->dbstride) ] & SMF__Q_BADB ) ) {

                /* Loop round all time slices being included in the map. */
                for( itime = pdata->t1; itime <= pdata->t2; itime++ ) {

                    /* Get the 1D vector index of the data sample. */
                    di = ibolo*pdata->dbstride + itime*pdata->dtstride;

                    /* Get the corresponding map pixel index. */
                    ipix = pdata->lut[ di ];

                    /* Skip to the next sample if the map pixel is not being processed by the
                       current thread. */
                    if( ipix >= pdata->p1 && ipix <= pdata->p2 ) {

                        /* Get the 1D vector index of the corresponding variane value. */
                        vi = ibolo*pdata->vbstride +
                             ( itime % pdata->vntslice )*pdata->vtstride;

                        /* Get the offset to the start of the buffer in which to store the pixel
                           value. */
                        if( pdata->whichmap ) {
                            if( pdata->whichmap[ itime ] != VAL__BADI ) {
                                ipix += pdata->whichmap[ itime ]*pdata->msize;
                            } else {
                                ipix = SMF__BADDIMT;
                            }
                        }

                        /* Check that the data and variance values are valid */
                        if( !( pdata->qual[ di ] & pdata->mask ) &&
                                ( pdata->var[ vi ] != 0.0 ) &&
                                ( ipix != SMF__BADDIMT ) ) {

                            /* Update things. */
                            pdata->hitsmap[ ipix ]++;
                            thisweight = 1/pdata->var[ vi ];

                            /* Weighted incremental algorithm */
                            temp = pdata->mapweight[ ipix ] + thisweight;
                            delta = pdata->dat[ di ] - pdata->map[ ipix ];
                            R = delta * thisweight / temp;
                            pdata->map[ ipix ] += R;
                            pdata->mapvar[ ipix ] += pdata->mapweight[ ipix ]*delta*R;
                            pdata->mapweight[ ipix ] = temp;

                            /* We don't need this sum anymore, but calculate it for consistency with the
                               interface */
                            pdata->mapweightsq[ ipix ] += thisweight*thisweight;
                        }
                    }
                }
            }
        }


        /* Map variance from spread of input values - VAL__BADD checking version.
           ==================================================================== */
    } else if( pdata->operation == 1 ) {

        for( ibolo = 0; ibolo < pdata->nbolo; ibolo++ ) {
            for( itime = pdata->t1; itime <= pdata->t2; itime++ ) {

                di = ibolo*pdata->dbstride + itime*pdata->dtstride;
                ipix = pdata->lut[ di ];
                if( ipix >= pdata->p1 && ipix <= pdata->p2 ) {
                    vi = ibolo*pdata->vbstride +
                         ( itime % pdata->vntslice )*pdata->vtstride;

                    if( pdata->whichmap ) {
                        if( pdata->whichmap[ itime ] != VAL__BADI ) {
                            ipix += pdata->whichmap[ itime ]*pdata->msize;
                        } else {
                            ipix = SMF__BADDIMT;
                        }
                    }


                    if( pdata->dat[ di ] != VAL__BADD &&
                            pdata->var[ vi ] != VAL__BADD &&
                            pdata->var[ vi ] != 0.0 && ipix != SMF__BADDIMT ) {

                        pdata->hitsmap[ ipix ]++;
                        thisweight = 1/pdata->var[ vi ];

                        temp = pdata->mapweight[ ipix ] + thisweight;
                        delta = pdata->dat[ di ] - pdata->map[ ipix ];
                        R = delta * thisweight / temp;
                        pdata->map[ ipix ] += R;
                        pdata->mapvar[ ipix ] += pdata->mapweight[ ipix ]*delta*R;
                        pdata->mapweight[ ipix ] = temp;

                        pdata->mapweightsq[ ipix ] += thisweight*thisweight;
                    }
                }
            }
        }

        /* Map variance from error propagation - quality checking version.
           ============================================================== */
    } else if( pdata->operation == 3 ) {

        for( ibolo = 0; ibolo < pdata->nbolo; ibolo++ ) {
            if( !( pdata->qual[ ibolo*pdata->dbstride ] & SMF__Q_BADB ) ) {
                for( itime = pdata->t1; itime <= pdata->t2; itime++ ) {
                    di = ibolo*pdata->dbstride + itime*pdata->dtstride;
                    ipix = pdata->lut[ di ];
                    if( ipix >= pdata->p1 && ipix <= pdata->p2 ) {
                        vi = ibolo*pdata->vbstride +
                             ( itime % pdata->vntslice )*pdata->vtstride;
                        if( pdata->whichmap ) {
                            if( pdata->whichmap[ itime ] != VAL__BADI ) {
                                ipix += pdata->whichmap[ itime ]*pdata->msize;
                            } else {
                                ipix = SMF__BADDIMT;
                            }
                        }

                        if( !( pdata->qual[ di ] & pdata->mask ) &&
                                ( pdata->var[ vi ] != 0.0 ) &&
                                ( ipix != SMF__BADDIMT ) ) {

                            thisweight = 1/pdata->var[ vi ];
                            pdata->map[ ipix ] += thisweight*pdata->dat[ di ];
                            pdata->mapweight[ ipix ] += thisweight;
                            pdata->mapweightsq[ ipix ] += thisweight*thisweight;
                            pdata->hitsmap[ ipix ]++;
                        }
                    }
                }
            }
        }

        /* Map variance from error propagation - VAL__BADD checking version.
           ============================================================== */
    } else if( pdata->operation == 4 ) {

        for( ibolo = 0; ibolo < pdata->nbolo; ibolo++ ) {
            for( itime = pdata->t1; itime <= pdata->t2; itime++ ) {
                di = ibolo*pdata->dbstride + itime*pdata->dtstride;
                ipix = pdata->lut[ di ];
                if( ipix >= pdata->p1 && ipix <= pdata->p2 ) {
                    vi = ibolo*pdata->vbstride +
                         ( itime % pdata->vntslice )*pdata->vtstride;
                    if( pdata->whichmap ) {
                        if( pdata->whichmap[ itime ] != VAL__BADI ) {
                            ipix += pdata->whichmap[ itime ]*pdata->msize;
                        } else {
                            ipix = SMF__BADDIMT;
                        }
                    }

                    if( pdata->dat[ di ] != VAL__BADD &&
                            pdata->var[ vi ] != VAL__BADD &&
                            pdata->var[ vi ] != 0.0 && ipix != SMF__BADDIMT  ) {

                        thisweight = 1/pdata->var[ vi ];
                        pdata->map[ ipix ] += thisweight*pdata->dat[ di ];
                        pdata->mapweight[ ipix ] += thisweight;
                        pdata->mapweightsq[ ipix ] += thisweight*thisweight;
                        pdata->hitsmap[ ipix ]++;
                    }
                }
            }
        }

        /* No variances - quality checking version.
           ========================================= */
    } else if( pdata->operation == 5 ) {

        for( ibolo = 0; ibolo < pdata->nbolo; ibolo++ ) {
            if( !( pdata->qual[ ibolo*pdata->dbstride ] & SMF__Q_BADB ) ) {
                for( itime = pdata->t1; itime <= pdata->t2; itime++ ) {
                    di = ibolo*pdata->dbstride + itime*pdata->dtstride;
                    ipix = pdata->lut[ di ];
                    if( ipix >= pdata->p1 && ipix <= pdata->p2 ) {
                        if( pdata->whichmap ) {
                            if( pdata->whichmap[ itime ] != VAL__BADI ) {
                                ipix += pdata->whichmap[ itime ]*pdata->msize;
                            } else {
                                ipix = SMF__BADDIMT;
                            }
                        }

                        if( !( pdata->qual[ di ] & pdata->mask ) &&
                                ( ipix != SMF__BADDIMT ) ) {
                            pdata->hitsmap[ ipix ]++;
                            temp = pdata->mapweight[ ipix ] + 1.0;
                            delta = pdata->dat[ di ] - pdata->map[ ipix ];
                            R = delta / temp;
                            pdata->map[ ipix ] += R;
                            pdata->mapvar[ ipix ] += pdata->mapweight[ ipix ]*delta*R;
                            pdata->mapweight[ ipix ] = temp;

                            /* We don't need this sum anymore, but calculate it for consistency with the
                               interface */
                            pdata->mapweightsq[ ipix ]++;
                        }
                    }
                }
            }
        }

        /* No variances - VAL__BADD checking version.
           ========================================= */
    } else if( pdata->operation == 6 ) {

        for( ibolo = 0; ibolo < pdata->nbolo; ibolo++ ) {
            for( itime = pdata->t1; itime <= pdata->t2; itime++ ) {
                di = ibolo*pdata->dbstride + itime*pdata->dtstride;
                ipix = pdata->lut[ di ];
                if( ipix >= pdata->p1 && ipix <= pdata->p2 ) {
                    if( pdata->whichmap ) {
                        if( pdata->whichmap[ itime ] != VAL__BADI ) {
                            ipix += pdata->whichmap[ itime ]*pdata->msize;
                        } else {
                            ipix = SMF__BADDIMT;
                        }
                    }

                    if( pdata->dat[ di ] != VAL__BADD &&
                            ipix != SMF__BADDIMT ) {
                        pdata->hitsmap[ ipix ]++;
                        temp = pdata->mapweight[ ipix ] + 1.0;
                        delta = pdata->dat[ di ] - pdata->map[ ipix ];
                        R = delta / temp;
                        pdata->map[ ipix ] += R;
                        pdata->mapvar[ ipix ] += pdata->mapweight[ ipix ]*delta*R;
                        pdata->mapweight[ ipix ] = temp;

                        /* We don't need this sum anymore, but calculate it for consistency with the
                           interface */
                        pdata->mapweightsq[ ipix ]++;
                    }
                }
            }
        }


        /* Final normalisation - input sample variance
           ========================================= */
    } else if( pdata->operation == 7 ) {

        pdata->scaleweight=0;
        pdata->scalevar=0;

        for( ipix = pdata->p1; ipix <= pdata->p2; ipix++ ) {

            /* If 0 weight set pixels to bad */
            if( pdata->mapweight[ ipix ] == 0.0 ) {
                pdata->mapweight[ ipix ] = VAL__BADD;
                pdata->map[ ipix ] = VAL__BADD;
                pdata->mapvar[ ipix ] = VAL__BADD;

                /* Otherwise re-normalize... although variance only reliable if we had
                   enough samples */
            } else if( pdata->hitsmap[ ipix ] >= SMF__MINSTATSAMP ) {
                double variance_n = pdata->mapvar[ ipix ]/pdata->mapweight[ ipix ];
                pdata->mapvar[ ipix ] = variance_n/( (double)(pdata->hitsmap[ ipix ]) - 1 );

                /* Work out average scale factor so that supplied weights would produce the
                   same map variance estimate as the sample variance calculation that we just
                   did. The average value is weighted by number of hits in the pixel to weight
                   well-sampled pixels more heavily */
                pdata->scalevar += pdata->hitsmap[ ipix ]*pdata->mapvar[ ipix ]
                                   *pdata->mapweight[ ipix ];
                pdata->scaleweight += pdata->hitsmap[ ipix ];
            } else {
                pdata->mapvar[ ipix ] = VAL__BADD;
            }
        }

        /* Final normalisation - error propagation
           ========================================= */
    } else if( pdata->operation == 8 ) {

        for( ipix = pdata->p1; ipix <= pdata->p2; ipix++ ) {

            /* If 0 weight set pixels to bad */
            if( pdata->mapweight[ ipix ] == 0.0 ) {
                pdata->mapweight[ ipix ] = VAL__BADD;
                pdata->mapweightsq[ ipix ] = VAL__BADD;
                pdata->map[ ipix ] = VAL__BADD;
                pdata->mapvar[ ipix ] = VAL__BADD;

                /* Otherwise re-normalize */
            } else {
                pdata->mapvar[ ipix ] = 1.0/pdata->mapweight[ ipix ];
                pdata->map[ ipix ] *= pdata->mapvar[ ipix ];
            }
        }


        /* Report an error if the worker was to do an unknown job.
           ====================================================== */
    } else {
        *status = SAI__ERROR;
        errRepf( "", "smf1_rebinmap1: Invalid operation (%d) supplied.",
                 status, pdata->operation );
    }
}
Example #5
0
void smf_rebinmap1( ThrWorkForce *wf, smfData *data, smfData *variance, int *lut,
                    size_t tslice1, size_t tslice2, int trange,
                    int *whichmap, dim_t nmap, smf_qual_t mask, int sampvar,
                    int flags, double *map, double *mapweight,
                    double *mapweightsq, int *hitsmap,
                    double *mapvar, dim_t msize, double *scalevariance,
                    int *status ) {

    /* Local Variables */
    SmfRebinMap1Data *job_data = NULL;
    SmfRebinMap1Data *pdata;
    double *dat=NULL;          /* Pointer to data array */
    size_t dbstride;           /* bolo stride of data */
    size_t dtstride;           /* tstride of data */
    int iw;                    /* Thread index */
    dim_t mbufsize;            /* Size of full (multi-map) map buffers */
    dim_t nbolo;               /* number of bolos */
    dim_t ntslice;             /* number of time slices */
    int nw;                    /* Number of worker threads */
    size_t pixstep;            /* Number of map pixels per thread */
    smf_qual_t * qual = NULL;  /* Quality pointer */
    double scalevar;           /* variance scale factor */
    double scaleweight;        /* weights for calculating scalevar */
    size_t t1, t2;             /* range of time slices to re-grid */
    double *var=NULL;          /* Pointer to variance array */
    size_t vbstride;           /* bolo stride of variance */
    dim_t vnbolo;              /* number of bolos in variance */
    dim_t vntslice;            /* number of bolos in variance */
    size_t vtstride;           /* tstride of variance */

    /* Main routine */
    if (*status != SAI__OK) return;

    /* Check inputs */
    if( !data || !map || !lut || !mapweight || !mapweightsq || !mapvar ||
            !hitsmap ) {
        *status = SAI__ERROR;
        errRep(" ", FUNC_NAME ": Null inputs", status );
        return;
    }

    if( !data->pntr[0] ) {
        *status = SAI__ERROR;
        errRep(" ", FUNC_NAME ": supplied data is empty", status );
        return;
    }

    dat = data->pntr[0];
    qual = smf_select_qualpntr( data, NULL, status );
    smf_get_dims( data, NULL, NULL, &nbolo, &ntslice, NULL, &dbstride,
                  &dtstride, status );

    /* Size of full map buffers */
    if( whichmap ) {
        mbufsize = nmap * msize;
    } else {
        mbufsize = msize;
    }

    if( variance ) {
        var = variance->pntr[0];
        smf_get_dims( variance, NULL, NULL, &vnbolo, &vntslice, NULL, &vbstride,
                      &vtstride, status );

        /* Check that the variance dimensions are compatible with data */
        if( (*status==SAI__OK) &&
                ((vnbolo != nbolo) || ((vntslice>1)&&(vntslice!=ntslice))) ) {
            *status = SAI__ERROR;
            errRep(" ", FUNC_NAME ": variance dimensions incompatible with data",
                   status );
            return;
        }
    }

    /* Range of time slices to regrid */
    if( trange ) {

        if( tslice2 >= ntslice ) {
            *status = SAI__ERROR;
            errRepf( "", FUNC_NAME ": tslice2 (%zu) can't be >= ntslice (%zu)",
                     status, tslice2, ntslice );
            return;
        }

        if( tslice1 > tslice2  ) {
            *status = SAI__ERROR;
            errRepf( "", FUNC_NAME ": tslice1 (%zu) > tslice2 (%zu)",
                     status, tslice1, tslice2 );
            return;
        }

        t1 = tslice1;
        t2 = tslice2;
    } else {
        t1 = 0;
        t2 = ntslice-1;
    }

    /* If this is the first data to be accumulated zero the arrays */
    if( flags & AST__REBININIT ) {
        memset( map, 0, mbufsize*sizeof(*map) );
        memset( mapweight, 0, mbufsize*sizeof(*mapweight) );
        memset( mapweightsq, 0, mbufsize*sizeof(*mapweightsq) );
        memset( mapvar, 0, mbufsize*sizeof(*mapvar) );
        memset( hitsmap, 0, mbufsize*sizeof(*hitsmap) );
    }


    /* How many threads do we get to play with */
    nw = wf ? wf->nworker : 1;

    /* Find how many map pixels to process in each worker thread. */
    pixstep = msize/nw;
    if( pixstep == 0 ) pixstep = 1;

    /* Allocate job data for threads, and store the range of pixels to be
       processed by each one. Ensure that the last thread picks up any
       left-over time pixels. */
    job_data = astCalloc( nw, sizeof(*job_data) );
    if( *status == SAI__OK ) {
        for( iw = 0; iw < nw; iw++ ) {
            pdata = job_data + iw;
            pdata->p1 = iw*pixstep;
            if( iw < nw - 1 ) {
                pdata->p2 = pdata->p1 + pixstep - 1;
            } else {
                pdata->p2 = msize - 1 ;
            }

            /* Store other values common to all jobs. */
            pdata->msize = msize;
            pdata->nbolo = nbolo;
            pdata->t1 = t1;
            pdata->t2 = t2;
            pdata->vntslice = vntslice;
            pdata->dat = dat;
            pdata->map = map;
            pdata->mapvar = mapvar;
            pdata->mapweightsq = mapweightsq;
            pdata->mapweight = mapweight;
            pdata->var = var;
            pdata->hitsmap = hitsmap;
            pdata->lut = lut;
            pdata->whichmap = whichmap;
            pdata->dbstride = dbstride;
            pdata->dtstride = dtstride;
            pdata->vbstride = vbstride;
            pdata->vtstride = vtstride;
            pdata->mask = mask;
            pdata->qual = qual;
            pdata->mbufsize = mbufsize;
        }
    }


    if( var ) {
        /* Accumulate data and weights in the case that variances are given*/

        if( sampvar ) {

            /* Measure weighted sample variance for varmap */
            if( qual ) {       /* QUALITY checking version */

                /* Set up jobs to add the previous estimate of COM back on to the
                   residuals, and then wait for the jobs to complete. These jobs also
                   clear any SMF__Q_COM flags set by previous iterations. */
                for( iw = 0; iw < nw; iw++ ) {
                    pdata = job_data + iw;
                    pdata->operation = 1;
                    thrAddJob( wf, 0, pdata, smf1_rebinmap1, 0, NULL, status );
                }
                thrWait( wf, status );

            } else {           /* VAL__BADD checking version */
                for( iw = 0; iw < nw; iw++ ) {
                    pdata = job_data + iw;
                    pdata->operation = 2;
                    thrAddJob( wf, 0, pdata, smf1_rebinmap1, 0, NULL, status );
                }
                thrWait( wf, status );

            }

        } else {
            /* Otherwise use simple error propagation for varmap */

            if( qual ) {       /* QUALITY checking version */
                for( iw = 0; iw < nw; iw++ ) {
                    pdata = job_data + iw;
                    pdata->operation = 3;
                    thrAddJob( wf, 0, pdata, smf1_rebinmap1, 0, NULL, status );
                }
                thrWait( wf, status );

            } else {           /* VAL__BADD checking version */
                for( iw = 0; iw < nw; iw++ ) {
                    pdata = job_data + iw;
                    pdata->operation = 4;
                    thrAddJob( wf, 0, pdata, smf1_rebinmap1, 0, NULL, status );
                }
                thrWait( wf, status );

            }
        }


    } else {
        /* Accumulate data and weights when no variances are given. In this case
           the variance map is always estimated from the sample variance */

        if( qual ) {       /* QUALITY checking version */
            for( iw = 0; iw < nw; iw++ ) {
                pdata = job_data + iw;
                pdata->operation = 5;
                thrAddJob( wf, 0, pdata, smf1_rebinmap1, 0, NULL, status );
            }
            thrWait( wf, status );

        } else {           /* VAL__BADD checking version */
            for( iw = 0; iw < nw; iw++ ) {
                pdata = job_data + iw;
                pdata->operation = 6;
                thrAddJob( wf, 0, pdata, smf1_rebinmap1, 0, NULL, status );
            }
            thrWait( wf, status );
        }
    }

    /* If this is the last data to be accumulated re-normalize */
    if( flags & AST__REBINEND ) {

        /* Find how many buffer pixels to process in each worker thread. May be
           different to the number of map pixels set up earlier. */
        pixstep = mbufsize/nw;
        if( pixstep == 0 ) pixstep = 1;

        for( iw = 0; iw < nw; iw++ ) {
            pdata = job_data + iw;
            pdata->p1 = iw*pixstep;
            if( iw < nw - 1 ) {
                pdata->p2 = pdata->p1 + pixstep - 1;
            } else {
                pdata->p2 = mbufsize - 1 ;
            }
        }


        if( sampvar || !var ) {

            for( iw = 0; iw < nw; iw++ ) {
                pdata = job_data + iw;
                pdata->operation = 7;
                thrAddJob( wf, 0, pdata, smf1_rebinmap1, 0, NULL, status );
            }
            thrWait( wf, status );

            scaleweight=0;
            scalevar=0;
            for( iw = 0; iw < nw; iw++ ) {
                pdata = job_data + iw;
                scaleweight += pdata->scaleweight;
                scalevar += pdata->scalevar;
            }

            /* Re-normalize scalevar */
            if( scaleweight ) {
                scalevar /= scaleweight;

                if( scalevariance ) {
                    *scalevariance = scalevar;
                }
            }

        } else {
            /* Re-normalization for error propagation case */

            for( iw = 0; iw < nw; iw++ ) {
                pdata = job_data + iw;
                pdata->operation = 8;
                thrAddJob( wf, 0, pdata, smf1_rebinmap1, 0, NULL, status );
            }
            thrWait( wf, status );

        }
    }

    job_data = astFree( job_data );

}
Example #6
0
void smurf_jsatileinfo( int *status ) {

/* Local Variables */
   AstRegion *region;
   AstFitsChan *fc;
   AstFrameSet *fs;
   HDSLoc *cloc = NULL;
   HDSLoc *xloc = NULL;
   char *jcmt_tiles;
   char *tilendf = NULL;
   char text[ 200 ];
   int axes[ 2 ];
   double dec[ 9 ];
   double dist;
   double gx[ 9 ];
   double gy[ 9 ];
   double maxdist;
   double norm_radec[2]; /* array to pass to astNorm */
   double point1[ 2 ];
   double point2[ 2 ];
   double ra[ 9 ];
   int create;
   int dirlen;
   int el;
   int exists;
   int i;
   int j;
   int indf1;
   int indf2;
   int indf3;
   int itile;
   int lbnd[ 2 ];
   int tlbnd[ 2 ];
   int tubnd[ 2 ];
   int local_origin;
   int nc;
   int place;
   int ubnd[ 2 ];
   double dlbnd[ 2 ];
   double dubnd[ 2 ];
   smfJSATiling skytiling;
   void *pntr;
   int *ipntr;
   AstCmpRegion *overlap;
   AstRegion *target;
   AstObject *obj;
   int flag;

/* Check inherited status */
   if( *status != SAI__OK ) return;

/* Start a new AST context. */
   astBegin;

/* Get the instrument to use abnd get the parameters describing the
   layout of its JSA tiles. */
   smf_jsainstrument( "INSTRUMENT", NULL, SMF__INST_NONE, &skytiling,
                      status );

/* Return the maximum tile index. */
   parPut0i( "MAXTILE", skytiling.ntiles - 1, status );

/* Abort if an error has occurred. */
   if( *status != SAI__OK ) goto L999;

/* If required, create an all-sky NDF in which each pixel covers the area
   of a single tile, and holds the integer tile index. The NDF has an
   initial size of 1x1 pixels, but is expanded later to the required size. */
   lbnd[ 0 ] = ubnd[ 0 ] = lbnd[ 1 ] = ubnd[ 1 ] = 1;
   ndfCreat( "ALLSKY", "_INTEGER", 2, lbnd, ubnd, &indf3, status );

/* If a null (!) value was supplied for parameter ALLSKY, annull the
   error and pass on. */
   if( *status == PAR__NULL ) {
      errAnnul( status );

/* Otherwise, create a FrameSet describing the whole sky in which each
   pixel corresponds to a single tile. */
   } else {
      smf_jsatile( -1, &skytiling, 0, NULL, &fs, NULL, lbnd, ubnd, status );

/* Change the bounds of the output NDF. */
      ndfSbnd( 2, lbnd, ubnd, indf3, status );

/* Store the FrameSet in the NDF. */
      ndfPtwcs( fs, indf3, status );

/* Map the data array. */
      ndfMap( indf3, "Data", "_INTEGER", "WRITE", (void **) &ipntr, &el,
              status );

/* Loop round every row and column */
      for( j = 0; j < ubnd[ 1 ] - lbnd[ 1 ] + 1; j++ ) {
         for( i = 0; i < ubnd[ 0 ] - lbnd[ 0 ] + 1; i++ ) {

/* Store the tile index at this pixel. */
            *(ipntr++) = smf_jsatilexy2i( i, j, &skytiling, status );
         }
      }

/* Store NDF title. */
      sprintf( text, "JSA tile indices for %s data", skytiling.name );
      ndfCput( text, indf3, "TITLE", status );

/* Store the instrument as a component in the SMURF extension. */
      ndfXnew( indf3, "SMURF", "INSTRUMENT", 0, 0, &xloc, status );
      ndfXpt0c( skytiling.name, indf3, "SMURF", "INSTRUMENT", status );
      datAnnul( &xloc, status );

/* Close the NDF. */
      ndfAnnul( &indf3, status );
   }

/* Abort if an error has occurred. */
   if( *status != SAI__OK ) goto L999;

/* Get the zero-based index of the required tile. If a null value is
   supplied, annull the error and skip to the end. */
   parGdr0i( "ITILE", 0, 0, skytiling.ntiles - 1, 0, &itile, status );
   if( *status == PAR__NULL ) {
       errAnnul( status );
       goto L999;
   }

/* See if the pixel origin is to be at the centre of the tile. */
   parGet0l( "LOCAL", &local_origin, status );

/* Display the tile number. */
   msgBlank( status );
   msgSeti( "ITILE", itile );
   msgSeti( "MAXTILE", skytiling.ntiles - 1);
   msgOut( " ", "   Tile ^ITILE (from 0 to ^MAXTILE):", status );

/* Get the FITS header, FrameSet and Region defining the tile, and the tile
   bounds in pixel indices. */
   smf_jsatile( itile, &skytiling, local_origin, &fc, &fs, &region, lbnd,
                ubnd, status );

/* Write the FITS headers out to a file, annulling the error if the
   header is not required. */
   if( *status == SAI__OK ) {
      atlDumpFits( "HEADER", fc, status );
      if( *status == PAR__NULL ) errAnnul( status );
   }

/* If required, write the Region out to a text file. */
   if( *status == SAI__OK ) {
      atlCreat( "REGION", (AstObject *) region, status );
      if( *status == PAR__NULL ) errAnnul( status );
   }

/* Store the lower and upper pixel bounds of the tile. */
   parPut1i( "LBND", 2, lbnd, status );
   parPut1i( "UBND", 2, ubnd, status );

/* Display pixel bounds on the screen. */
   msgSeti( "XL", lbnd[ 0 ] );
   msgSeti( "XU", ubnd[ 0 ] );
   msgSeti( "YL", lbnd[ 1 ] );
   msgSeti( "YU", ubnd[ 1 ] );
   msgOut( " ", "      Pixel bounds: (^XL:^XU,^YL:^YU)", status );

/* Transform the region centre and a collection of points on the edge
   of the region (corners and side mid-points) from GRID coords to RA,Dec. */
   point1[ 0 ] = 0.5;
   point1[ 1 ] = 0.5;
   point2[ 0 ] = ubnd[ 0 ] - lbnd[ 0 ] + 1;
   point2[ 1 ] = ubnd[ 1 ] - lbnd[ 1 ] + 1;

   gx[ 0 ] = 0.5*( point1[ 0 ] + point2[ 0 ] );   /* Centre */
   gy[ 0 ] = 0.5*( point1[ 1 ] + point2[ 1 ] );

   gx[ 1 ] = point1[ 0 ];      /* Bottom left */
   gy[ 1 ] = point1[ 1 ];

   gx[ 2 ] = point1[ 0 ];      /* Centre left */
   gy[ 2 ] = gy[ 0 ];

   gx[ 3 ] = point1[ 0 ];      /* Top left */
   gy[ 3 ] = point2[ 1 ];

   gx[ 4 ] = gx[ 0 ];          /* Top centre */
   gy[ 4 ] = point2[ 1 ];

   gx[ 5 ] = point2[ 0 ];      /* Top right */
   gy[ 5 ] = point2[ 1 ];

   gx[ 6 ] = point2[ 0 ];      /* Centre right */
   gy[ 6 ] = gy[ 0 ];

   gx[ 7 ] = point2[ 0 ];      /* Bottom right */
   gy[ 7 ] = point1[ 1 ];

   gx[ 8 ] = gx[ 0 ];          /* Bottom centre */
   gy[ 8 ] = point1[ 1 ];

   astTran2( fs, 9, gx, gy, 1, ra, dec );

/* Format the central RA and Dec. and display.
   Call astNorm on the coordinates provided that the frame set has the
   correct number of axes.  (Which it should as it comes from
   smf_jsatile.) */
   norm_radec[0] = ra[0];
   norm_radec[1] = dec[0];
   if (astGetI(fs, "Naxes") == 2) {
      astNorm(fs, norm_radec);
   }
   msgSetc( "RACEN",  astFormat( fs, 1, norm_radec[ 0 ] ));
   msgSetc( "DECCEN",  astFormat( fs, 2, norm_radec[ 1 ] ));
   msgOut( " ", "      Centre (ICRS): RA=^RACEN DEC=^DECCEN", status );

/* Write the tile centre ra and dec in radians to the output parameters. */
   parPut0d( "RACEN", ra[ 0 ], status );
   parPut0d( "DECCEN", dec[ 0 ], status );

/* Find the arc-distance from the centre to the furthest point from the
   centre. */
   point1[ 0 ] = ra[ 0 ];
   point1[ 1 ] = dec[ 0 ];
   maxdist = -1.0;

   for( i = 1; i < 9; i++ ) {
      point2[ 0 ] = ra[ i ];
      point2[ 1 ] = dec[ i ];
      dist = astDistance( fs, point1, point2 );
      if( dist > maxdist ) maxdist = dist;
   }

/* Format this size as a dec value (i.e. arc-distance) and display it. */
   msgSetc( "SIZE",  astFormat( fs, 2, maxdist ) );
   msgOut( " ", "      Size: ^SIZE", status );

/* Write the size to the output parameter as radians. */
   parPut0d( "SIZE", maxdist, status );

/* Get the translation of the environment variable JSA_TILE_DIR. */
   jcmt_tiles = getenv( "JSA_TILE_DIR" );

/* Initialise the path to the tile's NDF to hold the root directory.
   Use the current working directory if JSA_TILE_DIR is undefined. */
   if( jcmt_tiles ) {
      nc = 0;
      tilendf = astAppendString( tilendf, &nc, jcmt_tiles );

   } else {

      nc = 512;
      jcmt_tiles = astMalloc( nc );

      while( !getcwd( jcmt_tiles, nc ) ) {
         nc *= 2;
         jcmt_tiles = astRealloc( jcmt_tiles, nc );
      }

      nc = 0;
      tilendf = astAppendString( tilendf, &nc, jcmt_tiles );
      jcmt_tiles = astFree( jcmt_tiles );
   }

/* Complete the path to the tile's NDF. */
   tilendf = astAppendString( tilendf, &nc, "/" );
   tilendf = astAppendString( tilendf, &nc, skytiling.subdir );
   dirlen = nc;
   sprintf( text, "/tile_%d.sdf", itile );
   tilendf = astAppendString( tilendf, &nc, text );

/* Write it to the output parameter. */
   parPut0c( "TILENDF", tilendf, status );

/* See if the NDF exists, and store the flag in the output parameter. */
   exists = access( tilendf, F_OK ) ? 0 : 1;
   parPut0l( "EXISTS", exists, status );

/* If the NDF does not exist, create it if required. */
   parGet0l( "CREATE", &create, status );
   if( !exists && create && *status == SAI__OK ) {

/* Write the NDF info to the screen. */
      msgSetc( "NDF",  tilendf );
      msgOutif( MSG__NORM, " ", "      NDF: ^NDF (created)", status );

/* Temporarily terminate the NDF path at the end of the subdirectory. */
      tilendf[ dirlen ] = 0;

/* Create the required directory (does nothing if the directory
   already exists).  It is given read/write/search permissions for owner
   and group, and read/search permissions for others. */
      (void) mkdir( tilendf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH );

/* Replace the character temporarily overwritten above. */
      tilendf[ dirlen ] = '/';

/* Now create the tile's NDF. */
      ndfPlace( NULL, tilendf, &place, status );
      ndfNew( skytiling.type, 2, lbnd, ubnd, &place, &indf1, status );

/* Fill its data array with zeros. */
      ndfMap( indf1, "Data", skytiling.type, "WRITE/ZERO", &pntr, &el,
              status );

/* Store the WCS FrameSet. */
      ndfPtwcs( fs, indf1, status );

/* If the instrument jsatiles.have variance, fill the variance array with zeros. */
      if( skytiling.var ) {
         ndfMap( indf1, "Variance", skytiling.type, "WRITE/ZERO", &pntr,
                 &el, status );
      }

/* Create a SMURF extension. */
      ndfXnew( indf1, SMURF__EXTNAME, SMURF__EXTTYPE, 0, NULL, &xloc, status );

/* Store the tile number and instrument name in the extension. */
      datNew0I( xloc, "TILE", status );
      datFind( xloc, "TILE", &cloc, status );
      datPut0I( cloc, itile, status );
      datAnnul( &cloc, status );

      datNew0C( xloc, "INSTRUMENT", strlen( skytiling.name ), status );
      datFind( xloc, "INSTRUMENT", &cloc, status );
      datPut0C( cloc, skytiling.name, status );
      datAnnul( &cloc, status );

/* Create a weights NDF within the SMURF extension, and fill its data
   array with zeros. */
      ndfPlace( xloc, "WEIGHTS", &place, status );
      ndfNew( skytiling.type, 2, lbnd, ubnd, &place, &indf2, status );
      ndfMap( indf2, "Data", skytiling.type, "WRITE/ZERO", &pntr, &el,
              status );
      ndfPtwcs( fs, indf2, status );
      ndfAnnul( &indf2, status );

/* Annul the extension locator and the main NDF identifier. */
      datAnnul( &xloc, status );
      ndfAnnul( &indf1, status );

/* Write the NDF info to the screen. */
   } else {
      msgSetc( "NDF",  tilendf );
      msgSetc( "E",  exists ? "exists" : "does not exist" );
      msgOut( " ", "      NDF: ^NDF (^E)", status );
   }

/* Initialise TBND and TLBND to indicate no overlap. */
   tlbnd[ 0 ] = 1;
   tlbnd[ 1 ] = 1;
   tubnd[ 0 ] = 0;
   tubnd[ 1 ] = 0;

/* Attempt to to get an AST Region (assumed to be in some 2D sky coordinate
   system) using parameter "TARGET". */
   if( *status == SAI__OK ) {
      kpg1Gtobj( "TARGET", "Region",
                 (void (*)( void )) F77_EXTERNAL_NAME(ast_isaregion),
                 &obj, status );

/* Annul the error if none was obtained. */
      if( *status == PAR__NULL ) {
         errAnnul( status );

/* Otherwise, use the supplied object. */
      } else {
         target = (AstRegion *) obj;

/* If the target Region is 3-dimensional, remove the third axis, which
   is assumed to be a spectral axis. */
         if( astGetI( target, "Naxes" ) == 3 ) {
            axes[ 0 ] = 1;
            axes[ 1 ] = 2;
            target = astPickAxes( target, 2, axes, NULL );
         }

/* See if there is any overlap between the target and the tile. */
         overlap = NULL;
         flag = astOverlap( region, target );

         if( flag == 0 ) {
            msgOut( "", "      Cannot convert between the coordinate system of the "
                    "supplied target and the tile.", status );

         } else if( flag == 1 || flag == 6 ) {
            msgOut( "", "      There is no overlap between the target and the tile.",
                    status );

         } else if( flag == 2 ) {
            msgOut( "", "      The tile is contained within the target.",
                    status );
            tlbnd[ 0 ] = lbnd[ 0 ];
            tlbnd[ 1 ] = lbnd[ 1 ];
            tubnd[ 0 ] = ubnd[ 0 ];
            tubnd[ 1 ] = ubnd[ 1 ];

         } else if( flag == 3 ) {
            overlap = astCmpRegion( region, target, AST__AND, " " );

         } else if( flag == 4 ) {
            overlap = astCmpRegion( region, target, AST__AND, " " );

         } else if( flag == 5 ) {
            msgOut( "", "      The target and tile are identical.",
                    status );
            tlbnd[ 0 ] = lbnd[ 0 ];
            tlbnd[ 1 ] = lbnd[ 1 ];
            tubnd[ 0 ] = ubnd[ 0 ];
            tubnd[ 1 ] = ubnd[ 1 ];

         } else if( *status == SAI__OK ) {
            *status = SAI__OK;
            errRepf( "", "Unexpected value %d returned by astOverlap "
                     "(programming error).", status, flag );
         }

/* If a region containing the intersection of the tile and target was
   created above, map it into the grid coordinate system of the tile. */
         if( overlap ) {
            overlap = astMapRegion( overlap, astGetMapping( fs, AST__CURRENT,
                                                            AST__BASE ),
                                    astGetFrame( fs, AST__BASE ) );

/* Get its GRID bounds. */
            astGetRegionBounds( overlap, dlbnd, dubnd );

/* Convert to integer. */
            tlbnd[ 0 ] = ceil( dlbnd[ 0 ] - 0.5 );
            tlbnd[ 1 ] = ceil( dlbnd[ 1 ] - 0.5 );
            tubnd[ 0 ] = ceil( dubnd[ 0 ] - 0.5 );
            tubnd[ 1 ] = ceil( dubnd[ 1 ] - 0.5 );

/* Convert to PIXEL indicies within the tile. */
            tlbnd[ 0 ] += lbnd[ 0 ] - 1;
            tlbnd[ 1 ] += lbnd[ 1 ] - 1;
            tubnd[ 0 ] += lbnd[ 0 ] - 1;
            tubnd[ 1 ] += lbnd[ 1 ] - 1;

            msgOutf( "", "      The target overlaps section (%d:%d,%d:%d).",
                     status, tlbnd[ 0 ], tubnd[ 0 ], tlbnd[ 1 ], tubnd[ 1 ] );
         }
      }
   }

/* Store the pixel index bounds of the tiles overlap with the target. */
   parPut1i( "TLBND", 2, tlbnd, status );
   parPut1i( "TUBND", 2, tubnd, status );

/* Arrive here if an error occurs. */
   L999:;

/* Free resources. */
   tilendf = astFree( tilendf );

/* End the AST context. */
   astEnd;

/* Issue a status indication.*/
   msgBlank( status );
   if( *status == SAI__OK ) {
      msgOutif( MSG__VERB, "", "JSATILEINFO succeeded.", status);
   } else {
      msgOutif( MSG__VERB, "", "JSATILEINFO failed.", status);
   }
}