barsukov 1.3.3__py3-none-any.whl → 1.3.5__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.

Potentially problematic release.


This version of barsukov might be problematic. Click here for more details.

@@ -0,0 +1,297 @@
1
+ from PyQt5 import QtWidgets
2
+ import pyqtgraph as pg
3
+ import numpy as np
4
+ import sys
5
+
6
+ from barsukov.data import Lock_in_emulator, noise
7
+
8
+ def make_lorentzian(center, HW, amp):
9
+ def l(x):
10
+ return amp / ((x - center)**2 + HW**2)
11
+ return l
12
+
13
+ def make_antilorentzian(center, HW, amp):
14
+ def al(x):
15
+ return amp * (x - center) / np.abs(HW) / (1 + ( (x - center) / HW)**2)
16
+ return al
17
+
18
+ params = [
19
+ ("l_center", 2, "Center:", float),
20
+ ("l_HW", 1, "HW:", float),
21
+ ("l_amp", 1e-6, "Amplitude:", float),
22
+ ("al_center", 4, "Center:", float),
23
+ ("al_HW", 2, "HW:", float),
24
+ ("al_amp", 2e-6, "Amplitude:", float),
25
+ ("jT", 300, "Temperature (K):", float),
26
+ ("jR", 200, "Resistance (Ohms):", float),
27
+ ("sI", 1e-3, "Current (Amps):", float),
28
+ ("sR", 200, "Resistance (Ohms):", float),
29
+ ("oRMS", 1e-7, "RMS:", float),
30
+ ("rTU", 0.01, "Tau Up (s):", float),
31
+ ("rTD", 0.01, "Tau Down (s):", float),
32
+ ("rSU", 1e-7, "State Up:", float),
33
+ ("rSD", 1e-7, "State Down:", float),
34
+ ("bD", 64, "Bit Depth (bits):", int),
35
+ ("bMIN", -21, "Minimum Measurement:", float),
36
+ ("bMAX", 21, "Maximum Measurement:", float),
37
+ ("xstart_input", 0, "X Start:", float),
38
+ ("plotpoints_input", 500, "# Plot Points:", int),
39
+ ("time_input", 120, "Sweep Time (s):", float),
40
+ ("xstop_input", 10, "X Stop:", float),
41
+ ("xamp_input", 0.2, "Modulation Amp:", float),
42
+ ("f_input", 1000, "Modulation Freq (Hz):", float),
43
+ ("TC_input", 500e-3, "Time Constant (s):", float),
44
+ ("order_input", 4, "Filter Order:", int),
45
+ ("dt_input", 1e-4, "Sampling Step (s):", float),
46
+ ("buffersize_input", 10000, "Buffer Size:", int),
47
+ ("phase_input", 0, "Phase Offset (deg):", float),
48
+ ]
49
+
50
+ class MyWindow(QtWidgets.QWidget):
51
+ def __init__(self):
52
+ super().__init__()
53
+ self.setWindowTitle("Lock-in Amplifier Emulator")
54
+ self.resize(1000, 800)
55
+
56
+ #Window Area (horizontal)
57
+ main_layout = QtWidgets.QHBoxLayout(self)
58
+ self.inputs = {} # (widget, cast, method)
59
+ self.line_edits = {}
60
+
61
+ #Interactive Input Box Setup
62
+ for name, default, label, cast in params:
63
+ le = QtWidgets.QLineEdit(str(default))
64
+ #le.editingFinished.connect(self.update_plot)
65
+ self.line_edits[name] = le
66
+ self.inputs[name] = (le, lambda w=le, c=cast: c(w.text()))
67
+
68
+ #Left Area:
69
+ left_layout = QtWidgets.QVBoxLayout()
70
+ main_layout.addLayout(left_layout, 1)
71
+
72
+ # Signal Type Selector
73
+ signal_combo = QtWidgets.QComboBox()
74
+ signal_combo.addItems(["Lorentzian", "Anti-Lorentzian"])
75
+ #signal_combo.currentIndexChanged.connect(self.update_plot)
76
+ self.inputs["signal_type"] = (signal_combo, lambda w: str(w.currentText()))
77
+ left_layout.addWidget(QtWidgets.QLabel("<b>Signal Type:</b>"))
78
+ left_layout.addWidget(signal_combo)
79
+
80
+ # Signal Inputs Stack
81
+ signal_stack = QtWidgets.QStackedWidget()
82
+ left_layout.addWidget(signal_stack)
83
+
84
+ #Lorentzian Inputs
85
+ lorentz_widget = QtWidgets.QWidget()
86
+ lorentz_layout = QtWidgets.QFormLayout(lorentz_widget)
87
+ lorentz_layout.addRow("<b>Center:</b>", self.line_edits["l_center"])
88
+ lorentz_layout.addRow("<b>HW:</b>", self.line_edits["l_HW"])
89
+ lorentz_layout.addRow("<b>Amplitude:</b>", self.line_edits["l_amp"])
90
+ signal_stack.addWidget(lorentz_widget)
91
+
92
+ #Guassian Inputs
93
+ antilorentz_widget = QtWidgets.QWidget()
94
+ antilorentz_layout = QtWidgets.QFormLayout(antilorentz_widget)
95
+ antilorentz_layout.addRow("<b>Center:</b>", self.line_edits["al_center"])
96
+ antilorentz_layout.addRow("<b>HW:</b>", self.line_edits["al_HW"])
97
+ antilorentz_layout.addRow("<b>Amplitude:</b>", self.line_edits["al_amp"])
98
+ signal_stack.addWidget(antilorentz_widget)
99
+
100
+ signal_combo.currentIndexChanged.connect(signal_stack.setCurrentIndex)
101
+
102
+ #Noise Options:
103
+ left_layout.addWidget(QtWidgets.QLabel("<b>Noise Options:</b>"))
104
+
105
+ noises = [
106
+ ("Johnson Noise", "Temperature (K):,jT", "Resistance (Ohms):,jR"),
107
+ ("Shot Noise", "Current (Amps):,sI", "Resistance (Ohms):,sR"),
108
+ ("1/f Noise", "RMS:,oRMS"),
109
+ ("Random Telegraph Noise", "Tau Up (s):,rTU", "Tau Down (s):,rTD", "State Up:,rSU", "State Down:,rSD"),
110
+ ("Bit Noise", "Bit Depth (bits):,bD", "Minimum Measurement:,bMIN", "Maximum Measurement:,bMAX")
111
+ ]
112
+
113
+ for noise in noises:
114
+ #Noise Checkbox
115
+ cb = QtWidgets.QCheckBox(noise[0])
116
+ self.inputs[noise[0]] = (cb, lambda w=cb: w.isChecked())
117
+ left_layout.addWidget(cb)
118
+
119
+ # Group of Noise Inputs
120
+ group = QtWidgets.QGroupBox(noise[0] + " Settings")
121
+ group.setCheckable(False)
122
+ group.setVisible(False)
123
+ form = QtWidgets.QFormLayout(group)
124
+ for p in noise[1:]:
125
+ label, name = p.split(",")
126
+ form.addRow("<b>"+label+"</b>", self.line_edits[name])
127
+ left_layout.addWidget(group)
128
+
129
+ cb.toggled.connect(group.setVisible)
130
+ #cb.toggled.connect(self.update_plot)
131
+
132
+ left_layout.addStretch(1)
133
+
134
+ # Simulate Button
135
+ simulate_button = QtWidgets.QPushButton("Simulate")
136
+ simulate_button.clicked.connect(self.update_plot)
137
+ left_layout.addWidget(simulate_button)
138
+
139
+ # Reset Button
140
+ reset_button = QtWidgets.QPushButton("Reset")
141
+ reset_button.clicked.connect(self.reset_fields)
142
+ left_layout.addWidget(reset_button)
143
+
144
+ # User Notes
145
+ left_layout.addWidget(QtWidgets.QLabel("<b>User Notes:</b>"))
146
+ user_notes = QtWidgets.QLabel()
147
+ user_notes.setText("- SweepTime ≥ SamplingStep*BufferSize*#PlotPoints\n"
148
+ "- Runs well up to 50 million calculations\n"
149
+ " EX: 500 PlotPoints * 100000 BufferSize")
150
+ left_layout.addWidget(user_notes)
151
+
152
+ #Right Area (vertical) - right side of window
153
+ right_layout = QtWidgets.QVBoxLayout()
154
+ main_layout.addLayout(right_layout, 4)
155
+
156
+ #Graph 1:
157
+ plot1 = pg.PlotWidget(title="Original Signal vs X", background='w')
158
+ legend1 = plot1.addLegend()
159
+ legend1.anchor((1,0), (1,0))
160
+ right_layout.addWidget(plot1)
161
+
162
+ #Input Area 1:
163
+ input_layout1 = QtWidgets.QGridLayout()
164
+ cols = 4
165
+ for i, p in enumerate(params[18:22]):
166
+ name, label = p[0], p[2]
167
+ row = i // cols
168
+ col = i % cols
169
+
170
+ h_layout = QtWidgets.QHBoxLayout()
171
+ h_layout.addWidget(QtWidgets.QLabel("<b>"+label+"</b>"))
172
+ h_layout.addWidget(self.line_edits[name])
173
+ input_layout1.addLayout(h_layout, row, col)
174
+ right_layout.addLayout(input_layout1)
175
+
176
+ #Fit Results:
177
+ result_layout = QtWidgets.QHBoxLayout()
178
+ self.result_text = QtWidgets.QLabel()
179
+ result_layout.addWidget(self.result_text)
180
+ result_layout.addStretch(1)
181
+
182
+ #Label:
183
+ output2expected = QtWidgets.QLabel()
184
+ output2expected.setText("Output ≈ <sup>1</sup>&frasl;<sub>Diminish</sub> * Expected(<sup>x</sup>&frasl;<sub>Stretch</sub> - Shift)")
185
+ right_layout.addWidget(output2expected)
186
+
187
+ #Graph 2:
188
+ plot2 = pg.PlotWidget(title="Demodulated Signal vs X", background='w')
189
+ legend2 = plot2.addLegend()
190
+ legend2.anchor((1,0), (1,0))
191
+ right_layout.addWidget(plot2)
192
+
193
+ #Input Area 2:
194
+ input_layout2 = QtWidgets.QGridLayout()
195
+ for i, p in enumerate(params[22:]):
196
+ name, label = p[0], p[2]
197
+ row = i // cols
198
+ col = i % cols
199
+
200
+ h_layout = QtWidgets.QHBoxLayout()
201
+ h_layout.addWidget(QtWidgets.QLabel("<b>"+label+"</b>"))
202
+ h_layout.addWidget(self.line_edits[name])
203
+ input_layout2.addLayout(h_layout, row, col)
204
+ right_layout.addLayout(input_layout2)
205
+
206
+ #Plot Curve Initialization:
207
+ self.curve_orig = plot1.plot(pen=pg.mkPen(color='r', width=2), name="Original Signal")
208
+ self.curve_out = plot2.plot(pen=pg.mkPen(color='b', width=2), name="Output Signal")
209
+ self.curve_expected = plot2.plot(pen=pg.mkPen(color='r', width=2), name="Expected Signal")
210
+ self.curve_adjusted = plot2.plot(pen=pg.mkPen(color='g', width=2), name="Adjusted Signal")
211
+
212
+ #Show Curves Options:
213
+ curve_params = [ ("Output", self.curve_out), ("Expected", self.curve_expected), ("Adjusted", self.curve_adjusted) ]
214
+ for name, curve in curve_params:
215
+ cb = QtWidgets.QCheckBox(f"Show {name} Signal")
216
+ cb.setChecked(True)
217
+ cb.stateChanged.connect(lambda state, c=curve, box=cb: c.setVisible(box.isChecked()))
218
+ result_layout.addWidget(cb)
219
+ right_layout.insertLayout(2, result_layout)
220
+
221
+ self.update_plot()
222
+
223
+ def update_plot(self):
224
+ try:
225
+ # Read input v
226
+ v = {}
227
+ for name, (widget, extract) in self.inputs.items():
228
+ v[name] = extract(widget)
229
+
230
+ #Signal Setup
231
+ signal = 0
232
+ if v["signal_type"] == "Lorentzian":
233
+ signal = make_lorentzian(v["l_center"], v["l_HW"], v["l_amp"])
234
+ else:
235
+ signal = make_antilorentzian(v["al_center"], v["al_HW"], v["al_amp"])
236
+
237
+ #Noise Setup
238
+ jT = jR = sI = sR = oRMS = rTU = rTD = rSU = rSD = bD = bMIN = bMAX = 0
239
+ if v["Johnson Noise"]:
240
+ jT, jR = v["jT"], v["jR"]
241
+ if v["Shot Noise"]:
242
+ sI, sR = v["sI"], v["sR"]
243
+ if v["1/f Noise"]:
244
+ oRMS = v["oRMS"]
245
+ if v["Random Telegraph Noise"]:
246
+ rTU, rTD, rSU, rSD = v["rTU"], v["rTD"], v["rSU"], v["rSD"]
247
+ if v["Bit Noise"]:
248
+ bD, bMIN, bMAX = v["bD"], v["bMIN"], v["bMAX"]
249
+
250
+ # Run lock-in emulator
251
+ LI = Lock_in_emulator(
252
+ signal,
253
+ v["f_input"],
254
+ v["phase_input"],
255
+ v["xstart_input"],
256
+ v["xstop_input"],
257
+ v["xamp_input"],
258
+ v["time_input"],
259
+ v["dt_input"],
260
+ v["TC_input"],
261
+ v["order_input"],
262
+ v["plotpoints_input"],
263
+ v["buffersize_input"],
264
+ jT, jR, sI, sR, oRMS, rTU, rTD, rSU, rSD, bD, bMIN, bMAX
265
+ )
266
+ LI.run()
267
+
268
+ # Update plots
269
+ self.curve_orig.setData(LI.x_plot, LI.original_signal)
270
+ self.curve_out.setData(LI.x_plot, LI.output_signal)
271
+ self.curve_expected.setData(LI.x_plot, LI.expected_signal)
272
+ self.curve_adjusted.setData(LI.x_plot, LI.adjusted_signal)
273
+
274
+ # Update results
275
+ self.result_text.setText(f"<b>Diminish:</b> {LI.diminish:.6f}, "
276
+ f"<b>Shift:</b> {LI.shift:.6f}, "
277
+ f"<b>Stretch:</b> {LI.stretch:.6f}, "
278
+ f"<b>SNR</b>: {LI.snr:.6f}")
279
+
280
+ except Exception as e:
281
+ self.result_text.setText(f"Error: {e}")
282
+
283
+ def reset_fields(self):
284
+ for p in params:
285
+ name, default = p[0], p[1]
286
+ self.line_edits[name].clear()
287
+ self.line_edits[name].setText(str(default))
288
+ self.update_plot()
289
+
290
+ def run():
291
+ app = QtWidgets.QApplication(sys.argv)
292
+ w = MyWindow()
293
+ w.show()
294
+ sys.exit(app.exec_())
295
+
296
+ if __name__ == "__main__":
297
+ run()
barsukov/data/noise.py ADDED
@@ -0,0 +1,276 @@
1
+ import numpy as np
2
+
3
+ K_b = 1.380649e-23
4
+ q = 1.602176634e-19
5
+
6
+ def johnson(time_arr, T, R):
7
+ #CHECKED
8
+ """
9
+ Generates Johnson-Nyquist noise voltage signal in the time domain, for a given time array.
10
+
11
+ Parameters:
12
+ time_arr (array-like): Array of ordered time values in Seconds (assumed to be evenly spaced).
13
+ T (float): Temperature in Kelvin.
14
+ R (float): Resistance in Ohms.
15
+
16
+ Returns:
17
+ np.ndarray: Array of normally distributed noise values with RMS amplitude corresponding to the thermal noise voltage.
18
+ """
19
+ size = len(time_arr)
20
+ Df = 0.5 * (size - 1) / (time_arr[-1] - time_arr[0]) # Nyquist Frequency is this correct. Becomes noise's bandwidth
21
+ # Nyquist Frequency used for bandwidth as it is the maximum resolvable frequency that can be detected. Generating noise purely by itself, before any filtering is being simulated.
22
+
23
+ V_rms = np.sqrt(4 * K_b * T * R * Df)
24
+ return np.random.normal(0, V_rms, size)
25
+
26
+ def shot(time_arr, I, R):
27
+ #CHECKED
28
+ """
29
+ Generates Shot noise voltage signal in the time domain, for a given time array.
30
+
31
+ Parameters:
32
+ time_arr (array-like): Array of ordered time values in Seconds (assumed to be evenly spaced).
33
+ I (float): Current in Amperes.
34
+ R (float): Resistance in Ohms.
35
+
36
+ Returns:
37
+ np.ndarray: Array of normally distributed voltage noise values with RMS amplitude corresponding to shot noise.
38
+
39
+ Note:
40
+ Ideally, shot noise follows a Poisson distribution, but for large mean values (lambda), the Poisson distribution approximates a normal distribution.
41
+ """
42
+
43
+ size = len(time_arr)
44
+ Df = 0.5 * (size - 1) / (time_arr[-1] - time_arr[0])
45
+
46
+ I_rms = np.sqrt(2 * q * I * Df) # Shot noise current
47
+ V_rms = R * I_rms # Recalculating to Shot noise voltage
48
+ return np.random.normal(0, V_rms, size) #poisson should be used, but lamda is too large for np.random.poisson
49
+
50
+ def color(time_arr, V_rms, exponent=1):
51
+ """
52
+ Generates 1/f^(exponent) noise (pink noise by default, exp=1) in the time domain.
53
+
54
+ Parameters:
55
+ time_arr (array-like): Array of ordered time values in Seconds (assumed to be evenly spaced).
56
+ V_rms (float): RMS value of the generated noise.
57
+ exponent (float, optional): Power of the frequency dependence. Default is 1, which gives 1/f noise (pink noise). Set to 0 for white noise, 2 for brown noise, etc...
58
+
59
+ Returns:
60
+ np.ndarray: Array of noise values with PSD proportional to 1/f^(exponent).
61
+ """
62
+ #Generate Guassian White Noise in time domain
63
+ size = len(time_arr)
64
+ dt = (time_arr[-1] - time_arr[0]) / (size-1.0)
65
+ white = np.random.standard_normal(size)
66
+
67
+ #Fourier Transform to Frequency Domain
68
+ freqs = np.fft.rfftfreq(size, d=dt)
69
+ fft = np.fft.rfft(white, norm='backward') * dt
70
+
71
+ #Scale Fourier Transform by 1/f^(exponent/2) for 1/f^exponent PSD (psd proportional to fft^2)
72
+ freqs[0] = freqs[1] #Avoid division by zero
73
+ fft = fft / (freqs**(exponent*0.5))
74
+
75
+ #Convert back to time domain
76
+ ifft = (np.fft.irfft(fft, n=size, norm='forward') / dt).real
77
+ ifft_rms = np.sqrt(np.mean(ifft**2))
78
+
79
+ return V_rms * ifft / ifft_rms #Ensure V_rms is as specified
80
+
81
+
82
+ def rtn(time_arr, tau_up, tau_down, state_up=1, state_down=0, initial_state=None):
83
+ """
84
+ Generate random telegraph noise on a user-supplied time array.
85
+
86
+ Parameters:
87
+ time_arr (np.ndarray): Array of ordered time values in Seconds (assumed to be evenly spaced).
88
+ tau_up (float): Mean dwell time in the 'up' state.
89
+ tau_down (float): Mean dwell time in the 'down' state.
90
+ state_up (float): Value of the up state (default: 1).
91
+ state_down (float): Value of the down state (default: 0).
92
+ initial_state (float): Value of the first state (default: None = random(up, down))
93
+
94
+ Returns:
95
+ signal (np.ndarray): RTN signal array, same shape as time_arr.
96
+
97
+ Notes:
98
+ - PSD of RTN will have lorentzian profile: S(f) = 4*A^2*tau / (1 + (2pi*f*tau)^2),
99
+ with correlation time: tau = tau_up*tau_down / (tau_up+tau_down)
100
+ - Characteristic (roll-off) frequency corresponds to 1 / (2pi*tau)
101
+ """
102
+ if tau_up <= 0 or tau_down <= 0:
103
+ raise ValueError("tau_up and tau_down must be positive to avoid infinite loops.")
104
+
105
+ time_arr = np.asarray(time_arr)
106
+ signal = np.zeros_like(time_arr)
107
+
108
+ if initial_state is None:
109
+ current_state = np.random.choice([state_up,state_down])
110
+ else: current_state = initial_state
111
+
112
+ current_time = time_arr[0]
113
+ i = 0
114
+
115
+ while i < len(time_arr):
116
+ # Sample dwell time
117
+ dwell_time = np.random.exponential(tau_up if current_state == state_up else tau_down)
118
+ dwell_end_time = current_time + dwell_time
119
+
120
+ # Assign current state until dwell time is over
121
+ while i < len(time_arr) and time_arr[i] < dwell_end_time:
122
+ signal[i] = current_state
123
+ i += 1
124
+
125
+ # Flip state
126
+ current_time = dwell_end_time
127
+ current_state = state_down if current_state == state_up else state_up
128
+
129
+ return signal
130
+
131
+
132
+ def bit(signal_arr, bit_depth, measure_min, measure_max, noise_only = False):
133
+ """
134
+ Quantize an analog signal to simulate ADC behavior with given bit depth.
135
+
136
+ Parameters:
137
+ signal_arr (array-like): Input analog signal values.
138
+ bit_depth (int): number of bits used in quantization (e.g., 8, 12, 16).
139
+ measure_min (float): Minimum measurable value of the ADC range.
140
+ measure_max (float): Maximum measurable value of the ADC range.
141
+ noise_only (bool, optional): If True, quantization noise only. If False (default), return the quantized signal.
142
+
143
+ Returns:
144
+ np.ndarray: Quantized signal or Quantization noise, depending on 'noise_only'.
145
+
146
+ Notes:
147
+ - The signal is clipped to the measurement range before quantization.
148
+ - The number of quantization levels is 2^bit_depth.
149
+ """
150
+ levels = int(2**int(bit_depth)) # 1<<int(bit_depth)
151
+ quantization_step = (measure_max - measure_min) / (levels - 1)
152
+
153
+ signal_clipped = np.clip(signal_arr, measure_min, measure_max)
154
+ quantized_signal = np.round((signal_clipped - measure_min) / quantization_step) * quantization_step + measure_min
155
+
156
+ if noise_only is False:
157
+ return quantized_signal
158
+ else:
159
+ return quantized_signal - signal_arr
160
+
161
+
162
+ def psd(time_arr, signal_arr, return_onesided=True):
163
+ """
164
+ Computes the Power Spectral Density (PSD) of a time-domain signal using Welch's method.
165
+
166
+ Parameters:
167
+ time_arr (array-like): Ordered time values in Seconds (assumed to be increasing).
168
+ signal_arr (array-like): Signal values as a function of time.
169
+
170
+ Returns:
171
+ tuple:
172
+ - f_welch (np.ndarray): Array of frequency values corresponding to the PSD.
173
+ - psd_welch (np.ndarray): Power Spectral Density values in (signal_unit)^2/Hz.
174
+ - msg (str): Message describing the PSD normalization and units.
175
+
176
+ Notes:
177
+ - Welch's method averages overlapping FFTs to reduce variance in the PSD estimate.
178
+ - The RMS value of the signal over a bandwidth B can be computed from the PSD as:
179
+ V_rms = sqrt( ∫ PSD(f) df ) over bandwidth.
180
+ - The PSD values follow the convention:
181
+ mean(PSD) ≈ 2 × (b.fft)^2 / total duration
182
+ -When dealing with real signals, (one sided, only positive frequencies) the relationship is psd = np.abs(fft)**2 / total_time * 2, where factor of 2 is applied everywhere except at the DC and Nyquist bins. When dealing with complex signals (positive and negative) the relationship is psd = np.abs(fft)**2 / total_time.
183
+ """
184
+ from scipy import signal as sp_signal
185
+ #make equidistant
186
+ size = len(time_arr)
187
+ sampling_freq = (size-1) / (time_arr[-1] - time_arr[0])
188
+
189
+ f_welch, psd_welch = sp_signal.welch(signal_arr, sampling_freq, nperseg=min(size, 4024), return_onesided=return_onesided)
190
+ return f_welch, psd_welch
191
+
192
+ def noise_help(noise):
193
+ import matplotlib.pyplot as plt
194
+
195
+ doc = {}
196
+
197
+ if noise == "johnson":
198
+ time = np.arange(0,100)
199
+
200
+ doc = {
201
+ "name": "Johnson Noise",
202
+ "time": time,
203
+ "function": johnson,
204
+ "params": [300, 200],
205
+ "text_title_size": 20,
206
+ "text_midbreak_size": 15,
207
+ "text_main_size": 10,
208
+ "text_bullet_size": 10,
209
+ "text_title_x": .5,
210
+ "text_title_y": 9,
211
+ "text_main_x": .3,
212
+ "text_main_y": 8,
213
+ }
214
+
215
+ if noise == "johnson2":
216
+ time = np.arange(0,100)
217
+
218
+ doc = {
219
+ "name": "Johnson Noise",
220
+ "time": time,
221
+ "function": johnson,
222
+ "params": [300, 200],
223
+ "text_title_size": 20,
224
+ "text_midbreak_size": 15,
225
+ "text_main_size": 10,
226
+ "text_bullet_size": 10,
227
+ "text_title_x": .5,
228
+ "text_title_y": 9,
229
+ "text_main_x": .3,
230
+ "text_main_y": 6,
231
+ }
232
+
233
+ if noise == "shot":
234
+ time = np.arange(0,100)
235
+
236
+ doc = {
237
+ "name": "Shot Noise",
238
+ "time": time,
239
+ "function": shot,
240
+ "params": [1, 200],
241
+ "text_title_size": 30,
242
+ "text_midbreak_size": 15,
243
+ "text_main_size": 15,
244
+ "text_bullet_size":10,
245
+ "text_title_x": .5,
246
+ "text_title_y": 9,
247
+ "text_main_x": .5,
248
+ "text_main_y": 8,
249
+ }
250
+
251
+
252
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(22,7))
253
+ fig.suptitle(doc["name"], fontsize=20)
254
+ ax1.plot(doc["time"], doc["function"](doc["time"], *doc["params"]), 'red', label='(t^2)*exp(-(t^2))')
255
+
256
+
257
+ ax1.set(title="2 lines chart", xlabel="t", ylabel="y")
258
+ ax1.legend(loc="upper right")
259
+
260
+
261
+ # Set both x- and y-axis limits to [0, 10] instead of default [0, 1]
262
+
263
+
264
+ ax2.axis([0, 10, 0, 10])
265
+ ax2.tick_params(axis='x', colors='white')
266
+ ax2.tick_params(axis='y', colors='white')
267
+
268
+ ax2.text(doc["text_title_x"], doc["text_title_y"], doc["name"], weight='bold', fontsize=doc["text_title_size"],
269
+ bbox={'facecolor': 'red', 'alpha': 0.3, 'pad': 10})
270
+ ax2.text(doc["text_main_x"], doc["text_main_y"], doc["function"].__doc__, wrap="false", fontsize=doc["text_main_size"])
271
+
272
+
273
+ #use for bullet points#ax2.text(0.5, 5.5, 'Topic 2', weight='bold', fontsize=doc["text_midbreak_size"])
274
+ #use for bullet points#ax2.text(0.5, 4.5, '- Bullet pont 1\n- Bullet point 2', fontsize= doc["text_bullet_size"])
275
+
276
+ #use to display function # ax2.text(2, 3, r'a function to plot: $t^2*exp(-t^2)$', fontsize=12)
barsukov/exp/__init__.py CHANGED
@@ -1 +1,3 @@
1
1
  from . import mwHP
