lab1tools 0.1.0__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.
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.3
2
+ Name: lab1tools
3
+ Version: 0.1.0
4
+ Summary: Tools used in the first lab of Astron121.
5
+ Author: LeoIntril
6
+ Author-email: LeoIntril <leo_intriligator@berkeley.edu>
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+
File without changes
@@ -0,0 +1,17 @@
1
+ [project]
2
+ name = "lab1tools"
3
+ version = "0.1.0"
4
+ description = "Tools used in the first lab of Astron121."
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "LeoIntril", email = "leo_intriligator@berkeley.edu" }
8
+ ]
9
+ requires-python = ">=3.10"
10
+ dependencies = []
11
+
12
+ [project.scripts]
13
+ lab1tools = "lab1tools:main"
14
+
15
+ [build-system]
16
+ requires = ["uv_build>=0.10.0,<0.11.0"]
17
+ build-backend = "uv_build"
@@ -0,0 +1,3 @@
1
+ from .functions import compute_lags, compute_fwhm, gaussian, compute_period, freq_from_filename
2
+
3
+ __all__ = ['compute_lags', 'compute_fwhm', 'gaussian', 'compute period', 'freq_from_filename']
@@ -0,0 +1,68 @@
1
+ import numpy as np
2
+ import matplotlib.pyplot as plt
3
+ from pathlib import Path
4
+ import re
5
+ from scipy.signal import correlate
6
+ from scipy.stats import norm
7
+
8
+ def freq_from_filename(fname):
9
+ match = re.search(r"([\d\.]+)Hz", fname)
10
+ if match is None:
11
+ raise ValueError(f"Could not extract frequency from {fname}")
12
+ return float(match.group(1)) # Hz
13
+
14
+ def compute_period(y, fs):
15
+ y = y - np.mean(y) # remove DC
16
+ # Find zero crossings (sign change)
17
+ zero_crossings = np.where(np.diff(np.sign(y)) > 0)[0] # positive-going
18
+ if len(zero_crossings) < 2:
19
+ return None # cannot compute period
20
+ # Compute periods in samples
21
+ periods_samples = np.diff(zero_crossings)
22
+ # Convert to time in seconds
23
+ periods_sec = periods_samples / fs
24
+ return np.mean(periods_sec)
25
+
26
+ def compute_lags(n, fs):
27
+ return np.arange(-n+1, n)/fs
28
+
29
+ def compute_fwhm(x, y):
30
+ """
31
+ Compute FWHM of a peak in y(x) using linear interpolation.
32
+ Returns NaN if no half-max points are found.
33
+ """
34
+ y = np.array(y)
35
+ x = np.array(x)
36
+
37
+ y_max = np.max(y)
38
+ half_max = y_max / 2
39
+
40
+ # Indices where y crosses half-max
41
+ indices = np.where(y >= half_max)[0]
42
+ if len(indices) < 2:
43
+ return np.nan # not enough points to define FWHM
44
+
45
+ left_idx = indices[0]
46
+ right_idx = indices[-1]
47
+
48
+ # Linear interpolation for left half-max
49
+ if left_idx == 0:
50
+ x_left = x[0]
51
+ else:
52
+ x1, x2 = x[left_idx-1], x[left_idx]
53
+ y1, y2 = y[left_idx-1], y[left_idx]
54
+ x_left = x1 + (half_max - y1) * (x2 - x1) / (y2 - y1)
55
+
56
+ # Linear interpolation for right half-max
57
+ if right_idx == len(y)-1:
58
+ x_right = x[-1]
59
+ else:
60
+ x1, x2 = x[right_idx], x[right_idx+1]
61
+ y1, y2 = y[right_idx], y[right_idx+1]
62
+ x_right = x1 + (half_max - y1) * (x2 - x1) / (y2 - y1)
63
+
64
+ return x_right - x_left
65
+
66
+ def gaussian(x, A, sigma):
67
+ return A * np.exp(-0.5*(x/sigma)**2)
68
+
@@ -0,0 +1,3 @@
1
+ from .functions import compute_lags, compute_fwhm, gaussian, compute_period, freq_from_filename
2
+
3
+ __all__ = ['compute_lags', 'compute_fwhm', 'gaussian', 'compute period', 'freq_from_filename']
@@ -0,0 +1,68 @@
1
+ import numpy as np
2
+ import matplotlib.pyplot as plt
3
+ from pathlib import Path
4
+ import re
5
+ from scipy.signal import correlate
6
+ from scipy.stats import norm
7
+
8
+ def freq_from_filename(fname):
9
+ match = re.search(r"([\d\.]+)Hz", fname)
10
+ if match is None:
11
+ raise ValueError(f"Could not extract frequency from {fname}")
12
+ return float(match.group(1)) # Hz
13
+
14
+ def compute_period(y, fs):
15
+ y = y - np.mean(y) # remove DC
16
+ # Find zero crossings (sign change)
17
+ zero_crossings = np.where(np.diff(np.sign(y)) > 0)[0] # positive-going
18
+ if len(zero_crossings) < 2:
19
+ return None # cannot compute period
20
+ # Compute periods in samples
21
+ periods_samples = np.diff(zero_crossings)
22
+ # Convert to time in seconds
23
+ periods_sec = periods_samples / fs
24
+ return np.mean(periods_sec)
25
+
26
+ def compute_lags(n, fs):
27
+ return np.arange(-n+1, n)/fs
28
+
29
+ def compute_fwhm(x, y):
30
+ """
31
+ Compute FWHM of a peak in y(x) using linear interpolation.
32
+ Returns NaN if no half-max points are found.
33
+ """
34
+ y = np.array(y)
35
+ x = np.array(x)
36
+
37
+ y_max = np.max(y)
38
+ half_max = y_max / 2
39
+
40
+ # Indices where y crosses half-max
41
+ indices = np.where(y >= half_max)[0]
42
+ if len(indices) < 2:
43
+ return np.nan # not enough points to define FWHM
44
+
45
+ left_idx = indices[0]
46
+ right_idx = indices[-1]
47
+
48
+ # Linear interpolation for left half-max
49
+ if left_idx == 0:
50
+ x_left = x[0]
51
+ else:
52
+ x1, x2 = x[left_idx-1], x[left_idx]
53
+ y1, y2 = y[left_idx-1], y[left_idx]
54
+ x_left = x1 + (half_max - y1) * (x2 - x1) / (y2 - y1)
55
+
56
+ # Linear interpolation for right half-max
57
+ if right_idx == len(y)-1:
58
+ x_right = x[-1]
59
+ else:
60
+ x1, x2 = x[right_idx], x[right_idx+1]
61
+ y1, y2 = y[right_idx], y[right_idx+1]
62
+ x_right = x1 + (half_max - y1) * (x2 - x1) / (y2 - y1)
63
+
64
+ return x_right - x_left
65
+
66
+ def gaussian(x, A, sigma):
67
+ return A * np.exp(-0.5*(x/sigma)**2)
68
+