├── docs ├── requirements.txt ├── _static │ ├── kepler_12b.wav │ ├── BinaryPainting.png │ ├── astronify-TEXT.png │ ├── ASTRONIFY_white.png │ ├── astronify-CIRCLE.png │ ├── astronify-favicon.png │ ├── Intro_Astronify_Series_6_0.png │ ├── astronify.css │ ├── ASTRONIFY_Ball_white.svg │ ├── main.css │ ├── ASTRONIFY_white.svg │ └── ASTRONIFY.svg ├── contents.rst ├── astronify │ ├── How_To_Use_The_Simulator_files │ │ ├── How_To_Use_The_Simulator_12_0.png │ │ ├── How_To_Use_The_Simulator_16_0.png │ │ ├── How_To_Use_The_Simulator_20_0.png │ │ ├── How_To_Use_The_Simulator_24_0.png │ │ ├── How_To_Use_The_Simulator_28_0.png │ │ ├── How_To_Use_The_Simulator_2_0.png │ │ ├── How_To_Use_The_Simulator_32_0.png │ │ └── How_To_Use_The_Simulator_8_0.png │ ├── tutorials.rst │ ├── api.rst │ ├── install.rst │ ├── Intro_Astronify_Series.rst │ ├── index.rst │ └── How_To_Use_The_Simulator.rst ├── _templates │ └── layout.html ├── Makefile ├── make.bat ├── index.html ├── CreateWithLight.html ├── conf.py └── notebooks │ ├── Intro_Astronify_Series.ipynb │ └── How_To_Use_The_Simulator.ipynb ├── astronify ├── series │ ├── tests │ │ ├── __init__.py │ │ └── test_series.py │ └── __init__.py ├── utils │ ├── tests │ │ ├── __init__.py │ │ └── test_pitch_mapping.py │ ├── __init__.py │ ├── exceptions.py │ └── pitch_mapping.py ├── _astropy_init.py ├── data │ └── README.rst ├── __init__.py ├── simulator │ ├── __init__.py │ ├── example_cmds.sh │ ├── sim_lc_config.py │ ├── add_lc_noise.py │ ├── tests │ │ ├── test_check_flare_params.py │ │ ├── test_check_transit_params.py │ │ ├── test_add_lc_noise.py │ │ ├── test_sim_lc_config.py │ │ ├── test_add_sine_signal.py │ │ ├── test_add_transit_signal.py │ │ └── test_add_flare_signal.py │ ├── add_sine_signal.py │ ├── check_flare_params.py │ ├── check_transit_params.py │ ├── add_transit_signal.py │ ├── add_flare_signal.py │ ├── sim_lc_setup_args.py │ └── sim_lc.py └── conftest.py ├── pyproject.toml ├── .github ├── workflows │ ├── black.yml │ └── ci_workflows.yml ├── CODEOWNERS └── dependabot.yml ├── MANIFEST.in ├── licenses ├── README.rst ├── LICENSE.rst └── TEMPLATE_LICENCE.rst ├── .readthedocs.yml ├── CHANGES.rst ├── .gitignore ├── CITATION.cff ├── setup.cfg ├── setup.py ├── tox.ini ├── DEVELOPER_DOC.rst └── README.rst /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx-rtd-theme 2 | -------------------------------------------------------------------------------- /docs/_static/kepler_12b.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacetelescope/astronify/main/docs/_static/kepler_12b.wav -------------------------------------------------------------------------------- /docs/_static/BinaryPainting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacetelescope/astronify/main/docs/_static/BinaryPainting.png -------------------------------------------------------------------------------- /docs/_static/astronify-TEXT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacetelescope/astronify/main/docs/_static/astronify-TEXT.png -------------------------------------------------------------------------------- /docs/_static/ASTRONIFY_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacetelescope/astronify/main/docs/_static/ASTRONIFY_white.png -------------------------------------------------------------------------------- /docs/_static/astronify-CIRCLE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacetelescope/astronify/main/docs/_static/astronify-CIRCLE.png -------------------------------------------------------------------------------- /docs/_static/astronify-favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacetelescope/astronify/main/docs/_static/astronify-favicon.png -------------------------------------------------------------------------------- /docs/_static/Intro_Astronify_Series_6_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacetelescope/astronify/main/docs/_static/Intro_Astronify_Series_6_0.png -------------------------------------------------------------------------------- /astronify/series/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Licensed under a 3-clause BSD style license - see LICENSE.rst 2 | """ 3 | This module contains series tests. 4 | """ 5 | -------------------------------------------------------------------------------- /astronify/utils/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Licensed under a 3-clause BSD style license - see LICENSE.rst 2 | """ 3 | This module contains utils tests. 4 | """ 5 | -------------------------------------------------------------------------------- /docs/contents.rst: -------------------------------------------------------------------------------- 1 | .. toctree:: 2 | :maxdepth: 2 3 | 4 | astronify/install 5 | astronify/index 6 | astronify/api 7 | astronify/tutorials 8 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | 3 | requires = ["setuptools", 4 | "setuptools_scm", 5 | "wheel"] 6 | 7 | build-backend = 'setuptools.build_meta' 8 | -------------------------------------------------------------------------------- /docs/astronify/How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_12_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacetelescope/astronify/main/docs/astronify/How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_12_0.png -------------------------------------------------------------------------------- /docs/astronify/How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_16_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacetelescope/astronify/main/docs/astronify/How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_16_0.png -------------------------------------------------------------------------------- /docs/astronify/How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_20_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacetelescope/astronify/main/docs/astronify/How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_20_0.png -------------------------------------------------------------------------------- /docs/astronify/How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_24_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacetelescope/astronify/main/docs/astronify/How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_24_0.png -------------------------------------------------------------------------------- /docs/astronify/How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_28_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacetelescope/astronify/main/docs/astronify/How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_28_0.png -------------------------------------------------------------------------------- /docs/astronify/How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_2_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacetelescope/astronify/main/docs/astronify/How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_2_0.png -------------------------------------------------------------------------------- /docs/astronify/How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_32_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacetelescope/astronify/main/docs/astronify/How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_32_0.png -------------------------------------------------------------------------------- /docs/astronify/How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_8_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacetelescope/astronify/main/docs/astronify/How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_8_0.png -------------------------------------------------------------------------------- /.github/workflows/black.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v6 10 | - uses: psf/black@stable 11 | -------------------------------------------------------------------------------- /astronify/_astropy_init.py: -------------------------------------------------------------------------------- 1 | # Licensed under a 3-clause BSD style license - see LICENSE.rst 2 | 3 | __all__ = ["__version__"] 4 | 5 | try: 6 | from .version import version as __version__ 7 | except ImportError: 8 | __version__ = "" 9 | -------------------------------------------------------------------------------- /astronify/data/README.rst: -------------------------------------------------------------------------------- 1 | Data directory 2 | ============== 3 | 4 | This directory contains data files included with the package source 5 | code distribution. Note that this is intended only for relatively small files 6 | - large files should be externally hosted and downloaded as needed. 7 | -------------------------------------------------------------------------------- /astronify/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # Licensed under a 3-clause BSD style license - see LICENSE.rst 2 | 3 | # This sub-module is destined for common non-package specific utility 4 | # functions. 5 | 6 | 7 | from .pitch_mapping import * # noqa: F403 8 | 9 | __all__ = ["data_to_pitch"] # noqa: F405 10 | -------------------------------------------------------------------------------- /docs/astronify/tutorials.rst: -------------------------------------------------------------------------------- 1 | ******************* 2 | Astronify Tutorials 3 | ******************* 4 | 5 | This is a collection of tutorials on various Astronify features. 6 | 7 | 8 | .. toctree:: 9 | :maxdepth: 1 10 | 11 | Intro_Astronify_Series.rst 12 | 13 | How_To_Use_The_Simulator.rst 14 | -------------------------------------------------------------------------------- /docs/astronify/api.rst: -------------------------------------------------------------------------------- 1 | ************* 2 | Astronify API 3 | ************* 4 | 5 | .. automodapi:: astronify.series 6 | :no-inheritance-diagram: 7 | 8 | .. automodapi:: astronify.utils 9 | :no-inheritance-diagram: 10 | 11 | .. automodapi:: astronify.simulator 12 | :no-inheritance-diagram: 13 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This is a comment. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # These owners will be the default owners for everything in 5 | # the repo. Unless a later match takes precedence, 6 | # these owners will be requested for 7 | # review when someone opens a pull request. 8 | * @ceb8 @scfleming 9 | -------------------------------------------------------------------------------- /astronify/series/__init__.py: -------------------------------------------------------------------------------- 1 | # Licensed under a 3-clause BSD style license - see LICENSE.rst 2 | 3 | """ 4 | Data Series Sonification 5 | ======================== 6 | 7 | This module contains functionality for sonifying data series. 8 | 9 | """ 10 | 11 | 12 | from .series import * # noqa: F403 13 | 14 | __all__ = ["SoniSeries", "PitchMap"] # noqa: F405 15 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include CHANGES.rst 3 | include setup.cfg 4 | include LICENSE.rst 5 | include pyproject.toml 6 | 7 | recursive-include astronify *.pyx *.c *.pxd 8 | recursive-include docs * 9 | recursive-include licenses * 10 | recursive-include scripts * 11 | 12 | prune build 13 | prune docs/_build 14 | prune docs/api 15 | 16 | global-exclude *.pyc *.o 17 | -------------------------------------------------------------------------------- /licenses/README.rst: -------------------------------------------------------------------------------- 1 | Licenses 2 | ======== 3 | 4 | This directory holds license and credit information for the package, 5 | works the package is derived from, and/or datasets. 6 | 7 | Ensure that you pick a package licence which is in this folder and it matches 8 | the one mentioned in the top level README.rst file. If you are using the 9 | pre-rendered version of this template check for the word 'Other' in the README. 10 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: "ubuntu-24.04" 5 | tools: 6 | python: "3.11" 7 | apt_packages: 8 | - portaudio19-dev 9 | - libsndfile1-dev 10 | - libportmidi-dev 11 | - liblo-dev 12 | 13 | python: 14 | install: 15 | - method: pip 16 | path: . 17 | extra_requirements: 18 | - docs 19 | - all 20 | - requirements: docs/requirements.txt 21 | 22 | sphinx: 23 | configuration: docs/conf.py 24 | -------------------------------------------------------------------------------- /astronify/__init__.py: -------------------------------------------------------------------------------- 1 | # Licensed under a 3-clause BSD style license - see LICENSE.rst 2 | 3 | 4 | # Packages may add whatever they like to this file, but 5 | # should keep this content at the top. 6 | # ---------------------------------------------------------------------------- 7 | from ._astropy_init import * # noqa 8 | 9 | # ---------------------------------------------------------------------------- 10 | from . import series # noqa 11 | from . import simulator # noqa 12 | from . import utils # noqa 13 | 14 | __all__ = ["series", "simulator", "utils"] # noqa 15 | __version__ = "0.11.1" 16 | -------------------------------------------------------------------------------- /astronify/simulator/__init__.py: -------------------------------------------------------------------------------- 1 | # Licensed under a 3-clause BSD style license - see LICENSE.rst 2 | 3 | """ 4 | Data Simulation 5 | =============== 6 | 7 | This module contains code for creating simulated/test light curves 8 | with a variety of signals in them as either an `~astropy.table.Table`, 9 | or FITS file. The files are designed to be read by the Astronify 10 | software package to use when testing the sonification process. 11 | 12 | 13 | Author: Scott W. Fleming (fleming@stsci.edu) 14 | 15 | """ 16 | 17 | from .sim_lc import * # noqa: F403 18 | 19 | __all__ = ["simulated_lc", "SimLcConfig"] # noqa: F405 20 | -------------------------------------------------------------------------------- /astronify/utils/exceptions.py: -------------------------------------------------------------------------------- 1 | # Licensed under a 3-clause BSD style license - see LICENSE.rst 2 | 3 | """ 4 | Custom exceptions used in the astronify classes 5 | """ 6 | 7 | from astropy.utils.exceptions import AstropyWarning 8 | 9 | 10 | class InvalidInputError(Exception): 11 | """ 12 | Exception to be issued when user input is incorrect in a 13 | way that prevents the function from running. 14 | """ 15 | 16 | pass 17 | 18 | 19 | class InputWarning(AstropyWarning): 20 | """ 21 | Warning to be issued when user input is incorrect in 22 | some way but doesn't prevent the function from running. 23 | """ 24 | 25 | pass 26 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" # See documentation for possible values 9 | directory: ".github/workflows" # Location of package manifests 10 | schedule: 11 | interval: "monthly" 12 | groups: 13 | actions: 14 | patterns: 15 | - "*" 16 | -------------------------------------------------------------------------------- /CHANGES.rst: -------------------------------------------------------------------------------- 1 | 0.11.1 (TBD) 2 | ------------------- 3 | 4 | - Adds full suite of unit tests to the simulator package. Replaces 5 | some checks for presence of expected columns in the data table 6 | within series.py. Fixes unintentional drop of author Jennifer Medina 7 | from setup.cfg. 8 | 9 | 0.11 (2025-01-31) 10 | ---------------- 11 | 12 | - Fixes installation via pypi, confirmed to work with modern Python 13 | (e.g., 3.11, 3.12), updated installation instructions and tips, 14 | infrastructure improvements in CI, fixes automated documentation 15 | builds, added __version__. 16 | 17 | 0.1 (2020-11-25) 18 | ---------------- 19 | 20 | - Initial release. Includes features! 21 | -------------------------------------------------------------------------------- /docs/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | {% block sidebartitle %} 3 | 4 | 5 | {{ _('Logo') }} 6 | 7 | 8 | {% if theme_display_version %} 9 | {%- set nav_version = version %} 10 | {% if READTHEDOCS and current_version %} 11 | {%- set nav_version = current_version %} 12 | {% endif %} 13 | {% if nav_version %} 14 |
15 | {{ nav_version }} 16 |
17 | {% endif %} 18 | {% endif %} 19 | 20 | {% include "searchbox.html" %} 21 | 22 | {% endblock %} 23 | {% set css_files = css_files + [ "_static/astronify.css" ] %} 24 | -------------------------------------------------------------------------------- /astronify/simulator/example_cmds.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Flat light curves: 4 | #python sim_lc.py flat outputs/flat_nonoise.fits -v 5 | #python sim_lc.py flat outputs/flat_noise-1.fits -v -n 1 6 | #python sim_lc.py flat outputs/flat_noise-10.fits -v -n 10 7 | 8 | # Transit light curves: 9 | #python sim_lc.py transit outputs/transit_nonoise.fits -v 10 | #python sim_lc.py transit outputs/transit_shallow_withnoise.fits -v --transit_depth 1.5 --transit_period 145 --transit_width 42 -n 0.5 11 | 12 | # Sinosoidal light curves: 13 | #python sim_lc.py sine outputs/sine_nonoise.fits -v 14 | #python sim_lc.py sine outputs/sine_shallow_withnoise.fits -v --sine_amp 1.5 --sine_period 142 -n 0.5 15 | 16 | # Flare light curves: 17 | python sim_lc.py flare outputs/flare_nonoises.fits -v 18 | -------------------------------------------------------------------------------- /astronify/simulator/sim_lc_config.py: -------------------------------------------------------------------------------- 1 | class SimLcConfig: 2 | """ 3 | Class that holds the default configuration parameters 4 | for simulated light curves. 5 | """ 6 | 7 | # General Parameters 8 | sim_lc_ofile = "" 9 | sim_lc_length = 500 10 | sim_lc_noise = 0.0 11 | sim_lc_visualize = False 12 | sim_lc_yoffset = 100.0 13 | 14 | # Transit Parameters 15 | sim_lc_transit_depth = 10.0 16 | sim_lc_transit_period = 50.0 17 | sim_lc_transit_start = 10 18 | sim_lc_transit_width = 5 19 | 20 | # Sinusoidal Parameters 21 | sim_lc_sine_amp = 10.0 22 | sim_lc_sine_period = 50.0 23 | 24 | # Flare Parameters 25 | sim_lc_flare_time = 10 26 | sim_lc_flare_amp = 100.0 27 | sim_lc_flare_halfwidth = 5 28 | -------------------------------------------------------------------------------- /astronify/simulator/add_lc_noise.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: add_lc_noise 3 | :synopsis: Adds Gaussian noise of a specified width to an array of fluxes. 4 | 5 | .. moduleauthor:: Scott W. Fleming 6 | """ 7 | 8 | import numpy as np 9 | 10 | 11 | def add_lc_noise(fluxes, lc_noise): 12 | """ 13 | :param fluxes: Array of fluxes to add noise to. 14 | :type fluxes: numpy.ndarray 15 | 16 | :param lc_noise: Standard deviation of normal distribution to draw from when 17 | adding noise, a value of zero means no noise is added. 18 | :type lc_noise: float 19 | 20 | :returns: numpy.ndarray -- The fluxes after being adjusted by the noise. 21 | """ 22 | 23 | noise = np.random.normal(0, lc_noise, len(fluxes)) 24 | 25 | return fluxes + noise 26 | -------------------------------------------------------------------------------- /docs/_static/astronify.css: -------------------------------------------------------------------------------- 1 | div.topbar a.brand { 2 | background: transparent url("astronify-CIRCLE.png") no-repeat 10px 4px; 3 | background-image: url("astronify-CIRCLE.png"), none; 4 | background-size: 32px 32px; 5 | } 6 | 7 | div.wy-side-nav-search{ 8 | background: #00617E; 9 | } 10 | 11 | div.wy-side-scroll{ 12 | background: #0D4960; 13 | } 14 | 15 | a.reference:hover{ 16 | background: rgb(11, 61, 78); 17 | } 18 | 19 | .logo{ 20 | height: 120px !important; 21 | margin-left:auto !important; 22 | margin-right: auto !important; 23 | padding-bottom: 20px !important; 24 | margin-top: 15px !important; 25 | } 26 | 27 | .intro{ 28 | color: aqua; 29 | } 30 | 31 | .heading{ 32 | color: #0D4960; 33 | font-size: 25px; 34 | font-weight: 800; 35 | line-height: 30px; 36 | 37 | } 38 | 39 | -------------------------------------------------------------------------------- /astronify/simulator/tests/test_check_flare_params.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: test_check_flare_params 3 | :synopsis: Regression tests for check_flare_params 4 | 5 | .. moduleauthor:: Scott W. Fleming 6 | """ 7 | 8 | import argparse 9 | import pytest 10 | from astronify.simulator.check_flare_params import check_flare_params 11 | 12 | """ 13 | Test cases that should result in an argparse.ArgumentTypeError raised: 14 | 1.) flare_time > n_fluxes 15 | 2.) flare_amplitude = 0.0 16 | """ 17 | 18 | 19 | @pytest.mark.parametrize( 20 | "n_fluxes, flare_time, flare_amp", 21 | [ 22 | (10, 100, 5), 23 | (10, 1, 0.0), 24 | ], 25 | ) 26 | def test_check_flare_params(n_fluxes, flare_time, flare_amp): 27 | with pytest.raises(argparse.ArgumentTypeError) as e_info: 28 | check_flare_params(n_fluxes, flare_time, flare_amp) 29 | -------------------------------------------------------------------------------- /astronify/simulator/tests/test_check_transit_params.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: test_check_transit_params 3 | :synopsis: Regression tests for check_transit_params 4 | 5 | .. moduleauthor:: Scott W. Fleming 6 | """ 7 | 8 | import argparse 9 | import pytest 10 | from astronify.simulator.check_transit_params import check_transit_params 11 | 12 | """ 13 | Test cases that should result in an argparse.ArgumentTypeError raised: 14 | 1.) transit_start > n_fluxes 15 | 2.) transit_width >= transit_period 16 | """ 17 | 18 | 19 | @pytest.mark.parametrize( 20 | "n_fluxes, transit_period, transit_start, transit_width", 21 | [ 22 | (10, 30, 100, 5), 23 | (10, 3, 1, 50), 24 | ], 25 | ) 26 | def test_check_transit_params(n_fluxes, transit_period, transit_start, transit_width): 27 | with pytest.raises(argparse.ArgumentTypeError) as e_info: 28 | check_transit_params(n_fluxes, transit_period, transit_start, transit_width) 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.py[cod] 3 | *.a 4 | *.o 5 | *.so 6 | __pycache__ 7 | 8 | # Ignore .c files by default to avoid including generated code. If you want to 9 | # add a non-generated .c extension, use `git add -f filename.c`. 10 | *.c 11 | 12 | # Other generated files 13 | notebooks/miles_stellar_spectra 14 | */version.py 15 | */cython_version.py 16 | htmlcov 17 | .coverage 18 | MANIFEST 19 | .ipynb_checkpoints 20 | 21 | # Sphinx 22 | docs/api 23 | docs/_build 24 | 25 | # Eclipse editor project files 26 | .project 27 | .pydevproject 28 | .settings 29 | 30 | # Pycharm editor project files 31 | .idea 32 | 33 | # Floobits project files 34 | .floo 35 | .flooignore 36 | 37 | # Packages/installer info 38 | *.egg 39 | *.egg-info 40 | dist 41 | build 42 | eggs 43 | .eggs 44 | parts 45 | bin 46 | var 47 | sdist 48 | develop-eggs 49 | .installed.cfg 50 | distribute-*.tar.gz 51 | 52 | # Other 53 | .cache 54 | .tox 55 | .*.sw[op] 56 | *~ 57 | .project 58 | .pydevproject 59 | .settings 60 | pip-wheel-metadata/ 61 | 62 | # Mac OSX 63 | .DS_Store 64 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.1.0 2 | message: "Please cite the following works when using this software: https://ui.adsabs.harvard.edu/abs/2024ascl.soft08005B" 3 | authors: 4 | - family-names: Brasseur 5 | given-names: C. E. 6 | - family-names: Fleming 7 | given-names: S. 8 | - family-names: Kotler 9 | given-names: J. 10 | - family-names: Meredith 11 | given-names: K. 12 | - family-names: Medina 13 | given-names: J. 14 | - family-names: Lim 15 | given-names: P.-L. 16 | title: "Astronify: Astronomical data sonification" 17 | version: 0.11.1 18 | date-released: 2025-02-14 19 | identifiers: 20 | - type: "ascl-id" 21 | value: "2408.005" 22 | - type: "bibcode" 23 | value: "2024ascl.soft08005B" 24 | abstract: "Astronify contains tools for sonifying astronomical data, specifically data series. Data series sonification takes a data table and maps one column to time, and one column to pitch. This technique is commonly used to sonify light curves, where observation time is scaled to listening time and flux is mapped to pitch. While Astronify’s sonification uses the columns “time” and “flux” by default, any two columns can be supplied and a sonification created." 25 | -------------------------------------------------------------------------------- /astronify/simulator/tests/test_add_lc_noise.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: test_add_lc_noise 3 | :synopsis: Regression tests for add_lc_noise 4 | 5 | .. moduleauthor:: Scott W. Fleming 6 | """ 7 | 8 | import pytest 9 | import numpy as np 10 | from astronify.simulator.add_lc_noise import add_lc_noise 11 | 12 | fluxes = np.asarray([1.0] * 100) 13 | all_ones = np.asarray([1.0] * len(fluxes)) 14 | 15 | """ 16 | Test cases that result in return arrays: 17 | 1. lc_noise = 0.0 18 | 2. lc_noise = 4.0 19 | 20 | Note that, since this is randomly generated noise, the non-zero test just 21 | verifies not all fluxes are at the original flux value of 1.0. One could 22 | consider defining a test such that all fluxes are within the expected standard 23 | deviation of the input value, but that seems too much. 24 | 25 | Test cases that result in exceptions being thrown: 26 | 1. [TODO pending Issue #86] a negative value for lc_noise is submitted 27 | """ 28 | 29 | 30 | def test_add_lc_noise_zero(): 31 | assert np.array_equal( 32 | add_lc_noise(fluxes, 0.0), 33 | all_ones, 34 | ) 35 | 36 | 37 | def test_add_lc_noise_nonzero(): 38 | assert not np.array_equal( 39 | add_lc_noise(fluxes, 4.0), 40 | all_ones, 41 | ) 42 | -------------------------------------------------------------------------------- /astronify/simulator/tests/test_sim_lc_config.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: test_sim_lc_config 3 | :synopsis: Regression tests for sim_lc_config 4 | 5 | .. moduleauthor:: Scott W. Fleming 6 | """ 7 | 8 | import pytest 9 | from astronify.simulator.sim_lc_config import SimLcConfig 10 | 11 | """ 12 | Test cases that should return True that the SimLcConfig object has these attributes: 13 | 1.) [sim_lc_ofile, sim_lc_length, sim_lc_noise, sim_lc_visualize, 14 | sim_lc_yoffset, sim_lc_transit_depth, sim_lc_transit_period, 15 | sim_lc_transit_start, sim_lc_transit_width, sim_lc_sine_amp, 16 | sim_lc_sine_period, sim_lc_flare_time, sim_lc_flare_amp, 17 | sim_lc_flare_halfwidth] 18 | """ 19 | 20 | 21 | @pytest.mark.parametrize( 22 | "attribute", 23 | [ 24 | ("sim_lc_ofile"), 25 | ("sim_lc_length"), 26 | ("sim_lc_noise"), 27 | ("sim_lc_visualize"), 28 | ("sim_lc_yoffset"), 29 | ("sim_lc_transit_depth"), 30 | ("sim_lc_transit_period"), 31 | ("sim_lc_transit_start"), 32 | ("sim_lc_transit_width"), 33 | ("sim_lc_sine_amp"), 34 | ("sim_lc_sine_period"), 35 | ("sim_lc_flare_time"), 36 | ("sim_lc_flare_amp"), 37 | ("sim_lc_flare_halfwidth"), 38 | ], 39 | ) 40 | def test_sim_lc_config(attribute): 41 | assert hasattr(SimLcConfig(), attribute) 42 | -------------------------------------------------------------------------------- /astronify/simulator/add_sine_signal.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: add_sine_signal 3 | :synopsis: Adds a sinusoidal signal to an array of fluxes. 4 | 5 | .. moduleauthor:: Scott W. Fleming 6 | """ 7 | 8 | import numpy as np 9 | import warnings 10 | from astronify.utils.exceptions import InputWarning 11 | from astropy.modeling.models import Sine1D 12 | 13 | 14 | def add_sine_signal(times, fluxes, sine_amp, sine_period): 15 | """ 16 | :param times: Array of (unitless) times (flux bins) associated with the 17 | fluxes. 18 | :type times: numpy.ndarray 19 | 20 | :param fluxes: Array of fluxes to add the sinusoidal signal to. 21 | :type fluxes: numpy.ndarray 22 | 23 | :param sine_amp: Amplitude of the sinusoidal signal to add. 24 | :type sine_anp: float 25 | 26 | :param sine_period: Period of the sinusoidal signal to add. 27 | :type sine_period: float 28 | 29 | :returns: numpy.ndarray -- The fluxes with the sinusoidal signal added. 30 | """ 31 | 32 | # Generate sinusoidal signal. 33 | if sine_amp > 0.0: 34 | sine_signal = Sine1D(amplitude=sine_amp, frequency=1.0 / sine_period) 35 | fluxes_to_add = sine_signal(times) 36 | elif sine_amp == 0.0: 37 | warnings.warn( 38 | "Warning: requested to add a single signal of zero amplitude.", InputWarning 39 | ) 40 | fluxes_to_add = np.asarray([0.0] * fluxes.size) 41 | 42 | return fluxes + fluxes_to_add 43 | -------------------------------------------------------------------------------- /astronify/simulator/check_flare_params.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: check_flare_params 3 | :synopsis: Performs checks on flare parameters to make sure the values 4 | are self-consistent (start index isn't larger than the length of the 5 | array, amplitude is not negative, etc.) 6 | 7 | .. moduleauthor:: Scott W. Fleming 8 | """ 9 | 10 | import argparse 11 | 12 | 13 | def check_flare_params(n_fluxes, flare_time, flare_amp): 14 | """ 15 | :param n_fluxes: Number of fluxes in the light curve. 16 | :type n_fluxes: int 17 | 18 | :param flare_time: Time corresponding to the maximum flare flux. 19 | :type flare_time: int 20 | 21 | :param flare_amp: The peak (maximum flux) of the flare. 22 | :type flare_amp: float 23 | """ 24 | 25 | # Flare time index must be less than total numbr of fluxes. 26 | if flare_time > n_fluxes: 27 | raise argparse.ArgumentTypeError( 28 | "The flare time at peak flux must be" 29 | " less than the total number of fluxes" 30 | " in the simulated light curve." 31 | " Number of fluxes = " 32 | + str(n_fluxes) 33 | + ", flare time requested is " 34 | + str(flare_time) 35 | + "." 36 | ) 37 | 38 | # The flare amplitude must be greater than zero. 39 | if flare_amp <= 0.0: 40 | raise argparse.ArgumentTypeError( 41 | "Flare amplitude must be greater than" 42 | " zero. Requested" 43 | " flare amplitude = " + str(flare_amp) + "." 44 | ) 45 | -------------------------------------------------------------------------------- /licenses/LICENSE.rst: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020, Clara Brasseur, Scott Fleming, Jennifer Kotler, Kate Meredith 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, this 10 | list of conditions and the following disclaimer in the documentation and/or 11 | other materials provided with the distribution. 12 | * Neither the name of the Astropy Team nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /licenses/TEMPLATE_LICENCE.rst: -------------------------------------------------------------------------------- 1 | This project is based upon the Astropy package template 2 | (https://github.com/astropy/package-template/) which is licenced under the terms 3 | of the following licence. 4 | 5 | --- 6 | 7 | Copyright (c) 2018, Astropy Developers 8 | All rights reserved. 9 | 10 | Redistribution and use in source and binary forms, with or without modification, 11 | are permitted provided that the following conditions are met: 12 | 13 | * Redistributions of source code must retain the above copyright notice, this 14 | list of conditions and the following disclaimer. 15 | * Redistributions in binary form must reproduce the above copyright notice, this 16 | list of conditions and the following disclaimer in the documentation and/or 17 | other materials provided with the distribution. 18 | * Neither the name of the Astropy Team nor the names of its contributors may be 19 | used to endorse or promote products derived from this software without 20 | specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 26 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 29 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /astronify/simulator/check_transit_params.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: check_transit_params 3 | :synopsis: Performs checks on transit parameters to make sure the values 4 | are self-consistent (start index isn't larger than the length of the 5 | array, duration isn't longer than the period, etc.) 6 | 7 | .. moduleauthor:: Scott W. Fleming 8 | """ 9 | 10 | import argparse 11 | 12 | 13 | def check_transit_params(n_fluxes, transit_period, transit_start, transit_width): 14 | """ 15 | :param n_fluxes: Number of fluxes in the light curve. 16 | :type n_fluxes: int 17 | 18 | :param transit_period: Period of transit (number of fluxes/bins between 19 | the start of each event.) 20 | :type transit_period: int 21 | 22 | :param transit_start: Start index of transit (the index of the flux/bin to 23 | use as the start of the first transit event.) 24 | :type transit_start: int 25 | 26 | :param transit_width: Width of transit (number of fluxes/bins between 27 | the start and end of each event.) 28 | :type transit_width: int 29 | """ 30 | 31 | # Start index must be less than total numbr of fluxes. 32 | if transit_start > n_fluxes: 33 | raise argparse.ArgumentTypeError( 34 | "The transit start must be less than" 35 | " the total number of fluxes in the" 36 | " simulated light curve." 37 | " Number of fluxes = " 38 | + str(n_fluxes) 39 | + ", start index requested is " 40 | + str(transit_start) 41 | + "." 42 | ) 43 | 44 | # The transit period must be greater than the transit duration (width). 45 | if transit_width >= transit_period: 46 | raise argparse.ArgumentTypeError( 47 | "Transit duration must be less than" 48 | " the transit period. Requested" 49 | " transit duration = " + str(transit_width) + ", requested" 50 | " transit period = " + str(transit_period) + "." 51 | ) 52 | -------------------------------------------------------------------------------- /docs/_static/ASTRONIFY_Ball_white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 21 | 22 | -------------------------------------------------------------------------------- /docs/astronify/install.rst: -------------------------------------------------------------------------------- 1 | ************ 2 | Installation 3 | ************ 4 | 5 | 6 | Installing astronify 7 | ==================== 8 | 9 | Using pip 10 | --------- 11 | 12 | The easiest way to install Astronify is using pip:: 13 | 14 | pip install astronify 15 | 16 | Errors installing dependent packages 17 | ------------------------------------ 18 | 19 | You may experience difficulties installing Astronify without some 20 | libraries pre-installed. If you run into problems, we recommend 21 | installing the following dependencies of `pyo` prior to running the 22 | `pip install astronify` step. 23 | 24 | Mac 25 | ~~~ 26 | We recommend installing `homebrew` (https://brew.sh) and then running:: 27 | 28 | brew install portaudio portmidi libsndfile liblo 29 | 30 | Still having issues? 31 | ^^^^^^^^^^^^^^^^^^^^ 32 | If you are still unable to install `astronify` (or `pyo`) via `pip`, 33 | it's possible `pip` is looking for them in the wrong spot, depending on 34 | how you've installed other packages in your envionrment. If so, try 35 | adding the following flags to your shell of choice, open a new 36 | terminal, and then run `pip install astronify` again:: 37 | 38 | # Example for .cshrc 39 | setenv CFLAGS '-I/opt/homebrew/include/' 40 | setenv LDFLAGS '-L/opt/homebrew/lib/' 41 | 42 | # Example for .bashrc 43 | export CFLAGS="-I/opt/homebrew/include/" 44 | export LDFLAGS="-L/opt/homebrew/lib/" 45 | 46 | Linux 47 | ~~~~~ 48 | We recommend installing the following with apt-get:: 49 | 50 | apt-get install portaudio19-dev libsndfile1-dev libportmidi-dev liblo-dev 51 | 52 | From source 53 | ----------- 54 | 55 | To install the bleeding edge version from github without downloading, 56 | run the following command:: 57 | 58 | pip git+https://github.com/spacetelescope/astronify.git 59 | 60 | The latest development version of astrocut can be cloned from github 61 | using this command:: 62 | 63 | git clone https://github.com/spacetelescope/astronify.git 64 | 65 | To install astrocut (from the root of the source tree):: 66 | 67 | pip install . 68 | 69 | 70 | -------------------------------------------------------------------------------- /astronify/conftest.py: -------------------------------------------------------------------------------- 1 | # This file is used to configure the behavior of pytest when using the Astropy 2 | # test infrastructure. It needs to live inside the package in order for it to 3 | # get picked up when running the tests inside an interpreter using 4 | # packagename.test 5 | 6 | import os 7 | 8 | from astropy.version import version as astropy_version 9 | 10 | # For Astropy 3.0 and later, we can use the standalone pytest plugin 11 | if astropy_version < "3.0": 12 | from astropy.tests.pytest_plugins import * # noqa 13 | 14 | del pytest_report_header 15 | ASTROPY_HEADER = True 16 | else: 17 | try: 18 | from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS 19 | 20 | ASTROPY_HEADER = True 21 | except ImportError: 22 | ASTROPY_HEADER = False 23 | 24 | 25 | def pytest_configure(config): 26 | 27 | if ASTROPY_HEADER: 28 | 29 | config.option.astropy_header = True 30 | 31 | # Customize the following lines to add/remove entries from the list of 32 | # packages for which version numbers are displayed when running the tests. 33 | PYTEST_HEADER_MODULES.pop("Pandas", None) 34 | PYTEST_HEADER_MODULES["scikit-image"] = "skimage" 35 | 36 | from . import __version__ 37 | 38 | packagename = os.path.basename(os.path.dirname(__file__)) 39 | TESTED_VERSIONS[packagename] = __version__ 40 | 41 | 42 | # Uncomment the last two lines in this block to treat all DeprecationWarnings as 43 | # exceptions. For Astropy v2.0 or later, there are 2 additional keywords, 44 | # as follow (although default should work for most cases). 45 | # To ignore some packages that produce deprecation warnings on import 46 | # (in addition to 'compiler', 'scipy', 'pygments', 'ipykernel', and 47 | # 'setuptools'), add: 48 | # modules_to_ignore_on_import=['module_1', 'module_2'] 49 | # To ignore some specific deprecation warning messages for Python version 50 | # MAJOR.MINOR or later, add: 51 | # warnings_to_ignore_by_pyver={(MAJOR, MINOR): ['Message to ignore']} 52 | # from astropy.tests.helper import enable_deprecations_as_exceptions # noqa 53 | # enable_deprecations_as_exceptions() 54 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = astronify 3 | author = Clara Brasseur, Scott Fleming, Jennifer Kotler, Kate Meredith, Jennifer Medina, Pey-Lian Lim 4 | author_email = astronify@stsci.edu 5 | license = BSD 3-Clause 6 | license_file = licenses/LICENSE.rst 7 | url = https://github.com/spacetelescope/astronify 8 | description = Sonification of astronomical data. 9 | version = attr: astronify.__version__ 10 | long_description = file: README.rst 11 | long_description_content_type = text/x-rst 12 | edit_on_github = False 13 | github_project = spacetelscope/astronify 14 | 15 | [options] 16 | zip_safe = False 17 | packages = find: 18 | python_requires = >=3.9 19 | setup_requires = setuptools_scm 20 | install_requires = 21 | astropy 22 | scipy 23 | matplotlib 24 | pyo 25 | requests 26 | notebook 27 | 28 | [options.entry_points] 29 | console_scripts = 30 | astropy-package-template-example = packagename.example_mod:main 31 | 32 | [options.extras_require] 33 | test = 34 | pytest-astropy 35 | docs = 36 | sphinx-astropy 37 | 38 | [options.package_data] 39 | astronify = data/* 40 | 41 | [tool:pytest] 42 | testpaths = "astronify" "docs" 43 | astropy_header = true 44 | doctest_plus = enabled 45 | text_file_format = rst 46 | addopts = --doctest-rst 47 | 48 | [coverage:run] 49 | omit = 50 | astronify/_astropy_init* 51 | astronify/conftest.py 52 | astronify/*setup_package* 53 | astronify/tests/* 54 | astronify/*/tests/* 55 | astronify/extern/* 56 | astronify/version* 57 | */astronify/_astropy_init* 58 | */astronify/conftest.py 59 | */astronify/*setup_package* 60 | */astronify/tests/* 61 | */astronify/*/tests/* 62 | */astronify/extern/* 63 | */astronify/version* 64 | 65 | [coverage:report] 66 | exclude_lines = 67 | # Have to re-enable the standard pragma 68 | pragma: no cover 69 | # Don't complain about packages we have installed 70 | except ImportError 71 | # Don't complain if tests don't hit assertions 72 | raise AssertionError 73 | raise NotImplementedError 74 | # Don't complain about script hooks 75 | def main\(.*\): 76 | # Ignore branches that don't pertain to this version of Python 77 | pragma: py{ignore_python_version} 78 | # Don't complain about IPython completion helper 79 | def _ipython_key_completions_ 80 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Licensed under a 3-clause BSD style license - see LICENSE.rst 3 | 4 | # NOTE: The configuration for the package, including the name, version, and 5 | # other information are set in the setup.cfg file. 6 | 7 | import os 8 | import sys 9 | 10 | from setuptools import setup 11 | 12 | 13 | # First provide helpful messages if contributors try and run legacy commands 14 | # for tests or docs. 15 | 16 | TEST_HELP = """ 17 | Note: running tests is no longer done using 'python setup.py test'. Instead 18 | you will need to run: 19 | 20 | tox -e test 21 | 22 | If you don't already have tox installed, you can install it with: 23 | 24 | pip install tox 25 | 26 | If you only want to run part of the test suite, you can also use pytest 27 | directly with:: 28 | 29 | pip install -e .[test] 30 | pytest 31 | 32 | For more information, see: 33 | 34 | http://docs.astropy.org/en/latest/development/testguide.html#running-tests 35 | """ 36 | 37 | if "test" in sys.argv: 38 | print(TEST_HELP) 39 | sys.exit(1) 40 | 41 | DOCS_HELP = """ 42 | Note: building the documentation is no longer done using 43 | 'python setup.py build_docs'. Instead you will need to run: 44 | 45 | tox -e build_docs 46 | 47 | If you don't already have tox installed, you can install it with: 48 | 49 | pip install tox 50 | 51 | You can also build the documentation with Sphinx directly using:: 52 | 53 | pip install -e .[docs] 54 | cd docs 55 | make html 56 | 57 | For more information, see: 58 | 59 | http://docs.astropy.org/en/latest/install.html#builddocs 60 | """ 61 | 62 | if "build_docs" in sys.argv or "build_sphinx" in sys.argv: 63 | print(DOCS_HELP) 64 | sys.exit(1) 65 | 66 | VERSION_TEMPLATE = """ 67 | # Note that we need to fall back to the hard-coded version if either 68 | # setuptools_scm can't be imported or setuptools_scm can't determine the 69 | # version, so we catch the generic 'Exception'. 70 | try: 71 | from setuptools_scm import get_version 72 | version = get_version(root='..', relative_to=__file__) 73 | except Exception: 74 | version = '{version}' 75 | """.lstrip() 76 | 77 | setup( 78 | use_scm_version={ 79 | "write_to": os.path.join("astronify", "version.py"), 80 | "write_to_template": VERSION_TEMPLATE, 81 | } 82 | ) 83 | -------------------------------------------------------------------------------- /astronify/simulator/add_transit_signal.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: add_transit_signal 3 | :synopsis: Adds a periodic transit event to an array of fluxes. 4 | 5 | .. moduleauthor:: Scott W. Fleming 6 | """ 7 | 8 | import numpy as np 9 | 10 | 11 | def add_transit_signal( 12 | fluxes, transit_depth, transit_period, transit_start, transit_width 13 | ): 14 | """ 15 | :param fluxes: Array of fluxes to add the transit signal to. 16 | :type fluxes: numpy.ndarray 17 | 18 | :param transit_depth: Depth of transit, as a percent (e.g., 10.0 = 10%.) 19 | :type transit_depth: float 20 | 21 | :param transit_period: Period of transit (number of fluxes/bins between 22 | the start of each event.) 23 | :type transit_period: int 24 | 25 | :param transit_start: Start index of transit (the index of the flux/bin to 26 | use as the start of the first transit event.) 27 | :type transit_start: int 28 | 29 | :param transit_width: Width of transit (number of fluxes/bins between 30 | the start and end of each event.) 31 | :type transit_width: int 32 | 33 | :returns: numpy.ndarray -- The fluxes with the transit signal added. 34 | """ 35 | 36 | # Array holding the flux fractions to multiply the input flux by due to the transit. 37 | mod_flux_factors = np.asarray([1.0] * fluxes.size) 38 | 39 | # Get length of the flux array. 40 | n_fluxes = fluxes.size 41 | 42 | # Determine the indexes that should be at full transit depth. 43 | transit_indexes = np.zeros(n_fluxes) 44 | 45 | # Get the set of start indexes. 46 | start_indexes = np.arange(transit_start, n_fluxes + 1, transit_period, dtype=int) 47 | # Set transit indexes to 1. 48 | for st_ind in start_indexes: 49 | if st_ind >= 0: 50 | if st_ind + transit_width < fluxes.size: 51 | transit_indexes[st_ind : st_ind + transit_width] = 1 52 | else: 53 | transit_indexes[st_ind:] = 1 54 | elif st_ind > -1 * transit_width: 55 | # This is a negative start index that will result in a partial transit at the start. 56 | transit_indexes[0 : transit_width + st_ind] = 1 57 | 58 | # Calculate the flux fractions to multiply by to modify the fluxes due to the transit. 59 | mod_flux_factors[np.where(transit_indexes == 1)] = 1.0 - (transit_depth / 100.0) 60 | 61 | return fluxes * mod_flux_factors 62 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | py{39,310,311}-test{,-alldeps,-devdeps}{,-cov} 4 | py{39,310,311}-test-numpy{124,125,126,200,210} 5 | py{39,310,311}-test-astropy{50,60,lts} 6 | build_docs 7 | linkcheck 8 | codestyle 9 | requires = 10 | setuptools >= 30.3.0 11 | pip >= 19.3.1 12 | sphinx < 6 13 | sphinx_rtd_theme >= 2.0.0 14 | docutils < 0.19 15 | isolated_build = true 16 | 17 | [testenv] 18 | setenv = 19 | devdeps: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/astropy/simple https://pypi.anaconda.org/scientific-python-nightly-wheels/simple 20 | 21 | # Pass through the following environment variables which may be needed for the CI 22 | passenv = HOME,WINDIR,LC_ALL,LC_CTYPE,CC,CI 23 | 24 | # Run the tests in a temporary directory to make sure that we don't import 25 | # this package from the source tree 26 | changedir = .tmp/{envname} 27 | 28 | # tox environments are constructed with so-called 'factors' (or terms) 29 | # separated by hyphens, e.g. test-devdeps-cov. Lines below starting with factor: 30 | # will only take effect if that factor is included in the environment name. To 31 | # see a list of example environments that can be run, along with a description, 32 | # run: 33 | # 34 | # tox -l -v 35 | # 36 | description = 37 | run tests 38 | alldeps: with all optional dependencies 39 | devdeps: with the latest developer version of key dependencies 40 | oldestdeps: with the oldest supported version of key dependencies 41 | cov: and test coverage 42 | numpy124: with numpy 1.24.* 43 | numpy125: with numpy 1.26.* 44 | numpy126: with numpy 1.26.* 45 | numpy200: with numpy 2.0.* 46 | numpy210: with numpy 2.1.* 47 | astropy50: with astropy 5.0.* 48 | astropy60: with astropy 6.0.* 49 | astropylts: with the latest astropy LTS 50 | 51 | # The following provides some specific pinnings for key packages 52 | deps = 53 | 54 | numpy124: numpy==1.24.* 55 | numpy125: numpy==1.25.* 56 | numpy126: numpy==1.26.* 57 | numpy200: numpy==2.0.* 58 | numpy210: numpy==2.1.* 59 | 60 | astropy50: astropy==5.0.* 61 | astropy60: astropy==6.0.* 62 | astropylts: astropy==6.0.* 63 | 64 | devdeps: numpy>=0.0.dev0 65 | devdeps: astropy>=0.0.dev0 66 | devdeps: git+https://github.com/psf/requests.git 67 | 68 | # The following indicates which extras_require from setup.cfg will be installed 69 | extras = 70 | test 71 | alldeps: all 72 | 73 | commands = 74 | pip freeze 75 | !cov: pytest --pyargs astronify {toxinidir}/docs {posargs} 76 | cov: pytest --pyargs astronify {toxinidir}/docs --cov astronify --cov-config={toxinidir}/setup.cfg {posargs} 77 | cov: coverage xml -o {toxinidir}/coverage.xml 78 | 79 | [testenv:build_docs] 80 | changedir = docs 81 | description = invoke sphinx-build to build the HTML docs 82 | extras = docs 83 | deps= sphinx_rtd_theme 84 | commands = 85 | sphinx-build -b html . _build/html 86 | 87 | [testenv:linkcheck] 88 | changedir = docs 89 | description = check the links in the HTML docs 90 | extras = docs 91 | commands = 92 | sphinx-build -b linkcheck . _build/html 93 | -------------------------------------------------------------------------------- /DEVELOPER_DOC.rst: -------------------------------------------------------------------------------- 1 | Developer Documentation 2 | ----------------------- 3 | 4 | This documentation is intended for code maintainers and developers as a guide, especially when preparing to merge and release a new version of the code. 5 | 6 | Installation 7 | ^^^^^^^^^^^^ 8 | 9 | .. code-block:: bash 10 | 11 | $ git clone https://github.com/spacetelescope/astronify.git 12 | $ cd astronify 13 | $ pip install . 14 | 15 | For active development, install in develop mode 16 | 17 | .. code-block:: bash 18 | 19 | $ pip install -e . 20 | 21 | 22 | Testing 23 | ^^^^^^^ 24 | Testing is run with `tox `_ (``pip install tox``). 25 | Tests can be found in ``tests/`` sub-directories. 26 | 27 | .. code-block:: bash 28 | 29 | $ tox -e test 30 | 31 | Tests can also be run directly with pytest: 32 | 33 | .. code-block:: bash 34 | 35 | $ pip install -e .[test] 36 | $ pytest 37 | 38 | 39 | Documentation 40 | ^^^^^^^^^^^^^ 41 | 42 | Documentation files are found in ``docs/``. 43 | 44 | We build the documentation with `tox `_ (``pip install tox``): 45 | 46 | .. code-block:: bash 47 | 48 | $ tox -e build_docs 49 | 50 | You can also build the documentation with Sphinx directly using: 51 | 52 | .. code-block:: bash 53 | 54 | $ cd docs 55 | $ sphinx-build -M html . _build/ 56 | 57 | The built docs will be in ``docs/_build/html/``, to view them go to ``file://docs/_build/html/index.html`` in the browser of your choice. 58 | 59 | 60 | Release Protocol 61 | ^^^^^^^^^^^^^^^^ 62 | 63 | TO-BE-FINALIZED 64 | 65 | - Update the ``ci_workflows.yml`` under ``.github/workflows/`` to 66 | remove any inactive branches and add your new development branch, 67 | under the ``push`` section towards the top of the file. 68 | 69 | - Update the __init__.py file under the "astronify/" folder to update 70 | the __version__ variable to match the upcoming release version. This 71 | should be specified as a string. 72 | 73 | - Update the version information and release date in the CITATION.cff 74 | file, located in the top-level directory to match the upcoming release version. 75 | 76 | - Update the "CHANGES.rst" file to add the new version, release date, 77 | and summary of what's changing in this version. 78 | 79 | - Make a final commit to the branch, doing things like double checking 80 | Python versions, release dates, spell check documentation files, 81 | etc. Commit the final release with: 82 | 83 | .. code-block:: bash 84 | 85 | $ git commit -m "Preparing release " 86 | 87 | - Tag the commit with the version, using the "v' in front of the tag, 88 | even if the version in the __init__.py file does not. 89 | 90 | .. code-block:: bash 91 | 92 | $ git tag -a v -m "Release version " 93 | 94 | - Make sure the `build` package is up-to-date: 95 | 96 | .. code-block:: bash 97 | 98 | $ python -m build --sdist --outdir dist . 99 | 100 | - Twine upload. 101 | 102 | .. code-block:: bash 103 | 104 | twine upload dist/ 105 | -------------------------------------------------------------------------------- /docs/_static/main.css: -------------------------------------------------------------------------------- 1 | body{ 2 | background: linear-gradient(#0D4960,#00617E); 3 | font-family: 'Overpass', sans-serif; 4 | color: white; 5 | margin-left: auto; 6 | margin-right: auto; 7 | max-width: 800px; 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | 13 | .logo{ 14 | margin-left: auto; 15 | margin-right: auto; 16 | max-width: 700px; 17 | max-width: 700px; 18 | display: block; 19 | } 20 | 21 | h1{ 22 | text-align: center; 23 | max-width: 800px; 24 | margin-top: 0px; 25 | margin-left: auto; 26 | margin-right: auto; 27 | margin-bottom: 50px; 28 | } 29 | 30 | h2{ 31 | text-align: center; 32 | font-size: 55px; 33 | margin-bottom: 25px; 34 | margin-top: 30px; 35 | font-weight: 400; 36 | } 37 | 38 | h3{ 39 | font-size: 40px; 40 | margin-bottom: 15px; 41 | margin-right: 0px; 42 | } 43 | 44 | h5{ 45 | font-size: 95px; 46 | margin-bottom: 15px; 47 | margin-right: 0px; 48 | font-family: 'Oswald'; 49 | } 50 | 51 | a{ 52 | color: white; 53 | text-decoration: none; 54 | cursor: pointer; 55 | } 56 | 57 | 58 | p{ 59 | font-size: 25px; 60 | line-height: 30px; 61 | } 62 | 63 | br{ 64 | height: 10px; 65 | } 66 | 67 | .big-button{ 68 | background: #0D4960; 69 | border: 2px; 70 | border-color: white; 71 | border-style: solid; 72 | padding-left: 25px; 73 | padding-right: 20px; 74 | padding-bottom: 20px; 75 | padding-top: 5px; 76 | margin-bottom: 20px; 77 | display: block; 78 | } 79 | 80 | a:hover .big-button { 81 | background-color: #00617E; 82 | } 83 | 84 | .text-link{ 85 | text-decoration: underline; 86 | 87 | } 88 | 89 | .small-link{ 90 | background: #0D4960; 91 | font-size: 25px; 92 | margin-bottom: 20px; 93 | border: 2px; 94 | border-color: white; 95 | border-style: solid; 96 | padding: 15px; 97 | max-width: 800; 98 | text-align: center; 99 | font-weight: bold; 100 | display: block; 101 | } 102 | 103 | 104 | .small-link:hover{ 105 | background-color: #00617E; 106 | } 107 | 108 | 109 | .artist-link{ 110 | background: #0D4960; 111 | font-size: 20px; 112 | margin-bottom: 20px; 113 | border: 2px; 114 | border-color: white; 115 | border-style: solid; 116 | padding: 15px; 117 | max-width: 400px; 118 | text-align: center; 119 | font-weight: bold; 120 | display: block; 121 | margin-left: auto; 122 | margin-right: auto; 123 | } 124 | 125 | 126 | .artist-link:hover{ 127 | background-color: #00617E; 128 | } 129 | 130 | 131 | 132 | .big-button ORANGE{ 133 | background: #C75109; 134 | cursor: pointer; 135 | padding-left: 20px; 136 | padding-right: 20px; 137 | padding-bottom: 20px; 138 | padding-top: 5px; 139 | margin-bottom: 20px; 140 | } 141 | 142 | .footer{ 143 | margin-top: 100px; 144 | margin-bottom: 30px; 145 | } 146 | 147 | 148 | hr{ 149 | border-color: white; 150 | border-style:dashed; 151 | 152 | } 153 | 154 | .painting{ 155 | width: 800px; 156 | } 157 | 158 | -------------------------------------------------------------------------------- /astronify/simulator/tests/test_add_sine_signal.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: test_add_sine_signal 3 | :synopsis: Regression tests for add_sine_signal 4 | 5 | .. moduleauthor:: Scott W. Fleming 6 | """ 7 | 8 | import pytest 9 | import numpy as np 10 | from astronify.simulator.add_sine_signal import add_sine_signal 11 | 12 | times = np.arange(1, 101) 13 | fluxes = np.asarray([1.0] * len(times)) 14 | all_ones = np.asarray([1.0] * len(fluxes)) 15 | 16 | # params used for below: sine_amp = 1.0, sine_period = 3.0 17 | # fmt: off 18 | sine_signal = np.asarray( 19 | [ 20 | 1.8660254037844388, 0.13397459621556163, 0.9999999999999998, 21 | 1.8660254037844393, 0.13397459621556218, 0.9999999999999996, 22 | 1.8660254037844402, 0.1339745962155623, 0.9999999999999992, 23 | 1.8660254037844402, 0.1339745962155624, 0.999999999999999, 24 | 1.8660254037844404, 0.1339745962155643, 0.9999999999999988, 25 | 1.8660254037844406, 0.1339745962155644, 0.9999999999999986, 26 | 1.8660254037844406, 0.1339745962155645, 0.9999999999999983, 27 | 1.8660254037844406, 0.13397459621556462, 0.999999999999998, 28 | 1.8660254037844444, 0.13397459621556473, 0.9999999999999978, 29 | 1.8660254037844446, 0.13397459621556496, 0.9999999999999976, 30 | 1.866025403784441, 0.13397459621556507, 0.9999999999999902, 31 | 1.8660254037844448, 0.13397459621556163, 0.9999999999999971, 32 | 1.8660254037844415, 0.1339745962155653, 1.000000000000004, 33 | 1.866025403784445, 0.13397459621556185, 0.9999999999999966, 34 | 1.8660254037844415, 0.1339745962155655, 0.9999999999999892, 35 | 1.8660254037844453, 0.13397459621556207, 0.9999999999999961, 36 | 1.866025403784442, 0.13397459621557284, 1.0000000000000029, 37 | 1.8660254037844455, 0.1339745962155694, 0.9999999999999956, 38 | 1.866025403784442, 0.13397459621557317, 0.9999999999999882, 39 | 1.8660254037844457, 0.13397459621556962, 0.9999999999999951, 40 | 1.8660254037844424, 0.1339745962155663, 1.000000000000002, 41 | 1.866025403784446, 0.13397459621557706, 0.9999999999999803, 42 | 1.8660254037844426, 0.13397459621557362, 0.9999999999999872, 43 | 1.8660254037844393, 0.13397459621557017, 0.9999999999999941, 44 | 1.86602540378445, 0.13397459621556673, 1.0000000000000009, 45 | 1.8660254037844464, 0.1339745962155775, 1.0000000000000078, 46 | 1.866025403784443, 0.13397459621557406, 0.9999999999999862, 47 | 1.8660254037844397, 0.13397459621557062, 0.9999999999999931, 48 | 1.8660254037844504, 0.1339745962155673, 1.0, 49 | 1.8660254037844468, 0.13397459621557806, 0.9999999999999785, 50 | 1.8660254037844437, 0.13397459621557462, 0.9999999999999853, 51 | 1.8660254037844402, 0.13397459621557117, 0.9999999999999921, 52 | 1.8660254037844508, 0.13397459621556773, 0.999999999999999, 53 | 1.8660254037844617, 54 | ] 55 | ) 56 | # fmt: om 57 | 58 | """ 59 | Test cases that result in return arrays: 60 | 1. amplitude and period non-zero 61 | 2. amplitude equal to 0.0 62 | 63 | Test cases that result in exceptions being thrown: 64 | 1. [TODO pending Issue #89] a negative value for sine_amp is submitted 65 | 2. [TODO pending Issue #89] a zero or negative value for sine_period is submitted 66 | """ 67 | 68 | def test_add_sine_signal_nonzero(): 69 | assert np.allclose( 70 | add_sine_signal(times, fluxes, 1.0, 3.0), 71 | sine_signal, 72 | atol=0, 73 | rtol=1e-8, 74 | equal_nan=True, 75 | ) 76 | 77 | def test_add_sine_signal_zeroamp(): 78 | assert np.array_equal( 79 | add_sine_signal(times, fluxes, 0.0, 1.0), 80 | all_ones, 81 | ) 82 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: docs/_static/astronify-TEXT.png 2 | :width: 800 3 | :alt: Astronify logo 4 | 5 | Sonification of astronomical data. 6 | ---------------------------------- 7 | 8 | .. image:: http://img.shields.io/badge/powered%20by-AstroPy-orange.svg?style=flat 9 | :target: http://www.astropy.org 10 | :alt: Powered by Astropy Badge 11 | 12 | .. image:: https://badge.fury.io/py/astronify.svg 13 | :target: https://badge.fury.io/py/astronify 14 | :alt: PyPi Status 15 | 16 | .. image:: https://readthedocs.org/projects/astronify/badge/?version=latest 17 | :target: https://astronify.readthedocs.io/en/latest/?badge=latest 18 | :alt: Documentation Status 19 | 20 | .. image:: https://img.shields.io/badge/ascl-2408.005-blue.svg?colorB=262255 21 | :target: https://ascl.net/2408.005 22 | :alt: ascl:2408.005 23 | 24 | Tools for sonifying astronomical data. 25 | 26 | This package is currently in the very beginning stages and is under active development to 27 | include sonification of time series data, specifically light curves. 28 | 29 | Project Status 30 | -------------- 31 | 32 | .. image:: https://github.com/spacetelescope/astronify/workflows/CI/badge.svg 33 | :target: https://github.com/spacetelescope/astronify/actions 34 | :alt: Astronify's GitHub Actions CI Status 35 | 36 | .. image:: https://codecov.io/gh/spacetelescope/astronify/branch/main/graph/badge.svg 37 | :target: https://codecov.io/gh/spacetelescope/astronify 38 | :alt: Astronify's Codecov coverage status 39 | 40 | Getting Started 41 | --------------- 42 | Install instructions: https://astronify.readthedocs.io/en/latest/astronify/install.html 43 | 44 | Tutorials: https://astronify.readthedocs.io/en/latest/astronify/tutorials.html 45 | 46 | Contributing 47 | ------------ 48 | 49 | If you are a maintainer of the code, refer to the developer 50 | documentation (DEVELOPER_DOC.rst file) for guidelines on how to release a 51 | new version. 52 | 53 | We love contributions! Astronify is open source, 54 | built on open source, and we'd love to have you hang out in our community. 55 | 56 | **Imposter syndrome disclaimer**: We want your help. No, really. 57 | 58 | There may be a little voice inside your head that is telling you that you're not 59 | ready to be an open source contributor; that your skills aren't nearly good 60 | enough to contribute. What could you possibly offer a project like this one? 61 | 62 | We assure you - the little voice in your head is wrong. If you can write code at 63 | all, you can contribute code to open source. Contributing to open source 64 | projects is a fantastic way to advance one's coding skills. Writing perfect code 65 | isn't the measure of a good developer (that would disqualify all of us!); it's 66 | trying to create something, making mistakes, and learning from those 67 | mistakes. That's how we all improve, and we are happy to help others learn. 68 | 69 | Being an open source contributor doesn't just mean writing code either. You can 70 | help out by writing documentation, tests, or even giving feedback about the 71 | project (and yes - that includes giving feedback about the contribution 72 | process). Some of these contributions may be the most valuable to the project as 73 | a whole, because you're coming to the project with fresh eyes, so you can see 74 | the errors and assumptions that seasoned contributors have glossed over. 75 | 76 | Note: This disclaimer was originally written by 77 | `Adrienne Lowe `_ for a 78 | `PyCon talk `_, and was adapted by 79 | Astronify based on its use in the README file for the 80 | `MetPy project `_. 81 | 82 | 83 | License 84 | ------- 85 | 86 | This project is Copyright (c) Clara Brasseur, Scott Fleming, Jennifer Kotler, Kate Meredith and licensed under 87 | the terms of the BSD 3-Clause license. This package is based upon 88 | the `Astropy package template `_ 89 | which is licensed under the BSD 3-clause licence. See the licenses folder for 90 | more information. 91 | 92 | -------------------------------------------------------------------------------- /astronify/simulator/tests/test_add_transit_signal.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: test_add_transit_signal 3 | :synopsis: Regression tests for add_transit_signal 4 | 5 | .. moduleauthor:: Scott W. Fleming 6 | """ 7 | 8 | import pytest 9 | import numpy as np 10 | from astronify.simulator.add_transit_signal import add_transit_signal 11 | 12 | times = np.arange(1, 101) 13 | fluxes = np.asarray([1.0] * len(times)) 14 | all_ones = np.asarray([1.0] * len(fluxes)) 15 | 16 | # params used for below: transit_depth = 10.0, transit_period = 20, 17 | # transit_start = 3, transit_width = 3 18 | # fmt: off 19 | transit_signal = np.asarray( 20 | [ 21 | 1.0, 1.0, 1.0, 0.9, 0.9, 0.9, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 22 | 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.9, 0.9, 0.9, 1.0, 1.0, 23 | 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 24 | 1.0, 0.9, 0.9, 0.9, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 25 | 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.9, 0.9, 0.9, 1.0, 1.0, 1.0, 1.0, 26 | 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.9, 27 | 0.9, 0.9, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 28 | 1.0, 1.0, 29 | ] 30 | ) 31 | # fmt: om 32 | 33 | # params used for below: transit_depth = 10.0, transit_period = 20, 34 | # transit_start = -1, transit_width = 3 35 | # fmt: off 36 | transit_signal_negstart = np.asarray( 37 | [ 38 | 0.9, 0.9, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 39 | 1.0, 1.0, 1.0, 1.0, 1.0, 0.9, 0.9, 0.9, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 40 | 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.9, 0.9, 0.9, 41 | 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 42 | 1.0, 1.0, 1.0, 0.9, 0.9, 0.9, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 43 | 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.9, 0.9, 0.9, 1.0, 1.0, 44 | 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 45 | 1.0, 0.9, 46 | ] 47 | ) 48 | # fmt: om 49 | 50 | # params used for below: transit_depth = -10.0, transit_period = 20, 51 | # transit_start = 3, transit_width = 3 52 | # fmt: off 53 | transit_signal_antitransit = np.asarray( 54 | [ 55 | 1.0, 1.0, 1.0, 1.1, 1.1, 1.1, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 56 | 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.1, 1.1, 1.1, 1.0, 1.0, 57 | 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 58 | 1.0, 1.1, 1.1, 1.1, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 59 | 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.1, 1.1, 1.1, 1.0, 1.0, 1.0, 1.0, 60 | 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.1, 61 | 1.1, 1.1, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 62 | 1.0, 1.0, 63 | ] 64 | ) 65 | # fmt: om 66 | 67 | """ 68 | Test cases that result in return arrays: 69 | 1. depth, period, start, width all non-zero 70 | 2. start index is provided as a negative int 71 | 3. a negative transit depth is provided (simulate an "anti-transit") 72 | 4. transit depth equal to 0.0 73 | 74 | Test cases that result in exceptions being thrown: 75 | 1. [TODO pending Issue #90] a zero or negative value for transit_period is submitted 76 | 2. [TODO pending Issue #90] a zero or negative value for transit_width is submitted 77 | 3. [TODO pending Issue #90] a transit width is wide enough so that one transit doesn't end before the next one should start 78 | """ 79 | 80 | @pytest.mark.parametrize( 81 | "transit_depth, transit_period, transit_start, transit_width, expected_result", 82 | [ 83 | (10.0, 20, 3, 3, transit_signal), 84 | (10.0, 20, -1, 3, transit_signal_negstart), 85 | (-10.0, 20, 3, 3, transit_signal_antitransit), 86 | (0.0, 20, 3, 3, all_ones), 87 | ], 88 | ) 89 | def test_add_transit_signal(transit_depth, transit_period, transit_start, transit_width, expected_result): 90 | assert np.allclose( 91 | add_transit_signal(fluxes, transit_depth, transit_period , transit_start, transit_width), 92 | expected_result, 93 | atol=0, 94 | rtol=1e-8, 95 | equal_nan=True, 96 | ) 97 | -------------------------------------------------------------------------------- /astronify/series/tests/test_series.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import numpy as np 4 | 5 | from astropy.table import Table 6 | from pyo import Server 7 | 8 | from .. import SoniSeries, PitchMap 9 | from ...utils.exceptions import InputWarning 10 | 11 | 12 | def test_pitchmap(): 13 | """ 14 | Testing the PitchMap class. 15 | """ 16 | 17 | # Defaults 18 | my_pitchmapper = PitchMap() 19 | assert isinstance(my_pitchmapper.pitch_map_args, dict) 20 | assert "center_pitch" in my_pitchmapper.pitch_map_args.keys() 21 | assert my_pitchmapper.pitch_map_args["zero_point"] == "median" 22 | 23 | # Change args 24 | my_pitchmapper.pitch_map_args = { 25 | "pitch_range": [100, 10000], 26 | "center_pitch": 440, 27 | "zero_point": "mean", 28 | "stretch": "linear", 29 | "invert": True, 30 | } 31 | assert "center_pitch" in my_pitchmapper.pitch_map_args.keys() 32 | assert my_pitchmapper.pitch_map_args["zero_point"] == "mean" 33 | 34 | with pytest.warns(InputWarning): # setting with bad arg 35 | my_pitchmapper.pitch_map_args = { 36 | "pitch_range": [100, 10000], 37 | "center_pitch": 440, 38 | "zero_point": "mean", 39 | "stretch": "linear", 40 | "penguin": True, 41 | } 42 | 43 | assert "penguin" not in my_pitchmapper.pitch_map_args.keys() 44 | 45 | # Running function 46 | assert isinstance(my_pitchmapper([1, 1, 1, 1]), np.ndarray) 47 | 48 | # Changing function 49 | def my_map_func(data): # dummy function 50 | data = np.array(data) 51 | return data / 2 52 | 53 | with pytest.warns(InputWarning): # because of different args 54 | my_pitchmapper.pitch_map_func = my_map_func 55 | 56 | assert (my_pitchmapper([1, 1]) == [0.5, 0.5]).all() 57 | 58 | 59 | class TestSoniSeries(object): 60 | data = Table({"time": [0, 1, 2, 3, 4, 5, 6], "flux": [1, 2, 1, 2, 5, 3, np.nan]}) 61 | 62 | # defaults 63 | soni_obj = SoniSeries(data) 64 | assert soni_obj.note_duration == 0.5 65 | assert soni_obj.note_spacing == 0.01 66 | assert soni_obj.gain == 0.05 67 | assert isinstance(soni_obj.server, Server) 68 | assert len(soni_obj.data) == len(data) - 1 # nan row should be removed 69 | assert ~np.isnan(soni_obj.data["flux"]).any() 70 | assert soni_obj.data["flux"].dtype == np.float64 71 | 72 | soni_obj.sonify() 73 | assert "asf_pitch" in soni_obj.data.colnames 74 | assert "asf_onsets" in soni_obj.data.colnames 75 | assert soni_obj.data.meta["asf_exposure_time"] == 1 76 | assert soni_obj.data.meta["asf_note_duration"] == soni_obj.note_duration 77 | assert soni_obj.data.meta["asf_spacing"] == soni_obj.note_spacing 78 | 79 | onset_spacing = soni_obj.data["asf_onsets"][1:] - soni_obj.data["asf_onsets"][:-1] 80 | assert (np.isclose(onset_spacing, soni_obj.note_spacing)).all() 81 | 82 | def test_server_class(self): 83 | assert isinstance(self.soni_obj.server, Server) 84 | 85 | def test_nans_removed(self): 86 | assert ( 87 | len(self.soni_obj.data) == len(self.data) - 1 88 | ) # nan row should be removed 89 | assert ~np.isnan(self.soni_obj.data["flux"]).any() 90 | 91 | def test_flux_type_correct(self): 92 | assert self.soni_obj.data["flux"].dtype == np.float64 93 | 94 | def test_sonify_works(self): 95 | self.soni_obj.sonify() 96 | 97 | def test_sonify_new_columns_exist(self): 98 | assert "asf_pitch" in self.soni_obj.data.colnames 99 | assert "asf_onsets" in self.soni_obj.data.colnames 100 | 101 | def test_sonify_metadata(self): 102 | assert self.soni_obj.data.meta["asf_exposure_time"] == 1 103 | assert ( 104 | self.soni_obj.data.meta["asf_note_duration"] == self.soni_obj.note_duration 105 | ) 106 | assert self.soni_obj.data.meta["asf_spacing"] == self.soni_obj.note_spacing 107 | 108 | def test_onset_spacing(self): 109 | onset_spacing = ( 110 | self.soni_obj.data["asf_onsets"][1:] - self.soni_obj.data["asf_onsets"][:-1] 111 | ) 112 | assert (np.isclose(onset_spacing, self.soni_obj.note_spacing)).all() 113 | 114 | def test_pitch_min_max(self): 115 | pitch_min, pitch_max = self.soni_obj.pitch_mapper.pitch_map_args["pitch_range"] 116 | assert self.soni_obj.data["asf_pitch"].min() >= pitch_min 117 | assert self.soni_obj.data["asf_pitch"].max() <= pitch_max 118 | 119 | # TODO: change args and test 120 | 121 | # TODO: test write 122 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest 16 | 17 | #This is needed with git because git doesn't create a dir if it's empty 18 | $(shell [ -d "_static" ] || mkdir -p _static) 19 | 20 | help: 21 | @echo "Please use \`make ' where is one of" 22 | @echo " html to make standalone HTML files" 23 | @echo " dirhtml to make HTML files named index.html in directories" 24 | @echo " singlehtml to make a single large HTML file" 25 | @echo " pickle to make pickle files" 26 | @echo " json to make JSON files" 27 | @echo " htmlhelp to make HTML files and a HTML help project" 28 | @echo " qthelp to make HTML files and a qthelp project" 29 | @echo " devhelp to make HTML files and a Devhelp project" 30 | @echo " epub to make an epub" 31 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 32 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 33 | @echo " text to make text files" 34 | @echo " man to make manual pages" 35 | @echo " changes to make an overview of all changed/added/deprecated items" 36 | @echo " linkcheck to check all external links for integrity" 37 | 38 | clean: 39 | -rm -rf $(BUILDDIR) 40 | -rm -rf api 41 | -rm -rf generated 42 | 43 | html: 44 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 45 | @echo 46 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 47 | 48 | dirhtml: 49 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 50 | @echo 51 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 52 | 53 | singlehtml: 54 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 55 | @echo 56 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 57 | 58 | pickle: 59 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 60 | @echo 61 | @echo "Build finished; now you can process the pickle files." 62 | 63 | json: 64 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 65 | @echo 66 | @echo "Build finished; now you can process the JSON files." 67 | 68 | htmlhelp: 69 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 70 | @echo 71 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 72 | ".hhp project file in $(BUILDDIR)/htmlhelp." 73 | 74 | qthelp: 75 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 76 | @echo 77 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 78 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 79 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Astropy.qhcp" 80 | @echo "To view the help file:" 81 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Astropy.qhc" 82 | 83 | devhelp: 84 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 85 | @echo 86 | @echo "Build finished." 87 | @echo "To view the help file:" 88 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Astropy" 89 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Astropy" 90 | @echo "# devhelp" 91 | 92 | epub: 93 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 94 | @echo 95 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 96 | 97 | latex: 98 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 99 | @echo 100 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 101 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 102 | "(use \`make latexpdf' here to do that automatically)." 103 | 104 | latexpdf: 105 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 106 | @echo "Running LaTeX files through pdflatex..." 107 | make -C $(BUILDDIR)/latex all-pdf 108 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 109 | 110 | text: 111 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 112 | @echo 113 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 114 | 115 | man: 116 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 117 | @echo 118 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 119 | 120 | changes: 121 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 122 | @echo 123 | @echo "The overview file is in $(BUILDDIR)/changes." 124 | 125 | linkcheck: 126 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 127 | @echo 128 | @echo "Link check complete; look for any errors in the above output " \ 129 | "or in $(BUILDDIR)/linkcheck/output.txt." 130 | 131 | doctest: 132 | @echo "Run 'python setup.py test' in the root directory to run doctests " \ 133 | @echo "in the documentation." 134 | -------------------------------------------------------------------------------- /docs/_static/ASTRONIFY_white.svg: -------------------------------------------------------------------------------- 1 | ASTRONIFY_white -------------------------------------------------------------------------------- /astronify/simulator/tests/test_add_flare_signal.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: test_add_flare_signal 3 | :synopsis: Regression tests for add_flare_signal 4 | 5 | .. moduleauthor:: Scott W. Fleming 6 | """ 7 | 8 | import pytest 9 | import numpy as np 10 | from astronify.simulator.add_flare_signal import add_flare_signal 11 | 12 | fluxes = np.asarray([1.0] * 100) 13 | all_ones = np.asarray([1.0] * len(fluxes)) 14 | # params used for below: flare_time = 5, flare_amp = 100., flare_halfwidth = 5 15 | # fmt: off 16 | flare_within_window = np.asarray( 17 | [ 18 | 1., 1.5, 4.73867187, 20.61875, 19 | 54.45117188, 100.2, 79.50425097, 64.15981633, 20 | 52.70811908, 44.09251539, 37.54729309, 32.51733859, 21 | 28.59999823, 25.5030016, 23.0140085, 20.97856815, 22 | 19.28416612, 17.8486771, 16.61200614, 15.53003747, 23 | 14.57025342, 13.70856202, 12.92699942, 12.2120655, 24 | 11.55351786, 10.94349742, 10.37589439, 9.84588794, 25 | 9.34961192, 8.88391169, 8.44616705, 8.03416297, 26 | 7.64599514, 7.28000052, 6.93470632, 6.60879213, 27 | 6.30106173, 6.01042197, 5.7358667, 5.47646453, 28 | 5.23134925, 4.99971236, 4.78079704, 4.57389328, 29 | 4.37833382, 4.19349072, 4.01877245, 3.85362129, 30 | 3.69751114, 3.54994541, 3.41045527, 3.27859792, 31 | 3.15395508, 3.03613157, 2.92475397, 2.81946941, 32 | 2.71994438, 2.6258637, 2.53692945, 2.45286004, 33 | 2.37338931, 2.29826569, 2.22725134, 2.16012149, 34 | 2.09666363, 2.03667689, 1.97997141, 1.92636768, 35 | 1.87569605, 1.82779613, 1.7825163, 1.73971325, 36 | 1.6992515, 1.66100297, 1.62484662, 1.59066798, 37 | 1.5583589, 1.52781709, 1.4989459, 1.47165395, 38 | 1.44585484, 1.42146692, 1.398413, 1.37662012, 39 | 1.35601929, 1.33654531, 1.31813654, 1.30073471, 40 | 1.28428476, 1.2687346, 1.25403502, 1.2401395, 41 | 1.22700406, 1.21458711, 1.20284936, 1.19175366, 42 | 1.18126488, 1.17134983, 1.16197712, 1.1531171 43 | ] 44 | ) 45 | # fmt: on 46 | 47 | # params used for below: flare_time = -10, flare_amp = 100., flare_halfwidth = 5 48 | # fmt: off 49 | flare_partialwithin_window = np.asarray( 50 | [ 51 | 21.00538825, 19.30895006, 17.87178393, 16.63374721, 15.55067206, 52 | 14.58999083, 13.72756681, 12.94539788, 12.22995227, 11.57096202, 53 | 10.96054795, 10.39258472, 9.86223975, 9.36563821, 8.8996192 , 54 | 8.46155815, 8.04923718, 7.66075016, 7.29443313, 6.94881294, 55 | 6.62256927, 6.31450629, 6.02353143, 5.74863926, 5.48889915, 56 | 5.24344568, 5.01147116, 4.79221953, 4.58498151, 4.38909051, 57 | 4.20391922, 4.02887667, 3.86340566, 3.70698052, 3.55910508, 58 | 3.41931084, 3.28715531, 3.16222047, 3.04411132, 2.93245464, 59 | 2.82689767, 2.72710702, 2.63276756, 2.54358143, 2.45926706, 60 | 2.37955827, 2.30420347, 2.2329648 , 2.16561739, 2.1019487 , 61 | 2.04175775, 1.9848546 , 1.93105963, 1.88020307, 1.83212442, 62 | 1.78667194, 1.74370218, 1.70307952, 1.66467577, 1.62836971, 63 | 1.59404677, 1.56159863, 1.53092287, 1.5019227 , 1.47450657, 64 | 1.44858798, 1.42408512, 1.40092066, 1.37902149, 1.3583185 , 65 | 1.33874636, 1.32024329, 1.30275089, 1.28621397, 1.27058034, 66 | 1.25580064, 1.24182825, 1.22861906, 1.21613138, 1.20432581, 67 | 1.19316509, 1.18261398, 1.17263921, 1.16320927, 1.15429442, 68 | 1.14586652, 1.13789897, 1.13036663, 1.12324572, 1.11651376, 69 | 1.11014953, 1.10413292, 1.09844495, 1.09306767, 1.08798411, 70 | 1.08317822, 1.07863484, 1.07433964, 1.07027904, 1.06644025, 71 | ] 72 | ) 73 | # fmt: on 74 | 75 | """ 76 | Test cases that result in return arrays: 77 | 1. flare_time pushes flare entirely outside the flux array 78 | 2. flare_time pushes the flare part-way outside the flux array 79 | 3. flare_time sets the flare to entirely consist within the flux array 80 | 4. a flare_amp of 0.0 is submitted 81 | 5. [TODO pending Issue #83] a float for flare_halfwidth that can be converted to an int is handled 82 | 83 | Test cases that result in exceptions being thrown: 84 | 1. [TODO pending Issue #85] a negative value for flare_amp is submitted 85 | 2. [TODO pending Issue #85] a negative value for flare_halfwidth is submitted 86 | 3. [TODO pending Issue #83] a float for flare_halfwidth that can not be converted throws an exception 87 | 4. [TODO pending Issue #85] a flare_halfwidth of 0 is submitted 88 | """ 89 | 90 | 91 | @pytest.mark.parametrize( 92 | "flare_time, flare_amp, flare_halfwidth, expected_result", 93 | [ 94 | (-10000, 100.0, 5, all_ones), 95 | (-10, 100.0, 5, flare_partialwithin_window), 96 | (5, 100.0, 5, flare_within_window), 97 | (5, 0.0, 5, all_ones), 98 | ], 99 | ) 100 | def test_add_flare_signal(flare_time, flare_amp, flare_halfwidth, expected_result): 101 | assert np.allclose( 102 | add_flare_signal(fluxes, flare_time, flare_amp, flare_halfwidth), 103 | expected_result, 104 | atol=0, 105 | rtol=1e-8, 106 | equal_nan=True, 107 | ) 108 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | if NOT "%PAPER%" == "" ( 11 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 12 | ) 13 | 14 | if "%1" == "" goto help 15 | 16 | if "%1" == "help" ( 17 | :help 18 | echo.Please use `make ^` where ^ is one of 19 | echo. html to make standalone HTML files 20 | echo. dirhtml to make HTML files named index.html in directories 21 | echo. singlehtml to make a single large HTML file 22 | echo. pickle to make pickle files 23 | echo. json to make JSON files 24 | echo. htmlhelp to make HTML files and a HTML help project 25 | echo. qthelp to make HTML files and a qthelp project 26 | echo. devhelp to make HTML files and a Devhelp project 27 | echo. epub to make an epub 28 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 29 | echo. text to make text files 30 | echo. man to make manual pages 31 | echo. changes to make an overview over all changed/added/deprecated items 32 | echo. linkcheck to check all external links for integrity 33 | echo. doctest to run all doctests embedded in the documentation if enabled 34 | goto end 35 | ) 36 | 37 | if "%1" == "clean" ( 38 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 39 | del /q /s %BUILDDIR%\* 40 | del /q /s api 41 | del /q /s generated 42 | goto end 43 | ) 44 | 45 | if "%1" == "html" ( 46 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 47 | if errorlevel 1 exit /b 1 48 | echo. 49 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 50 | goto end 51 | ) 52 | 53 | if "%1" == "dirhtml" ( 54 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 55 | if errorlevel 1 exit /b 1 56 | echo. 57 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 58 | goto end 59 | ) 60 | 61 | if "%1" == "singlehtml" ( 62 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 63 | if errorlevel 1 exit /b 1 64 | echo. 65 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 66 | goto end 67 | ) 68 | 69 | if "%1" == "pickle" ( 70 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 71 | if errorlevel 1 exit /b 1 72 | echo. 73 | echo.Build finished; now you can process the pickle files. 74 | goto end 75 | ) 76 | 77 | if "%1" == "json" ( 78 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 79 | if errorlevel 1 exit /b 1 80 | echo. 81 | echo.Build finished; now you can process the JSON files. 82 | goto end 83 | ) 84 | 85 | if "%1" == "htmlhelp" ( 86 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 87 | if errorlevel 1 exit /b 1 88 | echo. 89 | echo.Build finished; now you can run HTML Help Workshop with the ^ 90 | .hhp project file in %BUILDDIR%/htmlhelp. 91 | goto end 92 | ) 93 | 94 | if "%1" == "qthelp" ( 95 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 96 | if errorlevel 1 exit /b 1 97 | echo. 98 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 99 | .qhcp project file in %BUILDDIR%/qthelp, like this: 100 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Astropy.qhcp 101 | echo.To view the help file: 102 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Astropy.ghc 103 | goto end 104 | ) 105 | 106 | if "%1" == "devhelp" ( 107 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 108 | if errorlevel 1 exit /b 1 109 | echo. 110 | echo.Build finished. 111 | goto end 112 | ) 113 | 114 | if "%1" == "epub" ( 115 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 116 | if errorlevel 1 exit /b 1 117 | echo. 118 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 119 | goto end 120 | ) 121 | 122 | if "%1" == "latex" ( 123 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 124 | if errorlevel 1 exit /b 1 125 | echo. 126 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 127 | goto end 128 | ) 129 | 130 | if "%1" == "text" ( 131 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 132 | if errorlevel 1 exit /b 1 133 | echo. 134 | echo.Build finished. The text files are in %BUILDDIR%/text. 135 | goto end 136 | ) 137 | 138 | if "%1" == "man" ( 139 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 140 | if errorlevel 1 exit /b 1 141 | echo. 142 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 143 | goto end 144 | ) 145 | 146 | if "%1" == "changes" ( 147 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 148 | if errorlevel 1 exit /b 1 149 | echo. 150 | echo.The overview file is in %BUILDDIR%/changes. 151 | goto end 152 | ) 153 | 154 | if "%1" == "linkcheck" ( 155 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 156 | if errorlevel 1 exit /b 1 157 | echo. 158 | echo.Link check complete; look for any errors in the above output ^ 159 | or in %BUILDDIR%/linkcheck/output.txt. 160 | goto end 161 | ) 162 | 163 | if "%1" == "doctest" ( 164 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 165 | if errorlevel 1 exit /b 1 166 | echo. 167 | echo.Testing of doctests in the sources finished, look at the ^ 168 | results in %BUILDDIR%/doctest/output.txt. 169 | goto end 170 | ) 171 | 172 | :end 173 | -------------------------------------------------------------------------------- /docs/astronify/Intro_Astronify_Series.rst: -------------------------------------------------------------------------------- 1 | .. doctest-skip-all 2 | 3 | Astronify: Sonifying Time Series 4 | ================================ 5 | 6 | This tutorial will demonstrate the sonification of time series data, 7 | specifically light curves using Astronify. It will cover basic usage, 8 | adjusting sonification parameters, playing the sonification within the 9 | browser, and saving the sonification as a wav file. 10 | 11 | 1. `Imports`_ 12 | 2. `Getting data`_ 13 | 3. `Basic sonification`_ 14 | 4. `Changing sonification parameters`_ 15 | 5. `Outputting an audio file`_ 16 | 17 | Imports 18 | ------- 19 | 20 | In addition to Astronify, we will use the 21 | `Lightkurve `__ package to query and 22 | download Kepler light curves, `matplotlib `__ 23 | to plot the light curves (these cells will be markes and can be skipped 24 | by those for whom plots are not useful), and 25 | `Astropy `__ for various data manipulation 26 | tasks. 27 | 28 | \* Don't worry if you get a warning about WxPython not being found, 29 | it's used for the GUI functionality audio library, which Astronify does 30 | not use. 31 | 32 | .. code:: python 33 | 34 | >>> from astronify.series import SoniSeries 35 | >>> import lightkurve 36 | 37 | >>> import matplotlib 38 | >>> import matplotlib.pyplot as plt 39 | 40 | 41 | 42 | Getting data 43 | ------------ 44 | 45 | Fundamentally all that Astronify requires is an Astropy Table object 46 | where one colum will be translated into time (default is “time”) and one 47 | column will be translated into pitch (default is “flux”). 48 | 49 | I will download a Kepler light curve using Lightkurve (Lightkurve is a user friendly way 50 | to get Kepler and TESS data). This is a light curve that shows Kepler 12b, a transiting 51 | exoplanet. 52 | 53 | .. code:: python 54 | 55 | >>> kep12b_lc = lightkurve.search_lightcurvefile("KIC 11804465", cadence="long", quarter=1).download_all()[0].SAP_FLUX.to_table() 56 | 57 | Basic sonification 58 | ------------------ 59 | 60 | Here we will use all of the sonification defaults and see what we get. 61 | 62 | First a small peak at the data visually for those of us who find that 63 | useful. 64 | 65 | .. code:: python 66 | 67 | >>> f, ax = plt.subplots(figsize=(12, 6)) 68 | >>> ax.plot(kep12b_lc['time'].jd, kep12b_lc['flux']) 69 | >>> ax.set_xlabel("Time (JD)") 70 | >>> ax.set_ylabel("Flux") 71 | 72 | >>> plt.show() 73 | 74 | .. image:: ../_static/Intro_Astronify_Series_6_0.png 75 | 76 | 77 | Now to sonify the same data. 78 | 79 | .. code:: python 80 | 81 | >>> kep12b_obj = SoniSeries(kep12b_lc) 82 | >>> kep12b_obj.sonify() 83 | 84 | 85 | And play the sonification. 86 | 87 | .. code:: python 88 | 89 | >>> kep12b_obj.play() 90 | 91 | 92 | The playback will stop at the end, but we can also stop it early. 93 | 94 | .. code:: python 95 | 96 | >>> kep12b_obj.stop() 97 | 98 | Changing sonification parameters 99 | -------------------------------- 100 | 101 | Let’s look at the current sonification parameters. 102 | 103 | .. code:: python 104 | 105 | >>> kep12b_obj.pitch_mapper.pitch_map_args 106 | {'pitch_range': [100, 10000], 107 | 'center_pitch': 440, 108 | 'zero_point': 'median', 109 | 'stretch': 'linear'} 110 | 111 | 112 | 113 | We can change all of these default arguments as well as adding any 114 | additional arguments allowed by the pitch mapping function. 115 | 116 | Changing the center pitch 117 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 118 | 119 | .. code:: python 120 | 121 | >>> kep12b_obj.pitch_mapper.pitch_map_args["center_pitch"] = 880 122 | 123 | >>> kep12b_obj.sonify() 124 | >>> kep12b_obj.play() 125 | 126 | 127 | 128 | .. code:: python 129 | 130 | >>> kep12b_obj.stop() 131 | 132 | >>> kep12b_obj.pitch_mapper.pitch_map_args["center_pitch"] = 440 133 | 134 | Changing the stretch to logarithmic 135 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 136 | 137 | .. code:: python 138 | 139 | >>> kep12b_obj.pitch_mapper.pitch_map_args["stretch"] = "log" 140 | 141 | >>> kep12b_obj.sonify() 142 | >>> kep12b_obj.play() 143 | 144 | 145 | .. code:: python 146 | 147 | >>> kep12b_obj.stop() 148 | 149 | >>> kep12b_obj.pitch_mapper.pitch_map_args["stretch"] = "linear" 150 | 151 | Removing the outer 1% of data points 152 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 153 | 154 | .. code:: python 155 | 156 | >>> kep12b_obj.pitch_mapper.pitch_map_args["minmax_percent"] = [0.5, 99.5] 157 | 158 | >>> kep12b_obj.sonify() 159 | >>> kep12b_obj.play() 160 | 161 | 162 | .. code:: python 163 | 164 | >>> kep12b_obj.stop() 165 | 166 | >>> del kep12b_obj.pitch_mapper.pitch_map_args["minmax_percent"] 167 | 168 | Outputting an audio file 169 | ------------------------ 170 | 171 | Once the sonification sounds the way we like we can output the result to 172 | a wav file. 173 | 174 | .. code:: python 175 | 176 | >>> kep12b_obj.write("kepler_12b.wav") 177 | Pyo message: Offline Server rendering file kepler_12b.wav dur=16.879996 178 | Pyo message: Offline Server rendering finished. 179 | 180 | 181 | `Kepler 12b audio file <../_static/kepler_12b.wav>`_ 182 | 183 | .. raw:: html 184 | 185 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /astronify/simulator/add_flare_signal.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: add_flare_signal 3 | :synopsis: Adds a flare event to an array of fluxes. 4 | 5 | .. moduleauthor:: Scott W. Fleming 6 | """ 7 | 8 | import numpy as np 9 | 10 | 11 | def add_flare_signal(fluxes, flare_time, flare_amp, flare_halfwidth): 12 | """ 13 | Model is based on Davenport et al. 14 | (https://ui.adsabs.harvard.edu/abs/2014ApJ...797..122D/abstract) 15 | 16 | F_rise = 1 + 1.941(t) - 0.175(t^2) - 2.246(t^3) - 1.125(t^4) 17 | where t = time at half the maximum flux, after normalizing such that 18 | the flux at t=0 is the peak of the flare. 19 | 20 | F_decay = 0.6890*e^(-1.600*t) + 0.3030*e^(-0.2783*t) 21 | where t = time at half the maximum flux, after normalizing such that 22 | the flux at t=0 is the peak of the flare. 23 | 24 | :param fluxes: Array of fluxes to add the flare signal to. 25 | :type fluxes: numpy.ndarray 26 | 27 | :param flare_time: Time corresponding to the maximum flare flux. 28 | :type flare_time: int 29 | 30 | :param flare_amp: The peak (maximum flux) of the flare. 31 | :type flare_amp: float 32 | 33 | :param flare_halfwidth: The flare half-width (measured in indices) that 34 | corresponds to "t_1/2" in the Davenport et al. flare template. 35 | :type flare_halfwidth: int 36 | 37 | :returns: numpy.ndarray -- The fluxes with the flare signal added. 38 | """ 39 | 40 | # This array will contain the fluxes of the flare to add to the input. 41 | fluxes_to_add = np.zeros(fluxes.shape[0]) 42 | 43 | # We must be careful if the flare_halfwidth pushes us past the zero'th 44 | # index though. If it does, we temporarily prepend some extra indices. 45 | truncated_start = flare_time - flare_halfwidth < 0 46 | if truncated_start: 47 | n_to_add_rise = abs(flare_time - flare_halfwidth + 1) 48 | fluxes_to_add = np.concatenate([np.zeros(n_to_add_rise), fluxes_to_add]) 49 | # Need to move the 'flare_time' index over now... 50 | flare_time += n_to_add_rise 51 | 52 | # Where "n" = 6 in Davenport et al., where the decay phase is defined from 53 | # t_1/2 = [0,6], but we will choose to extend to the end of the light curve 54 | # so that the decay gets as close to zero as possible. 55 | n_t12 = int((fluxes_to_add.shape[0] + 1 - flare_time) / flare_halfwidth) 56 | 57 | # Create the normalized part of the rise time. 58 | # In the Davenport et al. flare template, the rise part of the flare 59 | # is defined from a region extending from -1 * "t_1/2" (defined here as 60 | # 'flare_halfwidth') out to 't_1/2' = 0, which is the peak of the flare 61 | # (defined here as 'flare_time'. 62 | # Therefore, we use Davenport et al. Eqn. 1 to set the RISE fluxes 63 | # between 'flare_time-flare_halfwidth' and 'flare_time' indices. 64 | # 65 | # If we want a flare halfwidth of 4 indices, we need to make a linear step 66 | # array from -1. to 0. that is 'halfwidth' in length, e.g.: 67 | # [-1, -0.6667, -0.3333, 0.] 68 | # Then apply Davenport et al. Eqn. 1 to this array (now expressed in "t_1/2") 69 | # Eqn. 1 * [-1, -0.6667, -0.3333, 0.] 70 | # Then we insert these fluxes into the array at indices: 71 | # [flare_time-flare_halfwidth+1 : flare_time] 72 | 73 | # Generate indices in "t_1/2" units. 74 | t12_rise_indices = np.linspace(-1.0, 0.0, flare_halfwidth) 75 | # Compute fluxes for the rise part. 76 | rise_fluxes = ( 77 | 1.0 78 | + 1.941 * t12_rise_indices 79 | - 0.175 * t12_rise_indices**2.0 80 | - 2.246 * t12_rise_indices**3.0 81 | - 1.125 * t12_rise_indices**4.0 82 | ) 83 | # Insert these fluxes into the correct location in our light curve. 84 | fluxes_to_add[flare_time - flare_halfwidth + 1 : flare_time + 1] = rise_fluxes 85 | 86 | # Create the normalized part of the decay time. 87 | # In Davenport et al., they define their Eqn. 4 from t_1/2 = [0, 6]. 88 | # 89 | # If our halfwidth is 4 indices, then make a linear step array "n"*halfwidth 90 | # Davenport et al. defined the decay from [0, 6], so n=6 in their case... 91 | # [0., 0.26086957, 0.52173913, ... , 5.47826087, 5.73913043, 6.] 92 | # Then apply Davenport Eqn. 4 to this array expressed in "t_1/2" 93 | # Eqn. 4 * [0., 0.26086957, 0.52173913, ... , 5.47826087, 5.73913043, 6.] 94 | # 95 | # Then we insert these fluxes into the array of indices: 96 | # [flare_time : flare_time + n*flare_halfwidth-1 97 | 98 | # Generate indices in "t_1/2" units. 99 | t12_decay_indices = np.linspace(0.0, n_t12, n_t12 * flare_halfwidth) 100 | 101 | # Compute fluxes for the decay part. 102 | decay_fluxes = 0.6890 * np.exp(-1.600 * t12_decay_indices) + 0.3030 * np.exp( 103 | -0.2783 * t12_decay_indices 104 | ) 105 | 106 | # Insert these fluxes into the correct location in our light curve. 107 | # Note: the above index range is correct, but in Python you need to go one 108 | # extra when slicing, hence 6*flare_halfwidth-1+1 = 6*flare_halfwidth... 109 | fluxes_to_add[flare_time : flare_time + n_t12 * flare_halfwidth] = decay_fluxes 110 | 111 | # Scale the fluxes to add (which are normalized at this point) by 'flare_amp' 112 | fluxes_to_add *= flare_amp 113 | 114 | # If we needed to add some filler indices in the beginning, remove them now. 115 | if truncated_start: 116 | fluxes_to_add = fluxes_to_add[n_to_add_rise:] 117 | 118 | return fluxes + fluxes_to_add 119 | -------------------------------------------------------------------------------- /.github/workflows/ci_workflows.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - "**" 8 | tags: 9 | pull_request: 10 | branches: 11 | - main 12 | 13 | jobs: 14 | tests: 15 | name: ${{ matrix.name }} 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | fail-fast: true 19 | matrix: 20 | include: 21 | 22 | - name: Python 3.9.xx with minimal dependencies 23 | os: ubuntu-latest 24 | python: 3.9.21 25 | toxenv: py39-test 26 | 27 | - name: Python 3.9.xx with all optional dependencies 28 | os: ubuntu-latest 29 | python: 3.9.21 30 | toxenv: py39-test-alldeps 31 | toxargs: -v --develop 32 | toxposargs: -W error::ResourceWarning 33 | 34 | - name: Python 3.9.xx with numpy 1.24 and full coverage 35 | os: ubuntu-latest 36 | python: 3.9.21 37 | toxenv: py39-test-alldeps-numpy124-cov 38 | 39 | - name: Python 3.10.xx with minimal dependencies 40 | os: ubuntu-latest 41 | python: 3.10.16 42 | toxenv: py310-test 43 | 44 | - name: Python 3.10.xx with all optional dependencies 45 | os: ubuntu-latest 46 | python: 3.10.16 47 | toxenv: py310-test-alldeps 48 | toxargs: -v --develop 49 | toxposargs: -W error::ResourceWarning 50 | 51 | - name: Python 3.10.xx with numpy 1.24 and full coverage 52 | os: ubuntu-latest 53 | python: 3.10.16 54 | toxenv: py310-test-alldeps-numpy124-cov 55 | 56 | - name: Python 3.10.xx with numpy 2.10 and full coverage 57 | os: ubuntu-latest 58 | python: 3.10.16 59 | toxenv: py310-test-alldeps-numpy210-cov 60 | 61 | - name: Python 3.11.xx with minimal dependencies 62 | os: ubuntu-latest 63 | python: 3.11.11 64 | toxenv: py311-test 65 | 66 | - name: Python 3.11.xx with all optional dependencies 67 | os: ubuntu-latest 68 | python: 3.11.11 69 | toxenv: py311-test-alldeps 70 | toxargs: -v --develop 71 | toxposargs: -W error::ResourceWarning 72 | 73 | - name: Python 3.11.xx with numpy 1.24 and full coverage 74 | os: ubuntu-latest 75 | python: 3.11.11 76 | toxenv: py311-test-alldeps-numpy124-cov 77 | 78 | - name: Python 3.11.xx with numpy 2.10 and full coverage 79 | os: ubuntu-latest 80 | python: 3.11.11 81 | toxenv: py311-test-alldeps-numpy210-cov 82 | 83 | - name: Python 3.12.xx with minimal dependencies 84 | os: ubuntu-latest 85 | python: 3.12.8 86 | toxenv: py312-test 87 | 88 | - name: Python 3.12.xx with all optional dependencies 89 | os: ubuntu-latest 90 | python: 3.12.8 91 | toxenv: py312-test-alldeps 92 | toxargs: -v --develop 93 | toxposargs: -W error::ResourceWarning 94 | 95 | - name: Python 3.12.xx with numpy 2.10 and full coverage 96 | os: ubuntu-latest 97 | python: 3.12.8 98 | toxenv: py312-test-alldeps-numpy210-cov 99 | 100 | steps: 101 | - name: Checkout code 102 | uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v4.2.0 103 | with: 104 | fetch-depth: 0 105 | - name: Set up Python 106 | uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 107 | with: 108 | python-version: ${{ matrix.python }} 109 | - name: Install pyo dependencies 110 | if: startsWith(matrix.os, 'ubuntu') 111 | run: sudo apt-get install portaudio19-dev libsndfile1-dev libportmidi-dev liblo-dev 112 | - name: Install Python dependencies 113 | run: python -m pip install --upgrade tox codecov sphinx_rtd_theme 114 | - name: Run tests 115 | run: tox ${{ matrix.toxargs }} -e ${{ matrix.toxenv }} -- ${{ matrix.toxposargs }} 116 | - name: Upload coverage to codecov 117 | if: ${{ contains(matrix.toxenv,'-cov') }} 118 | uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 119 | with: 120 | token: ${{ secrets.CODECOV_TOKEN }} 121 | 122 | allowed_failures: 123 | name: ${{ matrix.name }} 124 | runs-on: ${{ matrix.os }} 125 | strategy: 126 | fail-fast: false 127 | matrix: 128 | include: 129 | 130 | - name: (Allowed Failure) Python 3.11 with dev version of key dependencies 131 | os: ubuntu-latest 132 | python: 3.11 133 | toxenv: py311-test-devdeps 134 | 135 | steps: 136 | - name: Checkout code 137 | uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v4.2.0 138 | with: 139 | fetch-depth: 0 140 | - name: Set up Python 141 | uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 142 | with: 143 | python-version: ${{ matrix.python }} 144 | - name: Install pyo dependencies 145 | if: startsWith(matrix.os, 'ubuntu') 146 | run: sudo apt-get install portaudio19-dev libsndfile1-dev libportmidi-dev liblo-dev 147 | - name: Install Python dependencies 148 | run: python -m pip install --upgrade tox codecov sphinx_rtd_theme 149 | - name: Run tests 150 | run: tox ${{ matrix.toxargs }} -e ${{ matrix.toxenv }} -- ${{ matrix.toxposargs }} 151 | -------------------------------------------------------------------------------- /astronify/utils/tests/test_pitch_mapping.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import numpy as np 3 | 4 | from ..pitch_mapping import data_to_pitch 5 | from ..exceptions import InputWarning, InvalidInputError 6 | 7 | 8 | def test_data_to_pitch(): 9 | """ 10 | Testing data_to_pitch function. 11 | """ 12 | 13 | # Simplifying output 14 | pitch_range = [400, 500] 15 | center_pitch = 450 16 | 17 | # basic linear stretch 18 | data_arr = np.array([[1.0, 0.0, 0.25, 0.75]]) 19 | pitch_arr = data_arr * (pitch_range[1] - pitch_range[0]) + pitch_range[0] 20 | 21 | assert ( 22 | pitch_arr 23 | == data_to_pitch(data_arr, pitch_range, center_pitch, stretch="linear") 24 | ).all() 25 | 26 | # invert 27 | pitch_arr = pitch_range[1] - data_arr * (pitch_range[1] - pitch_range[0]) 28 | assert ( 29 | pitch_arr 30 | == data_to_pitch( 31 | data_arr, pitch_range, center_pitch, stretch="linear", invert=True 32 | ) 33 | ).all() 34 | 35 | # linear stretch where input image must be scaled 36 | data_arr = np.array([10.0, 20.0, 12.5, 17.5]) 37 | pitch_arr = ( 38 | (data_arr - data_arr.min()) 39 | / (data_arr.max() - data_arr.min()) 40 | * (pitch_range[1] - pitch_range[0]) 41 | ) + pitch_range[0] 42 | assert ( 43 | pitch_arr 44 | == data_to_pitch(data_arr, pitch_range, center_pitch, stretch="linear") 45 | ).all() 46 | 47 | # linear stretch with non-equal lower/upper pitch ranges 48 | data_arr = np.array([[1.0, 0.0, 0.25, 0.75]]) 49 | pitch_arr = data_arr * (pitch_range[1] - pitch_range[0]) + pitch_range[0] 50 | 51 | pitch_range = [300, 500] 52 | assert ( 53 | pitch_arr == data_to_pitch(data_arr, [300, 500], center_pitch, stretch="linear") 54 | ).all() 55 | pitch_range = [400, 600] 56 | assert ( 57 | pitch_arr == data_to_pitch(data_arr, [400, 600], center_pitch, stretch="linear") 58 | ).all() 59 | pitch_range = [400, 500] 60 | 61 | # min_max val 62 | minval, maxval = 0, 1 63 | data_arr = np.array([1, 0, -1, 2]) 64 | pitch_arr = data_to_pitch( 65 | data_arr, 66 | pitch_range, 67 | center_pitch, 68 | stretch="linear", 69 | minmax_value=[minval, maxval], 70 | ) 71 | data_arr[data_arr < minval] = minval 72 | data_arr[data_arr > maxval] = maxval 73 | manual_pitch_arr = data_arr * (pitch_range[1] - pitch_range[0]) + pitch_range[0] 74 | assert (manual_pitch_arr == pitch_arr).all() 75 | 76 | minval, maxval = 0, 1 77 | data_arr = np.array([1.0, 0.0, 0.25, 0.75]) 78 | pitch_arr = data_to_pitch( 79 | data_arr, 80 | pitch_range, 81 | center_pitch, 82 | stretch="linear", 83 | minmax_value=[minval, maxval], 84 | ) 85 | data_arr[data_arr < minval] = minval 86 | data_arr[data_arr > maxval] = maxval 87 | manual_pitch_arr = data_arr * (pitch_range[1] - pitch_range[0]) + pitch_range[0] 88 | assert (manual_pitch_arr == pitch_arr).all() 89 | 90 | # min_max percent 91 | data_arr = np.array([1.1, -0.1, 1.0, 0.0, 0.25, 0.75]) 92 | pitch_arr = data_to_pitch( 93 | data_arr, pitch_range, center_pitch, stretch="linear", minmax_percent=[20, 80] 94 | ) 95 | assert ( 96 | np.isclose( 97 | pitch_arr, np.array([500, 400, 500, 400, 422.22222222, 477.77777778]) 98 | ) 99 | ).all() 100 | 101 | # asinh 102 | data_arr = np.array([1.0, 0.0, 0.25, 0.75]) 103 | zero_point = 0.21271901209248895 104 | pitch_arr = data_to_pitch( 105 | data_arr, pitch_range, center_pitch, zero_point, stretch="asinh" 106 | ) 107 | manual_pitch_arr = ( 108 | np.arcsinh(data_arr * 10) / np.arcsinh(10) * (pitch_range[1] - pitch_range[0]) 109 | + pitch_range[0] 110 | ) 111 | assert (manual_pitch_arr == pitch_arr).all() 112 | 113 | # sinh 114 | data_arr = np.array([1.0, 0.0, 0.25, 0.75]) 115 | zero_point = 0.7713965391706435 116 | pitch_arr = data_to_pitch( 117 | data_arr, pitch_range, center_pitch, zero_point, stretch="sinh" 118 | ) 119 | manual_pitch_arr = ( 120 | np.sinh(data_arr * 3) / np.sinh(3) * (pitch_range[1] - pitch_range[0]) 121 | + pitch_range[0] 122 | ) 123 | assert (manual_pitch_arr == pitch_arr).all() 124 | 125 | # sqrt 126 | data_arr = np.array([1.0, 0.0, 0.25, 0.75]) 127 | zero_point = 0.25 128 | pitch_arr = data_to_pitch( 129 | data_arr, pitch_range, center_pitch, zero_point, stretch="sqrt" 130 | ) 131 | manual_pitch_arr = ( 132 | np.sqrt(data_arr) * (pitch_range[1] - pitch_range[0]) + pitch_range[0] 133 | ) 134 | assert (manual_pitch_arr == pitch_arr).all() 135 | 136 | # log 137 | data_arr = np.array([1.0, 0.0, 0.25, 0.75]) 138 | zero_point = 0.030638584039112748 139 | pitch_arr = data_to_pitch( 140 | data_arr, pitch_range, center_pitch, zero_point, stretch="log" 141 | ) 142 | manual_pitch_arr = ( 143 | np.log(1000 * data_arr + 1) / np.log(1001) * (pitch_range[1] - pitch_range[0]) 144 | + pitch_range[0] 145 | ) 146 | assert (manual_pitch_arr == pitch_arr).all() 147 | 148 | # Bad stretch 149 | with pytest.raises(InvalidInputError): 150 | data_arr = np.array([1.0, 0.0, 0.25, 0.75]) 151 | data_to_pitch(data_arr, stretch="lin") 152 | 153 | # Giving both minmax percent and cut 154 | data_arr = np.array([1.1, -0.1, 1.0, 0.0, 0.25, 0.75]) 155 | pitch_arr = data_to_pitch( 156 | data_arr, pitch_range, center_pitch, stretch="linear", minmax_percent=[20, 80] 157 | ) 158 | with pytest.warns(InputWarning): 159 | test_arr = data_to_pitch( 160 | data_arr, 161 | pitch_range, 162 | center_pitch, 163 | stretch="linear", 164 | minmax_value=[0, 1], 165 | minmax_percent=[20, 80], 166 | ) 167 | assert (pitch_arr == test_arr).all() 168 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Astronify 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |

