├── .gitignore ├── LICENSE ├── README.md ├── cmd └── swerker │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── doc.go │ ├── handlers.c │ ├── handlers.h │ ├── msgpuck.c │ ├── msgpuck.h │ ├── swerker.c │ ├── sweversion.h │ ├── swex.c │ ├── swex.h │ ├── tr-stdio.c │ └── tr.h ├── const.go ├── planet_string.go ├── swecgo ├── examples_test.go ├── swecgo.go ├── swecgo_test.go ├── swecgolib.go ├── swecgowrap.go ├── swecl.c ├── swedate.c ├── swedate.h ├── swehel.c ├── swehouse.c ├── swehouse.h ├── swejpl.c ├── swejpl.h ├── swemmoon.c ├── swemplan.c ├── swemptab.h ├── swenut2000a.h ├── sweodef.h ├── sweph.c ├── sweph.h ├── swephexp.h ├── swephlib.c ├── swephlib.h ├── sweversion.h ├── swex.c └── swex.h ├── swego.go ├── swego_test.go ├── swerker ├── stdio │ ├── internal │ │ ├── lichdata │ │ │ ├── lichdata.go │ │ │ └── lichdata_test.go │ │ └── worker │ │ │ ├── funcs.go │ │ │ ├── funcs_gen.go │ │ │ ├── funcs_gen_test.go │ │ │ ├── funcs_test.go │ │ │ ├── mock_test.go │ │ │ ├── output.go │ │ │ ├── output_gen.go │ │ │ ├── output_gen_test.go │ │ │ ├── output_test.go │ │ │ ├── worker.go │ │ │ └── worker_test.go │ ├── stdio.go │ └── stdio_test.go ├── swerker.go ├── swerker_gen.go └── swerker_gen_test.go ├── swex ├── genversion.go ├── sweversion.h ├── swex.c └── swex.h └── swisseph ├── LICENSE ├── Makefile ├── swecl.c ├── swedate.c ├── swedate.h ├── swedll.h ├── swehel.c ├── swehouse.c ├── swehouse.h ├── swejpl.c ├── swejpl.h ├── swemmoon.c ├── swemplan.c ├── swemptab.h ├── swenut2000a.h ├── sweodef.h ├── swepcalc.c ├── swepcalc.h ├── swepdate.c ├── sweph.c ├── sweph.h ├── swephexp.h ├── swephlib.c ├── swephlib.h ├── swephprg.pdf ├── swetest.c └── swisseph.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 1997 - 2008 Astrodienst AG, Switzerland. All rights reserved. 2 | 3 | License conditions 4 | ------------------ 5 | 6 | This file is part of Swiss Ephemeris. 7 | 8 | Swiss Ephemeris is distributed with NO WARRANTY OF ANY KIND. No author 9 | or distributor accepts any responsibility for the consequences of using it, 10 | or for whether it serves any particular purpose or works at all, unless he 11 | or she says so in writing. 12 | 13 | Swiss Ephemeris is made available by its authors under a dual licensing 14 | system. The software developer, who uses any part of Swiss Ephemeris 15 | in his or her software, must choose between one of the two license models, 16 | which are 17 | a) GNU public license version 2 or later 18 | b) Swiss Ephemeris Professional License 19 | 20 | The choice must be made before the software developer distributes software 21 | containing parts of Swiss Ephemeris to others, and before any public 22 | service using the developed software is activated. 23 | 24 | If the developer choses the GNU GPL software license, he or she must fulfill 25 | the conditions of that license, which includes the obligation to place his 26 | or her whole software project under the GNU GPL or a compatible license. 27 | See http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 28 | 29 | If the developer choses the Swiss Ephemeris Professional license, 30 | he must follow the instructions as found in http://www.astro.com/swisseph/ 31 | and purchase the Swiss Ephemeris Professional Edition from Astrodienst 32 | and sign the corresponding license contract. 33 | 34 | The License grants you the right to use, copy, modify and redistribute 35 | Swiss Ephemeris, but only under certain conditions described in the License. 36 | Among other things, the License requires that the copyright notices and 37 | this notice be preserved on all copies. 38 | 39 | Authors of the Swiss Ephemeris: Dieter Koch and Alois Treindl 40 | 41 | The authors of Swiss Ephemeris have no control or influence over any of 42 | the derived works, i.e. over software or services created by other 43 | programmers which use Swiss Ephemeris functions. 44 | 45 | The names of the authors or of the copyright holder (Astrodienst) must not 46 | be used for promoting any software, product or service which uses or contains 47 | the Swiss Ephemeris. This copyright notice is the ONLY place where the 48 | names of the authors can legally appear, except in cases where they have 49 | given special permission in writing. 50 | 51 | The trademarks 'Swiss Ephemeris' and 'Swiss Ephemeris inside' may be used 52 | for promoting such software, products or services. 53 | */ 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swiss Ephemeris library for Go [![GoDoc](https://godoc.org/github.com/astrotools/swego?status.svg)](https://godoc.org/github.com/astrotools/swego) 2 | 3 | This repository contains multiple ways to interface with the Swiss Ephemeris. 4 | - `swecgo` interfaces with the C library via cgo. 5 | - `swerker` interfaces with the C library via a separate worker or workers. 6 | - `swerker-stdio` is a worker that runs as a subprocess. 7 | 8 | ## Pronunciation 9 | The name of this package is pronounced _swie-go_, like cgo: cee-go. 10 | -------------------------------------------------------------------------------- /cmd/swerker/.gitignore: -------------------------------------------------------------------------------- 1 | swerker-stdio 2 | *.lich 3 | -------------------------------------------------------------------------------- /cmd/swerker/Makefile: -------------------------------------------------------------------------------- 1 | # Compile first the Swiss Ephemeris as static library! 2 | # see ../../swisseph/Makefile 3 | 4 | CC = cc 5 | CFLAGS += -std=c99 -g -Wall -fPIC 6 | LDFLAGS += -Wl -dead_strip 7 | 8 | .o: 9 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $? -L../../swisseph -lswe -lm 10 | .c.o: 11 | $(CC) $(CFLAGS) -c -I../../swisseph $< 12 | 13 | swerker-stdio: swerker.o msgpuck.o handlers.o tr-stdio.o swex.o 14 | $(CC) $(CFLAGS) $(LDFLAGS) -L../../swisseph -lswe -lm \ 15 | -o swerker-stdio swerker.o msgpuck.o handlers.o tr-stdio.o swex.o 16 | 17 | clean: 18 | rm -f *.o swerker-stdio 19 | -------------------------------------------------------------------------------- /cmd/swerker/README.md: -------------------------------------------------------------------------------- 1 | # Swiss Ephemeris worker 2 | The Swiss Ephemeris is not safe for use from multiple threads. To use multiple 3 | processor cores, the Swiss Ephemeris is linked into a simple worker that can be 4 | parallelized by running multiple copies simultaneously. The worker act as a RPC 5 | server that can handle a single request at a time. 6 | 7 | ## Implemented library functions 8 | Most of the Swiss Ephemeris functions are implemented and exposed as functions 9 | that can be called as RPC. 10 | 11 | The following functions that are unsupported via RPC: 12 | - `swe_get_library_path` 13 | - `swe_set_astro_models` 14 | - `swe_get_astro_models` 15 | - `swe_set_interpolate_nut` 16 | - `swe_csnorm` 17 | - `swe_difcsn` 18 | - `swe_difcs2n` 19 | - `swe_cs2timestr` 20 | - `swe_cs2lonlatstr` 21 | - `swe_cs2degstr` 22 | 23 | Some Swiss Ephemeris functions depend on global state within the library. This 24 | state is modified via functions like `swe_set_jpl_file`. These functions are 25 | exposed as RPC functions and could be called as a _context call_. Each RPC call 26 | has a context with zero or more context calls. These context calls are executed 27 | before the actual library function is called. Clients may call these functions 28 | directly, however they are discouraged to do so as they may unable to 29 | predictably target a specific worker or a set of workers that will handle the 30 | call. 31 | 32 | The following functions modify the internal state of the library: 33 | - `swe_close` 34 | - `swe_set_ephe_path` 35 | - `swe_set_jpl_file` 36 | - `swe_set_topo` 37 | - `swe_set_sid_mode` 38 | - `swe_set_lapse_rate` 39 | - `swe_set_tid_acc` 40 | - `swe_set_delta_t_userdef` 41 | 42 | ## Wire format 43 | Requests and responses are serialized in the [MessagePack][msgpack] format. 44 | 45 | [msgpack]: https://msgpack.org/ 46 | 47 | ## Stateful functions in Swiss Ephemeris 48 | Some Swiss Ephemeris functions depend on global state within the library. This 49 | state is modified via functions like `swe_set_jpl_file`. These library 50 | functions are exposed as RPC functions. To bridge the gap between the Swiss 51 | Ephemeris and world of RPCs each RPC call has a context that allows to do 52 | _context calls_ that will modify the internal state. All context calls will be 53 | executed before the requested function that is called via RPC is executed. 54 | 55 | ## Worker implementation 56 | The RPC function handlers are internally defined as an array for quick 57 | dispatch. This means RPC functions are identified by the index in this array of 58 | handlers. The order of the handlers follows `swephexp.h` except for the 59 | heliacal functions, as they are defined after `swe_split_deg` followed by 60 | `swe_difdegn`. This means the index identifying a function is depended on the 61 | version of the library and is unstable. 62 | 63 | That said, there is an exception: the first entry in the handlers array (index 64 | 0). The first entry must always a RPC function called `rpc_funcs`. It returns 65 | an array that contains the name of each function in the internal array of 66 | handlers. This allows to create a mapping between function name and index in 67 | the handler array. 68 | 69 | ## RPC protocol 70 | ### Request 71 | A request is an array that contains three values: 72 | - Context: an `array` of zero or more context calls, may be `nil`. 73 | - Function: the function index encoded as an `uint8_t`. 74 | - Arguments: an `array` that matches the types of the library function, may be 75 | `nil` if there are no arguments. 76 | 77 | A context call is the same as request array except the `array` contains only a 78 | function and arguments, no context. 79 | 80 | A request that calls `swe_calc` with a couple of context calls looks like this: 81 | ``` 82 | 00000000 36 30 3c 93 93 92 0d 93 cb 40 14 77 77 8d d6 16 |60<......@.ww...| 83 | 00000010 f8 cb 40 4a 0a aa a7 de d6 bb 00 92 0e 93 01 00 |..@J............| 84 | 00000020 00 92 0b 91 a9 64 65 34 33 31 2e 65 70 68 04 93 |.....de431.eph..| 85 | 00000030 cb 41 42 72 8e d4 80 f1 2c 00 ce 00 01 81 01 3e |.ABr....,......>| 86 | ``` 87 | 88 | It corresponds to the following JSON: 89 | ```json 90 | [ 91 | [ 92 | [13, [5.116667, 52.083333, 0]], 93 | [14, [1, 0, 0]], 94 | [11, ["de431.eph"]] 95 | ], 96 | 4, 97 | [2417949.660185, 0, 98561] 98 | ] 99 | ``` 100 | 101 | And corresponds to the following C code: 102 | ```c 103 | swe_set_topo(5.116667, 52.083333, 0); 104 | swe_set_sid_mode(SE_SIDM_LAHIRI, 0, 0); 105 | swe_set_jpl_file("de431.eph"); 106 | 107 | int fl = SEFLG_JPLEPH | SEFLG_SPEED | SEFLG_TOPOCTR | SEFLG_SIDEREAL; 108 | double xx[6]; 109 | char err[AS_MAXCH]; 110 | int rc = swe_calc(2417949.660185, SE_SUN, fl, xx, err); 111 | ``` 112 | 113 | ### Response 114 | A response can either be an array with return values or an error string. 115 | 116 | The response for the example request looks like this: 117 | ``` 118 | 00000000 36 32 3c 93 ce 00 01 81 41 96 cb 40 70 8e e8 b7 |62<.....A..@p...| 119 | 00000010 e1 56 05 cb bf 5e 72 64 be 9d 1b 96 cb 3f ef 77 |.V...^rd.....?.w| 120 | 00000020 c2 d6 24 d2 ed cb 3f f0 61 6f 8d 18 b0 00 cb bf |..$...?.ao......| 121 | 00000030 6e d9 f9 96 82 f0 cc cb bf 1c bc 80 24 1e 00 00 |n...........$...| 122 | 00000040 a0 3e |.>| 123 | ``` 124 | 125 | It corresponds to the following JSON: 126 | ```json 127 | [ 128 | 98625, 129 | [ 130 | 264.931816, 131 | -0.001858, 132 | 0.983369, 133 | 1.023788, 134 | -0.003766, 135 | -0.000110 136 | ], 137 | "" 138 | ] 139 | ``` 140 | 141 | And corresponds to the following values in C: 142 | ```c 143 | rc = SEFLG_JPLEPH | SEFLG_NONUT | SEFLG_SPEED | SEFLG_TOPOCTR | SEFLG_SIDEREAL; 144 | xx = [264.931816, -0.001858, 0.983369, 1.023788, -0.003766, -0.000110]; 145 | err = ""; 146 | ``` 147 | 148 | ## Implementation specifics 149 | ### swerker-stdio 150 | This program is designed to run as subprocess of the client. It will read 151 | requests from stdin and write responses to stdout. The process will exit when a 152 | newline (`\n`) is read from stdin. Each request and response is framed in a 153 | [Lich][lich] data element so the logic in the worker is fairly simple. Framing 154 | enables reading the input into a buffer and parse the request incrementally. 155 | 156 | The client is able to call functions that change the Swiss Ephemeris library 157 | state. With this ability comes the responsibility for the client to initialize 158 | the worker properly by calling `swe_set_ephe_path` on start up and `swe_close` 159 | before quitting the process. 160 | 161 | [lich]: https://github.com/rentzsch/lich 162 | -------------------------------------------------------------------------------- /cmd/swerker/doc.go: -------------------------------------------------------------------------------- 1 | // Command swerker is a C program that is exposes the Swiss Ephemeris via a RPC 2 | // system. 3 | package main 4 | -------------------------------------------------------------------------------- /cmd/swerker/handlers.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "swex.h" 4 | #include "sweversion.h" 5 | 6 | #include "tr.h" 7 | #include "handlers.h" 8 | 9 | bool handlers_test_functions_enabled = false; 10 | static handler_t handlers[]; 11 | 12 | void handlers_init() { 13 | // 1.1 swe_set_ephe_path() 14 | // This is the first function that should be called before any other function 15 | // of the Swiss Ephemeris. Even if you don’t want to set an ephemeris path 16 | // and use the Moshier ephemeris, it is nevertheless recommended to call 17 | // swe_set_ephe_path(NULL), because this function makes important 18 | // initializations. If you don’t do that, the Swiss Ephemeris may work, but 19 | // the results may be not 100% consistent. 20 | swe_set_ephe_path(NULL); 21 | } 22 | 23 | static int64_t mp_get_int(const char **data) { 24 | switch (mp_typeof(**data)) { 25 | default: 26 | case MP_INT: return mp_decode_int(data); 27 | case MP_UINT: return (int64_t)mp_decode_uint(data); 28 | case MP_DOUBLE: return (int64_t)mp_decode_double(data); 29 | case MP_FLOAT: return (int64_t)mp_decode_float(data); 30 | } 31 | } 32 | 33 | static double mp_get_double(const char **data) { 34 | switch (mp_typeof(**data)) { 35 | default: 36 | case MP_DOUBLE: return mp_decode_double(data); 37 | case MP_FLOAT: return (double)mp_decode_float(data); 38 | case MP_UINT: return (double)mp_decode_uint(data); 39 | case MP_INT: return (double)mp_decode_int(data); 40 | } 41 | } 42 | 43 | static char *mp_put_int(char *data, int64_t num) { 44 | if (num < 0) { 45 | return mp_encode_int(data, num); 46 | } 47 | 48 | return mp_encode_uint(data, (uint64_t)num); 49 | } 50 | 51 | static char *mp_put_str(char *data, const char *str) { 52 | return mp_encode_str(data, str, strlen(str)); 53 | } 54 | 55 | static char *h_rpc_funcs(char *resp, __unused const char **req) { 56 | size_t n = handlers_count(); 57 | resp = mp_encode_array(resp, n); 58 | 59 | for (size_t i = 0; i < n; i++) { 60 | resp = mp_put_str(resp, handlers[i].name); 61 | } 62 | 63 | return resp; 64 | } 65 | 66 | static char *h_test_crash(char *resp, __unused const char **req) { 67 | if (!handlers_test_functions_enabled) { 68 | resp = mp_encode_map(resp, 1); 69 | resp = mp_encode_str(resp, "err", 3); 70 | resp = mp_encode_str(resp, "function disabled", 17); 71 | return resp; 72 | } 73 | 74 | fprintf(stderr, "DEBUG: func=test_crash\n"); 75 | fprintf(stderr, "ERROR: test_crash called\n"); 76 | exit(EXIT_FAILURE); 77 | } 78 | 79 | static char *h_test_error(char *resp, __unused const char **req) { 80 | if (!handlers_test_functions_enabled) { 81 | resp = mp_encode_map(resp, 1); 82 | resp = mp_encode_str(resp, "err", 3); 83 | resp = mp_encode_str(resp, "function disabled", 17); 84 | } else { 85 | resp = mp_encode_map(resp, 2); 86 | resp = mp_encode_str(resp, "err", 3); 87 | resp = mp_encode_str(resp, "test_error called", 17); 88 | resp = mp_encode_str(resp, "dbg", 3); 89 | resp = mp_encode_str(resp, "func=test_error", 15); 90 | } 91 | 92 | return resp; 93 | } 94 | 95 | static char *h_swe_version(char *resp, __unused const char **req) { 96 | resp = mp_encode_array(resp, 1); 97 | resp = mp_put_str(resp, SE_VERSION); 98 | return resp; 99 | } 100 | 101 | typedef int32 (* swe_calc_func)(double, int32_t, int32_t, double *, char *); 102 | static char *hf_swe_calc(char *resp, const char **req, swe_calc_func calc) { 103 | double jd = mp_get_double(req); 104 | int32_t pl = (int32_t)mp_get_int(req); 105 | int32_t fl = (int32_t)mp_get_int(req); 106 | 107 | double xx[6]; 108 | char err[AS_MAXCH] = {0}; 109 | int32_t rv = calc(jd, pl, fl, xx, err); 110 | 111 | resp = mp_encode_array(resp, 3); 112 | resp = mp_put_int(resp, rv); 113 | resp = mp_encode_array(resp, 6); 114 | for (size_t i = 0; i < 6; i++) { 115 | resp = mp_encode_double(resp, xx[i]); 116 | } 117 | resp = mp_put_str(resp, err); 118 | return resp; 119 | } 120 | 121 | static char *h_swe_calc(char *resp, const char **req) { 122 | return hf_swe_calc(resp, req, swe_calc); 123 | } 124 | 125 | static char *h_swe_calc_ut(char *resp, const char **req) { 126 | return hf_swe_calc(resp, req, swe_calc_ut); 127 | } 128 | 129 | typedef int32 (* swe_fixstar_func)(char *, double, int32, double *, char *); 130 | static char *hf_swe_fixstar(char *resp, const char **req, swe_fixstar_func calc) { 131 | char star[41] = {0}; 132 | uint32_t len = 0; 133 | const char *name = mp_decode_str(req, &len); 134 | strncpy(star, name, len); 135 | 136 | double jd = mp_get_double(req); 137 | int32_t fl = (int32_t)mp_get_int(req); 138 | 139 | double xx[6]; 140 | char err[AS_MAXCH] = {0}; 141 | int32_t rv = calc((char *)star, jd, fl, xx, err); 142 | 143 | resp = mp_encode_array(resp, 4); 144 | resp = mp_put_str(resp, star); 145 | resp = mp_put_int(resp, rv); 146 | resp = mp_encode_array(resp, 6); 147 | for (size_t i = 0; i < 6; i++) { 148 | resp = mp_encode_double(resp, xx[i]); 149 | } 150 | resp = mp_put_str(resp, err); 151 | return resp; 152 | } 153 | 154 | static char *h_swe_fixstar(char *resp, const char **req) { 155 | return hf_swe_fixstar(resp, req, swe_fixstar); 156 | } 157 | 158 | static char *h_swe_fixstar_ut(char *resp, const char **req) { 159 | return hf_swe_fixstar(resp, req, swe_fixstar_ut); 160 | } 161 | 162 | static char *h_swe_fixstar_mag(char *resp, const char **req) { 163 | char star[41] = {0}; 164 | uint32_t len = 0; 165 | const char *name = mp_decode_str(req, &len); 166 | strncpy(star, name, len); 167 | 168 | double mag; 169 | char err[AS_MAXCH] = {0}; 170 | int32_t rv = swe_fixstar_mag((char *)star, &mag, err); 171 | 172 | resp = mp_encode_array(resp, 4); 173 | resp = mp_put_str(resp, star); 174 | resp = mp_put_int(resp, rv); 175 | resp = mp_encode_double(resp, mag); 176 | resp = mp_put_str(resp, err); 177 | return resp; 178 | } 179 | 180 | static char *h_swe_close(char *resp, __unused const char **req) { 181 | swe_close(); 182 | resp = mp_encode_array(resp, 0); 183 | return resp; 184 | } 185 | 186 | static char *h_swe_set_ephe_path(char *resp, const char **req) { 187 | uint32_t len = 0; 188 | const char *path = mp_decode_str(req, &len); 189 | 190 | swe_set_ephe_path((char *)path); 191 | 192 | resp = mp_encode_array(resp, 0); 193 | return resp; 194 | } 195 | 196 | static char *h_swe_set_jpl_file(char *resp, const char **req) { 197 | uint32_t len = 0; 198 | const char *fname = mp_decode_str(req, &len); 199 | 200 | swex_set_jpl_file_len(fname, len); 201 | 202 | if (resp == NULL) { 203 | return NULL; 204 | } 205 | 206 | resp = mp_encode_array(resp, 0); 207 | return resp; 208 | } 209 | 210 | static char *h_swe_get_planet_name(char *resp, const char **req) { 211 | int32_t pl = (int32_t)mp_get_int(req); 212 | 213 | char name[AS_MAXCH] = {0}; 214 | swe_get_planet_name(pl, name); 215 | 216 | resp = mp_encode_array(resp, 1); 217 | resp = mp_put_str(resp, name); 218 | return resp; 219 | } 220 | 221 | static char *h_swe_set_topo(char *resp, const char **req) { 222 | double geolon = mp_get_double(req); 223 | double geolat = mp_get_double(req); 224 | double geoalt = mp_get_double(req); 225 | 226 | swex_set_topo(geolon, geolat, geoalt); 227 | 228 | if (resp == NULL) { 229 | return NULL; 230 | } 231 | 232 | resp = mp_encode_array(resp, 0); 233 | return resp; 234 | } 235 | 236 | static char *h_swe_set_sid_mode(char *resp, const char **req) { 237 | int32_t sidm = (int32_t)mp_get_int(req); 238 | double t0 = mp_get_double(req); 239 | double ayan_t0 = mp_get_double(req); 240 | 241 | swex_set_sid_mode(sidm, t0, ayan_t0); 242 | 243 | if (resp == NULL) { 244 | return NULL; 245 | } 246 | 247 | resp = mp_encode_array(resp, 0); 248 | return resp; 249 | } 250 | 251 | typedef int32 (* swe_get_ayanamsa_ex_func)(double, int32, double *, char *); 252 | static char *hf_swe_get_ayanamsa_ex(char *resp, const char **req, swe_get_ayanamsa_ex_func calc) { 253 | double jd = mp_get_double(req); 254 | int32_t fl = (int32_t)mp_get_int(req); 255 | 256 | double aya = 0; 257 | char err[AS_MAXCH] = {0}; 258 | int32_t rv = calc(jd, fl, &aya, err); 259 | 260 | resp = mp_encode_array(resp, 2); 261 | resp = mp_put_int(resp, rv); 262 | resp = mp_put_str(resp, err); 263 | return resp; 264 | } 265 | 266 | static char *h_swe_get_ayanamsa_ex(char *resp, const char **req) { 267 | return hf_swe_get_ayanamsa_ex(resp, req, swe_get_ayanamsa_ex); 268 | } 269 | 270 | static char *h_swe_get_ayanamsa_ex_ut(char *resp, const char **req) { 271 | return hf_swe_get_ayanamsa_ex(resp, req, swe_get_ayanamsa_ex_ut); 272 | } 273 | 274 | typedef double (* swe_get_ayanamsa_func)(double); 275 | static char *hf_swe_get_ayanamsa(char *resp, const char **req, swe_get_ayanamsa_func calc) { 276 | double jd = mp_get_double(req); 277 | 278 | double aya = calc(jd); 279 | 280 | resp = mp_encode_array(resp, 1); 281 | resp = mp_encode_double(resp, aya); 282 | return resp; 283 | } 284 | 285 | static char *h_swe_get_ayanamsa(char *resp, const char **req) { 286 | return hf_swe_get_ayanamsa(resp, req, swe_get_ayanamsa); 287 | } 288 | 289 | static char *h_swe_get_ayanamsa_ut(char *resp, const char **req) { 290 | return hf_swe_get_ayanamsa(resp, req, swe_get_ayanamsa_ut); 291 | } 292 | 293 | static char *h_swe_get_ayanamsa_name(char *resp, const char **req) { 294 | int32_t sidm = (int32_t)mp_get_int(req); 295 | 296 | const char *name = swe_get_ayanamsa_name(sidm); 297 | 298 | resp = mp_encode_array(resp, 1); 299 | resp = mp_put_str(resp, name); 300 | return resp; 301 | } 302 | 303 | // swe_date_conversion 304 | // swe_julday 305 | // swe_revjul 306 | // swe_utc_to_jd 307 | // swe_jdet_to_utc 308 | // swe_jdut1_to_utc 309 | // swe_utc_time_zone 310 | // swe_houses 311 | // swe_houses_ex 312 | // swe_houses_armc 313 | // swe_house_pos 314 | // swe_house_name 315 | // swe_gauquelin_sector 316 | // swe_sol_eclipse_where 317 | // swe_lun_occult_where 318 | // swe_sol_eclipse_how 319 | // swe_sol_eclipse_when_loc 320 | // swe_lun_occult_when_loc 321 | // swe_sol_eclipse_when_glob 322 | // swe_lun_occult_when_glob 323 | // swe_lun_eclipse_how 324 | // swe_lun_eclipse_when 325 | // swe_lun_eclipse_when_loc 326 | // swe_pheno 327 | // swe_pheno_ut 328 | // swe_refrac 329 | // swe_refrac_extended 330 | // swe_set_lapse_rate /* context */ 331 | // swe_azalt 332 | // swe_azalt_rev 333 | // swe_rise_trans_true_hor 334 | // swe_rise_trans 335 | // swe_nod_aps 336 | // swe_nod_aps_ut 337 | // swe_get_orbital_elements 338 | // swe_orbit_max_min_true_distance 339 | // swe_deltat 340 | // swe_deltat_ex 341 | // swe_time_equ 342 | // swe_lmt_to_lat 343 | // swe_lat_to_lmt 344 | // swe_sidtime0 345 | // swe_sidtime 346 | // swe_set_interpolate_nut 347 | // swe_cotrans 348 | // swe_cotrans_sp 349 | // swe_get_tid_acc 350 | // swe_set_tid_acc /* context */ 351 | // swe_set_delta_t_userdef /* context */ 352 | // swe_degnorm 353 | // swe_radnorm 354 | // swe_rad_midp 355 | // swe_deg_midp 356 | // swe_split_deg 357 | // swe_heliacal_ut 358 | // swe_heliacal_pheno_ut 359 | // swe_vis_limit_mag 360 | // swe_heliacal_angle 361 | // swe_topo_arcus_visionis 362 | // swe_day_of_week 363 | 364 | static handler_t handlers[] = { 365 | {"rpc_funcs", 0, false, h_rpc_funcs}, // keep this always on top! 366 | {"test_crash", 0, false, h_test_crash}, 367 | {"test_error", 0, false, h_test_error}, 368 | {"swe_version", 0, false, h_swe_version}, 369 | {"swe_calc", 3, false, h_swe_calc}, 370 | {"swe_calc_ut", 3, false, h_swe_calc_ut}, 371 | {"swe_fixstar", 3, false, h_swe_fixstar}, 372 | {"swe_fixstar_ut", 3, false, h_swe_fixstar_ut}, 373 | {"swe_fixstar_mag", 1, false, h_swe_fixstar_mag}, 374 | {"swe_close", 0, true, h_swe_close}, /* context */ 375 | {"swe_set_ephe_path", 1, true, h_swe_set_ephe_path}, /* context */ 376 | {"swe_set_jpl_file", 1, true, h_swe_set_jpl_file}, /* context */ 377 | {"swe_get_planet_name", 1, false, h_swe_get_planet_name}, 378 | {"swe_set_topo", 3, true, h_swe_set_topo}, /* context */ 379 | {"swe_set_sid_mode", 3, true, h_swe_set_sid_mode}, /* context */ 380 | 381 | #if SWEX_VERSION_MAJOR == 2 && SWEX_VERSION_MINOR >= 2 382 | {"swe_get_ayanamsa_ex", 2, false, h_swe_get_ayanamsa_ex}, 383 | {"swe_get_ayanamsa_ex_ut", 2, false, h_swe_get_ayanamsa_ex_ut}, 384 | #endif 385 | 386 | {"swe_get_ayanamsa", 2, false, h_swe_get_ayanamsa}, 387 | {"swe_get_ayanamsa_ut", 2, false, h_swe_get_ayanamsa_ut}, 388 | {"swe_get_ayanamsa_name", 2, false, h_swe_get_ayanamsa_name}, 389 | // swe_date_conversion 390 | // swe_julday 391 | // swe_revjul 392 | // swe_utc_to_jd 393 | // swe_jdet_to_utc 394 | // swe_jdut1_to_utc 395 | // swe_utc_time_zone 396 | // swe_houses 397 | // swe_houses_ex 398 | // swe_houses_armc 399 | // swe_house_pos 400 | // swe_house_name 401 | // swe_gauquelin_sector 402 | // swe_sol_eclipse_where 403 | // swe_lun_occult_where 404 | // swe_sol_eclipse_how 405 | // swe_sol_eclipse_when_loc 406 | // swe_lun_occult_when_loc 407 | // swe_sol_eclipse_when_glob 408 | // swe_lun_occult_when_glob 409 | // swe_lun_eclipse_how 410 | // swe_lun_eclipse_when 411 | // swe_lun_eclipse_when_loc 412 | // swe_pheno 413 | // swe_pheno_ut 414 | // swe_refrac 415 | // swe_refrac_extended 416 | // swe_set_lapse_rate /* context */ 417 | // swe_azalt 418 | // swe_azalt_rev 419 | // swe_rise_trans_true_hor 420 | // swe_rise_trans 421 | // swe_nod_aps 422 | // swe_nod_aps_ut 423 | 424 | #if SWEX_VERSION_MAJOR == 2 && SWEX_VERSION_MINOR >= 5 425 | // swe_get_orbital_elements 426 | // swe_orbit_max_min_true_distance 427 | #endif 428 | 429 | // swe_deltat 430 | // swe_deltat_ex 431 | // swe_time_equ 432 | // swe_lmt_to_lat 433 | // swe_lat_to_lmt 434 | // swe_sidtime0 435 | // swe_sidtime 436 | 437 | #if SWEX_VERSION_MAJOR == 2 && SWEX_VERSION_MINOR >= 6 438 | // swe_set_interpolate_nut /* context */ 439 | #endif 440 | 441 | // swe_cotrans 442 | // swe_cotrans_sp 443 | // swe_get_tid_acc 444 | // swe_set_tid_acc /* context */ 445 | 446 | #if SWEX_VERSION_MAJOR == 2 && SWEX_VERSION_MINOR >= 5 447 | // swe_set_delta_t_userdef /* context */ 448 | #endif 449 | 450 | // swe_degnorm 451 | // swe_radnorm 452 | // swe_rad_midp 453 | // swe_deg_midp 454 | // swe_split_deg 455 | // swe_heliacal_ut 456 | // swe_heliacal_pheno_ut 457 | // swe_vis_limit_mag 458 | // swe_heliacal_angle 459 | // swe_topo_arcus_visionis 460 | // swe_difdegn 461 | // swe_difdeg2n 462 | // swe_difrad2n 463 | // swe_d2l 464 | // swe_day_of_week 465 | }; 466 | 467 | size_t handlers_count() { 468 | return sizeof(handlers) / sizeof(handler_t); 469 | } 470 | 471 | handler_t *handlers_get(size_t idx) { 472 | if (idx >= handlers_count()) { 473 | return NULL; 474 | } 475 | 476 | return &handlers[idx]; 477 | } 478 | -------------------------------------------------------------------------------- /cmd/swerker/handlers.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | bool handlers_test_functions_enabled; 5 | 6 | // Buffer resp is NULL if called as context call. 7 | typedef char *(*handler_callback_t)(char *resp, const char **req); 8 | 9 | typedef struct handler handler_t; 10 | struct handler { 11 | char *name; 12 | size_t argc; 13 | bool ccall; 14 | handler_callback_t callback; 15 | }; 16 | 17 | void handlers_init(); 18 | size_t handlers_count(); 19 | handler_t *handlers_get(size_t idx); 20 | -------------------------------------------------------------------------------- /cmd/swerker/msgpuck.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 MsgPuck Authors 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or 6 | * without modification, are permitted provided that the following 7 | * conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above 10 | * copyright notice, this list of conditions and the 11 | * following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above 14 | * copyright notice, this list of conditions and the following 15 | * disclaimer in the documentation and/or other materials 16 | * provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 22 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 26 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 29 | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | */ 32 | 33 | #define MP_SOURCE 1 34 | #include "msgpuck.h" 35 | -------------------------------------------------------------------------------- /cmd/swerker/swerker.c: -------------------------------------------------------------------------------- 1 | #if !defined(DEBUG) || DEBUG == 0 2 | #define NDEBUG 1 3 | #else 4 | #undef NDEBUG 5 | #endif 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "tr.h" 13 | #include "handlers.h" 14 | 15 | int main(int argc, char const *argv[]) { 16 | handlers_init(); 17 | tr_init(argc, argv); 18 | 19 | char req[REQSIZE]; 20 | char resp[RESPSIZE]; 21 | 22 | while (true) { 23 | nextreq: 24 | memset(req, 0, REQSIZE); 25 | memset(resp, 0, RESPSIZE); 26 | const char *reqbuf = req; 27 | char *respbuf = resp; 28 | 29 | reqbuf = tr_recv(req); 30 | if (reqbuf == NULL) { 31 | goto nextreq; 32 | } 33 | 34 | const char *input = (char *)req; 35 | if (!mp_check(&input, reqbuf)) { 36 | tr_error("input is not valid msgpack", NULL, 0); 37 | goto nextreq; 38 | } 39 | 40 | // Reset pointer to start of request buffer. 41 | reqbuf = req; 42 | 43 | uint32_t fields = mp_decode_array(&reqbuf); 44 | if (fields != 3) { 45 | char dbg[DBGSIZE]; 46 | size_t dbglen = 0; 47 | #if DEBUG 48 | dbglen = sprintf(dbg, "size=%u", fields); 49 | #endif 50 | tr_error("array with 3 values expected (envelope)", dbg, dbglen); 51 | goto nextreq; 52 | } 53 | 54 | // Execute context calls first. 55 | // The type of the context value is either array or nil. 56 | if (mp_typeof(*reqbuf) == MP_NIL) { 57 | mp_decode_nil(&reqbuf); 58 | } else { 59 | uint32_t size = mp_decode_array(&reqbuf); 60 | for (size_t i = 0; i < size; i++) { 61 | uint32_t fields = mp_decode_array(&reqbuf); 62 | if (fields != 2) { 63 | char dbg[DBGSIZE]; 64 | size_t dbglen = 0; 65 | #if DEBUG 66 | dbglen = sprintf(dbg, "size=%u", fields); 67 | #endif 68 | tr_error("array with 2 values expected (ccall envelope)", dbg, dbglen); 69 | goto nextreq; 70 | } 71 | 72 | uint8_t idx = mp_load_u8(&reqbuf); 73 | handler_t *h = handlers_get(idx); 74 | if (h == NULL) { 75 | char dbg[DBGSIZE]; 76 | size_t dbglen = 0; 77 | #if DEBUG 78 | dbglen = sprintf(dbg, "func=%u", idx); 79 | #endif 80 | tr_error("invalid index (ccall function)", dbg, dbglen); 81 | goto nextreq; 82 | } 83 | 84 | // The type of the arguments value is either array or nil. 85 | if (mp_typeof(*reqbuf) == MP_NIL) { 86 | mp_decode_nil(&reqbuf); 87 | } else { 88 | uint32_t argc = mp_decode_array(&reqbuf); 89 | if (h->argc != argc) { 90 | char dbg[DBGSIZE]; 91 | size_t dbglen = 0; 92 | #if DEBUG 93 | dbglen = sprintf(dbg, "func=%u(%s) argc=%zu/%u", idx, h->name, h->argc, argc); 94 | #endif 95 | tr_error("invalid number of arguments (ccall function)", dbg, dbglen); 96 | goto nextreq; 97 | } 98 | } 99 | 100 | if (!h->ccall) { 101 | char dbg[DBGSIZE]; 102 | size_t dbglen = 0; 103 | #if DEBUG 104 | dbglen = sprintf(dbg, "func=%u(%s)", idx, h->name); 105 | #endif 106 | tr_error("function is invalid as context call", dbg, dbglen); 107 | } 108 | 109 | h->callback(NULL, &reqbuf); 110 | } 111 | } 112 | 113 | // Execute actual call. 114 | uint8_t idx = mp_load_u8(&reqbuf); 115 | handler_t *h = handlers_get(idx); 116 | if (h == NULL) { 117 | char dbg[DBGSIZE]; 118 | size_t dbglen = 0; 119 | #if DEBUG 120 | dbglen = sprintf(dbg, "func=%u", idx); 121 | #endif 122 | tr_error("invalid index (function)", dbg, dbglen); 123 | goto nextreq; 124 | } 125 | 126 | // The type of the arguments value is either array or nil. 127 | if (mp_typeof(*reqbuf) == MP_NIL) { 128 | // If the type is nil, invalidate the request buffer. 129 | reqbuf = NULL; 130 | } else { 131 | uint32_t argc = mp_decode_array(&reqbuf); 132 | if (h->argc != argc) { 133 | char dbg[DBGSIZE]; 134 | size_t dbglen = 0; 135 | #if DEBUG 136 | dbglen = sprintf(dbg, "func=%u(%s) argc=%zu/%u", idx, h->name, h->argc, argc); 137 | #endif 138 | tr_error("invalid number of arguments", dbg, dbglen); 139 | goto nextreq; 140 | } 141 | } 142 | 143 | respbuf = h->callback(respbuf, &reqbuf); 144 | if (respbuf == NULL) { 145 | char dbg[DBGSIZE]; 146 | size_t dbglen = 0; 147 | #if DEBUG 148 | dbglen = sprintf(dbg, "func=%u(%s)", idx, h->name); 149 | #endif 150 | tr_error("function call failed", dbg, dbglen); 151 | goto nextreq; 152 | } 153 | 154 | if (!tr_send(resp, respbuf)) { 155 | return EXIT_FAILURE; 156 | } 157 | } 158 | 159 | return EXIT_SUCCESS; 160 | } 161 | -------------------------------------------------------------------------------- /cmd/swerker/sweversion.h: -------------------------------------------------------------------------------- 1 | ../../swex/sweversion.h -------------------------------------------------------------------------------- /cmd/swerker/swex.c: -------------------------------------------------------------------------------- 1 | ../../swex/swex.c -------------------------------------------------------------------------------- /cmd/swerker/swex.h: -------------------------------------------------------------------------------- 1 | ../../swex/swex.h -------------------------------------------------------------------------------- /cmd/swerker/tr-stdio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "tr.h" 7 | #include "handlers.h" 8 | 9 | void tr_init(int argc, char const *argv[]) { 10 | for (size_t i = 1; i < argc; i++) { 11 | if (strncmp(argv[i], "-w", 2) == 0) { 12 | sleep(5); 13 | } 14 | 15 | if (strncmp(argv[i], "-dangerous_enable_test_functions", 32) == 0) { 16 | handlers_test_functions_enabled = true; 17 | } 18 | 19 | if (strncmp(argv[i], "-dangerous_no_funcs_on_init", 27) == 0) { 20 | exit(EXIT_FAILURE); 21 | } 22 | 23 | if (strncmp(argv[i], "-dangerous_invalid_funcs_on_init", 32) == 0) { 24 | puts("invalid func data"); 25 | exit(EXIT_FAILURE); 26 | } 27 | 28 | if (strncmp(argv[i], "-dangerous_invalid_funcs_types_on_init", 38) == 0) { 29 | fputs("1<\xc0>", stdout); // msgpack nil value 30 | exit(EXIT_FAILURE); 31 | } 32 | } 33 | 34 | setbuf(stdin, NULL); 35 | setvbuf(stdout, NULL, _IOFBF, RESPSIZE); 36 | 37 | // Write RPC functions, same as calling rpc_funcs function (index 0). 38 | char data[RESPSIZE]; 39 | char *buf = data; 40 | handler_t *h = handlers_get(0); 41 | buf = h->callback(buf, NULL); 42 | 43 | if (!tr_send(data, buf)) { 44 | exit(EXIT_FAILURE); 45 | } 46 | } 47 | 48 | char *tr_recv(char *buf) { 49 | int c = fgetc(stdin); 50 | if (c == EOF || c == '\n') { 51 | exit(EXIT_SUCCESS); 52 | } 53 | 54 | uint64_t len = 0; 55 | while ('0' <= c && c <= '9') { 56 | len *= 10; 57 | len += c - '0'; 58 | 59 | c = fgetc(stdin); 60 | if (c == EOF) { 61 | char dbg[DBGSIZE]; 62 | size_t dbglen = 0; 63 | #if DEBUG 64 | dbglen = sprintf(dbg, "len=%llu", len); 65 | #endif 66 | tr_error("reading unexpected EOF (length)", dbg, dbglen); 67 | return NULL; 68 | } 69 | } 70 | 71 | // We limit input data to REQSIZE bytes to protect against buffer overflows 72 | // and unbounded buffer allocations. 73 | if (len > REQSIZE) { 74 | char dbg[DBGSIZE]; 75 | size_t dbglen = 0; 76 | #if DEBUG 77 | dbglen = sprintf(dbg, "len=%llu, limit=%d", len, REQSIZE); 78 | #endif 79 | tr_error("input data is more than request size limit", dbg, dbglen); 80 | return NULL; 81 | } 82 | 83 | // char is already received in while loop 84 | if (c != '<') { 85 | char dbg[DBGSIZE]; 86 | size_t dbglen = 0; 87 | #if DEBUG 88 | dbglen = sprintf(dbg, "c='%c' c=%d", c, c); 89 | #endif 90 | tr_error("reading unexpected open type marker", dbg, dbglen); 91 | return NULL; 92 | } 93 | 94 | size_t n = 0; 95 | while (n < len) { 96 | c = fgetc(stdin); 97 | if (c == EOF) { 98 | tr_error("reading unexpected EOF (body)", NULL, 0); 99 | return NULL; 100 | } 101 | 102 | buf[n++] = c; 103 | } 104 | 105 | c = fgetc(stdin); 106 | if (c != '>') { 107 | char dbg[DBGSIZE]; 108 | size_t dbglen = 0; 109 | #if DEBUG 110 | dbglen = sprintf(dbg, "c='%c' c=%d", c, c); 111 | #endif 112 | tr_error("reading unexpected close type marker", dbg, dbglen); 113 | return NULL; 114 | } 115 | 116 | if (len == 0) { 117 | tr_error("input data expected", NULL, 0); 118 | return NULL; 119 | } 120 | 121 | return buf; 122 | } 123 | 124 | bool tr_send(char *data, char *end) { 125 | size_t len = end - data; 126 | 127 | int n = fprintf(stdout, "%lu<", len); 128 | fwrite(data, len, sizeof(char), stdout); 129 | putc('>', stdout); 130 | 131 | if (n < 0 || ferror(stdout)) { 132 | // Write to stdout is failed, so write error to stderr as last resort. 133 | #if DEBUG 134 | fprintf(stderr, "DEBUG: len=%zu data=", len); 135 | fwrite(data, len, sizeof(char), stderr); 136 | putc('\n', stderr); 137 | #endif 138 | fprintf(stderr, "ERROR: failed to write reponse\n"); 139 | return false; 140 | } 141 | 142 | if (fflush(stdout) != 0) { 143 | // Flush to stdout is failed, so write error to stderr as last resort. 144 | fprintf(stderr, "ERROR: failed to flush reponse\n"); 145 | return false; 146 | } 147 | 148 | return true; 149 | } 150 | 151 | void tr_error(const char *msg, const char *dbg, size_t dbglen) { 152 | char data[RESPSIZE]; 153 | char *buf = data; 154 | 155 | int size = 1; 156 | #if DEBUG 157 | if (dbglen != 0) { 158 | size = 2; 159 | } 160 | #endif 161 | 162 | buf = mp_encode_map(buf, size); 163 | buf = mp_encode_str(buf, "err", 3); 164 | buf = mp_encode_str(buf, msg, strlen(msg)); 165 | 166 | #if DEBUG 167 | if (dbglen != 0) { 168 | buf = mp_encode_str(buf, "dbg", 3); 169 | buf = mp_encode_str(buf, dbg, dbglen); 170 | } 171 | #endif 172 | 173 | tr_send(data, buf); 174 | } 175 | -------------------------------------------------------------------------------- /cmd/swerker/tr.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "msgpuck.h" 4 | 5 | #define REQSIZE 1024 6 | #define RESPSIZE 1024 7 | #define DBGSIZE 512 8 | 9 | void tr_init(int argc, char const *argv[]); 10 | char *tr_recv(char *buf); 11 | bool tr_send(char *data, char *end); 12 | void tr_error(const char *msg, const char *dbg, size_t dbglen); 13 | -------------------------------------------------------------------------------- /const.go: -------------------------------------------------------------------------------- 1 | package swego 2 | 3 | // Calendar types defined in swephexp.h. 4 | const ( 5 | Julian CalType = 0 6 | Gregorian CalType = 1 7 | ) 8 | 9 | // Planet, fictional body and asteroid constants defined in swephexp.h. 10 | const ( 11 | Sun Planet = 0 12 | Moon Planet = 1 13 | Mercury Planet = 2 14 | Venus Planet = 3 15 | Mars Planet = 4 16 | Jupiter Planet = 5 17 | Saturn Planet = 6 18 | Uranus Planet = 7 19 | Neptune Planet = 8 20 | Pluto Planet = 9 21 | MeanNode Planet = 10 22 | TrueNode Planet = 11 23 | MeanApogee Planet = 12 24 | OscuApogee Planet = 13 25 | Earth Planet = 14 26 | Chiron Planet = 15 27 | Pholus Planet = 16 28 | Ceres Planet = 17 29 | Pallas Planet = 18 30 | Juno Planet = 19 31 | Vesta Planet = 20 32 | InterApogee Planet = 21 33 | InterPerigee Planet = 22 34 | 35 | Varuna Planet = AstOffset + 20000 36 | Nessus Planet = AstOffset + 7066 37 | 38 | Cupido Planet = 40 39 | Hades Planet = 41 40 | Zeus Planet = 42 41 | Kronos Planet = 43 42 | Apollon Planet = 44 43 | Admetos Planet = 45 44 | Vulkanus Planet = 46 45 | Poseidon Planet = 47 46 | 47 | Isis Planet = 48 48 | Nibiru Planet = 49 49 | Harrington Planet = 50 50 | NeptuneLeverrier Planet = 51 51 | NeptuneAdams Planet = 52 52 | PlutoLowell Planet = 53 53 | PlutoPickering Planet = 54 54 | Vulcan Planet = 55 55 | WhiteMoon Planet = 56 56 | Proserpina Planet = 57 57 | Waldemath Planet = 58 58 | 59 | EclNut Planet = -1 60 | 61 | AstOffset = 10000 62 | ) 63 | 64 | //go:generate stringer -type=Planet 65 | 66 | // Indexes of related house positions defined in swephexp.h. 67 | const ( 68 | Asc = 0 69 | MC = 1 70 | ARMC = 2 71 | Vertex = 3 72 | EquAsc = 4 // "equatorial ascendant" 73 | CoAsc1 = 5 // "co-ascendant" (W. Koch) 74 | CoAsc2 = 6 // "co-ascendant" (M. Munkasey) 75 | PolAsc = 7 // "polar ascendant" (M. Munkasey) 76 | ) 77 | 78 | // Ephemerides that are implemented in the C library. 79 | const ( 80 | JPL Ephemeris = FlagEphJPL 81 | Swiss Ephemeris = FlagEphSwiss 82 | Moshier Ephemeris = FlagEphMoshier 83 | DefaultEph Ephemeris = FlagEphDefault 84 | ) 85 | 86 | // Calculation flags defined in swephexp.h. 87 | const ( 88 | FlagEphJPL = 1 << 0 89 | FlagEphSwiss = 1 << 1 90 | FlagEphMoshier = 1 << 2 91 | FlagHelio = 1 << 3 92 | FlagTruePos = 1 << 4 93 | FlagJ2000 = 1 << 5 94 | FlagNoNut = 1 << 6 95 | FlagSpeed = 1 << 8 96 | FlagNoGDefl = 1 << 9 97 | FlagNoAbber = 1 << 10 98 | FlagAstrometric = FlagNoAbber | FlagNoGDefl 99 | FlagEquatorial = 1 << 11 100 | FlagXYZ = 1 << 12 101 | FlagRadians = 1 << 13 102 | FlagBary = 1 << 14 103 | FlagTopo = 1 << 15 104 | FlagSidereal = 1 << 16 105 | FlagICRS = 1 << 17 106 | FlagJPLHor = 1 << 18 107 | FlagJPLHorApprox = 1 << 19 108 | FlagEphDefault = FlagEphSwiss 109 | ) 110 | 111 | // Sidereal modes (ayanamsas) implemented in the C library. 112 | const ( 113 | SidmFaganBradley Ayanamsa = 0 114 | SidmLahiri Ayanamsa = 1 115 | SidmDeluce Ayanamsa = 2 116 | SidmRaman Ayanamsa = 3 117 | SidmUshashashi Ayanamsa = 4 118 | SidmKrishnamurti Ayanamsa = 5 119 | SidmDjwhalKhul Ayanamsa = 6 120 | SidmYukteshwar Ayanamsa = 7 121 | SidmJNBhasin Ayanamsa = 8 122 | SidmBabylKruger1 Ayanamsa = 9 123 | SidmBabylKruger2 Ayanamsa = 10 124 | SidmBabylKruger3 Ayanamsa = 11 125 | SidmBabylHuber Ayanamsa = 12 126 | SidmBabylEtaPiscium Ayanamsa = 13 127 | SidmAldebaran15Tau Ayanamsa = 14 128 | SidmHipparchos Ayanamsa = 15 129 | SidmSassanian Ayanamsa = 16 130 | SidmGalCent0Sag Ayanamsa = 17 131 | SidmJ2000 Ayanamsa = 18 132 | SidmJ1900 Ayanamsa = 19 133 | SidmB1950 Ayanamsa = 20 134 | SidmSuryasiddhanta Ayanamsa = 21 135 | SidmSuryasiddhantaMSun Ayanamsa = 22 136 | SidmAryabhata Ayanamsa = 23 137 | SidmAryabhataMSun Ayanamsa = 24 138 | SidmSSRevati Ayanamsa = 25 139 | SidmSSCitra Ayanamsa = 26 140 | SidmTrueCitra Ayanamsa = 27 141 | SidmTrueRevati Ayanamsa = 28 142 | SidmTruePushya Ayanamsa = 29 143 | SidmGalCentGilBrand Ayanamsa = 30 144 | SidmGalAlignMardyks Ayanamsa = 31 145 | SidmGalEquIAU1958 Ayanamsa = 32 146 | SidmGalEquTrue Ayanamsa = 33 147 | SidmGalEquMula Ayanamsa = 34 148 | SidmGalTrueMula Ayanamsa = 35 149 | SidmGalCentMulaWilhelm Ayanamsa = 36 150 | SidmAryabhata522 Ayanamsa = 37 151 | SidmBabylBritton Ayanamsa = 38 152 | SidmUser Ayanamsa = 255 153 | ) 154 | 155 | // Options that augment a sidereal mode (ayanamsa). 156 | const ( 157 | SidbitEclT0 Ayanamsa = 256 158 | SidbitSSYPlane Ayanamsa = 512 159 | SidbitUserUT Ayanamsa = 1024 160 | ) 161 | 162 | // Nodes and apsides calculation bits defined in swephexp.h. 163 | const ( 164 | NodbitMean NodApsMethod = 1 165 | NodbitOscu NodApsMethod = 2 166 | NodbitOscuBary NodApsMethod = 4 167 | NodbitFoPoint NodApsMethod = 256 168 | ) 169 | 170 | // File name of JPL data files defined in swephexp.h. 171 | const ( 172 | FnameDE200 = "de200.eph" 173 | FnameDE406 = "de406.eph" 174 | FnameDE431 = "de431.eph" 175 | FnameDft = FnameDE431 176 | FnameDft2 = FnameDE406 177 | ) 178 | 179 | // House systems implemented in the C library. 180 | const ( 181 | Alcabitius HSys = 'B' 182 | Campanus HSys = 'C' 183 | EqualMC HSys = 'D' // Equal houses, where cusp 10 = MC 184 | Equal HSys = 'E' // also 'A' 185 | CarterPoliEquatorial HSys = 'F' 186 | Gauquelin HSys = 'G' 187 | Azimuthal HSys = 'H' // a.k.a Horizontal 188 | Sunshine HSys = 'I' // Makransky, solution Treindl 189 | SunshineAlt HSys = 'i' // Makransky, solution Makransky 190 | Koch HSys = 'K' 191 | PullenSD HSys = 'L' 192 | Morinus HSys = 'M' 193 | EqualAsc HSys = 'N' // Equal houses, where cusp 1 = 0° Aries 194 | Porphyrius HSys = 'O' // a.k.a Porphyry 195 | Placidus HSys = 'P' 196 | PullenSR HSys = 'Q' 197 | Regiomontanus HSys = 'R' 198 | Sripati HSys = 'S' 199 | PolichPage HSys = 'T' // a.k.a. Topocentric 200 | KrusinskiPisaGoelzer HSys = 'U' 201 | VehlowEqual HSys = 'V' // Equal Vehlow (Asc in middle of house 1) 202 | WholeSign HSys = 'W' 203 | AxialRotation HSys = 'X' // a.k.a. Meridian 204 | APCHouses HSys = 'Y' 205 | ) 206 | -------------------------------------------------------------------------------- /planet_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=Planet"; DO NOT EDIT 2 | 3 | package swego 4 | 5 | import "fmt" 6 | 7 | const ( 8 | _Planet_name_0 = "SunMoonMercuryVenusMarsJupiterSaturnUranusNeptunePlutoMeanNodeTrueNodeMeanApogeeOscuApogeeEarthChironPholusCeresPallasJunoVestaInterApogeeInterPerigee" 9 | _Planet_name_1 = "CupidoHadesZeusKronosApollonAdmetosVulkanusPoseidonIsisNibiruHarringtonNeptuneLeverrierNeptuneAdamsPlutoLowellPlutoPickeringVulcanWhiteMoonProserpinaWaldemath" 10 | _Planet_name_2 = "Nessus" 11 | _Planet_name_3 = "Varuna" 12 | ) 13 | 14 | var ( 15 | _Planet_index_0 = [...]uint8{0, 3, 7, 14, 19, 23, 30, 36, 42, 49, 54, 62, 70, 80, 90, 95, 101, 107, 112, 118, 122, 127, 138, 150} 16 | _Planet_index_1 = [...]uint8{0, 6, 11, 15, 21, 28, 35, 43, 51, 55, 61, 71, 87, 99, 110, 124, 130, 139, 149, 158} 17 | _Planet_index_2 = [...]uint8{0, 6} 18 | _Planet_index_3 = [...]uint8{0, 6} 19 | ) 20 | 21 | func (i Planet) String() string { 22 | switch { 23 | case 0 <= i && i <= 22: 24 | return _Planet_name_0[_Planet_index_0[i]:_Planet_index_0[i+1]] 25 | case 40 <= i && i <= 58: 26 | i -= 40 27 | return _Planet_name_1[_Planet_index_1[i]:_Planet_index_1[i+1]] 28 | case i == 17066: 29 | return _Planet_name_2 30 | case i == 30000: 31 | return _Planet_name_3 32 | default: 33 | return fmt.Sprintf("Planet(%d)", i) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /swecgo/examples_test.go: -------------------------------------------------------------------------------- 1 | // +build linux,cgo darwin,cgo 2 | 3 | package swecgo 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/astrotools/swego" 9 | ) 10 | 11 | func Example() { 12 | swe := Open() 13 | 14 | xx, cfl, err := swe.CalcUT(2451544.5, swego.Sun, &swego.CalcFlags{}) 15 | if err != nil { 16 | fmt.Println("calculation error: ", err) 17 | return 18 | } 19 | 20 | fmt.Printf("xx[0] %f\n", xx[0]) 21 | fmt.Printf("xx[1] %f\n", xx[1]) 22 | fmt.Printf("xx[2] %f\n", xx[2]) 23 | fmt.Printf("xx[3] %f\n", xx[3]) 24 | fmt.Printf("xx[4] %f\n", xx[4]) 25 | fmt.Printf("xx[5] %f\n", xx[5]) 26 | fmt.Printf("cfl %d\n", cfl) 27 | 28 | // Output: 29 | // xx[0] 279.859214 30 | // xx[1] 0.000230 31 | // xx[2] 0.983332 32 | // xx[3] 0.000000 33 | // xx[4] 0.000000 34 | // xx[5] 0.000000 35 | // cfl 2 36 | } 37 | 38 | func ExampleLocked() { 39 | swe := Open() 40 | 41 | // Locked guarantees that both calls to swe.CalcUT will be executed in order 42 | // as a single unit without the interference of other calls to the library. 43 | Locked(swe, func(swe Library) { 44 | // Expensive call because the result is computed. 45 | fl := new(swego.CalcFlags) 46 | swe.CalcUT(2451544.5, swego.Sun, fl) 47 | 48 | // Cheap call because results are cached. 49 | fl.Flags |= swego.FlagEquatorial 50 | swe.CalcUT(2451544.5, swego.Sun, fl) 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /swecgo/swecgo.go: -------------------------------------------------------------------------------- 1 | // +build linux,cgo darwin,cgo 2 | 3 | // Package swecgo embeds the Swiss Ephemeris library using cgo. 4 | package swecgo 5 | 6 | import ( 7 | "sync" 8 | 9 | "github.com/astrotools/swego" 10 | ) 11 | 12 | // Library extends the main library interface by exposing C library 13 | // life-cycle methods. 14 | type Library interface { 15 | // The following methods will always return nil as error: 16 | // Version 17 | // PlanetName 18 | // GetAyanamsaName 19 | // JulDay 20 | // RevJul 21 | // JdETToUTC 22 | // JdUT1ToUTC 23 | // HouseName 24 | // SidTime 25 | // SidTime0 26 | swego.Interface 27 | 28 | // SetPath opens the ephemeris and sets the data path. 29 | SetPath(path string) 30 | 31 | // Close closes the Swiss Ephemeris library. 32 | // The ephemeris can be reopened by calling SetPath. 33 | Close() 34 | 35 | // used for locking and prevent other interface implementations 36 | acquire() 37 | release() 38 | } 39 | 40 | // Open initializes the Swiss Ephemeris C library with DefaultPath as 41 | // ephemeris path. The returned object is safe for concurrent use. 42 | func Open() Library { return OpenWithPath(DefaultPath) } 43 | 44 | // OpenWithPath initializes the Swiss Ephemeris C library and calls 45 | // swe_set_ephe_path with ephePath as argument afterwards. 46 | // The returned object is safe for concurrent use. 47 | func OpenWithPath(ephePath string) Library { 48 | swe := Interface() 49 | swe.SetPath(ephePath) 50 | return swe 51 | } 52 | 53 | // Interface returns an object that calls the Swiss Ephemeris C library. 54 | // The returned object is safe for concurrent use. 55 | func Interface() Library { 56 | winit.Do(func() { 57 | checkLibrary() 58 | wrap = &wrapper{locker: new(sync.Mutex)} 59 | }) 60 | 61 | return wrap 62 | } 63 | 64 | var winit sync.Once 65 | var wrap Library 66 | 67 | // wrapper interfaces between swego.Interface and the library functions. 68 | // It protect stateful library functions with a mutex. When the wrapper is 69 | // exclusively locked, the mutex is temporary replaced by a no-op lock. 70 | type wrapper struct { 71 | locker sync.Locker 72 | } 73 | 74 | func (w *wrapper) acquire() { w.locker.Lock() } 75 | func (w *wrapper) release() { w.locker.Unlock() } 76 | 77 | type unlocked struct{} 78 | 79 | func (unlocked) Lock() {} 80 | func (unlocked) Unlock() {} 81 | 82 | var unlockedWrapper = &wrapper{locker: unlocked{}} 83 | 84 | type exclLocked struct { 85 | *wrapper 86 | locker sync.Locker 87 | } 88 | 89 | func (el exclLocked) ExclusiveUnlock() { el.locker.Unlock() } 90 | func (w *wrapper) ExclusiveLock() swego.LockedInterface { 91 | w.locker.Lock() 92 | return exclLocked{ 93 | wrapper: unlockedWrapper, // wrapper with no-op lock 94 | locker: w.locker, // the actual wrapper mutex 95 | } 96 | } 97 | 98 | // Locked exclusively locks the library, disable per function locking and 99 | // exposes the locked library to the callback function. Per function locking 100 | // is restored when execution is returned to the caller. 101 | // If either argument is nil, it panics. 102 | func Locked(swe Library, callback func(swe Library)) { 103 | if swe == nil { 104 | panic("swe is nil") 105 | } 106 | 107 | if callback == nil { 108 | panic("callback is nil") 109 | } 110 | 111 | w := swe.(*wrapper) 112 | l := w.ExclusiveLock().(exclLocked) 113 | callback(l) 114 | l.ExclusiveUnlock() 115 | } 116 | -------------------------------------------------------------------------------- /swecgo/swecgolib.go: -------------------------------------------------------------------------------- 1 | // +build linux,cgo darwin,cgo 2 | 3 | package swecgo 4 | 5 | import ( 6 | "unsafe" 7 | 8 | "github.com/astrotools/swego" 9 | ) 10 | 11 | /* 12 | 13 | // To aid debugging the Swiss Ephemeris supports tracing. It will write two 14 | // files in the current working directory. Note that the Go tool may change the 15 | // current working directory. The tracing facility has 2 modes: 16 | // 17 | // TRACE=1 writes swetrace.{c,txt} and appends each session to it. 18 | // TRACE=2 writes swetrace_.{c,txt} where pid is the running process id. 19 | // 20 | // To build a version of the Swiss Ephemeris that has tracing has enabled 21 | // uncomment one of the #cgo CFLAGS lines below or overwrite the cgo CFLAGS by 22 | // setting the CGO_CFLAGS environment variable to "-DTRACE=1" or "-DTRACE=2". 23 | // 24 | // The tracing facility is limited to TRACE_COUNT_MAX entries. You can change 25 | // this limit by overwriting this value by uncommenting the line below or using 26 | // the CGO_CFLAGS environment variable ("-DTRACE_COUNT_MAX=value"). 27 | // 28 | // When swe_close() is called it may possible to also close the tracing system. 29 | // To enable this uncomment the TRACE_CLOSE=1 line or set the CGO_CFLAGS 30 | // environment variable ("-DTRACE_CLOSE=1"). There is no guarantee that the 31 | // tracing can be reopened after it is closed the first time. 32 | // 33 | // It's very easy to enable the tracing and the trace limit per build using the 34 | // CGO_CFLAGS environment variable. It can be helpful to inspect the Go build 35 | // process pass the -x argument flag. 36 | 37 | // #cgo CFLAGS: -DTRACE=1 38 | // #cgo CFLAGS: -DTRACE=2 39 | // #cgo CFLAGS: -DTRACE_COUNT_MAX=10000000 40 | // #cgo CFLAGS: -DTRACE_CLOSE=1 41 | 42 | // ---------- 43 | 44 | // Disable thread local storage in library. 45 | #cgo CFLAGS: -DTLSOFF=1 46 | 47 | // ---------- 48 | 49 | #cgo CFLAGS: -w 50 | 51 | #include "swephexp.h" 52 | #include "sweph.h" 53 | #include "swex.h" 54 | #include "sweversion.h" 55 | 56 | double swecgo_deltat_automatic() { 57 | return SE_DELTAT_AUTOMATIC; 58 | } 59 | 60 | */ 61 | import "C" 62 | 63 | func checkLibrary() { 64 | if C.swex_supports_tls() { 65 | panic("swecgo: Thread Local Storage (TLS) is not supported") 66 | } 67 | 68 | if resetDeltaT != C.swecgo_deltat_automatic() { 69 | panic("swecgo: SE_DELTAT_AUTOMATIC mismatch") 70 | } 71 | } 72 | 73 | // withError calls fn with a pre allocated error variable that can passed to a 74 | // function in the C library. The code block must return true if an error is 75 | // returned from the C call. 76 | func withError(fn func(err *C.char) bool) error { 77 | var _err [C.AS_MAXCH]C.char 78 | 79 | if fn(&_err[0]) { 80 | return swego.Error(C.GoString(&_err[0])) 81 | } 82 | 83 | return nil 84 | } 85 | 86 | // Swiss Ephemeris version constants. 87 | const ( 88 | Version = C.SE_VERSION 89 | VersionMajor = C.SWEX_VERSION_MAJOR 90 | VersionMinor = C.SWEX_VERSION_MINOR 91 | VersionPatch = C.SWEX_VERSION_PATCH 92 | ) 93 | 94 | // DefaultPath is the default ephemeris path defined by the library. 95 | const DefaultPath = C.SE_EPHE_PATH 96 | 97 | const ( 98 | flgTopo = C.SEFLG_TOPOCTR 99 | flgSidereal = C.SEFLG_SIDEREAL 100 | ) 101 | 102 | func setEphePath(path string) { 103 | _path := C.CString(path) 104 | C.swe_set_ephe_path(_path) 105 | C.free(unsafe.Pointer(_path)) 106 | } 107 | 108 | func setJPLFile(name string) { 109 | _name := C.CString(name) 110 | C.swex_set_jpl_file(_name) 111 | C.free(unsafe.Pointer(_name)) 112 | } 113 | 114 | func setTopo(lng, lat, alt float64) { 115 | C.swex_set_topo(C.double(lng), C.double(lat), C.double(alt)) 116 | } 117 | 118 | func setSidMode(mode swego.Ayanamsa, t0, ayanT0 float64) { 119 | C.swex_set_sid_mode(C.int32_t(mode), C.double(t0), C.double(ayanT0)) 120 | } 121 | 122 | func closeEphemeris() { 123 | C.swe_close() 124 | } 125 | 126 | func planetName(pl swego.Planet) string { 127 | var _name [C.AS_MAXCH]C.char 128 | C.swe_get_planet_name(C.int(pl), &_name[0]) 129 | return C.GoString(&_name[0]) 130 | } 131 | 132 | type _calcFunc func(jd C.double, fl C.int32, xx *C.double, err *C.char) C.int32 133 | 134 | func _calc(jd float64, fl int32, fn _calcFunc) (_ []float64, cfl int, err error) { 135 | _jd := C.double(jd) 136 | _fl := C.int32(fl) 137 | 138 | // Both the float64 and C.double types are defined as an IEEE-754 64-bit 139 | // floating-point number. This means the representation in memory of a 140 | // float64 array is equivalent to that of a C.double array. It means it is 141 | // possible to cast between the two types. In Go land such operation is 142 | // considered unsafe, hence the use of the unsafe package. 143 | var xx [6]float64 144 | _xx := (*C.double)(unsafe.Pointer(&xx[0])) 145 | 146 | err = withError(func(err *C.char) bool { 147 | cfl = int(fn(_jd, _fl, _xx, err)) 148 | return cfl == C.ERR 149 | }) 150 | 151 | return xx[:], cfl, err 152 | } 153 | 154 | func calc(et float64, pl swego.Planet, fl int32) ([]float64, int, error) { 155 | return _calc(et, fl, func(jd C.double, fl C.int32, xx *C.double, err *C.char) C.int32 { 156 | return C.swe_calc(jd, C.int(pl), fl, xx, err) 157 | }) 158 | } 159 | 160 | func calcUT(ut float64, pl swego.Planet, fl int32) ([]float64, int, error) { 161 | return _calc(ut, fl, func(jd C.double, fl C.int32, xx *C.double, err *C.char) C.int32 { 162 | return C.swe_calc_ut(jd, C.int32(pl), fl, xx, err) 163 | }) 164 | } 165 | 166 | type _nodApsFunc func(jd C.double, pl, fl, m C.int32, nasc, ndsc, peri, aphe *C.double, err *C.char) C.int32 167 | 168 | func _nodAps(jd float64, pl swego.Planet, fl int32, m swego.NodApsMethod, fn _nodApsFunc) (_, _, _, _ []float64, err error) { 169 | _jd := C.double(jd) 170 | _pl := C.int32(pl) 171 | _fl := C.int32(fl) 172 | _m := C.int32(m) 173 | 174 | // Both the float64 and C.double types are defined as an IEEE-754 64-bit 175 | // floating-point number. This means the representation in memory of a 176 | // float64 array is equivalent to that of a C.double array. It means it is 177 | // possible to cast between the two types. In Go land such operation is 178 | // considered unsafe, hence the use of the unsafe package. 179 | var nasc, ndsc, peri, aphe [6]float64 180 | _nasc := (*C.double)(unsafe.Pointer(&nasc[0])) 181 | _ndsc := (*C.double)(unsafe.Pointer(&ndsc[0])) 182 | _peri := (*C.double)(unsafe.Pointer(&peri[0])) 183 | _aphe := (*C.double)(unsafe.Pointer(&aphe[0])) 184 | 185 | err = withError(func(err *C.char) bool { 186 | return C.ERR == fn(_jd, _pl, _fl, _m, _nasc, _ndsc, _peri, _aphe, err) 187 | }) 188 | 189 | return nasc[:], ndsc[:], peri[:], aphe[:], err 190 | } 191 | 192 | func nodAps(et float64, pl swego.Planet, fl int32, m swego.NodApsMethod) (nasc, ndsc, peri, aphe []float64, err error) { 193 | return _nodAps(et, pl, fl, m, func(jd C.double, pl, fl, m C.int32, nasc, ndsc, peri, aphe *C.double, err *C.char) C.int32 { 194 | return C.swe_nod_aps(jd, pl, fl, m, nasc, ndsc, peri, aphe, err) 195 | }) 196 | } 197 | 198 | func nodApsUT(ut float64, pl swego.Planet, fl int32, m swego.NodApsMethod) (nasc, ndsc, peri, aphe []float64, err error) { 199 | return _nodAps(ut, pl, fl, m, func(jd C.double, pl, fl, m C.int32, nasc, ndsc, peri, aphe *C.double, err *C.char) C.int32 { 200 | return C.swe_nod_aps_ut(jd, pl, fl, m, nasc, ndsc, peri, aphe, err) 201 | }) 202 | } 203 | 204 | type _getAyanamsaExFunc func(jd C.double, fl C.int32, aya *C.double, err *C.char) C.int32 205 | 206 | func _getAyanamsaEx(jd float64, fl int32, fn _getAyanamsaExFunc) (aya float64, err error) { 207 | _jd := C.double(jd) 208 | _fl := C.int32(fl) 209 | _aya := (*C.double)(unsafe.Pointer(&aya)) 210 | 211 | err = withError(func(err *C.char) bool { 212 | return C.ERR == fn(_jd, _fl, _aya, err) 213 | }) 214 | 215 | return 216 | } 217 | 218 | func getAyanamsaEx(et float64, fl int32) (float64, error) { 219 | return _getAyanamsaEx(et, fl, func(jd C.double, fl C.int32, aya *C.double, err *C.char) C.int32 { 220 | return C.swe_get_ayanamsa_ex(jd, fl, aya, err) 221 | }) 222 | } 223 | 224 | func getAyanamsaExUT(ut float64, fl int32) (float64, error) { 225 | return _getAyanamsaEx(ut, fl, func(jd C.double, fl C.int32, aya *C.double, err *C.char) C.int32 { 226 | return C.swe_get_ayanamsa_ex_ut(jd, fl, aya, err) 227 | }) 228 | } 229 | 230 | func getAyanamsaName(ayan swego.Ayanamsa) string { 231 | return C.GoString(C.swe_get_ayanamsa_name(C.int32(ayan))) 232 | } 233 | 234 | func julDay(y, m, d int, h float64, gf int) float64 { 235 | _y := C.int(y) 236 | _m := C.int(m) 237 | _d := C.int(d) 238 | _h := C.double(h) 239 | _gf := C.int(gf) 240 | return float64(C.swe_julday(_y, _m, _d, _h, _gf)) 241 | } 242 | 243 | func revJul(jd float64, gf int) (y, m, d int, h float64) { 244 | _jd := C.double(jd) 245 | _gf := C.int(gf) 246 | var _y, _m, _d C.int 247 | var _h C.double 248 | 249 | C.swe_revjul(_jd, _gf, &_y, &_m, &_d, &_h) 250 | 251 | y = int(_y) 252 | m = int(_m) 253 | d = int(_d) 254 | h = float64(_h) 255 | return 256 | } 257 | 258 | func utcToJD(y, m, d, h, i int, s float64, gf int) (et, ut float64, err error) { 259 | _y := C.int32(y) 260 | _m := C.int32(m) 261 | _d := C.int32(d) 262 | _h := C.int32(h) 263 | _i := C.int32(i) 264 | _s := C.double(s) 265 | _gf := C.int32(gf) 266 | var jds [2]C.double 267 | 268 | err = withError(func(err *C.char) bool { 269 | return C.ERR == C.swe_utc_to_jd(_y, _m, _d, _h, _i, _s, _gf, &jds[0], err) 270 | }) 271 | 272 | et = float64(jds[0]) 273 | ut = float64(jds[1]) 274 | return 275 | } 276 | 277 | type _jdToUTCFunc func(jd C.double, gf C.int32, y, m, d, h, i *C.int32, s *C.double) 278 | 279 | func _jdToUTC(jd float64, gf int, fn _jdToUTCFunc) (y, m, d, h, i int, s float64) { 280 | _jd := C.double(jd) 281 | _gf := C.int32(gf) 282 | var _y, _m, _d, _h, _i C.int32 283 | var _s C.double 284 | 285 | fn(_jd, _gf, &_y, &_m, &_d, &_h, &_i, &_s) 286 | 287 | y = int(_y) 288 | m = int(_m) 289 | d = int(_d) 290 | h = int(_h) 291 | i = int(_i) 292 | s = float64(_s) 293 | return 294 | } 295 | 296 | func jdETToUTC(et float64, gf int) (y, m, d, h, i int, s float64) { 297 | return _jdToUTC(et, gf, func(jd C.double, gf C.int32, y, m, d, h, i *C.int32, s *C.double) { 298 | C.swe_jdet_to_utc(jd, gf, y, m, d, h, i, s) 299 | }) 300 | } 301 | 302 | func jdUT1ToUTC(ut float64, gf int) (y, m, d, h, i int, s float64) { 303 | return _jdToUTC(ut, gf, func(jd C.double, gf C.int32, y, m, d, h, i *C.int32, s *C.double) { 304 | C.swe_jdut1_to_utc(jd, gf, y, m, d, h, i, s) 305 | }) 306 | } 307 | 308 | type _housesFunc func(lat C.double, hsys C.int, cusps, ascmc *C.double) C.int 309 | 310 | func _houses(lat float64, hsys swego.HSys, fn _housesFunc) (_, _ []float64, err error) { 311 | _lat := C.double(lat) 312 | _hsys := C.int(hsys) 313 | 314 | // Both the float64 and C.double types are defined as an IEEE-754 64-bit 315 | // floating-point number. This means the representation in memory of a 316 | // float64 array is equivalent to that of a C.double array. It means it is 317 | // possible to cast between the two types. In Go land such operation is 318 | // considered unsafe, hence the use of the unsafe package. 319 | var cusps [37]float64 320 | var ascmc [10]float64 321 | _cusps := (*C.double)(unsafe.Pointer(&cusps[0])) 322 | _ascmc := (*C.double)(unsafe.Pointer(&ascmc[0])) 323 | 324 | if C.ERR == fn(_lat, _hsys, _cusps, _ascmc) { 325 | err = swego.Error("") 326 | } 327 | 328 | // The house system letters are practically constants. If those are changed, 329 | // it is done via a new version of the Swiss Ephemeris anyway. Also this is 330 | // already fairly low level code, so a check for the Gauquelin 'house system' 331 | // letter is no problem. 332 | n := 13 333 | if hsys == 'G' || hsys == 'g' { 334 | n = 37 335 | } 336 | 337 | return cusps[:n:n], ascmc[:], err 338 | } 339 | 340 | func housesEx(ut float64, fl int32, lat, lng float64, hsys swego.HSys) ([]float64, []float64, error) { 341 | return _houses(lat, hsys, func(lat C.double, hsys C.int, cusps, ascmc *C.double) C.int { 342 | _jd := C.double(ut) 343 | _fl := C.int32(fl) 344 | _lng := C.double(lng) 345 | return C.swe_houses_ex(_jd, _fl, lat, _lng, hsys, cusps, ascmc) 346 | }) 347 | } 348 | 349 | func housesARMC(armc, lat, eps float64, hsys swego.HSys) ([]float64, []float64, error) { 350 | return _houses(lat, hsys, func(lat C.double, hsys C.int, cusps, ascmc *C.double) C.int { 351 | _armc := C.double(armc) 352 | _eps := C.double(eps) 353 | return C.swe_houses_armc(_armc, lat, _eps, hsys, cusps, ascmc) 354 | }) 355 | } 356 | 357 | func housePos(armc, geolat, eps float64, hsys swego.HSys, pllng, pllat float64) (pos float64, err error) { 358 | _armc := C.double(armc) 359 | _lat := C.double(geolat) 360 | _eps := C.double(eps) 361 | _hsys := C.int(hsys) 362 | xpin := [2]C.double{C.double(pllat), C.double(pllng)} 363 | 364 | err = withError(func(err *C.char) bool { 365 | pos = float64(C.swe_house_pos(_armc, _lat, _eps, _hsys, &xpin[0], err)) 366 | return *err != '\000' 367 | }) 368 | 369 | return 370 | } 371 | 372 | func houseName(hsys swego.HSys) string { 373 | return C.GoString(C.swe_house_name(C.int(hsys))) 374 | } 375 | 376 | func deltaTEx(jd float64, eph int32) (deltaT float64, err error) { 377 | err = withError(func(err *C.char) bool { 378 | deltaT = float64(C.swe_deltat_ex(C.double(jd), C.int32(eph), err)) 379 | return *err != '\000' 380 | }) 381 | 382 | return 383 | } 384 | 385 | func setDeltaTUserDef(v float64) { 386 | C.swe_set_delta_t_userdef(C.double(v)) 387 | } 388 | 389 | func timeEqu(jd float64) (E float64, err error) { 390 | var _E C.double 391 | 392 | err = withError(func(err *C.char) bool { 393 | return C.ERR == C.swe_time_equ(C.double(jd), &_E, err) 394 | }) 395 | 396 | E = float64(_E) 397 | return 398 | } 399 | 400 | type _convertLMTLATFunc func(from, lng C.double, to *C.double, err *C.char) C.int32 401 | 402 | func _convertLMTLAT(from, geolon float64, fn _convertLMTLATFunc) (to float64, err error) { 403 | _from := C.double(from) 404 | _lng := C.double(geolon) 405 | var _to C.double 406 | 407 | err = withError(func(err *C.char) bool { 408 | return C.ERR == fn(_from, _lng, &_to, err) 409 | }) 410 | 411 | to = float64(_to) 412 | return 413 | } 414 | 415 | func lmtToLAT(jdLMT, geolon float64) (float64, error) { 416 | return _convertLMTLAT(jdLMT, geolon, func(from, lng C.double, to *C.double, err *C.char) C.int32 { 417 | return C.swe_lmt_to_lat(from, lng, to, err) 418 | }) 419 | } 420 | 421 | func latToLMT(jdLAT, geolon float64) (float64, error) { 422 | return _convertLMTLAT(jdLAT, geolon, func(from, lng C.double, to *C.double, err *C.char) C.int32 { 423 | return C.swe_lat_to_lmt(from, lng, to, err) 424 | }) 425 | } 426 | 427 | func sidTime0(ut, eps, nut float64) float64 { 428 | _ut := C.double(ut) 429 | _eps := C.double(eps) 430 | _nut := C.double(nut) 431 | return float64(C.swe_sidtime0(_ut, _eps, _nut)) 432 | } 433 | 434 | func sidTime(ut float64) float64 { 435 | return float64(C.swe_sidtime(C.double(ut))) 436 | } 437 | -------------------------------------------------------------------------------- /swecgo/swecgowrap.go: -------------------------------------------------------------------------------- 1 | // +build linux,cgo darwin,cgo 2 | 3 | package swecgo 4 | 5 | import "github.com/astrotools/swego" 6 | 7 | // acquire locks the wrapper for exclusive library access. 8 | // release unlocks the wrapper from exclusive library access. 9 | 10 | var _ Library = (*wrapper)(nil) // assert interface 11 | 12 | func (w *wrapper) Version() (string, error) { 13 | return Version, nil 14 | } 15 | 16 | func (w *wrapper) SetPath(ephepath string) { 17 | w.acquire() 18 | setEphePath(ephepath) 19 | w.release() 20 | } 21 | 22 | func (w *wrapper) Close() { 23 | w.acquire() 24 | closeEphemeris() 25 | w.release() 26 | } 27 | 28 | const resetDeltaT = -1e-10 29 | 30 | func setDeltaT(dt *float64) { 31 | var f float64 32 | if dt == nil { 33 | f = resetDeltaT 34 | } else { 35 | f = *dt 36 | } 37 | 38 | setDeltaTUserDef(f) 39 | } 40 | 41 | func setCalcFlagsState(fl *swego.CalcFlags) int32 { 42 | if fl == nil { 43 | setDeltaT(nil) 44 | return 0 45 | } 46 | 47 | if (fl.Flags & flgTopo) == flgTopo { 48 | var lng, lat, alt float64 49 | 50 | if fl.TopoLoc != nil { 51 | lng = fl.TopoLoc.Long 52 | lat = fl.TopoLoc.Lat 53 | alt = fl.TopoLoc.Alt 54 | } 55 | 56 | setTopo(lng, lat, alt) 57 | } 58 | 59 | if (fl.Flags & flgSidereal) == flgSidereal { 60 | var mode swego.Ayanamsa 61 | var t0, ayanT0 float64 62 | 63 | if fl.SidMode != nil { 64 | mode = fl.SidMode.Mode 65 | t0 = fl.SidMode.T0 66 | ayanT0 = fl.SidMode.AyanT0 67 | } 68 | 69 | setSidMode(mode, t0, ayanT0) 70 | } 71 | 72 | if fl.JPLFile != "" { 73 | fl.JPLFile = swego.FnameDft 74 | } 75 | 76 | setJPLFile(fl.JPLFile) 77 | setDeltaT(fl.DeltaT) 78 | return fl.Flags 79 | } 80 | 81 | func (w *wrapper) PlanetName(pl swego.Planet) (string, error) { 82 | w.acquire() 83 | name := planetName(pl) 84 | w.release() 85 | return name, nil 86 | } 87 | 88 | func (w *wrapper) Calc(et float64, pl swego.Planet, fl *swego.CalcFlags) ([]float64, int, error) { 89 | w.acquire() 90 | flags := setCalcFlagsState(fl) 91 | xx, cfl, err := calc(et, pl, flags) 92 | w.release() 93 | return xx, cfl, err 94 | } 95 | 96 | func (w *wrapper) CalcUT(ut float64, pl swego.Planet, fl *swego.CalcFlags) ([]float64, int, error) { 97 | w.acquire() 98 | flags := setCalcFlagsState(fl) 99 | xx, cfl, err := calcUT(ut, pl, flags) 100 | w.release() 101 | return xx, cfl, err 102 | } 103 | 104 | func (w *wrapper) NodAps(et float64, pl swego.Planet, fl *swego.CalcFlags, m swego.NodApsMethod) (nasc, ndsc, peri, aphe []float64, err error) { 105 | w.acquire() 106 | flags := setCalcFlagsState(fl) 107 | nasc, ndsc, peri, aphe, err = nodAps(et, pl, flags, m) 108 | w.release() 109 | return 110 | } 111 | 112 | func (w *wrapper) NodApsUT(ut float64, pl swego.Planet, fl *swego.CalcFlags, m swego.NodApsMethod) (nasc, ndsc, peri, aphe []float64, err error) { 113 | w.acquire() 114 | flags := setCalcFlagsState(fl) 115 | nasc, ndsc, peri, aphe, err = nodApsUT(ut, pl, flags, m) 116 | w.release() 117 | return 118 | } 119 | 120 | func (w *wrapper) GetAyanamsaEx(et float64, fl *swego.AyanamsaExFlags) (float64, error) { 121 | w.acquire() 122 | setSidMode(fl.SidMode.Mode, fl.SidMode.T0, fl.SidMode.AyanT0) 123 | f, err := getAyanamsaEx(et, fl.Flags) 124 | w.release() 125 | return f, err 126 | } 127 | 128 | func (w *wrapper) GetAyanamsaExUT(ut float64, fl *swego.AyanamsaExFlags) (float64, error) { 129 | w.acquire() 130 | setSidMode(fl.SidMode.Mode, fl.SidMode.T0, fl.SidMode.AyanT0) 131 | f, err := getAyanamsaExUT(ut, fl.Flags) 132 | w.release() 133 | return f, err 134 | } 135 | 136 | func (w *wrapper) GetAyanamsaName(ayan swego.Ayanamsa) (string, error) { 137 | w.acquire() 138 | name := getAyanamsaName(ayan) 139 | w.release() 140 | return name, nil 141 | } 142 | 143 | func (w *wrapper) JulDay(y, m, d int, h float64, ct swego.CalType) (float64, error) { 144 | jd := julDay(y, m, d, h, int(ct)) 145 | return jd, nil 146 | } 147 | 148 | func (w *wrapper) RevJul(jd float64, ct swego.CalType) (y, m, d int, h float64, err error) { 149 | y, m, d, h = revJul(jd, int(ct)) 150 | return y, m, d, h, nil 151 | } 152 | 153 | func (w *wrapper) UTCToJD(y, m, d, h, i int, s float64, fl *swego.DateConvertFlags) (et, ut float64, err error) { 154 | w.acquire() 155 | setDeltaT(fl.DeltaT) 156 | et, ut, err = utcToJD(y, m, d, h, i, s, int(fl.Calendar)) 157 | w.release() 158 | return 159 | } 160 | 161 | func (w *wrapper) JdETToUTC(et float64, fl *swego.DateConvertFlags) (y, m, d, h, i int, s float64, err error) { 162 | w.acquire() 163 | setDeltaT(fl.DeltaT) 164 | y, m, d, h, i, s = jdETToUTC(et, int(fl.Calendar)) 165 | w.release() 166 | return y, m, d, h, i, s, nil 167 | } 168 | 169 | func (w *wrapper) JdUT1ToUTC(ut1 float64, fl *swego.DateConvertFlags) (y, m, d, h, i int, s float64, err error) { 170 | w.acquire() 171 | setDeltaT(fl.DeltaT) 172 | y, m, d, h, i, s = jdUT1ToUTC(ut1, int(fl.Calendar)) 173 | w.release() 174 | return y, m, d, h, i, s, nil 175 | } 176 | 177 | func (w *wrapper) HousesEx(ut float64, fl *swego.HousesExFlags, geolat, geolon float64, hsys swego.HSys) ([]float64, []float64, error) { 178 | w.acquire() 179 | var flags int32 180 | if fl != nil { 181 | flags = fl.Flags 182 | if (flags & flgSidereal) == flgSidereal { 183 | setSidMode(fl.SidMode.Mode, fl.SidMode.T0, fl.SidMode.AyanT0) 184 | } 185 | 186 | setDeltaT(fl.DeltaT) 187 | } else { 188 | setDeltaT(nil) 189 | } 190 | 191 | cusps, ascmc, err := housesEx(ut, flags, geolat, geolon, hsys) 192 | w.release() 193 | return cusps, ascmc, err 194 | } 195 | 196 | func (w *wrapper) HousesARMC(armc, geolat, eps float64, hsys swego.HSys) ([]float64, []float64, error) { 197 | w.acquire() 198 | cusps, ascmc, err := housesARMC(armc, geolat, eps, hsys) 199 | w.release() 200 | return cusps, ascmc, err 201 | } 202 | 203 | func (w *wrapper) HousePos(armc, geolat, eps float64, hsys swego.HSys, pllng, pllat float64) (float64, error) { 204 | w.acquire() 205 | pos, err := housePos(armc, geolat, eps, hsys, pllng, pllat) 206 | w.release() 207 | return pos, err 208 | } 209 | 210 | func (w *wrapper) HouseName(hsys swego.HSys) (string, error) { 211 | w.acquire() 212 | name := houseName(hsys) 213 | w.release() 214 | return name, nil 215 | } 216 | 217 | func (w *wrapper) DeltaTEx(jd float64, eph swego.Ephemeris) (float64, error) { 218 | w.acquire() 219 | dt, err := deltaTEx(jd, int32(eph)) 220 | w.release() 221 | return dt, err 222 | } 223 | 224 | func setTimeEquDeltaT(fl *swego.TimeEquFlags) { 225 | if fl == nil { 226 | setDeltaT(nil) 227 | } else { 228 | setDeltaT(fl.DeltaT) 229 | } 230 | } 231 | 232 | func (w *wrapper) TimeEqu(jd float64, fl *swego.TimeEquFlags) (float64, error) { 233 | w.acquire() 234 | setTimeEquDeltaT(fl) 235 | f, err := timeEqu(jd) 236 | w.release() 237 | return f, err 238 | } 239 | 240 | func (w *wrapper) LMTToLAT(lmt, geolon float64, fl *swego.TimeEquFlags) (float64, error) { 241 | w.acquire() 242 | setTimeEquDeltaT(fl) 243 | lat, err := lmtToLAT(lmt, geolon) 244 | w.release() 245 | return lat, err 246 | } 247 | 248 | func (w *wrapper) LATToLMT(lat, geolon float64, fl *swego.TimeEquFlags) (float64, error) { 249 | w.acquire() 250 | setTimeEquDeltaT(fl) 251 | lmt, err := latToLMT(lat, geolon) 252 | w.release() 253 | return lmt, err 254 | } 255 | 256 | func setSidTimeDeltaT(fl *swego.SidTimeFlags) { 257 | if fl == nil { 258 | setDeltaT(nil) 259 | } else { 260 | setDeltaT(fl.DeltaT) 261 | } 262 | } 263 | 264 | func (w *wrapper) SidTime0(ut, eps, nut float64, fl *swego.SidTimeFlags) (float64, error) { 265 | w.acquire() 266 | setSidTimeDeltaT(fl) 267 | f := sidTime0(ut, eps, nut) 268 | w.release() 269 | return f, nil 270 | } 271 | 272 | func (w *wrapper) SidTime(ut float64, fl *swego.SidTimeFlags) (float64, error) { 273 | w.acquire() 274 | setSidTimeDeltaT(fl) 275 | f := sidTime(ut) 276 | w.release() 277 | return f, nil 278 | } 279 | -------------------------------------------------------------------------------- /swecgo/swecl.c: -------------------------------------------------------------------------------- 1 | ../swisseph/swecl.c -------------------------------------------------------------------------------- /swecgo/swedate.c: -------------------------------------------------------------------------------- 1 | ../swisseph/swedate.c -------------------------------------------------------------------------------- /swecgo/swedate.h: -------------------------------------------------------------------------------- 1 | ../swisseph/swedate.h -------------------------------------------------------------------------------- /swecgo/swehel.c: -------------------------------------------------------------------------------- 1 | ../swisseph/swehel.c -------------------------------------------------------------------------------- /swecgo/swehouse.c: -------------------------------------------------------------------------------- 1 | ../swisseph/swehouse.c -------------------------------------------------------------------------------- /swecgo/swehouse.h: -------------------------------------------------------------------------------- 1 | ../swisseph/swehouse.h -------------------------------------------------------------------------------- /swecgo/swejpl.c: -------------------------------------------------------------------------------- 1 | ../swisseph/swejpl.c -------------------------------------------------------------------------------- /swecgo/swejpl.h: -------------------------------------------------------------------------------- 1 | ../swisseph/swejpl.h -------------------------------------------------------------------------------- /swecgo/swemmoon.c: -------------------------------------------------------------------------------- 1 | ../swisseph/swemmoon.c -------------------------------------------------------------------------------- /swecgo/swemplan.c: -------------------------------------------------------------------------------- 1 | ../swisseph/swemplan.c -------------------------------------------------------------------------------- /swecgo/swemptab.h: -------------------------------------------------------------------------------- 1 | ../swisseph/swemptab.h -------------------------------------------------------------------------------- /swecgo/swenut2000a.h: -------------------------------------------------------------------------------- 1 | ../swisseph/swenut2000a.h -------------------------------------------------------------------------------- /swecgo/sweodef.h: -------------------------------------------------------------------------------- 1 | ../swisseph/sweodef.h -------------------------------------------------------------------------------- /swecgo/sweph.c: -------------------------------------------------------------------------------- 1 | ../swisseph/sweph.c -------------------------------------------------------------------------------- /swecgo/sweph.h: -------------------------------------------------------------------------------- 1 | ../swisseph/sweph.h -------------------------------------------------------------------------------- /swecgo/swephexp.h: -------------------------------------------------------------------------------- 1 | ../swisseph/swephexp.h -------------------------------------------------------------------------------- /swecgo/swephlib.c: -------------------------------------------------------------------------------- 1 | ../swisseph/swephlib.c -------------------------------------------------------------------------------- /swecgo/swephlib.h: -------------------------------------------------------------------------------- 1 | ../swisseph/swephlib.h -------------------------------------------------------------------------------- /swecgo/sweversion.h: -------------------------------------------------------------------------------- 1 | ../swex/sweversion.h -------------------------------------------------------------------------------- /swecgo/swex.c: -------------------------------------------------------------------------------- 1 | ../swex/swex.c -------------------------------------------------------------------------------- /swecgo/swex.h: -------------------------------------------------------------------------------- 1 | ../swex/swex.h -------------------------------------------------------------------------------- /swego.go: -------------------------------------------------------------------------------- 1 | // Package swego defines an interface for interfacing with the Swiss Ephemeris. 2 | package swego 3 | 4 | // Error represents an error reported by the Swiss Ephemeris library. 5 | type Error string 6 | 7 | func (e Error) Error() string { 8 | return "swisseph: " + string(e) 9 | } 10 | 11 | // Planet is the type of planet constants. 12 | type Planet int 13 | 14 | // Ayanamsa is the type of sidereal mode constants. 15 | type Ayanamsa int32 16 | 17 | // SidMode represents library state changed by swe_set_sid_mode. 18 | type SidMode struct { 19 | Mode Ayanamsa 20 | T0 float64 21 | AyanT0 float64 22 | } 23 | 24 | // GeoLoc represents a geographic location. 25 | type GeoLoc struct { 26 | Long float64 27 | Lat float64 28 | Alt float64 29 | } 30 | 31 | // CalcFlags represents the library state of swe_calc and swe_calc_ut. 32 | type CalcFlags struct { 33 | Flags int32 34 | TopoLoc *GeoLoc // Arguments to swe_set_topo 35 | SidMode *SidMode // Arguments to swe_set_sid_mode 36 | JPLFile string // Argument to swe_set_jpl_file 37 | DeltaT *float64 // Argument to swe_set_delta_t_userdef, nil resets it. 38 | } 39 | 40 | // Copy returns a copy of the calculation flags fl. 41 | func (fl *CalcFlags) Copy() *CalcFlags { 42 | copy := new(CalcFlags) 43 | *copy = *fl 44 | return copy 45 | } 46 | 47 | // Ephemeris represents an ephemeris implemented in the C library. 48 | type Ephemeris int32 49 | 50 | // SetEphemeris sets the ephemeris flag in fl. 51 | func (fl *CalcFlags) SetEphemeris(eph Ephemeris) { fl.Flags |= int32(eph) } 52 | 53 | // SetDeltaT sets f as delta T in flags object fl. 54 | // Set fl.DeltaT to nil to reset the value within the Swiss Ephemeris. 55 | func (fl *CalcFlags) SetDeltaT(f float64) { fl.DeltaT = &f } 56 | 57 | // NodApsMethod is the type of Nodbit constants. 58 | type NodApsMethod int32 59 | 60 | // AyanamsaExFlags represents the library state of swe_get_ayanamsa_ex and 61 | // swe_get_ayanamsa_ex_ut. 62 | type AyanamsaExFlags struct { 63 | Flags int32 64 | SidMode *SidMode // Argument to swe_set_sid_mode 65 | DeltaT *float64 // Argument to swe_set_delta_t_userdef, nil resets it. 66 | } 67 | 68 | // SetDeltaT sets f as delta T in flags object fl. 69 | // Set fl.DeltaT to nil to reset the value within the Swiss Ephemeris. 70 | func (fl *AyanamsaExFlags) SetDeltaT(f float64) { fl.DeltaT = &f } 71 | 72 | // CalType represents the calendar type used in julian date conversions. 73 | type CalType int 74 | 75 | // DateConvertFlags represents the library state of swe_utc_to_jd, 76 | // swe_jdet_to_utc and swe_jdut1_to_utc. 77 | type DateConvertFlags struct { 78 | Calendar CalType // clearifies the input year, Julian or Gregorian 79 | DeltaT *float64 80 | } 81 | 82 | // SetDeltaT sets f as delta T in flags object fl. 83 | // Set fl.DeltaT to nil to reset the value within the Swiss Ephemeris. 84 | func (fl *DateConvertFlags) SetDeltaT(f float64) { fl.DeltaT = &f } 85 | 86 | // HousesExFlags represents library state of swe_houses_ex in a stateless way. 87 | type HousesExFlags struct { 88 | Flags int32 89 | SidMode *SidMode // Argument to swe_set_sid_mode 90 | DeltaT *float64 // Argument to swe_set_delta_t_userdef, nil resets it. 91 | } 92 | 93 | // SetDeltaT sets f as delta T in flags object fl. 94 | // Set fl.DeltaT to nil to reset the value within the Swiss Ephemeris. 95 | func (fl *HousesExFlags) SetDeltaT(f float64) { fl.DeltaT = &f } 96 | 97 | // HSys represents house system identifiers used in the C library. 98 | type HSys byte 99 | 100 | // NewHSys validates the input and returns a HSys value if valid. 101 | func NewHSys(c byte) (hsys HSys, ok bool) { 102 | if c == 'i' { 103 | return HSys(c), true 104 | } 105 | 106 | // It's trivial to convert lower case to upper case in ASCII. 107 | if 'a' <= c && c <= 'z' { 108 | c -= 'a' - 'A' 109 | } 110 | 111 | switch c { 112 | case 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'O', 113 | 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y': 114 | return HSys(c), true 115 | default: 116 | return 0, false 117 | } 118 | } 119 | 120 | // TimeEquFlags represents the library state of swe_time_equ, swe_lmt_to_lat 121 | // and swe_lat_to_lmt. 122 | type TimeEquFlags struct { 123 | DeltaT *float64 124 | } 125 | 126 | // SetDeltaT sets f as delta T in flags object fl. 127 | // Set fl.DeltaT to nil to reset the value within the Swiss Ephemeris. 128 | func (fl *TimeEquFlags) SetDeltaT(f float64) { fl.DeltaT = &f } 129 | 130 | // SidTimeFlags represents the library state of swe_sidtime0 and swe_sidtime. 131 | type SidTimeFlags struct { 132 | DeltaT *float64 133 | } 134 | 135 | // SetDeltaT sets f as delta T in flags object fl. 136 | // Set fl.DeltaT to nil to reset the value within the Swiss Ephemeris. 137 | func (fl *SidTimeFlags) SetDeltaT(f float64) { fl.DeltaT = &f } 138 | 139 | // Interface defines a standardized way for interfacing with the Swiss 140 | // Ephemeris library from Go. 141 | type Interface interface { 142 | // Version returns the version of the Swiss Ephemeris. 143 | Version() (string, error) 144 | 145 | // PlanetName returns the name of planet pl. 146 | PlanetName(pl Planet) (string, error) 147 | 148 | // Calc computes the position and optionally the speed of planet pl at Julian 149 | // Date (in Ephemeris Time) et with calculation flags fl. 150 | Calc(et float64, pl Planet, fl *CalcFlags) (xx []float64, cfl int, err error) 151 | // CalcUT computes the position and optionally the speed of planet pl at 152 | // Julian Date (in Universal Time) ut with calculation flags fl. Within the C 153 | // library swe_deltat is called to convert Universal Time to Ephemeris Time. 154 | CalcUT(ut float64, pl Planet, fl *CalcFlags) (xx []float64, cfl int, err error) 155 | 156 | // NodAps computes the positions of planetary nodes and apsides (perihelia, 157 | // aphelia, second focal points of the orbital ellipses) for planet pl at 158 | // Julian Date (in Ephemeris Time) et with calculation flags fl using method 159 | // m. 160 | NodAps(et float64, pl Planet, fl *CalcFlags, m NodApsMethod) (nasc, ndsc, peri, aphe []float64, err error) 161 | // NodApsUT computes the positions of planetary nodes and apsides (perihelia, 162 | // aphelia, second focal points of the orbital ellipses) for planet pl at 163 | // Julian Date (in Ephemeris Time) et with calculation flags fl using method 164 | // m. Within the C library swe_deltat is called to convert Universal Time to 165 | // Ephemeris Time. 166 | NodApsUT(ut float64, pl Planet, fl *CalcFlags, m NodApsMethod) (nasc, ndsc, peri, aphe []float64, err error) 167 | 168 | // GetAyanamsaEx returns the ayanamsa for Julian Date (in Ephemeris Time) et. 169 | // It is equal to GetAyanamsa but uses the ΔT consistent with the ephemeris 170 | // passed in fl.Flags. 171 | GetAyanamsaEx(et float64, fl *AyanamsaExFlags) (float64, error) 172 | // GetAyanamsaExUT returns the ayanamsa for Julian Date (in Universal Time) ut. 173 | // It is equal to GetAyanamsaUT but uses the ΔT consistent with the ephemeris 174 | // passed in fl.Flags. 175 | GetAyanamsaExUT(ut float64, fl *AyanamsaExFlags) (float64, error) 176 | // GetAyanamsaName returns the name of sidmode. 177 | GetAyanamsaName(ayan Ayanamsa) (string, error) 178 | 179 | // JulDay returns the corresponding Julian Date for the given date. 180 | // Calendar type ct is used to clearify the year y, Julian or Gregorian. 181 | JulDay(y, m, d int, h float64, ct CalType) (float64, error) 182 | // RevJul returns the corresponding calendar date for the given Julian Date. 183 | // Calendar type ct is used to clearify the year y, Julian or Gregorian. 184 | RevJul(jd float64, ct CalType) (y, m, d int, h float64, err error) 185 | // UTCToJD returns the corresponding Julian Date in Ephemeris and Universal 186 | // Time for the given date and accounts for leap seconds in the conversion. 187 | UTCToJD(y, m, d, h, i int, s float64, fl *DateConvertFlags) (et, ut float64, err error) 188 | // JdETToUTC returns the corresponding calendar date for the given Julian 189 | // Date in Ephemeris Time and accounts for leap seconds in the conversion. 190 | JdETToUTC(et float64, fl *DateConvertFlags) (y, m, d, h, i int, s float64, err error) 191 | // JdETToUTC returns the corresponding calendar date for the given Julian 192 | // Date in Universal Time and accounts for leap seconds in the conversion. 193 | JdUT1ToUTC(ut1 float64, fl *DateConvertFlags) (y, m, d, h, i int, s float64, err error) 194 | 195 | // HousesEx returns the house cusps and related positions for the given 196 | // geographic location using the given house system and the provided flags 197 | // (reference frame). The return values may contain data in case of an error. 198 | // Geolat and geolon are in degrees. 199 | HousesEx(ut float64, fl *HousesExFlags, geolat, geolon float64, hsys HSys) ([]float64, []float64, error) 200 | // HousesArmc returns the house cusps and related positions for the given 201 | // geographic location using the given house system, ecliptic obliquity and 202 | // ARMC (also known as RAMC). The return values may contain data in case of 203 | // an error. ARMC, geolat, geolon and eps are in degrees. 204 | HousesARMC(armc, geolat, eps float64, hsys HSys) ([]float64, []float64, error) 205 | // HousePos returns the house position for the ecliptic longitude and 206 | // latitude of a planet for a given ARMC (also known as RAMC) and geocentric 207 | // latitude using the given house system. ARMC, geolat, eps, pllng and pllat 208 | // are in degrees. 209 | // Before calling HousePos either Houses, HousesEx or HousesARMC should be 210 | // called first. 211 | HousePos(armc, geolat, eps float64, hsys HSys, pllng, pllat float64) (float64, error) 212 | // HouseName returns the name of the house system. 213 | HouseName(hsys HSys) (string, error) 214 | 215 | // DeltaTEx returns the ΔT for the Julian Date jd. 216 | DeltaTEx(jd float64, eph Ephemeris) (float64, error) 217 | 218 | // TimeEqu returns the difference between local apparent and local mean time 219 | // in days for the given Julian Date (in Universal Time). 220 | TimeEqu(jd float64, fl *TimeEquFlags) (float64, error) 221 | // LMTToLAT returns the local apparent time for the given Julian Date (in 222 | // Local Mean Time) and the geographic longitude. 223 | LMTToLAT(jdLMT, geolon float64, fl *TimeEquFlags) (float64, error) 224 | // LATToLMT returns the local mean time for the given Julian Date (in Local 225 | // Apparent Time) and the geographic longitude. 226 | LATToLMT(jdLAT, geolon float64, fl *TimeEquFlags) (float64, error) 227 | 228 | // SidTime0 returns the sidereal time for Julian Date jd, ecliptic obliquity 229 | // eps and nutation nut at the Greenwich medidian, measured in hours. 230 | SidTime0(ut, eps, nut float64, fl *SidTimeFlags) (float64, error) 231 | // SidTime returns the sidereal time for Julian Date jd at the Greenwich 232 | // medidian, measured in hours. 233 | SidTime(ut float64, fl *SidTimeFlags) (float64, error) 234 | } 235 | 236 | // Locked tries to exclusively lock the library handle, disable per function 237 | // locking and exposes the locked interface to the callback function. Per 238 | // function locking is restored when execution is returned to the caller. The 239 | // input object is directly passed to the callback function if it does not 240 | // implement ExclusiveLocker. If either argument is nil, it panics. 241 | func Locked(swe Interface, callback func(swe Interface)) { 242 | if swe == nil { 243 | panic("swe is nil") 244 | } 245 | 246 | if callback == nil { 247 | panic("callback is nil") 248 | } 249 | 250 | l, ok := swe.(ExclusiveLocker) 251 | if !ok { 252 | callback(swe) 253 | return 254 | } 255 | 256 | li := l.ExclusiveLock() 257 | callback(li) 258 | li.ExclusiveUnlock() 259 | } 260 | 261 | // An ExclusiveLocker is a library handle that can exclusively lock itself. 262 | type ExclusiveLocker interface { 263 | ExclusiveLock() LockedInterface 264 | } 265 | 266 | // An LockedInterface is a library handle that is exclusively locked. 267 | type LockedInterface interface { 268 | Interface 269 | ExclusiveUnlock() 270 | } 271 | -------------------------------------------------------------------------------- /swego_test.go: -------------------------------------------------------------------------------- 1 | package swego 2 | 3 | import "testing" 4 | 5 | func TestNewHSys(t *testing.T) { 6 | cases := []struct { 7 | in byte 8 | want bool 9 | }{ 10 | {'A', true}, // Equal 11 | {'B', true}, // Alcabitius 12 | {'C', true}, // Campanus 13 | {'D', true}, // Equal houses, where cusp 10 = MC 14 | {'E', true}, // Equal 15 | {'F', true}, // Carter poli-equatorial 16 | {'G', true}, // Gauquelin sectors 17 | {'H', true}, // Azimuthal 18 | {'I', true}, // Sunshine (Treindl) 19 | {'i', true}, // Sunshine (Makransky) 20 | {'K', true}, // Koch 21 | {'L', true}, // Pullen SD (sinusoidal delta) = ex Neo-Porphyry 22 | {'M', true}, // Morinus 23 | {'N', true}, // Equal houses, where cusp 1 = 0 Aries 24 | {'O', true}, // Porphyrius 25 | {'P', true}, // Placidus 26 | {'Q', true}, // Pullen SR (sinusoidal ratio) 27 | {'R', true}, // Regiomontanus 28 | {'S', true}, // Sripati 29 | {'T', true}, // Polich-Page 30 | {'U', true}, // Krusinski-Pisa-Goelzer 31 | {'V', true}, // Vehlow equal 32 | {'W', true}, // Whole sign 33 | {'X', true}, // Axial rotation 34 | {'Y', true}, // APC houses 35 | {'_', false}, 36 | {'Z', false}, 37 | } 38 | 39 | for _, c := range cases { 40 | hsys, ok := NewHSys(c.in) 41 | 42 | if ok && byte(hsys) != c.in { 43 | t.Errorf("input %q is not returned", c.in) 44 | } 45 | 46 | if !ok && byte(hsys) != '\000' { 47 | t.Errorf("invalid input does not return '\\000' as hsys") 48 | } 49 | 50 | if ok != c.want { 51 | t.Errorf("%q is no valid house system", c.in) 52 | } 53 | } 54 | } 55 | 56 | func TestNewHSys_lowerToUpper(t *testing.T) { 57 | got, ok := NewHSys('a') 58 | if !ok { 59 | t.Error("ok = false, want: true") 60 | } 61 | 62 | want := HSys('A') 63 | if got != want { 64 | t.Errorf("hsys = %c, want: %c", got, want) 65 | } 66 | } 67 | 68 | func TestCalcFlags_Copy(t *testing.T) { 69 | fl := new(CalcFlags) 70 | fl.Flags = FlagSpeed 71 | got := fl.Copy() 72 | 73 | if got == fl { 74 | t.Errorf("%p = %p, want copy", got, fl) 75 | } 76 | } 77 | 78 | func TestCalcFlags_SetEphemeris(t *testing.T) { 79 | fl := new(CalcFlags) 80 | fl.SetEphemeris(JPL) 81 | 82 | if fl.Flags != int32(JPL) { 83 | t.Error("flags value does not contain ephemeris flag") 84 | } 85 | } 86 | 87 | type testInterface struct{ Interface } 88 | type testExclLocker struct{ Interface } 89 | type testLockedIface struct{ Interface } 90 | 91 | func (l *testLockedIface) ExclusiveUnlock() {} 92 | func (el *testExclLocker) ExclusiveLock() LockedInterface { 93 | return &testLockedIface{el} 94 | } 95 | 96 | func TestLocked(t *testing.T) { 97 | t.Run("Interface", func(t *testing.T) { 98 | called := make(chan struct{}, 1) 99 | swe := new(testInterface) 100 | Locked(swe, func(i Interface) { 101 | if i != swe { 102 | t.Error("passed Interface no equal to input Interface") 103 | } 104 | 105 | called <- struct{}{} 106 | }) 107 | 108 | select { 109 | case <-called: 110 | default: 111 | t.Error("callback not called") 112 | } 113 | }) 114 | 115 | t.Run("ExclusiveLocker", func(t *testing.T) { 116 | called := make(chan struct{}, 1) 117 | swe := (*testExclLocker)(nil) 118 | Locked(swe, func(i Interface) { 119 | if i.(*testLockedIface).Interface != swe { 120 | t.Error("passed Interface no equal to input Interface") 121 | } 122 | 123 | called <- struct{}{} 124 | }) 125 | 126 | select { 127 | case <-called: 128 | default: 129 | t.Error("callback not called") 130 | } 131 | }) 132 | } 133 | -------------------------------------------------------------------------------- /swerker/stdio/internal/lichdata/lichdata.go: -------------------------------------------------------------------------------- 1 | // Package lichdata provides Lich data element read and write functionality. 2 | // 3 | // See https://github.com/rentzsch/lich for more information about Lich. 4 | package lichdata 5 | 6 | import ( 7 | "errors" 8 | "fmt" 9 | "io" 10 | 11 | "github.com/philhofer/fwd" 12 | ) 13 | 14 | type byteReader interface { 15 | io.Reader 16 | io.ByteReader 17 | } 18 | 19 | // ReadFrom reads a single Lich data element from reader r. 20 | func ReadFrom(r io.Reader) ([]byte, error) { 21 | if br, ok := r.(byteReader); ok { 22 | return readData(br) 23 | } 24 | 25 | return readData(fwd.NewReader(r)) 26 | } 27 | 28 | // ErrNoLength is returned if no ASCII length data is found. 29 | var ErrNoLength = errors.New("lichdata: no length") 30 | 31 | // Type marker errors. 32 | var ( 33 | ErrInvalidOpenMarker = errors.New("lichdata: invalid open marker") 34 | ErrInvalidCloseMarker = errors.New("lichdata: invalid close marker") 35 | ) 36 | 37 | // MaxLengthError is returned when the buffer is bigger than the maximum value 38 | // an int can hold. 39 | type MaxLengthError struct { 40 | N uint64 41 | } 42 | 43 | const maxInt = int(^uint(0) >> 1) 44 | 45 | func (e *MaxLengthError) Error() string { 46 | return fmt.Sprintf("lichdata: length is %d, limit %d exceeded", e.N, maxInt) 47 | } 48 | 49 | func readData(r byteReader) ([]byte, error) { 50 | c, err := r.ReadByte() 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | var size uint64 56 | for '0' <= c && c <= '9' { 57 | size *= 10 58 | size += uint64(c - '0') 59 | 60 | c, err = r.ReadByte() 61 | if err != nil { 62 | return nil, err 63 | } 64 | } 65 | 66 | if size == 0 { 67 | return nil, ErrNoLength 68 | } 69 | 70 | if c != '<' { 71 | return nil, ErrInvalidOpenMarker 72 | } 73 | 74 | if size > uint64(maxInt) { 75 | return nil, &MaxLengthError{size} 76 | } 77 | 78 | buf := make([]byte, int(size)) 79 | n, err := r.Read(buf) 80 | if err != nil { 81 | return nil, err 82 | } 83 | 84 | c, err = r.ReadByte() 85 | if err != nil { 86 | return nil, err 87 | } 88 | 89 | if c != '>' { 90 | return nil, ErrInvalidCloseMarker 91 | } 92 | 93 | return buf[:n], nil 94 | } 95 | 96 | // BufWriter represents a buffered writer. 97 | type BufWriter interface { 98 | io.Writer 99 | io.ByteWriter 100 | Flush() error 101 | } 102 | 103 | // Writer buffers an io.Writer and can write Lich data elements to it. 104 | type Writer struct { 105 | W BufWriter 106 | } 107 | 108 | // NewWriter returns a new Writer for underlying writer w. 109 | func NewWriter(w io.Writer) *Writer { 110 | if bw, ok := w.(BufWriter); ok { 111 | return &Writer{bw} 112 | } 113 | 114 | return &Writer{fwd.NewWriter(w)} 115 | } 116 | 117 | // Write writes byte slice buf as a Lich data element to the underlying writer 118 | // of writer w. 119 | func (w *Writer) Write(buf []byte) (n int, err error) { 120 | return writeData(w.W, buf) 121 | } 122 | 123 | func writeData(w BufWriter, data []byte) (n int, err error) { 124 | i, err := fmt.Fprintf(w, "%d<", len(data)) 125 | if err != nil { 126 | return 0, err 127 | } 128 | 129 | n += i 130 | i, err = w.Write(data) 131 | if err != nil { 132 | return 0, err 133 | } 134 | 135 | n += i 136 | if err = w.WriteByte('>'); err != nil { 137 | return 0, err 138 | } 139 | 140 | n++ 141 | err = w.Flush() 142 | if err != nil { 143 | return 0, err 144 | } 145 | 146 | return n, nil 147 | } 148 | -------------------------------------------------------------------------------- /swerker/stdio/internal/lichdata/lichdata_test.go: -------------------------------------------------------------------------------- 1 | package lichdata 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "io" 7 | "reflect" 8 | "strconv" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/philhofer/fwd" 13 | ) 14 | 15 | const testData = "\xbflength of input data expected" 16 | 17 | func testReader() byteReader { 18 | return strings.NewReader("30<" + testData + ">") 19 | } 20 | 21 | type basicReader struct { 22 | r io.Reader 23 | } 24 | 25 | func (br *basicReader) Read(buf []byte) (int, error) { 26 | return br.r.Read(buf) 27 | } 28 | 29 | func TestReader(t *testing.T) { 30 | readers := []io.Reader{ 31 | testReader(), 32 | &basicReader{testReader()}, 33 | } 34 | 35 | for _, r := range readers { 36 | name := reflect.TypeOf(r).Elem().Name() 37 | t.Run(name, func(t *testing.T) { 38 | data, err := ReadFrom(r) 39 | if err != nil { 40 | t.Errorf("err = %v, want: nil", err) 41 | } 42 | 43 | got := string(data) 44 | want := testData 45 | if got != want { 46 | t.Errorf("data = %q, want: %q", got, want) 47 | } 48 | }) 49 | } 50 | } 51 | 52 | func BenchmarkReaderBufio(b *testing.B) { 53 | for i := 0; i < b.N; i++ { 54 | readData(bufio.NewReader(testReader())) 55 | } 56 | } 57 | 58 | func BenchmarkReaderFwd(b *testing.B) { 59 | for i := 0; i < b.N; i++ { 60 | readData(fwd.NewReader(testReader())) 61 | } 62 | } 63 | 64 | func TestWriter(t *testing.T) { 65 | buf := new(bytes.Buffer) 66 | w := NewWriter(buf) 67 | 68 | n, err := io.WriteString(w, testData) 69 | if err != nil { 70 | t.Errorf("err = %v, want: nil", err) 71 | } 72 | 73 | nstr := strconv.Itoa(n) 74 | want := len(testData) + len(nstr) + 2 75 | if n != want { 76 | t.Errorf("n = %d, want: %d", n, want) 77 | } 78 | } 79 | 80 | type discard struct{} 81 | 82 | func (discard) Write([]byte) (int, error) { return 0, nil } 83 | func (discard) WriteByte(byte) error { return nil } 84 | func (discard) Flush() error { return nil } 85 | 86 | var _ BufWriter = discard{} 87 | 88 | func BenchmarkWriterBufio(b *testing.B) { 89 | d := discard{} 90 | data := []byte(testData) 91 | b.ResetTimer() 92 | for i := 0; i < b.N; i++ { 93 | writeData(bufio.NewWriter(d), data) 94 | } 95 | } 96 | 97 | func BenchmarkWriterFwd(b *testing.B) { 98 | d := discard{} 99 | data := []byte(testData) 100 | b.ResetTimer() 101 | for i := 0; i < b.N; i++ { 102 | writeData(fwd.NewWriter(d), data) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /swerker/stdio/internal/worker/funcs.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | //go:generate msgp -file $GOFILE 4 | 5 | // Funcs hold the list of function names returned from the worker. 6 | // The index of the function is the element index. 7 | type Funcs []string 8 | 9 | // LastIdx retuns the last valid index i found in functions list s. 10 | func (s Funcs) LastIdx() uint8 { return uint8(len(s)) - 1 } 11 | 12 | // FuncsMap returns a map from function name to the corresponding index. 13 | func (s Funcs) FuncsMap() FuncsMap { 14 | m := make(FuncsMap) 15 | for idx, name := range s { 16 | m[name] = uint8(idx) 17 | } 18 | 19 | return m 20 | } 21 | 22 | // Lookup does a linear search in s for function fn. 23 | func (s Funcs) Lookup(fn string) (idx uint8, ok bool) { 24 | for i, name := range s { 25 | if name == fn { 26 | return uint8(i), true 27 | } 28 | } 29 | 30 | return 0, false 31 | } 32 | 33 | //msgp:ignore FuncsMap 34 | 35 | // FuncsMap maps a function name to a RPC function index. 36 | type FuncsMap map[string]uint8 37 | -------------------------------------------------------------------------------- /swerker/stdio/internal/worker/funcs_gen.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | // NOTE: THIS FILE WAS PRODUCED BY THE 4 | // MSGP CODE GENERATION TOOL (github.com/tinylib/msgp) 5 | // DO NOT EDIT 6 | 7 | import ( 8 | "github.com/tinylib/msgp/msgp" 9 | ) 10 | 11 | // DecodeMsg implements msgp.Decodable 12 | func (z *Funcs) DecodeMsg(dc *msgp.Reader) (err error) { 13 | var zbai uint32 14 | zbai, err = dc.ReadArrayHeader() 15 | if err != nil { 16 | return 17 | } 18 | if cap((*z)) >= int(zbai) { 19 | (*z) = (*z)[:zbai] 20 | } else { 21 | (*z) = make(Funcs, zbai) 22 | } 23 | for zbzg := range *z { 24 | (*z)[zbzg], err = dc.ReadString() 25 | if err != nil { 26 | return 27 | } 28 | } 29 | return 30 | } 31 | 32 | // EncodeMsg implements msgp.Encodable 33 | func (z Funcs) EncodeMsg(en *msgp.Writer) (err error) { 34 | err = en.WriteArrayHeader(uint32(len(z))) 35 | if err != nil { 36 | return 37 | } 38 | for zcmr := range z { 39 | err = en.WriteString(z[zcmr]) 40 | if err != nil { 41 | return 42 | } 43 | } 44 | return 45 | } 46 | 47 | // MarshalMsg implements msgp.Marshaler 48 | func (z Funcs) MarshalMsg(b []byte) (o []byte, err error) { 49 | o = msgp.Require(b, z.Msgsize()) 50 | o = msgp.AppendArrayHeader(o, uint32(len(z))) 51 | for zcmr := range z { 52 | o = msgp.AppendString(o, z[zcmr]) 53 | } 54 | return 55 | } 56 | 57 | // UnmarshalMsg implements msgp.Unmarshaler 58 | func (z *Funcs) UnmarshalMsg(bts []byte) (o []byte, err error) { 59 | var zwht uint32 60 | zwht, bts, err = msgp.ReadArrayHeaderBytes(bts) 61 | if err != nil { 62 | return 63 | } 64 | if cap((*z)) >= int(zwht) { 65 | (*z) = (*z)[:zwht] 66 | } else { 67 | (*z) = make(Funcs, zwht) 68 | } 69 | for zajw := range *z { 70 | (*z)[zajw], bts, err = msgp.ReadStringBytes(bts) 71 | if err != nil { 72 | return 73 | } 74 | } 75 | o = bts 76 | return 77 | } 78 | 79 | // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message 80 | func (z Funcs) Msgsize() (s int) { 81 | s = msgp.ArrayHeaderSize 82 | for zhct := range z { 83 | s += msgp.StringPrefixSize + len(z[zhct]) 84 | } 85 | return 86 | } 87 | -------------------------------------------------------------------------------- /swerker/stdio/internal/worker/funcs_gen_test.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | // NOTE: THIS FILE WAS PRODUCED BY THE 4 | // MSGP CODE GENERATION TOOL (github.com/tinylib/msgp) 5 | // DO NOT EDIT 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | 11 | "github.com/tinylib/msgp/msgp" 12 | ) 13 | 14 | func TestMarshalUnmarshalFuncs(t *testing.T) { 15 | v := Funcs{} 16 | bts, err := v.MarshalMsg(nil) 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | left, err := v.UnmarshalMsg(bts) 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | if len(left) > 0 { 25 | t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) 26 | } 27 | 28 | left, err = msgp.Skip(bts) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | if len(left) > 0 { 33 | t.Errorf("%d bytes left over after Skip(): %q", len(left), left) 34 | } 35 | } 36 | 37 | func BenchmarkMarshalMsgFuncs(b *testing.B) { 38 | v := Funcs{} 39 | b.ReportAllocs() 40 | b.ResetTimer() 41 | for i := 0; i < b.N; i++ { 42 | v.MarshalMsg(nil) 43 | } 44 | } 45 | 46 | func BenchmarkAppendMsgFuncs(b *testing.B) { 47 | v := Funcs{} 48 | bts := make([]byte, 0, v.Msgsize()) 49 | bts, _ = v.MarshalMsg(bts[0:0]) 50 | b.SetBytes(int64(len(bts))) 51 | b.ReportAllocs() 52 | b.ResetTimer() 53 | for i := 0; i < b.N; i++ { 54 | bts, _ = v.MarshalMsg(bts[0:0]) 55 | } 56 | } 57 | 58 | func BenchmarkUnmarshalFuncs(b *testing.B) { 59 | v := Funcs{} 60 | bts, _ := v.MarshalMsg(nil) 61 | b.ReportAllocs() 62 | b.SetBytes(int64(len(bts))) 63 | b.ResetTimer() 64 | for i := 0; i < b.N; i++ { 65 | _, err := v.UnmarshalMsg(bts) 66 | if err != nil { 67 | b.Fatal(err) 68 | } 69 | } 70 | } 71 | 72 | func TestEncodeDecodeFuncs(t *testing.T) { 73 | v := Funcs{} 74 | var buf bytes.Buffer 75 | msgp.Encode(&buf, &v) 76 | 77 | m := v.Msgsize() 78 | if buf.Len() > m { 79 | t.Logf("WARNING: Msgsize() for %v is inaccurate", v) 80 | } 81 | 82 | vn := Funcs{} 83 | err := msgp.Decode(&buf, &vn) 84 | if err != nil { 85 | t.Error(err) 86 | } 87 | 88 | buf.Reset() 89 | msgp.Encode(&buf, &v) 90 | err = msgp.NewReader(&buf).Skip() 91 | if err != nil { 92 | t.Error(err) 93 | } 94 | } 95 | 96 | func BenchmarkEncodeFuncs(b *testing.B) { 97 | v := Funcs{} 98 | var buf bytes.Buffer 99 | msgp.Encode(&buf, &v) 100 | b.SetBytes(int64(buf.Len())) 101 | en := msgp.NewWriter(msgp.Nowhere) 102 | b.ReportAllocs() 103 | b.ResetTimer() 104 | for i := 0; i < b.N; i++ { 105 | v.EncodeMsg(en) 106 | } 107 | en.Flush() 108 | } 109 | 110 | func BenchmarkDecodeFuncs(b *testing.B) { 111 | v := Funcs{} 112 | var buf bytes.Buffer 113 | msgp.Encode(&buf, &v) 114 | b.SetBytes(int64(buf.Len())) 115 | rd := msgp.NewEndlessReader(buf.Bytes(), b) 116 | dc := msgp.NewReader(rd) 117 | b.ReportAllocs() 118 | b.ResetTimer() 119 | for i := 0; i < b.N; i++ { 120 | err := v.DecodeMsg(dc) 121 | if err != nil { 122 | b.Fatal(err) 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /swerker/stdio/internal/worker/funcs_test.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | var testFuncs = Funcs{ 9 | "rpc_funcs", // required by worker RPC system 10 | "test_crash", 11 | "test_error", 12 | "swe_version", 13 | } 14 | 15 | func TestFuncs_LastIdx(t *testing.T) { 16 | got := testFuncs.LastIdx() 17 | const want = 3 18 | 19 | if got != want { 20 | t.Errorf("LastIdx() = %d, want: %d", got, want) 21 | } 22 | } 23 | 24 | func TestFuncs_FuncsMap(t *testing.T) { 25 | got := testFuncs.FuncsMap() 26 | want := FuncsMap{ 27 | "rpc_funcs": 0, 28 | "test_crash": 1, 29 | "test_error": 2, 30 | "swe_version": 3, 31 | } 32 | 33 | if !reflect.DeepEqual(got, want) { 34 | t.Errorf("got %v, want: %v", got, want) 35 | } 36 | } 37 | 38 | func TestFuncs_Lookup(t *testing.T) { 39 | t.Run("Found", func(t *testing.T) { 40 | idx, ok := testFuncs.Lookup("rpc_funcs") 41 | if idx != 0 { 42 | t.Errorf("idx = %d, want: 0", idx) 43 | } 44 | 45 | if !ok { 46 | t.Error("ok = false, want: true") 47 | } 48 | }) 49 | 50 | t.Run("NotFound", func(t *testing.T) { 51 | idx, ok := testFuncs.Lookup("not_existent") 52 | if idx != 0 { 53 | t.Errorf("idx = %d, want: 0", idx) 54 | } 55 | 56 | if ok { 57 | t.Error("ok = true, want: false") 58 | } 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /swerker/stdio/internal/worker/mock_test.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "os/exec" 8 | "testing" 9 | 10 | "github.com/astrotools/swego/swerker/stdio/internal/lichdata" 11 | 12 | "github.com/tinylib/msgp/msgp" 13 | ) 14 | 15 | func mockOnly(t *testing.T) { 16 | if *useWorker { 17 | t.SkipNow() 18 | } 19 | } 20 | 21 | func swizzle(mock string, args ...string) func() { 22 | if !*useWorker { 23 | execCommand = testExecCommand("Test" + mock + "_SubProcess") 24 | } 25 | 26 | execCmdArgs = append(args, "-dangerous_enable_test_functions") 27 | 28 | return func() { 29 | execCommand = exec.Command 30 | execCmdArgs = nil 31 | } 32 | } 33 | 34 | // see https://npf.io/2015/06/testing-exec-command/ 35 | func testExecCommand(testName string) func(string, ...string) *exec.Cmd { 36 | return func(command string, cmdArgs ...string) *exec.Cmd { 37 | args := []string{"-test.run=" + testName, "--", command} 38 | args = append(args, cmdArgs...) 39 | cmd := exec.Command(os.Args[0], args...) 40 | cmd.Env = []string{"GO_TEST_SUBPROCESS=1"} 41 | return cmd 42 | } 43 | } 44 | 45 | func readInput() msgp.Raw { 46 | data, err := lichdata.ReadFrom(os.Stdin) 47 | if err != nil { 48 | if err == io.EOF || err == lichdata.ErrNoLength { 49 | os.Exit(0) 50 | } 51 | 52 | writePanic("failed to read request", err.Error()) 53 | } 54 | 55 | return data 56 | } 57 | 58 | func writeResponse(e msgp.Encodable) { 59 | w := msgp.NewWriter(lichdata.NewWriter(os.Stdout)) 60 | err := msgp.Encode(w, e) 61 | if err != nil { 62 | writePanic("failed to write response", err.Error()) 63 | } 64 | } 65 | 66 | func writeErrorMap(msg string, dbg ...string) { 67 | em := ErrorMap{"err": msg} 68 | if len(dbg) != 0 { 69 | em["dbg"] = dbg[0] 70 | } 71 | 72 | writeResponse(em) 73 | } 74 | 75 | func writePanic(msg string, dbg ...string) { 76 | if len(dbg) != 0 { 77 | fmt.Fprintln(os.Stderr, "DEBUG: "+dbg[0]) 78 | } 79 | 80 | fmt.Fprintln(os.Stderr, "ERROR: "+msg) 81 | os.Exit(1) 82 | } 83 | 84 | func writeInitalFuncs() { 85 | writeResponse(Funcs{ 86 | "rpc_funcs", // required by worker RPC system 87 | "test_crash", 88 | "test_error", 89 | "worker_is_mocked", 90 | }) 91 | } 92 | 93 | func TestNoFuncs_SubProcess(t *testing.T) { 94 | if os.Getenv("GO_TEST_SUBPROCESS") != "1" { 95 | t.SkipNow() 96 | } 97 | 98 | os.Exit(1) 99 | } 100 | 101 | func TestFuncsPanic_SubProcess(t *testing.T) { 102 | if os.Getenv("GO_TEST_SUBPROCESS") != "1" { 103 | t.SkipNow() 104 | } 105 | 106 | writePanic("funcs panic") 107 | } 108 | 109 | func TestInvalidFuncsData_SubProcess(t *testing.T) { 110 | if os.Getenv("GO_TEST_SUBPROCESS") != "1" { 111 | t.SkipNow() 112 | } 113 | 114 | fmt.Fprintln(os.Stdout, "invalid funcs data") 115 | os.Exit(1) 116 | } 117 | 118 | func TestInvalidFuncsType_SubProcess(t *testing.T) { 119 | if os.Getenv("GO_TEST_SUBPROCESS") != "1" { 120 | t.SkipNow() 121 | } 122 | 123 | w := msgp.NewWriter(lichdata.NewWriter(os.Stdout)) 124 | w.WriteNil() 125 | w.Flush() 126 | os.Exit(1) 127 | } 128 | 129 | func TestParseFuncs_SubProcess(t *testing.T) { 130 | if os.Getenv("GO_TEST_SUBPROCESS") != "1" { 131 | t.SkipNow() 132 | } 133 | 134 | writeInitalFuncs() 135 | os.Exit(0) 136 | } 137 | 138 | func TestCall_SubProcess(t *testing.T) { 139 | if os.Getenv("GO_TEST_SUBPROCESS") != "1" { 140 | t.SkipNow() 141 | } 142 | 143 | writeInitalFuncs() 144 | readInput() 145 | writeInitalFuncs() 146 | os.Exit(0) 147 | } 148 | 149 | func TestCallError_SubProcess(t *testing.T) { 150 | if os.Getenv("GO_TEST_SUBPROCESS") != "1" { 151 | t.SkipNow() 152 | } 153 | 154 | writeInitalFuncs() 155 | readInput() 156 | writeErrorMap("test_error called", "func=test_error") 157 | os.Exit(0) 158 | } 159 | 160 | func TestCrashedCall_SubProcess(t *testing.T) { 161 | if os.Getenv("GO_TEST_SUBPROCESS") != "1" { 162 | t.SkipNow() 163 | } 164 | 165 | writeInitalFuncs() 166 | readInput() 167 | writePanic("test_crash called", "func=test_crash") 168 | } 169 | 170 | func TestUnexpectedExit_SubProcess(t *testing.T) { 171 | if os.Getenv("GO_TEST_SUBPROCESS") != "1" { 172 | t.SkipNow() 173 | } 174 | 175 | writeInitalFuncs() 176 | readInput() 177 | os.Exit(1) 178 | } 179 | -------------------------------------------------------------------------------- /swerker/stdio/internal/worker/output.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "strings" 8 | "sync" 9 | 10 | "github.com/astrotools/swego/swerker/stdio/internal/lichdata" 11 | 12 | "github.com/tinylib/msgp/msgp" 13 | ) 14 | 15 | //go:generate msgp -file $GOFILE 16 | 17 | //msgp:ignore Error 18 | 19 | // Error represent an error in a worker subprocess. 20 | type Error struct { 21 | Msg string 22 | Debug string 23 | Panic bool 24 | } 25 | 26 | func (e *Error) Error() string { 27 | if e.Debug == "" { 28 | return e.Msg 29 | } 30 | 31 | return fmt.Sprintf("%s [%s]", e.Msg, e.Debug) 32 | } 33 | 34 | // ErrorMap is returned by the worker process in case of a RPC error. 35 | // It contains always an "err" key describing the error. 36 | // Optionally it contains a "dbg" key with additional (debug) information. 37 | type ErrorMap map[string]string 38 | 39 | func (em ErrorMap) error() *Error { 40 | return &Error{Msg: em["err"], Debug: em["dbg"]} 41 | } 42 | 43 | var readerPool = sync.Pool{New: func() interface{} { 44 | return new(bytes.Reader) 45 | }} 46 | 47 | func newReader(buf []byte) *bytes.Reader { 48 | r := readerPool.Get().(*bytes.Reader) 49 | r.Reset(buf) 50 | return r 51 | } 52 | 53 | func freeReader(r *bytes.Reader) { 54 | readerPool.Put(r) 55 | } 56 | 57 | type stdoutWriter struct { 58 | write func(msgp.Raw) 59 | } 60 | 61 | func (w *stdoutWriter) Write(data []byte) (int, error) { 62 | r := newReader(data) 63 | msg, err := lichdata.ReadFrom(r) 64 | freeReader(r) 65 | 66 | if err != nil { 67 | return 0, err 68 | } 69 | 70 | w.write(msg) 71 | return len(data), nil 72 | } 73 | 74 | type stderrWriter struct { 75 | report func(*Error) 76 | debug string 77 | } 78 | 79 | const ( 80 | prefixDebug = "DEBUG: " 81 | prefixError = "ERROR: " 82 | prefixDebugLen = len(prefixDebug) 83 | prefixErrorLen = len(prefixError) 84 | ) 85 | 86 | func (w *stderrWriter) Write(data []byte) (int, error) { 87 | r := newReader(data) 88 | 89 | s := bufio.NewScanner(r) 90 | for s.Scan() { 91 | line := s.Text() 92 | switch { 93 | case strings.HasPrefix(line, prefixDebug): 94 | w.debug = line[prefixDebugLen:] 95 | 96 | case strings.HasPrefix(line, prefixError): 97 | w.report(&Error{line[prefixErrorLen:], w.debug, true}) 98 | w.debug = "" 99 | } 100 | } 101 | 102 | freeReader(r) 103 | if err := s.Err(); err != nil { 104 | return 0, err 105 | } 106 | 107 | return len(data), nil 108 | } 109 | -------------------------------------------------------------------------------- /swerker/stdio/internal/worker/output_gen.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | // NOTE: THIS FILE WAS PRODUCED BY THE 4 | // MSGP CODE GENERATION TOOL (github.com/tinylib/msgp) 5 | // DO NOT EDIT 6 | 7 | import ( 8 | "github.com/tinylib/msgp/msgp" 9 | ) 10 | 11 | // DecodeMsg implements msgp.Decodable 12 | func (z *ErrorMap) DecodeMsg(dc *msgp.Reader) (err error) { 13 | var zajw uint32 14 | zajw, err = dc.ReadMapHeader() 15 | if err != nil { 16 | return 17 | } 18 | if (*z) == nil && zajw > 0 { 19 | (*z) = make(ErrorMap, zajw) 20 | } else if len((*z)) > 0 { 21 | for key, _ := range *z { 22 | delete((*z), key) 23 | } 24 | } 25 | for zajw > 0 { 26 | zajw-- 27 | var zbai string 28 | var zcmr string 29 | zbai, err = dc.ReadString() 30 | if err != nil { 31 | return 32 | } 33 | zcmr, err = dc.ReadString() 34 | if err != nil { 35 | return 36 | } 37 | (*z)[zbai] = zcmr 38 | } 39 | return 40 | } 41 | 42 | // EncodeMsg implements msgp.Encodable 43 | func (z ErrorMap) EncodeMsg(en *msgp.Writer) (err error) { 44 | err = en.WriteMapHeader(uint32(len(z))) 45 | if err != nil { 46 | return 47 | } 48 | for zwht, zhct := range z { 49 | err = en.WriteString(zwht) 50 | if err != nil { 51 | return 52 | } 53 | err = en.WriteString(zhct) 54 | if err != nil { 55 | return 56 | } 57 | } 58 | return 59 | } 60 | 61 | // MarshalMsg implements msgp.Marshaler 62 | func (z ErrorMap) MarshalMsg(b []byte) (o []byte, err error) { 63 | o = msgp.Require(b, z.Msgsize()) 64 | o = msgp.AppendMapHeader(o, uint32(len(z))) 65 | for zwht, zhct := range z { 66 | o = msgp.AppendString(o, zwht) 67 | o = msgp.AppendString(o, zhct) 68 | } 69 | return 70 | } 71 | 72 | // UnmarshalMsg implements msgp.Unmarshaler 73 | func (z *ErrorMap) UnmarshalMsg(bts []byte) (o []byte, err error) { 74 | var zlqf uint32 75 | zlqf, bts, err = msgp.ReadMapHeaderBytes(bts) 76 | if err != nil { 77 | return 78 | } 79 | if (*z) == nil && zlqf > 0 { 80 | (*z) = make(ErrorMap, zlqf) 81 | } else if len((*z)) > 0 { 82 | for key, _ := range *z { 83 | delete((*z), key) 84 | } 85 | } 86 | for zlqf > 0 { 87 | var zcua string 88 | var zxhx string 89 | zlqf-- 90 | zcua, bts, err = msgp.ReadStringBytes(bts) 91 | if err != nil { 92 | return 93 | } 94 | zxhx, bts, err = msgp.ReadStringBytes(bts) 95 | if err != nil { 96 | return 97 | } 98 | (*z)[zcua] = zxhx 99 | } 100 | o = bts 101 | return 102 | } 103 | 104 | // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message 105 | func (z ErrorMap) Msgsize() (s int) { 106 | s = msgp.MapHeaderSize 107 | if z != nil { 108 | for zdaf, zpks := range z { 109 | _ = zpks 110 | s += msgp.StringPrefixSize + len(zdaf) + msgp.StringPrefixSize + len(zpks) 111 | } 112 | } 113 | return 114 | } 115 | -------------------------------------------------------------------------------- /swerker/stdio/internal/worker/output_gen_test.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | // NOTE: THIS FILE WAS PRODUCED BY THE 4 | // MSGP CODE GENERATION TOOL (github.com/tinylib/msgp) 5 | // DO NOT EDIT 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | 11 | "github.com/tinylib/msgp/msgp" 12 | ) 13 | 14 | func TestMarshalUnmarshalErrorMap(t *testing.T) { 15 | v := ErrorMap{} 16 | bts, err := v.MarshalMsg(nil) 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | left, err := v.UnmarshalMsg(bts) 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | if len(left) > 0 { 25 | t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) 26 | } 27 | 28 | left, err = msgp.Skip(bts) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | if len(left) > 0 { 33 | t.Errorf("%d bytes left over after Skip(): %q", len(left), left) 34 | } 35 | } 36 | 37 | func BenchmarkMarshalMsgErrorMap(b *testing.B) { 38 | v := ErrorMap{} 39 | b.ReportAllocs() 40 | b.ResetTimer() 41 | for i := 0; i < b.N; i++ { 42 | v.MarshalMsg(nil) 43 | } 44 | } 45 | 46 | func BenchmarkAppendMsgErrorMap(b *testing.B) { 47 | v := ErrorMap{} 48 | bts := make([]byte, 0, v.Msgsize()) 49 | bts, _ = v.MarshalMsg(bts[0:0]) 50 | b.SetBytes(int64(len(bts))) 51 | b.ReportAllocs() 52 | b.ResetTimer() 53 | for i := 0; i < b.N; i++ { 54 | bts, _ = v.MarshalMsg(bts[0:0]) 55 | } 56 | } 57 | 58 | func BenchmarkUnmarshalErrorMap(b *testing.B) { 59 | v := ErrorMap{} 60 | bts, _ := v.MarshalMsg(nil) 61 | b.ReportAllocs() 62 | b.SetBytes(int64(len(bts))) 63 | b.ResetTimer() 64 | for i := 0; i < b.N; i++ { 65 | _, err := v.UnmarshalMsg(bts) 66 | if err != nil { 67 | b.Fatal(err) 68 | } 69 | } 70 | } 71 | 72 | func TestEncodeDecodeErrorMap(t *testing.T) { 73 | v := ErrorMap{} 74 | var buf bytes.Buffer 75 | msgp.Encode(&buf, &v) 76 | 77 | m := v.Msgsize() 78 | if buf.Len() > m { 79 | t.Logf("WARNING: Msgsize() for %v is inaccurate", v) 80 | } 81 | 82 | vn := ErrorMap{} 83 | err := msgp.Decode(&buf, &vn) 84 | if err != nil { 85 | t.Error(err) 86 | } 87 | 88 | buf.Reset() 89 | msgp.Encode(&buf, &v) 90 | err = msgp.NewReader(&buf).Skip() 91 | if err != nil { 92 | t.Error(err) 93 | } 94 | } 95 | 96 | func BenchmarkEncodeErrorMap(b *testing.B) { 97 | v := ErrorMap{} 98 | var buf bytes.Buffer 99 | msgp.Encode(&buf, &v) 100 | b.SetBytes(int64(buf.Len())) 101 | en := msgp.NewWriter(msgp.Nowhere) 102 | b.ReportAllocs() 103 | b.ResetTimer() 104 | for i := 0; i < b.N; i++ { 105 | v.EncodeMsg(en) 106 | } 107 | en.Flush() 108 | } 109 | 110 | func BenchmarkDecodeErrorMap(b *testing.B) { 111 | v := ErrorMap{} 112 | var buf bytes.Buffer 113 | msgp.Encode(&buf, &v) 114 | b.SetBytes(int64(buf.Len())) 115 | rd := msgp.NewEndlessReader(buf.Bytes(), b) 116 | dc := msgp.NewReader(rd) 117 | b.ReportAllocs() 118 | b.ResetTimer() 119 | for i := 0; i < b.N; i++ { 120 | err := v.DecodeMsg(dc) 121 | if err != nil { 122 | b.Fatal(err) 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /swerker/stdio/internal/worker/output_test.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import ( 4 | "io" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestStderrWriter(t *testing.T) { 10 | cases := []struct { 11 | name string 12 | in func(io.Writer) 13 | want []*Error 14 | }{ 15 | { 16 | "Basic/Unbuffered", 17 | func(w io.Writer) { 18 | io.WriteString(w, prefixDebug+"debug\n"+prefixError+"error") 19 | }, 20 | []*Error{&Error{"error", "debug", true}}, 21 | }, 22 | { 23 | "Basic/Buffered", 24 | func(w io.Writer) { 25 | io.WriteString(w, prefixDebug+"debug") 26 | io.WriteString(w, prefixError+"error") 27 | }, 28 | []*Error{&Error{"error", "debug", true}}, 29 | }, 30 | { 31 | "Input/ErrErr", 32 | func(w io.Writer) { 33 | io.WriteString(w, prefixError+"length of input data expected") 34 | io.WriteString(w, prefixError+"reading unexpected EOF (body)") 35 | }, 36 | []*Error{ 37 | &Error{Msg: "length of input data expected", Panic: true}, 38 | &Error{Msg: "reading unexpected EOF (body)", Panic: true}, 39 | }, 40 | }, 41 | { 42 | "Input/ErrDebug", 43 | func(w io.Writer) { 44 | io.WriteString(w, prefixError+"length of input data expected") 45 | io.WriteString(w, prefixDebug+"func=2") 46 | io.WriteString(w, prefixError+"invalid index (function)") 47 | }, 48 | []*Error{ 49 | &Error{Msg: "length of input data expected", Panic: true}, 50 | &Error{"invalid index (function)", "func=2", true}, 51 | }, 52 | }, 53 | { 54 | "Input/DebugErr", 55 | func(w io.Writer) { 56 | io.WriteString(w, prefixDebug+"func=2") 57 | io.WriteString(w, prefixError+"invalid index (function)") 58 | io.WriteString(w, prefixError+"length of input data expected") 59 | }, 60 | []*Error{ 61 | &Error{"invalid index (function)", "func=2", true}, 62 | &Error{Msg: "length of input data expected", Panic: true}, 63 | }, 64 | }, 65 | { 66 | "Input/DebugDebug", 67 | func(w io.Writer) { 68 | io.WriteString(w, prefixDebug+"c='9' c=57") 69 | io.WriteString(w, prefixError+"reading unexpected close type marker") 70 | io.WriteString(w, prefixDebug+"c='>' c=62") 71 | io.WriteString(w, prefixError+"reading unexpected open type marker") 72 | }, 73 | []*Error{ 74 | &Error{"reading unexpected close type marker", "c='9' c=57", true}, 75 | &Error{"reading unexpected open type marker", "c='>' c=62", true}, 76 | }, 77 | }, 78 | } 79 | 80 | for _, c := range cases { 81 | t.Run(c.name, func(t *testing.T) { 82 | var got []*Error 83 | w := &stderrWriter{report: func(e *Error) { 84 | got = append(got, e) 85 | }} 86 | 87 | c.in(w) 88 | if !reflect.DeepEqual(got, c.want) { 89 | t.Errorf("got %q, want: %q", got, c.want) 90 | } 91 | }) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /swerker/stdio/internal/worker/worker.go: -------------------------------------------------------------------------------- 1 | // Package worker provides an interface to the swerker-stdio worker binary. 2 | package worker 3 | 4 | import ( 5 | "errors" 6 | "os/exec" 7 | 8 | "github.com/astrotools/swego/swerker" 9 | "github.com/astrotools/swego/swerker/stdio/internal/lichdata" 10 | 11 | "github.com/tinylib/msgp/msgp" 12 | ) 13 | 14 | // Worker runs and interacts with a swerker-stdio subprocess. 15 | type Worker interface { 16 | Call(c *swerker.Call) (data msgp.Raw, crashed bool, err error) 17 | Exit() error 18 | } 19 | 20 | type worker struct { 21 | path string 22 | cmd *exec.Cmd 23 | in *lichdata.Writer 24 | out chan msgp.Raw 25 | err chan *Error 26 | 27 | waitErr error // valid after exited is closed 28 | waited chan struct{} 29 | } 30 | 31 | // New runs the swerker-stdio binary found at the specified path as process and 32 | // returns the RPC functions it exposes. 33 | func New(path string) (Worker, Funcs, error) { 34 | w := &worker{ 35 | path: path, 36 | out: make(chan msgp.Raw), 37 | err: make(chan *Error), 38 | waited: make(chan struct{}), 39 | } 40 | 41 | if err := w.startProcess(); err != nil { 42 | return nil, nil, err 43 | } 44 | 45 | funcs, err := w.unmarshalFuncs() 46 | if err != nil { 47 | return nil, nil, err 48 | } 49 | 50 | return w, funcs, nil 51 | } 52 | 53 | // for testing 54 | var execCommand = exec.Command 55 | var execCmdArgs []string 56 | 57 | func (w *worker) startProcess() error { 58 | w.cmd = execCommand(w.path, execCmdArgs...) 59 | 60 | in, err := w.cmd.StdinPipe() 61 | if err != nil { 62 | return err 63 | } 64 | 65 | w.in = lichdata.NewWriter(in) 66 | w.cmd.Stdout = &stdoutWriter{write: func(out msgp.Raw) { w.out <- out }} 67 | w.cmd.Stderr = &stderrWriter{report: func(err *Error) { w.err <- err }} 68 | 69 | if err = w.cmd.Start(); err != nil { 70 | return err 71 | } 72 | 73 | go w.waitForExit() 74 | return nil 75 | } 76 | 77 | func (w *worker) waitForExit() { 78 | w.waitErr = w.cmd.Wait() 79 | close(w.waited) 80 | } 81 | 82 | // Exit terminates the subprocess. If the process doesn't complete successfully 83 | // the error is of type *exec.ExitError. Other error types may be returned for 84 | // I/O problems. 85 | func (w *worker) Exit() error { 86 | if !w.exited() { 87 | w.in.W.WriteByte('\n') 88 | w.in.W.Flush() 89 | <-w.waited 90 | } 91 | 92 | return w.waitErr 93 | } 94 | 95 | func (w *worker) exited() bool { 96 | select { 97 | default: 98 | return false 99 | case <-w.waited: 100 | return true 101 | } 102 | } 103 | 104 | // NoFuncsError is returned when no initial funcs are returned. 105 | type NoFuncsError struct { 106 | // Err is the underlying error why no funcs are available. 107 | Err error 108 | } 109 | 110 | func (e *NoFuncsError) Error() string { 111 | return "worker: no initial funcs" 112 | } 113 | 114 | func (w *worker) unmarshalFuncs() (Funcs, error) { 115 | var data msgp.Raw 116 | select { 117 | case data = <-w.out: 118 | case err := <-w.err: 119 | <-w.waited 120 | return nil, &NoFuncsError{err} 121 | case <-w.waited: 122 | return nil, &NoFuncsError{w.waitErr} 123 | } 124 | 125 | var funcs Funcs 126 | if _, err := funcs.UnmarshalMsg(data); err != nil { 127 | return nil, &NoFuncsError{err} 128 | } 129 | 130 | return funcs, nil 131 | } 132 | 133 | // ErrProcessExited is returned if a Call is made to a worker that has a 134 | // terminated subprocess. 135 | var ErrProcessExited = errors.New("worker: process has exited") 136 | 137 | // UnexpectedExitError is returned when the subprocess is unexpeced exited. 138 | type UnexpectedExitError struct { 139 | // Err is the underlying error why the process has exited. 140 | Err error 141 | } 142 | 143 | func (e *UnexpectedExitError) Error() string { 144 | return "worker: unexpected exit" 145 | } 146 | 147 | // Call executes function call c in the worker subprocess. 148 | // Value crashed is true if the subprocess is crashed during the call. 149 | func (w *worker) Call(c *swerker.Call) (data msgp.Raw, crashed bool, err error) { 150 | if w.exited() { 151 | return nil, false, ErrProcessExited 152 | } 153 | 154 | data, err = c.MarshalMsg(nil) 155 | if err != nil { 156 | return nil, false, err 157 | } 158 | 159 | if _, err = w.in.Write(data); err != nil { 160 | return nil, false, err 161 | } 162 | 163 | select { 164 | case data = <-w.out: 165 | case err := <-w.err: 166 | <-w.waited 167 | return nil, true, err 168 | case <-w.waited: 169 | return nil, true, &UnexpectedExitError{w.waitErr} 170 | } 171 | 172 | if msgp.NextType(data) == msgp.MapType { 173 | var em ErrorMap 174 | 175 | _, err := em.UnmarshalMsg(data) 176 | if err == nil { 177 | err = em.error() 178 | } 179 | 180 | return nil, false, err 181 | } 182 | 183 | return data, false, nil 184 | } 185 | -------------------------------------------------------------------------------- /swerker/stdio/internal/worker/worker_test.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import ( 4 | "flag" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/astrotools/swego/swerker" 9 | "github.com/astrotools/swego/swerker/stdio/internal/lichdata" 10 | 11 | "github.com/tinylib/msgp/msgp" 12 | ) 13 | 14 | var ( 15 | useWorker = flag.Bool("worker", false, "Test against actual swerker-stdio binary.") 16 | workerPath = flag.String("worker.path", "../../../../cmd/swerker/swerker-stdio", "Path to swerker-stdio binary.") 17 | ) 18 | 19 | func TestNoFuncs(t *testing.T) { 20 | defer swizzle("NoFuncs", "-dangerous_no_funcs_on_init")() 21 | 22 | w, funcs, err := New(*workerPath) 23 | if w != nil { 24 | t.Errorf("w = %v, want: nil", w) 25 | } 26 | 27 | if funcs != nil { 28 | t.Errorf("funcs = %v, want: nil", funcs) 29 | } 30 | 31 | if _, ok := err.(*NoFuncsError); !ok { 32 | t.Errorf("err = %#v, want: %T value", err, (*NoFuncsError)(nil)) 33 | } 34 | } 35 | 36 | func TestFuncsPanic(t *testing.T) { 37 | mockOnly(t) 38 | defer swizzle("FuncsPanic")() 39 | 40 | w, funcs, err := New(*workerPath) 41 | if w != nil { 42 | t.Errorf("w = %v, want: nil", w) 43 | } 44 | 45 | if funcs != nil { 46 | t.Errorf("funcs = %v, want: nil", funcs) 47 | } 48 | 49 | want := &NoFuncsError{Err: &Error{Msg: "funcs panic", Panic: true}} 50 | if !reflect.DeepEqual(err, want) { 51 | t.Errorf("err = %#v, want: %q", err, want) 52 | } 53 | } 54 | 55 | func TestInvalidFuncsData(t *testing.T) { 56 | defer swizzle("InvalidFuncsData", "-dangerous_invalid_funcs_on_init")() 57 | 58 | w, funcs, err := New(*workerPath) 59 | if w != nil { 60 | t.Error("w != nil") 61 | } 62 | 63 | if funcs != nil { 64 | t.Error("funcs != nil") 65 | } 66 | 67 | if _, ok := err.(*NoFuncsError); !ok { 68 | t.Errorf("err.(type) = %T, want: %T", err, (*NoFuncsError)(nil)) 69 | } 70 | } 71 | 72 | func TestInvalidFuncsType(t *testing.T) { 73 | defer swizzle("InvalidFuncsType", "-dangerous_invalid_funcs_types_on_init")() 74 | 75 | w, funcs, err := New(*workerPath) 76 | if w != nil { 77 | t.Error("w != nil") 78 | } 79 | 80 | if funcs != nil { 81 | t.Error("funcs != nil") 82 | } 83 | 84 | want := &NoFuncsError{Err: msgp.TypeError{ 85 | Method: msgp.ArrayType, 86 | Encoded: msgp.NilType, 87 | }} 88 | 89 | if !reflect.DeepEqual(err, want) { 90 | t.Errorf("err = %#v, want: %#v", err, want) 91 | } 92 | } 93 | 94 | func TestParseFuncs(t *testing.T) { 95 | defer swizzle("Call")() 96 | 97 | w, funcs, err := New(*workerPath) 98 | if err != nil { 99 | t.Fatal(err) 100 | } 101 | 102 | want := workerFuncs(t) 103 | if err := w.Exit(); err != nil { 104 | t.Fatal(err) 105 | } 106 | 107 | if !reflect.DeepEqual(funcs, want) { 108 | t.Errorf("got: %+v, want: %+v", funcs, want) 109 | } 110 | } 111 | 112 | func workerFuncs(t *testing.T) Funcs { 113 | // New 114 | cmd := execCommand(*workerPath) 115 | 116 | wc, err := cmd.StdinPipe() 117 | if err != nil { 118 | t.Fatal(err) 119 | } 120 | 121 | rc, err := cmd.StdoutPipe() 122 | if err != nil { 123 | t.Fatal(err) 124 | } 125 | 126 | err = cmd.Start() 127 | if err != nil { 128 | t.Fatal(err) 129 | } 130 | 131 | // (*Worker).unmarshalFuncs 132 | data, err := lichdata.ReadFrom(rc) 133 | if err != nil { 134 | t.Fatal(err) 135 | } 136 | 137 | var funcs Funcs 138 | _, err = funcs.UnmarshalMsg(data) 139 | if err != nil { 140 | t.Fatal(err) 141 | } 142 | 143 | // (*Worker).Close 144 | wc.Write([]byte{'\n'}) 145 | wc.Close() 146 | 147 | // (*Worker).waitForExit 148 | err = cmd.Wait() 149 | if err != nil { 150 | t.Fatal(err) 151 | } 152 | 153 | return funcs 154 | } 155 | 156 | func TestCall(t *testing.T) { 157 | defer swizzle("Call")() 158 | 159 | w, funcs, err := New(*workerPath) 160 | if err != nil { 161 | t.Fatal(err) 162 | } 163 | 164 | defer w.Exit() 165 | 166 | resp, crashed, err := w.Call(&swerker.Call{Func: 0}) // rpc_funcs 167 | if crashed { 168 | t.Fatalf("worker crashed: %v", err) 169 | } 170 | 171 | if err != nil { 172 | t.Errorf("err = %#v, want: nil", err) 173 | } 174 | 175 | var got Funcs 176 | if _, err := got.UnmarshalMsg(resp); err != nil { 177 | t.Fatal(err) 178 | } 179 | 180 | if !reflect.DeepEqual(got, funcs) { 181 | t.Errorf("got %q, want: %q", got, funcs) 182 | } 183 | } 184 | 185 | func TestCallWhenExited(t *testing.T) { 186 | defer swizzle("Call")() 187 | 188 | w, _, err := New(*workerPath) 189 | if err != nil { 190 | t.Fatal(err) 191 | } 192 | 193 | w.Exit() 194 | 195 | resp, crashed, err := w.Call(&swerker.Call{Func: 0}) // rpc_funcs 196 | if crashed { 197 | t.Fatalf("worker crashed: %v", err) 198 | } 199 | 200 | if resp != nil { 201 | t.Errorf("resp = [% x], want: nil", resp) 202 | } 203 | 204 | if err != ErrProcessExited { 205 | t.Errorf("err = %#v, want: %q", err, ErrProcessExited) 206 | } 207 | } 208 | 209 | func TestCallError(t *testing.T) { 210 | defer swizzle("CallError")() 211 | 212 | w, funcs, err := New(*workerPath) 213 | if err != nil { 214 | t.Fatal(err) 215 | } 216 | 217 | defer w.Exit() 218 | 219 | testError, ok := funcs.Lookup("test_error") 220 | if !ok { 221 | t.Fatal(`worker does not implement "test_error" function`) 222 | } 223 | 224 | resp, crashed, err := w.Call(&swerker.Call{Func: testError}) 225 | if resp != nil { 226 | t.Errorf("resp = [% x], want: nil", resp) 227 | } 228 | 229 | if crashed { 230 | t.Error("process is crashed!") 231 | } 232 | 233 | want := &Error{Msg: "test_error called", Debug: "func=test_error"} 234 | if !reflect.DeepEqual(err, want) { 235 | t.Errorf("err = %#v, want: %q", err, want) 236 | } 237 | } 238 | 239 | func TestCrashedCall(t *testing.T) { 240 | defer swizzle("CrashedCall")() 241 | 242 | w, funcs, err := New(*workerPath) 243 | if err != nil { 244 | t.Fatal(err) 245 | } 246 | 247 | defer w.Exit() 248 | 249 | testCrash, ok := funcs.Lookup("test_crash") 250 | if !ok { 251 | t.Fatal(`worker does not implement "test_crash" function`) 252 | } 253 | 254 | resp, crashed, err := w.Call(&swerker.Call{Func: testCrash}) 255 | if !crashed { 256 | t.Error("process has not crashed") 257 | } 258 | 259 | if resp != nil { 260 | t.Errorf("resp = [% x], want: nil", resp) 261 | } 262 | 263 | want := &Error{"test_crash called", "func=test_crash", true} 264 | if !reflect.DeepEqual(err, want) { 265 | t.Errorf("err = %#v, want: %q", err, want) 266 | } 267 | } 268 | 269 | func TestExitedAfterCrashedCall(t *testing.T) { 270 | defer swizzle("CrashedCall")() 271 | 272 | w, funcs, err := New(*workerPath) 273 | if err != nil { 274 | t.Fatal(err) 275 | } 276 | 277 | defer w.Exit() 278 | 279 | testCrash, ok := funcs.Lookup("test_crash") 280 | if !ok { 281 | t.Fatal(`worker does not implement "test_crash" function`) 282 | } 283 | 284 | _, crashed, _ := w.Call(&swerker.Call{Func: testCrash}) 285 | if !crashed { 286 | t.Fatal("worker is not crashed") 287 | } 288 | 289 | if !w.(*worker).exited() { 290 | t.Error("Call returned before process has exited") 291 | } 292 | } 293 | 294 | func TestUnexpectedExit(t *testing.T) { 295 | mockOnly(t) 296 | defer swizzle("UnexpectedExit")() 297 | 298 | w, _, err := New(*workerPath) 299 | if err != nil { 300 | t.Fatal(err) 301 | } 302 | 303 | defer w.Exit() 304 | 305 | resp, crashed, err := w.Call(&swerker.Call{Func: 0}) // rpc_funcs 306 | if !crashed { 307 | t.Error("worker is not crashed") 308 | } 309 | 310 | if resp != nil { 311 | t.Errorf("resp = [% x], want: nil", resp) 312 | } 313 | 314 | if _, ok := err.(*UnexpectedExitError); !ok { 315 | t.Errorf("err.(type) = %T, want: %T", err, (*UnexpectedExitError)(nil)) 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /swerker/stdio/stdio.go: -------------------------------------------------------------------------------- 1 | // Package stdio implements a dispatcher that runs multiple swerker-stdio 2 | // processes. 3 | package stdio 4 | 5 | import ( 6 | "errors" 7 | "fmt" 8 | "path/filepath" 9 | "runtime" 10 | "strings" 11 | "sync" 12 | 13 | "github.com/astrotools/swego/swerker" 14 | "github.com/astrotools/swego/swerker/stdio/internal/worker" 15 | 16 | "github.com/tinylib/msgp/msgp" 17 | ) 18 | 19 | // Dispatcher runs a set of swerker-stdio worker processes. 20 | type Dispatcher struct { 21 | procs int 22 | path string 23 | data string 24 | workers []worker.Worker 25 | workersMu sync.RWMutex // protects workers 26 | queue chan task 27 | crashed chan worker.Worker 28 | workDone chan struct{} 29 | closed chan struct{} 30 | onNewErr func(error) 31 | onExitErr func(error) 32 | funcs worker.FuncsMap 33 | lastIdx uint8 34 | } 35 | 36 | type task struct { 37 | call *swerker.Call 38 | result chan result // TODO: benchmark impact of pooling 39 | } 40 | 41 | type result struct { 42 | data msgp.Raw 43 | err error 44 | } 45 | 46 | // An Option configures an optional Dispatcher parameter. 47 | type Option func(*Dispatcher) 48 | 49 | // NumWorkers configures the number of processes are started by Dispatcher. 50 | // If num is 0, the number of logical processors usable by the current process 51 | // is used. 52 | func NumWorkers(num int) Option { 53 | return func(d *Dispatcher) { 54 | d.procs = num 55 | } 56 | } 57 | 58 | // OnNewError configures a Dispatcher to call fn when a worker could not be 59 | // restarted. 60 | func OnNewError(fn func(err error)) Option { 61 | return func(d *Dispatcher) { 62 | d.onNewErr = fn 63 | } 64 | } 65 | 66 | // OnExitError configures a Dispatcher to call fn with the exit error of a 67 | // crashed worker. 68 | func OnExitError(fn func(err error)) Option { 69 | return func(d *Dispatcher) { 70 | d.onExitErr = fn 71 | } 72 | } 73 | 74 | var newWorker = worker.New // for testing 75 | 76 | // New returns a Dispatcher that interfaces via swerker-stdio with the 77 | // Swiss Ephemeris. As it takes the file system path to the binary and the 78 | // number of instances of the program as arguments. By default the number of 79 | // logical processors usable by the current process is used. 80 | func New(path string, opts ...Option) (d *Dispatcher, err error) { 81 | d = &Dispatcher{ 82 | path: path, 83 | queue: make(chan task), 84 | crashed: make(chan worker.Worker), 85 | workDone: make(chan struct{}), 86 | closed: make(chan struct{}), 87 | } 88 | 89 | for _, opt := range opts { 90 | opt(d) 91 | } 92 | 93 | if d.procs == 0 { 94 | d.procs = runtime.NumCPU() 95 | } 96 | 97 | defer func() { 98 | if err != nil { 99 | d.Close() 100 | } 101 | }() 102 | 103 | d.workers = make([]worker.Worker, d.procs) 104 | for i := 0; i < d.procs; i++ { 105 | w, funcs, err := d.newWorker() 106 | if err != nil { 107 | return nil, err 108 | } 109 | 110 | if i == 0 { 111 | d.funcs = funcs.FuncsMap() 112 | d.lastIdx = funcs.LastIdx() 113 | } 114 | 115 | d.workers[i] = w 116 | } 117 | 118 | go d.restartWorkers() 119 | return d, nil 120 | } 121 | 122 | func (d *Dispatcher) newWorker() (worker.Worker, worker.Funcs, error) { 123 | w, funcs, err := newWorker(d.path) 124 | if err != nil { 125 | return nil, nil, err 126 | } 127 | 128 | if d.data != "" { 129 | if idx, ok := d.IndexForName("swe_set_ephe_path"); ok { 130 | var args []byte 131 | args = msgp.AppendArrayHeader(args, 1) 132 | args = msgp.AppendString(args, d.data) 133 | w.Call(&swerker.Call{Func: idx, Args: args}) 134 | } 135 | } 136 | 137 | go d.runWorker(w) 138 | return w, funcs, nil 139 | } 140 | 141 | func (d *Dispatcher) runWorker(w worker.Worker) { 142 | for t := range d.queue { 143 | d.workersMu.RLock() 144 | 145 | data, crashed, err := w.Call(t.call) 146 | t.result <- result{data, err} 147 | if crashed { 148 | err := w.Exit() 149 | if err != nil && d.onExitErr != nil { 150 | d.onExitErr(err) 151 | } 152 | 153 | d.crashed <- w 154 | d.workersMu.RUnlock() 155 | return 156 | } 157 | 158 | d.workersMu.RUnlock() 159 | } 160 | 161 | if idx, ok := d.IndexForName("swe_close"); ok { 162 | w.Call(&swerker.Call{Func: idx}) 163 | } 164 | 165 | w.Exit() 166 | } 167 | 168 | func (d *Dispatcher) restartWorkers() { 169 | for { 170 | select { 171 | case cw := <-d.crashed: 172 | d.workersMu.Lock() 173 | 174 | for i, w := range d.workers { 175 | if w == cw { 176 | w, _, err := d.newWorker() 177 | if err != nil && d.onNewErr != nil { 178 | d.onNewErr(err) 179 | } else { 180 | d.workers[i] = w 181 | } 182 | 183 | break 184 | } 185 | } 186 | 187 | d.workersMu.Unlock() 188 | case <-d.workDone: 189 | d.closed <- struct{}{} 190 | return 191 | } 192 | } 193 | } 194 | 195 | // Close terminates and cleans the worker processes. 196 | func (d *Dispatcher) Close() error { 197 | close(d.queue) 198 | close(d.workDone) 199 | <-d.closed 200 | return nil 201 | } 202 | 203 | // Path returns the path of the swerker-stdio binary used by dispatcher d. 204 | func (d *Dispatcher) Path() string { return d.path } 205 | 206 | // DataPath configures one or more ephemeris data paths. These are passed to 207 | // each worker by calling swe_set_ephe_path. The paths are combined to a list 208 | // of separated paths. 209 | func DataPath(paths ...string) Option { 210 | return func(d *Dispatcher) { 211 | d.data = combineDataPaths(paths) 212 | } 213 | } 214 | 215 | const sep = string(filepath.Separator) 216 | const lsep = string(filepath.ListSeparator) 217 | 218 | func combineDataPaths(paths []string) string { 219 | var s []string 220 | 221 | for _, path := range paths { 222 | if !strings.HasSuffix(path, sep) { 223 | path += sep 224 | } 225 | 226 | s = append(s, path) 227 | } 228 | 229 | return strings.Join(s, lsep) 230 | } 231 | 232 | // DataPath returns the list of ephemeris data paths send to the workers. 233 | func (d *Dispatcher) DataPath() string { return d.data } 234 | 235 | // DataPaths returns a slice of ephemeris data paths send to the workers. 236 | func (d *Dispatcher) DataPaths() (s []string) { 237 | paths := strings.Split(d.DataPath(), lsep) 238 | for _, path := range paths { 239 | if strings.HasSuffix(path, sep) { 240 | path = path[:len(path)-len(sep)] 241 | } 242 | 243 | s = append(s, path) 244 | } 245 | 246 | return s 247 | } 248 | 249 | // IndexForName implements swerker.Dispatcher interface. 250 | func (d *Dispatcher) IndexForName(name string) (uint8, bool) { 251 | idx, ok := d.funcs[name] 252 | return idx, ok 253 | } 254 | 255 | // UnimplementedError is returned if the requested function is not implemented 256 | // by the worker process. 257 | type UnimplementedError struct { 258 | Func uint8 259 | } 260 | 261 | func (e *UnimplementedError) Error() string { 262 | return fmt.Sprintf("stdio: unimplemented function %d", e.Func) 263 | } 264 | 265 | // Dispatch implements swerker.Dispatcher interface. 266 | func (d *Dispatcher) Dispatch(c *swerker.Call) (msgp.Raw, error) { 267 | if c.Func > d.lastIdx { 268 | return nil, &UnimplementedError{c.Func} 269 | } 270 | 271 | t := task{c, make(chan result)} 272 | d.queue <- t 273 | r := <-t.result 274 | return r.data, r.err 275 | } 276 | 277 | // Version returns the Swiss Ephemeris version linked by the swerker-stdio 278 | // binary. 279 | func Version(path string) (v string, err error) { 280 | w, funcs, err := newWorker(path) 281 | if err != nil { 282 | return "", err 283 | } 284 | 285 | // terminate worker subprocess on function return 286 | defer func() { 287 | exitErr := w.Exit() 288 | if err == nil && exitErr != nil { 289 | err = exitErr 290 | } 291 | }() 292 | 293 | const name = "swe_version" 294 | fn, ok := funcs.Lookup(name) 295 | if !ok { 296 | return "", fmt.Errorf("stdio: function %q not found", name) 297 | } 298 | 299 | data, crashed, err := w.Call(&swerker.Call{Func: fn}) 300 | if crashed { 301 | return "", err 302 | } 303 | 304 | size, data, err := msgp.ReadArrayHeaderBytes(data) 305 | if err != nil { 306 | return "", errors.New("stdio: unexpected return value type") 307 | } 308 | 309 | if size != 1 { 310 | return "", errors.New("stdio: unexpected return value length") 311 | } 312 | 313 | v, _, err = msgp.ReadStringBytes(data) 314 | return v, err 315 | } 316 | -------------------------------------------------------------------------------- /swerker/stdio/stdio_test.go: -------------------------------------------------------------------------------- 1 | package stdio 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "os/exec" 7 | "reflect" 8 | "testing" 9 | 10 | "github.com/astrotools/swego/swerker" 11 | "github.com/astrotools/swego/swerker/stdio/internal/worker" 12 | 13 | "github.com/tinylib/msgp/msgp" 14 | ) 15 | 16 | type testWorker struct { 17 | path string 18 | call callFunc 19 | exit exitFunc 20 | } 21 | 22 | type newFunc func(string) (worker.Worker, worker.Funcs, error) 23 | type callFunc func(*swerker.Call) (msgp.Raw, bool, error) 24 | type exitFunc func() error 25 | 26 | func (w *testWorker) Exit() error { return w.exit() } 27 | func (w *testWorker) Call(c *swerker.Call) (msgp.Raw, bool, error) { 28 | return w.call(c) 29 | } 30 | 31 | func newTestWorker(funcs worker.Funcs, call callFunc, exit exitFunc) newFunc { 32 | return func(path string) (worker.Worker, worker.Funcs, error) { 33 | return &testWorker{path, call, exit}, funcs, nil 34 | } 35 | } 36 | 37 | const workerPath = "/path/to/swerker-stdio" 38 | 39 | func TestNew(t *testing.T) { 40 | dataPaths := []string{"/path/to/longfiles", "/path/to/files"} 41 | funcs := worker.Funcs{"rpc_funcs", "swe_set_ephe_path"} 42 | 43 | defer func() { newWorker = worker.New }() 44 | newWorker = newTestWorker(funcs, func(c *swerker.Call) (msgp.Raw, bool, error) { 45 | const idx = 1 46 | if c.Func != idx { 47 | t.Errorf("swe_set_ephe_path func = %d, want: %d", c.Func, idx) 48 | } 49 | 50 | var args []byte 51 | args = msgp.AppendArrayHeader(args, 1) 52 | args = msgp.AppendString(args, combineDataPaths(dataPaths)) 53 | 54 | if !bytes.Equal([]byte(c.Args), args) { 55 | t.Errorf("swe_set_ephe_path args =\n\t[% x]\nwant:\n\t[% x]", c.Args, args) 56 | } 57 | 58 | if c.Ctx != nil { 59 | t.Errorf("swe_set_ephe_path ctx = %#v, want: nil", c.Ctx) 60 | } 61 | 62 | return msgp.Raw{0x90}, false, nil 63 | }, func() error { 64 | return nil 65 | }) 66 | 67 | d, err := New(workerPath, NumWorkers(2), DataPath(dataPaths...)) 68 | if err != nil { 69 | t.Fatalf("err = %v, want: nil", err) 70 | } 71 | 72 | if d == nil { 73 | t.Fatal("d == nil") 74 | } 75 | 76 | if got := d.Path(); got != workerPath { 77 | t.Errorf("Path() = %q, want: %q", got, workerPath) 78 | } 79 | 80 | if got := d.DataPaths(); !reflect.DeepEqual(got, dataPaths) { 81 | t.Errorf("data path = %q, want: %q", got, dataPaths) 82 | } 83 | } 84 | 85 | func TestClose(t *testing.T) { 86 | funcs := worker.Funcs{"rpc_funcs", "swe_close"} 87 | 88 | defer func() { newWorker = worker.New }() 89 | newWorker = newTestWorker(funcs, func(c *swerker.Call) (msgp.Raw, bool, error) { 90 | const idx = 1 91 | if c.Func != idx { 92 | t.Errorf("swe_close func = %d, want: %d", c.Func, idx) 93 | } 94 | 95 | if !bytes.Equal([]byte(c.Args), nil) { 96 | t.Errorf("swe_close args =\n\t[% x]\nwant: []", c.Args) 97 | } 98 | 99 | if c.Ctx != nil { 100 | t.Errorf("swe_close ctx = %#v, want: nil", c.Ctx) 101 | } 102 | 103 | return msgp.Raw{0x90}, false, nil 104 | }, func() error { 105 | return nil 106 | }) 107 | 108 | d, err := New(workerPath, NumWorkers(1)) 109 | if err != nil { 110 | t.Fatalf("err = %v, want: nil", err) 111 | } 112 | 113 | if d == nil { 114 | t.Fatal("d == nil") 115 | } 116 | 117 | if err := d.Close(); err != nil { 118 | t.Errorf("err = %v, want: nil", err) 119 | } 120 | } 121 | 122 | func TestIndexForName(t *testing.T) { 123 | funcs := worker.Funcs{"rpc_funcs"} 124 | 125 | d := &Dispatcher{funcs: funcs.FuncsMap()} 126 | idx, ok := d.IndexForName("rpc_funcs") 127 | if !ok { 128 | t.Error("ok = false, want: true") 129 | } 130 | 131 | if idx != 0 { 132 | t.Errorf("idx = %d, want: 0", idx) 133 | } 134 | } 135 | 136 | func TestDispatch(t *testing.T) { 137 | funcs := worker.Funcs{"rpc_funcs", "test_func"} 138 | 139 | defer func() { newWorker = worker.New }() 140 | newWorker = newTestWorker(funcs, func(c *swerker.Call) (msgp.Raw, bool, error) { 141 | const idx = 1 142 | if c.Func != idx { 143 | t.Errorf("test_func func = %d, want: %d", c.Func, idx) 144 | } 145 | 146 | if !bytes.Equal([]byte(c.Args), nil) { 147 | t.Errorf("test_func args =\n\t[% x]\nwant: []", c.Args) 148 | } 149 | 150 | if c.Ctx != nil { 151 | t.Errorf("test_func ctx = %#v, want: nil", c.Ctx) 152 | } 153 | 154 | return msgp.Raw{0x90}, false, nil 155 | }, func() error { 156 | return nil 157 | }) 158 | 159 | d, err := New(workerPath, NumWorkers(1)) 160 | if err != nil { 161 | t.Fatalf("err = %v, want: nil", err) 162 | } 163 | 164 | if d == nil { 165 | t.Fatal("d == nil") 166 | } 167 | 168 | t.Run("Unimplemented", func(t *testing.T) { 169 | fn := funcs.LastIdx() + 1 170 | 171 | data, err := d.Dispatch(&swerker.Call{Func: fn}) 172 | if data != nil { 173 | t.Errorf("data = [% x], want: nil", data) 174 | } 175 | 176 | want := &UnimplementedError{Func: fn} 177 | if !reflect.DeepEqual(err, want) { 178 | t.Errorf("err = %v, want: %v", err, want) 179 | } 180 | }) 181 | 182 | t.Run("Call", func(t *testing.T) { 183 | data, err := d.Dispatch(&swerker.Call{Func: funcs.LastIdx()}) 184 | if err != nil { 185 | t.Errorf("err = %v, want: nil", err) 186 | } 187 | 188 | if !bytes.Equal(data, msgp.Raw{0x90}) { 189 | t.Errorf("data = [% x], want: [90]", data) 190 | } 191 | }) 192 | 193 | if err := d.Close(); err != nil { 194 | t.Errorf("err = %v, want: nil", err) 195 | } 196 | } 197 | 198 | func TestCrash(t *testing.T) { 199 | funcs := worker.Funcs{"rpc_funcs", "test_crash"} 200 | exitErr := &exec.Error{Name: workerPath, Err: errors.New("some exit error")} 201 | 202 | defer func() { newWorker = worker.New }() 203 | newWorker = newTestWorker(funcs, func(c *swerker.Call) (msgp.Raw, bool, error) { 204 | const idx = 1 205 | if c.Func != idx { 206 | t.Errorf("test_func func = %d, want: %d", c.Func, idx) 207 | } 208 | 209 | if !bytes.Equal([]byte(c.Args), nil) { 210 | t.Errorf("test_func args =\n\t[% x]\nwant: []", c.Args) 211 | } 212 | 213 | if c.Ctx != nil { 214 | t.Errorf("test_func ctx = %#v, want: nil", c.Ctx) 215 | } 216 | 217 | return nil, true, &worker.Error{ 218 | Msg: "test_crash called", 219 | Debug: "func=test_crash", 220 | Panic: true, 221 | } 222 | }, func() error { 223 | return exitErr 224 | }) 225 | 226 | d, err := New(workerPath, NumWorkers(2), OnExitError(func(err error) { 227 | if err != exitErr { 228 | t.Errorf("err = %v, want: %#v", err, exitErr) 229 | } 230 | }), OnNewError(func(err error) { 231 | if err != nil { 232 | t.Errorf("err = %v, want: nil", err) 233 | } 234 | })) 235 | 236 | if err != nil { 237 | t.Fatalf("err = %v, want: nil", err) 238 | } 239 | 240 | if d == nil { 241 | t.Fatal("d == nil") 242 | } 243 | 244 | data, err := d.Dispatch(&swerker.Call{Func: funcs.LastIdx()}) 245 | if data != nil { 246 | t.Errorf("data = [% x], want: nil", data) 247 | } 248 | 249 | want := &worker.Error{ 250 | Msg: "test_crash called", 251 | Debug: "func=test_crash", 252 | Panic: true, 253 | } 254 | 255 | if !reflect.DeepEqual(err, want) { 256 | t.Errorf("err = %v, want: %#v", err, want) 257 | } 258 | 259 | if err := d.Close(); err != nil { 260 | t.Errorf("err = %v, want: nil", err) 261 | } 262 | } 263 | 264 | func TestVersion(t *testing.T) { 265 | funcs := worker.Funcs{"rpc_funcs", "swe_version"} 266 | const version = "2.00" 267 | 268 | defer func() { newWorker = worker.New }() 269 | newWorker = newTestWorker(funcs, func(c *swerker.Call) (msgp.Raw, bool, error) { 270 | const idx = 1 271 | if c.Func != idx { 272 | t.Errorf("swe_version func = %d, want: %d", c.Func, idx) 273 | } 274 | 275 | if c.Args != nil { 276 | t.Errorf("swe_version args = [% x], want: nil", c.Args) 277 | } 278 | 279 | if c.Ctx != nil { 280 | t.Errorf("swe_version ctx = %#v, want: nil", c.Ctx) 281 | } 282 | 283 | return msgp.Raw("\x91\xa4" + version), false, nil 284 | }, func() error { 285 | return nil 286 | }) 287 | 288 | v, err := Version(workerPath) 289 | if err != nil { 290 | t.Fatalf("err = %v, want: nil", err) 291 | } 292 | 293 | if v != version { 294 | t.Errorf("v = %q, want: %q", v, version) 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /swerker/swerker.go: -------------------------------------------------------------------------------- 1 | // Package swerker provides an interface for interfacing with worker processes. 2 | package swerker 3 | 4 | import "github.com/tinylib/msgp/msgp" 5 | 6 | // Dispatcher dispatches calls to a backend worker. 7 | type Dispatcher interface { 8 | // IndexForName look up an index for a function name. If the function name is 9 | // found the index and true is returned, otherwise 0 and false. The lookup is 10 | // done in constant time if possible. 11 | // 12 | // This mapping is specific for a backend and Swiss Ephemeris version and 13 | // must not be cached by the client. 14 | IndexForName(string) (uint8, bool) 15 | 16 | // Dispatch dispatches a call to a backend. 17 | Dispatch(*Call) (msgp.Raw, error) 18 | } 19 | 20 | //go:generate msgp 21 | 22 | //msgp:tuple Call 23 | //msgp:tuple CtxCall 24 | 25 | // A Call represents a call to the Swiss Ephemeris. It has optionally a 26 | // context, one or more context calls that are executed before the actual call. 27 | type Call struct { 28 | Ctx []*CtxCall 29 | Func uint8 30 | Args msgp.Raw 31 | } 32 | 33 | // A CtxCall is a context call. 34 | type CtxCall struct { 35 | Func uint8 36 | Args msgp.Raw 37 | } 38 | -------------------------------------------------------------------------------- /swerker/swerker_gen.go: -------------------------------------------------------------------------------- 1 | package swerker 2 | 3 | // NOTE: THIS FILE WAS PRODUCED BY THE 4 | // MSGP CODE GENERATION TOOL (github.com/tinylib/msgp) 5 | // DO NOT EDIT 6 | 7 | import ( 8 | "github.com/tinylib/msgp/msgp" 9 | ) 10 | 11 | // DecodeMsg implements msgp.Decodable 12 | func (z *Call) DecodeMsg(dc *msgp.Reader) (err error) { 13 | var zbzg uint32 14 | zbzg, err = dc.ReadArrayHeader() 15 | if err != nil { 16 | return 17 | } 18 | if zbzg != 3 { 19 | err = msgp.ArrayError{Wanted: 3, Got: zbzg} 20 | return 21 | } 22 | var zbai uint32 23 | zbai, err = dc.ReadArrayHeader() 24 | if err != nil { 25 | return 26 | } 27 | if cap(z.Ctx) >= int(zbai) { 28 | z.Ctx = (z.Ctx)[:zbai] 29 | } else { 30 | z.Ctx = make([]*CtxCall, zbai) 31 | } 32 | for zxvk := range z.Ctx { 33 | if dc.IsNil() { 34 | err = dc.ReadNil() 35 | if err != nil { 36 | return 37 | } 38 | z.Ctx[zxvk] = nil 39 | } else { 40 | if z.Ctx[zxvk] == nil { 41 | z.Ctx[zxvk] = new(CtxCall) 42 | } 43 | var zcmr uint32 44 | zcmr, err = dc.ReadArrayHeader() 45 | if err != nil { 46 | return 47 | } 48 | if zcmr != 2 { 49 | err = msgp.ArrayError{Wanted: 2, Got: zcmr} 50 | return 51 | } 52 | z.Ctx[zxvk].Func, err = dc.ReadUint8() 53 | if err != nil { 54 | return 55 | } 56 | err = z.Ctx[zxvk].Args.DecodeMsg(dc) 57 | if err != nil { 58 | return 59 | } 60 | } 61 | } 62 | z.Func, err = dc.ReadUint8() 63 | if err != nil { 64 | return 65 | } 66 | err = z.Args.DecodeMsg(dc) 67 | if err != nil { 68 | return 69 | } 70 | return 71 | } 72 | 73 | // EncodeMsg implements msgp.Encodable 74 | func (z *Call) EncodeMsg(en *msgp.Writer) (err error) { 75 | // array header, size 3 76 | err = en.Append(0x93) 77 | if err != nil { 78 | return err 79 | } 80 | err = en.WriteArrayHeader(uint32(len(z.Ctx))) 81 | if err != nil { 82 | return 83 | } 84 | for zxvk := range z.Ctx { 85 | if z.Ctx[zxvk] == nil { 86 | err = en.WriteNil() 87 | if err != nil { 88 | return 89 | } 90 | } else { 91 | // array header, size 2 92 | err = en.Append(0x92) 93 | if err != nil { 94 | return err 95 | } 96 | err = en.WriteUint8(z.Ctx[zxvk].Func) 97 | if err != nil { 98 | return 99 | } 100 | err = z.Ctx[zxvk].Args.EncodeMsg(en) 101 | if err != nil { 102 | return 103 | } 104 | } 105 | } 106 | err = en.WriteUint8(z.Func) 107 | if err != nil { 108 | return 109 | } 110 | err = z.Args.EncodeMsg(en) 111 | if err != nil { 112 | return 113 | } 114 | return 115 | } 116 | 117 | // MarshalMsg implements msgp.Marshaler 118 | func (z *Call) MarshalMsg(b []byte) (o []byte, err error) { 119 | o = msgp.Require(b, z.Msgsize()) 120 | // array header, size 3 121 | o = append(o, 0x93) 122 | o = msgp.AppendArrayHeader(o, uint32(len(z.Ctx))) 123 | for zxvk := range z.Ctx { 124 | if z.Ctx[zxvk] == nil { 125 | o = msgp.AppendNil(o) 126 | } else { 127 | // array header, size 2 128 | o = append(o, 0x92) 129 | o = msgp.AppendUint8(o, z.Ctx[zxvk].Func) 130 | o, err = z.Ctx[zxvk].Args.MarshalMsg(o) 131 | if err != nil { 132 | return 133 | } 134 | } 135 | } 136 | o = msgp.AppendUint8(o, z.Func) 137 | o, err = z.Args.MarshalMsg(o) 138 | if err != nil { 139 | return 140 | } 141 | return 142 | } 143 | 144 | // UnmarshalMsg implements msgp.Unmarshaler 145 | func (z *Call) UnmarshalMsg(bts []byte) (o []byte, err error) { 146 | var zajw uint32 147 | zajw, bts, err = msgp.ReadArrayHeaderBytes(bts) 148 | if err != nil { 149 | return 150 | } 151 | if zajw != 3 { 152 | err = msgp.ArrayError{Wanted: 3, Got: zajw} 153 | return 154 | } 155 | var zwht uint32 156 | zwht, bts, err = msgp.ReadArrayHeaderBytes(bts) 157 | if err != nil { 158 | return 159 | } 160 | if cap(z.Ctx) >= int(zwht) { 161 | z.Ctx = (z.Ctx)[:zwht] 162 | } else { 163 | z.Ctx = make([]*CtxCall, zwht) 164 | } 165 | for zxvk := range z.Ctx { 166 | if msgp.IsNil(bts) { 167 | bts, err = msgp.ReadNilBytes(bts) 168 | if err != nil { 169 | return 170 | } 171 | z.Ctx[zxvk] = nil 172 | } else { 173 | if z.Ctx[zxvk] == nil { 174 | z.Ctx[zxvk] = new(CtxCall) 175 | } 176 | var zhct uint32 177 | zhct, bts, err = msgp.ReadArrayHeaderBytes(bts) 178 | if err != nil { 179 | return 180 | } 181 | if zhct != 2 { 182 | err = msgp.ArrayError{Wanted: 2, Got: zhct} 183 | return 184 | } 185 | z.Ctx[zxvk].Func, bts, err = msgp.ReadUint8Bytes(bts) 186 | if err != nil { 187 | return 188 | } 189 | bts, err = z.Ctx[zxvk].Args.UnmarshalMsg(bts) 190 | if err != nil { 191 | return 192 | } 193 | } 194 | } 195 | z.Func, bts, err = msgp.ReadUint8Bytes(bts) 196 | if err != nil { 197 | return 198 | } 199 | bts, err = z.Args.UnmarshalMsg(bts) 200 | if err != nil { 201 | return 202 | } 203 | o = bts 204 | return 205 | } 206 | 207 | // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message 208 | func (z *Call) Msgsize() (s int) { 209 | s = 1 + msgp.ArrayHeaderSize 210 | for zxvk := range z.Ctx { 211 | if z.Ctx[zxvk] == nil { 212 | s += msgp.NilSize 213 | } else { 214 | s += 1 + msgp.Uint8Size + z.Ctx[zxvk].Args.Msgsize() 215 | } 216 | } 217 | s += msgp.Uint8Size + z.Args.Msgsize() 218 | return 219 | } 220 | 221 | // DecodeMsg implements msgp.Decodable 222 | func (z *CtxCall) DecodeMsg(dc *msgp.Reader) (err error) { 223 | var zcua uint32 224 | zcua, err = dc.ReadArrayHeader() 225 | if err != nil { 226 | return 227 | } 228 | if zcua != 2 { 229 | err = msgp.ArrayError{Wanted: 2, Got: zcua} 230 | return 231 | } 232 | z.Func, err = dc.ReadUint8() 233 | if err != nil { 234 | return 235 | } 236 | err = z.Args.DecodeMsg(dc) 237 | if err != nil { 238 | return 239 | } 240 | return 241 | } 242 | 243 | // EncodeMsg implements msgp.Encodable 244 | func (z *CtxCall) EncodeMsg(en *msgp.Writer) (err error) { 245 | // array header, size 2 246 | err = en.Append(0x92) 247 | if err != nil { 248 | return err 249 | } 250 | err = en.WriteUint8(z.Func) 251 | if err != nil { 252 | return 253 | } 254 | err = z.Args.EncodeMsg(en) 255 | if err != nil { 256 | return 257 | } 258 | return 259 | } 260 | 261 | // MarshalMsg implements msgp.Marshaler 262 | func (z *CtxCall) MarshalMsg(b []byte) (o []byte, err error) { 263 | o = msgp.Require(b, z.Msgsize()) 264 | // array header, size 2 265 | o = append(o, 0x92) 266 | o = msgp.AppendUint8(o, z.Func) 267 | o, err = z.Args.MarshalMsg(o) 268 | if err != nil { 269 | return 270 | } 271 | return 272 | } 273 | 274 | // UnmarshalMsg implements msgp.Unmarshaler 275 | func (z *CtxCall) UnmarshalMsg(bts []byte) (o []byte, err error) { 276 | var zxhx uint32 277 | zxhx, bts, err = msgp.ReadArrayHeaderBytes(bts) 278 | if err != nil { 279 | return 280 | } 281 | if zxhx != 2 { 282 | err = msgp.ArrayError{Wanted: 2, Got: zxhx} 283 | return 284 | } 285 | z.Func, bts, err = msgp.ReadUint8Bytes(bts) 286 | if err != nil { 287 | return 288 | } 289 | bts, err = z.Args.UnmarshalMsg(bts) 290 | if err != nil { 291 | return 292 | } 293 | o = bts 294 | return 295 | } 296 | 297 | // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message 298 | func (z *CtxCall) Msgsize() (s int) { 299 | s = 1 + msgp.Uint8Size + z.Args.Msgsize() 300 | return 301 | } 302 | -------------------------------------------------------------------------------- /swerker/swerker_gen_test.go: -------------------------------------------------------------------------------- 1 | package swerker 2 | 3 | // NOTE: THIS FILE WAS PRODUCED BY THE 4 | // MSGP CODE GENERATION TOOL (github.com/tinylib/msgp) 5 | // DO NOT EDIT 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | 11 | "github.com/tinylib/msgp/msgp" 12 | ) 13 | 14 | func TestMarshalUnmarshalCall(t *testing.T) { 15 | v := Call{} 16 | bts, err := v.MarshalMsg(nil) 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | left, err := v.UnmarshalMsg(bts) 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | if len(left) > 0 { 25 | t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) 26 | } 27 | 28 | left, err = msgp.Skip(bts) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | if len(left) > 0 { 33 | t.Errorf("%d bytes left over after Skip(): %q", len(left), left) 34 | } 35 | } 36 | 37 | func BenchmarkMarshalMsgCall(b *testing.B) { 38 | v := Call{} 39 | b.ReportAllocs() 40 | b.ResetTimer() 41 | for i := 0; i < b.N; i++ { 42 | v.MarshalMsg(nil) 43 | } 44 | } 45 | 46 | func BenchmarkAppendMsgCall(b *testing.B) { 47 | v := Call{} 48 | bts := make([]byte, 0, v.Msgsize()) 49 | bts, _ = v.MarshalMsg(bts[0:0]) 50 | b.SetBytes(int64(len(bts))) 51 | b.ReportAllocs() 52 | b.ResetTimer() 53 | for i := 0; i < b.N; i++ { 54 | bts, _ = v.MarshalMsg(bts[0:0]) 55 | } 56 | } 57 | 58 | func BenchmarkUnmarshalCall(b *testing.B) { 59 | v := Call{} 60 | bts, _ := v.MarshalMsg(nil) 61 | b.ReportAllocs() 62 | b.SetBytes(int64(len(bts))) 63 | b.ResetTimer() 64 | for i := 0; i < b.N; i++ { 65 | _, err := v.UnmarshalMsg(bts) 66 | if err != nil { 67 | b.Fatal(err) 68 | } 69 | } 70 | } 71 | 72 | func TestEncodeDecodeCall(t *testing.T) { 73 | v := Call{} 74 | var buf bytes.Buffer 75 | msgp.Encode(&buf, &v) 76 | 77 | m := v.Msgsize() 78 | if buf.Len() > m { 79 | t.Logf("WARNING: Msgsize() for %v is inaccurate", v) 80 | } 81 | 82 | vn := Call{} 83 | err := msgp.Decode(&buf, &vn) 84 | if err != nil { 85 | t.Error(err) 86 | } 87 | 88 | buf.Reset() 89 | msgp.Encode(&buf, &v) 90 | err = msgp.NewReader(&buf).Skip() 91 | if err != nil { 92 | t.Error(err) 93 | } 94 | } 95 | 96 | func BenchmarkEncodeCall(b *testing.B) { 97 | v := Call{} 98 | var buf bytes.Buffer 99 | msgp.Encode(&buf, &v) 100 | b.SetBytes(int64(buf.Len())) 101 | en := msgp.NewWriter(msgp.Nowhere) 102 | b.ReportAllocs() 103 | b.ResetTimer() 104 | for i := 0; i < b.N; i++ { 105 | v.EncodeMsg(en) 106 | } 107 | en.Flush() 108 | } 109 | 110 | func BenchmarkDecodeCall(b *testing.B) { 111 | v := Call{} 112 | var buf bytes.Buffer 113 | msgp.Encode(&buf, &v) 114 | b.SetBytes(int64(buf.Len())) 115 | rd := msgp.NewEndlessReader(buf.Bytes(), b) 116 | dc := msgp.NewReader(rd) 117 | b.ReportAllocs() 118 | b.ResetTimer() 119 | for i := 0; i < b.N; i++ { 120 | err := v.DecodeMsg(dc) 121 | if err != nil { 122 | b.Fatal(err) 123 | } 124 | } 125 | } 126 | 127 | func TestMarshalUnmarshalCtxCall(t *testing.T) { 128 | v := CtxCall{} 129 | bts, err := v.MarshalMsg(nil) 130 | if err != nil { 131 | t.Fatal(err) 132 | } 133 | left, err := v.UnmarshalMsg(bts) 134 | if err != nil { 135 | t.Fatal(err) 136 | } 137 | if len(left) > 0 { 138 | t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) 139 | } 140 | 141 | left, err = msgp.Skip(bts) 142 | if err != nil { 143 | t.Fatal(err) 144 | } 145 | if len(left) > 0 { 146 | t.Errorf("%d bytes left over after Skip(): %q", len(left), left) 147 | } 148 | } 149 | 150 | func BenchmarkMarshalMsgCtxCall(b *testing.B) { 151 | v := CtxCall{} 152 | b.ReportAllocs() 153 | b.ResetTimer() 154 | for i := 0; i < b.N; i++ { 155 | v.MarshalMsg(nil) 156 | } 157 | } 158 | 159 | func BenchmarkAppendMsgCtxCall(b *testing.B) { 160 | v := CtxCall{} 161 | bts := make([]byte, 0, v.Msgsize()) 162 | bts, _ = v.MarshalMsg(bts[0:0]) 163 | b.SetBytes(int64(len(bts))) 164 | b.ReportAllocs() 165 | b.ResetTimer() 166 | for i := 0; i < b.N; i++ { 167 | bts, _ = v.MarshalMsg(bts[0:0]) 168 | } 169 | } 170 | 171 | func BenchmarkUnmarshalCtxCall(b *testing.B) { 172 | v := CtxCall{} 173 | bts, _ := v.MarshalMsg(nil) 174 | b.ReportAllocs() 175 | b.SetBytes(int64(len(bts))) 176 | b.ResetTimer() 177 | for i := 0; i < b.N; i++ { 178 | _, err := v.UnmarshalMsg(bts) 179 | if err != nil { 180 | b.Fatal(err) 181 | } 182 | } 183 | } 184 | 185 | func TestEncodeDecodeCtxCall(t *testing.T) { 186 | v := CtxCall{} 187 | var buf bytes.Buffer 188 | msgp.Encode(&buf, &v) 189 | 190 | m := v.Msgsize() 191 | if buf.Len() > m { 192 | t.Logf("WARNING: Msgsize() for %v is inaccurate", v) 193 | } 194 | 195 | vn := CtxCall{} 196 | err := msgp.Decode(&buf, &vn) 197 | if err != nil { 198 | t.Error(err) 199 | } 200 | 201 | buf.Reset() 202 | msgp.Encode(&buf, &v) 203 | err = msgp.NewReader(&buf).Skip() 204 | if err != nil { 205 | t.Error(err) 206 | } 207 | } 208 | 209 | func BenchmarkEncodeCtxCall(b *testing.B) { 210 | v := CtxCall{} 211 | var buf bytes.Buffer 212 | msgp.Encode(&buf, &v) 213 | b.SetBytes(int64(buf.Len())) 214 | en := msgp.NewWriter(msgp.Nowhere) 215 | b.ReportAllocs() 216 | b.ResetTimer() 217 | for i := 0; i < b.N; i++ { 218 | v.EncodeMsg(en) 219 | } 220 | en.Flush() 221 | } 222 | 223 | func BenchmarkDecodeCtxCall(b *testing.B) { 224 | v := CtxCall{} 225 | var buf bytes.Buffer 226 | msgp.Encode(&buf, &v) 227 | b.SetBytes(int64(buf.Len())) 228 | rd := msgp.NewEndlessReader(buf.Bytes(), b) 229 | dc := msgp.NewReader(rd) 230 | b.ReportAllocs() 231 | b.ResetTimer() 232 | for i := 0; i < b.N; i++ { 233 | err := v.DecodeMsg(dc) 234 | if err != nil { 235 | b.Fatal(err) 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /swex/genversion.go: -------------------------------------------------------------------------------- 1 | // +build IGNORE 2 | 3 | package main 4 | 5 | import ( 6 | "flag" 7 | "fmt" 8 | "io/ioutil" 9 | "log" 10 | "os" 11 | "regexp" 12 | "strings" 13 | ) 14 | 15 | func main() { 16 | log.SetPrefix("") 17 | flag.Parse() 18 | 19 | var header string 20 | 21 | const defaultHeader = "../swisseph/sweph.h" 22 | if _, err := os.Stat(defaultHeader); err == nil { 23 | header = defaultHeader 24 | } 25 | 26 | if header == "" { 27 | if flag.NArg() == 0 { 28 | log.Fatal("Provide path to sweph.h.") 29 | } 30 | 31 | header = flag.Arg(0) 32 | } 33 | 34 | re, err := regexp.Compile(`#define SE_VERSION\s+"(.*)"`) 35 | if err != nil { 36 | log.Fatalln("Error compiling regexp:", err) 37 | } 38 | 39 | data, err := ioutil.ReadFile(header) 40 | if err != nil { 41 | log.Fatalln("Error reading header file:", err) 42 | } 43 | 44 | match := re.FindSubmatch(data) 45 | version := string(match[1]) 46 | 47 | parts := strings.Split(version, ".") 48 | trimmed := make([]string, len(parts)) 49 | for i, p := range parts { 50 | trimmed[i] = strings.TrimPrefix(p, "0") 51 | } 52 | 53 | if len(trimmed) == 2 { 54 | parts = append(parts, "00") 55 | trimmed = append(trimmed, "0") 56 | } 57 | 58 | f, err := os.Create("sweversion.h") 59 | if err != nil { 60 | log.Fatalln("Error creating sweversion.h:", err) 61 | } 62 | 63 | fmt.Fprintln(f, "// DO NOT EDIT - generated by running:") 64 | fmt.Fprint(f, "// \tgo run genversion.go") 65 | 66 | if header == defaultHeader { 67 | fmt.Fprintln(f) 68 | } else { 69 | fmt.Fprintf(f, " %s\n", header) 70 | } 71 | 72 | fmt.Fprintln(f, "//") 73 | fmt.Fprintln(f, "// Re-run this command when the Swiss Ephemeris is updated.") 74 | fmt.Fprintln(f, "//") 75 | fmt.Fprintln(f) 76 | fmt.Fprintf(f, "#define SWEX_VERSION %s\n", strings.Join(parts, "")) 77 | fmt.Fprintf(f, "#define SWEX_VERSION_MAJOR %s\n", trimmed[0]) 78 | fmt.Fprintf(f, "#define SWEX_VERSION_MINOR %s\n", trimmed[1]) 79 | fmt.Fprintf(f, "#define SWEX_VERSION_PATCH %s\n", trimmed[2]) 80 | 81 | f.Close() 82 | } 83 | -------------------------------------------------------------------------------- /swex/sweversion.h: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT - generated by running: 2 | // go run genversion.go 3 | // 4 | // Re-run this command when the Swiss Ephemeris is updated. 5 | // 6 | 7 | #define SWEX_VERSION 20600 8 | #define SWEX_VERSION_MAJOR 2 9 | #define SWEX_VERSION_MINOR 6 10 | #define SWEX_VERSION_PATCH 0 11 | -------------------------------------------------------------------------------- /swex/swex.c: -------------------------------------------------------------------------------- 1 | #include "swex.h" 2 | 3 | #include 4 | #include 5 | #include "sweversion.h" 6 | 7 | bool swex_supports_tls() { 8 | #if defined(TLSOFF) && TLSOFF == 1 9 | return false; 10 | #else 11 | return true; 12 | #endif 13 | } 14 | 15 | void swex_set_jpl_file(const char *fname) { 16 | swex_set_jpl_file_len(fname, strlen(fname)); 17 | } 18 | 19 | void swex_set_jpl_file_len(const char *fname, size_t len) { 20 | if (strncmp(fname, swed.jplfnam, len) != 0) { 21 | swe_set_jpl_file((char *)fname); 22 | } 23 | } 24 | 25 | void swex_set_topo(double geolon, double geolat, double geoalt) { 26 | #if SWEX_VERSION_MAJOR == 2 && SWEX_VERSION_MINOR < 5 27 | if (swed.geopos_is_set == TRUE 28 | && swed.topd.geolon == geolon 29 | && swed.topd.geolat == geolat 30 | && swed.topd.geoalt == geoalt 31 | ) { 32 | return; 33 | } 34 | #endif 35 | 36 | swe_set_topo(geolon, geolat, geoalt); 37 | } 38 | 39 | void swex_set_sid_mode(int32_t sidm, double t0, double ayan_t0) { 40 | if (swed.ayana_is_set == TRUE 41 | && swed.sidd.sid_mode == sidm 42 | && swed.sidd.ayan_t0 == ayan_t0 43 | && swed.sidd.t0 == t0 44 | ) { 45 | return; 46 | } 47 | 48 | swe_set_sid_mode(sidm, t0, ayan_t0); 49 | } 50 | -------------------------------------------------------------------------------- /swex/swex.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | bool swex_supports_tls(); 5 | void swex_set_jpl_file(const char *fname); 6 | void swex_set_jpl_file_len(const char *fname, size_t len); 7 | void swex_set_topo(double geolon, double geolat, double geoalt); 8 | void swex_set_sid_mode(int32_t sidm, double t0, double ayan_t0); 9 | -------------------------------------------------------------------------------- /swisseph/LICENSE: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 1997 - 2008 Astrodienst AG, Switzerland. All rights reserved. 2 | 3 | License conditions 4 | ------------------ 5 | 6 | This file is part of Swiss Ephemeris. 7 | 8 | Swiss Ephemeris is distributed with NO WARRANTY OF ANY KIND. No author 9 | or distributor accepts any responsibility for the consequences of using it, 10 | or for whether it serves any particular purpose or works at all, unless he 11 | or she says so in writing. 12 | 13 | Swiss Ephemeris is made available by its authors under a dual licensing 14 | system. The software developer, who uses any part of Swiss Ephemeris 15 | in his or her software, must choose between one of the two license models, 16 | which are 17 | a) GNU public license version 2 or later 18 | b) Swiss Ephemeris Professional License 19 | 20 | The choice must be made before the software developer distributes software 21 | containing parts of Swiss Ephemeris to others, and before any public 22 | service using the developed software is activated. 23 | 24 | If the developer choses the GNU GPL software license, he or she must fulfill 25 | the conditions of that license, which includes the obligation to place his 26 | or her whole software project under the GNU GPL or a compatible license. 27 | See http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 28 | 29 | If the developer choses the Swiss Ephemeris Professional license, 30 | he must follow the instructions as found in http://www.astro.com/swisseph/ 31 | and purchase the Swiss Ephemeris Professional Edition from Astrodienst 32 | and sign the corresponding license contract. 33 | 34 | The License grants you the right to use, copy, modify and redistribute 35 | Swiss Ephemeris, but only under certain conditions described in the License. 36 | Among other things, the License requires that the copyright notices and 37 | this notice be preserved on all copies. 38 | 39 | Authors of the Swiss Ephemeris: Dieter Koch and Alois Treindl 40 | 41 | The authors of Swiss Ephemeris have no control or influence over any of 42 | the derived works, i.e. over software or services created by other 43 | programmers which use Swiss Ephemeris functions. 44 | 45 | The names of the authors or of the copyright holder (Astrodienst) must not 46 | be used for promoting any software, product or service which uses or contains 47 | the Swiss Ephemeris. This copyright notice is the ONLY place where the 48 | names of the authors can legally appear, except in cases where they have 49 | given special permission in writing. 50 | 51 | The trademarks 'Swiss Ephemeris' and 'Swiss Ephemeris inside' may be used 52 | for promoting such software, products or services. 53 | */ 54 | 55 | -------------------------------------------------------------------------------- /swisseph/Makefile: -------------------------------------------------------------------------------- 1 | # $Header$ 2 | # this Makefile creates a SwissEph library and a swetest sample on 64-bit 3 | # Redhat Enterprise Linux RHEL 6. 4 | 5 | # The mode marked as 'Linux' should also work with the GNU C compiler 6 | # gcc on other systems. 7 | 8 | # If you modify this makefile for another compiler, please 9 | # let us know. We would like to add as many variations as possible. 10 | # If you get warnings and error messages from your compiler, please 11 | # let us know. We like to fix the source code so that it compiles 12 | # free of warnings. 13 | # send email to the Swiss Ephemeris mailing list. 14 | # 15 | 16 | CFLAGS = -g -Wall -fPIC -DTLSOFF # for Linux and other gcc systems 17 | OP=$(CFLAGS) 18 | CC=cc #for Linux 19 | 20 | # compilation rule for general cases 21 | .o : 22 | $(CC) $(OP) -o $@ $? -lm 23 | .c.o: 24 | $(CC) -c $(OP) $< 25 | 26 | SWEOBJ = swedate.o swehouse.o swejpl.o swemmoon.o swemplan.o swepcalc.o sweph.o\ 27 | swepdate.o swephlib.o swecl.o swehel.o 28 | 29 | swetest: swetest.o libswe.a 30 | $(CC) $(OP) -o swetest swetest.o -L. -lswe -lm 31 | 32 | swemini: swemini.o libswe.a 33 | $(CC) $(OP) -o swemini swemini.o -L. -lswe -lm 34 | 35 | # create an archive and a dynamic link libary fro SwissEph 36 | # a user of this library will inlcude swephexp.h and link with -lswe 37 | 38 | libswe.a: $(SWEOBJ) 39 | ar r libswe.a $(SWEOBJ) 40 | 41 | libswe.so: $(SWEOBJ) 42 | $(CC) -shared -o libswe.so $(SWEOBJ) 43 | 44 | clean: 45 | rm -f *.o swetest libswe* 46 | 47 | ### 48 | swecl.o: swejpl.h sweodef.h swephexp.h swedll.h sweph.h swephlib.h 49 | sweclips.o: sweodef.h swephexp.h swedll.h 50 | swedate.o: swephexp.h sweodef.h swedll.h 51 | swehel.o: swephexp.h sweodef.h swedll.h 52 | swehouse.o: swephexp.h sweodef.h swedll.h swephlib.h swehouse.h 53 | swejpl.o: swephexp.h sweodef.h swedll.h sweph.h swejpl.h 54 | swemini.o: swephexp.h sweodef.h swedll.h 55 | swemmoon.o: swephexp.h sweodef.h swedll.h sweph.h swephlib.h 56 | swemplan.o: swephexp.h sweodef.h swedll.h sweph.h swephlib.h swemptab.h 57 | swepcalc.o: swepcalc.h swephexp.h sweodef.h swedll.h 58 | sweph.o: swejpl.h sweodef.h swephexp.h swedll.h sweph.h swephlib.h 59 | swephlib.o: swephexp.h sweodef.h swedll.h sweph.h swephlib.h 60 | swetest.o: swephexp.h sweodef.h swedll.h 61 | -------------------------------------------------------------------------------- /swisseph/swedate.h: -------------------------------------------------------------------------------- 1 | /********************************************************* 2 | $Header: /home/dieter/sweph/RCS/swedate.h,v 1.74 2008/06/16 10:07:20 dieter Exp $ 3 | version 15-feb-89 16:30 4 | *********************************************************/ 5 | 6 | /* Copyright (C) 1997 - 2008 Astrodienst AG, Switzerland. All rights reserved. 7 | 8 | License conditions 9 | ------------------ 10 | 11 | This file is part of Swiss Ephemeris. 12 | 13 | Swiss Ephemeris is distributed with NO WARRANTY OF ANY KIND. No author 14 | or distributor accepts any responsibility for the consequences of using it, 15 | or for whether it serves any particular purpose or works at all, unless he 16 | or she says so in writing. 17 | 18 | Swiss Ephemeris is made available by its authors under a dual licensing 19 | system. The software developer, who uses any part of Swiss Ephemeris 20 | in his or her software, must choose between one of the two license models, 21 | which are 22 | a) GNU public license version 2 or later 23 | b) Swiss Ephemeris Professional License 24 | 25 | The choice must be made before the software developer distributes software 26 | containing parts of Swiss Ephemeris to others, and before any public 27 | service using the developed software is activated. 28 | 29 | If the developer choses the GNU GPL software license, he or she must fulfill 30 | the conditions of that license, which includes the obligation to place his 31 | or her whole software project under the GNU GPL or a compatible license. 32 | See http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 33 | 34 | If the developer choses the Swiss Ephemeris Professional license, 35 | he must follow the instructions as found in http://www.astro.com/swisseph/ 36 | and purchase the Swiss Ephemeris Professional Edition from Astrodienst 37 | and sign the corresponding license contract. 38 | 39 | The License grants you the right to use, copy, modify and redistribute 40 | Swiss Ephemeris, but only under certain conditions described in the License. 41 | Among other things, the License requires that the copyright notices and 42 | this notice be preserved on all copies. 43 | 44 | Authors of the Swiss Ephemeris: Dieter Koch and Alois Treindl 45 | 46 | The authors of Swiss Ephemeris have no control or influence over any of 47 | the derived works, i.e. over software or services created by other 48 | programmers which use Swiss Ephemeris functions. 49 | 50 | The names of the authors or of the copyright holder (Astrodienst) must not 51 | be used for promoting any software, product or service which uses or contains 52 | the Swiss Ephemeris. This copyright notice is the ONLY place where the 53 | names of the authors can legally appear, except in cases where they have 54 | given special permission in writing. 55 | 56 | The trademarks 'Swiss Ephemeris' and 'Swiss Ephemeris inside' may be used 57 | for promoting such software, products or services. 58 | */ 59 | 60 | #ifdef __cplusplus 61 | extern "C" { 62 | #endif 63 | 64 | #ifndef _SWEDLL_H 65 | extern EXP32 int swe_date_conversion ( 66 | int y , int m , int d , /* year, month, day */ 67 | double utime, /* universal time in hours (decimal) */ 68 | char c, /* calendar g[regorian]|j[ulian]|a[stro = greg] */ 69 | double *tgmt); 70 | 71 | extern EXP32 double *swe_julday( 72 | int year, int month, int day, double hour, 73 | int gregflag); 74 | 75 | extern EXP32 void swe_revjul ( 76 | double jd, 77 | int gregflag, 78 | int *jyear, int *jmon, int *jday, double *jut); 79 | #endif 80 | #ifdef __cplusplus 81 | } /* extern C */ 82 | #endif 83 | -------------------------------------------------------------------------------- /swisseph/swehouse.h: -------------------------------------------------------------------------------- 1 | /******************************************************* 2 | $Header: /home/dieter/sweph/RCS/swehouse.h,v 1.74 2008/06/16 10:07:20 dieter Exp $ 3 | module swehouse.h 4 | house and (simple) aspect calculation 5 | 6 | *******************************************************/ 7 | 8 | /* Copyright (C) 1997 - 2008 Astrodienst AG, Switzerland. All rights reserved. 9 | 10 | License conditions 11 | ------------------ 12 | 13 | This file is part of Swiss Ephemeris. 14 | 15 | Swiss Ephemeris is distributed with NO WARRANTY OF ANY KIND. No author 16 | or distributor accepts any responsibility for the consequences of using it, 17 | or for whether it serves any particular purpose or works at all, unless he 18 | or she says so in writing. 19 | 20 | Swiss Ephemeris is made available by its authors under a dual licensing 21 | system. The software developer, who uses any part of Swiss Ephemeris 22 | in his or her software, must choose between one of the two license models, 23 | which are 24 | a) GNU public license version 2 or later 25 | b) Swiss Ephemeris Professional License 26 | 27 | The choice must be made before the software developer distributes software 28 | containing parts of Swiss Ephemeris to others, and before any public 29 | service using the developed software is activated. 30 | 31 | If the developer choses the GNU GPL software license, he or she must fulfill 32 | the conditions of that license, which includes the obligation to place his 33 | or her whole software project under the GNU GPL or a compatible license. 34 | See http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 35 | 36 | If the developer choses the Swiss Ephemeris Professional license, 37 | he must follow the instructions as found in http://www.astro.com/swisseph/ 38 | and purchase the Swiss Ephemeris Professional Edition from Astrodienst 39 | and sign the corresponding license contract. 40 | 41 | The License grants you the right to use, copy, modify and redistribute 42 | Swiss Ephemeris, but only under certain conditions described in the License. 43 | Among other things, the License requires that the copyright notices and 44 | this notice be preserved on all copies. 45 | 46 | Authors of the Swiss Ephemeris: Dieter Koch and Alois Treindl 47 | 48 | The authors of Swiss Ephemeris have no control or influence over any of 49 | the derived works, i.e. over software or services created by other 50 | programmers which use Swiss Ephemeris functions. 51 | 52 | The names of the authors or of the copyright holder (Astrodienst) must not 53 | be used for promoting any software, product or service which uses or contains 54 | the Swiss Ephemeris. This copyright notice is the ONLY place where the 55 | names of the authors can legally appear, except in cases where they have 56 | given special permission in writing. 57 | 58 | The trademarks 'Swiss Ephemeris' and 'Swiss Ephemeris inside' may be used 59 | for promoting such software, products or services. 60 | */ 61 | 62 | struct houses { 63 | double cusp[37]; 64 | double ac; 65 | double mc; 66 | double vertex; 67 | double equasc; 68 | double coasc1; 69 | double coasc2; 70 | double polasc; 71 | double sundec; // declination of Sun for Sunshine houses 72 | char serr[AS_MAXCH]; 73 | }; 74 | 75 | #define HOUSES struct houses 76 | #define VERY_SMALL 1E-10 77 | 78 | #define degtocs(x) (d2l((x) * DEG)) 79 | #define cstodeg(x) (double)((x) * CS2DEG) 80 | 81 | #define sind(x) sin((x) * DEGTORAD) 82 | #define cosd(x) cos((x) * DEGTORAD) 83 | #define tand(x) tan((x) * DEGTORAD) 84 | #define asind(x) (asin(x) * RADTODEG) 85 | #define acosd(x) (acos(x) * RADTODEG) 86 | #define atand(x) (atan(x) * RADTODEG) 87 | #define atan2d(y, x) (atan2(y, x) * RADTODEG) 88 | -------------------------------------------------------------------------------- /swisseph/swejpl.h: -------------------------------------------------------------------------------- 1 | /* 2 | | $Header: /home/dieter/sweph/RCS/swejpl.h,v 1.74 2008/06/16 10:07:20 dieter Exp $ 3 | | 4 | | Subroutines for reading JPL ephemerides. 5 | | derived from testeph.f as contained in DE403 distribution July 1995. 6 | | works with DE200, DE102, DE403, DE404, DE405, DE406, DE431 7 | | (attention, these ephemerides do not have exactly the same reference frame) 8 | 9 | Authors: Dieter Koch and Alois Treindl, Astrodienst Zurich 10 | 11 | **************************************************************/ 12 | /* Copyright (C) 1997 - 2008 Astrodienst AG, Switzerland. All rights reserved. 13 | 14 | License conditions 15 | ------------------ 16 | 17 | This file is part of Swiss Ephemeris. 18 | 19 | Swiss Ephemeris is distributed with NO WARRANTY OF ANY KIND. No author 20 | or distributor accepts any responsibility for the consequences of using it, 21 | or for whether it serves any particular purpose or works at all, unless he 22 | or she says so in writing. 23 | 24 | Swiss Ephemeris is made available by its authors under a dual licensing 25 | system. The software developer, who uses any part of Swiss Ephemeris 26 | in his or her software, must choose between one of the two license models, 27 | which are 28 | a) GNU public license version 2 or later 29 | b) Swiss Ephemeris Professional License 30 | 31 | The choice must be made before the software developer distributes software 32 | containing parts of Swiss Ephemeris to others, and before any public 33 | service using the developed software is activated. 34 | 35 | If the developer choses the GNU GPL software license, he or she must fulfill 36 | the conditions of that license, which includes the obligation to place his 37 | or her whole software project under the GNU GPL or a compatible license. 38 | See http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 39 | 40 | If the developer choses the Swiss Ephemeris Professional license, 41 | he must follow the instructions as found in http://www.astro.com/swisseph/ 42 | and purchase the Swiss Ephemeris Professional Edition from Astrodienst 43 | and sign the corresponding license contract. 44 | 45 | The License grants you the right to use, copy, modify and redistribute 46 | Swiss Ephemeris, but only under certain conditions described in the License. 47 | Among other things, the License requires that the copyright notices and 48 | this notice be preserved on all copies. 49 | 50 | Authors of the Swiss Ephemeris: Dieter Koch and Alois Treindl 51 | 52 | The authors of Swiss Ephemeris have no control or influence over any of 53 | the derived works, i.e. over software or services created by other 54 | programmers which use Swiss Ephemeris functions. 55 | 56 | The names of the authors or of the copyright holder (Astrodienst) must not 57 | be used for promoting any software, product or service which uses or contains 58 | the Swiss Ephemeris. This copyright notice is the ONLY place where the 59 | names of the authors can legally appear, except in cases where they have 60 | given special permission in writing. 61 | 62 | The trademarks 'Swiss Ephemeris' and 'Swiss Ephemeris inside' may be used 63 | for promoting such software, products or services. 64 | */ 65 | 66 | 67 | #include "sweodef.h" 68 | 69 | #define J_MERCURY 0 /* jpl body indices, modified by Alois */ 70 | #define J_VENUS 1 /* now they start at 0 and not at 1 */ 71 | #define J_EARTH 2 72 | #define J_MARS 3 73 | #define J_JUPITER 4 74 | #define J_SATURN 5 75 | #define J_URANUS 6 76 | #define J_NEPTUNE 7 77 | #define J_PLUTO 8 78 | #define J_MOON 9 79 | #define J_SUN 10 80 | #define J_SBARY 11 81 | #define J_EMB 12 82 | #define J_NUT 13 83 | #define J_LIB 14 84 | 85 | /* 86 | * compute position and speed at time et, for body ntarg with center 87 | * ncent. rrd must be double[6] to contain the return vectors. 88 | * ntarg can be all of the above, ncent all except J_NUT and J_LIB. 89 | * Librations and Nutations are not affected by ncent. 90 | */ 91 | extern int swi_pleph(double et, int ntarg, int ncent, double *rrd, char *serr); 92 | 93 | /* 94 | * read the ephemeris constants. ss[0..2] returns start, end and granule size. 95 | * If do_show is TRUE, a list of constants is printed to stdout. 96 | */ 97 | extern void swi_close_jpl_file(void); 98 | 99 | extern int swi_open_jpl_file(double *ss, char *fname, char *fpath, char *serr); 100 | 101 | extern int32 swi_get_jpl_denum(void); 102 | 103 | extern void swi_IERS_FK5(double *xin, double *xout, int dir); 104 | 105 | -------------------------------------------------------------------------------- /swisseph/sweodef.h: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | $Header: /home/dieter/sweph/RCS/sweodef.h,v 1.74 2008/06/16 10:07:20 dieter Exp $ 3 | definitions and constants for all Swiss Ephemeris source files, 4 | only required for compiling the libraries, not for the external 5 | interface of the libraries. 6 | 7 | The definitions are a subset of Astrodienst's ourdef.h content 8 | and must be kept compatible. Everything not used in SwissEph 9 | has been deleted. 10 | 11 | Does auto-detection of MSDOS (TURBO_C or MS_C), HPUNIX, Linux. 12 | Must be extended for more portability; there should be a way 13 | to detect byte order and file system type. 14 | 15 | ************************************************************/ 16 | 17 | /* Copyright (C) 1997 - 2008 Astrodienst AG, Switzerland. All rights reserved. 18 | 19 | License conditions 20 | ------------------ 21 | 22 | This file is part of Swiss Ephemeris. 23 | 24 | Swiss Ephemeris is distributed with NO WARRANTY OF ANY KIND. No author 25 | or distributor accepts any responsibility for the consequences of using it, 26 | or for whether it serves any particular purpose or works at all, unless he 27 | or she says so in writing. 28 | 29 | Swiss Ephemeris is made available by its authors under a dual licensing 30 | system. The software developer, who uses any part of Swiss Ephemeris 31 | in his or her software, must choose between one of the two license models, 32 | which are 33 | a) GNU public license version 2 or later 34 | b) Swiss Ephemeris Professional License 35 | 36 | The choice must be made before the software developer distributes software 37 | containing parts of Swiss Ephemeris to others, and before any public 38 | service using the developed software is activated. 39 | 40 | If the developer choses the GNU GPL software license, he or she must fulfill 41 | the conditions of that license, which includes the obligation to place his 42 | or her whole software project under the GNU GPL or a compatible license. 43 | See http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 44 | 45 | If the developer choses the Swiss Ephemeris Professional license, 46 | he must follow the instructions as found in http://www.astro.com/swisseph/ 47 | and purchase the Swiss Ephemeris Professional Edition from Astrodienst 48 | and sign the corresponding license contract. 49 | 50 | The License grants you the right to use, copy, modify and redistribute 51 | Swiss Ephemeris, but only under certain conditions described in the License. 52 | Among other things, the License requires that the copyright notices and 53 | this notice be preserved on all copies. 54 | 55 | Authors of the Swiss Ephemeris: Dieter Koch and Alois Treindl 56 | 57 | The authors of Swiss Ephemeris have no control or influence over any of 58 | the derived works, i.e. over software or services created by other 59 | programmers which use Swiss Ephemeris functions. 60 | 61 | The names of the authors or of the copyright holder (Astrodienst) must not 62 | be used for promoting any software, product or service which uses or contains 63 | the Swiss Ephemeris. This copyright notice is the ONLY place where the 64 | names of the authors can legally appear, except in cases where they have 65 | given special permission in writing. 66 | 67 | The trademarks 'Swiss Ephemeris' and 'Swiss Ephemeris inside' may be used 68 | for promoting such software, products or services. 69 | */ 70 | 71 | #ifndef _OURDEF_INCLUDED /* ourdef.h is a superset of sweodef.h */ 72 | #ifndef _SWEODEF_INCLUDED /* allow multiple #includes */ 73 | #define _SWEODEF_INCLUDED 74 | 75 | # define MY_TRUE 1 /* for use in other defines, before TRUE is defined */ 76 | # define MY_FALSE 0 /* for use in other defines, before TRUE is defined */ 77 | 78 | /* TLS support 79 | * 80 | * Sun Studio C/C++, IBM XL C/C++, GNU C and Intel C/C++ (Linux systems) -> __thread 81 | * Borland, VC++ -> __declspec(thread) 82 | */ 83 | #if !defined(TLSOFF) && !defined( __APPLE__ ) && !defined(WIN32) && !defined(DOS32) 84 | #if defined( __GNUC__ ) 85 | #define TLS __thread 86 | #else 87 | #define TLS __declspec(thread) 88 | #endif 89 | #else 90 | #define TLS 91 | #endif 92 | 93 | #ifdef _WIN32 /* Microsoft VC 5.0 does not define MSDOS anymore */ 94 | # undef MSDOS 95 | # define MSDOS MY_TRUE 96 | #include 97 | #include 98 | #include 99 | #include 100 | #include 101 | #include 102 | # define sleep(x) Sleep((x) * 1000) 103 | #endif 104 | 105 | #ifdef _MSC_VER 106 | # define MS_VC 107 | #endif 108 | 109 | #ifdef WIN32 /* Microsoft VC 5.0 does not define MSDOS anymore */ 110 | # define MSDOS MY_TRUE 111 | #endif 112 | 113 | #ifdef MSDOS /* already defined by some DOS compilers */ 114 | # undef MSDOS 115 | # define MSDOS MY_TRUE 116 | #endif 117 | 118 | #ifdef __TURBOC__ /* defined by turboc */ 119 | # ifndef MSDOS 120 | # define MSDOS MY_TRUE 121 | # endif 122 | # define TURBO_C 123 | #endif 124 | 125 | #ifdef __SC__ /* defined by Symantec C */ 126 | # ifndef MSDOS 127 | # define MSDOS MY_TRUE 128 | # endif 129 | # define SYMANTEC_C 130 | #endif 131 | 132 | #ifdef __WATCOMC__ /* defined by WatcomC */ 133 | # ifndef MSDOS 134 | # define MSDOS MY_TRUE 135 | # endif 136 | # define WATCOMC 137 | #endif 138 | 139 | #ifdef __MWERKS__ /* defined on Macintosh CodeWarrior */ 140 | # if macintosh && powerc 141 | # define MACOS MY_TRUE /* let it undefined otherwise */ 142 | # define MSDOS MY_FALSE /* in case one above fired falsely */ 143 | # endif 144 | #endif 145 | 146 | #ifdef MSDOS 147 | # define HPUNIX MY_FALSE 148 | # define INTEL_BYTE_ORDER 1 149 | # ifndef TURBO_C 150 | # define MS_C /* assume Microsoft C compiler */ 151 | # endif 152 | # define UNIX_FS MY_FALSE 153 | #else 154 | # ifdef MACOS 155 | # define HPUNIX MY_FALSE 156 | # define UNIX_FS MY_FALSE 157 | # else 158 | # define MSDOS MY_FALSE 159 | # define HPUNIX MY_TRUE 160 | # ifndef _HPUX_SOURCE 161 | # define _HPUX_SOURCE 162 | # endif 163 | # define UNIX_FS MY_TRUE 164 | # endif 165 | #endif 166 | 167 | #include 168 | #include 169 | #ifndef FILE 170 | # include 171 | #endif 172 | 173 | #if HPUNIX 174 | # include 175 | #endif 176 | 177 | /* 178 | * if we have 16-bit ints, we define INT_16; we will need %ld to printf an int32 179 | * if we have 64-bit long, we define LONG_64 180 | * If none is defined, we have int = long = 32 bit, and use %d to printf an int32 181 | */ 182 | #include 183 | #if INT_MAX < 40000 184 | # define INT_16 185 | #else 186 | # if LONG_MAX > INT_MAX 187 | # define LONG_64 188 | # endif 189 | #endif 190 | 191 | #ifdef BYTE_ORDER 192 | #ifdef LITTLE_ENDIAN 193 | # if BYTE_ORDER == LITTLE_ENDIAN 194 | # define INTEL_BYTE_ORDER 195 | # endif 196 | #endif 197 | #endif 198 | 199 | #ifdef INT_16 200 | typedef long int32; 201 | typedef unsigned long uint32; 202 | typedef int int16; 203 | typedef double REAL8; /* real with at least 64 bit precision */ 204 | typedef long INT4; /* signed integer with at least 32 bit precision */ 205 | typedef unsigned long UINT4; 206 | /* unsigned integer with at least 32 bit precision */ 207 | typedef int AS_BOOL; 208 | typedef unsigned int UINT2; /* unsigned 16 bits */ 209 | # define ABS4 labs /* abs function for long */ 210 | #else 211 | typedef int int32; 212 | typedef long long int64; 213 | typedef unsigned int uint32; 214 | typedef short int16; 215 | typedef double REAL8; /* real with at least 64 bit precision */ 216 | typedef int INT4; /* signed integer with at least 32 bit precision */ 217 | typedef unsigned int UINT4; 218 | /* unsigned integer with at least 32 bit precision */ 219 | typedef int AS_BOOL; 220 | typedef unsigned short UINT2; /* unsigned 16 bits */ 221 | # define ABS4 abs /* abs function for long */ 222 | #endif 223 | 224 | #if MSDOS 225 | # ifdef TURBO_C 226 | # include /* MSC needs malloc ! */ 227 | # else 228 | # include 229 | # endif 230 | # define SIGALRM SIGINT 231 | #endif 232 | 233 | #ifndef TRUE 234 | # define TRUE 1 235 | # define FALSE 0 236 | #endif 237 | 238 | #ifndef OK 239 | # define OK (0) 240 | # define ERR (-1) 241 | #endif 242 | 243 | /* hack because UCHAR is already used by mingw gcc */ 244 | #ifdef __GNUC__ 245 | #ifdef _WIN32 246 | #define UCHAR SWE_UCHAR 247 | #endif 248 | #endif 249 | 250 | typedef unsigned char UCHAR; 251 | #define UCP (UCHAR*) 252 | #define SCP (char*) 253 | 254 | # define ODEGREE_STRING "°" /* degree as string, utf8 encoding */ 255 | 256 | 257 | 258 | #ifndef HUGE 259 | # define HUGE 1.7E+308 /* biggest value for REAL8 */ 260 | #endif 261 | #ifndef M_PI 262 | # define M_PI 3.14159265358979323846 263 | #endif 264 | 265 | /* #define forward static obsolete */ 266 | 267 | #define AS_MAXCH 256 /* used for string declarations, allowing 255 char+\0 */ 268 | 269 | /* 270 | #define DEGTORAD 0.0174532925199433 271 | #define RADTODEG 57.2957795130823 272 | */ 273 | #define RADTODEG (180.0 / M_PI) 274 | #define DEGTORAD (M_PI / 180.0) 275 | 276 | typedef int32 centisec; /* centiseconds used for angles and times */ 277 | #define CS (centisec) /* use for casting */ 278 | #define CSEC centisec /* use for typing */ 279 | 280 | #define DEG 360000 /* degree expressed in centiseconds */ 281 | #define DEG7_30 (2700000) /* 7.5 degrees */ 282 | #define DEG15 (15 * DEG) 283 | #define DEG24 (24 * DEG) 284 | #define DEG30 (30 * DEG) 285 | #define DEG60 (60 * DEG) 286 | #define DEG90 (90 * DEG) 287 | #define DEG120 (120 * DEG) 288 | #define DEG150 (150 * DEG) 289 | #define DEG180 (180 * DEG) 290 | #define DEG270 (270 * DEG) 291 | #define DEG360 (360 * DEG) 292 | 293 | /* #define CSTORAD 4.84813681109536E-08 centisec to rad: pi / 180 /3600/100 */ 294 | /* #define RADTOCS 2.06264806247096E+07 rad to centisec 180*3600*100/pi */ 295 | #define CSTORAD (DEGTORAD / 360000.0) 296 | #define RADTOCS (RADTODEG * 360000.0) 297 | 298 | #define CS2DEG (1.0/360000.0) /* centisec to degree */ 299 | 300 | /* control strings for fopen() */ 301 | #if UNIX_FS 302 | # define BFILE_R_ACCESS "r" /* open binary file for reading */ 303 | # define BFILE_RW_ACCESS "r+" /* open binary file for writing and reading */ 304 | # define BFILE_W_CREATE "w" /* create/open binary file for write*/ 305 | # define BFILE_A_ACCESS "a+" /* create/open binary file for append*/ 306 | # define FILE_R_ACCESS "r" /* open text file for reading */ 307 | # define FILE_RW_ACCESS "r+" /* open text file for writing and reading */ 308 | # define FILE_W_CREATE "w" /* create/open text file for write*/ 309 | # define FILE_A_ACCESS "a+" /* create/open text file for append*/ 310 | # define O_BINARY 0 /* for open(), not defined in Unix */ 311 | # define OPEN_MODE 0666 /* default file creation mode */ 312 | # define DIR_GLUE "/" /* glue string for directory/file */ 313 | # define PATH_SEPARATOR ";:" /* semicolon or colon may be used */ 314 | #else 315 | # define BFILE_R_ACCESS "rb" /* open binary file for reading */ 316 | # define BFILE_RW_ACCESS "r+b" /* open binary file for writing and reading */ 317 | # define BFILE_W_CREATE "wb" /* create/open binary file for write*/ 318 | # define BFILE_A_ACCESS "a+b" /* create/open binary file for append*/ 319 | # define PATH_SEPARATOR ";" /* semicolon as PATH separator */ 320 | # define OPEN_MODE 0666 /* default file creation mode */ 321 | # ifdef MACOS 322 | # define FILE_R_ACCESS "r" /* open text file for reading */ 323 | # define FILE_RW_ACCESS "r+" /* open text file for writing and reading */ 324 | # define FILE_W_CREATE "w" /* create/open text file for write*/ 325 | # define FILE_A_ACCESS "a+" /* create/open text file for append*/ 326 | # define DIR_GLUE ":" /* glue string for directory/file */ 327 | # else 328 | # define FILE_R_ACCESS "rt" /* open text file for reading */ 329 | # define FILE_RW_ACCESS "r+t" /* open text file for writing and reading */ 330 | # define FILE_W_CREATE "wt" /* create/open text file for write*/ 331 | # define FILE_A_ACCESS "a+t" /* create/open text file for append*/ 332 | /* attention, all backslashes for msdos directry names must be written as \\, 333 | because it is the C escape character */ 334 | # define DIR_GLUE "\\" /* glue string for directory/file */ 335 | # endif 336 | #endif 337 | 338 | #include 339 | #include 340 | 341 | #endif /* _SWEODEF_INCLUDED */ 342 | #endif /* _OURDEF_INCLUDED */ 343 | -------------------------------------------------------------------------------- /swisseph/swepdate.c: -------------------------------------------------------------------------------- 1 | /***************************************************** 2 | $Header: swepdate.c,v 1.65 2003/06/14 13:02:01 alois Exp $ 3 | Placalc compatibility interface for Swiss Ephemeris. 4 | date functions 5 | 6 | *******************************************************/ 7 | /* Copyright (C) 1997 - 2008 Astrodienst AG, Switzerland. All rights reserved. 8 | 9 | License conditions 10 | ------------------ 11 | 12 | This file is part of Swiss Ephemeris. 13 | 14 | Swiss Ephemeris is distributed with NO WARRANTY OF ANY KIND. No author 15 | or distributor accepts any responsibility for the consequences of using it, 16 | or for whether it serves any particular purpose or works at all, unless he 17 | or she says so in writing. 18 | 19 | Swiss Ephemeris is made available by its authors under a dual licensing 20 | system. The software developer, who uses any part of Swiss Ephemeris 21 | in his or her software, must choose between one of the two license models, 22 | which are 23 | a) GNU public license version 2 or later 24 | b) Swiss Ephemeris Professional License 25 | 26 | The choice must be made before the software developer distributes software 27 | containing parts of Swiss Ephemeris to others, and before any public 28 | service using the developed software is activated. 29 | 30 | If the developer choses the GNU GPL software license, he or she must fulfill 31 | the conditions of that license, which includes the obligation to place his 32 | or her whole software project under the GNU GPL or a compatible license. 33 | See http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 34 | 35 | If the developer choses the Swiss Ephemeris Professional license, 36 | he must follow the instructions as found in http://www.astro.com/swisseph/ 37 | and purchase the Swiss Ephemeris Professional Edition from Astrodienst 38 | and sign the corresponding license contract. 39 | 40 | The License grants you the right to use, copy, modify and redistribute 41 | Swiss Ephemeris, but only under certain conditions described in the License. 42 | Among other things, the License requires that the copyright notices and 43 | this notice be preserved on all copies. 44 | 45 | Authors of the Swiss Ephemeris: Dieter Koch and Alois Treindl 46 | 47 | The authors of Swiss Ephemeris have no control or influence over any of 48 | the derived works, i.e. over software or services created by other 49 | programmers which use Swiss Ephemeris functions. 50 | 51 | The names of the authors or of the copyright holder (Astrodienst) must not 52 | be used for promoting any software, product or service which uses or contains 53 | the Swiss Ephemeris. This copyright notice is the ONLY place where the 54 | names of the authors can legally appear, except in cases where they have 55 | given special permission in writing. 56 | 57 | The trademarks 'Swiss Ephemeris' and 'Swiss Ephemeris inside' may be used 58 | for promoting such software, products or services. 59 | */ 60 | 61 | /* 62 | * This file is part of the PLACALC compatibility interface for Swiss Ephemeris. 63 | * It allows very easy porting of older Placalc application to the SwissEph. 64 | * A user has to replace #include "placalc.h" and "housasp.h" with 65 | * #include "swepcalc.h" 66 | * If he has used "ourdef.h" he replaces it with "sweodef.h". 67 | * Then he links his application with swepcalc.o and runs it against the 68 | * Swiss Ephemeris DLL or linkable library. 69 | * 70 | * All calls which were present in the placalc sources are contained 71 | * here, and either implemented directly or translated into Swiss Ephemeris 72 | * calls. 73 | * 74 | * 75 | */ 76 | 77 | #include "swepcalc.h" 78 | #include "swephexp.h" 79 | 80 | /*************** julday ******************************************** 81 | * This function returns the absolute Julian day number (JD) 82 | * for a given calendar date. 83 | * The arguments are a calendar date: day, month, year as integers, 84 | * hour as double with decimal fraction. 85 | * If gregflag = 1, Gregorian calendar is assumed, gregflag = 0 86 | * Julian calendar is assumed. 87 | * 88 | The Julian day number is system of numbering all days continously 89 | within the time range of known human history. It should be familiar 90 | for every astrological or astronomical programmer. The time variable 91 | in astronomical theories is usually expressed in Julian days or 92 | Julian centuries (36525 days per century) relative to some start day; 93 | the start day is called 'the epoch'. 94 | The Julian day number is a double representing the number of 95 | days since JD = 0.0 on 1 Jan -4712, 12:00 noon. 96 | 97 | Midnight has always a JD with fraction .5, because traditionally 98 | the astronomical day started at noon. This was practical because 99 | then there was no change of date during a night at the telescope. 100 | From this comes also the fact the noon ephemerides were printed 101 | before midnight ephemerides were introduced early in the 20th century. 102 | 103 | NOTE: The Julian day number is named after the monk Julianus. It must 104 | not be confused with the Julian calendar system, which is named after 105 | Julius Cesar, the Roman politician who introduced this calendar. 106 | The Julian century is named after Cesar, i.e. a century in the Julian 107 | calendar. The 'gregorian' century has a variable length. 108 | 109 | Be aware the we always use astronomical year numbering for the years 110 | before Christ, not the historical year numbering. 111 | Astronomical years are done with negative numbers, historical 112 | years with indicators BC or BCE (before common era). 113 | Year 0 (astronomical) = 1 BC 114 | year -1 (astronomical) = 2 BC 115 | etc. 116 | 117 | Original author: Marc Pottenger, Los Angeles. 118 | with bug fix for year < -4711 15-aug-88 by Alois Treindl 119 | (The parameter sequence m,d,y still indicates the US origin, 120 | be careful because the similar function date_conversion() uses 121 | other parameter sequence and also Astrodienst relative juldate.) 122 | 123 | References: Oliver Montenbruck, Grundlagen der Ephemeridenrechnung, 124 | Verlag Sterne und Weltraum (1987), p.49 ff 125 | 126 | related functions: revjul() reverse Julian day number: compute the 127 | calendar date from a given JD 128 | date_conversion() includes test for legal date values 129 | and notifies errors like 32 January. 130 | ****************************************************************/ 131 | 132 | double julday(int month, int day, int year, double hour, int gregflag) 133 | { 134 | double jd; 135 | jd = swe_julday(year, month, day, hour, gregflag); 136 | return jd; 137 | } 138 | 139 | /* 140 | * monday = 0, ... sunday = 6 141 | */ 142 | int day_of_week(double jd) 143 | { 144 | return (((int) floor (jd - 2433282 - 1.5) %7) + 7) % 7; 145 | } 146 | 147 | /*************** julday ****************** 148 | get absolute julian day number (author: Marc Pottenger) 149 | with bug fix for year < -4711 15-aug-88 150 | */ 151 | double juldays(int gregflag, ADATE *adp ) 152 | { 153 | return swe_julday(adp->year, adp->month, adp->day, adp->csec / 360000.0, gregflag); 154 | } 155 | 156 | /*** revjul ****************************************************** 157 | revjul() is the inverse function to julday(), see the description 158 | there. 159 | Arguments are julian day number, calendar flag (0=julian, 1=gregorian) 160 | return values are the calendar day, month, year and the hour of 161 | the day with decimal fraction (0 .. 23.999999). 162 | 163 | Be aware the we use astronomical year numbering for the years 164 | before Christ, not the historical year numbering. 165 | Astronomical years are done with negative numbers, historical 166 | years with indicators BC or BCE (before common era). 167 | Year 0 (astronomical) = 1 BC historical year 168 | year -1 (astronomical) = 2 BC historical year 169 | year -234 (astronomical) = 235 BC historical year 170 | etc. 171 | 172 | Original author Mark Pottenger, Los Angeles. 173 | with bug fix for year < -4711 16-aug-88 Alois Treindl 174 | *************************************************************************/ 175 | void revjul (double jd, int gregflag, 176 | int *jmon, int *jday, int *jyear, double *jut) 177 | { 178 | swe_revjul(jd, gregflag, jyear, jmon, jday, jut); 179 | } 180 | 181 | /************************************* revjul ********* 182 | get calendar date from julian day # 183 | with bug fix for year < -4711 16-aug-88 184 | arguments are julian day #, calendar flag (0=julian, 1=gregorian) 185 | */ 186 | void revjuls(double jd, int gregflag, struct adate *adp) 187 | { 188 | double jut; 189 | swe_revjul(jd, gregflag, &adp->year, &adp->month, &adp->day, &jut); 190 | adp->csec = jut * 360000.0 + 0.5; 191 | } 192 | 193 | /********************************************************* 194 | $Header: swepdate.c,v 1.65 2003/06/14 13:02:01 alois Exp $ 195 | version 15-feb-89 16:30 196 | 197 | This function converts some date+time input {d,m,y,utime} 198 | into the Julian day number tgmt, which is an Astrodienst relative 199 | Julian date. 200 | The function checks that the input is a legal combination 201 | of dates; for illegal dates like 32 January 1993 it returns ERR 202 | but still converts the date correctly, i.e. like 1 Feb 1993. 203 | The function is usually used to convert user input of birth data 204 | into the Julian day number. Illegal dates should be notified to the user. 205 | 206 | Be aware the we always use astronomical year numbering for the years 207 | before Christ, not the historical year numbering. 208 | Astronomical years are done with negative numbers, historical 209 | years with indicators BC or BCE (before common era). 210 | Year 0 (astronomical) = 1 BC historical. 211 | year -1 (astronomical) = 2 BC 212 | etc. 213 | Many users of Astro programs do not know about this difference. 214 | 215 | Return: OK or ERR (for illegal date) 216 | *********************************************************/ 217 | 218 | int date_conversion (int d , 219 | int m , 220 | int y , /* day, month, year */ 221 | centisec gutime, /* greenwich time in centiseconds */ 222 | char c, /* calendar g[regorian]|j[ulian]|a[stro = greg] */ 223 | double *tgmt 224 | /* julian date relative 0.Jan.1950 12:00 gmt */ 225 | /* shift is 2433282 from absolute Julian date */ 226 | ) 227 | { 228 | int rday, rmon, ryear; 229 | double rut, jd; 230 | int gregflag = SE_JUL_CAL; 231 | if (c == 'g' || c == 'a') 232 | gregflag = SE_GREG_CAL; 233 | rut = gutime / 360000.0; /* hours GMT */ 234 | jd = julday(m, d, y, rut, gregflag); 235 | revjul(jd, gregflag, &rmon, &rday, &ryear, &rut); 236 | *tgmt = jd - JUL_OFFSET; 237 | if (rmon == m && rday == d && ryear == y) { 238 | return OK; 239 | } else { 240 | return ERR; 241 | } 242 | } /* end date_conversion */ 243 | -------------------------------------------------------------------------------- /swisseph/swephlib.h: -------------------------------------------------------------------------------- 1 | 2 | /************************************************************ 3 | $Header: /home/dieter/sweph/RCS/swephlib.h,v 1.74 2008/06/16 10:07:20 dieter Exp $ 4 | 5 | Authors: Dieter Koch and Alois Treindl, Astrodienst Zurich 6 | 7 | ************************************************************/ 8 | /* Copyright (C) 1997 - 2008 Astrodienst AG, Switzerland. All rights reserved. 9 | 10 | License conditions 11 | ------------------ 12 | 13 | This file is part of Swiss Ephemeris. 14 | 15 | Swiss Ephemeris is distributed with NO WARRANTY OF ANY KIND. No author 16 | or distributor accepts any responsibility for the consequences of using it, 17 | or for whether it serves any particular purpose or works at all, unless he 18 | or she says so in writing. 19 | 20 | Swiss Ephemeris is made available by its authors under a dual licensing 21 | system. The software developer, who uses any part of Swiss Ephemeris 22 | in his or her software, must choose between one of the two license models, 23 | which are 24 | a) GNU public license version 2 or later 25 | b) Swiss Ephemeris Professional License 26 | 27 | The choice must be made before the software developer distributes software 28 | containing parts of Swiss Ephemeris to others, and before any public 29 | service using the developed software is activated. 30 | 31 | If the developer choses the GNU GPL software license, he or she must fulfill 32 | the conditions of that license, which includes the obligation to place his 33 | or her whole software project under the GNU GPL or a compatible license. 34 | See http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 35 | 36 | If the developer choses the Swiss Ephemeris Professional license, 37 | he must follow the instructions as found in http://www.astro.com/swisseph/ 38 | and purchase the Swiss Ephemeris Professional Edition from Astrodienst 39 | and sign the corresponding license contract. 40 | 41 | The License grants you the right to use, copy, modify and redistribute 42 | Swiss Ephemeris, but only under certain conditions described in the License. 43 | Among other things, the License requires that the copyright notices and 44 | this notice be preserved on all copies. 45 | 46 | Authors of the Swiss Ephemeris: Dieter Koch and Alois Treindl 47 | 48 | The authors of Swiss Ephemeris have no control or influence over any of 49 | the derived works, i.e. over software or services created by other 50 | programmers which use Swiss Ephemeris functions. 51 | 52 | The names of the authors or of the copyright holder (Astrodienst) must not 53 | be used for promoting any software, product or service which uses or contains 54 | the Swiss Ephemeris. This copyright notice is the ONLY place where the 55 | names of the authors can legally appear, except in cases where they have 56 | given special permission in writing. 57 | 58 | The trademarks 'Swiss Ephemeris' and 'Swiss Ephemeris inside' may be used 59 | for promoting such software, products or services. 60 | */ 61 | 62 | #define PREC_IAU_1976_CTIES 2.0 /* J2000 +/- two centuries */ 63 | #define PREC_IAU_2000_CTIES 2.0 /* J2000 +/- two centuries */ 64 | /* we use P03 for whole ephemeris */ 65 | #define PREC_IAU_2006_CTIES 75.0 /* J2000 +/- 75 centuries */ 66 | 67 | /* For reproducing JPL Horizons to 2 mas (SEFLG_JPLHOR): 68 | * The user has to keep the following files up to date which contain 69 | * the earth orientation parameters related to the IAU 1980 nutation 70 | * theory. 71 | * Download the file 72 | * datacenter.iers.org/eop/-/somos/5Rgv/document/tx13iers.u24/eopc04_08.62-now 73 | * and rename it as eop_1962_today.txt. For current data and estimations for 74 | * the near future, also download maia.usno.navy.mil/ser7/finals.all and 75 | * rename it as eop_finals.txt */ 76 | #define DPSI_DEPS_IAU1980_FILE_EOPC04 "eop_1962_today.txt" 77 | #define DPSI_DEPS_IAU1980_FILE_FINALS "eop_finals.txt" 78 | #define DPSI_DEPS_IAU1980_TJD0_HORIZONS 2437684.5 79 | #define HORIZONS_TJD0_DPSI_DEPS_IAU1980 2437684.5 80 | /*#define INCLUDE_CODE_FOR_DPSI_DEPS_IAU1980 TRUE*/ 81 | 82 | /*#define INCLUDE_CODE_FOR_DPSI_DEPS_IAU1980 TRUE */ 83 | /* You can set the latter false if you do not want to compile the 84 | * code required to reproduce JPL Horizons. 85 | * Keep it TRUE in order to reproduce JPL Horizons following 86 | * IERS Conventions 1996 (1992), p. 22. Call swe_calc_ut() with 87 | * iflag|SEFLG_JPLHOR. This options runs only, if the files 88 | * DPSI_DEPS_IAU1980_FILE_EOPC04 and DPSI_DEPS_IAU1980_FILE_FINALS 89 | * are in the ephemeris path. 90 | */ 91 | 92 | /* If the above define INCLUDE_CODE_FOR_DPSI_DEPS_IAU1980 is FALSE or 93 | * the software does not find the earth orientation files (see above) 94 | * in the ephemeris path, then SEFLG_JPLHOR will run as 95 | * SEFLG_JPLHOR_APPROX. 96 | * The following define APPROXIMATE_HORIZONS_ASTRODIENST defines 97 | * the handling of SEFLG_JPLHOR_APPROX. 98 | * With this flag, planetary positions are always calculated 99 | * using a recent precession/nutation model. 100 | * If APPROXIMATE_HORIZONS_ASTRODIENST is FALSE, then the 101 | * frame bias as recommended by IERS Conventions 2003 and 2010 102 | * is *not* applied. Instead, dpsi_bias and deps_bias are added to 103 | * nutation. This procedure is found in some older astronomical software. 104 | * Equatorial apparent positions will be close to JPL Horizons 105 | * (within a few mas) beetween 1962 and current years. Ecl. longitude 106 | * will be good, latitude bad. 107 | * If APPROXIMATE_HORIZONS_ASTRODIENST is TRUE, the approximation of 108 | * JPL Horizons is even better. Frame bias matrix is applied with 109 | * some correction to RA and another correction is added to epsilon. 110 | */ 111 | /*#define APPROXIMATE_HORIZONS_ASTRODIENST TRUE */ 112 | 113 | /*#define USE_HORIZONS_METHOD_BEFORE_1980 TRUE * Horizons method before 20-jan-1962 */ 114 | /* The latter, if combined with SEFLG_JPLHOR provides good agreement 115 | * with JPL Horizons for 1800 - today. However, Horizons uses correct 116 | * dpsi and deps only after 20-jan-1962. For all dates before that 117 | * it uses dpsi and deps of 20-jan-1962, which provides a continuous 118 | * ephemeris, but does not make sense otherwise. 119 | * Before 1800, even this option does not provide agreement with Horizons, 120 | * because Horizons uses a different precession model (Owen 1986) 121 | * before 1800, which is not included in the Swiss Ephemeris. 122 | * If this macro is FALSE then the program defaults to SEFLG_JPLHOR_APPROX 123 | * outside the time range of correction data dpsi and deps. 124 | * Note that this will result in a non-continuous ephemeris near 125 | * 20-jan-1962 and current years. 126 | */ 127 | 128 | /* coordinate transformation */ 129 | extern void swi_coortrf(double *xpo, double *xpn, double eps); 130 | 131 | /* coordinate transformation */ 132 | extern void swi_coortrf2(double *xpo, double *xpn, double sineps, double coseps); 133 | 134 | /* cartesian to polar coordinates */ 135 | extern void swi_cartpol(double *x, double *l); 136 | 137 | /* cartesian to polar coordinates with velocity */ 138 | extern void swi_cartpol_sp(double *x, double *l); 139 | extern void swi_polcart_sp(double *l, double *x); 140 | 141 | /* polar to cartesian coordinates */ 142 | extern void swi_polcart(double *l, double *x); 143 | 144 | /* GCRS to J2000 */ 145 | extern void swi_bias(double *x, double tjd, int32 iflag, AS_BOOL backward); 146 | extern void swi_get_eop_time_range(void); 147 | /* GCRS to FK5 */ 148 | extern void swi_icrs2fk5(double *x, int32 iflag, AS_BOOL backward); 149 | 150 | /* precession */ 151 | extern int swi_precess(double *R, double J, int32 iflag, int direction ); 152 | extern void swi_precess_speed(double *xx, double t, int32 iflag, int direction); 153 | 154 | extern int32 swi_guess_ephe_flag(); 155 | 156 | /* from sweph.c, light deflection, aberration, etc. */ 157 | extern void swi_deflect_light(double *xx, double dt, int32 iflag); 158 | extern void swi_aberr_light(double *xx, double *xe, int32 iflag); 159 | extern int swi_plan_for_osc_elem(int32 iflag, double tjd, double *xx); 160 | extern int swi_trop_ra2sid_lon(double *xin, double *xout, double *xoutr, int32 iflag); 161 | extern int swi_trop_ra2sid_lon_sosy(double *xin, double *xout, int32 iflag); 162 | extern int swi_get_observer(double tjd, int32 iflag, 163 | AS_BOOL do_save, double *xobs, char *serr); 164 | extern void swi_force_app_pos_etc(); 165 | 166 | /* obliquity of ecliptic */ 167 | extern void swi_check_ecliptic(double tjd, int32 iflag); 168 | extern double swi_epsiln(double J, int32 iflag); 169 | extern void swi_ldp_peps(double J, double *dpre, double *deps); 170 | 171 | /* nutation */ 172 | extern void swi_check_nutation(double tjd, int32 iflag); 173 | extern int swi_nutation(double J, int32 iflag, double *nutlo); 174 | extern void swi_nutate(double *xx, int32 iflag, AS_BOOL backward); 175 | 176 | extern void swi_mean_lunar_elements(double tjd, 177 | double *node, double *dnode, 178 | double *peri, double *dperi); 179 | /* */ 180 | extern double swi_mod2PI(double x); 181 | 182 | /* evaluation of chebyshew series and derivative */ 183 | extern double swi_echeb(double x, double *coef, int ncf); 184 | extern double swi_edcheb(double x, double *coef, int ncf); 185 | 186 | /* cross product of vectors */ 187 | extern void swi_cross_prod(double *a, double *b, double *x); 188 | /* dot product of vecotrs */ 189 | extern double swi_dot_prod_unit(double *x, double *y); 190 | 191 | extern double swi_angnorm(double x); 192 | 193 | /* generation of SWISSEPH file names */ 194 | extern void swi_gen_filename(double tjd, int ipli, char *fname); 195 | 196 | /* cyclic redundancy checksum (CRC), 32 bit */ 197 | extern uint32 swi_crc32(unsigned char *buf, int len); 198 | 199 | extern int swi_cutstr(char *s, char *cutlist, char *cpos[], int nmax); 200 | extern char *swi_right_trim(char *s); 201 | 202 | extern double swi_kepler(double E, double M, double ecce); 203 | 204 | extern char *swi_get_fict_name(int32 ipl, char *s); 205 | 206 | extern void swi_FK4_FK5(double *xp, double tjd); 207 | 208 | extern char *swi_strcpy(char *to, char *from); 209 | extern char *swi_strncpy(char *to, char *from, size_t n); 210 | 211 | extern double swi_deltat_ephe(double tjd_ut, int32 epheflag); 212 | 213 | #ifdef TRACE 214 | # define TRACE_COUNT_MAX 10000 215 | extern TLS FILE *swi_fp_trace_c; 216 | extern TLS FILE *swi_fp_trace_out; 217 | extern TLS int32 swi_trace_count; 218 | extern void swi_open_trace(char *serr); 219 | static const char *fname_trace_c = "swetrace.c"; 220 | static const char *fname_trace_out = "swetrace.txt"; 221 | #ifdef FORCE_IFLAG 222 | static const char *fname_force_flg = "force.flg"; 223 | #endif 224 | #endif /* TRACE */ 225 | -------------------------------------------------------------------------------- /swisseph/swephprg.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrotools/swego/58c37ed952ef669c1ae4e964d26a36c68cc2c897/swisseph/swephprg.pdf -------------------------------------------------------------------------------- /swisseph/swisseph.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrotools/swego/58c37ed952ef669c1ae4e964d26a36c68cc2c897/swisseph/swisseph.pdf --------------------------------------------------------------------------------