Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions content/en/docs/200_hardware-sharing/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ This page periodically crawls GitHub for HW_* repositories. It considers users/o

## Adding Published Plug-ins to Your Project

To clone an existing repo on GitHub into to your project's hardware folder, navigate to your project folder (`cd to/your_project_folder/`) and use the following command (requires [Git](/docs/100_development-environment/20_git/)):
To add an existing repo on GitHub into to your project's hardware folder, navigate to your project folder (`cd to/your_project_folder/`) and use the following command (requires [Git](/docs/100_development-environment/20_git/)):

```sh
git clone https://github.com/{THEIR_USER_NAME}/HW_{company_model}.git ScopeFoundryHW/{company_model}
git submodule add https://github.com/{THEIR_USER_NAME}/HW_{company_model}.git ScopeFoundryHW/{company_model}
```

Where `{THEIR_USER_NAME}` and `{company_name}` need to be replaced accordingly. **Hint:** The entries in the [Library](/docs/301_existing-hardware-components/) contain this line completed for the respective HardwareComponent.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,27 @@
---
title: Spatial Scanning
description: Create mapping measurements.
title: BaseRaster scan
description: Base classes that facilitated implementing scanning Measurements.
date: 2025-01-01
weight: 1
weight: 100
---

An example can be found by creating a new app using [ScopeFoundry tools](/docs/11_tools-tutorials/1_new-microscope-app/).

There are two variants of the functionally same base class.

## Traditional vs V2
## Version 1 vs Version 2

The two versions of these base classes differ in the way movement is defined.

1. The legacy base class is `ScopeFoundry.scanning.BaseRaster2DSlowScan` and requires overriding the following methods:

- `move_position_slow`

- `move_position_fast`

- `move_position_start`




