ecallistolib 0.2.2__py3-none-any.whl → 0.2.3.1__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.
ecallistolib/__init__.py CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  """
3
3
  e-callistolib: Tools for e-CALLISTO FITS dynamic spectra.
4
- Version 0.2.1
4
+ Version 0.2.3
5
5
  Sahan S Liyanage (sahanslst@gmail.com)
6
6
  Astronomical and Space Science Unit, University of Colombo, Sri Lanka.
7
7
  """
@@ -14,6 +14,7 @@ from .exceptions import (
14
14
  CropError,
15
15
  DownloadError,
16
16
  ECallistoError,
17
+ FrequencyOutOfRangeError,
17
18
  InvalidFilenameError,
18
19
  InvalidFITSError,
19
20
  )
@@ -50,6 +51,7 @@ __all__ = [
50
51
  "DownloadError",
51
52
  "CombineError",
52
53
  "CropError",
54
+ "FrequencyOutOfRangeError",
53
55
  ]
54
56
 
55
57
 
@@ -86,12 +88,14 @@ def __getattr__(name: str):
86
88
  "plot_dynamic_spectrum",
87
89
  "plot_raw_spectrum",
88
90
  "plot_background_subtracted",
91
+ "plot_light_curve",
89
92
  "TimeAxisConverter",
90
93
  }:
91
94
  from .plotting import (
92
95
  TimeAxisConverter,
93
96
  plot_background_subtracted,
94
97
  plot_dynamic_spectrum,
98
+ plot_light_curve,
95
99
  plot_raw_spectrum,
96
100
  )
97
101
 
@@ -99,6 +103,7 @@ def __getattr__(name: str):
99
103
  "plot_dynamic_spectrum": plot_dynamic_spectrum,
100
104
  "plot_raw_spectrum": plot_raw_spectrum,
101
105
  "plot_background_subtracted": plot_background_subtracted,
106
+ "plot_light_curve": plot_light_curve,
102
107
  "TimeAxisConverter": TimeAxisConverter,
103
108
  }[name]
104
109
 
ecallistolib/combine.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """
2
2
  e-callistolib: Tools for e-CALLISTO FITS dynamic spectra.
3
- Version 0.2.1
3
+ Version 0.2.3
4
4
  Sahan S Liyanage (sahanslst@gmail.com)
5
5
  Astronomical and Space Science Unit, University of Colombo, Sri Lanka.
6
6
  """
@@ -48,7 +48,7 @@ def combine_frequency(path1: str | Path, path2: str | Path) -> DynamicSpectrum:
48
48
 
49
49
  meta = dict(ds1.meta)
50
50
  meta["combined"] = {"mode": "frequency", "sources": [str(ds1.source), str(ds2.source)]}
51
- return DynamicSpectrum(data=data, freqs_mhz=freqs, time_s=ds1.time_s, source=None, meta=meta)
51
+ return DynamicSpectrum(data=data, freqs_mhz=freqs, time_s=ds1.time_s, source=ds1.source, meta=meta)
52
52
 
53
53
 
54
54
  def can_combine_time(paths: Iterable[str | Path], freq_atol: float = 0.01) -> bool:
@@ -106,5 +106,5 @@ def combine_time(paths: Iterable[str | Path]) -> DynamicSpectrum:
106
106
 
107
107
  meta = dict(ds0.meta)
108
108
  meta["combined"] = {"mode": "time", "sources": [str(Path(p)) for p in paths]}
109
- return DynamicSpectrum(data=combined_data, freqs_mhz=freqs, time_s=combined_time, source=None, meta=meta)
109
+ return DynamicSpectrum(data=combined_data, freqs_mhz=freqs, time_s=combined_time, source=ds0.source, meta=meta)
110
110
 
ecallistolib/crop.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """
2
2
  e-callistolib: Tools for e-CALLISTO FITS dynamic spectra.
3
- Version 0.2.1
3
+ Version 0.2.3
4
4
  Sahan S Liyanage (sahanslst@gmail.com)
5
5
  Astronomical and Space Science Unit, University of Colombo, Sri Lanka.
6
6
  """
ecallistolib/download.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """
2
2
  e-callistolib: Tools for e-CALLISTO FITS dynamic spectra.
3
- Version 0.2.1
3
+ Version 0.2.3
4
4
  Sahan S Liyanage (sahanslst@gmail.com)
5
5
  Astronomical and Space Science Unit, University of Colombo, Sri Lanka.
6
6
  """
@@ -1,6 +1,6 @@
1
1
  """
2
2
  e-callistolib: Tools for e-CALLISTO FITS dynamic spectra.
3
- Version 0.2.1
3
+ Version 0.2.3
4
4
  Sahan S Liyanage (sahanslst@gmail.com)
5
5
  Astronomical and Space Science Unit, University of Colombo, Sri Lanka.
6
6
  """
@@ -36,3 +36,8 @@ class CombineError(ECallistoError):
36
36
  class CropError(ECallistoError):
37
37
  """Raised when cropping parameters are invalid."""
38
38
  pass
39
+
40
+
41
+ class FrequencyOutOfRangeError(ECallistoError):
42
+ """Raised when the requested frequency is outside the spectrum's frequency range."""
43
+ pass
ecallistolib/io.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """
2
2
  e-callistolib: Tools for e-CALLISTO FITS dynamic spectra.
3
- Version 0.2.1
3
+ Version 0.2.3
4
4
  Sahan S Liyanage (sahanslst@gmail.com)
5
5
  Astronomical and Space Science Unit, University of Colombo, Sri Lanka.
6
6
  """
ecallistolib/models.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """
2
2
  e-callistolib: Tools for e-CALLISTO FITS dynamic spectra.
3
- Version 0.2.1
3
+ Version 0.2.3
4
4
  Sahan S Liyanage (sahanslst@gmail.com)
5
5
  Astronomical and Space Science Unit, University of Colombo, Sri Lanka.
6
6
  """
ecallistolib/plotting.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """
2
2
  e-callistolib: Tools for e-CALLISTO FITS dynamic spectra.
3
- Version 0.2.1
3
+ Version 0.2.3
4
4
  Sahan S Liyanage (sahanslst@gmail.com)
5
5
  Astronomical and Space Science Unit, University of Colombo, Sri Lanka.
6
6
  """
