Repository navigation
Use RGBDiodes and the pandabox as a new detector in spectroscopy #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a658ca9
e68cba1
8e00d95
5464f2b
64d2248
ad8bb19
f276567
91eec0d
9c4e18d
7f276ce
100972f
b86af01
686519c
5d7e047
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| fmc_in.val5_capture: Mean | ||
| fmc_in.val5_dataset: spectroscopy_diodes-red | ||
| fmc_in.val6_capture: Mean | ||
| fmc_in.val6_dataset: spectroscopy_diodes-green | ||
| fmc_in.val7_capture: Mean | ||
| fmc_in.val7_dataset: spectroscopy_diodes-blue | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -130,14 +130,26 @@ def fly_scan( | |
| exposure_time: float = 0.1, | ||
| detector_deadtime: float = 2e-3 * 1.01, | ||
| detector_trigger: DetectorTrigger = DetectorTrigger.EXTERNAL_EDGE, | ||
| panda_whitelist: list[str] | None = None, | ||
| pandabrick_whitelist: list[str] | None = None, | ||
| plan_name: str = "fly_scan", | ||
| metadata: dict[str, Any] | None = None, | ||
| pandabox: HDFPanda | None = None, | ||
| diodes: Readable | None = None, | ||
|
tomkane-dls marked this conversation as resolved.
Resolved
|
||
| ): | ||
| """Fly scan a triggered detector along a spec.""" | ||
| # The pandabox captures the diode counts, which only mean something alongside | ||
| # the amplifier range recorded from the diodes, so we need both or neither | ||
| if pandabox is not None and diodes is None: | ||
| raise ValueError("pandabox was given without diodes") | ||
| if diodes is not None and pandabox is None: | ||
| raise ValueError("diodes were given without pandabox") | ||
|
|
||
| detectors = [detector] if pandabox is None else [pandabox, detector] | ||
|
|
||
| yield from load_panda_settings( | ||
| panda=pandabrick, | ||
| design_name="pandabrick_baseline", | ||
| whitelist_pvs=panda_whitelist, | ||
| whitelist_pvs=pandabrick_whitelist, | ||
| ) | ||
|
|
||
| scan_frame_duration = exposure_time | ||
|
|
@@ -164,7 +176,7 @@ def fly_scan( | |
| # Reproduce metadata present in spec_scan | ||
| _md = { | ||
| "plan_args": { | ||
| "detectors": {det.name for det in [pandabrick, detector]}, | ||
| "detectors": {det.name for det in [pandabrick, *detectors]}, | ||
| "spec": repr(fly_spec), | ||
| }, | ||
| "plan_name": plan_name, | ||
|
|
@@ -192,21 +204,28 @@ def fly_scan( | |
|
|
||
| # @attach_data_session_metadata_decorator() | ||
| @bpp.run_decorator(md=_md) | ||
| @bpp.stage_decorator([pandabrick, pandabrick_seq_flyer, detector, pmac_flyer]) | ||
| @bpp.stage_decorator([pandabrick, pandabrick_seq_flyer, *detectors, pmac_flyer]) | ||
| def inner_plan(): | ||
| if diodes is not None: | ||
| yield from bps.create(name="diodes") | ||
| yield from bps.read(diodes) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This records one reading of the live values (and the gain, as configuration) at the start, in a |
||
| yield from bps.save() | ||
|
|
||
| # Prepare pmac with the trajectory | ||
| yield from bps.prepare(pmac_flyer, pmac_scan_info) | ||
| # prepare sequencer table | ||
| yield from bps.prepare(pandabrick_seq_flyer, scan_spec_info) | ||
| # prepare panda and hdf writer once, at start of scan | ||
| yield from bps.prepare(pandabrick, panda_hdf_info) | ||
| if pandabox is not None: | ||
|
tomkane-dls marked this conversation as resolved.
Resolved
|
||
| yield from bps.prepare(pandabox, panda_hdf_info) | ||
|
tomkane-dls marked this conversation as resolved.
Resolved
|
||
| # prepare detector and info | ||
| # waiting for this last prepare means all prepare functions will be complete | ||
| yield from bps.prepare(detector, detector_info, wait=True) | ||
|
|
||
| # Need to run this after detectors are prepared | ||
| yield from bps.declare_stream( | ||
| pandabrick, detector, name="primary", collect=True | ||
| pandabrick, *detectors, name="primary", collect=True | ||
| ) | ||
|
|
||
| # Start the detectors and hdf writers acquiring. | ||
|
|
@@ -215,6 +234,8 @@ def inner_plan(): | |
| # its own with wait=true. | ||
| yield from bps.kickoff(pandabrick) | ||
| yield from bps.kickoff(pandabrick_seq_flyer) | ||
| if pandabox is not None: | ||
| yield from bps.kickoff(pandabox) | ||
| yield from bps.kickoff(detector, wait=True) | ||
|
|
||
| # Start the trajectory. | ||
|
|
@@ -226,11 +247,11 @@ def inner_plan(): | |
| pmac_flyer, | ||
| pandabrick_seq_flyer, | ||
| pandabrick, | ||
| detector, | ||
| *detectors, | ||
| ), | ||
| dets=( | ||
| pandabrick, | ||
| detector, | ||
| *detectors, | ||
| ), | ||
| stream_name="primary", | ||
| flush_period=0.5, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,8 @@ | |
| from bluesky.protocols import Movable | ||
| from bluesky.utils import MsgGenerator | ||
| from dodal.common import inject | ||
| from dodal.devices.beamlines.b01_1.diodes import RGBDiodes | ||
| from dodal.devices.current_amplifiers import EnzLocumRange | ||
| from dodal.devices.motors import XYZStage | ||
| from dodal.plans import spec_scan | ||
| from ophyd_async.epics.adaravis import AravisDetector | ||
|
|
@@ -21,17 +23,37 @@ | |
| from ophyd_async.fastcs.panda import HDFPanda | ||
| from scanspec.specs import Line, Spec | ||
|
|
||
| from .plans import ScanType, fly_scan, load_settings, pandabrick, pmac, serialize_spec | ||
| from .plans import ( | ||
| ScanType, | ||
| fly_scan, | ||
| load_panda_settings, | ||
| load_settings, | ||
| pandabrick, | ||
| pmac, | ||
| serialize_spec, | ||
| ) | ||
|
|
||
| LOGGER = logging.getLogger(__name__) | ||
|
|
||
| spectroscopy_detector = inject("spectroscopy_detector") | ||
| sample_stage = inject("sample_stage") | ||
| spectroscopy_diodes = inject("spectroscopy_diodes") | ||
| pandabox = inject("pandabox") | ||
| sample_stage = inject("spectroscopy_stage") | ||
|
|
||
| # The encoder entries map to specific axes, so despite the PandA being shared | ||
| # hardware this list is spectroscopy's. | ||
| PANDA_WHITELIST = ["incenc-3-val_dataset", "incenc-2-val_dataset"] | ||
|
|
||
| # Only the diode channels, so the rest of the pandabox's setup is left alone | ||
| PANDABOX_WHITELIST = [ | ||
| "fmc_in-val5_capture", | ||
| "fmc_in-val5_dataset", | ||
| "fmc_in-val6_capture", | ||
| "fmc_in-val6_dataset", | ||
| "fmc_in-val7_capture", | ||
| "fmc_in-val7_dataset", | ||
| ] | ||
|
|
||
| MANTA_ACQUIRE_PERIOD_PAD = 1961e-6 | ||
| MANTA_DETECTOR_DEADTIME = 2e-3 * 1.01 | ||
|
|
||
|
|
@@ -44,13 +66,28 @@ def spectroscopy( | |
| sample_stage: XYZStage = sample_stage, | ||
| pmac: PmacIO = pmac, | ||
| pandabrick: HDFPanda = pandabrick, | ||
| pandabox: HDFPanda = pandabox, | ||
| spectroscopy_diodes: RGBDiodes = spectroscopy_diodes, | ||
| spec: Spec[Movable] | None = None, | ||
| exposure_time: float = 0.1, | ||
| amplifier_range: EnzLocumRange | None = None, | ||
| metadata: dict[str, Any] | None = None, | ||
| *, | ||
| scan_type: ScanType, | ||
| ) -> MsgGenerator[None]: | ||
| """Do a spectroscopy scan.""" | ||
| """Do a spectroscopy scan, acquiring from the camera and the RGB diodes.""" | ||
| # The range is only recorded once per scan, as configuration, so it must not | ||
| # change between points | ||
| scan_range = amplifier_range or ( | ||
| yield from bps.rd(spectroscopy_diodes.amplifier.range) | ||
| ) | ||
| if scan_range == EnzLocumRange.AUTO: | ||
| raise ValueError("The diode amplifier must be on a fixed range, not Auto") | ||
|
|
||
| if amplifier_range is not None: | ||
| yield from bps.abs_set( | ||
| spectroscopy_diodes.amplifier.range, amplifier_range, wait=True | ||
| ) | ||
|
|
||
| yield from load_settings( | ||
| device=spectroscopy_detector, | ||
|
|
@@ -136,20 +173,28 @@ def spectroscopy( | |
|
|
||
| if scan_type == ScanType.FLY: | ||
| LOGGER.info("Performing a fly scan.") | ||
| # Captures the diodes alongside the camera | ||
| yield from load_panda_settings( | ||
| panda=pandabox, | ||
| design_name="pandabox_baseline", | ||
| whitelist_pvs=PANDABOX_WHITELIST, | ||
| ) | ||
|
Comment on lines
+176
to
+181
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems slightly odd that we're loading settings for the pandabox here, while we load settings for the pandabrick inside the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose because the pandabrick is used by both experiments but the pandabox is only used by spectroscopy. but looking at things now, the split of what is in spectroscopy() and what is in fly_scan() is a little arbitrary. The idea was that fly_scan was the same core function needed by both spectroscopy and tomography, but now we are passing diodes and pandabox its lost a bit of this.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you suggest injecting pandabox inside plans.py?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure which way would be better. With the pandabox and diodes inside
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After another look, that won't work either as the pandabox needs kicking off and you want to read the diodes beforehand.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @EmsArnold I can't see an elegant way of doing this currently, a shared fly_scan seems to be getting more complicated and uglier, but i'm not exactly sure separating it out again for each plan is likely to make it better. I will investigate whether two completely separate plans might look better on a new PR once tomography is working. |
||
| yield from fly_scan( | ||
| spec, | ||
| spectroscopy_detector, | ||
| pmac=pmac, | ||
| pandabrick=pandabrick, | ||
| exposure_time=exposure_time, | ||
| detector_deadtime=MANTA_DETECTOR_DEADTIME, | ||
| panda_whitelist=PANDA_WHITELIST, | ||
| pandabrick_whitelist=PANDA_WHITELIST, | ||
| metadata=metadata, | ||
| pandabox=pandabox, | ||
| diodes=spectroscopy_diodes, | ||
| ) | ||
| elif scan_type == ScanType.STEP: | ||
| LOGGER.info("Performing a step scan.") | ||
| yield from spec_scan( | ||
| {spectroscopy_detector, sample_stage}, | ||
| {spectroscopy_detector, spectroscopy_diodes, sample_stage}, | ||
|
tomkane-dls marked this conversation as resolved.
Resolved
|
||
| spec, # type: ignore | ||
| metadata=metadata, | ||
| ) | ||
|
|
@@ -160,6 +205,8 @@ def demo_spectroscopy( | |
| sample_stage: XYZStage = sample_stage, | ||
| pmac: PmacIO = pmac, | ||
| pandabrick: HDFPanda = pandabrick, | ||
| pandabox: HDFPanda = pandabox, | ||
| spectroscopy_diodes: RGBDiodes = spectroscopy_diodes, | ||
| total_number_of_scan_points: int = 25, | ||
| grid_size: float = 5.0, | ||
| grid_origin_x: float = 0.0, | ||
|
|
@@ -207,6 +254,8 @@ def demo_spectroscopy( | |
| sample_stage=sample_stage, | ||
| pmac=pmac, | ||
| pandabrick=pandabrick, | ||
| pandabox=pandabox, | ||
| spectroscopy_diodes=spectroscopy_diodes, | ||
| spec=grid, | ||
| exposure_time=exposure_time, | ||
| metadata=metadata, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.