Exemplo n.º 1
0
void smf_rebinsparse( smfData *data, int first, int *ptime, AstFrame *ospecfrm,
                      AstMapping *ospecmap, AstSkyFrame *oskyframe,
                      Grp *detgrp, int lbnd_out[ 3 ], int ubnd_out[ 3 ],
                      int genvar, float *data_array, float *var_array,
                      int *ispec, float *texp_array, float *teff_array,
                      double *fcon, int *status ){

/* Local Variables */
   AstCmpMap *fmap = NULL;      /* Mapping from spectral grid to topo freq Hz */
   AstCmpMap *ssmap = NULL;     /* I/p GRID-> o/p PIXEL Mapping for spectral axis */
   AstFitsChan *fc = NULL;      /* Storage for FITS headers */
   AstFrame *specframe = NULL;  /* Spectral Frame in input FrameSet */
   AstFrame *specframe2 = NULL; /* Temporary copy of SpecFrame in input WCS */
   AstFrameSet *fs = NULL;      /* A general purpose FrameSet pointer */
   AstFrameSet *swcsin = NULL;  /* FrameSet describing spatial input WCS */
   AstMapping *fsmap = NULL;    /* Base->Current Mapping extracted from a FrameSet */
   AstMapping *specmap = NULL;  /* PIXEL -> Spec mapping in input FrameSet */
   char *fftwin = NULL;  /* Name of FFT windowing function */
   const char *name = NULL; /* Pointer to current detector name */
   const double *tsys=NULL; /* Pointer to Tsys value for first detector */
   dim_t timeslice_size; /* No of detector values in one time slice */
   double *spectab = NULL;/* Workspace for spectral output grid positions */
   double *xin = NULL;   /* Workspace for detector input grid positions */
   double *xout = NULL;  /* Workspace for detector output pixel positions */
   double *yin = NULL;   /* Workspace for detector input grid positions */
   double *yout = NULL;  /* Workspace for detector output pixel positions */
   double at;            /* Frequency at which to take the gradient */
   double dnew;          /* Channel width in Hz */
   double fcon2;         /* Variance factor for whole file */
   double k;             /* Back-end degradation factor */
   double tcon;          /* Variance factor for whole time slice */
   float *pdata = NULL;  /* Pointer to next data sample */
   float *qdata = NULL;  /* Pointer to next data sample */
   float rtsys;          /* Tsys value */
   float teff;           /* Effective integration time, times 4 */
   float texp;           /* Total time ( = ton + toff ) */
   float toff;           /* Off time */
   float ton;            /* On time */
   int *nexttime = NULL; /* Pointer to next time slice index to use */
   int dim[ 3 ];         /* Output array dimensions */
   int found;            /* Was current detector name found in detgrp? */
   int good;             /* Are there any good detector samples? */
   int ibasein;          /* Index of base Frame in input FrameSet */
   int ichan;            /* Index of current channel */
   int iv;               /* Offset to next element */
   int iz;               /* Output grid index on axis 3 */
   int nchan;            /* Number of input spectral channels */
   int pixax[ 3 ];       /* The output fed by each selected mapping input */
   int specax;           /* Index of spectral axis in input FrameSet */
   size_t irec;          /* Index of current input detector */
   size_t itime;         /* Index of current time slice */
   smfHead *hdr = NULL;  /* Pointer to data header for this time slice */

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

/* Begin an AST context.*/
   astBegin;

/* Store a pointer to the input NDFs smfHead structure. */
   hdr = data->hdr;

/* Store the dimensions of the output array. */
   dim[ 0 ] = ubnd_out[ 0 ] - lbnd_out[ 0 ] + 1;
   dim[ 1 ] = ubnd_out[ 1 ] - lbnd_out[ 1 ] + 1;
   dim[ 2 ] = ubnd_out[ 2 ] - lbnd_out[ 2 ] + 1;

/* Store the number of pixels in one time slice */
   timeslice_size = (data->dims)[ 0 ]*(data->dims)[ 1 ];

/* We want a description of the spectral WCS axis in the input file. If
   the input file has a WCS FrameSet containing a SpecFrame, use it,
   otherwise we will obtain it from the FITS header later. NOTE, if we knew
   that all the input NDFs would have the same spectral axis calibration,
   then the spectral WCS need only be obtained from the first NDF. However,
   in the general case, I presume that data files may be combined that use
   different spectral axis calibrations, and so these differences need to
   be taken into account. */
   if( hdr->tswcs ) {
      fs = astClone( hdr->tswcs );

/* The first axis should be a SpecFrame. See if this is so. If not annul
   the specframe pointer. */
      specax = 1;
      specframe = astPickAxes( fs, 1, &specax, NULL );
      if( !astIsASpecFrame( specframe ) ) specframe = astAnnul( specframe );
   }

/* If the above did not yield a SpecFrame, use the FITS-WCS headers in the
   FITS extension of the input NDF. Take a copy of the FITS header (so that
   the contents of the header are not changed), and then read a FrameSet
   out of it. */
   if( !specframe ) {
      fc = astCopy( hdr->fitshdr );
      astClear( fc, "Card" );
      fs = astRead( fc );

/* Extract the SpecFrame that describes the spectral axis from the current
   Frame of this FrameSet. This is assumed to be the third WCS axis (NB
   the different axis number). */
      specax = 3;
      specframe = astPickAxes( fs, 1, &specax, NULL );
   }

/* Split off the 1D Mapping for this single axis from the 3D Mapping for
   the whole WCS. This results in "specmap" holding the Mapping from
   SpecFrame value to GRID value. */
   fsmap = astGetMapping( fs, AST__CURRENT, AST__BASE );
   astMapSplit( fsmap, 1, &specax, pixax, &specmap );

/* Invert the Mapping for the spectral axis so that it goes from input GRID
   coord to spectral coord. */
   astInvert( specmap );

/* Get a Mapping that converts values in the input spectral system to the
   corresponding values in the output spectral system. */
   fs = astConvert( specframe, ospecfrm, "" );

/* Concatenate these Mappings with the supplied spectral Mapping to get
   a Mapping from the input spectral grid axis (pixel axis 1) to the
   output spectral grid axis (pixel axis 3). Simplify the Mapping. */
   ssmap = astCmpMap( astCmpMap( specmap, astGetMapping( fs, AST__BASE,
                                                         AST__CURRENT ),
                                 1, " " ),
                      ospecmap, 1, " " );
   ssmap = astSimplify( ssmap );

/* Create a table with one element for each channel in the input array,
   holding the index of the nearest corresponding output channel. */
   nchan = (data->dims)[ 0 ];
   spectab = astMalloc( sizeof( *spectab )*nchan );
   if( spectab ) {
      for( ichan = 0; ichan < nchan; ichan++ ) spectab[ ichan ] = ichan + 1;
      astTran1( ssmap, nchan, spectab, 1, spectab );
      for( ichan = 0; ichan < nchan; ichan++ ) {
         if( spectab[ ichan ] != AST__BAD ) {
            iz = floor( spectab[ ichan ] + 0.5 );
            if( iz >= 1 && iz <= dim[ 2 ] ) {
               spectab[ ichan ] = iz;
            } else {
               spectab[ ichan ] = 0;
            }
         } else {
            spectab[ ichan ] = 0;
         }
      }
   }

/* Allocate work arrays big enough to hold the coords of all the
   detectors in the current input file.*/
   xin = astMalloc( (data->dims)[ 1 ] * sizeof( *xin ) );
   yin = astMalloc( (data->dims)[ 1 ] * sizeof( *yin ) );
   xout = astMalloc( (data->dims)[ 1 ] * sizeof( *xout ) );
   yout = astMalloc( (data->dims)[ 1 ] * sizeof( *yout ) );

/* Initialise a string to point to the name of the first detector for which
   data is available */
   name = hdr->detname;

/* Store input coords for the detectors. Axis 1 is the detector index, and
   axis 2 is a dummy axis that always has the value 1. */
   for( irec = 0; irec < (data->dims)[ 1 ]; irec++ ) {
      xin[ irec ] = irec + 1.0;
      yin[ irec ] = 1.0;

/* If a group of detectors to be used was supplied, search the group for
   the name of the current detector. If not found, set the GRID coords bad. */
      if( detgrp ) {
         found = grpIndex( name, detgrp, 1, status );
         if( !found ) {
            xin[ irec ] = AST__BAD;
            yin[ irec ] = AST__BAD;
         }
      }

/* Move on to the next available detector name. */
      name += strlen( name ) + 1;
   }

/* Find the constant factor associated with the current input file. This
   is the squared backend degradation factor, divided by the noise bandwidth.
   Get the required FITS headers, checking they were found. */
   if( astGetFitsF( hdr->fitshdr, "BEDEGFAC", &k ) &&
       astGetFitsS( hdr->fitshdr, "FFT_WIN", &fftwin ) ){

/* Get a Mapping that converts values in the input spectral system to
   topocentric frequency in Hz, and concatenate this Mapping with the
   Mapping from input GRID coord to the input spectral system. The result
   is a Mapping from input GRID coord to topocentric frequency in Hz. */
      specframe2 = astCopy( specframe );
      astSet( specframe2, "system=freq,stdofrest=topo,unit=Hz" );
      fmap = astCmpMap( specmap, astGetMapping( astConvert( specframe,
                                                            specframe2,
                                                            "" ),
                                                AST__BASE, AST__CURRENT ),
                        1, " " );

/* Differentiate this Mapping at the mid channel position to get the width
   of an input channel in Hz. */
      at = 0.5*nchan;
      dnew = astRate( fmap, &at, 1, 1 );

/* Modify the channel width to take account of the effect of the FFT windowing
   function. Allow undef value because FFT_WIN for old data had a broken value
   in hybrid subband modes. */
      if( dnew != AST__BAD ) {
         dnew = fabs( dnew );

         if( !strcmp( fftwin, "truncate" ) ) {
            dnew *= 1.0;

         } else if( !strcmp( fftwin, "hanning" ) ) {
            dnew *= 1.5;

	    } else if( !strcmp( fftwin, "<undefined>" ) ) {
	      /* Deal with broken data - make an assumption */
	       dnew *= 1.0;

         } else if( *status == SAI__OK ) {
            *status = SAI__ERROR;
            msgSetc( "W", fftwin );
            errRep( FUNC_NAME, "FITS header FFT_WIN has unknown value "
                    "'^W' (programming error).", status );
         }

/* Form the required constant. */
         fcon2 = k*k/dnew;

      } else {
         fcon2 = VAL__BADD;
      }

   } else {
      fcon2 = VAL__BADD;
   }

/* Return the factor needed for calculating Tsys from the variance. */
   if( first ) {
      *fcon = fcon2;
   } else if( fcon2 != *fcon ) {
      *fcon = VAL__BADD;
   }

/* Initialise a pointer to the next time slice index to be used. */
   nexttime = ptime;

/* Loop round all the time slices in the input file. */
   for( itime = 0; itime < (data->dims)[ 2 ] && *status == SAI__OK; itime++ ) {

/* If this time slice is not being pasted into the output cube, pass on. */
      if( nexttime ){
         if( *nexttime != itime ) continue;
         nexttime++;
      }

/* Store a pointer to the first input data value in this time slice. */
      pdata = ( (float *) (data->pntr)[ 0 ] ) + itime*timeslice_size;

/* Get a FrameSet describing the spatial coordinate systems associated with
   the current time slice of the current input data file. The base frame in
   the FrameSet will be a 2D Frame in which axis 1 is detector number and
   axis 2 is unused. The current Frame will be a SkyFrame (the SkyFrame
   System may be any of the JCMT supported systems). The Epoch will be
   set to the epoch of the time slice. */
      smf_tslice_ast( data, itime, 1, NO_FTS, status );
      swcsin = hdr->wcs;

/* Note the total exposure time (texp) for all the input spectra produced by
   this time slice. */
      ton = hdr->state->acs_exposure;
      if( ton == 0.0 ) ton = VAL__BADR;

      toff = hdr->state->acs_offexposure;
      if( toff == 0.0 ) toff = VAL__BADR;

      if( ton != VAL__BADR && toff != VAL__BADR ) {
         texp = ton + toff;
         teff = 4*ton*toff/( ton + toff );
      } else {
         texp = VAL__BADR;
         teff = VAL__BADR;
      }

/* If output variances are being calculated on the basis of Tsys values
   in the input, find the constant factor associated with the current
   time slice. */
      tcon = AST__BAD;
      if( genvar == 2 && fcon2 != AST__BAD && texp != VAL__BADR ) {
         tcon = fcon2*( 1.0/ton + 1.0/toff );

/* Get a pointer to the start of the Tsys values for this time slice. */
         tsys = hdr->tsys + hdr->ndet*itime;
      }

/* We now create a Mapping from detector index to position in oskyframe. */
      astInvert( swcsin );
      ibasein = astGetI( swcsin, "Base" );
      fs = astConvert( swcsin, oskyframe, "SKY" );
      astSetI( swcsin, "Base", ibasein );
      astInvert( swcsin );

      if( fs == NULL ) {
         if( *status == SAI__OK ) {
            if (data->file) {
               smf_smfFile_msg(data->file, "FILE", 1, "<unknown>");
            } else {
               msgSetc( "FILE", "<unknown>" );
            }
            *status = SAI__ERROR;
            errRep( FUNC_NAME, "The spatial coordinate system in ^FILE "
                    "is not compatible with the spatial coordinate "
                    "system in the first input file.", status );
         }
         break;
      }

/* Transform the positions of the detectors from input GRID to oskyframe
   coords. */
      astTran2( fs, (data->dims)[ 1 ], xin, yin, 1, xout, yout );

/* Loop round all detectors. */
      for( irec = 0; irec < (data->dims)[ 1 ]; irec++ ) {

/* If the detector has a valid position, see if it produced any good
   data values. */
         if( xout[ irec ] != AST__BAD && yout[ irec ] != AST__BAD ) {
            qdata = pdata;
            good = 0;
            for( ichan = 0; ichan < nchan; ichan++ ){
               if( *(qdata++) != VAL__BADR ) {
                  good = 1;
                  break;
               }
            }

/* If it did, calculate the variance associated with each detector
   sample (if required), based on the input Tsys values, and copy the
   spectrum to the output NDF. */
            if( good ) {
               if( *ispec < dim[ 0 ] ){
                  rtsys = tsys ? (float) tsys[ irec ] : VAL__BADR;
                  if( rtsys <= 0.0 ) rtsys = VAL__BADR;
                  if( tcon != AST__BAD && genvar == 2 && rtsys != VAL__BADR ) {
                     var_array[ *ispec ] = tcon*rtsys*rtsys;
                  } else if( var_array ) {
                     var_array[ *ispec ] = VAL__BADR;
                  }

                  if( texp != VAL__BADR ) {
                     texp_array[ *ispec ] = texp;
                     teff_array[ *ispec ] = teff;
                  }

                  for( ichan = 0; ichan < nchan; ichan++, pdata++ ) {
                     iz = spectab[ ichan ] - 1;
                     if( iz >= 0 && iz < dim[ 2 ] ) {
                        iv = *ispec + dim[ 0 ]*iz;
                        data_array[ iv ] = *pdata;
                     }
                  }

                  (*ispec)++;

               } else if( *status == SAI__OK ){
                  *status = SAI__ERROR;
                  msgSeti( "DIM", dim[ 0 ] );
                  errRep( " ", "Too many spectra (more than ^DIM) for "
                          "the output NDF (programming error).", status );
                  break;
               }

/* If this detector does not have any valid data values, increment the data
   pointer to point at the first sample for the next detector. */
            } else {
               pdata += nchan;
            }

/* If this detector does not have a valid position, increment the data
   pointer to point at the first sample for the next detector. */
         } else {
            pdata += nchan;
         }
      }

/* For efficiency, explicitly annul the AST Objects created in this tight
   loop. */
      fs = astAnnul( fs );
   }

/* Free resources */
   spectab = astFree( spectab );
   xin = astFree( xin );
   yin = astFree( yin );
   xout = astFree( xout );
   yout = astFree( yout );

/* End the AST context. This will annul all AST objects created within the
   context (except for those that have been exported from the context). */
   astEnd;
}
Exemplo n.º 2
0
F77_INTEGER_FUNCTION(ast_convert)( INTEGER(FROM),
                                   INTEGER(TO),
                                   CHARACTER(NAMELIST),
                                   INTEGER(STATUS)
                                   TRAIL(NAMELIST) ) {
   GENPTR_INTEGER(FROM)
   GENPTR_INTEGER(TO)
   GENPTR_INTEGER(NAMELIST)
   F77_INTEGER_TYPE(RESULT);
   char *namelist;

   astAt( "AST_CONVERT", NULL, 0 );
   astWatchSTATUS(
      namelist = astString( NAMELIST, NAMELIST_length );
      RESULT = astP2I( astConvert( astI2P( *FROM ), astI2P( *TO ),
                                   namelist ) );
      namelist = astFree( namelist );
   )
   return RESULT;
}

