cloudnetpy 1.56.12__py3-none-any.whl → 1.56.14__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.
@@ -66,6 +66,9 @@ def parsivel2nc(
66
66
  disdrometer = Parsivel(disdrometer_file, site_meta, telegram, date, timestamps)
67
67
  disdrometer.sort_timestamps()
68
68
  disdrometer.remove_duplicate_timestamps()
69
+ if len(disdrometer.data["time"].data) < 2:
70
+ msg = "Too few data points"
71
+ raise DisdrometerDataError(msg)
69
72
  disdrometer.convert_units()
70
73
  disdrometer.add_meta()
71
74
  attributes = output.add_time_attribute(ATTRIBUTES, disdrometer.date)
@@ -495,30 +498,58 @@ def _read_toa5(filename: str | PathLike) -> dict[str, list]:
495
498
  return data
496
499
 
497
500
 
498
- def _read_typ_op4a(filename: str | PathLike) -> dict[str, list]:
501
+ def _read_typ_op4a(lines: list[str]) -> dict[str, list]:
499
502
  """Read output of "CS/PA" command. The output starts with line "TYP OP4A"
500
503
  followed by one line per measured variable in format: <number>:<value>.
501
504
  Output ends with characters: <ETX><CR><LF><NUL>. Lines are separated by
502
505
  <CR><LF>.
503
506
  """
504
507
  data = {}
505
- with open(filename, encoding="latin1", errors="ignore") as file:
506
- for line in file:
507
- if ":" not in line:
508
- continue
509
- key, value = line.strip().split(":", maxsplit=1)
510
- # Skip datetime and 16-bit values.
511
- if key in ("19", "30", "31", "32", "33"):
512
- continue
513
- varname = TELEGRAM.get(int(key))
514
- if varname is None:
515
- continue
516
- parser = PARSERS.get(varname, next)
517
- tokens = value.split(";")
518
- data[varname] = [parser(iter(tokens))]
508
+ for line in lines:
509
+ if ":" not in line:
510
+ continue
511
+ key, value = line.strip().split(":", maxsplit=1)
512
+ # Skip datetime and 16-bit values.
513
+ if key in ("19", "30", "31", "32", "33"):
514
+ continue
515
+ varname = TELEGRAM.get(int(key))
516
+ if varname is None:
517
+ continue
518
+ parser = PARSERS.get(varname, next)
519
+ tokens = value.split(";")
520
+ data[varname] = [parser(iter(tokens))]
519
521
  return data
520
522
 
521
523
 
524
+ def _read_fmi(content: str):
525
+ """Read format used by Finnish Meteorological Institute and University of
526
+ Helsinki:
527
+ - "[YYYY-MM-DD HH:MM:SS\n"
528
+ - output of "CS/PA" command without non-printable characters at the end
529
+ - "]\n"
530
+ """
531
+ output: dict[str, Any] = defaultdict(list)
532
+ for m in re.finditer(
533
+ r"\[(?P<year>\d+)-(?P<month>\d+)-(?P<day>\d+) "
534
+ r"(?P<hour>\d+):(?P<minute>\d+):(?P<second>\d+)"
535
+ r"(?P<output>[^\]]*)\]",
536
+ content,
537
+ ):
538
+ for key, value in _read_typ_op4a(m["output"].splitlines()).items():
539
+ output[key].append(value)
540
+ output["_datetime"].append(
541
+ datetime.datetime(
542
+ int(m["year"]),
543
+ int(m["month"]),
544
+ int(m["day"]),
545
+ int(m["hour"]),
546
+ int(m["minute"]),
547
+ int(m["second"]),
548
+ )
549
+ )
550
+ return output
551
+
552
+
522
553
  def _read_parsivel(
523
554
  filenames: Iterable[str | PathLike],
524
555
  telegram: Sequence[int | None] | None = None,
@@ -527,17 +558,20 @@ def _read_parsivel(
527
558
  combined_data = defaultdict(list)
528
559
  for filename in filenames:
529
560
  with open(filename, encoding="latin1", errors="ignore") as file:
530
- lines = file.read().splitlines()
561
+ content = file.read()
562
+ lines = content.splitlines()
531
563
  if not lines:
532
564
  msg = f"File '{filename}' is empty"
533
565
  raise DisdrometerDataError(msg)
534
566
  if "TOA5" in lines[0]:
535
567
  data = _read_toa5(filename)
536
568
  elif "TYP OP4A" in lines[0]:
537
- data = _read_typ_op4a(filename)
569
+ data = _read_typ_op4a(lines)
538
570
  elif "Date" in lines[0]:
539
571
  headers = _parse_headers(lines[0])
540
572
  data = _read_rows(headers, lines[1:])
573
+ elif "[" in lines[0]:
574
+ data = _read_fmi(content)
541
575
  elif telegram is not None:
542
576
  headers = _parse_telegram(telegram)
543
577
  data = _read_rows(headers, lines)
@@ -629,7 +629,8 @@ class Plot1D(Plot):
629
629
  sma = self._calculate_moving_average(data, time, window=5)
630
630
  gap_time = _get_max_gap_in_minutes(figure_data)
631
631
  gaps = self._find_time_gap_indices(time, max_gap_min=gap_time)
632
- sma[gaps] = np.nan
632
+ if len(gaps) > 0:
633
+ sma[gaps] = np.nan
633
634
  if len(sma) == len(time):
634
635
  self._ax.plot(
635
636
  time,
@@ -166,7 +166,7 @@ DEFINITIONS = {
166
166
  "Value 5: Good radar echo only.\n"
167
167
  "Value 6: No radar echo but known attenuation.\n"
168
168
  "Value 7: Radar echo corrected for liquid attenuation using microwave\n"
169
- " radiometer data."
169
+ " radiometer data.\n"
170
170
  "Value 8: Radar ground clutter.\n"
171
171
  "Value 9: Lidar clear-air molecular scattering."
172
172
  ),
cloudnetpy/version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  MAJOR = 1
2
2
  MINOR = 56
3
- PATCH = 12
3
+ PATCH = 14
4
4
  __version__ = f"{MAJOR}.{MINOR}.{PATCH}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cloudnetpy
3
- Version: 1.56.12
3
+ Version: 1.56.14
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -8,7 +8,7 @@ cloudnetpy/metadata.py,sha256=Bcu1a9UyUq61jomuZ0_6hYIOzf61e5qCXeiwLm46ikw,5040
8
8
  cloudnetpy/output.py,sha256=jD1pfBb4OQhVOrlhPEk-8FAi4bUW7zjAL468r6BPkJg,14586
9
9
  cloudnetpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  cloudnetpy/utils.py,sha256=yY5a5HLuAks2uzA4XbbqsGFEmXoyqECn_TjD3sMa0lI,27193
11
- cloudnetpy/version.py,sha256=r4Vw3rHhPq5448SnZHZxaWd0IE856tQqcSQ7Lh9ynX0,73
11
+ cloudnetpy/version.py,sha256=Fa0L-CaTgGkRxlnKqX3a-6es2Ei1Q8LSXzXmGmFXq7w,73
12
12
  cloudnetpy/categorize/__init__.py,sha256=gP5q3Vis1y9u9OWgA_idlbjfWXYN_S0IBSWdwBhL_uU,69
13
13
  cloudnetpy/categorize/atmos.py,sha256=cax3iRmvr7S-VkUZqz0JCfAN3WEsUVbGfH4zSHy1APo,12384
14
14
  cloudnetpy/categorize/atmos_utils.py,sha256=wndpwJxc2-QnNTkV8tc8I11Vs_WkNz9sVMX1fuGgUC4,3777
@@ -48,7 +48,7 @@ cloudnetpy/instruments/vaisala.py,sha256=E6PaK26lHprqOJUCEDZPZQu83Qan9n_THudTFQM
48
48
  cloudnetpy/instruments/weather_station.py,sha256=IMJHGXfMhb4jJw_i66oGDCkeeRn3_eko8zVehu6Fte0,5970
49
49
  cloudnetpy/instruments/disdrometer/__init__.py,sha256=lyjwttWvFvuwYxEkusoAvgRcbBmglmOp5HJOpXUqLWo,93
50
50
  cloudnetpy/instruments/disdrometer/common.py,sha256=nWlVqwvlxei4wJaubBN6NoNsAOpnEqDHvKCPjrAb6Go,15701
51
- cloudnetpy/instruments/disdrometer/parsivel.py,sha256=PkGfTPIjW-055MXC-7sk_eQsfTgg7QVW_r5V1J15g4s,19764
51
+ cloudnetpy/instruments/disdrometer/parsivel.py,sha256=7clC_RS1vbHQ9xY5kHp0pShSRk9mnRwnWHMx42MJJSU,20766
52
52
  cloudnetpy/instruments/disdrometer/thies.py,sha256=h7EwZ9tn47UUMiYqDQ68vkXv4q0rEqX1ZeFXd7XJYNg,5050
53
53
  cloudnetpy/model_evaluation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
54
  cloudnetpy/model_evaluation/file_handler.py,sha256=oUGIblcEWLLv16YKUch-M5KA-dGRAcuHa-9anP3xtX4,6447
@@ -92,9 +92,9 @@ cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py,sha256=Ra3r4V
92
92
  cloudnetpy/model_evaluation/tests/unit/test_tools.py,sha256=Ia_VrLdV2NstX5gbx_3AZTOAlrgLAy_xFZ8fHYVX0xI,3817
93
93
  cloudnetpy/plotting/__init__.py,sha256=lg9Smn4BI0dVBgnDLC3JVJ4GmwoSnO-qoSd4ApvwV6Y,107
94
94
  cloudnetpy/plotting/plot_meta.py,sha256=NWI8ECKMypN5YyM9XKCAp1WEthbFlKMvilxqXmYSEK4,14631
95
- cloudnetpy/plotting/plotting.py,sha256=FMt_rIstXmfjF6sdbZ7UGRf8Wfvmyq_VweP_wVLifKM,30684
95
+ cloudnetpy/plotting/plotting.py,sha256=2NhSl_4gcLxOSlUEGPnwBNc0mUCmPracaguHo9kL51A,30714
96
96
  cloudnetpy/products/__init__.py,sha256=2hRb5HG9hNrxH1if5laJkLeFeaZCd5W1q3hh4ewsX0E,273
97
- cloudnetpy/products/classification.py,sha256=J_FOMUSyxvFaT-hvdKVVcKPtuQ0u3V9PsV5xaIKzMjg,7843
97
+ cloudnetpy/products/classification.py,sha256=0E9OUGR3uLCsS1nORwQu0SqW0_8uX7n6LlRcVhtzKw4,7845
98
98
  cloudnetpy/products/der.py,sha256=HAdPvbJySEqkIwDrdZDPnli_wnN2qwm72_D1a82ZWIs,12398
99
99
  cloudnetpy/products/drizzle.py,sha256=BY2HvJeWt_ps6KKCGXwUUNRTy78q0cQM8bOCCoj8TWA,10803
100
100
  cloudnetpy/products/drizzle_error.py,sha256=4GwlHRtNbk9ks7bGtXCco-wXbcDOKeAQwKmbhzut6Qk,6132
@@ -106,8 +106,8 @@ cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe5
106
106
  cloudnetpy/products/mwr_tools.py,sha256=PRm5aCULccUehU-Byk55wYhhEHseMjoAjGBu5TSyHao,4621
107
107
  cloudnetpy/products/product_tools.py,sha256=E8CSijBY8cr70BH2JFa0lGQ-RzI9EcHQ0Fzt8CQ8rY4,10442
108
108
  docs/source/conf.py,sha256=IKiFWw6xhUd8NrCg0q7l596Ck1d61XWeVjIFHVSG9Og,1490
109
- cloudnetpy-1.56.12.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
110
- cloudnetpy-1.56.12.dist-info/METADATA,sha256=fhsI6VRRSKwXPVQDUIMkBs2fkjBD0PbWqw6luMDR9ag,5734
111
- cloudnetpy-1.56.12.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
112
- cloudnetpy-1.56.12.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
113
- cloudnetpy-1.56.12.dist-info/RECORD,,
109
+ cloudnetpy-1.56.14.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
110
+ cloudnetpy-1.56.14.dist-info/METADATA,sha256=nSphTgk88JFpj2s5KJX9SLUFuHkKBIcLvUcm0ZC2w8E,5734
111
+ cloudnetpy-1.56.14.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
112
+ cloudnetpy-1.56.14.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
113
+ cloudnetpy-1.56.14.dist-info/RECORD,,