2
+
3
+ from . import smKE
barsukov/exp/mwHP.py CHANGED
@@ -55,6 +55,8 @@ class mwHP:
55
55
  log_in_eq(self, msg, log=log)
56
56
  ### END These functions could be shared across all equipment.
57
57
 
58
+
59
+ ### BEGIN: mwHP Functions:
58
60
  def output(self, state=None, log=None, check=False):
59
61
  ### Always has a return! Which is the state of Output.
60
62
  ### output() reads and returns the state of Output.
@@ -231,6 +233,7 @@ class mwHP:
231
233
  if abs(dutyreal - duty) < 0.03*float(duty): self.log(f'Writing Pulse duty cycle as {dutyreal}.', log=log)
232
234
  else: self.log(f'Warning:Writing Pulse duty cycle as {dutyreal}, but was asked {duty}.', log='important')
233
235
  return freal, dutyreal
236
+ ### END: mwHP Functions:
234
237
 
235
238
 
236
239
  ### BEGIN: OBJ2FILE Tools
@@ -241,8 +244,10 @@ class mwHP:
241
244
  seriable_data = self.__dict__.copy()
242
245
  # take the attributes of unseriable data
243
246
  if self.script is None:
244
- seriable_data['logger'] == 'needsrebuild'
245
- seriable_data['logger_information'] = self.logger.__getargs__()
247
+ if self.logger is not None:
248
+ seriable_data['logger'] == 'needsrebuild'
249
+ seriable_data['logger_information'] = self.logger.__getargs__()
250
+ seriable_data['logger_information']['start_file'] = False
246
251
  else:
247
252
  seriable_data['script'] == 'needsrebuild'
248
253
  seriable_data['script_information'] = self.script.__getstate__()
@@ -259,5 +264,9 @@ class mwHP:
259
264
  self.script = Script(**seriable_data['script_information'])
260
265
  if self.logger == 'needsrebuild':
261
266
  self.logger = Logger(**seriable_data['logger_information'])
267
+ if (self.script is not None):
268
+ self.log(f'I am using Script saved in memory: {self.script.folder_name}.', log='screen')
269
+ elif (self.logger is not None):
270
+ self.log(f'I am using Logger saved in memory: {self.logger.description}.', log='screen')
262
271
  eq_reconnect(self)
263
272
  ### END: OBJ2FILE Tools