A Python package for sonifying astronomical data - turning telescope observations into sound!

18 | 19 | 20 | 21 | 22 |

Documentation

23 | 24 | Installation 25 | Documentation 26 | Astronify Tutorials 27 | 28 |

Astronify is under active development. Currently the package can sonify data series and will ultimately grow to encompass a range of sonification functionality.

29 | 30 |

We welcome feedback and code contributions. Visit us on GitHub at:

github.com/spacetelescope/astronify 31 | 32 | 33 | 34 |
35 |

Listen

36 | 37 |

We created 3 animations explaining sonification of Astronomy Data.

38 | 39 | 40 |
41 |

Sonification Examples

42 |

Our current collection of sonified astronomy data. The folder also contains written and narrated explanations of the science behind each sonification.

Feel free to use these recordings, especially for educational purposes!

43 |
44 |
45 | 46 | 47 |
48 |

Keep in Touch

49 | 50 | 51 |
52 |

Join our mailing list

53 |

Send a blank email to astronify-users-subscribe-request@maillist.stsci.edu and you will be subscribed.

54 | 55 |
56 |
57 | 58 | 59 |
60 |

Email Us at astronify@stsci.edu

61 |

We would love to hear your questions & ideas!

62 | 63 |
64 |
65 | 66 | 67 |
68 |

