pygnss 2.1.2__cp314-cp314t-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pygnss/__init__.py +1 -0
- pygnss/_c_ext/src/constants.c +36 -0
- pygnss/_c_ext/src/hatanaka.c +94 -0
- pygnss/_c_ext/src/helpers.c +17 -0
- pygnss/_c_ext/src/klobuchar.c +313 -0
- pygnss/_c_ext/src/mtable_init.c +50 -0
- pygnss/_c_ext.cpython-314t-darwin.so +0 -0
- pygnss/cl.py +148 -0
- pygnss/constants.py +4 -0
- pygnss/decorator.py +47 -0
- pygnss/file.py +36 -0
- pygnss/filter/__init__.py +77 -0
- pygnss/filter/ekf.py +80 -0
- pygnss/filter/models.py +74 -0
- pygnss/filter/particle.py +484 -0
- pygnss/filter/ukf.py +322 -0
- pygnss/geodetic.py +1177 -0
- pygnss/gnss/__init__.py +0 -0
- pygnss/gnss/edit.py +66 -0
- pygnss/gnss/observables.py +43 -0
- pygnss/gnss/residuals.py +43 -0
- pygnss/gnss/types.py +359 -0
- pygnss/hatanaka.py +70 -0
- pygnss/ionex.py +410 -0
- pygnss/iono/__init__.py +47 -0
- pygnss/iono/chapman.py +35 -0
- pygnss/iono/gim.py +131 -0
- pygnss/logger.py +70 -0
- pygnss/nequick.py +57 -0
- pygnss/orbit/__init__.py +0 -0
- pygnss/orbit/kepler.py +63 -0
- pygnss/orbit/tle.py +186 -0
- pygnss/parsers/rtklib/stats.py +166 -0
- pygnss/rinex.py +2161 -0
- pygnss/sinex.py +121 -0
- pygnss/stats.py +75 -0
- pygnss/tensorial.py +50 -0
- pygnss/time.py +350 -0
- pygnss-2.1.2.dist-info/METADATA +129 -0
- pygnss-2.1.2.dist-info/RECORD +44 -0
- pygnss-2.1.2.dist-info/WHEEL +6 -0
- pygnss-2.1.2.dist-info/entry_points.txt +8 -0
- pygnss-2.1.2.dist-info/licenses/LICENSE +21 -0
- pygnss-2.1.2.dist-info/top_level.txt +1 -0
pygnss/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.1.2"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#include <limits.h>
|
|
2
|
+
#include <stdint.h>
|
|
3
|
+
#include <stdlib.h>
|
|
4
|
+
|
|
5
|
+
const double CONSTANT_PI = 3.14159265358979323846;
|
|
6
|
+
const double CONSTANT_TAU = 6.283185307179586476925;
|
|
7
|
+
const double CONSTANT_HALFPI = 3.14159265358979323846 / 2.0;
|
|
8
|
+
const double CONSTANT_LIGHTSPEED = 299792458.0; // [m/s]
|
|
9
|
+
|
|
10
|
+
const double CONSTANT_DEG2RAD = 3.14159265358979323846 / 180.0;
|
|
11
|
+
|
|
12
|
+
// const double ROK_CT_TWOPI = 3.14159265358979323846 * 2.0;
|
|
13
|
+
// const double ROK_CT_QUARTERPI = 3.14159265358979323846 / 4.0;
|
|
14
|
+
// const double ROK_CT_RAD2DEG = 180.0 / 3.14159265358979323846;
|
|
15
|
+
// const double ROK_CT_RAD2SEMI = 1.0 / 3.14159265358979323846;
|
|
16
|
+
// const double ROK_CT_SEMI2RAD = 3.14159265358979323846;
|
|
17
|
+
// const double ROK_CT_EARTHGM = 3.986004418e14; // [m^3/s^2]
|
|
18
|
+
// const double ROK_CT_EARTHGM_GPS = 3.986005e+14; // [m^3/s^2]
|
|
19
|
+
// const double ROK_CT_EARTHGM_GLO = 3.9860044e+14; // [m^3/s^2]
|
|
20
|
+
// const double ROK_CT_EARTHROTATION = 7.2921151467e-5; // [rad/s]
|
|
21
|
+
// const double ROK_CT_EARTHROTATION_GLO = 7.2921150e-5; // [rad/s]
|
|
22
|
+
// const double ROK_CT_EARTH_F = -4.442807633e-10; // [s/sqrt(m)], GPS ICD-GPS-200H, page 96
|
|
23
|
+
// const double ROK_CT_WGS84_A = 6378137.0; // [m]
|
|
24
|
+
// const double ROK_CT_WGS84_E = 8.1819190842622e-2;
|
|
25
|
+
// const double ROK_CT_WGS84_F = 1.0 / 298.257223563;
|
|
26
|
+
// const double ROK_CT_EARTH_J2 = 1.0826257e-3;
|
|
27
|
+
// const double ROK_CT_NS2M = 1.0e-9 * 299792458.0;
|
|
28
|
+
// const double ROK_CT_M2NS = 1.0 / 299792458.0 / 1.0e-9;
|
|
29
|
+
// const double ROK_CT_WEEK2SECONDS = 604800.0;
|
|
30
|
+
// const double ROK_CT_DAY2SECONDS = 86400.0;
|
|
31
|
+
// const double ROK_CT_GRAVITY = 9.80665; // m/s^2
|
|
32
|
+
// const uint16_t ROK_CT_WEEK_GALT_GPST = 1024;
|
|
33
|
+
// const uint16_t ROK_CT_WEEK_BDST_GPST = 1356;
|
|
34
|
+
// const size_t ROK_CT_SIZE_3D = 3 * sizeof(double);
|
|
35
|
+
// const double ROK_CT_KNOTS_TO_MPS = 0.514444444444444444;
|
|
36
|
+
// const char* ROK_CT_DEF_RXNAME = "UNKN";
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#include <Python.h>
|
|
2
|
+
#include <datetime.h>
|
|
3
|
+
|
|
4
|
+
#include "hatanaka/crx2rnx.h"
|
|
5
|
+
|
|
6
|
+
static char* get_crx_line(void* _args, size_t n_max, char* dst) {
|
|
7
|
+
|
|
8
|
+
FILE* input_fh = (FILE*)_args;
|
|
9
|
+
return fgets(dst, n_max, input_fh);
|
|
10
|
+
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
static bool is_eof(void* _args) {
|
|
14
|
+
|
|
15
|
+
FILE* input_fh = (FILE*)_args;
|
|
16
|
+
return (fgetc(input_fh) == EOF);
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static int on_measurement(const struct gnss_meas* gnss_meas, void* _args) {
|
|
21
|
+
|
|
22
|
+
static const int N_FIELDS = 5; // Number of fields for struct gnss_meas
|
|
23
|
+
|
|
24
|
+
int ret = -1;
|
|
25
|
+
PyObject* list = (PyObject*)_args;
|
|
26
|
+
|
|
27
|
+
if (gnss_meas == NULL) {
|
|
28
|
+
goto exit;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
PyDateTime_IMPORT;
|
|
32
|
+
|
|
33
|
+
// Create Python lists for each inner list
|
|
34
|
+
PyObject* row = PyList_New(N_FIELDS);
|
|
35
|
+
|
|
36
|
+
double timestamp = (double)gnss_meas->gps_time.tv_sec + (double)gnss_meas->gps_time.tv_nsec / 1e9;
|
|
37
|
+
PyObject* time_tuple = Py_BuildValue("(d)", timestamp);
|
|
38
|
+
PyObject* date_time = PyDateTime_FromTimestamp(time_tuple);
|
|
39
|
+
|
|
40
|
+
PyList_SetItem(row, 0, date_time);
|
|
41
|
+
PyList_SetItem(row, 1, PyUnicode_FromStringAndSize(gnss_meas->satid, 3));
|
|
42
|
+
PyList_SetItem(row, 2, PyUnicode_FromStringAndSize(gnss_meas->rinex3_code, 3));
|
|
43
|
+
PyList_SetItem(row, 3, PyFloat_FromDouble(gnss_meas->value));
|
|
44
|
+
PyList_SetItem(row, 4, PyLong_FromUnsignedLong(gnss_meas->lli));
|
|
45
|
+
|
|
46
|
+
// Add inner lists to the outer list
|
|
47
|
+
PyList_Append(list, row);
|
|
48
|
+
Py_DECREF(row); // Decrement the reference count of 'row'
|
|
49
|
+
|
|
50
|
+
ret = 0;
|
|
51
|
+
exit:
|
|
52
|
+
return ret;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
PyObject *_read_crx(PyObject* self, PyObject* args, PyObject* kwargs) {
|
|
56
|
+
|
|
57
|
+
char *filename = NULL;
|
|
58
|
+
struct crx2rnx* crx2rnx = NULL;
|
|
59
|
+
int ret = -1;
|
|
60
|
+
PyObject* list = PyList_New(0);
|
|
61
|
+
|
|
62
|
+
struct crx2rnx_callbacks callbacks = {
|
|
63
|
+
.on_measurement = on_measurement,
|
|
64
|
+
.on_measurement_args = list
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Parse the filename argument
|
|
68
|
+
if (!PyArg_ParseTuple(args, "s", &filename)) {
|
|
69
|
+
PyErr_SetString(PyExc_TypeError, "Expected a string filename");
|
|
70
|
+
goto end;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Open the file
|
|
74
|
+
FILE* fp = fopen(filename, "r");
|
|
75
|
+
if (fp == NULL) {
|
|
76
|
+
PyErr_SetString(PyExc_IOError, "Could not open file");
|
|
77
|
+
goto end;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
crx2rnx = crx2rnx__init(false, false, NULL, get_crx_line, (void*)fp, is_eof, (void*)fp, &callbacks);
|
|
81
|
+
|
|
82
|
+
ret = crx2rnx__run(crx2rnx);
|
|
83
|
+
|
|
84
|
+
if (ret < 0) {
|
|
85
|
+
PyErr_SetString(PyExc_IOError, "There was an issue processing the Hatanaka file");
|
|
86
|
+
PyList_SetSlice(list, 0, PY_SSIZE_T_MAX, NULL); // clear the list
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Clean-up
|
|
90
|
+
fclose(fp);
|
|
91
|
+
end:
|
|
92
|
+
return list;
|
|
93
|
+
|
|
94
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#include <stdlib.h>
|
|
2
|
+
#include <Python.h>
|
|
3
|
+
|
|
4
|
+
#include "../include/helpers.h"
|
|
5
|
+
|
|
6
|
+
PyObject *convert_to_pylist(const double* array, size_t n) {
|
|
7
|
+
|
|
8
|
+
Py_ssize_t len = n;
|
|
9
|
+
PyObject* list = PyList_New(len);
|
|
10
|
+
|
|
11
|
+
for (Py_ssize_t i = 0; i < len; i++) {
|
|
12
|
+
PyObject* value = PyFloat_FromDouble(array[i]);
|
|
13
|
+
PyList_SetItem(list, i, value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return list;
|
|
17
|
+
}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
#include <Python.h>
|
|
2
|
+
#include <math.h>
|
|
3
|
+
#include <string.h>
|
|
4
|
+
|
|
5
|
+
#include "constants.h"
|
|
6
|
+
|
|
7
|
+
#include "klobuchar.h"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/** \brief Structure to hold the 8-parameters for the Klobuchar model */
|
|
11
|
+
struct klobuchar {
|
|
12
|
+
double alphas[4];
|
|
13
|
+
double betas[4];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
typedef struct {
|
|
18
|
+
PyObject_HEAD
|
|
19
|
+
struct klobuchar klobuchar;
|
|
20
|
+
} Klobuchar;
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
static double compute_klobuchar(const double tow,
|
|
24
|
+
const double longitude_rad, const double latitude_rad,
|
|
25
|
+
const double el_rad, const double az_rad,
|
|
26
|
+
const struct klobuchar* klobuchar) {
|
|
27
|
+
|
|
28
|
+
double el_semicircles;
|
|
29
|
+
double psi;
|
|
30
|
+
double phi_I;
|
|
31
|
+
double lambda_I;
|
|
32
|
+
double phi_m;
|
|
33
|
+
double t;
|
|
34
|
+
double phi_m_n[4];
|
|
35
|
+
double amplitude_I;
|
|
36
|
+
double period_I;
|
|
37
|
+
double phase_I;
|
|
38
|
+
double slant_factor;
|
|
39
|
+
double iono_delay_l1;
|
|
40
|
+
|
|
41
|
+
// Convert elevation from radians to semicircles
|
|
42
|
+
el_semicircles = el_rad / CONSTANT_PI;
|
|
43
|
+
|
|
44
|
+
// Earth-centered angle (psi)
|
|
45
|
+
psi = 0.0137 / (el_semicircles + 0.11) - 0.022;
|
|
46
|
+
|
|
47
|
+
// Latitude of the ionosphere pierce point (semicircles)
|
|
48
|
+
phi_I = (latitude_rad / CONSTANT_PI) + (psi * cos(az_rad));
|
|
49
|
+
if (phi_I > 0.416) {
|
|
50
|
+
phi_I = 0.416;
|
|
51
|
+
} else if (phi_I < -0.416) {
|
|
52
|
+
phi_I = -0.416;
|
|
53
|
+
} else {
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Longitude of the ionosphere pierce point (semicircles)
|
|
57
|
+
lambda_I = (longitude_rad / CONSTANT_PI) + (psi * sin(az_rad) / cos(phi_I * CONSTANT_PI));
|
|
58
|
+
|
|
59
|
+
// Geomagnetic latitude_rad of the ionosphere pierce point
|
|
60
|
+
phi_m = phi_I + (0.064 * cos((lambda_I - 1.617) * CONSTANT_PI));
|
|
61
|
+
|
|
62
|
+
// Local time at the IPP
|
|
63
|
+
t = fmod((43200.0 * lambda_I) + tow, 86400);
|
|
64
|
+
if (t < 0.0) {
|
|
65
|
+
t += 86400.0;
|
|
66
|
+
} else if (t >= 86400.0) {
|
|
67
|
+
t -= 86400.0;
|
|
68
|
+
} else {
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Compute powers of phi_m for later usage
|
|
72
|
+
for (int i = 0; i < 4; i++) {
|
|
73
|
+
phi_m_n[i] = pow(phi_m, (double)i);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Compute the *amplitude* of the ionospheric delay (seconds)
|
|
77
|
+
// Alpha coefficients are being used for this amount
|
|
78
|
+
//
|
|
79
|
+
// amplitude_I = \sum_{n=0}^3 \alpha_n \cdot \phi_m^n
|
|
80
|
+
//
|
|
81
|
+
amplitude_I = 0.0;
|
|
82
|
+
for (int i = 0; i < 4; i++) {
|
|
83
|
+
amplitude_I += klobuchar->alphas[i] * phi_m_n[i];
|
|
84
|
+
}
|
|
85
|
+
if (amplitude_I < 0.0) {
|
|
86
|
+
amplitude_I = 0.0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Compute the *period* of the ionospheric delay (seconds)
|
|
90
|
+
//
|
|
91
|
+
// period_I = \sum_{n=0}^3 \beta_n \cdot \phi_m^n
|
|
92
|
+
//
|
|
93
|
+
period_I = 0.0;
|
|
94
|
+
for (int i = 0; i < 4; i++) {
|
|
95
|
+
period_I += klobuchar->betas[i] * phi_m_n[i];
|
|
96
|
+
}
|
|
97
|
+
if (period_I < 72000.0) {
|
|
98
|
+
period_I = 72000.0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Compute the phase of the ionospheric delay (radians)
|
|
102
|
+
phase_I = CONSTANT_TAU * (t - 50400.0) / period_I;
|
|
103
|
+
|
|
104
|
+
// Compute the slant factor (elevation in semicircles)
|
|
105
|
+
slant_factor = 1.0 + (16.0 * pow(0.53 - el_semicircles, 3));
|
|
106
|
+
|
|
107
|
+
// Compute the ionospheric time delay (L1)
|
|
108
|
+
iono_delay_l1 = 5.0e-9;
|
|
109
|
+
if (fabs(phase_I) <= CONSTANT_HALFPI) {
|
|
110
|
+
iono_delay_l1 = iono_delay_l1
|
|
111
|
+
+ (amplitude_I * (1.0 - (pow(phase_I, 2) / 2.0) + (pow(phase_I, 4) / 24.0)));
|
|
112
|
+
}
|
|
113
|
+
iono_delay_l1 = iono_delay_l1 * slant_factor;
|
|
114
|
+
|
|
115
|
+
return iono_delay_l1 * CONSTANT_LIGHTSPEED;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
double compute_klobuchar_stec(const double tow,
|
|
119
|
+
const double longitude_rad, const double latitude_rad,
|
|
120
|
+
const double elevation_rad, const double azimuth_rad,
|
|
121
|
+
const struct klobuchar* klobuchar) {
|
|
122
|
+
|
|
123
|
+
static const double INV_ALPHA_1_TECU = 154.0 * 154.0 * 10.23e6 * 10.23e6 / 40.3 * 1.0e-16; /* f_1^2 / 40.3 (in TECU) */
|
|
124
|
+
|
|
125
|
+
double delay = compute_klobuchar(tow, longitude_rad, latitude_rad, elevation_rad, azimuth_rad, klobuchar);
|
|
126
|
+
double stec_tecu = delay * INV_ALPHA_1_TECU;
|
|
127
|
+
|
|
128
|
+
return stec_tecu;
|
|
129
|
+
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
static PyObject* new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
|
|
134
|
+
|
|
135
|
+
Klobuchar *self;
|
|
136
|
+
self = (Klobuchar *) type->tp_alloc(type, 0);
|
|
137
|
+
if (self != NULL) {
|
|
138
|
+
memset(&self->klobuchar, 0, sizeof(struct klobuchar));
|
|
139
|
+
}
|
|
140
|
+
return (PyObject *) self;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static void dealloc(Klobuchar *self) {
|
|
144
|
+
|
|
145
|
+
Py_TYPE(self)->tp_free((PyObject *) self);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
static int init(Klobuchar *self, PyObject *args, PyObject *kwds) {
|
|
149
|
+
|
|
150
|
+
static char *kwlist[] = {"alpha0", "alpha1", "alpha2", "alpha3",
|
|
151
|
+
"beta0", "beta1", "beta2", "beta3", NULL};
|
|
152
|
+
|
|
153
|
+
struct klobuchar* klob = &self->klobuchar;
|
|
154
|
+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "dddddddd", kwlist,
|
|
155
|
+
&klob->alphas[0], &klob->alphas[1], &klob->alphas[2], &klob->alphas[3],
|
|
156
|
+
&klob->betas[0], &klob->betas[1], &klob->betas[2], &klob->betas[3]))
|
|
157
|
+
return -1;
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
static PyObject* Klobuchar_ionospheric_delay(Klobuchar *self, PyObject *args) {
|
|
162
|
+
|
|
163
|
+
double tow, lat_deg, lon_deg, az_deg, el_deg;
|
|
164
|
+
if (!PyArg_ParseTuple(args, "ddddd", &tow, &lat_deg, &lon_deg, &az_deg, &el_deg))
|
|
165
|
+
return NULL;
|
|
166
|
+
|
|
167
|
+
double lat_rad = lat_deg * CONSTANT_DEG2RAD;
|
|
168
|
+
double lon_rad = lon_deg * CONSTANT_DEG2RAD;
|
|
169
|
+
double az_rad = az_deg * CONSTANT_DEG2RAD;
|
|
170
|
+
double el_rad = el_deg * CONSTANT_DEG2RAD;
|
|
171
|
+
double delay = compute_klobuchar(tow, lon_rad, lat_rad, el_rad, az_rad, &self->klobuchar);
|
|
172
|
+
|
|
173
|
+
return PyFloat_FromDouble(delay);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
static PyObject* Klobuchar_compute_stec(Klobuchar *self, PyObject *args) {
|
|
177
|
+
|
|
178
|
+
double tow, lat_deg, lon_deg, az_deg, el_deg;
|
|
179
|
+
if (!PyArg_ParseTuple(args, "ddddd", &tow, &lat_deg, &lon_deg, &az_deg, &el_deg))
|
|
180
|
+
return NULL;
|
|
181
|
+
|
|
182
|
+
double lat_rad = lat_deg * CONSTANT_DEG2RAD;
|
|
183
|
+
double lon_rad = lon_deg * CONSTANT_DEG2RAD;
|
|
184
|
+
double az_rad = az_deg * CONSTANT_DEG2RAD;
|
|
185
|
+
double el_rad = el_deg * CONSTANT_DEG2RAD;
|
|
186
|
+
double stec_tecu = compute_klobuchar_stec(tow, lon_rad, lat_rad, el_rad, az_rad, &self->klobuchar);
|
|
187
|
+
|
|
188
|
+
return PyFloat_FromDouble(stec_tecu);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
static PyObject* Klobuchar_compute_map(Klobuchar *self, PyObject *args, PyObject* kwargs) {
|
|
193
|
+
|
|
194
|
+
static char* arguments[] = {"" /*tow*/, "n_lats", "n_lons", NULL};
|
|
195
|
+
|
|
196
|
+
double tow;
|
|
197
|
+
int n_lats = 72;
|
|
198
|
+
int n_lons = 72;
|
|
199
|
+
|
|
200
|
+
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d|ii", arguments, &tow, &n_lats, &n_lons))
|
|
201
|
+
return NULL;
|
|
202
|
+
|
|
203
|
+
double* map = calloc(n_lats * n_lons, sizeof(double));
|
|
204
|
+
if (map == NULL)
|
|
205
|
+
return NULL;
|
|
206
|
+
|
|
207
|
+
/* compute the map */
|
|
208
|
+
double dlat = 180.0 / n_lats;
|
|
209
|
+
double dlon = 360.0 / n_lons;
|
|
210
|
+
|
|
211
|
+
for (int i = 0; i < n_lats; i ++) {
|
|
212
|
+
double lat_rad = (+90 - (i + 0.5) * dlat) * CONSTANT_DEG2RAD;
|
|
213
|
+
for (int j = 0; j < n_lons; j++) {
|
|
214
|
+
double lon_rad = (-180 + (j + 0.5) * dlon) * CONSTANT_DEG2RAD;
|
|
215
|
+
|
|
216
|
+
map[i * n_lons + j] = compute_klobuchar_stec(tow, lon_rad, lat_rad, CONSTANT_HALFPI, 0, &self->klobuchar);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* store the result into a list of lists*/
|
|
221
|
+
PyObject* matrix = PyList_New(n_lats);
|
|
222
|
+
|
|
223
|
+
// Fill the Python list of lists with sublists
|
|
224
|
+
for (int i = 0; i < n_lats; ++i) {
|
|
225
|
+
PyObject* sublist = PyList_New(n_lons);
|
|
226
|
+
for (int j = 0; j < n_lons; ++j) {
|
|
227
|
+
double value = map[i * n_lons + j];
|
|
228
|
+
PyObject* float_obj = PyFloat_FromDouble(value);
|
|
229
|
+
PyList_SET_ITEM(sublist, j, float_obj);
|
|
230
|
+
}
|
|
231
|
+
PyList_SET_ITEM(matrix, i, sublist);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
free(map);
|
|
235
|
+
|
|
236
|
+
return matrix;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
static PyMethodDef methods[] = {
|
|
241
|
+
{"compute_slant_delay", (PyCFunction) Klobuchar_ionospheric_delay, METH_VARARGS,
|
|
242
|
+
"Compute ionospheric delay.\n\n"
|
|
243
|
+
":param tow: Time of the Week (in seconds)\n"
|
|
244
|
+
":param lat_deg: Latitude (in degrees)\n"
|
|
245
|
+
":param lon_deg: Longitude (in degrees)\n"
|
|
246
|
+
":param az_deg: Azimuth (in degrees)\n"
|
|
247
|
+
":param el_deg: Elevation (in degrees)\n\n"
|
|
248
|
+
":return: Delay in meters for the GPS L1 frequency\n\n"
|
|
249
|
+
},
|
|
250
|
+
{"compute_stec", (PyCFunction) Klobuchar_compute_stec, METH_VARARGS,
|
|
251
|
+
"Compute Slant Total Electron Content\n\n"
|
|
252
|
+
":param tow: Time of the Week (in seconds)\n"
|
|
253
|
+
":param lat_deg: Latitude (in degrees)\n"
|
|
254
|
+
":param lon_deg: Longitude (in degrees)\n"
|
|
255
|
+
":param az_deg: Azimuth (in degrees)\n"
|
|
256
|
+
":param el_deg: Elevation (in degrees)\n\n"
|
|
257
|
+
":return: Slant Total Electron Content (in TEC Units)\n\n"},
|
|
258
|
+
{"compute_vtec_map", (PyCFunction) Klobuchar_compute_map, METH_VARARGS | METH_KEYWORDS,
|
|
259
|
+
"Compute a VTEC global ionospheric map\n\n"
|
|
260
|
+
":param tow: Time of the Week (in seconds)\n"
|
|
261
|
+
":param n_lats: Number of latitude bins (defaults to 72)\n"
|
|
262
|
+
":param n_lons: Number of longitude bins (defaults to 72)\n\n"
|
|
263
|
+
":return: Array of arrays containing VTEC values (n_lats rows * n_lons columns) (TECU)\n\n"},
|
|
264
|
+
{NULL} /* Sentinel */
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
static PyTypeObject Type = {
|
|
268
|
+
PyVarObject_HEAD_INIT(NULL, 0)
|
|
269
|
+
"pyrcore.Klobuchar", /* tp_name */
|
|
270
|
+
sizeof(Klobuchar), /* tp_basicsize */
|
|
271
|
+
0, /* tp_itemsize */
|
|
272
|
+
(destructor) dealloc, /* tp_dealloc */
|
|
273
|
+
0, /* tp_print */
|
|
274
|
+
0, /* tp_getattr */
|
|
275
|
+
0, /* tp_setattr */
|
|
276
|
+
0, /* tp_reserved */
|
|
277
|
+
0, /* tp_repr */
|
|
278
|
+
0, /* tp_as_number */
|
|
279
|
+
0, /* tp_as_sequence */
|
|
280
|
+
0, /* tp_as_mapping */
|
|
281
|
+
0, /* tp_hash */
|
|
282
|
+
0, /* tp_call */
|
|
283
|
+
0, /* tp_str */
|
|
284
|
+
0, /* tp_getattro */
|
|
285
|
+
0, /* tp_setattro */
|
|
286
|
+
0, /* tp_as_buffer */
|
|
287
|
+
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
|
288
|
+
"Klobuchar class\n\n"
|
|
289
|
+
">>> from pyrcore import Klobuchar\n"
|
|
290
|
+
">>> alphas = [.3820e-07, .1490e-07, -.1790e-06, .0000e-00]\n"
|
|
291
|
+
">>> betas = [.1430e+06, .0000e+00, -.3280e+06, .1130e+06]\n"
|
|
292
|
+
">>> klobuchar = Klobuchar(*alphas, *betas)\n"
|
|
293
|
+
">>> delay_L1 = klobuchar.delay(593100, 40, 260, 210, 20)", /* tp_doc */
|
|
294
|
+
0, /* tp_traverse */
|
|
295
|
+
0, /* tp_clear */
|
|
296
|
+
0, /* tp_richcompare */
|
|
297
|
+
0, /* tp_weaklistoffset */
|
|
298
|
+
0, /* tp_iter */
|
|
299
|
+
0, /* tp_iternext */
|
|
300
|
+
methods, /* tp_methods */
|
|
301
|
+
0, /* tp_members */
|
|
302
|
+
0, /* tp_getset */
|
|
303
|
+
0, /* tp_base */
|
|
304
|
+
0, /* tp_dict */
|
|
305
|
+
0, /* tp_descr_get */
|
|
306
|
+
0, /* tp_descr_set */
|
|
307
|
+
0, /* tp_dictoffset */
|
|
308
|
+
(initproc)init,/* tp_init */
|
|
309
|
+
0, /* tp_alloc */
|
|
310
|
+
new, /* tp_new */
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
PyTypeObject* KlobucharType = &Type;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#include <Python.h>
|
|
2
|
+
|
|
3
|
+
#include "hatanaka.h"
|
|
4
|
+
#include "klobuchar.h"
|
|
5
|
+
|
|
6
|
+
static PyMethodDef module_methods[] = {
|
|
7
|
+
{ "_read_crx", (PyCFunction)_read_crx, METH_VARARGS | METH_KEYWORDS,
|
|
8
|
+
"Read a Hatanaka (gzip uncompressed) file and generate a numpy array\n\n"
|
|
9
|
+
":param filename: Name of the Hatanaka file to process\n"
|
|
10
|
+
":return: Numpy array\n\n"},
|
|
11
|
+
{NULL, NULL, 0, NULL}, /* Sentinel */
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/*----------------------------------------------------------------------------*/
|
|
15
|
+
|
|
16
|
+
static struct PyModuleDef module = {
|
|
17
|
+
PyModuleDef_HEAD_INIT,
|
|
18
|
+
"_c_ext", /* name of the module*/
|
|
19
|
+
"C extension methods",
|
|
20
|
+
-1, // size of per-interpreter state of the module,
|
|
21
|
+
// or -1 if the module keeps state in global variables.
|
|
22
|
+
module_methods
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
PyMODINIT_FUNC PyInit__c_ext(void) {
|
|
27
|
+
|
|
28
|
+
PyObject* m = NULL;
|
|
29
|
+
|
|
30
|
+
// // Classes
|
|
31
|
+
// if (PyType_Ready(HatanakaReaderType) < 0) {
|
|
32
|
+
// goto end;
|
|
33
|
+
// }
|
|
34
|
+
if (PyType_Ready(KlobucharType) < 0)
|
|
35
|
+
return NULL;
|
|
36
|
+
|
|
37
|
+
m = PyModule_Create(&module);
|
|
38
|
+
if (m == NULL) {
|
|
39
|
+
goto end;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
Py_INCREF(KlobucharType);
|
|
43
|
+
PyModule_AddObject(m, "Klobuchar", (PyObject *)KlobucharType);
|
|
44
|
+
|
|
45
|
+
// Py_INCREF(HatanakaReaderType);
|
|
46
|
+
// PyModule_AddObject(m, "HatanakaReader", (PyObject*)HatanakaReaderType);
|
|
47
|
+
|
|
48
|
+
end:
|
|
49
|
+
return m;
|
|
50
|
+
}
|
|
Binary file
|
pygnss/cl.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Program to perform various columnar operations on inputs
|
|
3
|
+
|
|
4
|
+
All indicators have this format
|
|
5
|
+
|
|
6
|
+
'x0'
|
|
7
|
+
|
|
8
|
+
where 'x' can be one of the following
|
|
9
|
+
|
|
10
|
+
- 'c' - Select column
|
|
11
|
+
- 'd' - diff column relative to the previous value
|
|
12
|
+
- 'f' - diff column relative to the first value of the column
|
|
13
|
+
- 'm' - Compute the minutes elapsed since the first value (divide column by 60,
|
|
14
|
+
as it assumes that the values are in seconds))
|
|
15
|
+
- 'h' - Compute the hours elapsed since the first value (divide column by 3600,
|
|
16
|
+
as it assumes that the values are in seconds))
|
|
17
|
+
|
|
18
|
+
and '0' is the column number (1 based)
|
|
19
|
+
|
|
20
|
+
Examples:
|
|
21
|
+
|
|
22
|
+
(a) Select columns with the indicated order (first output 5th column and then the
|
|
23
|
+
first column)
|
|
24
|
+
cat file.txt | cl c5 c1
|
|
25
|
+
|
|
26
|
+
(b) Select 6th column and output 1st column relative to the first one
|
|
27
|
+
cat file.txt | cl c6 f1
|
|
28
|
+
|
|
29
|
+
(c) Make a diff of the third column relative to the first value
|
|
30
|
+
cat file.txt | cl f3
|
|
31
|
+
"""
|
|
32
|
+
import argparse
|
|
33
|
+
import sys
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ColumnProcess:
|
|
37
|
+
"""
|
|
38
|
+
Class that manages the processing of a set of fields based on some criteria
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(self, colprocstr):
|
|
42
|
+
"""
|
|
43
|
+
Class initialization. This method receives a string defining the type
|
|
44
|
+
of operation to be performed
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
if len(colprocstr) < 2:
|
|
48
|
+
raise ValueError(f"Do not know how to interpret [ {colprocstr} ], "
|
|
49
|
+
"column selector should be of the form 'n0', with "
|
|
50
|
+
"'n' being a character and '0' a column number")
|
|
51
|
+
|
|
52
|
+
self.process_type = colprocstr[0]
|
|
53
|
+
|
|
54
|
+
# Obtain the column number, taking into account that the indices must
|
|
55
|
+
# be translated from 1-based to 0-based
|
|
56
|
+
self.process_column = int(colprocstr[1:]) - 1
|
|
57
|
+
|
|
58
|
+
self.previous_value = None
|
|
59
|
+
self.first_value = None
|
|
60
|
+
|
|
61
|
+
def process(self, fields):
|
|
62
|
+
"""
|
|
63
|
+
Process a set of fields. Raise an exception if
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
if self.process_column >= len(fields):
|
|
67
|
+
raise IndexError(f"Unable to fecth column [ {self.process_column + 1} ] (1-based) "
|
|
68
|
+
f"in line with [ {len(fields)} ] fields. "
|
|
69
|
+
f"Offending line [ {' '.join(fields)} ]\n")
|
|
70
|
+
|
|
71
|
+
column_value = fields[self.process_column]
|
|
72
|
+
|
|
73
|
+
if self.process_type == 'c':
|
|
74
|
+
|
|
75
|
+
return column_value
|
|
76
|
+
|
|
77
|
+
elif self.process_type == 'f' or self.process_type == 'm' or self.process_type == 'h':
|
|
78
|
+
|
|
79
|
+
incoming_value = float(column_value)
|
|
80
|
+
|
|
81
|
+
if self.first_value is None:
|
|
82
|
+
self.first_value = incoming_value
|
|
83
|
+
|
|
84
|
+
value = incoming_value - self.first_value
|
|
85
|
+
|
|
86
|
+
if self.process_type == 'm':
|
|
87
|
+
value = value / 60.0
|
|
88
|
+
elif self.process_type == 'h':
|
|
89
|
+
value = value / 3600.0
|
|
90
|
+
|
|
91
|
+
return str(value)
|
|
92
|
+
|
|
93
|
+
elif self.process_type == 'd':
|
|
94
|
+
|
|
95
|
+
incoming_value = float(column_value)
|
|
96
|
+
|
|
97
|
+
if self.previous_value is None:
|
|
98
|
+
self.previous_value = incoming_value
|
|
99
|
+
|
|
100
|
+
value = incoming_value - self.previous_value
|
|
101
|
+
|
|
102
|
+
# Update internal value only if the process method is the difference
|
|
103
|
+
# relative to the previous value
|
|
104
|
+
if self.process_type == 'd':
|
|
105
|
+
self.previous_value = incoming_value
|
|
106
|
+
|
|
107
|
+
return str(value)
|
|
108
|
+
|
|
109
|
+
else:
|
|
110
|
+
raise ValueError("Do not know what process type is '%c'" % self.process_type)
|
|
111
|
+
|
|
112
|
+
def __str__(self):
|
|
113
|
+
|
|
114
|
+
return self.__repr__()
|
|
115
|
+
|
|
116
|
+
def __repr__(self):
|
|
117
|
+
|
|
118
|
+
return "Process type [ %s ], process column [ %d ]\n" % (self.process_type, self.process_column)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def entry_point():
|
|
122
|
+
|
|
123
|
+
# Process the options of the executable
|
|
124
|
+
|
|
125
|
+
parser = argparse.ArgumentParser(description=__doc__,
|
|
126
|
+
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
127
|
+
|
|
128
|
+
parser.add_argument('columns', metavar='<selector>', type=str, nargs='+',
|
|
129
|
+
help="Set of column selectors and operators")
|
|
130
|
+
|
|
131
|
+
args = parser.parse_args()
|
|
132
|
+
|
|
133
|
+
# Make an array of objects that will take care of processing the fields
|
|
134
|
+
colprocs = [ColumnProcess(colproc) for colproc in args.columns]
|
|
135
|
+
|
|
136
|
+
for line in sys.stdin:
|
|
137
|
+
|
|
138
|
+
# If line is empty, print an empty line
|
|
139
|
+
if len(line.strip()) == 0:
|
|
140
|
+
sys.stdout.write("\n")
|
|
141
|
+
continue
|
|
142
|
+
|
|
143
|
+
fields = line.strip().split()
|
|
144
|
+
|
|
145
|
+
# Process each column
|
|
146
|
+
newfields = [cp.process(fields) for cp in colprocs]
|
|
147
|
+
|
|
148
|
+
sys.stdout.write(" ".join(newfields) + "\n")
|
pygnss/constants.py
ADDED