smf_qual_t * smf_qual_unmap( ThrWorkForce *wf, int indf, smf_qfam_t family, smf_qual_t * qual, smf_qual_t mask, int * status ) { int canwrite = 0; /* can we write to the file? */ size_t nqbits = 0; /* Number of quality bits in this family */ SmfQualUnmapData *job_data = NULL; SmfQualUnmapData *pdata; int nw; size_t step; int iw; if (*status != SAI__OK) goto CLEANUP; /* do nothing if there is no quality */ if (!qual) return NULL; /* if we do not have an NDF identifier we just free the memory */ if (indf == NDF__NOID) goto CLEANUP; /* See if we have WRITE access to the file */ ndfIsacc( indf, "WRITE", &canwrite, status ); /* if we have WRITE access and the data were not mapped we have to copy to the file. Also check we have a non-NULL input pointer. If the data were mapped we still have to make sure the quality names are stored. */ if ( canwrite && qual ) { int highbit = -1; /* highest bit used */ size_t i; int itemp; int lowbit = -1; /* Lowest bit used */ size_t nout; int nqual = 0; void *qpntr[1]; size_t qcount[SMF__NQBITS]; /* statically allocate the largest array */ IRQLocs *qlocs; unsigned char * qmap; int there; ndfMsg( "FILE", indf ); msgOutif( MSG__DEBUG, "", "Finalising quality for file ^FILE", status); if (family == SMF__QFAM_TCOMP ) { /* note that TCOMP is not an allowed quality because SMURF should not be using it anywhere in a permanent way. */ *status = SAI__ERROR; ndfMsg( "NDF", indf ); errRepf( "", "Unsupported quality family '%s' for quality unmapping of " "file ^NDF", status, smf_qfamily_str(family,status) ); goto CLEANUP; } else if (family == SMF__QFAM_NULL) { /* In this case we have to assume that we just cast the quality to UBYTE and copy it without changing anything or naming the entries. Use a simple type conversion. */ ndfMap( indf, "QUALITY", "_UBYTE", "WRITE", &qpntr[0], &itemp, status ); qmap = qpntr[0]; nout = itemp; for (i = 0; i<nout; i++) { qmap[i] = qual[i]; } ndfUnmap( indf, "QUALITY", status ); /* Turn on all quality */ ndfSbb( 255, indf, status ); /* we are finished so jump to tidy up */ goto CLEANUP; } /* work out how many quality items are in this family */ nqbits = smf_qfamily_count( family, status ); /* initialize qcount */ for (i=0; i<SMF__NQBITS; i++) { qcount[i] = 0; } /* how many pixels in NDF (assumed to be number in quality) */ ndfSize( indf, &itemp, status ); nout = itemp; /* How many threads do we get to play with */ nw = wf ? wf->nworker : 1; /* Find how many elements to process in each worker thread. */ step = nout/nw; if( step == 0 ) step = 1; /* Allocate job data for threads, and store common values. Ensure that the last thread picks up any left-over elements. */ job_data = astCalloc( nw, sizeof(*job_data) ); if( *status == SAI__OK ) { for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; pdata->i1 = iw*step; if( iw < nw - 1 ) { pdata->i2 = pdata->i1 + step - 1; } else { pdata->i2 = nout - 1 ; } pdata->nqbits = nqbits; pdata->qual = qual; pdata->nout = nout; } } /* Work out which bits are actually used */ if (*status == SAI__OK) { size_t k; /* now we try to be a bit clever. It may be a mistake since we have to do multiple passes through "qual". First determine how many quality bits are actually set. */ for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; pdata->operation = 1; thrAddJob( wf, 0, pdata, smf1_qual_unmap, 0, NULL, status ); } thrWait( wf, status ); for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; for( k=0; k<nqbits; k++ ) { qcount[k] += pdata->qcount[k]; } } /* Reset the counts to zero for any bits that are not required (i.e. are not set in "mask"). */ for( k=0; k<nqbits; k++ ) { if( ! (mask & (1<<k)) ) qcount[k] = 0; } /* see how many we got */ for (k=0; k<nqbits; k++) { if ( qcount[k] ) { nqual++; highbit = k; if (lowbit < 0) lowbit = k; } } } /* for IRQ we need to ensure the SMURF extension exists so open and annul it if it is missing. We are completely rewriting any IRQ information so we have to delete any previously existing IRQ extension. */ irqDelet( indf, status ); ndfXstat( indf, SMURF__EXTNAME, &there, status ); if (!there) { HDSLoc * smurfloc = NULL; /* Create SMURF extension if it does not already exist */ ndfXnew( indf, SMURF__EXTNAME, SMURF__EXTTYPE, 0, NULL, &smurfloc, status ); if (smurfloc) datAnnul( &smurfloc, status ); } irqNew( indf, SMURF__EXTNAME, &qlocs, status ); /* malloced so we need to map and copy over the values. IRQ names need to be set BEFORE we copy. */ /* Map the quality component with WRITE access */ ndfMap( indf, "QUALITY", "_UBYTE", "WRITE", &qpntr[0], &itemp, status ); qmap = qpntr[0]; /* we assume the number of elements in "qual" is the same as in "qmap" */ if (*status == SAI__OK) { size_t k; /* if we only have 8 or fewer bits active we can just compress by mapping them to the lower 8 bits. This will work if we also set the IRQ quality names in the NDF. */ if (nqual == 0 ) { /* easy */ memset( qmap, 0, nout * smf_dtype_sz( SMF__UBYTE, status ) ); } else if ( nqual <= 8 ) { size_t curbit = 0; /* and the quality names. Start at lowbit and go to highbit knowing that we have shifted them down so that lowbit in qual is bit 0 in NDF. */ for (k=lowbit; k<=(size_t)highbit; k++) { if (qcount[k]) { int fixed = 0; /* is bit fixed? */ const char * qdesc = NULL; /* Description of quality */ const char * qstr = NULL; /* Quality string identifier */ curbit++; qstr = smf_qual_str( family, 1, k, &qdesc, status ); irqAddqn( qlocs, qstr, 0, qdesc, status ); irqFxbit( qlocs, qstr, curbit, &fixed, status ); } } /* shift them down */ for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; pdata->operation = 2; pdata->qmap = qmap; pdata->highbit = highbit; pdata->lowbit = lowbit; for( k=0; k<nqbits; k++ ) { pdata->qcount[k] = qcount[k]; } thrAddJob( wf, 0, pdata, smf1_qual_unmap, 0, NULL, status ); } thrWait( wf, status ); } else { size_t curbit = 0; /* Quality names are now needed and we have to write them all out because we have not compressed the bits in the output quality array we've only compressed the input. To limit the number of active bits we'd have to copy the compressed bits to the output and then set the quality names but IRQ does not let you do that so you would need to run through the entire array first counting which bits were used. */ for (k=0; k<SMF__NQBITS_TCOMP; k++) { int fixed = 0; const char * qdesc = NULL; /* Description of quality */ const char * qstr = NULL; /* Quality string identifier */ qstr = smf_qual_str( SMF__QFAM_TCOMP, 1, k, &qdesc, status ); /* Set the quality name */ irqAddqn( qlocs, qstr, 0, qdesc, status ); curbit++; irqFxbit( qlocs, qstr, curbit, &fixed, status ); } /* compress them */ for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; pdata->operation = 3; pdata->qmap = qmap; thrAddJob( wf, 0, pdata, smf1_qual_unmap, 0, NULL, status ); } thrWait( wf, status ); } } /* Unmap quality */ ndfUnmap( indf, "QUALITY", status ); /* Set the badbits mask to enable all quality by default. Do not do this for MAP quality at the moment. */ if (family != SMF__QFAM_MAP) ndfSbb( 255, indf, status ); /* release IRQ resources */ irqRlse( &qlocs, status ); } CLEANUP: /* Tidy up */ qual = astFree( qual ); job_data = astFree( job_data ); return NULL; }
int write_ndf( int argc, void *argv[]) { /* ** Declare variables */ void *arr; /* Pointer to the data array */ int n_dims; /* The number of dimensions */ int *arr_bnds; /* The dimensions */ IDL_STRING *ndf_name; /* The name of the NDF to be created */ IDL_STRING *comp; /* The component name */ IDL_STRING *type; /* The HDS type of the NDF to be created */ int badset; /* Whether bad value is set */ void *bad_value; /* Pointer to the bad value */ int status; /* Starlink status */ int lbnd[5]={1L,1L,1L,1L,1L}; /* Lower bounds array */ int ndf; /* NDF identifier */ int place; /* NDF placeholder */ int npix; /* Number of pixels */ void *ptr[3]; /* Pointer to mapped NDF data Fortran style */ size_t nbytes; /* Number of bytes in NDF */ int bpix=1; /* Number of bits/pixel */ int idltype=1; /* Pixel type code */ int fstat; /* Final status (before errLoad) */ int errn=0; /* Error sequence number */ char param[ERR__SZPAR]; /* Error message parameter name */ int parlen; /* Length of error message parameter name */ char opstr[ERR__SZMSG]; /* Error message */ int oplen; /* Length of error message */ /* ** Start Error context */ status = SAI__OK; errMark(); /* ** Check that the correct number of arguments were passed in */ if(argc != 8) { /* ** Print an error message and return */ status = SAI__ERROR; errRep( " ", "write_ndf: Incorrect number of arguments", &status ); } else { /* ** Extract the arguments to comprehensible names */ arr = argv[0]; n_dims = *(int *)argv[1]; arr_bnds = (int *)argv[2]; ndf_name = (IDL_STRING *)argv[3]; comp = (IDL_STRING *)argv[4]; type = (IDL_STRING *)argv[5]; badset = *(int *)argv[6]; bad_value = (void *)argv[7]; /* ** Enable NDF calls */ ndfBegin(); if ( !strcmp( comp->s, "DATA" ) ) { /* ** Create the NDF */ ndfOpen( NULL, ndf_name->s, "WRITE", "NEW", &ndf, &place, &status ); ndfNew( type->s, n_dims, lbnd, arr_bnds, &place, &ndf, &status ); } else { if ( !strcmp( comp->s, "QUALITY") && strcmp(type->s, "_UBYTE")) { status = SAI__ERROR; errRep( " ", "write_ndf: Incorrect type for QUALITY", &status ); } /* ** Open existing NDF */ ndfOpen( NULL, ndf_name->s, "UPDATE", "OLD", &ndf, &place, &status ); } /* ** Check the number of pixels is same in given array and NDF for QUALITY ** and VARIANCE. */ if ( strcmp( comp->s, "DATA" ) ) { ndfSize( ndf, &npix, &status ); if ( ( status == SAI__OK ) && ( npix != arr_bnds[n_dims+1] ) ) { /* ** Array size and NDF size do not agree */ status = SAI__ERROR; errRep( " ", "write_ndf: Incorrect number elements supplied", &status ); } } /* ** Obtain mapped access to the array component of the NDF */ ndfMap( ndf, comp->s, type->s, "WRITE", ptr, &npix, &status ); /* ** Now copy the values from ARR into the mapped NDF */ if ( status == SAI__OK ) { /* ** First check the returned pointer is good */ if ( ptr[0] == NULL ) { /* ** Fortran to C pointer conversion failed */ status = SAI__ERROR; errRep( " ", "write_ndf: Fortran to C pointer conversion failed", &status ); } else { /* ** then get the IDL type code and number of bytes per pixel */ if (!strcmp(type->s, "_REAL")) { idltype = 4; bpix = 4; } else if (!strcmp(type->s, "_INTEGER")) { idltype = 3; bpix = 4; } else if (!strcmp(type->s, "_WORD")) { idltype = 2; bpix = 2; } else if (!strcmp(type->s, "_DOUBLE")) { idltype = 5; bpix = 8; } else if (!strcmp(type->s, "_UBYTE")) { idltype = 1; bpix = 1; } else { status = SAI__ERROR; msgSetc( "TYPE", type->s ); errRep( " ", "Illegal type ^TYPE", &status ); } /* ** Now copy the data from the array to the NDF. ** If we need to check for bad values in the array, use copybadout. ** If any bad values are found copybadout will replace them with the ** appropriate PRIMDAT bad value. The NDF bad pixel flag is then set ** depending on whether copybadout detected any bad values. ** If we need not check for bad pixels just copy the whole lot; */ if ( status == SAI__OK ) { if ( badset ) { if ( !copybadout( ptr[0], arr, npix, idltype, bad_value ) ) ndfSbad( 0, ndf, comp->s, &status); } else { nbytes = bpix * npix; memcpy( ptr[0], arr, nbytes ); } } } } /* ** Close NDF */ ndfEnd( &status ); } /* ** Report any error messages ** Adding Starlink-style !! and ! prefix */ fstat = status; while ( status != SAI__OK ) { errLoad( param, ERR__SZPAR, &parlen, opstr, ERR__SZMSG, &oplen, &status ); if ( status != SAI__OK ) printf( "%s %s\r\n", errn++?"! ":"!!", opstr ); } errRlse(); /* ** That's it, return to the calling routine */ return( fstat == SAI__OK ); }
// Reads an NDF into a numpy array static PyObject* pyndf_read(NDF *self, PyObject *args) { int i; const char *comp; if(!PyArg_ParseTuple(args, "s:pyndf_read", &comp)) return NULL; // series of declarations in an attempt to avoid problem with // goto fail const int MXLEN=32; char type[MXLEN+1]; size_t nbyte; int npix, nelem; // Return None if component does not exist int state, status = SAI__OK; errBegin(&status); ndfState(self->_ndfid, comp, &state, &status); if (raiseNDFException(&status)) return NULL; if(!state) Py_RETURN_NONE; PyArrayObject* arr = NULL; // Get dimensions, reverse order to account for C vs Fortran int idim[NDF__MXDIM]; npy_intp rdim[NDF__MXDIM]; int ndim; errBegin(&status); ndfDim(self->_ndfid, NDF__MXDIM, idim, &ndim, &status); if (raiseNDFException(&status)) return NULL; // Reverse order to account for C vs Fortran for(i=0; i<ndim; i++) rdim[i] = idim[ndim-i-1]; // Determine the data type errBegin(&status); ndfType(self->_ndfid, comp, type, MXLEN+1, &status); if (raiseNDFException(&status)) return NULL; // Create array of correct dimensions and type to save data to int npytype = ndftype2numpy( type, &nbyte ); if (npytype == 0) return NULL; arr = (PyArrayObject*) PyArray_SimpleNew(ndim, rdim, npytype); if(arr == NULL) goto fail; // get number of elements, allocate space, map, store errBegin(&status); ndfSize(self->_ndfid, &npix, &status); if (raiseNDFException(&status)) goto fail; void *pntr[1]; errBegin(&status); ndfMap(self->_ndfid, comp, type, "READ", pntr, &nelem, &status); if (raiseNDFException(&status)) goto fail; if(nelem != npix){ PyErr_SetString(PyExc_IOError, "ndf_read error: number of elements different from number expected"); goto fail; } memcpy(arr->data, pntr[0], npix*nbyte); errBegin(&status); ndfUnmap(self->_ndfid, comp, &status); if (raiseNDFException(&status)) goto fail; return Py_BuildValue("N", PyArray_Return(arr)); fail: Py_XDECREF(arr); return NULL; };
smf_qual_t * smf_qual_map( ThrWorkForce *wf, int indf, const char mode[], smf_qfam_t *family, size_t *nmap, int * status ) { size_t i; /* Loop counter */ int itemp = 0; /* temporary int */ smf_qfam_t lfamily = SMF__QFAM_NULL; /* Local quality family */ size_t nout; /* Number of elements mapped */ size_t numqn = 0; /* number of quality names */ IRQLocs *qlocs = NULL;/* IRQ Quality */ unsigned char *qmap; /* pointer to mapped unsigned bytes */ void *qpntr[1]; /* Somewhere to put the mapped pointer */ smf_qual_t *retval = NULL; /* Returned pointer */ int there; /* Does the NDF Have a Quality component? */ char xname[DAT__SZNAM+1]; /* Name of extension holding quality names */ SmfQualMapData *job_data = NULL; SmfQualMapData *pdata; int nw; size_t step; int iw; if (*status != SAI__OK) return retval; /* Ensure jobs submitted to the workforce within this function are handled separately to any jobs submitted earlier (or later) by any other function. */ thrBeginJobContext( wf, status ); /* how many elements do we need */ ndfSize( indf, &itemp, status ); nout = itemp; if (nmap) *nmap = nout; /* malloc the QUALITY buffer. Initialise to zero to simplify logic below. It is difficult to determine in advance which case can use initialisation. */ retval = astCalloc( nout, sizeof(*retval) ); /* How many threads do we get to play with */ nw = wf ? wf->nworker : 1; /* Find how many elements to process in each worker thread. */ step = nout/nw; if( step == 0 ) step = 1; /* Allocate job data for threads, and store common values. Ensure that the last thread picks up any left-over elements. */ job_data = astCalloc( nw, sizeof(*job_data) ); if( *status == SAI__OK ) { for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; pdata->i1 = iw*step; if( iw < nw - 1 ) { pdata->i2 = pdata->i1 + step - 1; } else { pdata->i2 = nout - 1 ; } pdata->retval = retval; } } /* If the NDF has no QUality component, return the buffer filled with zeros. */ ndfState( indf, "QUALITY", &there, status ); if( there ) { /* READ and UPDATE mode require that the QUALITY is processed and copied before being returned. WRITE mode means that the buffer contains no information to copy yet. WRITE/ZERO and WRITE/BAD also require that we do not do any quality handling */ if ( strncmp(mode, "WRITE",5) == 0 ) { /* WRITE and WRITE/ZERO are actually treated the same way because we always initialise */ if ( strcmp( mode, "WRITE/BAD") == 0 ) { for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; pdata->operation = 1; thrAddJob( wf, 0, pdata, smf1_qual_map, 0, NULL, status ); } thrWait( wf, status ); } /* unmap the NDF buffer and return the pointer */ if (family) *family = lfamily; return retval; } /* Map the quality component (we always need to do this) */ ndfMap( indf, "QUALITY", "_UBYTE", mode, &qpntr[0], &itemp, status ); qmap = qpntr[0]; /* Need to find out what quality names are in play so we can work out which family to translate them to */ irqFind( indf, &qlocs, xname, status ); numqn = irqNumqn( qlocs, status ); if ( *status == IRQ__NOQNI || numqn == 0) { /* do not have any names defined so we have no choice in copying the values directly out the file */ if (*status != SAI__OK) errAnnul( status ); /* simple copy with type conversion */ for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; pdata->qmap = qmap; pdata->operation = 2; thrAddJob( wf, 0, pdata, smf1_qual_map, 0, NULL, status ); } thrWait( wf, status ); } else { IRQcntxt contxt = 0; int ndfqtosmf[NDFBITS]; /* NDF bit (arr index) and SMURF alternative */ int ndfqtoval[NDFBITS]; /* NDF bit (arr index) and corresponding Qual value */ int ndfqval[NDFBITS]; /* Bit values for NDF quality */ int identity = 1; /* Is this a simple identity map? */ /* prefill the mapping with bit to bit mapping */ for (i=0; i<NDFBITS; i++) { ndfqtosmf[i] = i; ndfqtoval[i] = BIT_TO_VAL(i); ndfqval[i] = ndfqtoval[i]; } /* Now translate each name to a bit */ for (i = 0; i < numqn && *status == SAI__OK; i++) { char qname[IRQ__SZQNM+1]; char commnt[IRQ__SZCOM+1]; int fixed; int value; int bit; int done; smf_qual_t qval; smf_qfam_t tmpfam = 0; irqNxtqn( qlocs, &contxt, qname, &fixed, &value, &bit, commnt,sizeof(commnt), &done, status ); bit--; /* IRQ starts at 1 */ /* Now convert the quality name to a quality value and convert that to a bit. These should all be less than 9 bits because they are in the NDF file. */ qval = smf_qual_str_to_val( qname, &tmpfam, status ); if (*status == SMF__BADQNM ) { /* annul status and just copy this bit from the file to SMURF without change. This might result in a clash of bits but we either do that or drop out the loop and assume everything is broken */ if (*status != SAI__OK) errAnnul(status); ndfqtosmf[bit] = bit; ndfqtoval[bit] = BIT_TO_VAL(bit); } else if( *status == SAI__OK ){ if (lfamily == SMF__QFAM_NULL) { lfamily = tmpfam; } else if (lfamily != tmpfam) { msgOutif(MSG__QUIET, "", "WARNING: Quality names in file come from different families", status ); } ndfqtosmf[bit] = smf_qual_to_bit( qval, status ); ndfqtoval[bit] = qval; /* not a 1 to 1 bit translation */ if (bit != ndfqtosmf[bit]) identity = 0; } } /* Now copy from the file and translate the bits. If this is an identity mapping or we do not know the family then we go quick. */ if (*status == SAI__OK) { if ( (identity && lfamily != SMF__QFAM_TCOMP) || lfamily == SMF__QFAM_NULL) { for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; pdata->qmap = qmap; pdata->ndfqval = ndfqval; pdata->lfamily = lfamily; pdata->ndfqtoval = ndfqtoval; pdata->ndfqval = ndfqval; pdata->operation = 2; thrAddJob( wf, 0, pdata, smf1_qual_map, 0, NULL, status ); } thrWait( wf, status ); } else { for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; pdata->qmap = qmap; pdata->ndfqval = ndfqval; pdata->lfamily = lfamily; pdata->ndfqtoval = ndfqtoval; pdata->ndfqval = ndfqval; pdata->operation = 3; thrAddJob( wf, 0, pdata, smf1_qual_map, 0, NULL, status ); } thrWait( wf, status ); /* we have uncompressed */ if (lfamily == SMF__QFAM_TCOMP) lfamily = SMF__QFAM_TSERIES; } } } /* Free quality */ irqRlse( &qlocs, status ); /* no longer need the mapped data */ ndfUnmap( indf, "QUALITY", status ); } /* End the Thr job context */ thrEndJobContext( wf, status ); /* Free other resources. */ job_data = astFree( job_data ); if (family) *family = lfamily; return retval; }
smf_qual_t * smf_qual_unmap( int indf, smf_qfam_t family, smf_qual_t * qual, int * status ) { int canwrite = 0; /* can we write to the file? */ size_t nqbits = 0; /* Number of quality bits in this family */ if (*status != SAI__OK) goto CLEANUP; /* do nothing if there is no quality */ if (!qual) return NULL; /* if we do not have an NDF identifier we just free the memory */ if (indf == NDF__NOID) goto CLEANUP; /* See if we have WRITE access to the file */ ndfIsacc( indf, "WRITE", &canwrite, status ); /* if we have WRITE access and the data were not mapped we have to copy to the file. Also check we have a non-NULL input pointer. If the data were mapped we still have to make sure the quality names are stored. */ if ( canwrite && qual ) { int highbit = -1; /* highest bit used */ size_t i; int itemp; int lowbit = -1; /* Lowest bit used */ size_t nout; int nqual = 0; void *qpntr[1]; size_t qcount[SMF__NQBITS]; /* statically allocate the largest array */ IRQLocs *qlocs; unsigned char * qmap; int there; ndfMsg( "FILE", indf ); msgOutif( MSG__DEBUG, "", "Finalising quality for file ^FILE", status); if (family == SMF__QFAM_TCOMP ) { /* note that TCOMP is not an allowed quality because SMURF should not be using it anywhere in a permanent way. */ *status = SAI__ERROR; ndfMsg( "NDF", indf ); errRepf( "", "Unsupported quality family '%s' for quality unmapping of " "file ^NDF", status, smf_qfamily_str(family,status) ); goto CLEANUP; } else if (family == SMF__QFAM_NULL) { /* In this case we have to assume that we just cast the quality to UBYTE and copy it without changing anything or naming the entries. Use a simple type conversion. */ ndfMap( indf, "QUALITY", "_UBYTE", "WRITE", &qpntr[0], &itemp, status ); qmap = qpntr[0]; nout = itemp; for (i = 0; i<nout; i++) { qmap[i] = qual[i]; } ndfUnmap( indf, "QUALITY", status ); /* Turn on all quality */ ndfSbb( 255, indf, status ); /* we are finished so jump to tidy up */ goto CLEANUP; } /* work out how many quality items are in this family */ nqbits = smf_qfamily_count( family, status ); /* initialize qcount */ for (i=0; i<SMF__NQBITS; i++) { qcount[i] = 0; } /* how many pixels in NDF (assumed to be number in quality) */ ndfSize( indf, &itemp, status ); nout = itemp; /* Work out which bits are actually used */ if (*status == SAI__OK) { size_t k; /* now we try to be a bit clever. It may be a mistake since we have to do multiple passes through "qual". First determine how many quality bits are actually set. */ for (i = 0; i<nout; i++) { /* try all the bits */ for( k=0; k<nqbits; k++ ) { if( qual[i] & BIT_TO_VAL(k) ) { qcount[k]++; } } } /* see how many we got */ for (k=0; k<nqbits; k++) { if ( qcount[k] ) { nqual++; highbit = k; if (lowbit < 0) lowbit = k; } } } /* for IRQ we need to ensure the SMURF extension exists so open and annul it if it is missing. We are completely rewriting any IRQ information so we have to delete any previously existing IRQ extension. */ irqDelet( indf, status ); ndfXstat( indf, SMURF__EXTNAME, &there, status ); if (!there) { HDSLoc * smurfloc = NULL; /* Create SMURF extension if it does not already exist */ ndfXnew( indf, SMURF__EXTNAME, SMURF__EXTTYPE, 0, NULL, &smurfloc, status ); if (smurfloc) datAnnul( &smurfloc, status ); } irqNew( indf, SMURF__EXTNAME, &qlocs, status ); /* malloced so we need to map and copy over the values. IRQ names need to be set BEFORE we copy. */ /* Map the quality component with WRITE access */ ndfMap( indf, "QUALITY", "_UBYTE", "WRITE", &qpntr[0], &itemp, status ); qmap = qpntr[0]; /* we assume the number of elements in "qual" is the same as in "qmap" */ if (*status == SAI__OK) { size_t k; /* if we only have 8 or fewer bits active we can just compress by mapping them to the lower 8 bits. This will work if we also set the IRQ quality names in the NDF. */ if (nqual == 0 ) { /* easy */ memset( qmap, 0, nout * smf_dtype_sz( SMF__UBYTE, status ) ); } else if ( nqual <= 8 ) { size_t curbit = 0; /* and the quality names. Start at lowbit and go to highbit knowing that we have shifted them down so that lowbit in qual is bit 0 in NDF. */ for (k=lowbit; k<=(size_t)highbit; k++) { if (qcount[k]) { int fixed = 0; /* is bit fixed? */ const char * qdesc = NULL; /* Description of quality */ const char * qstr = NULL; /* Quality string identifier */ curbit++; qstr = smf_qual_str( family, 1, k, &qdesc, status ); irqAddqn( qlocs, qstr, 0, qdesc, status ); irqFxbit( qlocs, qstr, curbit, &fixed, status ); } } /* shift them down */ for (i=0; i<nout; i++) { curbit = 0; qmap[i] = 0; for (k=lowbit; k<=(size_t)highbit; k++) { /* was this bit used by this data array? */ if (qcount[k]) { /* was the bit set for this location? */ if ( qual[i]&BIT_TO_VAL(k)) { qmap[i] |= BIT_TO_VAL(curbit); } curbit++; } } } } else { size_t curbit = 0; /* Quality names are now needed and we have to write them all out because we have not compressed the bits in the output quality array we've only compressed the input. To limit the number of active bits we'd have to copy the compressed bits to the output and then set the quality names but IRQ does not let you do that so you would need to run through the entire array first counting which bits were used. */ for (k=0; k<SMF__NQBITS_TCOMP; k++) { int fixed = 0; const char * qdesc = NULL; /* Description of quality */ const char * qstr = NULL; /* Quality string identifier */ qstr = smf_qual_str( SMF__QFAM_TCOMP, 1, k, &qdesc, status ); /* Set the quality name */ irqAddqn( qlocs, qstr, 0, qdesc, status ); curbit++; irqFxbit( qlocs, qstr, curbit, &fixed, status ); } /* compress them */ for (i = 0; i<nout; i++) { qmap[i] = 0; if (qual[i]) { if ( qual[i] & (SMF__Q_BADDA|SMF__Q_BADB|SMF__Q_NOISE) ) { qmap[i] |= SMF__TCOMPQ_BAD; } if ( qual[i] & (SMF__Q_APOD|SMF__Q_PAD) ) { qmap[i] |= SMF__TCOMPQ_ENDS; } if ( qual[i] & (SMF__Q_JUMP|SMF__Q_SPIKE|SMF__Q_FILT|SMF__Q_EXT|SMF__Q_LOWAP|SMF__Q_BADEF) ) { qmap[i] |= SMF__TCOMPQ_BLIP; } if ( qual[i] & (SMF__Q_COM) ) { qmap[i] |= SMF__TCOMPQ_MATCH; } if ( qual[i] & (SMF__Q_STAT) ) { qmap[i] |= SMF__TCOMPQ_TEL; } if (qmap[i] == 0 ) { /* something went wrong. We missed a quality bit somewhere */ msgOutiff(MSG__QUIET, "", FUNC_NAME ": Untested quality bit found" " in position %zu with value %u", status, i, (unsigned int)qual[i]); } } } } } /* Unmap quality */ ndfUnmap( indf, "QUALITY", status ); /* Set the badbits mask to enable all quality by default. Do not do this for MAP quality at the moment. */ if (family != SMF__QFAM_MAP) ndfSbb( 255, indf, status ); /* release IRQ resources */ irqRlse( &qlocs, status ); } CLEANUP: /* Tidy up */ if (qual) qual = astFree( qual ); return NULL; }