IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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
Note: See TracChangeset for help on using the changeset viewer.