F77_DOUBLE_FUNCTION(ast_angle)( INTEGER(THIS),
                                   DOUBLE_ARRAY(A),
                                   DOUBLE_ARRAY(B),
                                   DOUBLE_ARRAY(C),
                                   INTEGER(STATUS) ) {
   GENPTR_INTEGER(THIS)
   GENPTR_DOUBLE_ARRAY(A)
   GENPTR_DOUBLE_ARRAY(B)
   GENPTR_DOUBLE_ARRAY(C)
   F77_DOUBLE_TYPE(RESULT);
Exemplo n.º 3
0
void smf_mapbounds( int fast, Grp *igrp,  int size, const char *system,
                    AstFrameSet *spacerefwcs, int alignsys, int *lbnd_out,
                    int *ubnd_out, AstFrameSet **outframeset, int *moving,
                    smfBox ** boxes, fts2Port fts_port, int *status ) {

  /* Local Variables */
  AstSkyFrame *abskyframe = NULL; /* Output Absolute SkyFrame */
  int actval;           /* Number of parameter values supplied */
  AstMapping *bolo2map = NULL; /* Combined mapping bolo->map
                                  coordinates, WCS->GRID Mapping from
                                  input WCS FrameSet */
  smfBox *box = NULL;          /* smfBox for current file */
  smfData *data = NULL;        /* pointer to  SCUBA2 data struct */
  double dlbnd[ 2 ];    /* Floating point lower bounds for output map */
  drcntrl_bits drcntrl_mask = 0;/* Mask to use for DRCONTROL on this instrument */
  double dubnd[ 2 ];    /* Floating point upper bounds for output map */
  AstMapping *fast_map = NULL; /* Mapping from tracking to absolute map coords */
  smfFile *file = NULL;        /* SCUBA2 data file information */
  int first;                   /* Is this the first good subscan ? */
  AstFitsChan *fitschan = NULL;/* Fits channels to construct WCS header */
  AstFrameSet *fs = NULL;      /* A general purpose FrameSet pointer */
  smfHead *hdr = NULL;         /* Pointer to data header this time slice */
  int i;                       /* Loop counter */
  dim_t j;                     /* Loop counter */
  AstSkyFrame *junksky = NULL; /* Unused SkyFrame argument */
  dim_t k;                     /* Loop counter */
  int lbnd0[ 2 ];              /* Defaults for LBND parameter */
  double map_pa=0;             /* Map PA in output coord system (rads) */
  dim_t maxloop;               /* Number of times to go round the time slice loop */
  dim_t nbadt  = 0;            /* Number of bad time slices */
  dim_t ngoodt = 0;            /* Number of good time slices */
  double par[7];               /* Projection parameters */
  double shift[ 2 ];           /* Shifts from PIXEL to GRID coords */
  AstMapping *oskymap = NULL;  /* Mapping celestial->map coordinates,
                                  Sky <> PIXEL mapping in output
                                  FrameSet */
  AstSkyFrame *oskyframe = NULL;/* Output SkyFrame */
  char *refsys = NULL;         /* Sky system from supplied reference FrameSet */
  dim_t textreme[4];           /* Time index corresponding to minmax TCS posn */
  AstFrame *skyin = NULL;      /* Sky Frame in input FrameSet */
  double skyref[ 2 ];          /* Values for output SkyFrame SkyRef attribute */
  struct timeval tv1;          /* Timer */
  struct timeval tv2;          /* Timer */
  AstMapping *tmap;            /* Temporary Mapping */
  int trim;                    /* Trim borders of bad pixels from o/p image? */
  int ubnd0[ 2 ];              /* Defaults for UBND parameter */
  double x_array_corners[4];   /* X-Indices for corner bolos in array */
  double x_map[4];             /* Projected X-coordinates of corner bolos */
  double y_array_corners[4];   /* Y-Indices for corner pixels in array */
  double y_map[4];             /* Projected X-coordinates of corner bolos */

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

  /* Start a timer to see how long this takes */
  smf_timerinit( &tv1, &tv2, status );

  /* Initialize pointer to output FrameSet and moving-source flag */
  *outframeset = NULL;
  *moving = 0;

  /* initialize double precision output bounds and the proj pars */
  for( i = 0; i < 7; i++ ) par[ i ] = AST__BAD;
  dlbnd[ 0 ] = VAL__MAXD;
  dlbnd[ 1 ] = VAL__MAXD;
  dubnd[ 0 ] = VAL__MIND;
  dubnd[ 1 ] = VAL__MIND;

  /* If we have a supplied reference WCS we can use that directly
     without having to calculate it from the data. Replace the requested
     system with the system from the reference FrameSet (take a copy of the
     string since astGetC may re-use its buffer). */
  if (spacerefwcs) {
     oskyframe = astGetFrame( spacerefwcs, AST__CURRENT );
     int nc = 0;
     refsys = astAppendString( NULL, &nc, astGetC( oskyframe, "System" ) );
     system = refsys;
  }

  /* Create array of returned smfBox structures and store a pointer
     to the next one to be initialised. */
  *boxes = astMalloc( sizeof( smfBox ) * size );
  box = *boxes;

  astBegin;

  /* Loop over all files in the Grp */
  first = 1;
  for( i=1; i<=size; i++, box++ ) {

    /* Initialise the spatial bounds of section of the the output cube that is
       contributed to by the current ionput file. */
    box->lbnd[ 0 ] = VAL__MAXD;
    box->lbnd[ 1 ] = VAL__MAXD;
    box->ubnd[ 0 ] = VAL__MIND;
    box->ubnd[ 1 ] = VAL__MIND;

    /* Read data from the ith input file in the group */
    smf_open_file( NULL, igrp, i, "READ", SMF__NOCREATE_DATA, &data, status );

    if (*status != SAI__OK) {
      msgSeti( "I", i );
      errRep( "smf_mapbounds", "Could not open data file no ^I.", status );
      break;
    } else {
      if( *status == SAI__OK ) {
        if( data->file == NULL ) {
          *status = SAI__ERROR;
          errRep( FUNC_NAME, "No smfFile associated with smfData.",
                  status );
          break;

        } else if( data->hdr == NULL ) {
          *status = SAI__ERROR;
          errRep( FUNC_NAME, "No smfHead associated with smfData.",
                  status );
          break;

        } else if( data->hdr->fitshdr == NULL ) {
          *status = SAI__ERROR;
          errRep( FUNC_NAME, "No FITS header associated with smfHead.",
                  status );
          break;

        }
      }
    }

    /* convenience pointers */
    file = data->file;
    hdr = data->hdr;

    /* report name of the input file */
    smf_smfFile_msg( file, "FILE", 1, "<unknown>" );
    msgSeti("I", i);
    msgSeti("N", size);
    msgOutif(MSG__VERB, " ",
             "SMF_MAPBOUNDS: Processing ^I/^N ^FILE",
             status);

/* Check that there are 3 pixel axes. */
    if( data->ndims != 3 ) {
      smf_smfFile_msg( file, "FILE", 1, "<unknown>" );
      msgSeti( "NDIMS", data->ndims );
      *status = SAI__ERROR;
      errRep( FUNC_NAME, "^FILE has ^NDIMS pixel axes, should be 3.",
              status );
      break;
    }

    /* Check that the data dimensions are 3 (for time ordered data) */
    if( *status == SAI__OK ) {

      /* If OK Decide which detectors (GRID coord) to use for
         checking bounds, depending on the instrument in use. */

      switch( hdr->instrument ) {

      case INST__SCUBA2:
        drcntrl_mask = DRCNTRL__POSITION;
        /* 4 corner bolometers of the subarray */
        x_array_corners[0] = 1;
        x_array_corners[1] = 1;
        x_array_corners[2] = (data->dims)[0];
        x_array_corners[3] = (data->dims)[0];

        y_array_corners[0] = 1;
        y_array_corners[1] = (data->dims)[1];
        y_array_corners[2] = 1;
        y_array_corners[3] = (data->dims)[1];
        break;

      case INST__AZTEC:
        /* Rough guess for extreme bolometers around the edge */
        x_array_corners[0] = 22;
        x_array_corners[1] = 65;
        x_array_corners[2] = 73;
        x_array_corners[3] = 98;

        y_array_corners[0] = 1; /* Always 1 for AzTEC */
        y_array_corners[1] = 1;
        y_array_corners[2] = 1;
        y_array_corners[3] = 1;
        break;

      case INST__ACSIS:
        smf_find_acsis_corners( data, x_array_corners, y_array_corners,
                                status);
        break;

      default:
        *status = SAI__ERROR;
        errRep(FUNC_NAME, "Don't know how to calculate mapbounds for data created with this instrument", status);
      }
    }

    if( *status == SAI__OK) {
      size_t goodidx = SMF__BADSZT;

      /* Need to build up a frameset based on good telescope position.
         We can not assume that we the first step will be a good TCS position
         so we look for one. If we can not find anything we skip to the
         next file. */
      maxloop = (data->dims)[2];
      for (j=0; j<maxloop; j++) {
        JCMTState state = (hdr->allState)[j];
        if (state.jos_drcontrol >= 0 && state.jos_drcontrol & drcntrl_mask ) {
          /* bad TCS - so try again */
        } else {
          /* Good tcs */
          goodidx = j;
          break;
        }
      }

      if (goodidx == SMF__BADSZT) {
        smf_smfFile_msg( data->file, "FILE", 1, "<unknown>");
        msgOutif( MSG__QUIET, "", "No good telescope positions found in file ^FILE. Ignoring",
                  status );
        smf_close_file( NULL, &data, status );
        continue;
      }

      /* If we are dealing with the first good file, create the output
         SkyFrame. */
      if( first ) {
        first = 0;

        /* Create output SkyFrame if it has not come from a reference */
        if ( oskyframe == NULL ) {

          /* smf_tslice_ast only needs to get called once to set up framesets */
          if( hdr->wcs == NULL ) {
            smf_tslice_ast( data, goodidx, 1, fts_port, status);
          }

          /* Retrieve input SkyFrame */
          skyin = astGetFrame( hdr->wcs, AST__CURRENT );

          smf_calc_skyframe( skyin, system, hdr, alignsys, &oskyframe, skyref,
                             moving, status );

          /* Get the orientation of the map vertical within the output celestial
             coordinate system. This is derived form the MAP_PA FITS header, which
             gives the orientation of the map vertical within the tracking system. */
          map_pa = smf_calc_mappa( hdr, system, skyin, status );

          /* Provide a sensible default for the pixel size based on wavelength */
          par[4] = smf_calc_telres( hdr->fitshdr, status );
          par[4] *= AST__DD2R/3600.0;
          par[5] = par[4];

          /* Calculate the projection parameters. We do not enable autogrid determination
             for SCUBA-2 so we do not need to obtain all the data before calculating
             projection parameters. */
          smf_get_projpar( oskyframe, skyref, *moving, 0, 0, NULL, 0,
                           map_pa, par, NULL, NULL, status );

          if (skyin) skyin = astAnnul( skyin );

        /* If the output skyframe has been supplied, we still need to
           determine whether the source is moving or not, and set the
           reference position. */
        } else {

          /* smf_tslice_ast only needs to get called once to set up framesets */
          if( hdr->wcs == NULL ) {
            smf_tslice_ast( data, goodidx, 1, fts_port, status);
          }

          /* Retrieve input SkyFrame */
          skyin = astGetFrame( hdr->wcs, AST__CURRENT );
          smf_calc_skyframe( skyin, system, hdr, alignsys, &junksky, skyref,
                             moving, status );

          /* Store the sky reference position. If the target is moving,
             ensure the returned SkyFrame represents offsets from the
             reference position rather than absolute coords. */
          astSetD( oskyframe, "SkyRef(1)", skyref[ 0 ] );
          astSetD( oskyframe, "SkyRef(2)", skyref[ 1 ] );
          if( *moving ) astSet( oskyframe, "SkyRefIs=Origin" );

          /* Ensure the Epoch attribute in the map is set to the date of
             the first data in the map, rather than the date in supplied
             reference WCS. */
          astSetD( oskyframe, "Epoch", astGetD( junksky, "Epoch" ) );
        }

        if ( *outframeset == NULL && oskyframe != NULL && (*status == SAI__OK)){
          /* Now created a spatial Mapping. Use the supplied reference frameset
             if supplied */
          if (spacerefwcs) {
            oskymap = astGetMapping( spacerefwcs, AST__BASE, AST__CURRENT );
          } else {
            /* Now populate a FitsChan with FITS-WCS headers describing
               the required tan plane projection. The longitude and
               latitude axis types are set to either (RA,Dec) or (AZ,EL)
               to get the correct handedness. */
            fitschan = astFitsChan ( NULL, NULL, " " );
            smf_makefitschan( astGetC( oskyframe, "System"), &(par[0]),
                              &(par[2]), &(par[4]), par[6], fitschan, status );
            astClear( fitschan, "Card" );
            fs = astRead( fitschan );

            /* Extract the output PIXEL->SKY Mapping. */
            oskymap = astGetMapping( fs, AST__BASE, AST__CURRENT );

            /* Tidy up */
            fs = astAnnul( fs );
          }

          /* Create the output FrameSet */
          *outframeset = astFrameSet( astFrame(2, "Domain=GRID"), " " );

          /* Now add the SkyFrame to it */
          astAddFrame( *outframeset, AST__BASE, oskymap, oskyframe );

          /* Now add a POLANAL Frame if required (i.e. if the input time
             series are POL-2 Q/U values). */
          smf_addpolanal( *outframeset, hdr, status );

          /* Invert the oskymap mapping */
          astInvert( oskymap );

        } /* End WCS FrameSet construction */
      }

      /* Get a copy of the output SkyFrame and ensure it represents
         absolute coords rather than offset coords. */
      abskyframe = astCopy( oskyframe );
      astClear( abskyframe, "SkyRefIs" );
      astClear( abskyframe, "AlignOffset" );

      maxloop = (data->dims)[2];
      if (fast) {
        /* For scan map we scan through looking for largest telescope moves.
           For dream/stare we just look at the start and end time slices to
           account for sky rotation. */

        if (hdr->obsmode != SMF__OBS_SCAN) {
          textreme[0] = 0;
          textreme[1] = (data->dims)[2] - 1;
          maxloop = 2;

        } else {
          const char *tracksys;
          double *ac1list, *ac2list, *bc1list, *bc2list, *p1, *p2, *p3, *p4;
          double flbnd[4], fubnd[4];
          JCMTState state;

          /* If the output and tracking systems are different, get a
             Mapping between them. */
          tracksys = sc2ast_convert_system( (hdr->allState)[goodidx].tcs_tr_sys,
                                            status );
          if( strcmp( system, tracksys ) ) {
             AstSkyFrame *tempsf = astCopy( abskyframe );
             astSetC( tempsf, "System", tracksys );
             AstFrameSet *tempfs = astConvert( tempsf, abskyframe, "" );
             tmap = astGetMapping( tempfs, AST__BASE, AST__CURRENT );
             fast_map = astSimplify( tmap );
             tmap = astAnnul( tmap );
             tempsf = astAnnul( tempsf );
             tempfs = astAnnul( tempfs );
          } else {
             fast_map = NULL;
          }

          /* Copy all ac1/2 positions into two array, and transform them
             from tracking to absolute output sky coords. */
          ac1list = astMalloc( maxloop*sizeof( *ac1list ) );
          ac2list = astMalloc( maxloop*sizeof( *ac2list ) );
          if( *status == SAI__OK ) {
             p1 = ac1list;
             p2 = ac2list;
             for( j = 0; j < maxloop; j++ ) {
                state = (hdr->allState)[ j ];
                *(p1++) = state.tcs_tr_ac1;
                *(p2++) = state.tcs_tr_ac2;
             }
             if( fast_map ) astTran2( fast_map, maxloop, ac1list, ac2list, 1,
                                      ac1list, ac2list );
          }

          /* If the target is moving, we need to adjust these ac1/2 values
             to represent offsets from the base position. */
          if( *moving ) {

          /* Copy all bc1/2 positions into two arrays. */
             bc1list = astMalloc( maxloop*sizeof( *bc1list ) );
             bc2list = astMalloc( maxloop*sizeof( *bc2list ) );
             if( *status == SAI__OK ) {
                p1 = bc1list;
                p2 = bc2list;

                for( j = 0; j < maxloop; j++ ) {
                   state = (hdr->allState)[ j ];
                   *(p1++) = state.tcs_tr_bc1;
                   *(p2++) = state.tcs_tr_bc2;
                }

                /* Transform them from tracking to absolute output sky coords. */
                if( fast_map ) astTran2( fast_map, maxloop, bc1list, bc2list,
                                         1, bc1list, bc2list );

                /* Replace each ac1/2 position with the offsets from the
                   corresponding base position. */
                p1 = bc1list;
                p2 = bc2list;
                p3 = ac1list;
                p4 = ac2list;
                for( j = 0; j < maxloop; j++ ) {
                  smf_offsets( *(p1++), *(p2++), p3++, p4++, status );
                }
             }

             /* We no longer need the base positions. */
             bc1list = astFree( bc1list );
             bc2list = astFree( bc2list );
          }

          /* Transform the ac1/2 position from output sky coords to
             output pixel coords. */
          astTran2( oskymap, maxloop, ac1list, ac2list, 1, ac1list, ac2list );

          /* Find the bounding box containing these pixel coords and the
             time slices at which the boresight touches each edge of this
             box. */
          flbnd[ 0 ] = VAL__MAXD;
          flbnd[ 1 ] = VAL__MAXD;
          fubnd[ 0 ] = VAL__MIND;
          fubnd[ 1 ] = VAL__MIND;
          for( j = 0; j < 4; j++ ) textreme[ j ] = (dim_t) VAL__BADI;

          if( *status == SAI__OK ) {
             p1 = ac1list;
             p2 = ac2list;
             for( j = 0; j < maxloop; j++,p1++,p2++ ) {
                if( *p1 != VAL__BADD && *p2 != VAL__BADD ){

                   if ( *p1 < flbnd[0] ) { flbnd[0] = *p1; textreme[0] = j; }
                   if ( *p2 < flbnd[1] ) { flbnd[1] = *p2; textreme[1] = j; }
                   if ( *p1 > fubnd[0] ) { fubnd[0] = *p1; textreme[2] = j; }
                   if ( *p2 > fubnd[1] ) { fubnd[1] = *p2; textreme[3] = j; }
                }
             }
          }

          maxloop = 4;
          msgSetd("X1", textreme[0]);
          msgSetd("X2", textreme[1]);
          msgSetd("X3", textreme[2]);
          msgSetd("X4", textreme[3]);
          msgOutif( MSG__DEBUG, " ",
                    "Extrema time slices are ^X1, ^X2, ^X3 and ^X4",
                    status);

          ac1list = astFree( ac1list );
          ac2list = astFree( ac2list );

        }
      }

      /* Get the astrometry for all the relevant time slices in this data file */
      for( j=0; j<maxloop; j++ ) {
        dim_t ts;  /* Actual time slice to use */

        /* if we are doing the fast loop, we need to read the time slice
           index from textreme. Else we just use the index */
        if (fast) {
          /* get the index but make sure it is good */
          ts = textreme[j];
          if (ts == (dim_t)VAL__BADI) continue;
        } else {
          ts = j;
        }
        /* Calculate the bolo to map-pixel transformation for this tslice */
        bolo2map = smf_rebin_totmap( data, ts, abskyframe, oskymap,
                                     *moving, fts_port, status );

        if ( *status == SAI__OK ) {
          /* skip if we did not get a mapping this time round */
          if (!bolo2map) continue;

          /* Check corner pixels in the array for their projected extent
             on the sky to set the pixel bounds */
          astTran2( bolo2map, 4, x_array_corners, y_array_corners, 1,
                    x_map, y_map );

          /* Update min/max for this time slice */
          for( k=0; k<4; k++ ) {

            if( x_map[k] != AST__BAD && y_map[k] != AST__BAD ) {
              if( x_map[k] < dlbnd[0] ) dlbnd[0] = x_map[k];
              if( y_map[k] < dlbnd[1] ) dlbnd[1] = y_map[k];
              if( x_map[k] > dubnd[0] ) dubnd[0] = x_map[k];
              if( y_map[k] > dubnd[1] ) dubnd[1] = y_map[k];

              if( x_map[k] < box->lbnd[0] ) box->lbnd[0] = x_map[k];
              if( y_map[k] < box->lbnd[1] ) box->lbnd[1] = y_map[k];
              if( x_map[k] > box->ubnd[0] ) box->ubnd[0] = x_map[k];
              if( y_map[k] > box->ubnd[1] ) box->ubnd[1] = y_map[k];

            } else if( *status == SAI__OK ) {
              *status = SAI__ERROR;
              errRep( FUNC_NAME, "Extreme positions are bad.", status );
              break;
            }
          }
        }
        /* Explicitly annul these mappings each time slice for reduced
           memory usage */
        if (bolo2map) bolo2map = astAnnul( bolo2map );
        if (fs) fs = astAnnul( fs );

        /* Break out of loop over time slices if bad status */
        if (*status != SAI__OK) goto CLEANUP;
      }

      /* Annul any remaining Ast objects before moving on to the next file */
      if (fs) fs = astAnnul( fs );
      if (bolo2map) bolo2map = astAnnul( bolo2map );
    }

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

    /* Break out of loop over data files if bad status */
    if (*status != SAI__OK) goto CLEANUP;
  }

  /* make sure we got values - should not be possible with good status */
  if (dlbnd[0] == VAL__MAXD || dlbnd[1] == VAL__MAXD) {
    if (*status == SAI__OK) {
      *status = SAI__ERROR;
      errRep( " ", "Unable to find any valid map bounds", status );
    }
  }

  if (nbadt > 0) {
    msgOutf( "", "   Processed %zu time slices to calculate bounds,"
             " of which %zu had bad telescope data and were skipped",
             status, (size_t)(ngoodt+nbadt), (size_t)nbadt );
  }

  /* If spatial reference wcs was supplied, store par values that result in
     no change to the pixel origin. */
  if( spacerefwcs ){
    par[ 0 ] = 0.5;
    par[ 1 ] = 0.5;
  }

  /* Need to re-align with the interim GRID coordinates */
  lbnd_out[0] = ceil( dlbnd[0] - par[0] + 0.5 );
  ubnd_out[0] = ceil( dubnd[0] - par[0] + 0.5 );
  lbnd_out[1] = ceil( dlbnd[1] - par[1] + 0.5 );
  ubnd_out[1] = ceil( dubnd[1] - par[1] + 0.5 );

  /* Do the same with the individual input file bounding boxes */
  box = *boxes;
  for (i = 1; i <= size; i++, box++) {
    box->lbnd[0] = ceil( box->lbnd[0] - par[0] + 0.5);
    box->ubnd[0] = ceil( box->ubnd[0] - par[0] + 0.5);
    box->lbnd[1] = ceil( box->lbnd[1] - par[1] + 0.5);
    box->ubnd[1] = ceil( box->ubnd[1] - par[1] + 0.5);
  }

  /* Apply a ShiftMap to the output FrameSet to re-align the GRID
     coordinates */
  shift[0] = 2.0 - par[0] - lbnd_out[0];
  shift[1] = 2.0 - par[1] - lbnd_out[1];
  astRemapFrame( *outframeset, AST__BASE, astShiftMap( 2, shift, " " ) );

  /* Set the dynamic defaults for lbnd/ubnd */
  lbnd0[ 0 ] = lbnd_out[ 0 ];
  lbnd0[ 1 ] = lbnd_out[ 1 ];
  parDef1i( "LBND", 2, lbnd0, status );

  ubnd0[ 0 ] = ubnd_out[ 0 ];
  ubnd0[ 1 ] = ubnd_out[ 1 ];
  parDef1i( "UBND", 2, ubnd0, status );

  parGet1i( "LBND", 2, lbnd_out, &actval, status );
  if( actval == 1 ) lbnd_out[ 1 ] = lbnd_out[ 0 ];

  parGet1i( "UBND", 2, ubnd_out, &actval, status );
  if( actval == 1 ) ubnd_out[ 1 ] = ubnd_out[ 0 ];

  /* Ensure the bounds are the right way round. */
  if( lbnd_out[ 0 ] > ubnd_out[ 0 ] ) {
    int itmp = lbnd_out[ 0 ];
    lbnd_out[ 0 ] = ubnd_out[ 0 ];
    ubnd_out[ 0 ] = itmp;
  }

  if( lbnd_out[ 1 ] > ubnd_out[ 1 ] ) {
    int itmp = lbnd_out[ 1 ];
    lbnd_out[ 1 ] = ubnd_out[ 1 ];
    ubnd_out[ 1 ] = itmp;
  }

  /* If borders of bad pixels are being trimmed from the output image,
     then do not allow the user-specified bounds to extend outside the
     default bounding box (since we know that the default bounding box
     encloses all available data). */
  parGet0l( "TRIM", &trim, status );
  if( trim ) {
     if( lbnd_out[ 0 ] < lbnd0[ 0 ] ) lbnd_out[ 0 ] = lbnd0[ 0 ];
     if( lbnd_out[ 1 ] < lbnd0[ 1 ] ) lbnd_out[ 1 ] = lbnd0[ 1 ];
     if( ubnd_out[ 0 ] > ubnd0[ 0 ] ) ubnd_out[ 0 ] = ubnd0[ 0 ];
     if( ubnd_out[ 1 ] > ubnd0[ 1 ] ) ubnd_out[ 1 ] = ubnd0[ 1 ];
  }

  /* Modify the returned FrameSet to take account of the new pixel origin. */
  shift[ 0 ] = lbnd0[ 0 ] - lbnd_out[ 0 ];
  shift[ 1 ] = lbnd0[ 1 ] - lbnd_out[ 1 ];
  if( shift[ 0 ] != 0.0 || shift[ 1 ] != 0.0 ) {
    astRemapFrame( *outframeset, AST__BASE, astShiftMap( 2, shift, " " ) );
  }

/* Report the pixel bounds of the cube. */
  if( *status == SAI__OK ) {
    msgOutif( MSG__NORM, " ", " ", status );
    msgSeti( "XL", lbnd_out[ 0 ] );
    msgSeti( "YL", lbnd_out[ 1 ] );
    msgSeti( "XU", ubnd_out[ 0 ] );
    msgSeti( "YU", ubnd_out[ 1 ] );
    msgOutif( MSG__NORM, " ", "   Output map pixel bounds: ( ^XL:^XU, ^YL:^YU )",
              status );

    if( ( ubnd_out[ 0 ] - lbnd_out[ 0 ] + 1 ) > MAX_DIM ||
        ( ubnd_out[ 1 ] - lbnd_out[ 1 ] + 1 ) > MAX_DIM ) {
      *status = SAI__ERROR;
      errRep( "", FUNC_NAME ": The map is too big. Check your list of input "
              "data files does not include widely separated observations.",
              status );
    }
  }

  /* If no error has occurred, export the returned FrameSet pointer from the
     current AST context so that it will not be annulled when the AST
     context is ended. Otherwise, ensure a null pointer is returned. */
  if( *status == SAI__OK ) {
    astExport( *outframeset );
  } else {
    *outframeset = astAnnul( *outframeset );
  }

  msgOutiff( SMF__TIMER_MSG, "",
             "Took %.3f s to calculate map bounds",
             status, smf_timerupdate( &tv1, &tv2, status ) );

  /* Clean Up */
 CLEANUP:
  if (*status != SAI__OK) {
    errRep(FUNC_NAME, "Unable to determine map bounds", status);
  }
  if (oskymap) oskymap  = astAnnul( oskymap );
  if (bolo2map) bolo2map = astAnnul( bolo2map );
  if (fitschan) fitschan = astAnnul( fitschan );

  if( data != NULL )
    smf_close_file( NULL, &data, status );

  refsys = astFree( refsys );

  astEnd;

}
Exemplo n.º 4
0
int *smf_jsatiles_region( AstRegion *region, smfJSATiling *skytiling,
                          int *ntile, int *status ){

/* Local Variables */
   AstFrameSet *fs;
   AstKeyMap *km;
   AstRegion *region2;
   AstRegion *space_region;
   AstRegion *tregion;
   AstSkyFrame *skyframe;
   char text[ 200 ];
   const char *key;
   double *mesh = NULL;
   double *xmesh;
   double *ymesh;
   int *tiles = NULL;
   int axes[ 2 ];
   int i;
   int ineb;
   int itile2;
   int itile;
   int ix;
   int iy;
   int key_index;
   int lbnd[ 2 ];
   int mapsize;
   int npoint;
   int old_sv;
   int overlap;
   int ubnd[ 2 ];
   int value;
   int xoff[ 4 ] = { -1, 0, 1, 0 };
   int xt;
   int yoff[ 4 ] = { 0, 1, 0, -1 };
   int yt;

/* Initialise */
   *ntile = 0;

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

/* Start an AST context so that all AST objects created in this function
   are annulled automatically. */
   astBegin;

/* Identify the celestial axes in the Region. */
   atlFindSky( (AstFrame *) region, &skyframe, axes + 1, axes, status );

/* Report an error if no celestial axes were found. */
   if( !skyframe && *status == SAI__OK ) {
      space_region = NULL;
      *status = SAI__ERROR;
      errRep( "", "The current WCS Frame in the supplied Region or "
              "NDF does not include celestial longitude and latitude axes.",
              status );

/* Otherwise, if the Region itself is 2-dimensional, it does not contain
   any other axes, so just use it as is. */
   } else if( astGetI( region, "Naxes" ) == 2 ) {
      space_region = astClone( region );

/* Otherwise, create a new Region by picking the celestial axes from the
   supplied Region. Report an error if a Region cannot be created in this
   way. */
   } else {
      space_region = astPickAxes( region, 2, axes, NULL );
      if( !astIsARegion( space_region ) && *status == SAI__OK ) {
         *status = SAI__ERROR;
         errRep( "", "The  celestial longitude and latitude axes in the "
                 "supplied Region or NDF are not independent of the other "
                 "axes.", status );
      }
   }

/* Create a FrameSet describing the whole sky in which each pixel
   corresponds to a single tile in SMF__JSA_HPX projection. The current
   Frame is ICRS (RA,Dec) and the base Frame is grid coords in which each
   grid pixel corresponds to a single tile. */
   smf_jsatile( -1, skytiling, 0, SMF__JSA_HPX, NULL, &fs, NULL, lbnd, ubnd,
                status );

/* Map the Region using the FrameSet obtained above so that the new Region
   describes offsets in tiles from the lower left tile. If "space_region"
   is a Polygon, ensure that the SimpVertices attribute is set so that the
   simplify method will take non-linearities into account (such as the
   region being split by the RA=12h meridian). */
   astInvert( fs );
   fs = astConvert( space_region, fs, "SKY" );
   if( !fs && *status == SAI__OK ) {
      *status = SAI__ERROR;
      errRep( "", "Cannot convert the supplied Region to ICRS.", status );
      goto L999;
   }

   old_sv = -999;
   if( astIsAPolygon( space_region ) ){
      if( astTest( space_region, "SimpVertices" ) ) {
         old_sv = astGetI( space_region, "SimpVertices" );
      }
      astSetI( space_region, "SimpVertices", 0 );
   }

   region2 = astMapRegion( space_region, fs, fs );

   if( astIsAPolygon( space_region ) ){
      if( old_sv == -999 ) {
         astClear( space_region, "SimpVertices" );
      } else {
         astSetI( space_region, "SimpVertices", old_sv );
      }
   }

/* Get a mesh of all-sky "grid" positions (actually tile X and Y indices)
   covering the region. Since the mesh positions are limited in number
   and placed arbitrarily within the Region, the mesh will identify some,
   but potentially not all, of the tiles that overlap the Region. */
   astGetRegionMesh( region2, 0, 0, 2, &npoint, NULL );
   mesh = astMalloc( 2*npoint*sizeof( *mesh ) );
   astGetRegionMesh( region2, 0, npoint, 2, &npoint, mesh );

/* Find the index of the tile containing each mesh position, and store
   them in a KeyMap using the tile index as the key and "1" (indicating
   the tile overlaps the region) as the value. The KeyMap is sorted by
   age of entry. Neighbouring tiles will be added to this KeyMap later.
   If an entry has a value of zero, it means the tile does not overlap
   the supplied Region. If the value is positive, it means the tile
   does overlap the supplied Region. If the value is negative, it means
   the tile has not yet been tested to see if it overlaps the supplied
   Region. */
   km = astKeyMap( "SortBy=KeyAgeDown" );
   xmesh = mesh;
   ymesh = mesh + npoint;
   for( i = 0; i < npoint && *status == SAI__OK; i++ ) {
      ix = (int)( *(xmesh++) + 0.5 ) - 1;
      iy = (int)( *(ymesh++) + 0.5 ) - 1;
      itile = smf_jsatilexy2i( ix, iy, skytiling, status );
      if (itile != VAL__BADI) {
         sprintf( text, "%d", itile );
         astMapPut0I( km, text, 1, NULL );
      }
   }

/* Starting with the oldest entry in the KeyMap, loop round checking all
   entries, in the order they were added, until all have been checked.
   Checking an entry may cause further entries to be added to the end of
   the KeyMap. */
   key_index = 0;
   mapsize = astMapSize( km );
   while( key_index < mapsize && *status == SAI__OK ) {
      key = astMapKey( km, key_index++ );

/* Convert the key string to an integer tile index. */
      itile = atoi( key );

/* Get the integer value associated with the tile. */
      astMapGet0I( km, key, &value );

/* If the tile associated with the current KeyMap entry has not yet been
   tested for overlap with the requested Region (as shown by the entry
   value being -1), test it now. */
      if( value == -1 ) {

/* Get a Region covering the tile. */
         smf_jsatile( itile, skytiling, 0, SMF__JSA_HPX, NULL, NULL, &tregion,
                      lbnd, ubnd, status );

/* See if this Region overlaps the user supplied region. Set the value of
   the KeyMap entry to +1 or 0 accordingly. */
         overlap = astOverlap( tregion, space_region );
         if( overlap == 0 ) {
            if( *status == SAI__OK ) {
               *status = SAI__ERROR;
               errRep( "", "Cannot align supplied Region with the sky "
                       "tile coordinate system (programming error).",
                       status );
            }
         } else if( overlap == 1 || overlap == 6 ) {
            value = 0;
         } else {
            value = 1;
         }
         astMapPut0I( km, key, value, NULL );
      }

/* Skip the current KeyMap entry if the corresponding tile does not
   overlap the requested Region (as shown by the entry value being zero). */
      if( value == 1 ) {

/* The current tile overlaps the supplied Region, so add the tile index to
   the returned list of tile indices. */
         tiles = astGrow( tiles, ++(*ntile), sizeof( *tiles ) );
         if( *status == SAI__OK ) {
            tiles[ *ntile - 1 ] = itile;

/* Add the adjoining tiles to the end of the KeyMap so that they will be
   tested in their turn, giving them a value of -1 to indicate that they
   have not yet been tested to see if they overlap the supplied Region.
   Ignore adjoining tiles that are already in the keyMap. */
            smf_jsatilei2xy( itile, skytiling, &xt, &yt, NULL, status );
            for( ineb = 0; ineb < 4; ineb++ ) {
               itile2 = smf_jsatilexy2i( xt + xoff[ ineb ], yt + yoff[ ineb ],
                                         skytiling, status );
               if( itile2 != VAL__BADI ) {
                  sprintf( text, "%d", itile2 );
                  if( !astMapHasKey( km, text ) ) {
                     astMapPut0I( km, text, -1, NULL );
                     mapsize++;
                  }
               }
            }
         }
      }
   }

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

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

   if( *status != SAI__OK ) {
      tiles = astFree( tiles );
      *ntile = 0;
   }

   astEnd;

   return tiles;
}
Exemplo n.º 5
0
/* ============== */
int main( int argc, char *argv[] ) {
/*
*+
*  Name:
*     ast_test

*  Purpose:
*     Test installation of the AST library.

*  Type:
*     C program.

*  Description:
*     This program performs a simple test (without using graphics) of
*     the AST library, to check that it is correctly installed. It is
*     not an exhaustive test of the system.

*  Arguments:
*     None.

*  Copyright:
*     Copyright (C) 1997-2006 Council for the Central Laboratory of the
*     Research Councils

*  Licence:
*     This program is free software; you can redistribute it and/or
*     modify it under the terms of the GNU General Public Licence as
*     published by the Free Software Foundation; either version 2 of
*     the Licence, or (at your option) any later version.
*
*     This program is distributed in the hope that 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 Licence for more details.
*
*     You should have received a copy of the GNU General Public Licence
*     along with this program; if not, write to the Free Software
*     Foundation, Inc., 51 Franklin Street,Fifth Floor, Boston, MA
*     02110-1301, USA

*  Authors:
*     RFWS: R.F. Warren-Smith (Starlink)

*  History:
*     19-NOV-1997 (RFWS);
*        Original version.
*-
*/

/* Local Constants: */
#define NCOORD 10                /* Number of coordinates to transform */

/* Local Variables: */
   AstFrameSet *cvt;             /* Pointer to conversion FrameSet */
   AstSkyFrame *sky1;            /* Pointer to first SkyFrame */
   AstSkyFrame *sky2;            /* Pointer to second SkyFrame */
   double xin[ NCOORD ];         /* Input coordinate array */
   double xout[ NCOORD ];        /* Output coordinate array */
   double yin[ NCOORD ];         /* Input coordinate array */
   double yout[ NCOORD ];        /* Output coordinate array */
   int i;                        /* Loop counter for coordinates */

/* Begin an AST context. */
   astBegin;

/* Create two SkyFrames. */
   sky1 = astSkyFrame( "system = FK4_NO_E, equinox = B1920, epoch = B1958" );
   sky2 = astSkyFrame( "system = ecliptic, equinox = J2001" );

/* Create a FrameSet describing the conversion between them. */
   cvt = astConvert( sky1, sky2, "" );

/* If successful, set up some input coordinates. */
   if ( cvt != AST__NULL ) {
      for ( i = 0; i < NCOORD; i++ ) {
         xin[ i ] = 0.1 * (double) i;
         yin[ i ] = 0.2 * (double) i;
      }

/* Display the FrameSet. */
      astShow( cvt );
      printf( "\n");

/* Activate reporting of coordinate transformations. */
      astSet( cvt, "Report = 1" );

/* Perform the forward transformation. */
      astTran2( cvt, 10, xin, yin, 1, xout, yout );
      printf( "\n");

/* Perform the inverse transformation. */
      astTran2( cvt, 10, xout, yout, 0, xin, yin );
   }

/* End the AST context. */
   astEnd;

/* Return an error status. */
   return astOK ? 0 : 1;

/* Undefine local macros. */
#undef NCOORD
}
Exemplo n.º 6
0
void smf_coords_lut( smfData *data, int tstep, dim_t itime_lo,
                     dim_t itime_hi, AstSkyFrame *abskyfrm,
                     AstMapping *oskymap, int moving, int olbnd[ 2 ],
                     int oubnd[ 2 ], fts2Port fts_port, int *lut,
                     double *angle,
                     double *lon, double *lat, int *status ) {

/* Local Variables */
   AstCmpMap *bsmap = NULL;     /* Tracking -> output grid Mapping */
   AstFrame *trfrm = NULL;      /* Tracking Frame */
   AstFrameSet *fs = NULL;      /* Tracking -> output sky FrameSet */
   AstMapping *fullmap = NULL;  /* Full Mapping from bolo GRID to output map GRID */
   AstMapping *offmap = NULL;   /* Mapping from absolute to offset sky coords */
   AstMapping *tr2skyabs = NULL;/* Tracking -> output sky Mapping */
   AstSkyFrame *offsky = NULL;  /* Offset sky frame */
   JCMTState *state;     /* Pointer to telescope info for time slice */
   dim_t ibolo;          /* Vector index of bolometer */
   dim_t idimx;          /* Bolometers per row */
   dim_t idimy;          /* Bolometers per column */
   dim_t ilut;           /* Index of LUT element */
   dim_t itime0;         /* Time slice index at next full calculation */
   dim_t itime;          /* Time slice index */
   dim_t nbolo;          /* Total number of bolometers */
   double *outmapcoord;  /* Array holding output map GRID coords */
   double *px;           /* Pointer to next output map X GRID coord */
   double *py;           /* Pointer to next output map Y GRID coord */
   double *pgx;          /* Pointer to next X output grid coords */
   double *pgy;          /* Pointer to next Y output grid coords */
   double *wgx = NULL;   /* Work space to hold X output grid coords */
   double *wgy = NULL;   /* Work space to hold Y output grid coords */
   double bsx0;          /* Boresight output map GRID X at previous full calc */
   double bsx;           /* Boresight output map GRID X at current time slice */
   double bsxlast;       /* Boresight output map GRID X at previous time slice*/
   double bsy0;          /* Boresight output map GRID Y at previous full calc */
   double bsy;           /* Boresight output map GRID Y at current time slice */
   double bsylast;       /* Boresight output map GRID Y at previous time slice*/
   double dx;            /* Offset in GRID X from previous full calc */
   double dxlast;        /* Offset in GRID X from previous time slice */
   double dy;            /* Offset in GRID Y from previous full calc */
   double dylast;        /* Offset in GRID Y from previous time slice */
   double shift[ 2 ];    /* Shift from PIXEL to GRID in output map */
   double x;             /* Output GRID X at current bolo in current row */
   double xin[ 2 ];      /* Input X values */
   double xout[ 2 ];     /* Output X values */
   double y;             /* Output GRID Y at current bolo in current row */
   double yin[ 2 ];      /* Input Y values */
   double yout[ 2 ];     /* Output Y values */
   int lbnd_in[ 2 ];     /* Lower bounds of input array */
   int np;               /* Number of positions to transform */
   int odimx;            /* Output map X dimension in pixels */
   int odimy;            /* Output map Y dimension in pixels */
   int ox;               /* Output X GRID index (-1) containing current bolo */
   int oy;               /* Output Y GRID index (-1) containing current bolo */
   int ubnd_in[ 2 ];     /* Upper bounds of input array */

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

/* Begin an AST context. */
   astBegin;

/* Get the dimensions of the output map. */
   odimx = oubnd[ 0 ] - olbnd[ 0 ] + 1;
   odimy = oubnd[ 1 ] - olbnd[ 1 ] + 1;

/* Get the dimensions of the bolometer array. */
   idimx = (data->dims)[ 0 ];
   idimy = (data->dims)[ 1 ];

/* Store integer bounds within the input bolometer GRID system. */
   lbnd_in[ 0 ] = 1;
   lbnd_in[ 1 ] = 1;
   ubnd_in[ 0 ] = idimx;
   ubnd_in[ 1 ] = idimy;

/* Determine the number of bolometers. */
   nbolo = idimx*idimy;

/* Initialise the index of the next LUT element to write. */
   ilut = 0;

/* Ensure tstep is at least one. */
   if( tstep < 1 ) tstep = 1;

/* Get the time slice index at which to do the next full calculation. */
   itime0 = itime_lo;

/* We only need the following AST objects if we will be approximating
   some caclulations. */
   if( tstep > 1 ) {

/* We need to find the first good TCS index in order to get the
   tracking system (Which for SCUBA-2 won't be changing in the sequence) */
      itime0 = VAL__BADI;
      for (itime = itime_lo; itime <= itime_hi; itime++) {
        JCMTState * slice = &((data->hdr->allState)[itime]);
        if (!(slice->jos_drcontrol >= 0 && slice->jos_drcontrol & DRCNTRL__POSITION)) {
          itime0 = itime;
          break;
        }
      }

      if ((int)itime0 != VAL__BADI) {

/* We need a Frame describing absolute tracking system coords. Take a
   copy of the supplied skyframe (to inherit obslat, obslon, epoch,
   etc), and then set its system to the tracking system. */
        trfrm = astCopy( abskyfrm );
        astSetC( trfrm, "System",
                 sc2ast_convert_system( (data->hdr->allState)[itime0].tcs_tr_sys,
                                        status ) );

/* Get the Mapping from the tracking system to the output (absolute, since
   abskyfrm is absolute) sky system. */
        fs = astConvert( trfrm, abskyfrm, " " );
        if( !fs && *status == SAI__OK ) {
          *status = SAI__ERROR;
          errRep( " ", "smf_coords_lut: Failed to convert from "
                  "tracking system to output WCS system.", status );
        }
        tr2skyabs = astSimplify( astGetMapping( fs, AST__BASE, AST__CURRENT ) );

/* For moving targets, we also need a Frame describing offset sky
   coordinates in the output map, in which the reference point is the
   current telescope base position. This will involve changing the
   SkyRef attribute of the Frame for every time slice, so take a
   copy of the supplied SkyFrame to avoid changing it. */
        if( moving ) {
          offsky = astCopy( abskyfrm );
          astSet( offsky, "SkyRefIs=Origin" );
        }

/* Create the Mapping from offsets within the output map sky coordinate
   system output to map GRID coords to output map GRID coords. This uses a
   ShiftMap to convert from output PIXEL coords (produced by "oskymap") to
   output GRID coords. Note, if the target is moving, "oskymap" maps from
   sky *offsets* to output map PIXEL coords. */
        shift[ 0 ] = 1.5 - olbnd[ 0 ];
        shift[ 1 ] = 1.5 - olbnd[ 1 ];
        bsmap = astCmpMap( oskymap, astShiftMap( 2, shift, " " ), 1, " " );
      } else {
        /* We did not find any good TCS data so force us into tstep==1
         case and end up with VAL__BADI for every element. */
        tstep = 1;
        itime0 = itime_lo;
        msgOutiff(MSG__VERB, "", "All time slices from %zu -- %zu had bad TCS data.",
                 status, (size_t)itime_lo, (size_t)itime_hi );
      }
   }

/* Allocate memory to hold the (x,y) output map grid coords at each bolo
   for a single time slice. */
   outmapcoord = astMalloc( sizeof( *outmapcoord )*2*nbolo );

/* Initialise boresight position for the benefit of the tstep == 1 case. */
   bsx = bsy = 0.0;
   bsx0 = bsy0 = AST__BAD;
   bsxlast = bsylast = AST__BAD;

/* If lon and lat arrays are to be returned, allocate memory to hold the grid
   coords of every bolometer for a single sample. */
   if( lon && lat ) {
      wgx = astMalloc( nbolo*sizeof( *wgx ) );
      wgy = astMalloc( nbolo*sizeof( *wgy ) );
   }

/* Loop round each time slice. */
   state = data->hdr->allState + itime_lo;
   for( itime = itime_lo; itime <= itime_hi && *status == SAI__OK;
        itime++,state++ ) {

/* No need to get the boresight position if we are doing full
   calculations at every time slice. If this time slice has bad
   TCS data then the problem will be caught in smf_rebin_totmap. */
      if( tstep > 1 ) {

/* Transform the current boresight and base (if moving) positions from
   tracking coords to absolute output map sky coords. */
         xin[ 0 ] = state->tcs_tr_ac1;
         yin[ 0 ] = state->tcs_tr_ac2;
         if( moving ) {
            xin[ 1 ] = state->tcs_tr_bc1;
            yin[ 1 ] = state->tcs_tr_bc2;
            np = 2;
         } else {
            np = 1;
         }
         astTran2( tr2skyabs, np, xin, yin, 1, xout, yout );

/* If the target is moving, find the offsets within the output map sky
   coordinate system, from base to boresight at the current time slice.
   These offsets become the new "boresight" position (in xin/yin). Guard
   against assigning bad values to the ref pos, which can cause AST to
   report errors. */
         if( moving && xout[ 1 ] != AST__BAD && yout[ 1 ] != AST__BAD ) {

/* Set the current telescope base position as the reference point in
   "offsky". Then get the Mapping from absolute to offset sky coords. */
            astSetD( offsky, "SkyRef(1)", xout[ 1 ] );
            astSetD( offsky, "SkyRef(2)", yout[ 1 ] );
            offmap = astSkyOffsetMap( offsky );

/* Use this Mapping to convert the current boresight position from
   absolute sky coords to offsets from the current base position. */
            astTran2( offmap, 1, xout, yout, 1, xin, yin );

/* Annul the Mapping to avoid keep the number of AST objects to a minimum. */
            offmap = astAnnul( offmap );

/* If the target is stationary, we can just use the absolute boresight
   position as it is. */
         } else {
            xin[ 0 ] = xout[ 0 ];
            yin[ 0 ] = yout[ 0 ];
         }

/* Transform the above boresight position from output map sky coords to output
   map GRID coords. */
         astTran2( bsmap, 1, xin, yin, 1, &bsx, &bsy );
      }

/* If we have reached the next full calculation... */
      if( itime == itime0 ) {

/* Calculate the full bolometer to map-pixel transformation for the current
   time slice */
         fullmap = smf_rebin_totmap( data, itime, abskyfrm, oskymap, moving,
                                     fts_port, status );

/* If succesful, use it to transform every bolometer position from bolo
   GRID coords to output map GRID coords. */
         if( fullmap ) {
            itime0 += tstep;
            astTranGrid( fullmap, 2, lbnd_in, ubnd_in, 0.1, 1000000, 1,
                         2, nbolo, outmapcoord );
            fullmap = astAnnul( fullmap );

/* Record the boresight grid coords at this time slice. */
            bsx0 = bsx;
            bsy0 = bsy;

/* If we cannot determine a full Mapping for this time slice, move the
   node to the next time slice (otherwise we would loose all the data to
   the next node), and set the boresight position bad to indicate we
   have no mapping for this time slice. */
         } else {
            itime0++;
            bsx0 = AST__BAD;
            bsy0 = AST__BAD;
         }
      }

/* Get the offset from the boresight position at the previous full
   calculation and the current boresight position, in output map GRID
   coords. */
      dx = ( bsx != AST__BAD && bsx0 != AST__BAD ) ? bsx - bsx0 : AST__BAD;
      dy = ( bsy != AST__BAD && bsy0 != AST__BAD ) ? bsy - bsy0 : AST__BAD;

/* Work out the scan direction based on the GRID offsets between this and
   the previous time slice. Angles are calculated using atan2, with values
   ranging from -pi to +pi. */
      if( angle ) {
        double theta = AST__BAD;

        dxlast = ( bsx != AST__BAD && bsxlast != AST__BAD ) ?
          bsx - bsxlast : AST__BAD;

        dylast = ( bsy != AST__BAD && bsylast != AST__BAD ) ?
          bsy - bsylast : AST__BAD;

        if( dxlast != AST__BAD && dylast != AST__BAD &&
            !( !dxlast && !dylast ) ) {
          theta = atan2( dylast, dxlast );
        }

        angle[itime-itime_lo] = theta;

        bsxlast = bsx;
        bsylast = bsy;
      }

/* Initialise pointers to the place to store the final grid coords for
   each bolometer. */
      pgx = wgx;
      pgy = wgy;

/*   Loop round all bolometers. */
      px = outmapcoord;
      py = outmapcoord + nbolo;
      for( ibolo = 0; ibolo < nbolo; ibolo++ ){

/* If good, get the x and y output map GRID coords for this bolometer. */
         if( dx != AST__BAD && dy != AST__BAD ) {
            x = *(px++) + dx;
            y = *(py++) + dy;

/* If required, store them so that we can convert them into lon/lat
   values later. */
            if( pgx && pgy ) {
               *(pgx++) = x;
               *(pgy++) = y;
            }

/* Find the grid indices (minus one) of the output map pixel containing the
   mapped bolo grid coords. One is subtracted in order to simplify the
   subsequent calculation of the vector index. */
            ox = (int) ( x - 0.5 );
            oy = (int) ( y - 0.5 );

/* Check it is within the output map */
            if( ox >= 0 && ox < odimx && oy >= 0 && oy < odimy ) {

/* Find the 1-dimensional vector index into the output array for this
   pixel and store in the next element of the returned LUT. */
               lut[ ilut++ ] = ox + oy*odimx;

/* Store a bad value for points that are off the edge of the output map. */
            } else {
               lut[ ilut++ ] = VAL__BADI;
            }

/* If good, store a bad index in the LUT and move on to the next pixel. */
         } else {
            lut[ ilut++ ] = VAL__BADI;
            px++;
            py++;
            if( pgx && pgy ) {
               *(pgx++) = AST__BAD;
               *(pgy++) = AST__BAD;
            }
         }
      }

/* If required transform the grid coords into (lon,lat) coords and store in
   the relevant elements of the supplied "lon" and "lat" arrays. */
      if( wgx && wgy ) {
         astTran2( oskymap, nbolo, wgx, wgy, 0, lon + itime*nbolo,
                   lat + itime*nbolo );

/* Convert from rads to degs. */
         pgx = lon + itime*nbolo;
         pgy = lat + itime*nbolo;
         for( ibolo = 0; ibolo < nbolo; ibolo++ ) {
            if( *pgx != AST__BAD && *pgy != AST__BAD ) {
               *(pgx++) *= AST__DR2D;
               *(pgy++) *= AST__DR2D;
            } else {
               pgx++;
               pgy++;
            }
         }
      }
   }

/* To obtain a reasonable value for the first entry of angle, we simply
   duplicate the second value. */
   if( angle && (itime_hi > itime_lo) ) {
     angle[0] = angle[1];
   }

/* Free remaining work space. */
   outmapcoord = astFree( outmapcoord );
   wgx = astFree( wgx );
   wgy = astFree( wgy );

/* Export the WCS pointer in the data header since it will be annulled at
   a higher level. Note the FrameSet pointer may be null if the last
   full calculation was for a slice with bad telescope data. */
   if( data->hdr->wcs ) astExport( data->hdr->wcs );

/* End the AST context. */
   astEnd;
}
Exemplo n.º 7
0
double smf_calc_mappa( smfHead *hdr, const char *system, AstFrame *sf,
                       int *status ){

/* Local Variables */
   AstFrameSet *fs = NULL;    /* FrameSet joining tracking and requested systems */
   AstFrame *sf2 = NULL;      /* Frame in requested system */
   const char *oldsys = NULL; /* Original System value for supplied Frame */
   const char *trsys = NULL;  /* AST tracking system */
   const char *usesys = NULL; /* AST system for output cube */
   double map_pa;             /* MAP_PA in tracking system (degs) */
   double p1[ 2 ];            /* Base pointing position */
   double p2[ 2 ];            /* A point on the map vertical axis */
   double p3[ 2 ];            /* A point north of the base pointing position */
   double result;             /* The returned angle */
   double xin[ 2 ];           /* Longitude values in tracking system */
   double xout[ 2 ];          /* Longitude values in requested system */
   double yin[ 2 ];           /* Latitude values in tracking system */
   double yout[ 2 ];          /* Latitude values in requested system */

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

/* Determine the tracking system, and choose the celestial coordinate system
   for the output cube. */
   trsys = sc2ast_convert_system( hdr->state->tcs_tr_sys, status );
   if( !strcmp( system, "TRACKING" ) ) {
      usesys = trsys;
   } else {
      usesys = system;
   }

/* Save the System value of the supplied Frame, and set it to the
   tracking system. */
   oldsys = astGetC( sf, "System" );
   astSetC( sf, "System", trsys );

/* Get the base pointing position in the tracking system. */
   p1[ 0 ] = hdr->state->tcs_tr_bc1;
   p1[ 1 ] = hdr->state->tcs_az_bc2;

/* Move along the map "vertical" axis (as specified by the MAP_PA FITS
   header) for 1 arc-minute from the base pointing position. Set up a
   suitable default first in case the MAP_PA value is undefined. */
   map_pa = 0.0;
   smf_getfitsd( hdr, "MAP_PA", &map_pa, status );
   (void) astOffset2( sf, p1, map_pa*AST__DD2R, AST__DD2R/60.0, p2 );

/* Take a copy of the Frame and set its System value to the requested
   system. */
   sf2 = astCopy( sf );
   astSetC( sf2, "System", usesys );

/* Find a Mapping from the tracking system to the requested system. */
   fs = astConvert( sf, sf2, "" );

/* Use this Mapping to transform the above two positions from tracking to
   the requested system. */
   xin[ 0 ] = p1[ 0 ];
   yin[ 0 ] = p1[ 1 ];
   xin[ 1 ] = p2[ 0 ];
   yin[ 1 ] = p2[ 1 ];
   astTran2( fs, 2, xin, yin, 1, xout, yout );
   p1[ 0 ] = xout[ 0 ];
   p1[ 1 ] = yout[ 0 ];
   p2[ 0 ] = xout[ 1 ];
   p2[ 1 ] = yout[ 1 ];

/* Create a 3rd position which 1 arc-minute to the north of the base
   pointing position in the requested system. */
   p3[ 0 ] = p1[ 0 ];
   p3[ 1 ] = p1[ 1 ] + AST__DD2R/60.0;

/* Find the angle subtended at p1 by p2 and p3. */
   result = astAngle( sf2, p3, p1, p2 );

/* Re-instate the original System value in the supplied Frame. */
   astSetC( sf, "System", oldsys );

/* Free resources. */
   fs = astAnnul( fs );
   sf2 = astAnnul( sf2 );

/* Return the required angle. */
   return result;
}
Exemplo n.º 8
0
void smf_addpolanal( AstFrameSet *fset, smfHead *hdr, AstKeyMap *config,
                     int *status ){

/* Local Variables */
   AstCmpMap *tmap;
   AstFrame *cfrm;
   AstFrame *pfrm;
   AstFrame *tfrm;
   AstFrameSet *tfs;
   AstPermMap *pm;
   char *polnorth = NULL;
   const char *cursys;
   const char *trsys;
   int aloff;
   int icurr;
   int inperm[2];
   int outperm[2];
   int pol2fp;

/* Check inherited status, and also check the supplied angle is not bad. */
   if( *status != SAI__OK ) return;

/* Begin an AST object context. */
   astBegin;

/* Get the value of the POLNORTH FITS keyword from the supplied header.
   The rest only happens if the keyword is found. */
   if( astGetFitsS( hdr->fitshdr, "POLNORTH", &polnorth ) ) {

/* Normally, we do not allow maps to be made from Q/U time streams that
   use focal plane Y as the reference direction (because of the problems
   of sky rotation). Therefore we report an error. However, we do need to
   make such maps as part of the process of determining the parameters of
   the Instrumental Polarisation (IP) model. So only report the error if
   "pol2fp" config parameter is non-zero. */
      if( !strcmp( polnorth, "FPLANE" ) ) {
         astMapGet0I( config, "POL2FP", &pol2fp );

         if( pol2fp ) {
            msgBlank( status );
            msgOut( "", "WARNING: The input NDFs hold POL-2 Q/U data specified "
                    "with respect to focal plane Y.",status );
            msgOut( "", "Maps should normally be made from POL-2 data specified "
                    "with respect to celestial north.",status );
            msgOut( "", "The output map will not contain a POLANAL Frame and "
                    "so will be unusable by POLPACK applications.",status );
            msgBlank( status );

         } else if( *status == SAI__OK ) {
            *status = SAI__ERROR;
            errRep( "", "The input NDFs hold POL-2 Q/U data specified with "
                    "respect to focal plane Y.",status );
            errRep( "", "Maps can only be made from POL-2 data specified with "
                    "respect to celestial north.",status );
         }

/* If the ref. direction is celestial north, create a suitable Frame and
   Mapping and add them into the supplied FrameSet. */
      } else {

/* Check the current Frame is a SkyFrame. */
         cfrm = astGetFrame( fset, AST__CURRENT );
         if( astIsASkyFrame( cfrm ) ) {

/* Create a POLANAL Frame. */
            pfrm = astFrame( 2, "Domain=POLANAL" );
            astSet( pfrm, "Title=Polarimetry reference frame" );
            astSet( pfrm, "Label(1)=Polarimetry reference direction" );
            astSet( pfrm, "Label(2)=" );

/* Create a PermMap that ensures that axis 1 of the POLANAL Frame is parallel
   to the latitude axis (i.e. north) of the curent Frame (the current Frame axes
   may have been swapped). */
            outperm[ 0 ] = astGetI( cfrm, "LatAxis" );
            outperm[ 1 ] = astGetI( cfrm, "LonAxis" );
            inperm[ outperm[ 0 ] - 1 ] = 1;
            inperm[ outperm[ 1 ] - 1 ] = 2;
            pm = astPermMap( 2, inperm, 2, outperm, NULL, " " );

/* Record the index of the original current Frame. */
            icurr = astGetI( fset, "Current" );

/* Determine the system to use. */
            if( !strcmp( polnorth, "TRACKING" ) ) {
               trsys = sc2ast_convert_system( hdr->state->tcs_tr_sys, status );
            } else {
               trsys = polnorth;
            }

/* If the current Frame in the supplied FrameSet has this system. Then we
   use the above PermMap to connect the POLANAL Frame directly to the current
   Frame. */
            cursys = astGetC( cfrm, "System" );
            if( trsys && cursys && !strcmp( cursys, trsys ) ) {
               astAddFrame( fset, AST__CURRENT, pm, pfrm );

/* Otherwise we need to get a Mapping from the current Frame to the
   required frame. */
            } else {

/* Take a copy of the current Frame (in order to pick up epoch, observatory
   position, etc), and set its System to the required system. */
               tfrm = astCopy( cfrm );
               astSetC( tfrm, "System", trsys );

/* Get the Mapping from the original current Frame to this modified copy.
   Ensure alignment happens in absolute coords (alignment in offset
   coords is always a unit mapping and so no rotation occurs). */
               aloff = astGetI( cfrm, "AlignOffset" );
               if( aloff ) {
                  astSetI( cfrm, "AlignOffset", 0 );
                  astSetI( tfrm, "AlignOffset", 0 );
               }
               tfs = astConvert( cfrm, tfrm, "SKY" );
               if( aloff ) {
                  astSetI( cfrm, "AlignOffset", 1 );
                  astSetI( tfrm, "AlignOffset", 1 );
               }
               if( tfs ) {

/* Use it, in series with with the above PermMap, to connect the POLANAL frame
   to the current Frame. */
                  tmap = astCmpMap( astGetMapping( tfs, AST__BASE,
                                                   AST__CURRENT ),
                                    pm, 1, " " );
                  astAddFrame( fset, AST__CURRENT, astSimplify( tmap ),
                               pfrm );

/* Report an error if the mapping from current to required system could
   not be found. */
               } else if( *status == SAI__OK ) {
                  *status = SAI__ERROR;
                  errRepf( "", "smf_addpolanal: Could not convert Frame "
                           "from %s to %s (prgramming error).", status,
                           cursys, trsys );
               }
            }

/* Re-instate the original current Frame. */
            astSetI( fset, "Current", icurr );

/* Report an error if the current Frame is not a SkyFrame. */
         } else if( *status == SAI__OK ) {
            *status = SAI__ERROR;
            errRep( "", "smf_addpolanal: The current Frame in the "
                    "supplied FrameSet is not a SkyFrame (prgramming "
                    "error).", status );
         }
      }
   }

/* End the AST object context. */
   astEnd;
}