cloudnetpy 1.71.2__tar.gz → 1.71.4__tar.gz
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-1.71.2/cloudnetpy.egg-info → cloudnetpy-1.71.4}/PKG-INFO +6 -7
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/ceilo.py +1 -2
- cloudnetpy-1.71.4/cloudnetpy/instruments/vaisala.py +130 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/output.py +3 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/products/ier.py +2 -2
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/products/iwc.py +2 -2
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/version.py +1 -1
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4/cloudnetpy.egg-info}/PKG-INFO +6 -7
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy.egg-info/SOURCES.txt +0 -1
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy.egg-info/requires.txt +1 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/pyproject.toml +3 -5
- cloudnetpy-1.71.2/cloudnetpy/instruments/campbell_scientific.py +0 -152
- cloudnetpy-1.71.2/cloudnetpy/instruments/vaisala.py +0 -405
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/LICENSE +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/MANIFEST.in +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/README.md +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/atmos_utils.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/attenuation.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/attenuations/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/attenuations/gas_attenuation.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/attenuations/liquid_attenuation.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/attenuations/melting_attenuation.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/attenuations/rain_attenuation.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/categorize.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/classify.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/containers.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/disdrometer.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/droplet.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/falling.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/freezing.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/insects.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/itu.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/lidar.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/melting.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/model.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/mwr.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/categorize/radar.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/cli.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/cloudnetarray.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/concat_lib.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/constants.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/datasource.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/exceptions.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/basta.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/ceilometer.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/cl61d.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/cloudnet_instrument.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/copernicus.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/disdrometer/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/disdrometer/common.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/disdrometer/parsivel.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/disdrometer/thies.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/galileo.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/hatpro.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/instruments.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/lufft.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/mira.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/mrr.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/nc_lidar.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/nc_radar.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/pollyxt.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/radiometrics.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/rain_e_h3.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/rpg.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/rpg_reader.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/toa5.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/instruments/weather_station.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/metadata.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/file_handler.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/metadata.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/model_metadata.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/plotting/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/plotting/plot_meta.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/plotting/plot_tools.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/plotting/plotting.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/products/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/products/advance_methods.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/products/grid_methods.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/products/model_products.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/products/observation_products.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/products/product_resampling.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/products/tools.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/statistics/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/statistics/statistical_methods.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/e2e/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/e2e/conftest.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/e2e/process_cf/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/e2e/process_cf/main.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/e2e/process_cf/tests.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/main.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/tests.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/main.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/tests.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/unit/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/unit/conftest.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/unit/test_advance_methods.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/unit/test_grid_methods.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/unit/test_model_products.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/unit/test_observation_products.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/unit/test_plot_tools.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/unit/test_plotting.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/tests/unit/test_tools.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/model_evaluation/utils.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/plotting/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/plotting/plot_meta.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/plotting/plotting.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/products/__init__.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/products/classification.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/products/der.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/products/drizzle.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/products/drizzle_error.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/products/drizzle_tools.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/products/epsilon.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/products/lwc.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/products/mie_lu_tables.nc +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/products/mwr_tools.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/products/product_tools.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/py.typed +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy/utils.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy.egg-info/dependency_links.txt +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy.egg-info/entry_points.txt +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/cloudnetpy.egg-info/top_level.txt +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/docs/source/conf.py +0 -0
- {cloudnetpy-1.71.2 → cloudnetpy-1.71.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: cloudnetpy
|
3
|
-
Version: 1.71.
|
3
|
+
Version: 1.71.4
|
4
4
|
Summary: Python package for Cloudnet processing
|
5
5
|
Author: Simo Tukiainen
|
6
6
|
License: MIT License
|
@@ -33,14 +33,12 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
33
33
|
Classifier: Intended Audience :: Science/Research
|
34
34
|
Classifier: License :: OSI Approved :: MIT License
|
35
35
|
Classifier: Operating System :: OS Independent
|
36
|
-
Classifier: Programming Language :: Python :: 3
|
37
|
-
Classifier:
|
38
|
-
Classifier: Programming Language :: Python :: 3.12
|
39
|
-
Classifier: Programming Language :: Python :: 3.13
|
40
|
-
Classifier: Topic :: Scientific/Engineering
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
37
|
+
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
|
41
38
|
Requires-Python: >=3.10
|
42
39
|
Description-Content-Type: text/markdown
|
43
40
|
License-File: LICENSE
|
41
|
+
Requires-Dist: ceilopyter
|
44
42
|
Requires-Dist: cloudnetpy_qc>=1.15.0
|
45
43
|
Requires-Dist: doppy>=0.5.0
|
46
44
|
Requires-Dist: matplotlib
|
@@ -61,6 +59,7 @@ Requires-Dist: pre-commit; extra == "dev"
|
|
61
59
|
Requires-Dist: release-version; extra == "dev"
|
62
60
|
Provides-Extra: extras
|
63
61
|
Requires-Dist: voodoonet>=0.1.7; extra == "extras"
|
62
|
+
Dynamic: license-file
|
64
63
|
|
65
64
|
# CloudnetPy
|
66
65
|
|
@@ -6,10 +6,9 @@ import netCDF4
|
|
6
6
|
from numpy import ma
|
7
7
|
|
8
8
|
from cloudnetpy import output, utils
|
9
|
-
from cloudnetpy.instruments.campbell_scientific import Cs135
|
10
9
|
from cloudnetpy.instruments.cl61d import Cl61d
|
11
10
|
from cloudnetpy.instruments.lufft import LufftCeilo
|
12
|
-
from cloudnetpy.instruments.vaisala import ClCeilo, Ct25k
|
11
|
+
from cloudnetpy.instruments.vaisala import ClCeilo, Cs135, Ct25k
|
13
12
|
from cloudnetpy.metadata import MetaData
|
14
13
|
|
15
14
|
|
@@ -0,0 +1,130 @@
|
|
1
|
+
"""Module with classes for Vaisala ceilometers."""
|
2
|
+
|
3
|
+
import datetime
|
4
|
+
from collections.abc import Callable
|
5
|
+
|
6
|
+
import ceilopyter.version
|
7
|
+
import numpy as np
|
8
|
+
import numpy.typing as npt
|
9
|
+
from ceilopyter import read_cl_file, read_cs_file, read_ct_file
|
10
|
+
|
11
|
+
from cloudnetpy.exceptions import ValidTimeStampError
|
12
|
+
from cloudnetpy.instruments import instruments
|
13
|
+
from cloudnetpy.instruments.ceilometer import Ceilometer, NoiseParam
|
14
|
+
|
15
|
+
|
16
|
+
class VaisalaCeilo(Ceilometer):
|
17
|
+
"""Base class for Vaisala ceilometers."""
|
18
|
+
|
19
|
+
def __init__(
|
20
|
+
self,
|
21
|
+
reader: Callable,
|
22
|
+
full_path: str,
|
23
|
+
site_meta: dict,
|
24
|
+
expected_date: str | None = None,
|
25
|
+
):
|
26
|
+
super().__init__(self.noise_param)
|
27
|
+
self.reader = reader
|
28
|
+
self.full_path = full_path
|
29
|
+
self.site_meta = site_meta
|
30
|
+
self.expected_date = expected_date
|
31
|
+
self.sane_date = (
|
32
|
+
datetime.date.fromisoformat(self.expected_date)
|
33
|
+
if self.expected_date
|
34
|
+
else None
|
35
|
+
)
|
36
|
+
self.software = {"ceilopyter": ceilopyter.version.__version__}
|
37
|
+
|
38
|
+
def read_ceilometer_file(self, calibration_factor: float | None = None) -> None:
|
39
|
+
"""Read all lines of data from the file."""
|
40
|
+
time, data = self.reader(self.full_path)
|
41
|
+
range_res = data[0].range_resolution
|
42
|
+
n_gates = len(data[0].beta)
|
43
|
+
self.data["time"] = np.array(time)
|
44
|
+
self.data["range"] = np.arange(n_gates) * range_res + range_res / 2
|
45
|
+
self.data["beta_raw"] = np.stack([d.beta for d in data])
|
46
|
+
self.data["calibration_factor"] = calibration_factor or 1.0
|
47
|
+
self.data["beta_raw"] *= self.data["calibration_factor"]
|
48
|
+
self.data["zenith_angle"] = np.median([d.tilt_angle for d in data])
|
49
|
+
self._sort_time()
|
50
|
+
self._screen_date()
|
51
|
+
self._convert_to_fraction_hour()
|
52
|
+
self._store_ceilometer_info()
|
53
|
+
|
54
|
+
def _sort_time(self):
|
55
|
+
"""Sorts timestamps and removes duplicates."""
|
56
|
+
time = self.data["time"]
|
57
|
+
_time, ind = np.unique(time, return_index=True)
|
58
|
+
self._screen_time_indices(ind)
|
59
|
+
|
60
|
+
def _screen_date(self):
|
61
|
+
time = self.data["time"]
|
62
|
+
if self.sane_date is None:
|
63
|
+
self.sane_date = time[0].date()
|
64
|
+
self.expected_date = self.sane_date.isoformat()
|
65
|
+
is_valid = np.array([t.date() == self.sane_date for t in time])
|
66
|
+
self._screen_time_indices(is_valid)
|
67
|
+
|
68
|
+
def _screen_time_indices(
|
69
|
+
self, valid_indices: npt.NDArray[np.intp] | npt.NDArray[np.bool]
|
70
|
+
):
|
71
|
+
time = self.data["time"]
|
72
|
+
n_time = len(time)
|
73
|
+
if len(valid_indices) == 0 or (
|
74
|
+
valid_indices.dtype == np.bool and not np.any(valid_indices)
|
75
|
+
):
|
76
|
+
msg = "All timestamps screened"
|
77
|
+
raise ValidTimeStampError(msg)
|
78
|
+
for key, array in self.data.items():
|
79
|
+
if hasattr(array, "shape") and array.shape[:1] == (n_time,):
|
80
|
+
self.data[key] = self.data[key][valid_indices]
|
81
|
+
|
82
|
+
def _convert_to_fraction_hour(self):
|
83
|
+
time = self.data["time"]
|
84
|
+
midnight = time[0].replace(hour=0, minute=0, second=0, microsecond=0)
|
85
|
+
hour = datetime.timedelta(hours=1)
|
86
|
+
self.data["time"] = (time - midnight) / hour
|
87
|
+
self.date = self.expected_date.split("-") # type: ignore[union-attr]
|
88
|
+
|
89
|
+
def _store_ceilometer_info(self):
|
90
|
+
raise NotImplementedError
|
91
|
+
|
92
|
+
|
93
|
+
class ClCeilo(VaisalaCeilo):
|
94
|
+
"""Class for Vaisala CL31/CL51 ceilometers."""
|
95
|
+
|
96
|
+
noise_param = NoiseParam(noise_min=3.1e-8, noise_smooth_min=1.1e-8)
|
97
|
+
|
98
|
+
def __init__(self, full_path, site_meta, expected_date=None):
|
99
|
+
super().__init__(read_cl_file, full_path, site_meta, expected_date)
|
100
|
+
|
101
|
+
def _store_ceilometer_info(self):
|
102
|
+
n_gates = self.data["beta_raw"].shape[1]
|
103
|
+
if n_gates < 1540:
|
104
|
+
self.instrument = instruments.CL31
|
105
|
+
else:
|
106
|
+
self.instrument = instruments.CL51
|
107
|
+
|
108
|
+
|
109
|
+
class Ct25k(VaisalaCeilo):
|
110
|
+
"""Class for Vaisala CT25k ceilometer."""
|
111
|
+
|
112
|
+
noise_param = NoiseParam(noise_min=0.7e-7, noise_smooth_min=1.2e-8)
|
113
|
+
|
114
|
+
def __init__(self, full_path, site_meta, expected_date=None):
|
115
|
+
super().__init__(read_ct_file, full_path, site_meta, expected_date)
|
116
|
+
|
117
|
+
def _store_ceilometer_info(self):
|
118
|
+
self.instrument = instruments.CT25K
|
119
|
+
|
120
|
+
|
121
|
+
class Cs135(VaisalaCeilo):
|
122
|
+
"""Class for Campbell Scientific CS135 ceilometer."""
|
123
|
+
|
124
|
+
noise_param = NoiseParam()
|
125
|
+
|
126
|
+
def __init__(self, full_path, site_meta, expected_date=None):
|
127
|
+
super().__init__(read_cs_file, full_path, site_meta, expected_date)
|
128
|
+
|
129
|
+
def _store_ceilometer_info(self):
|
130
|
+
self.instrument = instruments.CS135
|
@@ -44,6 +44,9 @@ def save_level1b(
|
|
44
44
|
nc.source = get_l1b_source(obj.instrument)
|
45
45
|
if hasattr(obj, "serial_number") and obj.serial_number is not None:
|
46
46
|
nc.serial_number = obj.serial_number
|
47
|
+
if hasattr(obj, "software"):
|
48
|
+
for software, version in obj.software.items():
|
49
|
+
nc.setncattr(f"{software}_version", version)
|
47
50
|
nc.references = get_references()
|
48
51
|
return file_uuid
|
49
52
|
|
@@ -83,9 +83,9 @@ class IerSource(IceSource):
|
|
83
83
|
|
84
84
|
|
85
85
|
def _add_ier_comment(attributes: dict, ier: IerSource) -> dict:
|
86
|
-
freq = ier.radar_frequency
|
86
|
+
freq = round(ier.radar_frequency, 3)
|
87
87
|
coeffs = ier.coefficients
|
88
|
-
factor =
|
88
|
+
factor = round(coeffs[0] / 0.93, 3)
|
89
89
|
attributes["ier"] = attributes["ier"]._replace(
|
90
90
|
comment=f"This variable was calculated from the {freq}-GHz radar\n"
|
91
91
|
f"reflectivity factor after correction for gaseous attenuation,\n"
|
@@ -113,9 +113,9 @@ def _add_iwc_error_comment(attributes: dict, lwp_prior, uncertainty: float) -> d
|
|
113
113
|
|
114
114
|
|
115
115
|
def _add_iwc_comment(attributes: dict, iwc: IwcSource) -> dict:
|
116
|
-
freq = iwc.radar_frequency
|
116
|
+
freq = round(iwc.radar_frequency, 3)
|
117
117
|
coeffs = iwc.coefficients
|
118
|
-
factor = round(
|
118
|
+
factor = round(coeffs[0] / 0.93, 3)
|
119
119
|
attributes["iwc"] = attributes["iwc"]._replace(
|
120
120
|
comment=f"This variable was calculated from the {freq}-GHz radar reflectivity\n"
|
121
121
|
"factor after correction for gaseous attenuation, and temperature taken from\n"
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: cloudnetpy
|
3
|
-
Version: 1.71.
|
3
|
+
Version: 1.71.4
|
4
4
|
Summary: Python package for Cloudnet processing
|
5
5
|
Author: Simo Tukiainen
|
6
6
|
License: MIT License
|
@@ -33,14 +33,12 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
33
33
|
Classifier: Intended Audience :: Science/Research
|
34
34
|
Classifier: License :: OSI Approved :: MIT License
|
35
35
|
Classifier: Operating System :: OS Independent
|
36
|
-
Classifier: Programming Language :: Python :: 3
|
37
|
-
Classifier:
|
38
|
-
Classifier: Programming Language :: Python :: 3.12
|
39
|
-
Classifier: Programming Language :: Python :: 3.13
|
40
|
-
Classifier: Topic :: Scientific/Engineering
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
37
|
+
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
|
41
38
|
Requires-Python: >=3.10
|
42
39
|
Description-Content-Type: text/markdown
|
43
40
|
License-File: LICENSE
|
41
|
+
Requires-Dist: ceilopyter
|
44
42
|
Requires-Dist: cloudnetpy_qc>=1.15.0
|
45
43
|
Requires-Dist: doppy>=0.5.0
|
46
44
|
Requires-Dist: matplotlib
|
@@ -61,6 +59,7 @@ Requires-Dist: pre-commit; extra == "dev"
|
|
61
59
|
Requires-Dist: release-version; extra == "dev"
|
62
60
|
Provides-Extra: extras
|
63
61
|
Requires-Dist: voodoonet>=0.1.7; extra == "extras"
|
62
|
+
Dynamic: license-file
|
64
63
|
|
65
64
|
# CloudnetPy
|
66
65
|
|
@@ -44,7 +44,6 @@ cloudnetpy/categorize/attenuations/melting_attenuation.py
|
|
44
44
|
cloudnetpy/categorize/attenuations/rain_attenuation.py
|
45
45
|
cloudnetpy/instruments/__init__.py
|
46
46
|
cloudnetpy/instruments/basta.py
|
47
|
-
cloudnetpy/instruments/campbell_scientific.py
|
48
47
|
cloudnetpy/instruments/ceilo.py
|
49
48
|
cloudnetpy/instruments/ceilometer.py
|
50
49
|
cloudnetpy/instruments/cl61d.py
|
@@ -10,13 +10,11 @@ classifiers = [
|
|
10
10
|
"Intended Audience :: Science/Research",
|
11
11
|
"License :: OSI Approved :: MIT License",
|
12
12
|
"Operating System :: OS Independent",
|
13
|
-
"Programming Language :: Python :: 3
|
14
|
-
"
|
15
|
-
"Programming Language :: Python :: 3.12",
|
16
|
-
"Programming Language :: Python :: 3.13",
|
17
|
-
"Topic :: Scientific/Engineering",
|
13
|
+
"Programming Language :: Python :: 3",
|
14
|
+
"Topic :: Scientific/Engineering :: Atmospheric Science",
|
18
15
|
]
|
19
16
|
dependencies = [
|
17
|
+
"ceilopyter",
|
20
18
|
"cloudnetpy_qc>=1.15.0",
|
21
19
|
"doppy>=0.5.0",
|
22
20
|
"matplotlib",
|
@@ -1,152 +0,0 @@
|
|
1
|
-
import binascii
|
2
|
-
import re
|
3
|
-
from datetime import datetime, timezone
|
4
|
-
from typing import NamedTuple
|
5
|
-
|
6
|
-
import numpy as np
|
7
|
-
|
8
|
-
from cloudnetpy import utils
|
9
|
-
from cloudnetpy.exceptions import InconsistentDataError, ValidTimeStampError
|
10
|
-
from cloudnetpy.instruments import instruments
|
11
|
-
from cloudnetpy.instruments.ceilometer import Ceilometer
|
12
|
-
|
13
|
-
|
14
|
-
class Cs135(Ceilometer):
|
15
|
-
def __init__(
|
16
|
-
self,
|
17
|
-
full_path: str,
|
18
|
-
site_meta: dict,
|
19
|
-
expected_date: str | None = None,
|
20
|
-
):
|
21
|
-
super().__init__()
|
22
|
-
self.full_path = full_path
|
23
|
-
self.site_meta = site_meta
|
24
|
-
self.expected_date = expected_date
|
25
|
-
self.data = {}
|
26
|
-
self.metadata = {}
|
27
|
-
self.instrument = instruments.CS135
|
28
|
-
|
29
|
-
def read_ceilometer_file(self, calibration_factor: float | None = None) -> None:
|
30
|
-
with open(self.full_path, mode="rb") as f:
|
31
|
-
content = f.read()
|
32
|
-
timestamps = []
|
33
|
-
profiles = []
|
34
|
-
tilt_angles = []
|
35
|
-
range_resolutions = []
|
36
|
-
|
37
|
-
parts = re.split(rb"(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{6}),", content)
|
38
|
-
for i in range(1, len(parts), 2):
|
39
|
-
timestamp = datetime.strptime(
|
40
|
-
parts[i].decode(),
|
41
|
-
"%Y-%m-%dT%H:%M:%S.%f",
|
42
|
-
).replace(tzinfo=timezone.utc)
|
43
|
-
try:
|
44
|
-
self._check_timestamp(timestamp)
|
45
|
-
except ValidTimeStampError:
|
46
|
-
continue
|
47
|
-
try:
|
48
|
-
message = _read_message(parts[i + 1])
|
49
|
-
except InvalidMessageError:
|
50
|
-
continue
|
51
|
-
profile = (message.data[:-2] * 1e-8) * (message.scale / 100)
|
52
|
-
timestamps.append(timestamp)
|
53
|
-
profiles.append(profile)
|
54
|
-
tilt_angles.append(message.tilt_angle)
|
55
|
-
range_resolutions.append(message.range_resolution)
|
56
|
-
|
57
|
-
if len(timestamps) == 0:
|
58
|
-
msg = "No valid timestamps found in the file"
|
59
|
-
raise ValidTimeStampError(msg)
|
60
|
-
range_resolution = range_resolutions[0]
|
61
|
-
n_gates = len(profiles[0])
|
62
|
-
if any(res != range_resolution for res in range_resolutions):
|
63
|
-
msg = "Inconsistent range resolution"
|
64
|
-
raise InconsistentDataError(msg)
|
65
|
-
if any(len(profile) != n_gates for profile in profiles):
|
66
|
-
msg = "Inconsistent number of gates"
|
67
|
-
raise InconsistentDataError(msg)
|
68
|
-
|
69
|
-
self.data["beta_raw"] = np.array(profiles)
|
70
|
-
if calibration_factor is None:
|
71
|
-
calibration_factor = 1.0
|
72
|
-
self.data["beta_raw"] *= calibration_factor
|
73
|
-
self.data["calibration_factor"] = calibration_factor
|
74
|
-
self.data["range"] = (
|
75
|
-
np.arange(n_gates) * range_resolution + range_resolution / 2
|
76
|
-
)
|
77
|
-
self.data["time"] = utils.datetime2decimal_hours(timestamps)
|
78
|
-
self.data["zenith_angle"] = np.median(tilt_angles)
|
79
|
-
|
80
|
-
def _check_timestamp(self, timestamp: datetime) -> None:
|
81
|
-
timestamp_components = str(timestamp.date()).split("-")
|
82
|
-
if (
|
83
|
-
self.expected_date is not None
|
84
|
-
and timestamp_components != self.expected_date.split("-")
|
85
|
-
):
|
86
|
-
raise ValidTimeStampError
|
87
|
-
if not self.date:
|
88
|
-
self.date = timestamp_components
|
89
|
-
if timestamp_components != self.date:
|
90
|
-
msg = "Inconsistent dates in the file"
|
91
|
-
raise RuntimeError(msg)
|
92
|
-
|
93
|
-
|
94
|
-
class Message(NamedTuple):
|
95
|
-
scale: int
|
96
|
-
range_resolution: int
|
97
|
-
laser_pulse_energy: int
|
98
|
-
laser_temperature: int
|
99
|
-
tilt_angle: int
|
100
|
-
background_light: int
|
101
|
-
pulse_quantity: int
|
102
|
-
sample_rate: int
|
103
|
-
data: np.ndarray
|
104
|
-
|
105
|
-
|
106
|
-
class InvalidMessageError(Exception):
|
107
|
-
pass
|
108
|
-
|
109
|
-
|
110
|
-
def _read_message(message: bytes) -> Message:
|
111
|
-
end_idx = message.index(3)
|
112
|
-
content = message[1 : end_idx + 1]
|
113
|
-
expected_checksum = int(message[end_idx + 1 : end_idx + 5], 16)
|
114
|
-
actual_checksum = _crc16(content)
|
115
|
-
if expected_checksum != actual_checksum:
|
116
|
-
msg = (
|
117
|
-
"Invalid checksum: "
|
118
|
-
f"expected {expected_checksum:04x}, "
|
119
|
-
f"got {actual_checksum:04x}"
|
120
|
-
)
|
121
|
-
raise InvalidMessageError(msg)
|
122
|
-
lines = message.splitlines()
|
123
|
-
if len(lines[0]) != 11:
|
124
|
-
msg = f"Expected 11 characters in first line, got {len(lines[0])}"
|
125
|
-
raise NotImplementedError(msg)
|
126
|
-
if (msg_no := lines[0][-4:-1]) != b"002":
|
127
|
-
msg = f"Message number {msg_no.decode()} not implemented"
|
128
|
-
raise NotImplementedError(msg)
|
129
|
-
if len(lines) != 5:
|
130
|
-
msg = f"Expected 5 lines, got {len(lines)}"
|
131
|
-
raise InvalidMessageError(msg)
|
132
|
-
scale, res, n, energy, lt, ti, bl, pulse, rate, _sum = map(int, lines[2].split())
|
133
|
-
data = _read_backscatter(lines[3].strip(), n)
|
134
|
-
return Message(scale, res, energy, lt, ti, bl, pulse, rate, data)
|
135
|
-
|
136
|
-
|
137
|
-
def _read_backscatter(data: bytes, n_gates: int) -> np.ndarray:
|
138
|
-
"""Read backscatter values from hex-encoded two's complement values."""
|
139
|
-
n_chars = 5
|
140
|
-
n_bits = n_chars * 4
|
141
|
-
limit = (1 << (n_bits - 1)) - 1
|
142
|
-
offset = 1 << n_bits
|
143
|
-
out = np.array(
|
144
|
-
[int(data[i : i + n_chars], 16) for i in range(0, n_gates * n_chars, n_chars)],
|
145
|
-
)
|
146
|
-
out[out > limit] -= offset
|
147
|
-
return out
|
148
|
-
|
149
|
-
|
150
|
-
def _crc16(data: bytes) -> int:
|
151
|
-
"""Compute checksum similar to CRC-16-CCITT."""
|
152
|
-
return binascii.crc_hqx(data, 0xFFFF) ^ 0xFFFF
|