Compare commits
8 commits
1906e71696
...
2d41e30072
Author | SHA1 | Date | |
---|---|---|---|
2d41e30072 | |||
31a2729cf1 | |||
3c422b0770 | |||
83cc28c4f2 | |||
cf36182a28 | |||
3f862231b5 | |||
47c001529a | |||
1243b02136 |
39 changed files with 1276 additions and 34 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
*.FCBak
|
||||
.jupyter_ystore.db
|
||||
.ipynb_checkpoints/
|
||||
__pycache__/
|
||||
|
|
10
.pre-commit-config.yaml
Normal file
10
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: nbconvert
|
||||
name: Convert Jupyter Notebooks to Markdown
|
||||
language: system
|
||||
entry: ./.pre-commit/run-nbconvert.sh
|
||||
files: ^.*\.ipynb$
|
||||
pass_filenames: true
|
||||
fail_fast: true
|
8
.pre-commit/run-nbconvert.sh
Executable file
8
.pre-commit/run-nbconvert.sh
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
for nb in "$@"; do
|
||||
echo "Converting $nb to markdown..." >&2
|
||||
out_dir="$(dirname "$nb")"
|
||||
jupyter nbconvert "$nb" --to markdown --output-dir "$out_dir/rendered"
|
||||
done
|
|
@ -1,8 +1,17 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e3b0d5f1-f107-4d98-adad-403bfd9bc2b9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Connector Plate Load Estimate\n",
|
||||
"This notebook calculates an estimate maximum load for the connector plates used in the STM assembly."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"execution_count": 1,
|
||||
"id": "e70e1154-7d5d-4c92-9f62-2dd2c734f586",
|
||||
"metadata": {
|
||||
"editable": true,
|
||||
|
@ -24,7 +33,7 @@
|
|||
"<Quantity(171.312324, 'kilogram')>"
|
||||
]
|
||||
},
|
||||
"execution_count": 20,
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
|
@ -54,9 +63,9 @@
|
|||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"display_name": "stm-env",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
"name": "stm-env"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
|
@ -68,7 +77,7 @@
|
|||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.13.3"
|
||||
"version": "3.13.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
|
File diff suppressed because one or more lines are too long
BIN
Calculations/Laser-Wobble/output_14_0.png
(Stored with Git LFS)
BIN
Calculations/Laser-Wobble/output_14_0.png
(Stored with Git LFS)
Binary file not shown.
29
Calculations/rendered/Connector-Plate.md
Normal file
29
Calculations/rendered/Connector-Plate.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
## Connector Plate Load Estimate
|
||||
This notebook calculates an estimate maximum load for the connector plates used in the STM assembly.
|
||||
|
||||
|
||||
```python
|
||||
from pint import UnitRegistry
|
||||
unit = UnitRegistry()
|
||||
unit.formatter.default_format = "~"
|
||||
|
||||
preload_m4 = 3000 * unit.N
|
||||
n_screws = 4 # 2 on each side
|
||||
friction_coeff = 0.21 # Al on Al
|
||||
safety_factor = 1.5
|
||||
|
||||
max_load = preload_m4 * friction_coeff * n_screws / safety_factor / unit.standard_gravity
|
||||
max_load.to(unit.kg)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
171.312323780292 kg
|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
|
||||
```
|
139
Calculations/rendered/Eddy-current-brake-simulation.md
Normal file
139
Calculations/rendered/Eddy-current-brake-simulation.md
Normal file
|
@ -0,0 +1,139 @@
|
|||
```python
|
||||
import magpylib as magpy
|
||||
from scipy.spatial.transform import Rotation as R
|
||||
import pyvista as pv
|
||||
import numpy as np
|
||||
|
||||
# Creation of our magnets including place and orientation in the space (all dimensions guessed, need to put in correct ones)
|
||||
# magnetic polarization of 1.5 T pointing in x-direction
|
||||
# Dimensions assumed are 0.5, 1.5 and 3 cm (x,y,z)
|
||||
|
||||
|
||||
# Question: Is there a more elegant way to do this? Should I make a for-loop for creating our magnet array?
|
||||
|
||||
magnet1 = magpy.magnet.Cuboid(polarization=(1.5, 0, 0), dimension=(0.005, 0.015, 0.03))
|
||||
magnet1.position = (0, 0, 0)
|
||||
magnet1.orientation = R.from_euler("y", 90, degrees=True)
|
||||
|
||||
magnet2 = magpy.magnet.Cuboid(polarization=(1.5, 0, 0), dimension=(0.005, 0.015, 0.03))
|
||||
magnet2.position = (0.04, 0, 0)
|
||||
magnet2.orientation = R.from_euler("y", 90, degrees=True)
|
||||
|
||||
magnet3 = magpy.magnet.Cuboid(polarization=(1.5, 0, 0), dimension=(0.005, 0.015, 0.03))
|
||||
magnet3.position = (0, 0.04, 0)
|
||||
magnet3.orientation = R.from_euler("y", 90, degrees=True)
|
||||
|
||||
magnet4 = magpy.magnet.Cuboid(polarization=(1.5, 0, 0), dimension=(0.005, 0.015, 0.03))
|
||||
magnet4.position = (0.04, 0.04, 0)
|
||||
magnet4.orientation = R.from_euler("y", 90, degrees=True)
|
||||
|
||||
magnet5 = magpy.magnet.Cuboid(polarization=(1.5, 0, 0), dimension=(0.005, 0.015, 0.03))
|
||||
magnet5.position = (0, 0.08, 0)
|
||||
magnet5.orientation = R.from_euler("y", 90, degrees=True)
|
||||
|
||||
magnet6 = magpy.magnet.Cuboid(polarization=(1.5, 0, 0), dimension=(0.005, 0.015, 0.03))
|
||||
magnet6.position = (0.04, 0.08, 0)
|
||||
magnet6.orientation = R.from_euler("y", 90, degrees=True)
|
||||
|
||||
magnet7 = magpy.magnet.Cuboid(polarization=(1.5, 0, 0), dimension=(0.005, 0.015, 0.03))
|
||||
magnet7.position = (0, 0.12, 0)
|
||||
magnet7.orientation = R.from_euler("y", 90, degrees=True)
|
||||
|
||||
magnet8 = magpy.magnet.Cuboid(polarization=(1.5, 0, 0), dimension=(0.005, 0.015, 0.03))
|
||||
magnet8.position = (0.04, 0.12, 0)
|
||||
magnet8.orientation = R.from_euler("y", 90, degrees=True)
|
||||
|
||||
|
||||
# Grouping the magnets to collection
|
||||
coll = magpy.Collection(magnet1, magnet2, magnet3, magnet4, magnet5, magnet6, magnet7, magnet8)
|
||||
|
||||
# Plotting the magnet array
|
||||
magpy.show(coll, backend="plotly")
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
# Just for visualization: Showing the magnetic field vectors in 3D
|
||||
|
||||
spacing = np.array([0.003, 0.003, 0.003]) # defines the grid where the magnetic field is calculated
|
||||
|
||||
# The following is for getting the right dimensions and position of the grid
|
||||
magnets = [magnet1, magnet2, magnet3, magnet4, magnet5, magnet6, magnet7, magnet8]
|
||||
all_mins = []
|
||||
all_maxs = []
|
||||
|
||||
for m in magnets:
|
||||
pos = np.array(m.position)
|
||||
dim = np.array(m.dimension) / 2
|
||||
all_mins.append(pos - dim)
|
||||
all_maxs.append(pos + dim)
|
||||
|
||||
global_min = np.min(all_mins, axis=0) - 0.02 # add 2cm as buffer
|
||||
global_max = np.max(all_maxs, axis=0) + 0.02
|
||||
|
||||
dimensions = ((global_max - global_min) / spacing).astype(int)
|
||||
|
||||
grid = pv.ImageData(
|
||||
spacing=tuple(spacing),
|
||||
dimensions=tuple(dimensions),
|
||||
origin=(-0.02, -0.02, -0.02),
|
||||
)
|
||||
|
||||
# Calculation of the B-field in mT
|
||||
grid["B"] = coll.getB(grid.points) * 1000
|
||||
pl = pv.Plotter()
|
||||
pl.add_mesh(grid.outline(), color="blue", line_width=1)
|
||||
pl.add_points(grid.points, render_points_as_spheres=True, point_size=2, color='black')
|
||||
|
||||
# Add magnetic field vectors as arrows (glyphs)
|
||||
pl.add_mesh(
|
||||
grid.glyph(orient="B", scale=True, factor=0.00001), # use "B" vectors, scaling according to field strength, factor to make arrows smaller for visibility
|
||||
color="blue",
|
||||
label="Magnetic field vectors"
|
||||
)
|
||||
|
||||
magpy.show(coll, canvas=pl, units_length="m", backend="pyvista")
|
||||
|
||||
|
||||
|
||||
pl.show()
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
Widget(value='<iframe src="http://localhost:40551/index.html?ui=P_0x7d5c280f7fa0_15&reconnect=auto" class="pyv…
|
||||
|
||||
|
||||
|
||||
```python
|
||||
# Introducing the aluminium plate
|
||||
|
||||
z_position=0.01 # z_position = distance to the magnets (put in real number here!)
|
||||
plate_center = [0, 0, z_position]
|
||||
plate_size = [0.15, 0.40, 0.005] # [b,l, h]
|
||||
|
||||
plate = pv.Box(bounds=(
|
||||
plate_center[0] - plate_size[0]/2, plate_center[0] + plate_size[0]/2,
|
||||
plate_center[1] - plate_size[1]/2, plate_center[1] + plate_size[1]/2,
|
||||
plate_center[2] - plate_size[2]/2, plate_center[2] + plate_size[2]/2
|
||||
))
|
||||
pl.add_mesh(plate, color="silver", opacity=0.5, label="Aluminum plate")
|
||||
|
||||
pl.show()
|
||||
```
|
||||
|
||||
A view with name (P_0x7d5c280f7fa0_15) is already registered
|
||||
=> returning previous one
|
||||
|
||||
|
||||
|
||||
Widget(value='<iframe src="http://localhost:40551/index.html?ui=P_0x7d5c280f7fa0_15&reconnect=auto" class="pyv…
|
||||
|
||||
|
||||
|
||||
```python
|
||||
|
||||
```
|
|
@ -87,66 +87,66 @@ print(f"Video processing took {processing_time:.2f} seconds ({processing_time /
|
|||
Snapshot capture of frame 2331 at 77.70s: Intensity 12.612
|
||||
Snapshot capture of frame 2664 at 88.80s: Intensity 12.257
|
||||
Snapshot capture of frame 2997 at 99.90s: Intensity 12.728
|
||||
Video processing took 257.32 seconds (0.086 seconds per frame).
|
||||
Video processing took 300.84 seconds (0.100 seconds per frame).
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
|
@ -194,7 +194,7 @@ pass
|
|||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
|
@ -231,22 +231,111 @@ for a in ax:
|
|||
a.grid(axis="x", which="both")
|
||||
a.grid(axis="y", which="major")
|
||||
|
||||
a.axvline(x=1.317, color="red")
|
||||
a.annotate(" pendulum mode", xy=(1.317, 1080 / 2 - 150))
|
||||
|
||||
pass
|
||||
```
|
||||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
Of note is the highlighted _theoretical spring resonance_ which we had previously calculated using the known spring constant and mass of the system. Seeing a very visible peak at this frequency along the Y axis (the axis of the springs) is very nice.
|
||||
|
||||
In addition, the pendulum swinging resonance of the assembly was also roughly calculated and drawn into the diagrams. A peak is visible near this frequency as well.
|
||||
|
||||
|
||||
```python
|
||||
# If you wish to save the extracted displacement data, use this:
|
||||
# np.save("./laser-results/2025-09-07-011400-analyzed.npy", laser_position)
|
||||
```
|
||||
|
||||
### Theoretical Transmissibility vs. Measurement
|
||||
As a further step, the code below calculates the theoretical transmissibility curve and then plots it overlayed with the laser displacement amplitude spectral density in Y direction.
|
||||
|
||||
This gives a nice comparison, but the results should be interpreted with care: We do not have information on the spectrum of the excitation that was present during the measurement. For sure it was not even density broadband noise. Additionally, towards lower amplitudes, the measurement data gets increasingly noisy because the signal reaches the limit of the camera resolution.
|
||||
|
||||
|
||||
```python
|
||||
import math
|
||||
import numpy as np
|
||||
from pint import UnitRegistry
|
||||
unit = UnitRegistry()
|
||||
unit.formatter.default_format = "~"
|
||||
|
||||
# Parameters
|
||||
spring_constant = 1.1 * 4 * unit.N / unit.mm
|
||||
spring_length_resting = 112 * unit.mm
|
||||
|
||||
weight_total = 14 * unit.kg
|
||||
|
||||
dampening = 1 * unit.N / (unit.m / unit.s)
|
||||
|
||||
def spring_length_at(weight):
|
||||
return (weight * unit.standard_gravity / spring_constant + spring_length_resting).to(unit.mm)
|
||||
|
||||
def resonant_freq_at(weight):
|
||||
return (1 / (2 * math.pi) * np.sqrt(spring_constant / weight)).to(unit.Hz)
|
||||
|
||||
spring_length = spring_length_at(weight_total)
|
||||
f0 = resonant_freq_at(weight_total)
|
||||
|
||||
print(f"Length: {spring_length:~.1f}")
|
||||
print(f"Freq: {f0:~.3f}")
|
||||
|
||||
def lehr_dampening_factor(d, k, m):
|
||||
return d / (2 * np.sqrt(m * k))
|
||||
|
||||
lehr_dampening = lehr_dampening_factor(dampening, spring_constant, weight_total)
|
||||
lehr_dampening.ito_reduced_units()
|
||||
|
||||
def amplitude_ratio(lehr, f0, f):
|
||||
eta = f / f0
|
||||
return 1 / np.sqrt((1 - eta**2)**2 + (2 * eta * lehr)**2)
|
||||
|
||||
f_in = np.geomspace(0.5, 90, 100) * unit.Hz
|
||||
ratio = amplitude_ratio(lehr_dampening, f0, f_in)
|
||||
```
|
||||
|
||||
Length: 143.2 mm
|
||||
Freq: 2.822 Hz
|
||||
|
||||
|
||||
|
||||
```python
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
ax.axvline(x=2.822, color="red")
|
||||
# ax.annotate(" theoretical spring resonance", xy=(2.822, 1080 / 2 - 150))
|
||||
|
||||
ax.loglog(frequency_y, amplitude_spectral_density_y, label="laser")
|
||||
ax.loglog(f_in, ratio * 8, label="transmissibility")
|
||||
|
||||
a = ax
|
||||
a.set_xlabel("frequency / Hz")
|
||||
# a.set_ylabel("amplitude spectral density")
|
||||
a.set_xlim(0.5, 15)
|
||||
a.set_ylim(0.1, 1080 / 2)
|
||||
a.legend()
|
||||
a.grid(axis="x", which="both")
|
||||
a.grid(axis="y", which="major")
|
||||
|
||||
pass
|
||||
```
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
One more note about this graph: As the input amplitude is unknown, the theoretical curve was roughly scaled to sit ontop of the measurement data (`ratio * 8` in the code above). This is not a proper curve fit.
|
||||
|
||||
Also keep in mind that the measurement data gets pretty meaningless below an amplitude of 1 (pixel) due to the resolution limit of the camera.
|
||||
|
||||
|
||||
```python
|
||||
|
BIN
Calculations/rendered/Laser-Wobble_files/Laser-Wobble_14_0.png
(Stored with Git LFS)
Normal file
BIN
Calculations/rendered/Laser-Wobble_files/Laser-Wobble_14_0.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Calculations/rendered/Laser-Wobble_files/Laser-Wobble_19_0.png
(Stored with Git LFS)
Normal file
BIN
Calculations/rendered/Laser-Wobble_files/Laser-Wobble_19_0.png
(Stored with Git LFS)
Normal file
Binary file not shown.
132
Calculations/rendered/Laserbeam.md
Normal file
132
Calculations/rendered/Laserbeam.md
Normal file
|
@ -0,0 +1,132 @@
|
|||
```python
|
||||
import imageio.v2 as imageio
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from scipy import ndimage
|
||||
|
||||
# sensor horizontal size
|
||||
sensor_width = 7.75
|
||||
|
||||
# image file
|
||||
laser = imageio.imread("laser-img/2025-09-07-012621-2mm-aperture.jpg")
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
dimensions = np.array([sensor_width * laser.shape[0] / laser.shape[1], sensor_width])
|
||||
half_size = dimensions / 2
|
||||
|
||||
# Mean of all color channels as an approximation of the monochrome image
|
||||
laser_monochrome = np.mean(laser, axis=2)
|
||||
# Normalized intensity map
|
||||
intensity_map = laser_monochrome / np.max(laser_monochrome)
|
||||
|
||||
# Summed intensity in each axis
|
||||
intensity_x = np.sum(laser_monochrome, axis=0)
|
||||
intensity_y = np.sum(laser_monochrome, axis=1)
|
||||
|
||||
# Center of mass of the laser beam
|
||||
center = (
|
||||
(ndimage.center_of_mass(laser_monochrome) / np.array(laser_monochrome.shape) - 0.5)
|
||||
* dimensions
|
||||
* [-1, 1]
|
||||
)
|
||||
print(f"Center of the beam is at {center[0]:.3f}/{center[1]:.3f}")
|
||||
|
||||
|
||||
# Calculate FWHM in each axis
|
||||
def fwhm(curve, width):
|
||||
assert curve.ndim == 1
|
||||
half_max = (np.max(curve) + np.min(curve)) / 2
|
||||
diff = curve - half_max
|
||||
indices = np.where(diff > 0)[0]
|
||||
return (indices[-1] - indices[0]) / curve.size * width
|
||||
|
||||
|
||||
fwhm_y = fwhm(intensity_y, dimensions[0])
|
||||
fwhm_x = fwhm(intensity_x, dimensions[1])
|
||||
print(f"FWHM in X/Y: {fwhm_x:.2f}/{fwhm_y:.2f}")
|
||||
|
||||
eccentricity = np.sqrt(1 - (min(fwhm_y, fwhm_x) ** 2 / max(fwhm_y, fwhm_x) ** 2))
|
||||
print(f"Eccentricity (along cartesian axes): {eccentricity:.5f}")
|
||||
```
|
||||
|
||||
Center of the beam is at -0.088/0.238
|
||||
FWHM in X/Y: 2.16/1.46
|
||||
Eccentricity (along cartesian axes): 0.73687
|
||||
|
||||
|
||||
|
||||
```python
|
||||
with plt.style.context("dark_background"):
|
||||
fig, ax = plt.subplots(figsize=(14, 14))
|
||||
im = ax.imshow(
|
||||
intensity_map,
|
||||
cmap="magma",
|
||||
extent=(-half_size[1], half_size[1], -half_size[0], half_size[0]),
|
||||
interpolation="none",
|
||||
vmin=0,
|
||||
)
|
||||
ax.set_xlim(-np.max(half_size), np.max(half_size))
|
||||
ax.set_ylim(-np.max(half_size), np.max(half_size))
|
||||
|
||||
xy_scale = max(np.max(intensity_x), np.max(intensity_y))
|
||||
ax.plot(
|
||||
np.linspace(-half_size[1], half_size[1], intensity_x.size),
|
||||
intensity_x / xy_scale - np.max(half_size),
|
||||
color="red",
|
||||
)
|
||||
ax.plot(
|
||||
intensity_y / xy_scale - np.max(half_size),
|
||||
np.linspace(half_size[0], -half_size[0], intensity_y.size),
|
||||
color="red",
|
||||
)
|
||||
|
||||
# reticle
|
||||
ax.axvline(x=center[1], color="white", linestyle=":")
|
||||
ax.axhline(y=center[0], color="white", linestyle=":")
|
||||
|
||||
ax.axvline(x=center[1] - fwhm_x / 2, color="gray", linestyle=":")
|
||||
ax.axvline(x=center[1] + fwhm_x / 2, color="gray", linestyle=":")
|
||||
ax.axhline(y=center[0] - fwhm_y / 2, color="gray", linestyle=":")
|
||||
ax.axhline(y=center[0] + fwhm_y / 2, color="gray", linestyle=":")
|
||||
|
||||
cax = fig.add_axes(
|
||||
[
|
||||
ax.get_position().x1 + 0.01,
|
||||
ax.get_position().y0,
|
||||
0.02,
|
||||
ax.get_position().height,
|
||||
]
|
||||
)
|
||||
fig.colorbar(im, cax=cax)
|
||||
|
||||
text = (
|
||||
f"FWHM X/Y: {fwhm_x:.2f} mm/{fwhm_y:.2f} mm\n"
|
||||
f"Eccentricity: {eccentricity:.5f}\n"
|
||||
f"Offset X/Y: {center[1]:+.3f} mm/{center[0]:+.3f} mm"
|
||||
)
|
||||
fig.text(0.5, 0.05, text, ha="center")
|
||||
|
||||
fig
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
|
||||
```
|
BIN
Calculations/rendered/Laserbeam_files/Laserbeam_2_0.png
(Stored with Git LFS)
Normal file
BIN
Calculations/rendered/Laserbeam_files/Laserbeam_2_0.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Calculations/rendered/Laserbeam_files/Laserbeam_2_1.png
(Stored with Git LFS)
Normal file
BIN
Calculations/rendered/Laserbeam_files/Laserbeam_2_1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
52
Calculations/rendered/Preamp-Current.md
Normal file
52
Calculations/rendered/Preamp-Current.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from pint import UnitRegistry
|
||||
unit = UnitRegistry()
|
||||
unit.formatter.default_format = "~"
|
||||
unit.setup_matplotlib()
|
||||
|
||||
|
||||
gain_first_stage = 10e6 * unit.V / unit.A
|
||||
gain_second_stage = 100e3 / 1e3
|
||||
|
||||
tunnel_current_in = np.linspace(1e-12, 10e-9, 100) * unit.A
|
||||
volts_out = tunnel_current_in * gain_first_stage * gain_second_stage
|
||||
|
||||
plt.xscale("log")
|
||||
plt.plot(tunnel_current_in, volts_out)
|
||||
xticks = [1e-12, 10e-12, 100e-12, 1e-9, 10e-9] * unit.A
|
||||
plt.xticks(xticks, [f'{t.to("nA"):.3f~}' for t in xticks], minor=False)
|
||||
plt.xlabel("Tunneling Current")
|
||||
|
||||
plt.yscale("log")
|
||||
yticks = [0.001, 0.010, 0.1, 1.0, 5] * unit.V
|
||||
plt.yticks(yticks, [f'{t.to("V"):.3f~}' for t in yticks], minor=False)
|
||||
plt.ylabel("Preamp Voltage")
|
||||
plt.grid(True, "both")
|
||||
```
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
v = 0.359 * unit.V
|
||||
a = v / (gain_first_stage * gain_second_stage)
|
||||
a.to("pA")
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
359.0 pA
|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
|
||||
```
|
BIN
Calculations/rendered/Preamp-Current_files/Preamp-Current_0_0.png
(Stored with Git LFS)
Normal file
BIN
Calculations/rendered/Preamp-Current_files/Preamp-Current_0_0.png
(Stored with Git LFS)
Normal file
Binary file not shown.
138
Calculations/rendered/Spring-Dimensioning.md
Normal file
138
Calculations/rendered/Spring-Dimensioning.md
Normal file
|
@ -0,0 +1,138 @@
|
|||
```python
|
||||
import math
|
||||
import numpy as np
|
||||
from pint import UnitRegistry
|
||||
unit = UnitRegistry()
|
||||
unit.formatter.default_format = "~"
|
||||
|
||||
|
||||
# Parameters
|
||||
spring_constant = 1.1 * 4 * unit.N / unit.mm
|
||||
spring_length_resting = 112 * unit.mm
|
||||
|
||||
weight_total = 14 * unit.kg
|
||||
|
||||
dampening = 1 * unit.N / (unit.m / unit.s)
|
||||
|
||||
def spring_length_at(weight):
|
||||
return (weight * unit.standard_gravity / spring_constant + spring_length_resting).to(unit.mm)
|
||||
|
||||
def resonant_freq_at(weight):
|
||||
return (1 / (2 * math.pi) * np.sqrt(spring_constant / weight)).to(unit.Hz)
|
||||
|
||||
spring_length = spring_length_at(weight_total)
|
||||
f0 = resonant_freq_at(weight_total)
|
||||
|
||||
print(f"Length: {spring_length:~.1f}")
|
||||
print(f"Freq: {f0:~.3f}")
|
||||
```
|
||||
|
||||
Length: 143.2 mm
|
||||
Freq: 2.822 Hz
|
||||
|
||||
|
||||
|
||||
```python
|
||||
def lehr_dampening_factor(d, k, m):
|
||||
return d / (2 * np.sqrt(m * k))
|
||||
|
||||
lehr_dampening = lehr_dampening_factor(dampening, spring_constant, weight_total)
|
||||
lehr_dampening.ito_reduced_units()
|
||||
lehr_dampening
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
0.00201455741006345
|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
def amplitude_ratio(lehr, f0, f):
|
||||
eta = f / f0
|
||||
return 1 / np.sqrt((1 - eta**2)**2 + (2 * eta * lehr)**2)
|
||||
|
||||
f_in = np.geomspace(0.5, 90, 100) * unit.Hz
|
||||
ratio = amplitude_ratio(lehr_dampening, f0, f_in)
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
plt.plot(f_in, ratio)
|
||||
|
||||
def transmissibility_plot_setup(plt):
|
||||
plt.xscale('log')
|
||||
xticks = [1, 3, 10, 50]
|
||||
plt.xticks(xticks, [f"{t} Hz" for t in xticks], minor=False)
|
||||
plt.xlabel("Excitation Frequency")
|
||||
|
||||
plt.ylim(0.001, 10)
|
||||
plt.yscale('log')
|
||||
plt.ylabel("Transmissibility Ratio")
|
||||
|
||||
plt.grid(True, "both")
|
||||
|
||||
transmissibility_plot_setup(plt)
|
||||
```
|
||||
|
||||
/usr/lib/python3.13/site-packages/matplotlib/cbook.py:1355: UnitStrippedWarning: The unit of the quantity is stripped when downcasting to ndarray.
|
||||
return np.asarray(x, float)
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
def ratio_for_dkm(d, k, m, f_in):
|
||||
f0 = (1 / (2 * math.pi) * np.sqrt(k / m)).to(unit.Hz)
|
||||
lehr = lehr_dampening_factor(d, k, m)
|
||||
return amplitude_ratio(lehr, f0, f_in)
|
||||
|
||||
d = dampening
|
||||
k = [
|
||||
spring_constant,
|
||||
spring_constant / 4,
|
||||
]
|
||||
m = [
|
||||
weight_total,
|
||||
24 * unit.kg,
|
||||
]
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
for k_ in k:
|
||||
m_ = m[0]
|
||||
plt.plot(f_in, ratio_for_dkm(d, k_, m_, f_in), label=f"{k_:~.1f}, {m_:~.1f}")
|
||||
for m_ in m[1:]:
|
||||
k_ = k[0]
|
||||
plt.plot(f_in, ratio_for_dkm(d, k_, m_, f_in), label=f"{k_:~.1f}, {m_:~.1f}")
|
||||
|
||||
ref_thorlabs_f = [3, 4, 4.42, 5, 6, 7, 8, 9, 10, 40]
|
||||
ref_thorlabs_r = [2, 5, 15.0, 4, 1.5, 0.7, 0.5, 0.35, 0.28, 0.018]
|
||||
plt.plot(ref_thorlabs_f, ref_thorlabs_r, label="Thorlabs PTP702 (Passive)")
|
||||
|
||||
ref_thorlabs_f = [1, 1.35, 2, 3, 5, 9, 20, 27, 30]
|
||||
ref_thorlabs_r = [2, 3, 0.9, 0.3, 0.1, 0.02, 0.009, 0.007, 0.0023]
|
||||
plt.plot(ref_thorlabs_f, ref_thorlabs_r, label="Thorlabs PTS601 (Active)")
|
||||
|
||||
plt.legend(loc="upper right")
|
||||
transmissibility_plot_setup(plt)
|
||||
```
|
||||
|
||||
/usr/lib/python3.13/site-packages/matplotlib/cbook.py:1355: UnitStrippedWarning: The unit of the quantity is stripped when downcasting to ndarray.
|
||||
return np.asarray(x, float)
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
|
||||
```
|
BIN
Calculations/rendered/Spring-Dimensioning_files/Spring-Dimensioning_2_1.png
(Stored with Git LFS)
Normal file
BIN
Calculations/rendered/Spring-Dimensioning_files/Spring-Dimensioning_2_1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Calculations/rendered/Spring-Dimensioning_files/Spring-Dimensioning_3_1.png
(Stored with Git LFS)
Normal file
BIN
Calculations/rendered/Spring-Dimensioning_files/Spring-Dimensioning_3_1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
72
Calculations/rendered/Tunneling-Current-Distance.md
Normal file
72
Calculations/rendered/Tunneling-Current-Distance.md
Normal file
|
@ -0,0 +1,72 @@
|
|||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from pint import UnitRegistry
|
||||
|
||||
# Set up unit system
|
||||
unit = UnitRegistry()
|
||||
unit.formatter.default_format = "~"
|
||||
unit.setup_matplotlib()
|
||||
|
||||
# Physical constants
|
||||
e=-1.602176634e-19 * unit.C # electron charge
|
||||
m=9.109e-31 * unit.kg # electron mass
|
||||
hbar=6.62607015e-34/2/np.pi * unit.joule * unit.second # Planck constant
|
||||
phi=4 * unit.eV # Work function (see table)
|
||||
phi_joule=phi.to("joule")
|
||||
U=5 *unit.V
|
||||
|
||||
# Table working functions different metals
|
||||
|
||||
# Metal F(eV)
|
||||
# (Work Function)
|
||||
# Ag (silver) 4.26
|
||||
# Al (aluminum) 4.28
|
||||
# Au (gold) 5.1
|
||||
# Cs (cesium) 2.14
|
||||
# Cu (copper) 4.65
|
||||
# Li (lithium) 2.9
|
||||
# Pb (lead) 4.25
|
||||
# Sn (tin) 4.42
|
||||
# Chromium 4.6
|
||||
# Molybdenum 4.37
|
||||
# Stainless Steel 4.4
|
||||
# Gold 4.8
|
||||
# Tungsten 4.5
|
||||
# Copper 4.5
|
||||
# Nickel 4.6
|
||||
|
||||
# Distance range
|
||||
Distance_tip_sample=np.linspace(10e-13,2e-10,100)* unit.m
|
||||
Tunneling_current=U*np.exp(-2*np.sqrt(2*m*phi_joule)/hbar*Distance_tip_sample) /unit.V #please note: This is not the tunneling current as this formular gives just the proportionality. Calculating the current constant is difficult as there are for us unknown parameters
|
||||
Distance_tip_sample_nm=Distance_tip_sample.to("nm")
|
||||
|
||||
plt.plot(Distance_tip_sample_nm, Tunneling_current)
|
||||
plt.xlabel(f"Distance tip sample [{Distance_tip_sample_nm.units:~P}]")
|
||||
plt.ylabel(f"Tunneling-Proportionality [arb. Unit]")
|
||||
plt.xticks(ticks=np.linspace(0, 0.2, 5), labels=[f"{x:.2f}" for x in np.linspace(0, 0.2, 5)])
|
||||
#plt.yscale("log")
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
([<matplotlib.axis.XTick at 0x7e10b04929b0>,
|
||||
<matplotlib.axis.XTick at 0x7e10b04b0310>,
|
||||
<matplotlib.axis.XTick at 0x7e10b034ba60>,
|
||||
<matplotlib.axis.XTick at 0x7e10b0328670>,
|
||||
<matplotlib.axis.XTick at 0x7e10b0329360>],
|
||||
[Text(0.0, 0, '0.00'),
|
||||
Text(0.05, 0, '0.05'),
|
||||
Text(0.1, 0, '0.10'),
|
||||
Text(0.15000000000000002, 0, '0.15'),
|
||||
Text(0.2, 0, '0.20')])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
BIN
Calculations/rendered/Tunneling-Current-Distance_files/Tunneling-Current-Distance_0_1.png
(Stored with Git LFS)
Normal file
BIN
Calculations/rendered/Tunneling-Current-Distance_files/Tunneling-Current-Distance_0_1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
125
Control/rendered/STM_Control.md
Normal file
125
Control/rendered/STM_Control.md
Normal file
|
@ -0,0 +1,125 @@
|
|||
```python
|
||||
import time
|
||||
import numpy as np
|
||||
from matplotlib import pyplot as plt
|
||||
from rp_overlay import overlay
|
||||
import rp
|
||||
|
||||
fpga = overlay()
|
||||
rp.rp_Init()
|
||||
print("Red Pitaya initialized.")
|
||||
```
|
||||
|
||||
Check FPGA [OK].
|
||||
Red Pitaya initialized.
|
||||
|
||||
|
||||
|
||||
```python
|
||||
rp.rp_GenReset()
|
||||
rp.rp_AcqReset()
|
||||
print("Reset generator and acquisition.")
|
||||
```
|
||||
|
||||
Reset generator and acquisition.
|
||||
|
||||
|
||||
|
||||
```python
|
||||
# Generator parameters
|
||||
channel = rp.RP_CH_1
|
||||
channel2 = rp.RP_CH_2
|
||||
waveform = rp.RP_WAVEFORM_SINE
|
||||
freq = 1
|
||||
ampl = 1.0
|
||||
|
||||
print("Gen_start")
|
||||
rp.rp_GenWaveform(channel, waveform)
|
||||
rp.rp_GenFreqDirect(channel, freq)
|
||||
rp.rp_GenAmp(channel, ampl)
|
||||
|
||||
rp.rp_GenWaveform(channel2, waveform)
|
||||
rp.rp_GenFreqDirect(channel2, freq)
|
||||
rp.rp_GenAmp(channel2, ampl)
|
||||
|
||||
rp.rp_GenTriggerSource(channel, rp.RP_GEN_TRIG_SRC_INTERNAL)
|
||||
|
||||
rp.rp_GenOutEnableSync(True)
|
||||
rp.rp_GenSynchronise()
|
||||
print("Started generator.")
|
||||
```
|
||||
|
||||
Gen_start
|
||||
Generator started.
|
||||
|
||||
|
||||
|
||||
```python
|
||||
# Set Decimation
|
||||
rp.rp_AcqSetDecimationFactor(16384)
|
||||
rp.rp_AcqSetAveraging(True)
|
||||
rp.rp_AcqSetGain(rp.RP_CH_1, rp.RP_HIGH)
|
||||
|
||||
V=rp.rp_AcqGetGainV(rp.RP_CH_1)
|
||||
print("GainVoltage", V)
|
||||
|
||||
# Set trigger level and delay
|
||||
rp.rp_AcqSetTriggerLevel(rp.RP_T_CH_1, 0.5)
|
||||
rp.rp_AcqSetTriggerDelay(0)
|
||||
|
||||
# Start Acquisition
|
||||
print("Acq_start")
|
||||
rp.rp_AcqStart()
|
||||
|
||||
time.sleep(3)
|
||||
|
||||
# Specify trigger - immediately
|
||||
rp.rp_AcqSetTriggerSrc(rp.RP_TRIG_SRC_NOW)
|
||||
|
||||
# Trigger state
|
||||
while 1:
|
||||
trig_state = rp.rp_AcqGetTriggerState()[1]
|
||||
if trig_state == rp.RP_TRIG_STATE_TRIGGERED:
|
||||
break
|
||||
|
||||
# Fill state
|
||||
while 1:
|
||||
if rp.rp_AcqGetBufferFillState()[1]:
|
||||
break
|
||||
|
||||
|
||||
### Get data ###
|
||||
|
||||
# Volts
|
||||
N = 16384
|
||||
fbuff = rp.fBuffer(N)
|
||||
res = rp.rp_AcqGetOldestDataV(rp.RP_CH_1, N, fbuff)[1]
|
||||
|
||||
data_V = np.zeros(N, dtype = float)
|
||||
X = np.arange(0, N, 1)
|
||||
|
||||
for i in range(0, N, 1):
|
||||
data_V[i] = fbuff[i]
|
||||
|
||||
plt.plot(X, data_V)
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||
GainVoltage [0, 20.0]
|
||||
Acq_start
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
Only run the `rp_Release()` below when you do not want to further acquire data.
|
||||
|
||||
|
||||
```python
|
||||
# Release resources
|
||||
rp.rp_Release()
|
||||
```
|
BIN
Control/rendered/STM_Control_files/STM_Control_3_1.png
(Stored with Git LFS)
Normal file
BIN
Control/rendered/STM_Control_files/STM_Control_3_1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
225
Control/rendered/STM_Sweep.md
Normal file
225
Control/rendered/STM_Sweep.md
Normal file
|
@ -0,0 +1,225 @@
|
|||
# Synchronised one-pulse signal generation and acquisition
|
||||
Now that we are familiar with generating and acquiring signals with Red Pitaya, we can finally learn a few tricks for combining both and use them in practice. In this example, the acquisition is triggered together with the generation, which is used for measuring cable length and other applications where signal propagation delay is important.
|
||||
|
||||
**Note:**
|
||||
The voltage range of fast analog inputs on the Red Pitaya depends on the input jumper position. HV sets the input range to ±20 V, while LV sets the input range to ±1 V. For more information, please read the following [chapter](https://redpitaya.readthedocs.io/en/latest/developerGuide/hardware/125-14/fastIO.html#analog-inputs).
|
||||
|
||||
As previously, create a loop-back from fast analog outputs to fast analog inputs, as shown in the picture below.
|
||||
Please make sure the jumpers are set to ±1 V (LV).
|
||||
|
||||

|
||||
|
||||
## Libraries and FPGA image
|
||||
|
||||
|
||||
```python
|
||||
import time
|
||||
import numpy as np
|
||||
from matplotlib import pyplot as plt
|
||||
from rp_overlay import overlay
|
||||
import rp
|
||||
|
||||
fpga = overlay()
|
||||
rp.rp_Init()
|
||||
```
|
||||
|
||||
Check FPGA [OK].
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
0
|
||||
|
||||
|
||||
|
||||
## Marcos
|
||||
Throughout this tutorial we will mention macros multiple times. Here is a complete list of macros that will come in handy when customising this notebook. The marcos are a part of the **rp** library.
|
||||
|
||||
**GENERATION**
|
||||
|
||||
- **Waveforms** - RP_WAVEFORM_SINE, RP_WAVEFORM_SQUARE, RP_WAVEFORM_TRIANGLE, RP_WAVEFORM_RAMP_UP, RP_WAVEFORM_RAMP_DOWN, RP_WAVEFORM_DC, RP_WAVEFORM_PWM, RP_WAVEFORM_ARBITRARY, RP_WAVEFORM_DC_NEG, RP_WAVEFORM_SWEEP
|
||||
- **Generator modes** - RP_GEN_MODE_CONTINUOUS, RP_GEN_MODE_BURST
|
||||
- **Sweep direction** - RP_GEN_SWEEP_DIR_NORMAL, RP_GEN_SWEEP_DIR_UP_DOWN
|
||||
- **Sweep mode** - RP_GEN_SWEEP_MODE_LINEAR, RP_GEN_SWEEP_MODE_LOG
|
||||
- **Generator trigger source** - RP_GEN_TRIG_SRC_INTERNAL, RP_GEN_TRIG_SRC_EXT_PE, RP_GEN_TRIG_SRC_EXT_NE
|
||||
- **Generator triggers** - RP_T_CH_1, RP_T_CH_2, RP_T_CH_EXT
|
||||
- **Rise and fall times** - RISE_FALL_MIN_RATIO, RISE_FALL_MAX_RATIO
|
||||
|
||||
**ACQUISITION**
|
||||
|
||||
- **Decimation** - RP_DEC_1, RP_DEC_2, RP_DEC_4, RP_DEC_8, RP_DEC_16, RP_DEC_32, RP_DEC_64, RP_DEC_128, RP_DEC_256, RP_DEC_512, RP_DEC_1024, RP_DEC_2048, RP_DEC_4096, RP_DEC_8192, RP_DEC_16384, RP_DEC_32768, RP_DEC_65536
|
||||
- **Acquisition trigger** - RP_TRIG_SRC_DISABLED, RP_TRIG_SRC_NOW, RP_TRIG_SRC_CHA_PE, RP_TRIG_SRC_CHA_NE, RP_TRIG_SRC_CHB_PE, RP_TRIG_SRC_CHB_NE, RP_TRIG_SRC_EXT_PE, RP_TRIG_SRC_EXT_NE, RP_TRIG_SRC_AWG_PE, RP_TRIG_SRC_AWG_NE
|
||||
- **Acquisition trigger state** - RP_TRIG_STATE_TRIGGERED, RP_TRIG_STATE_WAITING
|
||||
- **Buffer size** - ADC_BUFFER_SIZE, DAC_BUFFER_SIZE
|
||||
- **Fast analog channels** - RP_CH_1, RP_CH_2
|
||||
|
||||
SIGNALlab 250-12 only:
|
||||
- **Input coupling** - RP_DC, RP_AC
|
||||
- **Generator gain** - RP_GAIN_1X, RP_GAIN_5X
|
||||
|
||||
STEMlab 125-14 4-Input only:
|
||||
- **Fast analog channels** - RP_CH_3, RP_CH_4
|
||||
- **Acquisition trigger** - RP_TRIG_SRC_CHC_PE, RP_TRIG_SRC_CHC_NE, RP_TRIG_SRC_CHD_PE, RP_TRIG_SRC_CHD_NE
|
||||
|
||||
## Synchronising generation and acquisition
|
||||
|
||||
The example itself is relatively trivial in comparison to others already presented during the tutorials. The only practical change is the **RP_TRIG_SOUR_AWG_PE**.
|
||||
|
||||
Parameters:
|
||||
|
||||
|
||||
```python
|
||||
###### Generation #####
|
||||
channel = rp.RP_CH_1 # rp.RP_CH_2
|
||||
waveform = rp.RP_WAVEFORM_RAMP_UP
|
||||
freq = 0.1164153218269348 * 2
|
||||
ampl = 1.0
|
||||
|
||||
ncyc = 1
|
||||
nor = 1
|
||||
period = 10
|
||||
|
||||
gen_trig_sour = rp.RP_GEN_TRIG_SRC_INTERNAL
|
||||
|
||||
##### Acquisition #####
|
||||
trig_lvl = 0.5
|
||||
trig_dly = 0
|
||||
dec = rp.RP_DEC_65536
|
||||
|
||||
acq_trig_sour = rp.RP_TRIG_SRC_AWG_PE
|
||||
|
||||
N = 32768 // 2
|
||||
|
||||
# Reset Generation and Acquisition
|
||||
rp.rp_GenReset()
|
||||
rp.rp_AcqReset()
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
0
|
||||
|
||||
|
||||
|
||||
Setting up both the generation and acquisition.
|
||||
|
||||
|
||||
```python
|
||||
###### Generation #####
|
||||
print("Gen_start")
|
||||
rp.rp_GenWaveform(channel, waveform)
|
||||
rp.rp_GenFreqDirect(channel, freq)
|
||||
rp.rp_GenAmp(channel, ampl)
|
||||
|
||||
# Change to burst mode
|
||||
rp.rp_GenMode(channel, rp.RP_GEN_MODE_BURST)
|
||||
rp.rp_GenBurstCount(channel, ncyc) # Ncyc
|
||||
rp.rp_GenBurstRepetitions(channel, nor) # Nor
|
||||
rp.rp_GenBurstPeriod(channel, period) # Period
|
||||
|
||||
|
||||
# Specify generator trigger source
|
||||
rp.rp_GenTriggerSource(channel, gen_trig_sour)
|
||||
|
||||
# Enable output synchronisation
|
||||
rp.rp_GenOutEnableSync(True)
|
||||
|
||||
|
||||
##### Acquisition #####
|
||||
# Set Decimation
|
||||
rp.rp_AcqSetDecimation(dec)
|
||||
rp.rp_AcqSetAveraging(True)
|
||||
rp.rp_AcqSetGain(rp.RP_CH_1, rp.RP_HIGH)
|
||||
|
||||
# Set trigger level and delay
|
||||
rp.rp_AcqSetTriggerLevel(rp.RP_T_CH_1, trig_lvl)
|
||||
rp.rp_AcqSetTriggerDelay(trig_dly)
|
||||
|
||||
```
|
||||
|
||||
Gen_start
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
0
|
||||
|
||||
|
||||
|
||||
Starting acquisition and capturing data.
|
||||
|
||||
|
||||
```python
|
||||
# Start Acquisition
|
||||
print("Acq_start")
|
||||
rp.rp_AcqStart()
|
||||
|
||||
# Specify trigger - input 1 positive edge
|
||||
rp.rp_AcqSetTriggerSrc(acq_trig_sour)
|
||||
|
||||
time.sleep(5)
|
||||
|
||||
rp.rp_GenTriggerOnly(channel) # Trigger generator
|
||||
|
||||
print(f"Trigger state: {rp.rp_AcqGetTriggerState()[1]}")
|
||||
|
||||
# Trigger state
|
||||
while 1:
|
||||
trig_state = rp.rp_AcqGetTriggerState()[1]
|
||||
if trig_state == rp.RP_TRIG_STATE_TRIGGERED:
|
||||
break
|
||||
|
||||
# Fill state
|
||||
print(f"Fill state: {rp.rp_AcqGetBufferFillState()[1]}")
|
||||
|
||||
while 1:
|
||||
if rp.rp_AcqGetBufferFillState()[1]:
|
||||
break
|
||||
|
||||
|
||||
### Get data ###
|
||||
# Volts
|
||||
fbuff = rp.fBuffer(N)
|
||||
res = rp.rp_AcqGetOldestDataV(rp.RP_CH_1, N, fbuff)[1]
|
||||
|
||||
data_V = np.zeros(N // 2, dtype = float)
|
||||
|
||||
for i in range(0, N // 2, 1):
|
||||
data_V[i] = fbuff[i + N // 2]
|
||||
|
||||
plt.plot(data_V)
|
||||
plt.show()
|
||||
```
|
||||
|
||||
Acq_start
|
||||
Trigger state: 0
|
||||
Fill state: False
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
# Release resources
|
||||
rp.rp_Release()
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
0
|
||||
|
||||
|
||||
|
||||
One of the applications for this example is to measure how many samples are taken from the triggering moment to the start of the captured pulse, which corresponds to propagation delay on the transmission line.
|
||||
|
||||
### Note
|
||||
There are a lot of different commands for the Acquisition. The list of available functions is quite an achievement to read through, so from now on, please refer to the *C and Python API section* of the [SCPI & API command list](https://redpitaya.readthedocs.io/en/latest/appsFeatures/remoteControl/command_list.html#list-of-supported-scpi-api-commands) for all available commands.
|
||||
|
BIN
Control/rendered/STM_Sweep_files/STM_Sweep_9_1.png
(Stored with Git LFS)
Normal file
BIN
Control/rendered/STM_Sweep_files/STM_Sweep_9_1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
9
Mechanical/extract_magnets.py
Normal file
9
Mechanical/extract_magnets.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
import FreeCAD
|
||||
|
||||
doc = FreeCAD.open("STM-Frame.FCStd")
|
||||
magnets = doc.findObjects(Label="Magnet.*")
|
||||
for m in magnets:
|
||||
if m.TypeId != "App::Link":
|
||||
continue
|
||||
pos = m.Placement.Base
|
||||
print(f"{pos.x:.5f}, {pos.y:.5f}, {pos.z:.5f}")
|
53
Misc/Temp-Monitor/rendered/Temperature-Data.md
Normal file
53
Misc/Temp-Monitor/rendered/Temperature-Data.md
Normal file
|
@ -0,0 +1,53 @@
|
|||
```python
|
||||
from matplotlib import pyplot as plt
|
||||
import pandas as pd
|
||||
|
||||
df = pd.read_csv('20250824-temperature-02.csv')
|
||||
df["Time"] = df["Time [ms]"] / 1000. / 60.
|
||||
|
||||
fig, ax = plt.subplots(figsize=(24, 8))
|
||||
ax.set_ylim(25.4, df["Raw"].max() + 0.01)
|
||||
ax.plot(df["Time"], df["Raw"])
|
||||
ax.plot(df["Time"], df["Temperature"])
|
||||
ax.set_ylabel("Temperature / °C")
|
||||
ax.set_xlabel("Time / minutes")
|
||||
ax.legend(["Raw Temperature", "Temperature"])
|
||||
ax.grid()
|
||||
|
||||
def event(time_minutes, label, yoff=0):
|
||||
y = ax.get_ylim()[1] - yoff
|
||||
ax.annotate(label, xy=(time_minutes, y))
|
||||
ax.axvline(x=time_minutes, color="red")
|
||||
|
||||
event(3330 / 60, "Start install aluminum")
|
||||
event(4440 / 60, "Start remove aluminum")
|
||||
event(5208 / 60, "New tip")
|
||||
event(5450 / 60, "Aluminum back on STM")
|
||||
event(5720 / 60, "First tunneling", 0.03)
|
||||
event(6860 / 60, "Stable tunneling started", 0.26)
|
||||
event(7100 / 60, "Stable tunneling stopped", 0.23)
|
||||
event(7200 / 60, "Heated up the STM externally")
|
||||
event(7669 / 60, "Stable tunneling start 80pA", 0.42)
|
||||
event(7780 / 60, "Changed to 210pA", 0.26)
|
||||
event(7990 / 60, "Drifted out of range", 0.38)
|
||||
event(8300 / 60, "Heated again")
|
||||
event(8327 / 60, "STM spontaneously starts tunneling", 0.52)
|
||||
event(8500 / 60, "Very noisy but long term tunneling", 0.2)
|
||||
event(8950 / 60, "End tunneling", 0.25)
|
||||
```
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
|
||||
```
|
BIN
Misc/Temp-Monitor/rendered/Temperature-Data_files/Temperature-Data_0_0.png
(Stored with Git LFS)
Normal file
BIN
Misc/Temp-Monitor/rendered/Temperature-Data_files/Temperature-Data_0_0.png
(Stored with Git LFS)
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue