├── .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 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | using namespace sg2; 41 | using namespace std; 42 | 43 | 44 | struct module_state { 45 | PyObject * Error; 46 | PyObject * types; 47 | PyObject * ns; 48 | }; 49 | 50 | static std::array el = { 51 | &sg2::ELLPS_WGS84, 52 | &sg2::ELLPS_RGF83, 53 | &sg2::ELLPS_NTF, 54 | &sg2::ELLPS_AA, 55 | &sg2::ELLPS_SPA, 56 | &sg2::ELLPS_NGP, 57 | &sg2::ELLPS_SPHERE 58 | }; 59 | 60 | template 61 | void set_python_exception(Args ... args) { 62 | char tmp[1024]; 63 | snprintf(tmp, 1024, args...); 64 | PyErr_SetString(PyExc_RuntimeError, tmp); 65 | } 66 | 67 | 68 | template<> 69 | void set_python_exception(char const * str) { 70 | PyErr_SetString(PyExc_RuntimeError, str); 71 | } 72 | 73 | 74 | template 75 | inline static T & PyArray_Get(PyArrayObject * arr, int i) 76 | { 77 | return *reinterpret_cast(PyArray_GETPTR1(arr, i)); 78 | } 79 | 80 | template 81 | inline static T & PyArray_Get(PyArrayObject * arr, int i, int j) 82 | { 83 | return *reinterpret_cast(PyArray_GETPTR2(arr, i, j)); 84 | } 85 | 86 | template 87 | inline static T & PyArray_Get(PyArrayObject * arr, int i, int j, int k) 88 | { 89 | return *reinterpret_cast(PyArray_GETPTR3(arr, i, j, k)); 90 | } 91 | 92 | static double _pysg2_ymdh_to_jd(int64_t year, int64_t month, int64_t day_of_month, double hours) 93 | { 94 | sg2::ymdh date(year, month, day_of_month, hours); 95 | return sg2::julian(date).value; 96 | } 97 | 98 | // return year, month, day and microseconds 99 | static tuple _pysg2_jd_to_ymdh(double jd) 100 | { 101 | sg2::ymdh date(jd); 102 | return tuple{date.year, date.month, date.day_of_month, date.hour}; 103 | } 104 | 105 | // Before numpy 2.0.0 this function does no exist thus define this macro. 106 | #if NPY_API_VERSION < 0x00000012 107 | /* define this macro for old numpy versions */ 108 | #define PyDataType_C_METADATA(obj) ((obj)->c_metadata) 109 | #endif 110 | 111 | static PyArray_Descr * create_datetime64_ms_dtype() 112 | { 113 | // Extrapolated from numpy sources 114 | PyArray_Descr * dtype = PyArray_DescrNewFromType(NPY_DATETIME); 115 | // TODO: Check for NULL ptr. 116 | reinterpret_cast(PyDataType_C_METADATA(dtype))->meta.base = NPY_FR_ms; 117 | return dtype; 118 | } 119 | 120 | namespace { // anonymous namespace 121 | 122 | 123 | template 124 | struct _map_api { 125 | virtual auto create(int n) -> PyArrayObject * = 0; 126 | virtual auto create(int n, int j) -> PyArrayObject * = 0; 127 | virtual void apply(PyArrayObject * dst, T const & d, int n) = 0; 128 | virtual void apply(PyArrayObject * dst, T const & d, int n, int j) = 0; 129 | virtual void clear(PyArrayObject * dst, int n) = 0; 130 | virtual void clear(PyArrayObject * dst, int n, int j) = 0; 131 | }; 132 | 133 | 134 | template 135 | struct _map_data { 136 | string const & key; 137 | PyArrayObject * dst; 138 | _map_api * api; 139 | }; 140 | 141 | 142 | template 143 | struct _map_api_objmember : public _map_api { 144 | 145 | virtual void apply(PyArrayObject * dst, T const & d, int n) override 146 | { 147 | PyArray_Get(dst, n) = d.*m; 148 | } 149 | 150 | virtual void apply(PyArrayObject * dst, T const & d, int n, int j) override 151 | { 152 | PyArray_Get(dst, n, j) = d.*m; 153 | } 154 | 155 | }; 156 | 157 | template 158 | struct _map_api_objmember_double : public _map_api_objmember { 159 | 160 | static _map_api * _new() 161 | { 162 | static _map_api_objmember_double singleton; 163 | return &singleton; 164 | } 165 | 166 | virtual auto create(int n) -> PyArrayObject * override 167 | { 168 | npy_intp dims[] = {n}; 169 | return reinterpret_cast(PyArray_SimpleNew(1, dims, NPY_DOUBLE)); 170 | } 171 | 172 | virtual auto create(int n, int j) -> PyArrayObject * override 173 | { 174 | npy_intp dims[] = {n, j}; 175 | return reinterpret_cast(PyArray_SimpleNew(2, dims, NPY_DOUBLE)); 176 | } 177 | 178 | virtual void clear(PyArrayObject * dst, int n) override 179 | { 180 | PyArray_Get(dst, n) = std::nan(""); 181 | } 182 | 183 | virtual void clear(PyArrayObject * dst, int n, int j) override 184 | { 185 | PyArray_Get(dst, n, j) = std::nan(""); 186 | } 187 | 188 | }; 189 | 190 | 191 | template 192 | struct _map_api_objmember_int : public _map_api_objmember { 193 | 194 | static _map_api * _new() 195 | { 196 | static _map_api_objmember_int singleton; 197 | return &singleton; 198 | } 199 | 200 | virtual auto create(int n) -> PyArrayObject * override 201 | { 202 | npy_intp dims[] = {n}; 203 | return reinterpret_cast(PyArray_SimpleNew(1, dims, NPY_INT)); 204 | } 205 | 206 | virtual auto create(int n, int j) -> PyArrayObject * override 207 | { 208 | npy_intp dims[] = {n, j}; 209 | return reinterpret_cast(PyArray_SimpleNew(2, dims, NPY_INT)); 210 | } 211 | 212 | virtual void clear(PyArrayObject * dst, int n) override 213 | { 214 | PyArray_Get(dst, n) = std::numeric_limits::min(); 215 | } 216 | 217 | virtual void clear(PyArrayObject * dst, int n, int j) override 218 | { 219 | PyArray_Get(dst, n, j) = std::numeric_limits::min(); 220 | } 221 | 222 | }; 223 | 224 | 225 | template 226 | struct _map_api_objmember_date : public _map_api_objmember { 227 | 228 | static _map_api * _new() 229 | { 230 | static _map_api_objmember_date singleton; 231 | return &singleton; 232 | } 233 | 234 | virtual auto create(int n) -> PyArrayObject * override 235 | { 236 | npy_intp dims[] = {n}; 237 | return reinterpret_cast(PyArray_SimpleNewFromDescr(1, dims, create_datetime64_ms_dtype())); 238 | } 239 | 240 | virtual auto create(int n, int j) -> PyArrayObject * override 241 | { 242 | npy_intp dims[] = {n, j}; 243 | return reinterpret_cast(PyArray_SimpleNewFromDescr(2, dims, create_datetime64_ms_dtype())); 244 | } 245 | 246 | virtual void apply(PyArrayObject * dst, T const & d, int n) override 247 | { 248 | PyArray_Get(dst, n) = (d.*m).msec; 249 | } 250 | 251 | virtual void apply(PyArrayObject * dst, T const & d, int n, int j) override 252 | { 253 | PyArray_Get(dst, n, j) = (d.*m).msec; 254 | } 255 | 256 | virtual void clear(PyArrayObject * dst, int n) override 257 | { 258 | PyArray_Get(dst, n) = NPY_DATETIME_NAT; 259 | } 260 | 261 | virtual void clear(PyArrayObject * dst, int n, int j) override 262 | { 263 | PyArray_Get(dst, n, j) = NPY_DATETIME_NAT; 264 | } 265 | 266 | }; 267 | 268 | 269 | /* 270 | * return a pointer map the map name of arg1 (a Python list) to sg2 objects 271 | * 272 | * returned pointer must be freed. 273 | */ 274 | template 275 | struct _generic_handler { 276 | using _type = T; 277 | vector<_map_data<_type>> ref; 278 | 279 | void _construct(PyObject * fields, unordered_map * > const & m, regex & r) 280 | { 281 | unordered_set inserted; 282 | 283 | PyObject * iter = PyObject_GetIter(fields); 284 | if (iter == nullptr) 285 | return; 286 | for(auto obj = PyIter_Next(iter); obj != nullptr; obj = PyIter_Next(iter)) { 287 | if(!PyUnicode_Check(obj)) { 288 | Py_DECREF(obj); 289 | continue; 290 | } 291 | auto str = PyUnicode_AsUTF8(obj); 292 | if (str == nullptr) { 293 | Py_DECREF(obj); 294 | continue; 295 | } 296 | cmatch cm; 297 | if (!regex_match(str, cm, r)) { 298 | Py_DECREF(obj); 299 | continue; 300 | } 301 | 302 | // If the field is all, insert all known fields. 303 | if (cm[1] == "all") { 304 | for (auto & x: m) { 305 | if (inserted.count(x.first) == 0) { 306 | inserted.insert(x.first); 307 | ref.emplace_back(_map_data<_type>{x.first, nullptr, x.second}); 308 | } 309 | } 310 | Py_DECREF(obj); 311 | continue; 312 | } 313 | 314 | auto x = m.find(cm[1]); 315 | if (x != m.end() && inserted.count(x->first) == 0) { 316 | inserted.insert(x->first); 317 | ref.emplace_back(_map_data<_type>{x->first,nullptr,x->second}); 318 | } 319 | Py_DECREF(obj); 320 | } 321 | Py_DECREF(iter); 322 | } 323 | 324 | void apply(_type const & d, int n) 325 | { 326 | for(auto & r: ref) { 327 | r.api->apply(r.dst, d, n); 328 | } 329 | } 330 | 331 | void apply(_type const & d, int n, int j) 332 | { 333 | for(auto & r: ref) { 334 | r.api->apply(r.dst, d, n, j); 335 | } 336 | } 337 | 338 | PyObject * as_object (PyObject * m) 339 | { 340 | auto ms = reinterpret_cast(PyModule_GetState(m)); 341 | PyObject * o = PyObject_CallObject(ms->ns, NULL); 342 | for (auto & r: ref) { 343 | PyObject_SetAttrString(o, r.key.c_str(), reinterpret_cast(r.dst)); 344 | } 345 | return o; 346 | } 347 | 348 | void clear(int n, int j) { 349 | for(auto & r: ref) { 350 | r.api->clear(r.dst, n, j); 351 | } 352 | } 353 | 354 | ~_generic_handler() 355 | { 356 | for (auto & r: ref) { 357 | Py_XDECREF(reinterpret_cast(r.dst)); 358 | } 359 | } 360 | 361 | }; 362 | 363 | 364 | /* 365 | * return a pointer map the map name of arg1 (a Python list) to sg2 objects 366 | * 367 | * returned pointer must be freed. 368 | */ 369 | struct _geocentric : public _generic_handler { 370 | 371 | _geocentric(PyObject * fields, int nt) 372 | { 373 | static unordered_map * > const m = { 374 | {"ut", _map_api_objmember_date <_type, &_type::ut >::_new()}, 375 | {"tt", _map_api_objmember_date <_type, &_type::tt >::_new()}, 376 | {"R", _map_api_objmember_double<_type, &_type::R >::_new()}, 377 | {"L", _map_api_objmember_double<_type, &_type::L >::_new()}, 378 | {"delta", _map_api_objmember_double<_type, &_type::delta >::_new()}, 379 | {"EOT", _map_api_objmember_double<_type, &_type::EOT >::_new()}, 380 | {"Theta_a", _map_api_objmember_double<_type, &_type::Theta_a >::_new()}, 381 | {"epsilon", _map_api_objmember_double<_type, &_type::epsilon >::_new()}, 382 | {"nu", _map_api_objmember_double<_type, &_type::nu >::_new()}, 383 | {"r_alpha", _map_api_objmember_double<_type, &_type::r_alpha >::_new()}, 384 | {"Delta_psi",_map_api_objmember_double<_type, &_type::Delta_psi >::_new()} 385 | }; 386 | 387 | static regex r{"^geoc\\.(.+)$"}; 388 | _construct(fields, m, r); 389 | 390 | for (auto & r: ref) { 391 | r.dst = r.api->create(nt); 392 | } 393 | 394 | } 395 | }; 396 | 397 | 398 | struct _geopoint : public _generic_handler { 399 | 400 | _geopoint(PyObject * fields, int np) 401 | { 402 | static unordered_map *> m = { 403 | {"lambda", _map_api_objmember_double<_type, &_type::lambda >::_new()}, 404 | {"phi", _map_api_objmember_double<_type, &_type::phi >::_new()}, 405 | {"u", _map_api_objmember_double<_type, &_type::u >::_new()}, 406 | {"x", _map_api_objmember_double<_type, &_type::x >::_new()}, 407 | {"y", _map_api_objmember_double<_type, &_type::y >::_new()} 408 | }; 409 | 410 | static regex r{"^gp\\.(.+)$"}; 411 | _construct(fields, m, r); 412 | 413 | for (auto & r: ref) { 414 | r.dst = r.api->create(np); 415 | } 416 | 417 | } 418 | 419 | }; 420 | 421 | 422 | struct _topoc : public _generic_handler { 423 | 424 | _topoc(PyObject * fields, int np, int nt) 425 | { 426 | static unordered_map *> m = { 427 | {"delta", _map_api_objmember_double<_type, &_type::delta >::_new()}, 428 | {"alpha_S", _map_api_objmember_double<_type, &_type::alpha_S >::_new()}, 429 | {"gamma_S0", _map_api_objmember_double<_type, &_type::gamma_S0 >::_new()}, 430 | {"omega", _map_api_objmember_double<_type, &_type::omega >::_new()}, 431 | {"r_alpha", _map_api_objmember_double<_type, &_type::r_alpha >::_new()}, 432 | {"toa_hi", _map_api_objmember_double<_type, &_type::toa_hi >::_new()}, 433 | {"toa_ni", _map_api_objmember_double<_type, &_type::toa_ni >::_new()} 434 | }; 435 | 436 | 437 | static regex r{"^topoc\\.(.+)$"}; 438 | _construct(fields, m, r); 439 | 440 | for (auto & r: ref) { 441 | r.dst = r.api->create(np, nt); 442 | } 443 | 444 | } 445 | 446 | }; 447 | 448 | } // anonymous namespace 449 | 450 | static char const _doc_sun_position[] = 451 | "Compute the Sun-Earth position (geocentric) and the local sun position (topocentric).\n" 452 | "\n" 453 | "Parameters\n" 454 | "----------\n" 455 | "\n" 456 | "geopoints : 2D array of double\n" 457 | " This is a list of geopoints as 2D array of (N,3) where each row is\n" 458 | " repectively longitude in degrees, latitude in degrees and altitude in\n" 459 | " meters.\n" 460 | "\n" 461 | "timstamp : 1D or 2D array of double, 1D or 2D array of numpy.datetime64\n" 462 | " Array of timestamp at wich the computation will be done. If the array\n" 463 | " is of type double, the timestamps are read as julian date (decimal number\n" 464 | " of continuous day from Julian Day 0, which began at noon on January 1, 4713\n" 465 | " B. C.). Moreover the shape of the array can be (M,) or (M,2) in the later\n" 466 | " case the row are respectively a timestamp in ut and the coresponding\n" 467 | " terrestrial time in ut. In the case of (M,) it must contain only the\n" 468 | " timestamp in ut, in that case the terrestrial time will be computed with\n" 469 | " a predefined piecewise polynomial function.\n" 470 | "\n" 471 | "fields : List of str.\n" 472 | " This is the list of needed outputs. See returns value for more details\n" 473 | "\n" 474 | "elipsotid : Constant, optional\n" 475 | " The reference elipsoid can be specified in the optional parameter ellipse and can be:\n" 476 | "\n" 477 | " * WGS84 (default)\n" 478 | " * RGF83\n" 479 | " * NTF\n" 480 | " * AA\n" 481 | " * SPA\n" 482 | " * NGP\n" 483 | " * SPHERE\n" 484 | "\n" 485 | " If not specified reference geoid WGS84 will be used.\n" 486 | "\n" 487 | "\n" 488 | "Returns\n" 489 | "-------\n" 490 | "\n" 491 | "output : Object\n" 492 | " The output is a python list of fields identified by python strings. Each\n" 493 | " given fields will be computed and provided in the output o as follow :\n" 494 | " *\"field.name\"* will be *o.field.name*. Possible fields and there output\n" 495 | " type are:\n" 496 | "\n" 497 | " * \"geoc.ut\" **timestamp** as array of datetime64[ms]\n" 498 | " * \"geoc.tt\" **terrestrial time** as array of datetime64[ms]\n" 499 | " * \"geoc.R\" **Radius Sun-Earth** in astronomical unit (au) as array of double\n" 500 | " * \"geoc.L\" **Heliocentric Earth true longitude** in radians as array of double\n" 501 | " * \"geoc.delta\" **Geocentric declination** in radians as array of double\n" 502 | " * \"geoc.EOT\" **Equation of Time: difference between apparent solar time and\n" 503 | " mean solar time** in radians as array of double to get the time difference in hour\n" 504 | " you need : h = (EOT/2/PI-floor(EOT/2/PI+0.5))*12.0\n" 505 | " * \"geoc.Theta_a\" **Geocentric Earth true longitude** in radians as array of double\n" 506 | " * \"geoc.epsilon\" **Earth true obliquity** in radians as array of double\n" 507 | " * \"geoc.nu\" **Apparent sideral time** in radians as array of double\n" 508 | " * \"geoc.r_alpha\" **Geocentric right ascension** in radians as array of double\n" 509 | " * \"geoc.Delta_psi\" **Nutation in Geocentric Sun longitude** in radians as\n" 510 | " array of double\n" 511 | " * \"gp.lambda\" **Longitude** in radians as array of double\n" 512 | " * \"gp.phi\" **Latitude** in radians as array of double\n" 513 | " * \"gp.u\" **phi geocentric**\n" 514 | " * \"gp.x\"\n" 515 | " * \"gp.y\"\n" 516 | " * \"topoc.delta\" **Topocentric sun declination** in radians as array of double\n" 517 | " * \"topoc.alpha_S\" **Topocentric sun azimuth** in radians as array of double\n" 518 | " * \"topoc.gamma_S0\" **Topocentric sun elevation angle without correction of\n" 519 | " atm. refraction.** in radians as array of double\n" 520 | " * \"topoc.omega\" **Topocentric local hour angle** in radians as array of double\n" 521 | " * \"topoc.r_alpha\" **Topocentric right sun ascension** in radians as array of double\n" 522 | " * \"topoc.toa_hi\" **Top of atmosphere horizontal irradiance** in W.m^{-2} as array of double\n" 523 | " * \"topoc.toa_ni\" **Top of atmosphere normal irradiance** in W.m^{-2} as array of double\n" 524 | "\n" 525 | " The abreviations geoc and topoc stand for respectively geocentric and topocentric.\n" 526 | "\n" 527 | " Moreover the size of arrays of geoc.* fields will be (M,), the size of\n" 528 | " arrays of gp.* fields will be (N,) and the size of arrays of topoc.* will\n" 529 | " be (N,M).\n" 530 | "\n" 531 | "Examples\n" 532 | "--------\n" 533 | ">>> sun_position([[45.0, 30.0, 100.0]], [2458130.0, 2458131.0], [\"topoc.gamma_S0\", \"topoc.alpha_S\"])\n" 534 | ; 535 | 536 | /** 537 | * @input arg0: array(N,3) with lon, lat, alt in each row 538 | * @input arg1: array(M) with jd 539 | * @input arg2: list of outputs elements. 540 | * @return array(M,N,len(arg2)) 541 | */ 542 | static PyObject * py_sun_position(PyObject * self, PyObject * args) 543 | { 544 | PyArrayObject * arr0 = NULL; 545 | PyArrayObject * arr1 = NULL; 546 | 547 | try { 548 | 549 | PyObject * i_array_geopoint = NULL; 550 | PyObject * i_array_timestamp = NULL; 551 | PyObject * i_array_options = NULL; 552 | int i_elipsoid = 0; 553 | 554 | int warning_count = 0; 555 | vector geoc_list; 556 | vector geopoint_list; 557 | int nd0, nd1, np, nt; 558 | 559 | /* get args */ 560 | if (!PyArg_ParseTuple(args, "OOO|i", &i_array_geopoint, &i_array_timestamp, &i_array_options, &i_elipsoid)) { 561 | set_python_exception("Invalid inputs arguments, expect 3 arguments\n"); 562 | goto error; 563 | } 564 | 565 | /* Get an array from any python object */ 566 | arr0 = reinterpret_cast(PyArray_FROM_OTF(i_array_geopoint, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY)); 567 | if (arr0 == NULL) { 568 | set_python_exception("Invalid inputs arguments, geopoints must be an array\n"); 569 | goto error; 570 | } 571 | 572 | arr1 = reinterpret_cast(PyArray_FROM_OF(i_array_timestamp, NPY_ARRAY_IN_ARRAY)); 573 | if (arr1 == NULL) { 574 | set_python_exception("Invalid inputs arguments, timestamp must be an array\n"); 575 | goto error; 576 | } 577 | 578 | if(!PyList_Check(i_array_options)) { 579 | set_python_exception("Invalid inputs arguments, fields must be a list\n"); 580 | goto error; 581 | } 582 | 583 | nd0 = PyArray_NDIM(arr0); 584 | if (nd0 != 2) { 585 | set_python_exception("Invalid number of dimension for geopoints, got %d expect 2\n", nd0); 586 | goto error; 587 | } 588 | 589 | nd1 = PyArray_NDIM(arr1); 590 | if ((nd1 != 1) && (nd1 != 2)) { 591 | set_python_exception("Invalid number of dimension for timestamp, got %u expect 1 or 2 \n", nd1); 592 | goto error; 593 | } 594 | 595 | if ((nd1 == 2) && (PyArray_DIM(arr1, 1) != 2)) { 596 | set_python_exception("Invalid shape for timestamp, got (M,%lu) expect (M,2)\n", PyArray_DIM(arr1, 1)); 597 | goto error; 598 | } 599 | 600 | if (PyArray_DIM(arr0, 1) != 3) { 601 | set_python_exception("Invalid shape for geopoints, got (N,%lu) expect (N,3)\n", PyArray_DIM(arr0, 1)); 602 | goto error; 603 | } 604 | 605 | np = PyArray_DIM(arr0, 0); 606 | if (np <= 0) { 607 | set_python_exception("Invalid shape for geopoints, got (%lu,3) expect (N,3) with N > 0\n", PyArray_DIM(arr0, 0)); 608 | goto error; 609 | } 610 | 611 | nt = PyArray_DIM(arr1, 0); 612 | if (nt <= 0) { 613 | set_python_exception("Invalid shape for timestamp, got (%lu,) expect (M,) with M > 0\n", PyArray_DIM(arr0, 0)); 614 | goto error; 615 | } 616 | 617 | if (PyArray_ISINTEGER(reinterpret_cast(arr1))) { 618 | // Expected in millisecond. 619 | fprintf(stderr, "Warning: using integer array as input will be interpreted as datetime64[ms]\n"); 620 | auto tmp = reinterpret_cast(PyArray_FromArray(arr1, PyArray_DescrFromType(NPY_INT64), NPY_ARRAY_IN_ARRAY)); 621 | std::swap(arr1, tmp); 622 | Py_XDECREF(tmp); 623 | } else if (PyArray_ISFLOAT(reinterpret_cast(arr1))) { 624 | auto tmp = reinterpret_cast(PyArray_FromArray(arr1, PyArray_DescrFromType(NPY_DOUBLE), NPY_ARRAY_IN_ARRAY)); 625 | std::swap(arr1, tmp); 626 | Py_XDECREF(tmp); 627 | } else if (PyArray_TYPE(arr1) == NPY_DATETIME) { 628 | // Ensure datetime64[ns] 629 | auto tmp = reinterpret_cast(PyObject_CallMethod(reinterpret_cast(arr1), "astype", "(s)", "datetime64[ms]")); 630 | std::swap(arr1, tmp); 631 | Py_XDECREF(tmp); 632 | // Ensure integer 633 | tmp = reinterpret_cast(PyObject_CallMethod(reinterpret_cast(arr1), "astype", "(s)", "i8")); 634 | std::swap(arr1, tmp); 635 | Py_XDECREF(tmp); 636 | tmp = reinterpret_cast(PyArray_FromArray(arr1, PyArray_DescrFromType(NPY_INT64), NPY_ARRAY_IN_ARRAY)); 637 | std::swap(arr1, tmp); 638 | Py_XDECREF(tmp); 639 | } else { 640 | set_python_exception("Invalid type for timestamp, timestamp must be an array of integer or floating point\n"); 641 | goto error; 642 | } 643 | 644 | if (arr1 == NULL) { 645 | set_python_exception("Convertion of timestamp failled\n"); 646 | goto error; 647 | } 648 | 649 | { // do the computation 650 | 651 | _geocentric geocx{i_array_options, nt}; 652 | _geopoint gpx{i_array_options, np}; 653 | _topoc topocx{i_array_options, np, nt}; 654 | 655 | geoc_list.resize(nt); 656 | 657 | if (PyArray_ISFLOAT(arr1)) { 658 | if (nd1 == 1) { 659 | for (int i = 0; i < nt; ++i) { 660 | geoc_list[i] = sg2::geocentric_data{PyArray_Get(arr1, i)}; 661 | geocx.apply(geoc_list[i], i); 662 | } 663 | } else { // nd1 == 2 664 | for (int i = 0; i < nt; ++i) { 665 | geoc_list[i] = sg2::geocentric_data{ 666 | PyArray_Get(arr1, i, 0), 667 | PyArray_Get(arr1, i, 1) 668 | }; 669 | geocx.apply(geoc_list[i], i); 670 | } 671 | } 672 | } else { 673 | if (nd1 == 1) { 674 | for (int i = 0; i < nt; ++i) { 675 | geoc_list[i] = sg2::geocentric_data{PyArray_Get(arr1, i)}; 676 | geocx.apply(geoc_list[i], i); 677 | } 678 | } else { // nd1 == 2 679 | for (int i = 0; i < nt; ++i) { 680 | geoc_list[i] = sg2::geocentric_data{ 681 | PyArray_Get(arr1, i, 0), 682 | PyArray_Get(arr1, i, 1) 683 | }; 684 | geocx.apply(geoc_list[i], i); 685 | } 686 | } 687 | } 688 | 689 | geopoint_list.resize(np); 690 | 691 | for (int i = 0; i < np; ++i) { 692 | double lon = PyArray_Get(arr0, i, 0); 693 | double lat = PyArray_Get(arr0, i, 1); 694 | double alt = PyArray_Get(arr0, i, 2); 695 | geopoint_list[i] = sg2::geopoint{lon, lat, alt, *el.at(i_elipsoid)}; 696 | gpx.apply(geopoint_list[i], i); 697 | } 698 | 699 | if (topocx.ref.size() > 0) { 700 | for (int p = 0; p < np; ++p) { 701 | for (int t = 0; t < nt; ++t) { 702 | auto const & geoc = geoc_list[t]; 703 | auto const & geopoint = geopoint_list[p]; 704 | try { 705 | sg2::topocentric_data topoc(geoc, geopoint); 706 | topocx.apply(topoc, p, t); 707 | } catch(int & e) { 708 | topocx.clear(p, t); 709 | warning_count += 1; 710 | continue; 711 | } 712 | } 713 | } 714 | } 715 | 716 | if(warning_count > 0) 717 | fprintf(stderr, "Warning: %d invalid value(s) encountered\n", warning_count); 718 | 719 | auto ms = reinterpret_cast(PyModule_GetState(self)); 720 | PyObject * dict = PyObject_CallObject(ms->ns, NULL); 721 | { 722 | auto obj = geocx.as_object(self); 723 | PyObject_SetAttrString(dict, "geoc", obj); 724 | Py_DECREF(obj); 725 | } 726 | 727 | { 728 | auto obj = gpx.as_object(self); 729 | PyObject_SetAttrString(dict, "gp", obj); 730 | Py_DECREF(obj); 731 | } 732 | 733 | { 734 | auto obj = topocx.as_object(self); 735 | PyObject_SetAttrString(dict, "topoc", obj); 736 | Py_DECREF(obj); 737 | } 738 | 739 | Py_XDECREF(arr0); /* release reference to this arr0 */ 740 | Py_XDECREF(arr1); 741 | return dict; 742 | } 743 | 744 | error: 745 | if (arr0) 746 | Py_XDECREF(arr0); 747 | if (arr1) 748 | Py_XDECREF(arr1); 749 | return NULL; 750 | 751 | } catch (...) { 752 | if(arr0) 753 | Py_XDECREF(arr0); 754 | if(arr1) 755 | Py_XDECREF(arr1); 756 | return NULL; 757 | } 758 | } 759 | 760 | static char const _doc_sun_rise[] = 761 | "Compute time of sun rise, sun set and sun transit.\n" 762 | "\n" 763 | "Parameters\n" 764 | "----------\n" 765 | "\n" 766 | "geopoint : 2D array of double\n" 767 | " (N,3) of double each row is lon in degrees, lat in degrees, altitude in metter.\n" 768 | "\n" 769 | "timespamp : 1D array of double, 1D array of numpy.datetime64\n" 770 | " List of requested timestamp in ut\n" 771 | "\n" 772 | "Returns\n" 773 | "-------\n" 774 | "\n" 775 | "output : 3D array of datetime64[ms]\n" 776 | " (N,M,3) datetime64[ms] with repectively sun rise, sun transit and sun set\n" 777 | " in ut.\n" 778 | ; 779 | 780 | static PyObject * py_sun_rise(PyObject * self, PyObject * args) 781 | { 782 | PyArrayObject * arr0 = NULL; 783 | PyArrayObject * arr1 = NULL; 784 | 785 | try { 786 | 787 | PyObject * arg0 = NULL; 788 | PyObject * arg1 = NULL; 789 | 790 | int warning_count = 0; 791 | PyArrayObject * out_arr; 792 | int nd0, nd1, np, nt; 793 | 794 | /* get args */ 795 | if (!PyArg_ParseTuple(args, "OO", &arg0, &arg1)) { 796 | set_python_exception("Invalid inputs arguments, expect 2 arguments\n"); 797 | throw 0; 798 | } 799 | 800 | /* Get an array from any python object */ 801 | arr0 = reinterpret_cast(PyArray_FROM_OTF(arg0, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY)); 802 | if (arr0 == NULL) { 803 | set_python_exception("Invalid inputs arguments, the first arguments must be an array\n"); 804 | throw 0; 805 | } 806 | 807 | arr1 = reinterpret_cast(PyArray_FROM_OF(arg1, NPY_ARRAY_IN_ARRAY)); 808 | if (arr1 == NULL) { 809 | set_python_exception("Invalid inputs arguments, the second arguments must be an array\n"); 810 | throw 0; 811 | } 812 | 813 | if (PyArray_ISINTEGER(reinterpret_cast(arr1))) { 814 | auto tmp = reinterpret_cast(PyArray_FromArray(arr1, PyArray_DescrFromType(NPY_INT64), NPY_ARRAY_IN_ARRAY)); 815 | std::swap(arr1, tmp); 816 | Py_XDECREF(tmp); 817 | } else if (PyArray_ISFLOAT(reinterpret_cast(arr1))) { 818 | auto tmp = reinterpret_cast(PyArray_FromArray(arr1, PyArray_DescrFromType(NPY_DOUBLE), NPY_ARRAY_IN_ARRAY)); 819 | std::swap(arr1, tmp); 820 | Py_XDECREF(tmp); 821 | } else if (PyArray_TYPE(arr1) == NPY_DATETIME) { 822 | // Ensure datetime64[ms] 823 | auto tmp = reinterpret_cast(PyObject_CallMethod(reinterpret_cast(arr1), "astype", "(s)", "datetime64[ms]")); 824 | std::swap(arr1, tmp); 825 | Py_XDECREF(tmp); 826 | // Ensure integer 827 | tmp = reinterpret_cast(PyObject_CallMethod(reinterpret_cast(arr1), "astype", "(s)", "i8")); 828 | std::swap(arr1, tmp); 829 | Py_XDECREF(tmp); 830 | tmp = reinterpret_cast(PyArray_FromArray(arr1, PyArray_DescrFromType(NPY_INT64), NPY_ARRAY_IN_ARRAY)); 831 | std::swap(arr1, tmp); 832 | Py_XDECREF(tmp); 833 | } else { 834 | set_python_exception("Invalid inputs arguments, the second arguments must be an array of int or float\n"); 835 | throw 0; 836 | } 837 | 838 | if (arr1 == NULL) { 839 | set_python_exception("Invalid inputs arguments, the second arguments must be an array\n"); 840 | throw 0; 841 | } 842 | 843 | nd0 = PyArray_NDIM(arr0); 844 | if (nd0 != 2) { 845 | set_python_exception("Input arguments rank is invalid, got %d expect 2\n", nd0); 846 | throw 0; 847 | } 848 | 849 | nd1 = PyArray_NDIM(arr1); 850 | if ((nd1 != 1)) { 851 | set_python_exception("Input arguments rank is invalid, got %u expect 1 \n", nd1); 852 | throw 0; 853 | } 854 | 855 | if (PyArray_DIM(arr0, 1) != 3) { 856 | set_python_exception("Input arguments shape is invalid, got (N,%lu) expect (N,3)\n", PyArray_DIM(arr0, 1)); 857 | throw 0; 858 | } 859 | 860 | np = PyArray_DIM(arr0, 0); 861 | if (np <= 0) { 862 | set_python_exception("Input arguments shape is invalid, got (N,3) expect (%lu,3)\n", PyArray_DIM(arr0, 0)); 863 | throw 0; 864 | } 865 | 866 | nt = PyArray_DIM(arr1, 0); 867 | if (nt <= 0) { 868 | set_python_exception("Input arguments shape is invalid, got (M,) expect (%lu,)\n", PyArray_DIM(arr0, 0)); 869 | throw 0; 870 | } 871 | 872 | vector time_list; 873 | time_list.resize(nt); 874 | 875 | if (PyArray_ISFLOAT(arr1)) { 876 | for (int i = 0; i < nt; ++i) { 877 | time_list[i] = sg2::date{PyArray_Get(arr1, i)}; 878 | } 879 | } else { 880 | for (int i = 0; i < nt; ++i) { 881 | time_list[i] = sg2::date{PyArray_Get(arr1, i)}; 882 | } 883 | } 884 | 885 | vector geopoint_list; 886 | geopoint_list.resize(np); 887 | 888 | for (int i = 0; i < np; ++i) { 889 | double lon = PyArray_Get(arr0, i, 0); 890 | double lat = PyArray_Get(arr0, i, 1); 891 | double alt = PyArray_Get(arr0, i, 2); 892 | geopoint_list[i] = sg2::geopoint{lon, lat, alt}; 893 | } 894 | 895 | npy_intp dims[] = {nt, np, 3}; 896 | out_arr = reinterpret_cast(PyArray_SimpleNewFromDescr(3, dims, create_datetime64_ms_dtype())); 897 | 898 | for (int t = 0; t < nt; ++t) { 899 | /** TODO: maybe avoid this memcopy **/ 900 | auto const & time = time_list[t]; 901 | for (int p = 0; p < np; ++p) { 902 | /** TODO: maybe avoid this memcopy **/ 903 | auto const & geopoint = geopoint_list[p]; 904 | try { 905 | auto r = sg2::sunrise(time, geopoint); 906 | 907 | PyArray_Get(out_arr, t, p, 0) = get<0>(r).msec; 908 | PyArray_Get(out_arr, t, p, 1) = get<1>(r).msec; 909 | PyArray_Get(out_arr, t, p, 2) = get<2>(r).msec; 910 | 911 | } catch(int & e) { 912 | 913 | PyArray_Get(out_arr, t, p, 0) = NPY_DATETIME_NAT; 914 | PyArray_Get(out_arr, t, p, 1) = NPY_DATETIME_NAT; 915 | PyArray_Get(out_arr, t, p, 2) = NPY_DATETIME_NAT; 916 | 917 | warning_count += 1; 918 | continue; 919 | } 920 | } 921 | } 922 | 923 | if(warning_count > 0) 924 | fprintf(stderr, "Warning: %d invalid value(s) encountered\n", warning_count); 925 | 926 | 927 | Py_XDECREF(arr0); /* release reference to this arr0 */ 928 | Py_XDECREF(arr1); 929 | 930 | return reinterpret_cast(out_arr); 931 | 932 | } catch (...) { 933 | if(arr0) 934 | Py_XDECREF(arr0); 935 | if(arr1) 936 | Py_XDECREF(arr1); 937 | return NULL; 938 | } 939 | } 940 | 941 | 942 | #define TPL_FUNCTION(name) {#name, py_##name, METH_VARARGS, "Not documented"} 943 | 944 | static PyMethodDef methods[] = 945 | { 946 | {"sun_position", py_sun_position, METH_VARARGS, _doc_sun_position}, 947 | {"sun_rise", py_sun_rise, METH_VARARGS, _doc_sun_rise}, 948 | {NULL, NULL, 0, NULL} 949 | }; 950 | 951 | static int pysg2_traverse(PyObject *m, visitproc visit, void *arg) { 952 | auto ms = reinterpret_cast(PyModule_GetState(m)); 953 | Py_VISIT(ms->Error); 954 | Py_VISIT(ms->types); 955 | Py_VISIT(ms->ns); 956 | return 0; 957 | } 958 | 959 | static int pysg2_clear(PyObject *m) { 960 | auto ms = reinterpret_cast(PyModule_GetState(m)); 961 | Py_CLEAR(ms->Error); 962 | Py_CLEAR(ms->types); 963 | Py_CLEAR(ms->ns); 964 | return 0; 965 | } 966 | 967 | static struct PyModuleDef moduledef = { 968 | PyModuleDef_HEAD_INIT, 969 | "sg2", 970 | NULL, 971 | sizeof(module_state), 972 | methods, 973 | NULL, 974 | pysg2_traverse, 975 | pysg2_clear, 976 | NULL 977 | }; 978 | 979 | 980 | static string const _doc_ymdh_to_jd = 981 | "Convert date in year-month-day_of_month-hours from to julian date\n" 982 | "\n" 983 | "Parameters\n" 984 | "----------\n" 985 | "\n" 986 | "y : 1D array\n" 987 | " The list year numbers\n" 988 | "\n" 989 | "m : 1D array\n" 990 | " The list of month number in [1,12]\n" 991 | "\n" 992 | "d : 1D array\n" 993 | " The list of day of month in [1,31]\n" 994 | "\n" 995 | "h : 1D array\n" 996 | " array of decimal hour within the day, ex. 12:30 is 12.5\n" 997 | "\n" 998 | "Returns\n" 999 | "-------\n" 1000 | "\n" 1001 | "julian_days : 1D array\n" 1002 | " List of julian day\n" 1003 | "\n" 1004 | "\n" 1005 | "Notes\n" 1006 | "-----\n" 1007 | "\n" 1008 | "Inputs array must have the same size\n" 1009 | ; 1010 | 1011 | static string const _doc_jd_to_ymdh = 1012 | "Convert julian day to year-month-day_of_month-hours form\n" 1013 | "\n" 1014 | "Parameters\n" 1015 | "----------\n" 1016 | "\n" 1017 | "julian_days : 1D array of double\n" 1018 | " List of julian days to convert\n" 1019 | "\n" 1020 | "Returns\n" 1021 | "-------\n" 1022 | "\n" 1023 | "y : 1D array\n" 1024 | " Array of year number\n" 1025 | "\n" 1026 | "m : 1D array\n" 1027 | " Array of month number in [1,12]\n" 1028 | "\n" 1029 | "\n" 1030 | "d : 1D array\n" 1031 | " Array of day of month in [1,31]\n" 1032 | "\n" 1033 | "h : 1D array\n" 1034 | " Array of decimal hour within the day, ex. 12:30 is 12.5\n" 1035 | ; 1036 | 1037 | static string const _doc_topocentric_correction_refraction_SAE = 1038 | "Compute the atmosphere refraction using SAE correction\n" 1039 | "\n" 1040 | "Parameters\n" 1041 | "----------\n" 1042 | "\n" 1043 | "gamma_S0 : 1D array of double\n" 1044 | " Array of gamma_S0 (solar elevation with no refraction correction), in radians\n" 1045 | "\n" 1046 | "P : 1D array of double\n" 1047 | " Array of pressure in Pa\n" 1048 | "\n" 1049 | "T : 1D array of double\n" 1050 | " Array of temperature in degrees Celsus\n" 1051 | "\n" 1052 | "Returns\n" 1053 | "-------\n" 1054 | "\n" 1055 | "gamma : 1D array of double\n" 1056 | " Array of corrected solar elevation\n" 1057 | ; 1058 | 1059 | static string const _doc_topocentric_correction_refraction_ZIM = 1060 | "Compute the atmosphere refraction using SAE correction\n" 1061 | "\n" 1062 | "Paramters\n" 1063 | "---------\n" 1064 | "\n" 1065 | "gamma_S0 : 1D array of double\n" 1066 | " Array of gamma_S0 (solar elevation with no refraction correction), in radians\n" 1067 | "\n" 1068 | "P : 1D array of double\n" 1069 | " Array of pressure in Pa\n" 1070 | "\n" 1071 | "T : 1D array of double\n" 1072 | " Array of temperature in degrees Celsus as double\n" 1073 | "\n" 1074 | "Returns\n" 1075 | "-------\n" 1076 | "\n" 1077 | "gamma : 1D array of double\n" 1078 | " Array of corrected solar elevation\n" 1079 | ; 1080 | 1081 | PyMODINIT_FUNC 1082 | PyInit_sg2(void) 1083 | { 1084 | PyObject * m = PyModule_Create(&moduledef); 1085 | if (m == NULL) 1086 | return NULL; 1087 | 1088 | auto ms = reinterpret_cast(PyModule_GetState(m)); 1089 | 1090 | ms->Error = PyErr_NewException("sg2.error", NULL, NULL); 1091 | Py_INCREF(ms->Error); 1092 | PyModule_AddObject(m, "error", ms->Error); 1093 | 1094 | /** init numpy **/ 1095 | import_array(); 1096 | import_umath(); 1097 | PyDateTime_IMPORT; 1098 | 1099 | ms->types = PyImport_ImportModule("types"); 1100 | if (ms->types == NULL) 1101 | return NULL; 1102 | ms->ns = PyObject_GetAttrString(ms->types, "SimpleNamespace"); 1103 | if (ms->ns == NULL) 1104 | return NULL; 1105 | 1106 | PyModule_AddIntConstant(m, "WGS84", 0); 1107 | PyModule_AddIntConstant(m, "RGF83", 1); 1108 | PyModule_AddIntConstant(m, "NTF", 2); 1109 | PyModule_AddIntConstant(m, "AA", 3); 1110 | PyModule_AddIntConstant(m, "SPA", 4); 1111 | PyModule_AddIntConstant(m, "NGP", 5);; 1112 | PyModule_AddIntConstant(m, "SPHERE", 6); 1113 | 1114 | static python_bind_helper::build_ufunc ufunc_ymdh_to_jd("ymdh_to_jd", _doc_ymdh_to_jd); 1115 | ufunc_ymdh_to_jd.register_to(m); 1116 | static python_bind_helper::build_ufunc ufunc_jd_to_ymdh("jd_to_ymdh", _doc_jd_to_ymdh); 1117 | ufunc_jd_to_ymdh.register_to(m); 1118 | static python_bind_helper::build_ufunc ufunc_topocentric_correction_refraction_SAE("topocentric_correction_refraction_SAE", _doc_topocentric_correction_refraction_SAE); 1119 | ufunc_topocentric_correction_refraction_SAE.register_to(m); 1120 | static python_bind_helper::build_ufunc ufunc_topocentric_correction_refraction_ZIM("topocentric_correction_refraction_ZIM", _doc_topocentric_correction_refraction_ZIM); 1121 | ufunc_topocentric_correction_refraction_ZIM.register_to(m); 1122 | 1123 | return m; 1124 | 1125 | } 1126 | 1127 | -------------------------------------------------------------------------------- /resources/geocentric.data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gschwind/sg2/ca99a3ac35454ae85a4b2ec02c231c5bdebf2aa3/resources/geocentric.data -------------------------------------------------------------------------------- /resources/geocentric.data.README: -------------------------------------------------------------------------------- 1 | data.bin is designed to be mmapped or included in binary form in the library, 2 | the file is binary file using little endian and formated as follow: 3 | 4 | - first 8 bytes is signed time offset of following data in second since 5 | 1970-01-01 00:00:00 6 | - second 8 bytes is signed time delta of following data in second 7 | - third 8 bytes is signed count of following data, see next item. 8 | - following data is an array of IEEE double of shape [count][3] with columns: 9 | * sin(L) 10 | * cos(L) 11 | * R 12 | 13 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from distutils.core import setup, Extension 5 | from subprocess import check_output 6 | from collections import defaultdict 7 | import numpy 8 | import os, sys, re 9 | 10 | params = { 11 | 'include_dirs': [ 12 | numpy.get_include(), 13 | "third-parties/python-bind-helper", 14 | "src" 15 | ], 16 | 'sources': [ 17 | "python/pysg2.cxx", 18 | "src/sg2.cxx", 19 | "src/sg2_data_handler.cxx", 20 | "src/sg2_date.cxx", 21 | "src/sg2_geocentric.cxx", 22 | "src/sg2_geocentric_data.cxx", 23 | "src/sg2_geopoint.cxx", 24 | "src/sg2_topocentric.cxx", 25 | "src/sg2_utils.cxx" 26 | ], 27 | 'depends': [ 28 | "src/sg2_data_handler.hxx", 29 | "src/sg2_date.hxx", 30 | "src/sg2_err.hxx", 31 | "src/sg2_geocentric.hxx", 32 | "src/sg2_geopoint.hxx", 33 | "src/sg2.hxx", 34 | "src/sg2_math.hxx", 35 | "src/sg2_topocentric.hxx", 36 | "src/sg2_typedef.hxx", 37 | "src/sg2_utils.hxx", 38 | "third-parties/python-bind-helper/python-bind-helper.hxx" 39 | ] 40 | } 41 | 42 | if os.name == 'nt': 43 | params['extra_compile_args'] = ['/O2', '/DSG2_WIN32', '/std:c++17'] 44 | else: 45 | params['extra_compile_args'] = ['-O3', '-std=c++11'] 46 | 47 | setup(ext_modules = [Extension('sg2', **params)]) 48 | 49 | -------------------------------------------------------------------------------- /sg2.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: Solar Geometry 2 library v@PACKAGE_VERSION@ 7 | Description: Solar Geometry 2 library v@PACKAGE_VERSION@ 8 | Requires: 9 | Version: @PACKAGE_VERSION@ 10 | Libs: -L${libdir} -lsg2 11 | Cflags: @VDT_DEFINE@ -I${includedir}/ 12 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = sg2_query sg2_benchmark sg2_convert_date 2 | 3 | BUILT_SOURCES = sg2_geocentric_data.cxx 4 | 5 | CLEANFILES = sg2_geocentric_data.cxx 6 | 7 | AM_CXXFLAGS = \ 8 | @VDT_DEFINE@ \ 9 | @VDT_CFLAGS@ 10 | 11 | AM_CCASFLAGS = \ 12 | -I$(srcdir) 13 | 14 | lib_LTLIBRARIES = libsg2.la 15 | include_HEADERS = \ 16 | sg2.hxx \ 17 | sg2_math.hxx \ 18 | sg2_typedef.hxx \ 19 | sg2_err.hxx \ 20 | sg2_date.hxx \ 21 | sg2_geocentric.hxx \ 22 | sg2_topocentric.hxx 23 | 24 | # /!\ Be causious when updating version number, then do not match PACKAGE_VERSION on purpose 25 | # The version-info is defined as follow: current:revision:age 26 | # The rule to apply are explained in [1] as follow: 27 | # 28 | # If either revision or age are omitted, they default to 0. Also note that age 29 | # must be less than or equal to the current interface number. 30 | # 31 | # Here are a set of rules to help you update your library version information: 32 | # 33 | # 1. Start with version information of ‘0:0:0’ for each libtool library. 34 | # 2. Update the version information only immediately before a public release 35 | # of your software. More frequent updates are unnecessary, and only 36 | # guarantee that the current interface number gets larger faster. 37 | # 3. If the library source code has changed at all since the last update, 38 | # then increment revision (‘c:r:a’ becomes ‘c:r+1:a’). 39 | # 4. If any interfaces have been added, removed, or changed since the last 40 | # update, increment current, and set revision to 0. 41 | # 5. If any interfaces have been added since the last public release, then 42 | # increment age. 43 | # 6. If any interfaces have been removed or changed since the last public 44 | # release, then set age to 0. 45 | # 46 | # [1] https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html 47 | # 48 | libsg2_la_LDFLAGS = -version-info 3:1:0 49 | libsg2_la_SOURCES = \ 50 | sg2.cxx \ 51 | sg2.hxx \ 52 | sg2_date.cxx \ 53 | sg2_date.hxx \ 54 | sg2_geopoint.cxx \ 55 | sg2_geopoint.hxx \ 56 | sg2_geocentric.cxx \ 57 | sg2_geocentric.hxx \ 58 | sg2_topocentric.cxx \ 59 | sg2_topocentric.hxx \ 60 | sg2_data_handler.cxx \ 61 | sg2_data_handler.hxx \ 62 | sg2_utils.cxx \ 63 | sg2_utils.hxx \ 64 | sg2_geocentric_data.cxx 65 | 66 | sg2_query_LDADD = @VDT_LIBS@ libsg2.la 67 | sg2_query_SOURCES = \ 68 | sg2_query.cxx 69 | 70 | sg2_benchmark_LDADD = @VDT_LIBS@ libsg2.la 71 | sg2_benchmark_SOURCES = \ 72 | sg2_benchmark.cxx 73 | 74 | sg2_convert_date_LDADD = @VDT_LIBS@ libsg2.la 75 | sg2_convert_date_SOURCES = \ 76 | sg2_convert_date.cxx 77 | 78 | sg2_geocentric_data.cxx: Makefile 79 | $(PYTHON) $(top_srcdir)/gen-geocentric-data-code.py $(top_srcdir)/resources/geocentric.data >$@ 80 | 81 | -------------------------------------------------------------------------------- /src/sg2.cxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2021 MINES ParisTech 3 | * Copyright 2011 Philippe Blanc 4 | * Copyright 2014-2021 Benoit Gschwind 5 | * 6 | * This file is part of libsg2. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the Lesser GNU General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (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 | * Lesser GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the Lesser GNU General Public 19 | * License along with this program. If not, see 20 | * . 21 | */ 22 | 23 | #define SG2_C_ 24 | 25 | #include "sg2.hxx" 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | namespace sg2 { 34 | 35 | // date 'd' Must be close to mean solar time. 36 | std::tuple sunrise(date const & d, geopoint const & gp) 37 | { 38 | // the sun rise and sun set when sun go above this limit. 39 | // SPA is using RAD(-0.8333) with some refracton correction 40 | // TODO: make it as parameter 41 | static double const sun_rise_gamma = RAD(0.0); 42 | static double const sin_sun_rise_gamma = sin(sun_rise_gamma); 43 | 44 | // Round at 12:00 local mst in UT. 45 | 46 | int64_t const delta_mst = (gp.lambda/2.0/PI)*86400000L; 47 | int64_t mst = d.msec + delta_mst; 48 | 49 | // to round at 12h floor(mst-86400000L/2L+86400000L/2L)+86400000L/2L 50 | // with floor(T) == T - T%86400000L 51 | if (mst < 0) 52 | mst = mst - (86400000L + mst%86400000L) + 86400000L/2L; 53 | else 54 | mst = mst - mst%86400000L + 86400000L/2L; 55 | 56 | int64_t const ut_ref = mst - delta_mst; 57 | 58 | date d0{ut_ref}; // D0 is close to MST 59 | date dp{d0.msec-86400000L}; 60 | date dn{d0.msec+86400000L}; 61 | 62 | geocentric_data geoc_d0x{d0}; 63 | 64 | if (geoc_d0x.tt.isnat()) { 65 | return {sg2::nat, sg2::nat, sg2::nat}; 66 | } 67 | 68 | int64_t const delta_tt = geoc_d0x.tt.msec-geoc_d0x.ut.msec; 69 | 70 | geocentric_data geoc_dp{dp, dp}; 71 | 72 | if (geoc_dp.tt.isnat()) { 73 | return {sg2::nat, sg2::nat, sg2::nat}; 74 | } 75 | 76 | geocentric_data geoc_d0{d0, d0}; 77 | 78 | if (geoc_d0.tt.isnat()) { 79 | return {sg2::nat, sg2::nat, sg2::nat}; 80 | } 81 | 82 | geocentric_data geoc_dn{dn, dn}; 83 | 84 | if (geoc_dn.tt.isnat()) { 85 | return {sg2::nat, sg2::nat, sg2::nat}; 86 | } 87 | 88 | // Eq A3 89 | // CLAMP here because geoc_d0.r_alpha and geoc_d0x.nu may have 2*PI 90 | // extrat differences 91 | // m0 relatif to 12h MST, m0 offset match d0 92 | // Should be close to 0 93 | 94 | // r_alpha - nu - lambda is the relative angle between sun and gp location in 95 | // the celestial equator 96 | // m0 should be close to 0 given geoc_d0x 97 | double m0 = sg2::CLAMP_PI_PI(geoc_d0.r_alpha - geoc_d0x.nu - gp.lambda)/D_PI; 98 | 99 | // Eq A4 100 | // Safe way to deal with divide by zero. 101 | // note that in this case if cos(x) is close to 0.0 or equal to zero 102 | // sin(x) will be close to 1.0 meaning we do not have to check for 103 | // 0 divide by 0, we are sure the nominator will be far from zero 104 | // when denominator will be close to 0. 105 | double xnominator = sin_sun_rise_gamma-sin(gp.phi)*sin(geoc_d0.delta); 106 | double xdenominator = cos(gp.phi)*cos(geoc_d0.delta); 107 | if (fabs(xnominator)>fabs(xdenominator)) { 108 | // The sun will never go above sun_rise_gamma. 109 | return {sg2::nat, sg2::nat, sg2::nat}; 110 | } 111 | 112 | double H0 = acos(xnominator/xdenominator); 113 | 114 | // Eq. A5 115 | double m1 = m0-H0/D_PI; 116 | double m2 = m0+H0/D_PI; 117 | 118 | double v0 = geoc_d0x.nu + RAD(360.985647)*m0; 119 | double v1 = geoc_d0x.nu + RAD(360.985647)*m1; 120 | double v2 = geoc_d0x.nu + RAD(360.985647)*m2; 121 | 122 | double n0 = m0 + delta_tt/(86400e3); 123 | double n1 = m1 + delta_tt/(86400e3); 124 | double n2 = m2 + delta_tt/(86400e3); 125 | 126 | double alpha_a = geoc_d0.r_alpha-geoc_dp.r_alpha; 127 | alpha_a = sg2::CLAMP_0_2PI(alpha_a); 128 | 129 | double delta_a = geoc_d0.delta-geoc_dp.delta; 130 | 131 | double alpha_b = geoc_dn.r_alpha-geoc_d0.r_alpha; 132 | alpha_b = sg2::CLAMP_0_2PI(alpha_b); 133 | 134 | double delta_b = geoc_dn.delta-geoc_d0.delta; 135 | 136 | double alpha_c = alpha_b - alpha_a; 137 | double delta_c = delta_b - delta_a; 138 | 139 | double r_alpha_p0 = geoc_d0.r_alpha + n0*(alpha_a+alpha_b+alpha_c*n0)/2.0; 140 | double r_alpha_p1 = geoc_d0.r_alpha + n1*(alpha_a+alpha_b+alpha_c*n1)/2.0; 141 | double r_alpha_p2 = geoc_d0.r_alpha + n2*(alpha_a+alpha_b+alpha_c*n2)/2.0; 142 | 143 | double delta_p1 = geoc_d0.delta + n1*(delta_a+delta_b+delta_c*n1)/2.0; 144 | double delta_p2 = geoc_d0.delta + n2*(delta_a+delta_b+delta_c*n2)/2.0; 145 | 146 | double Hp0 = -sg2::CLAMP_PI_PI(r_alpha_p0 - v0 - gp.lambda); 147 | double Hp1 = -sg2::CLAMP_PI_PI(r_alpha_p1 - v1 - gp.lambda); 148 | double Hp2 = -sg2::CLAMP_PI_PI(r_alpha_p2 - v2 - gp.lambda); 149 | 150 | // Hp1 must be negative, because the sun rise on morning 151 | if (Hp1 > 0.0) 152 | Hp1 -= 2.0*PI; 153 | 154 | // Must be positive, because sun set later 155 | if (Hp2 < 0.0) 156 | Hp2 += 2.0*PI; 157 | 158 | double T = m0 - Hp0/D_PI; 159 | 160 | double R; 161 | double S; 162 | 163 | // Check if sun will rise 164 | double x1sin = sin(gp.phi)*sin(delta_p1); 165 | double x1cos = cos(gp.phi)*cos(delta_p1); 166 | if (fabs(sin_sun_rise_gamma-x1sin)>fabs(x1cos)) { 167 | // The sun will never rise today. 168 | R = std::numeric_limits::quiet_NaN(); 169 | } else { 170 | double h1 = asin(x1sin+x1cos*cos(Hp1)); 171 | // Must be negative because it's the morning. 172 | double xH1 = -acos((sin_sun_rise_gamma-x1sin)/x1cos); 173 | R = m1 + (xH1 - Hp1) / (D_PI); 174 | } 175 | 176 | // Check if sun will set 177 | double x2sin = sin(gp.phi)*sin(delta_p2); 178 | double x2cos = cos(gp.phi)*cos(delta_p2); 179 | if (fabs(sin_sun_rise_gamma-x2sin)>fabs(x2cos)) { 180 | // The sun will never set today. 181 | S = std::numeric_limits::quiet_NaN(); 182 | } else { 183 | double h2 = asin(x2sin+x2cos*cos(Hp2)); 184 | double xH2 = acos((sin_sun_rise_gamma-x2sin)/x2cos); 185 | S = m2 + (xH2 - Hp2) / (D_PI); 186 | } 187 | 188 | return {std::isfinite(R)?date{d0.msec+static_cast(R*86400e3)}:sg2::nat, 189 | date{d0.msec+static_cast(T*86400e3)}, 190 | std::isfinite(S)?date{d0.msec+static_cast(S*86400e3)}:sg2::nat}; 191 | 192 | } 193 | 194 | } 195 | -------------------------------------------------------------------------------- /src/sg2.hxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2021 MINES ParisTech 3 | * Copyright 2011 Philippe Blanc 4 | * Copyright 2014-2021 Benoit Gschwind 5 | * 6 | * This file is part of libsg2. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the Lesser GNU General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (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 | * Lesser GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the Lesser GNU General Public 19 | * License along with this program. If not, see 20 | * . 21 | */ 22 | 23 | #ifndef SG2_H_ 24 | #define SG2_H_ 25 | 26 | #include "sg2_typedef.hxx" 27 | #include "sg2_date.hxx" 28 | #include "sg2_geocentric.hxx" 29 | #include "sg2_topocentric.hxx" 30 | 31 | #include 32 | 33 | namespace sg2 { 34 | 35 | std::tuple sunrise(date const &, geopoint const &); 36 | 37 | } 38 | 39 | #endif /* SG2_H_ */ 40 | -------------------------------------------------------------------------------- /src/sg2_benchmark.cxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2021 MINES ParisTech 3 | * Copyright 2017-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 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | void usage(void) { 30 | printf("usage: benchmark_libsg2\n"); 31 | } 32 | 33 | int main(int argc, char ** argv) { 34 | 35 | double const jd_begin = 2457868.500000; 36 | double const jd_delta = 1.0/24.0/60.0; 37 | int const jd_count = 1440*366*10; 38 | 39 | double const lat = 45.0; 40 | double const lon = 5.0; 41 | double const alt = 100.0; 42 | 43 | if(argc != 1) { 44 | usage(); 45 | return -1; 46 | } 47 | 48 | auto tic = std::chrono::steady_clock::now(); 49 | 50 | std::vector out(jd_count); 51 | for (int i = 0; i < jd_count; ++i) { 52 | double jd = jd_begin + i * 1.0/24.0/60.0; 53 | /** location related data **/ 54 | sg2::geopoint geopoint{lon, lat, alt, sg2::ELLPS_WGS84}; 55 | /** time related data **/ 56 | sg2::geocentric_data geoc{sg2::date{jd}}; 57 | /** local-time related data **/ 58 | sg2::topocentric_data topoc{geoc, geopoint}; 59 | out[i] = topoc.gamma_S0; 60 | } 61 | 62 | auto toc = std::chrono::steady_clock::now(); 63 | 64 | std::chrono::nanoseconds diff = toc-tic; 65 | 66 | printf("elapsed time: %f seconds\n", diff.count()/1e9); 67 | 68 | return 0; 69 | 70 | } 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/sg2_convert_date.cxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 MINES ParisTech 3 | * Copyright 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 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | int main(int argc, char ** argv) 29 | { 30 | if (argc < 2) { 31 | printf("invalid arguments\n"); 32 | return 1; 33 | } 34 | 35 | sg2::julian jd; 36 | sg2::date d; 37 | sg2::ymdh ymdh; 38 | sg2::ymdhmsn ymdhmsn; 39 | sg2::ydoyh ydoyh; 40 | 41 | if (strcmp("--jd", argv[1]) == 0) { 42 | if (argc != 3) { 43 | printf("invalid arguments\n"); 44 | return 1; 45 | } 46 | 47 | jd = sg2::julian{atof(argv[2])}; 48 | d = sg2::date{jd.value}; 49 | ymdh = sg2::ymdh{jd.value}; 50 | ymdhmsn = sg2::ymdhmsn{d}; 51 | ydoyh = sg2::ydoyh{ymdh}; 52 | } 53 | 54 | if (strcmp("--msec", argv[1]) == 0) { 55 | if (argc != 3) { 56 | printf("invalid arguments\n"); 57 | return 1; 58 | } 59 | 60 | d = sg2::date{static_cast(atoll(argv[2]))}; 61 | jd = sg2::julian{d}; 62 | ymdh = sg2::ymdh{jd.value}; 63 | ymdhmsn = sg2::ymdhmsn{d}; 64 | ydoyh = sg2::ydoyh{ymdh}; 65 | } 66 | 67 | if (strcmp("--ymdh", argv[1]) == 0) { 68 | if (argc != 6) { 69 | printf("invalid arguments\n"); 70 | return 1; 71 | } 72 | 73 | ymdh = sg2::ymdh(atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), atof(argv[5])); 74 | d = sg2::date{ymdh}; 75 | jd = sg2::julian{ymdh}; 76 | ymdhmsn = sg2::ymdhmsn{d}; 77 | ydoyh = sg2::ydoyh{ymdh}; 78 | } 79 | 80 | printf("jd ............ %f\n", jd.value); 81 | printf("msec .......... %ld\n", d.msec); 82 | printf("ymdh .......... %04d-%02d-%02d %f\n", ymdh.year, ymdh.month, ymdh.day_of_month, ymdh.hour); 83 | printf("ymdhmsn ....... %04d-%02d-%02dT%02d:%02d:%02d.%09d\n", ymdhmsn.year, ymdhmsn.month, ymdhmsn.day_of_month, ymdhmsn.hour, ymdhmsn.min, ymdhmsn.sec, ymdhmsn.msec); 84 | printf("ydoyh ......... %04d-%03d %f\n", ydoyh.year, ydoyh.day_of_year, ydoyh.hour); 85 | 86 | return 0; 87 | } 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/sg2_data_handler.cxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 MINES ParisTech 3 | * Copyright 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 "sg2_data_handler.hxx" 23 | 24 | namespace sg2 { 25 | 26 | 27 | 28 | } // namespace sg2 29 | 30 | -------------------------------------------------------------------------------- /src/sg2_data_handler.hxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 MINES ParisTech 3 | * Copyright 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 | #ifndef SRC_SG2_DATA_HANDLER_HXX_ 23 | #define SRC_SG2_DATA_HANDLER_HXX_ 24 | 25 | #include 26 | 27 | namespace sg2 { 28 | 29 | struct geocentric_data_format { 30 | double sinL; 31 | double cosL; 32 | double R; 33 | }; 34 | 35 | extern int64_t const _geocentric_data_time_offset; 36 | extern int64_t const _geocentric_data_time_delta; 37 | extern int64_t const _geocentric_data_xcount; 38 | 39 | extern geocentric_data_format const _geocentric_data[]; 40 | 41 | inline int64_t const & _geocentric_data_offset() { 42 | return _geocentric_data_time_offset; 43 | } 44 | 45 | inline int64_t const & _geocentric_data_delta() { 46 | return _geocentric_data_time_delta; 47 | } 48 | 49 | inline int64_t const & _geocentric_data_count() { 50 | return _geocentric_data_xcount; 51 | } 52 | 53 | inline double const & _geocentric_data_sinL(int l) { 54 | return _geocentric_data[l].sinL; 55 | } 56 | 57 | inline double const & _geocentric_data_cosL(int l) { 58 | return _geocentric_data[l].cosL; 59 | } 60 | 61 | inline double const & _geocentric_data_R(int l) { 62 | return _geocentric_data[l].R; 63 | } 64 | 65 | 66 | } 67 | 68 | 69 | 70 | #endif /* SRC_SG2_DATA_HANDLER_HXX_ */ 71 | -------------------------------------------------------------------------------- /src/sg2_date.cxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2021 MINES ParisTech 3 | * Copyright 2011 Philippe Blanc 4 | * Copyright 2014-2021 Benoit Gschwind 5 | * 6 | * This file is part of libsg2. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the Lesser GNU General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (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 | * Lesser GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the Lesser GNU General Public 19 | * License along with this program. If not, see 20 | * . 21 | */ 22 | 23 | #include "sg2_date.hxx" 24 | #include "sg2_err.hxx" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "sg2_utils.hxx" 31 | 32 | namespace sg2 { 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/sg2_date.hxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2021 MINES ParisTech 3 | * Copyright 2011 Philippe Blanc 4 | * Copyright 2014-2021 Benoit Gschwind 5 | * 6 | * This file is part of libsg2. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the Lesser GNU General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (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 | * Lesser GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the Lesser GNU General Public 19 | * License along with this program. If not, see 20 | * . 21 | */ 22 | 23 | #ifndef SG2_DATE_HXX_ 24 | #define SG2_DATE_HXX_ 25 | 26 | #include "sg2_typedef.hxx" 27 | #include "sg2_err.hxx" 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | 38 | 39 | namespace sg2 { 40 | 41 | /** 1970-01-01T00:00 **/ 42 | static double const EPOCH_JD = 2440587.5; 43 | 44 | inline int _date_leapyear(int year) { 45 | return (((year % 4) == 0) && (((year % 100) != 0) || ((year % 400) == 0))); 46 | } 47 | 48 | /* return the julian day at 12h */ 49 | inline int _ymd_to_julian_day(int year, int month, int day) 50 | { 51 | double Y, M, D; 52 | 53 | Y = year; 54 | M = month; 55 | D = day; 56 | if (M < 3) { 57 | M += 12; 58 | Y -= 1; 59 | } 60 | 61 | return 1721028.0 + D + floor((153.0 * M - 2.0) / 5.0) + 365.0 * Y 62 | + floor(Y / 4.0) - floor(Y / 100.0) + floor(Y / 400.0); 63 | 64 | } 65 | 66 | inline void _julian_day_to_ymd(int jd, int & year, int & month, int & day) 67 | { 68 | double L, N, I, J, K; 69 | 70 | L = jd + 68569.0; 71 | N = floor(4 * L / 146097.0); 72 | L = L - floor((146097.0 * N + 3.0) / 4.0); 73 | I = floor(4000 * (L + 1) / 1461001.0); 74 | L = L - floor(1461.0 * I / 4.0) + 31.0; 75 | 76 | J = floor(80.0 * L / 2447.0); 77 | K = L - floor(2447.0 * J / 80.0); 78 | L = floor(J / 11.0); 79 | J = J + 2.0 - 12.0 * L; 80 | I = 100.0 * (N - 49.0) + I + L; 81 | 82 | year = I; 83 | month = J; 84 | day = K; 85 | 86 | } 87 | 88 | struct date { 89 | int64_t msec; // millisecond since 1970 90 | 91 | date(); 92 | date(int64_t msec); 93 | date(double jd); 94 | date(ymdh const & d); 95 | date(ydoyh const & d); 96 | 97 | operator std::string() const; 98 | 99 | bool operator ==(date const & x) const; 100 | bool operator <=(date const & x) const; 101 | bool operator >=(date const & x) const; 102 | bool operator <(date const & x) const; 103 | bool operator >(date const & x) const; 104 | 105 | bool isnat() const; 106 | 107 | }; 108 | 109 | // not a time constant 110 | static date const nat; 111 | 112 | struct julian { 113 | double value; // julian date in factionnal days 114 | 115 | julian(); 116 | julian(double jd); 117 | julian(ymdh const & d); 118 | julian(ydoyh const & d); 119 | julian(date const nsec); 120 | }; 121 | 122 | /* Date YMD + H en heure décimale UT */ 123 | struct ymdh { 124 | int year; 125 | int month; 126 | int day_of_month; 127 | double hour; 128 | 129 | ymdh(); 130 | ymdh(short year, short month, char day_of_month, double hour); 131 | ymdh(double jd); 132 | ymdh(date const & d); 133 | ymdh(ydoyh const & p_ydoyh); 134 | 135 | }; 136 | 137 | struct ymdhmsn { 138 | int year; 139 | int month; 140 | int day_of_month; 141 | int hour; 142 | int min; 143 | int sec; 144 | int msec; 145 | 146 | ymdhmsn(); 147 | ymdhmsn(date const date); 148 | 149 | operator std::string() const; 150 | 151 | }; 152 | 153 | struct ydoyh { 154 | short year; 155 | short day_of_year; 156 | double hour; 157 | 158 | ydoyh(); 159 | ydoyh(ymdh const & p_ymdh); 160 | 161 | }; 162 | 163 | inline date::date() : 164 | msec{std::numeric_limits::min()} 165 | { 166 | 167 | } 168 | 169 | inline date::date(int64_t msec) : 170 | msec{msec} 171 | { 172 | 173 | } 174 | 175 | inline date::date(double jd) : 176 | msec(std::isfinite(jd)?std::round((jd-EPOCH_JD)*(24.0*60.0*60.0*1e3)):nat.msec) 177 | { 178 | 179 | } 180 | 181 | inline date::date(ymdh const & d) 182 | { 183 | int64_t xjd = _ymd_to_julian_day(d.year, d.month, d.day_of_month); 184 | msec = (xjd-EPOCH_JD)*(24LL*60LL*60LL*1000LL) + (d.hour-12.0)*60.0*60.0*1e3; 185 | } 186 | 187 | inline date::date(ydoyh const & d) : 188 | date{ymdh{d}} 189 | { 190 | 191 | } 192 | 193 | inline date::operator std::string() const 194 | { 195 | return static_cast(ymdhmsn{*this}); 196 | } 197 | 198 | inline bool date::operator ==(date const & x) const 199 | { 200 | // NAT always equal to nothing else 201 | if (this->isnat() || x.isnat()) 202 | return false; 203 | return this->msec == x.msec; 204 | } 205 | 206 | inline bool date::operator <=(date const & x) const 207 | { 208 | // NAT always equal to nothing else 209 | if (this->isnat() || x.isnat()) 210 | return false; 211 | return this->msec <= x.msec; 212 | } 213 | 214 | inline bool date::operator >=(date const & x) const 215 | { 216 | // NAT always equal to nothing else 217 | if (this->isnat() || x.isnat()) 218 | return false; 219 | return this->msec >= x.msec; 220 | } 221 | 222 | inline bool date::operator <(date const & x) const 223 | { 224 | // NAT always equal to nothing else 225 | if (this->isnat() || x.isnat()) 226 | return false; 227 | return this->msec < x.msec; 228 | } 229 | 230 | inline bool date::operator >(date const & x) const 231 | { 232 | // NAT always equal to nothing else 233 | if (this->isnat() || x.isnat()) 234 | return false; 235 | return this->msec > x.msec; 236 | } 237 | 238 | inline bool date::isnat() const 239 | { 240 | return msec == nat.msec; 241 | } 242 | 243 | inline julian::julian() 244 | { 245 | 246 | } 247 | 248 | inline julian::julian(double jd) : 249 | value{jd} 250 | { 251 | 252 | } 253 | 254 | inline julian::julian(ymdh const & d) 255 | { 256 | int64_t xjd = _ymd_to_julian_day(d.year, d.month, d.day_of_month); 257 | value = xjd - 0.5 + d.hour / 24.0; 258 | } 259 | 260 | inline julian::julian(ydoyh const & d) : 261 | julian{ymdh{d}} 262 | { 263 | 264 | } 265 | 266 | inline julian::julian(date const d) : 267 | value{d.msec/(24.0*60.0*60.0*1e3)+EPOCH_JD} 268 | { 269 | } 270 | 271 | inline ymdh::ymdh() // do not initialize member for performance. 272 | { 273 | 274 | } 275 | 276 | inline ymdh::ymdh(short year, short month, char day_of_month, double hour) : 277 | year{year}, 278 | month{month}, 279 | day_of_month{day_of_month}, 280 | hour{hour} 281 | { 282 | 283 | } 284 | 285 | inline ymdh::ymdh(double jd) 286 | { 287 | _julian_day_to_ymd(floor(jd + 0.5), year, month, day_of_month); 288 | hour = (jd + 0.5 - floor(jd + 0.5)) * 24.0; 289 | } 290 | 291 | inline ymdh::ymdh(date const & d) 292 | { 293 | int jd = d.msec / (1e3 * 60.0 * 60.0 * 24.0) + EPOCH_JD + 0.5; 294 | _julian_day_to_ymd(jd, year, month, day_of_month); 295 | hour = static_cast(d.msec % (1000LL*60LL*60LL*24LL))/(60.0*60.0*1e3); 296 | } 297 | 298 | inline ymdh::ymdh(ydoyh const & p_ydoyh) 299 | { 300 | double B; 301 | B = (double) _date_leapyear(p_ydoyh.year); 302 | this->year = p_ydoyh.year; 303 | this->hour = p_ydoyh.hour; 304 | 305 | if (p_ydoyh.day_of_year < 32) { 306 | this->month = 1; 307 | } else { 308 | this->month = 1 309 | + (short) (floor((303.0 + 5. 310 | * (((double) p_ydoyh.day_of_year) - 59.0 - B))/ 153.0)); 311 | } 312 | 313 | if (this->month < 2) { 314 | this->day_of_month = p_ydoyh.day_of_year - 31 * (this->month - 1); 315 | } else { 316 | this->day_of_month = p_ydoyh.day_of_year 317 | - ((short) (floor((153.0 * ((double) this->month) - 2.0) / 5.0) 318 | - 32.0 + B)); 319 | } 320 | } 321 | 322 | inline ymdhmsn::ymdhmsn() 323 | { 324 | 325 | } 326 | 327 | inline ymdhmsn::ymdhmsn(date const date) 328 | { 329 | int64_t xnsec = date.msec; 330 | 331 | // Find the nearest integer jd. 332 | int jd = xnsec / (1e3 * 60.0 * 60.0 * 24.0) + EPOCH_JD + 0.5; 333 | _julian_day_to_ymd(jd, year, month, day_of_month); 334 | 335 | /* in this order we avoid a lot of modulo */ 336 | xnsec %= (1000LL * 60LL * 60LL * 24LL); 337 | hour = xnsec / (1000LL * 60LL * 60LL); 338 | xnsec -= hour * (1000LL * 60LL * 60LL); 339 | min = xnsec / (1000LL * 60LL); 340 | xnsec -= min * (1000LL * 60LL); 341 | sec = xnsec / (1000LL); 342 | xnsec -= sec * (1000LL); 343 | msec = (int)xnsec; 344 | 345 | } 346 | 347 | inline ymdhmsn::operator std::string() const 348 | { 349 | std::array buf; 350 | snprintf(&buf[0], buf.size(), "%04d-%02d-%02dT%02d:%02d:%02d.%03d", year, month, day_of_month, hour, min, sec, msec); 351 | return std::string{&buf[0]}; 352 | } 353 | 354 | inline ydoyh::ydoyh() 355 | { 356 | 357 | } 358 | 359 | inline ydoyh::ydoyh(ymdh const & p_ymdh) 360 | { 361 | double K; 362 | 363 | K = 2 - _date_leapyear(p_ymdh.year); 364 | this->year = p_ymdh.year; 365 | this->hour = p_ymdh.hour; 366 | this->day_of_year = (short) floor(275.0 * ((double) p_ymdh.month) / 9.0) 367 | - K * ((double) (p_ymdh.month > 2)) + ((double) p_ymdh.day_of_month) 368 | - 30.0; 369 | } 370 | 371 | 372 | } 373 | 374 | #endif /* SG2_DATE_HXX_ */ 375 | -------------------------------------------------------------------------------- /src/sg2_err.hxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2021 MINES ParisTech 3 | * Copyright 2011 Philippe Blanc 4 | * Copyright 2014-2021 Benoit Gschwind 5 | * 6 | * This file is part of libsg2. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the Lesser GNU General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (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 | * Lesser GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the Lesser GNU General Public 19 | * License along with this program. If not, see 20 | * . 21 | */ 22 | 23 | #ifndef SG2_ERR_HXX_ 24 | #define SG2_ERR_HXX_ 25 | 26 | namespace sg2 { 27 | 28 | static int const ERR_DATE_JD_SET_JD_TT_OUTOFPERIOD = -401; 29 | 30 | static int const ERR_HELIOCENTRIC_SET_HELIOC_JDTTNOTSET = -510; 31 | static int const ERR_HELIOCENTRIC_SET_HELIOC_OUTOFPERIOD = -511; 32 | 33 | static int const ERR_GEOCENTRIC_SET_GEOC_JDTTNOTSET = -610; 34 | static int const ERR_GEOCENTRIC_SET_GEOC_OUTOFPERIOD = -611; 35 | 36 | static int const ERR_TOPOCENTRIC_CORRECTION_REFRACTION_METHOD = -720; 37 | 38 | static int const ERR_SET_TOA_IRRAD_NTSNOTVALID = -920; 39 | 40 | } 41 | // namespace sg2 42 | 43 | #endif /* SG2_ERR_HXX_ */ 44 | -------------------------------------------------------------------------------- /src/sg2_geocentric.cxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2021 MINES ParisTech 3 | * Copyright 2011 Philippe Blanc 4 | * Copyright 2014-2021 Benoit Gschwind 5 | * 6 | * This file is part of libsg2. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the Lesser GNU General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (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 | * Lesser GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the Lesser GNU General Public 19 | * License along with this program. If not, see 20 | * . 21 | */ 22 | 23 | #include "sg2_geocentric.hxx" 24 | #include "sg2_err.hxx" 25 | #include "sg2_data_handler.hxx" 26 | #include "sg2_utils.hxx" 27 | 28 | #include 29 | 30 | namespace sg2 { 31 | 32 | static std::tuple _heliocentric_compute_R_and_L(date const tt) noexcept 33 | { 34 | int64_t x = (tt.msec - _geocentric_data_offset()*1000) / (_geocentric_data_delta()*1000); 35 | int64_t dx = (tt.msec - _geocentric_data_offset()*1000) % (_geocentric_data_delta()*1000); 36 | 37 | if ((x < 0) || (x > _geocentric_data_count() - 1)) { 38 | return std::make_tuple(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); 39 | } 40 | 41 | double alpha = static_cast(dx) 42 | /static_cast(_geocentric_data_delta()*1000); 43 | 44 | double sinL = std::fma(alpha,(_geocentric_data_sinL(x+1)-_geocentric_data_sinL(x)),_geocentric_data_sinL(x)); 45 | double cosL = std::fma(alpha,(_geocentric_data_cosL(x+1)-_geocentric_data_cosL(x)),_geocentric_data_cosL(x)); 46 | double R = std::fma(alpha,(_geocentric_data_R (x+1)-_geocentric_data_R (x)),_geocentric_data_R (x)); 47 | 48 | return std::make_tuple(R, math::atan2(sinL, cosL)); 49 | } 50 | 51 | geocentric_data::geocentric_data(date const & ut) noexcept : 52 | ut{ut} 53 | { 54 | if (ut.isnat()) { 55 | tt = nat; 56 | } else { 57 | ymdh d{ut}; 58 | date d0{ymdh(d.year, 1, 1, 0.0)}; 59 | double year = d.year+(ut.msec - d0.msec)/((_date_leapyear(d.year)?366.0:365.0)*24*60*60*1e3); 60 | int64_t delta_tt = approx_deltat_msc.compute(year); 61 | if (delta_tt == numeric_limits::min()) { 62 | tt.msec = numeric_limits::min(); 63 | } else { 64 | tt.msec = ut.msec + delta_tt; 65 | } 66 | } 67 | _init_all(); 68 | } 69 | 70 | geocentric_data::geocentric_data(date const & ut, date const & tt) noexcept : 71 | ut{tt.isnat()?nat:ut}, 72 | tt{ut.isnat()?nat:tt} 73 | { 74 | _init_all(); 75 | } 76 | 77 | void geocentric_data::_init_all() noexcept 78 | { 79 | short idx0; 80 | int kd; 81 | double sin_Theta_a_kd, cos_epsilon_kd; 82 | double nu0_kd, Delta_psi_cos_epsilon_kd, M_kd; 83 | 84 | if (ut.isnat()) { 85 | _nan_all(); 86 | return; 87 | } 88 | 89 | std::tie(R, L) = _heliocentric_compute_R_and_L(tt); 90 | 91 | if (!std::isfinite(R) || !std::isfinite(L)) { 92 | _nan_all(); 93 | return; 94 | } 95 | 96 | Delta_psi = approx_Dpsi.compute(julian{tt}.value); 97 | epsilon = approx_epsilon.compute(julian{tt}.value); 98 | 99 | Theta_a = L + PI + Delta_psi 100 | + Delta_tau; 101 | 102 | sin_Theta_a_kd = math::sin(Theta_a); 103 | cos_epsilon_kd = math::cos(epsilon); 104 | 105 | r_alpha = math::atan2(sin_Theta_a_kd * cos_epsilon_kd, 106 | math::cos(Theta_a)); 107 | delta = math::asin(sin_Theta_a_kd * math::sin(epsilon)); 108 | 109 | // The compiler look smart enough to merge conversions to jd with computation. 110 | nu0_kd = approx_nu0.compute(julian{ut}.value); 111 | 112 | Delta_psi_cos_epsilon_kd = Delta_psi * cos_epsilon_kd; 113 | M_kd = approx_M_0.compute(julian{tt}.value); 114 | 115 | nu = nu0_kd + Delta_psi_cos_epsilon_kd; 116 | EOT = M_kd - 0.0001 - r_alpha + Delta_psi_cos_epsilon_kd; 117 | } 118 | 119 | void geocentric_data::_nan_all() noexcept 120 | { 121 | R = std::numeric_limits::quiet_NaN(); 122 | L = std::numeric_limits::quiet_NaN(); 123 | Delta_psi = std::numeric_limits::quiet_NaN(); 124 | epsilon = std::numeric_limits::quiet_NaN(); 125 | Theta_a = std::numeric_limits::quiet_NaN(); 126 | r_alpha = std::numeric_limits::quiet_NaN(); 127 | delta = std::numeric_limits::quiet_NaN(); 128 | nu = std::numeric_limits::quiet_NaN(); 129 | EOT = std::numeric_limits::quiet_NaN(); 130 | } 131 | 132 | } // namespace sg2 133 | -------------------------------------------------------------------------------- /src/sg2_geocentric.hxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2021 MINES ParisTech 3 | * Copyright 2011 Philippe Blanc 4 | * Copyright 2014-2021 Benoit Gschwind 5 | * 6 | * This file is part of libsg2. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the Lesser GNU General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (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 | * Lesser GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the Lesser GNU General Public 19 | * License along with this program. If not, see 20 | * . 21 | */ 22 | 23 | #ifndef SG2_GEOCENTRIC_HXX_ 24 | #define SG2_GEOCENTRIC_HXX_ 25 | 26 | #include "sg2_typedef.hxx" 27 | #include "sg2_date.hxx" 28 | #include "sg2_err.hxx" 29 | 30 | #include "sg2_math.hxx" 31 | 32 | namespace sg2 { 33 | 34 | /* Geocentric coordinates */ 35 | struct geocentric_data { 36 | date ut; /* Universal Time */ 37 | date tt; /* Terrestrial Time */ 38 | double R; /* Radius Sun-Earth (ua) */ 39 | double L; /* Heliocentric Earth true longitude (rad) */ 40 | double Delta_psi; /* Nutation in Geocentric Sun longitude (rad) */ 41 | double epsilon; /* Earth true obliquity (rad) */ 42 | double Theta_a; /* Geocentric Earth true longitude (rad) */ 43 | double r_alpha; /* Geocentric right ascension (rad) */ 44 | double delta; /* Geocentric declination (rad) */ 45 | double nu; /* Apparent sideral time (rad) */ 46 | double EOT; /* Equation of Time (rad) : difference between apparent solar time and mean solar time */ 47 | 48 | geocentric_data() = default; 49 | explicit geocentric_data(date const & ut) noexcept; 50 | geocentric_data(date const & ut, date const & tt) noexcept; 51 | 52 | geocentric_data(geocentric_data const &) = default; 53 | 54 | auto operator=(geocentric_data const &) -> geocentric_data & = default; 55 | 56 | void _init_all() noexcept; 57 | void _nan_all() noexcept; 58 | 59 | double get_sun_earth_radius() const noexcept { 60 | return R; 61 | } 62 | 63 | double get_heliocentric_true_longitude() const noexcept { 64 | return L; 65 | } 66 | 67 | double get_delta_psi() const noexcept { 68 | return Delta_psi; 69 | } 70 | 71 | double get_earth_true_obliquity() const noexcept { 72 | return epsilon; 73 | } 74 | 75 | double get_geocentric_true_longitude() const noexcept { 76 | return Theta_a; 77 | } 78 | 79 | double get_geocentric_right_ascension() const noexcept { 80 | return r_alpha; 81 | } 82 | 83 | double get_geocentric_declination() const noexcept { 84 | return delta; 85 | } 86 | 87 | double get_apparent_sideral_time() const noexcept { 88 | return nu; 89 | } 90 | 91 | double get_mst_solar_time_difference() const noexcept { 92 | return EOT; 93 | } 94 | 95 | }; 96 | 97 | 98 | 99 | } // namespace sg2 100 | 101 | #endif /* SG2_GEOCENTRIC_HXX_ */ 102 | -------------------------------------------------------------------------------- /src/sg2_geopoint.cxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2021 MINES ParisTech 3 | * Copyright 2011 Philippe Blanc 4 | * Copyright 2014-2021 Benoit Gschwind 5 | * 6 | * This file is part of libsg2. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the Lesser GNU General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (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 | * Lesser GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the Lesser GNU General Public 19 | * License along with this program. If not, see 20 | * . 21 | */ 22 | 23 | #include "sg2_geopoint.hxx" 24 | 25 | namespace sg2 { 26 | 27 | ellps const ELLPS_WGS84 = { 6378137.0, 3.352810664747481e-003 }; /* WGS84 */ 28 | ellps const ELLPS_RGF83 = { 6378137.0, 3.352810681182319e-003 }; /* RFG83 */ 29 | ellps const ELLPS_NTF = { 6378249.2, 3.407549520015651e-003 }; /* NTF / CLARKE1880 */ 30 | ellps const ELLPS_AA = { 6378136.6, 3.352819697896193e-003 }; /* AA */ 31 | ellps const ELLPS_SPA = { 6378140.0, 3.352810000000000e-003 }; /* SPA */ 32 | ellps const ELLPS_NGP = { 6378169.0, 3.384231430681783e-003 }; /* NGP*/ 33 | ellps const ELLPS_SPHERE = { 6378130.0, 0.0 }; /* SPHERE */ 34 | 35 | 36 | } // namespace sg2 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/sg2_geopoint.hxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2021 MINES ParisTech 3 | * Copyright 2011 Philippe Blanc 4 | * Copyright 2014-2021 Benoit Gschwind 5 | * 6 | * This file is part of libsg2. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the Lesser GNU General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (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 | * Lesser GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the Lesser GNU General Public 19 | * License along with this program. If not, see 20 | * . 21 | */ 22 | 23 | #ifndef SRC_SG2_GEOPOINT_HXX_ 24 | #define SRC_SG2_GEOPOINT_HXX_ 25 | 26 | #include "sg2_typedef.hxx" 27 | #include "sg2_math.hxx" 28 | 29 | namespace sg2 { 30 | 31 | extern ellps const ELLPS_WGS84; 32 | extern ellps const ELLPS_RGF83; 33 | extern ellps const ELLPS_NTF; 34 | extern ellps const ELLPS_AA; 35 | extern ellps const ELLPS_SPA; 36 | extern ellps const ELLPS_NGP; 37 | extern ellps const ELLPS_SPHERE; 38 | 39 | struct ellps { 40 | double a; /* Axis a (m) */ 41 | double f; /* Flatness (-)*/ 42 | 43 | ellps() = default; 44 | ellps(ellps const &) = default; 45 | ellps(double a, double f); 46 | 47 | }; 48 | 49 | struct geopoint { 50 | ellps ellipse; 51 | double phi; /* Latitude (rad) */ 52 | double lambda; /* Longitude (rad) */ 53 | double h; /* Altitude Above the Reference Ellipsoid */ 54 | double u; ///< phi geocentric 55 | double x; 56 | double y; 57 | 58 | double cos_phi_kp; ///< Usefull cache 59 | double sin_phi_kp; ///< Usefull cache 60 | 61 | geopoint() = default; 62 | geopoint(geopoint const &) = default; 63 | 64 | /** 65 | * @input lat: latitude in degrees 66 | * @input lon: longitude in degrees 67 | * @input h: altitude in meters 68 | **/ 69 | geopoint(double lon, double lat, double h, ellps const & p_data_ellps = ELLPS_WGS84); 70 | 71 | auto operator=(geopoint const &) -> geopoint & = default; 72 | 73 | double const & get_phi() const { 74 | return phi; 75 | } 76 | 77 | double get_lambda() const { 78 | return lambda; 79 | } 80 | 81 | double get_u() const { 82 | return u; 83 | } 84 | 85 | double get_x() const { 86 | return x; 87 | } 88 | 89 | double get_y() const { 90 | return y; 91 | } 92 | 93 | }; 94 | 95 | inline ellps::ellps(double a, double f) : 96 | a{a}, 97 | f{f} 98 | { 99 | 100 | } 101 | 102 | inline geopoint::geopoint(double lon, double lat, double h, ellps const & ellipse) : 103 | ellipse{ellipse}, 104 | phi{RAD(lat)}, 105 | lambda{RAD(lon)}, 106 | h{h} 107 | { 108 | 109 | double a = ellipse.a; 110 | double app = 1.0 - ellipse.f; 111 | 112 | cos_phi_kp = math::cos(phi); 113 | sin_phi_kp = math::sin(phi); 114 | double tan_phi_kp = math::tan(phi); 115 | 116 | double h_a_kp = h / a; 117 | double u_kp = math::atan(app * tan_phi_kp); 118 | x = math::cos(u_kp) + h_a_kp * cos_phi_kp; 119 | y = app * math::sin(u_kp) + h_a_kp * sin_phi_kp; 120 | u = u_kp; 121 | 122 | } 123 | 124 | } // namespace sg2 125 | 126 | #endif /* SRC_SG2_GEOPOINT_HXX_ */ 127 | -------------------------------------------------------------------------------- /src/sg2_math.hxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2021 MINES ParisTech 3 | * Copyright 2011 Philippe Blanc 4 | * Copyright 2014-2021 Benoit Gschwind 5 | * 6 | * This file is part of libsg2. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the Lesser GNU General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (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 | * Lesser GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the Lesser GNU General Public 19 | * License along with this program. If not, see 20 | * . 21 | */ 22 | 23 | #ifndef SRC_SG2_MATH_HXX_ 24 | #define SRC_SG2_MATH_HXX_ 25 | 26 | #include 27 | 28 | namespace sg2 { 29 | 30 | template 31 | struct _ipow; 32 | 33 | template 34 | struct _ipow { 35 | static inline constexpr double get(double const x) { 36 | return _ipow::get(x)*x; 37 | } 38 | }; 39 | 40 | template 41 | struct _ipow { 42 | static inline constexpr double get(double const x) { 43 | return _ipow::get(x)*_ipow::get(x); 44 | } 45 | }; 46 | 47 | template<> 48 | struct _ipow<2, false> { 49 | static inline constexpr double get(double const x) { 50 | return x*x; 51 | } 52 | }; 53 | 54 | 55 | template<> 56 | struct _ipow<1, true> { 57 | static inline constexpr double get(double const x) { 58 | return x; 59 | } 60 | }; 61 | 62 | 63 | template 64 | inline constexpr double ipow(double const x) 65 | { 66 | return _ipow::get(x); 67 | } 68 | 69 | template 70 | struct _polyval { 71 | static inline constexpr double get(double x, std::tuple const & t) 72 | { 73 | return x*_polyval::get(x, t)+std::get(t); 74 | } 75 | }; 76 | 77 | template 78 | struct _polyval<0, ARGS...> { 79 | static inline constexpr double get(double x, std::tuple const & t) 80 | { 81 | return std::get<0>(t); 82 | } 83 | }; 84 | 85 | 86 | template 87 | inline constexpr double polyval(double x, ARGS ... args) 88 | { 89 | return _polyval::get(x, std::tuple{args...}); 90 | } 91 | 92 | } // namespace sg2 93 | 94 | #ifndef SG2_HAVE_VDT 95 | 96 | #include 97 | 98 | namespace sg2 { 99 | 100 | namespace math { 101 | 102 | inline static double sin(double x) { 103 | return std::sin(x); 104 | } 105 | 106 | inline static double cos(double x) { 107 | return std::cos(x); 108 | } 109 | 110 | inline static double tan(double x) { 111 | return std::tan(x); 112 | } 113 | 114 | inline static void sincos(double x, double & s, double & c) { 115 | s = std::sin(x); 116 | c = std::cos(x); 117 | } 118 | 119 | inline static double atan2(double x, double y) { 120 | return std::atan2(x, y); 121 | } 122 | 123 | inline static double asin(double x) { 124 | return std::asin(x); 125 | } 126 | 127 | inline static double atan(double x) { 128 | return std::atan(x); 129 | } 130 | 131 | } 132 | 133 | } 134 | 135 | #else 136 | 137 | #include 138 | 139 | namespace sg2 { 140 | 141 | namespace math { 142 | 143 | inline static double sin(double x) { 144 | return vdt::fast_sin(x); 145 | } 146 | 147 | inline static double cos(double x) { 148 | return vdt::fast_cos(x); 149 | } 150 | 151 | inline static double tan(double x) { 152 | return vdt::fast_tan(x); 153 | } 154 | 155 | inline static void sincos(double x, double & s, double & c) { 156 | vdt::fast_sincos(x, s, c); 157 | } 158 | 159 | inline static double atan2(double x, double y) { 160 | return vdt::fast_atan2(x, y); 161 | } 162 | 163 | inline static double asin(double x) { 164 | return vdt::fast_asin(x); 165 | } 166 | 167 | inline static double atan(double x) { 168 | return vdt::fast_atan(x); 169 | } 170 | 171 | } 172 | 173 | } 174 | 175 | #endif 176 | 177 | 178 | #endif /* SRC_SG2_MATH_HXX_ */ 179 | -------------------------------------------------------------------------------- /src/sg2_query.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 23 | #include 24 | #include 25 | 26 | void usage(void) { 27 | printf("usage: test_sg2 \n"); 28 | } 29 | 30 | int main(int argc, char ** argv) { 31 | 32 | if(argc != 5) { 33 | usage(); 34 | return -1; 35 | } 36 | 37 | double lat = atof(argv[1]); 38 | double lon = atof(argv[2]); 39 | double alt = atof(argv[3]); 40 | double jd = atof(argv[4]); 41 | 42 | sg2::julian xjd{jd}; 43 | 44 | /** location related data **/ 45 | sg2::geopoint geopoint{lon, lat, alt, sg2::ELLPS_WGS84}; 46 | 47 | /** time related data **/ 48 | sg2::geocentric_data geoc{sg2::date{xjd.value}}; 49 | 50 | /** local-time related data **/ 51 | sg2::topocentric_data topoc{geoc, geopoint}; 52 | 53 | /** 54 | * Computing solar system state. 55 | **/ 56 | 57 | auto sunrise = sg2::sunrise(geoc.ut, geopoint); 58 | 59 | printf("Time related data (i.e. solar system geometry)\n"); 60 | printf("geoc.ut = %s\n", static_cast(geoc.ut).c_str()); 61 | printf("geoc.tt = %s\n", static_cast(geoc.tt).c_str()); 62 | 63 | printf("geoc.R = %f\n", geoc.R); 64 | printf("geoc.L = %f\n", geoc.L); 65 | 66 | printf("geoc.delta = %f\n", geoc.delta); 67 | printf("geoc.EOT = %f\n", geoc.EOT); 68 | printf("geoc.Theta_a = %f\n", geoc.Theta_a); 69 | printf("geoc.epsilon = %f\n", geoc.epsilon); 70 | printf("geoc.nu = %f\n", geoc.nu); 71 | printf("geoc.r_alpha = %f\n", geoc.r_alpha); 72 | printf("geoc.Delta_psi = %f\n", geoc.Delta_psi); 73 | 74 | printf("Location related data\n"); 75 | 76 | printf("geopoint.lambda = %f\n", geopoint.lambda); 77 | printf("geopoint.phi = %f\n", geopoint.phi); 78 | printf("geopoint.u = %f\n", geopoint.u); 79 | printf("geopoint.x = %f\n", geopoint.x); 80 | printf("geopoint.y = %f\n", geopoint.y); 81 | 82 | printf("Sun related data\n"); 83 | 84 | printf("topoc.delta = %f\n", topoc.delta); 85 | printf("topoc.alpha_S = %f\n", topoc.alpha_S); 86 | printf("topoc.gamma_S0 = %f\n", topoc.gamma_S0); 87 | printf("topoc.omega = %f\n", topoc.omega); 88 | printf("topoc.r_alpha = %f\n", topoc.r_alpha); 89 | printf("topoc.toa_hi = %f\n", topoc.toa_hi); 90 | printf("topoc.toa_ni = %f\n", topoc.toa_ni); 91 | 92 | printf("Extra data\n"); 93 | double x = (xjd.value + (lon/360.0) - (topoc.omega/(M_PI*2.0))); 94 | printf("tst-tu = %f\n", (x-floor(x+0.5))); 95 | 96 | printf("sun_rise = %s\n", static_cast(std::get<0>(sunrise)).c_str()); 97 | printf("sun_transite = %s\n", static_cast(std::get<1>(sunrise)).c_str()); 98 | printf("sun_set = %s\n", static_cast(std::get<2>(sunrise)).c_str()); 99 | 100 | return 0; 101 | 102 | } 103 | 104 | 105 | -------------------------------------------------------------------------------- /src/sg2_topocentric.cxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2021 MINES ParisTech 3 | * Copyright 2011 Philippe Blanc 4 | * Copyright 2014-2021 Benoit Gschwind 5 | * 6 | * This file is part of libsg2. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the Lesser GNU General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (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 | * Lesser GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the Lesser GNU General Public 19 | * License along with this program. If not, see 20 | * . 21 | */ 22 | 23 | 24 | #include "sg2_topocentric.hxx" 25 | 26 | namespace sg2 { 27 | 28 | 29 | } // namespace sg2 30 | -------------------------------------------------------------------------------- /src/sg2_topocentric.hxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2021 MINES ParisTech 3 | * Copyright 2011 Philippe Blanc 4 | * Copyright 2014-2021 Benoit Gschwind 5 | * 6 | * This file is part of libsg2. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the Lesser GNU General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (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 | * Lesser GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the Lesser GNU General Public 19 | * License along with this program. If not, see 20 | * . 21 | */ 22 | 23 | #ifndef SG2_TOPOCENTRIC_HXX_ 24 | #define SG2_TOPOCENTRIC_HXX_ 25 | 26 | #include "sg2_typedef.hxx" 27 | #include "sg2_geocentric.hxx" 28 | #include "sg2_geopoint.hxx" 29 | #include "sg2_math.hxx" 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | namespace sg2 { 37 | 38 | enum correction_refraction_e { 39 | REFRACTION_NONE = 0, 40 | REFRACTION_SAE = 1, 41 | REFRACTION_ZIM = 2 42 | }; 43 | 44 | struct topocentric_data { 45 | double r_alpha; /* Topocentric right sun ascension (rad) */ 46 | double delta; /* Topocentric sun declination (rad) */ 47 | double omega; /* Topocentric local hour angle (rad) */ 48 | double gamma_S0; /* Topocentric sun elevation angle without correction of atm. corr. (rad)*/ 49 | double alpha_S; /* Topocentric sun azimuth (rad) */ 50 | double toa_ni; /* irradiation at top of atmosphere normal incidence (W/m2) */ 51 | double toa_hi; /* irradiation at top of atmosphere horizontal incidence (W/m2) */ 52 | 53 | topocentric_data() = default; 54 | topocentric_data(topocentric_data const &) = default; 55 | topocentric_data(geocentric_data const & geoc, geopoint const & gp); 56 | 57 | auto operator=(topocentric_data const &) -> topocentric_data & = default; 58 | 59 | double topocentric_correction_refraction(double const P, double const T, correction_refraction_e method = REFRACTION_NONE) const; 60 | 61 | /* Topocentric right sun ascension (rad) */ 62 | double get_right_sun_ascension() const { 63 | return r_alpha; 64 | } 65 | 66 | /* Topocentric sun declination (rad) */ 67 | double get_sun_declination() const { 68 | return delta; 69 | } 70 | 71 | /* Topocentric local hour angle (rad) */ 72 | double get_omega() const { 73 | return omega; 74 | } 75 | 76 | /* Topocentric sun elevation angle without correction of atm. corr. (rad)*/ 77 | double get_geometric_sun_elevation() const { 78 | return gamma_S0; 79 | } 80 | 81 | /* Topocentric sun azimuth (rad) */ 82 | double get_geometric_azimuth() const { 83 | return alpha_S; 84 | } 85 | 86 | /* irradiation at top of atmosphere normal incidence (W/m2) */ 87 | double get_top_of_atmosphere_normal_incidence() const { 88 | return toa_ni; 89 | } 90 | 91 | /* irradiation at top of atmosphere horizontal incidence (W/m2) */ 92 | double get_top_of_atmosphere_horizontal_incidence() const { 93 | return toa_hi; 94 | } 95 | 96 | }; 97 | 98 | 99 | inline double topocentric_correction_refraction_SAE(double const gamma_S0, double const P, double const T) 100 | { 101 | double const gamma_S0_seuil = -0.010035643198967; 102 | double const R = 0.029614018235657; 103 | /*(tan(gamma_S0_seuil + 0.0031376 / (gamma_S0_seuil+ 0.089186))) */ 104 | double K; 105 | 106 | K = (P / 1010.0) * (283. / (273. + T)) * 2.96706e-4; 107 | 108 | if (gamma_S0 > gamma_S0_seuil) { 109 | return gamma_S0 + K / (math::tan(gamma_S0 + 0.0031376 / (gamma_S0 + 0.089186))); 110 | } else { 111 | return gamma_S0 + (K / R) * math::tan(gamma_S0_seuil) / math::tan(gamma_S0); 112 | } 113 | 114 | } 115 | 116 | 117 | inline double topocentric_correction_refraction_ZIM(double const gamma_S0, double const P, double const T) 118 | { 119 | double K; 120 | double tan_gamma_S0 = math::tan(gamma_S0); 121 | 122 | K = (P / 1013.0) * (283. / (273. + T)) * 4.848136811095360e-006; 123 | 124 | if (gamma_S0 <= -0.010036) { 125 | return gamma_S0 + K * (-20.774 / tan_gamma_S0); 126 | } else if (gamma_S0 <= 0.087266) { 127 | return gamma_S0 + K * polyval(gamma_S0, 7.66231727e6, -2.405683e6, 3.394422e5, -2.969067e4, 1735.0); 128 | } else if (gamma_S0 <= 1.483529864195180) { 129 | return gamma_S0 + K * (58.1 / tan_gamma_S0 - 0.07 / ipow<3>(tan_gamma_S0) + 0.000086 / ipow<5>(tan_gamma_S0)); 130 | } else { 131 | return NAN; 132 | } 133 | 134 | } 135 | 136 | inline double topocentric_data::topocentric_correction_refraction(double const P, 137 | double const T, correction_refraction_e method) const 138 | { 139 | switch (method) { 140 | case REFRACTION_SAE: 141 | return topocentric_correction_refraction_SAE(gamma_S0, P, T); 142 | break; 143 | case REFRACTION_ZIM: 144 | return topocentric_correction_refraction_ZIM(gamma_S0, P, T); 145 | break; 146 | case REFRACTION_NONE: 147 | return gamma_S0; 148 | break; 149 | default: 150 | return NAN; 151 | break; 152 | } 153 | } 154 | 155 | inline topocentric_data::topocentric_data(geocentric_data const & geoc, geopoint const & gp) 156 | { 157 | 158 | if (!std::isfinite(geoc.R)) { 159 | r_alpha = std::numeric_limits::quiet_NaN(); 160 | delta = std::numeric_limits::quiet_NaN(); 161 | omega = std::numeric_limits::quiet_NaN(); 162 | gamma_S0 = std::numeric_limits::quiet_NaN(); 163 | alpha_S = std::numeric_limits::quiet_NaN(); 164 | toa_ni = std::numeric_limits::quiet_NaN(); 165 | toa_hi = std::numeric_limits::quiet_NaN(); 166 | return; 167 | } 168 | 169 | double xi = (gp.ellipse.a / AU); 170 | 171 | double omega_g_kp_kd = geoc.nu - geoc.r_alpha + gp.lambda; 172 | double cos_geoc_delta_kd = math::cos(geoc.delta); 173 | 174 | double Delta_r_alpha_kp_kd = (-gp.x * math::sin(omega_g_kp_kd) 175 | / cos_geoc_delta_kd * xi); 176 | r_alpha = geoc.r_alpha + Delta_r_alpha_kp_kd; 177 | 178 | delta = geoc.delta + (gp.x * math::cos(omega_g_kp_kd) * math::sin(geoc.delta) 179 | - gp.y * cos_geoc_delta_kd) * xi; 180 | 181 | omega = omega_g_kp_kd - Delta_r_alpha_kp_kd; 182 | 183 | double cos_omega_kp_kd = math::cos(omega); 184 | double cos_delta_kp_kd = math::cos(delta); 185 | double sin_delta_kp_kd = math::sin(delta); 186 | double tan_delta_kp_kd = sin_delta_kp_kd / cos_delta_kp_kd; 187 | 188 | gamma_S0 = math::asin(gp.sin_phi_kp * sin_delta_kp_kd 189 | + gp.cos_phi_kp * cos_delta_kp_kd * cos_omega_kp_kd); 190 | 191 | alpha_S = math::atan2(math::sin(omega), cos_omega_kp_kd * gp.sin_phi_kp - tan_delta_kp_kd * gp.cos_phi_kp) 192 | + PI; 193 | 194 | if (gamma_S0 > 0.0) { 195 | toa_ni = SOLAR_CONSTANT / (geoc.R * geoc.R); 196 | toa_hi = toa_ni * math::sin(gamma_S0); 197 | } else { 198 | toa_ni = 0.0; 199 | toa_hi = 0.0; 200 | } 201 | } 202 | 203 | } // namespace sg2 204 | 205 | #endif /* SG2_TOPOCENTRIC_HXX_ */ 206 | -------------------------------------------------------------------------------- /src/sg2_typedef.hxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2021 MINES ParisTech 3 | * Copyright 2011 Philippe Blanc 4 | * Copyright 2014-2021 Benoit Gschwind 5 | * 6 | * This file is part of libsg2. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the Lesser GNU General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (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 | * Lesser GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the Lesser GNU General Public 19 | * License along with this program. If not, see 20 | * . 21 | */ 22 | 23 | #ifndef SG2_TYPEDEF_HXX_ 24 | #define SG2_TYPEDEF_HXX_ 25 | 26 | #include 27 | 28 | #include 29 | 30 | namespace sg2 { 31 | 32 | using namespace std; 33 | 34 | static constexpr double const PI = 3.1415926535897932384626433832795028841971; 35 | static constexpr double const D_PI = PI*2.0; 36 | 37 | constexpr inline double DEG(double RAD) { 38 | return RAD*180.0/PI; 39 | } 40 | 41 | constexpr inline double RAD(double DEG) { 42 | return DEG*PI/180.0; 43 | } 44 | 45 | /* Top of atmosphere broadband irradiance on normal incidence (W/m2) */ 46 | static constexpr double const SOLAR_CONSTANT = 1362.0; 47 | /* Astronomical unit : annual average of the Sun-Earth distance (m) */ 48 | static constexpr double const AU = 149597870691.0; /* +/- 6 m (McCarthy et Petit, 2003) */ 49 | 50 | inline double CLAMP_0_2PI(double angle) { return ((angle)-std::floor((angle)/(D_PI))*(D_PI)); } 51 | inline double CLAMP_PI_PI(double angle) { return ((angle)-std::round((angle)/(D_PI))*(D_PI)); } 52 | 53 | struct date; 54 | struct julian; 55 | struct ymdh; 56 | struct ydoyh; 57 | struct ellps; 58 | 59 | struct geopoint; 60 | struct geocentric_data; 61 | struct topocentric_data; 62 | 63 | } // namespace sg2 64 | 65 | #endif /* SG2_TYPEDEF_HXX_ */ 66 | -------------------------------------------------------------------------------- /src/sg2_utils.cxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 MINES ParisTech 3 | * Copyright 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 "sg2_utils.hxx" 23 | 24 | 25 | namespace sg2 { 26 | 27 | sinusoidal_approx<1> approx_Dpsi = { 28 | 2444239.5, 0.0, 0.0, 29 | { 2.0*PI/6791.164405 }, 30 | { 8.329092e-5 }, 31 | { -2.052757 } 32 | }; 33 | 34 | 35 | sinusoidal_approx<1> approx_epsilon = { 36 | 2444239.5, -6.216374e-9, 4.091383e-1, 37 | { 2.0*PI/6791.164405 }, 38 | { 4.456183e-5 }, 39 | { 2.660352 } 40 | }; 41 | 42 | sinusoidal_approx<0> approx_nu0 = { 43 | 2444239.5, 6.300388099, 1.742079140, 44 | { }, 45 | { }, 46 | { } 47 | }; 48 | 49 | sinusoidal_approx<0> approx_M_0 = { 50 | 2444239.5, 1.0/58.130099904, -1.399410798, 51 | { }, 52 | { }, 53 | { } 54 | }; 55 | 56 | polynomial_approx<5, 6> approx_deltat_msc = { 57 | { 58 | {{1941,1961}, 1950, {0.0, 0.0, 1.0/2547.0, -1.0/233.0, 0.407, 29.07}}, 59 | {{1961,1986}, 1975, {0.0, 0.0, -1.0/718.0, -1.0/260.0, 1.067, 45.45}}, 60 | {{1986,2005}, 2000, {0.00002373599, 0.000651814, 0.0017275, -0.060374, 0.3345, 63.86}}, 61 | {{2005,2050}, 2000, {0.0, 0.0, 0.0, 0.005576068, 0.32100612, 62.8938127}}, 62 | {{2050,2150}, 2000, {0.0, 0.0, 0.0, 1.0/312.5, 1.7148, -0.74-0.1-0.01}} 63 | }, 64 | }; 65 | 66 | 67 | } // namepsace sg2 68 | 69 | -------------------------------------------------------------------------------- /src/sg2_utils.hxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 MINES ParisTech 3 | * Copyright 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 | #ifndef SRC_SG2_UTILS_HXX_ 23 | #define SRC_SG2_UTILS_HXX_ 24 | 25 | #include "sg2_typedef.hxx" 26 | #include "sg2_math.hxx" 27 | 28 | #include 29 | 30 | namespace sg2 { 31 | 32 | template 33 | struct sinusoidal_approx { 34 | double j0; 35 | double a; 36 | double b; 37 | array a0; 38 | array ro; 39 | array phi; 40 | 41 | double compute(double jd) const { 42 | double jc = jd - j0; 43 | double s = a*jc+b; 44 | for (int kf = 0; kf < N; ++kf) { 45 | s += ro[kf]*math::cos(a0[kf]*jc-phi[kf]); 46 | } 47 | return s; 48 | } 49 | 50 | }; 51 | 52 | template 53 | struct polynomial_approx { 54 | struct { 55 | int year_interval[2]; 56 | int year_offset; 57 | double poly_coeff[S]; 58 | } data[nP]; 59 | 60 | int64_t compute(double y) const { 61 | if (y < data[0].year_interval[0]) 62 | return numeric_limits::min(); 63 | 64 | int i; 65 | for (i = 0;; ++i) { 66 | if (i >= nP) 67 | return numeric_limits::min(); 68 | if (y(s*1e3); 79 | } 80 | 81 | }; 82 | 83 | extern sinusoidal_approx<1> approx_Dpsi; 84 | extern sinusoidal_approx<1> approx_epsilon; 85 | extern sinusoidal_approx<0> approx_nu0; 86 | extern sinusoidal_approx<0> approx_M_0; 87 | extern polynomial_approx<5, 6> approx_deltat_msc; 88 | 89 | static double const Delta_tau = -9.9337353631981704e-005; 90 | 91 | } // namespace sg2 92 | 93 | 94 | #endif /* SRC_SG2_UTILS_HXX_ */ 95 | --------------------------------------------------------------------------------