cloudnetpy 1.71.3__tar.gz → 1.71.5__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.3/cloudnetpy.egg-info → cloudnetpy-1.71.5}/PKG-INFO +6 -7
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/ceilo.py +1 -2
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/hatpro.py +2 -0
- cloudnetpy-1.71.5/cloudnetpy/instruments/vaisala.py +130 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/output.py +3 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/plotting/plotting.py +1 -1
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/version.py +1 -1
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5/cloudnetpy.egg-info}/PKG-INFO +6 -7
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy.egg-info/SOURCES.txt +0 -1
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy.egg-info/requires.txt +1 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/pyproject.toml +3 -5
- cloudnetpy-1.71.3/cloudnetpy/instruments/campbell_scientific.py +0 -203
- cloudnetpy-1.71.3/cloudnetpy/instruments/vaisala.py +0 -405
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/LICENSE +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/MANIFEST.in +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/README.md +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/atmos_utils.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/attenuation.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/attenuations/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/attenuations/gas_attenuation.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/attenuations/liquid_attenuation.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/attenuations/melting_attenuation.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/attenuations/rain_attenuation.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/categorize.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/classify.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/containers.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/disdrometer.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/droplet.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/falling.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/freezing.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/insects.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/itu.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/lidar.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/melting.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/model.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/mwr.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/categorize/radar.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/cli.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/cloudnetarray.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/concat_lib.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/constants.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/datasource.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/exceptions.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/basta.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/ceilometer.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/cl61d.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/cloudnet_instrument.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/copernicus.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/disdrometer/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/disdrometer/common.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/disdrometer/parsivel.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/disdrometer/thies.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/galileo.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/instruments.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/lufft.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/mira.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/mrr.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/nc_lidar.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/nc_radar.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/pollyxt.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/radiometrics.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/rain_e_h3.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/rpg.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/rpg_reader.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/toa5.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/instruments/weather_station.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/metadata.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/file_handler.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/metadata.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/model_metadata.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/plotting/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/plotting/plot_meta.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/plotting/plot_tools.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/plotting/plotting.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/products/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/products/advance_methods.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/products/grid_methods.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/products/model_products.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/products/observation_products.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/products/product_resampling.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/products/tools.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/statistics/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/statistics/statistical_methods.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/e2e/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/e2e/conftest.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/e2e/process_cf/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/e2e/process_cf/main.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/e2e/process_cf/tests.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/main.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/tests.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/main.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/tests.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/unit/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/unit/conftest.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/unit/test_advance_methods.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/unit/test_grid_methods.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/unit/test_model_products.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/unit/test_observation_products.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/unit/test_plot_tools.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/unit/test_plotting.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/tests/unit/test_tools.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/model_evaluation/utils.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/plotting/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/plotting/plot_meta.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/products/__init__.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/products/classification.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/products/der.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/products/drizzle.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/products/drizzle_error.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/products/drizzle_tools.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/products/epsilon.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/products/ier.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/products/iwc.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/products/lwc.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/products/mie_lu_tables.nc +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/products/mwr_tools.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/products/product_tools.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/py.typed +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy/utils.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy.egg-info/dependency_links.txt +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy.egg-info/entry_points.txt +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/cloudnetpy.egg-info/top_level.txt +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/docs/source/conf.py +0 -0
- {cloudnetpy-1.71.3 → cloudnetpy-1.71.5}/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.5
|
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
|
|
@@ -103,6 +103,8 @@ def hatpro2l1c(
|
|
103
103
|
|
104
104
|
if "ir_wavelength" in hatpro.data:
|
105
105
|
hatpro.data["ir_wavelength"].dimensions = ("ir_channel",)
|
106
|
+
if "irt" in hatpro.data:
|
107
|
+
hatpro.data["irt"].dimensions = ("time", "ir_channel")
|
106
108
|
|
107
109
|
for key in ("latitude", "longitude", "altitude"):
|
108
110
|
if key in site_meta:
|
@@ -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
|
|
@@ -199,7 +199,7 @@ class FigureData:
|
|
199
199
|
if "altitude" not in self.file.variables:
|
200
200
|
msg = "No altitude information in the file."
|
201
201
|
raise ValueError(msg)
|
202
|
-
height -= self.file.variables["altitude"][:]
|
202
|
+
height -= np.median(self.file.variables["altitude"][:])
|
203
203
|
return height * con.M_TO_KM
|
204
204
|
if "range" in self.file.variables:
|
205
205
|
return self.file.variables["range"][:] * con.M_TO_KM
|
@@ -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.5
|
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,203 +0,0 @@
|
|
1
|
-
import binascii
|
2
|
-
import datetime
|
3
|
-
import re
|
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
|
-
def _date_format_to_regex(fmt: bytes) -> bytes:
|
15
|
-
"""Converts a date format string to a regex pattern."""
|
16
|
-
mapping = {
|
17
|
-
b"%Y": rb"\d{4}",
|
18
|
-
b"%m": rb"0[1-9]|1[0-2]",
|
19
|
-
b"%d": rb"0[1-9]|[12]\d|3[01]",
|
20
|
-
b"%H": rb"[01]\d|2[0-3]",
|
21
|
-
b"%M": rb"[0-5]\d",
|
22
|
-
b"%S": rb"[0-5]\d",
|
23
|
-
b"%f": rb"\d{6}",
|
24
|
-
}
|
25
|
-
pattern = re.escape(fmt)
|
26
|
-
for key, value in mapping.items():
|
27
|
-
pattern = pattern.replace(
|
28
|
-
re.escape(key), b"(?P<" + key[1:] + b">" + value + b")"
|
29
|
-
)
|
30
|
-
return pattern
|
31
|
-
|
32
|
-
|
33
|
-
FORMATS = [
|
34
|
-
re.compile(_date_format_to_regex(fmt))
|
35
|
-
for fmt in [
|
36
|
-
b"%Y-%m-%dT%H:%M:%S.%f,",
|
37
|
-
b"%%% %Y/%m/%d %H:%M:%S %%%\n",
|
38
|
-
]
|
39
|
-
]
|
40
|
-
|
41
|
-
|
42
|
-
class Cs135(Ceilometer):
|
43
|
-
def __init__(
|
44
|
-
self,
|
45
|
-
full_path: str,
|
46
|
-
site_meta: dict,
|
47
|
-
expected_date: str | None = None,
|
48
|
-
):
|
49
|
-
super().__init__()
|
50
|
-
self.full_path = full_path
|
51
|
-
self.site_meta = site_meta
|
52
|
-
self.expected_date = expected_date
|
53
|
-
self.data = {}
|
54
|
-
self.metadata = {}
|
55
|
-
self.instrument = instruments.CS135
|
56
|
-
|
57
|
-
def read_ceilometer_file(self, calibration_factor: float | None = None) -> None:
|
58
|
-
with open(self.full_path, mode="rb") as f:
|
59
|
-
content = f.read()
|
60
|
-
timestamps = []
|
61
|
-
profiles = []
|
62
|
-
tilt_angles = []
|
63
|
-
range_resolutions = []
|
64
|
-
|
65
|
-
for fmt in FORMATS:
|
66
|
-
parts = re.split(fmt, content)
|
67
|
-
for i in range(1, len(parts), fmt.groups + 1):
|
68
|
-
timestamp = datetime.datetime(
|
69
|
-
int(parts[i + fmt.groupindex["Y"] - 1]),
|
70
|
-
int(parts[i + fmt.groupindex["m"] - 1]),
|
71
|
-
int(parts[i + fmt.groupindex["d"] - 1]),
|
72
|
-
int(parts[i + fmt.groupindex["H"] - 1]),
|
73
|
-
int(parts[i + fmt.groupindex["M"] - 1]),
|
74
|
-
int(parts[i + fmt.groupindex["S"] - 1]),
|
75
|
-
int(parts[i + fmt.groupindex["f"] - 1])
|
76
|
-
if "f" in fmt.groupindex
|
77
|
-
else 0,
|
78
|
-
tzinfo=datetime.timezone.utc,
|
79
|
-
)
|
80
|
-
try:
|
81
|
-
self._check_timestamp(timestamp)
|
82
|
-
except ValidTimeStampError:
|
83
|
-
continue
|
84
|
-
try:
|
85
|
-
message = _read_message(parts[i + fmt.groups])
|
86
|
-
except InvalidMessageError:
|
87
|
-
continue
|
88
|
-
profile = (message.data[:-2] * 1e-8) * (message.scale / 100)
|
89
|
-
timestamps.append(timestamp)
|
90
|
-
profiles.append(profile)
|
91
|
-
tilt_angles.append(message.tilt_angle)
|
92
|
-
range_resolutions.append(message.range_resolution)
|
93
|
-
|
94
|
-
if len(timestamps) == 0:
|
95
|
-
msg = "No valid timestamps found in the file"
|
96
|
-
raise ValidTimeStampError(msg)
|
97
|
-
range_resolution = range_resolutions[0]
|
98
|
-
n_gates = len(profiles[0])
|
99
|
-
if any(res != range_resolution for res in range_resolutions):
|
100
|
-
msg = "Inconsistent range resolution"
|
101
|
-
raise InconsistentDataError(msg)
|
102
|
-
if any(len(profile) != n_gates for profile in profiles):
|
103
|
-
msg = "Inconsistent number of gates"
|
104
|
-
raise InconsistentDataError(msg)
|
105
|
-
|
106
|
-
self.data["beta_raw"] = np.array(profiles)
|
107
|
-
if calibration_factor is None:
|
108
|
-
calibration_factor = 1.0
|
109
|
-
self.data["beta_raw"] *= calibration_factor
|
110
|
-
self.data["calibration_factor"] = calibration_factor
|
111
|
-
self.data["range"] = (
|
112
|
-
np.arange(n_gates) * range_resolution + range_resolution / 2
|
113
|
-
)
|
114
|
-
self.data["time"] = utils.datetime2decimal_hours(timestamps)
|
115
|
-
self.data["zenith_angle"] = np.median(tilt_angles)
|
116
|
-
|
117
|
-
def _check_timestamp(self, timestamp: datetime.datetime) -> None:
|
118
|
-
timestamp_components = str(timestamp.date()).split("-")
|
119
|
-
if (
|
120
|
-
self.expected_date is not None
|
121
|
-
and timestamp_components != self.expected_date.split("-")
|
122
|
-
):
|
123
|
-
raise ValidTimeStampError
|
124
|
-
if not self.date:
|
125
|
-
self.date = timestamp_components
|
126
|
-
if timestamp_components != self.date:
|
127
|
-
msg = "Inconsistent dates in the file"
|
128
|
-
raise RuntimeError(msg)
|
129
|
-
|
130
|
-
|
131
|
-
class Message(NamedTuple):
|
132
|
-
scale: int
|
133
|
-
range_resolution: int
|
134
|
-
laser_pulse_energy: int
|
135
|
-
laser_temperature: int
|
136
|
-
tilt_angle: int
|
137
|
-
background_light: int
|
138
|
-
pulse_quantity: int
|
139
|
-
sample_rate: int
|
140
|
-
data: np.ndarray
|
141
|
-
|
142
|
-
|
143
|
-
class InvalidMessageError(Exception):
|
144
|
-
pass
|
145
|
-
|
146
|
-
|
147
|
-
def _read_message(message: bytes) -> Message:
|
148
|
-
end_idx = message.index(3)
|
149
|
-
content = message[1 : end_idx + 1]
|
150
|
-
expected_checksum = int(message[end_idx + 1 : end_idx + 5], 16)
|
151
|
-
actual_checksum = _crc16(content)
|
152
|
-
if expected_checksum != actual_checksum:
|
153
|
-
msg = (
|
154
|
-
"Invalid checksum: "
|
155
|
-
f"expected {expected_checksum:04x}, "
|
156
|
-
f"got {actual_checksum:04x}"
|
157
|
-
)
|
158
|
-
raise InvalidMessageError(msg)
|
159
|
-
lines = message[1 : end_idx - 1].splitlines()
|
160
|
-
n_lines = len(lines) + 1
|
161
|
-
n_first = len(lines[0]) + 1
|
162
|
-
if n_first != 11:
|
163
|
-
msg = f"Expected 11 characters in first line, got {n_first}"
|
164
|
-
raise NotImplementedError(msg)
|
165
|
-
msg_no = lines[0][-4:-1]
|
166
|
-
if msg_no == b"002":
|
167
|
-
if n_lines != 5:
|
168
|
-
msg = f"Expected 5 lines, got {len(lines)}"
|
169
|
-
raise InvalidMessageError(msg)
|
170
|
-
scale, res, n, energy, lt, ti, bl, pulse, rate, _sum = map(
|
171
|
-
int, lines[2].split()
|
172
|
-
)
|
173
|
-
data = _read_backscatter(lines[3].strip(), n)
|
174
|
-
return Message(scale, res, energy, lt, ti, bl, pulse, rate, data)
|
175
|
-
if msg_no == b"004":
|
176
|
-
if n_lines != 6:
|
177
|
-
msg = f"Expected 6 lines, got {len(lines)}"
|
178
|
-
raise InvalidMessageError(msg)
|
179
|
-
scale, res, n, energy, lt, ti, bl, pulse, rate, _sum = map(
|
180
|
-
int, lines[3].split()
|
181
|
-
)
|
182
|
-
data = _read_backscatter(lines[4].strip(), n)
|
183
|
-
return Message(scale, res, energy, lt, ti, bl, pulse, rate, data)
|
184
|
-
msg = f"Message number {msg_no.decode()} not implemented"
|
185
|
-
raise NotImplementedError(msg)
|
186
|
-
|
187
|
-
|
188
|
-
def _read_backscatter(data: bytes, n_gates: int) -> np.ndarray:
|
189
|
-
"""Read backscatter values from hex-encoded two's complement values."""
|
190
|
-
n_chars = 5
|
191
|
-
n_bits = n_chars * 4
|
192
|
-
limit = (1 << (n_bits - 1)) - 1
|
193
|
-
offset = 1 << n_bits
|
194
|
-
out = np.array(
|
195
|
-
[int(data[i : i + n_chars], 16) for i in range(0, n_gates * n_chars, n_chars)],
|
196
|
-
)
|
197
|
-
out[out > limit] -= offset
|
198
|
-
return out
|
199
|
-
|
200
|
-
|
201
|
-
def _crc16(data: bytes) -> int:
|
202
|
-
"""Compute checksum similar to CRC-16-CCITT."""
|
203
|
-
return binascii.crc_hqx(data, 0xFFFF) ^ 0xFFFF
|