From 36c68fc45f4a6c97d6ef9513c480d1c23c3f968f Mon Sep 17 00:00:00 2001
From: Philipp Niedermayer <p.niedermayer@gsi.de>
Date: Tue, 31 May 2022 13:14:59 +0200
Subject: [PATCH] Documentation

---
 README.md   | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 data.py     | 10 +++++++---
 plotting.py | 43 +++++++++++++++++++++++++++++++++++++-----
 3 files changed, 99 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index 44576cc..f632901 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,61 @@ git submodule init
 git -c http.sslVerify=false submodule update bdiolib
 ```
 
+### Examples
 
+**Tip**: In jupyter notbooks press SHIFT+TAB to show method signature and docstrings
+
+- https://git.gsi.de/p.niedermayer/data-analysis-2022-05-06-exciter/-/blob/main/analysis.ipynb
+- https://git.gsi.de/p.niedermayer/data-analysis-2022-05-08-btf/-/blob/main/analysis.ipynb
+
+
+```python
+from analysis_utils.data import *
+from analysis_utils.fitting import *
+from analysis_utils.plotting import *
+
+fig, ax = plt.subplots(constrained_layout=True)
+
+# Libera bunch-by-bunch data
+data = LiberaBBBData.from_file('libera_ireg_dump.bin').to_tbt_data(h=2)
+plot_tbt(ax, data, 'fxys',
+         turn_range=(0, 1900000), 
+         over_time=True,
+)
+plot_tune_spectrogram(ax, data, 'x', 
+                      over_time=True,
+)
+plot_tune_spectrum(ax, data, 'x', 
+                   tune_range=(0.315,0.335)
+                   smoothing=500, 
+                   fit=fit_lorenzian,
+)
+
+
+# Data from old SIS18 IPM
+data = IPMData.from_file(d['ipm'],
+                         from_event=EVT_MB_TRIGGER,
+)
+ax.imshow(data.x.T, extent=(data.t[0], data.t[-1], data.w[-1], data.w[0]),
+          aspect='auto', rasterized=True, cmap='gist_heat_r', vmin=0, vmax=100
+)
+plot_beam_size(ax, data, 'x',
+               time_range=(2, 4),
+               smoothing=10
+)
+
+
+# Data from SIS18 BTF Network analyser
+data = NWAData.from_file('magnitude.csv', 'phase.csv'],
+                         isdeg=True, unwrap=True)
+data.m_unit = 'dB'
+plot_btf(ax1, ax2, data,
+         frev=854e3
+)
+
+
+
+```
 
 
 
diff --git a/data.py b/data.py
index 471d974..851e406 100644
--- a/data.py
+++ b/data.py
@@ -54,7 +54,8 @@ class LassieSpillData(Trace):
         """Read in time series data from a *.tdf file saved with lassiespill or DCCT application
            
         :param fname: path to filename
-        :param from_event: return data from this event, time will also be relative to this event                    
+        :param from_event: return data from this event, time will also be relative to this event
+        :param time_offset: adds a given time offset (in s) to the timestamps
         
         References: https://git.acc.gsi.de/bi/bdiolib.python
         """
@@ -170,8 +171,9 @@ class IPMData(Trace):
            
         :param fname: path to filename
         :param clean: if True, use calibrated data from adc_clean.dat instead of raw data from adc.dat
-        :param from_event: return data from this event on, time will also be relative to this event
         :param subtract_background: if True, subtract background level prior to injection
+        :param from_event: return data from this event on, time will also be relative to this event
+        :param time_offset: adds a given time offset (in s) to the timestamps
         
         References: 
             Giacomini, Tino <T.Giacomini@gsi.de>
@@ -291,6 +293,8 @@ class LiberaBBBData(LiberaData):
         """Read in bunch-by-bunch data from a *.bin file saved with libera-ireg
         
         :param fname: path to filename
+        :param time_offset: adds a given time offset (in s) to the timestamps
+        
         References: Libera_Hadron_User_Manual_1.04.pdf
         """
         bunch = np.memmap(fname, dtype=np.dtype([
@@ -341,7 +345,7 @@ class NWAData(Trace):
     def from_file(cls, magfile, phasefile=None, *, isdeg=True, unwrap=False, verbose=0):
         """Read in data from CSV file for magnitude and phase
         
-        :param magfile: path to filename with magnitude trace data
+        :param magfile: path to filename with magnitude trace data (and optional phase data, see phasefile)
         :param phasefile: path to filename with phase trace data. If None, phase data is assumed to be the second column in magfile.
         :param unwrap: if true, relative phase is unwraped to absolute phase centered around zero
         :param isdeg: if phase data is in degree (True) or radians (False)
diff --git a/plotting.py b/plotting.py
index e4dd012..d404abd 100644
--- a/plotting.py
+++ b/plotting.py
@@ -64,7 +64,7 @@ def add_resonance_vlines(axes, max_order, color='r'):
                       alpha=1/max(1, m-2), text_vertical=True, text_top=True)
 
 def subplot_shared_labels(axes, xlabel=None, ylabel=None, clear='auto'):
-    """Adds labels to shared axes as needed
+    """Adds and removes labels to shared axes as needed
     
     :param axes: 2D array of axes from subplots (pass squeeze=False to plt.subplots if required)
     :param xlabel: the shared xlabel
@@ -88,6 +88,11 @@ def subplot_shared_labels(axes, xlabel=None, ylabel=None, clear='auto'):
                 axes[r,c].set(ylabel=ylabel)
 
 def grid_diagonal(ax, **kwargs):
+    """Adds a diagonal grid to the given axes
+    
+    :param ax: the axes
+    :param kwargs: optional arguments passed to ax.axline
+    """
     for k, v in dict(color='lightgray',lw=1,zorder=-100).items():
         kwargs.setdefault(k, v)
     xlim, ylim = ax.get_xlim(), ax.get_ylim()
@@ -102,8 +107,14 @@ def grid_diagonal(ax, **kwargs):
     
     
 def fiberplot(ax, datasets, *, labels=[None, None], vertical=True):
-    """Create a 
-    :param datasets: 2D array of datasets (position, x, y) or dict with two levels
+    """Create a fiberplot with multiple datasets of x and y data
+    x is plotted on the horizontal (vertical) axis
+    y determines the height (width) of the fiberplot
+    position of each dataset determines the fiberplot position on the vertical (horizontal) axis
+    label can be used to compare 2 categories where the fiberplot is split into two
+    
+    :param datasets: 2D array of datasets [(position, x, y), ...] or dict with two levels {label: {position: (x,y), ...}, ...}
+    
     """
     if type(datasets) is dict:
         labels = list(datasets.keys())
@@ -163,7 +174,11 @@ def turn_or_time_range(time, turn_range=None, time_range=None):
 def plot_tbt(ax, libera_data, what='fsxy', *, over_time=True, turn_range=None, time_range=None):
     """Plot turn-by-turn data    
     
-    :param what: list of signals to plot: f, s, x, y
+    :param libera_data: instance of LiberaTBTData
+    :param what: signals to plot, any combination of 'f' (revolution frequency), 's' (sum signal), 'x' and/or 'y' (position)
+    :param over_time: if True, plot data as function of time rather than turn
+    :param turn_range: (start, stop) tuple of turns to plot
+    :param time_range: (start, stop) tuple of time in s to plot
     """
     assert isinstance(libera_data, LiberaTBTData), f'Expected LiberaTBTData but got {type(libera_data)}'
     
@@ -196,6 +211,14 @@ def plot_tbt(ax, libera_data, what='fsxy', *, over_time=True, turn_range=None, t
     
 
 def plot_btf(axf, axp, data, *, frev=None, **kwargs):
+    """Plot beam transfer function
+    
+    :param axf: axis for magnitude response
+    :param axp: axis for phase response
+    :param data: instance of NWAData
+    :param frev: if not None, plot fraction of revolution frequency (tune) on x axis
+    :param kwargs: arguments passed to plot function
+    """
     
     f = data.f*(1/frev if frev else 1)
     axf.plot(f, data.m, **kwargs)
@@ -212,7 +235,10 @@ def plot_tune_spectrum(ax, libera_data, xy, turn_range=None, time_range=None, tu
     :param libera_data: Instance of LiberaTBTData class
     :param xy: either 'x' or 'y'
     :param turn_range: tuple of (start_turn, stop_turn) for range to plot
-    :param turn_range: tuple of (start_time, stop_time) in seconds for range to plot
+    :param time_range: tuple of (start_time, stop_time) in seconds for range to plot
+    :param tune_range: tuple of (start_tune, stop_tune) for range to plot
+    :param fit: if True or any of (fit_lorenzian, fit_gaussian), determine the tune from a fit on the spectrum
+    :param smoothing: if specified, apply a moving average smoothing filter of this width to the data
     """
     assert isinstance(libera_data, LiberaTBTData), f'Expected LiberaTBTData but got {type(libera_data)}'
     
@@ -417,6 +443,13 @@ def plot_tune_spectrogram(ax, libera_data, xy, *, nperseg=2**12, noverlap=None,
 ###########
 
 def plot_beam_size(ax, ipm_data, xy, time_range=None, smoothing=None, **kwargs):
+    """Plot beam size as function of time from fitted IPM data
+    
+    :param ipm_data: instance of IPMData
+    :param xy: plane to consider, either 'x' or 'y'
+    :param time_range: range of (start, stop) time in s to plot
+    :param smoothing: if specified, apply a moving average smoothing filter of this width to the data
+    """
     mask = irng(ipm_data.t, *time_range)
     data = getattr(ipm_data, xy)[mask]    
     
-- 
GitLab