{ "cells": [ { "cell_type": "markdown", "id": "44ef49d8", "metadata": {}, "source": [ "# Calibration and error propagation\n", "\n", "Previously, we compared `opticam` to [`Source-Extractor`](https://sextractor.readthedocs.io/en/latest/index.html) and showed that the two programs produce [very similar results](sextractor_comparison.ipynb). However, we neglected image calibrations in that example to keep things simple.\n", "\n", "When performing image calibrations, `opticam` will automatically propagate the errors; in this notebook, I will prove that `opticam` does this correctly. Additionally, I will also verify that calibrations applied by `opticam` are correct. \n", "\n", "## Generating data\n", "\n", "First, we need to generate some data that contain only noise. We will include typical values for the bias and dark current, and simulate long exposures to allow for a significant dark noise contribution. We will also generate flat-field images, which introduce some additional noise. These images will be generated as if they were produced by OPTICAM-MX, but we'll only simulate data from a single camera for simplicity.\n", "\n", "Let's define the properties of these generated data: " ] }, { "cell_type": "code", "execution_count": 1, "id": "fdc354ea", "metadata": { "execution": { "iopub.execute_input": "2026-02-16T18:29:00.215066Z", "iopub.status.busy": "2026-02-16T18:29:00.214974Z", "iopub.status.idle": "2026-02-16T18:29:00.217524Z", "shell.execute_reply": "2026-02-16T18:29:00.217115Z" } }, "outputs": [], "source": [ "X, Y = 512, 512 # image dimensions\n", "\n", "gain = 1. # gain of OPTICAM-MX\n", "read_noise = 1.1 # nominal read noise of OPTICAM-MX\n", "dark_curr = 0.1 # nominal dark current of OPTICAM-MX\n", "texp = 30. # exposure time of science images (in seconds)\n", "flat_texp = 3. # exposure time of flat-field images (in seconds)\n", "fltr = 'g' # image filter\n", "\n", "binning = f'{2048 // X}x{2048 // Y}' # binning mode\n", "\n", "bias_value = 400. # typical bias according to Handbook of CCD Astronomy, Howell\n", "\n", "flat_level = 40000. # mean flux of flats\n", "\n", "# dark noise contributions\n", "dark_signal = dark_curr * texp\n", "flat_dark_signal = dark_curr * flat_texp\n", "\n", "n_calibration_images = 30 # number of images to generate for each calibration step\n", "\n", "science_flux = 1000. # mean flux in science images (before noise)" ] }, { "cell_type": "markdown", "id": "e1eec252", "metadata": {}, "source": [ "We can compute the analytical variances for each calibration:" ] }, { "cell_type": "code", "execution_count": 2, "id": "ecb6071d", "metadata": { "execution": { "iopub.execute_input": "2026-02-16T18:29:00.218439Z", "iopub.status.busy": "2026-02-16T18:29:00.218339Z", "iopub.status.idle": "2026-02-16T18:29:00.242771Z", "shell.execute_reply": "2026-02-16T18:29:00.242439Z" } }, "outputs": [], "source": [ "import numpy as np\n", "\n", "true_bias_var = read_noise**2 / n_calibration_images\n", "\n", "true_dark_var = np.pi / (2 * n_calibration_images) * (dark_signal + read_noise**2 + true_bias_var)\n", "\n", "true_flat_var = np.pi / (2 * n_calibration_images) * (flat_level + read_noise**2 + true_bias_var + flat_dark_signal) / flat_level**2\n", "effective_flat_var = true_flat_var * science_flux**2" ] }, { "cell_type": "markdown", "id": "70542fa0", "metadata": {}, "source": [ "I have included $\\frac{\\pi}{2 N}$ factors in the dark and flat variances since `opticam` computes master darks and master flats using the median (to remove outliers due to, e.g., cosmic rays), while the master bias is computed using the mean. Note that the dark and flat-field variances include a bias contribution, and the flat-field variance further includes a (small) dark-noise contribution. The flat variance also has an additional term because it acts multiplicatively, whereas the bias and dark corrections are additive.\n", "\n", "Now let's create the directories for storing our simulated data:" ] }, { "cell_type": "code", "execution_count": 3, "id": "af934725", "metadata": { "execution": { "iopub.execute_input": "2026-02-16T18:29:00.244516Z", "iopub.status.busy": "2026-02-16T18:29:00.244388Z", "iopub.status.idle": "2026-02-16T18:29:00.246846Z", "shell.execute_reply": "2026-02-16T18:29:00.246506Z" } }, "outputs": [], "source": [ "from pathlib import Path\n", "\n", "out_dir = Path('out')\n", "\n", "if not out_dir.joinpath('biases').is_dir():\n", " out_dir.joinpath('biases').mkdir(parents=True)\n", "\n", "if not out_dir.joinpath('darks').is_dir():\n", " out_dir.joinpath('darks').mkdir(parents=True)\n", "\n", "if not out_dir.joinpath('flats').is_dir():\n", " out_dir.joinpath('flats').mkdir(parents=True)\n", "\n", "if not out_dir.joinpath('flat_darks').is_dir():\n", " out_dir.joinpath('flat_darks').mkdir(parents=True)" ] }, { "cell_type": "markdown", "id": "97a40484", "metadata": {}, "source": [ "We'll use `numpy`'s `default_rng()` Generator to generate random noise. For reproducibility, I'll pass a fixed seed:" ] }, { "cell_type": "code", "execution_count": 4, "id": "33f7ef94", "metadata": { "execution": { "iopub.execute_input": "2026-02-16T18:29:00.247653Z", "iopub.status.busy": "2026-02-16T18:29:00.247468Z", "iopub.status.idle": "2026-02-16T18:29:00.252556Z", "shell.execute_reply": "2026-02-16T18:29:00.252232Z" } }, "outputs": [], "source": [ "rng = np.random.default_rng(42)" ] }, { "cell_type": "markdown", "id": "8aeb4644", "metadata": {}, "source": [ "Let's now generate our calibration images.\n", "\n", "### Biases\n", "\n", "Bias images should be taken with the telescope shutter closed and have an exposure time of 0.0 seconds:" ] }, { "cell_type": "code", "execution_count": 5, "id": "b5186356", "metadata": { "execution": { "iopub.execute_input": "2026-02-16T18:29:00.253414Z", "iopub.status.busy": "2026-02-16T18:29:00.253294Z", "iopub.status.idle": "2026-02-16T18:29:04.128993Z", "shell.execute_reply": "2026-02-16T18:29:04.128568Z" } }, "outputs": [], "source": [ "from astropy.io import fits\n", "\n", "for i in range(n_calibration_images):\n", " bias = rng.normal(bias_value, read_noise, size=(X, Y))\n", " \n", " hdu = fits.PrimaryHDU(bias)\n", " \n", " hdu.header['EXPOSURE'] = 0.0\n", " hdu.header[\"BINNING\"] = binning\n", " hdu.header[\"GAIN\"] = gain\n", " hdu.header['FILTER'] = fltr\n", " hdu.header['INSTRUME'] = 'OPTICAM'\n", " \n", " path = out_dir / 'biases' / f'image_{i}.fits.gz'\n", " if not path.is_file():\n", " hdu.writeto(path, overwrite=True)" ] }, { "cell_type": "markdown", "id": "6a7bbb48", "metadata": {}, "source": [ "Let's pass these bias images to a `BiasCorrector`: " ] }, { "cell_type": "code", "execution_count": 6, "id": "08f054a1", "metadata": { "execution": { "iopub.execute_input": "2026-02-16T18:29:04.130409Z", "iopub.status.busy": "2026-02-16T18:29:04.130292Z", "iopub.status.idle": "2026-02-16T18:29:06.178438Z", "shell.execute_reply": "2026-02-16T18:29:06.178042Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\r", "[OPTICAM] Scanning data directory: 0%| |[00:00" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from matplotlib import pyplot as plt\n", "\n", "fig, axes = plt.subplots(\n", " nrows=2,\n", " ncols=2,\n", " figsize=(12.8, 12.8),\n", " )\n", "\n", "axes[0, 0].set_title('Bias')\n", "axes[0, 0].hist(np.mean(bias_vars / true_bias_var, axis=0).flatten(),\n", " bins=100,\n", " histtype='step',\n", " color='k',\n", " )\n", "\n", "axes[0, 1].set_title('Dark')\n", "axes[0, 1].hist(np.mean(dark_vars / true_dark_var, axis=0).flatten(),\n", " bins=100,\n", " histtype='step',\n", " color='k',\n", " )\n", "\n", "axes[1, 0].set_title('Flat')\n", "axes[1, 0].hist(np.mean(flat_vars / effective_flat_var, axis=0).flatten(),\n", " bins=100,\n", " histtype='step',\n", " color='k',\n", " )\n", "\n", "axes[1, 1].set_title('Total')\n", "axes[1, 1].hist((empirical_err / measured_err).flatten(),\n", " bins=100,\n", " histtype='step',\n", " color='k',\n", " )\n", "\n", "for ax in axes.flatten():\n", " ax.axvline(1, c='r', ls='-')\n", " ax.set_xlabel('Measured / expected')\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "5ba1508f", "metadata": {}, "source": [ "Clearly, `opticam` is correctly propagating the errors from each calibration. However, this doesn't prove that the calibrations have actually been applied correctly. To verify this, we can check that the mean flux of the calibrated images is close to the expected value:" ] }, { "cell_type": "code", "execution_count": 21, "id": "c591828b", "metadata": { "execution": { "iopub.execute_input": "2026-02-16T18:29:24.968552Z", "iopub.status.busy": "2026-02-16T18:29:24.968464Z", "iopub.status.idle": "2026-02-16T18:29:24.984782Z", "shell.execute_reply": "2026-02-16T18:29:24.984449Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.9999779237287508\n" ] } ], "source": [ "print(np.mean(calibrated_images / science_flux))" ] }, { "cell_type": "code", "execution_count": 22, "id": "f52323de", "metadata": { "execution": { "iopub.execute_input": "2026-02-16T18:29:24.985643Z", "iopub.status.busy": "2026-02-16T18:29:24.985567Z", "iopub.status.idle": "2026-02-16T18:29:25.001766Z", "shell.execute_reply": "2026-02-16T18:29:25.001296Z" }, "tags": [ "remove-cell" ] }, "outputs": [], "source": [ "assert np.isclose(np.mean(calibrated_images / science_flux), 1., rtol=0.01, atol=0)" ] }, { "cell_type": "markdown", "id": "03ec43d8", "metadata": {}, "source": [ "Another reassuring result! Comparing the calibrated fluxes graphically:" ] }, { "cell_type": "code", "execution_count": 23, "id": "d75e9d7a", "metadata": { "execution": { "iopub.execute_input": "2026-02-16T18:29:25.002927Z", "iopub.status.busy": "2026-02-16T18:29:25.002850Z", "iopub.status.idle": "2026-02-16T18:29:25.164535Z", "shell.execute_reply": "2026-02-16T18:29:25.164208Z" } }, "outputs": [ { "data": { "image/png": "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nVq0aKFnnnlGX3/9tZo1a6ZnnnnG0vYAtVlYjRkpLCxUly5ddMstt/jvL4dq3LhxWr58uZ566imde+652r17t3bv3l3FLQUqLy4uTm3atNG3335bofqbNm3SpZdeqrPOOkszZsxQYmKioqKitHTpUv3lL3+p0EDOkxFsbEZWVpZGjhypwYMH695771XLli0VGRmpzMxMbdq0qdx9Hm3zPffco7S0tKB1zjjjjJNreIgiIiKClhcXF5e5zbJlyyRJv/76q/7zn//wxA5QhrAKI/3791f//v3L/PzQoUN68MEH9be//U2//fabzjnnHD3++OP++QvcbrfmzJmjb7/91v9XU1l/1QFWuuqqq/TCCy8oLy9PKSkpJ6z7wQcf6NChQ3r//fflcDj85cc+uRKqkpISbd682X81RJI2bNggSRWalfStt95S+/bttXjx4oBf4kevahxV1i/4o1dlGjVqVO7cH+3atdOPP/5Yqnz9+vXltjMUp5xyiiTpt99+Cyjftm1b0PrZ2dl66aWXdN999+m1117TiBEj9MUXX6hhw7D6sQvUiLC6TVOe9PR05eXl6fXXX9fXX3+tG264QVdccYX/B9XRJxQ+/PBDnX766UpKStLo0aO5MoJa57777lPjxo01evRoFRQUlPp806ZNevrppyUdGZ8hKeAx0j179mj+/Pkn1YZZs2b5/22M0axZs9SoUSNdeuml5W4brE1ffPGF8vLyAurZbDZJpX/Bt2zZUn379tXzzz+vHTt2lNr/rl27/P++8sor9fnnn2v16tUBn7/22mvltjMU7dq1U2RkpFasWBFQHuzpmN9++02jR49Wjx49NG3aNL300ktau3atpk2bVqVtAuqKOhPRPR6P5s+fL4/HozZt2kg6cok3Oztb8+fP17Rp07R582Zt27ZNb775pl599VUVFxfr7rvv1vXXX6+PP/7Y4m8A/FeHDh20cOFCDR06VE6nM2AG1lWrVunNN9/0z4dx+eWXKyoqSgMHDtTtt9+u/fv368UXX1TLli2D/iKviJiYGGVnZ2vEiBHq2bOn/v73v2vJkiV64IEHSg1WDeaqq67S4sWLdc0112jAgAHasmWL5s6dq86dO2v//v3+erGxsercubMWLVqkjh07qkWLFjrnnHN0zjnnaPbs2brooot07rnnasyYMWrfvr0KCgqUl5en//znP/rq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AFDfEEaAGubxeOR0OuXz+QLKbTab7Ha7Ra1CuDk+wNrtdhbTQ9gijAA1zOv1yufzKSsrS06n01/OLxNUhN1ul81mk8vlCii32Wxyu92cQwhLhBHAIk6nU8nJyVY3A2HG4XDI7XaXus3ncrnk9XoJIwhLhBEACDMOh4PQgTqFp2kAAIClCCMAAMBShBEAAGApwggAALAUYQQAAFiKMAIAACxFGAEAAJYijAAAAEtVKozMnj1bSUlJiomJUc+ePbV69eoy67744ou6+OKLdcopp+iUU05RamrqCesDAID6JeQwsmjRIk2YMEEZGRlau3atunTporS0NO3cuTNo/dzcXN1444365JNPlJeXp8TERF1++eX66aefTrrxQLjweDxau3at1q5dywq9qDZut9t/nnk8HqubA1RYhDHGhLJBz549dcEFF2jWrFmSpJKSEiUmJuquu+7SxIkTy92+uLhYp5xyimbNmqXhw4dX6Jh79+5Vs2bNtGfPHsXFxYXSXMBywVbpZVGzchQWSk2aHPn3/v1S48bWtqeW4xxDbVXR398hrU1TVFSkNWvWaNKkSf6yBg0aKDU1VXl5eRXah8/n0++//64WLVqEcmggbAVbpZcVelGVjl88j4XzEG5CCiNer1fFxcVKSEgIKE9ISNAPP/xQoX3cf//9atOmjVJTU8usc+jQIR06dMj/fu/evaE0E6iVWKUX1YnF8xDOavRpmj//+c96/fXX9c477ygmJqbMepmZmWrWrJn/lZiYWIOtBAAANSmkMGK32xUZGamCgoKA8oKCArVq1eqE2z711FP685//rOXLl+u88847Yd1JkyZpz549/tf27dtDaSYAAAgjIYWRqKgode/eXTk5Of6ykpIS5eTkKCUlpcztnnjiCT366KPKzs7W+eefX+5xoqOjFRcXF/ACAAB1U0hjRiRpwoQJGjFihM4//3z16NFDM2fOVGFhoUaNGiVJGj58uNq2bavMzExJ0uOPP64pU6Zo4cKFSkpKUn5+viSpSZMmanJ0tDwAAKi3Qg4jQ4cO1a5duzRlyhTl5+era9euys7O9g9q9Xg8atDgvxdc5syZo6KiIl1//fUB+8nIyNDDDz98cq0HAABhL+QwIknp6elKT08P+llubm7A+61bt1bmEAAAoJ5gbRoAAGApwggAALBUpW7TACibx+Pxz4QpibVoYJnj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N1fJycmKjo7WGWecoQULFpSqE07nQnX0QX04F3bs2KGbbrpJHTt2VIMGDQIm0TzWm2++qbPOOksxMTE699xztXTp0qpvfBWpjj5YsGBBqXMhJiamer5AFSOMVJNFixZpwoQJysjI0Nq1a9WlSxelpaVp586dQesvXLhQEydOVEZGhtxut15++WUtWrRIDzzwQEC9s88+Wzt27PC/Pvvss5r4OpUWaj889NBDev755/Xss8/q+++/1x133KFrrrlGX375ZaX3abXq6AMp/M6FwsJCdenSRbNnz65Q/S1btmjAgAHq16+f1q1bp/Hjx2v06NFatmyZv064nQvV0QdS3T8XDh06pPj4eD300EPq0qVL0DqrVq3SjTfeqFtvvVVffvmlBg8erMGDB+vbb7+tyqZXmeroA+nI7KzHngvbtm2rqiZXL4Nq0aNHDzN27Fj/++LiYtOmTRuTmZkZtP7YsWPNJZdcElA2YcIE07t3b//7jIwM06VLl2ppb3UJtR9at25tZs2aFVB27bXXmptvvrnS+7RadfRBOJ4Lx5Jk3nnnnRPWue+++8zZZ58dUDZ06FCTlpbmfx9u58KxqqoP6sO5cKw+ffqYcePGlSofMmSIGTBgQEBZz549ze23336SLax+VdUH8+fPN82aNauydtUkroxUg6KiIq1Zs0apqan+sgYNGig1NVV5eXlBt+nVq5fWrFnjv8S8efNmLV26VFdeeWVAvR9//FFt2rRR+/btdfPNN8vj8VTfFzlJlemHQ4cOlbqsGBsb6/9LrzL7tFJ19MFR4XQuVEZeXl5Av0lSWlqav9/C7VyojPL64Ki6fi5UREX7qq7bv3+/2rVrp8TERA0aNEjfffed1U2qEMJINfB6vSouLvZPkX9UQkKC8vPzg25z00036ZFHHtFFF12kRo0aqUOHDurbt2/AbZqePXtqwYIFys7O1pw5c7RlyxZdfPHF2rdvX7V+n8qqTD+kpaVpxowZ+vHHH1VSUqKPPvpIixcv1o4dOyq9TytVRx9I4XcuVEZ+fn7Qftu7d68OHDgQdudCZZTXB1L9OBcqoqy+qivnQkV06tRJ8+bN03vvvaesrCyVlJSoV69e+s9//mN108pFGKklcnNzNW3aND333HNau3atFi9erCVLlujRRx/11+nfv79uuOEGnXfeeUpLS9PSpUv122+/6Y033rCw5VXr6aef1plnnqmzzjpLUVFRSk9P16hRowLWO6rrKtIH9eFcQMVwLuColJQUDR8+XF27dlWfPn20ePFixcfH6/nnn7e6aeWqPz/ha5DdbldkZKQKCgoCygsKCtSqVaug20yePFnDhg3T6NGjde655+qaa67RtGnTlJmZqZKSkqDbNG/eXB07dtTGjRur/DtUhcr0Q3x8vN59910VFhZq27Zt+uGHH9SkSRO1b9++0vu0UnX0QTC1/VyojFatWgXtt7i4OMXGxobduVAZ5fVBMHXxXKiIsvqqrpwLldGoUSN169YtLM4Fwkg1iIqKUvfu3ZWTk+MvKykpUU5OjlJSUoJu4/P5Sv31HxkZKUkyZSwftH//fm3atEmtW7euopZXrcr0w1ExMTFq27atDh8+rLfffluDBg066X1aoTr6IJjafi5URkpKSkC/SdJHH33k77dwOxcqo7w+CKYungsVUZm+quuKi4v1zTffhMe5YPUI2rrq9ddfN9HR0WbBggXm+++/N7fddptp3ry5yc/PN8YYM2zYMDNx4kR//YyMDNO0aVPzt7/9zWzevNksX77cdOjQwQwZMsRf53//939Nbm6u2bJli1m5cqVJTU01drvd7Ny5s8a/X0WF2g+ff/65efvtt82mTZvMihUrzCWXXGJOP/108+uvv1Z4n7VNdfRBOJ4L+/btM19++aX58ssvjSQzY8YM8+WXX5pt27YZY4yZOHGiGTZsmL/+5s2bjc1mM/fee69xu91m9uzZJjIy0mRnZ/vrhNu5UB19UB/OBWOMv3737t3NTTfdZL788kvz3Xff+T9fuXKladiwoXnqqaeM2+02GRkZplGjRuabb76p0e9WUdXRB1OnTjXLli0zmzZtMmvWrDF/+MMfTExMTECd2oowUo2effZZ43A4TFRUlOnRo4f5/PPP/Z/16dPHjBgxwv/+999/Nw8//LDp0KGDiYmJMYmJieaPf/xjwC+goUOHmtatW5uoqCjTtm1bM3ToULNx48Ya/EaVE0o/5ObmGqfTaaKjo82pp55qhg0bZn766aeQ9lkbVXUfhOO58MknnxhJpV5Hv/uIESNMnz59Sm3TtWtXExUVZdq3b2/mz59far/hdC5URx/Ul3MhWP127doF1HnjjTdMx44dTVRUlDn77LPNkiVLauYLVUJ19MH48eP9/y8kJCSYK6+80qxdu7bmvtRJiDCmjHsAAAAANYAxIwAAwFKEEQAAYCnCCAAAsBRhBAAAWIowAgAALEUYAQAAliKMAAAASxFGAFS7vn37avz48VY3I+xERETo3XfftboZQLUjjAA1YOTIkYqIiNAdd9xR6rOxY8cqIiJCI0eOrPmG1TKvvPKKLrroIqubcVIIEEDoCCNADUlMTNTrr7+uAwcO+MsOHjyohQsXyuFwWNiyiikqKqr2Y7z33nu6+uqrq/04AGoXwghQQ5KTk5WYmKjFixf7yxYvXiyHw6Fu3boF1C0pKVFmZqZOP/10xcbGqkuXLnrrrbf8nxcXF+vWW2/1f96pUyc9/fTTAfvIzc1Vjx491LhxYzVv3ly9e/fWtm3bJB25UjN48OCA+uPHj1ffvn397/v27av09HSNHz9edrtdaWlpkqRvv/1W/fv3V5MmTZSQkKBhw4bJ6/X6tyssLNTw4cPVpEkTtW7dWtOnT69Q/xw8eFDLly8/YRh57733lJycrJiYGLVv315Tp07V4cOHJUmPPPKI2rRpo19++cVff8CAAerXr59KSkokHblqMWfOHPXv31+xsbFq3759QL9K0vbt2zVkyBA1b95cLVq00KBBg7R169aAOvPmzdPZZ5+t6OhotW7dWunp6ZKkpKQkSdI111yjiIgI//vy2i5JP/74o/7f//t/iomJUefOnfXRRx9VqN+AuoAwAtSgW265RfPnz/e/nzdvnkaNGlWqXmZmpl599VXNnTtX3333ne6++265XC7985//lHQkrJx22ml688039f3332vKlCl64IEH9MYbb0iSDh8+rMGDB6tPnz76+uuvlZeXp9tuu00REREhtfeVV15RVFSUVq5cqblz5+q3337TJZdcom7duunf//63srOzVVBQoCFDhvi3uffee/XPf/5T7733npYvX67c3FytXbu23GPl5OSobdu2Ouuss4J+/umnn2r48OEaN26cvv/+ez3//PNasGCB/vSnP0mSHnzwQSUlJWn06NGSpNmzZ2vVqlV65ZVX1KDBf3/UTZ48Wdddd52++uor3XzzzfrDH/4gt9stSfr999+Vlpampk2b6tNPP9XKlSvVpEkTXXHFFf4rQ3PmzNHYsWN122236ZtvvtH777+vM844Q5L0r3/9S5I0f/587dixw/++vLaXlJTo2muvVVRUlL744gvNnTtX999/f8X/QwHhzuqV+oD6YMSIEWbQoEFm586dJjo62mzdutVs3brVxMTEmF27dplBgwb5V+s8ePCgsdlsZtWqVQH7uPXWW82NN95Y5jHGjh1rrrvuOmOMMb/88ouRZHJzc0/YnmONGzcuYJXQPn36mG7dugXUefTRR83ll18eULZ9+3Yjyaxfv97s27fPREVFmTfeeMP/+S+//GJiY2PNuHHjymy7McaMGTPG3HPPPWV+fumll5pp06YFlP31r381rVu39r/ftGmTadq0qbn//vtNbGysee211wLqSzJ33HFHQFnPnj3NnXfe6d9fp06dTElJif/zQ4cOmdjYWLNs2TJjjDFt2rQxDz74YJntlGTeeeedkNq+bNky07Bhw4DVmf/+978H3RdQFzW0NAkB9Ux8fLwGDBigBQsWyBijAQMGyG63B9TZuHGjfD6fLrvssoDyoqKigNs5s2fP1rx58+TxeHTgwAEVFRWpa9eukqQWLVpo5MiRSktL02WXXabU1FQNGTJErVu3Dqm93bt3D3j/1Vdf6ZNPPlGTJk1K1d20aZO/HT179vSXt2jRQp06dTrhcYwx+uCDD/xXdoL56quvtHLlSv/VBOnI7aqDBw/K5/PJZrOpffv2euqpp3T77bdr6NChuummm0rtJyUlpdT7devW+Y+xceNGNW3aNKDOwYMHtWnTJu3cuVM///yzLr300hN+n1Db7na7lZiYqDZt2pTZTqAuI4wANeyWW27xjzGYPXt2qc/3798vSVqyZInatm0b8Fl0dLQk6fXXX9c999yj6dOnKyUlRU2bNtWTTz6pL774wl93/vz5+p//+R9lZ2dr0aJFeuihh/TRRx/pwgsvVIMGDWSMCdj377//XqotjRs3LtW2gQMH6vHHHy9Vt3Xr1tq4cWNFuqCU1atX6/Dhw+rVq1eZdfbv36+pU6fq2muvLfVZTEyM/98rVqxQZGSktm7dqsOHD6thw4r/mNu/f7+6d++u1157rdRn8fHxAbd7QlHRtgP1FWEEqGFHxx9ERET4B4Ueq3PnzoqOjpbH41GfPn2C7mPlypXq1auX/vjHP/rLNm3aVKpet27d1K1bN02aNEkpKSlauHChLrzwQsXHx+vbb78NqLtu3To1atTohG1PTk7W22+/raSkpKC/5Dt06KBGjRrpiy++8D8h9Ouvv2rDhg1lfhfpyODOAQMGKDIy8oTHXr9+vX98RjCLFi3S4sWLlZubqyFDhujRRx/V1KlTA+p8/vnnGj58eMD7o1eckpOTtWjRIrVs2VJxcXFBj5GUlKScnBz169cv6OeNGjVScXFxSG13Op3avn27duzY4b969fnnn5f5PYE6x+LbREC9cPwYjT179pg9e/b43x87ZsQYYx588EFz6qmnmgULFpiNGzeaNWvWmGeeecYsWLDAGGPM008/beLi4kx2drZZv369eeihh0xcXJzp0qWLMcaYzZs3m4kTJ5pVq1aZrVu3mmXLlplTTz3VPPfcc8YYY7Kzs01ERIR55ZVXzIYNG8yUKVNMXFxcqTEjx4/z+Omnn0x8fLy5/vrrzerVq83GjRtNdna2GTlypDl8+LAxxpg77rjDtGvXzuTk5JhvvvnGXH311aZJkyYnHDNy9tlnm7fffvuEfZidnW0aNmxoHn74YfPtt9+a77//3vztb3/zj9/Yvn27OeWUU8wzzzwTUD8vL8+/D0nGbrebl19+2axfv95MmTLFNGjQwHz33XfGGGMKCwvNmWeeafr27WtWrFhhNm/ebD755BNz1113me3btxtjjFmwYIGJiYkxTz/9tNmwYYP/v81RZ555prnzzjvNjh07zO7duyvU9uLiYtO5c2dz2WWXmXXr1pkVK1aY7t27M2YE9QZhBKgBwQaMHuv4MFJSUmJmzpxpOnXqZBo1amTi4+NNWlqa+ec//2mMOTLIdeTIkaZZs2amefPm5s477zQTJ070h5H8/HwzePBg07p1axMVFWXatWtnpkyZYoqLi/3HmDJliklISDDNmjUzd999t0lPTy83jBhjzIYNG8w111xjmjdvbmJjY81ZZ51lxo8f7x/0uW/fPuNyuYzNZjMJCQnmiSeeKHNfxhizceNGEx0dbfbv319uP2ZnZ5tevXqZ2NhYExcXZ3r06GFeeOEFU1JSYi699FKTlpYWMPj0rrvuMh06dDD79u0zxhwJI7NnzzaXXXaZiY6ONklJSWbRokUBx9ixY4cZPny4sdvtJjo62rRv396MGTMmIDzOnTvX/9+mdevW5q677vJ/9v7775szzjjDNGzY0LRr167cth+1fv16c9FFF5moqCjTsWNHk52dTRhBvRFhzHE3jgGgBs2YMUP/+Mc/tHTp0mo/VkREhN55551Sc6wAsBbzjACw1GmnnaZJkyZZ3QwAFmIAKwBLHTthGoD6iTACoN7grjRQO3GbBgAAWIowAgAALEUYAQAAliKMAAAASxFGAACApQgjAADAUoQRAABgKcIIAACwFGEEAABY6v8D6DkpKAhgTSMAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig, ax = plt.subplots()\n", "\n", "ax.hist(\n", " calibrated_images.flatten() / science_flux,\n", " bins=100,\n", " histtype='step',\n", " color='k',\n", " )\n", "\n", "ax.axvline(1, c='r')\n", "ax.set_xlabel('Measured / expected')\n", "ax.set_title('Calibrated flux')\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "dbabc8bd", "metadata": {}, "source": [ "We can see that, unsurprisingly, the calibrated fluxes follow a Gaussian distribution centred on the expected value.\n", "\n", "That concludes this demonstration of `opticam`'s calibration error propagation." ] } ], "metadata": { "kernelspec": { "display_name": "opticam6", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.14.2" } }, "nbformat": 4, "nbformat_minor": 5 }