IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 27304 for trunk


Ignore:
Timestamp:
Mar 16, 2010, 6:28:29 PM (16 years ago)
Author:
watersc1
Message:

IPP->OTIS image quality statistic publishing tool, with all the recent updates to make it work correctly and produce useful tables.

Location:
trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/ippTasks/dqstats.pro

    r26378 r27304  
    11## dqstats.pro : globals and support macros : -*- sh -*-
    22## this file contains the tasks for running the dqstats analysis stage
    3 ## these tasks use the book dqstatsPendingExp
     3## these tasks use the book dqstatsPendingBundle
    44
    55# test for required global variables
    66check.globals
    77
    8 book init dqstatsPendingExp
     8book init dqstatsPendingBundle
    99
    1010macro dqstats.status
    11   book listbook dqstatsPendingExp
     11  book listbook dqstatsPendingBundle
    1212end
    1313
    1414macro dqstats.reset
    15   book init dqstatsPendingExp
     15  book init dqstatsPendingBundle
    1616end
    1717
     
    4545
    4646# select images ready for dqstats analysis
    47 # new entries are added to dqstatsPendingExp
     47# new entries are added to dqstatsPendingBundle
    4848# skip already-present entries
    4949task           dqstats.load
     
    7070      if ($dqstats_DB >= $DB:n) set dqstats_DB = 0
    7171    end
    72     add_poll_args run
    73     add_poll_labels run
     72#    add_poll_args run
     73#    add_poll_labels run
    7474    command $run
    7575  end
     
    7878  task.exit    0
    7979    # convert 'stdout' to book format
    80     ipptool2book stdout dqstatsPendingExp -key dqstats_id -uniq -setword dbname $options:0 -setword pantaskState INIT
     80    ipptool2book stdout dqstatsPendingBundle -key dqstats_id -uniq -setword dbname $options:0 -setword pantaskState INIT
    8181    if ($VERBOSE > 2)
    82       book listbook dqstatsPendingExp
     82      book listbook dqstatsPendingBundle
    8383    end
    8484
    8585    # delete existing entries in the appropriate pantaskStates
    86     process_cleanup dqstatsPendingExp
     86    process_cleanup dqstatsPendingBundle
    8787  end
    8888
     
    106106  periods      -poll $RUNPOLL
    107107  periods      -exec $RUNEXEC
    108   periods      -timeout 10
     108  periods      -timeout 600
    109109
    110110  ## we want only a single outstanding dqstats job. 
     
    113113
    114114  task.exec
    115     book npages dqstatsPendingExp -var N
     115    book npages dqstatsPendingBundle -var N
    116116    if ($N == 0) break
    117117    if ($NETWORK == 0) break
    118118   
    119     # look for new images in dqstatsPendingExp (pantaskState == INIT)
    120     book getpage dqstatsPendingExp 0 -var pageName -key pantaskState INIT
     119    # look for new images in dqstatsPendingBundle (pantaskState == INIT)
     120    book getpage dqstatsPendingBundle 0 -var pageName -key pantaskState INIT
    121121    if ("$pageName" == "NULL") break
    122122
    123     book setword dqstatsPendingExp $pageName pantaskState RUN
    124     book getword dqstatsPendingExp $pageName camera -var CAMERA
    125     book getword dqstatsPendingExp $pageName exp_tag -var EXP_TAG
    126     book getword dqstatsPendingExp $pageName dqstats_id -var DQSTATS_ID
    127 #    book getword dqstatsPendingExp $pageName camroot -var CAMROOT
    128 #    book getword dqstatsPendingExp $pageName workdir -var WORKDIR_TEMPLATE
    129 #    book getword dqstatsPendingExp $pageName reduction -var REDUCTION
    130 #    book getword dqstatsPendingExp $pageName dvodb  -var DVODB
    131 #    book getword dqstatsPendingExp $pageName image_only -var IMAGE_ONLY
    132     book getword dqstatsPendingExp $pageName dbname -var DBNAME
     123    book setword dqstatsPendingBundle $pageName pantaskState RUN
     124    book getword dqstatsPendingBundle $pageName camera -var CAMERA
     125    book getword dqstatsPendingBundle $pageName dqstats_id -var DQSTATS_ID
     126    book getword dqstatsPendingBundle $pageName dbname -var DBNAME
    133127
    134128    # specify choice of remote host based on camera and chip (class_id)
     
    153147    stderr $LOGDIR/dqstats.log
    154148
    155     $run = dqstats_bundle.pl --exp_tag $EXP_TAG --dqstats_id $DQSTATS_ID --camera GPC1 --redirect-output
    156 #     if ("$REDUCTION" != "NULL")
    157 #       $run = $run --reduction $REDUCTION
    158 #     end
    159 #     if ("$IMAGE_ONLY" == "T")
    160 #       $run = $run --image-only
    161 #     end
    162 
     149    $run = dqstats_bundle.pl --dqstats_id $DQSTATS_ID --camera $CAMERA --redirect-output
    163150       
    164151    add_standard_args run
     
    176163  # success
    177164  task.exit default
    178     process_exit dqstatsPendingExp $options:0 $JOB_STATUS
     165    process_exit dqstatsPendingBundle $options:0 $JOB_STATUS
    179166  end
    180167
     
    183170    showcommand crash
    184171    echo "hostname: $JOB_HOSTNAME"
    185     book setword dqstatsPendingExp $options:0 pantaskState CRASH
     172    book setword dqstatsPendingBundle $options:0 pantaskState CRASH
    186173  end
    187174
     
    189176  task.exit    timeout
    190177    showcommand timeout
    191     book setword dqstatsPendingExp $options:0 pantaskState TIMEOUT
     178    book setword dqstatsPendingBundle $options:0 pantaskState TIMEOUT
    192179  end
    193180end
  • trunk/ippTools/share/dqstatstool_definebyquery.sql

    r26378 r27304  
    11-- query to identify unique data to bundle
    22
    3 SELECT exp_id,chip_id,cam_id,warp_id,invalid from rawExp
     3SELECT DISTINCT exp_id,chip_id,cam_id,warp_id,invalid from rawExp
    44        JOIN chipRun USING(exp_id)
    55        JOIN camRun USING(chip_id)
     
    77        LEFT OUTER JOIN warpRun USING(fake_id)
    88        LEFT OUTER JOIN dqstatsContent USING (exp_id,chip_id,cam_id,warp_id)
    9 WHERE (invalid IS NULL OR invalid = 1)
     9WHERE camRun.state = 'full'
  • trunk/ippTools/share/dqstatstool_get_run.sql

    r26378 r27304  
    1 SELECT * from dqstatsRun
    2         WHERE state = 'new' AND (fault = 0)
     1SELECT DISTINCT dqstatsRun.*,rawExp.camera from dqstatsRun
     2       JOIN dqstatsContent USING(dqstats_id)
     3       JOIN rawExp USING(exp_id)
     4        WHERE dqstatsRun.state = 'new' AND (dqstatsRun.fault = 0)
  • trunk/ippTools/src/dqstatstool.c

    r26981 r27304  
    4545  }
    4646
    47   psFree(config);
     47  //psFree(config);
    4848  pmConfigDone();
    4949  psLibFinalize();
     
    5454  int exit_status = pxerrorGetExitStatus();
    5555
    56   psFree(config);
     56  //    psFree(config);
    5757  pmConfigDone();
    5858  psLibFinalize();
     
    8888  }
    8989  // use psDBGenerateWhereConditionSQL because the SQL ends in a WHERE
     90  if (force) {
     91    psStringAppend(&query, " AND (invalid IS NULL OR invalid = 1 OR invalid = 0) ");
     92  }
     93  else {
     94    psStringAppend(&query, " AND (invalid IS NULL OR invalid = 1) ");
     95  }
     96
    9097  if (where && psListLength(where->list)) {
    9198    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
    9299    psStringAppend(&query, " AND %s", whereClause);
    93100    psFree(whereClause);
    94   }
    95   if (force) {
    96     psStringAppend(&query, " OR invalid = 0");
    97101  }
    98102
     
    103107    psFree(limitString);
    104108  }
    105 
     109  psTrace("debug",3,">> %s",query);
    106110  if (!p_psDBRunQuery(config->dbh, query)) {
    107111    psError(PS_ERR_UNKNOWN, false, "database error");
     
    325329  psFree(output);   // This held the information about the Run.
    326330
    327   // Read column list from config file.
     331  // Read column list from config file
    328332  psMetadata *recipe = psMetadataLookupPtr(&status, config->modules->recipes, "DQSTATS");
    329333  psMetadata *columns = psMetadataLookupMetadata(&status,recipe,"STATS.COLUMNS");
     
    342346  psMetadata *colTypes = psMetadataAlloc();
    343347  while ((colItem = psMetadataGetAndIncrement(iter))) {
     348    if (strcmp(colItem->name, "COLUMN")) continue;
     349    if (colItem->type != PS_DATA_METADATA) {
     350      psError(PS_ERR_UNKNOWN, false, "dqstatstool COLUMN not of type METADATA\n");
     351      return(NULL);
     352    }
     353   
    344354    bool status;
    345355    psMetadata *coldef = colItem->data.md;
     
    369379    return(false);
    370380  }
     381
     382  // Read decision engine settings from config file.
     383  psMetadata *engine = psMetadataLookupMetadata(&status,recipe,"DECISION.ENGINE");
     384  if (!engine) {
     385    psError(PS_ERR_UNKNOWN, false, "Could not read DECISION.ENGINE metadata");
     386    psFree(whereClause);
     387    return(false);
     388  }
     389  psArray *decisionRules = psArrayAllocEmpty(4); // list of screened and verified rules.
     390  psMetadataIterator *iterEngine = psMetadataIteratorAlloc(engine, PS_LIST_HEAD,NULL);
     391  psMetadataItem *engineItem = NULL;
     392
     393  while ((engineItem = psMetadataGetAndIncrement(iterEngine))) {
     394    if (strcmp(engineItem->name, "RULE")) continue;
     395    if (engineItem->type != PS_DATA_METADATA) {
     396      psError(PS_ERR_UNKNOWN, false, "dqstatstool RULE not of type METADATA\n");
     397      return(NULL);
     398    }
     399   
     400    bool status;
     401    psMetadata *enginedef = engineItem->data.md;
     402
     403    if (psMetadataLookupStr(&status,enginedef,"COLNAME")&&
     404        isfinite(psMetadataLookupF32(&status,enginedef,"MINIMUM"))&&
     405        isfinite(psMetadataLookupF32(&status,enginedef,"MAXIMUM"))) {
     406      psArrayAdd(decisionRules,decisionRules->n,enginedef);
     407    }
     408    else {
     409      psError(PS_ERR_UNKNOWN, false, "Rule invalid.");
     410    }
     411  }
     412  psFree(iterEngine);
     413
     414  if (decisionRules->n < 1) {
     415    psError(PS_ERR_UNKNOWN, false, "Failed to generate a decision engine");
     416    psFree(decisionRules);
     417    psFree(engine);
     418    return(false);
     419  }
     420  psFree(engine);
    371421
    372422  // Find the contents that comprise this run
     
    439489    bool status;
    440490    psString MYquery = psStringCopy(query);
     491    if (!MYquery) {
     492      psTrace("debug",4,"Failed to copy, trying again.");
     493      MYquery = psStringCopy(query);
     494    }
     495
    441496    psMetadata *where = psMetadataAlloc();
    442497
     
    445500    psMetadataAddS64(where,PS_LIST_TAIL, "camRun.cam_id", 0, "==", psMetadataLookupS64(&status,row,"cam_id"));
    446501    // This may not be real, so we need to check first. Is there a better way to check for a null here?
    447     if (psMetadataLookupS64(&status,row,"warp_id") && (psMetadataLookupS64(&status,row,"warp_id") != 9223372036854775807)) {
     502    if (psMetadataLookupS64(&status,row,"warp_id") && (psMetadataLookupS64(&status,row,"warp_id") != PS_MAX_S64)) {
    448503      psMetadataAddS64(where,PS_LIST_TAIL, "warpRun.warp_id", 0, "==", psMetadataLookupS64(&status,row,"warp_id"));
    449504    }
    450505
    451506    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
     507    if (!whereClause) {
     508      psError(PS_ERR_UNKNOWN, false, "Unable to generate Where Condition");
     509      return(false);
     510    }
    452511    psStringAppend(&MYquery, " WHERE %s", whereClause);
    453     psFree(whereClause);
    454     psFree(where);
     512
     513/*     psTrace("debug",2,"%ld %ld %ld %ld %s\n",psMetadataLookupS64(&status,row,"exp_id"), */
     514/*          psMetadataLookupS64(&status,row,"chip_id"), */
     515/*          psMetadataLookupS64(&status,row,"cam_id"), */
     516/*          psMetadataLookupS64(&status,row,"warp_id"), */
     517/*          whereClause); */
     518           
     519/*      psFree(whereClause); */
     520/*     psFree(where); */
    455521    //    printf("%s\n",MYquery);
    456522
     
    465531      return(false);
    466532    }
    467     psFree(MYquery);
    468533
    469534    psArray *statsoutput = p_psDBFetchResult(config->dbh);
     
    478543    }
    479544    if (!psArrayLength(statsoutput)) {
    480       psWarning("dqstatstool No stats rows found");
     545      psWarning("dqstatstool No stats rows found: %ld %s %s",i,MYquery,query);
    481546      psFree(colNames);
    482547      psFree(colTypes);
     
    487552      return(false);
    488553    }
     554    psFree(MYquery);
     555    psFree(whereClause);
     556    psFree(where);
    489557
    490558    // Magic happens and the fits table is filled.
     
    546614        success = psMetadataAddStr(tableRow,PS_LIST_TAIL,colName,0,"",rowItem->data.str);
    547615      }
    548 
     616      else if (!strcmp(colType,"PS_DATA_TIME")) {
     617        psString timeString = psTimeToISO(rowItem->data.V);
     618        success = psMetadataAddStr(tableRow,PS_LIST_TAIL,colName,0,"",timeString);
     619        psFree(timeString);
     620      }
     621     
    549622      if (!success) {
    550         psError(PS_ERR_UNKNOWN,false, "Error constructing fits table row.");
     623        psError(PS_ERR_UNKNOWN,false, "Error constructing fits table row. %s %s",colType,colName);
    551624        psFree(colNames);
    552625        psFree(colTypes);
     
    562635      }
    563636    }
    564 
     637    // Calculate if this exposure was "good" or not.
     638    bool accept = true;
     639    for (long j = 0; j < decisionRules->n; j++) {
     640      psMetadata *rule = decisionRules->data[j];
     641      bool status = true;
     642      psString colname = psMetadataLookupStr(&status,rule,"COLNAME");
     643      if (!status) {
     644        psError(PS_EXIT_CONFIG_ERROR, false, "Unable to find column name");
     645        return(false);
     646      }     
     647      psF32 min_value  = psMetadataLookupF32(&status,rule,"MINIMUM");
     648      if (!status) {
     649        psError(PS_EXIT_CONFIG_ERROR, false, "Unable to find min value for %s",colname);
     650        return(false);
     651      }     
     652      psF32 max_value  = psMetadataLookupF32(&status,rule,"MAXIMUM");
     653      if (!status) {
     654        psError(PS_EXIT_CONFIG_ERROR, false, "Unable to find max value for %s",colname);
     655        return(false);
     656      }     
     657      // Not happy with this being set to a F32. Can this ever be something else?
     658      psF32 value      = psMetadataLookupF32(&status,tableRow,colname);
     659      if (!status) {
     660        psError(PS_EXIT_CONFIG_ERROR, false, "Unable to find column %s (%f %f %f) in compare with rule.",colname,min_value,max_value,value);
     661        psFree(colNames);
     662        psFree(colTypes);
     663        psFree(contents);
     664        psFree(query);
     665        psFree(outTable);
     666        psFree(tableRow);
     667        return(false);
     668      }
     669      if ((value > max_value)||(value < min_value)) {
     670        accept = false;
     671      }     
     672    }
     673    bool success;
     674    success = psMetadataAddBool(tableRow,PS_LIST_TAIL,"ACCEPT",0,"",accept);
     675    if (!success) {
     676      psError(PS_ERR_UNKNOWN,false, "Error adding exposure quality to fits table row.");
     677      psFree(colNames);
     678      psFree(colTypes);
     679      psFree(contents);
     680      psFree(query);
     681      psFree(outTable);
     682      psFree(statsoutput);
     683      psFree(statIter);
     684      psFree(tableRow);
     685      return(false);
     686    }
     687
     688    // Add the row to the table array.
    565689    psArrayAdd(outTable,0,tableRow);
    566690    psFree(tableRow);
     
    600724static bool updaterunMode(pxConfig *config) {
    601725  PS_ASSERT_PTR_NON_NULL(config,false);
    602   PXOPT_LOOKUP_S64(dqstats_id, config->args, "-dqstats_id",     "dqstatsRun.dqstats_id", "==");
    603   PXOPT_LOOKUP_STR(state,      config->args, "-set_state",      "state",                 "==");
    604   PXOPT_LOOKUP_S16(fault,      config->args, "-fault",          "fault",                 "==");
     726  PXOPT_LOOKUP_S64(dqstats_id, config->args, "-dqstats_id",     true, false);
     727  PXOPT_LOOKUP_STR(state,      config->args, "-set_state",      true, false);
     728  PXOPT_LOOKUP_S16(fault,      config->args, "-fault",          false, false);
    605729
    606730  if (state && ! pxIsValidState(state)) {
  • trunk/ippTools/src/dqstatstoolConfig.c

    r27082 r27304  
    4949  psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_label",          0, "set label", NULL);
    5050  psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_state",          0, "set dqstatsRun stats", NULL);
     51  psMetadataAddS16(updaterunArgs, PS_LIST_TAIL, "-fault",              0, "set fault code", 0);
    5152
    5253  // -pendingbundle
  • trunk/ippconfig/recipes/dqStatsTool.config

    r26369 r27304  
    22# BUNDLE_AGE      ?      "2 hours"? # I'd like this to be a way to tell if we can just skip ahead, since the bundle is "old" Don't know how, though.
    33
     4DECISION.ENGINE METADATA           # Definition of the rules to determine if a given exposure is good or not.
     5    RULE MULTI
     6    RULE METADATA
     7        COLNAME STR FWHM_MAJOR
     8        MINIMUM F32 0.0
     9        MAXIMUM F32 5.0
     10    END
     11    RULE METADATA
     12        COLNAME STR FWHM_MINOR
     13        MINIMUM F32 0.0
     14        MAXIMUM F32 5.0
     15    END
     16#    TYPE VAL COLNAME      MINIMUM        MAXIMUM
     17#    RULE VAL FWHM_MAJOR   0.0            5.0
     18#    RULE VAL FWHM_MINOR   0.0            5.0
     19END
    420
    521STATS.COLUMNS METADATA             # Column definitions for what goes into a bundle
     22    COLUMN MULTI
     23    TYPE   VAL TABLE            ENTRY             DATATYPE       COLNAME
     24    COLUMN VAL rawExp           exp_name          PS_DATA_STRING EXPNAME             # Exposure name
     25    COLUMN VAL rawExp           dateobs           PS_DATA_TIME   DATEOBS             # Date of observation
     26    COLUMN VAL rawExp           filter            PS_DATA_STRING FILTER              # Filter used
     27    COLUMN VAL rawExp           comment           PS_DATA_STRING COMMENT             # Obs comment
     28    COLUMN VAL rawExp           ra                PS_DATA_F64    RA                  # Right ascension
     29    COLUMN VAL rawExp           decl              PS_DATA_F64    DEC                 # Declination
     30    COLUMN VAL rawExp           exp_time          PS_DATA_F32    EXPTIME             # Exposure time
    631
    7 C1 METADATA
    8     TABLE    STR rawExp
    9     ENTRY    STR ra
    10     DATATYPE STR PS_DATA_F64
    11     COLNAME  STR RA
     32    COLUMN VAL camProcessedExp  zpt_obs           PS_DATA_F32    ZEROPOINT           
     33    COLUMN VAL camProcessedExp  zpt_stdev         PS_DATA_F32    ZEROPOINT_STDEV     
     34    COLUMN VAL camProcessedExp  zpt_lq            PS_DATA_F32    ZEROPOINT_LQ       
     35    COLUMN VAL camProcessedExp  zpt_uq            PS_DATA_F32    ZEROPOINT_UQ       
     36
     37    COLUMN VAL camProcessedExp  fwhm_major        PS_DATA_F32    FWHM_MAJOR         
     38    COLUMN VAL camProcessedExp  fwhm_major_lq     PS_DATA_F32    FWHM_MAJOR_LQ       
     39    COLUMN VAL camProcessedExp  fwhm_major_uq     PS_DATA_F32    FWHM_MAJOR_UQ       
     40    COLUMN VAL camProcessedExp  fwhm_minor        PS_DATA_F32    FWHM_MINOR         
     41    COLUMN VAL camProcessedExp  fwhm_minor_lq     PS_DATA_F32    FWHM_MINOR_LQ       
     42    COLUMN VAL camProcessedExp  fwhm_minor_uq     PS_DATA_F32    FWHM_MINOR_UQ       
     43
     44    COLUMN VAL camProcessedExp  iq_fwhm_major     PS_DATA_F32    IQ_FWHM_MAJOR       
     45    COLUMN VAL camProcessedExp  iq_fwhm_major_err PS_DATA_F32    IQ_FWHM_MAJOR_ERR   
     46    COLUMN VAL camProcessedExp  iq_fwhm_minor     PS_DATA_F32    IQ_FWHM_MINOR       
     47    COLUMN VAL camProcessedExp  iq_fwhm_minor_err PS_DATA_F32    IQ_FWHM_MINOR_ERR   
     48
     49    COLUMN VAL camProcessedExp  iq_m2             PS_DATA_F32    IQ_M2               
     50    COLUMN VAL camProcessedExp  iq_m2_err         PS_DATA_F32    IQ_M2_ERR           
     51    COLUMN VAL camProcessedExp  iq_m2_lq          PS_DATA_F32    IQ_M2_LQ           
     52    COLUMN VAL camProcessedExp  iq_m2_uq          PS_DATA_F32    IQ_M2_UQ           
     53
     54    COLUMN VAL camProcessedExp  iq_m2c            PS_DATA_F32    IQ_M2C             
     55    COLUMN VAL camProcessedExp  iq_m2c_err        PS_DATA_F32    IQ_M2C_ERR         
     56    COLUMN VAL camProcessedExp  iq_m2c_lq         PS_DATA_F32    IQ_M2C_LQ           
     57    COLUMN VAL camProcessedExp  iq_m2c_uq         PS_DATA_F32    IQ_M2C_UQ           
     58
     59    COLUMN VAL camProcessedExp  iq_m2s            PS_DATA_F32    IQ_M2S             
     60    COLUMN VAL camProcessedExp  iq_m2s_err        PS_DATA_F32    IQ_M2S_ERR         
     61    COLUMN VAL camProcessedExp  iq_m2s_lq         PS_DATA_F32    IQ_M2S_LQ           
     62    COLUMN VAL camProcessedExp  iq_m2s_uq         PS_DATA_F32    IQ_M2S_UQ           
     63
     64    COLUMN VAL camProcessedExp  iq_m3             PS_DATA_F32    IQ_M3               
     65    COLUMN VAL camProcessedExp  iq_m3_err         PS_DATA_F32    IQ_M3_ERR           
     66    COLUMN VAL camProcessedExp  iq_m3_lq          PS_DATA_F32    IQ_M3_LQ           
     67    COLUMN VAL camProcessedExp  iq_m3_uq          PS_DATA_F32    IQ_M3_UQ           
     68
     69    COLUMN VAL camProcessedExp  iq_m4             PS_DATA_F32    IQ_M4               
     70    COLUMN VAL camProcessedExp  iq_m4_err         PS_DATA_F32    IQ_M4_ERR           
     71    COLUMN VAL camProcessedExp  iq_m4_lq          PS_DATA_F32    IQ_M4_LQ           
     72    COLUMN VAL camProcessedExp  iq_m4_uq          PS_DATA_F32    IQ_M4_UQ           
    1273END
    1374
    14 C2 METADATA
    15     TABLE    STR rawExp
    16     ENTRY    STR decl
    17     DATATYPE STR PS_DATA_F64
    18     COLNAME  STR DEC
    19 END
    20 
    21 C3 METADATA
    22     TABLE    STR camProcessedExp
    23     ENTRY    STR iq_m2
    24     DATATYPE STR PS_DATA_F32
    25     COLNAME  STR IQ_M2
    26 END
    27 
    28 C4 METADATA
    29     TABLE    STR camProcessedExp
    30     ENTRY    STR iq_fwhm_major
    31     DATATYPE STR PS_DATA_F32
    32     COLNAME  STR IQ_FWHM_MAJOR
    33 END
    34 
    35 
    36 END
    37 
Note: See TracChangeset for help on using the changeset viewer.