@@ -155,39 +155,60 @@ def _get_filename_title(ds: DynamicSpectrum, suffix: str) -> str:
155
155
  """Generate plot title from DynamicSpectrum source filename."""
156
156
  if ds.source is not None:
157
157
  filename = ds.source.stem # Get filename without extension
158
+ # Also strip .fit if it's a double extension like .fit.gz
159
+ if filename.endswith(".fit"):
160
+ filename = filename[:-4]
158
161
  return f"{filename}_{suffix}"
159
162
  return suffix
160
163
 
161
164
 
162
- def plot_raw_spectrum(
165
+ # Conversion factor for Digits to dB (pseudo-calibration)
166
+ # dB = Digits * 2500 / 256 / 25.4 = Digits * 0.384
167
+ DIGITS_TO_DB_FACTOR = 2500.0 / 256.0 / 25.4 # ~0.384
168
+
169
+
170
+ def plot_dynamic_spectrum(
163
171
  ds: DynamicSpectrum,
172
+ process: Literal["raw", "background_subtracted", "noise_reduced"] = "raw",
173
+ clip_low: float | None = None,
174
+ clip_high: float | None = None,
164
175
  title: str | None = None,
165
- cmap: str = "viridis",
176
+ cmap: str = "inferno",
166
177
  figsize: tuple[float, float] | None = None,
167
- vmin: float | None = None,
168
- vmax: float | None = None,
169
178
  ax: Optional[plt.Axes] = None,
170
179
  show_colorbar: bool = True,
171
180
  time_format: Literal["seconds", "ut"] = "seconds",
181
+ intensity_units: Literal["digits", "dB"] = "digits",
172
182
  **imshow_kwargs,
173
183
  ) -> tuple["Figure", "Axes", "AxesImage"]:
174
184
  """
175
- Plot a raw DynamicSpectrum without any processing.
185
+ Plot a DynamicSpectrum with selectable processing mode.
186
+
187
+ This is the main plotting function that supports raw, background-subtracted,
188
+ and noise-reduced visualization modes with full matplotlib customization.
176
189
 
177
190
  Parameters
178
191
  ----------
179
192
  ds : DynamicSpectrum
180
193
  The dynamic spectrum to plot.
181
- title : str
182
- Plot title.
194
+ process : {"raw", "background_subtracted", "noise_reduced"}
195
+ Processing mode to apply before plotting:
196
+ - "raw": Plot the original data without any processing.
197
+ - "background_subtracted": Subtract mean over time for each frequency.
198
+ - "noise_reduced": Apply background subtraction and clipping (requires
199
+ clip_low and clip_high).
200
+ clip_low : float | None
201
+ Lower clipping threshold. Required when process="noise_reduced".
202
+ Also used for colormap normalization in all modes if provided.
203
+ clip_high : float | None
204
+ Upper clipping threshold. Required when process="noise_reduced".
205
+ Also used for colormap normalization in all modes if provided.
206
+ title : str | None
207
+ Plot title. If None, auto-generates from filename and process mode.
183
208
  cmap : str
184
- Matplotlib colormap name.
209
+ Matplotlib colormap name (e.g., "inferno", "viridis", "jet").
185
210
  figsize : tuple[float, float] | None
186
211
  Figure size as (width, height) in inches. Ignored if ax is provided.
187
- vmin : float | None
188
- Minimum value for colormap normalization.
189
- vmax : float | None
190
- Maximum value for colormap normalization.
191
212
  ax : plt.Axes | None
192
213
  Existing axes to plot on. If None, creates a new figure.
193
214
  show_colorbar : bool
@@ -195,19 +216,60 @@ def plot_raw_spectrum(
195
216
  time_format : {"seconds", "ut"}
196
217
  Format for the time axis. "seconds" shows elapsed seconds,
197
218
  "ut" shows Universal Time (requires ut_start_sec in metadata).
219
+ intensity_units : {"digits", "dB"}
220
+ Units for the intensity axis. "digits" shows raw ADU values,
221
+ "dB" converts using dB = Digits * 0.384 (pseudo-calibration).
198
222
  **imshow_kwargs
199
223
  Additional keyword arguments passed to matplotlib's imshow().
224
+ Common options include:
225
+ - interpolation: str ("nearest", "bilinear", "bicubic", etc.)
226
+ - origin: str ("upper", "lower")
227
+ - alpha: float (transparency)
200
228
 
201
229
  Returns
202
230
  -------
203
231
  tuple[Figure, Axes, AxesImage]
204
232
  The figure, axes, and image objects.
205
233
 
234
+ Raises
235
+ ------
236
+ ValueError
237
+ If process="noise_reduced" but clip_low or clip_high is not provided.
238
+
206
239
  Example
207
240
  -------
208
241
  >>> ds = read_fits("spectrum.fit.gz")
209
- >>> fig, ax, im = plot_raw_spectrum(ds, figsize=(12, 6), cmap="plasma")
242
+ >>> # Plot raw spectrum
243
+ >>> fig, ax, im = plot_dynamic_spectrum(ds, process="raw")
244
+ >>> # Plot noise-reduced spectrum
245
+ >>> fig, ax, im = plot_dynamic_spectrum(
246
+ ... ds, process="noise_reduced",
247
+ ... clip_low=-5, clip_high=20,
248
+ ... cmap="jet"
249
+ ... )
210
250
  """
251
+ from .processing import background_subtract, noise_reduce_mean_clip
252
+
253
+ # Validate parameters for noise_reduced mode
254
+ if process == "noise_reduced":
255
+ if clip_low is None or clip_high is None:
256
+ raise ValueError(
257
+ "When process='noise_reduced', both clip_low and clip_high must be provided."
258
+ )
259
+
260
+ # Apply processing
261
+ if process == "background_subtracted":
262
+ ds_plot = background_subtract(ds)
263
+ title_suffix = "background_subtracted"
264
+ elif process == "noise_reduced":
265
+ ds_plot = noise_reduce_mean_clip(
266
+ ds, clip_low=clip_low, clip_high=clip_high, scale=None
267
+ )
268
+ title_suffix = "noise_clipped"
269
+ else: # raw
270
+ ds_plot = ds
271
+ title_suffix = "raw"
272
+
211
273
  if ax is None:
212
274
  fig, ax = plt.subplots(figsize=figsize)
213
275
  else:
@@ -215,8 +277,18 @@ def plot_raw_spectrum(
215
277
 
216
278
  extent, converter = _compute_extent(ds, time_format)
217
279
 
280
+ # Convert to dB if requested
281
+ plot_data = ds_plot.data
282
+ vmin, vmax = clip_low, clip_high
283
+ if intensity_units == "dB":
284
+ plot_data = plot_data * DIGITS_TO_DB_FACTOR
285
+ if vmin is not None:
286
+ vmin = vmin * DIGITS_TO_DB_FACTOR
287
+ if vmax is not None:
288
+ vmax = vmax * DIGITS_TO_DB_FACTOR
289
+
218
290
  im = ax.imshow(
219
- ds.data,
291
+ plot_data,
220
292
  aspect="auto",
221
293
  extent=extent,
222
294
  cmap=cmap,
@@ -226,65 +298,64 @@ def plot_raw_spectrum(
226
298
  )
227
299
  # Use filename-based title if not provided
228
300
  if title is None:
229
- title = _get_filename_title(ds, "raw")
301
+ title = _get_filename_title(ds, title_suffix)
230
302
  ax.set_title(title)
231
303
  _format_time_axis(ax, converter, time_format)
232
304
  ax.set_ylabel("Frequency [MHz]")
233
305
 
234
306
  if show_colorbar:
235
307
  cbar = fig.colorbar(im, ax=ax)
236
- cbar.set_label("Intensity [DN]")
308
+ if intensity_units == "dB":
309
+ cbar.set_label("Intensity [dB]")
310
+ else:
311
+ cbar.set_label("Intensity [Digits]")
237
312
 
238
313
  return fig, ax, im
239
314
 
240
315
 
241
- def plot_dynamic_spectrum(
316
+ def plot_raw_spectrum(
242
317
  ds: DynamicSpectrum,
243
318
  title: str | None = None,
244
- cmap: str = "inferno",
319
+ cmap: str = "viridis",
245
320
  figsize: tuple[float, float] | None = None,
246
- vmin: float | None = None,
247
- vmax: float | None = None,
321
+ clip_low: float | None = None,
322
+ clip_high: float | None = None,
248
323
  ax: Optional[plt.Axes] = None,
249
324
  show_colorbar: bool = True,
250
325
  time_format: Literal["seconds", "ut"] = "seconds",
326
+ intensity_units: Literal["digits", "dB"] = "digits",
251
327
  **imshow_kwargs,
252
328
  ) -> tuple["Figure", "Axes", "AxesImage"]:
253
329
  """
254
- Plot a DynamicSpectrum using matplotlib with full customization.
330
+ Plot a raw DynamicSpectrum without any processing.
255
331
 
256
- This is the main plotting function with support for all matplotlib
257
- imshow parameters including colormap clipping (vmin/vmax), figure size,
258
- and custom time axis formats.
332
+ This is a convenience function that calls plot_dynamic_spectrum with
333
+ process="raw" and a default colormap suitable for raw data.
259
334
 
260
335
  Parameters
261
336
  ----------
262
337
  ds : DynamicSpectrum
263
338
  The dynamic spectrum to plot.
264
- title : str
339
+ title : str | None
265
340
  Plot title.
266
341
  cmap : str
267
- Matplotlib colormap name (e.g., "inferno", "viridis", "magma", "plasma").
342
+ Matplotlib colormap name. Default is "viridis".
268
343
  figsize : tuple[float, float] | None
269
- Figure size as (width, height) in inches. Ignored if ax is provided.
270
- vmin : float | None
271
- Minimum value for colormap normalization (clipping lower bound).
272
- vmax : float | None
273
- Maximum value for colormap normalization (clipping upper bound).
344
+ Figure size as (width, height) in inches.
345
+ clip_low : float | None
346
+ Minimum value for colormap normalization.
347
+ clip_high : float | None
348
+ Maximum value for colormap normalization.
274
349
  ax : plt.Axes | None
275
350
  Existing axes to plot on. If None, creates a new figure.
276
351
  show_colorbar : bool
277
352
  Whether to show a colorbar.
278
353
  time_format : {"seconds", "ut"}
279
- Format for the time axis. "seconds" shows elapsed seconds,
280
- "ut" shows Universal Time (requires ut_start_sec in metadata).
354
+ Format for the time axis.
355
+ intensity_units : {"digits", "dB"}
356
+ Units for the intensity axis.
281
357
  **imshow_kwargs
282
358
  Additional keyword arguments passed to matplotlib's imshow().
283
- Common options include:
284
- - interpolation: str ("nearest", "bilinear", "bicubic", etc.)
285
- - origin: str ("upper", "lower")
286
- - alpha: float (transparency)
287
- - norm: matplotlib.colors.Normalize (custom normalization)
288
359
 
289
360
  Returns
290
361
  -------
@@ -294,43 +365,22 @@ def plot_dynamic_spectrum(
294
365
  Example
295
366
  -------
296
367
  >>> ds = read_fits("spectrum.fit.gz")
297
- >>> ds_reduced = noise_reduce_mean_clip(ds)
298
- >>> fig, ax, im = plot_dynamic_spectrum(
299
- ... ds_reduced,
300
- ... title="Noise Reduced",
301
- ... vmin=-5, vmax=20,
302
- ... figsize=(12, 6),
303
- ... cmap="magma"
304
- ... )
368
+ >>> fig, ax, im = plot_raw_spectrum(ds, figsize=(12, 6), cmap="plasma")
305
369
  """
306
- if ax is None:
307
- fig, ax = plt.subplots(figsize=figsize)
308
- else:
309
- fig = ax.figure
310
-
311
- extent, converter = _compute_extent(ds, time_format)
312
-
313
- im = ax.imshow(
314
- ds.data,
315
- aspect="auto",
316
- extent=extent,
370
+ return plot_dynamic_spectrum(
371
+ ds,
372
+ process="raw",
373
+ clip_low=clip_low,
374
+ clip_high=clip_high,
375
+ title=title,
317
376
  cmap=cmap,
318
- vmin=vmin,
319
- vmax=vmax,
377
+ figsize=figsize,
378
+ ax=ax,
379
+ show_colorbar=show_colorbar,
380
+ time_format=time_format,
381
+ intensity_units=intensity_units,
320
382
  **imshow_kwargs,
321
383
  )
322
- # Use filename-based title if not provided
323
- if title is None:
324
- title = _get_filename_title(ds, "dynamic_spectrum")
325
- ax.set_title(title)
326
- _format_time_axis(ax, converter, time_format)
327
- ax.set_ylabel("Frequency [MHz]")
328
-
329
- if show_colorbar:
330
- cbar = fig.colorbar(im, ax=ax)
331
- cbar.set_label("Intensity [DN]")
332
-
333
- return fig, ax, im
334
384
 
335
385
 
336
386
  def plot_background_subtracted(
@@ -338,34 +388,34 @@ def plot_background_subtracted(
338
388
  title: str | None = None,
339
389
  cmap: str = "jet",
340
390
  figsize: tuple[float, float] | None = None,
341
- vmin: float | None = None,
342
- vmax: float | None = None,
391
+ clip_low: float | None = None,
392
+ clip_high: float | None = None,
343
393
  ax: Optional[plt.Axes] = None,
344
394
  show_colorbar: bool = True,
345
395
  time_format: Literal["seconds", "ut"] = "seconds",
396
+ intensity_units: Literal["digits", "dB"] = "digits",
346
397
  **imshow_kwargs,
347
398
  ) -> tuple["Figure", "Axes", "AxesImage"]:
348
399
  """
349
400
  Plot a DynamicSpectrum after background subtraction (before clipping).
350
401
 
351
- This is a convenience function that applies background subtraction
352
- (mean removal per frequency channel) and plots the result. This shows
353
- the intermediate step before clipping is applied in noise reduction.
402
+ This is a convenience function that calls plot_dynamic_spectrum with
403
+ process="background_subtracted".
354
404
 
355
405
  Parameters
356
406
  ----------
357
407
  ds : DynamicSpectrum
358
408
  The raw dynamic spectrum (will be background-subtracted internally).
359
- title : str
409
+ title : str | None
360
410
  Plot title.
361
411
  cmap : str
362
- Matplotlib colormap name. Default is "RdBu_r" (diverging colormap)
363
- which works well for showing positive/negative deviations.
412
+ Matplotlib colormap name. Default is "jet" which works well for
413
+ showing positive/negative deviations.
364
414
  figsize : tuple[float, float] | None
365
415
  Figure size as (width, height) in inches.
366
- vmin : float | None
416
+ clip_low : float | None
367
417
  Minimum value for colormap normalization.
368
- vmax : float | None
418
+ clip_high : float | None
369
419
  Maximum value for colormap normalization.
370
420
  ax : plt.Axes | None
371
421
  Existing axes to plot on. If None, creates a new figure.
@@ -373,6 +423,8 @@ def plot_background_subtracted(
373
423
  Whether to show a colorbar.
374
424
  time_format : {"seconds", "ut"}
375
425
  Format for the time axis.
426
+ intensity_units : {"digits", "dB"}
427
+ Units for the intensity axis.
376
428
  **imshow_kwargs
377
429
  Additional keyword arguments passed to matplotlib's imshow().
378
430
 
@@ -384,23 +436,183 @@ def plot_background_subtracted(
384
436
  Example
385
437
  -------
386
438
  >>> ds = read_fits("spectrum.fit.gz")
387
- >>> fig, ax, im = plot_background_subtracted(ds, vmin=-10, vmax=30)
439
+ >>> fig, ax, im = plot_background_subtracted(ds, clip_low=-10, clip_high=30)
388
440
  """
389
- from .processing import background_subtract
390
-
391
- ds_bg = background_subtract(ds)
392
- # Use filename-based title if not provided
393
- if title is None:
394
- title = _get_filename_title(ds, "background_subtracted")
395
441
  return plot_dynamic_spectrum(
396
- ds_bg,
442
+ ds,
443
+ process="background_subtracted",
444
+ clip_low=clip_low,
445
+ clip_high=clip_high,
397
446
  title=title,
398
447
  cmap=cmap,
399
448
  figsize=figsize,
400
- vmin=vmin,
401
- vmax=vmax,
402
449
  ax=ax,
403
450
  show_colorbar=show_colorbar,
404
451
  time_format=time_format,
452
+ intensity_units=intensity_units,
405
453
  **imshow_kwargs,
406
454
  )
455
+
456
+
457
+ def plot_light_curve(
458
+ ds: DynamicSpectrum,
459
+ frequency_mhz: float,
460
+ process: Literal["raw", "background_subtracted", "noise_reduced"] = "raw",
461
+ title: str | None = None,
462
+ figsize: tuple[float, float] | None = None,
463
+ ax: Optional[plt.Axes] = None,
464
+ time_format: Literal["seconds", "ut"] = "seconds",
465
+ clip_low: float | None = None,
466
+ clip_high: float | None = None,
467
+ intensity_units: Literal["digits", "dB"] = "digits",
468
+ **plot_kwargs,
469
+ ) -> tuple["Figure", "Axes", "plt.Line2D"]:
470
+ """
471
+ Plot a light curve (intensity vs time) at a specific frequency.
472
+
473
+ This function extracts the intensity values at the frequency channel closest
474
+ to the specified frequency and plots them against time. The data can be
475
+ plotted raw, after background subtraction, or after full noise reduction.
476
+
477
+ Parameters
478
+ ----------
479
+ ds : DynamicSpectrum
480
+ The dynamic spectrum to extract the light curve from.
481
+ frequency_mhz : float
482
+ The target frequency in MHz. The function will use the closest
483
+ available frequency channel in the spectrum.
484
+ process : {"raw", "background_subtracted", "noise_reduced"}
485
+ Processing to apply before plotting:
486
+ - "raw": Use the original data without any processing.
487
+ - "background_subtracted": Subtract mean over time for each frequency.
488
+ - "noise_reduced": Apply full noise reduction (requires clip_low/clip_high).
489
+ title : str | None
490
+ Plot title. If None, generates title from filename and frequency.
491
+ figsize : tuple[float, float] | None
492
+ Figure size as (width, height) in inches. Ignored if ax is provided.
493
+ ax : plt.Axes | None
494
+ Existing axes to plot on. If None, creates a new figure.
495
+ time_format : {"seconds", "ut"}
496
+ Format for the time axis. "seconds" shows elapsed seconds,
497
+ "ut" shows Universal Time (requires ut_start_sec in metadata).
498
+ clip_low : float | None
499
+ Lower clipping threshold for noise reduction. Required if process="noise_reduced".
500
+ clip_high : float | None
501
+ Upper clipping threshold for noise reduction. Required if process="noise_reduced".
502
+ intensity_units : {"digits", "dB"}
503
+ Units for the intensity axis. "digits" shows raw ADU values,
504
+ "dB" converts using dB = Digits * 0.384 (pseudo-calibration).
505
+ **plot_kwargs
506
+ Additional keyword arguments passed to matplotlib's plot().
507
+
508
+ Returns
509
+ -------
510
+ tuple[Figure, Axes, Line2D]
511
+ The figure, axes, and line objects.
512
+
513
+ Raises
514
+ ------
515
+ FrequencyOutOfRangeError
516
+ If the requested frequency is outside the spectrum's frequency range.
517
+ ValueError
518
+ If process="noise_reduced" but clip_low or clip_high is not provided.
519
+
520
+ Example
521
+ -------
522
+ >>> ds = read_fits("spectrum.fit.gz")
523
+ >>> # Plot raw light curve at 60 MHz
524
+ >>> fig, ax, line = plot_light_curve(ds, frequency_mhz=60, process="raw")
525
+ >>> # Plot noise-reduced light curve with custom clipping
526
+ >>> fig, ax, line = plot_light_curve(
527
+ ... ds, frequency_mhz=60, process="noise_reduced",
528
+ ... clip_low=-5, clip_high=20
529
+ ... )
530
+ """
531
+ from .exceptions import FrequencyOutOfRangeError
532
+ from .processing import background_subtract, noise_reduce_mean_clip
533
+
534
+ # Validate frequency is within range
535
+ freq_min = float(ds.freqs_mhz.min())
536
+ freq_max = float(ds.freqs_mhz.max())
537
+
538
+ if frequency_mhz < freq_min or frequency_mhz > freq_max:
539
+ raise FrequencyOutOfRangeError(
540
+ f"Requested frequency {frequency_mhz} MHz is outside the spectrum's "
541
+ f"frequency range [{freq_min:.2f}, {freq_max:.2f}] MHz."
542
+ )
543
+
544
+ # Validate clip parameters for noise_reduced
545
+ if process == "noise_reduced":
546
+ if clip_low is None or clip_high is None:
547
+ raise ValueError(
548
+ "When process='noise_reduced', both clip_low and clip_high must be provided."
549
+ )
550
+
551
+ # Find the closest frequency channel
552
+ freq_idx = int(np.argmin(np.abs(ds.freqs_mhz - frequency_mhz)))
553
+ actual_freq = float(ds.freqs_mhz[freq_idx])
554
+
555
+ # Apply processing
556
+ if process == "background_subtracted":
557
+ ds_processed = background_subtract(ds)
558
+ elif process == "noise_reduced":
559
+ ds_processed = noise_reduce_mean_clip(
560
+ ds, clip_low=clip_low, clip_high=clip_high, scale=None
561
+ )
562
+ else: # raw
563
+ ds_processed = ds
564
+
565
+ # Extract light curve data
566
+ light_curve = ds_processed.data[freq_idx, :]
567
+
568
+ # Convert to dB if requested
569
+ if intensity_units == "dB":
570
+ light_curve = light_curve * DIGITS_TO_DB_FACTOR
571
+
572
+ # Create figure and axes
573
+ if ax is None:
574
+ fig, ax = plt.subplots(figsize=figsize)
575
+ else:
576
+ fig = ax.figure
577
+
578
+ # Prepare time axis
579
+ if time_format == "ut":
580
+ converter = TimeAxisConverter.from_dynamic_spectrum(ds)
581
+ time_values = ds.time_s + converter.ut_start_sec
582
+ ax.set_xlabel("Time [UT]")
583
+ # Format x-tick labels as UT times
584
+ from matplotlib.ticker import FuncFormatter
585
+
586
+ def fmt(x, pos):
587
+ return converter.seconds_to_ut(x - converter.ut_start_sec)
588
+
589
+ ax.xaxis.set_major_formatter(FuncFormatter(fmt))
590
+ else:
591
+ time_values = ds.time_s
592
+ ax.set_xlabel("Time [s]")
593
+
594
+ # Plot the light curve
595
+ (line,) = ax.plot(time_values, light_curve, **plot_kwargs)
596
+
597
+ # Set title
598
+ if title is None:
599
+ # Map process name to title suffix
600
+ title_suffix = "noise_clipped" if process == "noise_reduced" else process
601
+ if ds.source is not None:
602
+ filename = ds.source.stem
603
+ # Strip .fit if it's a double extension like .fit.gz
604
+ if filename.endswith(".fit"):
605
+ filename = filename[:-4]
606
+ title = f"{filename}_light_curve_{actual_freq:.1f}MHz_{title_suffix}"
607
+ else:
608
+ title = f"Light Curve @ {actual_freq:.1f} MHz ({title_suffix})"
609
+
610
+ ax.set_title(title)
611
+
612
+ if intensity_units == "dB":
613
+ ax.set_ylabel("Intensity [dB]")
614
+ else:
615
+ ax.set_ylabel("Intensity [Digits]")
616
+
617
+ return fig, ax, line
618
+
@@ -1,6 +1,6 @@
1
1
  """
2
2
  e-callistolib: Tools for e-CALLISTO FITS dynamic spectra.
3
- Version 0.2.1
3
+ Version 0.2.3
4
4
  Sahan S Liyanage (sahanslst@gmail.com)
5
5
  Astronomical and Space Science Unit, University of Colombo, Sri Lanka.
6
6
  """
@@ -14,8 +14,8 @@ from .models import DynamicSpectrum
14
14
 
15
15
  def noise_reduce_mean_clip(
16
16
  ds: DynamicSpectrum,
17
- clip_low: float = -5.0,
18
- clip_high: float = 20.0,
17
+ clip_low: float,
18
+ clip_high: float,
19
19
  scale: float | None = (2500.0 / 255.0 / 25.4),
20
20
  ) -> DynamicSpectrum:
21
21
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ecallistolib
3
- Version: 0.2.2
3
+ Version: 0.2.3.1
4
4
  Summary: Tools to download, read, process, and plot e-CALLISTO FITS dynamic spectra.
5
5
  Author: Sahan S. Liyanage
6
6
  License: MIT
@@ -117,11 +117,14 @@ import ecallistolib as ecl
117
117
  # Read a FITS file
118
118
  spectrum = ecl.read_fits("ALASKA_20230101_120000_01.fit.gz")
119
119
 
120
- # Apply noise reduction
121
- cleaned = ecl.noise_reduce_mean_clip(spectrum)
122
-
123
- # Plot the result
124
- fig, ax, im = ecl.plot_dynamic_spectrum(cleaned, title="Solar Radio Burst")
120
+ # Plot with different processing modes
121
+ fig, ax, im = ecl.plot_dynamic_spectrum(
122
+ spectrum,
123
+ process="noise_reduced",
124
+ clip_low=-5,
125
+ clip_high=20,
126
+ title="Solar Radio Burst"
127
+ )
125
128
  ```
126
129
 
127
130
  ---
@@ -200,14 +203,11 @@ import ecallistolib as ecl
200
203
 
201
204
  spectrum = ecl.read_fits("my_spectrum.fit.gz")
202
205
 
203
- # Apply noise reduction with default parameters
204
- cleaned = ecl.noise_reduce_mean_clip(spectrum)
205
-
206
- # Or customize the parameters
206
+ # Apply noise reduction with required clipping values
207
207
  cleaned = ecl.noise_reduce_mean_clip(
208
208
  spectrum,
209
- clip_low=-5.0, # Lower clipping threshold
210
- clip_high=20.0, # Upper clipping threshold
209
+ clip_low=-5.0, # Lower clipping threshold (required)
210
+ clip_high=20.0, # Upper clipping threshold (required)
211
211
  scale=2500.0 / 255.0 / 25.4 # Scaling factor (None to disable)
212
212
  )
213
213
 
@@ -333,28 +333,28 @@ if ecl.can_combine_time(files):
333
333
 
334
334
  ### Plotting
335
335
 
336
- Create dynamic spectrum visualizations with full customization:
336
+ Create dynamic spectrum visualizations with selectable processing modes:
337
337
 
338
338
  ```python
339
339
  import ecallistolib as ecl
340
340
  import matplotlib.pyplot as plt
341
341
 
342
342
  spectrum = ecl.read_fits("my_spectrum.fit.gz")
343
- cleaned = ecl.noise_reduce_mean_clip(spectrum)
344
343
 
345
- # Basic plot
346
- fig, ax, im = ecl.plot_dynamic_spectrum(cleaned, title="Solar Radio Observation")
344
+ # Plot raw spectrum
345
+ fig, ax, im = ecl.plot_dynamic_spectrum(spectrum, process="raw")
347
346
  plt.show()
348
347
 
349
- # Customized plot with clipping values, colormap, and figure size
348
+ # Plot noise-reduced spectrum with required clipping values
350
349
  fig, ax, im = ecl.plot_dynamic_spectrum(
351
- cleaned,
350
+ spectrum,
351
+ process="noise_reduced", # Apply noise reduction
352
+ clip_low=-5, # Lower clipping bound (required)
353
+ clip_high=20, # Upper clipping bound (required)
352
354
  title="Type III Solar Burst",
353
- cmap="magma", # Matplotlib colormap
354
- vmin=-5, # Colormap lower bound
355
- vmax=20, # Colormap upper bound
356
- figsize=(12, 6), # Figure size in inches
357
- interpolation="bilinear" # Any matplotlib imshow kwarg
355
+ cmap="magma",
356
+ figsize=(12, 6),
357
+ interpolation="bilinear"
358
358
  )
359
359
  plt.savefig("spectrum.png", dpi=150, bbox_inches="tight")
360
360
  ```
@@ -385,8 +385,8 @@ spectrum = ecl.read_fits("my_spectrum.fit.gz")
385
385
  # Plot after background subtraction but before clipping
386
386
  fig, ax, im = ecl.plot_background_subtracted(
387
387
  spectrum,
388
- vmin=-10,
389
- vmax=30,
388
+ clip_low=-10,
389
+ clip_high=30,
390
390
  cmap="RdBu_r" # Diverging colormap for +/- values
391
391
  )
392
392
  ```
@@ -407,6 +407,24 @@ ecl.plot_dynamic_spectrum(spectrum, time_format="seconds")
407
407
  ecl.plot_dynamic_spectrum(spectrum, time_format="ut")
408
408
  ```
409
409
 
410
+ #### Intensity Units
411
+
412
+ Choose between raw digital values (Digits/ADU) or pseudo-calibrated dB:
413
+
414
+ ```python
415
+ import ecallistolib as ecl
416
+
417
+ spectrum = ecl.read_fits("my_spectrum.fit.gz")
418
+
419
+ # Default: intensity in Digits (raw ADU values)
420
+ ecl.plot_dynamic_spectrum(spectrum, intensity_units="digits")
421
+
422
+ # Convert to dB using: dB = Digits * 0.384 (pseudo-calibration)
423
+ ecl.plot_dynamic_spectrum(spectrum, intensity_units="dB")
424
+ ```
425
+
426
+ > **Note:** The dB conversion uses the formula: dB = Digits × 2500 / 256 / 25.4 ≈ Digits × 0.384
427
+
410
428
  #### Time Axis Converter
411
429
 
412
430
  Convert between elapsed seconds and UT time programmatically:
@@ -439,12 +457,65 @@ fig, axes = plt.subplots(1, 2, figsize=(14, 5))
439
457
  spectrum1 = ecl.read_fits("file1.fit.gz")
440
458
  spectrum2 = ecl.read_fits("file2.fit.gz")
441
459
 
442
- ecl.plot_raw_spectrum(spectrum1, ax=axes[0], title="Raw")
460
+ ecl.plot_dynamic_spectrum(spectrum1, process="raw", ax=axes[0], title="Raw")
443
461
  ecl.plot_dynamic_spectrum(
444
- ecl.noise_reduce_mean_clip(spectrum2),
462
+ spectrum2,
463
+ process="noise_reduced",
445
464
  ax=axes[1],
446
465
  title="Noise Reduced",
447
- vmin=-5, vmax=20
466
+ clip_low=-5, clip_high=20
467
+ )
468
+
469
+ plt.tight_layout()
470
+ plt.show()
471
+ ```
472
+
473
+ #### Light Curve Plotting
474
+
475
+ Plot intensity vs time at a specific frequency:
476
+
477
+ ```python
478
+ import ecallistolib as ecl
479
+ import matplotlib.pyplot as plt
480
+
481
+ spectrum = ecl.read_fits("my_spectrum.fit.gz")
482
+
483
+ # Plot raw light curve at 60 MHz
484
+ fig, ax, line = ecl.plot_light_curve(spectrum, frequency_mhz=60, process="raw")
485
+ plt.show()
486
+
487
+ # Plot background-subtracted light curve
488
+ fig, ax, line = ecl.plot_light_curve(
489
+ spectrum, frequency_mhz=60, process="background_subtracted"
490
+ )
491
+ plt.show()
492
+
493
+ # Plot noise-reduced light curve (must provide clip values)
494
+ fig, ax, line = ecl.plot_light_curve(
495
+ spectrum,
496
+ frequency_mhz=60,
497
+ process="noise_reduced",
498
+ clip_low=-5,
499
+ clip_high=20
500
+ )
501
+ plt.show()
502
+ ```
503
+
504
+ Compare all three processing modes:
505
+
506
+ ```python
507
+ import ecallistolib as ecl
508
+ import matplotlib.pyplot as plt
509
+
510
+ spectrum = ecl.read_fits("my_spectrum.fit.gz")
511
+
512
+ fig, axes = plt.subplots(3, 1, figsize=(12, 10))
513
+
514
+ ecl.plot_light_curve(spectrum, 60, process="raw", ax=axes[0], title="Raw")
515
+ ecl.plot_light_curve(spectrum, 60, process="background_subtracted", ax=axes[1], title="BG Sub")
516
+ ecl.plot_light_curve(
517
+ spectrum, 60, process="noise_reduced", ax=axes[2], title="Noise Reduced",
518
+ clip_low=-5, clip_high=20
448
519
  )
449
520
 
450
521
  plt.tight_layout()
@@ -652,36 +723,65 @@ Concatenate spectra horizontally (time concatenation).
652
723
 
653
724
  ### Plotting Functions
654
725
 
655
- #### `plot_dynamic_spectrum(ds, title="...", cmap="inferno", figsize=None, vmin=None, vmax=None, ax=None, show_colorbar=True, time_format="seconds", **imshow_kwargs)`
726
+ #### `plot_dynamic_spectrum(ds, process="raw", clip_low=None, clip_high=None, title=None, cmap="inferno", figsize=None, ax=None, show_colorbar=True, time_format="seconds", intensity_units="digits", **imshow_kwargs)`
656
727
 
657
- Plot a dynamic spectrum with full customization.
728
+ Plot a dynamic spectrum with selectable processing mode.
658
729
 
659
730
  | Parameter | Type | Default | Description |
660
731
  |-----------|------|---------|-------------|
661
732
  | `ds` | `DynamicSpectrum` | — | Spectrum to plot |
662
- | `title` | `str` | `"Dynamic Spectrum"` | Plot title |
733
+ | `process` | `str` | `"raw"` | Processing mode: `"raw"`, `"background_subtracted"`, or `"noise_reduced"` |
734
+ | `clip_low` | `float \| None` | `None` | Lower clipping bound (required for `"noise_reduced"`) |
735
+ | `clip_high` | `float \| None` | `None` | Upper clipping bound (required for `"noise_reduced"`) |
736
+ | `title` | `str \| None` | `None` | Plot title (auto-generated if `None`) |
663
737
  | `cmap` | `str` | `"inferno"` | Matplotlib colormap |
664
738
  | `figsize` | `tuple \| None` | `None` | Figure size as `(width, height)` in inches |
665
- | `vmin` | `float \| None` | `None` | Colormap lower bound (clipping) |
666
- | `vmax` | `float \| None` | `None` | Colormap upper bound (clipping) |
667
739
  | `ax` | `Axes \| None` | `None` | Existing axes (creates new if `None`) |
668
740
  | `show_colorbar` | `bool` | `True` | Whether to display colorbar |
669
741
  | `time_format` | `str` | `"seconds"` | `"seconds"` or `"ut"` for time axis format |
742
+ | `intensity_units` | `str` | `"digits"` | `"digits"` (raw ADU) or `"dB"` (pseudo-calibrated) |
670
743
  | `**imshow_kwargs` | — | — | Additional kwargs passed to `matplotlib.imshow()` |
671
744
 
672
745
  **Returns:** Tuple of `(fig, ax, im)`.
673
746
 
747
+ **Raises:** `ValueError` if `process="noise_reduced"` without `clip_low` and `clip_high`.
748
+
749
+ ---
750
+
751
+ #### `plot_raw_spectrum(ds, clip_low=None, clip_high=None, title=None, cmap="viridis", ...)`
752
+
753
+ Convenience function that calls `plot_dynamic_spectrum` with `process="raw"`.
754
+
674
755
  ---
675
756
 
676
- #### `plot_raw_spectrum(ds, title="Raw Spectrum", cmap="viridis", ...)`
757
+ #### `plot_background_subtracted(ds, clip_low=None, clip_high=None, title=None, cmap="jet", ...)`
677
758
 
678
- Plot raw spectrum data without any processing. Accepts the same parameters as `plot_dynamic_spectrum`.
759
+ Convenience function that calls `plot_dynamic_spectrum` with `process="background_subtracted"`.
679
760
 
680
761
  ---
681
762
 
682
- #### `plot_background_subtracted(ds, title="Background Subtracted", cmap="RdBu_r", ...)`
763
+ #### `plot_light_curve(ds, frequency_mhz, process="raw", title=None, figsize=None, ax=None, time_format="seconds", clip_low=None, clip_high=None, **plot_kwargs)`
764
+
765
+ Plot a light curve (intensity vs time) at a specific frequency.
766
+
767
+ | Parameter | Type | Default | Description |
768
+ |-----------|------|---------|-------------|
769
+ | `ds` | `DynamicSpectrum` | — | Spectrum to extract light curve from |
770
+ | `frequency_mhz` | `float` | — | Target frequency in MHz |
771
+ | `process` | `str` | `"raw"` | Processing mode: `"raw"`, `"background_subtracted"`, or `"noise_reduced"` |
772
+ | `title` | `str \| None` | `None` | Plot title (auto-generated if `None`) |
773
+ | `figsize` | `tuple \| None` | `None` | Figure size as `(width, height)` in inches |
774
+ | `ax` | `Axes \| None` | `None` | Existing axes (creates new if `None`) |
775
+ | `time_format` | `str` | `"seconds"` | `"seconds"` or `"ut"` for time axis format |
776
+ | `clip_low` | `float \| None` | `None` | Lower clip threshold (required for `"noise_reduced"`) |
777
+ | `clip_high` | `float \| None` | `None` | Upper clip threshold (required for `"noise_reduced"`) |
778
+ | `**plot_kwargs` | — | — | Additional kwargs passed to `matplotlib.plot()` |
779
+
780
+ **Returns:** Tuple of `(fig, ax, line)`.
683
781
 
684
- Plot spectrum after background subtraction (before clipping). Automatically applies `background_subtract()` and plots the result. Accepts the same parameters as `plot_dynamic_spectrum`.
782
+ **Raises:**
783
+ - `FrequencyOutOfRangeError` if frequency is outside spectrum's range.
784
+ - `ValueError` if `process="noise_reduced"` without `clip_low` and `clip_high`.
685
785
 
686
786
  ---
687
787
 
@@ -715,6 +815,7 @@ The library provides a hierarchy of custom exceptions for robust error handling:
715
815
  | `DownloadError` | Raised when downloading files from the archive fails |
716
816
  | `CombineError` | Raised when spectra cannot be combined |
717
817
  | `CropError` | Raised when cropping parameters are invalid |
818
+ | `FrequencyOutOfRangeError` | Raised when the requested frequency is outside the spectrum's range |
718
819
 
719
820
  #### Error Handling Example
720
821
 
@@ -0,0 +1,14 @@
1
+ ecallistolib/__init__.py,sha256=MOEjuYi132aHYdFNN8ReRcu95eXOVWFsyRvX2zPb6Os,3003
2
+ ecallistolib/combine.py,sha256=G1ySitPAFbKVNL3u_-Fva4prdYXzq4glIpf8YC8aF5g,3327
3
+ ecallistolib/crop.py,sha256=DactJZ9DtZ50zj7Cp6Ii0F6sCieVloGyzWqMY7HmJ9I,6383
4
+ ecallistolib/download.py,sha256=XbqcLqptKS16D8MP9bWZZprFviQdw1hbt9ctsW1N91s,4114
5
+ ecallistolib/exceptions.py,sha256=e993ColPiVOyOP7Dh7RY4GlRoClwFCPABLiWaS5cLuk,1027
6
+ ecallistolib/io.py,sha256=_uSw6L6Bm1t5GjpVMGtdoF_5NmgEVVDzCXW2nRVFbV8,3686
7
+ ecallistolib/models.py,sha256=Mv8fRWKsbNlMQi20_6oxUz9UX6wkckZAcS9Tj0kH-b4,1299
8
+ ecallistolib/plotting.py,sha256=rEItUrxAXfMj76_MPmoycgfxcbsuWztc3cduWKppkSU,20616
9
+ ecallistolib/processing.py,sha256=enwu8KSome09u4Iv5zjhY80t1HbnGFz3xXf2Wr3Lp7k,1962
10
+ ecallistolib-0.2.3.1.dist-info/licenses/LICENSE,sha256=WunjkzsBGyy9vIQxfNe_GDV1yKBQJ-0WbFt4AXZ5Rvc,1073
11
+ ecallistolib-0.2.3.1.dist-info/METADATA,sha256=DKCz6dCWETaMp-LGv3nzlJmeurCal8zfljHeK8uc0YQ,26292
12
+ ecallistolib-0.2.3.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
13
+ ecallistolib-0.2.3.1.dist-info/top_level.txt,sha256=DmLjR5jlE2i2mQXou5gyCpaHOOlNs4DIQyCPoGXhsbc,13
14
+ ecallistolib-0.2.3.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,14 +0,0 @@
1
- ecallistolib/__init__.py,sha256=9O277WBX99gg3MqWcv-tiaQxB6IKeMfHM91uDJP_9O0,2833
2
- ecallistolib/combine.py,sha256=79LuR2HngpTbaM2xBg7aqUGK_1xkK3RomKmomWG2FpE,3315
3
- ecallistolib/crop.py,sha256=zcAsd5XP-_lG9exBPUFYt4PJslzxLabjSLvbdSADhDk,6383
4
- ecallistolib/download.py,sha256=gUmDLZDjyVIElzqZnGoAREkpOaxYNsAUDeujlGwd_o4,4114
5
- ecallistolib/exceptions.py,sha256=F0EdRCi54ut8qQDtDVZxhGn3P_JbsoQ8hNVxKCFpO5s,879
6
- ecallistolib/io.py,sha256=e-BMtWvILsqs2ua89Ysa2vwZ9s4mDukDGVPYDA3ed-E,3686
7
- ecallistolib/models.py,sha256=v8bl1fN3jKHVbQmpfVSv3ZusFrkWZJ1ciZkuufygmjE,1299
8
- ecallistolib/plotting.py,sha256=RhTJK5qGwCcBRRJP-mDNyGoWzd5diHCbx1IyXK8PraU,12296
9
- ecallistolib/processing.py,sha256=eBH3uGt3RmGM1NRdH_wJ7AAcVeadk9te-pHc9pWy2Lo,1976
10
- ecallistolib-0.2.2.dist-info/licenses/LICENSE,sha256=WunjkzsBGyy9vIQxfNe_GDV1yKBQJ-0WbFt4AXZ5Rvc,1073
11
- ecallistolib-0.2.2.dist-info/METADATA,sha256=rYRt_P0-XByK08F3UfgEaFlZSDyyALh7Ot4EFPkr4Rc,22765
12
- ecallistolib-0.2.2.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
13
- ecallistolib-0.2.2.dist-info/top_level.txt,sha256=DmLjR5jlE2i2mQXou5gyCpaHOOlNs4DIQyCPoGXhsbc,13
14
- ecallistolib-0.2.2.dist-info/RECORD,,