Media

69 | Video: Hearing the Light - Astronomy Data Sonification 70 | 71 | Video: How Sonification Deepens our Understanding of the Cosmos and Makes Astronomy More Accessible 72 | 73 | Podcast: Out of the Blocks- Space Sonification

74 | 75 | Podcast: Astro[sound]bytes- Scintillating Sounds of Science 76 | 77 | Blog: Hearing the Sound of Light 78 | 79 | Blog: Discovering Exoplanets Using Sonification 80 | 81 | Blog: Musical Fingerprints of Wobbly Stars 82 | 83 | Create With Light: Summer Art Program 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 |

Games

100 |

Test your ears! Listen to real sonified data and answer questions about what you heard. Playing these games will help us learn how to improve our sonifications.

101 | 102 | 103 | 104 |
105 |

Level 1 - Flares & Transits

106 |

Put on your astronomer hat and see if you can learn information about the universe... using your ears!

107 | 108 |
109 |
110 | 111 | 112 |
113 |

Level 2 - Exoplanets!

114 |

This more advanced game focuses on different ways of analyzing exoplanet data.

115 |
116 |
117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /astronify/simulator/sim_lc_setup_args.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: sim_lc_setup_args 3 | :synopsis: Sets up command-line argument parser. 4 | 5 | .. moduleauthor:: Scott W. Fleming 6 | """ 7 | 8 | import argparse 9 | from .sim_lc_config import SimLcConfig as sim_lc_config 10 | 11 | 12 | def sim_lc_setup_args(): 13 | """ 14 | Set up command-line arguments and options. 15 | 16 | :returns: ArgumentParser -- Stores arguments and options. 17 | """ 18 | 19 | parser = argparse.ArgumentParser( 20 | description="Create simulated light curves, as FITS files, for use with" 21 | " the Astronify sonification software. Types include flat, transit, sine" 22 | " and flare." 23 | ) 24 | 25 | parser.add_argument( 26 | "lc_type", 27 | action="store", 28 | type=str, 29 | help="Type of light curve to create.", 30 | choices=["flat", "transit", "sine", "flare"], 31 | ) 32 | 33 | parser.add_argument( 34 | "-o", 35 | dest="lc_ofile", 36 | action="store", 37 | type=str, 38 | help="Name of output FITS file to create.", 39 | default=sim_lc_config.sim_lc_ofile, 40 | ) 41 | 42 | parser.add_argument( 43 | "-l", 44 | action="store", 45 | type=int, 46 | default=sim_lc_config.sim_lc_length, 47 | dest="lc_length", 48 | help="Total number of flux" 49 | " measurements in the light curve. Default =" 50 | " %(default)s.", 51 | ) 52 | 53 | parser.add_argument( 54 | "-n", 55 | action="store", 56 | type=float, 57 | default=sim_lc_config.sim_lc_noise, 58 | dest="lc_noise", 59 | help="Amount of noise to add to the" 60 | " measurements in the light curve, specified by the" 61 | " standard deviation of the normal distribution to draw" 62 | " from. Set to zero for no noise. Default =" 63 | " %(default)s.", 64 | ) 65 | 66 | parser.add_argument( 67 | "-v", 68 | action="store_true", 69 | dest="visualize", 70 | default=sim_lc_config.sim_lc_vizualize, 71 | help="If True, a plot of the light curve" 72 | " that is generated will be plot on the screen. Default" 73 | " = %(default)s.", 74 | ) 75 | 76 | parser.add_argument( 77 | "-y", 78 | action="store", 79 | type=float, 80 | default=sim_lc_config.sim_lc_yoffset, 81 | dest="lc_yoffset", 82 | help="Baseline (unitless) flux height" 83 | " of the light curve. Used to test sonification of" 84 | " sources with different total brightness. Default =" 85 | " %(default)s.", 86 | ) 87 | 88 | # Transit-related parameters here. 89 | transit_group = parser.add_argument_group( 90 | "transit", "Parameters for transit signals." 91 | ) 92 | 93 | transit_group.add_argument( 94 | "--transit_depth", 95 | type=float, 96 | default=sim_lc_config.sim_lc_transit_depth, 97 | dest="transit_depth", 98 | help="Depth of the transit" 99 | " signal specified as a percent, e.g., set to" 100 | " 10.0 for a 10%% depth transit. Default =" 101 | " %(default)s.", 102 | ) 103 | 104 | transit_group.add_argument( 105 | "--transit_period", 106 | type=int, 107 | default=sim_lc_config.sim_lc_transit_period, 108 | dest="transit_period", 109 | help="Period of the" 110 | " transit signal, specified as the number of" 111 | " fluxes (bins) between the start of each event." 112 | " Default = %(default)s.", 113 | ) 114 | 115 | transit_group.add_argument( 116 | "--transit_start", 117 | type=int, 118 | default=sim_lc_config.sim_lc_transit_start, 119 | dest="transit_start", 120 | help="Start of the first" 121 | " transit, specified as the index of the" 122 | " flux (bin) to use as the start of the first" 123 | " transit event. Default = %(default)s.", 124 | ) 125 | 126 | transit_group.add_argument( 127 | "--transit_width", 128 | type=int, 129 | default=sim_lc_config.sim_lc_transit_width, 130 | dest="transit_width", 131 | help="Width of the" 132 | " transit signal, specified as the number of" 133 | " fluxes (bins) between the start and end of each" 134 | " event. Default = %(default)s.", 135 | ) 136 | 137 | # Sinusoidal-related parameters here. 138 | sine_group = parser.add_argument_group( 139 | "sinusoidal", "Parameters for sinusoidal signals." 140 | ) 141 | 142 | sine_group.add_argument( 143 | "--sine_amp", 144 | type=float, 145 | default=sim_lc_config.sim_lc_sine_amp, 146 | dest="sine_amp", 147 | help="Amplitude of the sinusoidal signal to add. Default = %(default)s.", 148 | ) 149 | 150 | sine_group.add_argument( 151 | "--sine_period", 152 | type=float, 153 | default=sim_lc_config.sim_lc_sine_period, 154 | dest="sine_period", 155 | help="Period of the sinusoidal signal, specified in the (unitless)" 156 | " time axis (flux bins). Default = %(default)s.", 157 | ) 158 | 159 | # Flare-related parameters here. 160 | flare_group = parser.add_argument_group("flare", "Parameters for adding flares.") 161 | 162 | flare_group.add_argument( 163 | "--flare_time", 164 | type=int, 165 | default=sim_lc_config.sim_lc_flare_time, 166 | dest="flare_time", 167 | help="Time corresponding to" 168 | " the maximum flux of the flare, specified" 169 | " as the index of the flux (bin) to use as" 170 | " the peak time. Default = %(default)s.", 171 | ) 172 | 173 | flare_group.add_argument( 174 | "--flare_amp", 175 | type=float, 176 | default=sim_lc_config.sim_lc_flare_amp, 177 | dest="flare_amp", 178 | help="Amplitude (maximum flux) of the flare to add. Default = %(default)s.", 179 | ) 180 | 181 | flare_group.add_argument( 182 | "--flare_halfwidth", 183 | type=int, 184 | default="flare_halfwidth", 185 | help="The flare" 186 | " half-width (measured in indices) that" 187 | " corresponds to 't_1/2' in the Davenport et al." 188 | " flare template.", 189 | ) 190 | 191 | return parser 192 | -------------------------------------------------------------------------------- /astronify/utils/pitch_mapping.py: -------------------------------------------------------------------------------- 1 | # Licensed under a 3-clause BSD style license - see LICENSE.rst 2 | """ 3 | Pitch mapping functionality 4 | =========================== 5 | 6 | Functionality for taking arbitrary data values to audible pitches. 7 | """ 8 | 9 | import warnings 10 | 11 | import numpy as np 12 | 13 | from astropy.visualization import ( 14 | SqrtStretch, 15 | LogStretch, 16 | AsinhStretch, 17 | SinhStretch, 18 | LinearStretch, 19 | MinMaxInterval, 20 | ManualInterval, 21 | AsymmetricPercentileInterval, 22 | ) 23 | 24 | from .exceptions import InputWarning, InvalidInputError 25 | 26 | __all__ = ["data_to_pitch"] 27 | 28 | 29 | def data_to_pitch( 30 | data_array, 31 | pitch_range=[100, 10000], 32 | center_pitch=440, 33 | zero_point="median", 34 | stretch="linear", 35 | minmax_percent=None, 36 | minmax_value=None, 37 | invert=False, 38 | ): 39 | """ 40 | Map data array to audible pitches in the given range, and apply stretch and scaling 41 | as required. 42 | 43 | Parameters 44 | ---------- 45 | data_array : array-like 46 | Data to map to pitch values. Individual data values should be floats. 47 | pitch_range : array 48 | Optional, default [100,10000]. Range of acceptable pitches in Hz. 49 | center_pitch : float 50 | Optional, default 440. The pitch in Hz where that the the zero point of the 51 | data will be mapped to. 52 | zero_point : str or float 53 | Optional, default "median". The data value that will be mapped to the center 54 | pitch. Options are mean, median, or a specified data value (float). 55 | stretch : str 56 | Optional, default 'linear'. The stretch to apply to the data array. 57 | Valid values are: asinh, sinh, sqrt, log, linear 58 | minmax_percent : array 59 | Optional. Interval based on a keeping a specified fraction of data values 60 | (can be asymmetric) when scaling the data. The format is [lower percentile, 61 | upper percentile], where data values below the lower percentile and above the upper 62 | percentile are clipped. Only one of minmax_percent and minmax_value should be specified. 63 | minmax_value : array 64 | Optional. Interval based on user-specified data values when scaling the data array. 65 | The format is [min value, max value], where data values below the min value and above 66 | the max value are clipped. 67 | Only one of minmax_percent and minmax_value should be specified. 68 | invert : bool 69 | Optional, default False. If True the pitch array is inverted (low pitches become high 70 | and vice versa). 71 | 72 | Returns 73 | ------- 74 | response : array 75 | The normalized data array, with values in given pitch range. 76 | """ 77 | # Parsing the zero point 78 | if zero_point in ("med", "median"): 79 | zero_point = np.median(data_array) 80 | if zero_point in ("ave", "mean", "average"): 81 | zero_point = np.mean(data_array) 82 | 83 | # The center pitch cannot be >= max() pitch range, or <= min() of pitch range. 84 | # If it is, fall back to using the mean of the pitch range provided. 85 | if center_pitch <= pitch_range[0] or center_pitch >= pitch_range[1]: 86 | warnings.warn( 87 | "Given center pitch is outside the pitch range, defaulting to the mean.", 88 | InputWarning, 89 | ) 90 | center_pitch = np.mean(pitch_range) 91 | 92 | if (data_array == zero_point).all(): 93 | # All values are the same, no more calculation needed 94 | return np.full(len(data_array), center_pitch) 95 | 96 | # Normalizing the data_array and adding the zero point (so it can go through the same transform) 97 | data_array = np.append(np.array(data_array), zero_point) 98 | 99 | # Setting up the transform with the stretch 100 | if stretch == "asinh": 101 | transform = AsinhStretch() 102 | elif stretch == "sinh": 103 | transform = SinhStretch() 104 | elif stretch == "sqrt": 105 | transform = SqrtStretch() 106 | elif stretch == "log": 107 | transform = LogStretch() 108 | elif stretch == "linear": 109 | transform = LinearStretch() 110 | else: 111 | raise InvalidInputError("Stretch {} is not supported!".format(stretch)) 112 | 113 | # Adding the scaling to the transform 114 | if minmax_percent is not None: 115 | transform += AsymmetricPercentileInterval(*minmax_percent) 116 | 117 | if minmax_value is not None: 118 | warnings.warn( 119 | "Both minmax_percent and minmax_value are set, minmax_value will be ignored.", 120 | InputWarning, 121 | ) 122 | elif minmax_value is not None: 123 | transform += ManualInterval(*minmax_value) 124 | else: # Default, scale the entire image range to [0,1] 125 | transform += MinMaxInterval() 126 | 127 | # Performing the transform and then putting it into the pich range 128 | pitch_array = transform(data_array) 129 | 130 | if invert: 131 | pitch_array = 1 - pitch_array 132 | 133 | zero_point = pitch_array[-1] 134 | pitch_array = pitch_array[:-1] 135 | 136 | # In rare cases, the zero-point at this stage might be 0.0. 137 | # One example is an input array of two values where the median() is the same as the 138 | # lowest of the two values. In this case, the zero-point is 0.0 and will lead to error 139 | # (divide by zero). Change to small value to avoid dividing by zero (in reality the choice 140 | # of zero-point calculation by the user was probably poor, but not in purview to mandate or 141 | # change user's choice here. May want to consider providing info back to the user about the 142 | # distribution of pitches actually used based on their sonification options in some way. 143 | if zero_point == 0.0: 144 | zero_point = 1e-6 145 | 146 | if ( 147 | (1 / zero_point) * (center_pitch - pitch_range[0]) + pitch_range[0] 148 | ) <= pitch_range[1]: 149 | pitch_array = (pitch_array / zero_point) * ( 150 | center_pitch - pitch_range[0] 151 | ) + pitch_range[0] 152 | else: 153 | pitch_array = ((pitch_array - zero_point) / (1 - zero_point)) * ( 154 | pitch_range[1] - center_pitch 155 | ) + center_pitch 156 | 157 | return pitch_array 158 | -------------------------------------------------------------------------------- /docs/astronify/index.rst: -------------------------------------------------------------------------------- 1 | *********************** 2 | Astronify Documentation 3 | *********************** 4 | 5 | Introduction 6 | ============ 7 | 8 | Astronify contains tools for sonifying astronomical data. Currently Astronify can sonify data series. This package is under active development, and will ultimately grow to encompass a range of sonification functionality. 9 | 10 | 11 | Data Series Sonification 12 | ======================== 13 | 14 | Data series sonification refers to taking a data table and mapping one column to 15 | time, and one column to pitch. In astronomy this technique is commonly used to 16 | sonify light curves, where observation time is scaled to listening time 17 | and flux is mapped to pitch. While Astronify's sonification uses the columns "time" 18 | and "flux" by default, any two columns can be supplied and a sonification created. 19 | 20 | 21 | Basic Usage 22 | ----------- 23 | 24 | At base, all that is required to make a sonification is an `~astropy.table.Table` with 25 | two columns. By default these columns are assumed to be named "time" and "flux", but 26 | alternate column names can also be provided (see `~astronify.series.SoniSeries`). 27 | 28 | .. code-block:: python 29 | 30 | >>> from astronify.series import SoniSeries 31 | >>> from astropy.table import Table 32 | 33 | >>> data_table = Table({"time":[0, 1, 2, 3, 4, 5, 9, 10, 11, 12], 34 | ... "flux": [0.3, 0.4, 0.3, 0.5, 0.5, 0.4, 0.3, 0.2, 0.3, 0.1]}) 35 | 36 | >>> data_soni = SoniSeries(data_table) 37 | >>> data_soni.note_spacing = 0.2 38 | >>> data_soni.sonify() 39 | >>> data_soni.play() #doctest: +SKIP 40 | 41 | 42 | The default note spacing (median time between notes in seconds) is 0.01, so for short data series 43 | we need to slow it down to hear all the data points. 44 | 45 | This more interesting example sonifies real data from the Kepler space telescope. 46 | The package `Lightkurve `_ is used to download a Kepler 47 | light curve and then sonify it. 48 | 49 | 50 | .. code-block:: python 51 | 52 | >>> from astronify.series import SoniSeries 53 | >>> import lightkurve #doctest: +SKIP 54 | 55 | >>> kep12b_lc = lightkurve.search_lightcurvefile("KIC 11804465", cadence="long", quarter=1).download_all()[0].SAP_FLUX.to_table() #doctest: +SKIP 56 | >>> kep12b_obj = SoniSeries(kep12b_lc) #doctest: +SKIP 57 | >>> kep12b_obj.sonify() #doctest: +SKIP 58 | >>> kep12b_obj.play() #doctest: +SKIP 59 | 60 | 61 | A variety of arguments can be set to change the parameters of the sonification. You can control note spacing, note duration (each data point will get a note of the same duration), 62 | and change a number of aspects of the algorithm used to transform data values into pitches. 63 | 64 | .. code-block:: python 65 | 66 | >>> from astronify.series import SoniSeries 67 | >>> import lightkurve #doctest: +SKIP 68 | 69 | >>> kep12b_lc = lightkurve.search_lightcurvefile("KIC 11804465", cadence="long", quarter=1).download_all()[0].SAP_FLUX.to_table() #doctest: +SKIP 70 | >>> kep12b_obj = SoniSeries(kep12b_lc) #doctest: +SKIP 71 | >>> kep12b_obj.pitch_mapper.pitch_map_args["center_pitch"] = 880 #doctest: +SKIP 72 | >>> kep12b_obj.sonify() #doctest: +SKIP 73 | >>> kep12b_obj.play() #doctest: +SKIP 74 | 75 | 76 | See `~astronify.utils.data_to_pitch` for a full list of the parameters that can be changed in the 77 | default pitch mapping function. The default pitch mapping function can also be replaced with 78 | a user supplied function, see `~astronify.series.PitchMap` for the requirements on this function. 79 | 80 | 81 | Sonification Algorithm 82 | ---------------------- 83 | 84 | While the user can supply any function they like to transform data values in to pitch in Hz, 85 | the default function for Astronify is `~astronify.utils.data_to_pitch` which takes in an array 86 | of float values and transformed them into audible pitch values (in Hz). 87 | 88 | **The algorithm is as follows:** 89 | 90 | Given a center pitch, zero point, and pitch range, the data values will be scaled with a 91 | chosen stretch (linear, hyperbolic sine, hyperbolic arcsine, logarithmic or square 92 | root) such that the zero point maps to the center pitch and all pitches fall within 93 | the pitch range. The given pitch range defines the maximum pitch boundaries, but 94 | depending on the parameters of sonification output pitches may not reach the edges. 95 | 96 | The zero point is calculated based on the input argument (mean, median, or specified value) 97 | and then appended to the data array. The resulting array is scaled to the interval [0,1] 98 | taking into account any requested clipping, and the requested stretch is applied. At this point 99 | if the invert argument is set, the array is inverted by subtracting all values from 1. 100 | 101 | The scaled zero point is then removed from the array which is scaled to the pitch range 102 | such that the scaled zero point becomes the center pitch value and the entire pitch range 103 | falls within the input pitch range. In practice this means one of two things: 104 | The array is scaled such that the 0 corresponds to the minimum of the input pitch range and the 105 | scaled zero point corresponds to the center pitch value. Or, the scaled zero point corresponds to 106 | the center pitch value and 1 corresponds to the maximum of the input pitch range. Whichever 107 | scaling means that all output pitch values fall within the desired range. 108 | 109 | 110 | Troubleshooting 111 | --------------- 112 | 113 | This package is still in beta and as such may not be completely stable. 114 | If you encounter problems please open a 115 | `github issue `_. We 116 | also welcome code contributions in the form of pull requests. 117 | 118 | The following are known issues/troubleshooting tips: 119 | 120 | - Sonifications cease playing when running in a Jupyter notebook— 121 | Restart the kernel, particularly if it has been running for a while. 122 | 123 | - Sonification will not play when run in a script— 124 | Currently sonifications cannot be played (using the `.play()` method from 125 | python scripts (as opposed to in interactive mode). Instead write the 126 | sonification to a file and play the result in the audio player of your choice. 127 | 128 | 129 | 130 | Light Curve Simulator 131 | ===================== 132 | 133 | Astronify also provides a simulation package for creating synthetic light 134 | curves with various characteristics that can then be sonified. The main 135 | function is `~astronify.simulator.simulated_lc` which allows the user to 136 | create a light curve that is flat, sinusoidal, or contains a transiting 137 | exoplanet or stellar flare. 138 | 139 | .. code-block:: python 140 | 141 | >>> from astronify import simulator, series 142 | 143 | >>> lc_data = simulator.simulated_lc("transit", visualize=False, transit_depth=1.5, 144 | ... transit_period=145, transit_width=42, 145 | ... lc_noise=0.5, lc_length=750) 146 | >>> soni_obj = series.SoniSeries(lc_data) 147 | >>> soni_obj.sonify() 148 | >>> soni_obj.play() #doctest: +SKIP 149 | -------------------------------------------------------------------------------- /docs/CreateWithLight.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Create With Light 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
CREATE WITH LIGHT
18 | 19 | 20 |

We hired students to create art inspired by space sonification that can be enjoyed by a blind audience. We all met by video chat for 6 weeks during the summer of 2021. The student artists were both blind and sighted themselves—so we had a lot of interesting discussions about acessibility and art.

21 | 22 |

The students created: 2 musical pieces, an animation with spoken word poetry, and a 3-dimensional painting.

23 | 24 | 25 |
26 |
27 |

Music By Emril Bennett

28 | Artist Statement 29 | 30 | 31 | 32 | 33 |
34 |
35 |

Music By Ashley Neall

36 | Artist Statement 37 | 38 | 39 | 40 | 41 | 42 |
43 |
44 |

World Beyond Us 45 |
By Moonasia Williams

46 | Artist Statement 47 | 48 | 49 | 50 | 51 | 52 |
53 |
54 |

Painting By Caitlin Caughlan

55 | Sonification Inspiration 56 | Eclipsing Binary Painting 57 | 58 | 59 | 60 |
61 |
62 |
63 | 64 |
65 | 66 |

Artist Statements

67 | 68 |

Emril Bennet's notes on artistic process

69 |

70 | I have been thinking about making art all day, and haven’t taken action on it. Maybe a creative thought will slip through the exhaustion-induced chaos. I zombie walk to my desk. I pick up the iPhone then proceed to open Garageband. It certainly isn’t one of the better editors out there, but it’s accessibility is decent. I also happen to be very familiar with it, as I have played with it a lot in the past. I open up a new project file and begin aimlessly scrolling through the eleven selection categories. I stumble upon the sampler which, surprisingly, I have not found use for. This is mainly due to the fact that it makes a speed modification in edition to the pitch change, which I did not want or expect on my first use. Since I was letting ideas loose, and was not familiar with this interface in particular, I decided I would explore it. This art program is about problem solving and open mindedness. I click the button and begin exploring. From experience, I know my keyboard starts near the middle and extends to the bottom of the screen, when holding the phone in the proper orientation. I proceed to find the keyboard and start fiddling with it, making discoveries along the way. Once I got familiar with finding the sample controls, which hide in confusing (and sometimes inaccessible) menus, I record a test sample. Once I record a suitable test sample, I begin editing it and seeing what happens along the way. Though the experience is slow and laggy, it was also somewhat natural. I get carried away playing with this, using the different controls and tapping and sliding through the admittedly small keyboard screen. I became inspired to learn more about it, as the variations of interaction and response are so few and yet so many. This is especially true of samples which have pitch change recorded into them, such as those taken from certain types of sonified data files. This truly intrigues me and I can not wait to see what comes out of it! There is much work to be done… 71 |

72 | 73 |
74 |
75 |
76 | 77 |

Ashley Neall's Artist Statement

78 |

79 | The inspiration for this musical piece is derived from listening to various sonifications and incorporating such auditory data into the piece, while also expressing the emotions I feel when I listen to the sonifications through this piece. While listening to the sonifications before beginning the production process, I would condition myself to think about nothing else except for this auditory data. As I deciphered how the sonifications made me feel and what musical elements could describe this, I began to spontaneously play notes on my electric piano - hoping to find a melody that would successfully convey this emotion, while also blending with the chosen sonification itself. 80 |
81 |
82 | I picked one specific sonification that is considered a flare. To discover how I would create music from this auditory data, I began listening to this specific sonification and re-creating this sequence of notes on my piano by ear. From this exploration on my piano, I created a melody and used different virtual instruments with this same melody that have timbres that “feel” like something extraterrestrial. After adding some voices and synths to this melody, I decided to use a file converter to turn the audio file of the original flare sonification into a MIDI file so that I could further explore the elements of this sonification and manipulate its tempo, pitch, and timbre. From this MIDI version of the flare sonification, I gathered more inspiration as I had greater freedom to incorporate this flare into the melody I now had as a starting point. Continuing to transport, alter, and add tracks, I progressed slowly but ultimately finished with a piece that I believe merges the elements of - and feelings I perceive from - my melody with the flare sonification. 83 |
84 |
85 | I’d rather not explain what emotion I believe this work evokes. Instead, I encourage the audience to decipher their own perceptions of this piece by immersing themselves in the music as they think critically and creatively. Leaving the meaning of this musical piece implicit allows the music to have an infinitely sensational impact. 86 |

87 | 88 |
89 |
90 |
91 | 92 |

Moonasia Williams' Artist Statement

93 |

94 | A World Beyond Us is a word art and digital imagery design in graphics and shadow colors. In A Word Beyond Us it has beautifully done sound, and sound from other outside sources. A World Beyond Us was a developed idea that changed over time due to time constraints. The piece turned out pretty well but definitely could have some improvements in the future. 95 |

96 | 97 |
98 |
99 |
100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Licensed under a 3-clause BSD style license - see LICENSE.rst 3 | # 4 | # Astropy documentation build configuration file. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this file. 9 | # 10 | # All configuration values have a default. Some values are defined in 11 | # the global Astropy configuration which is loaded here before anything else. 12 | # See astropy.sphinx.conf for which values are set there. 13 | 14 | # If extensions (or modules to document with autodoc) are in another directory, 15 | # add these directories to sys.path here. If the directory is relative to the 16 | # documentation root, use os.path.abspath to make it absolute, like shown here. 17 | # sys.path.insert(0, os.path.abspath('..')) 18 | # IMPORTANT: the above commented section was generated by sphinx-quickstart, but 19 | # is *NOT* appropriate for astropy or Astropy affiliated packages. It is left 20 | # commented out with this explanation to make it clear why this should not be 21 | # done. If the sys.path entry above is added, when the astropy.sphinx.conf 22 | # import occurs, it will import the *source* version of astropy instead of the 23 | # version installed (if invoked as "make html" or directly with sphinx), or the 24 | # version in the build directory (if "python setup.py build_sphinx" is used). 25 | # Thus, any C-extensions that are needed to build the documentation will *not* 26 | # be accessible, and the documentation will not build correctly. 27 | 28 | import os 29 | import sys 30 | import datetime 31 | from importlib import import_module 32 | 33 | try: 34 | from sphinx_astropy.conf.v1 import * # noqa 35 | except ImportError: 36 | print( 37 | "ERROR: the documentation requires the sphinx-astropy package to be installed" 38 | ) 39 | sys.exit(1) 40 | 41 | # Get configuration information from setup.cfg 42 | from configparser import ConfigParser 43 | 44 | conf = ConfigParser() 45 | 46 | conf.read([os.path.join(os.path.dirname(__file__), "..", "setup.cfg")]) 47 | setup_cfg = dict(conf.items("metadata")) 48 | 49 | # -- General configuration ---------------------------------------------------- 50 | 51 | # By default, highlight as Python 3. 52 | highlight_language = "python3" 53 | 54 | # If your documentation needs a minimal Sphinx version, state it here. 55 | # needs_sphinx = '5.0' 56 | 57 | # To perform a Sphinx version check that needs to be more specific than 58 | # major.minor, call `check_sphinx_version("x.y.z")` here. 59 | # check_sphinx_version("1.2.1") 60 | 61 | # List of patterns, relative to source directory, that match files and 62 | # directories to ignore when looking for source files. 63 | # exclude_patterns.append('_templates') 64 | 65 | # This is added to the end of RST files - a good place to put substitutions to 66 | # be used globally. 67 | rst_epilog += """ 68 | """ 69 | 70 | # -- Project information ------------------------------------------------------ 71 | 72 | # This does not *have* to match the package name, but typically does 73 | project = setup_cfg["name"] 74 | author = setup_cfg["author"] 75 | copyright = "{0}, {1}".format(datetime.datetime.now().year, setup_cfg["author"]) 76 | 77 | # The version info for the project you're documenting, acts as replacement for 78 | # |version| and |release|, also used in various other places throughout the 79 | # built documents. 80 | 81 | import_module(setup_cfg["name"]) 82 | package = sys.modules[setup_cfg["name"]] 83 | 84 | # The short X.Y version. 85 | version = package.__version__.split("-", 1)[0] 86 | # The full version, including alpha/beta/rc tags. 87 | release = package.__version__ 88 | 89 | 90 | # -- Options for HTML output -------------------------------------------------- 91 | 92 | # A NOTE ON HTML THEMES 93 | # The global astropy configuration uses a custom theme, 'bootstrap-astropy', 94 | # which is installed along with astropy. A different theme can be used or 95 | # the options for this theme can be modified by overriding some of the 96 | # variables set in the global configuration. The variables set in the 97 | # global configuration are listed below, commented out. 98 | 99 | 100 | # Add any paths that contain custom themes here, relative to this directory. 101 | # To use a different custom theme, add the directory containing the theme. 102 | # html_theme_path = ["_themes",] 103 | 104 | # Custome template path, adding custom css and home link 105 | templates_path = ["_templates"] 106 | 107 | # The theme to use for HTML and HTML Help pages. See the documentation for 108 | # a list of builtin themes. To override the custom theme, set this to the 109 | # name of a builtin theme or the name of a custom theme in html_theme_path. 110 | html_theme = "sphinx_rtd_theme" 111 | 112 | 113 | def setup_style(app): 114 | app.add_stylesheet("astronify.css") 115 | 116 | 117 | master_doc = "contents" 118 | html_extra_path = ["index.html", "CreateWithLight.html"] 119 | 120 | # Custom sidebar templates, maps document names to template names. 121 | html_sidebars = {"**": ["globaltoc.html", "localtoc.html", "searchbox.html"]} 122 | 123 | # The name of an image file (relative to this directory) to place at the top 124 | # of the sidebar. 125 | html_logo = "_static/ASTRONIFY_Ball_white.svg" 126 | 127 | # The name of an image file (within the static path) to use as favicon of the 128 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 129 | # pixels large. 130 | html_favicon = "_static/astronify-favicon.png" 131 | 132 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 133 | # using the given strftime format. 134 | # html_last_updated_fmt = '' 135 | 136 | # The name for this set of Sphinx documents. If None, it defaults to 137 | # " v documentation". 138 | html_title = "{0} v{1}".format(project, release) 139 | 140 | # Output file base name for HTML help builder. 141 | htmlhelp_basename = project + "doc" 142 | 143 | # Static files to copy after template files 144 | html_static_path = ["_static"] 145 | # html_style = 'astronify.css' 146 | 147 | html_css_files = ["astronify.css"] 148 | 149 | 150 | # -- Options for LaTeX output ------------------------------------------------- 151 | 152 | # Grouping the document tree into LaTeX files. List of tuples 153 | # (source start file, target name, title, author, documentclass [howto/manual]). 154 | latex_documents = [ 155 | ("index", project + ".tex", project + " Documentation", author, "manual") 156 | ] 157 | 158 | 159 | # -- Options for manual page output ------------------------------------------- 160 | 161 | # One entry per manual page. List of tuples 162 | # (source start file, name, description, authors, manual section). 163 | man_pages = [("index", project.lower(), project + " Documentation", [author], 1)] 164 | 165 | 166 | # -- Options for the edit_on_github extension --------------------------------- 167 | 168 | if setup_cfg.get("edit_on_github").lower() == "true": 169 | 170 | extensions += ["sphinx_astropy.ext.edit_on_github"] 171 | 172 | edit_on_github_project = setup_cfg["github_project"] 173 | edit_on_github_branch = "main" 174 | 175 | edit_on_github_source_root = "" 176 | edit_on_github_doc_root = "docs" 177 | 178 | # -- Resolving issue number to links in changelog ----------------------------- 179 | github_issues_url = "https://github.com/{0}/issues/".format(setup_cfg["github_project"]) 180 | 181 | # -- Turn on nitpicky mode for sphinx (to warn about references not found) ---- 182 | # 183 | # nitpicky = True 184 | # nitpick_ignore = [] 185 | # 186 | # Some warnings are impossible to suppress, and you can list specific references 187 | # that should be ignored in a nitpick-exceptions file which should be inside 188 | # the docs/ directory. The format of the file should be: 189 | # 190 | # 191 | # 192 | # for example: 193 | # 194 | # py:class astropy.io.votable.tree.Element 195 | # py:class astropy.io.votable.tree.SimpleElement 196 | # py:class astropy.io.votable.tree.SimpleElementWithContent 197 | # 198 | # Uncomment the following lines to enable the exceptions: 199 | # 200 | # for line in open('nitpick-exceptions'): 201 | # if line.strip() == "" or line.startswith("#"): 202 | # continue 203 | # dtype, target = line.split(None, 1) 204 | # target = target.strip() 205 | # nitpick_ignore.append((dtype, six.u(target))) 206 | -------------------------------------------------------------------------------- /docs/notebooks/Intro_Astronify_Series.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Astronify: Sonifying Time Series\n", 8 | "\n", 9 | "This tutorial will demonstrate the sonification of time series data, specifically light curves using Astronify. It will cover basic usage, adjusting sonification parameters, playing the sonification within the browser, and saving the sonification as a wav file.\n", 10 | "\n", 11 | "1. [Imports](#Imports)\n", 12 | "2. [Getting data](#Getting-data)\n", 13 | "3. [Basic sonification](#Basic-sonification)\n", 14 | "4. [Changing sonification parameters](#Changing-sonification-parameters)\n", 15 | "5. [Outputing an audio file](#Outputing-an-audio-file)" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "metadata": {}, 21 | "source": [ 22 | "## Imports\n", 23 | "\n", 24 | "In addition to Astronify, we will use the [Lightkurve](https://docs.lightkurve.org/) package to query and download Kepler light curves, [matplotlib](https://matplotlib.org/) to plot the light curves (these cells will be markes and can be skipped by those for whom plots are not useful), and [Astropy](https://www.astropy.org/) for various data manipulation tasks.\n", 25 | "\n", 26 | "\\* Don't worry if you get a warning about WxPython not being found, it's used for the GUI functionality audio library, which Astronify does not use." 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": null, 32 | "metadata": {}, 33 | "outputs": [], 34 | "source": [ 35 | "from astronify.series import SoniSeries\n", 36 | "import lightkurve\n", 37 | "\n", 38 | "import matplotlib\n", 39 | "%matplotlib inline\n", 40 | "import matplotlib.pyplot as plt\n", 41 | "\n", 42 | "from astropy.table import Table" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "## Getting data\n", 50 | "\n", 51 | "Fundamentally all that Astronify requires is an Astropy Table object where one colum will be translated into time (default is “time”) and one column will be translated into pitch (default is “flux”).\n", 52 | "\n", 53 | "Simply because it is easy I will download a Kepler light curve using lightkurve (lightkurve is a user friendly way to get Kepler and TESS data). This is a light curve that shows Kepler 12b, a transiting exoplanet." 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": null, 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "kep12b_lc = lightkurve.search_lightcurvefile(\"KIC 11804465\", cadence=\"long\", quarter=1).download_all()[0].SAP_FLUX.to_table()" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "## Basic sonification\n", 70 | "\n", 71 | "Here we will use all of the sonification defaults and see what we get.\n", 72 | "\n", 73 | "First a small peak at the data visually for those of us who find that useful." 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "f, ax = plt.subplots(figsize=(12, 6))\n", 83 | "ax.plot(kep12b_lc['time'].jd, kep12b_lc['flux'])\n", 84 | "ax.set_xlabel(\"Time (JD)\")\n", 85 | "ax.set_ylabel(\"Flux\")\n", 86 | " \n", 87 | "plt.show()" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": {}, 93 | "source": [ 94 | "Now to sonify the same data." 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "kep12b_obj = SoniSeries(kep12b_lc)\n", 104 | "kep12b_obj.sonify()" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "metadata": {}, 110 | "source": [ 111 | "And play the sonification." 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": null, 117 | "metadata": {}, 118 | "outputs": [], 119 | "source": [ 120 | "kep12b_obj.play()" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "The playback will stop at the end, but we can also stop it early." 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": null, 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "kep12b_obj.stop()" 137 | ] 138 | }, 139 | { 140 | "cell_type": "markdown", 141 | "metadata": {}, 142 | "source": [ 143 | "## Changing sonification parameters\n", 144 | "\n", 145 | "Let's look at the current sonification parameters." 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": null, 151 | "metadata": {}, 152 | "outputs": [], 153 | "source": [ 154 | "kep12b_obj.pitch_mapper.pitch_map_args" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "metadata": {}, 160 | "source": [ 161 | "We can change all of these default arguments as well as adding any additional arguments allowed by the pitch mapping function.\n", 162 | "\n", 163 | "### Changing the center pitch" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": null, 169 | "metadata": {}, 170 | "outputs": [], 171 | "source": [ 172 | "kep12b_obj.pitch_mapper.pitch_map_args[\"center_pitch\"] = 880\n", 173 | "\n", 174 | "kep12b_obj.sonify()\n", 175 | "kep12b_obj.play()" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": null, 181 | "metadata": {}, 182 | "outputs": [], 183 | "source": [ 184 | "kep12b_obj.stop()\n", 185 | "\n", 186 | "kep12b_obj.pitch_mapper.pitch_map_args[\"center_pitch\"] = 440" 187 | ] 188 | }, 189 | { 190 | "cell_type": "markdown", 191 | "metadata": {}, 192 | "source": [ 193 | "### Changing the stretch to logarithmic" 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": null, 199 | "metadata": {}, 200 | "outputs": [], 201 | "source": [ 202 | "kep12b_obj.pitch_mapper.pitch_map_args[\"stretch\"] = \"log\"\n", 203 | "\n", 204 | "kep12b_obj.sonify()\n", 205 | "kep12b_obj.play()" 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": null, 211 | "metadata": {}, 212 | "outputs": [], 213 | "source": [ 214 | "kep12b_obj.stop()\n", 215 | "\n", 216 | "kep12b_obj.pitch_mapper.pitch_map_args[\"stretch\"] = \"linear\"" 217 | ] 218 | }, 219 | { 220 | "cell_type": "markdown", 221 | "metadata": {}, 222 | "source": [ 223 | "### Removing the outer 1% of data points" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "metadata": {}, 230 | "outputs": [], 231 | "source": [ 232 | "kep12b_obj.pitch_mapper.pitch_map_args[\"minmax_percent\"] = [0.5, 99.5]\n", 233 | "\n", 234 | "kep12b_obj.sonify()\n", 235 | "kep12b_obj.play()" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": null, 241 | "metadata": {}, 242 | "outputs": [], 243 | "source": [ 244 | "kep12b_obj.stop()\n", 245 | "\n", 246 | "del kep12b_obj.pitch_mapper.pitch_map_args[\"minmax_percent\"]" 247 | ] 248 | }, 249 | { 250 | "cell_type": "markdown", 251 | "metadata": {}, 252 | "source": [ 253 | "## Outputing an audio file\n", 254 | "\n", 255 | "Once the sonification sounds the way we like we can output the result to a wav file." 256 | ] 257 | }, 258 | { 259 | "cell_type": "code", 260 | "execution_count": null, 261 | "metadata": {}, 262 | "outputs": [], 263 | "source": [ 264 | "kep12b_obj.write(\"kepler_12b.wav\")" 265 | ] 266 | } 267 | ], 268 | "metadata": { 269 | "kernelspec": { 270 | "display_name": "Python 3", 271 | "language": "python", 272 | "name": "python3" 273 | }, 274 | "language_info": { 275 | "codemirror_mode": { 276 | "name": "ipython", 277 | "version": 3 278 | }, 279 | "file_extension": ".py", 280 | "mimetype": "text/x-python", 281 | "name": "python", 282 | "nbconvert_exporter": "python", 283 | "pygments_lexer": "ipython3", 284 | "version": "3.8.1" 285 | } 286 | }, 287 | "nbformat": 4, 288 | "nbformat_minor": 4 289 | } 290 | -------------------------------------------------------------------------------- /docs/astronify/How_To_Use_The_Simulator.rst: -------------------------------------------------------------------------------- 1 | .. doctest-skip-all 2 | 3 | Astronify: How to Use the Simulator 4 | =================================== 5 | 6 | .. code:: python 7 | 8 | from astropy.io import fits 9 | from astropy.table import Table 10 | from astronify import simulator, series 11 | 12 | 13 | .. parsed-literal:: 14 | 15 | 16 | WxPython is not found for the current python version. 17 | Pyo will use a minimal GUI toolkit written with Tkinter (if available). 18 | This toolkit has limited functionnalities and is no more 19 | maintained or updated. If you want to use all of pyo's 20 | GUI features, you should install WxPython, available here: 21 | http://www.wxpython.org/ 22 | 23 | 24 | 25 | Let’s start off with a simulated light curve that has almost no 26 | variation at all, to orient ourselves. We’ll use the simulator to create 27 | a “flat” light curve with almost zero noise (specified via the noise 28 | parameter). The length parameter specifies how long we want our light 29 | curve to be. Setting visualize to True means a plot will be made showing 30 | the fluxes (brightness) as a function of time. The yoffset parameter is 31 | used to specify the baseline brightness level. 32 | 33 | .. code:: python 34 | 35 | lc_data = simulator.simulated_lc("flat", lc_length=500, lc_noise=0.1, visualize=True, lc_yoffset=100.) 36 | 37 | 38 | 39 | .. image:: How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_2_0.png 40 | 41 | 42 | Now let’s create a sonified version of the data! 43 | 44 | .. code:: python 45 | 46 | soni_obj = series.SoniSeries(lc_data) 47 | soni_obj.sonify() 48 | 49 | And now let’s listen to the sound! 50 | 51 | .. code:: python 52 | 53 | soni_obj.play() 54 | 55 | 56 | .. parsed-literal:: 57 | 58 | Pyo warning: Portmidi warning: no midi device found! 59 | Portmidi closed. 60 | 61 | 62 | You can inject different amounts of noise. Let’s make a light curve with 63 | a lot of noise. In our first example, we used a value of 0.1 for the 64 | noise parameter. Now we’ll make a light curve with 1000 times the level 65 | of noise. 66 | 67 | .. code:: python 68 | 69 | lc_data = simulator.simulated_lc("flat", lc_length=500, lc_noise=100., visualize=True, lc_yoffset=100.) 70 | 71 | 72 | 73 | .. image:: How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_8_0.png 74 | 75 | 76 | Let’s sonify this light curve and listen to it. 77 | 78 | .. code:: python 79 | 80 | soni_obj = series.SoniSeries(lc_data) 81 | soni_obj.sonify() 82 | soni_obj.play() 83 | 84 | 85 | .. parsed-literal:: 86 | 87 | Pyo warning: Portmidi warning: no midi device found! 88 | Portmidi closed. 89 | 90 | 91 | You can also use the simulator to inject signals of different types. 92 | Let’s inject a signal expected from a transiting extrasolar planet. For 93 | now, we will use all the defaults for the parameters. 94 | 95 | .. code:: python 96 | 97 | lc_data = simulator.simulated_lc("transit", visualize=True, transit_width=10) 98 | 99 | 100 | 101 | .. image:: How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_12_0.png 102 | 103 | 104 | Let’s sonify this light curve and see if we can hear the drop in 105 | brightness caused by the planet blocking a very small part of the star’s 106 | surface when it passes in front of the star. 107 | 108 | .. code:: python 109 | 110 | soni_obj = series.SoniSeries(lc_data) 111 | soni_obj.pitch_mapper.pitch_map_args["zero_point"] = 95. 112 | soni_obj.sonify() 113 | soni_obj.play() 114 | 115 | 116 | .. parsed-literal:: 117 | 118 | Pyo warning: Portmidi warning: no midi device found! 119 | Portmidi closed. 120 | 121 | 122 | Now let’s explore the options when adding a transiting extrasolar planet 123 | signal. We will specify the depth of the transit (how much the planet 124 | blocks), the period (how long it takes the planet to make one full pass 125 | around the star), and the width (how long the planet takes to cross the 126 | star’s surface). We’ll also add some noise to the light curve, and 127 | finally ask for a slightly longer light curve so we can get more 128 | opportunities to have the planet cross in front of the star. 129 | 130 | .. code:: python 131 | 132 | lc_data = simulator.simulated_lc("transit", visualize=True, transit_depth=1.5, transit_period=145, 133 | transit_width=42, lc_noise=0.5, lc_length=750) 134 | 135 | 136 | 137 | .. image:: How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_16_0.png 138 | 139 | 140 | Let’s sonify this light curve! 141 | 142 | .. code:: python 143 | 144 | soni_obj = series.SoniSeries(lc_data) 145 | soni_obj.pitch_mapper.pitch_map_args["zero_point"] = 99. 146 | soni_obj.sonify() 147 | soni_obj.play() 148 | 149 | 150 | .. parsed-literal:: 151 | 152 | Pyo warning: Portmidi warning: no midi device found! 153 | Portmidi closed. 154 | 155 | 156 | You can add a stellar flare to the data. Let’s add one using the default 157 | parameters. Stellar flares are sudden increases in brightness over a 158 | short time. 159 | 160 | .. code:: python 161 | 162 | lc_data = simulator.simulated_lc("flare", visualize=True) 163 | 164 | 165 | 166 | .. image:: How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_20_0.png 167 | 168 | 169 | Time to sonify! 170 | 171 | .. code:: python 172 | 173 | soni_obj = series.SoniSeries(lc_data) 174 | soni_obj.sonify() 175 | soni_obj.play() 176 | 177 | 178 | .. parsed-literal:: 179 | 180 | Pyo warning: Portmidi warning: no midi device found! 181 | Portmidi closed. 182 | 183 | 184 | You can change the amplitude (height) of the flare, and the width. Let’s 185 | make one that is 10 times larger in amplitude and lasts 10 times as 186 | long. You can also specify the index that corresponds to the peak of the 187 | flare. 188 | 189 | .. code:: python 190 | 191 | lc_data = simulator.simulated_lc("flare", visualize=True, flare_amp=1000., flare_halfwidth=50, flare_time=100) 192 | 193 | 194 | 195 | .. image:: How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_24_0.png 196 | 197 | 198 | Let’s give it a listen. 199 | 200 | .. code:: python 201 | 202 | soni_obj = series.SoniSeries(lc_data) 203 | soni_obj.sonify() 204 | soni_obj.play() 205 | 206 | 207 | .. parsed-literal:: 208 | 209 | Pyo warning: Portmidi warning: no midi device found! 210 | Portmidi closed. 211 | 212 | 213 | You can also add sinusoidal signals to the data. Let’s create a light 214 | curve like this using the default parameters. 215 | 216 | .. code:: python 217 | 218 | lc_data = simulator.simulated_lc("sine", visualize=True) 219 | 220 | 221 | 222 | .. image:: How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_28_0.png 223 | 224 | 225 | Let’s sonify this light curve and listen to it. 226 | 227 | .. code:: python 228 | 229 | soni_obj = series.SoniSeries(lc_data) 230 | soni_obj.sonify() 231 | soni_obj.play() 232 | 233 | 234 | .. parsed-literal:: 235 | 236 | Pyo warning: Portmidi warning: no midi device found! 237 | Portmidi closed. 238 | 239 | 240 | Now let’s make a sinusoidal signal and use some of the parameter 241 | options. We will change the amplitude (how “big” the curve is from top 242 | to bottom), and the period (how long it takes to make one full cycle 243 | from peak to bottom and back to peak again). We’ll also add a little 244 | noise to the light curve, and ask for a light curve that lasts twice as 245 | long as the default. 246 | 247 | .. code:: python 248 | 249 | lc_data = simulator.simulated_lc("sine", visualize=True, sine_amp=1.5, sine_period=142, lc_noise=0.5, lc_length=1000) 250 | 251 | 252 | 253 | .. image:: How_To_Use_The_Simulator_files/How_To_Use_The_Simulator_32_0.png 254 | 255 | 256 | Let’s sonify this. 257 | 258 | .. code:: python 259 | 260 | soni_obj = series.SoniSeries(lc_data) 261 | soni_obj.sonify() 262 | soni_obj.play() 263 | 264 | 265 | .. parsed-literal:: 266 | 267 | Pyo warning: Portmidi warning: no midi device found! 268 | Portmidi closed. 269 | 270 | 271 | If you would like to save a light curve you create with the simulator to 272 | a file for use later, you can specify an output file name and a FITS 273 | file will be created that will store the parameters you used to create 274 | the light curve, as well as the times, fluxes, and fluxes without noise 275 | added. Let’s do that now using the call above (and we won’t ask for a 276 | plot this time). Note: because noise is added randomly, it won’t be 277 | exactly the same as the one above, but will be close enough. 278 | 279 | .. code:: python 280 | 281 | lc_data = simulator.simulated_lc("sine", lc_ofile="sim_lc_sine.fits", visualize=False, sine_amp=1.5, sine_period=142, 282 | lc_noise=0.5, lc_length=1000) 283 | -------------------------------------------------------------------------------- /astronify/simulator/sim_lc.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: sim_lc 3 | :synopsis: Creates simulated light curves with a variety of signals in them 4 | in a FITS format. The files are designed to be read by the Astronify 5 | software package to use when testing the sonification process. 6 | 7 | .. moduleauthor:: Scott W. Fleming 8 | """ 9 | 10 | from astropy.io import fits 11 | from astropy.table import Table 12 | import matplotlib.pyplot as plt 13 | import numpy as np 14 | import os 15 | from .sim_lc_config import SimLcConfig 16 | from .add_flare_signal import add_flare_signal 17 | from .add_lc_noise import add_lc_noise 18 | from .add_sine_signal import add_sine_signal 19 | from .add_transit_signal import add_transit_signal 20 | from .check_transit_params import check_transit_params 21 | from .check_flare_params import check_flare_params 22 | from .sim_lc_setup_args import sim_lc_setup_args 23 | 24 | 25 | __all__ = ["simulated_lc", "SimLcConfig"] 26 | 27 | 28 | def simulated_lc( 29 | lc_type, 30 | lc_ofile=SimLcConfig.sim_lc_ofile, 31 | lc_length=SimLcConfig.sim_lc_length, 32 | lc_noise=SimLcConfig.sim_lc_noise, 33 | visualize=SimLcConfig.sim_lc_visualize, 34 | lc_yoffset=SimLcConfig.sim_lc_yoffset, 35 | transit_depth=SimLcConfig.sim_lc_transit_depth, 36 | transit_period=SimLcConfig.sim_lc_transit_period, 37 | transit_start=SimLcConfig.sim_lc_transit_start, 38 | transit_width=SimLcConfig.sim_lc_transit_width, 39 | sine_amp=SimLcConfig.sim_lc_sine_amp, 40 | sine_period=SimLcConfig.sim_lc_sine_period, 41 | flare_time=SimLcConfig.sim_lc_flare_time, 42 | flare_amp=SimLcConfig.sim_lc_flare_amp, 43 | flare_halfwidth=SimLcConfig.sim_lc_flare_halfwidth, 44 | ): 45 | """ 46 | Create light curve with specified parameters as a `~astropy.table.Table`, 47 | and optionally writes a FITS file with the same information. 48 | 49 | All parameters default to the configuration values. 50 | 51 | Parameters 52 | ---------- 53 | lc_type : str 54 | The type of light curve to make. Valid options are 'flat', 'transit', 55 | 'sine', and 'flare'. 56 | 57 | lc_ofile : str or None 58 | Optional. Name of output FITS file. If set to None, 59 | no file will be saved to disk. 60 | 61 | lc_length : int 62 | Optional. Length of the light curve (i.e. the number of flux values). 63 | 64 | lc_noise : float 65 | Optional. Standard deviation of normal distribution to draw from when 66 | adding noise, a value of zero means no noise is added. 67 | 68 | visualize : bool 69 | Optional. If True, plot the light curve being made to the screen. 70 | 71 | lc_yoffset : float 72 | Optional. Baseline flux level (unitless). 73 | 74 | transit_depth: float 75 | Depth of transit, as a percent (e.g., 10.0 = 10%.) 76 | 77 | transit_period : int 78 | Period of transit (number of fluxes/bins between the start of each event.) 79 | (Only relevant for transit type light curve). 80 | 81 | transit_start : int 82 | Start index of transit (the index of the flux/bin to use as the start of the first transit 83 | event.) 84 | (Only relevant for transit type light curve). 85 | 86 | transit_width : int 87 | Width of transit (number of fluxes/bins between the start and end of each event.) 88 | 89 | sine_amp : float 90 | Amplitude of the sinusoidal signal to add. 91 | 92 | sine_period : float 93 | Period of the sinusoidal signal to add. 94 | 95 | flare_time: int 96 | Time corresponding to the maximum flare flux. 97 | 98 | flare_amp : float 99 | The peak (maximum flux) of the flare. 100 | 101 | flare_halfwidth : float 102 | The flare half-width (measured in indices) that 103 | corresponds to "t_1/2" in the Davenport et al. flare template. 104 | 105 | Returns 106 | ------- 107 | response : `~astropy.table.Table` 108 | The time and flux columns. 109 | """ 110 | 111 | # Generate baseline light curve fluxes. 112 | fluxes = np.full(lc_length, lc_yoffset) 113 | 114 | # We don't need real times for the simulation, it's just an array of indexes. 115 | times = np.arange(fluxes.size) 116 | 117 | # Apply signal of choice if needed. 118 | if lc_type == "flare": 119 | check_flare_params(fluxes.size, flare_time, flare_amp) 120 | fluxes = add_flare_signal(fluxes, flare_time, flare_amp, flare_halfwidth) 121 | elif lc_type == "sine": 122 | fluxes = add_sine_signal(times, fluxes, sine_amp, sine_period) 123 | elif lc_type == "transit": 124 | check_transit_params(fluxes.size, transit_period, transit_start, transit_width) 125 | fluxes = add_transit_signal( 126 | fluxes, transit_depth, transit_period, transit_start, transit_width 127 | ) 128 | 129 | # Add noise based on standard deviation. 130 | fluxes_with_noise = add_lc_noise(fluxes, lc_noise) 131 | 132 | # Visualize the light curve, if desired. 133 | if visualize: 134 | _, ax1 = plt.subplots(1) 135 | ax1.plot(times, fluxes_with_noise, "bo") 136 | plt.show() 137 | 138 | if lc_ofile: 139 | # Save light curve as FITS file. 140 | hdr = fits.Header() 141 | # Add input arguments as keyword headers here. 142 | hdr.append(("LCTYPE", lc_type, "Type of signal.")) 143 | hdr.append(("LCLENGTH", lc_length, "Number of fluxes.")) 144 | hdr.append(("LCYOFF", lc_yoffset, "Baseline flux value (unitless).")) 145 | hdr.append( 146 | ("LCNOISE", lc_noise, "Std. dev. of normal dist. used to apply noise.") 147 | ) 148 | # Record the flare parameters used if adding a flare. 149 | if lc_type == "flare": 150 | hdr.append( 151 | ( 152 | "FLARETIM", 153 | flare_time, 154 | "Index corresponding to the peak of the flare.", 155 | ) 156 | ) 157 | hdr.append(("FLAREAMP", flare_amp, "Amplitude of the flare.")) 158 | hdr.append( 159 | ("FLAREWID", flare_halfwidth, "Flare half-width (number of indices).") 160 | ) 161 | # Record the sinusoidal parameters if adding a sinusoid. 162 | if lc_type == "sine": 163 | hdr.append(("SINEAMP", sine_amp, "Amplitude of sine.")) 164 | hdr.append(("SINEPER", sine_amp, "Period of sine.")) 165 | # Record the transit parameters if adding a transit. 166 | if lc_type == "transit": 167 | hdr.append(("TRANDEP", transit_depth, "Depth of transit.")) 168 | hdr.append(("TRANPER", transit_period, "Period of planet.")) 169 | hdr.append(("TRANSTAR", transit_start, "Start index of transit.")) 170 | hdr.append(("TRANWID", transit_width, "Width of transit.")) 171 | # This builds the primary header, no data, just keywords. 172 | primary_hdu = fits.PrimaryHDU(header=hdr) 173 | # This sets up the binary table and creates the first extension header. 174 | col1 = fits.Column(name="time", array=times, format="D") 175 | col2 = fits.Column(name="flux", array=fluxes_with_noise, format="D") 176 | col3 = fits.Column(name="flux_pure", array=fluxes, format="D") 177 | hdu1 = fits.BinTableHDU.from_columns([col1, col2, col3]) 178 | # If the output directory doesn't exist, create it. 179 | if not os.path.isdir(os.path.abspath(os.path.dirname(lc_ofile))): 180 | os.makedirs(os.path.dirname(os.path.abspath(lc_ofile))) 181 | # This combines the primary HDU and first extension header together and 182 | # writes to the output file. 183 | hdu_list = fits.HDUList([primary_hdu, hdu1]) 184 | hdu_list.writeto(lc_ofile, overwrite=True, checksum=True) 185 | 186 | # Return the times and fluxes as an astropy Table so it can be directly 187 | # used later in a script. 188 | return Table( 189 | [times, fluxes_with_noise, fluxes], names=("time", "flux", "flux_pure") 190 | ) 191 | 192 | 193 | if __name__ == "__main__": 194 | # Get command-line arguments. 195 | INPUT_ARGS = sim_lc_setup_args().parse_args() 196 | 197 | simulated_lc( 198 | INPUT_ARGS.lc_type, 199 | INPUT_ARGS.lc_ofile, 200 | INPUT_ARGS.lc_length, 201 | INPUT_ARGS.lc_noise, 202 | INPUT_ARGS.visualize, 203 | INPUT_ARGS.lc_yoffset, 204 | INPUT_ARGS.transit_depth, 205 | INPUT_ARGS.transit_period, 206 | INPUT_ARGS.transit_start, 207 | INPUT_ARGS.transit_width, 208 | INPUT_ARGS.sine_amp, 209 | INPUT_ARGS.sine_period, 210 | INPUT_ARGS.flare_time, 211 | INPUT_ARGS.flare_amp, 212 | INPUT_ARGS.flare_halfwidth, 213 | ) 214 | -------------------------------------------------------------------------------- /docs/_static/ASTRONIFY.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 28 | 29 | 30 | 48 | 49 | 50 | 55 | 68 | 72 | 79 | 84 | 86 | 90 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /docs/notebooks/How_To_Use_The_Simulator.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from astropy.io import fits\n", 10 | "from astropy.table import Table\n", 11 | "from astronify import simulator, series" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "Let's start off with a simulated light curve that has almost no variation at all, to orient ourselves. We'll use the simulator to create a \"flat\" light curve with almost zero noise (specified via the noise parameter). The length parameter specifies how long we want our light curve to be. Setting visualize to True means a plot will be made showing the fluxes (brightness) as a function of time. The yoffset parameter is used to specify the baseline brightness level." 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "lc_data = simulator.simulated_lc(\"flat\", lc_length=500, lc_noise=0.1, visualize=True, lc_yoffset=100.)" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": {}, 33 | "source": [ 34 | "Now let's create a sonified version of the data!" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "soni_obj = series.SoniSeries(lc_data)\n", 44 | "soni_obj.sonify()" 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "And now let's listen to the sound!" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "soni_obj.play()" 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "metadata": {}, 66 | "source": [ 67 | "You can inject different amounts of noise. Let's make a light curve with a lot of noise. In our first example, we used a value of 0.1 for the noise parameter. Now we'll make a light curve with 1000 times the level of noise." 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": null, 73 | "metadata": {}, 74 | "outputs": [], 75 | "source": [ 76 | "lc_data = simulator.simulated_lc(\"flat\", lc_length=500, lc_noise=100., visualize=True, lc_yoffset=100.)" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "Let's sonify this light curve and listen to it." 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "soni_obj = series.SoniSeries(lc_data)\n", 93 | "soni_obj.sonify()\n", 94 | "soni_obj.play()" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "You can also use the simulator to inject signals of different types. Let's inject a signal expected from a transiting extrasolar planet. For now, we will use all the defaults for the parameters." 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": {}, 108 | "outputs": [], 109 | "source": [ 110 | "lc_data = simulator.simulated_lc(\"transit\", visualize=True, transit_width=10)" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "Let's sonify this light curve and see if we can hear the drop in brightness caused by the planet blocking a very small part of the star's surface when it passes in front of the star." 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": null, 123 | "metadata": {}, 124 | "outputs": [], 125 | "source": [ 126 | "soni_obj = series.SoniSeries(lc_data)\n", 127 | "soni_obj.pitch_mapper.pitch_map_args[\"zero_point\"] = 95.\n", 128 | "soni_obj.sonify()\n", 129 | "soni_obj.play()" 130 | ] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "metadata": {}, 135 | "source": [ 136 | "Now let's explore the options when adding a transiting extrasolar planet signal. We will specify the depth of the transit (how much the planet blocks), the period (how long it takes the planet to make one full pass around the star), and the width (how long the planet takes to cross the star's surface). We'll also add some noise to the light curve, and finally ask for a slightly longer light curve so we can get more opportunities to have the planet cross in front of the star." 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": null, 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [ 145 | "lc_data = simulator.simulated_lc(\"transit\", visualize=True, transit_depth=1.5, transit_period=145,\n", 146 | " transit_width=42, lc_noise=0.5, lc_length=750)" 147 | ] 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "metadata": {}, 152 | "source": [ 153 | "Let's sonify this light curve!" 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": null, 159 | "metadata": {}, 160 | "outputs": [], 161 | "source": [ 162 | "soni_obj = series.SoniSeries(lc_data)\n", 163 | "soni_obj.pitch_mapper.pitch_map_args[\"zero_point\"] = 99.\n", 164 | "soni_obj.sonify()\n", 165 | "soni_obj.play()" 166 | ] 167 | }, 168 | { 169 | "cell_type": "markdown", 170 | "metadata": {}, 171 | "source": [ 172 | "You can add a stellar flare to the data. Let's add one using the default parameters. Stellar flares are sudden increases in brightness over a short time." 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": null, 178 | "metadata": {}, 179 | "outputs": [], 180 | "source": [ 181 | "lc_data = simulator.simulated_lc(\"flare\", visualize=True)" 182 | ] 183 | }, 184 | { 185 | "cell_type": "markdown", 186 | "metadata": {}, 187 | "source": [ 188 | "Time to sonify!" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": null, 194 | "metadata": {}, 195 | "outputs": [], 196 | "source": [ 197 | "soni_obj = series.SoniSeries(lc_data)\n", 198 | "soni_obj.sonify()\n", 199 | "soni_obj.play()" 200 | ] 201 | }, 202 | { 203 | "cell_type": "markdown", 204 | "metadata": {}, 205 | "source": [ 206 | "You can change the amplitude (height) of the flare, and the width. Let's make one that is 10 times larger in amplitude and lasts 10 times as long. You can also specify the index that corresponds to the peak of the flare." 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": null, 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "lc_data = simulator.simulated_lc(\"flare\", visualize=True, flare_amp=1000., flare_halfwidth=50, flare_time=100)" 216 | ] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "metadata": {}, 221 | "source": [ 222 | "Let's give it a listen." 223 | ] 224 | }, 225 | { 226 | "cell_type": "code", 227 | "execution_count": null, 228 | "metadata": {}, 229 | "outputs": [], 230 | "source": [ 231 | "soni_obj = series.SoniSeries(lc_data)\n", 232 | "soni_obj.sonify()\n", 233 | "soni_obj.play()" 234 | ] 235 | }, 236 | { 237 | "cell_type": "markdown", 238 | "metadata": {}, 239 | "source": [ 240 | "You can also add sinusoidal signals to the data. Let's create a light curve like this using the default parameters." 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": null, 246 | "metadata": {}, 247 | "outputs": [], 248 | "source": [ 249 | "lc_data = simulator.simulated_lc(\"sine\", visualize=True)" 250 | ] 251 | }, 252 | { 253 | "cell_type": "markdown", 254 | "metadata": {}, 255 | "source": [ 256 | "Let's sonify this light curve and listen to it." 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": null, 262 | "metadata": {}, 263 | "outputs": [], 264 | "source": [ 265 | "soni_obj = series.SoniSeries(lc_data)\n", 266 | "soni_obj.sonify()\n", 267 | "soni_obj.play()" 268 | ] 269 | }, 270 | { 271 | "cell_type": "markdown", 272 | "metadata": {}, 273 | "source": [ 274 | "Now let's make a sinusoidal signal and use some of the parameter options. We will change the amplitude (how \"big\" the curve is from top to bottom), and the period (how long it takes to make one full cycle from peak to bottom and back to peak again). We'll also add a little noise to the light curve, and ask for a light curve that lasts twice as long as the default." 275 | ] 276 | }, 277 | { 278 | "cell_type": "code", 279 | "execution_count": null, 280 | "metadata": {}, 281 | "outputs": [], 282 | "source": [ 283 | "lc_data = simulator.simulated_lc(\"sine\", visualize=True, sine_amp=1.5, sine_period=142, lc_noise=0.5, lc_length=1000)" 284 | ] 285 | }, 286 | { 287 | "cell_type": "markdown", 288 | "metadata": {}, 289 | "source": [ 290 | "Let's sonify this." 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": null, 296 | "metadata": {}, 297 | "outputs": [], 298 | "source": [ 299 | "soni_obj = series.SoniSeries(lc_data)\n", 300 | "soni_obj.sonify()\n", 301 | "soni_obj.play()" 302 | ] 303 | }, 304 | { 305 | "cell_type": "markdown", 306 | "metadata": {}, 307 | "source": [ 308 | "If you would like to save a light curve you create with the simulator to a file for use later, you can specify an output file name and a FITS file will be created that will store the parameters you used to create the light curve, as well as the times, fluxes, and fluxes without noise added. Let's do that now using the call above (and we won't ask for a plot this time). Note: because noise is added randomly, it won't be exactly the same as the one above, but will be close enough." 309 | ] 310 | }, 311 | { 312 | "cell_type": "code", 313 | "execution_count": null, 314 | "metadata": {}, 315 | "outputs": [], 316 | "source": [ 317 | "lc_data = simulator.simulated_lc(\"sine\", lc_ofile=\"sim_lc_sine.fits\", visualize=False, sine_amp=1.5, sine_period=142,\n", 318 | " lc_noise=0.5, lc_length=1000)" 319 | ] 320 | } 321 | ], 322 | "metadata": { 323 | "kernelspec": { 324 | "display_name": "Python 3", 325 | "language": "python", 326 | "name": "python3" 327 | }, 328 | "language_info": { 329 | "codemirror_mode": { 330 | "name": "ipython", 331 | "version": 3 332 | }, 333 | "file_extension": ".py", 334 | "mimetype": "text/x-python", 335 | "name": "python", 336 | "nbconvert_exporter": "python", 337 | "pygments_lexer": "ipython3", 338 | "version": "3.8.1" 339 | } 340 | }, 341 | "nbformat": 4, 342 | "nbformat_minor": 2 343 | } 344 | --------------------------------------------------------------------------------