{ "cells": [ { "cell_type": "markdown", "id": "9359fc63-3229-4fd3-8631-09c181e6a27b", "metadata": {}, "source": [ "# Nodding Data Reduction\n", "\n", "This notebook shows the reduction path of a nodding observation with a multi-beam receiver, the K-band Focal Plane Array (KFPA).\n", "\n", "Similar to a position-switched observation, two beams are selected for simultaneous observing (though the receiver can have more than two beams). In the first scan BEAM1 is looking at the source, while BEAM2 is looking at an assumed OFF position. In the next scan, BEAM2 will be looking at the source, while BEAM1 is looking at (another) OFF position. This will result in two position-switched solutions, which are then averaged for the final spectrum. \n", "\n", "One advantage of this observing mode is that the telescope is always ON source, bringing the noise down by $\\sqrt{2}$ compared to a classic position switched observation. Minus a small amount of slewing time of course. However, the beam separation in the receiver should be large enough to ensure that the off position is not on the source. Otherwise, a proper Position Switching observation is needed with a large enough offset.\n", "\n", "In the observations used in this tutorial there are also position switched observations of the same source, so we can compare the results of using beam nodding versus position switching.\n", "\n", "The data in this tutorial were also presented \n", "[in a similar GBTIDL data reduction](https://gbtdocs.readthedocs.io/en/latest/how-tos/data_reduction/gbtidl.html#basic-nodding).\n", "\n", "\n", "## Background\n", "\n", "The spectral line observed here is the NH$_3$ (1,1) line at 23.69 GHz with the KFPA receiver. This receiver has 7 beams: one central beam and six beams in a roughly hexagonal pattern around the central beam. The source is a position in the W3 cloud, a roughly two degree sized Giant Molecular Cloud (GMC) with active star formation. See also https://herscheltelescope.org.uk/results/w3-star-forming-region/\n", "\n", "## Dysh commands\n", "\n", "The following dysh commands are introduced (leaving out all the function arguments):\n", "\n", " filename = dysh_data()\n", " sdf = GBTFITSLoad()\n", " sb = sdf.getnod()\n", " ta = sb.timeaverage()\n", " ta.baseline()\n", " ta.smooth()\n", " ta.average()\n", " ta.plot()\n", " ta.plot().spectrum\n", "\n", "\n", "## Loading Modules\n", "We start by loading the modules we will use for the data reduction. \n" ] }, { "cell_type": "code", "execution_count": 1, "id": "e34b86e9-6438-4e84-8503-d6dd1699e243", "metadata": {}, "outputs": [], "source": [ "# These modules are required for the data reduction.\n", "from dysh.log import init_logging\n", "from dysh.fits.gbtfitsload import GBTFITSLoad\n", "from astropy import units as u\n", "\n", "# These modules are used for file I/O\n", "from dysh.util.files import dysh_data\n", "from pathlib import Path" ] }, { "cell_type": "markdown", "id": "124a7432-5874-4cf2-81f4-217d2c064575", "metadata": {}, "source": [ "## Setup Logging\n", "\n", "dysh uses a logger to communicate. If you are working in the command line, then the logging is setup for you. If you are working in a jupyter lab instance, then you need to set it up. You can do so using the `init_logging` function imported above. As an argument, `init_logging` takes a number, the verbosity `level`. `level` 0 is for error messages only, 1 for warning, 2 for info and 3 for debug. Here we set it to `level` 2. " ] }, { "cell_type": "code", "execution_count": 2, "id": "65930caf-a171-43fd-8ea1-148ee9d15b59", "metadata": {}, "outputs": [], "source": [ "init_logging(2)\n", "\n", "# also create a local \"output\" directory where temporary notebook files can be stored.\n", "output_dir = Path.cwd() / \"output\"\n", "output_dir.mkdir(exist_ok=True)" ] }, { "cell_type": "markdown", "id": "5554680e-732f-491e-9acc-be087d268905", "metadata": {}, "source": [ "## Data Retrieval\n", "\n", "Download the example SDFITS data, if necessary." ] }, { "cell_type": "code", "execution_count": 3, "id": "be47e0aa-097a-4000-a2aa-fc5b0d4dc49e", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "18:54:34.452 I Resolving example=nod2 -> nod-KFPA/data/TGBT22A_503_02.raw.vegas.trim.fits\n" ] } ], "source": [ "filename = dysh_data(example=\"nod2\")" ] }, { "cell_type": "markdown", "id": "6ac043ae-0334-47c8-bf32-fa032e0113ca", "metadata": {}, "source": [ "## Data Loading\n", "\n", "Next, we use `GBTFITSLoad` to load the data, and then its `summary` method to inspect its contents.\n", "\n", "This trimmed dataset is an extraction from a much larger dataset (19GB) and can take some time to load if it's the first time. This would be `example=\"nod\"`\n" ] }, { "cell_type": "code", "execution_count": 4, "id": "fb10b18f-4510-4707-ad2a-70a1ec2b4d79", "metadata": {}, "outputs": [], "source": [ "sdfits = GBTFITSLoad(filename)" ] }, { "cell_type": "code", "execution_count": 5, "id": "62f47e96-7553-48c9-9dfb-032d2af60897", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
SCANOBJECTVELOCITYPROCPROCSEQNRESTFREQDOPFREQ# IF# POL# INT# FEEDAZIMUTHELEVATION
60W3_1-40.0OnOff123.69449623.69449611311324.227938.7060
61W3_1-40.0OnOff223.69449623.69449611311324.152639.0121
62W3_1-40.0Nod123.69449623.69449611317324.274338.4194
63W3_1-40.0Nod223.69449623.69449611317324.367238.2858
" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sdfits.summary()" ] }, { "cell_type": "markdown", "id": "17ac95ab-15f2-4f45-80cd-cc9d6ca0f001", "metadata": {}, "source": [ "## Data Reduction\n", "\n", "### Nodding Data Reduction\n", "\n", "We start calibrating the Nod observations in scans 62 and 63. We use the `GBTFITSLoad.getnod` method to calibrate the data. This method will automatically find a pair of Nod scans given one scan number. It will also automatically figure out which feeds where used during the nodding observations. The return of `getnod` is a `ScanBlock` with at least two `NodScan` in it." ] }, { "cell_type": "code", "execution_count": 6, "id": "c3f65c36-56fe-4249-a35e-1df5e1a1dcd9", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "18:54:34.754 I Ignoring 1 blanked integration(s).\n", "18:54:34.894 I Ignoring 1 blanked integration(s).\n" ] }, { "data": { "text/plain": [ "ScanBlock([,\n", " ])" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nodsb = sdfits.getnod(scan=62, ifnum=0, plnum=0)\n", "nodsb" ] }, { "cell_type": "markdown", "id": "42566293-35b1-4f83-9f4c-78cbfad5e1c3", "metadata": {}, "source": [ "Each `NodScan` holds all of the calibrated integrations for each feed used during the Nod observations.\n", "We can query the `NodScan` objects for information about the data, such as what is the system temperature, in K, or the exposure time, in seconds. `NodScan` is a sub class of a `Scan` object." ] }, { "cell_type": "code", "execution_count": 7, "id": "aa8f99fc-ddbd-4295-a5f2-f4541146ee7c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(array([64.0651583 , 65.84723928, 63.32246857, 64.9772735 , 63.77161495,\n", " 62.82267302, 66.41525105, 62.88090753, 60.91140946, 62.18854417,\n", " 60.81960096, 61.42624138, 59.32883166, 60.16711155, 61.45232073,\n", " 60.71780304, 59.21079399, 62.45297224, 62.54435652, 64.53549963,\n", " 65.65949202, 64.34252275, 64.24356553, 65.4308054 , 65.54581989,\n", " 64.64741319, 62.45646939, 62.62895287, 61.91170188, 63.05362163]),\n", " array([73.42457338, 73.89318167, 77.95484204, 77.06676577, 79.64183646,\n", " 80.67187842, 75.41180075, 74.18340271, 79.06335209, 82.58279698,\n", " 78.22566126, 70.88384081, 68.7912057 , 69.28174137, 72.48114802,\n", " 72.24762512, 74.46373175, 73.26477026, 73.02711593, 70.51565007,\n", " 72.7222005 , 71.37948284, 71.11466717, 70.09686344, 70.74295942,\n", " 69.47116087, 70.22771754, 67.13519555, 70.62961633, 69.03334933]))" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nodsb[0].tsys, nodsb[1].tsys" ] }, { "cell_type": "code", "execution_count": 8, "id": "6b28008c-3cd8-483c-a8b0-b3218bc64c0d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(array([0.97587454, 0.97236673, 0.97587454, 0.97095654, 0.97587454,\n", " 0.97587454, 0.97060335, 0.97587454, 0.97060334, 0.97587454,\n", " 0.97587454, 0.97587454, 0.97060334, 0.97587454, 0.97060334,\n", " 0.97587454, 0.97587454, 0.97095652, 0.97587454, 0.97095652,\n", " 0.97587454, 0.97587454, 0.97060334, 0.97587454, 0.97060335,\n", " 0.97587454, 0.97587454, 0.97060335, 0.97587454, 0.97587454]),\n", " array([0.97587454, 0.97201457, 0.97587454, 0.97095654, 0.97587454,\n", " 0.97587454, 0.97095654, 0.97587454, 0.97060334, 0.97587454,\n", " 0.97587454, 0.97587454, 0.97095654, 0.97587454, 0.97060334,\n", " 0.97587454, 0.97587454, 0.97060334, 0.97587454, 0.97095652,\n", " 0.97587454, 0.97587454, 0.97095654, 0.97587454, 0.97095654,\n", " 0.97587454, 0.97587454, 0.97095654, 0.97587454, 0.97587454]))" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nodsb[0].exposure, nodsb[1].exposure" ] }, { "cell_type": "code", "execution_count": 9, "id": "ac58c8c1-5662-472a-ba79-439b82cdde7c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(2, 6)" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nodsb[0].fdnum, nodsb[1].fdnum" ] }, { "cell_type": "markdown", "id": "8638aa65-c8dd-40e6-88ac-f27b0b80a747", "metadata": {}, "source": [ "From the above we see that the beams used for nodding had fdnum of 2 and 6.\n", "\n", "#### Inspecting Integrations\n", "\n", "To access the calibrated integrations we use the `calibrated` method of a `Scan` object. The return is a `Spectrum` object with the calibrated data. The argument to `calibrated` is the integration number." ] }, { "cell_type": "code", "execution_count": 10, "id": "4701d61e-1aa2-46be-b88c-fa72ac4e838c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\n", " target: \n", " observer to target (computed from above):\n", " radial_velocity=-18.322117122957543 km / s\n", " redshift=-6.111413673348665e-05\n", " doppler_rest=23694495500.0 Hz\n", " doppler_convention=radio)\n", " [2.36845708e+10 2.36845715e+10 2.36845722e+10 ... 2.37080061e+10\n", " 2.37080069e+10 2.37080076e+10] Hz> (length=32768))>" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nod_int = nodsb[0].getspec(0)\n", "nod_int" ] }, { "cell_type": "markdown", "id": "64b37d9e-ee0f-4d5b-8e62-d0c42659a7e3", "metadata": {}, "source": [ "`Spectrum` objects have convenience functions to plot, smooth, and remove baselines, among others. Here we use the `plot` function to display the calibrated data for the first integration." ] }, { "cell_type": "code", "execution_count": 11, "id": "2204d559-7174-45bc-ae77-697e8693ce68", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "abfcaa7b831448a38b41a84fc6a89c62", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox(children=(HBox(children=(Button(description='Clear All Regions', style=ButtonStyle(), tooltip='Clear all …" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nod_int.plot()" ] }, { "cell_type": "markdown", "id": "debb9724-7d8e-424a-9584-6f9ad10cf435", "metadata": {}, "source": [ "The plot shows a noise-like signal since it is only one 1 s integration. To see more details, and potentially a signal, we must time average the data.\n", "\n", "#### Time Averaging\n", "\n", "To time average we can use the `timeaverage` function. We can use this function directly from a `ScanBlock`, in which case all of the data in the `ScanBlock` will be time averaged, or from a `Scan`, and only average the data inside the `Scan`. Here we will average all the data in the `ScanBlock`.\n", "\n", "By default time averaging uses the following weights: \n", "$$\n", "\\frac{T^{2}_{sys}}{\\Delta\\nu\\Delta t}\n", "$$\n", "with $T_{sys}$ the system temperature, $\\Delta\\nu$ the channel width and $\\Delta t$ the integration time. In dysh these are set using `weights='tsys'` (the default).\n", "\n", "The return of `timeaverage` is a `Spectrum` object." ] }, { "cell_type": "code", "execution_count": 12, "id": "1bfca3af-c586-43a7-9332-f71681215857", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\n", " target: \n", " observer to target (computed from above):\n", " radial_velocity=-18.31506654520219 km / s\n", " redshift=-6.109061997461307e-05\n", " doppler_rest=23694495500.0 Hz\n", " doppler_convention=radio)\n", " [2.36845708e+10 2.36845715e+10 2.36845722e+10 ... 2.37080061e+10\n", " 2.37080069e+10 2.37080076e+10] Hz> (length=32768))>" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nod_ta = nodsb.timeaverage()\n", "nod_ta" ] }, { "cell_type": "markdown", "id": "17ace044-0ed9-4f89-8629-976cfba75450", "metadata": {}, "source": [ "Now we plot the time average." ] }, { "cell_type": "code", "execution_count": 13, "id": "43dd7e8e-4ff7-41b2-92e1-f334ef1b5df5", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "10b691e673224d99aeadefd6c4c499a8", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox(children=(HBox(children=(Button(description='Clear All Regions', style=ButtonStyle(), tooltip='Clear all …" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nod_ta.plot()" ] }, { "cell_type": "markdown", "id": "812cffc1-f324-4402-8ece-a9711a1491a3", "metadata": {}, "source": [ "We see hints of a line. We can further reduce our data to bring out the signal from the noise.\n", "\n", "#### Smoothing\n", "\n", "One way of reducing the noise in the time average is by smoothing the data along the spectral axis. This is done using the `Spectrum.smooth` function. The first argument is the kernel, and the second the width of the kernel in channels. The available kernels are a boxcar, Gaussian and a Hanning window. We use a boxcar with a width of 51 channels." ] }, { "cell_type": "code", "execution_count": 14, "id": "b640e77c-01db-45a2-a777-73dbb3e948fd", "metadata": {}, "outputs": [], "source": [ "nod_ta_smooth = nod_ta.smooth('box', 51)" ] }, { "cell_type": "code", "execution_count": 15, "id": "50d83fed-6976-4a4b-93bc-e942dffd939d", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "3e36be201be840e8b51e7fd5678c2696", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox(children=(HBox(children=(Button(description='Clear All Regions', style=ButtonStyle(), tooltip='Clear all …" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nod_ta_smooth.plot(xaxis_unit=\"GHz\")" ] }, { "cell_type": "markdown", "id": "ebf1af6a-715a-4928-961b-ce4af10f0dd0", "metadata": {}, "source": [ "The signal is clear now. \n", "\n", "#### Statistics\n", "\n", "We can quantify the reduction in the noise using the `Spectrum.stats` function. To reduce the bias in the statistics, we will slice the spectrum to avoid the bandpass roll off channels. We use a channel range between 23.687 and 23.694 GHz." ] }, { "cell_type": "code", "execution_count": 16, "id": "b4f6413c-9a3d-4c93-ae07-909f5e8fa8bc", "metadata": {}, "outputs": [], "source": [ "s = slice(23.687*u.GHz, 23.694*u.GHz)" ] }, { "cell_type": "code", "execution_count": 17, "id": "745f4f1c-4986-4c07-8456-2cb86d99a8ee", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "18:54:36.431 I Note: found 1 NaN (masked) values\n" ] }, { "data": { "text/plain": [ "{'mean': ,\n", " 'median': ,\n", " 'rms': ,\n", " 'min': ,\n", " 'max': ,\n", " 'npt': 191,\n", " 'nan': np.int64(1)}" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nod_ta_smooth[s].stats() # rms 0.04587208 K" ] }, { "cell_type": "code", "execution_count": 18, "id": "c8eebc52-360b-4c9c-b486-c85ae0afd115", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "18:54:36.779 I Note: found 52 NaN (masked) values\n" ] }, { "data": { "text/plain": [ "{'mean': ,\n", " 'median': ,\n", " 'rms': ,\n", " 'min': ,\n", " 'max': ,\n", " 'npt': 9787,\n", " 'nan': np.int64(52)}" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nod_ta[s].stats() # rms 0.33878936 K" ] }, { "cell_type": "markdown", "id": "585c4857-d44c-4b8c-ae74-c56b7331b470", "metadata": {}, "source": [ "Before smoothing the rms was 339 mK, after smoothing it went down to 46 mK, so the reduction in the noise is 6% higher than $\\sqrt{51}$.\n", "\n", "#### Baseline Subtraction\n", "\n", "Now we will subtract a baseline from the data. We use the `Spectrum.baseline` function to do it. We will ignore the edge channels and the region with line emission during the baseline fitting. This is specified with the `exclude` or `include` argument of `baseline`. Here we use `include`. Since the line free channels seem to have a flat frequency response, away from the window edges, we use an order 1 polynomial as our baseline model." ] }, { "cell_type": "code", "execution_count": 19, "id": "97c9eb68-0522-4624-8424-d6412a1191f7", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "18:54:36.789 I EXCLUDING [Spectral Region, 1 sub-regions:\n", " (23684570787.125 Hz, 23687000000.0 Hz) \n", ", Spectral Region, 1 sub-regions:\n", " (23694000000.0 Hz, 23700000000.0 Hz) \n", ", Spectral Region, 1 sub-regions:\n", " (23705000000.0 Hz, 23707989690.47583 Hz) \n", "]\n" ] } ], "source": [ "include = [(23.687*u.GHz, 23.694*u.GHz),\n", " (23.700*u.GHz, 23.705*u.GHz)\n", " ]\n", "model = \"poly\"\n", "order = 1\n", "\n", "nod_ta_smooth.baseline(order, model=model, include=include, remove=True)" ] }, { "cell_type": "code", "execution_count": 20, "id": "683e0134-190a-4838-b3f6-61658a5560ce", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "8ae362ebbadd483baf9bc71589455f4e", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox(children=(HBox(children=(Button(description='Clear All Regions', style=ButtonStyle(), tooltip='Clear all …" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nod_ta_smooth.plot()" ] }, { "cell_type": "markdown", "id": "ccc37cb9-76d7-4420-9ad7-1610aa04ce98", "metadata": {}, "source": [ "Now we will proceed with the calibration of the OnOff observations for comparison.\n", "\n", "### OnOff Data Reduction\n", "\n", "Scans 60 and 61 contain OnOff observations (position switched). For the 7-beam KFPA receiver, the central beam (`fdnum=0`) will be the source tracking beam for the OnOff observations. As with the Nod calibration, dysh knows how to pair OnOff observations given a scan number. We use the `GBTFITSLoad.getps` function to calibrate position switched observations. As with `getnod`, the return of `getps` is a `ScanBlock`, but this time containing `PSScan` objects, which are also sub classes of the `Scan` class, so they share many of their functions and properties." ] }, { "cell_type": "code", "execution_count": 21, "id": "8550e75f-2018-4c8b-9b5c-f9de6a6d4c93", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "18:54:36.999 I Ignoring 1 blanked integration(s).\n" ] }, { "data": { "text/plain": [ "ScanBlock([])" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pssb = sdfits.getps(scan=60, plnum=0, ifnum=0, fdnum=0)\n", "pssb" ] }, { "cell_type": "markdown", "id": "2ff3551b-4786-43e2-906e-6c285f025897", "metadata": {}, "source": [ "We time average, smooth and baseline subtract the calibrated data. We use a chain of functions for the time averaging and smoothing." ] }, { "cell_type": "code", "execution_count": 22, "id": "45c126bc-0c94-4f5d-9a16-66c89fef7474", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "18:54:37.311 I 23707989720.475834 Hz is above the maximum spectral axis 23707989720.47583 Hz. Replacing.\n", "18:54:37.311 I EXCLUDING [Spectral Region, 1 sub-regions:\n", " (23684570817.125004 Hz, 23687000000.0 Hz) \n", ", Spectral Region, 1 sub-regions:\n", " (23694000000.0 Hz, 23700000000.0 Hz) \n", ", Spectral Region, 1 sub-regions:\n", " (23705000000.0 Hz, 23707989720.47583 Hz) \n", "]\n" ] } ], "source": [ "ps_ta_smooth = pssb.timeaverage().smooth(\"box\", 51)\n", "ps_ta_smooth.baseline(order, model=model, include=include, remove=True)" ] }, { "cell_type": "code", "execution_count": 23, "id": "f6cda236-7c84-4a0d-947c-9bc0185920dc", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "b91eb50e041c4d669f30bac00d0f9412", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox(children=(HBox(children=(Button(description='Clear All Regions', style=ButtonStyle(), tooltip='Clear all …" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ps_ta_smooth.plot(ymin=-0.2)" ] }, { "cell_type": "markdown", "id": "d2909a5c-27e1-4ac2-b786-5b9f4eb158ac", "metadata": {}, "source": [ "## Comparing the Methods\n", "\n", "Both the Nod and OnOff scans shows a signal, How do the properties of the spectra compare?" ] }, { "cell_type": "code", "execution_count": 24, "id": "f4fd1dbe-5b2f-4db4-8f72-50ffdc0e0983", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "18:54:37.836 I Note: found 1 NaN (masked) values\n", "18:54:38.176 I Note: found 1 NaN (masked) values\n" ] }, { "data": { "text/plain": [ "(, )" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Line free rms\n", "nod_ta_smooth[s].stats()[\"rms\"], ps_ta_smooth[s].stats()[\"rms\"]" ] }, { "cell_type": "markdown", "id": "15b44836-cdbb-4e51-a5a9-0c7a443e1fa4", "metadata": {}, "source": [ "The noise in the Nod spectrum is lower than that in the OnOff one. This makes sense since the exposure time in the Nod observations is almost twice as long as in the OnOff ones, for the same observing time. If that is the only factor, we would expect the noise in the Nod spectrum to be a factor $\\sqrt{2}$ lower than the OnOff one. Let's check." ] }, { "cell_type": "code", "execution_count": 25, "id": "cbd48696-cd88-4b32-b316-5733b7243a6a", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "18:54:38.517 I Note: found 1 NaN (masked) values\n", "18:54:38.846 I Note: found 1 NaN (masked) values\n" ] }, { "data": { "text/latex": [ "$0.85899819 \\; \\mathrm{}$" ], "text/plain": [ "" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ps_ta_smooth[s].stats()[\"rms\"]/nod_ta_smooth[s].stats()[\"rms\"]/2**0.5" ] }, { "cell_type": "markdown", "id": "0d91a723-39bc-4746-a3c0-6409cb9db9fe", "metadata": {}, "source": [ "The ratio is 0.85, meaning that the noise reduction was 15% less than expected. This is likely because the system temperature of the feeds is different. Let's check." ] }, { "cell_type": "code", "execution_count": 26, "id": "cc9fbeab-917e-44fa-97fc-ad08e68d7d0c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "np.float64(0.8432606926356401)" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ps_ta_smooth.meta[\"TSYS\"]/nod_ta_smooth.meta[\"TSYS\"]" ] }, { "cell_type": "markdown", "id": "4cf3c230-1372-4151-a6e8-16a19c757797", "metadata": {}, "source": [ "The system temperature of the feed used for the OnOff observations is 15% lower than that of the feeds used for the Nod. This explains the observed difference in the noise.\n", "\n", "The line peak is consistent between the Nod and OnOff spectra." ] }, { "cell_type": "code", "execution_count": 27, "id": "a72a075f-1acd-471d-b5bb-d4d7987edaf9", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "18:54:38.888 I Note: found 1 NaN (masked) values\n", "18:54:38.890 I Note: found 2 NaN (masked) values\n" ] }, { "data": { "text/plain": [ "(, )" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Line peak\n", "nod_ta_smooth.stats()[\"max\"], ps_ta_smooth.stats()[\"max\"]" ] }, { "cell_type": "code", "execution_count": 28, "id": "a651e206-8936-4734-8456-9a858801f5da", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "8688637147634defaa3e7260fa1df9a8", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox(children=(HBox(children=(Button(description='Clear All Regions', style=ButtonStyle(), tooltip='Clear all …" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stderr", "output_type": "stream", "text": [ "18:54:38.990 I Note: found 1 NaN (masked) values\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "d15481a88fae4aafa54d23e577654efe", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox(children=(HBox(children=(Button(description='Clear All Regions', style=ButtonStyle(), tooltip='Clear all …" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stderr", "output_type": "stream", "text": [ "18:54:39.076 I Note: found 1 NaN (masked) values\n" ] }, { "data": { "text/plain": [ "(np.float64(1.004327561874331), np.float64(0.9949180273182217))" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# radiometer in a non-signal part of the spectrum: (1.0043275608304512, 0.994918029194119) \n", "nod_ta_smooth[50:250].plot(xaxis_unit=\"chan\").spectrum.radiometer(), ps_ta_smooth[50:250].plot(xaxis_unit=\"chan\").spectrum.radiometer()," ] }, { "cell_type": "markdown", "id": "9a72d5ea-2927-46bb-a75c-f59c55e9515e", "metadata": {}, "source": [ "## Final Stats\n", "Finally, at the end we compute some statistics over a spectrum, merely as a checksum if the notebook is reproducible.\n", "\n" ] }, { "cell_type": "code", "execution_count": 29, "id": "adaf4e9f-eee1-484d-b810-458732ef82ba", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "18:54:39.081 I Note: found 1 NaN (masked) values\n", "18:54:39.081 I rms is OK \n" ] } ], "source": [ "nod_ta_smooth.check_stats(0.11354235 * u.K)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.13.9" } }, "nbformat": 4, "nbformat_minor": 5 }