├── .gitmodules
├── COPYING
├── Makefile.am
├── README.linux
├── README.md
├── README.win
├── build-wheels.ps1
├── build-wheels.sh.in
├── configure.ac
├── gen-geocentric-data-code.py
├── m4
└── .keep
├── matlab
├── Makefile.am
├── sg2_jd_to_ms.m
├── sg2_jd_to_ymdh.cxx
├── sg2_jd_to_ymdh.m
├── sg2_ms_to_jd.m
├── sg2_sun_position.cxx
├── sg2_sun_position.m
├── sg2_sun_rise.cxx
├── sg2_topocentric_correction_refraction_SAE.cxx
├── sg2_topocentric_correction_refraction_SAE.m
├── sg2_topocentric_correction_refraction_ZIM.cxx
├── sg2_topocentric_correction_refraction_ZIM.m
└── sg2_ymdh_to_jd.m
├── pyproject.toml
├── python
└── pysg2.cxx
├── resources
├── geocentric.data
└── geocentric.data.README
├── setup.py
├── sg2.pc.in
└── src
├── Makefile.am
├── sg2.cxx
├── sg2.hxx
├── sg2_benchmark.cxx
├── sg2_convert_date.cxx
├── sg2_data_handler.cxx
├── sg2_data_handler.hxx
├── sg2_date.cxx
├── sg2_date.hxx
├── sg2_err.hxx
├── sg2_geocentric.cxx
├── sg2_geocentric.hxx
├── sg2_geopoint.cxx
├── sg2_geopoint.hxx
├── sg2_math.hxx
├── sg2_query.cxx
├── sg2_topocentric.cxx
├── sg2_topocentric.hxx
├── sg2_typedef.hxx
├── sg2_utils.cxx
└── sg2_utils.hxx
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "third-parties/python-bind-helper"]
2 | path = third-parties/python-bind-helper
3 | url = https://github.com/gschwind/python-bind-helper.git
4 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/Makefile.am:
--------------------------------------------------------------------------------
1 | SUBDIRS = src matlab
2 |
3 | ACLOCAL_AMFLAGS = -I m4
4 |
5 | pkgconfigdir = $(libdir)/pkgconfig
6 | pkgconfig_DATA = sg2.pc
7 |
8 | EXTRA_DIST = \
9 | resources \
10 | python/pysg2.cxx \
11 | third-parties/python-bind-helper/python-bind-helper.hxx \
12 | pyproject.toml \
13 | setup.py \
14 | gen-geocentric-data-code.py \
15 | build-wheels.sh \
16 | build-wheels.ps1 \
17 | README.linux \
18 | README.win
19 |
20 |
--------------------------------------------------------------------------------
/README.linux:
--------------------------------------------------------------------------------
1 |
2 | docker images for x86:
3 | quay.io/pypa/manylinux2014_x86_64
4 | quay.io/pypa/manylinux2014_i686
5 | quay.io/pypa/manylinux_2_28_x86_64
6 |
7 |
8 | docker images for aarch64:
9 | quay.io/pypa/manylinux_2_28_aarch64
10 | quay.io/pypa/manylinux2014_aarch64
11 |
12 |
13 | Build python wheels:
14 |
15 | docker pull "$img"
16 | docker container run -t -v "$(pwd):/io" "$img" /io/build-wheels.sh
17 |
18 |
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Solar Geometry 2
2 |
3 | Solar Geometry 2 (SG2) is the second generation of library for computing the relative position of the sun and the earth. Valid over the time period 1980-2100, the algorithm is 20 times faster than the well-know SPA algorithm, with an accuracy order of approx. 0.005°. Reference article: Blanc P. and L. Wald, The SG2 algorithm for a fast and accurate computation of the position of the sun for multi-decadal time period. Solar Energy 88, 3072-3083, 2012, doi: 10.1016/j.solener.2012.07.018.
4 |
5 | # License
6 |
7 | Solar Geometry 2 is released under [LGPLv3](https://www.gnu.org/licenses/lgpl-3.0.html).
8 |
9 | # Python binding
10 |
11 | ## Python installation
12 |
13 | We provide wheel binary package that can be installed using pip:
14 |
15 | ```
16 | pip install sg2
17 | ```
18 |
19 | To compile it see README.linux or README.win.
20 |
21 | ## Python usage
22 |
23 | see help(sg2)
24 |
25 | ```
26 | $ python3
27 | >>> import sg2
28 | >>> help(sg2)
29 | ```
30 |
31 |
--------------------------------------------------------------------------------
/README.win:
--------------------------------------------------------------------------------
1 | Matlab build
2 | ============
3 |
4 | Start with:
5 | mex -setup
6 | To ensure that a compiler is properly setup then:
7 |
8 | mex -v -output matlab/sg2_sun_position.mexa64 matlab/sg2_sun_position.cxx -O -Isrc/ src/sg2.cxx src/sg2_date.cxx src/sg2_utils.cxx src/sg2_geocentric.cxx src/sg2_geopoint.cxx src/sg2_topocentric.cxx src/sg2_data_handler.cxx src/sg2_geocentric_data.cxx
9 | mex -v -output matlab/sg2_sun_rise.mexa64 matlab/sg2_sun_rise.cxx -O -Isrc/ src/sg2.cxx src/sg2_date.cxx src/sg2_utils.cxx src/sg2_geocentric.cxx src/sg2_geopoint.cxx src/sg2_topocentric.cxx src/sg2_data_handler.cxx src/sg2_geocentric_data.cxx
10 | mex -v -output matlab/sg2_topocentric_correction_refraction_ZIM.mexa64 matlab/sg2_topocentric_correction_refraction_ZIM.cxx -O -Isrc/ src/sg2.cxx src/sg2_date.cxx src/sg2_utils.cxx src/sg2_geocentric.cxx src/sg2_geopoint.cxx src/sg2_topocentric.cxx src/sg2_data_handler.cxx src/sg2_geocentric_data.cxx
11 | mex -v -output matlab/sg2_topocentric_correction_refraction_SAE.mexa64 matlab/sg2_topocentric_correction_refraction_SAE.cxx -O -Isrc/ src/sg2.cxx src/sg2_date.cxx src/sg2_utils.cxx src/sg2_geocentric.cxx src/sg2_geopoint.cxx src/sg2_topocentric.cxx src/sg2_data_handler.cxx src/sg2_geocentric_data.cxx
12 |
13 |
14 |
--------------------------------------------------------------------------------
/build-wheels.ps1:
--------------------------------------------------------------------------------
1 |
2 | $pyvers="38","39","310","311","312"
3 | foreach($ver in $pyvers) {
4 | if (Test-Path -LiteralPath "dev-py$ver") {
5 | Remove-Item -LiteralPath "dev-py$ver" -Force -Recurse
6 | }
7 | Start-Process -Wait -NoNewWindow -FilePath "C:\Python$ver\python.exe" -ArgumentList '-m','venv',"dev-py$ver"
8 | Start-Process -Wait -NoNewWindow -FilePath "$PSScriptRoot\dev-py$ver\Scripts\python.exe" -ArgumentList '-m','pip',"install","-U","pip"
9 | . "$PSScriptRoot\dev-py$ver\Scripts\Activate.ps1"
10 | pip wheel . -w .\wheelhouse
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/build-wheels.sh.in:
--------------------------------------------------------------------------------
1 | #/bin/bash
2 | # -*- coding: utf-8 -*-
3 | #
4 | # copyright (2021) MINES ParisTech
5 | # copyright (2021) Benoit Gschwind
6 | #
7 | # This script is used to build wheel package for python.
8 | #
9 | # it is intended to be used in docker as follow:
10 | # docker pull "quay.io/pypa/manylinux2014_x86_64"
11 | # docker container run -t -v "$(pwd):/io" "quay.io/pypa/manylinux2014_x86_64" /io/build-wheel.sh
12 | #
13 |
14 | # run this section only if user is root
15 | if [ $UID == 0 ]; then
16 | useradd -m user
17 | runuser -u user -- "$0" "$@"
18 | cp /home/user/wheelhouse/* /io/wheelhouse/
19 | exit 0
20 | fi
21 |
22 | # This section is run if we aren't root
23 |
24 | function repair_wheel {
25 | wheel="$1"
26 | if ! auditwheel show "$wheel"; then
27 | echo "Skipping non-platform wheel $wheel"
28 | else
29 | auditwheel repair "$wheel" --plat "$AUDITWHEEL_PLAT" -w /io/wheelhouse/
30 | fi
31 | }
32 |
33 | export PATH=$PATH:/home/user/.local/bin
34 | tar -xzf /io/sg2-@PACKAGE_VERSION@.tar.gz -C /home/user/
35 | cd /home/user/sg2-@PACKAGE_VERSION@
36 |
37 | # Compile wheels
38 | for PYBIN in cp38-cp38 cp39-cp39 cp310-cp310 cp311-cp311 cp312-cp312; do
39 | "/opt/python/${PYBIN}/bin/pip" wheel . --no-deps -w /home/user/wheelhouse/
40 | done
41 |
42 | if [ -n "$AUDITWHEEL_PLAT" ]; then
43 | for whl in /home/user/wheelhouse/*.whl; do
44 | repair_wheel "$whl"
45 | done
46 | fi
47 |
48 |
--------------------------------------------------------------------------------
/configure.ac:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | AC_PREREQ([2.71])
4 | AC_INIT([sg2],[2.3.3])
5 | AC_CONFIG_MACRO_DIR([m4])
6 | AM_MAINTAINER_MODE([disable])
7 |
8 | LT_PREREQ([2.4.2])
9 | LT_INIT([pic-only shared static])
10 | AM_INIT_AUTOMAKE([foreign])
11 |
12 | AC_CONFIG_SRCDIR([src/sg2.cxx])
13 |
14 | # Checks for programs.
15 | AC_PROG_CXX
16 | AC_PROG_INSTALL
17 | AM_PROG_AS
18 |
19 | AM_PATH_PYTHON([3.7.0])
20 |
21 | # check for c++11
22 | AX_CXX_COMPILE_STDCXX_11(noext, mandatory)
23 |
24 | AC_ARG_ENABLE(vdt, [AS_HELP_STRING([--enable-vdt], [Use VDT library for fast trigonometric math])], [
25 | PKG_CHECK_MODULES(VDT, vdt)
26 | AC_SUBST(VDT_DEFINE, [-DSG2_HAVE_VDT])
27 | AC_SUBST(VDT_CFLAGS)
28 | AC_SUBST(VDT_LIBS)
29 | ])
30 |
31 | AC_ARG_ENABLE(matlab, [AS_HELP_STRING([--enable-matlab], [Generate matlab mex files])], [
32 | AC_PATH_PROG(MEX, [mex])
33 | if test -z "$ac_cv_path_MEX"; then
34 | AC_MSG_ERROR([mex executable not found.])
35 | fi
36 | AC_SUBST(MEX)
37 | AC_SUBST(MATLAB_MEX, ["sg2_sun_position.mexa64 sg2_topocentric_correction_refraction_ZIM.mexa64 sg2_topocentric_correction_refraction_SAE.mexa64 sg2_sun_rise.mexa64"])
38 | ])
39 |
40 | AC_CONFIG_FILES([
41 | Makefile
42 | src/Makefile
43 | matlab/Makefile
44 | sg2.pc
45 | ])
46 | AC_CONFIG_FILES([build-wheels.sh], [chmod +x build-wheels.sh])
47 | AC_OUTPUT
48 |
--------------------------------------------------------------------------------
/gen-geocentric-data-code.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # coding=utf-8
3 |
4 | import sys
5 | import struct
6 |
7 | DOUBLE_SAFE_FORMAT="{:.17g}"
8 |
9 | def double_to_literal(b):
10 | return DOUBLE_SAFE_FORMAT.format(struct.unpack("d", b)[0])
11 |
12 | data = bytes()
13 | with open(sys.argv[1], "rb") as f:
14 | buf = f.read()
15 | while len(buf)>0:
16 | data += buf
17 | buf = f.read()
18 |
19 | time_offset, time_delta, count = struct.unpack("qqq", data[0:8*3])
20 |
21 | hex_data = [double_to_literal(data[b:b+8]) for b in range(8*3, len(data), 8)]
22 |
23 | print("/* code generated by gen-geocentric-data-code.py */")
24 |
25 | # Sanity check that converting data to float and conveting them back
26 | # provide the same result.
27 | c = 0
28 | e = 0
29 | for b in range(8*3, len(data), 8):
30 | c += 1
31 | h = data[b:b+8]
32 | o = struct.pack("d", float(double_to_literal(h)))
33 | if h != o:
34 | e+=1
35 | print(f"/* Error: {c} {d:.18g} ({struct.unpack('d', o)[0]:.18g}) {h.hex()} != {o.hex()} */")
36 | print(f"/* invalid conversions: {e}/{c} */")
37 |
38 | if e != 0:
39 | exit(1)
40 |
41 | print("#include ")
42 |
43 | print('#include "sg2_data_handler.hxx"')
44 |
45 | print("namespace sg2 {")
46 | print(f"int64_t const _geocentric_data_time_offset = {time_offset}l;")
47 | print(f"int64_t const _geocentric_data_time_delta = {time_delta}l;")
48 | print(f"int64_t const _geocentric_data_xcount = {count}l;")
49 | print("geocentric_data_format const _geocentric_data[_geocentric_data_xcount] = {")
50 | print(",\n".join(("{"+",".join(hex_data[n:n+3])+"}" for n in range(0, len(hex_data), 3))))
51 | print("};")
52 | print("} // namespace sg2")
53 |
54 |
--------------------------------------------------------------------------------
/m4/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gschwind/sg2/ca99a3ac35454ae85a4b2ec02c231c5bdebf2aa3/m4/.keep
--------------------------------------------------------------------------------
/matlab/Makefile.am:
--------------------------------------------------------------------------------
1 |
2 | EXTRA_DIST = \
3 | sg2_ms_to_jd.m \
4 | sg2_jd_to_ms.m \
5 | sg2_jd_to_ymdh.m \
6 | sg2_sun_position.m \
7 | sg2_topocentric_correction_refraction_SAE.m \
8 | sg2_topocentric_correction_refraction_ZIM.m \
9 | sg2_ymdh_to_jd.m \
10 | sg2_sun_position.cxx \
11 | sg2_topocentric_correction_refraction_ZIM.cxx \
12 | sg2_topocentric_correction_refraction_SAE.cxx \
13 | sg2_sun_rise.cxx
14 |
15 | all: @MATLAB_MEX@
16 |
17 | %.mexa64: %.cxx
18 | @MEX@ -output $@ $^ -O -v -I$(top_srcdir)/src $(top_builddir)/src/.libs/libsg2.a
19 |
20 | clean:
21 | rm -f sg2_sun_position.mexa64
22 | rm -f sg2_topocentric_correction_refraction_ZIM.mexa64
23 | rm -f sg2_topocentric_correction_refraction_SAE.mexa64
24 | rm -f sg2_sun_rise.mexa64
25 |
--------------------------------------------------------------------------------
/matlab/sg2_jd_to_ms.m:
--------------------------------------------------------------------------------
1 | function ms = sg2_jd_to_ms(jd)
2 | % function ms = sg2_ms_to_jd(jd)
3 | % Transformation 1-D int64 array of size (M,1) of milliseconds since
4 | % 1970-01-01T00:00:00Z into a 1-D array of Julian Date (jd) of size (M,1)
5 | %
6 | %
7 | % Input:
8 | %
9 | % jd: N-D array of double
10 | %
11 | % Output:
12 | %
13 | % ms: N-D int64 array with the same size of jd
14 |
15 | shape = size(jd);
16 | jd = jd(:);
17 |
18 | jd_notnan = ~isnan(jd);
19 |
20 | jd0 = 2440587.5;
21 |
22 | ms = repmat(int64(-2^63),size(jd));
23 | ms(jd_notnan) = int64((jd(jd_notnan)-jd0)*86400000.0);
24 | ms = reshape(ms, shape);
25 |
26 |
27 |
--------------------------------------------------------------------------------
/matlab/sg2_jd_to_ymdh.cxx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 MINES ParisTech
3 | * Copyright 2022-2021 Benoit Gschwind
4 | *
5 | * This file is part of libsg2.
6 | *
7 | * This program is free software: you can redistribute it and/or modify
8 | * it under the terms of the Lesser GNU General Public License as
9 | * published by the Free Software Foundation, either version 3 of the
10 | * License, or (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 | * Lesser GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the Lesser GNU General Public
18 | * License along with this program. If not, see
19 | * .
20 | */
21 |
22 | // Matlab headers
23 | #include
24 | #include
25 |
26 | #include
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] )
34 | {
35 |
36 | if (nrhs != 1)
37 | mexErrMsgIdAndTxt("sg2:InvalidArgumentsCount", "sg2_ymdh_to_jd need 1 arguments got %d", nrhs);
38 |
39 | if (nlhs != 4)
40 | mexErrMsgIdAndTxt("sg2:InvalidArgumentsCount", "sg2_ymdh_to_jd need 4 return values got %d", nlhs);
41 |
42 | for (int i = 0; i < 1; ++i) {
43 | if (mxGetClassID(prhs[i]) != mxDOUBLE_CLASS)
44 | mexErrMsgIdAndTxt("sg2:TypeError", "Arguments %d must be array of doubles", i);
45 | }
46 |
47 | double * jd = reinterpret_cast(mxGetData(prhs[0]));
48 |
49 | plhs[0] = mxCreateDoubleMatrix(mxGetM(prhs[0]), mxGetN(prhs[0]), mxREAL);
50 | double * year = reinterpret_cast(mxGetData(plhs[0]));
51 | plhs[1] = mxCreateDoubleMatrix(mxGetM(prhs[0]), mxGetN(prhs[0]), mxREAL);
52 | double * month = reinterpret_cast(mxGetData(plhs[1]));
53 | plhs[2] = mxCreateDoubleMatrix(mxGetM(prhs[0]), mxGetN(prhs[0]), mxREAL);
54 | double * day = reinterpret_cast(mxGetData(plhs[2]));
55 | plhs[3] = mxCreateDoubleMatrix(mxGetM(prhs[0]), mxGetN(prhs[0]), mxREAL);
56 | double * hours = reinterpret_cast(mxGetData(plhs[3]));
57 |
58 | int const n = mxGetM(prhs[0]) * mxGetN(prhs[0]);
59 | for (int i = 0; i < n; ++i) {
60 | sg2::ymdh d{jd[i]};
61 | year[i] = d.year;
62 | month[i] = d.month;
63 | day[i] = d.day_of_month;
64 | hours[i] = d.hour;
65 | }
66 |
67 | }
68 |
69 |
70 |
--------------------------------------------------------------------------------
/matlab/sg2_jd_to_ymdh.m:
--------------------------------------------------------------------------------
1 | function ymdh = sg2_jd_to_ymdh(jd)
2 | % function ymdh = sg2_jd_to_ymdh(jd)
3 | %
4 | % Transformation of a N-D array of Julian Date (jd) into a
5 | % (N+1)-D array of size with (year month day hour)
6 | %
7 | % Input:
8 | %
9 | % jd: N-D array of double
10 | %
11 | % Output:
12 | %
13 | % ymdh: (N+1)-D array of size [size(jd) 4] with (y,m,d,h) elements where:
14 | % - y is an array of year number
15 | % - m is an array of month number in [1,12]
16 | % - d is an array of day of month in [1,31]
17 | % - h is an array of decimal hour within the day, ex. 12:30 is 12.5
18 |
19 | shape = size(jd);
20 | jd = jd(:);
21 |
22 | H = (jd + 0.5 - floor(jd + 0.5)) * 24;
23 | L = floor(jd + 0.5) + 68569;
24 | N = floor(4 * L / 146097);
25 | L = L - floor((146097 * N + 3) / 4);
26 | I = floor(4000 * (L + 1) / 1461001);
27 | L = L - floor(1461 * I / 4) + 31;
28 |
29 | J = floor(80 * L / 2447);
30 | K = L - floor(2447 * J / 80);
31 | L = floor(J / 11);
32 | J = J + 2 - 12 * L;
33 | I = 100 * (N - 49) + I + L;
34 |
35 | ymdh = reshape([I J K H],[shape 4]);
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/matlab/sg2_ms_to_jd.m:
--------------------------------------------------------------------------------
1 | function jd = sg2_ms_to_jd(ms)
2 | % function jd = sg2_ms_to_jd(ms)
3 | % Transformation of a 1-D array of Julian Date (jd) of size (M,1) into a
4 | % 1-D int64 array of size (M,1) of milliseconds since 1970-01-01T00:00:00Z
5 | %
6 | % Input:
7 | %
8 | % ms: N-D int64 array
9 | %
10 | % Output:
11 | %
12 | % jd: N-D array of double with the same size of ms
13 |
14 | if (~isa(ms,'int64'))
15 | error('input ms must be of type int64');
16 | end
17 |
18 | jd0 = 2440587.5;
19 |
20 | shape = size(ms);
21 | ms = ms(:);
22 | ms_notnan = (ms~=int64(-2^63));
23 |
24 | jd = nan(size(ms));
25 | jd(ms_notnan) = jd0 + double(ms(ms_notnan))/86400000.0;
26 |
27 | jd = reshape(jd, shape);
28 |
29 |
--------------------------------------------------------------------------------
/matlab/sg2_sun_position.cxx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 MINES ParisTech
3 | * Copyright 2022 Benoit Gschwind
4 | *
5 | * This file is part of libsg2.
6 | *
7 | * This program is free software: you can redistribute it and/or modify
8 | * it under the terms of the Lesser GNU General Public License as
9 | * published by the Free Software Foundation, either version 3 of the
10 | * License, or (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 | * Lesser GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the Lesser GNU General Public
18 | * License along with this program. If not, see
19 | * .
20 | */
21 |
22 | #include
23 | #include
24 |
25 | #include
26 |
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | using namespace std;
34 |
35 | namespace { // anonymous namespace
36 |
37 | // This structucture is a convenient structure to avoid to constantly call
38 | // mxGetM, mxGetData or mxGetN
39 | struct mxArray_handler {
40 | mxArray const * arr;
41 | void * data_ptr;
42 | int M; // N row
43 | int N; // N col
44 |
45 | mxArray_handler(mxArray_handler const &) = default;
46 | mxArray_handler & operator=(mxArray_handler const &) = default;
47 |
48 | mxArray_handler(mxArray const * arr) : arr{arr} {
49 | data_ptr = mxGetData(arr);
50 | M = mxGetM(arr);
51 | N = mxGetN(arr);
52 | }
53 |
54 | template
55 | T& get(int y, int x) const
56 | {
57 | return reinterpret_cast(data_ptr)[y + M*x];
58 | }
59 |
60 | };
61 |
62 |
63 | template
64 | struct _map_api {
65 | virtual auto create(int n, int j) const -> mxArray * = 0;
66 | virtual void apply(mxArray_handler & dst, int y, int x, T const & d) const = 0;
67 | virtual void clear(mxArray_handler & dst, int y, int x) const = 0;
68 | };
69 |
70 |
71 | template
72 | struct _map_data {
73 | string const & key;
74 | mxArray_handler dst;
75 | _map_api * api;
76 | };
77 |
78 |
79 | template
80 | struct _map_api_objmember : public _map_api {
81 |
82 | virtual void apply(mxArray_handler & dst, int y, int x, T const & d) const override
83 | {
84 | dst.get(y, x) = d.*m;
85 | }
86 |
87 | };
88 |
89 | template
90 | struct _map_api_objmember_double : public _map_api_objmember {
91 |
92 | static _map_api * _new()
93 | {
94 | static _map_api_objmember_double singleton;
95 | return &singleton;
96 | }
97 |
98 | virtual auto create(int n, int j) const -> mxArray * override
99 | {
100 | return mxCreateDoubleMatrix(n, j, mxREAL);
101 | }
102 |
103 | virtual void clear(mxArray_handler & dst, int n, int j) const override
104 | {
105 | dst.get(n, j) = std::nan("");
106 | }
107 |
108 | };
109 |
110 |
111 | template
112 | struct _map_api_objmember_date : public _map_api_objmember {
113 |
114 | static _map_api * _new()
115 | {
116 | static _map_api_objmember_date singleton;
117 | return &singleton;
118 | }
119 |
120 | virtual auto create(int n, int j) const -> mxArray * override
121 | {
122 | return mxCreateNumericMatrix(n, j, mxINT64_CLASS, mxREAL);
123 | }
124 |
125 | virtual void apply(mxArray_handler & dst, int y, int x, T const & d) const override
126 | {
127 | dst.get(y, x) = (d.*m).msec;
128 | }
129 |
130 | virtual void clear(mxArray_handler & dst, int y, int x) const override
131 | {
132 | dst.get(y, x) = std::numeric_limits::min();
133 | }
134 |
135 | };
136 |
137 |
138 | /*
139 | * return a pointer map the map name of arg1 (a Python list) to sg2 objects
140 | *
141 | * returned pointer must be freed.
142 | */
143 | template
144 | struct _generic_handler {
145 | using _type = T;
146 | vector<_map_data<_type>> ref;
147 |
148 | void _construct(mxArray const * fields, unordered_map * > const & m, regex & r, int n_row, int n_col)
149 | {
150 | unordered_set inserted;
151 | int nfields = mxGetN(fields);
152 | for (int i = 0; i < nfields; ++i) {
153 | mxArray * cell = mxGetCell(fields, i);
154 | char fields_value[100];
155 | mxGetString(cell, fields_value, 100);
156 | cmatch cm;
157 | if (!regex_match(fields_value, cm, r)) {
158 | continue;
159 | }
160 |
161 | // If the field is all, insert all known fields.
162 | if (cm[1] == "all") {
163 | for (auto & x: m) {
164 | if (inserted.count(x.first) == 0) {
165 | inserted.insert(x.first);
166 | ref.emplace_back(_map_data<_type>{x.first, x.second->create(n_row, n_col), x.second});
167 | }
168 | }
169 | continue;
170 | }
171 |
172 | auto x = m.find(cm[1]);
173 | if (x != m.end() && inserted.count(x->first) == 0) {
174 | inserted.insert(x->first);
175 | ref.emplace_back(_map_data<_type>{x->first, x->second->create(n_row, n_col), x->second});
176 | }
177 | }
178 | }
179 |
180 | void apply(int y, int x, _type const & d)
181 | {
182 | for(auto & r: ref) {
183 | r.api->apply(r.dst, y, x, d);
184 | }
185 | }
186 |
187 | mxArray * as_object ()
188 | {
189 | vector field_def;
190 | for (auto & r: ref) {
191 | field_def.push_back(r.key.c_str());
192 | }
193 |
194 | mxArray * o = mxCreateStructMatrix(1, 1, field_def.size(), &field_def[0]);
195 | for (int i = 0; i < ref.size(); ++i) {
196 | mxSetFieldByNumber(o, 0, i, const_cast(ref[i].dst.arr));
197 | }
198 | return o;
199 | }
200 |
201 | void clear(int y, int x) {
202 | for(auto & r: ref) {
203 | r.api->clear(r.dst, y, x);
204 | }
205 | }
206 |
207 | ~_generic_handler()
208 | {
209 |
210 | }
211 |
212 | };
213 |
214 |
215 | /*
216 | * return a pointer map the map name of arg1 (a Python list) to sg2 objects
217 | *
218 | * returned pointer must be freed.
219 | */
220 | struct _geocentric : public _generic_handler {
221 |
222 | _geocentric(mxArray const * fields, int nt)
223 | {
224 | static unordered_map * > const m = {
225 | {"ut", _map_api_objmember_date <_type, &_type::ut >::_new()},
226 | {"tt", _map_api_objmember_date <_type, &_type::tt >::_new()},
227 | {"R", _map_api_objmember_double<_type, &_type::R >::_new()},
228 | {"L", _map_api_objmember_double<_type, &_type::L >::_new()},
229 | {"delta", _map_api_objmember_double<_type, &_type::delta >::_new()},
230 | {"EOT", _map_api_objmember_double<_type, &_type::EOT >::_new()},
231 | {"Theta_a", _map_api_objmember_double<_type, &_type::Theta_a >::_new()},
232 | {"epsilon", _map_api_objmember_double<_type, &_type::epsilon >::_new()},
233 | {"nu", _map_api_objmember_double<_type, &_type::nu >::_new()},
234 | {"r_alpha", _map_api_objmember_double<_type, &_type::r_alpha >::_new()},
235 | {"Delta_psi",_map_api_objmember_double<_type, &_type::Delta_psi >::_new()}
236 | };
237 |
238 | static regex r{"^geoc\\.(.+)$"};
239 | _construct(fields, m, r, 1, nt);
240 |
241 | }
242 | };
243 |
244 |
245 | struct _geopoint : public _generic_handler {
246 |
247 | _geopoint(mxArray const * fields, int np)
248 | {
249 | static unordered_map *> m = {
250 | {"lambda", _map_api_objmember_double<_type, &_type::lambda >::_new()},
251 | {"phi", _map_api_objmember_double<_type, &_type::phi >::_new()},
252 | {"u", _map_api_objmember_double<_type, &_type::u >::_new()},
253 | {"x", _map_api_objmember_double<_type, &_type::x >::_new()},
254 | {"y", _map_api_objmember_double<_type, &_type::y >::_new()}
255 | };
256 |
257 | static regex r{"^gp\\.(.+)$"};
258 | _construct(fields, m, r, 1, np);
259 |
260 | }
261 |
262 | };
263 |
264 |
265 | struct _topoc : public _generic_handler {
266 |
267 | _topoc(mxArray const * fields, int np, int nt)
268 | {
269 | static unordered_map *> m = {
270 | {"delta", _map_api_objmember_double<_type, &_type::delta >::_new()},
271 | {"alpha_S", _map_api_objmember_double<_type, &_type::alpha_S >::_new()},
272 | {"gamma_S0", _map_api_objmember_double<_type, &_type::gamma_S0 >::_new()},
273 | {"omega", _map_api_objmember_double<_type, &_type::omega >::_new()},
274 | {"r_alpha", _map_api_objmember_double<_type, &_type::r_alpha >::_new()},
275 | {"toa_hi", _map_api_objmember_double<_type, &_type::toa_hi >::_new()},
276 | {"toa_ni", _map_api_objmember_double<_type, &_type::toa_ni >::_new()}
277 | };
278 |
279 | static regex r{"^topoc\\.(.+)$"};
280 | _construct(fields, m, r, np, nt);
281 |
282 | }
283 |
284 | };
285 |
286 | } // anonymous namespace
287 |
288 |
289 | static inline bool mxIsInteger(mxArray * a)
290 | {
291 | auto c = mxGetClassID(a);
292 | return (c==mxUINT8_CLASS)
293 | || (c==mxUINT16_CLASS)
294 | || (c==mxUINT32_CLASS)
295 | || (c==mxUINT32_CLASS)
296 | || (c==mxINT8_CLASS)
297 | || (c==mxINT16_CLASS)
298 | || (c==mxINT32_CLASS)
299 | || (c==mxINT32_CLASS);
300 | }
301 |
302 | static inline bool mxIsFloating(mxArray * a)
303 | {
304 | auto c = mxGetClassID(a);
305 | return (c==mxDOUBLE_CLASS)
306 | || (c==mxSINGLE_CLASS);
307 | }
308 |
309 | template
310 | static inline void read_time(mxArray const * in, vector & geoc_list, _geocentric & geocx, int nt)
311 | {
312 | mxArray_handler arr{in};
313 | if (mxGetM(in) == 1) {
314 | for (int i = 0; i < nt; ++i) {
315 | geoc_list[i] = sg2::geocentric_data{arr.get(0, i)};
316 | geocx.apply(0, i, geoc_list[i]);
317 | }
318 | } else { // nd1 == 2
319 | for (int i = 0; i < nt; ++i) {
320 | geoc_list[i] = sg2::geocentric_data{
321 | arr.get(0, i),
322 | arr.get(1, i)
323 | };
324 | geocx.apply(0, i, geoc_list[i]);
325 | }
326 | }
327 | }
328 |
329 |
330 | void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] )
331 | {
332 |
333 | double * arr0 = NULL;
334 | double * arr1 = NULL;
335 |
336 | try {
337 |
338 | double * arg0 = NULL;
339 | int arg3 = 0;
340 |
341 | int err = 0;
342 | int i, j;
343 | int warning_count = 0;
344 | vector geoc_list;
345 | vector geopoint_list;
346 | int nd0, nd1, np, nt;
347 |
348 | /* get args */
349 | if (nrhs != 3) {
350 | mexErrMsgIdAndTxt("sg2:InvalidArgumentCount", "Invalid inputs arguments, expect 3 arguments");
351 | }
352 |
353 | /* Get an array from any python object */
354 | arr0 = mxGetPr(prhs[0]);
355 | if (arr0 == NULL) {
356 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Invalid inputs arguments, the first arguments must be an array");
357 | }
358 |
359 | arr1 = mxGetPr(prhs[1]);
360 | if (arr1 == NULL) {
361 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Invalid inputs arguments, the second arguments must be an array");
362 | }
363 |
364 | if (arr1 == NULL) {
365 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Invalid inputs arguments, the second arguments must be an array");
366 | }
367 |
368 | if(!mxIsCell(prhs[2])) {
369 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Invalid inputs arguments, the third arguments must be a list");
370 | }
371 |
372 | nd0 = mxGetNumberOfDimensions(prhs[0]);
373 | if (nd0 != 2) {
374 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Input arguments rank is invalid, got %d expect 2", nd0);
375 | }
376 |
377 | nd1 = mxGetNumberOfDimensions(prhs[1]);
378 | if ((nd1 != 1) && (nd1 != 2)) {
379 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Input arguments rank is invalid, got %u expect 1 or 2", nd1);
380 | }
381 |
382 | if (mxGetN(prhs[0]) != 3) {
383 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Input arguments shape is invalid, got (N,%lu) expect (N,3)", mxGetN(prhs[0]));
384 | }
385 |
386 | np = mxGetM(prhs[0]);
387 | if (np <= 0) {
388 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Input arguments shape is invalid, got (N,3) expect (%d,3)\n", np);
389 | }
390 |
391 | nt = mxGetN(prhs[1]);
392 | if (nt <= 0) {
393 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Input arguments shape is invalid, got (M,1) expect (1,%d)\n", nt);
394 | }
395 |
396 | if (mxGetClassID(prhs[0]) != mxDOUBLE_CLASS) {
397 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Invalid type for the first argument location, expected double\n");
398 | }
399 |
400 | _geocentric geocx{prhs[2], nt};
401 | _geopoint gpx{prhs[2], np};
402 | _topoc topocx{prhs[2], np, nt};
403 |
404 | geoc_list.resize(nt);
405 |
406 | if (mxGetClassID(prhs[1]) == mxDOUBLE_CLASS) {
407 | read_time(prhs[1], geoc_list, geocx, nt);
408 | } else if (mxGetClassID(prhs[1]) == mxINT64_CLASS) {
409 | read_time(prhs[1], geoc_list, geocx, nt);
410 | } else {
411 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Invalid type for time argument, expected int64 or double\n");
412 | }
413 |
414 | geopoint_list.resize(np);
415 |
416 | {
417 | mxArray_handler arr0{prhs[0]};
418 | for (int i = 0; i < np; ++i) {
419 | double lon = arr0.get(i, 0);
420 | double lat = arr0.get(i, 1);
421 | double alt = arr0.get(i, 2);
422 | #ifndef USE_SPA_ELLPS
423 | geopoint_list[i] = sg2::geopoint{lon, lat, alt, sg2::ELLPS_WGS84};
424 | #else
425 | geopoint_list[i] = sg2::geopoint{lon, lat, alt, sg2::ELLPS_SPA};
426 | #endif
427 | gpx.apply(0, i, geopoint_list[i]);
428 | }
429 | }
430 |
431 | if (topocx.ref.size() > 0) {
432 | for (int p = 0; p < np; ++p) {
433 | for (int t = 0; t < nt; ++t) {
434 | auto const & geoc = geoc_list[t];
435 | auto const & geopoint = geopoint_list[p];
436 | try {
437 | sg2::topocentric_data topoc(geoc, geopoint);
438 | topocx.apply(p, t, topoc);
439 | } catch(int & e) {
440 | topocx.clear(p, t);
441 | warning_count += 1;
442 | continue;
443 | }
444 | }
445 | }
446 | }
447 |
448 | if(warning_count > 0)
449 | mexPrintf("Warning: %d invalid value(s) encountered\n", warning_count);
450 |
451 | char const * xfields[] ={"geoc", "gp", "topoc"};
452 | mxArray * dict = mxCreateStructMatrix(1, 1, 3, xfields);
453 | {
454 | auto obj = geocx.as_object();
455 | mxSetFieldByNumber(dict, 0, 0, obj);
456 | }
457 |
458 | {
459 | auto obj = gpx.as_object();
460 | mxSetFieldByNumber(dict, 0, 1, obj);
461 | }
462 |
463 | {
464 | auto obj = topocx.as_object();
465 | mxSetFieldByNumber(dict, 0, 2, obj);
466 | }
467 |
468 | plhs[0] = dict;
469 |
470 | } catch (...) {
471 | mexErrMsgIdAndTxt("sg2:UnknownError", "Unknown exception occurred in sg2_sun_position");
472 | }
473 |
474 | }
475 |
--------------------------------------------------------------------------------
/matlab/sg2_sun_position.m:
--------------------------------------------------------------------------------
1 | % function out = sg2_sun_rise(geopoints, timestamp, fields)
2 | %
3 | % Computes the Sun-Earth position (geocentric) and the local sun position
4 | % (topocentric)
5 | %
6 | % Inputs:
7 | %
8 | % geopoints : 2-D array of double
9 | % This is a list of N geopoints as 2-D array of size (N,3) where each
10 | % row is repectively longitude in degrees, latitude in degrees and
11 | % altitude in meters.
12 | %
13 | % timstamp: 2-D array of double or 2-D array of int64
14 | % Array of timestamps at wich the computation will be done. If the 2-D
15 | % array is of type int64, the timestamps are read as integer numbers of
16 | % milliseconds since 1970-01-01T00:00:00Z. If the 2-D array if of type
17 | % double, the timestamps are read as julian date (decimal number of
18 | % day from Julian Day 0, which began at noon on January 1, 4713 B. C.).
19 | % Moreover the shape of the array can be (M,1) or (M,2) in the later
20 | % case the row are respectively a timestamp in UT and the terrestrial
21 | % time (TT). In the case of (M,1) it must contain only the timestamp
22 | % in UT, in that case the terrestrial time will be computed
23 | % with a predefined piecewise polynomial function.
24 | % The valid time range is between 1950-01-01 and 2100-12-31
25 | %
26 | % fields: 1D Cells of char strings
27 | % This is the list of requested outputs. See returns value for more details
28 | %
29 | % Output:
30 | %
31 | % out: a structure of fields computed as requested:
32 | % * "geoc.ut" **timestamp** as array of datetime64[ms]
33 | % * "geoc.tt" **terrestrial time** as array of datetime64[ms]
34 | % * "geoc.R" **Radius Sun-Earth** in astronomical unit (au) as array of double
35 | % * "geoc.L" **Heliocentric Earth true longitude** in radians as array of double
36 | % * "geoc.delta" **Geocentric declination** in radians as array of double
37 | % * "geoc.EOT" **Equation of Time: difference between apparent solar time
38 | % and mean solar time** in radians as array of double
39 | % (to be multiplied by pi/12 for hourly values)
40 | % * "geoc.Theta_a" **Geocentric Earth true longitude** in radians as array of double
41 | % * "geoc.epsilon" **Earth true obliquity** in radians as array of double
42 | % * "geoc.nu" **Apparent sideral time** in radians as array of double
43 | % * "geoc.r_alpha" **Geocentric right ascension** in radians as array of double
44 | % * "geoc.Delta_psi" **Nutation in Geocentric Sun longitude** in radians as array of double
45 | % * "gp.lambda" **Longitude** in radians as array of double
46 | % * "gp.phi" **Latitude** in radians as array of double
47 | % * "gp.u" **phi geocentric**
48 | % * "gp.x"
49 | % * "gp.y"
50 | % * "topoc.delta" **Topocentric sun declination** in radians as array of double
51 | % * "topoc.alpha_S" **Topocentric sun azimuth** in radians as array of double
52 | % * "topoc.gamma_S0" **Topocentric sun elevation angle without correction
53 | % of atm. refraction.** in radians as array of double
54 | % * "topoc.omega" **Topocentric local hour angle** in radians as array of double
55 | % * "topoc.r_alpha" **Topocentric right sun ascension** in radians as array of double
56 | % * "topoc.toa_hi" **Top of atmosphere horizontal irradiance** in W.m^{-2} as array of double
57 | % * "topoc.toa_ni" **Top of atmosphere normal irradiance** in W.m^{-2} as array of double
58 | %
59 | % The abreviations geoc and topoc stand for respectively geocentric and topocentric.
60 | %
61 | % Moreover the size of arrays of geoc.* fields will be (1,M), the size of
62 | % arrays of gp.* fields will be (N,1) and the size of arrays of topoc.* will
63 | % be (N,M).
64 | %
65 | %
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/matlab/sg2_sun_rise.cxx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 MINES ParisTech
3 | * Copyright 2022-2021 Benoit Gschwind
4 | *
5 | * This file is part of libsg2.
6 | *
7 | * This program is free software: you can redistribute it and/or modify
8 | * it under the terms of the Lesser GNU General Public License as
9 | * published by the Free Software Foundation, either version 3 of the
10 | * License, or (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 | * Lesser GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the Lesser GNU General Public
18 | * License along with this program. If not, see
19 | * .
20 | */
21 |
22 | // Matlab headers
23 | #include
24 | #include
25 |
26 | #include
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | using namespace std;
34 |
35 | void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] )
36 | {
37 |
38 | /* get args */
39 | if (nrhs != 2) {
40 | mexErrMsgIdAndTxt("sg2:InvalidArgumentCount", "Invalid inputs arguments, expect 3 arguments");
41 | }
42 |
43 | /* Get an array from any python object */
44 | auto arr0 = reinterpret_cast(mxGetData(prhs[0]));
45 | if (arr0 == NULL) {
46 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Invalid inputs arguments, the first arguments must be an array");
47 | }
48 |
49 | auto arr1 = reinterpret_cast(mxGetData(prhs[1]));
50 | if (arr1 == NULL) {
51 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Invalid inputs arguments, the second arguments must be an array");
52 | }
53 |
54 | if (mxGetClassID(prhs[0]) != mxDOUBLE_CLASS) {
55 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Invalid type for the first argument location, expected double\n");
56 | }
57 |
58 | if (mxGetNumberOfDimensions(prhs[0]) != 2) {
59 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Location size must have 2 dimensions got %d dimensions\n", mxGetNumberOfDimensions(prhs[0]));
60 | }
61 |
62 | if (mxGetN(prhs[0]) != 3) {
63 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Location size must be (M, 3) got (M, %d)\n", mxGetN(prhs[0]));
64 | }
65 |
66 | int const nt = mxGetM(prhs[1])*mxGetN(prhs[1]);
67 | vector time;
68 | time.resize(nt);
69 | if (mxGetClassID(prhs[1]) == mxDOUBLE_CLASS) {
70 | mxDouble * data = reinterpret_cast(mxGetData(prhs[1]));
71 | for (int i = 0; i < nt; ++i) {
72 | time[i] = sg2::date{data[i]};
73 | }
74 | } else if (mxGetClassID(prhs[1]) == mxINT64_CLASS) {
75 | mxInt64 * data = reinterpret_cast(mxGetData(prhs[1]));
76 | for (int i = 0; i < nt; ++i) {
77 | time[i] = sg2::date{data[i]};
78 | }
79 | } else {
80 | mexErrMsgIdAndTxt("sg2:InvalidArgument", "Invalid type for time argument, expected int64 or double\n");
81 | }
82 |
83 |
84 | vector geopoint_list;
85 | int const np = mxGetM(prhs[0]);
86 | geopoint_list.resize(np);
87 |
88 | for (int i = 0; i < np; ++i) {
89 | double lon = arr0[i + np*0];
90 | double lat = arr0[i + np*1];
91 | double alt = arr0[i + np*2];
92 | #ifndef USE_SPA_ELLPS
93 | geopoint_list[i] = sg2::geopoint{lon, lat, alt, sg2::ELLPS_WGS84};
94 | #else
95 | geopoint_list[i] = sg2::geopoint{lon, lat, alt, sg2::ELLPS_SPA};
96 | #endif
97 | }
98 |
99 | mwSize const dims[3] = {np, nt, 3};
100 | plhs[0] = mxCreateNumericArray(3, dims, mxINT64_CLASS, mxREAL);
101 | mxInt64 * out = reinterpret_cast(mxGetData(plhs[0]));
102 |
103 | for (int i = 0; i < np; ++i) {
104 | for (int j = 0; j < nt; ++j) {
105 | try {
106 | sg2::date sunrise, transit, sunset;
107 | std::tie(sunrise, transit, sunset) = sg2::sunrise(time[j], geopoint_list[i]);
108 | out[i + dims[0]*j + dims[0]*dims[1]*0] = sunrise.msec;
109 | out[i + dims[0]*j + dims[0]*dims[1]*1] = transit.msec;
110 | out[i + dims[0]*j + dims[0]*dims[1]*2] = sunset.msec;
111 | } catch (...) {
112 | out[i + dims[0]*j + dims[0]*dims[1]*0] = std::numeric_limits::max();
113 | out[i + dims[0]*j + dims[0]*dims[1]*1] = std::numeric_limits::max();
114 | out[i + dims[0]*j + dims[0]*dims[1]*2] = std::numeric_limits::max();
115 | }
116 | }
117 | }
118 |
119 | }
120 |
121 |
122 |
--------------------------------------------------------------------------------
/matlab/sg2_topocentric_correction_refraction_SAE.cxx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 MINES ParisTech
3 | * Copyright 2022-2021 Benoit Gschwind
4 | *
5 | * This file is part of libsg2.
6 | *
7 | * This program is free software: you can redistribute it and/or modify
8 | * it under the terms of the Lesser GNU General Public License as
9 | * published by the Free Software Foundation, either version 3 of the
10 | * License, or (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 | * Lesser GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the Lesser GNU General Public
18 | * License along with this program. If not, see
19 | * .
20 | */
21 |
22 | // Matlab headers
23 | #include
24 | #include
25 |
26 | #include
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] )
34 | {
35 |
36 | if (nrhs != 3)
37 | mexErrMsgIdAndTxt("sg2:InvalidArgumentsCount", "sg2_ymdh_to_jd need 4 arguments got %d", nrhs);
38 |
39 | for (int i = 0; i < 3; ++i) {
40 | if (mxGetClassID(prhs[i]) != mxDOUBLE_CLASS)
41 | mexErrMsgIdAndTxt("sg2:TypeError", "Arguments %d must be array of doubles", i);
42 | }
43 |
44 | for (int i = 0; i < 3; ++i) {
45 | if (mxGetM(prhs[0]) != mxGetM(prhs[i]))
46 | mexErrMsgIdAndTxt("sg2:SizeError", "Arguments must have the same size");
47 | if (mxGetN(prhs[0]) != mxGetN(prhs[i]))
48 | mexErrMsgIdAndTxt("sg2:SizeError", "Arguments must have the same size");
49 | }
50 |
51 | mxDouble * gamma = reinterpret_cast(mxGetData(prhs[0]));
52 | mxDouble * P = reinterpret_cast(mxGetData(prhs[1]));
53 | mxDouble * T = reinterpret_cast(mxGetData(prhs[2]));
54 |
55 | plhs[0] = mxCreateDoubleMatrix(mxGetM(prhs[0]), mxGetN(prhs[0]), mxREAL);
56 | mxDouble * gamma_corrected = reinterpret_cast(mxGetData(plhs[0]));
57 |
58 | int const n = mxGetM(prhs[0]) * mxGetN(prhs[0]);
59 | for (int i = 0; i < n; ++i) {
60 | gamma_corrected[i] = sg2::topocentric_correction_refraction_SAE(gamma[i], P[i], T[i]);
61 | }
62 |
63 | }
64 |
65 |
66 |
--------------------------------------------------------------------------------
/matlab/sg2_topocentric_correction_refraction_SAE.m:
--------------------------------------------------------------------------------
1 | % function gamma_S = sg2_topocentric_correction_refraction_SAE(gamma_S0, P, T)
2 | %
3 | % Application of the atmospheric refraction correction of to a 1-D array of
4 | % solar elevation following the method SAE
5 | %
6 | % Inputs:
7 | %
8 | % gamma_S0: 1-D array of size (N,1) of solar elevation with no refraction
9 | % correction, in radians
10 | %
11 | % P : 1-D array of size (N,1) of atm. pressure in Pa (mbar)
12 | %
13 | % T : 1-D array of size (N,1) of air temperature in Celsius
14 | %
15 | % Output:
16 | %
17 | % gamma_S : 1-D array of size (N,1) of corrected solar elevation in radians
18 |
--------------------------------------------------------------------------------
/matlab/sg2_topocentric_correction_refraction_ZIM.cxx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 MINES ParisTech
3 | * Copyright 2022-2021 Benoit Gschwind
4 | *
5 | * This file is part of libsg2.
6 | *
7 | * This program is free software: you can redistribute it and/or modify
8 | * it under the terms of the Lesser GNU General Public License as
9 | * published by the Free Software Foundation, either version 3 of the
10 | * License, or (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 | * Lesser GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the Lesser GNU General Public
18 | * License along with this program. If not, see
19 | * .
20 | */
21 |
22 | // Matlab headers
23 | #include
24 | #include
25 |
26 | #include
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] )
34 | {
35 |
36 | if (nrhs != 3)
37 | mexErrMsgIdAndTxt("sg2:InvalidArgumentsCount", "sg2_ymdh_to_jd need 4 arguments got %d", nrhs);
38 |
39 | for (int i = 0; i < 3; ++i) {
40 | if (mxGetClassID(prhs[i]) != mxDOUBLE_CLASS)
41 | mexErrMsgIdAndTxt("sg2:TypeError", "Arguments %d must be array of doubles", i);
42 | }
43 |
44 | for (int i = 0; i < 3; ++i) {
45 | if (mxGetM(prhs[0]) != mxGetM(prhs[i]))
46 | mexErrMsgIdAndTxt("sg2:SizeError", "Arguments must have the same size");
47 | if (mxGetN(prhs[0]) != mxGetN(prhs[i]))
48 | mexErrMsgIdAndTxt("sg2:SizeError", "Arguments must have the same size");
49 | }
50 |
51 | mxDouble * gamma = reinterpret_cast(mxGetData(prhs[0]));
52 | mxDouble * P = reinterpret_cast(mxGetData(prhs[1]));
53 | mxDouble * T = reinterpret_cast(mxGetData(prhs[2]));
54 |
55 | plhs[0] = mxCreateDoubleMatrix(mxGetM(prhs[0]), mxGetN(prhs[0]), mxREAL);
56 | mxDouble * gamma_corrected = reinterpret_cast(mxGetData(plhs[0]));
57 |
58 | int const n = mxGetM(prhs[0]) * mxGetN(prhs[0]);
59 | for (int i = 0; i < n; ++i) {
60 | gamma_corrected[i] = sg2::topocentric_correction_refraction_ZIM(gamma[i], P[i], T[i]);
61 | }
62 |
63 | }
64 |
65 |
66 |
--------------------------------------------------------------------------------
/matlab/sg2_topocentric_correction_refraction_ZIM.m:
--------------------------------------------------------------------------------
1 | % function gamma_S = sg2_topocentric_correction_refraction_ZIM(gamma_S0, P, T)
2 | %
3 | % Application of the atmospheric refraction correction of to a 1-D array of
4 | % solar elevation following the method ZIM
5 | %
6 | % Inputs:
7 | %
8 | % gamma_S0: 1-D array of size (N,1) of solar elevation with no refraction
9 | % correction, in radians
10 | %
11 | % P : 1-D array of size (N,1) of atm. pressure in Pa (mbar)
12 | %
13 | % T : 1-D array of size (N,1) of air temperature in Celsius
14 | %
15 | % Output:
16 | %
17 | % gamma_S : 1-D array of size (N,1) of corrected solar elevation in radians
18 |
--------------------------------------------------------------------------------
/matlab/sg2_ymdh_to_jd.m:
--------------------------------------------------------------------------------
1 | function jd = sg2_ymdh_to_jd(ymdh)
2 | % function jd = sg2_ymdh_to_jd(ymdh)
3 | %
4 | % Transformation of (N+1)-D array with elements (year month day hour)
5 | % into a N-D array of Julian Date (jd)
6 | %
7 | %
8 | % Input:
9 | %
10 | % ymdh: (N+1)-D array of (n1, ..., nN, 4) with (y,m,d,h) elements where:
11 | % - y is an array of year number
12 | % - m is an array of month number in [1,12]
13 | % - d is an array of day of month in [1,31]
14 | % - h is an array of decimal hour within the day, ex. 12:30 is 12.5
15 | %
16 | % Output:
17 | %
18 | % jd: N-D array of double of size (n1, ..., nN)
19 |
20 | shape = size(ymdh);
21 | if (shape(end)~=4)
22 | error('The last size of ymdh should be 4');
23 | end
24 |
25 | n = prod(shape(1:end-1));
26 | ymdh = reshape(ymdh,[n 4]);
27 |
28 | jd = zeros(n,1);
29 |
30 | idx = find(ymdh(:,2)<3);
31 | ymdh(idx,2) = ymdh(idx,2)+12;
32 | ymdh(idx,1) = ymdh(idx,1)-1;
33 |
34 | jd = 1721028 + ymdh(:,3) + floor((153 * ymdh(:,2) - 2) / 5) + 365 * ymdh(:,1) + floor(ymdh(:,1) / 4) ...
35 | - floor(ymdh(:,1) / 100) + floor(ymdh(:,1) / 400) + ymdh(:,4) / 24 - 0.5;
36 |
37 | jd = reshape(jd, [shape(1:end-1) 1]);
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools", 'numpy == 1.22.0 ; python_version == "3.8"', 'numpy >= 2.0.0 ; python_version >= "3.9"']
3 | build-backend = "setuptools.build_meta"
4 |
5 | [project]
6 | name = "sg2"
7 | version = "2.3.3"
8 | description = "Solar Geometry 2 (SG2) is the second generation of library for computing the relative position of the sun and the earth. Valid over the time period 1980-2100, the algorithm is 20 times faster than the well-know SPA algorithm, with an accuracy order of approx. 0.005°. Reference article: Blanc P. and L. Wald, The SG2 algorithm for a fast and accurate computation of the position of the sun for multi-decadal time period. Solar Energy 88, 3072-3083, 2012, doi: 10.1016/j.solener.2012.07.018."
9 | requires-python = ">= 3.8"
10 | readme = {file = "README.md", content-type = "text/markdown"}
11 | keywords = ["solar", "sun", "geometry", "astronomy"]
12 | authors = [
13 | {name = "MINES ParisTech"},
14 | {name = "Benoit Gschwind", email = "benoit.gschwind@mines-paristech.fr"},
15 | {name = "Philippe Blanc", email = "philippe.blanc@mines-paristech.fr"}
16 | ]
17 | maintainers = [
18 | {name = "Benoit Gschwind", email = "benoit.gschwind@mines-paristech.fr"}
19 | ]
20 | classifiers = [
21 | "Development Status :: 5 - Production/Stable",
22 | "Intended Audience :: Developers",
23 | "Intended Audience :: Science/Research",
24 | "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
25 | "Operating System :: Microsoft :: Windows",
26 | "Operating System :: POSIX :: Linux",
27 | "Programming Language :: Python :: 3.8",
28 | "Programming Language :: Python :: 3.9",
29 | "Programming Language :: Python :: 3.10",
30 | "Programming Language :: Python :: 3.11",
31 | "Programming Language :: Python :: 3.12",
32 | "Programming Language :: C++",
33 | "Topic :: Scientific/Engineering :: GIS",
34 | "Topic :: Scientific/Engineering :: Information Analysis"
35 | ]
36 | dependencies = [
37 | "numpy>=1.22.0",
38 | ]
39 |
40 | [project.urls]
41 | Homepage = "https://www.oie.minesparis.psl.eu/Valorisation/Outils/Solar-Geometry/"
42 | Repository = "https://github.com/gschwind/sg2"
43 | Issues = "https://github.com/gschwind/sg2/issues"
44 |
45 |
--------------------------------------------------------------------------------
/python/pysg2.cxx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2021 MINES ParisTech
3 | * Copyright 2014-2021 Benoit Gschwind
4 | *
5 | * This file is part of libsg2.
6 | *
7 | * This program is free software: you can redistribute it and/or modify
8 | * it under the terms of the Lesser GNU General Public License as
9 | * published by the Free Software Foundation, either version 3 of the
10 | * License, or (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 | * Lesser GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the Lesser GNU General Public
18 | * License along with this program. If not, see
19 | * .
20 | */
21 |
22 | #include "python-bind-helper.hxx"
23 |
24 | #include
25 |
26 | #include "sg2.hxx"
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include