Example #1
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;
}
Example #2
0
static void smf1_correct_extinction( void *job_data_ptr, int *status ) {
/*
*  Name:
*     smf1_correct_extinction

*  Purpose:
*     Executed in a worker thread to do various calculations for
*     smf_correct_extinction.

*  Invocation:
*     smf1_correct_extinction( void *job_data_ptr, int *status )

*  Arguments:
*     job_data_ptr = SmfCorrectExtinctionData * (Given)
*        Data structure describing the job to be performed by the worker
*        thread.
*     status = int * (Given and Returned)
*        Inherited status.

*/

  /* Local Variables: */
  SmfCorrectExtinctionData *pdata;
  double airmass;          /* Airmass */
  double state_airmass;    /* Airmass read from header */
  double state_az_ac2;     /* Elevation read from header */
  double amprev;           /* Previous airmass in loop */
  double *azel = NULL;     /* AZEL coordinates */
  size_t base;             /* Offset into 3d data array */
  double extcorr = 1.0;    /* Extinction correction factor */
  dim_t i;                 /* Loop counter */
  dim_t k;                 /* Loop counter */
  AstFrameSet *wcs = NULL; /* Pointer to AST WCS frameset */
  AstFrameSet *state_wcs = NULL; /* Pointer to copy of frameset from header */

  /* 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 = (SmfCorrectExtinctionData *) job_data_ptr;

  /* It is more efficient to call astTranGrid than astTran2
     Allocate memory in adaptive mode just in case. */
  if (pdata->method == SMF__EXTMETH_FULL ||
      pdata->method == SMF__EXTMETH_ADAPT ) {
    azel = astMalloc( (2*pdata->npts)*sizeof(*azel) );
  }

  amprev = pdata->amfirst;

  /* Assume we are using quick mode for all time slices. */
  pdata->allquick = 1;

  for ( k=pdata->f1; k<=pdata->f2 && (*status == SAI__OK) ; k++) {
    /* Flags to indicate which mode we are using for this time slice */
    int quick = 0;  /* use single airmass */
    int adaptive = 0; /* switch from quick to full if required */
    if (pdata->method == SMF__EXTMETH_SINGLE) {
      quick = 1;
    } else if (pdata->method == SMF__EXTMETH_ADAPT) {
      quick = 1;
      adaptive = 1;
    } else {
      pdata->allquick = 0;
    }

    /* Call tslice_ast to update the header for the particular
       timeslice. If we're in QUICK mode then we don't need the WCS. Use
       a mutex to prevent multiple threads writing to the header at the same
       time.  */
    thrMutexLock( &data_mutex, status );
    smf_lock_data( pdata->data, 1, status );
    smf_tslice_ast( pdata->data, k, !quick, NO_FTS, status );

    /* Copy the required bit of the header into thread-local storage. */
    if( *status == SAI__OK ) {
       state_airmass = pdata->hdr->state->tcs_airmass;
       state_az_ac2 = pdata->hdr->state->tcs_az_ac2;
       if( !quick && pdata->tau != VAL__BADD && pdata->hdr->wcs) state_wcs = astCopy( pdata->hdr->wcs );
    }

    /* Unlock the AST Objects in the smfData then unlock the local mutex. */
    smf_lock_data( pdata->data, 0, status );
    thrMutexUnlock( &data_mutex, status );

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

    /* Read the WVM tau value if required */
    if (pdata->tausrc == SMF__TAUSRC_WVMRAW) {
      pdata->tau = pdata->wvmtau[k];
    }

    /* in all modes we need to keep track of the previous airmass in case
       we need to gap fill bad telescope data */
    if ( quick && pdata->ndims == 2 ) {
      /* for 2-D we use the FITS header directly */
      /* This may change depending on exact FITS keyword */
      airmass = pdata->amstart;

      /* speed is not an issue for a 2d image */
      adaptive = 0;

    } else {
      /* Else use airmass value in state structure */
      airmass = state_airmass;

      /* if things have gone bad use the previous value else store
         this value. We also need to switch to quick mode and disable adaptive. */
      if (airmass == VAL__BADD || airmass == 0.0 ) {
        if ( state_az_ac2 != VAL__BADD ) {
          /* try the elevation */
          airmass = palAirmas( M_PI_2 - state_az_ac2 );
        } else {
          airmass = amprev;
          quick = 1;
          adaptive = 0;
        }
      } else {
        amprev = airmass;
      }
    }

    /* If we're using the FAST application method, we assume a single
       airmass and tau for the whole array but we have to consider adaptive mode.
       If the tau is bad the extinction correction must also be bad. */
    if( pdata->tau == VAL__BADD) {
      extcorr = VAL__BADD;
    } else if (quick) {
      /* we have an airmass, see if we need to provide per-pixel correction */
      if (adaptive) {
        if (is_large_delta_atau( airmass, pdata->hdr->state->tcs_az_ac2,
                                 pdata->tau, status) ) {
          /* we need WCS if we disable fast mode */
          quick = 0;
          pdata->allquick = 0;

          thrMutexLock( &data_mutex, status );
          smf_lock_data( pdata->data, 1, status );
          smf_tslice_ast( pdata->data, k, 1, NO_FTS, status );
          state_airmass = pdata->hdr->state->tcs_airmass;
          state_az_ac2 = pdata->hdr->state->tcs_az_ac2;
          if (pdata->hdr->wcs) state_wcs = astCopy( pdata->hdr->wcs );
          smf_lock_data( pdata->data, 0, status );
          thrMutexUnlock( &data_mutex, status );

        }
      }

      if (quick) extcorr = exp(airmass*pdata->tau);
    }

    /* The previous test may have forced quick off so we can not combine
       the tests in one if-then-else block */
    if (!quick && pdata->tau != VAL__BADD )  {
      /* Not using quick so retrieve WCS to obtain elevation info */
      wcs = state_wcs;
      /* Check current frame, store it and then select the AZEL
         coordinate system */
      if (wcs != NULL) {
        if (strcmp(astGetC(wcs,"SYSTEM"), "AZEL") != 0) {
          astSet( wcs, "SYSTEM=AZEL"  );
        }
        /* Transfrom from pixels to AZEL */
        astTranGrid( wcs, 2, pdata->lbnd, pdata->ubnd, 0.1, 1000000, 1, 2,
                     pdata->npts, azel );
      } else {
        /* this time slice may have bad telescope data so we trap for this and re-enable
           "quick" with a default value. We'll only get here if airmass was good but
           SMU was bad so we use the good airmass. The map-maker won't be using this
           data but we need to use something plausible so that we do not throw off the FFTs */
        quick = 1;
        extcorr = exp(airmass*pdata->tau);
      }
    }
    /* Loop over data in time slice. Start counting at 1 since this is
       the GRID coordinate frame */
    base = pdata->npts * k;  /* Offset into 3d data array (time-ordered) */

    for (i=0; i < pdata->npts && ( *status == SAI__OK ); i++ ) {
      /* calculate array indices - assumes that astTranGrid fills up
         azel[] array in same order as bolometer data are aligned */
      size_t index;
      if ( pdata->isTordered ) {
        index = base + i;
      } else {
        index = k + (pdata->nframes * i);
      }

      if (!quick) {
        if (pdata->tau != VAL__BADD) {
          double zd;
          zd = M_PI_2 - azel[pdata->npts+i];
          airmass = palAirmas( zd );
          extcorr = exp(airmass*pdata->tau);
        } else {
          extcorr = VAL__BADD;
        }
      }

      if( pdata->allextcorr ) {
        /* Store extinction correction factor */
        pdata->allextcorr[index] = extcorr;
      } else {
        /* Otherwise Correct the data */
        if (extcorr != VAL__BADD) {
          if( pdata->indata && (pdata->indata[index] != VAL__BADD) ) {
            pdata->indata[index] *= extcorr;
          }

          /* Correct the variance */
          if( pdata->vardata && (pdata->vardata[index] != VAL__BADD) ) {
            pdata->vardata[index] *= extcorr * extcorr;
          }
        } else {
          if (pdata->indata) pdata->indata[index] = VAL__BADD;
          if (pdata->vardata) pdata->vardata[index] = VAL__BADD;
        }
      }

    }

    /* Note that we do not need to free "wcs" or revert its SYSTEM
       since smf_tslice_ast will replace the object immediately. */
  } /* End loop over timeslice */

  azel = astFree( azel );

}