2. The V2 version, `ScopeFoundry.scanning.BaseRaster2DSlowScanV2`, instead requires defining `actuators` with the settings paths associated with the stage's target position.
2. The V2 version, `ScopeFoundry.scanning.BaseRaster2DSlowScanV2`, instead requires defining [actuators](/docs/20_built-in-measurements/scanning/sweeping/#actuators) with the settings paths associated with the stage's target position.

{{% pageinfo color="info" %}}
BaseRaster2DSlowScan***V2*** released in ScopeFoundry 2.1
{{% /pageinfo %}}



An example can be found by creating a new app using [ScopeFoundry tools](/docs/11_tools-tutorials/1_new-microscope-app/).


16 changes: 4 additions & 12 deletions content/en/docs/20_built-in-measurements/Scanning/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,14 @@ description: Choose a measurement paradigm based on your needs.
date: 2025-06-01
---

Since version 2.1, ScopeFoundry provides two built in measurements in which the following cycling repeats:
Built in Measurements where the following cycle repeats:

1. Change the position, voltage, ... in predetermined way
2. Collect data

For this common task ScopeFoundry provides several solutions. Read the following to decide which fits your task:
ScopeFoundry 2.2 introduced generic sweeping and scanning Measurements that work out of the box but can also subclassed, see [sweeping](sweeping).

1. For [spatial scanning](spatial-scanning):
Previously, [BaseRaster_scan](Baseraster-scan) were base classes that facilitated implementing scanning Measurements.

- requires subclassing and override a view methods.
- Natively has a concept of spatiality in 2D and 3D, which gives end-users an advanced interaction experience.
2. For generic setting [sweeping](sweeping):

- Works out of the Box! This Measurement can collect data and run other measurements without subclassing.
- Easier to extend with additional data channels by providing a flexible data structure and composing multiple `Collector` classes.


For your own Measurement from scratch consider using this [shortcut](https://scopefoundry.org/docs/30_tips-and-tricks/settings-ranges/).
For your own Measurement from scratch read [this tutorial](/docs/11_tools-tutorials/3_measurement/) where [this](/docs/30_tips-and-tricks/settings-ranges/) might be useful.

Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,25 @@ class MyFancyApp(BaseMicroscopeApp):
...
```

![out_of_the_box](out_of_the_box.png)

Depending on the dimensionality, i.e., the number of actuators varied, use:

- Sweep1D
- Sweep2D
- Sweep3D
- Sweep4D
- Map2D

Easy-to-use configurations [below](#more-configurations). Most sweeping tasks are already solved with the out-of-the-box solution.
Easy-to-use configurations [below](#more-configurations).

This article aims to outline further configurability that elevates the usage of these sweep classes.
Most sweeping tasks are already solved with this out-of-the-box solution. This article aims to outline further configurability that elevates the usage of these sweep classes. Note that MapXD extends SweepXD with features that paricularly apply for spattial mapping.

## Basic Concepts

The two central ingredients are so-called:

1. *"Actuators"*, the objects that are being varied. These can be defined using a settings path or by providing a function. You can sweep multiple actuators with different sweep modes. Out of the box, any setting that has a write-to-hardware function can be used. Typically, such a setting only acquires a hardware write function after it is connected. The list of possible actuators can be updated after connecting to hardware by pressing the appropriate button in the UI.
1. *"Actuators"*, the objects that are being varied. These can be defined using a settings path or by providing a function (see costume actuator). Such a setting only requires a hardware write function. As settings only get associated with hardware write functions after they are connected the list of possible actuators can be updated after connecting to hardware by pressing the appropriate button in the UI.

2. *"Collectors"*, responsible for collecting data at each sweep point. The user can activate multiple collectors and can change the order in which they are executed by dragging and dropping. Further, each collector can be repeated. For more info on collectors, see below.

Expand All @@ -48,17 +52,19 @@ Define how data is collected and what is collected at each sweep point.

- If you want to run any of your measurements at each sweep point, you *might* get away with using the *any_measurement* option. The resulting data file of the sweep measurement only contains data of the specified sub-measurement if the sub-measurement defines a dictionary named `data`. So this feature works best if all your target measurements populate a dictionary `data` with your data.

- The *any_measurement* is not further configurable. If you need to configure data collection further, implement your own collector class and pass it to your Sweep class. This becomes particularly interesting if you want to use multiple collectors at a given data point and data collection requires changing state before collection.
### Costume collector

The *any_measurement* is not further configurable. If you need to configure data collection further, implement your own collector class and pass it to your Sweep class. This becomes particularly interesting if you want to use multiple collectors at a given actuator state and each collector requires changing state of the setup before collection.

Here is an example that moves the shutter before and after reading powers (using a target_measurement "power_readout" that implements a "get_data" method):
Here is an example that moves the shutter before and after reading powers (using a target_measurement "power_readout" that implements a "get_data" method):

```python
# shuttered_power_collector.py
from ScopeFoundry import Collector

class ShutteredPowerCollector(Collector):

name = "power measurement with shutter"
name = "power_measurement_with_shutter"
target_measure_name = "power_readout"
repeated_dset_names = ("powers",)

Expand Down Expand Up @@ -90,11 +96,15 @@ Define how data is collected and what is collected at each sweep point.
def release(self, host_measurement, *args, **kwargs) -> None:
self.app.hardware.my_shutter.settings["position"] = "close"
```

The sweep measurement calls these 3 functions at each data point in the following order:

1. `prepare` exactly one time. (If you find yourself repeating code for all your custom collectors, you might be better off with a [generic actuator](#generic-actuators))
2. `run` according to the number of repetitions the user specified.

The sweep measurement calls these 3 functions at each sweep point in the following order:

1. `prepare` exactly one time. (If you find yourself repeating code for all your custom collectors, you might be better off with a [costume actuator](#costume-actuators))

2. `run` according to the number of repetitions the user specified. Note that the Sweep Measurement incorporates the content of the `data` dictionary.

The attribute `repeated_dset_names = ("powers",)` informs the sweep measurement that only "powers" is collected for every repetition at each sweep point.

3. `release` exactly one time.

​ To add your collector to the sweeps, alter the app file:
Expand Down Expand Up @@ -134,9 +144,9 @@ Note that the above actuator is defined with a tuple of length 2. However, it is
```python
explicit_actuators = [
(
"x", # intuitive name
"hw/xyz_stage/x_position", # path to setting with hardware read function
"hw/xyz_stage/x_target_position", # path to setting with hardware write function
"x", # intuitive name
"hw/xyz_stage/x_target_position", # path to setting with hardware write function
"hw/xyz_stage/x_position", # path to setting with hardware read function
), ...
]
```
Expand All @@ -147,34 +157,35 @@ Either way, to add them to your measurement, alter the app file:
self.add_measurement(Sweep2D(self, actuators=explicit_actuators))
```

### Generic Actuators
### Costume Actuators

The above defines an actuator with lq_paths. You can define more generic actuators using custom functions. Study this example:
The above defines an actuator with lq_paths. You can define more sofisticated actutators by at least defining write function with excalty the new position as an argument.

```python
class MyFancyApp(BaseMicroscopeApp):

name = "My Fancy App"

def my_generic_actuator_write_function(self, new_position):
def my_actuator_write_function(self, new_position):
# new_position
self.hardware.my_hardware.settings["target_position"] = new_position
self.hardware.my_hardware_2.settings["target_position"] = new_position ** 2
# potentially wait until values are set.
...

def my_generic_actuator_read_function(self) -> float:
def my_actuator_read_function(self) -> float:
pass
# return a value

def setup(self):
...
actuators = [("generic_actuator", self.my_generic_actuator_read_function, self.my_generic_actuator_write_function),]
actuators = [("generic_actuator", self.my_actuator_write_function, self.my_actuator_read_function),]

from ScopeFoundry import Map2D, Sweep1D, Sweep2D, Sweep3D, Sweep4D
self.add_measurement(Sweep2D(self, actuators=actuators))
...
```
The read_function is not required and be completely omitted.

## More Configurations

Expand All @@ -184,7 +195,7 @@ class MyFancyApp(BaseMicroscopeApp):
self.add_measurement(Sweep2D(self, n_any_measurements=5, n_read_any_settings=5))
```

- The values that are swept (the sweep array) are by default defined using a start, stop, num pattern (i.e., a single interval). You can use multiple intervals to define the sweep intervals.
- The values that are swept (the sweep array) are by default defined using a start, stop, num pattern to define a single interval with equally spaced values. You can use multiple intervals to define several intervals (with varying density of values).
```python
self.add_measurement(Sweep2D(self, range_n_intervals=(3, 5)))
```
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 25 additions & 14 deletions content/en/docs/30_tips-and-tricks/analyze-with-ipynb/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@ This feature works best with Jupyter Notebook installed. We recommend the follow
1. Pylance (Microsoft)
2. Jupyter (Microsoft)



New in ScopeFoundry 2.2: We recommend to trigger this feature after a success measurement as a new cell that loads the latest measurement is added to the notebook.


New in ScopeFoundry 2.2: We recommend to trigger this feature after a success-full measurement as a new cell that loads the latest measurement is appended to the notebook.

## Trigger Feature

There are two ways to start this feature:
Note: In some versions of VS Code the data folder has to be opened explicitly (File -> Open Folder ...) for this feature to work.

There are two ways to trigger this feature:

### In the app

Expand All @@ -50,12 +48,15 @@ python -m ScopeFoundry.tools

Click the button on the Welcome tab to proceed.

*or* with 2.1
*or* with 2.3

```bash
python -m ScopeFoundry ipynb
python -m ScopeFoundry ipynb-XX
# (where XX is 'last' (default), 'all' or 'remaining')
```

where XX is 'last' (default), 'all' or 'remaining'.

## Result

This feature generates the following:
Expand All @@ -67,13 +68,23 @@ This feature generates the following:

In the notebook, the top two cells are generated:

- **Cell 1**: Imports the data loaders.
- **Cell 2**: Lists paths to each `.h5` file and demonstrates how they can be loaded.
- **`CELL #0`**: Imports the data loaders.
- **`Cell #1`**: Commands to load files in the folder.

## Re-triggering

After adding more datafiles to the folder, it is generally safe to re-trigger this feature to update cell 2 and add more loaders. However, the following caveats apply:
After adding more data-files to the folder, it is generally safe to re-trigger this feature to update `CELL #1` and add more loaders. However, the following caveats apply:

- `CELL #0` **will be overwritten:** All changes will be lost.
- Content of `CELL #1` will never be deleted. Lines that do not already *exist fuzzily* will be added. For example, a line that has been commented will not be added again.
- Cells onwards are not altered. New in Version 2.2: re-triggering appends a new cell with a line that loads the latest file.



# Clean up data folder

{{% pageinfo color="info" %}}
New in ScopeFoundry 2.3
{{% /pageinfo %}}

- **Cell 1 will be overwritten:** All changes will be lost.
- Content of Cell 2 will never be deleted. Lines that do not already *exist fuzzily* will be added. For example, a line that has been commented will not be added again.
- Cell 3 and onwards are not altered. New in Version 2.2: re-triggering appends a new cell with a line that loads the latest file.
See `CELL #0` for instructions to archive .h5 that are not mentioned in your notebook.
40 changes: 40 additions & 0 deletions content/en/docs/30_tips-and-tricks/settings-ranges/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,43 @@ To add corresponding widgets to your `.ui`:
<img src="range.png" alt="range" style="zoom: 50%;" />

The `sweep_type` determines how the values are traversed.

## Multi interval ranges

{{% pageinfo color="info" %}}
New in ScopeFoundry 2.2
{{% /pageinfo %}}

Define a range of X-values with up to n (bellow set to 5) intervals, each interval having a start, stop, and step setting.

```python
from ScopeFoundry.measurement import Measurement


class XSweep(Measurement):

name = "x_sweep"

def setup(self):

self.range = self.settings.new_intervaled_range("X",
n_intervals=5,
include_center_span: bool = False,
include_sweep_type: bool = False,
initials=None,
unit=None,
si=False,
ro=False,
vmin=-1_000_000_000_000,
vmax=1_000_000_000_000,
spinbox_decimals=4,
description="",
**kwargs
)

def run(self):
# Access the array of X-values
for x in self.range.sweep_array:
# Perform operations for each X-value
...
```