IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 12873


Ignore:
Timestamp:
Apr 17, 2007, 6:29:18 PM (19 years ago)
Author:
Paul Price
Message:

Fixing bias, adding catalog and random stars.

Location:
trunk/ppSim/src
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/ppSim/src/ppSimArguments.c

    r12834 r12873  
    1616{
    1717    fprintf(stderr, "\nPan-STARRS data simulator\n\n");
    18     fprintf(stderr, "Usage: %s -camera CAMERA_NAME -format FORMAT_NAME\n", config->argv[0]);
     18    fprintf(stderr, "Usage: %s -camera CAMERA_NAME\n", config->argv[0]);
    1919    fprintf(stderr, "\n");
    2020    psArgumentHelp(arguments);
     
    3535    float value = psMetadataLookupF32(NULL, arguments, argName); // Bias level
    3636    if (isnan(value)) {
    37         value = psMetadataLookupF32(NULL, recipe, recipeName);
    38         if (isnan(value)) {
     37        bool mdok;                      // Status of MD lookup
     38        value = psMetadataLookupF32(&mdok, recipe, recipeName);
     39        if (!mdok) {
    3940            psErrorStackPrint(stderr, "Unable to find %s in recipe %s", recipeName, PPSIM_RECIPE);
    4041            psFree((psPtr)arguments);
     
    4344        }
    4445    }
    45 
    4646    return psMetadataAddF32(target, PS_LIST_TAIL, recipeName, 0, NULL, value);
    4747}
    48 
    4948
    5049void ppSimArguments(pmConfig *config)
     
    6463    psMetadataAddF32(arguments, PS_LIST_TAIL, "-shuttertime", 0, "Shutter time (s)", NAN);
    6564    psMetadataAddF32(arguments, PS_LIST_TAIL, "-skyrate", 0, "Sky rate (e/s)", NAN);
     65    psMetadataAddStr(arguments, PS_LIST_TAIL, "-stars", 0, "File with star RA, Dec, Mag", NULL);
     66    psMetadataAddF32(arguments, PS_LIST_TAIL, "-ra", 0, "RA (degrees)", NAN);
     67    psMetadataAddF32(arguments, PS_LIST_TAIL, "-dec", 0, "Dec (degrees)", NAN);
     68    psMetadataAddF32(arguments, PS_LIST_TAIL, "-pa", 0, "Position angle (degrees)", NAN);
     69    psMetadataAddF32(arguments, PS_LIST_TAIL, "-scale", 0, "Plate scale (arcsec/pixel)", NAN);
     70    psMetadataAddF32(arguments, PS_LIST_TAIL, "-zp", 0, "Photometric zero point", NAN);
     71    psMetadataAddF32(arguments, PS_LIST_TAIL, "-seeing", 0, "Seeing FWHM (arcsec)", NAN);
     72    psMetadataAddF32(arguments, PS_LIST_TAIL, "-starslum", 0, "Fake star luminosity function slope", NAN);
     73    psMetadataAddF32(arguments, PS_LIST_TAIL, "-starsmag", 0, "Brightest magnitude for fake stars", NAN);
     74    psMetadataAddF32(arguments, PS_LIST_TAIL, "-starsdensity", 0, "Density of fake stars at magnitude", NAN);
    6675
    6776    if (!config->camera) {
     
    7483    }
    7584
    76     const char *formatName = psMetadataLookupStr(NULL, arguments, "-format"); // Name of format
    77     if (!formatName) {
    78         psErrorStackPrint(stderr, "A camera format must be specified using -format");
    79         usage(arguments, config);
    80     }
    81     psMetadataAddStr(config->arguments, PS_LIST_TAIL, "FORMAT", 0, "Camera format name", formatName);
     85    psString formatName = psMetadataLookupStr(NULL, arguments, "-format"); // Name of format
     86    if (formatName) {
     87        config->formatName = psMemIncrRefCounter(formatName);
     88
     89        psMetadata *formats = psMetadataLookupMetadata(NULL, config->camera, "FORMATS"); // The camera formats
     90        if (!formats) {
     91            psErrorStackPrint(stderr, "Unable to find FORMATS in camera configuration.");
     92            psFree(arguments);
     93            psFree(config);
     94            exit(PS_EXIT_CONFIG_ERROR);
     95        }
     96        psMetadata *format = psMetadataLookupMetadata(NULL, formats, formatName); // Format of interest
     97        if (!format) {
     98            psErrorStackPrint(stderr, "Unable to find format %s in camera FORMATS.", formatName);
     99            psFree(arguments);
     100            psFree(config);
     101            exit(PS_EXIT_CONFIG_ERROR);
     102        }
     103        config->format = psMemIncrRefCounter(format);
     104    }
    82105
    83106    const char *typeStr = psMetadataLookupStr(NULL, arguments, "-type"); // Exposure type
     
    124147    }
    125148
    126     valueArgRecipe(config, arguments, "-biaslevel",   recipe, "BIAS.LEVEL",   config->arguments);
    127     valueArgRecipe(config, arguments, "-biasrange",   recipe, "BIAS.RANGE",   config->arguments);
    128     valueArgRecipe(config, arguments, "-darkrate",    recipe, "DARK.RATE",    config->arguments);
    129     valueArgRecipe(config, arguments, "-flatsigma",   recipe, "FLAT.SIGMA",   config->arguments);
    130     valueArgRecipe(config, arguments, "-flatrate",    recipe, "FLAT.RATE",    config->arguments);
    131     valueArgRecipe(config, arguments, "-shuttertime", recipe, "SHUTTER.TIME", config->arguments);
    132     valueArgRecipe(config, arguments, "-skyrate",     recipe, "SKY.RATE",     config->arguments);
     149    valueArgRecipe(config, arguments, "-biaslevel",    recipe, "BIAS.LEVEL",    config->arguments);
     150    valueArgRecipe(config, arguments, "-biasrange",    recipe, "BIAS.RANGE",    config->arguments);
     151    valueArgRecipe(config, arguments, "-darkrate",     recipe, "DARK.RATE",     config->arguments);
     152    valueArgRecipe(config, arguments, "-flatsigma",    recipe, "FLAT.SIGMA",    config->arguments);
     153    valueArgRecipe(config, arguments, "-flatrate",     recipe, "FLAT.RATE",     config->arguments);
     154    valueArgRecipe(config, arguments, "-shuttertime",  recipe, "SHUTTER.TIME",  config->arguments);
     155    valueArgRecipe(config, arguments, "-skyrate",      recipe, "SKY.RATE",      config->arguments);
     156    valueArgRecipe(config, arguments, "-starslum",     recipe, "STARS.LUM",     config->arguments);
     157    valueArgRecipe(config, arguments, "-starsmag",     recipe, "STARS.MAG",     config->arguments);
     158    valueArgRecipe(config, arguments, "-starsdensity", recipe, "STARS.DENSITY", config->arguments);
     159
     160    bool mdok;                          // Status of MD lookup
     161    int biasOrder = psMetadataLookupS32(&mdok, recipe, "BIAS.ORDER"); // Overscan polynomial order
     162    if (!mdok) {
     163        psWarning("BIAS.ORDER(S32) is not set in the recipe %s --- assuming %d", PPSIM_RECIPE, biasOrder);
     164    }
     165    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "BIAS.ORDER", 0,
     166                     "Overscan polynomial order", biasOrder);
     167
    133168
    134169    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image",
    135170                     config->argv[1]);
    136171
     172
     173    // Values common for stars
     174    float scale = psMetadataLookupF32(NULL, arguments, "-scale"); // Plate scale
     175    float zp = psMetadataLookupF32(NULL, arguments, "-zp"); // Zero point
     176    float seeing = psMetadataLookupF32(NULL, arguments, "-seeing"); // Zero point
     177    psMetadataAddF32(config->arguments, PS_LIST_TAIL, "SCALE", 0, "Plate scale (arcsec/pix)", scale);
     178    psMetadataAddF32(config->arguments, PS_LIST_TAIL, "ZEROPOINT", 0, "Photometric zeropoint", zp);
     179    psMetadataAddF32(config->arguments, PS_LIST_TAIL, "SEEING", 0, "Seeing simga (pix)",
     180                     seeing / 2.0 / sqrt(2.0 * log(2.0)) / scale);
     181
     182    const char *starName = psMetadataLookupStr(NULL, arguments, "-stars"); // Name of file with stars
     183    if (starName) {
     184        float ra0 = psMetadataLookupF32(NULL, arguments, "-ra"); // Right Ascension of boresight
     185        float dec0 = psMetadataLookupF32(NULL, arguments, "-dec"); // Declination of boresight
     186        float pa = psMetadataLookupF32(NULL, arguments, "-pa"); // Position angle
     187
     188        if (isnan(ra0) || isnan(dec0) || isnan(pa) || isnan(scale) || isnan(zp) || isnan(seeing)) {
     189            psError(PS_ERR_BAD_PARAMETER_VALUE, false,
     190                    "-ra, -dec, -pa -scale, -zp, -seeing must be specified with -stars");
     191            usage(arguments, config);
     192        }
     193        psMetadataAddF32(config->arguments, PS_LIST_TAIL, "RA", 0, "Boresight RA (radians)",
     194                         ra0 * M_PI / 180.0);
     195        psMetadataAddF32(config->arguments, PS_LIST_TAIL, "DEC", 0, "Boresight Declination (radians)",
     196                         dec0 * M_PI / 180.0);
     197        psMetadataAddF32(config->arguments, PS_LIST_TAIL, "PA", 0, "Boresight position angle (radians)",
     198                         pa * M_PI / 180.0);
     199
     200        psArray *raDecMag = psVectorsReadFromFile(starName, "%f %f %f"); // Vectors for RA Dec Mag
     201        if (!raDecMag || raDecMag->n != 3) {
     202            psError(PS_ERR_IO, false, "Unable to read RA Dec Mag from %s", starName);
     203            psFree(raDecMag);
     204            psFree(arguments);
     205            psFree(config);
     206            exit(PS_EXIT_CONFIG_ERROR);
     207        }
     208
     209        psVector *ra = raDecMag->data[0]; // Star RAs
     210        psVector *dec = raDecMag->data[1]; // Star Declinations
     211        psVector *mag = raDecMag->data[2]; // Star magnitudes
     212        for (long i = 0; i < ra->n; i++) {
     213            ra->data.F32[i] *= M_PI / 180.0;
     214            dec->data.F32[i] *= M_PI / 180.0;
     215        }
     216
     217        psMetadataAddVector(config->arguments, PS_LIST_TAIL, "CATALOG.RA", 0, "Star RAs (radians)", ra);
     218        psMetadataAddVector(config->arguments, PS_LIST_TAIL, "CATALOG.DEC", 0, "Star Decs (radians)", dec);
     219        psMetadataAddVector(config->arguments, PS_LIST_TAIL, "CATALOG.MAG", 0, "Star magnitudes", mag);
     220        psFree(raDecMag);
     221    }
     222
     223    if (psMetadataLookupF32(NULL, config->arguments, "STARS.DENSITY") > 0 &&
     224        (isnan(scale) || isnan(zp) || isnan(seeing))) {
     225        psWarning("-scale, -zp, -seeing must be specified if fake stars are to be generated --- turned off.");
     226        psMetadataAddS32(config->arguments, PS_LIST_TAIL, "STASR.NUM", PS_META_REPLACE,
     227                         "Fake stars turned off", 0);
     228    }
     229
     230
    137231    psFree(arguments);
     232    return;
    138233}
  • trunk/ppSim/src/ppSimCreate.c

    r12834 r12873  
    1313    PS_ASSERT_PTR_NON_NULL(config, NULL);
    1414
    15     psString formatName = psMetadataLookupStr(NULL, config->arguments, "FORMAT"); // Input format name
    16     config->formatName = psMemIncrRefCounter(formatName);
    17 
    18     psMetadata *formats = psMetadataLookupMetadata(NULL, config->camera, "FORMATS"); // The camera formats
    19     if (!formats) {
    20         psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find FORMATS in camera configuration.");
    21         return NULL;
    22     }
    23     psMetadata *format = psMetadataLookupMetadata(NULL, formats, formatName); // Format of interest
    24     if (!format) {
    25         psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find format %s in camera FORMATS.", formatName);
    26         return NULL;
    27     }
    28     pmFPALevel phuLevel = pmFPAPHULevel(format); // Level at which PHU goes
    29 
    3015    pmFPA *fpa = pmFPAConstruct(config->camera); // FPA to contain the observation
    3116    if (!fpa) {
     
    3419    }
    3520
     21    pmFPAfile *file = pmFPAfileDefineOutput(config, fpa, OUTPUT_FILE);
     22    psFree(fpa);
     23    if (!file) {
     24        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to create output file from %s.  "
     25                "Did you forget to specify the format?", OUTPUT_FILE);
     26        return NULL;
     27    }
     28    if (file->type != PM_FPA_FILE_IMAGE) {
     29        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "%s type is not IMAGE", OUTPUT_FILE);
     30        psFree(file);
     31        return NULL;
     32    }
     33
     34    pmFPALevel phuLevel = pmFPAPHULevel(file->format); // Level at which PHU goes
     35
    3636    pmFPAview *view = pmFPAviewAlloc(0);// View for current level
    3737
    3838    if (phuLevel == PM_FPA_LEVEL_FPA) {
    39         if (!pmFPAAddSourceFromView(fpa, "Simulation", view, format)) {
     39        if (!pmFPAAddSourceFromView(fpa, "Simulation", view, file->format)) {
    4040            psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to FPA.");
    4141            psFree(fpa);
     
    4848    while ((chip = pmFPAviewNextChip(view, fpa, 1))) {
    4949        if (phuLevel == PM_FPA_LEVEL_CHIP) {
    50             if (!pmFPAAddSourceFromView(fpa, "Simulation", view, format)) {
     50            if (!pmFPAAddSourceFromView(fpa, "Simulation", view, file->format)) {
    5151                psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to FPA.");
    5252                psFree(fpa);
     
    5959        while ((cell = pmFPAviewNextCell(view, fpa, 1))) {
    6060            if (phuLevel == PM_FPA_LEVEL_CELL) {
    61                 if (!pmFPAAddSourceFromView(fpa, "Simulation", view, format)) {
     61                if (!pmFPAAddSourceFromView(fpa, "Simulation", view, file->format)) {
    6262                    psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to FPA.");
    6363                    psFree(fpa);
     
    7070    psFree(view);
    7171
    72     pmFPAfile *file = pmFPAfileDefineOutput(config, fpa, OUTPUT_FILE);
    73     psFree(fpa);
    74     if (!file) {
    75         psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to create output file from %s", OUTPUT_FILE);
    76         return NULL;
    77     }
    78     if (file->type != PM_FPA_FILE_IMAGE) {
    79         psError(PS_ERR_BAD_PARAMETER_TYPE, true, "%s type is not IMAGE", OUTPUT_FILE);
    80         psFree(file);
    81         return NULL;
    82     }
    83 
    8472    return file;
    8573}
  • trunk/ppSim/src/ppSimLoop.c

    r12834 r12873  
    99#include "ppSim.h"
    1010
    11 #define COMPARE(VALUE,MIN,MAX) \
    12     if (VALUE < MIN) { \
    13         MIN = VALUE; \
    14     } \
    15     if (VALUE > MAX) { \
    16         MAX = VALUE; \
    17     }
     11#define STAR_RANGE_MIN 4.5              // Minimum pixel range for adding stars, sigma
     12#define STAR_BG_FRAC 0.1                // Fraction of background error to go out to when adding stars
     13
     14#define LOG10(X) (log(X)/log(10))       // Base 10 logarithm
     15
     16// Return FPA position, given a cell position; calculations are all done in pixel units
     17static inline float cell2fpa(float pos, // Cell position
     18                             int cell0, // Cell offset
     19                             int cellParity, // Cell parity
     20                             int chip0, // Chip offset
     21                             int chipParity // Chip parity
     22    )
     23{
     24    return (chip0 + chipParity * (cell0 + cellParity * pos));
     25}
     26
     27// Return cell position, given an FPA position; calculations are all done in pixel units
     28static inline float fpa2cell(float pos, // FPA position
     29                             int cell0, // Cell offset
     30                             int cellParity, // Cell parity
     31                             int chip0, // Chip offset
     32                             int chipParity // Chip parity
     33    )
     34{
     35    return ((pos - chip0) * chipParity - cell0) * cellParity;
     36}
     37
     38// Compare a value with minimum and maximum values, replacing where required.
     39#define COMPARE(VALUE,MIN,MAX) { \
     40        if (VALUE < MIN) { \
     41            MIN = VALUE; \
     42        } \
     43        if (VALUE > MAX) { \
     44            MAX = VALUE; \
     45        } \
     46    }
     47
     48// Return bounds of a chip, based on the concepts
     49static psRegion *chipBounds(const pmChip *chip, // Chip for which to determine size
     50                            pmFPAview *view // View for chip
     51    )
     52{
     53    assert(chip);
     54    assert(view);
     55
     56    // Bounds of chip
     57    int xMin = INT_MAX;
     58    int xMax = - INT_MAX;
     59    int yMin = INT_MAX;
     60    int yMax = - INT_MAX;
     61
     62    int x0Chip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.X0");
     63    int y0Chip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.Y0");
     64    int xParityChip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.XPARITY");
     65    int yParityChip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.YPARITY");
     66
     67    if ((xParityChip != 1 && xParityChip != -1) || (yParityChip != 1 && yParityChip != -1)) {
     68        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Chip parities are not set.");
     69        psFree(view);
     70        return NULL;
     71    }
     72
     73    pmCell *cell;                   // Cell from chip
     74    while ((cell = pmFPAviewNextCell(view, chip->parent, 1))) {
     75        int xSize = psMetadataLookupS32(NULL, cell->concepts, "CELL.XSIZE");
     76        int ySize = psMetadataLookupS32(NULL, cell->concepts, "CELL.YSIZE");
     77
     78        if (xSize == 0 || ySize == 0) {
     79            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cell sizes are not set.");
     80            psFree(view);
     81            return NULL;
     82        }
     83
     84        int x0Cell = psMetadataLookupS32(NULL, cell->concepts, "CELL.X0");
     85        int y0Cell = psMetadataLookupS32(NULL, cell->concepts, "CELL.Y0");
     86        int xParityCell = psMetadataLookupS32(NULL, cell->concepts, "CELL.XPARITY");
     87        int yParityCell = psMetadataLookupS32(NULL, cell->concepts, "CELL.YPARITY");
     88
     89        if ((xParityCell != 1 && xParityCell != -1) || (yParityCell != 1 && yParityCell != -1)) {
     90            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cell parities are not set.");
     91            psFree(view);
     92            return NULL;
     93        }
     94
     95        // "Left", "Right", "Bottom" and "Top" don't take into account the parity
     96        int cellLeft = cell2fpa(0, x0Cell, xParityCell, x0Chip, xParityChip);
     97        int cellRight = cell2fpa(xSize, x0Cell, xParityCell, x0Chip, xParityChip);
     98        int cellBottom = cell2fpa(0, y0Cell, yParityCell, y0Chip, yParityChip);
     99        int cellTop = cell2fpa(ySize, y0Cell, yParityCell, y0Chip, yParityChip);
     100
     101        COMPARE(cellLeft, xMin, xMax);
     102        COMPARE(cellRight, xMin, xMax);
     103        COMPARE(cellBottom, yMin, yMax);
     104        COMPARE(cellTop, yMin, yMax);
     105    }
     106
     107    return psRegionAlloc(xMin, xMax, yMin, yMax);
     108}
    18109
    19110// Return bounds of an FPA, based on the concepts
     
    33124    pmChip *chip;                       // Chip from FPA
    34125    while ((chip = pmFPAviewNextChip(view, fpa, 1))) {
    35         int x0Chip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.X0");
    36         int y0Chip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.Y0");
    37         int xParityChip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.XPARITY");
    38         int yParityChip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.YPARITY");
    39 
    40         if ((xParityChip != 1 && xParityChip != -1) || (yParityChip != 1 && yParityChip != -1)) {
    41             psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Chip parities are not set.");
    42             psFree(view);
    43             return NULL;
    44         }
    45 
    46         pmCell *cell;                   // Cell from chip
    47         while ((cell = pmFPAviewNextCell(view, fpa, 1))) {
    48             int xSize = psMetadataLookupS32(NULL, cell->concepts, "CELL.XSIZE");
    49             int ySize = psMetadataLookupS32(NULL, cell->concepts, "CELL.YSIZE");
    50 
    51             if (xSize == 0 || ySize == 0) {
    52                 psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cell sizes are not set.");
    53                 psFree(view);
    54                 return NULL;
    55             }
    56 
    57             int x0Cell = psMetadataLookupS32(NULL, cell->concepts, "CELL.X0");
    58             int y0Cell = psMetadataLookupS32(NULL, cell->concepts, "CELL.Y0");
    59             int xParityCell = psMetadataLookupS32(NULL, cell->concepts, "CELL.XPARITY");
    60             int yParityCell = psMetadataLookupS32(NULL, cell->concepts, "CELL.YPARITY");
    61 
    62             if ((xParityCell != 1 && xParityCell != -1) || (yParityCell != 1 && yParityCell != -1)) {
    63                 psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cell parities are not set.");
    64                 psFree(view);
    65                 return NULL;
    66             }
    67 
    68             // "Left", "Right", "Bottom" and "Top" don't take into account the parity
    69             int cellLeft = x0Chip + xParityChip * x0Cell;
    70             int cellRight = x0Chip + xParityChip * (x0Cell + xParityCell * xSize);
    71             int cellBottom = y0Chip + yParityChip * y0Cell;
    72             int cellTop = y0Chip + yParityChip * (y0Cell + yParityCell * ySize);
    73 
    74             COMPARE(cellLeft, xMin, xMax);
    75             COMPARE(cellRight, xMin, xMax);
    76             COMPARE(cellBottom, yMin, yMax);
    77             COMPARE(cellTop, yMin, yMax);
    78         }
     126        psRegion *bounds = chipBounds(chip, view); // Bounds for chip
     127        COMPARE(bounds->x0, xMin, xMax);
     128        COMPARE(bounds->x1, xMin, xMax);
     129        COMPARE(bounds->y0, yMin, yMax);
     130        COMPARE(bounds->y1, yMin, yMax);
     131        psFree(bounds);
    79132    }
    80133
    81134    return psRegionAlloc(xMin, xMax, yMin, yMax);
    82135}
     136
     137
     138// Add noise to an image
     139static psImage *addNoise(psImage *signal, // Signal image, modified and returned
     140                         const psImage *variance, // Variance image
     141                         psRandom *rng, // Random number generator
     142                         float gain     // CCD gain (e/ADU)
     143    )
     144{
     145    assert(signal->type.type == PS_TYPE_F32);
     146    assert(signal->numCols == variance->numCols && signal->numRows == variance->numRows);
     147
     148    // Add the noise into the image
     149    for (int y = 0; y < signal->numRows; y++) {
     150        for (int x = 0; x < signal->numCols; x++) {
     151            signal->data.F32[y][x] += sqrtf(variance->data.F32[y][x]) * psRandomGaussian(rng);
     152            signal->data.F32[y][x] /= gain; // Converting to ADU
     153        }
     154    }
     155
     156    return signal;
     157}
     158
     159// Apply saturation limit to image
     160static void saturate(psImage *image, // Image to apply saturation
     161                     float saturation // Saturation level
     162    )
     163{
     164    for (int y = 0; y < image->numRows; y++) {
     165        for (int x = 0; x < image->numCols; x++) {
     166            if (image->data.F32[y][x] > saturation) {
     167                image->data.F32[y][x] = saturation;
     168            }
     169        }
     170    }
     171    return;
     172}
     173
     174// Return star flux at a position
     175static inline float starFlux(float x, float y, // Position of interest
     176                             float x0, float y0, // Centre of star
     177                             float seeing // Seeing FWHM (pixels)
     178    )
     179{
     180#ifdef STAR_GAUSSIAN
     181    // Gaussian star
     182    return exp(-0.5 * (PS_SQR(x - x0) + PS_SQR(y - y0)) / PS_SQR(seeing));
     183#else
     184    // Waussian star
     185    float z = (PS_SQR(x - x0) + PS_SQR(y - y0)) / (2.0 * PS_SQR(seeing));
     186    return 1.0 / (1.0 + z + PS_SQR(z));
     187#endif
     188}
     189
     190// Return size of star in pixels
     191static inline int starSize(float noise, float peak, float seeing)
     192{
     193#ifdef STAR_GAUSSIAN
     194    // Gaussian star (solving Gaussian)
     195    float target = STAR_BG_FRAC * seeing * sqrt(M_2_PI) * noise / peak;
     196    return target < 1.0 ? 2.0 * seeing * sqrtf(-logf(target)) + 0.5 : seeing * STAR_RANGE_MIN;
     197#else
     198    // Waussian star (solving Waussian where z >> 1 and peak/sqrt(bg) >> 1)
     199    float target = STAR_BG_FRAC * noise / peak;
     200    return PS_MAX(sqrtf(1.0 / target) + 0.5, seeing * STAR_RANGE_MIN);
     201#endif
     202}
     203
     204// Add a star into the signal and variance images
     205static void star(psImage *signal,       // Signal image, to which to add star
     206                 psImage *variance,     // Variance image, to which to add star
     207                 float x0, float y0,    // Position of star
     208                 float peak,            // Peak flux of star
     209                 float noise,           // Rough noise estimate
     210                 float seeing,           // Seeing for star
     211                 const psImage *correction // Exposure correction as a function of position
     212    )
     213{
     214    int size = starSize(noise, peak, seeing); // Size of star
     215
     216    // Range in image pixels on which to add star
     217    int xMin = PS_MAX(0, x0 - size);
     218    int xMax = PS_MIN(signal->numCols - 1, x0 + size);
     219    int yMin = PS_MAX(0, y0 - size);
     220    int yMax = PS_MIN(signal->numRows - 1, y0 + size);
     221
     222    for (int y = yMin; y <= yMax; y++) {
     223        for (int x = xMin; x <= xMax; x++) {
     224            float star = peak * correction->data.F32[y][x] * starFlux(x, y, x0, y0, seeing); // Star flux
     225            signal->data.F32[y][x] += star;
     226            variance->data.F32[y][x] += star;
     227        }
     228    }
     229    return;
     230}
     231
    83232
    84233
     
    92241    assert(fpa);
    93242
     243    bool mdok;                          // Status of MD lookups
     244
    94245    psRegion *bounds = fpaBounds(fpa);  // Bounds of FPA
    95246    if (!bounds || (bounds->x0 == 0.0 && bounds->x1 == 0.0) || (bounds->y0 == 0.0 && bounds->y1 == 0.0)) {
     
    98249    }
    99250
     251    psVector *ra = psMetadataLookupPtr(NULL, config->arguments, "CATALOG.RA"); // Star RAs
     252    psVector *dec = psMetadataLookupPtr(NULL, config->arguments, "CATALOG.DEC"); // Star Declinations
     253    psVector *mag = psMetadataLookupPtr(NULL, config->arguments, "CATALOG.MAG"); // Star magnitudes
     254    float seeing = psMetadataLookupF32(NULL, config->arguments, "SEEING"); // Seeing sigma (pix)
     255    float scale = psMetadataLookupF32(NULL, config->arguments, "SCALE"); // Plate scale (arcsec/pixel)
     256    scale *= M_PI / 3600.0 / 180.0; // Convert to radians/pixel
     257    float zp = psMetadataLookupF32(NULL, config->arguments, "ZEROPOINT"); // Photometric zero point
     258
     259    if (ra && dec && mag) {
     260        // Convert star ra,dec to pixels from FPA centre
     261        float ra0 = psMetadataLookupF32(NULL, config->arguments, "RA"); // Boresight RA (radians)
     262        float dec0 = psMetadataLookupF32(NULL, config->arguments, "DEC"); // Boresight Dec (radians)
     263        float pa = psMetadataLookupF32(NULL, config->arguments, "PA"); // Position angle (radians)
     264
     265        // Conversion loop
     266        for (long i = 0; i < ra->n; i++) {
     267            float div = sin(dec->data.F32[i]) * sin(dec0) +
     268                cos(dec->data.F32[i]) * cos(dec0) * cos(ra->data.F32[i] - ra0); // Divisor
     269
     270            // Convert to x,y position on tangent plane, in pixels
     271            float xi = cos(dec->data.F32[i]) * sin(ra->data.F32[i] - ra0) / div / scale;
     272            float eta = (sin(dec->data.F32[i]) * cos(dec0) -
     273                cos(dec->data.F32[i]) * sin(dec0) * cos(ra->data.F32[i] - ra0)) / div / scale;
     274
     275            // Apply rotation
     276            ra->data.F32[i] = cos(pa) * xi - sin(pa) * eta;
     277            dec->data.F32[i] = sin(pa) * xi + cos(pa) * eta;
     278            ra->data.F32[i] = xi;
     279            dec->data.F32[i] = eta;
     280
     281            // Convert magnitude to peak flux
     282            mag->data.F32[i] = powf(10.0, -0.4 * (mag->data.F32[i] - zp)) / sqrt(2.0*M_PI) / seeing;
     283        }
     284    }
     285
    100286    float biasLevel = psMetadataLookupF32(NULL, config->arguments, "BIAS.LEVEL"); // Bias level
    101287    float biasRange = psMetadataLookupF32(NULL, config->arguments, "BIAS.RANGE"); // Bias range
     288    int biasOrder = psMetadataLookupS32(NULL, config->arguments, "BIAS.ORDER"); // Bias order
    102289    float darkRate = psMetadataLookupF32(NULL, config->arguments, "DARK.RATE"); // Dark rate
    103290    float flatSigma = psMetadataLookupF32(NULL, config->arguments, "FLAT.SIGMA"); // Flat-field coefficient
     
    105292    float shutterTime = psMetadataLookupF32(NULL, config->arguments, "SHUTTER.TIME"); // Shutter time
    106293    float skyRate = psMetadataLookupF32(NULL, config->arguments, "SKY.RATE"); // Sky rate
     294    float starsLum = psMetadataLookupF32(NULL, config->arguments, "STARS.LUM"); // Star luminosity func slope
     295    float starsMag = psMetadataLookupF32(NULL, config->arguments, "STARS.MAG"); // Star brightest magnitude
     296    float starsDensity = psMetadataLookupF32(NULL, config->arguments, "STARS.DENSITY"); // Density of fakes
    107297
    108298    float expTime = psMetadataLookupF32(NULL, config->arguments, "EXPTIME"); // Exposure time
    109299    const char *filter = psMetadataLookupStr(NULL, config->arguments, "FILTER"); // Filter name
    110300    ppSimType type = psMetadataLookupS32(NULL, config->arguments, "TYPE"); // Type of image to simulate
     301
     302    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSIM_RECIPE); // Recipe
    111303
    112304    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
     
    139331        int yParityChip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.YPARITY");
    140332
     333        // Correct chip offsets so that boresight is in the middle of the FPA
     334        x0Chip -= 0.5 * (bounds->x1 - bounds->x0);
     335        y0Chip -= 0.5 * (bounds->y1 - bounds->y0);
     336
     337#if 0
     338        // Add random stars
     339        if (type == PPSIM_TYPE_OBJECT && starsDensity > 0) {
     340            // Peak fluxes: faintest and brightest levels for random stars
     341            float faint = roughNoise;
     342            float bright = powf(10.0, -0.4 * (starsMag - zp)) / sqrt(2.0*M_PI) / seeing * expTime;
     343            if (bright < faint) {
     344                psLogMsg("ppSim", PS_LOG_INFO,
     345                         "Image noise is above brightest random star --- no random stars added.");
     346            } else {
     347                // Normalisation, set by the specified stellar density at the specified bright
     348                // magnitude
     349                float norm = starsDensity * numCols * numRows * PS_SQR(scale * 180.0 / M_PI) /
     350                    powf(bright, starsLum);
     351
     352                // Total number of stars down to the faint flux end
     353                long num = expf(logf(norm) + starsLum * logf(faint)) + 0.5;
     354
     355                printf("Adding %ld stars to %f\n", num, faint);
     356
     357                for (long j = 0; j < num; j++) {
     358                    float flux = expf((logf(j + 1) - logf(norm)) / starsLum); // Flux of star
     359
     360                    // TO DO: put stars on chips, rather than cells, so they can overlap cell
     361                    // boundaries
     362
     363                    // Position on cell
     364                    float x = psRandomUniform(rng) * numCols;
     365                    float y = psRandomUniform(rng) * numRows;
     366                    star(signal, variance, x, y, flux, roughNoise, seeing, expCorr);
     367                }
     368            }
     369        }
     370#endif
     371
    141372        pmCell *cell;                   // Cell from chip
    142373        while ((cell = pmFPAviewNextCell(view, fpa, 1))) {
     
    152383            float gain = psMetadataLookupF32(NULL, cell->concepts, "CELL.GAIN"); // CCD gain, e/ADU
    153384            if (isnan(gain)) {
    154                 psWarning("CELL.GAIN is not set; reverting to recipe value.");
    155                 gain = psMetadataLookupF32(NULL, config->arguments, "GAIN");
    156             }
    157             float readnoise = psMetadataLookupF32(NULL, cell->concepts, "CELL.RDNOISE"); // CCD read noise, e
     385                psWarning("CELL.GAIN is not set; reverting to recipe value GAIN.");
     386                gain = psMetadataLookupF32(&mdok, recipe, "GAIN");
     387                if (!mdok) {
     388                    psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find GAIN in recipe.");
     389                    psFree(bounds);
     390                    psFree(rng);
     391                    return PS_EXIT_CONFIG_ERROR;
     392                }
     393            }
     394            float readnoise = psMetadataLookupF32(NULL, cell->concepts, "CELL.READNOISE");// CCD read noise, e
    158395            if (isnan(readnoise)) {
    159                 psWarning("CELL.RDNOISE is not set; reverting to recipe value.");
    160                 readnoise = psMetadataLookupF32(NULL, config->arguments, "RDNOISE");
     396                psWarning("CELL.READNOISE is not set; reverting to recipe value READNOISE.");
     397                readnoise = psMetadataLookupF32(&mdok, recipe, "READNOISE");
     398                if (!mdok) {
     399                    psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find READNOISE in recipe.");
     400                    psFree(bounds);
     401                    psFree(rng);
     402                    return PS_EXIT_CONFIG_ERROR;
     403                }
     404            }
     405
     406            float saturation = psMetadataLookupF32(NULL, cell->concepts, "CELL.SATURATION");
     407            if (isnan(saturation)) {
     408                psWarning("CELL.SATURATION is not set; reverting to recipe value SATURATION.");
     409                readnoise = psMetadataLookupF32(&mdok, recipe, "SATURATION");
     410                if (!mdok) {
     411                    psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find SATURATION in recipe.");
     412                    psFree(bounds);
     413                    psFree(rng);
     414                    return PS_EXIT_CONFIG_ERROR;
     415                }
     416            }
     417
     418            int readdir = psMetadataLookupS32(NULL, cell->concepts, "CELL.READDIR"); // Read direction
     419            if (readdir != 1) {
     420                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "CELL.READDIR = 1 is the only supported mode.");
     421                psFree(bounds);
     422                psFree(rng);
     423                return PS_EXIT_CONFIG_ERROR;
     424            }
     425
     426            psList *biassec = psMetadataLookupPtr(NULL, cell->concepts, "CELL.BIASSEC"); // Bias regions
     427            int numBias = psListLength(biassec); // Number of bias sections
     428            psVector *biasCols;
     429            if (numBias > 0) {
     430                biasCols = psVectorAlloc(numBias, PS_TYPE_S32);
     431                psListIterator *iter = psListIteratorAlloc(biassec, PS_LIST_HEAD, false); // Iterator
     432                psRegion *bias;         // Bias region, from iteration
     433                int i = 0;              // Counter
     434                while ((bias = psListGetAndIncrement(iter))) {
     435                    biasCols->data.S32[i++] = bias->x1 = bias->x0;
     436                }
     437            } else {
     438                biasCols = psVectorAlloc(1, PS_TYPE_S32);
     439                biasCols->data.S32[0] = psMetadataLookupS32(&mdok, recipe, "OVERSCAN.SIZE");
     440                if (!mdok) {
     441                    psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find OVERSCAN.SIZE in recipe.");
     442                    psFree(biasCols);
     443                    psFree(bounds);
     444                    psFree(rng);
     445                    return PS_EXIT_CONFIG_ERROR;
     446                }
     447                psRegion *newBias = psRegionAlloc(NAN, NAN, NAN, NAN); // New bias region, to be set later
     448                biassec = psListAlloc(newBias);
     449                psFree(newBias);
     450                psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.BIASSEC", PS_DATA_LIST | PS_META_REPLACE,
     451                              "Bias sections", biassec);
     452                psFree(biassec);
     453                numBias = 1;
    161454            }
    162455
     
    172465                psImage *variance = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Noise in pixels
    173466
     467                // Polynomial for bias
     468                psPolynomial1D *biasPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, biasOrder);
     469                for (int j = 0; j < biasOrder + 1; j++) {
     470                    biasPoly->coeff[j] = biasRange * psRandomGaussian(rng);
     471                }
     472                psVector *rowBias = psVectorAlloc(numRows, PS_TYPE_F32); // Bias value, per row
     473                int biasOffset = 0.5 * numRows * psRandomUniform(rng); // Offset to prevent common pattern
     474
     475                psImage *expCorr = NULL; // Exposure correction per pixel, for adding objects
     476                if (type == PPSIM_TYPE_OBJECT) {
     477                    expCorr = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     478                }
     479
    174480                for (int y = 0; y < numRows; y++) {
    175481                    // Adjust bias level for this row
    176                     float rowBias = biasLevel += psRandomGaussian(rng) * biasRange;
    177                     float yFPA = (y0Chip + yParityChip * (y0Cell + yParityCell * y) - 2 * bounds->y0) /
     482                    rowBias->data.F32[y] = psPolynomial1DEval(biasPoly, (float)(y + biasOffset) /
     483                                                              (float)numRows - 0.5) + biasLevel;
     484                    float yFPA = cell2fpa(y, y0Cell, yParityCell, y0Chip, yParityChip) * 2.0 /
    178485                        (bounds->y1 - bounds->y0); // Relative y position in FPA
    179486
    180487                    for (int x = 0; x < numCols; x++) {
    181                         float xFPA = (x0Chip + xParityChip * (x0Cell + xParityCell * x) - 2 * bounds->x0) /
     488                        float xFPA = cell2fpa(x, x0Cell, xParityCell, x0Chip, xParityChip) * 2.0 /
    182489                            (bounds->x1 - bounds->x0); // Relative x position in FPA
    183490
    184491                        // Bias level
    185                         signal->data.F32[y][x] = rowBias;
     492                        signal->data.F32[y][x] = rowBias->data.F32[y];
    186493                        variance->data.F32[y][x] = PS_SQR(readnoise);
    187494
     
    211518                            continue;
    212519                        }
    213 
     520                        expCorr->data.F32[y][x] = flatValue * realExpTime / expTime;
    214521                        // Sky background
    215522                        float skyFlux = skyRate * realExpTime * flatValue; // Flux from sky
     
    219526                        // TO DO: Add fringes
    220527
    221                         // TO DO: Add stars
    222528                    }
    223529                }
    224530
    225                 // TO DO: Add overscan
    226 
    227 #if 0
    228                 // Add the noise into the image
    229                 for (int y = 0; y < numRows; y++) {
    230                     for (int x = 0; x < numCols; x++) {
    231                         signal->data.F32[y][x] /= gain; // Converting to ADU
    232                         signal->data.F32[y][x] += sqrtf(variance->data.F32[y][x] / gain) *
    233                             psRandomGaussian(rng);
     531                // Rough noise estimate, appropriate for entire cell
     532                float roughNoise = sqrtf(PS_SQR(readnoise) + (darkRate + skyRate) * expTime);
     533
     534
     535                // XXX Not sure I've got the scaling right on the peak flux: it's multiplied by the expTime
     536                // here, and the exposure up above.
     537
     538                // Add specified (catalogue) stars
     539                if (type == PPSIM_TYPE_OBJECT && ra && dec && mag) {
     540                    for (long j = 0; j < ra->n; j++) {
     541                        // Position on the cell
     542                        float x = fpa2cell(ra->data.F32[j], x0Cell, xParityCell, x0Chip, xParityChip);
     543                        float y = fpa2cell(dec->data.F32[j], y0Cell, yParityCell, y0Chip, yParityChip);
     544                        star(signal, variance, x, y, mag->data.F32[j] * expTime,
     545                             roughNoise, seeing, expCorr);
    234546                    }
    235547                }
    236 #endif
    237                 readout->image = signal;
     548
     549                // Add random stars
     550                if (type == PPSIM_TYPE_OBJECT && starsDensity > 0) {
     551                    // Peak fluxes: faintest and brightest levels for random stars
     552                    float faint = roughNoise;
     553                    float bright = powf(10.0, -0.4 * (starsMag - zp)) / sqrt(2.0*M_PI) / seeing * expTime;
     554                    if (bright < faint) {
     555                        psLogMsg("ppSim", PS_LOG_INFO,
     556                                 "Image noise is above brightest random star --- no random stars added.");
     557                    } else {
     558                        // Normalisation, set by the specified stellar density at the specified bright
     559                        // magnitude
     560                        float norm = starsDensity * numCols * numRows * PS_SQR(scale * 180.0 / M_PI) /
     561                            powf(bright, starsLum);
     562
     563                        // Total number of stars down to the faint flux end
     564                        long num = expf(logf(norm) + starsLum * logf(faint)) + 0.5;
     565
     566                        printf("Adding %ld stars to %f\n", num, faint);
     567
     568                        for (long j = 0; j < num; j++) {
     569                            float flux = expf((logf(j + 1) - logf(norm)) / starsLum); // Flux of star
     570
     571                            // TO DO: put stars on chips, rather than cells, so they can overlap cell
     572                            // boundaries
     573
     574                            // Position on cell
     575                            float x = psRandomUniform(rng) * numCols;
     576                            float y = psRandomUniform(rng) * numRows;
     577                            star(signal, variance, x, y, flux, roughNoise, seeing, expCorr);
     578                        }
     579                    }
     580                }
     581
     582                readout->image = addNoise(signal, variance, rng, gain);
     583                saturate(readout->image, saturation);
    238584                psFree(variance);
    239585
     586                psFree(expCorr);
     587
     588                // Add overscan
     589                for (int j = 0; j < numBias; j++) {
     590                    psImage *signal = psImageAlloc(biasCols->data.S32[j], numRows, PS_TYPE_F32); // Overscan
     591                    for (int y = 0; y < signal->numRows; y++) {
     592                        for (int x = 0; x < signal->numCols; x++) {
     593                            signal->data.F32[y][x] = rowBias->data.F32[y];
     594                        }
     595                    }
     596                    psImage *variance = psImageAlloc(biasCols->data.S32[j], numRows, PS_TYPE_F32); // Variance
     597                    psImageInit(variance, PS_SQR(readnoise));
     598                    addNoise(signal, variance, rng, gain);
     599                    psFree(variance);
     600
     601                    psListAdd(readout->bias, PS_LIST_TAIL, signal);
     602                    psFree(signal);     // Drop reference
     603                }
     604
     605                psFree(rowBias);
    240606                psFree(readout);        // Drop reference
    241607            }
     608
    242609
    243610            psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.EXPOSURE", PS_META_REPLACE,
Note: See TracChangeset for help on using the changeset viewer.