IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Feb 26, 2009, 2:45:31 PM (17 years ago)
Author:
Paul Price
Message:

Reorganised the concepts files, moving the Write functions into pmConceptsWrite, and the Read functions into pmConceptsRead, which sort of makes sense. Added new type of concept property: copy. Most concepts use the default copy method (which is to simply copy the item), but for the TIMESYS concepts, we want to ensure that the target is of the correct type --- if it's already set (e.g., via the DEFAULTS), then we don't copy it. This will allow us to set the target TIMESYS using the DEFAULTS in the camera format, and have the output time be adjusted appropriately. Without this, the TIMESYS is simply copied, and specifying the TIMESYS in the DEFAULTS results in a warning ('values don't match'). Removed pmFPACopyConcepts, since it is pretty much the same as pmConceptsCopyFPA.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/psModules/src/concepts/pmConcepts.c

    r20951 r22699  
    1919#include "pmConceptsWrite.h"
    2020#include "pmConceptsStandard.h"
    21 #include "pmConceptsUpdate.h"
    2221
    2322static bool conceptsInitialised = false;// Have concepts been read?
     
    4948
    5049pmConceptSpec *pmConceptSpecAlloc(psMetadataItem *blank, pmConceptParseFunc parse,
    51                                   pmConceptFormatFunc format, bool required)
     50                                  pmConceptFormatFunc format, pmConceptCopyFunc copy,
     51                                  bool required)
    5252{
    5353    pmConceptSpec *spec = psAlloc(sizeof(pmConceptSpec));
     
    5757    spec->parse = parse;
    5858    spec->format = format;
     59    spec->copy = copy;
    5960    spec->required = required;
    6061
     
    8687}
    8788
     89psMetadata *pmConceptsSpecs(pmFPALevel level)
     90{
     91    if (!conceptsInitialised) {
     92        pmConceptsInit();
     93    }
     94
     95    // Get the appropriate concepts
     96    psMetadata *concepts = conceptsFromLevel(level); // Metadata of concepts specs
     97    if (!concepts) {
     98        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Invalid concept level provided: %d\n", level);
     99        return NULL;
     100    }
     101
     102    return concepts;
     103}
     104
    88105bool pmConceptGetRequired(const char *name, pmFPALevel level)
    89106{
     
    129146
    130147bool pmConceptRegister(psMetadataItem *blank, pmConceptParseFunc parse,
    131                         pmConceptFormatFunc format, bool required, pmFPALevel level)
     148                       pmConceptFormatFunc format, pmConceptCopyFunc copy,
     149                       bool required, pmFPALevel level)
    132150{
    133151    PS_ASSERT_PTR_NON_NULL(blank, false);
     
    144162    }
    145163
    146     pmConceptSpec *spec = pmConceptSpecAlloc(blank, parse, format, required); // The concept specification
     164    pmConceptSpec *spec = pmConceptSpecAlloc(blank, parse, format, copy, required); // Concept specification
    147165    psMetadataAdd(target, PS_LIST_TAIL, blank->name, PS_DATA_UNKNOWN | PS_META_REPLACE,
    148166                  "Concepts specification", spec);
     
    188206
    189207
    190 
    191 // Read all registered concepts for the specified level
    192 static bool conceptsRead(psMetadata **specs, // One of the concepts specifications
    193                          pmFPA *fpa,    // The FPA
    194                          pmChip *chip,  // The chip
    195                          pmCell *cell, // The cell
    196                          unsigned int *read,     // What's already been read
    197                          pmConceptSource source, // The source of the concepts to read
    198                          pmConfig *config, // Configuration
    199                          psMetadata *target // Place into which to read the concepts
    200                         )
    201 {
    202     assert(specs);
    203     assert(read);
    204     assert(target);
    205 
    206     if (!conceptsInitialised) {
    207         pmConceptsInit();
    208     }
    209 
    210     // At least one HDU is required for the reading functions
    211     pmHDU *hduLow = pmHDUGetLowest(fpa, chip, cell); // Lowest HDU.
    212     if (!hduLow) {
    213         // Can't do anything --- don't record any success, but don't return an error either
    214         return true;
    215     }
    216     pmHDU *hduHigh = pmHDUGetHighest(fpa, chip, cell); // Highest HDU
    217 
    218     if (cell && (cell->conceptsRead == PM_CONCEPT_SOURCE_NONE)) {
    219         pmConceptsBlankCell(cell);
    220         cell->conceptsRead = PM_CONCEPT_SOURCE_BLANK;
    221     }
    222     if (chip && (chip->conceptsRead == PM_CONCEPT_SOURCE_NONE)) {
    223         pmConceptsBlankChip(chip);
    224         chip->conceptsRead = PM_CONCEPT_SOURCE_BLANK;
    225     }
    226     if (fpa && (fpa->conceptsRead == PM_CONCEPT_SOURCE_NONE)) {
    227         pmConceptsBlankFPA(fpa);
    228         fpa->conceptsRead = PM_CONCEPT_SOURCE_BLANK;
    229     }
    230 
    231     bool success = true;                // Success in reading concepts?
    232     if (source & PM_CONCEPT_SOURCE_CELLS && !(*read & PM_CONCEPT_SOURCE_CELLS) && cell) {
    233         if (p_pmConceptsReadFromCells(target, *specs, cell)) {
    234             *read |= PM_CONCEPT_SOURCE_CELLS;
    235         } else {
    236             psError(PS_ERR_UNKNOWN, false, "Error reading concepts from camera configuration.\n");
    237             success = false;
    238         }
    239     }
    240 
    241     if (source & PM_CONCEPT_SOURCE_DEFAULTS && !(*read & PM_CONCEPT_SOURCE_DEFAULTS)) {
    242         if (p_pmConceptsReadFromDefaults(target, *specs, fpa, chip, cell)) {
    243             *read |= PM_CONCEPT_SOURCE_DEFAULTS;
    244         } else {
    245             psError(PS_ERR_UNKNOWN, false, "Error reading concepts from defaults.\n");
    246             success = false;
    247         }
    248     }
    249 
    250     if (source & PM_CONCEPT_SOURCE_PHU && !(*read & PM_CONCEPT_SOURCE_PHU) && hduHigh->header) {
    251         if (p_pmConceptsReadFromHeader(target, *specs, fpa, chip, cell)) {
    252             *read |= PM_CONCEPT_SOURCE_PHU;
    253         } else {
    254             psError(PS_ERR_UNKNOWN, false, "Error reading concepts from PHU.\n");
    255             success = false;
    256         }
    257     }
    258 
    259     // If there are multiple HDUs, then it may be that one of them hasn't been read yet (hdu->header not set)
    260     if (source & PM_CONCEPT_SOURCE_HEADER && !(*read & PM_CONCEPT_SOURCE_HEADER) &&
    261         hduLow != hduHigh && hduLow->header) {
    262         if (p_pmConceptsReadFromHeader(target, *specs, fpa, chip, cell)) {
    263             *read |= PM_CONCEPT_SOURCE_HEADER;
    264         } else {
    265             psError(PS_ERR_UNKNOWN, false, "Error reading concepts from header.\n");
    266             success = false;
    267         }
    268     }
    269 
    270     #ifdef HAVE_PSDB
    271     if (source & PM_CONCEPT_SOURCE_DATABASE && !(*read & PM_CONCEPT_SOURCE_DATABASE)) {
    272         if (p_pmConceptsReadFromDatabase(target, *specs, fpa, chip, cell, config)) {
    273             *read |= PM_CONCEPT_SOURCE_DATABASE;
    274         } else {
    275             psError(PS_ERR_UNKNOWN, false, "Error reading concepts from database.\n");
    276             success = false;
    277         }
    278     }
    279     #endif
    280 
    281     pmConceptsUpdate(fpa, chip, cell);
    282 
    283     return success;
    284 }
    285 
    286 // Write all registered concepts for the specified level
    287 static bool conceptsWrite(psMetadata **specs, // One of the concepts specifications
    288                           const pmFPA *fpa,   // The FPA
    289                           const pmChip *chip, // The chip
    290                           const pmCell *cell, // The cell
    291                           pmConceptSource source, // The source of the concepts to write
    292                           pmConfig *config, // Configuration
    293                           const psMetadata *concepts // The concepts to write out
    294                          )
    295 {
    296     assert(specs);
    297     assert(concepts);
    298 
    299     if (!conceptsInitialised) {
    300         pmConceptsInit();
    301     }
    302 
    303     psTrace("psModules.concepts", 3, "Writing concepts (%p %p %p): %d\n", fpa, chip, cell, source);
    304 
    305     if (source & PM_CONCEPT_SOURCE_CELLS) {
    306         p_pmConceptsWriteToCells(*specs, cell, concepts);
    307     }
    308     if (source & PM_CONCEPT_SOURCE_DEFAULTS) {
    309         p_pmConceptsWriteToDefaults(*specs, fpa, chip, cell, concepts);
    310     }
    311     if (source & (PM_CONCEPT_SOURCE_PHU | PM_CONCEPT_SOURCE_HEADER)) {
    312         p_pmConceptsWriteToHeader(*specs, fpa, chip, cell, concepts);
    313     }
    314     if (source & PM_CONCEPT_SOURCE_DATABASE) {
    315         p_pmConceptsWriteToDatabase(*specs, fpa, chip, cell, config, concepts);
    316     }
    317 
    318     return true;
    319 }
    320 
    321 
    322 bool pmConceptsRead(pmFPA *fpa, pmChip *chip, pmCell *cell, pmConceptSource source, pmConfig *config)
    323 {
    324     PS_ASSERT_PTR_NON_NULL(fpa, false);
    325     bool success = conceptsRead(&conceptsFPA, fpa, chip, cell, &fpa->conceptsRead, source,
    326                                 config, fpa->concepts);
    327     if (chip) {
    328         success &= conceptsRead(&conceptsChip, fpa, chip, cell, &chip->conceptsRead, source,
    329                                 config, chip->concepts);
    330     }
    331     if (cell) {
    332         success &= conceptsRead(&conceptsCell, fpa, chip, cell, &cell->conceptsRead, source,
    333                                 config, cell->concepts);
    334     }
    335 
    336     return success;
    337 }
    338 
    339 
    340208bool pmConceptsBlankFPA(pmFPA *fpa)
    341209{
     
    345213}
    346214
    347 
    348 bool pmConceptsReadFPA(pmFPA *fpa, pmConceptSource source, bool propagateDown, pmConfig *config)
    349 {
    350     PS_ASSERT_PTR_NON_NULL(fpa, false);
    351     psTrace("psModules.concepts", 5, "Reading FPA concepts: %p %p\n", conceptsFPA, fpa->concepts);
    352     bool success = conceptsRead(&conceptsFPA, fpa, NULL, NULL, &fpa->conceptsRead, source,
    353                                 config, fpa->concepts);
    354     if (propagateDown) {
    355         psArray *chips = fpa->chips;    // Array of chips
    356         for (long i = 0; i < chips->n; i++) {
    357             pmChip *chip = chips->data[i]; // Chip of interest
    358             if (chip) {
    359                 success &= pmConceptsReadChip(chip, source, false, true, config);
    360             }
    361         }
    362     }
    363 
    364     return success;
    365 }
    366 
    367 bool pmConceptsWriteFPA(const pmFPA *fpa, pmConceptSource source, bool propagateDown, pmConfig *config)
    368 {
    369     PS_ASSERT_PTR_NON_NULL(fpa, false);
    370     psTrace("psModules.concepts", 5, "Writing FPA concepts: %p %p\n", conceptsFPA, fpa->concepts);
    371     bool success = conceptsWrite(&conceptsFPA, fpa, NULL, NULL, source, config, fpa->concepts);
    372     if (propagateDown) {
    373         psArray *chips = fpa->chips;        // Array of chips
    374         for (long i = 0; i < chips->n; i++) {
    375             pmChip *chip = chips->data[i];  // Chip of interest
    376             if (chip && !chip->hdu) {
    377                 success &= pmConceptsWriteChip(chip, source, false, true, config);
    378             }
    379         }
    380     }
    381     return success;
    382 }
    383 
    384215bool pmConceptsBlankChip(pmChip *chip)
    385216{
     
    389220}
    390221
    391 bool pmConceptsReadChip(pmChip *chip, pmConceptSource source, bool propagateUp,
    392                         bool propagateDown, pmConfig *config)
    393 {
    394     PS_ASSERT_PTR_NON_NULL(chip, false);
    395     psTrace("psModules.concepts", 5, "Reading chip concepts: %p %p\n", conceptsChip, chip->concepts);
    396     pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
    397     bool success = conceptsRead(&conceptsChip, fpa, chip, NULL, &chip->conceptsRead, source, config,
    398                                 chip->concepts);
    399     if (propagateUp) {
    400         success &= conceptsRead(&conceptsFPA, fpa, chip, NULL, &fpa->conceptsRead, source,
    401                                 config, fpa->concepts);
    402     }
    403     if (propagateDown) {
    404         psArray *cells = chip->cells;        // Array of cells
    405         for (long i = 0; i < cells->n; i++) {
    406             pmCell *cell = cells->data[i];  // Cell of interest
    407             if (cell) {
    408                 success &= pmConceptsReadCell(cell, source, false, config);
    409             }
    410         }
    411     }
    412     return success;
    413 }
    414 
    415 bool pmConceptsWriteChip(const pmChip *chip, pmConceptSource source, bool propagateUp,
    416                          bool propagateDown, pmConfig *config)
    417 {
    418     PS_ASSERT_PTR_NON_NULL(chip, false);
    419     psTrace("psModules.concepts", 5, "Writing chip concepts: %p %p\n", conceptsChip, chip->concepts);
    420     pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
    421     bool success = conceptsWrite(&conceptsChip, fpa, chip, NULL, source, config, chip->concepts);
    422     if (propagateUp && !fpa->hdu) {
    423         success &= conceptsWrite(&conceptsFPA, fpa, chip, NULL, source, config, fpa->concepts);
    424     }
    425     if (propagateDown) {
    426         psArray *cells = chip->cells;        // Array of cells
    427         for (long i = 0; i < cells->n; i++) {
    428             pmCell *cell = cells->data[i];  // Cell of interest
    429             if (cell && !cell->hdu) {
    430                 success &= pmConceptsWriteCell(cell, source, false, config);
    431             }
    432         }
    433     }
    434     return success;
    435 }
    436 
    437222bool pmConceptsBlankCell(pmCell *cell)
    438223{
     
    440225    psTrace("psModules.concepts", 5, "Blanking cell concepts: %p %p\n", conceptsCell, cell->concepts);
    441226    return conceptsBlank(&conceptsCell, cell->concepts);
    442 }
    443 
    444 bool pmConceptsReadCell(pmCell *cell, pmConceptSource source, bool propagateUp, pmConfig *config)
    445 {
    446     PS_ASSERT_PTR_NON_NULL(cell, false);
    447     psTrace("psModules.concepts", 5, "Reading cell concepts: %p %p\n", conceptsCell, cell->concepts);
    448     pmChip *chip = cell->parent;        // Chip to which the cell belongs
    449     pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
    450 
    451     bool success = conceptsRead(&conceptsCell, fpa, chip, cell, &cell->conceptsRead, source, config,
    452                                 cell->concepts);
    453     if (propagateUp) {
    454         success &= conceptsRead(&conceptsChip, fpa, chip, cell, &chip->conceptsRead, source, config,
    455                                 chip->concepts);
    456         success &= conceptsRead(&conceptsFPA, fpa, chip, cell, &fpa->conceptsRead, source, config,
    457                                 fpa->concepts);
    458     }
    459 
    460     return success;
    461 }
    462 
    463 bool pmConceptsWriteCell(const pmCell *cell, pmConceptSource source, bool propagateUp, pmConfig *config)
    464 {
    465     PS_ASSERT_PTR_NON_NULL(cell, false);
    466     psTrace("psModules.concepts", 5, "Writing cell concepts: %p %p\n", conceptsCell, cell->concepts);
    467     pmChip *chip = cell->parent;        // Chip to which the cell belongs
    468     pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
    469 
    470     bool success = conceptsWrite(&conceptsCell, fpa, chip, cell, source, config, cell->concepts);
    471     if (propagateUp) {
    472         if (!chip->hdu) {
    473             success &= conceptsWrite(&conceptsChip, fpa, chip, cell, source, config, chip->concepts);
    474             if (!fpa->hdu) {
    475                 success &= conceptsWrite(&conceptsFPA, fpa, chip, cell, source, config, fpa->concepts);
    476             }
    477         }
    478     }
    479 
    480     return success;
    481227}
    482228
     
    485231static void conceptRegister##SUFFIX(const char *name, /* Name of concept */ \
    486232                                    const char *comment, /* Comment for concept */ \
    487                                     pmConceptParseFunc parse, /* Parsing function */ \
    488                                     pmConceptFormatFunc format, /* Formatting function */ \
     233                                    pmConceptParseFunc parse, /* Parsing function, or NULL */ \
     234                                    pmConceptFormatFunc format, /* Formatting function, or NULL */ \
     235                                    pmConceptCopyFunc copy, /* Copying function, or NULL */ \
    489236                                    bool required, /* Required concept? */ \
    490237                                    pmFPALevel level /* Level at which concept applies */ \
     
    492239{ \
    493240    psMetadataItem *item = psMetadataItemAlloc##TYPENAME(name, comment, DEFAULT); /* Item to add */ \
    494     pmConceptRegister(item, parse, format, required, level); \
     241    pmConceptRegister(item, parse, format, copy, required, level); \
    495242    psFree(item); \
    496243    return; \
     
    515262    psMetadataItem *item = psMetadataItemAlloc(name, PS_DATA_TIME, comment, time);
    516263    psFree(time);
    517     pmConceptRegister(item, p_pmConceptParse_TIME, p_pmConceptFormat_TIME, required, level);
     264    pmConceptRegister(item, p_pmConceptParse_TIME, p_pmConceptFormat_TIME, NULL, required, level);
    518265    psFree(item);
    519266}
     
    521268bool pmConceptsInit(void)
    522269{
     270    if (conceptsInitialised) {
     271        return true;
     272    }
     273
    523274    conceptsInitialised = true;
    524275
     
    527278    bool init = false;                  // Did we initialise anything?
    528279
    529     if (! conceptsFPA) {
     280    if (!conceptsFPA) {
    530281        conceptsFPA = psMetadataAlloc();
    531282        init = true;
    532283
    533284        // Install the standard concepts
    534         conceptRegisterStr("FPA.TELESCOPE", "Telescope of origin", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    535         conceptRegisterStr("FPA.INSTRUMENT", "Instrument name (according to the instrument)", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    536         conceptRegisterStr("FPA.DETECTOR", "Detector name", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    537         conceptRegisterStr("FPA.COMMENT", "Observation comment", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    538         conceptRegisterStr("FPA.OBS.MODE", "Observation mode (eg, survey id)", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    539         conceptRegisterStr("FPA.OBS.GROUP", "Observation group (eg, associated images)", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    540         conceptRegisterF32("FPA.FOCUS", "Telescope focus", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    541         conceptRegisterF32("FPA.AIRMASS", "Airmass at boresight", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    542         conceptRegisterStr("FPA.FILTERID", "Filter used (parsed, abstract name)", p_pmConceptParse_FPA_FILTER, p_pmConceptFormat_FPA_FILTER, false, PM_FPA_LEVEL_FPA);
    543         conceptRegisterStr("FPA.FILTER", "Filter used (instrument name)", false, NULL, NULL, PM_FPA_LEVEL_FPA);
    544         conceptRegisterF32("FPA.POSANGLE", "Position angle of instrument", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    545         conceptRegisterF32("FPA.ROTANGLE", "Rotator angle of instrument", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    546         conceptRegisterStr("FPA.RADECSYS", "Celestial coordinate system", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    547         conceptRegisterF64("FPA.RA", "Right Ascension of boresight", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, false, PM_FPA_LEVEL_FPA);
    548         conceptRegisterF64("FPA.DEC", "Declination of boresight", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, false, PM_FPA_LEVEL_FPA);
    549         conceptRegisterF64("FPA.LONGITUDE", "West longitude of observatory", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, false, PM_FPA_LEVEL_FPA);
    550         conceptRegisterF64("FPA.LATITUDE", "Latitude of observatory", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, false, PM_FPA_LEVEL_FPA);
    551         conceptRegisterF32("FPA.ELEVATION", "Elevation of observatory (metres)", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    552         conceptRegisterStr("FPA.OBSTYPE", "Type of observation", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    553         conceptRegisterStr("FPA.OBJECT", "Object of observation", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    554         conceptRegisterF64("FPA.ALT", "Altitude of boresight", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    555         conceptRegisterF64("FPA.AZ", "Azimuth of boresight", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    556         conceptRegisterEnum("FPA.TIMESYS", "Time system", p_pmConceptParse_TIMESYS, p_pmConceptFormat_TIMESYS, false, PM_FPA_LEVEL_FPA);
     285        conceptRegisterStr("FPA.TELESCOPE", "Telescope of origin", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     286        conceptRegisterStr("FPA.INSTRUMENT", "Instrument name (according to the instrument)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     287        conceptRegisterStr("FPA.DETECTOR", "Detector name", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     288        conceptRegisterStr("FPA.COMMENT", "Observation comment", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     289        conceptRegisterStr("FPA.OBS.MODE", "Observation mode (eg, survey id)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     290        conceptRegisterStr("FPA.OBS.GROUP", "Observation group (eg, associated images)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     291        conceptRegisterF32("FPA.FOCUS", "Telescope focus", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     292        conceptRegisterF32("FPA.AIRMASS", "Airmass at boresight", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     293        conceptRegisterStr("FPA.FILTERID", "Filter used (parsed, abstract name)", p_pmConceptParse_FPA_FILTER, p_pmConceptFormat_FPA_FILTER, NULL, false, PM_FPA_LEVEL_FPA);
     294        conceptRegisterStr("FPA.FILTER", "Filter used (instrument name)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     295        conceptRegisterF32("FPA.POSANGLE", "Position angle of instrument", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     296        conceptRegisterF32("FPA.ROTANGLE", "Rotator angle of instrument", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     297        conceptRegisterStr("FPA.RADECSYS", "Celestial coordinate system", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     298        conceptRegisterF64("FPA.RA", "Right Ascension of boresight", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, NULL, false, PM_FPA_LEVEL_FPA);
     299        conceptRegisterF64("FPA.DEC", "Declination of boresight", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, NULL, false, PM_FPA_LEVEL_FPA);
     300        conceptRegisterF64("FPA.LONGITUDE", "West longitude of observatory", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, NULL, false, PM_FPA_LEVEL_FPA);
     301        conceptRegisterF64("FPA.LATITUDE", "Latitude of observatory", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, NULL, false, PM_FPA_LEVEL_FPA);
     302        conceptRegisterF32("FPA.ELEVATION", "Elevation of observatory (metres)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     303        conceptRegisterStr("FPA.OBSTYPE", "Type of observation", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     304        conceptRegisterStr("FPA.OBJECT", "Object of observation", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     305        conceptRegisterF64("FPA.ALT", "Altitude of boresight", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     306        conceptRegisterF64("FPA.AZ", "Azimuth of boresight", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     307        conceptRegisterEnum("FPA.TIMESYS", "Time system", p_pmConceptParse_TIMESYS, p_pmConceptFormat_TIMESYS, p_pmConceptCopy_TIMESYS, false, PM_FPA_LEVEL_FPA);
    557308        conceptRegisterTime("FPA.TIME", "Time of exposure", false, PM_FPA_LEVEL_FPA);
    558         conceptRegisterF32("FPA.TEMP", "Temperature of focal plane", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    559         conceptRegisterF32("FPA.M1X", "Primary Mirror X Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    560         conceptRegisterF32("FPA.M1Y", "Primary Mirror Y Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    561         conceptRegisterF32("FPA.M1Z", "Primary Mirror Z Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    562         conceptRegisterF32("FPA.M1TIP", "Primary Mirror TIP", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    563         conceptRegisterF32("FPA.M1TILT", "Primary Mirror TILT", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    564         conceptRegisterF32("FPA.M2X", "Primary Mirror X Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    565         conceptRegisterF32("FPA.M2Y", "Primary Mirror Y Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    566         conceptRegisterF32("FPA.M2Z", "Primary Mirror Z Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    567         conceptRegisterF32("FPA.M2TIP", "Primary Mirror TIP", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    568         conceptRegisterF32("FPA.M2TILT", "Primary Mirror TILT", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    569         conceptRegisterF32("FPA.ENV.TEMP", "Environment: Temperature", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    570         conceptRegisterF32("FPA.ENV.HUMID", "Environment: Humidity", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    571         conceptRegisterF32("FPA.ENV.WIND", "Environment: Wind speed", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    572         conceptRegisterF32("FPA.ENV.DIR", "Environment: Wind direction", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    573         conceptRegisterF32("FPA.TELTEMP.M1", "Telescope Temperatures: M1", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
    574         conceptRegisterF32("FPA.TELTEMP.M1CELL", "Telescope Temperatures: M1 cell", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
    575         conceptRegisterF32("FPA.TELTEMP.M2", "Telescope Temperatures: M2", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
    576         conceptRegisterF32("FPA.TELTEMP.SPIDER", "Telescope Temperatures: spider", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
    577         conceptRegisterF32("FPA.TELTEMP.TRUSS", "Telescope Temperatures: truss", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
    578         conceptRegisterF32("FPA.TELTEMP.EXTRA", "Telescope Temperatures: extra", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
    579         conceptRegisterF32("FPA.PON.TIME", "Power On Time", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    580         conceptRegisterF32("FPA.EXPOSURE", "Exposure time (sec)", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    581     }
    582     if (! conceptsChip) {
     309        conceptRegisterF32("FPA.TEMP", "Temperature of focal plane", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     310        conceptRegisterF32("FPA.M1X", "Primary Mirror X Position", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     311        conceptRegisterF32("FPA.M1Y", "Primary Mirror Y Position", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     312        conceptRegisterF32("FPA.M1Z", "Primary Mirror Z Position", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     313        conceptRegisterF32("FPA.M1TIP", "Primary Mirror TIP", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     314        conceptRegisterF32("FPA.M1TILT", "Primary Mirror TILT", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     315        conceptRegisterF32("FPA.M2X", "Primary Mirror X Position", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     316        conceptRegisterF32("FPA.M2Y", "Primary Mirror Y Position", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     317        conceptRegisterF32("FPA.M2Z", "Primary Mirror Z Position", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     318        conceptRegisterF32("FPA.M2TIP", "Primary Mirror TIP", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     319        conceptRegisterF32("FPA.M2TILT", "Primary Mirror TILT", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     320        conceptRegisterF32("FPA.ENV.TEMP", "Environment: Temperature", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     321        conceptRegisterF32("FPA.ENV.HUMID", "Environment: Humidity", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     322        conceptRegisterF32("FPA.ENV.WIND", "Environment: Wind speed", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     323        conceptRegisterF32("FPA.ENV.DIR", "Environment: Wind direction", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     324        conceptRegisterF32("FPA.TELTEMP.M1", "Telescope Temperatures: M1", p_pmConceptParse_TELTEMPS, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     325        conceptRegisterF32("FPA.TELTEMP.M1CELL", "Telescope Temperatures: M1 cell", p_pmConceptParse_TELTEMPS, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     326        conceptRegisterF32("FPA.TELTEMP.M2", "Telescope Temperatures: M2", p_pmConceptParse_TELTEMPS, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     327        conceptRegisterF32("FPA.TELTEMP.SPIDER", "Telescope Temperatures: spider", p_pmConceptParse_TELTEMPS, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     328        conceptRegisterF32("FPA.TELTEMP.TRUSS", "Telescope Temperatures: truss", p_pmConceptParse_TELTEMPS, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     329        conceptRegisterF32("FPA.TELTEMP.EXTRA", "Telescope Temperatures: extra", p_pmConceptParse_TELTEMPS, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     330        conceptRegisterF32("FPA.PON.TIME", "Power On Time", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     331        conceptRegisterF32("FPA.EXPOSURE", "Exposure time (sec)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     332    }
     333    if (!conceptsChip) {
    583334        conceptsChip = psMetadataAlloc();
    584335        init = true;
    585336
    586337        // Install the standard concepts
    587         conceptRegisterS32("CHIP.XPARITY", "Orientation in x compared to the rest of the FPA", NULL, NULL, true, PM_FPA_LEVEL_CHIP);
    588         conceptRegisterS32("CHIP.YPARITY", "Orientation in y compared to the rest of the FPA", NULL, NULL, true, PM_FPA_LEVEL_CHIP);
    589         conceptRegisterS32("CHIP.X0", "Position of (0,0) on the FPA",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CHIP);
    590         conceptRegisterS32("CHIP.Y0", "Position of (0,0) on the FPA",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CHIP);
    591         conceptRegisterS32("CHIP.XSIZE", "Size of chip (pixels)", NULL, NULL, true, PM_FPA_LEVEL_CHIP);
    592         conceptRegisterS32("CHIP.YSIZE", "Size of chip (pixels)", NULL, NULL, true, PM_FPA_LEVEL_CHIP);
    593         conceptRegisterF32("CHIP.TEMP", "Temperature of chip", NULL, NULL, false, PM_FPA_LEVEL_CHIP);
    594         conceptRegisterStr("CHIP.ID", "Chip identifier", NULL, NULL, false, PM_FPA_LEVEL_CHIP);
    595     }
    596 
    597     if (! conceptsCell) {
     338        conceptRegisterS32("CHIP.XPARITY", "Orientation in x compared to the rest of the FPA", NULL, NULL, NULL, true, PM_FPA_LEVEL_CHIP);
     339        conceptRegisterS32("CHIP.YPARITY", "Orientation in y compared to the rest of the FPA", NULL, NULL, NULL, true, PM_FPA_LEVEL_CHIP);
     340        conceptRegisterS32("CHIP.X0", "Position of (0,0) on the FPA",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, NULL, true, PM_FPA_LEVEL_CHIP);
     341        conceptRegisterS32("CHIP.Y0", "Position of (0,0) on the FPA",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, NULL, true, PM_FPA_LEVEL_CHIP);
     342        conceptRegisterS32("CHIP.XSIZE", "Size of chip (pixels)", NULL, NULL, NULL, true, PM_FPA_LEVEL_CHIP);
     343        conceptRegisterS32("CHIP.YSIZE", "Size of chip (pixels)", NULL, NULL, NULL, true, PM_FPA_LEVEL_CHIP);
     344        conceptRegisterF32("CHIP.TEMP", "Temperature of chip", NULL, NULL, NULL, false, PM_FPA_LEVEL_CHIP);
     345        conceptRegisterStr("CHIP.ID", "Chip identifier", NULL, NULL, NULL, false, PM_FPA_LEVEL_CHIP);
     346    }
     347
     348    if (!conceptsCell) {
    598349        conceptsCell = psMetadataAlloc();
    599350        init = true;
    600351
    601352        // Install the standard concepts
    602         conceptRegisterF32("CELL.GAIN", "CCD gain (e/count)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
    603         conceptRegisterF32("CELL.READNOISE", "CCD read noise (e)", p_pmConceptParse_CELL_READNOISE, p_pmConceptFormat_CELL_READNOISE, true, PM_FPA_LEVEL_CELL);
    604         conceptRegisterF32("CELL.SATURATION", "Saturation level (counts)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
    605         conceptRegisterF32("CELL.BAD", "Bad level (counts)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
    606         conceptRegisterS32("CELL.XPARITY", "Orientation in x compared to the rest of the chip", NULL, NULL, true, PM_FPA_LEVEL_CELL);
    607         conceptRegisterS32("CELL.YPARITY", "Orientation in y compared to the rest of the chip", NULL, NULL, true, PM_FPA_LEVEL_CELL);
    608         conceptRegisterS32("CELL.READDIR", "Read direction, rows=1, cols=2", NULL, NULL, true, PM_FPA_LEVEL_CELL);
     353        conceptRegisterF32("CELL.GAIN", "CCD gain (e/count)", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
     354        conceptRegisterF32("CELL.READNOISE", "CCD read noise (e)", p_pmConceptParse_CELL_READNOISE, p_pmConceptFormat_CELL_READNOISE, NULL, true, PM_FPA_LEVEL_CELL);
     355        conceptRegisterF32("CELL.SATURATION", "Saturation level (counts)", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
     356        conceptRegisterF32("CELL.BAD", "Bad level (counts)", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
     357        conceptRegisterS32("CELL.XPARITY", "Orientation in x compared to the rest of the chip", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
     358        conceptRegisterS32("CELL.YPARITY", "Orientation in y compared to the rest of the chip", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
     359        conceptRegisterS32("CELL.READDIR", "Read direction, rows=1, cols=2", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
    609360
    610361        // These (CELL.EXPOSURE and CELL.DARKTIME) used to be READOUT.EXPOSURE and READOUT.DARKTIME, but that
     
    613364        // readout is a plane in a 3D image.  We'll have to dream up some additional suffix to specify these,
    614365        // but for now....
    615         conceptRegisterF32("CELL.EXPOSURE", "Exposure time (sec)", NULL, NULL, false, PM_FPA_LEVEL_CELL);
    616         conceptRegisterF32("CELL.DARKTIME", "Time since flush (sec)", NULL, NULL, false, PM_FPA_LEVEL_CELL);
    617 
    618         conceptRegisterS32("CELL.XBIN", "Binning in x", p_pmConceptParse_CELL_Binning,p_pmConceptFormat_CELL_XBIN, true, PM_FPA_LEVEL_CELL);
    619         conceptRegisterS32("CELL.YBIN", "Binning in y",p_pmConceptParse_CELL_Binning,p_pmConceptFormat_CELL_YBIN, true, PM_FPA_LEVEL_CELL);
    620         conceptRegisterEnum("CELL.TIMESYS", "Time system", p_pmConceptParse_TIMESYS,p_pmConceptFormat_TIMESYS, false, PM_FPA_LEVEL_CELL);
     366        conceptRegisterF32("CELL.EXPOSURE", "Exposure time (sec)", NULL, NULL, NULL, false, PM_FPA_LEVEL_CELL);
     367        conceptRegisterF32("CELL.DARKTIME", "Time since flush (sec)", NULL, NULL, NULL, false, PM_FPA_LEVEL_CELL);
     368
     369        conceptRegisterS32("CELL.XBIN", "Binning in x", p_pmConceptParse_CELL_Binning,p_pmConceptFormat_CELL_XBIN, NULL, true, PM_FPA_LEVEL_CELL);
     370        conceptRegisterS32("CELL.YBIN", "Binning in y",p_pmConceptParse_CELL_Binning,p_pmConceptFormat_CELL_YBIN, NULL, true, PM_FPA_LEVEL_CELL);
     371        conceptRegisterEnum("CELL.TIMESYS", "Time system", p_pmConceptParse_TIMESYS,p_pmConceptFormat_TIMESYS, p_pmConceptCopy_TIMESYS, false, PM_FPA_LEVEL_CELL);
    621372        conceptRegisterTime("CELL.TIME", "Time of exposure", false, PM_FPA_LEVEL_CELL);
    622         conceptRegisterS32("CELL.X0", "Position of (0,0) on the chip",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
    623         conceptRegisterS32("CELL.Y0", "Position of (0,0) on the chip",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
    624         conceptRegisterS32("CELL.XSIZE", "Size of cell (pixels)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
    625         conceptRegisterS32("CELL.YSIZE", "Size of cell (pixels)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
    626         conceptRegisterS32("CELL.XWINDOW", "Start of cell window (pixels)",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
    627         conceptRegisterS32("CELL.YWINDOW", "Start of cell window (pixels)",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
    628         conceptRegisterF32("CELL.VARFACTOR", "Variance factor for conversion from large to small scales", NULL, NULL, true, PM_FPA_LEVEL_CELL);
     373        conceptRegisterS32("CELL.X0", "Position of (0,0) on the chip",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, NULL, true, PM_FPA_LEVEL_CELL);
     374        conceptRegisterS32("CELL.Y0", "Position of (0,0) on the chip",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, NULL, true, PM_FPA_LEVEL_CELL);
     375        conceptRegisterS32("CELL.XSIZE", "Size of cell (pixels)", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
     376        conceptRegisterS32("CELL.YSIZE", "Size of cell (pixels)", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
     377        conceptRegisterS32("CELL.XWINDOW", "Start of cell window (pixels)",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, NULL, true, PM_FPA_LEVEL_CELL);
     378        conceptRegisterS32("CELL.YWINDOW", "Start of cell window (pixels)",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, NULL, true, PM_FPA_LEVEL_CELL);
     379        conceptRegisterF32("CELL.VARFACTOR", "Variance factor for conversion from large to small scales", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
    629380
    630381        // CELL.TRIMSEC
     
    635386                                          "Trim section", trimsec);
    636387            psFree(trimsec);
    637             pmConceptRegister(cellTrimsec, p_pmConceptParse_CELL_TRIMSEC,p_pmConceptFormat_CELL_TRIMSEC, true, PM_FPA_LEVEL_CELL);
     388            pmConceptRegister(cellTrimsec, p_pmConceptParse_CELL_TRIMSEC,p_pmConceptFormat_CELL_TRIMSEC, NULL, true, PM_FPA_LEVEL_CELL);
    638389            psFree(cellTrimsec);
    639390        }
     
    645396                                          "Bias sections", biassecs);
    646397            psFree(biassecs);
    647             pmConceptRegister(cellBiassec, p_pmConceptParse_CELL_BIASSEC, p_pmConceptFormat_CELL_BIASSEC, true, PM_FPA_LEVEL_CELL);
     398            pmConceptRegister(cellBiassec, p_pmConceptParse_CELL_BIASSEC, p_pmConceptFormat_CELL_BIASSEC, NULL, true, PM_FPA_LEVEL_CELL);
    648399            psFree(cellBiassec);
    649400        }
     
    666417
    667418    conceptsInitialised = false;
    668 }
    669 
    670 
    671 // List of concepts not to copy, for each level.
    672 // Must be NULL-terminated
    673 static const char *dontCopyConceptsFPA[] = { "FPA.OBS", "FPA.NAME", "FPA.CAMERA", 0 };
    674 static const char *dontCopyConceptsChip[] = { "CHIP.NAME", 0 };
    675 static const char *dontCopyConceptsCell[] = { "CELL.NAME", "CELL.TRIMSEC", "CELL.BIASSEC", 0 };
    676 
    677 // Copy concepts from a source container to a target container, avoiding certain entries
    678 static void copyConcepts(psMetadata *target, // Target metadata container
    679                          psMetadata *source, // Source metadata container
    680                          const char *dontCopyConcepts[] // Don't copy these concepts
    681                          )
    682 {
    683     assert(target);
    684     assert(source);
    685     assert(dontCopyConcepts);
    686 
    687     psMetadataIterator *iter = psMetadataIteratorAlloc(source, PS_LIST_HEAD, NULL);
    688     psMetadataItem *item;               // Item from iteration
    689     while ((item = psMetadataGetAndIncrement(iter))) {
    690         const char *name = item->name;  // Name of concept
    691         bool copyOK = true;            // OK to copy
    692         for (int i = 0; dontCopyConcepts[i] && copyOK; i++) {
    693             if (!strcmp(name, dontCopyConcepts[i])) {
    694                 copyOK = false;
    695             }
    696         }
    697         if (!copyOK) {
    698             continue;
    699         }
    700         psMetadataItem *new = psMetadataItemCopy(item); // New item, a copy of the old
    701         psMetadataAddItem(target, new, PS_LIST_TAIL, PS_META_REPLACE);
    702         psFree(new);                    // Drop reference
    703     }
    704     psFree(iter);
    705 
    706     return;
    707 }
    708 
    709 
    710 bool pmFPACopyConcepts(pmFPA *target, const pmFPA *source)
    711 {
    712     PS_ASSERT_PTR_NON_NULL(target, false);
    713     PS_ASSERT_PTR_NON_NULL(source, false);
    714 
    715     // Copy FPA concepts
    716     copyConcepts(target->concepts, source->concepts, dontCopyConceptsFPA);
    717 
    718     // Copy chip concepts
    719     psArray *targetChips = target->chips; // Chips in target
    720     psArray *sourceChips = source->chips; // Chips in source
    721     if (targetChips->n != sourceChips->n) {
    722         psError(PS_ERR_IO, true, "Number of chips in target (%ld) and source (%ld) differ --- unable to copy "
    723                 "concepts.\n", targetChips->n, sourceChips->n);
    724         return false;
    725     }
    726     for (int i = 0; i < targetChips->n; i++) {
    727         pmChip *targetChip = targetChips->data[i]; // Target chip of interest
    728         pmChip *sourceChip = sourceChips->data[i]; // Source chip of interest
    729         if (! targetChip || ! sourceChip) {
    730             continue;
    731         }
    732 
    733         copyConcepts(targetChip->concepts, sourceChip->concepts, dontCopyConceptsChip);
    734 
    735         // Copy cell concepts
    736         psArray *targetCells = targetChip->cells; // Cells in target
    737         psArray *sourceCells = sourceChip->cells; // Cells in source
    738         if (targetCells->n != sourceCells->n) {
    739             psError(PS_ERR_IO, true, "Number of cells in target (%ld) and source (%ld) differ for chip %d ---"
    740                     " unable to copy concepts.\n", targetCells->n, sourceCells->n, i);
    741             return false;
    742         }
    743         for (int j = 0; j < targetCells->n; j++) {
    744             pmCell *targetCell = targetCells->data[j]; // Target chip of interest
    745             pmCell *sourceCell = sourceCells->data[j]; // Source chip of interest
    746             if (! targetCell || ! sourceCell) {
    747                 continue;
    748             }
    749 
    750             copyConcepts(targetCell->concepts, sourceCell->concepts, dontCopyConceptsCell);
    751         }
    752     }
    753 
    754     return true;
    755 }
    756 
    757 
    758 bool pmConceptsCopyFPA(pmFPA *target, const pmFPA *source, bool chips, bool cells)
    759 {
    760     PS_ASSERT_PTR_NON_NULL(target, false);
    761     PS_ASSERT_PTR_NON_NULL(source, false);
    762 
    763     // Copy FPA concepts
    764     copyConcepts(target->concepts, source->concepts, dontCopyConceptsFPA);
    765 
    766     // Copy chip concepts
    767     bool status = true;                 // Status of chips
    768     if (chips) {
    769         psArray *targetChips = target->chips; // Chips in target
    770         psArray *sourceChips = source->chips; // Chips in source
    771         if (targetChips->n != sourceChips->n) {
    772             psError(PS_ERR_IO, true,
    773                     "Number of chips in target (%ld) and source (%ld) differ --- unable to copy concepts.",
    774                     targetChips->n, sourceChips->n);
    775             return false;
    776         }
    777         for (int i = 0; i < targetChips->n; i++) {
    778             pmChip *targetChip = targetChips->data[i]; // Target chip of interest
    779             pmChip *sourceChip = sourceChips->data[i]; // Source chip of interest
    780             if (! targetChip || ! sourceChip) {
    781                 continue;
    782             }
    783 
    784             status &= pmConceptsCopyChip(targetChip, sourceChip, cells);
    785         }
    786     }
    787 
    788     return status;
    789 }
    790 
    791 bool pmConceptsCopyChip(pmChip *target, const pmChip *source, bool cells)
    792 {
    793     PS_ASSERT_PTR_NON_NULL(target, false);
    794     PS_ASSERT_PTR_NON_NULL(source, false);
    795 
    796     // Copy chip concepts
    797     copyConcepts(target->concepts, source->concepts, dontCopyConceptsChip);
    798 
    799     // Copy cell concepts
    800     bool status = true;                 // Status of cells
    801     if (cells) {
    802         psArray *targetCells = target->cells; // Cells in target
    803         psArray *sourceCells = source->cells; // Cells in source
    804         if (targetCells->n != sourceCells->n) {
    805             psError(PS_ERR_IO, true,
    806                     "Number of cells in target (%ld) and source (%ld) differ --- unable to copy concepts.",
    807                     targetCells->n, sourceCells->n);
    808             return false;
    809         }
    810         for (int j = 0; j < targetCells->n; j++) {
    811             pmCell *targetCell = targetCells->data[j]; // Target chip of interest
    812             pmCell *sourceCell = sourceCells->data[j]; // Source chip of interest
    813             if (! targetCell || ! sourceCell) {
    814                 continue;
    815             }
    816 
    817             status &= pmConceptsCopyCell(targetCell, sourceCell);
    818         }
    819     }
    820 
    821     return status;
    822 }
    823 
    824 
    825 bool pmConceptsCopyCell(pmCell *target, const pmCell *source)
    826 {
    827     PS_ASSERT_PTR_NON_NULL(target, false);
    828     PS_ASSERT_PTR_NON_NULL(source, false);
    829 
    830     copyConcepts(target->concepts, source->concepts, dontCopyConceptsCell);
    831 
    832     return true;
    833419}
    834420
Note: See TracChangeset for help on using the changeset viewer.