Changeset 12873
- Timestamp:
- Apr 17, 2007, 6:29:18 PM (19 years ago)
- Location:
- trunk/ppSim/src
- Files:
-
- 3 edited
-
ppSimArguments.c (modified) (6 diffs)
-
ppSimCreate.c (modified) (5 diffs)
-
ppSimLoop.c (modified) (10 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/ppSim/src/ppSimArguments.c
r12834 r12873 16 16 { 17 17 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]); 19 19 fprintf(stderr, "\n"); 20 20 psArgumentHelp(arguments); … … 35 35 float value = psMetadataLookupF32(NULL, arguments, argName); // Bias level 36 36 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) { 39 40 psErrorStackPrint(stderr, "Unable to find %s in recipe %s", recipeName, PPSIM_RECIPE); 40 41 psFree((psPtr)arguments); … … 43 44 } 44 45 } 45 46 46 return psMetadataAddF32(target, PS_LIST_TAIL, recipeName, 0, NULL, value); 47 47 } 48 49 48 50 49 void ppSimArguments(pmConfig *config) … … 64 63 psMetadataAddF32(arguments, PS_LIST_TAIL, "-shuttertime", 0, "Shutter time (s)", NAN); 65 64 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); 66 75 67 76 if (!config->camera) { … … 74 83 } 75 84 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 } 82 105 83 106 const char *typeStr = psMetadataLookupStr(NULL, arguments, "-type"); // Exposure type … … 124 147 } 125 148 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 133 168 134 169 psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", 135 170 config->argv[1]); 136 171 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 137 231 psFree(arguments); 232 return; 138 233 } -
trunk/ppSim/src/ppSimCreate.c
r12834 r12873 13 13 PS_ASSERT_PTR_NON_NULL(config, NULL); 14 14 15 psString formatName = psMetadataLookupStr(NULL, config->arguments, "FORMAT"); // Input format name16 config->formatName = psMemIncrRefCounter(formatName);17 18 psMetadata *formats = psMetadataLookupMetadata(NULL, config->camera, "FORMATS"); // The camera formats19 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 interest24 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 goes29 30 15 pmFPA *fpa = pmFPAConstruct(config->camera); // FPA to contain the observation 31 16 if (!fpa) { … … 34 19 } 35 20 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 36 36 pmFPAview *view = pmFPAviewAlloc(0);// View for current level 37 37 38 38 if (phuLevel == PM_FPA_LEVEL_FPA) { 39 if (!pmFPAAddSourceFromView(fpa, "Simulation", view, f ormat)) {39 if (!pmFPAAddSourceFromView(fpa, "Simulation", view, file->format)) { 40 40 psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to FPA."); 41 41 psFree(fpa); … … 48 48 while ((chip = pmFPAviewNextChip(view, fpa, 1))) { 49 49 if (phuLevel == PM_FPA_LEVEL_CHIP) { 50 if (!pmFPAAddSourceFromView(fpa, "Simulation", view, f ormat)) {50 if (!pmFPAAddSourceFromView(fpa, "Simulation", view, file->format)) { 51 51 psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to FPA."); 52 52 psFree(fpa); … … 59 59 while ((cell = pmFPAviewNextCell(view, fpa, 1))) { 60 60 if (phuLevel == PM_FPA_LEVEL_CELL) { 61 if (!pmFPAAddSourceFromView(fpa, "Simulation", view, f ormat)) {61 if (!pmFPAAddSourceFromView(fpa, "Simulation", view, file->format)) { 62 62 psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to FPA."); 63 63 psFree(fpa); … … 70 70 psFree(view); 71 71 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 84 72 return file; 85 73 } -
trunk/ppSim/src/ppSimLoop.c
r12834 r12873 9 9 #include "ppSim.h" 10 10 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 17 static 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 28 static 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 49 static 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 } 18 109 19 110 // Return bounds of an FPA, based on the concepts … … 33 124 pmChip *chip; // Chip from FPA 34 125 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); 79 132 } 80 133 81 134 return psRegionAlloc(xMin, xMax, yMin, yMax); 82 135 } 136 137 138 // Add noise to an image 139 static 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 160 static 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 175 static 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 191 static 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 205 static 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 83 232 84 233 … … 92 241 assert(fpa); 93 242 243 bool mdok; // Status of MD lookups 244 94 245 psRegion *bounds = fpaBounds(fpa); // Bounds of FPA 95 246 if (!bounds || (bounds->x0 == 0.0 && bounds->x1 == 0.0) || (bounds->y0 == 0.0 && bounds->y1 == 0.0)) { … … 98 249 } 99 250 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 100 286 float biasLevel = psMetadataLookupF32(NULL, config->arguments, "BIAS.LEVEL"); // Bias level 101 287 float biasRange = psMetadataLookupF32(NULL, config->arguments, "BIAS.RANGE"); // Bias range 288 int biasOrder = psMetadataLookupS32(NULL, config->arguments, "BIAS.ORDER"); // Bias order 102 289 float darkRate = psMetadataLookupF32(NULL, config->arguments, "DARK.RATE"); // Dark rate 103 290 float flatSigma = psMetadataLookupF32(NULL, config->arguments, "FLAT.SIGMA"); // Flat-field coefficient … … 105 292 float shutterTime = psMetadataLookupF32(NULL, config->arguments, "SHUTTER.TIME"); // Shutter time 106 293 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 107 297 108 298 float expTime = psMetadataLookupF32(NULL, config->arguments, "EXPTIME"); // Exposure time 109 299 const char *filter = psMetadataLookupStr(NULL, config->arguments, "FILTER"); // Filter name 110 300 ppSimType type = psMetadataLookupS32(NULL, config->arguments, "TYPE"); // Type of image to simulate 301 302 psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSIM_RECIPE); // Recipe 111 303 112 304 psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator … … 139 331 int yParityChip = psMetadataLookupS32(NULL, chip->concepts, "CHIP.YPARITY"); 140 332 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 141 372 pmCell *cell; // Cell from chip 142 373 while ((cell = pmFPAviewNextCell(view, fpa, 1))) { … … 152 383 float gain = psMetadataLookupF32(NULL, cell->concepts, "CELL.GAIN"); // CCD gain, e/ADU 153 384 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 158 395 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; 161 454 } 162 455 … … 172 465 psImage *variance = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Noise in pixels 173 466 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 174 480 for (int y = 0; y < numRows; y++) { 175 481 // 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 / 178 485 (bounds->y1 - bounds->y0); // Relative y position in FPA 179 486 180 487 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 / 182 489 (bounds->x1 - bounds->x0); // Relative x position in FPA 183 490 184 491 // Bias level 185 signal->data.F32[y][x] = rowBias ;492 signal->data.F32[y][x] = rowBias->data.F32[y]; 186 493 variance->data.F32[y][x] = PS_SQR(readnoise); 187 494 … … 211 518 continue; 212 519 } 213 520 expCorr->data.F32[y][x] = flatValue * realExpTime / expTime; 214 521 // Sky background 215 522 float skyFlux = skyRate * realExpTime * flatValue; // Flux from sky … … 219 526 // TO DO: Add fringes 220 527 221 // TO DO: Add stars222 528 } 223 529 } 224 530 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); 234 546 } 235 547 } 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); 238 584 psFree(variance); 239 585 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); 240 606 psFree(readout); // Drop reference 241 607 } 608 242 609 243 610 psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.EXPOSURE", PS_META_REPLACE,
Note:
See TracChangeset
for help on using the changeset viewer.
