cloudnetpy 1.49.9__py3-none-any.whl → 1.50.0__py3-none-any.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.
- cloudnetpy/instruments/campbell_scientific.py +110 -0
- cloudnetpy/instruments/ceilo.py +12 -2
- cloudnetpy/instruments/instruments.py +8 -0
- cloudnetpy/version.py +2 -2
- {cloudnetpy-1.49.9.dist-info → cloudnetpy-1.50.0.dist-info}/METADATA +1 -1
- {cloudnetpy-1.49.9.dist-info → cloudnetpy-1.50.0.dist-info}/RECORD +9 -8
- {cloudnetpy-1.49.9.dist-info → cloudnetpy-1.50.0.dist-info}/LICENSE +0 -0
- {cloudnetpy-1.49.9.dist-info → cloudnetpy-1.50.0.dist-info}/WHEEL +0 -0
- {cloudnetpy-1.49.9.dist-info → cloudnetpy-1.50.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,110 @@
|
|
1
|
+
import logging
|
2
|
+
import re
|
3
|
+
from datetime import datetime
|
4
|
+
|
5
|
+
import numpy as np
|
6
|
+
|
7
|
+
from cloudnetpy import utils
|
8
|
+
from cloudnetpy.exceptions import ValidTimeStampError
|
9
|
+
from cloudnetpy.instruments import instruments
|
10
|
+
from cloudnetpy.instruments.ceilometer import Ceilometer
|
11
|
+
|
12
|
+
|
13
|
+
class Cs135(Ceilometer):
|
14
|
+
def __init__(
|
15
|
+
self, full_path: str, site_meta: dict, expected_date: str | None = None
|
16
|
+
):
|
17
|
+
super().__init__()
|
18
|
+
self.full_path = full_path
|
19
|
+
self.site_meta = site_meta
|
20
|
+
self.expected_date = expected_date
|
21
|
+
self.data = {}
|
22
|
+
self.metadata = {}
|
23
|
+
self.instrument = instruments.CS135
|
24
|
+
|
25
|
+
def read_ceilometer_file(self, calibration_factor: float | None = None) -> None:
|
26
|
+
with open(self.full_path, mode="r", encoding="utf-8") as f:
|
27
|
+
lines = f.readlines()
|
28
|
+
timestamps, profiles, scales, tilt_angles = [], [], [], []
|
29
|
+
range_resolution, n_gates = 0, 0
|
30
|
+
for i, line in enumerate(lines):
|
31
|
+
line_splat = line.strip().split(",")
|
32
|
+
if is_timestamp(line_splat[0]):
|
33
|
+
timestamp = datetime.strptime(line_splat[0], "%Y-%m-%dT%H:%M:%S.%f")
|
34
|
+
try:
|
35
|
+
self._check_timestamp(timestamp)
|
36
|
+
except ValidTimeStampError:
|
37
|
+
continue
|
38
|
+
timestamps.append(timestamp)
|
39
|
+
_line1 = line_splat[1]
|
40
|
+
if len(_line1) != 11:
|
41
|
+
raise NotImplementedError("Unknown message number")
|
42
|
+
if (msg_no := _line1[-4:-1]) != "002":
|
43
|
+
raise NotImplementedError(
|
44
|
+
f"Message number {msg_no} not implemented"
|
45
|
+
)
|
46
|
+
_line3 = lines[i + 2].strip().split(" ")
|
47
|
+
scale, range_resolution, n_gates, tilt_angle = [
|
48
|
+
int(_line3[ind]) for ind in [0, 1, 2, 5]
|
49
|
+
]
|
50
|
+
scales.append(scale)
|
51
|
+
tilt_angles.append(tilt_angle)
|
52
|
+
_line4 = lines[i + 3].strip()
|
53
|
+
profiles.append(_hex2backscatter(_line4, n_gates))
|
54
|
+
|
55
|
+
if len(timestamps) == 0:
|
56
|
+
raise ValidTimeStampError("No valid timestamps found in the file")
|
57
|
+
array = self._handle_large_values(np.array(profiles))
|
58
|
+
self.data["beta_raw"] = _scale_backscatter(array, scales)
|
59
|
+
if calibration_factor is None:
|
60
|
+
calibration_factor = 1.0
|
61
|
+
self.data["beta_raw"] *= calibration_factor
|
62
|
+
self.data["calibration_factor"] = calibration_factor
|
63
|
+
self.data["range"] = (
|
64
|
+
np.arange(n_gates) * range_resolution + range_resolution / 2
|
65
|
+
)
|
66
|
+
self.data["time"] = utils.datetime2decimal_hours(timestamps)
|
67
|
+
self.data["zenith_angle"] = np.median(tilt_angles)
|
68
|
+
|
69
|
+
def _check_timestamp(self, timestamp: datetime):
|
70
|
+
timestamp_components = str(timestamp.date()).split("-")
|
71
|
+
if self.expected_date is not None:
|
72
|
+
if timestamp_components != self.expected_date.split("-"):
|
73
|
+
raise ValidTimeStampError
|
74
|
+
if not self.date:
|
75
|
+
self.date = timestamp_components
|
76
|
+
assert timestamp_components == self.date
|
77
|
+
|
78
|
+
@staticmethod
|
79
|
+
def _handle_large_values(array: np.ndarray) -> np.ndarray:
|
80
|
+
ind = np.where(array > 524287)
|
81
|
+
if ind[0].size > 0:
|
82
|
+
array[ind] -= 1048576
|
83
|
+
return array
|
84
|
+
|
85
|
+
|
86
|
+
def is_timestamp(timestamp: str) -> bool:
|
87
|
+
"""Tests if the input string is formatted as -yyyy-mm-dd hh:mm:ss"""
|
88
|
+
reg_exp = re.compile(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{6}")
|
89
|
+
if reg_exp.match(timestamp) is not None:
|
90
|
+
return True
|
91
|
+
return False
|
92
|
+
|
93
|
+
|
94
|
+
def _hex2backscatter(data: str, n_gates: int):
|
95
|
+
"""Converts hex string to backscatter values."""
|
96
|
+
n_chars = 5
|
97
|
+
return [
|
98
|
+
int(data[i : i + n_chars], 16) for i in range(0, n_gates * n_chars, n_chars)
|
99
|
+
]
|
100
|
+
|
101
|
+
|
102
|
+
def _scale_backscatter(data: np.ndarray, scales: list) -> np.ndarray:
|
103
|
+
"""Scales backscatter values."""
|
104
|
+
unit_conversion_factor = 1e-8
|
105
|
+
scales_array = np.array(scales)
|
106
|
+
ind = np.where(scales_array != 100)
|
107
|
+
if ind[0].size > 0:
|
108
|
+
logging.info(f"{ind[0].size} profiles have not 100% scaling")
|
109
|
+
data[ind, :] *= scales_array[ind] / 100
|
110
|
+
return data * unit_conversion_factor
|
cloudnetpy/instruments/ceilo.py
CHANGED
@@ -4,6 +4,7 @@ from itertools import islice
|
|
4
4
|
import netCDF4
|
5
5
|
|
6
6
|
from cloudnetpy import output
|
7
|
+
from cloudnetpy.instruments.campbell_scientific import Cs135
|
7
8
|
from cloudnetpy.instruments.cl61d import Cl61d
|
8
9
|
from cloudnetpy.instruments.lufft import LufftCeilo
|
9
10
|
from cloudnetpy.instruments.vaisala import ClCeilo, Ct25k
|
@@ -91,9 +92,16 @@ def ceilo2nc(
|
|
91
92
|
|
92
93
|
def _initialize_ceilo(
|
93
94
|
full_path: str, site_meta: dict, date: str | None = None
|
94
|
-
) -> ClCeilo | Ct25k | LufftCeilo | Cl61d:
|
95
|
+
) -> ClCeilo | Ct25k | LufftCeilo | Cl61d | Cs135:
|
95
96
|
if "model" in site_meta:
|
96
|
-
if site_meta["model"] not in (
|
97
|
+
if site_meta["model"] not in (
|
98
|
+
"cl31",
|
99
|
+
"cl51",
|
100
|
+
"cl61d",
|
101
|
+
"ct25k",
|
102
|
+
"chm15k",
|
103
|
+
"cs135",
|
104
|
+
):
|
97
105
|
raise ValueError(f"Invalid ceilometer model: {site_meta['model']}")
|
98
106
|
if site_meta["model"] in ("cl31", "cl51"):
|
99
107
|
model = "cl31_or_cl51"
|
@@ -107,6 +115,8 @@ def _initialize_ceilo(
|
|
107
115
|
return Ct25k(full_path, site_meta, date)
|
108
116
|
if model == "cl61d":
|
109
117
|
return Cl61d(full_path, site_meta, date)
|
118
|
+
if model == "cs135":
|
119
|
+
return Cs135(full_path, site_meta, date)
|
110
120
|
return LufftCeilo(full_path, site_meta, date)
|
111
121
|
|
112
122
|
|
@@ -35,6 +35,14 @@ CL31 = Instrument(
|
|
35
35
|
wavelength=910.0,
|
36
36
|
)
|
37
37
|
|
38
|
+
CS135 = Instrument(
|
39
|
+
manufacturer="Campbell Scientific",
|
40
|
+
domain="lidar",
|
41
|
+
category="ceilometer",
|
42
|
+
model="CS135",
|
43
|
+
wavelength=905.0,
|
44
|
+
)
|
45
|
+
|
38
46
|
CT25K = Instrument(
|
39
47
|
manufacturer="Vaisala",
|
40
48
|
domain="lidar",
|
cloudnetpy/version.py
CHANGED
@@ -8,7 +8,7 @@ cloudnetpy/metadata.py,sha256=-oRmr4HWjG_-P8jOjdBYMgRkOYnJKr6jmGIF-38Tno8,5023
|
|
8
8
|
cloudnetpy/output.py,sha256=6ysoCcrk_pS_fWhyQoJX29V403f7UOloFw0SZjHCwKk,14236
|
9
9
|
cloudnetpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
10
|
cloudnetpy/utils.py,sha256=lKVPF7VpbX2IPXTztI8eJZm8rLne9IuhMngQXKP12vg,26971
|
11
|
-
cloudnetpy/version.py,sha256=
|
11
|
+
cloudnetpy/version.py,sha256=f1gG8ukA8980EWUjCKF9qA8P9pYzoCtL8gGNb2eCEX0,72
|
12
12
|
cloudnetpy/categorize/__init__.py,sha256=gP5q3Vis1y9u9OWgA_idlbjfWXYN_S0IBSWdwBhL_uU,69
|
13
13
|
cloudnetpy/categorize/atmos.py,sha256=_8VU0UpzKh7ZFh3TbGs-g3SYMRsRIR5mio0PmP66O7o,12372
|
14
14
|
cloudnetpy/categorize/atmos_utils.py,sha256=6WdfGqzOvnaDW7vlMMrZBJIxW_eHQdjH-Xl_iPv1TTI,3716
|
@@ -26,14 +26,15 @@ cloudnetpy/categorize/mwr.py,sha256=PSXf-OukhRLlQIpXtkKhcdgiy-fQy-X-CaVh_G42P9s,
|
|
26
26
|
cloudnetpy/categorize/radar.py,sha256=3Or3_jWxs9rbwJ3XKzl4hPilg0bFdCRMrbkIAuS3H08,12425
|
27
27
|
cloudnetpy/instruments/__init__.py,sha256=-MOKjKNu8PRciX_PXEBRihGVaKrPIc_2sR-n0D9NEkc,374
|
28
28
|
cloudnetpy/instruments/basta.py,sha256=9KeP65uxOTUH1YavaI8sp35n-VcM-WgtqMfB3bxKSPo,3714
|
29
|
-
cloudnetpy/instruments/
|
29
|
+
cloudnetpy/instruments/campbell_scientific.py,sha256=HQMPLBMHUojNFnt4WV8cvGrNPfSvv_IUQVeVyC3KJyo,4237
|
30
|
+
cloudnetpy/instruments/ceilo.py,sha256=_T_rbvy0kB6hol5iwNLtfFqYDM7g4ce9Mlt_Z1Vg2hU,8013
|
30
31
|
cloudnetpy/instruments/ceilometer.py,sha256=j3Wb2wJlSGLaZkguPe3nv4751TfGd-hJjithKYNOsO4,10498
|
31
32
|
cloudnetpy/instruments/cl61d.py,sha256=Qk2YQRrRkivc6nW2gOI7KKLt9rR4JAWF0bfbj8Hd_lY,1653
|
32
33
|
cloudnetpy/instruments/cloudnet_instrument.py,sha256=nnaOtJJotXzYdoMoYGKeLZ1MciDYBx7tqVBFKBQjKL0,3304
|
33
34
|
cloudnetpy/instruments/copernicus.py,sha256=FDS7Rsunp4ieTPFh_T_LXvreNi5_HTv4ZzR3OnTcAX8,5013
|
34
35
|
cloudnetpy/instruments/galileo.py,sha256=F_XyoAb9PR-ifGhqhXziKnv0KfyOh-yEBaE1NgRMzNg,4318
|
35
36
|
cloudnetpy/instruments/hatpro.py,sha256=H0FgTIxIp3fl59nrTS9z8NyRX7ugkR32B2N1uwEOWtQ,7438
|
36
|
-
cloudnetpy/instruments/instruments.py,sha256=
|
37
|
+
cloudnetpy/instruments/instruments.py,sha256=x_YV4UG05lF-39Yr-K3h5OrxSqrsndpF1K-KM0VURI0,2975
|
37
38
|
cloudnetpy/instruments/lufft.py,sha256=E2qzvya206gNiML7BSM6vm1lzeOMBePrIuPT81NHPvw,3397
|
38
39
|
cloudnetpy/instruments/mira.py,sha256=5wmmJGYHVglxaCpSuL92WIisADRD-85k_jlsC9mKLgQ,5063
|
39
40
|
cloudnetpy/instruments/nc_lidar.py,sha256=9FSsInEM8fdyyhgNXb2INBjb5OEGFE5ZaVSowHjzoCA,1386
|
@@ -105,8 +106,8 @@ cloudnetpy/products/mwr_multi.py,sha256=9dw5DqU9uae54SDk0Pjzp4EKtQrjo1DeP-Xx41NE
|
|
105
106
|
cloudnetpy/products/mwr_single.py,sha256=tfUYvkVf_Hh1GcpBnjjE8T30EYzyYc07UuzGJCBME-8,2931
|
106
107
|
cloudnetpy/products/product_tools.py,sha256=Gk5e4N1m071IIFT9dy9lUvcDICsMYx-pMEtcWTJ54nw,9739
|
107
108
|
docs/source/conf.py,sha256=baQlgkkUGJi4952W6NRhLkIBbRtwFgqrIOBuEeSCLfk,1488
|
108
|
-
cloudnetpy-1.
|
109
|
-
cloudnetpy-1.
|
110
|
-
cloudnetpy-1.
|
111
|
-
cloudnetpy-1.
|
112
|
-
cloudnetpy-1.
|
109
|
+
cloudnetpy-1.50.0.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
|
110
|
+
cloudnetpy-1.50.0.dist-info/METADATA,sha256=mzFxNLsFFEPSD86rJ6RmgYmHdx9OA8zYONk3n_SL3h8,5759
|
111
|
+
cloudnetpy-1.50.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
112
|
+
cloudnetpy-1.50.0.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
|
113
|
+
cloudnetpy-1.50.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|