IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 6570


Ignore:
Timestamp:
Mar 10, 2006, 5:20:10 PM (20 years ago)
Author:
Paul Price
Message:

Changing concepts to only get stuff as the source becomes available; nearly done

Location:
branches/rel10_ifa/psModules/src/astrom
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • branches/rel10_ifa/psModules/src/astrom/pmConcepts.c

    r6552 r6570  
    2323
    2424pmConceptSpec *pmConceptSpecAlloc(psMetadataItem *blank, // Blank value; contains the name
    25                                   pmConceptReadFunc read, // Function to call to read the concept
    26                                   pmConceptWriteFunc write // Function to call to write the concept
     25                                  pmConceptParseFunc parse, // Function to call to parse the concept
     26                                  pmConceptFormatFunc format // Function to call to format the concept
    2727                                 )
    2828{
     
    3131
    3232    spec->blank = psMemIncrRefCounter(blank);
    33     spec->read = read;
    34     spec->write = write;
     33    spec->parse = parse;
     34    spec->format = format;
    3535
    3636    return spec;
     
    3939
    4040bool pmConceptRegister(psMetadataItem *blank, // Blank value; contains the name
    41                        pmConceptReadFunc read, // Function to call to read the concept
    42                        pmConceptWriteFunc write, // Function to call to write the concept
     41                       pmConceptParseFunc parse, // Function to call to parse the concept
     42                       pmConceptFormatFunc format, // Function to call to format the concept
    4343                       pmConceptLevel level // Level at which to store concept in the FPA hierarchy
    4444                      )
     
    4949    }
    5050
    51     pmConceptSpec *spec = pmConceptSpecAlloc(blank, read, write); // The concept specification
     51    pmConceptSpec *spec = pmConceptSpecAlloc(blank, parse, format); // The concept specification
    5252    psMetadata **target = NULL;         // The metadata of known concepts to write to
    5353    switch (level) {
     
    7474}
    7575
    76 // This function gets called for the really boring concepts --- where all you have to do is read from a header
    77 // or database and you don't need to muck around with conversions.
    78 // There is no similar "writePlain", since the type is already known.
    79 static psMetadataItem *readPlain(psMetadataItem *blank, // The blank value with name, comment, type
    80                                  pmFPA *fpa, // The FPA of interest
    81                                  pmChip *chip, // The Chip of interest
    82                                  pmCell *cell, // The cell of interest
    83                                  psDB *db // Database handle
    84                                 )
    85 {
    86     switch (blank->type) {
    87     case PS_DATA_STRING: {
    88             psString string = pmConceptReadString(fpa, chip, cell, db, blank->name);
    89             psMetadataItem *item = psMetadataItemAllocStr(blank->name, blank->comment, string);
    90             psFree(string);
    91             return item;
    92         }
    93     case PS_DATA_S32:
    94         return psMetadataItemAllocS32(blank->name, blank->comment,
    95                                       pmConceptReadS32(fpa, chip, cell, db, blank->name));
    96     case PS_DATA_F32:
    97         return psMetadataItemAllocF32(blank->name, blank->comment,
    98                                       pmConceptReadF32(fpa, chip, cell, db, blank->name));
    99     case PS_DATA_F64:
    100         return psMetadataItemAllocF64(blank->name, blank->comment,
    101                                       pmConceptReadF64(fpa, chip, cell, db, blank->name));
    102     default:
    103         psLogMsg(__func__, PS_LOG_WARN, "Concept %s (%s) is not of a standard type (%x)\n",
    104                  blank->name, blank->comment, blank->type);
    105     }
    106     return NULL;
    107 }
    10876
    10977// Set all registered concepts to blank value for the specified level
    110 static bool conceptsBlank(psMetadata **specs,  // One of the concepts specifications
     78static bool conceptsBlank(psMetadata *specs,  // One of the concepts specifications
    11179                          psMetadata *target // Place to install the concepts
    11280                         )
     
    12896}
    12997
     98
     99
    130100// Read all registered concepts for the specified level
    131 static bool conceptsRead(psMetadata **specs, // One of the concepts specifications
     101static bool conceptsRead(psMetadata *specs, // One of the concepts specifications
    132102                         pmFPA *fpa,    // The FPA
    133103                         pmChip *chip,  // The chip
    134104                         pmCell *cell,  // The cell
     105                         pmConceptSource source, // The source of the concepts to read
    135106                         psDB *db,      // Database handle
    136107                         psMetadata *target // Place into which to read the concepts
     
    140111        pmConceptsInit();
    141112    }
    142     psMetadataIterator *specsIter = psMetadataIteratorAlloc(*specs, PS_LIST_HEAD, NULL); // Iterator on specs
    143     psMetadataItem *specItem = NULL;    // Item from the specs metadata
    144     while ((specItem = psMetadataGetAndIncrement(specsIter))) {
    145         pmConceptSpec *spec = specItem->data.V; // The specification
    146         psMetadataItem *conceptItem = NULL; // The item to add to the concepts
    147         if (spec->read) {
    148             conceptItem = spec->read(fpa, chip, cell, db);
    149         } else {
    150             conceptItem = readPlain(spec->blank, fpa, chip, cell, db);
    151             if (conceptItem->type != spec->blank->type) {
    152                 psLogMsg(__func__, PS_LOG_ERROR, "Type of concept %s following read (%x) does not match "
    153                          "blank type (%x).\n", spec->blank->name, conceptItem->type, spec->blank->type);
    154             }
    155         }
    156         if (conceptItem) {
    157             psMetadataAddItem(target, conceptItem, PS_LIST_TAIL, PS_META_REPLACE);
    158             psFree(conceptItem);        // Drop reference
    159         }
    160         // No error if conceptItem is NULL, since that may only mean that the required information isn't
    161         // present yet.
    162     }
    163     psFree(specsIter);
     113
     114    if (source & PM_CONCEPT_SOURCE_CAMERA || source == PM_CONCEPT_SOURCE_ALL) {
     115        pmConceptsReadFromCamera(specs, cell, target);
     116    }
     117    if (source & PM_CONCEPT_SOURCE_DEFAULTS || source == PM_CONCEPT_SOURCE_ALL) {
     118        pmConceptsReadFromDefaults(specs, fpa, chip, cell, target);
     119    }
     120    if (source & PM_CONCEPT_SOURCE_HEADER || source == PM_CONCEPT_SOURCE_ALL) {
     121        pmConceptsReadFromHeader(specs, fpa, chip, cell, target);
     122    }
     123    if (source & PM_CONCEPT_SOURCE_DATABASE || source == PM_CONCEPT_SOURCE_ALL) {
     124        pmConceptsReadFromHeader(specs, fpa, chip, cell, db, target);
     125    }
     126
    164127    return true;
    165128}
    166129
    167130// Write all registered concepts for the specified level
    168 static bool conceptsWrite(psMetadata **specs, // One of the concepts specifications
     131static bool conceptsWrite(psMetadata *specs, // One of the concepts specifications
    169132                          pmFPA *fpa,   // The FPA
    170133                          pmChip *chip, // The chip
    171134                          pmCell *cell, // The cell
     135                          pmConceptSource source, // The source of the concepts to write
    172136                          psDB *db,      // Database handle
    173                           psMetadata *source // The concepts to write out
     137                          psMetadata *concepts // The concepts to write out
    174138                         )
    175139{
     
    177141        pmConceptsInit();
    178142    }
    179     if (! fpa->camera) {
    180         return false;
    181     }
    182     psMetadataIterator *iter = psMetadataIteratorAlloc(source, PS_LIST_HEAD, NULL); // Iterator on concepts
    183     psMetadataItem *item = NULL;    // Item from the concepts
    184     while ((item = psMetadataGetAndIncrement(iter))) {
    185         const char *name = item->name;  // Name of the concept
    186         if (!strcmp(name, "CELL.NAME") || !strcmp(name, "CHIP.NAME")) {
    187             // These concepts are not written out; they are set from things like the FITS extname
    188             continue;
    189         }
    190         psMetadataItem *specItem = psMetadataLookup(*specs, name); // Specification for the concept
    191         if (specItem) {
    192             pmConceptSpec *spec = specItem->data.V; // The specification
    193             if (spec->write) {
    194                 spec->write(fpa, chip, cell, db); // The concept
    195             } else {
    196                 pmConceptWrite(fpa, chip, cell, db, source, spec->blank->name);
    197             }
    198         } else {
    199             psLogMsg(__func__, PS_LOG_WARN, "Unable to find specification to write concept %s in FPA\n",
    200                      name);
    201         }
    202     }
    203     psFree(iter);
     143
     144    if (source & PM_CONCEPT_SOURCE_CAMERA || source == PM_CONCEPT_SOURCE_ALL) {
     145        pmConceptsWriteToCamera(specs, cell, concepts);
     146    }
     147    if (source & PM_CONCEPT_SOURCE_DEFAULTS || source == PM_CONCEPT_SOURCE_ALL) {
     148        pmConceptsWriteToDefaults(specs, fpa, chip, cell, concepts);
     149    }
     150    if (source & PM_CONCEPT_SOURCE_HEADER || source == PM_CONCEPT_SOURCE_ALL) {
     151        pmConceptsWriteToHeader(specs, fpa, chip, cell, concepts);
     152    }
     153    if (source & PM_CONCEPT_SOURCE_DATABASE || source == PM_CONCEPT_SOURCE_ALL) {
     154        pmConceptsWriteToHeader(specs, fpa, chip, cell, db, concepts);
     155    }
    204156
    205157    return true;
    206158}
     159
    207160
    208161// Set the concepts for a given FPA to blanks
     
    211164{
    212165    psTrace("psModule.concepts", 5, "Blanking FPA concepts: %x %x\n", conceptsFPA, fpa->concepts);
    213     return conceptsBlank(&conceptsFPA, fpa->concepts);
     166    return conceptsBlank(conceptsFPA, fpa->concepts);
    214167}
    215168
    216169// Read the concepts for a given FPA
    217170bool pmConceptsReadFPA(pmFPA *fpa,      // FPA for which to read concepts
     171                       pmConceptSource source, // The source of the concepts to read
    218172                       psDB *db         // Database handle
    219173                      )
    220174{
    221175    psTrace("psModule.concepts", 5, "Reading FPA concepts: %x %x\n", conceptsFPA, fpa->concepts);
    222     return conceptsRead(&conceptsFPA, fpa, NULL, NULL, db, fpa->concepts);
     176    return conceptsRead(conceptsFPA, fpa, NULL, NULL, source, db, fpa->concepts);
    223177}
    224178
     
    229183{
    230184    psTrace("psModule.concepts", 5, "Writing FPA concepts: %x %x\n", conceptsFPA, fpa->concepts);
    231     return conceptsWrite(&conceptsFPA, fpa, NULL, NULL, db, fpa->concepts);
     185    return conceptsWrite(conceptsFPA, fpa, NULL, NULL, db, fpa->concepts);
    232186}
    233187
     
    237191{
    238192    psTrace("psModule.concepts", 5, "Blanking chip concepts: %x %x\n", conceptsChip, chip->concepts);
    239     return conceptsBlank(&conceptsChip, chip->concepts);
     193    return conceptsBlank(conceptsChip, chip->concepts);
    240194}
    241195
    242196// Read the concepts for a given FPA
    243197bool pmConceptsReadChip(pmChip *chip,   // Chip for which to read concepts
     198                        pmConceptSource source, // The source of the concepts to read
    244199                        psDB *db        // Database handle
    245200                       )
     
    247202    psTrace("psModule.concepts", 5, "Reading chip concepts: %x %x\n", conceptsChip, chip->concepts);
    248203    pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
    249     return conceptsRead(&conceptsChip, fpa, chip, NULL, db, chip->concepts);
     204    return conceptsRead(conceptsChip, fpa, chip, NULL, source, db, chip->concepts);
    250205}
    251206
     
    257212    psTrace("psModule.concepts", 5, "Writing chip concepts: %x %x\n", conceptsChip, chip->concepts);
    258213    pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
    259     return conceptsWrite(&conceptsChip, fpa, chip, NULL, db, chip->concepts);
     214    return conceptsWrite(conceptsChip, fpa, chip, NULL, db, chip->concepts);
    260215}
    261216
     
    265220{
    266221    psTrace("psModule.concepts", 5, "Blanking cell concepts: %x %x\n", conceptsCell, cell->concepts);
    267     return conceptsBlank(&conceptsCell, cell->concepts);
     222    return conceptsBlank(conceptsCell, cell->concepts);
    268223}
    269224
    270225// Read the concepts for a given FPA
    271226bool pmConceptsReadCell(pmCell *cell,   // Cell for which to read concepts
     227                        pmConceptSource source, // The source of the concepts to read
    272228                        psDB *db        // Database handle
    273229                       )
     
    276232    pmChip *chip = cell->parent;        // Chip to which the cell belongs
    277233    pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
    278     return conceptsRead(&conceptsCell, fpa, chip, cell, db, cell->concepts);
     234    return conceptsRead(conceptsCell, fpa, chip, cell, source, db, cell->concepts);
    279235}
    280236
     
    287243    pmChip *chip = cell->parent;        // Chip to which the cell belongs
    288244    pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
    289     return conceptsWrite(&conceptsCell, fpa, chip, cell, db, cell->concepts);
     245    return conceptsWrite(conceptsCell, fpa, chip, cell, db, cell->concepts);
    290246}
    291247
     
    300256        // Install the standard concepts
    301257
     258        #if 0
    302259        // FPA.NAME
    303260        {
     
    306263            psFree(fpaName);
    307264        }
     265        #endif
    308266
    309267        // FPA.AIRMASS
     
    340298        {
    341299            psMetadataItem *fpaRa = psMetadataItemAllocF64("FPA.RA", "Right Ascension of boresight", NAN);
    342             pmConceptRegister(fpaRa, (pmConceptReadFunc)pmConceptRead_FPA_RA,
    343                               (pmConceptWriteFunc)pmConceptWrite_FPA_RA, PM_CONCEPT_LEVEL_FPA);
     300            pmConceptRegister(fpaRa, (pmConceptParseFunc)pmConceptParse_FPA_Coords,
     301                              (pmConceptFormatFunc)pmConceptFormat_FPA_Coords, PM_CONCEPT_LEVEL_FPA);
    344302            psFree(fpaRa);
    345303        }
     
    348306        {
    349307            psMetadataItem *fpaDec = psMetadataItemAllocF64("FPA.DEC", "Declination of boresight", NAN);
    350             pmConceptRegister(fpaDec, (pmConceptReadFunc)pmConceptRead_FPA_DEC,
    351                               (pmConceptWriteFunc)pmConceptWrite_FPA_DEC, PM_CONCEPT_LEVEL_FPA);
     308            pmConceptRegister(fpaDec, (pmConceptParseFunc)pmConceptParse_FPA_Coords,
     309                              (pmConceptFormatFunc)pmConceptFormat_FPA_Coords, PM_CONCEPT_LEVEL_FPA);
    352310            psFree(fpaDec);
    353311        }
     
    436394
    437395        // CELL.DARKTIME
    438         {
     396        {Read
    439397            psMetadataItem *cellDarktime = psMetadataItemAllocF32("CELL.DARKTIME",
    440398                                           "Time since flush (sec)", NAN);
     
    450408                                          "Trim section", trimsec);
    451409            psFree(trimsec);
    452             pmConceptRegister(cellTrimsec, (pmConceptReadFunc)pmConceptRead_CELL_TRIMSEC,
    453                               (pmConceptWriteFunc)pmConceptWrite_CELL_TRIMSEC, PM_CONCEPT_LEVEL_CELL);
     410            pmConceptRegister(cellTrimsec, (pmConceptParseFunc)pmConceptParse_CELL_TRIMSEC,
     411                              (pmConceptFormatFunc)pmConceptFormat_CELL_TRIMSEC, PM_CONCEPT_LEVEL_CELL);
    454412            psFree(cellTrimsec);
    455413        }
     
    461419                                          "Bias sections", biassecs);
    462420            psFree(biassecs);
    463             pmConceptRegister(cellBiassec, (pmConceptReadFunc)pmConceptRead_CELL_BIASSEC,
    464                               (pmConceptWriteFunc)pmConceptWrite_CELL_BIASSEC, PM_CONCEPT_LEVEL_CELL);
     421            pmConceptRegister(cellBiassec, (pmConceptParseFunc)pmConceptParse_CELL_BIASSEC,
     422                              (pmConceptFormatFunc)pmConceptFormat_CELL_BIASSEC, PM_CONCEPT_LEVEL_CELL);
    465423            psFree(cellBiassec);
    466424        }
     
    469427        {
    470428            psMetadataItem *cellXbin = psMetadataItemAllocS32("CELL.XBIN", "Binning in x", 0);
    471             pmConceptRegister(cellXbin, (pmConceptReadFunc)pmConceptRead_CELL_XBIN,
    472                               (pmConceptWriteFunc)pmConceptWrite_CELL_XBIN, PM_CONCEPT_LEVEL_CELL);
     429            pmConceptRegister(cellXbin, (pmConceptParseFunc)pmConceptParse_CELL_Binning,
     430                              (pmConceptFormatFunc)pmConceptFormat_CELL_Binning, PM_CONCEPT_LEVEL_CELL);
    473431            psFree(cellXbin);
    474432        }
     
    477435        {
    478436            psMetadataItem *cellYbin = psMetadataItemAllocS32("CELL.YBIN", "Binning in y", 0);
    479             pmConceptRegister(cellYbin, (pmConceptReadFunc)pmConceptRead_CELL_YBIN,
    480                               (pmConceptWriteFunc)pmConceptWrite_CELL_YBIN, PM_CONCEPT_LEVEL_CELL);
     437            pmConceptRegister(cellYbin, (pmConceptParseFunc)pmConceptParse_CELL_Binning,
     438                              (pmConceptFormatFunc)pmConceptFormat_CELL_Binning, PM_CONCEPT_LEVEL_CELL);
    481439            psFree(cellYbin);
    482440        }
     
    485443        {
    486444            psMetadataItem *cellTimesys = psMetadataItemAllocS32("CELL.TIMESYS", "Time system", -1);
    487             pmConceptRegister(cellTimesys, (pmConceptReadFunc)pmConceptRead_CELL_TIMESYS,
    488                               (pmConceptWriteFunc)pmConceptWrite_CELL_TIMESYS, PM_CONCEPT_LEVEL_CELL);
     445            pmConceptRegister(cellTimesys, (pmConceptParseFunc)pmConceptParse_CELL_TIMESYS,
     446                              (pmConceptFormatFunc)pmConceptFormat_CELL_TIMESYS, PM_CONCEPT_LEVEL_CELL);
    489447            psFree(cellTimesys);
    490448        }
     
    499457                                       "Time of exposure", time);
    500458            psFree(time);
    501             pmConceptRegister(cellTime, (pmConceptReadFunc)pmConceptRead_CELL_TIME,
    502                               (pmConceptWriteFunc)pmConceptWrite_CELL_TIME, PM_CONCEPT_LEVEL_CELL);
     459            pmConceptRegister(cellTime, (pmConceptParseFunc)pmConceptParse_CELL_TIME,
     460                              (pmConceptFormatFunc)pmConceptFormat_CELL_TIME, PM_CONCEPT_LEVEL_CELL);
    503461            psFree(cellTime);
    504462        }
     
    507465        {
    508466            psMetadataItem *cellX0 = psMetadataItemAllocS32("CELL.X0", "Position of (0,0) on the chip", 0);
    509             pmConceptRegister(cellX0, (pmConceptReadFunc)pmConceptRead_CELL_X0,
    510                               (pmConceptWriteFunc)pmConceptWrite_CELL_X0, PM_CONCEPT_LEVEL_CELL);
     467            pmConceptRegister(cellX0, (pmConceptParseFunc)pmConceptParse_CELL_Positions,
     468                              (pmConceptFormatFunc)pmConceptFormat_CELL_Positions, PM_CONCEPT_LEVEL_CELL);
    511469            psFree(cellX0);
    512470        }
     
    515473        {
    516474            psMetadataItem *cellY0 = psMetadataItemAllocS32("CELL.Y0", "Position of (0,0) on the chip", 0);
    517             pmConceptRegister(cellY0, (pmConceptReadFunc)pmConceptRead_CELL_Y0,
    518                               (pmConceptWriteFunc)pmConceptWrite_CELL_Y0, PM_CONCEPT_LEVEL_CELL);
     475            pmConceptRegister(cellY0, (pmConceptParseFunc)pmConceptParse_CELL_Positions,
     476                              (pmConceptFormatFunc)pmConceptFormat_CELL_Positions, PM_CONCEPT_LEVEL_CELL);
    519477            psFree(cellY0);
    520478        }
  • branches/rel10_ifa/psModules/src/astrom/pmConcepts.h

    r6552 r6570  
    66#include "pmFPA.h"
    77
    8 // Function to call to read a concept
    9 typedef psMetadataItem* (*pmConceptReadFunc)(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    10 // Function to call to write a concept
    11 typedef bool (*pmConceptWriteFunc)(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
     8
     9// Function to call to parse a concept once it has been read
     10typedef psMetadataItem* (*pmConceptParseFunc)(psMetadataItem *concept, pmConceptSpec *spec, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
     11// Function to call to format a concept for writing
     12typedef psMetadataItem* (*pmConceptFormatFunc)(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
    1213
    1314// A "concept" specification
     
    1516{
    1617    psMetadataItem *blank;              // Blank value of concept; also contains the name
    17     pmConceptReadFunc read;         // Function to call to read the concept
    18     pmConceptWriteFunc write;       // Function to call to write the concept
     18    pmConceptParseFunc parse;           // Function to call to read the concept
     19    pmConceptFormatFunc format;         // Function to call to write the concept
    1920}
    2021pmConceptSpec;
     
    2223// Allocator
    2324pmConceptSpec *pmConceptSpecAlloc(psMetadataItem *blank, // Blank value; contains the name
    24                                   pmConceptReadFunc read, // Function to call to read the concept
    25                                   pmConceptWriteFunc write // Function to call to write the concept
     25                                  pmConceptParseFunc parse, // Function to call to parse the concept
     26                                  pmConceptFormatFunc format // Function to call to format the concept
    2627                                 );
    2728
     
    3536// Register a new concept
    3637bool pmConceptRegister(psMetadataItem *blank, // Blank value; contains the name
    37                        pmConceptReadFunc read, // Function to call to read the concept
    38                        pmConceptWriteFunc write, // Function to call to write the concept
     38                       pmConceptParseFunc parse, // Function to call to parse the concept
     39                       pmConceptFormatFunc format, // Function to call to format the concept
    3940                       pmConceptLevel level // Level at which to store concept in the FPA hierarchy
    4041                      );
    4142
    42 #if 0
    4343// Some specificity to reading and writing concepts
    4444typedef enum {
    45     PM_CONCEPT_SOURCE_ALL      = 0x00,  // Do all sources
    46     PM_CONCEPT_SOURCE_CAMERA   = 0x01,  // Do concepts that come from the camera information
    47     PM_CONCEPT_SOURCE_DEFAULTS = 0x02,  // Do concepts that come from defaults
    48     PM_CONCEPT_SOURCE_HEADER   = 0x04,  // Do concepts that come from FITS header
    49     PM_CONCEPT_SOURCE_DATABASE = 0x08   // Do concepts that come from database
     45    PM_CONCEPT_SOURCE_ALL      = 0x00,  // All concepts
     46    PM_CONCEPT_SOURCE_CAMERA   = 0x01,  // Concept comes from the camera information
     47    PM_CONCEPT_SOURCE_DEFAULTS = 0x02,  // Concept comes from defaults
     48    PM_CONCEPT_SOURCE_HEADER   = 0x04,  // Concept comes from FITS header
     49    PM_CONCEPT_SOURCE_DATABASE = 0x08   // Concept comes from database
    5050} pmConceptSource;
    51 #endif
    5251
    5352// Set blanks, read or write concepts at the appropriate level
     
    5554                       );
    5655bool pmConceptsReadFPA(pmFPA *fpa,      // FPA for which to read concepts
     56                       pmConceptSource source, // Source for concepts
    5757                       psDB *db         // Database handle
    5858                      );
    5959bool pmConceptsWriteFPA(pmFPA *fpa,     // FPA for which to write concepts
     60                        pmConceptSource source, // Source for concepts
    6061                        psDB *db        // Database handle
    6162                       );
     
    6364                        );
    6465bool pmConceptsReadChip(pmChip *chip,   // Chip for which to read concepts
     66                        pmConceptSource source, // Source for concepts
    6567                        psDB *db        // Database handle
    6668                       );
    6769bool pmConceptsWriteChip(pmChip *chip,  // Chip for which to write concepts
     70                         pmConceptSource source, // Source for concepts
    6871                         psDB *db       // Database handle
    6972                        );
     
    7174                        );
    7275bool pmConceptsReadCell(pmCell *cell,   // Cell for which to read concepts
     76                        pmConceptSource source, // Source for concepts
    7377                        psDB *db        // Database handle
    7478                       );
    7579bool pmConceptsWriteCell(pmCell *cell,  // FPA for which to write concepts
     80                         pmConceptSource source, // Source for concepts
    7681                         psDB *db       // Database handle
    7782                        );
  • branches/rel10_ifa/psModules/src/astrom/pmConceptsRead.c

    r6552 r6570  
    55#include "pmFPA.h"
    66#include "pmConceptsRead.h"
    7 #include "psAdditionals.h"
    8 
     7
     8//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     9// File-static functions
     10//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     11
     12
     13static float parseF32(psMetadataItem *item
     14                     )
     15{
     16    switch (item->type) {
     17    case PS_DATA_F32:
     18        return item->data.F32;
     19    case PS_DATA_F64:
     20        // Assume it's OK to truncate to floating point from double
     21        return (float)item->data.F64;
     22    case PS_DATA_S32:
     23        // Promote to float
     24        return (float)item->data.S32;
     25    default:
     26        psError(PS_ERR_IO, true, "Concept %s (%s) is not of floating point type (%x) --- treating as NaN.\n",
     27                item->name, item->comment, item->type);
     28        return NAN;
     29    }
     30}
     31
     32static double parseF64(psMetadataItem *item
     33                      )
     34{
     35    switch (item->type) {
     36    case PS_TYPE_F64:
     37        return item->data.F64;
     38    case PS_TYPE_F32:
     39        // Promote to double
     40        return (double)item->data.F32;
     41    case PS_TYPE_S32:
     42        // Promote to double
     43        return (double)item->data.S32;
     44    default:
     45        psError(PS_ERR_IO, true, "Concept %s (%s) is not of double-precision floating point type (%x) "
     46                "--- treating as NaN.\n", item->name, item->comment, item->type);
     47        return NAN;
     48    }
     49}
     50
     51static int parseS32(psMetadataItem *item
     52                   )
     53{
     54    switch (item->type) {
     55    case PS_TYPE_S32:
     56        return item->data.S32;
     57    case PS_TYPE_F32:
     58        psLogMsg(__func__, PS_LOG_WARN, "Concept %s (%s) should be S32, but is F32 --- converting.\n",
     59                 item->name, comment);
     60        return (int)item->data.F32;
     61    case PS_TYPE_F64:
     62        psLogMsg(__func__, PS_LOG_WARN, "Concept %s (%s) should be S32, but is F64 --- converting.\n",
     63                 item->name, comment);
     64        return (int)item->data.F64;
     65    default:
     66        psError(PS_ERR_IO, true, "Concept %s (%s) is not of integer type (%x) --- treating as zero.\n",
     67                item->name, item->comment, item->type);
     68        return 0;
     69    }
     70}
     71
     72static psString parseString(psMetadataItem *item
     73                           )
     74{
     75    switch (item->type) {
     76    case PS_DATA_STRING:
     77        return psMemIncrRefCounter(item->data.V);
     78    case PS_DATA_F32: {
     79            psString value = NULL;    // String to return
     80            psStringAppend(&value, "%f", item->data.F32);
     81            return value;
     82        }
     83    case PS_DATA_S32: {
     84            psString value = NULL;    // String to return
     85            psStringAppend(&value, "%d", item->data.S32);
     86            return value;
     87        }
     88    default:
     89        psError(PS_ERR_IO, true, "Concept %s (%s) is not of string type (%x) --- treating as "
     90                "undefined.\n", name, item->comment, item->type);
     91        return psStringCopy("");
     92    }
     93}
     94
     95
     96// This function gets called for the really boring concepts --- where all you have to do is parse from a
     97// header or database and you don't need to muck around with conversions.  There is no similar "formatPlain",
     98// since the type is already known.
     99static psMetadataItem *parsePlain(psMetadataItem *concept, // The concept to parse
     100                                  pmConceptSpec *spec // The concept specification
     101                                 )
     102{
     103    psMetadataItem *blank = spec->blank;// The blank specification, which carries the name, comment, type
     104    switch (blank->type) {
     105    case PS_DATA_STRING: {
     106            psString string = parseString(concept); // Get the string, so I can free it after it goes on the MDI
     107            psMetadataItem *item = psMetadataItemAllocStr(blank->name, blank->comment, string);
     108            psFree(string);
     109            return item;
     110        }
     111    case PS_DATA_S32:
     112        return psMetadataItemAllocS32(blank->name, blank->comment, parseS32(concept));
     113    case PS_DATA_F32:
     114        return psMetadataItemAllocF32(blank->name, blank->comment, parseF32(concept));
     115    case PS_DATA_F64:
     116        return psMetadataItemAllocF64(blank->name, blank->comment, parseF64(concept));
     117    default:
     118        psLogMsg(__func__, PS_LOG_WARN, "Concept %s (%s) is not of a standard type (%x)\n",
     119                 blank->name, blank->comment, blank->type);
     120        return NULL;
     121    }
     122}
     123
     124// Get the lowest HDU
     125static pmHDU *getLowestHDU(pmFPA *fpa, // The FPA
     126                           pmChip *chip, // The chip, or NULL
     127                           pmCell *cell // The cell, or NULL
     128                          )
     129{
     130    pmHDU *hdu = NULL;          // The HDU that's at the lowest level
     131    if (cell) {
     132        hdu = pmHDUFromCell(cell);
     133    } else if (chip) {
     134        hdu = pmHDUFromChip(chip);
     135    } else if (fpa) {
     136        hdu = pmHDUFromFPA(fpa);
     137    }
     138
     139    return hdu;
     140}
     141
     142
     143// Parse a single concept
     144static bool conceptParse(pmConceptSpec *spec, // The concept specification
     145                         psMetadataItem *concept, // The concept to parse
     146                         psMetadata *cameraFormat, // The camera format
     147                         psMetadata *target, // The target
     148                         pmFPA *fpa,    // The FPA
     149                         pmChip *chip,  // The chip
     150                         pmCell *cell   // The cell
     151                        )
     152{
     153    if (concept) {
     154        psMetadataItem *parsed = NULL;  // The parsed concept
     155        if (spec->parse) {
     156            parsed = spec->parse(concept, spec, cameraFormat, fpa, chip, cell);
     157        } else {
     158            parsed = parsePlain(concept, spec->blank);
     159        }
     160
     161        // Reformat so that everything's clean
     162        if (strcmp(spec->blank->name, parsedItem->name) != 0 ||
     163                strcmp(spec->blank->comment, parsedItem->comment) != 0) {
     164            psMetadataItem *cleaned = NULL;     // Item that's been cleaned up --- correct name and comment
     165            switch (spec->type) {
     166            case PS_DATA_STRING:
     167                cleaned = psMetadataItemAllocStr(spec->blank->name, spec->blank->comment, concept->data.V);
     168                break;
     169            case PS_DATA_S32:
     170                cleaned = psMetadataItemAllocS32(spec->blank->name, spec->blank->comment, concept->data.S32);
     171                break;
     172            case PS_DATA_F32:
     173                cleaned = psMetadataItemAllocF32(spec->blank->name, spec->blank->comment, concept->data.F32);
     174                break;
     175            case PS_DATA_F64:
     176                cleaned = psMetadataItemAllocF64(spec->blank->name, spec->blank->comment, concept->data.F64);
     177                break;
     178            default:
     179                cleaned = psMetadataItemAlloc(spec->blank->name, concept->type, spec->blank->comment,
     180                                              concept->data.V);
     181            }
     182            psFree(parsed);
     183            parsed = cleaned;
     184        }
     185        psMetadataAddItem(target, parsed, PS_LIST_TAIL, PS_META_REPLACE);
     186        psFree(parsed);     // Drop reference
     187        return true;
     188    }
     189
     190    return false;
     191}
     192
     193//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     194// Public functions
     195//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     196
     197bool pmConceptsReadFromCamera(psMetadata *specs, // The concept specifications
     198                              pmCell *cell,  // The cell
     199                              psMetadata *target // Place into which to read the concepts
     200                             )
     201{
     202    if (cell) {
     203        pmHDU *hdu = getLowestHDU(NULL, NULL, cell); // The HDU at the lowest level
     204        psMetadata *cameraFormat = hdu->format; // The camera format
     205        psMetadata *cellConfig = cell->config; // The camera configuration for this cell
     206        psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
     207        psMetadataItem *specItem = NULL;    // Item from the specs metadata
     208        while ((specItem = psMetadataGetAndIncrement(specsIter))) {
     209            pmConceptSpec *spec = specItem->data.V; // The specification
     210            psString name = specItem->name; // The concept name
     211            psMetadataItem *conceptItem = psMetadataLookup(cellConfig, name); // The concept, or NULL
     212            psMetadataItem *value = NULL; // The value of the concept
     213            if (conceptItem) {
     214                // Check the SOURCE
     215                psString nameSource = NULL; // String with the concept name and ".SOURCE" added
     216                psStringAppend(nameSource, "%s.SOURCE", name);
     217                bool mdok = true;       // Status of MD lookup
     218                psString source = psMetadataLookupStr(&mdok, cell->config, nameSource); // The source
     219                if (mdok && strlen(source)) {
     220                    if (strcasecmp(source, "HEADER") == 0 && conceptItem->type == PS_DATA_STRING) {
     221                        value = psMetadataLookup(hdu->header, conceptItem->data.V);
     222                    } else if (strcasecmp(source, "VALUE") == 0) {
     223                        value = psMemIncrRefCounter(conceptItem);
     224                    } else {
     225                        psError(PS_ERR_IO, true, "%s isn't HEADER or VALUE --- can't read %s\n", source,
     226                                name);
     227                        continue;
     228                    }
     229                } else {
     230                    // Assume it's specified by value
     231                    value = psMemIncrRefCounter(conceptItem);
     232                }
     233                conceptParse(spec, value, cameraFormat, target, NULL, NULL, cell);
     234            }
     235        }
     236        psFree(specsIter);
     237        return true;
     238    }
     239    return false;
     240}
     241
     242
     243bool pmConceptsReadFromDefaults(psMetadata *specs, // The concept specifications
     244                                pmFPA *fpa, // The FPA
     245                                pmChip *chip, // The chip
     246                                pmCell *cell, // The cell
     247                                psMetadata *target // Place into which to read the concepts
     248                               )
     249{
     250    bool mdok = true;               // Status of MD lookup
     251    psMetadata *defaults = psMetadataLookupMD(&mdok, cameraFormat, "DEFAULTS"); // The DEFAULTS spec
     252    if (mdok && defaults) {
     253        pmHDU *hdu = getLowestHDU(fpa, chip, cell); // The HDU at the lowest level
     254        psMetadata *cameraFormat = hdu->format; // The camera format
     255        psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
     256        psMetadataItem *specItem = NULL;    // Item from the specs metadata
     257        while ((specItem = psMetadataGetAndIncrement(specsIter))) {
     258            pmConceptSpec *spec = specItem->data.V; // The specification
     259            psString name = specItem->name; // The concept name
     260            psMetadataItem *conceptItem = psMetadataLookup(defaults, name); // The concept, or NULL
     261            conceptParse(spec, conceptItem, cameraFormat, target, fpa, chip, cell);
     262        }
     263        psFree(specsIter);
     264        return true;
     265    }
     266    return false;
     267}
     268
     269bool pmConceptsReadFromHeader(psMetadata *specs, // The concept specifications
     270                              pmFPA *fpa, // The FPA
     271                              pmChip *chip, // The chip
     272                              pmCell *cell,  // The cell
     273                              psMetadata *target // Place into which to read the concepts
     274                             )
     275{
     276    bool mdok = true;               // Status of MD lookup
     277    psMetadata *transSpec = psMetadataLookupMD(&mdok, cameraFormat, "TRANSLATION"); // The TRANSLATION spec
     278    if (mdok && transSpec) {
     279        pmHDU *hdu = getLowestHDU(fpa, chip, cell); // The HDU at the lowest level
     280        psMetadata *cameraFormat = hdu->format; // The camera format
     281        psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
     282        psMetadataItem *specItem = NULL;    // Item from the specs metadata
     283        while ((specItem = psMetadataGetAndIncrement(specsIter))) {
     284            pmConceptSpec *spec = specItem->data.V; // The specification
     285            psString name = specItem->name; // The concept name
     286            psString keywords = psMetadataLookupStr(&mdok, transSpec, name); // The FITS keywords
     287            if (mdok && strlen(keyword) > 0) {
     288                // In case there are multiple headers
     289                psList *keys = psStringSplit(keywords, " ,;"); // List of keywords
     290                psMetadataItem *headerItem = NULL; // The item, to be returned
     291                if (keys->n == 1) {
     292                    // Only one key --- proceed as usual
     293                    headerItem = psMetadataLookup(hdu->header, keywords);
     294                } else {
     295                    psListIterator *keysIter = psListIteratorAlloc(keys, PS_LIST_HEAD, false); // Iterator
     296                    psMetadataItem *key = NULL; // Item from iteration
     297                    psList *values = psListAlloc(void); // List containing the values
     298                    while ((key = psListGetAndIncrement(keysIter))) {
     299                        psMetadataItem *value = psMetadataLookup(hdu->header, key);
     300                        psListAdd(values, PS_LIST_TAIL, value);
     301                    }
     302                    psFree(keysIter);
     303                    headerItem = psMetadataItemAlloc(name, PS_DATA_LIST, specItem->comment, values);
     304                    psFree(values);
     305                }
     306                psFree(keys);
     307
     308                // This will also clean up the name
     309                conceptParse(spec, headerItem, cameraFormat, target, fpa, chip, cell);
     310            }
     311        }
     312        psFree(specsIter);
     313        return true;
     314    }
     315    return false;
     316}
     317
     318// XXX --- the below code has NOT been tested!
     319bool pmConceptsReadFromDatabase(psMetadata *specs, // The concept specifications
     320                                pmFPA *fpa, // The FPA
     321                                pmChip *chip, // The chip
     322                                pmCell *cell,  // The cell
     323                                psDB *db, // The database handle
     324                                psMetadata *target // Place into which to read the concepts
     325                               )
     326{
     327    bool mdok = true;               // Status of MD lookup
     328    psMetadata *dbSpec = psMetadataLookupMD(&mdok, cameraFormat, "DATABSE"); // The DATABASE spec
     329    if (mdok && dbSpec) {
     330        pmHDU *hdu = getLowestHDU(fpa, chip, cell); // The HDU at the lowest level
     331        psMetadata *cameraFormat = hdu->format; // The camera format
     332        psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
     333        psMetadataItem *specItem = NULL;    // Item from the specs metadata
     334        while ((specItem = psMetadataGetAndIncrement(specsIter))) {
     335            pmConceptSpec *spec = specItem->data.V; // The specification
     336            psString name = specItem->name; // The concept name
     337
     338            psMetadata *dbLookup = psMetadataLookupMD(&mdok, dbSpec, name);
     339            if (mdok && dbLookup) {
     340                const char *tableName = psMetadataLookupStr(&mdStatus, dbLookup, "TABLE"); // Table name
     341                // Names of the "where" columns
     342                const char *givenCols = psMetadataLookupStr(&mdStatus, dbLookup, "GIVENDBCOL");
     343                // Values of the "where" columns
     344                const char *givenPS = psMetadataLookupStr(&mdStatus, dbLookup, "GIVENPS");
     345
     346                // Now, need to get the "given"s
     347                if (strlen(givenCols) > 0 || strlen(givenPS) > 0) {
     348                    psList *cols = psStringSplit(givenCols, ",;"); // List of column names
     349                    psList *values = psStringSplit(givenPS, ",;"); // List of value names for the columns
     350                    psMetadata *selection = psMetadataAlloc(); // The stuff to select in the DB
     351                    if (cols->n != values->n) {
     352                        psLogMsg(__func__, PS_LOG_WARN,
     353                                 "The GIVENDBCOL and GIVENPS entries for %s do not have "
     354                                 "the same number of entries --- ignored.\n", concept);
     355                    } else {
     356                        // Iterators for the lists
     357                        psListIterator *colsIter = psListIteratorAlloc(cols, PS_LIST_HEAD, false);
     358                        psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false);
     359                        char *column = NULL;    // Name of the column
     360                        while ((column = psListGetAndIncrement(colsIter))) {
     361                            char *name = psListGetAndIncrement(valuesIter); // Name for the value
     362                            if (!strlen(column) || !strlen(name)) {
     363                                psLogMsg(__func__, PS_LOG_WARN, "One of the columns or value names for %s is "
     364                                         " empty --- ignored.\n", concept);
     365                            } else {
     366                                // Search for the value name
     367                                psMetadataItem *item = pmConceptReadFromHeader(fpa, chip, cell, name);
     368                                if (! item) {
     369                                    item = pmConceptReadFromDefault(fpa, chip, cell, name);
     370                                }
     371                                if (! item) {
     372                                    psLogMsg(__func__, PS_LOG_ERROR, "Unable to find the value name %s for DB"
     373                                             " lookup on %s --- ignored.\n", name, concept);
     374                                } else {
     375                                    // We need to create a new psMetadataItem.  I don't think we can't
     376                                    // simply hack the existing one, since that could conceivably cause
     377                                    // memory leaks
     378                                    psMetadataItem *newItem = psMetadataItemAlloc(concept, item->type,
     379                                                              item->comment,
     380                                                              item->data.V);
     381                                    psMetadataAddItem(selection, newItem, PS_LIST_TAIL, PS_META_REPLACE);
     382                                    psFree(newItem);
     383                                }
     384                            }
     385                            psFree(name);
     386                            psFree(column);
     387                        } // Iterating through the columns
     388                        psFree(colsIter);
     389                        psFree(valuesIter);
     390
     391                        psArray *dbResult = psDBSelectRows(db, tableName, selection, 2); // Lookup result
     392                        // Note that we use limit=2 in order to test if there are multiple rows returned
     393
     394                        psMetadataItem *conceptItem = NULL; // The final result of the DB lookup
     395                        if (dbResult->n == 0) {
     396                            psLogMsg(__func__, PS_LOG_WARN,
     397                                     "Unable to find any rows in DB for %s --- ignored\n", concept);
     398                        } else {
     399                            if (dbResult-> n > 1) {
     400                                psLogMsg(__func__, PS_LOG_WARN,
     401                                         "Multiple rows returned in DB lookup for %s --- "
     402                                         " using the first one only.\n", concept);
     403                            }
     404                            conceptItem = (psMetadataItem*)dbResult->data[0];
     405                        }
     406
     407                        // Now we have the result
     408                        conceptParse(spec, headerItem, cameraFormat, target, fpa, chip, cell);
     409
     410                    }
     411                    psFree(cols);
     412                    psFree(values);
     413                }
     414            } // Doing the "given"s.
     415
     416        } // Iterating through the concept specifications
     417        psFree(specsIter);
     418
     419        return true;
     420    }
     421    return false;
     422}
     423
     424
     425
     426
     427#if 0
    9428
    10429psMetadataItem *pmConceptReadFromCamera(pmCell *cell, // The cell
     
    241660
    242661
    243 float pmConceptReadF32(pmFPA *fpa,        // The FPA
    244                        pmChip *chip,      // The chip
    245                        pmCell *cell,      // The cell
    246                        psDB *db,          // DB handle
    247                        const char *name // Name of the concept
    248                       )
    249 {
    250     psMetadataItem *item = pmConceptRead(fpa, chip, cell, db, name);
    251     float value = NAN;
    252     if (item) {
    253         switch (item->type) {
    254         case PS_DATA_F32:
    255             value = item->data.F32;
    256             break;
    257         case PS_DATA_F64:
    258             // Assume it's OK to truncate to floating point from double
    259             value = (float)item->data.F64;
    260             break;
    261         case PS_DATA_S32:
    262             // Promote to float
    263             value = (float)item->data.S32;
    264             break;
    265         default:
    266             psError(PS_ERR_IO, true, "Concept %s (%s) is not of floating point type (%x) --- treating as "
    267                     "undefined.\n", name, item->comment, item->type);
    268         }
    269         psTrace(__func__, 7, "Adding %s (%s): %f\n", name, item->comment, value);
    270     } else {
    271         psError(PS_ERR_IO, true, "Concept %s is not defined.\n", name);
    272     }
    273 
    274     return value;
    275 }
    276 
    277 double pmConceptReadF64(pmFPA *fpa,   // The FPA
    278                         pmChip *chip, // The chip
    279                         pmCell *cell, // The cell
    280                         psDB *db,     // DB handle
    281                         const char *name // Name of the concept
    282                        )
    283 {
    284     psMetadataItem *item = pmConceptRead(fpa, chip, cell, db, name);
    285     double value = NAN;
    286     if (item) {
    287         switch (item->type) {
    288         case PS_TYPE_F64:
    289             value = item->data.F64;
    290             break;
    291         case PS_TYPE_F32:
    292             // Promote to double
    293             value = (double)item->data.F32;
    294             break;
    295         case PS_TYPE_S32:
    296             // Promote to double
    297             value = (double)item->data.S32;
    298             break;
    299         default:
    300             psError(PS_ERR_IO, true, "Concept %s (%s) is not of double-precision floating point type (%x) "
    301                     "--- treating as undefined.\n", name, item->comment, item->type);
    302         }
    303         psTrace(__func__, 7, "Adding %s (%s): %f\n", name, item->comment, value);
    304     } else {
    305         psError(PS_ERR_IO, true, "Concept %s is not defined.\n", name);
    306     }
    307 
    308     return value;
    309 }
    310 
    311 int pmConceptReadS32(pmFPA *fpa,   // The FPA
    312                      pmChip *chip, // The chip
    313                      pmCell *cell, // The cell
    314                      psDB *db,     // DB handle
    315                      const char *name // Name of the concept
    316                     )
    317 {
    318     psMetadataItem *item = pmConceptRead(fpa, chip, cell, db, name);
    319     int value = 0;
    320     if (item) {
    321         switch (item->type) {
    322         case PS_TYPE_S32:
    323             value = item->data.S32;
    324             break;
    325         case PS_TYPE_F32:
    326             psLogMsg(__func__, PS_LOG_WARN, "Concept %s (%s) should be S32, but is F32 --- converting.\n",
    327                      name, comment);
    328             value = (int)item->data.F32;
    329             break;
    330         case PS_TYPE_F64:
    331             psLogMsg(__func__, PS_LOG_WARN, "Concept %s (%s) should be S32, but is F64 --- converting.\n",
    332                      name, comment);
    333             value = (int)item->data.F64;
    334             break;
    335         default:
    336             psError(PS_ERR_IO, true, "Concept %s (%s) is not of integer type (%x) --- treating as "
    337                     "undefined.\n", name, item->comment, item->type);
    338         }
    339         psTrace(__func__, 7, "Read concept %s (%s): %d\n", name, item->comment, value);
    340     } else {
    341         psError(PS_ERR_IO, true, "Concept %s is not defined.\n", name);
    342     }
    343 
    344     return value;
    345 }
    346 
    347 psString pmConceptReadString(pmFPA *fpa, // The FPA
    348                              pmChip *chip, // The chip
    349                              pmCell *cell, // The cell
    350                              psDB *db,  // DB handle
    351                              const char *name // Name of the concept
    352                             )
    353 {
    354     psMetadataItem *item = pmConceptRead(fpa, chip, cell, db, name);
    355     psString value = NULL;
    356     if (item) {
    357         switch (item->type) {
    358         case PS_DATA_STRING:
    359             value = psMemIncrRefCounter(item->data.V);
    360             break;
    361         case PS_DATA_F32:
    362             psStringAppend(&value, "%f", item->data.F32);
    363             break;
    364         case PS_DATA_S32:
    365             psStringAppend(&value, "%d", item->data.S32);
    366             break;
    367         default:
    368             psError(PS_ERR_IO, true, "Concept %s (%s) is not of string type (%x) --- treating as "
    369                     "undefined.\n", name, item->comment, item->type);
    370         }
    371         psTrace(__func__, 7, "Read concept %s (%s): %s\n", name, item->comment, value);
    372     } else {
    373         psError(PS_ERR_IO, true, "Concept %s is not defined.\n", name);
    374         value = psStringCopy("");
    375     }
    376 
    377     return value;
    378 }
    379 
     662#endif
  • branches/rel10_ifa/psModules/src/astrom/pmConceptsStandard.c

    r6552 r6570  
    1616
    1717
    18 // Read FPA.RA and translate
    19 psMetadataItem *pmConceptRead_FPA_RA(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    20 {
    21     double ra = NAN;                    // The Right Ascension
    22     psMetadataItem *raItem = pmConceptRead(fpa, NULL, NULL, db, "FPA.RA"); // The FPA.RA item
    23     if (raItem) {
    24         switch (raItem->type) {
    25         case PS_TYPE_F32:
    26             ra = raItem->data.F32;
    27             break;
    28         case PS_TYPE_F64:
    29             ra = raItem->data.F64;
    30             break;
    31         case PS_DATA_STRING:
    32             // Sexagesimal format
     18static double defaultCoordScaling(psMetadataItem *pattern)
     19{
     20    if (strcmp(pattern->name, "FPA.RA") == 0) {
     21        psLogMsg(__func__, PS_LOG_WARN, "Assuming format for %s is HOURS.\n", pattern->name);
     22        return M_PI / 12.0;
     23    } else if (strcmp(pattern->name, "FPA.DEC") == 0) {
     24        psLogMsg(__func__, PS_LOG_WARN, "Assuming format for %s is DEGREES.\n", pattern->name);
     25        return M_PI / 180.0;
     26    } else {
     27        psAbort("Should never ever get here.\n");
     28    }
     29}
     30
     31
     32// FPA.RA and FPA.DEC
     33psMetadataItem *pmConceptParse_FPA_Coords(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
     34{
     35    assert(concept);
     36    assert(pattern);
     37    assert(cameraFormat);
     38
     39    double coords = NAN;                // The coordinates
     40    switch (concept->type) {
     41    case PS_TYPE_F32:
     42        coords = concept->data.F32;
     43        break;
     44    case PS_TYPE_F64:
     45        coords = concept->data.F64;
     46        break;
     47    case PS_DATA_STRING:
     48        // Sexagesimal format
     49        {
     50            int big, medium;
     51            float small;
     52            // XXX: Upgrade path is to allow dd:mm.mmm
     53            if (sscanf(concept->data.V, "%d:%d:%f", &big, &medium, &small) != 3 &&
     54                    sscanf(concept->data.V, "%d %d %f", &big, &medium, &small) != 3)
    3355            {
    34                 int big, medium;
    35                 float small;
    36                 // XXX: Upgrade path is to allow dd:mm.mmm
    37                 if (sscanf(raItem->data.V, "%d:%d:%f", &big, &medium, &small) != 3 &&
    38                         sscanf(raItem->data.V, "%d %d %f", &big, &medium, &small) != 3)
    39                 {
    40                     psError(PS_ERR_IO, true, "Cannot interpret FPA.RA: %s\n", raItem->data.V);
    41                     break;
    42                 }
    43                 ra = abs(big) + (float)medium/60.0 + small/3600.0;
    44                 if (big < 0)
    45                 {
    46                     ra *= -1.0;
    47                 }
    48             }
    49             break;
    50         default:
    51             psError(PS_ERR_IO, true, "FPA.RA is of an unexpected type: %x\n", raItem->type);
    52         }
    53 
    54         // How to interpret the RA
    55         const psMetadata *camera = fpa->camera; // Camera configuration data
    56         bool mdok = true;           // Status of MD lookup
    57         psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
    58         if (mdok && formats) {
    59             psString raFormat = psMetadataLookupStr(&mdok, formats, "FPA.RA");
    60             if (mdok && strlen(raFormat) > 0) {
    61                 if (strcasecmp(raFormat, "HOURS") == 0) {
    62                     ra *= M_PI / 12.0;
    63                 } else if (strcasecmp(raFormat, "DEGREES") == 0) {
    64                     ra *= M_PI / 180.0;
    65                 } else if (strcasecmp(raFormat, "RADIANS") == 0) {
    66                     // No action required
    67                 } else {
    68                     psLogMsg(__func__, PS_LOG_WARN, "Don't understand FPA.RA in FORMATS (%s) --- assuming"
    69                              " HOURS.\n");
    70                     ra *= M_PI / 12.0;
    71                 }
    72             } else {
    73                 psError(PS_ERR_IO, false, "Unable to find FPA.RA in FORMATS --- assuming HOURS.\n");
    74                 ra *= M_PI / 12.0;
    75             }
    76         } else {
    77             psError(PS_ERR_IO, false, "Unable to find FORMAT metadata in camera configuration --- "
    78                     "assuming format for FPA.RA is HOURS.\n");
    79             ra *= M_PI / 12.0;
    80         }
    81     } else {
    82         psError(PS_ERR_IO, false, "Couldn't find FPA.RA.\n");
    83     }
    84 
    85     return psMetadataItemAllocF64("FPA.RA", "Right Ascension of boresight", ra);
    86 }
    87 
    88 // Read FPA.DEC and translate
    89 psMetadataItem *pmConceptRead_FPA_DEC(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    90 {
    91     double dec = NAN;               // The DEC
    92     psMetadataItem *decItem = pmConceptRead(fpa, NULL, NULL, db, "FPA.DEC"); // The FPA.DEC item
    93     if (decItem) {
    94         switch (decItem->type) {
    95         case PS_TYPE_F32:
    96             dec = decItem->data.F32;
    97             break;
    98         case PS_TYPE_F64:
    99             dec = decItem->data.F64;
    100             break;
    101         case PS_DATA_STRING:
    102             // Sexagesimal format
     56                psError(PS_ERR_IO, true, "Cannot interpret FPA.RA: %s\n", concept->data.V);
     57                break;
     58            }
     59            coords = abs(big) + (float)medium/60.0 + small/3600.0;
     60            if (big < 0)
    10361            {
    104                 int big, medium;
    105                 float small;
    106                 // XXX: Upgrade path is to allow dd:mm.mmm
    107                 if (sscanf(decItem->data.V, "%d:%d:%f", &big, &medium, &small) != 3 &&
    108                         sscanf(decItem->data.V, "%d %d %f", &big, &medium, &small) != 3)
    109                 {
    110                     psError(PS_ERR_IO, true, "Cannot interpret FPA.DEC: %s\n", decItem->data.V);
    111                     break;
    112                 }
    113                 dec = abs(big) + (float)medium/60.0 + small/3600.0;
    114                 if (big < 0)
    115                 {
    116                     dec *= -1.0;
    117                 }
    118             }
    119             break;
    120         default:
    121             psError(PS_ERR_IO, true, "FPA.DEC is of an unexpected type: %x\n", decItem->type);
    122         }
    123 
    124         // How to interpret the DEC
    125         const psMetadata *camera = fpa->camera; // Camera configuration data
    126         bool mdok = true;           // Status of MD lookup
    127         psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
    128         if (mdok && formats) {
    129             psString decFormat = psMetadataLookupStr(&mdok, formats, "FPA.DEC");
    130             if (mdok && strlen(decFormat) > 0) {
    131                 if (strcasecmp(decFormat, "HOURS") == 0) {
    132                     dec *= M_PI / 12.0;
    133                 } else if (strcasecmp(decFormat, "DEGREES") == 0) {
    134                     dec *= M_PI / 180.0;
    135                 } else if (strcasecmp(decFormat, "RADIANS") == 0) {
    136                     // No action required
    137                 } else {
    138                     psLogMsg(__func__, PS_LOG_WARN, "Don't understand FPA.DEC in FORMATS (%s) --- "
    139                              "assuming DEGREES.\n");
    140                     dec *= M_PI / 180.0;
    141                 }
    142             } else {
    143                 psError(PS_ERR_IO, false, "Unable to find FPA.DEC in FORMATS --- assuming DEGREES.\n");
    144                 dec *= M_PI / 180.0;
    145             }
    146         } else {
    147             psError(PS_ERR_IO, false, "Unable to find FORMATS metadata in camera configuration --- "
    148                     "assuming format for FPA.DEC is DEGREES.\n");
    149             dec *= M_PI / 180.0;
    150         }
    151     } else {
    152         psError(PS_ERR_IO, false, "Couldn't find FPA.DEC.\n");
    153     }
    154 
    155     return psMetadataItemAllocF64("FPA.DEC", "Declination of boresight", dec);
    156 }
    157 
    158 
    159 bool pmConceptWrite_FPA_RA(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    160 {
    161     double ra = psMetadataLookupF64(NULL, fpa->concepts, "FPA.RA"); // The RA
    162 
    163     // How to interpret the RA
    164     const psMetadata *camera = fpa->camera; // Camera configuration data
    165     bool mdok = true;                   // Status of MD lookup
    166     psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
     62                coords *= -1.0;
     63            }
     64        }
     65        break;
     66    default:
     67        psError(PS_ERR_IO, true, "%s concept is of an unexpected type: %x\n", pattern->name, concept->type);
     68        return NULL;
     69    }
     70
     71    // How to interpret the coordinates
     72    bool mdok = true;           // Status of MD lookup
     73    psMetadata *formats = psMetadataLookupMD(&mdok, cameraFormat, "FORMATS");
    16774    if (mdok && formats) {
    168         psString raFormat = psMetadataLookupStr(&mdok, formats, "FPA.RA");
    169         if (mdok && strlen(raFormat) > 0) {
    170             if (strcasecmp(raFormat, "HOURS") == 0) {
    171                 ra /= M_PI / 12.0;
    172             } else if (strcasecmp(raFormat, "DEGREES") == 0) {
    173                 ra /= M_PI / 180.0;
    174             } else if (strcasecmp(raFormat, "RADIANS") == 0) {
     75        psString format = psMetadataLookupStr(&mdok, formats, blank->name);
     76        if (mdok && strlen(format) > 0) {
     77            if (strcasecmp(format, "HOURS") == 0) {
     78                coords *= M_PI / 12.0;
     79            } else if (strcasecmp(format, "DEGREES") == 0) {
     80                coords *= M_PI / 180.0;
     81            } else if (strcasecmp(format, "RADIANS") == 0) {
    17582                // No action required
    17683            } else {
    177                 psLogMsg(__func__, PS_LOG_WARN, "Don't understand FPA.RA in FORMATS (%s) --- assuming"
    178                          " HOURS.\n");
    179                 ra /= M_PI / 12.0;
     84                coords *= defaultCoordScaling(pattern);
    18085            }
    18186        } else {
    182             psError(PS_ERR_IO, false, "Unable to find FPA.RA in FORMATS --- assuming HOURS.\n");
    183             ra /= M_PI / 12.0;
     87            coords *= defaultCoordScaling(pattern);
    18488        }
    18589    } else {
    186         psError(PS_ERR_IO, false, "Unable to find FORMAT metadata in camera configuration --- "
    187                 "assuming format for FPA.RA is HOURS.\n");
    188         ra /= M_PI / 12.0;
     90        coords *= defaultCoordScaling(pattern);
     91    }
     92
     93    return psMetadataItemAllocF64(pattern->name, pattern->comment, coords);
     94}
     95
     96// FPA.RA and FPA.DEC
     97psMetadataItem *pmConceptFormat_FPA_Coords(psMetadataItem *concept, psMetadataItem *pattern, pmConceptSpec *spec, psMetadata *cameraFormat)
     98{
     99    assert(concept);
     100    assert(pattern);
     101    assert(cameraFormat);
     102
     103    double coords = concept->data.F64;  // The coordinates
     104
     105    // How to interpret the coordinates
     106    bool mdok = true;                   // Status of MD lookup
     107    psMetadata *formats = psMetadataLookupMD(&mdok, cameraFormat, "FORMATS");
     108    if (mdok && formats) {
     109        psString format = psMetadataLookupStr(&mdok, formats, pattern->name);
     110        if (mdok && strlen(format) > 0) {
     111            if (strcasecmp(format, "HOURS") == 0) {
     112                coords /= M_PI / 12.0;
     113            } else if (strcasecmp(format, "DEGREES") == 0) {
     114                coords /= M_PI / 180.0;
     115            } else if (strcasecmp(format, "RADIANS") == 0) {
     116                // No action required
     117            } else {
     118                coords /= defaultCoordScaling(pattern);
     119            }
     120        } else {
     121            coords /= defaultCoordScaling(pattern);
     122        }
     123    } else {
     124        coords /= defaultCoordScaling(pattern);
    189125    }
    190126
     
    193129    float small;
    194130    big = (int)ra;
    195     medium = (int)(60.0*(ra - (double)big));
    196     small = 3600.0*(ra - (double)big - 60.0 * (double)medium);
    197     psString raString = psStringCopy("");
    198     psStringAppend(&raString, "%d:%d:%.2f", big, medium, small);
    199     psMetadataItem *raItem = psMetadataItemAllocStr("FPA.RA", "Right Ascension of the boresight",
    200                              raString);
    201     pmConceptWriteItem(fpa, NULL, NULL, db, raItem);
    202     psFree(raItem);
    203     psFree(raString);
    204 
    205     return true;
    206 }
    207 
    208 bool pmConceptWrite_FPA_DEC(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    209 {
    210     double dec = psMetadataLookupF64(NULL, fpa->concepts, "FPA.DEC"); // The DEC
    211 
    212     // How to interpret the DEC
    213     const psMetadata *camera = fpa->camera; // Camera configuration data
    214     bool mdok = true;               // Status of MD lookup
    215     psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
    216     if (mdok && formats) {
    217         psString decFormat = psMetadataLookupStr(&mdok, formats, "FPA.DEC");
    218         if (mdok && strlen(decFormat) > 0) {
    219             if (strcasecmp(decFormat, "HOURS") == 0) {
    220                 dec /= M_PI / 12.0;
    221             } else if (strcasecmp(decFormat, "DEGREES") == 0) {
    222                 dec /= M_PI / 180.0;
    223             } else if (strcasecmp(decFormat, "RADIANS") == 0) {
    224                 // No action required
    225             } else {
    226                 psLogMsg(__func__, PS_LOG_WARN, "Don't understand FPA.DEC in FORMATS (%s) --- assuming"
    227                          " DEGREES.\n");
    228                 dec /= M_PI / 180.0;
    229             }
    230         } else {
    231             psError(PS_ERR_IO, false, "Unable to find FPA.DEC in FORMATS --- assuming DEGREES.\n");
    232             dec /= M_PI / 180.0;
    233         }
    234     } else {
    235         psError(PS_ERR_IO, false, "Unable to find FORMAT metadata in camera configuration --- "
    236                 "assuming format for FPA.DEC is HOURS.\n");
    237         dec /= M_PI / 12.0;
    238     }
    239 
    240     // We choose to write sexagesimal format
    241     int big, medium;
    242     float small;
    243     big = (int)dec;
    244     medium = (int)(60.0*(dec - (double)big));
    245     small = 3600.0*(dec - (double)big - 60.0 * (double)medium);
    246     psString decString = psStringCopy("");
    247     psStringAppend(&decString, "%d:%d:%.2f", big, medium, small);
    248     psMetadataItem *decItem = psMetadataItemAllocStr("FPA.DEC", "Right Ascension of the boresight",
    249                               decString);
    250     pmConceptWriteItem(fpa, NULL, NULL, db, decItem);
    251     psFree(decItem);
    252     psFree(decString);
    253 
    254     return true;
    255 }
    256 
    257 
    258 psMetadataItem *pmConceptRead_CELL_TRIMSEC(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    259 {
     131    medium = (int)(60.0*(coords - (double)big));
     132    small = 3600.0*(coords - (double)big - 60.0 * (double)medium);
     133    psString coordString = NULL;        // String with the coordinates in sexagesimal format
     134    psStringAppend(&coordString, "%d:%d:%.2f", big, medium, small);
     135    psMetadataItem *coordItem = psMetadataItemAllocStr(pattern->name, pattern->comment, coordString);
     136    psFree(coordString);
     137
     138    return coordItem;
     139}
     140
     141
     142psMetadataItem *pmConceptParse_CELL_TRIMSEC(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
     143{
     144    assert(concept);
     145    assert(cell);
     146    assert(pattern);
     147
     148    psMetadata *cellConfig = cell->config; // The cell configuration
    260149    psRegion *trimsec = psAlloc(sizeof(psRegion)); // Make space for a psRegion (usually passed by value)
    261150
    262     psMetadataItem *secItem = pmConceptRead(fpa, chip, cell, db, "CELL.TRIMSEC");
    263     if (! secItem) {
    264         psError(PS_ERR_IO, false, "Couldn't find CELL.TRIMSEC.\n");
    265         *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
    266     } else if (secItem->type != PS_DATA_STRING) {
    267         psError(PS_ERR_IO, true, "CELL.TRIMSEC is not of type STR (%x)\n", secItem->type);
     151    if (concept->type != PS_DATA_STRING) {
     152        psError(PS_ERR_IO, true, "CELL.TRIMSEC after read is not of type STR (%x)\n", concept->type);
    268153        *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
    269154    } else {
    270         psString section = secItem->data.V; // The section string
    271 
    272         psMetadataItem *sourceItem = pmConceptRead(fpa, chip, cell, db, "CELL.TRIMSEC.SOURCE");
    273         if (! sourceItem) {
    274             psError(PS_ERR_IO, false, "Couldn't find CELL.TRIMSEC.SOURCE.\n");
    275             *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
    276         } else if (sourceItem->type != PS_DATA_STRING) {
    277             psError(PS_ERR_IO, true, "CELL.TRIMSEC.SOURCE is not of type STR (%x)\n", sourceItem->type);
    278             *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
    279         } else {
    280             psString source = sourceItem->data.V; // The source string
    281 
    282             if (strcasecmp(source, "VALUE") == 0) {
    283                 *trimsec = psRegionFromString(section);
    284             } else if (strcasecmp(source, "HEADER") == 0) {
    285                 psMetadata *header = NULL; // The FITS header
    286                 if (cell->hdu) {
    287                     header = cell->hdu->header;
    288                 } else if (chip->hdu) {
    289                     header = chip->hdu->header;
    290                 } else if (fpa->hdu) {
    291                     header = fpa->hdu->header;
    292                 }
    293                 if (! header) {
    294                     psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
    295                     *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
    296                 } else {
    297                     bool mdok = true;               // Status of MD lookup
    298                     psString secValue = psMetadataLookupStr(&mdok, header, section);
    299                     if (! mdok || ! secValue) {
    300                         psError(PS_ERR_IO, false, "Unable to locate header %s\n", section);
    301                         *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
    302                     } else {
    303                         *trimsec = psRegionFromString(secValue);
    304                     }
    305                 }
    306             } else {
    307                 psError(PS_ERR_IO, true, "CELL.TRIMSEC.SOURCE (%s) is not HEADER or VALUE --- trying "
    308                         "VALUE.\n", source);
    309                 *trimsec = psRegionFromString(section);
    310             } // Value of CELL.TRIMSEC.SOURCE
    311         } // Looking up CELL.TRIMSEC.SOURCE
    312     } // Looking up CELL.TRIMSEC
    313 
    314     psMetadataItem *item = psMetadataItemAllocPtr("CELL.TRIMSEC", PS_DATA_UNKNOWN, "Trim section", trimsec);
     155        *trimsec = psRegionFromString(concept->data.V);
     156    }
     157
     158    psMetadataItem *item = psMetadataItemAllocPtr(pattern->name, PS_DATA_UNKNOWN, pattern->comment, trimsec);
    315159    psFree(trimsec);
    316160    return item;
    317161}
    318162
    319 
    320 psMetadataItem *pmConceptRead_CELL_BIASSEC(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    321 {
     163psMetadataItem *pmConceptParse_CELL_BIASSEC(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
     164{
     165    assert(concept);
     166    assert(cell);
     167    assert(pattern);
     168    psMetadata *cellConfig = cell->config; // The cell configuration
     169
    322170    psList *biassecs = psListAlloc(NULL); // List of bias sections
    323171    psMetadataItem *item = psMetadataItemAlloc("CELL.BIASSEC", PS_DATA_LIST, "Bias sections", biassecs);
    324172    psFree(biassecs);               // Drop reference
    325173
    326     psMetadataItem *secItem = pmConceptRead(fpa, chip, cell, db, "CELL.BIASSEC");
    327     if (! secItem) {
    328         psError(PS_ERR_IO, false, "Couldn't find CELL.BIASSEC.\n");
    329         return item;
    330     }
    331     if (secItem->type != PS_DATA_STRING) {
    332         psError(PS_ERR_IO, true, "CELL.BIASSEC is not of type STR (%x)\n", secItem->type);
    333         return item;
    334     }
    335 
    336     psString sections = secItem->data.V; // The section string
    337 
    338     psMetadataItem *sourceItem = pmConceptRead(fpa, chip, cell, db, "CELL.BIASSEC.SOURCE");
    339     if (! sourceItem) {
    340         psError(PS_ERR_IO, false, "Couldn't find CELL.BIASSEC.SOURCE.\n");
    341         return item;
    342     } else if (sourceItem->type != PS_DATA_STRING) {
    343         psError(PS_ERR_IO, true, "CELL.BIASSEC.SOURCE is not of type STR (%x)\n", sourceItem->type);
    344         return item;
    345     }
    346 
    347     psString source = sourceItem->data.V; // The source string
    348 
    349     if (! strcasecmp(source, "NONE")) {
    350         return item;                // There is no biassec
    351     }
    352 
    353     psList *secList = psStringSplit(sections, " ;"); // List of sections
    354     psListIterator *secIter = psListIteratorAlloc(secList, PS_LIST_HEAD, false); // Iterator over
    355     // sections
    356     psString aSection = NULL; // A section from the list
    357     while ((aSection = psListGetAndIncrement(secIter))) {
    358         psRegion *region = psAlloc(sizeof(psRegion)); // Make space for a psRegion (usually passed by
    359         // value)
    360 
    361         if (strcasecmp(source, "VALUE") == 0) {
    362             *region = psRegionFromString(aSection);
    363         } else if (strcasecmp(source, "HEADER") == 0) {
    364             psMetadata *header = NULL; // The FITS header
    365             if (cell->hdu) {
    366                 header = cell->hdu->header;
    367             } else if (chip->hdu) {
    368                 header = chip->hdu->header;
    369             } else if (fpa->hdu) {
    370                 header = fpa->hdu->header;
    371             }
    372             if (! header) {
    373                 psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
    374                 *region = psRegionSet(0.0,0.0,0.0,0.0);
     174    switch (concept->type) {
     175    case PS_DATA_STRING: {
     176            psList *regions = psStringSplit(concept->data.V, " ;"); // List of regions
     177            psListIterator *regionsIter = psListIteratorAlloc(regions, PS_LIST_HEAD, false); // Iterator
     178            psString regionString = NULL; // Region string from iteration
     179            while ((regionString = psListGetAndIncrement(regionsIter))) {
     180                psRegion *region = psAlloc(sizeof(psRegion)); // The region
     181                *region = psRegionFromString(regionString);
     182                psListAdd(biassecs, PS_LIST_TAIL, region);
     183                psFree(region);           // Drop reference
     184            }
     185            psFree(regionsIter);
     186            psFree(regions);
     187            break;
     188        }
     189    case PS_DATA_LIST: {
     190            psList *regions = concept->data.V; // The list of regions
     191            psListIterator *regionsIter = psListIteratorAlloc(regions, PS_LIST_HEAD, false); // Iterator
     192            psMetadataItem *regionItem = NULL; // Item from list iteration
     193            while ((regionItem = psListGetAndIncrement(regionsIter))) {
     194                if (regionItem->type != PS_DATA_STRING) {
     195                    psLogMsg(__func__, PS_LOG_WARN, "CELL.BIASSEC member is not of type STR --- ignored.\n");
     196                    continue;
     197                }
     198                psRegion *region = psAlloc(sizeof(psRegion)); // The region
     199                *region = psRegionFromString(regionItem->data.V);
     200                psListAdd(biassecs, PS_LIST_TAIL, region);
     201                psFree(region);           // Drop reference
     202            }
     203            psFree(regionsIter);
     204            break;
     205        }
     206    default:
     207        psError(PS_ERR_IO, true, "CELL.BIASSEC after read is not of type STRING or LIST --- assuming "
     208                "blank.\n");
     209    }
     210
     211    psMetadataItem *item = psMetadataItemAllocPtr(pattern->name, PS_DATA_LIST, pattern->comment, biassecs);
     212    psFree(biassecs);
     213    return item;
     214}
     215
     216// CELL.XBIN and CELL.YBIN
     217psMetadataItem *pmConceptParse_CELL_BINNING(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
     218{
     219    assert(concept);
     220    assert(pattern);
     221
     222    int binning = 1;                    // Binning factor in x
     223    if (concept->type == PS_DATA_STRING) {
     224        psString binString = concept->data.V; // The string containing the binning
     225        if ((strcmp(pattern->name, "CELL.XBIN") == 0 && sscanf(binString, "%d %*d", &binning) != 1 &&
     226                sscanf(binString, "%d,%*d", &binning) != 1) ||
     227                (strcmp(pattern->name, "CELL.YBIN") == 0 && sscanf(binString, "%*d %d", &binning) != 1 &&
     228                 sscanf(binString, "%*d,%d", &binning) != 1) {
     229                psError(PS_ERR_IO, true, "Unable to parse string to get %s: %s\n", pattern->name, binString)
     230                    ;
     231                }
     232            } else if (concept->type == PS_TYPE_S32) {
     233                binning = concept->data.S32;
    375234            } else {
    376                 bool mdok = true;           // Status of MD lookup
    377                 psString secValue = psMetadataLookupStr(&mdok, header, aSection);
    378                 if (! mdok || ! secValue) {
    379                     psError(PS_ERR_IO, false, "Unable to locate header %s\n", aSection);
    380                     *region = psRegionSet(0.0,0.0,0.0,0.0);
    381                 } else {
    382                     *region = psRegionFromString(secValue);
    383                 }
    384             }
    385         } else {
    386             psError(PS_ERR_IO, true, "CELL.BIASSEC.SOURCE (%s) is not HEADER or VALUE --- trying "
    387                     "VALUE.\n", source);
    388             *region = psRegionFromString(aSection);
    389         } // Value of CELL.BIASSEC.SOURCE
    390 
    391         psListAdd(biassecs, PS_LIST_TAIL, region);
    392         psFree(region);
    393     } // Iterating over multiple sections
    394     psFree(secIter);
    395     psFree(secList);
    396 
    397     return item;
    398 }
    399 
    400 
    401 psMetadataItem *pmConceptRead_CELL_XBIN(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    402 {
    403     int xBin = 1;                   // Binning factor in x
    404     psMetadataItem *binItem = pmConceptRead(fpa, chip, cell, db, "CELL.XBIN");
    405     if (! binItem) {
    406         psError(PS_ERR_IO, false, "Couldn't find CELL.XBIN.\n");
    407     } else if (binItem->type == PS_DATA_STRING) {
    408         psString binString = binItem->data.V; // The string containing the binning
    409         if (sscanf(binString, "%d %*d", &xBin) != 1 &&
    410                 sscanf(binString, "%d,%*d", &xBin) != 1) {
    411             psError(PS_ERR_IO, true, "Unable to read string to get x binning: %s\n", binString);
    412         }
    413     } else if (binItem->type == PS_TYPE_S32) {
    414         xBin = binItem->data.S32;
    415     } else {
    416         psError(PS_ERR_IO, true, "Note sure how to interpret CELL.XBIN of type %x --- assuming 1.\n",
    417                 binItem->type);
    418     }
    419 
    420     return psMetadataItemAllocS32("CELL.XBIN", "Binning in x", xBin);
    421 }
    422 
    423 psMetadataItem *pmConceptRead_CELL_YBIN(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    424 {
    425     int yBin = 1;                   // Binning factor in y
    426     psMetadataItem *binItem = pmConceptRead(fpa, chip, cell, db, "CELL.YBIN");
    427     if (! binItem) {
    428         psError(PS_ERR_IO, false, "Couldn't find CELL.YBIN.\n");
    429     } else if (binItem->type == PS_DATA_STRING) {
    430         psString binString = binItem->data.V; // The string containing the binning
    431         if (sscanf(binString, "%*d %d", &yBin) != 1 &&
    432                 sscanf(binString, "%*d,%d", &yBin) != 1) {
    433             psError(PS_ERR_IO, true, "Unable to read string to get y binning: %s\n", binString);
    434         }
    435     } else if (binItem->type == PS_TYPE_S32) {
    436         yBin = binItem->data.S32;
    437     } else {
    438         psError(PS_ERR_IO, true, "Note sure how to interpret CELL.YBIN of type %x --- assuming 1.\n",
    439                 binItem->type);
    440     }
    441 
    442     return psMetadataItemAllocS32("CELL.YBIN", "Binning in y", yBin);
    443 }
    444 
    445 psMetadataItem *pmConceptRead_CELL_TIMESYS(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    446 {
     235                psError(PS_ERR_IO, true, "Note sure how to parse %s of type %x --- assuming 1.\n", pattern->name,
     236                        binItem->type);
     237            }
     238
     239            return psMetadataItemAllocS32(pattern->name, pattern->comment, binning);
     240}
     241
     242
     243psMetadataItem *pmConceptParse_CELL_TIMESYS(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
     244{
     245    assert(concept);
     246    assert(pattern);
     247
    447248    psTimeType timeSys = PS_TIME_UTC;   // The time system
    448     psString sys = pmConceptReadString(fpa, chip, cell, db, "CELL.TIMESYS"); // The time system as a string
    449     if (! sys || strlen(sys) <= 0) {
     249    if (concept->type != PS_DATA_STRING || strlen(sys) <= 0) {
    450250        psError(PS_ERR_IO, true, "Can't interpret CELL.TIMESYS --- assuming UTC.\n");
    451251    } else if (strcasecmp(sys, "TAI") == 0) {
     
    462262
    463263    psFree(sys);
    464     return psMetadataItemAllocS32("CELL.TIMESYS", "Time system", timeSys);
    465 }
    466 
    467 
    468 psMetadataItem *pmConceptRead_CELL_TIME(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    469 {
     264    return psMetadataItemAllocS32(pattern->name, pattern->comment, timeSys);
     265}
     266
     267
     268psMetadataItem *pmConceptParse_CELL_TIME(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
     269{
     270    assert(concept);
     271    assert(cameraFormat);
     272    assert(cell);
     273
    470274    // Need CELL.TIMESYS first
    471275    bool mdok = false;                  // Result of MD lookup
     
    475279        timeSys = PS_TIME_UTC;
    476280    }
     281
     282    // Get format
     283    bool mdok = true;               // Status of MD lookup
     284    psMetadata *formats = psMetadataLookupMD(&mdok, cameraFormat, "FORMATS");
     285    if (!mdok || !formats) {
     286        psError(PS_ERR_IO, false, "Unable to find FORMATS in camera configuration.\n");
     287        return NULL;
     288    }
     289
     290    psString timeFormat = psMetadataLookupStr(&mdok, formats, "CELL.TIME");
     291    if (!mdok || strlen(timeFormat) == 0) {
     292        psError(PS_ERR_IO, false, "Unable to find CELL.TIME in FORMATS.\n");
     293        return NULL;
     294    }
     295
    477296    psTime *time = NULL;                // The time
    478 
    479     psMetadataItem *timeItem = pmConceptRead(fpa, chip, cell, db, "CELL.TIME");
    480     if (! timeItem) {
    481         psError(PS_ERR_IO, false, "Couldn't find CELL.TIME.\n");
    482     } else {
    483         // Get format
    484         const psMetadata *camera = fpa->camera; // The camera configuration data
    485         bool mdok = true;               // Status of MD lookup
    486         psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
    487         if (mdok && formats) {
    488             psString timeFormat = psMetadataLookupStr(&mdok, formats, "CELL.TIME");
    489             if (mdok && strlen(timeFormat) > 0) {
     297    switch (concept->type) {
     298    case PS_DATA_LIST: {
     299            // The date and time are stored separately
     300            // Assume the date is first and the time second
     301            psList *dateTime = concept->data.V; // The list containing items for date and time
     302            psMetadataItem *dateItem = psListGet(dateTime, PS_LIST_HEAD); // Item containing the date
     303            psMetadataItem *timeItem = psListGet(dateTime, PS_LIST_HEAD + 1); // Item containing the time
     304            if (dateItem->type != PS_DATA_STRING) {
     305                psError(PS_ERR_IO, true, "Date is not of type STR.\n");
     306                return NULL;
     307            }
     308            psString dateString = dateItem->data.V; // The string with the date
     309            int day = 0, month = 0, year = 0;
     310            if (sscanf(dateString, "%d-%d-%d", &day, &month, &year) != 3 &&
     311                    sscanf(dateString, "%d/%d/%d", &day, &month, &year) != 3) {
     312                psError(PS_ERR_IO, true, "Unable to read date: %s\n", dateString);
     313                return NULL;
     314            }
     315            if (strstr(timeFormat, "BACKWARDS")) {
     316                int temp = day;
     317                day = year;
     318                year = temp;
     319            }
     320            if (strstr(timeFormat, "PRE2000") || year < 2000) {
     321                year += 2000;
     322            }
     323
     324            psString timeString = NULL; // The string with the time
     325            if (timeItem->type == PS_DATA_STRING) {
     326                timeString = timeItem->data.V;
     327            } else {
     328                // Assume that time is specified in Second of Day (!)
     329                double seconds = NAN;
    490330                switch (timeItem->type) {
    491                 case PS_DATA_STRING: {
    492                         psString timeString = timeItem->data.V;   // String with the time
    493                         if (strcasecmp(timeFormat, "ISO") == 0) {
    494                             // timeString contains an ISO time
    495                             time = psTimeFromISO(timeString, timeSys);
    496                         } else if (strcasecmp(timeFormat, "JD") == 0) {
    497                             double timeValue = strtod (timeString, NULL);
    498                             time = psTimeFromJD(timeValue);
    499                         } else if (strcasecmp(timeFormat, "MJD") == 0) {
    500                             double timeValue = strtod (timeString, NULL);
    501                             time = psTimeFromMJD(timeValue);
    502                         } else if (strstr(timeFormat, "SEPARATE")) {
    503                             // timeString contains headers for the date and time
    504                             psMetadata *header = NULL; // The FITS header
    505                             if (cell->hdu) {
    506                                 header = cell->hdu->header;
    507                             } else if (chip->hdu) {
    508                                 header = chip->hdu->header;
    509                             } else if (fpa->hdu) {
    510                                 header = fpa->hdu->header;
    511                             }
    512                             if (! header) {
    513                                 psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
    514                             } else {
    515                                 // Get the headers
    516                                 char *stuff1 = strpbrk(timeString, " ,;");
    517                                 psString dateName = psStringNCopy(timeString,
    518                                                                   strlen(timeString) - strlen(stuff1));
    519                                 char *stuff2 = strpbrk(stuff1, "abcdefghijklmnopqrstuvwxyz"
    520                                                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    521                                 psString timeName = psStringCopy(stuff2);
    522 
    523                                 bool mdok = true; // Status of MD lookup
    524                                 psString dateString = psMetadataLookupStr(&mdok, header, dateName);
    525                                 psFree(dateName);
    526                                 int day = 0, month = 0, year = 0;
    527                                 if (sscanf(dateString, "%d-%d-%d", &day, &month, &year) != 3 &&
    528                                         sscanf(dateString, "%d/%d/%d", &day, &month, &year) != 3) {
    529                                     psError(PS_ERR_IO, true, "Unable to read date: %s\n", dateString);
    530                                 } else {
    531                                     if (strstr(timeFormat, "BACKWARDS")) {
    532                                         int temp = day;
    533                                         day = year;
    534                                         year = temp;
    535                                     }
    536                                     if (strstr(timeFormat, "PRE2000") || year < 2000) {
    537                                         year += 2000;
    538                                     }
    539 
    540                                     psMetadataItem *timeItem = psMetadataLookup(header, timeName);
    541                                     if (! timeItem) {
    542                                         psError(PS_ERR_IO, false, "Unable to find time header: %s\n",
    543                                                 timeName);
    544                                     } else if (timeItem->type == PS_DATA_STRING) {
    545                                         // Time is a string, in the usual way:
    546                                         psStringAppend(&dateString, "T%s", timeItem->data.V);
    547                                     } else {
    548                                         // Assume that time is specified in Second of Day
    549                                         double seconds = NAN;
    550                                         switch (timeItem->type) {
    551                                         case PS_TYPE_S32:
    552                                             seconds = timeItem->data.S32;
    553                                             break;
    554                                         case PS_TYPE_F32:
    555                                             seconds = timeItem->data.F32;
    556                                             break;
    557                                         case PS_TYPE_F64:
    558                                             seconds = timeItem->data.F64;
    559                                             break;
    560                                         default:
    561                                             psError(PS_ERR_IO, true, "Time header (%s) is not of an "
    562                                                     "expected type: %x\n", timeName, timeItem->type);
    563                                         }
    564                                         // Now print to timeString as "hh:mm:ss.ss"
    565                                         int hours = seconds / 3600;
    566                                         seconds -= (double)hours * 3600.0;
    567                                         int minutes = seconds / 60;
    568                                         seconds -= (double)minutes * 60.0;
    569                                         psStringAppend(&dateString, "T%02d:%02d:%02f", hours, minutes,
    570                                                        seconds);
    571                                     }
    572                                     time = psTimeFromISO(dateString, timeSys);
    573                                 } // Reading date and time
    574                                 psFree(timeName);
    575                             } // Reading headers
    576                         } else {
    577                             psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%s) --- trying "
    578                                     "ISO\n", timeString);
    579                             time = psTimeFromISO(timeString, timeSys);
    580                         } // Interpreting the time string
    581                     }
     331                case PS_TYPE_S32:
     332                    seconds = timeItem->data.S32;
    582333                    break;
    583                 case PS_TYPE_F32: {
    584                         double timeValue = (double)timeItem->data.F32;
    585                         if (strcasecmp(timeFormat, "JD") == 0) {
    586                             time = psTimeFromJD(timeValue);
    587                         } else if (strcasecmp(timeFormat, "MJD") == 0) {
    588                             time = psTimeFromMJD(timeValue);
    589                         } else {
    590                             psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%f) --- trying "
    591                                     "JD\n", timeValue);
    592                             time = psTimeFromJD(timeValue);
    593                         }
    594                     }
     334                case PS_TYPE_F32:
     335                    seconds = timeItem->data.F32;
    595336                    break;
    596                 case PS_TYPE_F64: {
    597                         double timeValue = (double)timeItem->data.F64;
    598                         if (strcasecmp(timeFormat, "JD") == 0) {
    599                             time = psTimeFromJD(timeValue);
    600                         } else if (strcasecmp(timeFormat, "MJD") == 0) {
    601                             time = psTimeFromMJD(timeValue);
    602                         } else {
    603                             psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%f) --- trying "
    604                                     "JD\n", timeValue);
    605                             time = psTimeFromJD(timeValue);
    606                         }
    607                     }
     337                case PS_TYPE_F64:
     338                    seconds = timeItem->data.F64;
    608339                    break;
    609340                default:
    610                     psError(PS_ERR_IO, true, "Unable to parse CELL.TIME.\n");
     341                    psError(PS_ERR_IO, true, "Time is not of an expected type: %x\n", timeItem->type);
     342                    return NULL;
    611343                }
     344                // Now print to timeString as "hh:mm:ss.ss"
     345                int hours = seconds / 3600;
     346                seconds -= (double)hours * 3600.0;
     347                int minutes = seconds / 60;
     348                seconds -= (double)minutes * 60.0;
     349                psStringAppend(&timeString, "%02d:%02d:%02f", hours, minutes, seconds);
     350            }
     351            psString dateTimeString = NULL;
     352            psStringAppend(&dateTimeString, "%sT%s", dateString, timeString);
     353            time = psTimeFromISO(dateTimeString, timeSys);
     354            break;
     355        }
     356    case PS_DATA_STRING: {
     357            psString timeString = concept->data.V;   // String with the time
     358            if (strcasecmp(timeFormat, "ISO") == 0) {
     359                // timeString contains an ISO time
     360                time = psTimeFromISO(timeString, timeSys);
     361            } else if (strcasecmp(timeFormat, "JD") == 0) {
     362                double timeValue = strtod (timeString, NULL);
     363                time = psTimeFromJD(timeValue);
     364            } else if (strcasecmp(timeFormat, "MJD") == 0) {
     365                double timeValue = strtod (timeString, NULL);
     366                time = psTimeFromMJD(timeValue);
    612367            } else {
    613                 psError(PS_ERR_IO, false, "Unable to find CELL.TIME in FORMATS.\n");
    614             } // Getting the format
    615         } else {
    616             psError(PS_ERR_IO, false, "Unable to find FORMATS in camera configuration.\n");
    617         } // Getting the formats
    618     } // Getting CELL.TIME
    619 
    620     psMetadataItem *item = psMetadataItemAllocPtr("CELL.TIME", PS_DATA_TIME, "Time of exposure", time);
     368                psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%s) --- trying "
     369                        "ISO\n", timeString);
     370                time = psTimeFromISO(timeString, timeSys);
     371            } // Interpreting the time string
     372            break;
     373        }
     374    case PS_TYPE_F32: {
     375            double timeValue = (double)concept->data.F32;
     376            if (strcasecmp(timeFormat, "JD") == 0) {
     377                time = psTimeFromJD(timeValue);
     378            } else if (strcasecmp(timeFormat, "MJD") == 0) {
     379                time = psTimeFromMJD(timeValue);
     380            } else {
     381                psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%f) --- trying "
     382                        "JD\n", timeValue);
     383                time = psTimeFromJD(timeValue);
     384            }
     385            break;
     386        }
     387    case PS_TYPE_F64: {
     388            double timeValue = (double)concept->data.F64;
     389            if (strcasecmp(timeFormat, "JD") == 0) {
     390                time = psTimeFromJD(timeValue);
     391            } else if (strcasecmp(timeFormat, "MJD") == 0) {
     392                time = psTimeFromMJD(timeValue);
     393            } else {
     394                psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%f) --- trying "
     395                        "JD\n", timeValue);
     396                time = psTimeFromJD(timeValue);
     397            }
     398            break;
     399        }
     400    default:
     401        psError(PS_ERR_IO, true, "Unable to parse CELL.TIME.\n");
     402        return NULL;
     403    }
     404
     405    psMetadataItem *item = psMetadataItemAllocPtr(pattern->name, PS_DATA_TIME, pattern->comment, time);
    621406    psFree(time);
    622407    return item;
     
    624409
    625410// Correct a position --- in case the user wants FORTRAN indexing (like the FITS standard...)
    626 static int fortranCorr(pmFPA *fpa,       // FPA, contains the camera configuration
     411static int fortranCorr(psMetadata *cameraFormat, // The camera format description
    627412                       const char *name // Name of concept to check for FORTRAN indexing
    628413                      )
    629414{
    630415    bool mdok = false;                  // Result of MD lookup
    631     psMetadata *formats = psMetadataLookupMD(&mdok, fpa->camera, "FORMATS");
     416    psMetadata *formats = psMetadataLookupMD(&mdok, cameraFormat, "FORMATS");
    632417    if (mdok && formats) {
    633418        psString format = psMetadataLookupStr(&mdok, formats, name);
     
    639424}
    640425
    641 psMetadataItem *pmConceptRead_CELL_X0(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    642 {
    643     int x0 = pmConceptReadS32(fpa, chip, cell, db, "CELL.X0");
    644     x0 += fortranCorr(fpa, "CELL.X0");
    645     return psMetadataItemAllocS32("CELL.X0", "Position of (0,0) on the chip", x0);
    646 }
    647 
    648 
    649 psMetadataItem *pmConceptRead_CELL_Y0(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    650 {
    651     int y0 = pmConceptReadS32(fpa, chip, cell, db, "CELL.Y0");
    652     y0 += fortranCorr(fpa, "CELL.X0");
    653     return psMetadataItemAllocS32("CELL.Y0", "Position of (0,0) on the chip", y0);
    654 }
    655 
    656 
    657 bool pmConceptWrite_CELL_TRIMSEC(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    658 {
    659     psMetadataItem *trimsecItem = psMetadataLookup(cell->concepts, "CELL.TRIMSEC");
    660     psRegion *trimsec = trimsecItem->data.V; // The trimsec region
    661     psString source = pmConceptReadString(fpa, chip, cell, db, "CELL.TRIMSEC.SOURCE"); // The source string
    662 
    663     if (strcasecmp(source, "VALUE") == 0) {
    664         // Check that it's the same value as stored in the camera
    665         psString checkString = psMetadataLookupStr(NULL, cell->config, "CELL.TRIMSEC");
    666         psRegion checkRegion = psRegionFromString(checkString);
    667         if (! COMPARE_REGIONS(&checkRegion, trimsec)) {
    668             psError(PS_ERR_IO, true, "Target CELL.TRIMSEC is specified by value, and values don't "
    669                     "match.\n");
    670             return false;
    671         }
    672         return true;
    673     }
    674 
    675     if (strcasecmp(source, "HEADER") == 0) {
    676         psMetadata *header = NULL; // The FITS header
    677         if (cell->hdu) {
    678             header = cell->hdu->header;
    679         } else if (chip->hdu) {
    680             header = chip->hdu->header;
    681         } else if (fpa->hdu) {
    682             header = fpa->hdu->header;
    683         }
    684         if (! header) {
    685             psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
    686             return false;
    687         }
    688         psMetadataAddItem(header, trimsecItem, PS_LIST_TAIL, PS_META_REPLACE);
    689         return true;
    690     }
    691 
    692     psError(PS_ERR_IO, true, "CELL.TRIMSEC.SOURCE (%s) is not HEADER or VALUE.\n", source);
    693     return false;
    694 }
    695 
    696 bool pmConceptWrite_CELL_BIASSEC(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    697 {
    698     psMetadataItem *biassecItem = psMetadataLookup(cell->concepts, "CELL.BIASSEC");
    699     psList *biassecs = biassecItem->data.V; // The biassecs region list
    700 
    701     if (biassecs->n == 0) {
    702         psMetadataItem *noneItem = psMetadataItemAllocStr("CELL.BIASSEC", "Bias section", "NONE");
    703         pmConceptWriteItem(fpa, chip, cell, db, noneItem);
    704         return true;
    705     }
    706 
    707     psString source = pmConceptReadString(fpa, chip, cell, db, "CELL.TRIMSEC.SOURCE"); // The source string
    708 
    709     if (strcasecmp(source, "VALUE") == 0) {
    710         // Check that it's the same value as stored in the camera
    711         psString checkString = psMetadataLookupStr(NULL, cell->config, "CELL.BIASSEC");
    712         psList *checkList = psStringSplit(checkString, " ;");
    713         if (biassecs->n != checkList->n) {
    714             psError(PS_ERR_IO, true, "Target CELL.BIASSEC is specified by value, but number of "
    715                     "entries doesn't match.\n");
    716             psFree(checkList);
    717             return false;
    718         }
    719 
    720         // We don't care if the order matches or not
    721         psVector *check = psVectorAlloc(biassecs->n, PS_TYPE_U8); // Vector to mark regions off
    722         for (int i = 0; i < check->n; i++) {
    723             check->data.U8[i] = 0;
    724         }
    725         psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
    726         psListIterator *checkListIter = psListIteratorAlloc(checkList, PS_LIST_HEAD, false);
    727         psRegion *biassec = NULL; // Region from iteration
    728         while ((biassec = psListGetAndIncrement(biassecsIter))) {
    729             psListIteratorSet(checkListIter, PS_LIST_HEAD);
    730             psString checkRegionString = NULL; // Region string from iteration
    731             int i = 0;              // Counter
    732             while ((checkRegionString = psListGetAndIncrement(checkListIter))) {
    733                 psRegion checkRegion = psRegionFromString(checkRegionString);
    734                 psTrace(__func__, 7, "Checking [%.0f:%.0f,%.0f:%.0f] against "
    735                         "[%.0f:%.0f,%.0f:%.0f]\n", biassec->x0, biassec->x1, biassec->y0,
    736                         biassec->y1, checkRegion.x0, checkRegion.x1, checkRegion.y0,
    737                         checkRegion.y1);
    738                 if (COMPARE_REGIONS(biassec, &checkRegion)) {
    739                     check->data.U8[i] = 1;
    740                     i++;
    741                     break;
    742                 }
    743                 i++;
    744             }
    745         }
    746 
    747         bool allMatch = true;           // Does everything match?
    748         for (int i = 0; i < check->n; i++) {
    749             if (check->data.U8[i] == 0) {
    750                 psError(PS_ERR_IO, true, "Target CELL.BIASSEC is specified by value, but values "
    751                         "don't match.\n");
    752                 allMatch = false;
    753             }
    754         }
    755         // Clean up
    756         psFree(checkListIter);
    757         psFree(checkList);
    758         psFree(biassecsIter);
    759         psFree(check);
    760 
    761         return allMatch;
    762     }
    763 
    764     if (strcasecmp(source, "HEADER") == 0) {
    765         psString keywordsString = psMetadataLookupStr(NULL, cell->config, "CELL.BIASSEC");
    766         psList *keywords = psStringSplit(keywordsString, " ,;");
    767         if (biassecs->n != keywords->n) {
    768             psError(PS_ERR_IO, true, "Target CELL.BIASSEC is sepcified by headers, but the number "
    769                     "of headers doesn't match.\n");
    770             psFree(keywords);
    771             return false;
    772         }
    773 
    774         psMetadata *header = NULL; // The FITS header
    775         if (cell->hdu) {
    776             header = cell->hdu->header;
    777         } else if (chip->hdu) {
    778             header = chip->hdu->header;
    779         } else if (fpa->hdu) {
    780             header = fpa->hdu->header;
    781         }
    782         if (! header) {
    783             psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
    784             psFree(keywords);
    785             return false;
    786         }
    787 
    788         psListIterator *keywordsIter = psListIteratorAlloc(keywords, PS_LIST_HEAD, false);
    789         psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false);
    790         psString keyword = NULL; // Header keyword from list
    791         while ((keyword = psListGetAndIncrement(keywordsIter))) {
    792             // Update the header
    793             psRegion *biassec = psListGetAndIncrement(biassecsIter);
    794             psString biassecString = psRegionToString(*biassec);
    795             psMetadataAdd(header, PS_LIST_TAIL, keyword, PS_DATA_STRING | PS_META_REPLACE, "Bias section",
    796                           biassecString);
    797             psFree(biassecString);
    798         }
    799         psFree(keywordsIter);
    800         psFree(biassecsIter);
    801         psFree(keywords);
    802 
    803         return true;
    804     }
    805 
    806     psError(PS_ERR_IO, true, "CELL.BIASSEC.SOURCE (%s) is not HEADER or VALUE.\n", source);
    807     return false;
    808 }
    809 
    810 // This function actually does both CELL.XBIN and CELL.YBIN, since if CELL.XBIN and CELL.YBIN are specified by
    811 // the same header, we need to check to update them together.
    812 bool pmConceptWrite_CELL_XBIN(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    813 {
    814     psMetadataItem *xBinItem = psMetadataLookup(cell->concepts, "CELL.XBIN"); // Binning factor in x
    815     psMetadataItem *yBinItem = psMetadataLookup(cell->concepts, "CELL.YBIN"); // Binning factor in y
    816 
    817     const psMetadata *camera = fpa->camera;
    818     psMetadata *translation = psMetadataLookupMD(NULL, camera, "TRANSLATION");
    819     psString xKeyword = psMetadataLookupStr(NULL, translation, "CELL.XBIN");
    820     psString yKeyword = psMetadataLookupStr(NULL, translation, "CELL.YBIN");
    821     if (strlen(xKeyword) > 0 && strcasecmp(xKeyword, yKeyword) != 0) {
    822         pmConceptWriteToHeader(fpa, chip, cell, xBinItem);
    823         xBinItem = NULL;
    824     }
    825     if (strlen(yKeyword) > 0 && strcasecmp(xKeyword, yKeyword) != 0) {
    826         pmConceptWriteToHeader(fpa, chip, cell, yBinItem);
    827         yBinItem = NULL;
    828     }
    829     if (strlen(xKeyword) > 0 && strlen(yKeyword) > 0 && strcasecmp(xKeyword, yKeyword) == 0) {
     426psMetadataItem *pmConceptParse_CELL_Positions(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
     427{
     428    assert(concept);
     429    assert(cameraFormat);
     430
     431    if (concept->type != PS_TYPE_S32) {
     432        psError(PS_ERR_IO, true, "Concept %s is not of type S32, as expected.\n", pattern->name);
     433        return NULL;
     434    }
     435    int offset = concept->data.S32;
     436    offset += fortranCorr(cameraFormat, pattern->name);
     437    return psMetadataItemAllocS32(pattern->name, pattern->comment, offset);
     438}
     439
     440
     441
     442psMetadataItem *pmConceptFormat_CELL_TRIMSEC(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
     443{
     444    assert(concept);
     445
     446    psRegion *trimsec = concept->data.V; // The trimsec region
     447    psString trimsecString = psRegionToString(*trimsec);
     448    psMetadataItem *formatted = psMetadataItemAllocStr(trimsecItem->name, trimsecItem->comment,
     449                                trimsecString);
     450    psFree(trimsecString);
     451    return formatted;
     452}
     453
     454
     455bool pmConceptFormat_CELL_BIASSEC(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
     456{
     457    psList *biassecs = concept->data.V; // The biassecs region list
     458    psString biassecString = NULL;      // String containing the biassecs
     459    psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
     460    psRegion *region = NULL;            // Region from iteration
     461    bool first = true;                  // Are we on the first one?
     462    while ((region = psListGetAndIncrement(biassecsIter))) {
     463        psString regionString = psRegionToString(*region); // The string region "[x0:x1,y0:y1]"
     464        if (first) {
     465            psStringAppend(&biassecString, "%s", regionString);
     466            first = false;
     467        } else {
     468            psStringAppend(&biassecString, ";%s", regionString); // Put in a semi-colon
     469        }
     470        psFree(regionString);
     471    }
     472    psFree(biassecsIter);
     473    psMetadataItem *formatted = psMetadataItemAllocStr(concept->name, concept->comment, biassecString);
     474    psFree(biassecString);              // Drop reference
     475    return formatted;
     476}
     477
     478
     479// This function actually does both CELL.XBIN and CELL.YBIN if CELL.XBIN and CELL.YBIN are specified by the
     480// same header.
     481psMetadataItem *pmConceptFormat_CELL_XBIN(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
     482{
     483    assert(concept);
     484
     485    psMetadata *translation = psMetadataLookupMD(NULL, cameraFormat, "TRANSLATION");
     486    bool xBinOK = true, yBinOK = true;  // Status of MD lookups
     487    psString xKeyword = psMetadataLookupStr(&xBinOK, translation, "CELL.XBIN");
     488    psString yKeyword = psMetadataLookupStr(&yBinOK, translation, "CELL.YBIN");
     489    if (xBinOK && yBinOK && strlen(xKeyword) > 0 && strlen(yKeyword) > 0 &&
     490            strcasecmp(xKeyword, yKeyword) == 0) {
     491        psMetadataItem *yBinItem = psMetadataLookup(cell->concepts, "CELL.YBIN"); // Binning factor in y
    830492        psString binString = psStringCopy("");
    831         psStringAppend(&binString, "%d,%d", xBinItem->data.S32, yBinItem->data.S32);
    832         psMetadataItem *binItem = psMetadataItemAllocStr(xKeyword, "Binning factor in x and y", binString);
     493        psStringAppend(&binString, "%d,%d", concept->data.S32, yBinItem->data.S32);
     494        psMetadataItem *binItem = psMetadataItemAllocStr(concept->name, concept->comment, binString);
    833495        psFree(binString);
    834         psMetadata *header = NULL; // The FITS header
    835         if (cell->hdu) {
    836             header = cell->hdu->header;
    837         } else if (chip->hdu) {
    838             header = chip->hdu->header;
    839         } else if (fpa->hdu) {
    840             header = fpa->hdu->header;
    841         }
    842         if (! header) {
    843             psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
    844             return false;
    845         }
    846         psMetadataAddItem(header, binItem, PS_LIST_TAIL, PS_META_REPLACE);
    847         xBinItem = NULL;
    848         yBinItem = NULL;
    849         psFree(binItem);
    850     }
    851 
    852     // Do it in the usual way if we have to
    853     if (xBinItem) {
    854         pmConceptWriteItem(fpa, chip, cell, db, xBinItem);
    855     }
    856     if (yBinItem) {
    857         pmConceptWriteItem(fpa, chip, cell, db, yBinItem);
    858     }
    859 
    860     return true;
    861 }
    862 
    863 // This is a dummy function, since CELL.YBIN is done by CELL.XBIN
    864 bool pmConceptWrite_CELL_YBIN(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    865 {
    866     return true;
    867 }
    868 
    869 
    870 
    871 bool pmConceptWrite_CELL_TIMESYS(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    872 {
    873     psMetadataItem *sysItem = psMetadataLookup(cell->concepts, "CELL.TIMESYS");
     496        return binItem;
     497    }
     498
     499    // Otherwise, there's no formatting required
     500    return concept;
     501}
     502
     503// Only need to format if both if CELL.XBIN and CELL.YBIN are not specified by the same header.
     504psMetadataItem *pmConceptFormat_CELL_YBIN(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
     505{
     506    assert(concept);
     507
     508    psMetadata *translation = psMetadataLookupMD(NULL, cameraFormat, "TRANSLATION");
     509    bool xBinOK = true, yBinOK = true;  // Status of MD lookups
     510    psString xKeyword = psMetadataLookupStr(&xBinOK, translation, "CELL.XBIN");
     511    psString yKeyword = psMetadataLookupStr(&yBinOK, translation, "CELL.YBIN");
     512    if (xBinOK && yBinOK && strlen(xKeyword) > 0 && strlen(yKeyword) > 0 &&
     513            strcasecmp(xKeyword, yKeyword) == 0) {
     514        // Censor this --- it's already done (though no harm if it's done twice
     515        return NULL;
     516    }
     517
     518    // No formatting required
     519    return concept;
     520}
     521
     522
     523psMetadataItem *pmConceptFormat_CELL_TIMESYS(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
     524{
    874525    psString sys = NULL;            // String to store
    875     switch (sysItem->data.S32) {
     526    switch (concept->data.S32) {
    876527    case PS_TIME_TAI:
    877528        sys = psStringCopy("TAI");
     
    889540        sys = psStringCopy("Unknown");
    890541    }
    891     psMetadataItem *newItem = psMetadataItemAllocStr("CELL.TIMESYS", "Time system", sys);
    892     bool success = pmConceptWriteItem(fpa, chip, cell, db, newItem);
    893     psFree(newItem);
     542    psMetadataItem *newItem = psMetadataItemAllocStr(concept->name, concept->comment, sys);
    894543    psFree(sys);
    895544
     
    897546}
    898547
    899 bool pmConceptWrite_CELL_TIME(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    900 {
    901     psMetadataItem *timeItem = psMetadataLookup(cell->concepts, "CELL.TIME");
    902     psTime *time = timeItem->data.V; // The time
    903     psString dateTimeString = psTimeToISO(time); // String representation
    904 
    905     psMetadata *formats = psMetadataLookupMD(NULL, fpa->camera, "FORMATS");
     548psMetadataItem *pmConceptFormat_CELL_TIME(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
     549{
     550    psTime *time = concept->data.V;     // The time
     551    psMetadata *formats = psMetadataLookupMD(NULL, cameraFormat, "FORMATS");
    906552    psString format = psMetadataLookupStr(NULL, formats, "CELL.TIME");
    907553
    908     if (strlen(format) == 0) {
    909         // No format specified --> do it in the usual way (maybe it's a DB lookup)
    910         psMetadataItem *newTimeItem = psMetadataItemAllocStr("CELL.TIME", "Time of observation",
    911                                       dateTimeString);
    912         bool success = pmConceptWriteItem(fpa, chip, cell, db, newTimeItem);
    913         psFree(newTimeItem);
     554    if (strlen(format) == 0 || strcasecmp(format, "ISO") == 0) {
     555        psString dateTimeString = psTimeToISO(time); // String representation
     556        psMetadataItem *item = psMetadataItemAllocStr(concept->name, concept->comment, dateTimeString);
    914557        psFree(dateTimeString);
    915         return success;
    916     }
    917     if (strcasecmp(format, "ISO") == 0) {
    918         // dateTimeString contains an ISO time
    919         psMetadataItem *newTimeItem = psMetadataItemAllocStr("CELL.TIME", "Time of observation",
    920                                       dateTimeString);
    921         bool success = pmConceptWriteItem(fpa, chip, cell, db, newTimeItem);
    922         psFree(newTimeItem);
     558        return item;
     559    }
     560    if (strstr(format, "SEPARATE")) {
     561        // We're working with two separate headers --- construct a list with the date and time separately
     562        psString dateTimeString = psTimeToISO(time); // String representation
     563        psList *dateTime = psStringSplit(dateTimeString, "T");
    923564        psFree(dateTimeString);
    924         return success;
    925     }
    926     if (strstr(format, "SEPARATE")) {
    927         // We're working with two separate headers
    928         psMetadata *header = NULL; // The FITS header
    929         if (cell->hdu) {
    930             header = cell->hdu->header;
    931         } else if (chip->hdu) {
    932             header = chip->hdu->header;
    933         } else if (fpa->hdu) {
    934             header = fpa->hdu->header;
    935         }
    936         if (! header) {
    937             psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
    938             psFree(dateTimeString);
    939             return false;
    940         }
    941 
    942         // Get the headers
    943         const psMetadata *camera = fpa->camera;
    944         psMetadata *translation = psMetadataLookupMD(NULL, camera, "TRANSLATION");
    945         psString keywords = psMetadataLookupStr(NULL, translation, "CELL.TIME");
    946         psList *dateTimeKeywords = psStringSplit(keywords, " ,;");
    947         psString dateKeyword = psListGet(dateTimeKeywords, PS_LIST_HEAD);
    948         psString timeKeyword = psListGet(dateTimeKeywords, PS_LIST_TAIL);
    949 
    950         psList *dateTime = psStringSplit(dateTimeString, " T"); // Find the middle T
    951         psString dateString = psListGet(dateTime, PS_LIST_HEAD);
    952         psString timeString = psListGet(dateTime, PS_LIST_TAIL);
    953 
     565        psString dateString = psListGet(dateTime, PS_LIST_HEAD); // The date string
     566        psString timeString = psListGet(dateTime, PS_LIST_TAIL); // The time string
     567
     568        // Need to format the strings....
    954569        // XXX: Couldn't be bothered doing these right now
    955570        if (strstr(format, "PRE2000")) {
    956571            psError(PS_ERR_IO, true, "Don't you realise it's the twenty-first century?\n");
    957             psFree(dateTimeString);
    958             // Should free other stuff, but this is work in progress
    959572            return false;
    960573        }
    961574        if (strstr(format, "BACKWARDS")) {
    962575            psError(PS_ERR_IO, true, "You want it BACKWARDS?  Not right now, thanks.\n");
    963             psFree(dateTimeString);
    964             // Should free other stuff, but this is work in progress
    965576            return false;
    966577        }
    967578
    968         bool success = true;
    969         psMetadataItem *dateItem = psMetadataItemAllocStr(dateKeyword, "Date of observation", dateString);
    970         psMetadataItem *timeItem = psMetadataItemAllocStr(timeKeyword, "Time of observation", timeString);
    971         success = psMetadataAddItem(header, dateItem, PS_LIST_TAIL, PS_META_REPLACE) &&
    972                   psMetadataAddItem(header, timeItem, PS_LIST_TAIL, PS_META_REPLACE);
    973 
    974         psFree(dateTimeKeywords);
    975         psFree(dateTime);
    976         psFree(dateItem);
    977         psFree(timeItem);
    978 
    979         return success;
     579        psMetadataItem *dateItem = psMetadataItemAllocStr(concept->name, "The date of observation",
     580                                   dateString);
     581        psMetadataItem *timeItem = psMetadataItemAllocStr(concept->name, "The time of observation",
     582                                   timeString);
     583        psFree(dateString);
     584        psFree(timeString);
     585
     586        psListRemove(dateTime, PS_LIST_HEAD);
     587        psListRemove(dateTime, PS_LIST_HEAD);
     588        psListAdd(dateTime, PS_LIST_HEAD, dateItem);
     589        psListAdd(dateTime, PS_LIST_TAIL, timeItem);
     590
     591        psMetadataItem *item = psMetadataItemAllocPtr(concept->name, PS_DATA_LIST, concept->comment,
     592                               dateTime);
     593        return item;
    980594    }
    981595    if (strcasecmp(format, "MJD") == 0) {
    982596        double mjd = psTimeToMJD(time);
    983         psMetadataItem *newTimeItem = psMetadataItemAllocF64("CELL.TIME", "MJD of observation", mjd);
    984         bool success = pmConceptWriteItem(fpa, chip, cell, db, newTimeItem);
    985         psFree(newTimeItem);
    986         psFree(dateTimeString);
    987         return success;
     597        return psMetadataItemAllocF64(concept->name, concept->comment, mjd);
    988598    }
    989599    if (strcasecmp(format, "JD") == 0) {
    990600        double jd = psTimeToMJD(time);
    991         psMetadataItem *newTimeItem = psMetadataItemAllocF64("CELL.TIME", "JD of observation", jd);
    992         bool success = pmConceptWriteItem(fpa, chip, cell, db, newTimeItem);
    993         psFree(newTimeItem);
    994         psFree(dateTimeString);
    995         return success;
    996     }
    997 
    998     psError(PS_ERR_IO, true, "Not sure how to write concept CELL.TIME (%s)\n", dateTimeString);
    999     return false;
    1000 }
    1001 
    1002 bool pmConceptWrite_CELL_X0(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    1003 {
    1004     psMetadataItem *x0item = psMetadataLookup(cell->concepts, "CELL.X0");
    1005     psMetadataItem *newItem = psMetadataItemAllocS32("CELL.X0", "Position of (0,0) on the chip",
    1006                               x0item->data.S32 - fortranCorr(fpa, "CELL.X0"));
    1007     return pmConceptWriteItem(fpa, chip, cell, db, newItem);
    1008 }
    1009 
    1010 bool pmConceptWrite_CELL_Y0(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db)
    1011 {
    1012     psMetadataItem *y0item = psMetadataLookup(cell->concepts, "CELL.Y0");
    1013     psMetadataItem *newItem = psMetadataItemAllocS32("CELL.Y0", "Position of (0,0) on the chip",
    1014                               y0item->data.S32 - fortranCorr(fpa, "CELL.Y0"));
    1015     return pmConceptWriteItem(fpa, chip, cell, db, newItem);
    1016 }
    1017 
     601        return psMetadataItemAllocF64("CELL.TIME", "JD of observation", jd);
     602    }
     603
     604    psError(PS_ERR_IO, true, "Not sure how to format concept CELL.TIME (%s)\n", dateTimeString);
     605    return NULL;
     606}
     607
     608psMetadataItem *pmConceptFormat_CELL_Positions(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell *db)
     609{
     610    assert(concept);
     611    assert(cameraFormat);
     612
     613    if (concept->type != PS_TYPE_S32) {
     614        psError(PS_ERR_IO, true, "Concept %s is not of type S32, as expected.\n", pattern->name);
     615        return NULL;
     616    }
     617    int offset = concept->data.S32;
     618    offset -= fortranCorr(cameraFormat, concept->name);
     619    return psMetadataItemAllocS32(concept->name, concept->comment, offset);
     620}
     621
  • branches/rel10_ifa/psModules/src/astrom/pmConceptsStandard.h

    r6448 r6570  
    55#include "pmFPA.h"
    66
    7 psMetadataItem *pmConceptRead_FPA_RA(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    8 psMetadataItem *pmConceptRead_FPA_DEC(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    9 bool pmConceptWrite_FPA_RA(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    10 bool pmConceptWrite_FPA_DEC(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    11 psMetadataItem *pmConceptRead_CELL_TRIMSEC(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    12 psMetadataItem *pmConceptRead_CELL_BIASSEC(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    13 psMetadataItem *pmConceptRead_CELL_XBIN(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    14 psMetadataItem *pmConceptRead_CELL_YBIN(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    15 psMetadataItem *pmConceptRead_CELL_TIMESYS(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    16 psMetadataItem *pmConceptRead_CELL_TIME(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    17 psMetadataItem *pmConceptRead_CELL_X0(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    18 psMetadataItem *pmConceptRead_CELL_Y0(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    19 bool pmConceptWrite_CELL_TRIMSEC(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    20 bool pmConceptWrite_CELL_BIASSEC(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    21 bool pmConceptWrite_CELL_XBIN(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    22 bool pmConceptWrite_CELL_YBIN(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    23 bool pmConceptWrite_CELL_TIMESYS(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    24 bool pmConceptWrite_CELL_TIME(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    25 bool pmConceptWrite_CELL_X0(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
    26 bool pmConceptWrite_CELL_Y0(pmFPA *fpa, pmChip *chip, pmCell *cell, psDB *db);
     7psMetadataItem *pmConceptParse_FPA_Coords(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
     8psMetadataItem *pmConceptFormat_FPA_Coords(psMetadataItem *concept, psMetadataItem *pattern, pmConceptSpec *spec, psMetadata *cameraFormat);
     9psMetadataItem *pmConceptParse_CELL_TRIMSEC(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
     10psMetadataItem *pmConceptParse_CELL_BIASSEC(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
     11psMetadataItem *pmConceptParse_CELL_BINNING(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
     12psMetadataItem *pmConceptParse_CELL_TIMESYS(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
     13psMetadataItem *pmConceptParse_CELL_TIME(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
     14psMetadataItem *pmConceptParse_CELL_Positions(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
     15psMetadataItem *pmConceptFormat_CELL_TRIMSEC(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
     16bool pmConceptFormat_CELL_BIASSEC(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
     17psMetadataItem *pmConceptFormat_CELL_XBIN(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
     18psMetadataItem *pmConceptFormat_CELL_YBIN(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
     19psMetadataItem *pmConceptFormat_CELL_TIMESYS(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
     20psMetadataItem *pmConceptFormat_CELL_TIME(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
     21psMetadataItem *pmConceptFormat_CELL_Positions(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell *db);
    2722
    2823#endif
  • branches/rel10_ifa/psModules/src/astrom/pmConceptsWrite.c

    r6552 r6570  
    77#include "psAdditionals.h"
    88
     9//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     10// File-static functions
     11//////////////////////////////////////////////////////////////////////////////////////////////////////////////
    912
    1013static bool compareConcepts(psMetadataItem *item1, // First item to compare
     
    6770}
    6871
     72// Format a single concept
     73static psMetadataItem *conceptFormat(pmConceptSpec *spec, // The concept specification
     74                                     psMetadataItem *concept, // The concept to parse
     75                                     psMetadata *cameraFormat, // The camera format
     76                                     pmFPA *fpa, // The FPA
     77                                     pmChip *chip, // The chip
     78                                     pmCell *cell // The cell
     79                                    )
     80{
     81    if (concept) {
     82        psMetadataItem *formatted = NULL;  // The formatted concept
     83        if (spec->format) {
     84            formatted = spec->format(concept, cameraFormat, fpa, chip, cell);
     85        } else {
     86            formatted = psMemIncrRefCounter(concept);
     87        }
     88
     89        return formatted;
     90    }
     91}
     92
     93static bool writeHeader(pmHDU *hdu,     // HDU for which to add to the header
     94                        const char *keyword, // Keyword to add
     95                        psMetadataItem *item // Item to add to the header
     96                       )
     97{
     98    switch (item->type) {
     99    case PS_DATA_STRING:
     100        return psMetadataAddStr(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
     101                                item->data.V);
     102    case PS_DATA_S32:
     103        return psMetadataAddS32(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
     104                                item->data.S32);
     105    case PS_DATA_F32:
     106        return psMetadataAddF32(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
     107                                item->data.F32);
     108    case PS_DATA_F64:
     109        return psMetadataAddF64(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
     110                                item->data.F64);
     111    default:
     112        psLogMsg(__func__, PS_LOG_WARN, "Type of %s is not suitable for a FITS header --- not added.\n",
     113                 item->name);
     114        return false;
     115    }
     116}
     117
     118
     119//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     120// Public functions
     121//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     122
     123bool pmConceptsWriteToCamera(psMetadata *specs, // The concept specifications
     124                             pmCell *cell,   // The cell
     125                             psMetadata *concepts // The concepts
     126                            )
     127{
     128    if (cell) {
     129        pmHDU *hdu = getLowestHDU(NULL, NULL, cell); // The HDU at the lowest level
     130        psMetadata *cameraFormat = hdu->format; // The camera format
     131        psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
     132        psMetadataItem *specItem = NULL;    // Item from the specs metadata
     133        while ((specItem = psMetadataGetAndIncrement(specsIter))) {
     134            pmConceptSpec *spec = specItem->data.V; // The specification
     135            psString name = specItem->name; // The concept name
     136            psMetadataItem *cameraItem = psMetadataLookup(cell->config, name); // The concept from the camera,
     137            // or NULL
     138            if (cameraItem) {
     139                // Grab the concept
     140                psMetadataItem *conceptItem = psMetadataLookup(concepts, name); // The concept
     141                // Formatted version
     142                psMetadataItem *formatted = conceptFormat(spec, conceptItem, cameraFormat, NULL, NULL, cell);
     143                psString nameSource = NULL; // String with the concept name and ".SOURCE" added
     144                psStringAppend(nameSource, "%s.SOURCE", name);
     145                bool mdok = true;       // Status of MD lookup
     146                psString source = psMetadataLookupStr(&mdok, cell->config, nameSource); // The source
     147                if (mdok && strlen(source) > 0) {
     148                    if (strcasecmp(source, "HEADER") == 0) {
     149                        if (cameraItem->type != PS_DATA_STRING) {
     150                            psLogMsg(__func__, PS_LOG_WARN, "Concept %s is specified by header, but is not "
     151                                     "of type STR --- ignored.\n", conceptItem->name)
     152                            continue;
     153                        }
     154                        writeHeader(hdu, cameraItem->data.V, formatted);
     155                        continue;
     156                    }
     157                    if (strcasecmp(source, "VALUE") == 0) {
     158                        if (! compareConcepts(cameraItem, formatted)) {
     159                            psLogMsg(__func__, PS_LOG_WARN, "Concept %s is specified by value in the camera "
     160                                     "format, but the values don't match.\n");
     161                        }
     162                        continue;
     163                    }
     164                    psLogMsg(__func__, PS_LOG_WARN, "Concept source %s isn't HEADER or VALUE --- can't "
     165                             "write\n", nameSource);
     166                    continue;
     167                }
     168                // Assume it's specified by value
     169                if (! compareConcepts(cameraItem, formatted)) {
     170                    psLogMsg(__func__, PS_LOG_WARN, "Concept %s is specified by value in the camera "
     171                             "format, but the values don't match.\n");
     172                }
     173            }
     174
     175        }
     176        psFree(specsIter);
     177        return true;
     178    }
     179    return false;
     180}
     181
     182
     183bool pmConceptsWriteToDefault(psMetadata *specs, // The concept specifications
     184                              pmFPA *fpa, // The FPA
     185                              pmChip *chip, // The chip
     186                              pmCell *cell, // The cell
     187                              psMetadata *concepts // The concepts
     188                             )
     189{
     190    bool mdok = true;               // Status of MD lookup
     191    psMetadata *defaults = psMetadataLookupMD(&mdok, cameraFormat, "DEFAULTS"); // The DEFAULTS spec
     192    if (mdok && defaults) {
     193        pmHDU *hdu = getLowestHDU(fpa, chip, cell); // The HDU at the lowest level
     194        psMetadata *cameraFormat = hdu->format; // The camera format
     195        psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
     196        psMetadataItem *specItem = NULL;    // Item from the specs metadata
     197        while ((specItem = psMetadataGetAndIncrement(specsIter))) {
     198            pmConceptSpec *spec = specItem->data.V; // The specification
     199            psString name = specItem->name; // The concept name
     200            psMetadataItem *defaultItem = psMetadataLookup(defaults, name); // The item from the DEFAULTS
     201            if (defaultItem) {
     202                psMetadataItem *conceptItem = psMetadataLookup(concepts, name); // The item from the concepts
     203                psMetadataItem *formatted = conceptFormat(spec, conceptItem, cameraFormat, fpa, chip, cell);
     204                if (! compareConcepts(cameraItem, formatted)) {
     205                    psLogMsg(__func__, PS_LOG_WARN, "Concept %s is specified by the DEFAULTS in the camera "
     206                             "format, but the values don't match.\n");
     207                }
     208            }
     209        }
     210        psFree(specsIter);
     211        return true;
     212    }
     213    return false;
     214}
     215
     216// XXX need to write multiple headers if I get a list
     217bool pmConceptsWriteToHeader(psMetadata *specs, // The concept specifications
     218                             pmFPA *fpa, // The FPA
     219                             pmChip *chip, // The chip
     220                             pmCell *cell, // The cell
     221                             psMetadata *concepts // The concepts
     222                            )
     223{
     224    bool mdok = true;               // Status of MD lookup
     225    psMetadata *translation = psMetadataLookupMD(&mdok, cameraFormat, "TRANSLATION"); // The TRANSLATION spec
     226    if (mdok && translation) {
     227        pmHDU *hdu = getLowestHDU(fpa, chip, cell); // The HDU at the lowest level
     228        psMetadata *cameraFormat = hdu->format; // The camera format
     229        psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
     230        psMetadataItem *specItem = NULL;    // Item from the specs metadata
     231        while ((specItem = psMetadataGetAndIncrement(specsIter))) {
     232            pmConceptSpec *spec = specItem->data.V; // The specification
     233            psString name = specItem->name; // The concept name
     234            psMetadataItem *headerItem = psMetadataLookup(translation, name); // The item from the TRANSLATION
     235            if (headerItem) {
     236                if (headerItem->type != PS_DATA_STRING) {
     237                    psLogMsg(__func__, PS_LOG_WARN, "TRANSLATION keyword for concept %s isn't of type STR ---"
     238                             " ignored.", name);
     239                    continue;
     240                }
     241                psMetadataItem *conceptItem = psMetadataLookup(concepts, name); // The item from the concepts
     242                psMetadataItem *formatted = conceptFormat(spec, conceptItem, cameraFormat, fpa, chip, cell);
     243                psList *keywords = psStringSplit(headerItem->data.V, " ,;"); // List of header keywords
     244                if (formatted->type == PS_DATA_LIST) {
     245                    psList *values = formatted->data.V; // The values for the headers
     246                    if (values->n != keywords->n) {
     247                        psLogMsg(__func__, PS_LOG_WARN, "Number of headers specified does not match number "
     248                                 "of values for concept %s.\n", name);
     249                    }
     250                    psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false); // Iterator
     251                    psListIterator *keywordsIter = psListIteratorAlloc(keywords, PS_LIST_HEAD, false);
     252                    psMetadataItem *valuesItem = NULL; // Item from list
     253                    while ((valuesItem = psListGetAndIncrement(valuesIter))) {
     254                        psString keyword = psListGetAndIncrement(keywordsIter); // Keyword from the list
     255                        if (strlen(keyword) > 0) {
     256                            writeHeader(hdu, keyword, formatted);
     257                        }
     258                    }
     259                    psFree(valuesIter);
     260                    psFree(keywordsIter);
     261                } else {
     262                    psString keyword = psListGet(keywords, PS_LIST_HEAD); // The keyword
     263                    writeHeader(hdu, keyword, formatted);
     264                }
     265            }
     266        }
     267        psFree(specsIter);
     268        return true;
     269    }
     270    return false;
     271}
     272
     273
     274bool pmConceptsWriteToDatabase(psMetadata *specs, // The concept specifications
     275                               pmFPA *fpa, // The FPA
     276                               pmChip *chip, // The chip
     277                               pmCell *cell, // The cell
     278                               psDB *db,// The database handle
     279                               psMetadata *concepts // The concepts
     280                              )
     281{
     282    bool mdok = true;               // Status of MD lookup
     283    psMetadata *database = psMetadataLookupMD(&mdok, cameraFormat, "DATABASE"); // The DATABASE spec
     284    if (mdok && database) {
     285        pmHDU *hdu = getLowestHDU(fpa, chip, cell); // The HDU at the lowest level
     286        psMetadata *cameraFormat = hdu->format; // The camera format
     287        psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
     288        psMetadataItem *specItem = NULL;    // Item from the specs metadata
     289        while ((specItem = psMetadataGetAndIncrement(specsIter))) {
     290            pmConceptSpec *spec = specItem->data.V; // The specification
     291            psString name = specItem->name; // The concept name
     292
     293            psMetadataItem *dbItem = psMetadataLookup(&mdok, database, name); // The item from the DATABASE
     294            if (dbItem) {
     295                if (dbItem->type != PS_DATA_METADATA) {
     296                    psLogMsg(__func__, PS_LOG_WARN, "DATABASE keyword for concept %s isn't of type METADATA "
     297                             "--- ignored.\n", name);
     298                    continue;
     299                }
     300
     301                psMetadataItem *conceptItem = psMetadataLookup(concepts, name); // The item from the concepts
     302                psMetadataItem *formatted = conceptFormat(spec, conceptItem, cameraFormat, fpa, chip, cell);
     303
     304                psMetadata *dbLookup = dbItem->data.V; // How to look up the value of interest
     305                // Name of the table
     306                const char *tableName = psMetadataLookupStr(&mdStatus, dbLookup, "TABLE");
     307                // Name of "where" columns
     308                const char *givenCols = psMetadataLookupStr(&mdStatus, dbLookup, "GIVENDBCOL");
     309                // Values for "where" columns
     310                const char *givenPS = psMetadataLookupStr(&mdStatus, dbLookup, "GIVENPS");
     311
     312                // Now, need to get the "given"s
     313                if (strlen(givenCols) || strlen(givenPS)) {
     314                    psList *cols = psStringSplit(givenCols, ",;"); // List of column names
     315                    psList *values = psStringSplit(givenPS, ",;"); // List of value names for the columns
     316                    psMetadata *selection = psMetadataAlloc(); // The stuff to select in the DB
     317                    if (cols->n != values->n) {
     318                        psLogMsg(__func__, PS_LOG_WARN,
     319                                 "The GIVENDBCOL and GIVENPS entries for %s do not have "
     320                                 "the same number of entries --- ignored.\n", concept);
     321                    } else {
     322                        // Iterators for the lists
     323                        psListIterator *colsIter = psListIteratorAlloc(cols, PS_LIST_HEAD, false);
     324                        psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false);
     325                        char *column = NULL;    // Name of the column
     326                        while ((column = psListGetAndIncrement(colsIter))) {
     327                            char *name = psListGetAndIncrement(valuesIter); // Name for the value
     328                            if (!strlen(column) || !strlen(name)) {
     329                                psLogMsg(__func__, PS_LOG_WARN, "One of the columns or value names for %s is "
     330                                         " empty --- ignored.\n", concept);
     331                            } else {
     332                                // Search for the value name
     333                                psMetadataItem *item = pmConceptReadFromHeader(fpa, chip, cell, name);
     334                                if (! item) {
     335                                    item = pmConceptReadFromDefault(fpa, chip, cell, name);
     336                                }
     337                                if (! item) {
     338                                    psLogMsg(__func__, PS_LOG_ERROR,
     339                                             "Unable to find the value name %s for DB "
     340                                             " lookup on %s --- ignored.\n", name, concept);
     341                                } else {
     342                                    // We need to create a new psMetadataItem.  I don't think we can't simply
     343                                    // hack the existing one, since that could conceivably cause memory leaks
     344                                    psMetadataAddItem(selection, formatted, PS_LIST_TAIL, PS_META_REPLACE);
     345                                    psFree(formatted);
     346                                }
     347                            }
     348                            psFree(name);
     349                            psFree(column);
     350                        } // Iterating through the columns
     351                        psFree(colsIter);
     352                        psFree(valuesIter);
     353
     354                        // Check first to make sure we're only going to touch one row
     355                        psArray *dbResult = psDBSelectRows(db, tableName, selection, 2); // Lookup result
     356                        // Note that we use limit=2 in order to test if there are multiple rows returned
     357                        if (! dbResult || dbResult->n == 0) {
     358                            psLogMsg(__func__, PS_LOG_WARN, "Unable to find any rows in DB for %s --- "
     359                                     "ignored\n", concept->name);
     360                            return false;
     361                        } else {
     362                            if (dbResult->n > 1) {
     363                                psLogMsg(__func__, PS_LOG_WARN, "Multiple rows returned in DB lookup for %s "
     364                                         "--- ignored.\n", concept->name);
     365                            }
     366                            // Update the DB
     367                            psMetadata *update = psMetadataAlloc();
     368                            psMetadataAddItem(update, concept, PS_LIST_HEAD, 0);
     369                            psDBUpdateRows(db, tableName, selection, update);
     370                            psFree(update);
     371                            return true;
     372                        }
     373                    }
     374                    psFree(cols);
     375                    psFree(values);
     376                } // Doing the "given"s.
     377            }
     378        }
     379        psFree(specsIter);
     380        return true;
     381    }
     382    return false;
     383}
     384
     385
     386
     387
     388#if 0
    69389
    70390// Well, not really "write", but check to make sure it's there and matches
     
    368688}
    369689
     690#endif
Note: See TracChangeset for help on using the changeset viewer.