diff --git a/docs/api/base.rst b/docs/api/base.md similarity index 71% rename from docs/api/base.rst rename to docs/api/base.md index 5fd57ea..4667741 100644 --- a/docs/api/base.rst +++ b/docs/api/base.md @@ -1,7 +1,8 @@ -sigpyproc.base -============== +# sigpyproc.base +```{eval-rst} .. automodule:: sigpyproc.base :members: :undoc-members: :show-inheritance: +``` diff --git a/docs/api/block.rst b/docs/api/block.md similarity index 52% rename from docs/api/block.rst rename to docs/api/block.md index eab5a77..b1fe472 100644 --- a/docs/api/block.rst +++ b/docs/api/block.md @@ -1,7 +1,8 @@ -sigpyproc.block -=============== +# sigpyproc.block +```{eval-rst} .. automodule:: sigpyproc.block :members: :undoc-members: - :show-inheritance: \ No newline at end of file + :show-inheritance: +``` diff --git a/docs/api/core.rst b/docs/api/core.md similarity index 61% rename from docs/api/core.rst rename to docs/api/core.md index a2a2f5c..8003872 100644 --- a/docs/api/core.rst +++ b/docs/api/core.md @@ -1,27 +1,29 @@ -sigpyproc.core module -===================== +# sigpyproc.core module -sigpyproc.core.stats --------------------- +## sigpyproc.core.stats +```{eval-rst} .. automodule:: sigpyproc.core.stats :members: :undoc-members: :show-inheritance: +``` -sigpyproc.core.rfi --------------------- +## sigpyproc.core.rfi +```{eval-rst} .. automodule:: sigpyproc.core.rfi :members: :undoc-members: :show-inheritance: +``` -sigpyproc.core.kernels ----------------------- +## sigpyproc.core.kernels +```{eval-rst} .. automodule:: sigpyproc.core.kernels :members: :undoc-members: :show-inheritance: +``` diff --git a/docs/api/foldedcube.rst b/docs/api/foldedcube.md similarity index 68% rename from docs/api/foldedcube.rst rename to docs/api/foldedcube.md index ecae8e3..b7b56f9 100644 --- a/docs/api/foldedcube.rst +++ b/docs/api/foldedcube.md @@ -1,7 +1,8 @@ -sigpyproc.foldedcube -==================== +# sigpyproc.foldedcube +```{eval-rst} .. automodule:: sigpyproc.foldedcube :members: :undoc-members: :show-inheritance: +``` diff --git a/docs/api/fourierseries.rst b/docs/api/fourierseries.md similarity index 66% rename from docs/api/fourierseries.rst rename to docs/api/fourierseries.md index e169712..6fafd97 100644 --- a/docs/api/fourierseries.rst +++ b/docs/api/fourierseries.md @@ -1,7 +1,8 @@ -sigpyproc.fourierseries -======================= +# sigpyproc.fourierseries +```{eval-rst} .. automodule:: sigpyproc.fourierseries :members: :undoc-members: :show-inheritance: +``` diff --git a/docs/api/header.rst b/docs/api/header.md similarity index 70% rename from docs/api/header.rst rename to docs/api/header.md index 621a061..ecb6987 100644 --- a/docs/api/header.rst +++ b/docs/api/header.md @@ -1,7 +1,8 @@ -sigpyproc.header -================ +# sigpyproc.header +```{eval-rst} .. automodule:: sigpyproc.header :members: :undoc-members: :show-inheritance: +``` diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 0000000..5d6e626 --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,31 @@ +# sigpyproc + +```{eval-rst} +.. py:module:: sigpyproc + +``` + +## Subpackages + +```{toctree} +:maxdepth: 1 + +core +io +``` + +## Submodules + +```{toctree} +:maxdepth: 1 + +base +block +foldedcube +fourierseries +header +params +readers +timeseries +utils +``` diff --git a/docs/api/io.rst b/docs/api/io.md similarity index 61% rename from docs/api/io.rst rename to docs/api/io.md index 3851643..921ae6a 100644 --- a/docs/api/io.rst +++ b/docs/api/io.md @@ -1,34 +1,37 @@ -sigpyproc.io module -=================== +# sigpyproc.io module -sigpyproc.io.sigproc --------------------- +## sigpyproc.io.sigproc +```{eval-rst} .. automodule:: sigpyproc.io.sigproc :members: :undoc-members: :show-inheritance: +``` -sigpyproc.io.bits ------------------ +## sigpyproc.io.bits +```{eval-rst} .. automodule:: sigpyproc.io.bits :members: :undoc-members: :show-inheritance: +``` -sigpyproc.io.fileio -------------------- +## sigpyproc.io.fileio +```{eval-rst} .. automodule:: sigpyproc.io.fileio :members: :undoc-members: :show-inheritance: +``` -sigpyproc.io.pfits -------------------- +## sigpyproc.io.pfits +```{eval-rst} .. automodule:: sigpyproc.io.pfits :members: :undoc-members: - :show-inheritance: \ No newline at end of file + :show-inheritance: +``` diff --git a/docs/api/params.rst b/docs/api/params.md similarity index 70% rename from docs/api/params.rst rename to docs/api/params.md index 13b103c..f5fac04 100644 --- a/docs/api/params.rst +++ b/docs/api/params.md @@ -1,7 +1,8 @@ -sigpyproc.params -================ +# sigpyproc.params +```{eval-rst} .. automodule:: sigpyproc.params :members: :undoc-members: :show-inheritance: +``` diff --git a/docs/api/readers.rst b/docs/api/readers.md similarity index 70% rename from docs/api/readers.rst rename to docs/api/readers.md index 73dcb19..2c3d4af 100644 --- a/docs/api/readers.rst +++ b/docs/api/readers.md @@ -1,7 +1,8 @@ -sigpyproc.readers -================= +# sigpyproc.readers +```{eval-rst} .. automodule:: sigpyproc.readers :members: :undoc-members: :show-inheritance: +``` diff --git a/docs/api/timeseries.rst b/docs/api/timeseries.md similarity index 68% rename from docs/api/timeseries.rst rename to docs/api/timeseries.md index ef6a0c4..dff0df5 100644 --- a/docs/api/timeseries.rst +++ b/docs/api/timeseries.md @@ -1,7 +1,8 @@ -sigpyproc.timeseries -==================== +# sigpyproc.timeseries +```{eval-rst} .. automodule:: sigpyproc.timeseries :members: :undoc-members: :show-inheritance: +``` diff --git a/docs/api/utils.rst b/docs/api/utils.md similarity index 70% rename from docs/api/utils.rst rename to docs/api/utils.md index cc61c98..83876cc 100644 --- a/docs/api/utils.rst +++ b/docs/api/utils.md @@ -1,7 +1,8 @@ -sigpyproc.utils -=============== +# sigpyproc.utils +```{eval-rst} .. automodule:: sigpyproc.utils :members: :undoc-members: :show-inheritance: +``` diff --git a/docs/changes.md b/docs/changes.md new file mode 100644 index 0000000..fd515b1 --- /dev/null +++ b/docs/changes.md @@ -0,0 +1,5 @@ +# Changelog + +```{eval-rst} +.. include:: ../HISTORY.rst +``` diff --git a/docs/changes.rst b/docs/changes.rst deleted file mode 100644 index 2892aaf..0000000 --- a/docs/changes.rst +++ /dev/null @@ -1,4 +0,0 @@ -Changelog -========= - -.. include:: ../HISTORY.rst \ No newline at end of file diff --git a/docs/cmd.rst b/docs/cmd.md similarity index 65% rename from docs/cmd.rst rename to docs/cmd.md index 9d06932..4a58f7b 100644 --- a/docs/cmd.rst +++ b/docs/cmd.md @@ -1,18 +1,19 @@ -Command line utilities -====================== +# Command line utilities +```{eval-rst} .. click:: sigpyproc.apps.spp_header:main :prog: spp_header :nested: full +``` -.. click:: sigpyproc.apps.spp_decimate:main - :prog: spp_decimate - :nested: full - +```{eval-rst} .. click:: sigpyproc.apps.spp_extract:main :prog: spp_extract :nested: full +``` +```{eval-rst} .. click:: sigpyproc.apps.spp_clean:main :prog: spp_clean - :nested: full \ No newline at end of file + :nested: full +``` diff --git a/docs/conf.py b/docs/conf.py index 3a8a5e2..1c981c4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -10,27 +10,22 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -import sigpyproc - -import os +import datetime import sys +from importlib.metadata import version as meta_version +from pathlib import Path -sys.path.insert(0, os.path.abspath("../")) +sys.path.insert(0, Path("../").resolve().as_posix()) # -- Project information ----------------------------------------------------- project = "sigpyproc3" -copyright = "2020, Fast Radio Burst Software" author = "Fast Radio Burst Software" - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. - -# The full version, including alpha/beta/rc tags. -version = sigpyproc.__version__ +year = datetime.datetime.now(tz=datetime.timezone.utc).date().year +copyright = f"{year}, {author}" # noqa: A001 +version = meta_version("sigpyproc") release = version - +master_doc = "index" # -- General configuration --------------------------------------------------- @@ -38,10 +33,14 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ "sphinx.ext.autodoc", - "sphinx.ext.napoleon", + "numpydoc", + "sphinx_autodoc_typehints", + "sphinx.ext.coverage", "sphinx.ext.intersphinx", - "myst_nb", "sphinx_click", + "sphinx-prompt", + "sphinx_copybutton", + "myst_nb", ] # Add any paths that contain templates here, relative to this directory. @@ -52,6 +51,10 @@ # This pattern also affects html_static_path and html_extra_path. exclude_patterns = ["_build", "**.ipynb_checkpoints", "Thumbs.db", ".DS_Store"] +rst_epilog = f""" +.. |project| replace:: {project} +""" +nitpicky = True # -- Options for HTML output ------------------------------------------------- @@ -59,7 +62,7 @@ # a list of builtin themes. html_theme = "sphinx_book_theme" -html_title = "sigpyproc3" +html_title = project html_theme_options = { "repository_url": "https://github.com/FRBs/sigpyproc3", "use_repository_button": True, @@ -71,9 +74,6 @@ # so a file named "default.css" will overwrite the builtin "default.css". # \html_static_path = ["_static"] -jupyter_execute_notebooks = "auto" -execution_timeout = -1 - # -- Extension configuration ------------------------------------------------- autoclass_content = "class" # include both class docstring and __init__ @@ -81,20 +81,40 @@ autodoc_typehints = "none" autodoc_inherit_docstrings = True -napoleon_google_docstring = False -napoleon_numpy_docstring = True -napoleon_include_init_with_doc = False -napoleon_include_private_with_doc = False -napoleon_include_special_with_doc = False -napoleon_use_admonition_for_examples = True -napoleon_use_admonition_for_notes = True -napoleon_use_admonition_for_references = True -napoleon_use_ivar = True -napoleon_use_param = True -napoleon_use_rtype = True -napoleon_preprocess_types = True -napoleon_type_aliases = None -napoleon_attr_annotations = False +typehints_document_rtype = False + +numpydoc_use_plots = True +numpydoc_class_members_toctree = False +numpydoc_show_inherited_class_members = False +numpydoc_xref_param_type = True +numpydoc_xref_aliases = { + "ndarray": "numpy.ndarray", + "dtype": "numpy.dtype", + "ArrayLike": "numpy.typing.ArrayLike", + "plt": "matplotlib.pyplot", + "scipy": "scipy", + "astropy": "astropy", + "attrs": "attrs", + "Path": "pathlib.Path", + "Buffer": "typing_extensions.Buffer", + "Iterator": "collections.abc.Iterator", + "Callable": "collections.abc.Callable", +} +numpydoc_xref_ignore = { + "of", + "shape", + "type", + "optional", + "default", +} + + +coverage_show_missing_items = True + +myst_enable_extensions = ["colon_fence"] + +nb_execution_mode = "auto" +nb_execution_timeout = -1 # -- Options for intersphinx extension --------------------------------------- @@ -104,4 +124,6 @@ "scipy": ("https://docs.scipy.org/doc/scipy/reference", None), "astropy": ("https://docs.astropy.org/en/stable/", None), "attrs": ("https://www.attrs.org/en/stable/", None), + "matplotlib": ("https://matplotlib.org/stable/", None), + "typing_extensions": ("https://typing-extensions.readthedocs.io/en/stable/", None), } diff --git a/docs/dev.md b/docs/dev.md new file mode 100644 index 0000000..8006e6d --- /dev/null +++ b/docs/dev.md @@ -0,0 +1,65 @@ +(dev)= + +# Developer documentation + +Here we will cover all the steps required to add new functionality to the +`sigpyproc` package. To do this, we will first consider adding a new function +to the {class}`~sigpyproc.base.Filterbank` class. + +## Adding a new function: `bandpass()` + +**Aim:** Add a new function called bandpass, which will return the total power +as a function of frequency for our observation. + +**Files to be modified:** `sigpyproc/base.py`, `sigpyproc/core/kernels.py`. + +### Step 1: Write the API part + +The first step is to write the user API of the function. As this function +will run on data with both time and frequency resolution, +it belongs in the {class}`~sigpyproc.base.Filterbank` class. + +```python +def bandpass(self, gulp=512, **kwargs): + bpass_ar = np.zeros(self.header.nchans, dtype=np.float32) + num_samples = 0 + for nsamps, ii, data in self.read_plan(**plan_kwargs): + kernel.extract_bpass(data, bpass_ar, self.header.nchans, nsamps) + num_samples += nsamps + bpass_ar = bpass_ar / num_samples + return TimeSeries(bpass_ar, self.header.new_header({"nchans": 1})) +``` + +Looking at the important lines, we have: +Return an instance of the {class}`~sigpyproc.timeseries.TimeSeries` class. +The {class}`~sigpyproc.timeseries.TimeSeries` class takes two arguments, +an instance of {py:obj}`numpy.ndarray` and an instance of +{class}`~sigpyproc.header.Header`. + +Now we have something similar to a normal {py:obj}`numpy.ndarray`, +which exports several other methods for convenience. + +### Step 2: Write the core Numba kernel + +We called a kernel function named `extract_bpass`. This function belongs in the +`sigpyproc/core/kernels.py` file. In `kernels.py`, our function looks like: + +```python +@njit(["void(u1[:], f4[:], i4, i4)", "void(f4[:], f4[:], i4, i4)"], cache=True, parallel=True) +def extract_bpass(inarray, outarray, nchans, nsamps): + for ichan in prange(nchans): + for isamp in range(nsamps): + outarray[ichan] += inarray[nchans * isamp + ichan] +``` + +This function receives a block of data and sums that block along the time axis. +We use the jit compiler directive `parallel` to enable OpenMP threading. + +## Reporting an issue + +[Post an issue on the GitHub repository](https://github.com/FRBs/sigpyproc3/issues). When you post an issue, +please provide the details to reproduce the issue. + +## Contributing code or documentation + +An excellent place to start is the [AstroPy developer docs](https://docs.astropy.org/en/stable/development/workflow/development_workflow.html). diff --git a/docs/dev.rst b/docs/dev.rst deleted file mode 100644 index 3122f9e..0000000 --- a/docs/dev.rst +++ /dev/null @@ -1,73 +0,0 @@ -.. _dev: - -Developer documentation -======================= - -Here we will cover all the steps required to add new functionality to the -``sigpyproc`` package. To do this, we will first consider adding a new function -to the :class:`~sigpyproc.base.Filterbank` class. - - -Adding a new function: ``bandpass()`` -------------------------------------- -**Aim:** Add a new function called bandpass, which will return the total power -as a function of frequency for our observation. - -**Files to be modified:** ``sigpyproc/base.py``, ``sigpyproc/core/kernels.py``. - -Step 1: Write the API part -^^^^^^^^^^^^^^^^^^^^^^^^^^ -The first step is to write the user API of the function. As this function -will run on data with both time and frequency resolution, -it belongs in the :class:`~sigpyproc.base.Filterbank` class. - -.. code-block:: python - - def bandpass(self, gulp=512, **kwargs): - bpass_ar = np.zeros(self.header.nchans, dtype=np.float32) - num_samples = 0 - for nsamps, ii, data in self.read_plan(**plan_kwargs): - kernel.extract_bpass(data, bpass_ar, self.header.nchans, nsamps) - num_samples += nsamps - bpass_ar = bpass_ar / num_samples - return TimeSeries(bpass_ar, self.header.new_header({"nchans": 1})) - -Looking at the important lines, we have: -Return an instance of the :class:`~sigpyproc.timeseries.TimeSeries` class. -The :class:`~sigpyproc.timeseries.TimeSeries` class takes two arguments, -an instance of :py:obj:`numpy.ndarray` and an instance of -:class:`~sigpyproc.header.Header`. - -Now we have something similar to a normal :py:obj:`numpy.ndarray`, -which exports several other methods for convenience. - -Step 2: Write the core Numba kernel -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -We called a kernel function named ``extract_bpass``. This function belongs in the -``sigpyproc/core/kernels.py`` file. In ``kernels.py``, our function looks like: - -.. code-block:: python - - @njit(["void(u1[:], f4[:], i4, i4)", "void(f4[:], f4[:], i4, i4)"], cache=True, parallel=True) - def extract_bpass(inarray, outarray, nchans, nsamps): - for ichan in prange(nchans): - for isamp in range(nsamps): - outarray[ichan] += inarray[nchans * isamp + ichan] - -This function receives a block of data and sums that block along the time axis. -We use the jit compiler directive ``parallel`` to enable OpenMP threading. - - -Reporting an issue ------------------- - -`Post an issue on the GitHub repository -`_. When you post an issue, -please provide the details to reproduce the issue. - - -Contributing code or documentation ----------------------------------- - -An excellent place to start is the `AstroPy developer docs -`_. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..6cd97ee --- /dev/null +++ b/docs/index.md @@ -0,0 +1,46 @@ +# sigpyproc + +**sigpyproc** is a FRB/pulsar data analysis library for Python. It provides an +OOP approach to pulsar data handling through the use of objects representing +different data types (e.g. +[SIGPROC filterbank](http://sigproc.sourceforge.net), +[PSRFITS](https://www.atnf.csiro.au/research/pulsar/psrfits_definition/Psrfits.html), +time series, fourier series, etc.). + +As pulsar data processing is often time critical, speed is maintained using +the excellent [numba](https://numba.pydata.org/) library. + +`sigpyproc` is intended to be an Python alternative for the +[SIGPROC filterbank](http://sigproc.sourceforge.net) pulsar signal processing toolbox. +Over time it has also developed and become an independent project in its own right. +Unlike [SIGPROC](http://sigproc.sourceforge.net) and [PRESTO](https://github.com/scottransom/presto), +`sigpyproc` does not currently have full capabilities as a piece of FRB/pulsar searching software. +Instead, `sigpyproc` provides data manipulation routines which are well suited to preprocessing +and micro-management of pulsar data. The structure of the package also makes it an ideal development +environment, with a simple plug-and-play system with new modules and extensions. + +[![GitHub CI](https://github.com/FRBs/sigpyproc3/workflows/GitHub%20CI/badge.svg)](https://github.com/FRBs/sigpyproc3/actions) +[![Docs](https://readthedocs.org/projects/sigpyproc3/badge/?version=latest)](https://sigpyproc3.readthedocs.io/en/latest/?badge=latest) +[![codecov](https://codecov.io/gh/FRBs/sigpyproc3/branch/main/graph/badge.svg)](https://codecov.io/gh/FRBs/sigpyproc3) +[![License](https://img.shields.io/github/license/FRBs/sigpyproc3)](https://github.com/FRBs/sigpyproc3/blob/main/LICENSE) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) + +## Contents + +```{toctree} +:caption: User Guide +:maxdepth: 1 + +install +cmd +modules +dev +changes +``` + +```{toctree} +:caption: Tutorials +:maxdepth: 1 + +tutorials/quickstart.ipynb +``` diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 3414a4d..0000000 --- a/docs/index.rst +++ /dev/null @@ -1,53 +0,0 @@ -sigpyproc -========= - -**sigpyproc** is a FRB/pulsar data analysis library for Python. It provides an -OOP approach to pulsar data handling through the use of objects representing -different data types (e.g. -`SIGPROC filterbank `_, -`PSRFITS `_, -time series, fourier series, etc.). - -As pulsar data processing is often time critical, speed is maintained using -the excellent `numba `_ library. - -`sigpyproc` is intended to be an Python alternative for the -`SIGPROC filterbank `_ pulsar signal processing toolbox. -Over time it has also developed and become an independent project in its own right. -Unlike `SIGPROC `_ and `PRESTO `_, -`sigpyproc` does not currently have full capabilities as a piece of FRB/pulsar searching software. -Instead, `sigpyproc` provides data manipulation routines which are well suited to preprocessing -and micro-management of pulsar data. The structure of the package also makes it an ideal development -environment, with a simple plug-and-play system with new modules and extensions. - -.. image:: https://github.com/FRBs/sigpyproc3/workflows/GitHub%20CI/badge.svg - :target: https://github.com/FRBs/sigpyproc3/actions -.. image:: https://readthedocs.org/projects/sigpyproc3/badge/?version=latest - :target: https://sigpyproc3.readthedocs.io/en/latest/?badge=latest - :alt: Documentation Status -.. image:: https://codecov.io/gh/FRBs/sigpyproc3/branch/main/graph/badge.svg - :target: https://codecov.io/gh/FRBs/sigpyproc3 -.. image:: https://img.shields.io/github/license/FRBs/sigpyproc3 - :target: https://github.com/FRBs/sigpyproc3/blob/main/LICENSE -.. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/psf/black - - -Contents --------- - -.. toctree:: - :maxdepth: 1 - :caption: User Guide - - install - cmd - modules - dev - changes - -.. toctree:: - :maxdepth: 1 - :caption: Tutorials - - tutorials/quickstart.ipynb diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 0000000..986ce80 --- /dev/null +++ b/docs/install.md @@ -0,0 +1,56 @@ +(install)= + +# Installation + +:::{note} +`sigpyproc` requires Python 3.8 and later. +::: + +## Using pip + +The recommended method of installing *sigpyproc* is with [pip](https://pip.pypa.io): + +```bash +python -m pip install -U git+https://github.com/FRBs/sigpyproc3 +``` + +Some of the required dependencies are: + +- [numpy](https://numpy.org) +- [numba](https://numba.pydata.org) +- [astropy](https://www.astropy.org) +- [matplotlib](https://matplotlib.org) +- [bottleneck](https://bottleneck.readthedocs.io) +- [h5py](https://docs.h5py.org/en/stable/) +- [attrs](https://attrs.org) +- [rich](https://rich.readthedocs.io) +- [click](https://click.palletsprojects.com/en/latest/) + +(source)= + +## From Source + +The source code for *sigpyproc* can be downloaded and installed [from GitHub](https://github.com/FRBs/sigpyproc3) by running + +```bash +git clone https://github.com/FRBs/sigpyproc3.git +cd sigpyproc3 +python -m pip install -e . +``` + +## Testing + +To run the unit tests with [pytest](https://docs.pytest.org), +first install the testing dependencies using pip: + +```bash +python -m pip install -e ".[tests]" +``` + +and then execute: + +```bash +python -m pytest -v tests +``` + +Normally, all of the tests should pass. diff --git a/docs/install.rst b/docs/install.rst deleted file mode 100644 index 554e872..0000000 --- a/docs/install.rst +++ /dev/null @@ -1,63 +0,0 @@ -.. _install: - -Installation -============ - -.. note:: ``sigpyproc`` requires Python 3.8 and later. - -Using pip ---------- - -The recommended method of installing *sigpyproc* is with `pip -`_: - -.. code-block:: bash - - python -m pip install -U git+https://github.com/FRBs/sigpyproc3 - - -Some of the required dependencies are: - -- `numpy `_ -- `astropy `_ -- `numba `_ -- `bottleneck `_ -- `h5py `_ -- `attrs `_ -- `rich `_ -- `click `_ -- `iqrm `_ - - -.. _source: - -From Source ------------ - -The source code for *sigpyproc* can be downloaded and installed `from GitHub -`_ by running - -.. code-block:: bash - - git clone https://github.com/FRBs/sigpyproc3.git - cd sigpyproc3 - python -m pip install -e . - - -Testing -------- - -To run the unit tests with `pytest `_, -first install the testing dependencies using pip: - -.. code-block:: bash - - python -m pip install -e ".[tests]" - -and then execute: - -.. code-block:: bash - - python -m pytest -v tests - -Normally, all of the tests should pass. \ No newline at end of file diff --git a/docs/modules.md b/docs/modules.md new file mode 100644 index 0000000..2960d4c --- /dev/null +++ b/docs/modules.md @@ -0,0 +1,7 @@ +# API Reference + +```{toctree} +:titlesonly: true + +api/index +``` diff --git a/docs/modules.rst b/docs/modules.rst deleted file mode 100644 index 3efcee9..0000000 --- a/docs/modules.rst +++ /dev/null @@ -1,16 +0,0 @@ -API documentation -================= - -.. toctree:: - :maxdepth: 4 - - api/readers - api/header - api/block - api/timeseries - api/fourierseries - api/foldedcube - api/base - api/utils - api/io - api/core diff --git a/docs/tutorials/quickstart.ipynb b/docs/tutorials/quickstart.ipynb index 511275c..dee4940 100644 --- a/docs/tutorials/quickstart.ipynb +++ b/docs/tutorials/quickstart.ipynb @@ -33,14 +33,22 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Lets start by loading our filterbank file into sigpyproc. To do this, we require the :class:`~sigpyproc.readers.FilReader` class from the :automodule:`sigpyproc.readers` module." + "Lets start by loading our filterbank file into sigpyproc. To do this, we require the [FilReader](#sigpyproc.readers.FilReader) class from the [sigpyproc.readers](#sigpyproc.readers) module." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "OMP: Info #276: omp_set_nested routine deprecated, please use omp_set_max_active_levels instead.\n" + ] + } + ], "source": [ "import numpy as np\n", "from rich.pretty import Pretty\n", @@ -64,7 +72,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 3, @@ -80,7 +88,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "`myFil` now contains an instance of the :class:`sigpyproc.readers.FilReader` class. We can access obervational\n", + "`myFil` now contains an instance of the [FilReader](#sigpyproc.readers.FilReader) class. We can access obervational\n", "metadata through the `myFil.header` attribute:" ] }, @@ -104,9 +112,9 @@ " nsamples=187520,\n", " nifs=1,\n", " coord=<SkyCoord (ICRS): (ra, dec) in deg\n", - " (0., 0.)>,\n", - " azimuth=<Angle 0. deg>,\n", - " zenith=<Angle 0. deg>,\n", + " (0., 0.)>,\n", + " azimuth=<Angle 0. deg>,\n", + " zenith=<Angle 0. deg>,\n", " telescope='Fake',\n", " backend='FAKE',\n", " source='P: 250.000000000000 ms, DM: 30.000',\n", @@ -117,12 +125,19 @@ " period=0,\n", " accel=0,\n", " signed=False,\n", - " rawdatafile=None,\n", - " hdrlens=[244],\n", - " datalens=[3000320],\n", - " filenames=['../../tests/data/tutorial.fil'],\n", - " nsamples_files=[187520],\n", - " tstart_files=[50000.0]\n", + " rawdatafile='',\n", + " stream_info=StreamInfo(\n", + " entries=[\n", + " FileInfo(\n", + " filename='../../tests/data/tutorial.fil',\n", + " hdrlen=244,\n", + " datalen=3000320,\n", + " nsamples=187520,\n", + " tstart=50000.0,\n", + " tsamp=0.00032\n", + " )\n", + " ]\n", + " )\n", ")\n", "\n" ], @@ -139,9 +154,9 @@ " \u001b[33mnsamples\u001b[0m=\u001b[1;36m187520\u001b[0m,\n", " \u001b[33mnifs\u001b[0m=\u001b[1;36m1\u001b[0m,\n", " \u001b[33mcoord\u001b[0m=\u001b[1m<\u001b[0m\u001b[1;95mSkyCoord\u001b[0m\u001b[39m \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mICRS\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mra, dec\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m in deg\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;39m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[39m., \u001b[0m\u001b[1;36m0\u001b[0m\u001b[39m.\u001b[0m\u001b[1;39m)\u001b[0m\u001b[1m>\u001b[0m,\n", - " \u001b[33mazimuth\u001b[0m=\u001b[1m<\u001b[0m\u001b[1;95mAngle\u001b[0m\u001b[39m \u001b[0m\u001b[1;36m0\u001b[0m\u001b[39m. deg\u001b[0m\u001b[1m>\u001b[0m,\n", - " \u001b[33mzenith\u001b[0m=\u001b[1m<\u001b[0m\u001b[1;95mAngle\u001b[0m\u001b[39m \u001b[0m\u001b[1;36m0\u001b[0m\u001b[39m. deg\u001b[0m\u001b[1m>\u001b[0m,\n", + "\u001b[39m \u001b[0m\u001b[1;39m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[39m., \u001b[0m\u001b[1;36m0\u001b[0m\u001b[39m.\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m>,\u001b[0m\n", + "\u001b[39m \u001b[0m\u001b[33mazimuth\u001b[0m\u001b[39m=,\u001b[0m\n", + "\u001b[39m \u001b[0m\u001b[33mzenith\u001b[0m\u001b[39m=\u001b[0m,\n", " \u001b[33mtelescope\u001b[0m=\u001b[32m'Fake'\u001b[0m,\n", " \u001b[33mbackend\u001b[0m=\u001b[32m'FAKE'\u001b[0m,\n", " \u001b[33msource\u001b[0m=\u001b[32m'P: 250.000000000000 ms, DM: 30.000'\u001b[0m,\n", @@ -152,12 +167,19 @@ " \u001b[33mperiod\u001b[0m=\u001b[1;36m0\u001b[0m,\n", " \u001b[33maccel\u001b[0m=\u001b[1;36m0\u001b[0m,\n", " \u001b[33msigned\u001b[0m=\u001b[3;91mFalse\u001b[0m,\n", - " \u001b[33mrawdatafile\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mhdrlens\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;36m244\u001b[0m\u001b[1m]\u001b[0m,\n", - " \u001b[33mdatalens\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;36m3000320\u001b[0m\u001b[1m]\u001b[0m,\n", - " \u001b[33mfilenames\u001b[0m=\u001b[1m[\u001b[0m\u001b[32m'../../tests/data/tutorial.fil'\u001b[0m\u001b[1m]\u001b[0m,\n", - " \u001b[33mnsamples_files\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;36m187520\u001b[0m\u001b[1m]\u001b[0m,\n", - " \u001b[33mtstart_files\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;36m50000.0\u001b[0m\u001b[1m]\u001b[0m\n", + " \u001b[33mrawdatafile\u001b[0m=\u001b[32m''\u001b[0m,\n", + " \u001b[33mstream_info\u001b[0m=\u001b[1;35mStreamInfo\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mentries\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mFileInfo\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mfilename\u001b[0m=\u001b[32m'../../tests/data/tutorial.fil'\u001b[0m,\n", + " \u001b[33mhdrlen\u001b[0m=\u001b[1;36m244\u001b[0m,\n", + " \u001b[33mdatalen\u001b[0m=\u001b[1;36m3000320\u001b[0m,\n", + " \u001b[33mnsamples\u001b[0m=\u001b[1;36m187520\u001b[0m,\n", + " \u001b[33mtstart\u001b[0m=\u001b[1;36m50000\u001b[0m\u001b[1;36m.0\u001b[0m,\n", + " \u001b[33mtsamp\u001b[0m=\u001b[1;36m0\u001b[0m\u001b[1;36m.00032\u001b[0m\n", + " \u001b[1m)\u001b[0m\n", + " \u001b[1m]\u001b[0m\n", + " \u001b[1m)\u001b[0m\n", "\u001b[1m)\u001b[0m\n" ] }, @@ -256,7 +278,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "e6efb548100d49e99f7b00fd712a5fbd", + "model_id": "bf9aae68075e48c8ad4f72173b9e6798", "version_major": 2, "version_minor": 0 }, @@ -303,7 +325,7 @@ { "data": { "text/plain": [ - "TimeSeries([108., 100., 102., ..., 105., 111., 107.], dtype=float32)" + "" ] }, "execution_count": 8, @@ -339,10 +361,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Here we have dedispersed to a DM of 30 pc cm$^{-3}$ with the result being an instance of the\n", - ":autoclass:`sigpyproc.timeseries.TimeSeries` class, which we have called `myTim`.\n", + "Here we have dedispersed to a DM of 30 pc cm$^{-3}$ with the result being an instance of the [TimeSeries](#sigpyproc.timeseries.TimeSeries) class, which we have called `myTim`.\n", "\n", - "The :autoclass:`sigpyproc.timeseries.TimeSeries` class in a subclass of `numpy.ndarray`, and is capable of using\n", + "The [TimeSeries](#sigpyproc.timeseries.TimeSeries) class in a subclass of `numpy.ndarray`, and is capable of using\n", "all standard numpy functions. For example:" ] }, @@ -354,7 +375,7 @@ { "data": { "text/plain": [ - "TimeSeries(19636992., dtype=float32)" + "np.float32(19636992.0)" ] }, "execution_count": 10, @@ -363,7 +384,7 @@ } ], "source": [ - "myTim.sum()" + "myTim.data.sum()" ] }, { @@ -374,7 +395,7 @@ { "data": { "text/plain": [ - "TimeSeries(121., dtype=float32)" + "np.float32(121.0)" ] }, "execution_count": 11, @@ -383,7 +404,7 @@ } ], "source": [ - "myTim.max()" + "myTim.data.max()" ] }, { @@ -394,7 +415,7 @@ { "data": { "text/plain": [ - "TimeSeries(88., dtype=float32)" + "np.float32(88.0)" ] }, "execution_count": 12, @@ -403,7 +424,7 @@ } ], "source": [ - "myTim.min()" + "myTim.data.min()" ] }, { @@ -414,7 +435,7 @@ { "data": { "text/plain": [ - "TimeSeries(105., dtype=float32)" + "np.float32(105.0)" ] }, "execution_count": 13, @@ -423,7 +444,7 @@ } ], "source": [ - "np.median(myTim)" + "np.median(myTim.data)" ] }, { @@ -486,10 +507,7 @@ { "data": { "text/plain": [ - "FourierSeries([ 1.9636884e+07 +0.j , -2.9424850e+02 +429.87863j,\n", - " 5.4652838e+02 -577.44696j, ...,\n", - " -1.3942198e+03+1670.1677j , -1.2117781e+03-1779.332j ,\n", - " -2.7670000e+03 +0.j ], dtype=complex64)" + "" ] }, "execution_count": 16, @@ -505,9 +523,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The :autoclass:`sigpyproc.fourierseries.FourierSeries` is also a subclass of `numpy.ndarray`, where array elements are `numpy.complex64`.\n", + "The [FourierSeries](#sigpyproc.fourierseries.FourierSeries) is also a subclass of `numpy.ndarray`, where array elements are `numpy.complex64`.\n", "\n", - "Using the `remove_rednoise` method of `myFS`, we can de-redden the Fourier series:" + "Using the `deredden` method of `myFS`, we can de-redden the Fourier series:" ] }, { @@ -516,7 +534,7 @@ "metadata": {}, "outputs": [], "source": [ - "myFS_red = myFS.remove_rednoise()" + "myFS_red = myFS.deredden()" ] }, { @@ -527,10 +545,7 @@ { "data": { "text/plain": [ - "FourierSeries([ 1. +0.j , nan +nanj,\n", - " nan +nanj, ..., -0.8724377+1.045113j,\n", - " -0.7582742-1.113423j, -1.7314595+0.j ],\n", - " dtype=complex64)" + "" ] }, "execution_count": 18, @@ -555,7 +570,7 @@ "metadata": {}, "outputs": [], "source": [ - "mySpec = myFS_red.form_spec(interpolated=True)" + "mySpec = myFS_red.form_spec(interpolate=True)" ] }, { @@ -566,8 +581,7 @@ { "data": { "text/plain": [ - "PowerSpectrum([1. , nan, nan, ..., 1.3613995, 1.5284487,\n", - " 1.7314595], dtype=float32)" + "" ] }, "execution_count": 20, @@ -583,7 +597,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Here we have set the `interpolated` flag to True, causing the `formSpec` function to perform nearest bin interpolation.\n", + "Here we have set the `interpolate` flag to True, causing the `formSpec` function to perform nearest bin interpolation.\n", "\n", "`mySpec` contains several convenience methods to help with navigating the power spectrum. For instance:" ] @@ -652,16 +666,11 @@ { "data": { "text/plain": [ - "[PowerSpectrum([1. , nan, nan, ..., 2.7979643, 2.9650135,\n", - " 1.7314595], dtype=float32),\n", - " PowerSpectrum([1. , nan, nan, ..., 2.7979643, 2.9650135,\n", - " 1.7314595], dtype=float32),\n", - " PowerSpectrum([1. , nan, nan, ..., 2.7979643, 2.9650135,\n", - " 1.7314595], dtype=float32),\n", - " PowerSpectrum([1. , nan, nan, ..., 2.7979643, 2.9650135,\n", - " 1.7314595], dtype=float32),\n", - " PowerSpectrum([1. , nan, nan, ..., 2.7979643, 2.9650135,\n", - " 1.7314595], dtype=float32)]" + "[,\n", + " ,\n", + " ,\n", + " ,\n", + " ]" ] }, "execution_count": 24, @@ -691,7 +700,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Both the :autoclass:`sigpyproc.timeseries.TimeSeries` and the :autoclass:`sigpyproc.fourierseries.FourierSeries`\n", + "Both the [TimeSeries](#sigpyproc.timeseries.TimeSeries) and the [FourierSeries](#sigpyproc.fourierseries.FourierSeries) \n", "have methods to phase fold their data. Using our earlier myFil instance, we will fold our filterbank file with a period\n", "of 250 ms and a DM of pc cm$^{-3}$ and acceleration of 0 ms$^{-2}$." ] @@ -704,7 +713,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "64a13bef55324e1fb97eb996d7468347", + "model_id": "d2e049fc431e4dbbb962835417dd8a6b", "version_major": 2, "version_minor": 0 }, @@ -780,23 +789,23 @@ } ], "source": [ - "myFold.shape" + "myFold.data.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "The the :autoclass:`sigpyproc.foldedcube.FoldedData` has several functions to enable simple slicing and summing of\n", + "The the [FoldedData](#sigpyproc.foldedcube.FoldedData) has several functions to enable simple slicing and summing of\n", "the folded data cube. These include:\n", "\n", - "* `getSubband`: select all data in a single frequency band\n", - "* `getSubint`: select all data in a single subintegration\n", - "* `getFreqPhase`: sum the data in the time axis\n", - "* `getTimePhase`: sum the data in the frequency axis\n", - "*`getProfile`: get the pulse profile of the fold\n", + "* `get_subband`: select all data in a single frequency band\n", + "* `get_subint`: select all data in a single subintegration\n", + "* `get_freqPhase`: sum the data in the time axis\n", + "* `get_timePhase`: sum the data in the frequency axis\n", + "* `get_profile`: get the pulse profile of the fold\n", "\n", - "We can also tweak the DM and period of the fold using the `updateParams` method:" + "We can also tweak the DM and period of the fold using the `update_params` method:" ] }, { @@ -833,16 +842,16 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 30, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -861,9 +870,59 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 30, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[0;31mSignature:\u001b[0m\n", + "\u001b[0mmyFil\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdownsample\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mtfactor\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'int'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mffactor\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'int'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0moutfile_name\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'str | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mgulp\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'int'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m16384\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mstart\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'int'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mnsamps\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'int | None'\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mplan_kwargs\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'Unpack[PlanKwargs]'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;34m'str'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mDocstring:\u001b[0m\n", + "Downsample data in time and/or frequency and write to file.\n", + "\n", + "Parameters\n", + "----------\n", + "tfactor : int, optional\n", + " factor by which to downsample in time, by default 1\n", + "ffactor : int, optional\n", + " factor by which to downsample in frequency, by default 1\n", + "outfile_name : str, optional\n", + " name of file to write to, by default ``basename_tfactor_ffactor.fil``\n", + "gulp : int, optional\n", + " number of samples in each read, by default 16384\n", + "start : int, optional\n", + " start sample, by default 0\n", + "nsamps : int, optional\n", + " number of samples to read, by default all\n", + "**plan_kwargs : dict\n", + " Additional keyword arguments for :func:`read_plan`.\n", + "\n", + "Returns\n", + "-------\n", + "str\n", + " output file name\n", + "\n", + "Raises\n", + "------\n", + "ValueError\n", + " If number of channels is not divisible by `ffactor`.\n", + "\u001b[0;31mFile:\u001b[0m /opt/homebrew/Caskroom/miniforge/base/envs/py3/lib/python3.11/site-packages/sigpyproc/base.py\n", + "\u001b[0;31mType:\u001b[0m method" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "myFil.downsample?" ] @@ -886,13 +945,13 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ff74b23ce7794699b5bd31fab49275de", + "model_id": "fbb2d48c89a945af9563dad26395838f", "version_major": 2, "version_minor": 0 }, @@ -928,7 +987,7 @@ } ], "source": [ - "spectrum = FilReader(\"../../tests/data/tutorial.fil\").collapse().rfft().remove_rednoise().form_spec(True)" + "spectrum = FilReader(\"../../tests/data/tutorial.fil\").collapse().rfft().deredden().form_spec(interpolate=True)" ] }, { @@ -956,7 +1015,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.12" + "version": "3.11.9" } }, "nbformat": 4, diff --git a/pyproject.toml b/pyproject.toml index a1dfad9..ddc0b79 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,12 +52,19 @@ tests = [ "mypy", "scipy", ] -docs = ["sphinx", "sphinx-book-theme", "sphinx-click", "myst-nb"] +docs = [ + "sphinx", + "sphinx-book-theme", + "sphinx-click", + "sphinx-prompt", + "sphinx-copybutton", + "myst-nb", + "numpydoc", +] develop = ["ruff"] [project.scripts] spp_header = "sigpyproc.apps.spp_header:main" -spp_decimate = "sigpyproc.apps.spp_decimate:main" spp_extract = "sigpyproc.apps.spp_extract:main" spp_clean = "sigpyproc.apps.spp_clean:main" @@ -66,6 +73,7 @@ include = [ "pyproject.toml", "sigpyproc/**/*.py", "tests/**/*.py", + "docs/**/*.py", ] exclude = ["sigpyproc/apps/spp_digifil.py"] @@ -117,3 +125,6 @@ exclude_lines = [ enable_incomplete_feature = ["Unpack"] ignore_missing_imports = true plugins = ["numpy.typing.mypy_plugin"] + +[tool.numpydoc_validation] +checks = ["all", "GL08", "EX01", "SA01", "ES01", "RT01", "SS02"] diff --git a/sigpyproc/base.py b/sigpyproc/base.py index 14e5ca3..3511e17 100644 --- a/sigpyproc/base.py +++ b/sigpyproc/base.py @@ -138,7 +138,7 @@ def read_plan( Yields ------ - :py:obj:`~collections.abc.Iterator` (tuple(int, int, :py:obj:`~numpy.ndarray`)) + Iterator[tuple[int, int, ndarray]] Tuple of number of samples read, index of read, and the unpacked data read Raises @@ -185,7 +185,7 @@ def compute_stats( start sample, by default 0 nsamps : int, optional number of samples to read, by default all - **plan_kwargs : Unpack[PlanKwargs] + **plan_kwargs : dict Keyword arguments for :func:`read_plan`. """ bag = ChannelStats(self.header.nchans, self.header.nsamples) @@ -215,7 +215,7 @@ def compute_stats_basic( start sample, by default 0 nsamps : int, optional number of samples to read, by default all - **plan_kwargs : Unpack[PlanKwargs] + **plan_kwargs : dict Keyword arguments for :func:`read_plan`. """ bag = ChannelStats(self.header.nchans, self.header.nsamples) @@ -1168,7 +1168,7 @@ def clean_rfi( Returns ------- - tuple(str, :class:`~sigpyproc.core.rfi.RFIMask`) + tuple[str, :class:`~sigpyproc.core.rfi.RFIMask`] Filename and mask of cleaned data Raises diff --git a/sigpyproc/core/stats.py b/sigpyproc/core/stats.py index 412d92f..0d8d449 100644 --- a/sigpyproc/core/stats.py +++ b/sigpyproc/core/stats.py @@ -16,11 +16,11 @@ class ZScoreResult: Attributes ---------- - zscores: np.ndarray + zscores: numpy.ndarray Robust Z-scores of the array. loc: float Estimated location used for the Z-score calculation. - scale: float | np.ndarray + scale: float | numpy.ndarray Estimated scale used for the Z-score calculation. """ @@ -83,7 +83,7 @@ def estimate_loc(array: np.ndarray, method: str = "median") -> float: Parameters ---------- - array : np.ndarray + array : numpy.ndarray The array to estimate the location of. method : str, optional The method to use for estimating the location, by default "median". @@ -119,14 +119,14 @@ def estimate_scale(array: np.ndarray, method: str = "mad") -> float | np.ndarray Parameters ---------- - array : np.ndarray + array : numpy.ndarray The array to estimate the scale of. method : str, optional The method to use for estimating the scale, by default "mad". Returns ------- - float | np.ndarray + float | numpy.ndarray The estimated scale of the array. Raises @@ -178,7 +178,7 @@ def zscore( Parameters ---------- - array : np.ndarray + array : numpy.ndarray The array to calculate the Z-score of. loc_method : str, optional The method to use for estimating the location, by default "median". diff --git a/sigpyproc/foldedcube.py b/sigpyproc/foldedcube.py index a058307..3cbe5af 100644 --- a/sigpyproc/foldedcube.py +++ b/sigpyproc/foldedcube.py @@ -257,13 +257,13 @@ def centre(self) -> FoldedData: def replace_nan(self) -> None: self.data[np.isnan(self.data)] = np.nanmedian(self.data) - def dedisperse(self, dm: float) -> None: - """Rotate the data cube to remove dispersion delay between subbands. + def update_dm(self, dm: float) -> None: + """Install a new DM in the data cube. Parameters ---------- dm : float - New DM to dedisperse to + The new DM to dedisperse to """ dmdelays = self._get_dmdelays(dm) for isubint in range(self.nsubints): @@ -281,7 +281,7 @@ def update_period(self, period: float) -> None: Parameters ---------- period : float - the new period to fold with + The new period to fold with """ pdelays = self._get_pdelays(period) for isubint in range(self.nsubints): diff --git a/sigpyproc/io/fileio.py b/sigpyproc/io/fileio.py index be0758f..1074823 100644 --- a/sigpyproc/io/fileio.py +++ b/sigpyproc/io/fileio.py @@ -148,7 +148,7 @@ class FileReader(FileBase): Parameters ---------- - sinfo : StreamInfo + sinfo : :py:obj:`~sigpyproc.io.sigproc.StreamInfo` stream information object containing header and data lengths mode : str, optional file opening mode, by default "r" @@ -397,7 +397,7 @@ def cwrite(self, arr: np.ndarray) -> None: def write(self, bo: bytes) -> None: """Write the given bytes-like object, bo to the file stream. - Wrapper for :py:obj:`io.RawIOBase.write()`. + Wrapper for :py:obj:`io.RawIOBase.write`. Parameters ---------- diff --git a/sigpyproc/io/pfits.py b/sigpyproc/io/pfits.py index a7efa46..6aca47c 100644 --- a/sigpyproc/io/pfits.py +++ b/sigpyproc/io/pfits.py @@ -131,7 +131,7 @@ def location(self) -> EarthLocation: @property def receiver(self) -> Receiver: - """:class:`~sigpyproc.io.hdu.Receiver`: Receiver information.""" + """:class:`Receiver`: Receiver information.""" return Receiver( name=self.header["FRONTEND"], nrcvr=self.header["NRCVR"], @@ -145,7 +145,7 @@ def receiver(self) -> Receiver: @property def backend(self) -> Backend: - """:class:`~sigpyproc.io.hdu.Backend`: Backend information.""" + """:class:`Backend`: Backend information.""" return Backend( name=self.header["BACKEND"], phase=self.header["BE_PHASE"], @@ -550,7 +550,7 @@ def read_freqs(self, isub: int) -> FrequencyChannels: Returns ------- - FrequencyChannels + :py:obj:`~sigpyproc.utils.FrequencyChannels` Centre frequency for each channel in MHz (NCHAN) """ freqs = self._fits["SUBINT"].data[isub]["DAT_FREQ"] diff --git a/sigpyproc/readers.py b/sigpyproc/readers.py index 222f484..63cd58b 100644 --- a/sigpyproc/readers.py +++ b/sigpyproc/readers.py @@ -41,7 +41,7 @@ class FilReader(Filterbank): Notes ----- To be considered as a Sigproc format filterbank file the header must only - contain keywords found in the :data:`~sigpyproc.io.sigproc.header_keys` dictionary. + contain keywords found in the :data:`sigpyproc.params.header_keys` dictionary. """ def __init__( @@ -446,7 +446,7 @@ def get_data(self, pad_mode: str = "median") -> FilterbankBlock: Returns ------- - FilterbankBlock + :class:`~sigpyproc.block.FilterbankBlock` Data block. """ logger.info( diff --git a/sigpyproc/utils.py b/sigpyproc/utils.py index 772de37..a7deb9e 100644 --- a/sigpyproc/utils.py +++ b/sigpyproc/utils.py @@ -7,31 +7,33 @@ import numpy as np from astropy import units from astropy.time import Time, TimeDelta +from numpy import typing as npt from rich.logging import RichHandler if TYPE_CHECKING: import inspect -def roll_array(arr: np.ndarray, shift: int, axis: int = 0) -> np.ndarray: - """Roll the elements in the array by `shift` positions along the given axis. +def roll_array(arr: npt.ArrayLike, shift: int, axis: int = 0) -> np.ndarray: + """ + Roll the elements in the array by `shift` positions along the given axis. The shift direction is from the end towards the beginning of the axis, opposite to the shift direction of :py:func:`~numpy.roll`. Parameters ---------- - arr : :py:obj:`~numpy.typing.ArrayLike` - input array to roll + arr : ArrayLike + Input array to roll. shift : int - number of bins to shift by + Number of bins to shift by. axis : int - axis to roll along, by default 0 + Axis to roll along, by default 0. Returns ------- - :py:obj:`~numpy.ndarray` - shifted numpy array + ndarray + Shifted numpy array. """ arr = np.asanyarray(arr) arr_size = arr.shape[axis] @@ -43,19 +45,20 @@ def roll_array(arr: np.ndarray, shift: int, axis: int = 0) -> np.ndarray: def nearest_factor(num: int, fac: int) -> int: - """Find nearest factor Calculates the factor of `num`, which is closest to `fac`. + """ + Find nearest factor Calculates the factor of `num`, which is closest to `fac`. Parameters ---------- num : int - number that we wish to factor + Number that we wish to factor. fac : int - number around which we wish to find factor + Number around which we wish to find factor. Returns ------- int - nearest factor + Nearest factor. """ factors = { factor @@ -67,6 +70,24 @@ def nearest_factor(num: int, fac: int) -> int: def next2_to_n(x: int) -> int: + """ + Find the next power of 2 greater than or equal to `x`. + + Parameters + ---------- + x : int + Number to find the next power of 2 for. + + Returns + ------- + int + Next power of 2 greater than or equal to `x`. + + Raises + ------ + ValueError + If `x` is not positive. + """ if x <= 0: msg = "Input must be positive." raise ValueError(msg) @@ -80,23 +101,24 @@ def get_logger( quiet: bool = False, log_file: str | None = None, ) -> logging.Logger: - """Get a fancy configured logger. + """ + Get a fancy configured logger. Parameters ---------- name : str - logger name + Logger name. level : int or str, optional - logging level, by default logging.INFO + Logging level, by default logging.INFO. quiet : bool, optional - if True set `level` as logging.WARNING, by default False + If True set `level` as logging.WARNING, by default False. log_file : str, optional - path to log file, by default None + Path to log file, by default None. Returns ------- logging.Logger - a logging object + A logging object. """ logger = logging.getLogger(name) logger.setLevel(logging.WARNING if quiet else level) @@ -129,21 +151,22 @@ def get_callerfunc(stack: list[inspect.FrameInfo]) -> str: def time_after_nsamps(tstart: float, tsamp: float, nsamps: int = 0) -> Time: - """Get time after given nsamps. If nsamps is not given then just return tstart. + """ + Get time after given nsamps. If nsamps is not given then just return tstart. Parameters ---------- tstart : float - starting mjd. + Starting mjd. tsamp : float - sampling time in seconds. + Sampling time in seconds. nsamps : int, optional - number of samples, by default 0 + Number of samples, by default 0. Returns ------- :class:`~astropy.time.Time` - Astropy Time object after given nsamps + Astropy Time object after given nsamps. """ precision = int(np.ceil(abs(np.log10(tsamp)))) tstart = Time(tstart, format="mjd", scale="utc", precision=precision) @@ -151,17 +174,18 @@ def time_after_nsamps(tstart: float, tsamp: float, nsamps: int = 0) -> Time: def duration_string(duration: float) -> str: - """Convert duration in seconds to human readable string. + """ + Convert duration in seconds to human readable string. Parameters ---------- duration : float - duration in seconds + Duration in seconds. Returns ------- str - human readable duration string + Human readable duration string. """ if duration < 60: return f"{duration:.1f} seconds" @@ -173,12 +197,13 @@ def duration_string(duration: float) -> str: class FrequencyChannels: - """FrequencyChannels class to handle frequency channels. + """ + FrequencyChannels class to handle frequency channels. Parameters ---------- freqs : :py:obj:`~numpy.ndarray` - array of frequencies + Array of frequencies. """ def __init__(self, freqs: np.ndarray) -> None: @@ -196,7 +221,7 @@ def array(self) -> units.Quantity: @property def nchans(self) -> int: - """float: Number of channels.""" + """int: Number of channels.""" return self._nchans @property diff --git a/tests/test_foldedcube.py b/tests/test_foldedcube.py index 7191fb4..999b751 100644 --- a/tests/test_foldedcube.py +++ b/tests/test_foldedcube.py @@ -108,7 +108,7 @@ def test_gets(self, filfile_4bit: str) -> None: def test_methods(self, filfile_4bit: str) -> None: fil = FilReader(filfile_4bit) cube = fil.fold(period=1, dm=10, nints=16, nbins=50) - cube.dedisperse(20.0) + cube.update_dm(20.0) np.testing.assert_equal(cube.dm, 20.0) cube.update_period(2.0) np.testing.assert_equal(cube.period, 2.0)