├── Doc
├── requirements.txt
├── _static
│ ├── lemon-sidebar.png
│ └── HAT-P-16b-2014-10-31.jpg
├── user
│ ├── images
│ │ └── gui
│ │ │ ├── finding-chart.png
│ │ │ ├── main-window.png
│ │ │ ├── star-details.png
│ │ │ └── export-to-file.png
│ └── install.rst
├── _themes
│ ├── kr
│ │ ├── theme.conf
│ │ ├── relations.html
│ │ ├── layout.html
│ │ └── static
│ │ │ └── small_flask.css
│ ├── kr_small
│ │ ├── theme.conf
│ │ ├── layout.html
│ │ └── static
│ │ │ └── flasky.css_t
│ ├── README.rst
│ ├── LICENSE
│ └── flask_theme_support.py
├── commands
│ ├── index.rst
│ └── rename.sh
├── _templates
│ ├── sidebarlogo.html
│ └── sidebarintro.html
├── index.rst
├── make.bat
└── Makefile
├── juicer
├── __init__.py
├── gui
│ ├── img
│ │ ├── lemon.png
│ │ └── compass.png
│ ├── overview.glade
│ ├── loading-dialog.glade
│ ├── finding-chart-dialog.glade
│ ├── about.glade
│ └── snr-threshold-dialog.glade
├── main.py
├── simbad.py
├── glade.py
└── config.py
├── util
├── test
│ ├── __init__.py
│ ├── test_queue.py
│ └── test_log.py
├── __init__.py
├── memoize.py
├── context.py
├── gtkutil.py
├── queue.py
├── display.py
├── log.py
└── coords.py
├── Misc
├── lemon-icon.png
├── lemon-icon_200px.png
├── ACKS
└── CHANGES
├── pre-requirements.txt
├── sextractor
├── sextractor.conv
├── sextractor.nnw
├── sextractor.param
└── sextractor.sex
├── version.py
├── .gitignore
├── __init__.py
├── .github
├── workflows
│ └── black.yml
└── stale.yml
├── test
├── __init__.py
├── integration
│ ├── wasp10b-download.sh
│ ├── WASP10b-coordinates.txt
│ └── README.md
├── test_data
│ ├── filters
│ │ ├── Unknown
│ │ ├── Cousins
│ │ ├── Harris
│ │ ├── Halpha
│ │ ├── 2MASS
│ │ ├── Gunn
│ │ └── SDSS
│ └── sextractor_noasciihead.cat
├── test_customparser.py
└── dss_images.py
├── Pipfile
├── Makefile
├── requirements.txt
├── style.py
├── README.rst
├── run_tests.py
├── .travis.yml
├── ci
└── travis-setup.sh
├── defaults.py
├── lemon
├── export.py
├── keywords.py
├── setup.py
├── json_parse.py
├── lemon-completion.sh
├── check_versions.py
└── customparser.py
/Doc/requirements.txt:
--------------------------------------------------------------------------------
1 | sphinxcontrib-images>=0.5.0
2 |
--------------------------------------------------------------------------------
/juicer/__init__.py:
--------------------------------------------------------------------------------
1 | # Dummy file to make this directory a package.
2 |
--------------------------------------------------------------------------------
/util/test/__init__.py:
--------------------------------------------------------------------------------
1 | # Empty __init__.py to make this directory a package.
2 |
--------------------------------------------------------------------------------
/Misc/lemon-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vterron/lemon/HEAD/Misc/lemon-icon.png
--------------------------------------------------------------------------------
/juicer/gui/img/lemon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vterron/lemon/HEAD/juicer/gui/img/lemon.png
--------------------------------------------------------------------------------
/Misc/lemon-icon_200px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vterron/lemon/HEAD/Misc/lemon-icon_200px.png
--------------------------------------------------------------------------------
/juicer/gui/img/compass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vterron/lemon/HEAD/juicer/gui/img/compass.png
--------------------------------------------------------------------------------
/Doc/_static/lemon-sidebar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vterron/lemon/HEAD/Doc/_static/lemon-sidebar.png
--------------------------------------------------------------------------------
/Doc/_static/HAT-P-16b-2014-10-31.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vterron/lemon/HEAD/Doc/_static/HAT-P-16b-2014-10-31.jpg
--------------------------------------------------------------------------------
/Doc/user/images/gui/finding-chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vterron/lemon/HEAD/Doc/user/images/gui/finding-chart.png
--------------------------------------------------------------------------------
/Doc/user/images/gui/main-window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vterron/lemon/HEAD/Doc/user/images/gui/main-window.png
--------------------------------------------------------------------------------
/Doc/user/images/gui/star-details.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vterron/lemon/HEAD/Doc/user/images/gui/star-details.png
--------------------------------------------------------------------------------
/pre-requirements.txt:
--------------------------------------------------------------------------------
1 | astropy>=0.2.4 # needed by APLpy / montage-wrapper
2 | d2to1>=0.2.10 # needed by PyRAF
3 |
4 |
--------------------------------------------------------------------------------
/Doc/user/images/gui/export-to-file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vterron/lemon/HEAD/Doc/user/images/gui/export-to-file.png
--------------------------------------------------------------------------------
/sextractor/sextractor.conv:
--------------------------------------------------------------------------------
1 | CONV NORM
2 | # 3x3 ``all-ground'' convolution mask with FWHM = 2 pixels.
3 | 1 2 1
4 | 2 4 2
5 | 1 2 1
6 |
--------------------------------------------------------------------------------
/version.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | __version_info__ = (0, 3, 0)
4 | __version__ = ".".join(str(x) for x in __version_info__)
5 |
--------------------------------------------------------------------------------
/Doc/_themes/kr/theme.conf:
--------------------------------------------------------------------------------
1 | [theme]
2 | inherit = basic
3 | stylesheet = flasky.css
4 | pygments_style = flask_theme_support.FlaskyStyle
5 |
6 | [options]
7 | touch_icon =
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.py[cod]
2 | *~
3 |
4 | # PyRAF cache directory
5 | pyraf
6 |
7 | # User login file for IRAF
8 | login.cl
9 |
10 | # IRAF user parameter directory
11 | uparm
12 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 | # encoding:UTF-8
3 |
4 | from version import __version_info__, __version__
5 |
6 | __author__ = "Víctor Terrón"
7 | __email__ = "`echo vt2rron1iaa32s | tr 132 @.e`"
8 | __license__ = "GNU GPL v3"
9 |
--------------------------------------------------------------------------------
/util/__init__.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | from .context import *
4 | from .coords import *
5 | from .display import *
6 | from .gtkutil import *
7 | from .io import *
8 | from .log import *
9 | from .memoize import *
10 | from .queue import *
11 |
--------------------------------------------------------------------------------
/Doc/_themes/kr_small/theme.conf:
--------------------------------------------------------------------------------
1 | [theme]
2 | inherit = basic
3 | stylesheet = flasky.css
4 | nosidebar = true
5 | pygments_style = flask_theme_support.FlaskyStyle
6 |
7 | [options]
8 | index_logo = ''
9 | index_logo_height = 120px
10 | github_fork = ''
11 |
--------------------------------------------------------------------------------
/.github/workflows/black.yml:
--------------------------------------------------------------------------------
1 | name: Enforce Black formatting
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | lint:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v2
10 | - uses: actions/setup-python@v2
11 | - uses: psf/black@stable
12 |
--------------------------------------------------------------------------------
/test/__init__.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | import sys
4 |
5 | # Several convenient features of unittest, such as assertRaises as a context
6 | # manager and test skipping, are not available until Python 2.7. In previous
7 | # versions, use unittest2, a backport of the new features.
8 |
9 | if sys.version_info < (2, 7):
10 | import unittest2 as unittest
11 | else:
12 | import unittest
13 |
--------------------------------------------------------------------------------
/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | name = "pypi"
3 | url = "https://pypi.org/simple"
4 | verify_ssl = true
5 |
6 | [dev-packages]
7 |
8 | [packages]
9 | APLpy = ">=0.9.13"
10 | astropy = ">=1.0.2"
11 | d2to1 = "==0.2.11"
12 | matplotlib = "==1.4.1"
13 | mock = "==1.0.1"
14 | montage-wrapper = ">=0.9.8"
15 | pyfits = "==3.2"
16 | pyraf = ">=2.1.7"
17 | requests = "==2.20.0"
18 | scipy = "==0.14.0"
19 | subprocess32 = "==3.2.6"
20 | uncertainties = "==2.4.6.1"
21 | unittest2 = "==0.5.1"
22 | numpy = ">=1.10.1"
23 | pyside = "*"
24 |
25 | [requires]
26 | python_version = "2.7"
27 |
--------------------------------------------------------------------------------
/Doc/_themes/kr/relations.html:
--------------------------------------------------------------------------------
1 |
Related Topics
2 |
20 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Inspired by https://pipenv.pypa.io/en/latest/advanced/#travis-ci.
2 |
3 | .PHONY: all init test
4 |
5 | all: init test
6 |
7 | init:
8 | # Use --site-packages + PIP_IGNORE_INSTALLED "to make sure that any
9 | # pip-installable dependencies actually are installed into the virtual
10 | # environment, with the system site-packages then being used only to
11 | # resolve imports that aren't covered by the Python level dependency
12 | # graph at all" (in our case, that's PyGTK). See
13 | # https://github.com/pypa/pipenv/issues/748#issue-261233053
14 | pipenv --two --site-packages && PIP_IGNORE_INSTALLED=1 pipenv install
15 |
16 | test:
17 | pipenv run run_tests.py
18 |
--------------------------------------------------------------------------------
/Doc/commands/index.rst:
--------------------------------------------------------------------------------
1 | .. _commands-index:
2 |
3 | ##############
4 | LEMON commands
5 | ##############
6 |
7 | The LEMON pipeline consists of ten commands, implementing different atronomical
8 | data reduction and analysis steps, that are usually run sequentially, although
9 | depending on your needs only a specific subset of them may be used. You may,
10 | for example, be interested in just aligning your FITS images or update their
11 | headers with astrometric information. In this sense, and following the Unix
12 | tools philosophy (*write programs that do one thing and do it well*), LEMON
13 | can be viewed as a set of tasks that *may* be used as a pipeline.
14 |
15 | .. toctree::
16 | :numbered:
17 |
18 | import.rst
19 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | # The reason why we need two different requirements files, and also
2 | # why numpy>=1.7.1 is not included in any of them, is that some of
3 | # the packages depend on another package in order to even run their
4 | # setup.py, without mattering that the dependency is also going to be
5 | # installed. As explained by the pip developers, these are broken
6 | # setup.py files, not pip's fault. Serial installation is the only
7 | # answer here [https://github.com/pypa/pip/issues/25]
8 |
9 | absl-py>=0.9.0
10 | APLpy>=0.9.9
11 | scipy>=0.12.0
12 | matplotlib>=1.2.1
13 | mock>=1.0.1
14 | prettytable>=0.7.2
15 | pyfits>=3.2
16 | pyraf>=2.1.1
17 | uncertainties>=2.4.1
18 | unittest2>=0.5.1
19 | montage-wrapper>=0.9.7
20 | requests>=2.0.1
21 | subprocess32>=3.2.6
22 |
--------------------------------------------------------------------------------
/Doc/_themes/kr_small/layout.html:
--------------------------------------------------------------------------------
1 | {% extends "basic/layout.html" %}
2 | {% block header %}
3 | {{ super() }}
4 | {% if pagename == 'index' %}
5 |
6 | {% endif %}
7 | {% endblock %}
8 | {% block footer %}
9 | {% if pagename == 'index' %}
10 |
11 | {% endif %}
12 | {% endblock %}
13 | {# do not display relbars #}
14 | {% block relbar1 %}{% endblock %}
15 | {% block relbar2 %}
16 | {% if theme_github_fork %}
17 |
19 | {% endif %}
20 | {% endblock %}
21 | {% block sidebar1 %}{% endblock %}
22 | {% block sidebar2 %}{% endblock %}
23 |
--------------------------------------------------------------------------------
/Doc/_templates/sidebarlogo.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
13 |
14 |
15 |
16 | LEMON is an astronomical pipeline for automated time-series reduction and analysis. Input your FITS images and obtain the light curve of each object — it's almost as simple as that.
17 |
18 |
--------------------------------------------------------------------------------
/Doc/_themes/README.rst:
--------------------------------------------------------------------------------
1 | krTheme Sphinx Style
2 | ====================
3 |
4 | This repository contains sphinx styles Kenneth Reitz uses in most of
5 | his projects. It is a drivative of Mitsuhiko's themes for Flask and Flask related
6 | projects. To use this style in your Sphinx documentation, follow
7 | this guide:
8 |
9 | 1. put this folder as _themes into your docs folder. Alternatively
10 | you can also use git submodules to check out the contents there.
11 |
12 | 2. add this to your conf.py: ::
13 |
14 | sys.path.append(os.path.abspath('_themes'))
15 | html_theme_path = ['_themes']
16 | html_theme = 'kr'
17 |
18 | The following themes exist:
19 |
20 | **kr**
21 | the standard flask documentation theme for large projects
22 |
23 | **kr_small**
24 | small one-page theme. Intended to be used by very small addon libraries.
25 |
26 |
--------------------------------------------------------------------------------
/Doc/_themes/kr/layout.html:
--------------------------------------------------------------------------------
1 | {%- extends "basic/layout.html" %}
2 | {%- block extrahead %}
3 | {{ super() }}
4 | {% if theme_touch_icon %}
5 |
6 | {% endif %}
7 |
9 |
10 | {% endblock %}
11 | {%- block relbar2 %}{% endblock %}
12 | {%- block footer %}
13 |
16 |
17 |
18 |
19 |
20 |
21 | {%- endblock %}
22 |
--------------------------------------------------------------------------------
/Doc/_templates/sidebarintro.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
13 |
14 |
15 |
16 | LEMON is an astronomical pipeline for automated time-series reduction and analysis. Input your FITS images and obtain the light curve of each object — it's almost as simple as that.
17 |
18 |
19 |
20 | Useful Links
21 |
25 |
--------------------------------------------------------------------------------
/juicer/gui/overview.glade:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | True
7 | vertical
8 |
9 |
10 | True
11 | True
12 | automatic
13 | automatic
14 |
15 |
16 | True
17 | True
18 |
19 |
20 |
21 |
22 | 0
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/juicer/main.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | # Copyright (c) 2012 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify it
9 | # under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | import app
22 |
23 |
24 | def main(**kwargs):
25 | juicer = app.LEMONJuicerGUI(**kwargs)
26 | juicer.run()
27 |
28 |
29 | if __name__ == "__main__":
30 | main()
31 |
--------------------------------------------------------------------------------
/test/integration/wasp10b-download.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | set -Eeuo pipefail
4 |
5 | # Takes the path to a directory as argument and downloads the WASP10b test data
6 | # if any of the expected files is missing. In this manner, the .xz file is only
7 | # downloaded from the remote server if necessary.
8 |
9 | # Name of the file stored in $WASP10_URL.
10 | XZ_FILE="WASP10b-2011-08-23.tar.xz"
11 |
12 | pushd $1
13 |
14 | if sha1sum -c SHA1SUMS; then
15 | # Cache archives expire after 45 days for repositories on https://travis-ci.com.
16 | # [https://docs.travis-ci.com/user/caching/#caches-expiration] Write the current
17 | # date and time to a file so that (because of this modification inside the cache
18 | # directory) the expiration delay is reset.
19 | date > last-access.txt
20 |
21 | popd
22 | exit 0
23 | fi
24 |
25 | # Don't use rm *; explicitly list the files to remove.
26 | rm -vrf *.fits SHA1SUMS $XZ_FILE
27 | curl --remote-header-name --remote-name $WASP10_URL
28 | tar -xvf $XZ_FILE
29 | rm -fv $XZ_FILE
30 | chmod a-w -v *.fits
31 |
32 | popd
33 |
--------------------------------------------------------------------------------
/style.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | # Copyright (c) 2012 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | # Used to change the format with which the logging module displays messages
22 | LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
23 |
24 | # Prefix that will be added to each line printed to the standard output
25 | prefix = ">> "
26 |
27 | # The error message to be printed if the execution is aborted.w
28 | error_exit_message = "%sExecution aborted." % prefix
29 |
--------------------------------------------------------------------------------
/test/test_data/filters/Unknown:
--------------------------------------------------------------------------------
1 | # A series of two-element tuples, one per line, containing two strings.
2 | # The first element must be the name of the photometric filter whose
3 | # system is unknown, while the second is the letter that the Passband
4 | # class must identify when the former is parsed.
5 |
6 | 'wide', 'WIDE'
7 | 'B', 'B'
8 | 'Z', 'Z'
9 | 'G', 'G'
10 | 'I', 'I'
11 | 'H', 'H'
12 | 'K', 'K'
13 | 'J', 'J'
14 | 'M', 'M'
15 | 'L', 'L'
16 | 'N', 'N'
17 | 'KS', 'KS'
18 | 'R', 'R'
19 | 'U', 'U'
20 | 'W', 'W'
21 | 'V', 'V'
22 | 'Y', 'Y'
23 | 'narrow', 'NARROW'
24 |
25 | # Somewhat convoluted test cases
26 |
27 | "__--_wiDE___", 'WIDE'
28 | "wide---", 'WIDE'
29 | "_---b- -", 'B'
30 | "___b _-", 'B'
31 | "Z-", 'Z'
32 | "Z-__", 'Z'
33 | "__G", 'G'
34 | "g-", 'G'
35 | "--_-I", 'I'
36 | " i-", 'I'
37 | "h_---", 'H'
38 | "H -", 'H'
39 | "-K ", 'K'
40 | "-__--K--_ ", 'K'
41 | " j", 'J'
42 | " J", 'J'
43 | "_-__-M__", 'M'
44 | "_-m-", 'M'
45 | " l", 'L'
46 | " _L _-", 'L'
47 | " -__N-", 'N'
48 | " -N--__", 'N'
49 | "kS", 'KS'
50 | "KS-", 'KS'
51 | "_r_-_", 'R'
52 | "- ___R-_", 'R'
53 | " _U", 'U'
54 | " -_U ", 'U'
55 | "-__W-", 'W'
56 | "- _W--", 'W'
57 | "_-V -", 'V'
58 | " v_ _", 'V'
59 | "-_Y_", 'Y'
60 | "Y_", 'Y'
61 | "NARRow_-_", 'NARROW'
62 | "-_narRoW", 'NARROW'
63 |
--------------------------------------------------------------------------------
/util/memoize.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | # Copyright (c) 2019 Victor Terron. All rights reserved.
4 | #
5 | # This file is part of LEMON.
6 | #
7 | # LEMON is free software: you can redistribute it and/or modify it
8 | # under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation, either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program. If not, see .
19 |
20 | import functools
21 |
22 |
23 | def memoize(f):
24 | """Minimalistic memoization decorator (*args / **kwargs)
25 | Based on: http://code.activestate.com/recipes/577219/"""
26 |
27 | cache = {}
28 |
29 | @functools.wraps(f)
30 | def memf(*args, **kwargs):
31 | fkwargs = frozenset(kwargs.iteritems())
32 | if (args, fkwargs) not in cache:
33 | cache[args, fkwargs] = f(*args, **kwargs)
34 | return cache[args, fkwargs]
35 |
36 | return memf
37 |
--------------------------------------------------------------------------------
/Doc/commands/rename.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | INITIAL_NUMBER=$1; # will be zero if no argument is provided
4 | PREFIX="ferM_";
5 | PREFIX_LENGTH=${#PREFIX};
6 | EXTENSION=".fits";
7 | NUMBER_OF_DIGITS=4;
8 |
9 |
10 | echo "Initial counting number: $INITIAL_NUMBER";
11 | echo "Filename prefix: \"$PREFIX\""
12 | echo "Filename prefix length: $PREFIX_LENGTH"
13 | echo "File extension: \"$EXTENSION\""
14 | echo "Number of digits in the filename: $NUMBER_OF_DIGITS"
15 |
16 | LAST_NUMBER=`ls *$EXTENSION | sort| tail -n 1`;
17 | echo "Last file in destination directory is: $LAST_NUMBER";
18 |
19 | for filename in *.fits;
20 | do
21 | originalName=$filename;
22 | echo "Filename: $filename";
23 | filename=`basename $filename $EXTENSION`;
24 | #echo "Stripping file extension: $filename";
25 | filename=${filename:$PREFIX_LENGTH};
26 | #echo "Stripping filename prefix: $filename";
27 |
28 | fileNumber=$INITIAL_NUMBER;
29 | let INITIAL_NUMBER++;
30 | echo "Assigned file number: $fileNumber"
31 |
32 | until [ ${#fileNumber} -ge $NUMBER_OF_DIGITS ];
33 | do
34 | fileNumber="0$fileNumber";
35 | done;
36 |
37 | echo "Adding the necessary number of zeros: $fileNumber"
38 | DEST_NAME="$PREFIX$fileNumber$EXTENSION"
39 | echo "New filename: $DEST_NAME"
40 | echo "Renaming $originalName to $DEST_NAME"
41 | mv $originalName $DEST_NAME
42 |
43 | done
44 |
45 |
--------------------------------------------------------------------------------
/juicer/simbad.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | # Copyright (c) 2014 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify it
9 | # under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | import webbrowser
22 |
23 |
24 | def coordinate_query(ra, dec):
25 | """Look up an astronomical object in the SIMBAD database.
26 |
27 | Submit a coordinate-query to the SIMBAD database, using the given right
28 | ascension and declination (ICRS, J2000, 2000.0). Display it using the
29 | default browser, opening the page in the same window if possible.
30 |
31 | """
32 |
33 | SIMBAD_URL = (
34 | "http://simbad.u-strasbg.fr/simbad/sim-basic?"
35 | "Ident={0}+{1}&submit=SIMBAD+search"
36 | )
37 | url = SIMBAD_URL.format(ra, dec)
38 | webbrowser.open(url)
39 |
--------------------------------------------------------------------------------
/juicer/glade.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | # Copyright (c) 2012 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify it
9 | # under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | import functools
22 | import os.path
23 |
24 | GLADE_DIR = os.path.join(os.path.dirname(__file__), "./gui/")
25 | get = functools.partial(os.path.join, GLADE_DIR)
26 |
27 | GUI_MAIN = get("main.glade")
28 | GUI_ABOUT = get("about.glade")
29 | GUI_OVERVIEW = get("overview.glade")
30 | LOADING_DIALOG = get("loading-dialog.glade")
31 | STAR_DETAILS = get("star-details.glade")
32 | SNR_THRESHOLD_DIALOG = get("snr-threshold-dialog.glade")
33 | EXPORT_CURVE_DIALOG = get("curve-dump-dialog.glade")
34 | FINDING_CHART_DIALOG = get("finding-chart-dialog.glade")
35 | CHART_PREFERENCES_DIALOG = get("chart-preferences-dialog.glade")
36 |
--------------------------------------------------------------------------------
/Doc/_themes/kr/static/small_flask.css:
--------------------------------------------------------------------------------
1 | /*
2 | * small_flask.css_t
3 | * ~~~~~~~~~~~~~~~~~
4 | *
5 | * :copyright: Copyright 2010 by Armin Ronacher.
6 | * :license: Flask Design License, see LICENSE for details.
7 | */
8 |
9 | body {
10 | margin: 0;
11 | padding: 20px 30px;
12 | }
13 |
14 | div.documentwrapper {
15 | float: none;
16 | background: white;
17 | }
18 |
19 | div.sphinxsidebar {
20 | display: block;
21 | float: none;
22 | width: 102.5%;
23 | margin: 50px -30px -20px -30px;
24 | padding: 10px 20px;
25 | background: #333;
26 | color: white;
27 | }
28 |
29 | div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
30 | div.sphinxsidebar h3 a {
31 | color: white;
32 | }
33 |
34 | div.sphinxsidebar a {
35 | color: #aaa;
36 | }
37 |
38 | div.sphinxsidebar p.logo {
39 | display: none;
40 | }
41 |
42 | div.document {
43 | width: 100%;
44 | margin: 0;
45 | }
46 |
47 | div.related {
48 | display: block;
49 | margin: 0;
50 | padding: 10px 0 20px 0;
51 | }
52 |
53 | div.related ul,
54 | div.related ul li {
55 | margin: 0;
56 | padding: 0;
57 | }
58 |
59 | div.footer {
60 | display: none;
61 | }
62 |
63 | div.bodywrapper {
64 | margin: 0;
65 | }
66 |
67 | div.body {
68 | min-height: 0;
69 | padding: 0;
70 | }
71 |
72 | .rtd_doc_footer {
73 | display: none;
74 | }
75 |
76 | .document {
77 | width: auto;
78 | }
79 |
80 | .footer {
81 | width: auto;
82 | }
83 |
84 | .footer {
85 | width: auto;
86 | }
87 |
88 | .github {
89 | display: none;
90 | }
--------------------------------------------------------------------------------
/test/integration/WASP10b-coordinates.txt:
--------------------------------------------------------------------------------
1 | 348.9695584 31.4569795
2 | 348.9706593 31.4786831
3 | 348.9708807 31.4946751
4 | 348.9716835 31.4465233
5 | 348.9755939 31.5118557
6 | 348.9762762 31.4427023
7 | 348.9768874 31.5459851
8 | 348.9802179 31.4777670
9 | 348.9877599 31.5169965
10 | 348.9929917 31.4628800
11 | 348.9948081 31.5149581
12 | 348.9955399 31.5168039
13 | 349.0005937 31.4859562
14 | 349.0026969 31.4235611
15 | 349.0037716 31.5311517
16 | 349.0071724 31.4950792
17 | 349.0090988 31.4468615
18 | 349.0178073 31.5295721
19 | 349.0216961 31.5262667
20 | 349.0256513 31.4267100
21 | 349.0267606 31.4887238
22 | 349.0279426 31.4565166
23 | 349.0290935 31.4751772
24 | 349.0347477 31.4282842
25 | 349.0355844 31.5335729
26 | 349.0368935 31.4940076
27 | 349.0378078 31.4599099
28 | 349.0418034 31.4913782
29 | 349.0424539 31.5084784
30 | 349.0487474 31.5452358
31 | 349.0511326 31.5124036
32 | 349.0521123 31.4836650
33 | 349.0524923 31.4603205
34 | 349.0578772 31.4870474
35 | 349.0622240 31.5246747
36 | 349.0664677 31.5208836
37 | 349.0675991 31.4276511
38 | 349.0680332 31.4743616
39 | 349.0686085 31.4869822
40 | 349.0700309 31.5271816
41 | 349.0800821 31.5451665
42 | 349.0810459 31.5465297
43 | 349.0861453 31.4386185
44 | 349.0906927 31.4335912
45 | 349.0910049 31.5242583
46 | 349.0920768 31.5274161
47 | 349.0940299 31.5444274
48 | 349.0945085 31.4288481
49 | 349.0960586 31.4818339
50 | 349.0971139 31.4691589
51 | 349.0978631 31.4487778
52 | 349.0988346 31.4819169
53 | 349.0996688 31.4372169
54 | 349.1003909 31.4279043
55 | 349.1067420 31.5295854
56 | 349.1107878 31.4343480
57 | 349.1151697 31.5462396
58 | 349.1196789 31.4575158
59 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | LEMON
2 | =====
3 |
4 | LEMON_ is a differential-photometry pipeline, written in Python_, that determines the changes in the brightness of astronomical objects over time and compiles their measurements into `light curves`_. The aim of this program is to make it possible to completely reduce thousands of FITS images of time series in a matter of only a few hours, requiring minimal user interaction.
5 |
6 | For example, to get the light curve of a transit of HAT-P-16b_:
7 |
8 | ::
9 |
10 | $ lemon astrometry data/*.fits HAT-P-16/
11 | $ lemon mosaic HAT-P-16/*.fits HAT-P-16-mosaic.fits
12 | $ lemon photometry HAT-P-16-mosaic.fits HAT-P-16/*.fits phot.LEMONdB
13 | $ lemon diffphot phot.LEMONdB curves.LEMONdB
14 |
15 | The above commands produce, among many others, the following plot:
16 |
17 | .. image:: Doc/_static/HAT-P-16b-2014-10-31.jpg
18 |
19 | Homepage
20 | --------
21 |
22 | http://lemon.readthedocs.org/
23 |
24 | Acknowledging us
25 | ----------------
26 |
27 | If you use LEMON, please cite the package via its Zenodo_ record.
28 |
29 | .. image:: https://zenodo.org/badge/4550305.svg
30 | :target: https://zenodo.org/badge/latestdoi/4550305
31 |
32 | Project Status
33 | --------------
34 |
35 | .. image:: https://travis-ci.org/vterron/lemon.png?branch=master
36 | :target: https://travis-ci.org/vterron/lemon
37 |
38 | .. image:: https://img.shields.io/badge/code%20style-black-000000.svg
39 | :target: https://github.com/psf/black
40 |
41 | .. _LEMON: https://lemon.readthedocs.org/
42 | .. _Python: https://www.python.org/
43 | .. _light curves: https://en.wikipedia.org/wiki/Light_curve
44 | .. _HAT-P-16b: http://exoplanet.eu/catalog/hat-p-16_b/
45 | .. _Zenodo: https://zenodo.org/
46 |
--------------------------------------------------------------------------------
/run_tests.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | # Copyright (c) 2012 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | """
22 | This is a convenience script for finding all the unit tests in the ./test/
23 | directory and running them. Test modules are automatically detected, using
24 | TestLoader.discover(), which loads the test files that match the Unix-shell
25 | pattern 'test*.py' (such as ./test/test_passband.py).
26 |
27 | """
28 |
29 | import sys
30 | from test import unittest
31 |
32 | # This import checks whether the FITS images used by some tests are where
33 | # expected and, if that is not the case, automatically downloads them from the
34 | # STScI Digitized Sky Survey. In this manner, any image retrieval will be done
35 | # before running the unit tests, never halfway through their execution.
36 | import test.dss_images
37 |
38 | if __name__ == "__main__":
39 |
40 | loader = unittest.TestLoader()
41 | tests = loader.discover(".", top_level_dir=".")
42 | runner = unittest.runner.TextTestRunner(verbosity=2)
43 | runner.failfast = True
44 | result = runner.run(tests)
45 | sys.exit(not result.wasSuccessful())
46 |
--------------------------------------------------------------------------------
/juicer/gui/loading-dialog.glade:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 5
7 | True
8 | center-on-parent
9 | dialog
10 | True
11 | False
12 | cancel
13 | Loading database
14 | This may take a while...
15 |
16 |
17 | True
18 | vertical
19 | 2
20 |
21 |
22 | True
23 | True
24 |
25 |
26 | 3
27 |
28 |
29 |
30 |
31 | True
32 | end
33 |
34 |
35 | False
36 | end
37 | 0
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Doc/index.rst:
--------------------------------------------------------------------------------
1 | .. LEMON documentation master file, created by
2 | sphinx-quickstart on Mon May 6 12:08:02 2013.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | LEMON: differential photometry
7 | ==============================
8 |
9 | LEMON is a scientific pipeline, written in Python_, that determines the changes in the brightness of astronomical objects over time and compiles their measurements into `light curves`_. The aim of this program is to make it possible to completely **reduce thousands of FITS images of time series** in a matter of only a few hours, requiring minimal user interaction.
10 |
11 | For example, to get the light curve of a transit of HAT-P-16b_:
12 |
13 | ::
14 |
15 | $ lemon astrometry data/*.fits HAT-P-16/
16 | $ lemon mosaic HAT-P-16/*.fits HAT-P-16-mosaic.fits
17 | $ lemon photometry HAT-P-16-mosaic.fits HAT-P-16/*.fits phot.LEMONdB
18 | $ lemon diffphot phot.LEMONdB curves.LEMONdB
19 |
20 | The above commands produce, among many others, the following plot:
21 |
22 | .. only:: html
23 |
24 | .. thumbnail:: _static/HAT-P-16b-2014-10-31.svg
25 | :title: The light curve of a transit of exoplanet HAT-P-16b
26 |
27 | .. only:: latex
28 |
29 | .. image:: _static/HAT-P-16b-2014-10-31.jpg
30 |
31 | LEMON aims at taking most of the burden out of the astronomer, working out of the box with any set of images that conform to the `FITS standard`_. In most scenarios, the above four commands are enough to generate the high-precision light curves of all your astronomical objects.
32 |
33 | .. _Python: https://www.python.org/
34 | .. _light curves: https://en.wikipedia.org/wiki/Light_curve
35 | .. _HAT-P-16b: http://exoplanet.eu/catalog/hat-p-16_b/
36 | .. _FITS standard: http://fits.gsfc.nasa.gov/fits_standard.html
37 |
38 | User Guide
39 | ==========
40 |
41 | .. toctree::
42 | :maxdepth: 2
43 |
44 | user/install
45 | user/quickstart
46 |
--------------------------------------------------------------------------------
/test/test_data/filters/Cousins:
--------------------------------------------------------------------------------
1 | # A series of two-element tuples, one per line, containing two strings.
2 | # The first element must be the name of the Cousins photometric filter,
3 | # while the second is the letter that the Passband class must identify
4 | # when the former is parsed.
5 |
6 | "Cousins R", 'R'
7 | "Cousins I", 'I'
8 | "R (Cousins)", 'R'
9 | "I (Cousins)", 'I'
10 | "rCousins", 'R'
11 | "iCousins", 'I'
12 |
13 | # Suggested by @paramoreta at issue #81
14 | # https://github.com/vterron/lemon/issues/81
15 |
16 | "CousR", 'R'
17 | "Cous I", 'I'
18 | "CousinR", 'R'
19 | "Cousin I", 'I'
20 |
21 | # ... and, by extension:
22 |
23 | "Cous_R", 'R'
24 | "Cous-I", 'I'
25 | "r-Cousin", 'R'
26 | "Icousin", 'I'
27 |
28 | # Extremely convoluted test cases
29 |
30 | " coui_-_", 'I'
31 | "-- coUI_-", 'I'
32 | "- _cOui_", 'I'
33 | " _cOUI -", 'I'
34 | "-_ COUI_-", 'I'
35 | "_-_-couR", 'R'
36 | "-coU -_r-_", 'R'
37 | "-_---CouR_", 'R'
38 | "__CoU-_-r--_", 'R'
39 | " cousInS__i--_", 'I'
40 | "__- couSinsi__", 'I'
41 | " -__-cOUSInsi ", 'I'
42 | " ___CouSInS_i-", 'I'
43 | "-COUsInSi___-_", 'I'
44 | "____ COUsINsi-_ ", 'I'
45 | "__COUSiNsI-", 'I'
46 | "--cousinSr- -", 'R'
47 | "-- -coUSins-r-", 'R'
48 | " _cOusiNSR _", 'R'
49 | " _cOUsins-r ", 'R'
50 | " _COUsiNs- r__", 'R'
51 | "- COUsINS-_r -_-", 'R'
52 | "_icoU_-__", 'I'
53 | "_---icOu-__", 'I'
54 | " -i__COU-", 'I'
55 | "_I--_ (- cou_-)-_ ", 'I'
56 | "-Icou -", 'I'
57 | "_I_ cOU----", 'I'
58 | "I-Cou_- __", 'I'
59 | "--I _ -CoU--", 'I'
60 | "-I -CoU- ", 'I'
61 | "__ICOu_-", 'I'
62 | "-__-ICOU ", 'I'
63 | "__icousINs- -", 'I'
64 | " -icouSins--_-", 'I'
65 | "- icouSIns__", 'I'
66 | "i_ -_cOUsiNs_-", 'I'
67 | "-_i_--(_COUsiNS ) ", 'I'
68 | " __i_COUsInS__", 'I'
69 | "_- -I__(_ cOUSInS- -__)__-_-", 'I'
70 | "ICousins-", 'I'
71 | "__rcou--", 'R'
72 | "-r-- (_-cou__ -)--_-", 'R'
73 | "---rcOU-_ ", 'R'
74 | "_RcOU-__-_", 'R'
75 | "_RCOU -", 'R'
76 | " rCoUsInS___ ", 'R'
77 | " -_r_-( __COusInS_-__) -- _", 'R'
78 | "__rCOUsins-", 'R'
79 | " R-__ (--COusInS_)__-", 'R'
80 |
--------------------------------------------------------------------------------
/Doc/_themes/LICENSE:
--------------------------------------------------------------------------------
1 | Modifications:
2 |
3 | Copyright (c) 2010 Kenneth Reitz.
4 |
5 |
6 | Original Project:
7 |
8 | Copyright (c) 2010 by Armin Ronacher.
9 |
10 |
11 | Some rights reserved.
12 |
13 | Redistribution and use in source and binary forms of the theme, with or
14 | without modification, are permitted provided that the following conditions
15 | are met:
16 |
17 | * Redistributions of source code must retain the above copyright
18 | notice, this list of conditions and the following disclaimer.
19 |
20 | * Redistributions in binary form must reproduce the above
21 | copyright notice, this list of conditions and the following
22 | disclaimer in the documentation and/or other materials provided
23 | with the distribution.
24 |
25 | * The names of the contributors may not be used to endorse or
26 | promote products derived from this software without specific
27 | prior written permission.
28 |
29 | We kindly ask you to only use these themes in an unmodified manner just
30 | for Flask and Flask-related products, not for unrelated projects. If you
31 | like the visual style and want to use it for your own projects, please
32 | consider making some larger changes to the themes (such as changing
33 | font faces, sizes, colors or margins).
34 |
35 | THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
39 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
40 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
41 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
42 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
43 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
44 | ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
45 | POSSIBILITY OF SUCH DAMAGE.
46 |
--------------------------------------------------------------------------------
/test/test_data/filters/Harris:
--------------------------------------------------------------------------------
1 | # A series of two-element tuples, one per line, containing two strings.
2 | # The first element must be the name of the Harris photometric filter,
3 | # while the second is the letter that the Passband class must identify
4 | # when the former is parsed.
5 |
6 | "Harris U", 'U'
7 | "Harris_B", 'B'
8 | "Harris-V", 'V'
9 | "R (Harris)", 'R'
10 | "I_(Harris)", 'I'
11 | "u-(harris)", 'U'
12 | "bHarris", 'B'
13 | "v-Harris", 'V'
14 | "r_harris", 'R'
15 |
16 | # These are *real* examples of Harris filters, kindly provided by
17 | # Javier Blasco, who found them among the FITS images of several
18 | # astronomical campaigns.
19 |
20 | "Harris R", 'R'
21 | "Harris V", 'V'
22 |
23 | # Extremely convoluted test cases
24 |
25 | "--HArRis_-_V_ __-", 'V'
26 | "__--IhaRriS--", 'I'
27 | "----I_haRRiS_", 'I'
28 | " v-__-_(hArris_)-_ _", 'V'
29 | "___ i -_(_haRris_)", 'I'
30 | "__u-_(_HARris_)-", 'U'
31 | "- __hARRiSb--", 'B'
32 | " _-hArriS-- u--", 'U'
33 | "_-r-_haRrIS_ -", 'R'
34 | "___V -_(____HArrIS__) -", 'V'
35 | "--_V( -haRris_ )", 'V'
36 | "__-HarRisb-", 'B'
37 | "-_-haRriSI_ ", 'I'
38 | "-HARRisR--", 'R'
39 | "_ _iHArris--_", 'I'
40 | "B__harRIs", 'B'
41 | " -HaRrisU", 'U'
42 | "BhArris-_", 'B'
43 | "--rHARRis ", 'R'
44 | "__ haRRISb__-_ ", 'B'
45 | " ihARRis---", 'I'
46 | "-_HArRISI_ ", 'I'
47 | " -HaRRis_ _-r_", 'R'
48 | "- _haRrisb__-", 'B'
49 | "_ -v-HarRis--__", 'V'
50 | "b---haRRIs- ", 'B'
51 | "___ vhaRRIS", 'V'
52 | "v_HaRrIs ", 'V'
53 | "--v- _HarrIs-__ _", 'V'
54 | "-harrISu--__", 'U'
55 | "V_ __Harris ", 'V'
56 | "- u-_-(-hARrIS-)_-", 'U'
57 | "b(_hArrIs _ -)", 'B'
58 | "-BHarRis--_ ", 'B'
59 | "__--uhARrIs__-", 'U'
60 | " -__bhARrIs_--", 'B'
61 | " _-HArRISv__---", 'V'
62 | " v-_-hARRIS-__ ", 'V'
63 | "B-Harris-", 'B'
64 | "_-R__hARrIs_-_-", 'R'
65 | "_-RhARRiS", 'R'
66 | "-_-_I--__(__HARRiS- _) - -", 'I'
67 | " -hARRISb -_-", 'B'
68 | "_ -_bhaRrIS-", 'B'
69 | "_-haRRis_- u", 'U'
70 | "_-HARrIsv-", 'V'
71 | " V- (_-_harRIs)- _", 'V'
72 | "_ -hARRis _u-_", 'U'
73 | "b--(---HArriS----_)- ", 'B'
74 | "I_-- HarRis_-", 'I'
75 |
--------------------------------------------------------------------------------
/util/context.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | # Copyright (c) 2019 Victor Terron. All rights reserved.
4 | #
5 | # This file is part of LEMON.
6 | #
7 | # LEMON is free software: you can redistribute it and/or modify it
8 | # under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation, either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program. If not, see .
19 |
20 | import contextlib
21 | import os
22 | import tempfile
23 |
24 |
25 | @contextlib.contextmanager
26 | def tmp_chdir(path):
27 | """A context manager to temporarily change the working directory.
28 |
29 | This is a rather simple context manager to change the current working
30 | directory within a with statement, restoring the original one upon exit.
31 |
32 | """
33 |
34 | cwd = os.getcwd()
35 | try:
36 | os.chdir(path)
37 | yield
38 | finally:
39 | os.chdir(cwd)
40 |
41 |
42 | @contextlib.contextmanager
43 | def tempinput(data):
44 | """A context manager to work with StringIO-like temporary files.
45 |
46 | The open() built-in command only takes filenames, so we cannot feed to it a
47 | StringIO. This context manager writes 'data' to a temporary file, ensuring
48 | that it is cleaned up afterwards. In this manner, we can work in a similar
49 | manner to what we would have done with StringIO, as the data is written to
50 | disk only temporarily and transparently to us.
51 |
52 | with tempinput('some data\more data') as path:
53 | open(path)
54 |
55 | Taken from Martijn Pieters's answer on Stack Overflow:
56 | [URL] https://stackoverflow.com/a/11892712/184363
57 |
58 | """
59 |
60 | fd, path = tempfile.mkstemp()
61 | os.write(fd, data)
62 | os.close(fd)
63 | yield path
64 | os.unlink(path)
65 |
--------------------------------------------------------------------------------
/Misc/ACKS:
--------------------------------------------------------------------------------
1 | Acknowledgements
2 | ----------------
3 |
4 | This list, hopefully complete and in chronological order, is a compendium of
5 | all the people who contributed and whom I would like to thank. I am deeply
6 | grateful for the ideas, enthusiastic questions, thorough answers and moral
7 | support received during these years, and look forward to the day on which
8 | somebody finally submits a patch. See you at GitHub! -- Víctor
9 |
10 | Matilde Fernández, aka The Mastermind (also, the Principal Investigator)
11 | Nuria Huélamo, a loyal companion at the Fortress of Solitude (1.23m CAHA)
12 | Nicolás Cardiel, for his help on error propagation (via email)
13 | Frank Valdes, on qphot and stars saturation (the IRAF.net forums)
14 |
15 | Michael Newberry,
16 | Jim Jones,
17 | Gary Walker and
18 | Bradley Walter [AAVSO-Photometry mailing list]
19 |
20 | Hernán J. González, also helped on error propagation (at Math Stack Exchange)
21 | César Husillos, for his help on image alignment through cross-correlation
22 | Ilse Plauchu, for her useful comments on Gaussian distribution fitting
23 | Emmanuel Bertin, for the ample and diligent help with SExtractor
24 | Javier Blasco, our most enthusiastic user, for such thoughtful suggestions
25 | Pablo Ramírez, the second person to ever use LEMON — that we know of
26 | Sofía León, for designing for us such a marvelous LEMON icon
27 | José Enrique Ruiz, for suggesting that we set extglob
28 | Jean-Baptiste Marquette, for reporting a bug under Mac OS X
29 | Scott Engle, for the many bugs diligently reported
30 | Juha Sahakangas, for the workaround for GTK+ bug 632538
31 | Felipe Gallego, for reporting a bug in get__version__()
32 | Muhammad Yusuf, for reporting bugs in the unit tests
33 | Pablo Galindo, our most relentless bug hunter
34 | Jorge Gómez, for reporting a bug with PyFITS > 3.2
35 | Ted Groner, for reporting a bug in the unit tests on 32-bit IRAF
36 | William Schoenell, for helping debug the aforementioned bug
37 | Emilio Falco, for the many bugs perseveringly reported
38 | Hervé Bouy, for the help and advice on Astrometry.net
39 | Michael Hlabathe, for reporting a bug in the unit tests.
40 | Derek O'Keeffe, for the bug fix for load_coordinates().
41 | Ana Karla Díaz (@akdiaz), for the help debugging `juicer`.
42 |
--------------------------------------------------------------------------------
/sextractor/sextractor.nnw:
--------------------------------------------------------------------------------
1 | NNW
2 | # Neural Network Weights for the SExtractor star/galaxy classifier (V1.3)
3 | # inputs: 9 for profile parameters + 1 for seeing.
4 | # outputs: ``Stellarity index'' (0.0 to 1.0)
5 | # Seeing FWHM range: from 0.025 to 5.5'' (images must have 1.5 < FWHM < 5 pixels)
6 | # Optimized for Moffat profiles with 2<= beta <= 4.
7 |
8 | 3 10 10 1
9 |
10 | -1.56604e+00 -2.48265e+00 -1.44564e+00 -1.24675e+00 -9.44913e-01 -5.22453e-01 4.61342e-02 8.31957e-01 2.15505e+00 2.64769e-01
11 | 3.03477e+00 2.69561e+00 3.16188e+00 3.34497e+00 3.51885e+00 3.65570e+00 3.74856e+00 3.84541e+00 4.22811e+00 3.27734e+00
12 |
13 | -3.22480e-01 -2.12804e+00 6.50750e-01 -1.11242e+00 -1.40683e+00 -1.55944e+00 -1.84558e+00 -1.18946e-01 5.52395e-01 -4.36564e-01 -5.30052e+00
14 | 4.62594e-01 -3.29127e+00 1.10950e+00 -6.01857e-01 1.29492e-01 1.42290e+00 2.90741e+00 2.44058e+00 -9.19118e-01 8.42851e-01 -4.69824e+00
15 | -2.57424e+00 8.96469e-01 8.34775e-01 2.18845e+00 2.46526e+00 8.60878e-02 -6.88080e-01 -1.33623e-02 9.30403e-02 1.64942e+00 -1.01231e+00
16 | 4.81041e+00 1.53747e+00 -1.12216e+00 -3.16008e+00 -1.67404e+00 -1.75767e+00 -1.29310e+00 5.59549e-01 8.08468e-01 -1.01592e-02 -7.54052e+00
17 | 1.01933e+01 -2.09484e+01 -1.07426e+00 9.87912e-01 6.05210e-01 -6.04535e-02 -5.87826e-01 -7.94117e-01 -4.89190e-01 -8.12710e-02 -2.07067e+01
18 | -5.31793e+00 7.94240e+00 -4.64165e+00 -4.37436e+00 -1.55417e+00 7.54368e-01 1.09608e+00 1.45967e+00 1.62946e+00 -1.01301e+00 1.13514e-01
19 | 2.20336e-01 1.70056e+00 -5.20105e-01 -4.28330e-01 1.57258e-03 -3.36502e-01 -8.18568e-02 -7.16163e+00 8.23195e+00 -1.71561e-02 -1.13749e+01
20 | 3.75075e+00 7.25399e+00 -1.75325e+00 -2.68814e+00 -3.71128e+00 -4.62933e+00 -2.13747e+00 -1.89186e-01 1.29122e+00 -7.49380e-01 6.71712e-01
21 | -8.41923e-01 4.64997e+00 5.65808e-01 -3.08277e-01 -1.01687e+00 1.73127e-01 -8.92130e-01 1.89044e+00 -2.75543e-01 -7.72828e-01 5.36745e-01
22 | -3.65598e+00 7.56997e+00 -3.76373e+00 -1.74542e+00 -1.37540e-01 -5.55400e-01 -1.59195e-01 1.27910e-01 1.91906e+00 1.42119e+00 -4.35502e+00
23 |
24 | -1.70059e+00 -3.65695e+00 1.22367e+00 -5.74367e-01 -3.29571e+00 2.46316e+00 5.22353e+00 2.42038e+00 1.22919e+00 -9.22250e-01 -2.32028e+00
25 |
26 |
27 | 0.00000e+00
28 | 1.00000e+00
29 |
--------------------------------------------------------------------------------
/test/test_customparser.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | # Copyright (c) 2015 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | # LEMON modules
22 | from test import unittest
23 | import customparser
24 |
25 |
26 | class CustomParserTest(unittest.TestCase):
27 | def test_additional_options_callback(self):
28 |
29 | parser = customparser.get_parser(None)
30 | parser.add_option(
31 | "-o",
32 | action="callback",
33 | type="str",
34 | dest="parsed_options",
35 | default={},
36 | callback=customparser.additional_options_callback,
37 | )
38 |
39 | arguments = [
40 | "-o",
41 | "-v",
42 | "-o",
43 | "--batch",
44 | "-o",
45 | "-r=2",
46 | "-o",
47 | "--sigma 3",
48 | "-o=-h",
49 | "-o=--invert",
50 | "-o=-d=cubic",
51 | "-o=--no-verify = True",
52 | ]
53 |
54 | (options, args) = parser.parse_args(args=arguments)
55 | options = options.parsed_options
56 | self.assertIsNone(options["-v"])
57 | self.assertIsNone(options["--batch"])
58 | self.assertEqual(options["-r"], "2")
59 | self.assertEqual(options["--sigma"], "3")
60 | self.assertIsNone(options["-h"])
61 | self.assertIsNone(options["--invert"])
62 | self.assertEqual(options["-d"], "cubic")
63 | self.assertEqual(options["--no-verify"], "True")
64 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Configuration for probot-stale - https://github.com/probot/stale
2 |
3 | # Number of days of inactivity before an Issue or Pull Request becomes stale
4 | daysUntilStale: 180
5 |
6 | # Number of days of inactivity before a stale Issue or Pull Request is closed.
7 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
8 | daysUntilClose: 30
9 |
10 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
11 | exemptLabels:
12 | - WIP
13 | - work in progress
14 | - important
15 | - locked
16 | - feature-request
17 | - pinned
18 | - security
19 |
20 | # Set to true to ignore issues in a project (defaults to false)
21 | exemptProjects: false
22 |
23 | # Set to true to ignore issues in a milestone (defaults to false)
24 | exemptMilestones: false
25 |
26 | # Label to use when marking as stale
27 | staleLabel: stale
28 |
29 | # Comment to post when marking as stale. Set to `false` to disable
30 | markComment: >
31 | Thanks for contributing to this issue. As it has been 180 days since the last activity, we'll be automatically closing the issue soon. This is often because the request was already solved in some way and it just wasn't updated or it's just no longer relevant. If that's not the case, please respond here within the next 30 days to keep it open. You can read more here: https://github.com/probot/stale#is-closing-stale-issues-really-a-good-idea.
32 |
33 | # Comment to post when removing the stale label.
34 | # unmarkComment: >
35 | # Your comment here.
36 |
37 | # Comment to post when closing a stale Issue or Pull Request.
38 | #closeComment: >
39 | # Your comment here.
40 |
41 | # Limit the number of actions per hour, from 1-30. Default is 30
42 | limitPerRun: 30
43 |
44 | # Limit to only `issues` or `pulls`
45 | only: issues
46 |
47 | # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
48 | # pulls:
49 | # daysUntilStale: 30
50 | # markComment: >
51 | # This pull request has been automatically marked as stale because it has not had
52 | # recent activity. It will be closed if no further activity occurs. Thank you
53 | # for your contributions.
54 |
55 | # issues:
56 | # exemptLabels:
57 | # - confirmed
58 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 |
3 | python:
4 | # Don't test against 2.6. We use apt-get to install many dependencies, but
5 | # Python system packages only include Python 2.7 libraries on Ubuntu 12.04.
6 | - "2.7"
7 |
8 | env:
9 | global:
10 | - WASP10_DATA=$HOME/WASP10_data
11 | - secure: "EEA1YcB7rIBu5jQipl89lGxmRvk1IZ158i0J1PC1L7tFaq6hK7g1nf4rJWjrwYBS4gZT8z1fA8eiP3KJ8hRmh4S9cYq6FD/PpwknFo2QysoUrS2d2XxK92nfJSkjJ7Gx5F1fVqtqITda5uJwhEVGZdgkCFv5JGvJcdGWHZVyNAc="
12 |
13 | virtualenv:
14 | system_site_packages: true
15 |
16 | git:
17 | depth: false
18 |
19 | cache:
20 | directories:
21 | - $WASP10_DATA
22 |
23 | before_install:
24 | - sudo apt-get update
25 | - sudo apt-get install -y alien realpath
26 | - sudo apt-get install -y git python-pip csh realpath
27 | - sudo apt-get build-dep -y python-matplotlib python-scipy
28 | - sudo easy_install -U distribute
29 | - sudo pip install "numpy>=1.7.1"
30 | - sudo pip install -r pre-requirements.txt
31 | # 'travis_wait': avoid timeout during SciPy installation
32 | - travis_wait 45 sudo pip install -r requirements.txt
33 | # Install IRAF and SExtractor
34 | - sudo ./ci/travis-setup.sh
35 | - python ./setup.py
36 |
37 | - export iraf=/iraf/iraf/
38 | - export IRAFARCH=linux64
39 | - export PATH=$(pwd):$PATH
40 |
41 | install: true
42 |
43 | jobs:
44 | include:
45 | # Download integration test data to the cached directory.
46 | - stage: Setup
47 | name: "Prepare test data"
48 | script: ./test/integration/wasp10b-download.sh $WASP10_DATA
49 |
50 | - stage: Tests
51 | name: "Unit Tests"
52 | script: ./run_tests.py
53 |
54 | # Exercise the multiprocessing logic of `photometry` and `diffphot`.
55 | # The goal is to make sure that light cuves computed in parallel are
56 | # absolutely independent of each other.
57 |
58 | - script: NCORES=1 ./test/integration/wasp10b.py
59 | name: "Integration Test (cores = 1)"
60 |
61 | - script: NCORES=2 ./test/integration/wasp10b.py
62 | name: "Integration Test (cores = 2)"
63 |
64 | - script: NCORES=3 ./test/integration/wasp10b.py
65 | name: "Integration Test (cores = 3)"
66 |
67 | - script: NCORES=4 ./test/integration/wasp10b.py
68 | name: "Integration Test (cores = 4)"
69 |
70 | notifications:
71 | email:
72 | on_success: change
73 | on_failure: change
74 |
--------------------------------------------------------------------------------
/test/test_data/filters/Halpha:
--------------------------------------------------------------------------------
1 | # A series of two-element tuples, one per line, containing two strings.
2 | # The first element must be the name of the H-alpha photometric filter,
3 | # while the second is the wavelength of the filter that the Passband
4 | # class must identify when the former is parsed.
5 |
6 | 'H6341', '6341'
7 | 'H6342/73', '6342'
8 | 'Ha0043', '0043'
9 | 'HA_6344/74', '6344'
10 | 'Halpha-6345', '6345'
11 | 'Halpha_6346/75', '6346'
12 |
13 | # These are *real* examples of Halpha filters, provided by Javier Blasco,
14 | # who found them among the FITS images of several astronomical campaigns.
15 |
16 | 'H6563', '6563'
17 | 'H6607', '6607'
18 | 'H6650', '6650'
19 | 'H6652', '6652'
20 | 'H6700', '6700'
21 | 'H6702', '6702'
22 | 'Ha6607', '6607'
23 | 'Ha6652', '6652'
24 | 'HALPHA6563', '6563'
25 |
26 | # Extremely convoluted test cases
27 |
28 | " __H_-__ A-_--0817/91", '0817'
29 | "H_-_ A0825/63", '0825'
30 | "_--H - _-A __0939", '0939'
31 | "-_ -ha -_ 1096", '1096'
32 | "hA_1288/75", '1288'
33 | "__-H A--_1518/13", '1518'
34 | "-_h a_1611", '1611'
35 | "H_--a__--_1807", '1807'
36 | " -hA 2291/42", '2291'
37 | "_--H- A 2683", '2683'
38 | " - H-_A_ -_2731/70", '2731'
39 | " --_h--_a__3137/61", '3137'
40 | " H_-_A_3786/51", '3786'
41 | "-- _H _--a_3970", '3970'
42 | "-- h _-a4409", '4409'
43 | "_H-a-_- _5354", '5354'
44 | "- h_ -A-5754/38", '5754'
45 | " -___H-a-6917/17", '6917'
46 | "-h_-a-7062/24", '7062'
47 | "_- _hA --7324", '7324'
48 | "_h-_a---_7512", '7512'
49 | "-__- h- _a_7758", '7758'
50 | "hA ___8816/37", '8816'
51 | " __h-a9032", '9032'
52 | "- __h-A-9079", '9079'
53 | "-_h- a9605/99", '9605'
54 | "_--ha 9727/84", '9727'
55 | "H-alpha0189", '0189'
56 | "- _h -alpHA0305/93", '0305'
57 | "_ -__H_aLpHa 1416", '1416'
58 | "-_-H- _ _ALpha_-_1664/09", '1664'
59 | " -hALpHA1673/77", '1673'
60 | "_- H_ alPHa _-2559", '2559'
61 | "_--H_ALphA -0335", '0335'
62 | "-___h_ -_AlPHA-3584", '3584'
63 | "--HaLPha-_-_-4043/96", '4043'
64 | "___ H __alphA--_4766", '4766'
65 | "_ _H-_AlPHA_5061", '5061'
66 | "--H AlphA__-5389", '5389'
67 | "H-_ALPhA--5571/04", '5571'
68 | "H__ _aLphA___-5742", '5742'
69 | "_-H-___aLpha__6506", '6506'
70 | "h- _ alpHA_6683", '6683'
71 | " -__H_AlPha0008", '0008'
72 | " -_h-_AlPHa_ 6867/02", '6867'
73 | "___ H-- ALpha _7433", '7433'
74 | " _-H_Alpha_ -7571", '7571'
75 | " HALpHa-8515", '8515'
76 | " -_H-_-aLphA8622", '8622'
77 | "--_-h___ alPHA_9253/17", '9253'
78 |
--------------------------------------------------------------------------------
/ci/travis-setup.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # Install LEMON dependencies in the Travis CI environment OS
4 | # Author: Victor Terron (c) 2013
5 | # License: GNU GPLv3
6 |
7 | set -e # exit if any statement returns a non-true return value
8 | set -u # any attempt to use an undefined variable is an error
9 | set -x # print commands and their arguments as they are executed
10 |
11 | REGEXP=".*64$"
12 | HARDWARE_NAME=`uname -m`
13 |
14 | echo -n "Machine architecture: "
15 | if [[ $HARDWARE_NAME =~ $REGEXP ]]; then
16 | ARCH_64_BITS=1;
17 | echo "64-bit"
18 | else
19 | ARCH_64_BITS=0;
20 | echo "32-bit"
21 | fi
22 |
23 | CWD=$(pwd)
24 |
25 | cd ~
26 |
27 | ########### Install IRAF ################
28 |
29 | IRAF_TAR="v2.16.1+2018.11.01.tar.gz"
30 | IRAF_SERVER="https://github.com/iraf-community/iraf/archive/"
31 | IRAF_URL=$IRAF_SERVER$IRAF_TAR
32 |
33 | IRAF_DIR="/iraf/iraf/"
34 | mkdir -p $IRAF_DIR
35 | IRAF_DIR=`realpath $IRAF_DIR`
36 | cd $IRAF_DIR
37 |
38 | wget $IRAF_URL
39 | tar xfz $IRAF_TAR
40 | mv iraf-2.16.1-2018.11.01/* .
41 |
42 | # https://iraf-community.github.io/install
43 | yes "" | ./install --system
44 | make linux64
45 | make sysgen 2>&1 | tee build.log
46 | ./test/run_tests
47 | rm $IRAF_TAR
48 |
49 | ########### Install SExtractor ###########
50 |
51 | cd ~
52 |
53 | if [[ $ARCH_64_BITS == 1 ]]; then
54 | SEXTRACTOR_RPM="sextractor-2.19.5-1.x86_64.rpm"
55 | else
56 | SEXTRACTOR_RMP="sextractor-2.19.5-1.i386.rpm"
57 | fi
58 |
59 | SEXTRACTOR_SERVER="http://www.astromatic.net/download/sextractor/"
60 | SEXTRACTOR_URL=$SEXTRACTOR_SERVER$SEXTRACTOR_RPM
61 | wget $SEXTRACTOR_URL
62 | alien -i $SEXTRACTOR_RPM
63 | rm $SEXTRACTOR_RPM
64 |
65 | cd $CWD # back to the LEMON directory
66 |
67 | # The unit tests use several FITS images that are downloaded from the
68 | # STScI Digitized Sky Survey to test/test_data/fits/. Be considerate
69 | # and, instead of downloading them every time the tests are run, keep
70 | # a copy on our server.
71 |
72 | TEST_FITS_DIR="test/test_data/fits/"
73 | DSS_IMAGES_URL="https://github.com/vterron/lemon-test-data/raw/master/DSS/"
74 |
75 | DSS_FILENAMES=(
76 | "Barnard's_Star.fits"
77 | "IC_5070.fits"
78 | "IC_5146.fits"
79 | "Messier_92.fits"
80 | "NGC_2264.fits"
81 | "Orion.fits"
82 | "RMC_136.fits"
83 | "Serpens.fits"
84 | "Trapezium.fits"
85 | "Trumpler_37.fits"
86 | )
87 |
88 | mkdir -p $TEST_FITS_DIR
89 | cd $TEST_FITS_DIR
90 |
91 | echo "Downloading test FITS images to $(pwd)"
92 | for filename in "${DSS_FILENAMES[@]}"; do
93 | wget $DSS_IMAGES_URL$filename -O $filename;
94 | done;
95 |
96 | cd $CWD
97 |
98 | exit 0
99 |
--------------------------------------------------------------------------------
/juicer/gui/finding-chart-dialog.glade:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 5
7 | normal
8 | False
9 |
10 |
11 | True
12 | vertical
13 | 2
14 |
15 |
16 | True
17 | vertical
18 |
19 |
20 | True
21 |
22 |
23 |
24 |
25 |
26 | 0
27 |
28 |
29 |
30 |
31 | True
32 |
33 |
34 |
35 |
36 |
37 | False
38 | 1
39 |
40 |
41 |
42 |
43 | 1
44 |
45 |
46 |
47 |
48 | True
49 | True
50 | True
51 | center
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | False
64 | end
65 | 0
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/sextractor/sextractor.param:
--------------------------------------------------------------------------------
1 | NUMBER
2 |
3 | X_IMAGE
4 | Y_IMAGE
5 |
6 | ALPHA_SKY
7 | DELTA_SKY
8 |
9 | XWIN_IMAGE # parameter needed by SCAMP, do _not_ comment!
10 | YWIN_IMAGE # parameter needed by SCAMP, do _not_ comment!
11 |
12 | ERRAWIN_IMAGE # parameter needed by SCAMP, do _not_ comment!
13 | ERRBWIN_IMAGE # parameter needed by SCAMP, do _not_ comment!
14 | ERRTHETAWIN_IMAGE # parameter needed by SCAMP, do _not_ comment!
15 |
16 | # SCAMP advises (but it is not mandatory) to include these fields
17 | # in the catalog. They are used to filter out blended and corrupted
18 | # detections, and also in order to identify small glitches or
19 | # extended objects.
20 |
21 | FLAGS
22 | #FLAGS_WEIGHT # "WARNING: FLAGS_WEIGHT catalog parameter unknown" ?
23 | #IMAFLAGS_ISO # This one needed to use weight-maps (from flag.fits)
24 | FLUX_RADIUS
25 |
26 |
27 | FLUX_ISO # SNR is defined as FLUX_ISO divided by FLUXERR_APER
28 | FLUXERR_ISO # # (that is, the signal divided by the noise ;-)
29 | #MAG_ISO
30 | #MAGERR_ISO
31 |
32 | #FLUX_ISOCOR
33 | #FLUXERR_ISOCOR
34 | #MAG_ISOCOR
35 | #MAGERR_ISOCOR
36 |
37 | #FLUX_APER(1)
38 | #FLUXERR_APER(1)
39 | #MAG_APER(1)
40 | #MAGERR_APER(1)
41 |
42 | FLUX_AUTO # parameter needed by SCAMP, do _not_ comment out!
43 | FLUXERR_AUTO # parameter needed by SCAMP, do _not_ comment out!
44 | MAG_AUTO # Automatic aperture magnitudes
45 | MAGERR_AUTO
46 |
47 | #FLUX_BEST
48 | #FLUXERR_BEST
49 | #MAG_BEST
50 | #MAGERR_BEST
51 |
52 | #KRON_RADIUS
53 | #BACKGROUND
54 |
55 | #THRESHOLD
56 | #MU_THRESHOLD
57 | FLUX_MAX
58 | #MU_MAX
59 |
60 |
61 | ISOAREAF_IMAGE # Isophotal area (filtered) above Detection threshold (Pix^2)
62 | ISOAREAF_WORLD # Isophotal area (filtered) above Detection threshold (Deg^2)
63 |
64 |
65 | #XMIN_IMAGE
66 | #YMIN_IMAGE
67 | #XMAX_IMAGE
68 | #YMAX_IMAGE
69 |
70 |
71 | #X_WORLD
72 | #Y_WORLD
73 |
74 | #ALPHA_J2000
75 | #DELTA_J2000
76 | #ALPHA_B1950
77 | #DELTA_B1950
78 |
79 | #X2_IMAGE
80 | #Y2_IMAGE
81 | #XY_IMAGE
82 | #X2_WORLD
83 | #Y2_WORLD
84 | #XY_WORLD
85 |
86 | #CXX_IMAGE
87 | #CYY_IMAGE
88 | #CXY_IMAGE
89 | #CXX_WORLD
90 | #CYY_WORLD
91 | #CXY_WORLD
92 |
93 | #A_IMAGE
94 | #B_IMAGE
95 | #A_WORLD
96 | #B_WORLD
97 |
98 | #THETA_IMAGE
99 | #THETA_WORLD
100 | #THETA_SKY
101 | #THETA_J2000
102 | #THETA_B1950
103 |
104 | # Do _not_ comment!
105 | ELONGATION
106 | #ELLIPTICITY
107 |
108 | #ERRX2_IMAGE
109 | #ERRY2_IMAGE
110 | #ERRXY_IMAGE
111 | #ERRX2_WORLD
112 | #ERRY2_WORLD
113 | #ERRXY_WORLD
114 |
115 | #ERRCXX_IMAGE
116 | #ERRCYY_IMAGE
117 | #ERRCXY_IMAGE
118 | #ERRCXX_WORLD
119 | #ERRCYY_WORLD
120 | #ERRCXY_WORLD
121 |
122 | #ERRA_IMAGE
123 | #ERRB_IMAGE
124 | #ERRA_WORLD
125 | #ERRB_WORLD
126 |
127 | #ERRTHETA_IMAGE
128 | #ERRTHETA_WORLD
129 | #ERRTHETA_SKY
130 | #ERRTHETA_J2000
131 | #ERRTHETA_B1950
132 |
133 | #FWHM_IMAGE
134 | #FWHM_WORLD
135 |
136 | #ISO0
137 | #ISO1
138 | #ISO2
139 | #ISO3
140 | #ISO4
141 | #ISO5
142 | #ISO6
143 | #ISO7
144 |
145 | #NIMAFLAGS_ISO(1)
146 |
147 | #CLASS_STAR
148 | #VIGNET(5,5)
149 |
--------------------------------------------------------------------------------
/test/test_data/filters/2MASS:
--------------------------------------------------------------------------------
1 | # A series of two-element tuples, one per line, containing two strings.
2 | # The first element must be the name of the 2MASS photometric filter,
3 | # while the second is the letter that the Passband class must identify
4 | # when the former is parsed.
5 |
6 | "2MASS J", 'J'
7 | "2MASS h", 'H'
8 | "2MASS Ks", 'KS'
9 | "2mass_J", 'J'
10 | "H (2MASS)", 'H'
11 | "KS (2MASS)", 'KS'
12 | "j2MASS", 'J'
13 | "2MASSh", 'H'
14 | "2massKS", 'KS'
15 |
16 | # Extremely convoluted test cases
17 |
18 | " _ 2masS-_H_", 'H'
19 | " 2maSS __--H -_", 'H'
20 | "-2mASsH- -_-", 'H'
21 | "-2mASSh-", 'H'
22 | "-_2MaSS H--", 'H'
23 | " -__2MAsSH-_", 'H'
24 | "--2MASS_h-_", 'H'
25 | "---_2MASSH_____", 'H'
26 | "__2massj_-", 'J'
27 | "-_2masS_--j-_", 'J'
28 | "- --2MasS __J-", 'J'
29 | " -_2MASSj-_", 'J'
30 | "---2MASSJ__ -", 'J'
31 | " 2masSkS-", 'KS'
32 | "-_2maSsKS _", 'KS'
33 | "- _2mAsSkS_", 'KS'
34 | "___-2MasSks_", 'KS'
35 | " 2MAss_ Ks--", 'KS'
36 | "_- 2m--h- -_-", 'H'
37 | "-2m--_-h- -", 'H'
38 | "__--_2m-_--H-", 'H'
39 | " _-_2M--- h_", 'H'
40 | "__ 2M-h-", 'H'
41 | "__-_2Mh__-", 'H'
42 | "_2M_h _-_", 'H'
43 | "-2MH-", 'H'
44 | "-_ 2mj-__", 'J'
45 | " _2M_ _j -", 'J'
46 | "___2M _ j -__", 'J'
47 | "--_- 2Mj", 'J'
48 | "--2Mj _", 'J'
49 | " _2MJ____", 'J'
50 | " ---2MJ ", 'J'
51 | "___2MJ_", 'J'
52 | "- 2m_KS-- _", 'KS'
53 | " __2M-ks _---", 'KS'
54 | " -2Mks-", 'KS'
55 | "-2M--_ks__--", 'KS'
56 | "__2M_kS -___", 'KS'
57 | "- 2M-KS-", 'KS'
58 | "-__2M--KS --", 'KS'
59 | "____-h2mass---", 'H'
60 | "_- --h _ __(- 2masS-)-", 'H'
61 | "__h__ (_-2mAss_)_", 'H'
62 | "__--h_ _ (_---2mAsS__- )_ -", 'H'
63 | "-h2mAsS - _-", 'H'
64 | "-_h-_2MaSS__", 'H'
65 | "_---H_ (_- 2mass)-__-", 'H'
66 | "-- H -_ _(_-2MAsS---) ", 'H'
67 | "--H2MASS -_ ", 'H'
68 | "_--h-2m-_-_-", 'H'
69 | "-h__- (_2m-)_", 'H'
70 | "-h--__ 2m", 'H'
71 | "_-__-h2M_ _", 'H'
72 | "- --h2M--", 'H'
73 | "-h_ _2M_", 'H'
74 | "-h2M-", 'H'
75 | " --_-H2m_", 'H'
76 | "-H--2M ---", 'H'
77 | "__j (__ -2masS ) -", 'J'
78 | "___-j2mASS", 'J'
79 | " - _-j-_2Mass-- _", 'J'
80 | "- j2MasS", 'J'
81 | "_-_j____2MASs_--", 'J'
82 | "_-_J_--2masS_-_", 'J'
83 | " -J_2mAss--_-", 'J'
84 | "--_J__-( _2mASS- --)-_", 'J'
85 | "___J_(2Mass_- )-", 'J'
86 | "__ _J-(----2MASS-_ )-_--", 'J'
87 | " - -j-2m--", 'J'
88 | "_j -__ (_-2m__ )-", 'J'
89 | "--j-__ 2m-___", 'J'
90 | " j2M-", 'J'
91 | "____j_ -2M -- ", 'J'
92 | "-_j2M__", 'J'
93 | " -J2m-_", 'J'
94 | " J_- -_(----2m-) -", 'J'
95 | " J-_2m-", 'J'
96 | "-_ J- (-2m- _ )_- ", 'J'
97 | "__J_ -( --2M- _)__", 'J'
98 | "_-J2M_- ", 'J'
99 | "_-J2M_-_-", 'J'
100 | "_ ks(- - 2Mass--) _", 'KS'
101 | "-_ks__- (-2Mass__-)-", 'KS'
102 | " -- ks _ ( - 2MAss___)- _", 'KS'
103 | "- _-kS -( _-2MaSs_--)_ _ ", 'KS'
104 | "__ Ks -_2MAsS-", 'KS'
105 | " ___-KS-(__-_2mASs-)_", 'KS'
106 | " KS- 2MASs -", 'KS'
107 | "-_-ks2M--- ", 'KS'
108 | " _--kS- _ 2m-", 'KS'
109 | " -_ kS2M-__-", 'KS'
110 | "_-_kS-- _(- 2M-)-", 'KS'
111 | "- -kS2M-___", 'KS'
112 | "--kS2M __-", 'KS'
113 | "__Ks2M-", 'KS'
114 | "_ KS _2m _", 'KS'
115 | "_KS2m_-", 'KS'
116 | "-__KS_- (-___-2m_)---", 'KS'
117 | "-_ KS2M_-", 'KS'
118 |
--------------------------------------------------------------------------------
/juicer/gui/about.glade:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 5
7 | True
8 | center-on-parent
9 | img/lemon.png
10 | dialog
11 | True
12 | True
13 | True
14 | False
15 | Lemon Juicer
16 | x.x.x
17 | Copyright © YYYY Víctor Terrón & Matilde Fernández
18 | Institute of Astrophysics of Andalusia, IAA-CSIC
19 | Lemon Juicer is a scientific tool for automated variable stars detection and analysis and part of the LEMON (Long-term Photometric Monitoring) pipeline.
20 | https://github.com/vterron/lemon
21 | Lemon Juicer is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
22 |
23 | Lemon Juicer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
24 |
25 | You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
26 | Víctor Terrón <vterron@iaa.es>
27 | Matilde Fernández <matilde@iaa.es>
28 | Sofía León (lemon icon)
29 | Asher Abbasi (compass icon)
30 | Artwork license: CC BY-NC-ND 3.0
31 | img/lemon.png
32 | True
33 |
34 |
35 | True
36 | vertical
37 | 2
38 |
39 |
40 |
41 |
42 |
43 | True
44 | end
45 |
46 |
47 | False
48 | end
49 | 0
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/test/test_data/filters/Gunn:
--------------------------------------------------------------------------------
1 | # A series of two-element tuples, one per line, containing two strings.
2 | # The first element must be the name of the Gunn photometric filter,
3 | # while the second is the letter that the Passband class must identify
4 | # when the former is parsed.
5 |
6 | "U Gunn", 'U'
7 | "V Gunn", 'V'
8 | "G (Gunn)", 'G'
9 | "R (Gunn)", 'R'
10 |
11 | "uGunn", 'U'
12 | "vGunn", 'V'
13 | "GunnG", 'G'
14 | "GunnR", 'R'
15 |
16 | # These are *real* examples of Gunn filters, provided by Javier Blasco,
17 | # who found them among the FITS images of several astronomical campaigns.
18 |
19 | "RGun", 'R'
20 | "rgunn", 'R'
21 | "rGunn", 'R'
22 | "Rgunn", 'R'
23 |
24 | # Extremely convoluted test cases
25 |
26 | "--_-g_( _-_gUn- )", 'G'
27 | "_ g Gun- -", 'G'
28 | "_ __Ggun_ -", 'G'
29 | "_-_G_-_gun-__", 'G'
30 | "-_ G__-_(GUN-__- )-_-", 'G'
31 | "__ggUnN---", 'G'
32 | " _g--_GunN_---_", 'G'
33 | "_-gGuNN-", 'G'
34 | "-g---(_--GUnn _-)___-", 'G'
35 | "__ G___GUnn --_", 'G'
36 | " guNg__-", 'G'
37 | "--guN -- g ", 'G'
38 | "__-gUNg---", 'G'
39 | "_ _-_Gung__", 'G'
40 | "_-GuNg__-", 'G'
41 | "__GUNG- ", 'G'
42 | " gunng-_ ", 'G'
43 | "- _gunNg ", 'G'
44 | " guNnG _", 'G'
45 | "_GunN__ -_G -", 'G'
46 | " --guNnR - ", 'R'
47 | " guNN----R__--", 'R'
48 | "-_gUNNr ", 'R'
49 | "_Gunnr--", 'R'
50 | "_- GunNr__", 'R'
51 | " -GUnN r -_", 'R'
52 | "_ -gunN--_U_", 'U'
53 | "_- _gUnnU ---", 'U'
54 | "Gunn___ u_--", 'U'
55 | " ____GunNu_-", 'U'
56 | "-GUnNU _", 'U'
57 | "-gunNV -", 'V'
58 | " guNn-__v --", 'V'
59 | "-__ guNn- v___", 'V'
60 | "_-guNN_- V__", 'V'
61 | "--guNR____", 'R'
62 | "_-- gUNr", 'R'
63 | "_gUn u-- __", 'U'
64 | "-___GUnU___", 'U'
65 | "___-GUN- U__-", 'U'
66 | "_-__-gun-_v__", 'V'
67 | "_--gunV ", 'V'
68 | "_Gun_v__-", 'V'
69 | "_GUnV--", 'V'
70 | "_GUNv-", 'V'
71 | " GUNV-__", 'V'
72 | "_GUNV_-_--", 'V'
73 | "-_--GUNV", 'V'
74 | "- rGuNn --_", 'R'
75 | "-r-_-_GUnn -__", 'R'
76 | "- _r (_-_GUNN_ -)_ - ", 'R'
77 | "-_R- (_guNn-)", 'R'
78 | "-RguNN ", 'R'
79 | "- -_RGUnn _-_", 'R'
80 | "_- _R___GUnN_- ", 'R'
81 | " r-guN ", 'R'
82 | "_-_-rguN__-", 'R'
83 | "- _-r_( _guN_-)-_ _", 'R'
84 | " r_(-__-gUn )-", 'R'
85 | "-_-rgUn---", 'R'
86 | "-__rgUN-", 'R'
87 | "___rGuN_-_", 'R'
88 | "-_-_-rGuN-", 'R'
89 | "----r-(_GuN--_) ", 'R'
90 | "-___R__gun--", 'R'
91 | "--_-R---_gun-__", 'R'
92 | "-RgUn ", 'R'
93 | "_-_ RGuN ", 'R'
94 | "-RGuN ", 'R'
95 | "_R-(__--GUN )-_ -", 'R'
96 | "u-guNn___-", 'U'
97 | "u- _( ---_gUnn_-)_", 'U'
98 | "_ u (__gUnN-)_-", 'U'
99 | "_-_ ugUNn", 'U'
100 | "__--u_ GUNn--_", 'U'
101 | "-u__-( _GUNn _ ) ", 'U'
102 | " -_UGUnN_--", 'U'
103 | "-_U-(__-GUNN )___ _", 'U'
104 | "-_u ---(- gun__-__)_", 'U'
105 | "_-u gUN---", 'U'
106 | "---_u- (--- Gun---)____", 'U'
107 | "__uGUN _", 'U'
108 | "-_ -_u__-GUN - _-", 'U'
109 | "__-Ugun ", 'U'
110 | "--U_-_(-GUn )-", 'U'
111 | "- UGUN-", 'U'
112 | "----_U_(--GUN_--)--_", 'U'
113 | "_ -v_gUNN-", 'V'
114 | "_vGUnN__", 'V'
115 | "--v_GUNN_", 'V'
116 | "- -V -( gunN--_) __", 'V'
117 | "-__V__( _guNn) _", 'V'
118 | " --VgUNn ", 'V'
119 | " _V GunN _", 'V'
120 | "V __(_GUnN_ )-_-_ ", 'V'
121 | "_vGun_", 'V'
122 | "-___v_( __-GuN _-- )", 'V'
123 | "-- V ___gun- -", 'V'
124 | " V----(-GUn-- )-", 'V'
125 | "_VGUn ", 'V'
126 |
--------------------------------------------------------------------------------
/util/gtkutil.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | # Copyright (c) 2012 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify it
9 | # under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | import contextlib
22 | import functools
23 | import gtk
24 |
25 |
26 | @contextlib.contextmanager
27 | def destroying(thing):
28 | try:
29 | yield thing
30 | finally:
31 | thing.destroy()
32 |
33 |
34 | def show_message_dialog(
35 | parent_window, title, msg, msg_type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_CLOSE
36 | ):
37 |
38 | kwargs = dict(type=msg_type, buttons=buttons, message_format=msg)
39 | msg_dlg = gtk.MessageDialog(**kwargs)
40 | msg_dlg.set_title(title)
41 | msg_dlg.set_transient_for(parent_window)
42 | msg_dlg.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
43 | res = msg_dlg.run()
44 | msg_dlg.destroy()
45 | return res
46 |
47 |
48 | show_error_dialog = functools.partial(show_message_dialog, msg_type=gtk.MESSAGE_ERROR)
49 |
50 |
51 | @contextlib.contextmanager
52 | def gtk_sync():
53 | """A context manager to force an update to the GTK application window.
54 |
55 | By design, all GTK events (including window refreshing and updates) are
56 | handled in the main loop, which cannot handle window update events while
57 | the application or callback code is running [1]. This means that nothing
58 | will happen in the application windows unless we explicitly tell GTK to
59 | process any events that have been left pending. This is what this context
60 | manager does, running any pending iteration of the main loop immediately
61 | before and after the 'with' block.
62 |
63 | [1] PyGTK: FAQ Entry 3.7
64 | http://faq.pygtk.org/index.py?req=show&file=faq03.007.htp
65 |
66 | """
67 |
68 | def sync():
69 | # Ensure rendering is done immediately
70 | while gtk.events_pending():
71 | gtk.main_iteration()
72 |
73 | sync()
74 | try:
75 | yield
76 | finally:
77 | sync()
78 |
79 |
80 | @contextlib.contextmanager
81 | def disable_while(widget):
82 | """A context manager to temporarily disable a GTK widget.
83 |
84 | Use the gtk.Widget.set_sensitive() method to set the 'sensitive' property
85 | of the widget to False (therefore disabling it, so that it appears 'grayed
86 | out' and the user cannot interact with it) when the block is entered and to
87 | True (enabling it again) after the block is exited. In this manner, we can
88 | disable a GTK widget while in the 'with block'.
89 |
90 | """
91 |
92 | with gtk_sync():
93 | widget.set_sensitive(False)
94 |
95 | yield
96 |
97 | with gtk_sync():
98 | widget.set_sensitive(True)
99 |
--------------------------------------------------------------------------------
/Doc/user/install.rst:
--------------------------------------------------------------------------------
1 | .. _install:
2 |
3 | Installation
4 | ============
5 |
6 | Release v\ |version|.
7 |
8 | The installation of LEMON is a somewhat tedious —although not particularly difficult— process, involving several dependencies for which there not exist Debian packages.
9 |
10 | These are the steps to install LEMON on a fresh `Debian 7`_ machine:
11 |
12 | 1. ``apt-get install git python-pip csh``
13 | #. ``apt-get build-dep python-matplotlib python-scipy``
14 | #. ``apt-get install openmpi-dev``
15 | #. ``easy_install -U distribute``
16 | #. ``git clone --branch v0.3 git://github.com/vterron/lemon.git ~/lemon``
17 | #. ``cd ~/lemon``
18 | #. ``pip install "numpy>=1.7.1"``
19 | #. ``pip install -r pre-requirements.txt`` # :download:`[View] <../../pre-requirements.txt>`
20 | #. ``pip install -r requirements.txt`` # :download:`[View] <../../requirements.txt>`
21 |
22 | #. Install IRAF_.
23 | #. Install SExtractor_ (version 2.19.5 or newer) [#]_
24 | #. Install `Astrometry.net`_.
25 | #. Install the MPI-enabled Montage_ binaries [#]_
26 | #. ``python ./setup.py``
27 | #. ``echo 'PATH=$PATH:~/lemon' >> ~/.bashrc``
28 | #. ``echo "source ~/lemon/lemon-completion.sh" >> ~/.bashrc``
29 | #. ``./run_tests.py`` — optional, although recommended!
30 |
31 | Note that in October 2018 NOAO began the transition of IRAF to an end-of-support state. IRAF is now maintained by the `iraf-community project `_ on GitHub, which integrates any available patches into the source code. You can download the IRAF 2.16.1 snapshot `here `_.
32 |
33 | .. [#] The important thing to `keep in mind `_ is that SExtractor does not rely on the CLAPACK_ implementation of LAPACK_ — instead, it only uses the subset of the LAPACK functions available in ATLAS_. That is the reason why, in case the ``liblapack-dev`` package is installed, you may encounter an error such as :code:`configure: error: CBLAS/LAPack library files not found at usual locations! Exiting`. If that is your case, you may need to do something like this:
34 |
35 | .. code:: bash
36 |
37 | cd ./sextractor-2.19.5
38 | apt-get install fftw3-dev libatlas-base-dev
39 | update-alternatives --set liblapack.so /usr/lib/atlas-base/atlas/liblapack.so
40 | ./configure --with-atlas-incdir=/usr/include/atlas
41 | make
42 | make install
43 |
44 | .. [#] Edit these two lines in ``Montage/Makefile.LINUX`` before doing ``make``
45 |
46 | .. code:: bash
47 |
48 | # uncomment the next two lines to build MPI modules
49 | # MPICC = mpicc
50 | # BINS = $(SBINS) $(MBINS)
51 |
52 |
53 | .. note::
54 |
55 | LEMON is **not** yet available on PyPI_, but we intend to package it soon. This will enormously simplify the installation process, which should consist of a single ``pip install lemon`` command — provided that IRAF_, SExtractor_, `Astrometry.net`_ and Montage_ are already installed on your system.
56 |
57 | .. _Debian 7: https://www.debian.org/releases/wheezy/
58 | .. _IRAF: http://iraf.noao.edu/
59 | .. _SExtractor: http://www.astromatic.net/software/sextractor
60 | .. _Astrometry.net: http://astrometry.net/use.html
61 | .. _Montage: http://montage.ipac.caltech.edu/docs/download2.html
62 | .. _CLAPACK: http://www.netlib.org/clapack/
63 | .. _LAPACK: http://www.netlib.org/lapack/
64 | .. _ATLAS: http://math-atlas.sourceforge.net/
65 | .. _PyPI: https://pypi.python.org/pypi
66 |
--------------------------------------------------------------------------------
/test/integration/README.md:
--------------------------------------------------------------------------------
1 | # WASP-10b integration test
2 |
3 | LEMON has [an integration test](./wasp10b.py) that reduces a transit of exoplanet [WASP-10b](https://en.wikipedia.org/wiki/WASP-10b), comparing the resulting light curve to that in a [golden file](./WASP10b-golden-curve.txt). Although this test doesn't exercise the entire pipeline, it covers the two critical commands: `photometry` (to perform aperture photometry) and `diffphot` (to generate the light curves). These are the steps that the integration test follows:
4 |
5 | 1. Downloads a 2.3 GiB `.xz` file with [the test data](#test-data).
6 | * The URL of the file is read from the `WASP10_URL` environment variable.
7 | 1. Extracts and verifies the SHA-1 checksums of the test data.
8 | 1. Runs `lemon photometry` on the test FITS images.
9 | * Instead of relying on [SExtractor](http://www.astromatic.net/software/sextractor) for the detection of stars, we use a [hand-curated list](./WASP10b-coordinates.txt) of just 58 stars in the field of view. This considerably improves the execution time of the light curve generation (in the subsequent step), given the quadratic complexity of [Broeg's algorithm](http://adsabs.harvard.edu/abs/2005AN....326..134B).
10 | 1. Runs `lemon diffphot`, generating the light curve of all the objects.
11 | 1. Runs `lemon export`, writing the light curve of WASP-10 to a text file.
12 | 1. Compares the exported light curve to those in [the golden file](./WASP10b-golden-curve.txt).
13 | * For each data point, compares three values: Julian Date, differential magnitude and signal-to-noise ratio.
14 | * For each (floating-point) value, uses [`assertAlmostEqual()`](https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertAlmostEqual) with `places=6`.
15 |
16 | The light curve is generated for multiple values of `--cores`, including a single CPU. This exercises the multiprocessing logic, making sure that light curves generated in parallel are absolutely independent of each other: the resulting light curves are always the same, regardless of the number of CPUs among which the work was divided.
17 |
18 | This integration test is part of [our Travis CI configuration](../.travis.yml), and thus runs automatically for any code change.
19 |
20 | ## Test data
21 |
22 | The FITS files used in the integration test are 182 images of a transit of [exoplanet WASP-10b](https://en.wikipedia.org/wiki/WASP-10b). These images were taken by our team with [the 1.5 telescope](https://www.osn.iaa.csic.es/en/page/15-m-telescope) at the [Sierra Nevada Observatory](https://www.osn.iaa.csic.es/en/) (Granada, Spain) on August 4th 2011. The data is compressed into a 2.3 GiB `.xz` file stored on @vterron's [NextCloud](https://nextcloud.com/) server. For this reason, the URL of the file is stored in [an encrypted environment variable](https://docs.travis-ci.com/user/environment-variables/#defining-encrypted-variables-in-travisyml), and not publicly available. Although not infallible, the goal of this approach is merely to avoid unnecesarily exposing the address of a personal NextCloud server.
23 |
24 | ### Obtaining a copy
25 |
26 | If you'd like to download a copy of WASP-10b's transit FITS images, please [file an issue](https://github.com/vterron/lemon/issues/new) and we'll share them with you.
27 |
28 | ### Plotting the exoplanet transit
29 |
30 | This is what the WASP-10b's transit in [the golden file](./WASP10b-golden-curve.txt) looks like:
31 |
32 | 
33 |
--------------------------------------------------------------------------------
/defaults.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | # Copyright (c) 2012 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | """ Definition of the default options used by the different modules """
22 |
23 | import multiprocessing
24 |
25 | # LEMON modules
26 | import setup
27 |
28 | desc = {} # option descriptions (for optparse)
29 |
30 | ncores = multiprocessing.cpu_count()
31 | desc["ncores"] = (
32 | "the maximum number of cores available to the module. This option "
33 | "defaults to the number of CPUs in the system, which are automatically "
34 | "detected [default: %default]"
35 | )
36 |
37 | maximum = 50000
38 | desc["maximum"] = (
39 | "the CCD saturation level, in ADUs. Those star which have one or more "
40 | "pixels above this value are considered to be saturated. Note that, for "
41 | "coadded images, the effective saturation level is obtained by multiplying "
42 | "this value by the number of coadds (see --coaddk option) [default: %default]"
43 | )
44 |
45 | margin = 0
46 | desc["margin"] = (
47 | "the width, in pixels, of the areas adjacent to the edges that will be "
48 | "ignored when detecting sources on the reference image. Stars whose center "
49 | "is fewer than 'margin' pixels from any border (horizontal or vertical) of "
50 | "the FITS image are not considered. [default: %default]"
51 | )
52 |
53 | verbosity = 0
54 | desc["verbosity"] = (
55 | "increase the amount of information given during the execution. A single "
56 | "-v tracks INFO events, while two or more enable DEBUG messages. These "
57 | "are probably only useful when debugging the module."
58 | )
59 |
60 | snr_percentile = 25
61 | desc["snr_percentile"] = (
62 | "the score at the percentile of the signal-to-noise ratio of the "
63 | "astronomical objects that are to be included in the calculation of the "
64 | "FWHM / elongation of a FITS image. Objects whose SNR is below this "
65 | "score are excluded. Note that the percentile is calculated taking into "
66 | "account only the astronomical objects within the image margins (see "
67 | "the '--margin' option) [default: %default]"
68 | )
69 |
70 | desc["mean"] = (
71 | "in order to compute the FWHM / elongation of a FITS image, take the "
72 | "arithmetic mean of the astronomical objects (detected by SExtractor) "
73 | "instead of the median. So you say you prefer a non-robust statistic?"
74 | )
75 |
76 | desc["filter"] = (
77 | "The supported systems are Johnson, Cousins, Gunn, SDSS, 2MASS, Stromgren "
78 | "and H-alpha, but letters, designating a particular section of the "
79 | "electromagnetic spectrum, may also be used without a system (e.g., 'V'). "
80 | "In addition to the built-in photometric systems, custom filters are "
81 | "supported via the %s configuration file." % setup.CONFIG_FILENAME
82 | )
83 |
--------------------------------------------------------------------------------
/util/queue.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | # Copyright (c) 2019 Victor Terron. All rights reserved.
4 | #
5 | # This file is part of LEMON.
6 | #
7 | # LEMON is free software: you can redistribute it and/or modify it
8 | # under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation, either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program. If not, see .
19 |
20 |
21 | import multiprocessing
22 | import multiprocessing.queues
23 |
24 |
25 | class SharedCounter(object):
26 | """A synchronized shared counter.
27 |
28 | The locking done by multiprocessing.Value ensures that only a single
29 | process or thread may read or write the in-memory ctypes object. However,
30 | in order to do n += 1, Python performs a read followed by a write, so a
31 | second process may read the old value before the new one is written by the
32 | first process. The solution is to use a multiprocessing.Lock to guarantee
33 | the atomicity of the modifications to Value.
34 |
35 | This class comes almost entirely from Eli Bendersky's blog:
36 | http://eli.thegreenplace.net/2012/01/04/shared-counter-with-pythons-multiprocessing/
37 |
38 | """
39 |
40 | def __init__(self, n=0):
41 | self.count = multiprocessing.Value("i", n)
42 |
43 | def increment(self, n=1):
44 | """ Increment the counter by n (default = 1) """
45 | with self.count.get_lock():
46 | self.count.value += n
47 |
48 | @property
49 | def value(self):
50 | """ Return the value of the counter """
51 | return self.count.value
52 |
53 |
54 | class Queue(multiprocessing.queues.Queue):
55 | """A portable implementation of multiprocessing.Queue.
56 |
57 | Because of multithreading / multiprocessing semantics, Queue.qsize() may
58 | raise the NotImplementedError exception on Unix platforms like Mac OS X
59 | where sem_getvalue() is not implemented. This subclass addresses this
60 | problem by using a synchronized shared counter (initialized to zero) and
61 | increasing / decreasing its value every time the put() and get() methods
62 | are called, respectively. This not only prevents NotImplementedError from
63 | being raised, but also allows us to implement a reliable version of both
64 | qsize() and empty().
65 |
66 | """
67 |
68 | def __init__(self, *args, **kwargs):
69 | super(Queue, self).__init__(*args, **kwargs)
70 | self._size = SharedCounter(0)
71 |
72 | def put(self, *args, **kwargs):
73 | super(Queue, self).put(*args, **kwargs)
74 | self._size.increment(1)
75 |
76 | def get(self, *args, **kwargs):
77 | item = super(Queue, self).get(*args, **kwargs)
78 | self._size.increment(-1)
79 | return item
80 |
81 | def qsize(self):
82 | """ Reliable implementation of multiprocessing.Queue.qsize() """
83 | return self._size.value
84 |
85 | def empty(self):
86 | """ Reliable implementation of multiprocessing.Queue.empty() """
87 | return not self.qsize()
88 |
89 | def clear(self):
90 | """ Remove all elements from the Queue. """
91 | while not self.empty():
92 | self.get()
93 |
--------------------------------------------------------------------------------
/util/test/test_queue.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | # Copyright (c) 2019 Victor Terron. All rights reserved.
4 | #
5 | # This file is part of LEMON.
6 | #
7 | # LEMON is free software: you can redistribute it and/or modify it
8 | # under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation, either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program. If not, see .
19 |
20 | from __future__ import division
21 |
22 | import Queue
23 | import time
24 | from Queue import Empty, Full
25 |
26 |
27 | # LEMON modules
28 | from test import unittest
29 | from util import Queue
30 |
31 |
32 | class QueueTest(unittest.TestCase):
33 | def shortDescription(self):
34 | """Don't use the first line of the test method's docstring."""
35 | pass
36 |
37 | def test_put_and_get(self):
38 | """Test that this is a FIFO queue."""
39 |
40 | q = Queue()
41 | q.put(5)
42 | q.put(7)
43 | q.put(9)
44 |
45 | self.assertEqual(5, q.get())
46 | self.assertEqual(7, q.get())
47 | self.assertEqual(9, q.get())
48 |
49 | def test_put_when_queue_is_full(self):
50 | """Test that if Full is raised, qsize() doesn't break."""
51 |
52 | q = Queue(maxsize=1)
53 | q.put(5)
54 | self.assertEqual(1, q.qsize())
55 | with self.assertRaises(Full):
56 | q.put(7, block=False)
57 |
58 | # Internal counter doesn't change, see:
59 | # https://github.com/vterron/lemon/pull/103
60 | self.assertEqual(1, q.qsize())
61 |
62 | # Pause to allow queue to complete operation before terminating
63 | # Otherwise sometimes results in 'IOError: [Errno 32] Broken pipe'
64 | time.sleep(0.1)
65 |
66 | def test_get_when_queue_is_empty(self):
67 | """Test that if Empty is raised, qsize() doesn't break."""
68 |
69 | q = Queue()
70 | self.assertEqual(0, q.qsize())
71 | with self.assertRaises(Empty):
72 | q.get(block=False)
73 |
74 | # Internal counter doesn't change, see:
75 | # https://github.com/vterron/lemon/pull/103
76 | self.assertEqual(0, q.qsize())
77 | time.sleep(0.1)
78 |
79 | def test_qsize(self):
80 | """Test that qsize() gets updated correctly as we put() and get()."""
81 |
82 | q = Queue()
83 | self.assertEqual(0, q.qsize())
84 | q.put(5)
85 | self.assertEqual(1, q.qsize())
86 | q.put(7)
87 | self.assertEqual(2, q.qsize())
88 | q.put(9)
89 | self.assertEqual(3, q.qsize())
90 |
91 | q.get()
92 | self.assertEqual(2, q.qsize())
93 | q.get()
94 | self.assertEqual(1, q.qsize())
95 | q.get()
96 | self.assertEqual(0, q.qsize())
97 |
98 | def test_empty(self):
99 | """Test empty()."""
100 |
101 | q = Queue()
102 | self.assertTrue(q.empty())
103 | q.put(1)
104 | self.assertFalse(q.empty())
105 | time.sleep(0.1)
106 |
107 | def test_clear(self):
108 | """Test that clear() empties the queue."""
109 |
110 | q = Queue()
111 | q.put(5)
112 | q.put(7)
113 | q.put(9)
114 |
115 | q.clear()
116 | self.assertEqual(0, q.qsize())
117 |
--------------------------------------------------------------------------------
/util/display.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | # Copyright (c) 2019 Victor Terron. All rights reserved.
4 | #
5 | # This file is part of LEMON.
6 | #
7 | # LEMON is free software: you can redistribute it and/or modify it
8 | # under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation, either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program. If not, see .
19 |
20 | import functools
21 | import sys
22 | import time
23 | import traceback
24 |
25 | # LEMON modules
26 | import style
27 |
28 |
29 | def show_progress(percentage):
30 | """Print a progress bar strikingly similar to that of the wget command.
31 |
32 | Displays a progress bar with the format used as the Unix wget command:
33 | 51%[===============================> ]
34 |
35 | The whole bar, including the percentage and the surrounding square
36 | brackets, has a length of 79 characters, as recommended by the Style Guide
37 | for Python Code (PEP 8). It should also be noted that the progress bar is
38 | printed in the current line, so a new one should be started before calling
39 | the method if the current line is not to be overwritten. It also does not
40 | include a newline character either. The percentage must be in the range
41 | [0, 100].
42 |
43 | """
44 |
45 | if not 0 <= percentage <= 100:
46 | raise ValueError("The value of 'percentage' must be in the range [0,100]")
47 |
48 | length = 79 # the 79 character line recommendation
49 | occupied = 3 + len(style.prefix) # "%" + "[" + "]" + style.prefix
50 |
51 | percent = str(int(percentage))
52 | available = length - occupied - len(percent)
53 | bar = int((available) * int(percent) / 100.0)
54 | spaces = available - bar
55 |
56 | sys.stdout.write(
57 | "\r" + style.prefix + percent + "%[" + bar * "=" + ">" + spaces * " " + "]"
58 | )
59 | sys.stdout.flush()
60 |
61 |
62 | def utctime(seconds=None, suffix=True):
63 | """UTC version of time.ctime.
64 |
65 | Convert a time expressed in seconds since the Unix epoch to a 28-character
66 | string, representing Coordinated Universal Time, of the following form:
67 | 'Sun Jun 20 23:21:05 1993 UTC'. Fractions of a second are ignored. The last
68 | part of the string, ' UTC', is omitted (and therefore a 24-character string
69 | returned) if the 'suffix' argument evaluates to False. If 'seconds' is not
70 | provided or None, the current time as returned by time.time() is used.
71 |
72 | """
73 |
74 | utc_ctime = time.asctime(time.gmtime(seconds))
75 | if suffix:
76 | utc_ctime += " UTC"
77 | return utc_ctime
78 |
79 |
80 | def print_exception_traceback(func):
81 | """Decorator to print the stack trace of an exception.
82 |
83 | This decorator catches any exception raised by the decorated function,
84 | prints its information to the standard output (traceback.print_exc()) and
85 | re-raises it. In particular, this may be used to decorate functions called
86 | through the multiprocessing module, since exceptions in spawned child
87 | processes do not print stack traces.
88 |
89 | The idea for this decorator comes from Chuan Ji's blog:
90 | http://seasonofcode.com/posts/python-multiprocessing-and-exceptions.html
91 |
92 | """
93 |
94 | @functools.wraps(func)
95 | def wrapper(*args, **kwargs):
96 | try:
97 | return func(*args, **kwargs)
98 | except Exception:
99 | traceback.print_exc()
100 | print
101 | raise
102 |
103 | return wrapper
104 |
--------------------------------------------------------------------------------
/sextractor/sextractor.sex:
--------------------------------------------------------------------------------
1 | # Default configuration file for SExtractor 2.3.5
2 | # EB 2004-12-15
3 | #
4 |
5 | #-------------------------------- Catalog ------------------------------------
6 |
7 | CATALOG_NAME test.cat # name of the output catalog
8 | CATALOG_TYPE ASCII_HEAD # "NONE","ASCII_HEAD","ASCII","FITS_1.0"
9 | # or "FITS_LDAC"
10 |
11 | PARAMETERS_NAME sextractor.param # name of the file containing catalog contents
12 |
13 | #------------------------------- Extraction ----------------------------------
14 |
15 | DETECT_TYPE CCD # "CCD" or "PHOTO"
16 | FLAG_IMAGE flag.fits # filename for an input FLAG-image
17 | DETECT_MINAREA 5 # minimum number of pixels above threshold
18 | DETECT_THRESH 1.5 # or , in mag.arcsec-2
19 | ANALYSIS_THRESH 100.0 # or , in mag.arcsec-2
20 |
21 | FILTER Y # apply filter for detection ("Y" or "N")?
22 | FILTER_NAME sextractor.conv # name of the file containing the filter
23 |
24 | DEBLEND_NTHRESH 32 # Number of deblending sub-thresholds
25 | DEBLEND_MINCONT 0.005 # Minimum contrast parameter for deblending
26 |
27 | CLEAN Y # Clean spurious detections? (Y or N)?
28 | CLEAN_PARAM 1.0 # Cleaning efficiency
29 |
30 | MASK_TYPE CORRECT # type of detection MASKing: can be one of
31 | # "NONE", "BLANK" or "CORRECT"
32 |
33 | #------------------------------ Photometry -----------------------------------
34 |
35 | PHOT_APERTURES 5 # MAG_APER aperture diameter(s) in pixels
36 | PHOT_AUTOPARAMS 2.5, 3.5 # MAG_AUTO parameters: ,
37 | PHOT_PETROPARAMS 2.0, 3.5 # MAG_PETRO parameters: ,
38 | #
39 |
40 | SATUR_LEVEL 50000.0 # level (in ADUs) at which arises saturation
41 | SATUR_KEY DUMMY123PI # keyword for the saturation level (in ADUs); this will never be
42 | # read from the header, so we need here a 'dummy' value here.
43 |
44 | MAG_ZEROPOINT 25.0 # the zero point of the magnitude scale (IRAF qphot's zmag)
45 | MAG_GAMMA 4.0 # gamma of emulsion (for photographic scans)
46 | GAIN 0.0 # detector gain in e-/ADU
47 | PIXEL_SCALE 1.0 # size of pixel in arcsec (0=use FITS WCS info)
48 |
49 | #------------------------- Star/Galaxy Separation ----------------------------
50 |
51 | SEEING_FWHM 1.2 # stellar FWHM in arcsec
52 | STARNNW_NAME sextractor.nnw # Neural-Network_Weight table filename
53 |
54 | #------------------------------ Background -----------------------------------
55 |
56 | BACK_SIZE 64 # Background mesh: or ,
57 | BACK_FILTERSIZE 3 # Background filter: or ,
58 |
59 | BACKPHOTO_TYPE GLOBAL # can be "GLOBAL" or "LOCAL"
60 |
61 | #------------------------------ Check Image ----------------------------------
62 |
63 | CHECKIMAGE_TYPE NONE # can be one of "NONE", "BACKGROUND",
64 | # "MINIBACKGROUND", "-BACKGROUND", "OBJECTS",
65 | # "-OBJECTS", "SEGMENTATION", "APERTURES",
66 | # or "FILTERED"
67 | CHECKIMAGE_NAME check.fits # Filename for the check-image
68 |
69 | #--------------------- Memory (change with caution!) -------------------------
70 |
71 | MEMORY_OBJSTACK 12000 # number of objects in stack
72 | MEMORY_PIXSTACK 1800000 # number of pixels in stack
73 | MEMORY_BUFSIZE 4096 # number of lines in buffer
74 |
75 | #----------------------------- Miscellaneous ---------------------------------
76 |
77 | VERBOSE_TYPE NORMAL # can be "QUIET", "NORMAL" or "FULL"
78 | NTHREADS 1 # Number of simultaneous threads for the SMP
79 | # version of SExtractor; use one thread for
80 | # each of the processes spawned in parallel
--------------------------------------------------------------------------------
/lemon:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | # Copyright (c) 2012 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify it
9 | # under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | # Use import hooks, which are added to sys.meta_path, to enforce
22 | # a minimum version of the modules defined in requirements.txt.
23 | import check_versions
24 |
25 | import atexit
26 | import difflib
27 | import logging
28 | import os.path
29 | import requests
30 | import sys
31 |
32 |
33 | API_QUERY_TIMEOUT = 2 # seconds
34 | LEMON_COMMANDS = [
35 | "annuli",
36 | "astrometry",
37 | "diffphot",
38 | "export",
39 | "import",
40 | "juicer",
41 | "mosaic",
42 | "offsets",
43 | "photometry",
44 | "seeing",
45 | ]
46 |
47 |
48 | def show_help(name):
49 | """ Help message, listing all commands, that looks like Git's """
50 |
51 | print "usage: %s [--help] [--version] [--update] COMMAND [ARGS]" % name
52 | print
53 | print "The essential commands are:"
54 | print " astrometry Calibrate the images astrometrically"
55 | print " mosaic Assemble the images into a mosaic"
56 | print " photometry Perform aperture photometry"
57 | print " diffphot Generate light curves"
58 | print " juicer LEMONdB browser and variability analyzer"
59 | print " export Print the light curve of an object"
60 | print
61 | print "The auxiliary, not-always-necessary commands are:"
62 | print " import Group the images of an observing campaign"
63 | print " seeing Discard images with bad seeing or elongated"
64 | print " annuli Find optimal parameters for photometry"
65 |
66 | print
67 | print "See '%s COMMAND' for more information on a specific command." % name
68 |
69 |
70 | if __name__ == "__main__":
71 |
72 | name = os.path.basename(sys.argv[0])
73 |
74 | if len(sys.argv) == 1 or "--help" in sys.argv:
75 | show_help(name)
76 | sys.exit(0)
77 |
78 | command = sys.argv[1]
79 | args = sys.argv[2:]
80 |
81 | if command not in LEMON_COMMANDS:
82 | msg = "%s: '%s' is not a lemon command. See 'lemon --help'."
83 | print msg % (name, command)
84 |
85 | # Show suggestions, if any, when the command does not exist
86 | matches = difflib.get_close_matches(command, LEMON_COMMANDS)
87 | if matches:
88 | print
89 | print "Did you mean",
90 | print len(matches) == 1 and "this?" or "one of these?"
91 | for match in matches:
92 | print " " * 8 + match
93 |
94 | sys.exit(1)
95 |
96 | elif command == "juicer":
97 | import juicer.main
98 |
99 | kwargs = {}
100 | if args:
101 | kwargs["db_path"] = args[0]
102 | juicer.main.main(**kwargs)
103 |
104 | else:
105 |
106 | # Add the name of the command to the script name so that the brief
107 | # summary of the imported script options includes it (for example,
108 | # "lemon photometry" instead of just "lemon".
109 |
110 | sys.argv[0] = "%s %s" % (name, command)
111 |
112 | # The 'import' statement cannot be used as the name of the module
113 | # is only known at runtime. We need to manually invoke __import__
114 | # to import the module by name and then run its main() function.
115 |
116 | module = __import__(command)
117 | module.main(args)
118 |
--------------------------------------------------------------------------------
/Doc/_themes/flask_theme_support.py:
--------------------------------------------------------------------------------
1 | # flasky extensions. flasky pygments style based on tango style
2 | from pygments.style import Style
3 | from pygments.token import (
4 | Keyword,
5 | Name,
6 | Comment,
7 | String,
8 | Error,
9 | Number,
10 | Operator,
11 | Generic,
12 | Whitespace,
13 | Punctuation,
14 | Other,
15 | Literal,
16 | )
17 |
18 |
19 | class FlaskyStyle(Style):
20 | background_color = "#f8f8f8"
21 | default_style = ""
22 |
23 | styles = {
24 | # No corresponding class for the following:
25 | # Text: "", # class: ''
26 | Whitespace: "underline #f8f8f8", # class: 'w'
27 | Error: "#a40000 border:#ef2929", # class: 'err'
28 | Other: "#000000", # class 'x'
29 | Comment: "italic #8f5902", # class: 'c'
30 | Comment.Preproc: "noitalic", # class: 'cp'
31 | Keyword: "bold #004461", # class: 'k'
32 | Keyword.Constant: "bold #004461", # class: 'kc'
33 | Keyword.Declaration: "bold #004461", # class: 'kd'
34 | Keyword.Namespace: "bold #004461", # class: 'kn'
35 | Keyword.Pseudo: "bold #004461", # class: 'kp'
36 | Keyword.Reserved: "bold #004461", # class: 'kr'
37 | Keyword.Type: "bold #004461", # class: 'kt'
38 | Operator: "#582800", # class: 'o'
39 | Operator.Word: "bold #004461", # class: 'ow' - like keywords
40 | Punctuation: "bold #000000", # class: 'p'
41 | # because special names such as Name.Class, Name.Function, etc.
42 | # are not recognized as such later in the parsing, we choose them
43 | # to look the same as ordinary variables.
44 | Name: "#000000", # class: 'n'
45 | Name.Attribute: "#c4a000", # class: 'na' - to be revised
46 | Name.Builtin: "#004461", # class: 'nb'
47 | Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
48 | Name.Class: "#000000", # class: 'nc' - to be revised
49 | Name.Constant: "#000000", # class: 'no' - to be revised
50 | Name.Decorator: "#888", # class: 'nd' - to be revised
51 | Name.Entity: "#ce5c00", # class: 'ni'
52 | Name.Exception: "bold #cc0000", # class: 'ne'
53 | Name.Function: "#000000", # class: 'nf'
54 | Name.Property: "#000000", # class: 'py'
55 | Name.Label: "#f57900", # class: 'nl'
56 | Name.Namespace: "#000000", # class: 'nn' - to be revised
57 | Name.Other: "#000000", # class: 'nx'
58 | Name.Tag: "bold #004461", # class: 'nt' - like a keyword
59 | Name.Variable: "#000000", # class: 'nv' - to be revised
60 | Name.Variable.Class: "#000000", # class: 'vc' - to be revised
61 | Name.Variable.Global: "#000000", # class: 'vg' - to be revised
62 | Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
63 | Number: "#990000", # class: 'm'
64 | Literal: "#000000", # class: 'l'
65 | Literal.Date: "#000000", # class: 'ld'
66 | String: "#4e9a06", # class: 's'
67 | String.Backtick: "#4e9a06", # class: 'sb'
68 | String.Char: "#4e9a06", # class: 'sc'
69 | String.Doc: "italic #8f5902", # class: 'sd' - like a comment
70 | String.Double: "#4e9a06", # class: 's2'
71 | String.Escape: "#4e9a06", # class: 'se'
72 | String.Heredoc: "#4e9a06", # class: 'sh'
73 | String.Interpol: "#4e9a06", # class: 'si'
74 | String.Other: "#4e9a06", # class: 'sx'
75 | String.Regex: "#4e9a06", # class: 'sr'
76 | String.Single: "#4e9a06", # class: 's1'
77 | String.Symbol: "#4e9a06", # class: 'ss'
78 | Generic: "#000000", # class: 'g'
79 | Generic.Deleted: "#a40000", # class: 'gd'
80 | Generic.Emph: "italic #000000", # class: 'ge'
81 | Generic.Error: "#ef2929", # class: 'gr'
82 | Generic.Heading: "bold #000080", # class: 'gh'
83 | Generic.Inserted: "#00A000", # class: 'gi'
84 | Generic.Output: "#888", # class: 'go'
85 | Generic.Prompt: "#745334", # class: 'gp'
86 | Generic.Strong: "bold #000000", # class: 'gs'
87 | Generic.Subheading: "bold #800080", # class: 'gu'
88 | Generic.Traceback: "bold #a40000", # class: 'gt'
89 | }
90 |
--------------------------------------------------------------------------------
/juicer/gui/snr-threshold-dialog.glade:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | True
7 |
8 |
9 | True
10 |
11 |
12 | 5
13 | normal
14 | False
15 |
16 |
17 | True
18 | vertical
19 | 2
20 |
21 |
22 | True
23 | vertical
24 |
25 |
26 | True
27 | Differential magnitudes whose signal-to-noise ratio is below this threshold are not plotted
28 | center
29 | True
30 | word-char
31 |
32 |
33 | False
34 | 5
35 | 0
36 |
37 |
38 |
39 |
40 | True
41 | True
42 |
43 |
44 | True
45 | True
46 | 4
47 | ●
48 | True
49 | 4
50 | snr-threshold-adjustment
51 | True
52 | if-valid
53 |
54 |
55 | False
56 | False
57 | 5
58 | 0
59 |
60 |
61 |
62 |
63 | 5
64 | 1
65 |
66 |
67 |
68 |
69 | False
70 | 1
71 |
72 |
73 |
74 |
75 | True
76 | center
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | False
86 | end
87 | 0
88 |
89 |
90 |
91 |
92 |
93 |
94 | 100
95 | 9999
96 | 1
97 | 10
98 |
99 |
100 |
--------------------------------------------------------------------------------
/export.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 | # encoding: UTF-8
3 |
4 | # Author: Victor Terron (c) 2020
5 | # Email: `echo vt2rron1iaa32s | tr 132 @.e`
6 | # License: GNU GPLv3
7 |
8 | from __future__ import division
9 | from __future__ import print_function
10 | from __future__ import absolute_import
11 | from __future__ import unicode_literals
12 |
13 | _DESCRIPTION = """
14 | Print the light curve of an object stored in a LEMONdB.
15 |
16 | This command takes as input the right ascension and declination of an object,
17 | and finds the one stored in the LEMONdB that's close to these coordinates. It
18 | then prints to standard output the (a) time, (b) differential magnitude and
19 | (c) signal-to-noise ratio of all the points in the light curve of the object
20 | in the specified photometric filter.
21 | """
22 |
23 | import argparse
24 | import astropy.time
25 | import os.path
26 | import prettytable
27 | import re
28 | import sys
29 | import time
30 |
31 | # LEMON modules
32 | import database
33 | import passband
34 | import util.coords
35 | import util
36 |
37 | parser = argparse.ArgumentParser(description=_DESCRIPTION)
38 | parser.add_argument(
39 | "db_path",
40 | metavar="LEMON_DB",
41 | type=str,
42 | help="the LEMON database with the light curves",
43 | )
44 | parser.add_argument(
45 | "ra",
46 | metavar="",
47 | type=float,
48 | help="Right adcension of the astronomical object, " "in decimal degrees.",
49 | )
50 | parser.add_argument(
51 | "dec",
52 | metavar="",
53 | type=float,
54 | help="Declination of the astronomical object, in " "decimal degrees.",
55 | )
56 | parser.add_argument(
57 | "filter",
58 | metavar="",
59 | type=passband.Passband,
60 | help="The name of the photometric filter.",
61 | )
62 | parser.add_argument(
63 | "--decimal_places",
64 | dest="places",
65 | type=int,
66 | default=3,
67 | help="Round floating-point numbers to this many decimal places.",
68 | )
69 | parser.add_argument(
70 | "--output_file",
71 | dest="output",
72 | type=argparse.FileType("w"),
73 | default=sys.stdout,
74 | help="File to which to write the light curve data points",
75 | )
76 |
77 |
78 | def main(arguments=None):
79 |
80 | if arguments is None:
81 | arguments = sys.argv[1:]
82 | args = parser.parse_args(args=arguments)
83 |
84 | with database.LEMONdB(args.db_path) as db:
85 | print("Input coordinates:")
86 | print("α: {} ({})".format(args.ra, util.coords.ra_str(args.ra)))
87 | print("δ: {} ({})".format(args.dec, util.coords.dec_str(args.dec)))
88 |
89 | star_id, distance = db.star_closest_to_world_coords(args.ra, args.dec)
90 | star = db.get_star(star_id)
91 |
92 | print()
93 | print("Selected star:")
94 | print("ID: {}".format(star_id))
95 | print("α: {} ({})".format(star.ra, util.coords.ra_str(star.ra)))
96 | print("δ: {} ({})".format(star.dec, util.coords.dec_str(star.dec)))
97 | print("Distance to input coordinates: {} deg".format(distance))
98 | print()
99 |
100 | if args.output == sys.stdout:
101 | print("Light curve in {!r} photometric filter:".format(args.filter))
102 |
103 | star_diff = db.get_light_curve(star_id, args.filter)
104 | if star_diff is None:
105 | raise ValueError(
106 | "no light curve for {!r} photometric filter".format(args.filter)
107 | )
108 |
109 | table = prettytable.PrettyTable()
110 | table.field_names = ["Date (UTC)", "JD", "Δ Mag", "SNR"]
111 |
112 | def format_float(f):
113 | """Returns f as a string rounded to parser.places decimal places."""
114 | return "{:.{places}f}".format(f, places=args.places)
115 |
116 | for unix_time, magnitude, snr in star_diff:
117 | jd = astropy.time.Time(unix_time, format="unix").jd
118 | table.add_row(
119 | [
120 | util.utctime(unix_time, suffix=False),
121 | format_float(jd),
122 | format_float(magnitude),
123 | format_float(snr),
124 | ]
125 | )
126 |
127 | args.output.write(str(table))
128 | args.output.write("\n")
129 |
130 | if args.output != sys.stdout:
131 | print(
132 | "Wrote light curve in {!r} photometric filter to {!r}.".format(
133 | args.filter, args.output.name
134 | )
135 | )
136 |
137 |
138 | if __name__ == "__main__":
139 | sys.exit(main())
140 |
--------------------------------------------------------------------------------
/keywords.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | # Copyright (c) 2012 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | # Description of the optparse.OptionGroup
22 | group_description = (
23 | "These options customize the FITS keywords from which some required "
24 | "information is extracted. The default values are expected to work "
25 | "well with any file that conforms to the FITS standard. If that is "
26 | "not your case, you should make sure to set these options to the "
27 | "correct values, or otherwise face apocalyptic consequences."
28 | )
29 |
30 | desc = {} # option descriptions (for optparse)
31 | filterk = "FILTER"
32 | desc[
33 | "filterk"
34 | ] = "keyword for the name of the filter of the observation [default: %default]"
35 |
36 | rak = "RA"
37 | desc["rak"] = (
38 | "keyword for the right ascension of the astronomical object(s), expressed "
39 | "either as a floating point number in decimal degrees, or as a string in "
40 | "the 'hh:mm:ss[.sss]' format [default: %default]"
41 | )
42 |
43 | deck = "DEC"
44 | desc["deck"] = (
45 | "keyword for the declination of the astronomical object(s), expressed "
46 | "either as a floating point number in decimal degrees, or as a string "
47 | "in the 'dd:mm:ss[.sss]' format [default: %default]"
48 | )
49 |
50 | datek = "DATE-OBS"
51 | desc["datek"] = (
52 | "keyword for the date of the observation, in the new Y2K compliant "
53 | "date format: 'yyyy-mm-dd' or 'yyyy-mm-ddTHH:MM:SS[.sss] "
54 | "[default: %default]"
55 | )
56 |
57 | timek = "TIME-OBS"
58 | desc["timek"] = (
59 | "keyword for the time at which the observation started, in the format "
60 | "HH:MM:SS[.sss]. This keyword is used in conjunction with --datek to "
61 | "determine the starting time of the observation: --datek gives the "
62 | "starting calendar date and this keyword the time within that day. This "
63 | "keyword is not necessary (and thus this option ignored) if the time is "
64 | "included directly as part of the --datek keyword value with the format "
65 | "yyyy-mm-ddTHH:MM:SS[.sss] [default: %default]"
66 | )
67 |
68 | exptimek = "EXPTIME"
69 | desc["exptimek"] = "keyword for the exposure time [default: %default]"
70 |
71 | airmassk = "AIRMASS"
72 | desc["airmassk"] = "keyword for the airmass [default: %default]"
73 |
74 | gaink = "GAIN"
75 | desc["gaink"] = (
76 | "keyword for the gain of the CCD, in e-/ADU. Needed in order to "
77 | "accurately calculate the SNR of each measurement [default: %default]"
78 | )
79 |
80 | uncimgk = None
81 | desc["uncimgk"] = (
82 | "keyword that stores the path to the uncalibrated image used to check for "
83 | "saturation -- as the overscan, bias and (particularly) flat-fielding steps "
84 | "may take a saturated pixel below the saturation threshold. If (as by "
85 | "default) this option is not set, saturation is checked for on the same "
86 | "image on which we do photometry."
87 | )
88 |
89 | fwhmk = "LEMON FWHM"
90 | desc["fwhmk"] = (
91 | "keyword for the Full Width at Half Maximum (FWHM) of the image, which is "
92 | "written to the FITS header by the 'seeing' command [default: %default]"
93 | )
94 |
95 | objectk = "OBJECT"
96 | desc["objectk"] = "keyword for the name of the object observed [default: %default]"
97 |
98 | typek = "IMAGETYP"
99 | desc["typek"] = (
100 | "keyword that identifies the type of image, with values such as 'dark', "
101 | "'flat' or 'object', to cite some of the most common [default: %default]"
102 | )
103 |
104 | # Used by seeing.FITSeeingImage to 'cache' the SExtractor catalog
105 | sex_catalog = "SEX-CAT"
106 | sex_md5sum = "SEX-MD5"
107 |
108 | coaddk = "NCOADDS"
109 | desc["coaddk"] = (
110 | "keyword for the number of effective coadds. This value is essential to "
111 | "determine the number of counts at which saturation arises in coadded "
112 | "observations. If the keyword is missing, we assume a value of one (that "
113 | "is, that the observation consisted of a single exposure) [default: %default]"
114 | )
115 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | # Copyright (c) 2012 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | from __future__ import with_statement
22 |
23 | """ Just a shadow of what the actual setup.py will look like, this simple
24 | script, when run, creates the IRAF login script (login.cl file) and the user
25 | parameters directory (uparm) in the directory where LEMON is installed. This
26 | directory is automatically detected as it is assumed to be the same in which
27 | this file is located, so it does not depend on where it is executed from.
28 |
29 | """
30 |
31 | import os
32 | import os.path
33 | import errno
34 | import shutil
35 | import subprocess
36 |
37 | MKIRAF_BIN = "mkiraf"
38 | TERM_TYPE = "xgterm"
39 | LOGIN_FILE = "login.cl"
40 | UPARM_DIR = "uparm"
41 | PYRAF_CACHE = "pyraf"
42 |
43 | # The LEMON configuration file
44 | CONFIG_FILENAME = "~/.lemonrc"
45 | CONFIG_PATH = os.path.expanduser(CONFIG_FILENAME)
46 |
47 |
48 | def mkiraf(path):
49 | """Create the IRAF login script and the uparm directory.
50 |
51 | This function implements a high-level wrapper around IRAF's mkiraf, which
52 | initializes the IRAF login script and the user parameters directory in the
53 | path given as input. Any existing login script or uparm directory is
54 | silently overwritten, although mkiraf makes a backup of the former by
55 | appending '.OLD' to its name. In this manner, the original login script
56 | 'login.cl' becomes 'login.cl.OLD'.
57 |
58 | The login script that this function creates chooses 'xgterm' as its
59 | terminal type. It is usually the best choice, being a xterm-like terminal
60 | program written specifically to work with IRAF. Note that, although we are
61 | required to choose a terminal type when mkiraf is run, LEMON never needs
62 | to use it.
63 |
64 | """
65 |
66 | os.chdir(path)
67 |
68 | # Avoid having to answer the "Initialize uparm? (y|n):" question
69 | if os.path.exists(UPARM_DIR):
70 | shutil.rmtree(UPARM_DIR)
71 |
72 | # mkiraf reads the terminal type ("Enter terminal type:") from the user,
73 | # not accepting it as a command line argument. Thus, we need to use a pipe
74 | # to send the terminal type to mkiraf's stdin.
75 | args = [MKIRAF_BIN]
76 | p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
77 | p.communicate(input=TERM_TYPE)
78 |
79 | if p.returncode:
80 | msg = "execution of %s failed" % MKIRAF_BIN
81 | raise subprocess.CalledProcessError(p.returncode, args)
82 |
83 | with open(LOGIN_FILE, "rt") as fd:
84 | for line in fd:
85 | splitted = line.split()
86 | if len(splitted) == 2:
87 | if splitted[0] == "stty":
88 | if splitted[1] == TERM_TYPE:
89 | break
90 | else:
91 | msg = "terminal type wasn't set correctly"
92 | raise ValueError(msg)
93 | else:
94 | msg = "terminal type not defined in %s" % LOGIN_FILE
95 | raise ValueError(msg)
96 |
97 |
98 | def mkdir_p(path):
99 | """Create a directory, give no error if it already exists.
100 |
101 | This implements the functionality of Unix `mkdir -p`, creating a
102 | directory but without giving any error if it already exists and
103 | making parent directories as needed.
104 | [URL] http://stackoverflow.com/a/600612/184363
105 |
106 | """
107 |
108 | try:
109 | os.mkdir(path)
110 | except OSError as exc: # Python >2.5
111 | if exc.errno == errno.EEXIST and os.path.isdir(path):
112 | pass
113 | else:
114 | raise
115 |
116 |
117 | if __name__ == "__main__":
118 |
119 | lemon_path = os.path.dirname(os.path.realpath(__file__))
120 | print "Setting up IRAF's %s in %s ..." % (LOGIN_FILE, lemon_path),
121 | mkiraf(lemon_path)
122 | print "done."
123 |
124 | print "Creating pyraf/ directory for cache...",
125 | pyraf_cache_path = os.path.join(lemon_path, PYRAF_CACHE)
126 | mkdir_p(pyraf_cache_path)
127 | print "done."
128 |
--------------------------------------------------------------------------------
/juicer/config.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | # Copyright (c) 2012 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify it
9 | # under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | import ConfigParser
22 | import os.path
23 |
24 | CONFIG_FILENAME = ".juicerc"
25 | CONFIG_PATH = os.path.expanduser("~/%s" % CONFIG_FILENAME)
26 |
27 | VIEW_SECTION = "view"
28 | VIEW_SEXAGESIMAL = "sexagesimal"
29 | VIEW_DECIMAL = "decimal"
30 | PLOT_AIRMASSES = "airmasses"
31 | PLOT_JULIAN = "julian_dates"
32 | PLOT_MIN_SNR = "snr_threshold"
33 |
34 | DEFAULT_VIEW_SEXAGESIMAL = True
35 | DEFAULT_VIEW_DECIMAL = False
36 | DEFAULT_PLOT_AIRMASSES = True
37 | DEFAULT_PLOT_JULIAN = False
38 | DEFAULT_PLOT_MIN_SNR = 100
39 |
40 | # The color codes can use any of the following formats supported by matplotlib:
41 | # abbreviations ('g'), full names ('green'), hexadecimal strings ('#008000') or
42 | # a string encoding float on the 0-1 range ('0.75') for gray shades.
43 |
44 | COLOR_SECTION = "colors"
45 | DEFAULT_COLORS = dict(
46 | U="violet",
47 | B="blue",
48 | V="green",
49 | R="#ff4246", # light red
50 | I="#e81818", # dark red
51 | Z="cyan",
52 | Y="brown",
53 | J="yellow",
54 | H="pink",
55 | KS="orange",
56 | K="orange",
57 | L="0.75", # light gray
58 | M="0.50",
59 | ) # dark gray
60 |
61 | # The options for how light curves are dumped to plain-text files
62 | CURVEDUMP_SECTION = "curve-export"
63 | DEFAULT_CURVEDUMP_OPTS = dict(
64 | dump_date_text=1,
65 | dump_date_julian=1,
66 | dump_date_seconds=1,
67 | dump_magnitude=1,
68 | dump_snr=1,
69 | dump_max_merr=1,
70 | dump_min_merr=1,
71 | dump_instrumental_magnitude=1,
72 | dump_instrumental_snr=1,
73 | decimal_places=8,
74 | )
75 |
76 |
77 | class Configuration(ConfigParser.SafeConfigParser):
78 | """Just a quite simple wrapper to automatically have the configuration
79 | file loaded at instantiation and written to disk with the update method"""
80 |
81 | DEFAULT_CONFIG = "\n".join(
82 | [
83 | "[%s]" % VIEW_SECTION,
84 | "%s = %d" % (VIEW_SEXAGESIMAL, DEFAULT_VIEW_SEXAGESIMAL),
85 | "%s = %d" % (VIEW_DECIMAL, DEFAULT_VIEW_DECIMAL),
86 | "%s = %d" % (PLOT_AIRMASSES, DEFAULT_PLOT_AIRMASSES),
87 | "%s = %d" % (PLOT_JULIAN, DEFAULT_PLOT_JULIAN),
88 | "%s = %d" % (PLOT_MIN_SNR, DEFAULT_PLOT_MIN_SNR),
89 | "",
90 | "[%s]" % COLOR_SECTION,
91 | ]
92 | + ["%s = %s" % (k, v) for k, v in DEFAULT_COLORS.iteritems()]
93 | + ["", "[%s]" % CURVEDUMP_SECTION]
94 | + ["%s = %s" % (k, v) for k, v in DEFAULT_CURVEDUMP_OPTS.iteritems()]
95 | )
96 |
97 | def __init__(self, path, update=True):
98 | """Parse a configuration file, creating and populating it with
99 | the default options in case 'path' does not exist"""
100 |
101 | ConfigParser.SafeConfigParser.__init__(self)
102 |
103 | if not os.path.exists(path):
104 | with open(path, "wt") as fd:
105 | fd.write(self.DEFAULT_CONFIG)
106 |
107 | self.read([path])
108 | self.path = path
109 |
110 | def color(self, letter):
111 | """ Return the color code to be used for a photometric filter """
112 | return self.get(COLOR_SECTION, letter.upper())
113 |
114 | def update(self):
115 | """ Write to disk the configuration file """
116 | with open(self.path, "wt") as fd:
117 | self.write(fd)
118 |
119 | # SafeConfigParser is an old-style class (does not support properties)
120 | def get_minimum_snr(self):
121 | """ Return the PLOT_MIN_SNR option in the VIEW_SECTION section """
122 | return self.getint(VIEW_SECTION, PLOT_MIN_SNR)
123 |
124 | def set_minimum_snr(self, snr):
125 | """ Set the value of the PLOT_MIN_SNR option in the VIEW_SECTION """
126 | self.set(VIEW_SECTION, PLOT_MIN_SNR, str(int(snr)))
127 |
128 | def dumpint(self, option):
129 | """ Coerce 'option' in the curves export section to an integer """
130 | return self.getint(CURVEDUMP_SECTION, option)
131 |
132 | def dumpset(self, option, value):
133 | """ Set 'option' to 'value' in the curves export section """
134 | self.set(CURVEDUMP_SECTION, option, str(value))
135 |
--------------------------------------------------------------------------------
/json_parse.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | # Copyright (c) 2012 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | import collections
22 | import copy
23 | import json
24 | import operator
25 |
26 | # LEMON modules
27 | import passband
28 |
29 | typename = "CandidateAnnuli"
30 | field_names = "aperture, annulus, dannulus, stdev"
31 |
32 |
33 | class CandidateAnnuli(collections.namedtuple(typename, field_names)):
34 | """Encapsulate the quality of a set of photometric parameters.
35 |
36 | How do we determine how good a set of parameters for aperture photometry
37 | is? In order to compare them, we need to identify the most constant stars
38 | (or, by extension, any other astronomical object) in the field and compute
39 | their light curves. The better the aperture, annulus and dannulus that we
40 | use are, the lower the standard deviation of the resulting curves.
41 |
42 | This class simply encapsulates these four values, mapping the parameters
43 | for aperture photometry (aperture, annulus and dannulus) to the standard
44 | deviation of the light curves of the most constant astronomical objects.
45 |
46 | Fields:
47 | aperture - the aperture radius, in pixels.
48 | annulus - the inner radius of the sky annulus, in pixels.
49 | dannulus - the width of the sky annulus, in pixels.
50 | stdev - the median, arithmetic mean or a similar statistical measure of the
51 | standard deviation of the light curves of the astronomical objects
52 | when photometry is done using these aperture, annulus and dannulus
53 | values.
54 |
55 | """
56 |
57 | @staticmethod
58 | def dump(annuli, path):
59 | """Save a series of CadidateAnnuli objects to a JSON file.
60 |
61 | Serialize 'annuli' to a JSON file. It must be a dictionary which maps
62 | each photometric filter (a Passband object) to a sequence of the
63 | corresponding CandidateAnnuli objects -- i.e., the different aperture
64 | photometric parameters that were evaluated for that filter. The output
65 | file will be mercilessly overwritten if it already exists.
66 |
67 | """
68 |
69 | # Being a subclass of tuple, JSON serializes namedtuples as lists. We
70 | # need to convert them to ordered dictionaries (namedtuple._asdict())
71 | # first, so that the field names are not lost in the serialization.
72 | data = copy.deepcopy(annuli)
73 | for values in data.itervalues():
74 | for index in xrange(len(values)):
75 | values[index] = values[index]._asdict()
76 | values.sort(key=operator.itemgetter("stdev"))
77 |
78 | # Use strings, not Passband objects, as keys
79 | for pfilter in data.keys():
80 | data[str(pfilter)] = data.pop(pfilter)
81 |
82 | with open(path, "wt") as fd:
83 | kwargs = dict(indent=2, sort_keys=True)
84 | json.dump(data, fd, **kwargs)
85 |
86 | @classmethod
87 | def load(cls, path):
88 | """Load a series of CandidateAnnuli objects from a JSON file.
89 |
90 | Deserialize a JSON file created with CandidateAnnuli.dump(), returning
91 | a dictionary which maps each photometric filter (a Passband object) to
92 | a list of the corresponding CandidateAnnuli objects. These lists are
93 | sorted in increasing order by the standard deviation ('stdev' attribute
94 | of the namedtuples), so that the one with the lowest standard deviation
95 | (and therefore the optimal for aperture photometry) is returned first.
96 |
97 | """
98 |
99 | with open(path, "rt") as fd:
100 | data = json.load(fd)
101 |
102 | # Convert the dictionaries back to namedtuples, and then sort them by
103 | # their standard deviation, in increasing order.
104 | for values in data.itervalues():
105 | for index in xrange(len(values)):
106 | values[index] = cls(**values[index])
107 | values.sort(key=operator.attrgetter("stdev"))
108 |
109 | # Use Passband objects as keys
110 | for pfilter in data.keys():
111 | data[passband.Passband(pfilter)] = data.pop(pfilter)
112 |
113 | return data
114 |
--------------------------------------------------------------------------------
/util/log.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | # Copyright (c) 2019 Victor Terron. All rights reserved.
4 | #
5 | # This file is part of LEMON.
6 | #
7 | # LEMON is free software: you can redistribute it and/or modify it
8 | # under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation, either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program. If not, see .
19 |
20 | import logging
21 | import re
22 | import sys
23 | import warnings
24 |
25 |
26 | def func_catchall(func, *args, **kwargs):
27 | """Return func(*args, **kwargs), or None if an exception is raised.
28 |
29 | Return whatever func() returns when called with the positional arguments
30 | 'args' and keyword arguments 'keywords'. If any exception is raised by
31 | func(), catch it, log it and return None. This is a convenience function
32 | useful when we need to call a function that may raise an exception,
33 | scenario in which we want to use None instead of what would have been
34 | normally returned.
35 |
36 | """
37 |
38 | try:
39 | return func(*args, **kwargs)
40 | except Exception as e:
41 | exc_type = sys.exc_info()[0]
42 | args = (func.__name__, exc_type.__name__, str(e))
43 | msg = "%s() raised %s (%s), None returned instead" % args
44 | logging.debug(msg)
45 | return None
46 |
47 |
48 | class StreamToWarningFilter(object):
49 | """A file-like class that matches strings and issues them as warnings.
50 |
51 | This class creates a file-like object that normally writes a string to
52 | 'fd', a file type object. However, those lines that match 'regexp' are
53 | issued as warnings of the 'category' class and logged at INFO level. The
54 | message used for the warning is that matched by the 'msg' named group that
55 | must be present in the regular expression; otherwise, IndexError is raised.
56 | This class may be used (and, in fact, was coded with this purpose in mind),
57 | for example, to capture a message written by a third-party library to the
58 | standard error and issue it as a warning instead.
59 |
60 | For example, consider the scenario where the following object is created:
61 | fd = StreamToWarningFilter(sys.stdout, 'v(?P(\d\.?)+)', UserWarning)
62 | After this, fd.write('v2.19.5') will raise UserWarning (with the message
63 | '2.19.5'), while fd.write('Nobody expects the Spanish inquisition') will
64 | not match the regexp and therefore print the string to sys.stdout.
65 |
66 | """
67 |
68 | def __init__(self, fd, regexp, category):
69 | self.fd = fd
70 | self.regexp = regexp
71 | self.category = category
72 |
73 | def write(self, str_):
74 | """ Write str_ to the file; issue as a warning if regexp is matched"""
75 |
76 | match = re.match(self.regexp, str_)
77 | if match:
78 | msg = match.group("msg")
79 | logging.info(msg)
80 | warnings.warn(msg, self.category)
81 | else:
82 | self.fd.write(str_)
83 |
84 | def flush(self):
85 | self.fd.flush()
86 |
87 | def close(self):
88 | self.fd.close()
89 |
90 |
91 | class LoggerWriter(object):
92 | """Wrap a logger with a file-like API.
93 |
94 | Sometimes, we need to interface to a third-party API which expects a
95 | file-like object to write to, but we want to direct the API's output to a
96 | logger. This can be done using this class, based on that written by Vinay
97 | Sajip [https://stackoverflow.com/a/9422332/184363].
98 |
99 | """
100 |
101 | def __init__(self, level):
102 | """Initialize the LoggerWritter object.
103 |
104 | The argument 'level' must be a valid logging level: 'debug', 'info',
105 | 'warning', 'error' or 'critical', and will correspond to the function
106 | of the logging module that will be used every time the method write()
107 | is called. For example, LoggerWriter('info').write(msg) is equivalent
108 | to logging.info(msg).
109 |
110 | """
111 |
112 | self.log_func = getattr(logging, level)
113 |
114 | def write(self, msg):
115 | self.log_func(msg)
116 |
117 | def flush(self):
118 | """Do nothing -- required by PyRAF.
119 |
120 | This method does nothing, but it is required in order to be able to
121 | redirect the output of the PyRAF tasks to the logger. Otherwise, the
122 | AttributeError ('LoggerWriter' object has no attribute 'flush')
123 | exception is raised.
124 |
125 | """
126 |
127 | pass
128 |
--------------------------------------------------------------------------------
/test/dss_images.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | # Copyright (c) 2013 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | from __future__ import division
22 |
23 | """ This module provides access to a series of FITS images that can be used in
24 | the unit tests. In order to keep the size of the repository as small as
25 | possible, these images are not included along with the source code, but instead
26 | downloaded automatically from the STScI Digitized Sky Survey the first time the
27 | module is imported and copied to the ./test_data/fits directory. Any removed
28 | images will be downloaded again the next time the module is imported. In
29 | practical terms, the only thing that we need to know is that the module-level
30 | variable TEST_IMAGES is a set with the paths to several FITS images that were
31 | transparently downloaded from the Digitized Sky Survey.
32 |
33 | """
34 |
35 | import functools
36 | import re
37 | import os
38 | import os.path
39 | import sys
40 | import urllib
41 |
42 | DATA_DIR = os.path.join(os.path.dirname(__file__), "./test_data")
43 | IMAGES_DIR = os.path.join(DATA_DIR, "fits")
44 |
45 |
46 | def get_dss_image(path, ra, dec):
47 | """Download an image from the STScI Digitized Sky Survey.
48 |
49 | This method uses the DSS CGI script to automatically download a FITS image
50 | from the STScI Digitized Sky Survey, copying it to 'path'. The version of
51 | the survey is POSS2/UKSTU Infrared (second generation, 1 arcsex/pixel).
52 | The rest of the parameters use their default values, which means that the
53 | coordinates are assumed to be J2000 and the dimensions of the FITS image
54 | are 15x15 arcmins.
55 |
56 | DSS Web Access Script Interface Control Document:
57 | http://archive.stsci.edu/dss/script_usage.html
58 |
59 | If any exception is raised by urllib.urlretrieve() (and this includes
60 | KeyboardException, if the user decides to stop the execution), the file
61 | being downloaded is silently removed before the exception is re-raised.
62 | This guarantees that we will not be left with truncated data.
63 |
64 | """
65 |
66 | base_url = "http://archive.stsci.edu/cgi-bin/dss_search?"
67 | parameters = dict(v="poss2ukstu_ir", ra=ra, dec=dec)
68 | url = base_url + urllib.urlencode(parameters)
69 |
70 | # For example, "Downloading test/test_data/fits/IC_5146.fits: 87 %"
71 | status = functools.partial(("Downloading %s: {0:d} %%" % path).format)
72 | sys.stdout.write(status(0))
73 | sys.stdout.flush()
74 |
75 | def update_status(count, block_size, total_size):
76 | percent = int(count * block_size / total_size * 100)
77 | sys.stdout.write("\r" + status(percent))
78 | sys.stdout.flush()
79 |
80 | try:
81 | urllib.urlcleanup()
82 | urllib.urlretrieve(url, filename=path, reporthook=update_status)
83 | except:
84 | try:
85 | os.unlink(path)
86 | except:
87 | pass
88 | raise
89 | finally:
90 | print
91 |
92 |
93 | # Map each object to its right ascension and declination
94 | TEST_OBJECTS = {
95 | "IC 5070": (312.75, 44.37),
96 | "IC 5146": (328.35, 47.267),
97 | "Messier 92": (259.281, 43.136),
98 | "NGC 2264": (100.242, 9.895),
99 | "RMC 136": (98.67, 4.03),
100 | "Serpens": (277.454, 1.247),
101 | "Orion": (83.822, -5.391),
102 | "Trapezium": (83.819, -5.387),
103 | "Trumpler 37": (324.536, 57.447),
104 | "Barnard's Star": (269.452, 4.693),
105 | }
106 |
107 |
108 | def get_image_path(name):
109 | """Determine the local path to which to download the image of an object.
110 |
111 | The base name of the image is that of the astronomical object, but with any
112 | whitespace characters replaced with underscores and the '.fits' extension.
113 | The directory where all the images are downloaded is IMAGES_DIR.
114 |
115 | """
116 | basename = "%s.fits" % re.sub("\s+", "_", name)
117 | path = os.path.join(IMAGES_DIR, basename)
118 | return os.path.normpath(path)
119 |
120 |
121 | if not os.path.exists(IMAGES_DIR):
122 | os.makedirs(IMAGES_DIR)
123 |
124 | TEST_IMAGES = set()
125 |
126 | for name, (ra, dec) in sorted(TEST_OBJECTS.items()):
127 | path = get_image_path(name)
128 | if not os.path.exists(path):
129 | get_dss_image(path, ra, dec)
130 | TEST_IMAGES.add(path)
131 |
--------------------------------------------------------------------------------
/test/test_data/sextractor_noasciihead.cat:
--------------------------------------------------------------------------------
1 | 1 844.359 434.109 100.2910553 +9.4697032 845.428 428.986 0.0016 0.0016 -89.0 3 6.334 8545557 12811.09 5030140 25148.38 8.2460 0.0054 530815.6 8085 0.0006238426 1.562
2 | 2 926.403 493.260 100.3013814 +9.4623269 926.130 492.814 0.0001 0.0001 42.9 7 4.980 1.451282e+08 15277.67 1.379576e+08 10755.87 4.6506 0.0001 1043586 11498 0.0008871913 1.527
3 | 3 868.621 87.703 100.2950454 +9.5130007 868.981 88.247 0.0005 0.0005 84.6 18 1.805 1687773 5936.371 1806055 11282.74 9.3582 0.0068 358059.9 1736 0.0001339506 2.445
4 | 4 824.381 41.622 100.2893902 +9.5186384 824.420 41.680 0.0001 0.0001 -0.2 2 0.949 2445914 2919.919 2354658 1779.543 9.0702 0.0008 503292.4 420 3.240741e-05 1.676
5 | 5 835.545 51.657 100.2908246 +9.5174165 835.552 51.656 0.0001 0.0001 -0.0 2 1.032 4835000 3587.49 4674093 1927.399 8.3258 0.0004 804313.6 634 4.891975e-05 1.835
6 | 6 287.085 29.306 100.2187420 +9.5191253 287.109 29.286 0.0001 0.0001 89.8 0 0.910 1867840 1779.543 1828299 1094.39 9.3449 0.0007 534860.9 156 1.203704e-05 1.067
7 | 7 35.369 24.882 100.1853310 +9.5195156 35.382 24.961 0.0001 0.0001 -0.1 0 0.836 1423676 1527.9 1396500 997.3421 9.6374 0.0008 418107.4 115 8.873457e-06 1.081
8 | 8 806.948 17.857 100.2871619 +9.5215485 808.116 19.477 0.1955 0.1955 -23.3 3 4.374 6874.336 668.2784 22301.68 2584.307 14.1292 0.1258 546.2891 22 1.697531e-06 2.222
9 | 9 813.876 8.434 100.2880866 +9.5227352 810.118 12.496 0.2091 0.2091 0.1 18 4.084 10759.01 854.8647 19686.22 2014.935 14.2646 0.1112 485.2656 36 2.777778e-06 3.162
10 | 10 877.773 13.191 100.2964237 +9.5223040 878.442 12.535 0.1898 0.1898 -77.3 18 3.500 7334.547 697.9941 16434.45 2413.721 14.4606 0.1595 606.8359 24 1.851852e-06 1.842
11 | 11 1171.949 11.431 100.3346245 +9.5232353 1171.984 11.416 0.0018 0.0017 -90.0 0 0.859 114979.5 767.2645 116282.1 878.29 12.3362 0.0082 33955.11 29 2.237654e-06 1.042
12 | 12 1007.948 2.728 100.3134096 +9.5239382 1007.846 3.188 0.0709 0.0643 0.0 27 2.748 23481.08 1017.492 27160.19 1534.529 13.9152 0.0614 1542.508 51 3.935185e-06 1.498
13 | 13 1016.912 6.631 100.3145610 +9.5234716 1016.964 6.723 0.0003 0.0003 89.9 19 0.865 626432.2 1305.827 614346.3 1046.991 10.5290 0.0019 197852.5 84 6.481482e-06 1.071
14 | 14 1023.253 5.818 100.3153875 +9.5235891 1023.261 5.874 0.0007 0.0007 -0.5 19 0.870 305333.6 1094.39 301237.5 987.1127 11.3027 0.0036 91746.89 59 4.552469e-06 1.086
15 | 15 769.909 8.013 100.2823328 +9.5226777 769.966 8.012 0.0008 0.0008 -0.7 0 0.830 222962.2 842.9079 222821.9 866.6564 11.6301 0.0042 85319.17 35 2.700617e-06 1.026
16 | 16 128.857 6.777 100.1976740 +9.5217104 128.933 6.855 0.0007 0.0007 89.5 16 0.874 303882 997.3421 302489.8 923.3594 11.2982 0.0033 105111.1 49 3.780864e-06 1.042
17 | 17 1952.364 7.778 100.4336796 +9.5243017 1952.359 7.854 0.0261 0.0238 -0.6 0 0.792 7049.852 493.5564 6605.125 712.3872 15.4503 0.1171 2235.203 12 9.259259e-07 1.190
18 | 18 1465.682 8.021 100.3723484 +9.5242123 1465.569 8.040 0.0342 0.0314 -0.0 0 0.850 5593.281 450.5532 6215.094 697.9941 15.5164 0.1220 1833.492 10 7.71605e-07 1.128
19 | 19 1447.478 5.934 100.3700380 +9.5244535 1447.446 5.975 0.0158 0.0145 -0.0 16 0.850 13166.91 551.8128 12944.2 753.9198 14.7198 0.0633 3705.555 15 1.157407e-06 1.033
20 | 20 1108.887 7.122 100.3264885 +9.5236333 1108.973 7.068 0.0433 0.0430 -88.3 0 0.771 3314.062 402.9871 3625.68 621.0448 16.1015 0.1860 1461.047 8 6.172839e-07 1.184
21 | 21 184.985 4.916 100.2051261 +9.5219726 184.999 4.929 0.0083 0.0083 90.0 16 0.875 23107.68 652.9137 23901.38 866.6564 14.0539 0.0394 8696.203 21 1.62037e-06 1.017
22 | 22 1691.916 1.760 100.4010942 +9.5252161 1692.030 1.225 0.0002 0.0002 -0.3 24 0.823 633661.2 1085.076 622473.5 740.3345 10.5147 0.0013 300382.2 58 4.475309e-06 1.355
23 | 23 638.753 4.101 100.2651241 +9.5228435 638.806 4.112 0.0303 0.0302 -0.0 16 1.067 7779.977 493.5564 10418.25 976.7761 14.9555 0.1018 3039.453 12 9.259259e-07 1.158
24 |
--------------------------------------------------------------------------------
/lemon-completion.sh:
--------------------------------------------------------------------------------
1 | #! bash
2 | #
3 | # Bash completion support for LEMON (commands and --long-options)
4 | #
5 | # Copyright (c) 2013 Victor Terron. All rights reserved.
6 | # Institute of Astrophysics of Andalusia, IAA-CSIC
7 | # Distributed under the GNU General Public License, version 3.0
8 |
9 | # To use these completion routines:
10 | # 1) Copy this file to somewhere (e.g. ~/.lemon-completion.sh).
11 | # 2) Add the following line to your .bashrc/.zshrc:
12 | # source ~/.lemon-completion.sh
13 |
14 | shopt -s extglob
15 |
16 | FITS_EXTS="fit?(s)|FIT?(S)"
17 | JSON_EXTS="json|JSON"
18 | LEMONDB_EXTS="LEMONdB|lemondb"
19 |
20 | # Match the current word against the list given as argument
21 | _match()
22 | {
23 | COMPREPLY=( $(compgen -W "${1}" -- ${cur}) )
24 | }
25 |
26 | _lemon_import()
27 | {
28 | local opts
29 | opts="--object --pattern --counts --filename --follow --exact
30 | --datek --timek --expk= --objectk --uik"
31 |
32 | if [[ ${cur} != -* ]]; then
33 | _filedir @($FITS_EXTS)
34 | else
35 | _match "${opts}"
36 | fi
37 | }
38 |
39 | _lemon_seeing()
40 | {
41 | local opts
42 |
43 | opts="--filename --maximum --margin --snr-percentile --mean
44 | --sources-percentile --suffix --overwrite --cores --verbose
45 | --fsigma --fwhm_dir --esigma --elong_dir --coaddk --fwhmk"
46 |
47 | if [[ ${cur} == -* ]]; then
48 | _match "${opts}"
49 | else
50 | _filedir @($FITS_EXTS)
51 | fi
52 | }
53 |
54 | _lemon_mosaic()
55 | {
56 | local opts
57 | opts="--overwrite --background-match --no-reprojection --combine
58 | --filter --cores --filterk"
59 |
60 | if [[ ${cur} == -* ]]; then
61 | _match "${opts}"
62 | elif [[ ${prev} == --combine ]]; then
63 | _match "mean median count"
64 | else
65 | _filedir @($FITS_EXTS)
66 | fi
67 | }
68 |
69 | _lemon_astrometry()
70 | {
71 | local opts
72 | opts="--radius --blind --timeout --suffix --cores -o --verbose --rak --deck"
73 |
74 | if [[ ${cur} == -* ]]; then
75 | _match "${opts}"
76 | else
77 | _filedir @($FITS_EXTS)
78 | fi
79 | }
80 |
81 | _lemon_annuli()
82 | {
83 | local opts
84 | opts="--overwrite --margin --gain --cores --verbose --aperture
85 | --annulus --dannulus --min-sky --constant --minimum-constant
86 | --lower --upper --step --sky --width --snr-percentile --mean
87 | --maximum --minimum-images --minimum-stars --pct
88 | --weights-threshold --max-iters --worst-fraction -objectk
89 | --filterk --datek --timek --expk --coaddk --gaink --fwhmk
90 | --airmk --uik"
91 |
92 | if [[ ${cur} == -* ]]; then
93 | _match "${opts}"
94 | else
95 | # Input FITS images / output JSON file
96 | _filedir @($FITS_EXTS|$JSON_EXTS)
97 | fi
98 | }
99 |
100 | _lemon_photometry()
101 | {
102 | local opts
103 | opts="--overwrite --filter --exclude --cbox --maximum --margin
104 | --gain --annuli --cores --verbose --coordinates --epoch --aperture
105 | --annulus --dannulus --min-sky --individual-fwhm --aperture-pix
106 | --annulus-pix --dannulus-pix --snr-percentile --mean --objectk
107 | --filterk --datek --timek --expk --coaddk --gaink --fwhmk --airmk
108 | --uik"
109 |
110 | case $prev in
111 | --annuli)
112 | _filedir @($JSON_EXTS)
113 | return 0
114 | ;;
115 | --coordinates)
116 | _filedir
117 | return 0
118 | ;;
119 | esac
120 |
121 | if [[ ${cur} == -* ]]; then
122 | _match "${opts}"
123 | else
124 | # Input FITS images / output LEMONdB
125 | _filedir @($FITS_EXTS|$LEMONDB_EXTS)
126 | fi
127 | }
128 |
129 | _lemon_diffphot()
130 | {
131 | local opts
132 | opts="--overwrite --cores --verbose --minimum-images --stars
133 | --minimum-stars --pct --weights-threshold --max-iters --worst-fraction"
134 |
135 | if [[ ${cur} == -* ]]; then
136 | _match "${opts}"
137 | else
138 | _filedir @($LEMONDB_EXTS)
139 | fi
140 | }
141 |
142 | _lemon_juicer()
143 | {
144 | _filedir @($LEMONDB_EXTS)
145 | }
146 |
147 | _lemon()
148 | {
149 | local cur prev commands
150 | COMPREPLY=()
151 | cur="${COMP_WORDS[COMP_CWORD]}"
152 | prev="${COMP_WORDS[COMP_CWORD-1]}"
153 | commands="import seeing astrometry mosaic annuli photometry
154 | diffphot juicer"
155 |
156 | # The options that autocomplete depend on the LEMON command being
157 | # executed. For example, the '--exact' option is specific to the
158 | # 'import' command, so it must be available only when that is the
159 | # second word in the current command line (i.e., 'lemon import ...')
160 |
161 | case "${COMP_WORDS[1]}" in
162 | import)
163 | _lemon_import
164 | return 0
165 | ;;
166 | seeing)
167 | _lemon_seeing
168 | return 0
169 | ;;
170 | mosaic)
171 | _lemon_mosaic
172 | return 0
173 | ;;
174 | astrometry)
175 | _lemon_astrometry
176 | return 0
177 | ;;
178 | annuli)
179 | _lemon_annuli
180 | return 0
181 | ;;
182 | photometry)
183 | _lemon_photometry
184 | return 0
185 | ;;
186 | diffphot)
187 | _lemon_diffphot
188 | return 0
189 | ;;
190 | juicer)
191 | _lemon_juicer
192 | return 0
193 | ;;
194 | esac
195 |
196 | _match "${commands}"
197 |
198 | }
199 |
200 | complete -F _lemon lemon
201 |
--------------------------------------------------------------------------------
/Misc/CHANGES:
--------------------------------------------------------------------------------
1 | 0.3 (2015-06-08)
2 | ================
3 |
4 | - Fix NotImplementedError raised by Queue.qsize() on Mac OS X (9ca6b4b)
5 | - Show the stack trace of exceptions raised by map_async() (348e0e5)
6 | - Use unittest2 in Python versions < 2.7 (90d43aa)
7 | - Require SExtractor >= 2.19.5 (8165cbe)
8 | - Add support for user-defined photometric filters (dd278a2)
9 | - Raise an error if the α or δ of an image are out of range (0e28041)
10 | - Add --version option (f2c8971)
11 | - Add --update option (6cb2f79)
12 | - Notify the user when there is a new version available (723e76e)
13 | - Use more widespread default option values (be7671c)
14 | - Use JSON to serialize data — say bye to XML (aeab015)
15 | - Fix outdated version warnings — take dates into account (6dfe004)
16 | - Add support for astronomical objects with known proper motions (4cdc774)
17 | - Differentiate between 'essential' and 'auxiliary' commands (756ebed)
18 | - Add support for sexagesimal coordinates (51277fc)
19 | - Update README for Debian 7 (Wheezy) (ca2b696)
20 | - Remove (lots of) unused code (844a7d8)
21 | - Remove 'periods' command (0748dfc)
22 | - Add support for the Harris photometric system (3117d6a)
23 |
24 | Documentation
25 | *************
26 | - Add brief introduction (041eb30)
27 | - Add Installation section (edf16f6)
28 | - Add Quickstart (b16ec3d)
29 | - Add "Fork me on GitHub" ribbon (e8ffde5)
30 | - Use 'thumbnail' directive for HTML builds and 'image' for LaTeX builds (f48b5d7)
31 | - Show an example light curve of HAT-P-16b (0e09f7d)
32 |
33 | astrometry
34 | **********
35 | - Ignore FITS files that cannot be astrometrically solved (ca44b80)
36 | - Add options --rack and --deck (4ad0329 and f879a06)
37 | - Limit the search to the α and δ read from the FITS header (e9d8c7c)
38 | - Add --blind option (0bf7bad)
39 | - Add --timeout option (bddfacd)
40 | - Add multicore support (6ffe2c5)
41 | - Add -o option (2a3eb4f)
42 |
43 | mosaic
44 | ******
45 | - Add --filter option (76a3a80)
46 | - Reproject the mosaic so that North is up (f73293d)
47 | - Add --no-reprojection option (26c4e07)
48 | - Require all mosaicked images to be astrometrically solved (b16a9bf)
49 | - Ignore the photometric filter of the images unless --filter is given (3c8b08c)
50 | - Add --combine option (2b91673)
51 | - Add workaround for 'background_match' bug in montage.mosaic() (a8ec3c5)
52 |
53 | photometry
54 | **********
55 | - Avoid bug in IRAF that returns invalid xcenter values (2869943)
56 | - Allow missing FITS keywords in the sources image (afcc3f8)
57 | - Remove warning message if --expk is missing from sources image (d043495)
58 | - Allow to do photometry on the sources FITS image (9edc3ae)
59 | - Allow to use the --filter option multiple times (1585212)
60 | - Add --exclude option (0e0e81d)
61 | - Do not talk about --margin if no astronomical object is ignored (37fc430)
62 | - Make it possible to use Montage mosaics as the sources image (095d6f3)
63 | - Add --cbox option (5d307d3)
64 | - Fix qphot unit tests for 32-bit IRAF (8772356)
65 |
66 | diffphot
67 | ********
68 | - Remove --output option (fac49a9)
69 | - Fix bug with comparison stars without a standard deviation (cfa00ee)
70 | - Set the default value of --worst-fraction to 0.10 (e36625c)
71 |
72 | juicer
73 | ******
74 | - Open LEMONdBs from the command line (868c6f0)
75 | - Show celestial coordinates in finding chart (b5cf393)
76 | - Add 'Look up in SIMBAD' button (540eb5c)
77 | - Use world coordinates to mark stars in finding chart (79b8fbf)
78 | - Limit instrumental magnitudes to three decimal places (266780b)
79 | - Add a 'Preferences' dialog to the finding chart (a636d86)
80 | - Fix ValueError if we double click on stars without light curves (785a49d)
81 | - Add an option to use Julian Dates (9a23043)
82 | - Allow to export instrumental magnitudes and SNRs to a file (e6a3f40)
83 | - Plot user-defined filters without a specified color with a random one (3aa1d10)
84 | - Use "Δ magnitude" as the label of the y-axis (fc3fcea)
85 | - Prevent the y-axis label from overlapping with the airmass ticks (3608d07)
86 | - Remove "Select stars by their amplitudes" (257e774)
87 |
88 | seeing
89 | ******
90 | - Add option --overwrite (3676803)
91 | - Do not modify input FITS files (53406c0)
92 | - Fix error caused by HIERARCH + CONTINUE keywords (f1c4912)
93 | - Avoid unnecessary copy of the temporary FITS files (68ca820)
94 |
95 | 0.2 (2014-03-24)
96 | ================
97 |
98 | - Remove the 'offsets' command (6b473af)
99 | - Identify astronomical objects by their α and δ (2dff622)
100 | - Use IPAC's Montage to assemble images into a mosaic (abc2438)
101 | - Do astrometry on multiple FITS files at once (027819c)
102 | - Add LEMON icon — by Sofía León (e7b72e9)
103 | - Use a local build of Astrometry.net for calibration (9b8a82a)
104 | - Enforce a minimum version of dependencies (10575ec)
105 | - Add support for Cousins, Gunn, SDSS, 2MASS, Strömgren and H-alpha (8bad300)
106 | - Use Travis for continuous integration (5d23b2f)
107 | - Start writing the documentation using Sphinx (e8e8632)
108 | - Add Bash completion support (31937c5)
109 | - Add the 'lemon' porcelain command (c48ed0e)
110 | - Allow simultaneous observations (d808766)
111 | - Switch from xml.dom.minidom to lxml — much faster (c5a9b34)
112 | - Use .LEMONdB as the database extension (fcfe881)
113 | - Add Juicer, the GUI for data analysis (675953e)
114 | - Add unit tests for several modules (f52f9c8)
115 |
116 | 0.1 (2012-06-04)
117 | ================
118 | - Initial release
119 |
--------------------------------------------------------------------------------
/util/test/test_log.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | # Copyright (c) 2014 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | from __future__ import division
22 |
23 | import StringIO
24 | import operator
25 | import os
26 | import warnings
27 |
28 |
29 | # LEMON modules
30 | from test import unittest
31 | import util
32 |
33 |
34 | class FuncCatchllallTest(unittest.TestCase):
35 | def test_func_catchall(self):
36 |
37 | # Returns func(*args, **kwargs) ...
38 | self.assertEqual(3, util.func_catchall(operator.div, 9, 3))
39 | self.assertEqual(4, util.func_catchall(int, "4"))
40 | self.assertEqual(-5, util.func_catchall(max, -1, -5, 4, key=abs))
41 |
42 | # ... unless the function raises an exception. In that case, it is
43 | # catched and None is returned instead.
44 |
45 | def foo_except():
46 | raise ValueError
47 |
48 | self.assertEqual(None, util.func_catchall(foo_except))
49 | self.assertEqual(None, util.func_catchall(operator.div, 1, 0))
50 |
51 |
52 | class StreamToWarningFilterTest(unittest.TestCase):
53 | def test_filter_stream(self):
54 |
55 | # A filter that, if the 'foa', 'fooa' or 'foooa' strings are matched,
56 | # issues UserWarning using the string as the warning message. If not,
57 | # writes the string to the output stream (here, a StringIO)
58 |
59 | output = StringIO.StringIO()
60 | expected_output = "fo{1,3}a"
61 | regexp = "(?P{0})".format(expected_output)
62 | category = UserWarning
63 | args = output, regexp, category
64 | stdout_filter = util.StreamToWarningFilter(*args)
65 |
66 | with warnings.catch_warnings():
67 | warnings.filterwarnings("error")
68 |
69 | # 'fooa' matches the regexp, so issue the warning
70 | with self.assertRaisesRegexp(category, expected_output):
71 | stdout_filter.write("fooa")
72 |
73 | # 'spam' does not match, so write to the output stream
74 | str_ = "spam"
75 | stdout_filter.write(str_)
76 | self.assertEqual(output.getvalue(), str_)
77 |
78 | # A filter that, if "Warning: Keyword: EXPTIME not found" is matched,
79 | # issues RuntimeWarning with "EXPTIME not matched" as its message. If
80 | # not, writes the string to the output stream (again, a StringIO).
81 |
82 | output = StringIO.StringIO()
83 | expected_output = "EXPTIME not found"
84 | regexp = "Warning: Keyword: (?P{0})".format(expected_output)
85 | category = RuntimeWarning
86 | args = output, regexp, category
87 | stdout_filter = util.StreamToWarningFilter(*args)
88 |
89 | with warnings.catch_warnings():
90 | warnings.filterwarnings("error")
91 |
92 | with self.assertRaisesRegexp(category, expected_output):
93 | str_ = "Warning: Keyword: EXPTIME not found"
94 | stdout_filter.write(str_)
95 |
96 | str_ = str_.replace("EXPTIME", "OBJECT")
97 | stdout_filter.write(str_)
98 | self.assertEqual(output.getvalue(), str_)
99 |
100 | # The example mentioned in the class docstring
101 |
102 | output = StringIO.StringIO()
103 | expected_output = "2.19.5"
104 | regexp = "v(?P(\d\.?)+)"
105 | category = UserWarning
106 | args = output, regexp, category
107 | stdout_filter = util.StreamToWarningFilter(*args)
108 |
109 | with warnings.catch_warnings():
110 | warnings.filterwarnings("error")
111 |
112 | with self.assertRaisesRegexp(category, expected_output):
113 | stdout_filter.write("v2.19.5")
114 |
115 | str_ = "Nobody expects the Spanish inquisition"
116 | stdout_filter.write(str_)
117 | self.assertEqual(output.getvalue(), str_)
118 |
119 | # The regular expression does not include the mandatory 'msg' named
120 | # group, so IndexError is raised when the string is matched, as the
121 | # StreamToWarningFilter class tries to refer to a non-existent group.
122 |
123 | output = StringIO.StringIO()
124 | regexp = "spam"
125 | args = output, regexp, UserWarning
126 | stdout_filter = util.StreamToWarningFilter(*args)
127 |
128 | with self.assertRaisesRegexp(IndexError, "no such group"):
129 | stdout_filter.write("spam")
130 |
131 | def test_filter_close(self):
132 |
133 | fd = open(os.devnull, "wt")
134 | regexp = "Keyword (?PEXPTIME) not found"
135 | args = fd, regexp, RuntimeWarning
136 | devnull_filter = util.StreamToWarningFilter(*args)
137 | # Must close the underlying file object
138 | devnull_filter.close()
139 | self.assertTrue(fd.closed)
140 |
--------------------------------------------------------------------------------
/Doc/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 | set I18NSPHINXOPTS=%SPHINXOPTS% .
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
31 | echo. text to make text files
32 | echo. man to make manual pages
33 | echo. texinfo to make Texinfo files
34 | echo. gettext to make PO message catalogs
35 | echo. changes to make an overview over all changed/added/deprecated items
36 | echo. linkcheck to check all external links for integrity
37 | echo. doctest to run all doctests embedded in the documentation if enabled
38 | goto end
39 | )
40 |
41 | if "%1" == "clean" (
42 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
43 | del /q /s %BUILDDIR%\*
44 | goto end
45 | )
46 |
47 | if "%1" == "html" (
48 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
49 | if errorlevel 1 exit /b 1
50 | echo.
51 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
52 | goto end
53 | )
54 |
55 | if "%1" == "dirhtml" (
56 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
57 | if errorlevel 1 exit /b 1
58 | echo.
59 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
60 | goto end
61 | )
62 |
63 | if "%1" == "singlehtml" (
64 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
65 | if errorlevel 1 exit /b 1
66 | echo.
67 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
68 | goto end
69 | )
70 |
71 | if "%1" == "pickle" (
72 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
73 | if errorlevel 1 exit /b 1
74 | echo.
75 | echo.Build finished; now you can process the pickle files.
76 | goto end
77 | )
78 |
79 | if "%1" == "json" (
80 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
81 | if errorlevel 1 exit /b 1
82 | echo.
83 | echo.Build finished; now you can process the JSON files.
84 | goto end
85 | )
86 |
87 | if "%1" == "htmlhelp" (
88 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
89 | if errorlevel 1 exit /b 1
90 | echo.
91 | echo.Build finished; now you can run HTML Help Workshop with the ^
92 | .hhp project file in %BUILDDIR%/htmlhelp.
93 | goto end
94 | )
95 |
96 | if "%1" == "qthelp" (
97 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
98 | if errorlevel 1 exit /b 1
99 | echo.
100 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
101 | .qhcp project file in %BUILDDIR%/qthelp, like this:
102 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\LEMON.qhcp
103 | echo.To view the help file:
104 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\LEMON.ghc
105 | goto end
106 | )
107 |
108 | if "%1" == "devhelp" (
109 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
110 | if errorlevel 1 exit /b 1
111 | echo.
112 | echo.Build finished.
113 | goto end
114 | )
115 |
116 | if "%1" == "epub" (
117 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
118 | if errorlevel 1 exit /b 1
119 | echo.
120 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
121 | goto end
122 | )
123 |
124 | if "%1" == "latex" (
125 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
126 | if errorlevel 1 exit /b 1
127 | echo.
128 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
129 | goto end
130 | )
131 |
132 | if "%1" == "text" (
133 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
134 | if errorlevel 1 exit /b 1
135 | echo.
136 | echo.Build finished. The text files are in %BUILDDIR%/text.
137 | goto end
138 | )
139 |
140 | if "%1" == "man" (
141 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
142 | if errorlevel 1 exit /b 1
143 | echo.
144 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
145 | goto end
146 | )
147 |
148 | if "%1" == "texinfo" (
149 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
150 | if errorlevel 1 exit /b 1
151 | echo.
152 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
153 | goto end
154 | )
155 |
156 | if "%1" == "gettext" (
157 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
158 | if errorlevel 1 exit /b 1
159 | echo.
160 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
161 | goto end
162 | )
163 |
164 | if "%1" == "changes" (
165 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
166 | if errorlevel 1 exit /b 1
167 | echo.
168 | echo.The overview file is in %BUILDDIR%/changes.
169 | goto end
170 | )
171 |
172 | if "%1" == "linkcheck" (
173 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
174 | if errorlevel 1 exit /b 1
175 | echo.
176 | echo.Link check complete; look for any errors in the above output ^
177 | or in %BUILDDIR%/linkcheck/output.txt.
178 | goto end
179 | )
180 |
181 | if "%1" == "doctest" (
182 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
183 | if errorlevel 1 exit /b 1
184 | echo.
185 | echo.Testing of doctests in the sources finished, look at the ^
186 | results in %BUILDDIR%/doctest/output.txt.
187 | goto end
188 | )
189 |
190 | :end
191 |
--------------------------------------------------------------------------------
/Doc/_themes/kr_small/static/flasky.css_t:
--------------------------------------------------------------------------------
1 | /*
2 | * flasky.css_t
3 | * ~~~~~~~~~~~~
4 | *
5 | * Sphinx stylesheet -- flasky theme based on nature theme.
6 | *
7 | * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
8 | * :license: BSD, see LICENSE for details.
9 | *
10 | */
11 |
12 | @import url("basic.css");
13 |
14 | /* -- page layout ----------------------------------------------------------- */
15 |
16 | body {
17 | font-family: 'Georgia', serif;
18 | font-size: 17px;
19 | color: #000;
20 | background: white;
21 | margin: 0;
22 | padding: 0;
23 | }
24 |
25 | div.documentwrapper {
26 | float: left;
27 | width: 100%;
28 | }
29 |
30 | div.bodywrapper {
31 | margin: 40px auto 0 auto;
32 | width: 700px;
33 | }
34 |
35 | hr {
36 | border: 1px solid #B1B4B6;
37 | }
38 |
39 | div.body {
40 | background-color: #ffffff;
41 | color: #3E4349;
42 | padding: 0 30px 30px 30px;
43 | }
44 |
45 | img.floatingflask {
46 | padding: 0 0 10px 10px;
47 | float: right;
48 | }
49 |
50 | div.footer {
51 | text-align: right;
52 | color: #888;
53 | padding: 10px;
54 | font-size: 14px;
55 | width: 650px;
56 | margin: 0 auto 40px auto;
57 | }
58 |
59 | div.footer a {
60 | color: #888;
61 | text-decoration: underline;
62 | }
63 |
64 | div.related {
65 | line-height: 32px;
66 | color: #888;
67 | }
68 |
69 | div.related ul {
70 | padding: 0 0 0 10px;
71 | }
72 |
73 | div.related a {
74 | color: #444;
75 | }
76 |
77 | /* -- body styles ----------------------------------------------------------- */
78 |
79 | a {
80 | color: #004B6B;
81 | text-decoration: underline;
82 | }
83 |
84 | a:hover {
85 | color: #6D4100;
86 | text-decoration: underline;
87 | }
88 |
89 | div.body {
90 | padding-bottom: 40px; /* saved for footer */
91 | }
92 |
93 | div.body h1,
94 | div.body h2,
95 | div.body h3,
96 | div.body h4,
97 | div.body h5,
98 | div.body h6 {
99 | font-family: 'Garamond', 'Georgia', serif;
100 | font-weight: normal;
101 | margin: 30px 0px 10px 0px;
102 | padding: 0;
103 | }
104 |
105 | {% if theme_index_logo %}
106 | div.indexwrapper h1 {
107 | text-indent: -999999px;
108 | background: url({{ theme_index_logo }}) no-repeat center center;
109 | height: {{ theme_index_logo_height }};
110 | }
111 | {% endif %}
112 |
113 | div.body h2 { font-size: 180%; }
114 | div.body h3 { font-size: 150%; }
115 | div.body h4 { font-size: 130%; }
116 | div.body h5 { font-size: 100%; }
117 | div.body h6 { font-size: 100%; }
118 |
119 | a.headerlink {
120 | color: white;
121 | padding: 0 4px;
122 | text-decoration: none;
123 | }
124 |
125 | a.headerlink:hover {
126 | color: #444;
127 | background: #eaeaea;
128 | }
129 |
130 | div.body p, div.body dd, div.body li {
131 | line-height: 1.4em;
132 | }
133 |
134 | div.admonition {
135 | background: #fafafa;
136 | margin: 20px -30px;
137 | padding: 10px 30px;
138 | border-top: 1px solid #ccc;
139 | border-bottom: 1px solid #ccc;
140 | }
141 |
142 | div.admonition p.admonition-title {
143 | font-family: 'Garamond', 'Georgia', serif;
144 | font-weight: normal;
145 | font-size: 24px;
146 | margin: 0 0 10px 0;
147 | padding: 0;
148 | line-height: 1;
149 | }
150 |
151 | div.admonition p.last {
152 | margin-bottom: 0;
153 | }
154 |
155 | div.highlight{
156 | background-color: white;
157 | }
158 |
159 | dt:target, .highlight {
160 | background: #FAF3E8;
161 | }
162 |
163 | div.note {
164 | background-color: #eee;
165 | border: 1px solid #ccc;
166 | }
167 |
168 | div.seealso {
169 | background-color: #ffc;
170 | border: 1px solid #ff6;
171 | }
172 |
173 | div.topic {
174 | background-color: #eee;
175 | }
176 |
177 | div.warning {
178 | background-color: #ffe4e4;
179 | border: 1px solid #f66;
180 | }
181 |
182 | p.admonition-title {
183 | display: inline;
184 | }
185 |
186 | p.admonition-title:after {
187 | content: ":";
188 | }
189 |
190 | pre, tt {
191 | font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
192 | font-size: 0.85em;
193 | }
194 |
195 | img.screenshot {
196 | }
197 |
198 | tt.descname, tt.descclassname {
199 | font-size: 0.95em;
200 | }
201 |
202 | tt.descname {
203 | padding-right: 0.08em;
204 | }
205 |
206 | img.screenshot {
207 | -moz-box-shadow: 2px 2px 4px #eee;
208 | -webkit-box-shadow: 2px 2px 4px #eee;
209 | box-shadow: 2px 2px 4px #eee;
210 | }
211 |
212 | table.docutils {
213 | border: 1px solid #888;
214 | -moz-box-shadow: 2px 2px 4px #eee;
215 | -webkit-box-shadow: 2px 2px 4px #eee;
216 | box-shadow: 2px 2px 4px #eee;
217 | }
218 |
219 | table.docutils td, table.docutils th {
220 | border: 1px solid #888;
221 | padding: 0.25em 0.7em;
222 | }
223 |
224 | table.field-list, table.footnote {
225 | border: none;
226 | -moz-box-shadow: none;
227 | -webkit-box-shadow: none;
228 | box-shadow: none;
229 | }
230 |
231 | table.footnote {
232 | margin: 15px 0;
233 | width: 100%;
234 | border: 1px solid #eee;
235 | }
236 |
237 | table.field-list th {
238 | padding: 0 0.8em 0 0;
239 | }
240 |
241 | table.field-list td {
242 | padding: 0;
243 | }
244 |
245 | table.footnote td {
246 | padding: 0.5em;
247 | }
248 |
249 | dl {
250 | margin: 0;
251 | padding: 0;
252 | }
253 |
254 | dl dd {
255 | margin-left: 30px;
256 | }
257 |
258 | pre {
259 | padding: 0;
260 | margin: 15px -30px;
261 | padding: 8px;
262 | line-height: 1.3em;
263 | padding: 7px 30px;
264 | background: #eee;
265 | border-radius: 2px;
266 | -moz-border-radius: 2px;
267 | -webkit-border-radius: 2px;
268 | }
269 |
270 | dl pre {
271 | margin-left: -60px;
272 | padding-left: 60px;
273 | }
274 |
275 | tt {
276 | background-color: #ecf0f3;
277 | color: #222;
278 | /* padding: 1px 2px; */
279 | }
280 |
281 | tt.xref, a tt {
282 | background-color: #FBFBFB;
283 | }
284 |
285 | a:hover tt {
286 | background: #EEE;
287 | }
288 |
--------------------------------------------------------------------------------
/Doc/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 | # the i18n builder cannot share the environment and doctrees with the others
15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
16 |
17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
18 |
19 | help:
20 | @echo "Please use \`make ' where is one of"
21 | @echo " html to make standalone HTML files"
22 | @echo " dirhtml to make HTML files named index.html in directories"
23 | @echo " singlehtml to make a single large HTML file"
24 | @echo " pickle to make pickle files"
25 | @echo " json to make JSON files"
26 | @echo " htmlhelp to make HTML files and a HTML help project"
27 | @echo " qthelp to make HTML files and a qthelp project"
28 | @echo " devhelp to make HTML files and a Devhelp project"
29 | @echo " epub to make an epub"
30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
31 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
32 | @echo " text to make text files"
33 | @echo " man to make manual pages"
34 | @echo " texinfo to make Texinfo files"
35 | @echo " info to make Texinfo files and run them through makeinfo"
36 | @echo " gettext to make PO message catalogs"
37 | @echo " changes to make an overview of all changed/added/deprecated items"
38 | @echo " linkcheck to check all external links for integrity"
39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
40 |
41 | clean:
42 | -rm -rf $(BUILDDIR)/*
43 |
44 | html:
45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
46 | @echo
47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
48 |
49 | dirhtml:
50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
51 | @echo
52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
53 |
54 | singlehtml:
55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
56 | @echo
57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
58 |
59 | pickle:
60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
61 | @echo
62 | @echo "Build finished; now you can process the pickle files."
63 |
64 | json:
65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
66 | @echo
67 | @echo "Build finished; now you can process the JSON files."
68 |
69 | htmlhelp:
70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
71 | @echo
72 | @echo "Build finished; now you can run HTML Help Workshop with the" \
73 | ".hhp project file in $(BUILDDIR)/htmlhelp."
74 |
75 | qthelp:
76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
77 | @echo
78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/LEMON.qhcp"
81 | @echo "To view the help file:"
82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/LEMON.qhc"
83 |
84 | devhelp:
85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
86 | @echo
87 | @echo "Build finished."
88 | @echo "To view the help file:"
89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/LEMON"
90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/LEMON"
91 | @echo "# devhelp"
92 |
93 | epub:
94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
95 | @echo
96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
97 |
98 | latex:
99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
100 | @echo
101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
103 | "(use \`make latexpdf' here to do that automatically)."
104 |
105 | latexpdf:
106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
107 | @echo "Running LaTeX files through pdflatex..."
108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
110 |
111 | text:
112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
113 | @echo
114 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
115 |
116 | man:
117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
118 | @echo
119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
120 |
121 | texinfo:
122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
123 | @echo
124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
125 | @echo "Run \`make' in that directory to run these through makeinfo" \
126 | "(use \`make info' here to do that automatically)."
127 |
128 | info:
129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
130 | @echo "Running Texinfo files through makeinfo..."
131 | make -C $(BUILDDIR)/texinfo info
132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
133 |
134 | gettext:
135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
136 | @echo
137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
138 |
139 | changes:
140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
141 | @echo
142 | @echo "The overview file is in $(BUILDDIR)/changes."
143 |
144 | linkcheck:
145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
146 | @echo
147 | @echo "Link check complete; look for any errors in the above output " \
148 | "or in $(BUILDDIR)/linkcheck/output.txt."
149 |
150 | doctest:
151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
152 | @echo "Testing of doctests in the sources finished, look at the " \
153 | "results in $(BUILDDIR)/doctest/output.txt."
154 |
--------------------------------------------------------------------------------
/check_versions.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | # Copyright (c) 2013 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | """ Use Python import hooks to enforce a minimum version of those modules
22 | defined in the requirements.txt file. Another option would have been to check
23 | the versions manually, but that would have forced us to do it multiple times
24 | and across all the code, every time one of the modules were imported. And, as
25 | more code is added in the future, we could forget to test for this. With this
26 | solution we only need to define the hooks once, here, and this guarantee that
27 | the modules will always have the required version, no matter how, when or
28 | where they are imported.
29 |
30 | """
31 |
32 | import imp
33 | import re
34 | import sys
35 |
36 | # LEMON module
37 | import astromatic
38 |
39 |
40 | def version_to_str(version):
41 | """ From (0, 2, 4) to '0.2.4' for example """
42 | return ".".join(str(x) for x in version)
43 |
44 |
45 | def str_to_version(version):
46 | """ From '0.2.4' to (0, 2, 4), for example """
47 | return tuple(int(x) for x in version.split("."))
48 |
49 |
50 | class RequireModuleVersionHook(object):
51 | """An import hook to enforce minimum versions of Python modules.
52 |
53 | This class implements both the module finder (find_module) and loader
54 | (load_module). It can be installed in sys.meta_path to intercept every
55 | attempt to import a new module, checking whether it has the required
56 | version and raising ImportError otherwise.
57 |
58 | """
59 |
60 | def __init__(self, fullname, min_version, vfunc):
61 | """Instantiation method for the RequireModuleVersionHook class.
62 |
63 | The 'fullname' parameter is the a fully qualified name of the module
64 | that we want to import, such as 'pyfits' or 'scipy'. 'min_version' is a
65 | tuple of integers specifying the minimum version of the module, such as
66 | (1, 2, 1). Finally, 'vfunc' is a hook function that will be passed the
67 | module object, after it is imported, and that must return its version
68 | as a tuple of integers.
69 |
70 | """
71 |
72 | self.fullname = fullname
73 | self.min_version = min_version
74 | self.vfunc = vfunc
75 |
76 | def find_module(self, fullname, path=None):
77 | """The module finder.
78 |
79 | Receive the fully qualified name of the module to be imported, along
80 | with, optionally, the path where it is supposed to be found. Return
81 | None if the module cannot be found by this particular finder and self
82 | (since this class also implements the module loader) otherwise.
83 |
84 | """
85 |
86 | if fullname == self.fullname:
87 | self.path = path
88 | return self
89 | return None
90 |
91 | def load_module(self, fullname):
92 | """The module loader.
93 |
94 | Receive the fully qualified name of the module that we want to import.
95 | Then, import the module, call vfunc() to get its version as a tuple of
96 | integers and compare it to the specified minimum version: if the module
97 | version is equal to or higher than the required one; return the module
98 | object; otherwise, raise ImportError.
99 |
100 | """
101 |
102 | # If module already imported, return it
103 | if fullname in sys.modules:
104 | return sys.modules[fullname]
105 |
106 | module_info = imp.find_module(fullname, self.path)
107 | module = imp.load_module(fullname, *module_info)
108 |
109 | version = self.vfunc(module)
110 | if not version >= self.min_version:
111 | msg = "%s >= %s is required, found %s"
112 | args = (fullname, version_to_str(self.min_version), version_to_str(version))
113 | raise ImportError(msg % args)
114 | else:
115 | sys.modules[fullname] = module
116 | return module
117 |
118 |
119 | def get__version__(module):
120 | """ Return module.__version__, as a tuple of integers """
121 |
122 | version = module.__version__
123 | # Extract '2.1.1' from '2.1.1-r1785' / '3.2' from '3.2.dev'
124 | regexp = "\d+(\.\d+)+"
125 | match = re.match(regexp, version)
126 | if match is None:
127 | msg = "cannot extract version from '%s' (%s)" % (version, module)
128 | raise Exception(msg)
129 | else:
130 | version = match.group(0)
131 | return str_to_version(version)
132 |
133 |
134 | # For each module whose minimum version has been defined in requirements.txt,
135 | # create an import hook and add it to sys.meta_path, which is searched before
136 | # any implicit default finders or sys.path.
137 |
138 | for module, version in [
139 | ("numpy", (1, 7, 1)),
140 | ("aplpy", (0, 9, 9)),
141 | ("scipy", (0, 12, 0)),
142 | ("matplotlib", (1, 2, 1)),
143 | ("mock", (1, 0, 1)),
144 | ("pyfits", (3, 1, 2)),
145 | ("pyraf", (2, 1, 1)),
146 | ("uncertainties", (2, 4, 1)),
147 | ]:
148 | hook = RequireModuleVersionHook(module, version, get__version__)
149 | sys.meta_path.append(hook)
150 |
151 | # If the minimum version is not met, raise SExtractorUpgradeRequired as
152 | # soon as possible, instead of waiting until we attempt to run SExtractor.
153 | if astromatic.sextractor_version() < astromatic.SEXTRACTOR_REQUIRED_VERSION:
154 | raise astromatic.SExtractorUpgradeRequired()
155 |
--------------------------------------------------------------------------------
/test/test_data/filters/SDSS:
--------------------------------------------------------------------------------
1 | # The first element must be the name of the SDSS photometric filter,
2 | # while the second is the letter that the Passband class must identify
3 | # when the former is parsed.
4 |
5 | "u'", 'U'
6 | "g'", 'G'
7 | "rSDSS", 'R'
8 | "rSloan", 'R'
9 | "iSDSS", 'I'
10 | "Isloan", 'I'
11 | "z SDSS", 'Z'
12 | "Z sloan", 'Z'
13 |
14 | "SDSS U", 'U'
15 | "Sloan_U", 'U'
16 | "sdss g'", 'G'
17 | "sloan-g'", 'G'
18 | "r' (SDSS)", 'R'
19 | "r' (Sloan)", 'R'
20 | "i (sdss)", 'I'
21 | "i_(sloan)", 'I'
22 | "z'SDSS", 'Z'
23 | "z'Sloan", 'Z'
24 |
25 | # Suggested by @cdilga at issue #90
26 | # https://github.com/vterron/lemon/issues/90
27 |
28 | "zprime", 'Z'
29 | "uprime", 'U'
30 | "gprime", 'G'
31 | "rprime", 'R'
32 | "iprime", 'I'
33 |
34 | # ... and, by extension:
35 |
36 | "z prime", 'Z'
37 | "u_prime", 'U'
38 | "G-prime", 'G'
39 | "r_PRIME", 'R'
40 | "iPrime", 'I'
41 |
42 | # These are *real* examples of SDSS filters, kindly provided by César
43 | # Husillos and Pablo Ramírez, who found them among the FITS images of
44 | # several astronomical campaigns.
45 |
46 | "g'_SDSS", 'G'
47 | "i'_SDSS", 'I'
48 | "r'_SDSS", 'R'
49 | "u'_SDSS", 'U'
50 | "Sloanr", 'R'
51 | "Sloang", 'G'
52 | "Sloanu", 'U'
53 | "Sloani", 'I'
54 |
55 | # Extremely convoluted test cases
56 |
57 | "_g'_--", 'G'
58 | "---__g' ", 'G'
59 | "-g'__", 'G'
60 | " __-_G'____", 'G'
61 | "-g'__sdSS-_ -", 'G'
62 | "__g _(--sDsS - )_ ", 'G'
63 | "_gsDsS--", 'G'
64 | "_g'_-SdSS-", 'G'
65 | "____g_SDss--", 'G'
66 | " -__g'SDSs-", 'G'
67 | "_G-_-sdsS_- _ ", 'G'
68 | " GsdSS_--", 'G'
69 | "_-_G-(_- -sDSs_- _)-_ ", 'G'
70 | "_G'sDSS_ __", 'G'
71 | "-_-G'sDSS___ -", 'G'
72 | "-_ -_GSdss_- ", 'G'
73 | "__G'SdsS__-", 'G'
74 | "_- GSdsS_", 'G'
75 | " _G___-(--_-_SDss_ ) ", 'G'
76 | " __G'-_SDSS---", 'G'
77 | "_i'-- -", 'I'
78 | "- _-i'- _", 'I'
79 | "__-_I' --- __", 'I'
80 | "_-_ - I'-_-_", 'I'
81 | "_-_-I'---", 'I'
82 | "--I'-_-", 'I'
83 | "I'__-_", 'I'
84 | " i -(_ sdss- ) _", 'I'
85 | "_i-_sDss_ ", 'I'
86 | "_ iSdss ", 'I'
87 | "i Sdss_-", 'I'
88 | " __i'- SdsS_", 'I'
89 | "__i -_-_SdSS_", 'I'
90 | "_-i-_SDsS ", 'I'
91 | "-_-i'_-__-( --SDSs-__)-", 'I'
92 | "- i'__ -_(_ - SDSS --)--", 'I'
93 | "__- I_--sdss-_", 'I'
94 | "- IsdsS _ _-", 'I'
95 | " _IsDSs---_", 'I'
96 | "____-I sDSS__", 'I'
97 | " I'_- _(_ _SdSs_-)_--_", 'I'
98 | "-I-__ (_-_SdSS _)__ -", 'I'
99 | "__I-(_SDsS_- -)_--", 'I'
100 | "----I_ _(--- SDsS_)-- -", 'I'
101 | "_--r'-_", 'R'
102 | "----r'_ _ -_-", 'R'
103 | "r'-", 'R'
104 | " --- R'- ", 'R'
105 | "_-R'_ ", 'R'
106 | "_r-__-(_-sdss--)_ _", 'R'
107 | "_ r _sdSS___", 'R'
108 | "_rsDss-", 'R'
109 | "-___-r'___-(_----sDsS-__--)_-__", 'R'
110 | "-- _-r_--_sDsS----", 'R'
111 | "--r'-sDSs--", 'R'
112 | "--rsDSs_-_", 'R'
113 | "--r'___SdsS--__", 'R'
114 | "_-rSdSs _", 'R'
115 | "-__-R sdSs_ ", 'R'
116 | "_- R' --(-__-SdSS_)--__", 'R'
117 | "RSDSS", 'R'
118 | " sdssG_-__", 'G'
119 | "-sdSsg--_", 'G'
120 | "__-_sdSs--G'-", 'G'
121 | " ---SdsSg'_--", 'G'
122 | "_- -SDSs G'___", 'G'
123 | "-sdssi_--", 'I'
124 | " sDsSi-", 'I'
125 | "__-sDSs-_ _-I_-", 'I'
126 | "_-_ sDSS_I' _", 'I'
127 | "-Sdss __I_ ", 'I'
128 | "_-SdSsi' -", 'I'
129 | "SdSsI_ ", 'I'
130 | "-SdSS_-I---", 'I'
131 | "-sdss_R-__", 'R'
132 | "_-sdSSr___- ", 'R'
133 | "_-- sDss__-r'__ ", 'R'
134 | "--_sDss_r--_-", 'R'
135 | "-sDss r", 'R'
136 | "-sDssr'_--", 'R'
137 | "__sDSs__R--", 'R'
138 | " Sdssr_-", 'R'
139 | "_- Sdss-- r'__", 'R'
140 | "-SDSs-_r__", 'R'
141 | "_SDSSR_-", 'R'
142 | "sdss-_-u_", 'U'
143 | "-_ sdsSu' ", 'U'
144 | " _--sDss_u_", 'U'
145 | " _ sDsSU'-___", 'U'
146 | "sDSSu- ", 'U'
147 | "_ -_Sdss_u-", 'U'
148 | "_-_- SdsS- u'___", 'U'
149 | "-_SdsS__u", 'U'
150 | "SdSsu", 'U'
151 | "__SDsSu_", 'U'
152 | "_SDsS_ U'- ", 'U'
153 | "sdsSZ_", 'Z'
154 | " --sdSs--z__-- ", 'Z'
155 | "-sdSs_--z__-", 'Z'
156 | "_sdSS__z'- ", 'Z'
157 | "__ -sDsS_-- Z'-", 'Z'
158 | "_-_SdSs_z__-", 'Z'
159 | "_-_-SdSS_ --z _-_", 'Z'
160 | "_SdSSz__- ", 'Z'
161 | " _-SDSsZ_ _", 'Z'
162 | " --__SDSsZ'_", 'Z'
163 | "--SDSs__-Z'--__", 'Z'
164 | "-_-SDSSZ'_ __", 'Z'
165 | " __-u--sdss_", 'U'
166 | "usDSS - ", 'U'
167 | "_--_uSDss-_", 'U'
168 | "____-u_- -(SDSS-_) -__", 'U'
169 | "_uSDSS_", 'U'
170 | "__U'sdss", 'U'
171 | " -__U'_-(-__sdSs-_)__ _", 'U'
172 | "---U'_-_sdSs_ ", 'U'
173 | "-U--sdSS _ -", 'U'
174 | "--_-U'-_ sDSS_ ", 'U'
175 | "--_-USdss-", 'U'
176 | "__ -U'SdsS _- ", 'U'
177 | " -U- _-SDSs__", 'U'
178 | "_-_ u' __ -", 'U'
179 | "_-u'_-___", 'U'
180 | "-- -u'__", 'U'
181 | "-- U'_", 'U'
182 | "--__U'_", 'U'
183 | "U'__ -", 'U'
184 | "_-z'__-(_ sDss__) -__", 'Z'
185 | "_-z -_sDSS_--_-", 'Z'
186 | "--z'SDss-", 'Z'
187 | "_ z'_-(__- SDsS)_ -_", 'Z'
188 | "-z -( SDsS-)_-", 'Z'
189 | "--- z-SDSs_ ", 'Z'
190 | "--zSDSs_ -_", 'Z'
191 | "--Z _-(_sDsS__)-", 'Z'
192 | "_-Z'_(_ - _Sdss__ )-_-_", 'Z'
193 | "- _Z'--(--SdsS-- )__-", 'Z'
194 | "-ZSdSs-", 'Z'
195 | " _Z_---(_SDsS )-", 'Z'
196 | " _-Z'--_-(-_SDSs )-", 'Z'
197 | " z'___", 'Z'
198 | "-_ z' ", 'Z'
199 | "--__z'_", 'Z'
200 | "---_z'___", 'Z'
201 | " --_Z' --- __- ", 'Z'
202 | "_--Z'__-", 'Z'
203 | "-_ Z'_", 'Z'
204 | "--Z'__ ", 'Z'
205 | "-Z'--_ ", 'Z'
206 | "Z'__", 'Z'
207 | "SLOANz -", 'Z'
208 | "-SLOaNz ", 'Z'
209 | " U--_(_sLOAN)__ ", 'U'
210 | "g_-__(-- SloAN)--__", 'G'
211 | "____i--SloAN", 'I'
212 | "_g(_-SloAN)", 'G'
213 | "rSlOan", 'R'
214 | " - sLoANg__-", 'G'
215 | "_--USloAn ", 'U'
216 | "- _SLOANr_ -", 'R'
217 | "___z_(slOan-)__", 'Z'
218 | "-Gsloan", 'G'
219 | "___SLOaNG__-", 'G'
220 | " __u__(SlOan_--)--", 'U'
221 | "_zsLoAn__- ", 'Z'
222 | "_-Rsloan _ -", 'R'
223 | "_-SlOanU--", 'U'
224 | "_-__I_(- --sLOaN_) -_", 'I'
225 | "_____USLoaN _ -", 'U'
226 | " -sLoang_- ", 'G'
227 | "_--rSLoan_- _", 'R'
228 | "--_G _ SlOaN_-_", 'G'
229 | " SlOAN-_G_-__ ", 'G'
230 | " -sLOaNI", 'I'
231 | "__r-_(_SLoAn- )-- __", 'R'
232 | "SLoaNG_", 'G'
233 | "_ -i- _- slOan_-_", 'I'
234 | "-__-zsloan_- ", 'Z'
235 | "_ _sLOanG_-", 'G'
236 | "_-_I----_SlOan -", 'I'
237 | "_-__ sLOanI_", 'I'
238 | "SLoang-", 'G'
239 | "-zslOAn--_", 'Z'
240 | "sLoAnR---_", 'R'
241 | "---SLOAni --", 'I'
242 | "zsloan_-", 'Z'
243 | "--U (sLoan--_)___", 'U'
244 | "sLOAn_G_-", 'G'
245 | "_-SLoAn_-u__", 'U'
246 | " _I-sloaN-", 'I'
247 | "_sLOAN_ R --", 'R'
248 | "-_Z_ SLOAn", 'Z'
249 | "-_--SlOAnI_-", 'I'
250 | "G_(_SLOAN)-", 'G'
251 | "__ SlOAn-_I_ ", 'I'
252 | "z(____slOan___)_", 'Z'
253 | "G-sLOAn ", 'G'
254 | "G--__(---sLOAN--) ", 'G'
255 | "sLoanr_-", 'R'
256 | "isLOan ", 'I'
257 |
--------------------------------------------------------------------------------
/util/coords.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | # Copyright (c) 2019 Victor Terron. All rights reserved.
4 | #
5 | # This file is part of LEMON.
6 | #
7 | # LEMON is free software: you can redistribute it and/or modify it
8 | # under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation, either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program. If not, see .
19 |
20 | import math
21 | import re
22 |
23 |
24 | def DMS_to_DD(degrees, arcminutes, arcseconds):
25 | """ Degrees, arcminutes, arcseconds to decimal degrees conversion. """
26 |
27 | decimal = abs(degrees) + float(arcminutes) / 60 + float(arcseconds) / 3600
28 | if degrees < 0:
29 | decimal = -decimal
30 | return decimal
31 |
32 |
33 | def DD_to_DMS(decimal_degrees):
34 | """ Decimal degrees to degrees, arcminutes, arcseconds conversion. """
35 |
36 | # math.modf(x) returns the fractional and integer parts of x
37 | arcminutes, degrees = math.modf(decimal_degrees)
38 | arcminutes = abs(arcminutes) * 60 # do not propagate the minus sign, if any
39 | arcseconds, arcminutes = math.modf(arcminutes)
40 | arcseconds *= 60
41 | return int(degrees), int(arcminutes), arcseconds
42 |
43 |
44 | def HMS_to_DD(hours, minutes, seconds):
45 | """ Hours, minutes, seconds to decimal degrees conversion. """
46 |
47 | decimal = abs(hours) * 15 + float(minutes) / 60 * 15 + float(seconds) / 3600 * 15
48 | if hours < 0:
49 | decimal = -decimal
50 | return decimal
51 |
52 |
53 | def DD_to_HMS(decimal_degrees):
54 | """ Decimal degrees to hours, minutes, seconds conversion. """
55 |
56 | # math.modf(x) returns the fractional and integer parts of x
57 | minutes, hours = math.modf(decimal_degrees / 15.0)
58 | hours = abs(hours) # do not propagate the minus sign, if any
59 | minutes = abs(minutes) * 60
60 | seconds, minutes = math.modf(minutes)
61 | seconds *= 60
62 | return int(hours), int(minutes), seconds
63 |
64 |
65 | def ra_str(decimal_degrees):
66 | """Return the string representation of a right ascension.
67 | Example: 14h 03m 12.64s"""
68 |
69 | ra_hours, ra_min, ra_sec = DD_to_HMS(decimal_degrees)
70 | ra_hours = "%02d" % ra_hours
71 | ra_min = "%02d" % ra_min
72 | ra_sec = "%05.2f" % ra_sec
73 | return "%sh %sm %ss" % (ra_hours, ra_min, ra_sec)
74 |
75 |
76 | def dec_str(decimal_degrees):
77 | """Return the string representation of a declination.
78 | Example: +57d 33m 10.75s"""
79 |
80 | dec_deg, dec_arcmin, dec_arcsec = DD_to_DMS(decimal_degrees)
81 | dec_deg = "%+03d" % dec_deg
82 | dec_arcmin = "%02d" % dec_arcmin
83 | dec_arcsec = "%05.2f" % dec_arcsec
84 | return "%sd %sm %ss" % (dec_deg, dec_arcmin, dec_arcsec)
85 |
86 |
87 | def load_coordinates(path):
88 | """Load a list of celestial coordinates from a text file.
89 |
90 | Parse a text file containing the celestial coordinates of a series of
91 | astronomical objects, one per line, and return a generator that yields (ra,
92 | dec, pm_ra, pm_dec) tuples. The file must have two columns, with the right
93 | ascension and declination (in decimal degrees) and, optionally, two other
94 | columns with the proper motion in right ascension and declination (in
95 | seconds of arc per year) surrounded by brackets. For example:
96 |
97 | 269.456271 4.665281
98 | 269.452075 4.693391 [-0.79858] [10.32812] # Barnard's Star
99 | 269.466450 4.705625 [0.0036] [-.0064] # TYC 425-262-1
100 |
101 | The four-element tuples contain the right ascension, declination, proper
102 | motion in right ascension and proper motion in declination, respectively.
103 | Nones are used in case the proper motion of an astronomical object is not
104 | specified. Empty lines, as well as comments (which start with the hash
105 | character, #, and extend to the end of the physical line) are ignored.
106 |
107 | ValueError is raised (a) if in any line there is a number of coordinates
108 | other than two (right ascension and declination) or the proper motions are
109 | not surrounded by brackets, (b) if any right ascension or declination is
110 | out of range or (c) if the proper motion in right ascension is specified
111 | but not that in declination, or vice versa.
112 |
113 | """
114 |
115 | with open(path, "rt") as fd:
116 | for line in fd:
117 |
118 | # Ignore comments
119 | line = line.split("#")[0]
120 |
121 | # Ignore empty lines
122 | regexp = "^\s*$"
123 | if re.match(regexp, line):
124 | continue
125 |
126 | kwargs = dict(float="([+-]?\d+(\.\d+)(?:[eE][+\-]?\d+)*)")
127 | regexp = (
128 | "^\s*"
129 | "(?P{float})"
130 | "\s+"
131 | "(?P{float})"
132 | "("
133 | "\s+"
134 | "\[\s*(?P{float})\s*\]"
135 | "\s+"
136 | "\[\s*(?P{float})\s*\]"
137 | ")?"
138 | "\s*$"
139 | )
140 |
141 | match = re.match(regexp.format(**kwargs), line)
142 |
143 | if not match:
144 | msg = (
145 | "Unable to parse line %r. Astronomical objects must be "
146 | "listed one per line with coordinate values in columns one "
147 | "(right ascension) and two (declination). Proper motions "
148 | "may be optionally specified in columns three (ra) and "
149 | "four (dec), surrounded by brackets -- but, in that case, "
150 | "both of them are required." % line
151 | )
152 | raise ValueError(msg)
153 |
154 | ra = float(match.group("ra"))
155 | if not 0 <= ra < 360:
156 | msg = "Right ascension '%s' not in range [0, 360[ degrees"
157 | raise ValueError(msg % ra)
158 |
159 | dec = float(match.group("dec"))
160 | if not -90 <= dec <= 90:
161 | msg = "Declination '%s' not in range [-90, 90] degrees"
162 | raise ValueError(msg % dec)
163 |
164 | pm_ra = match.group("pm_ra")
165 | if pm_ra is not None:
166 | pm_ra = float(pm_ra)
167 |
168 | pm_dec = match.group("pm_dec")
169 | if pm_dec is not None:
170 | pm_dec = float(pm_dec)
171 |
172 | yield ra, dec, pm_ra, pm_dec
173 |
--------------------------------------------------------------------------------
/customparser.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | # Copyright (c) 2013 Victor Terron. All rights reserved.
4 | # Institute of Astrophysics of Andalusia, IAA-CSIC
5 | #
6 | # This file is part of LEMON.
7 | #
8 | # LEMON is free software: you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | import copy
22 | import optparse
23 | import re
24 | import textwrap
25 |
26 | # LEMON modules
27 | import passband
28 |
29 |
30 | class NewlinesFormatter(optparse.IndentedHelpFormatter):
31 | """This quick-and-dirty trick prevents optparse from stripping newlines
32 | (using textwrap) when the description of the module is printed. This should
33 | be acceptable enough until the transition to argparse is made."""
34 |
35 | def _format_text(self, text):
36 | text_width = self.width - self.current_indent
37 | indent = " " * self.current_indent
38 | # Wrap one paragraph at a time, then concatenate them
39 | formatted_text = ""
40 | for paragraph in text.split("\n\n"):
41 |
42 | formatted_text += textwrap.fill(
43 | paragraph.strip(),
44 | text_width,
45 | initial_indent=indent,
46 | subsequent_indent=indent,
47 | )
48 | formatted_text += "\n\n"
49 |
50 | return formatted_text.rstrip()
51 |
52 |
53 | def check_passband(option, opt, value):
54 | """Type-checking function for the 'passband' optparse option type.
55 |
56 | This is the type-checking function for 'passband', a custom optparse type
57 | that accepts a string with the name of a photometric filter and returns
58 | it as a passband.Passband object. 'option' is an optpase.Option instance,
59 | 'opt' is the option string (e.g., -f), and 'value' is the string from the
60 | command line that must be checked and converted to a Passband object.
61 |
62 | In case of doubt, please refer to:
63 | http://docs.python.org/2.7/library/optparse.html#adding-new-types
64 |
65 | """
66 |
67 | try:
68 | return passband.Passband(value)
69 | except ValueError, e:
70 | msg = "option %s: invalid photometric filter: %r (%s)"
71 | raise optparse.OptionValueError(msg % (opt, value, e))
72 |
73 |
74 | class PassbandOption(optparse.Option):
75 | """Custom optparse option type encapsulating a photometric filter.
76 |
77 | This subclass of optparse's Option class implements 'passband', a custom
78 | option type: it receives a string with the name of a photometric filter,
79 | such as 'Johnson V', and returns it as a passband.Passband object. This
80 | option supports all the photometric systems allowed by the Passband class.
81 |
82 | """
83 |
84 | # A tuple of type names
85 | TYPES = optparse.Option.TYPES + ("passband",)
86 | # A dictionary mapping type names to type-checking functions
87 | TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
88 | TYPE_CHECKER["passband"] = check_passband
89 |
90 |
91 | def get_parser(description):
92 | """Return the OptionParser object used in the LEMON modules.
93 |
94 | This function instantiates an optparse.OptionParser object and returns it.
95 | Its 'description' argument (a paragraph of text giving a brief overview of
96 | the program) is set to the value of the argument of the same name, while
97 | the NewlinesFormatter class is used for printing help text ('formatter'
98 | argument). This parser adds a custom option type, 'passband', which
99 | receives a string with the name of a photometric filter and converts it to
100 | a passband.Passband object. Neither the default -h nor --help options are
101 | included.
102 |
103 | """
104 |
105 | kwargs = dict(
106 | description=description,
107 | add_help_option=False,
108 | formatter=NewlinesFormatter(),
109 | option_class=PassbandOption,
110 | )
111 | parser = optparse.OptionParser(**kwargs)
112 | return parser
113 |
114 |
115 | def clear_metavars(parser):
116 | """Set all the meta-variables of an OptionParser to a whitespace.
117 |
118 | This is a hackish convenience function to set the meta-variables of all the
119 | options of an OptionParser, independently of whether they are contained in
120 | option groups, to the string ' '. This is not an empty string, but a string
121 | consisting of a whitespace: this is necessary because if the meta-variable
122 | evaluates to False the OptionParser converts the destination variable name
123 | to uppercase and uses that instead.
124 |
125 | Using this function on an OptionParser instance clears the meta-variables,
126 | which means that where the help message showed '--filename=FILE' now only
127 | '--filename=' will be displayed, with the equals sign indicating the fact
128 | that the option takes a value.
129 |
130 | """
131 |
132 | EMPTY_VALUE = " "
133 | for option in parser.option_list:
134 | option.metavar = EMPTY_VALUE
135 | for group in parser.__dict__["option_groups"]:
136 | for option in group.option_list:
137 | option.metavar = EMPTY_VALUE
138 |
139 |
140 | def additional_options_callback(option, opt_str, value, parser):
141 | """opt-parse callback function to parse option strings.
142 |
143 | Use this function to parse a string containing an option with, if any, the
144 | argument that it takes. For example, given the string '--downsample = 2',
145 | '--downsample' would be recognized as the name of the option, and '2' as
146 | the corresponding argument. The name of the option is mapped to the value
147 | in option.dest, which is expected to be a dictionary. If the option does
148 | not take any argument (for example, '-v' or '--verbose'), it is mapped to
149 | None. Raises ValueError if the string can not be successfully parsed.
150 |
151 | option - the Option instance that is calling the callback.
152 | opt_str - the option string seen on the command-line.
153 | value - the argument to this option seen on the command-line.
154 | parser - the OptionParser instance driving the whole thing.
155 |
156 | """
157 |
158 | regexp = "=?(?P-+[\w-]+)\s*=?\s*(?P\w+)?"
159 | match = re.match(regexp, value)
160 | if not match:
161 | msg = (
162 | "cannot parse additional option. Are you using the GNU/POSIX "
163 | "syntax? Note that options should always start with one or "
164 | "two hyphens. Examples of valid syntax are '-o', '-o value', "
165 | "'--name' and '--name=value', among many others."
166 | )
167 | raise ValueError(msg)
168 |
169 | opt_ = match.group("option")
170 | value = match.group("value")
171 | getattr(parser.values, option.dest)[opt_] = value
172 |
--------------------------------------------------------------------------------