-
Notifications
You must be signed in to change notification settings - Fork 26
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
Implementation of pmac trajectory #440
base: main
Are you sure you want to change the base?
Conversation
# Send trajectory to brick | ||
for axis in scanAxes: | ||
if axis != "DURATION": | ||
self.profile_cs_name.set(self.cs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then if we add to Motor:
def __init__(...):
...
self.input_link = epics_signal_r(str, prefix + ".INP")
Then we can do somewhere above:
input_links = asyncio.gather(*[axis.input_link.get_value() for axis in scanAxes])
cs_ports = set()
cs_axes: Dict[Motor, int] = {}
for axis in scanAxes:
input_link = await axis.input_link.get_value()
# Split "@asyn(PORT,num)" into ["PORT", "num"]
split = input_link.split("(")[1].rstrip(")").split(",")
cs_ports.add(split[0].strip())
cs_axes[axis] = int(split[1].strip())
assert len(cs_ports) == 1, "Motors in more than one CS"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this would only work for compound motors. An Axis with straight through mapping, (p45 Sample X for example) does not have a compound motor and its link is '@ASYN(PORT, num)' where PORT is the brick and num refers to its axis number on the brick rather than a CS assignment.
However from the brick and axis number I can then use '{PMAC_PREFIX}:M{num}:CsPort_RBV' and '{PMAC_PREFIX}:M{num}:CsAxis_RBV' to get the port and axis. But this feels quite long-winded...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this would only work for compound motors.
Correct
An Axis with straight through mapping, (p45 Sample X for example) does not have a compound motor and its link is '@ASYN(PORT, num)' where PORT is the brick and num refers to its axis number on the brick rather than a CS assignment.
I forgot P45 didn't have all compound axes... I guess we'll have to solve this now.
However from the brick and axis number I can then use '{PMAC_PREFIX}:M{num}:CsPort_RBV' and '{PMAC_PREFIX}:M{num}:CsAxis_RBV' to get the port and axis. But this feels quite long-winded...
Yes, that seems right. However I think I did the wrong thing in Malcolm attaching them to the motor. We should attach them to the generic PMAC object instead, in the same way as the EDM screens have an "Axes" table with deferred move, CS port and CS Axis.
I think we need to discuss this with the DeviceVector
part below, but for now could you attach these somewhere to the PMAC object so you can use them in the CS code above?
for axis in scanAxes: | ||
if axis != "DURATION": | ||
await self.motors[axis.lower()].set(self.initial_pos[axis]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a separate issue we should do this via the direct demands deferred move like https://github.com/dls-controls/pymalcolm/blob/2c3d7048026ef9c5a0fcd69db89bd88d74f62ad9/malcolm/modules/pmac/parts/cspart.py#L53-L85
7c4b49d
to
22a2ad8
Compare
self.cs = cs | ||
super().__init__(prefix, cs, name=name) | ||
|
||
async def _ramp_up_velocity_pos( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: On the motor record I refer to this as run_up_distance
rather than ramp_up
. I don't have a preference for which is better, but it would be nice if they're the same
a6ff99b
to
a40bfbe
Compare
for i in range(scanSize): | ||
if axis != "DURATION": | ||
self.profile[cs_axes[axis] + "_velocity"].append( | ||
(stack[0].upper[axis][i] - stack[0].lower[axis][i]) | ||
/ (stack[0].midpoints["DURATION"][i]) | ||
) | ||
self.profile[cs_axes[axis]].append(stack[0].midpoints[axis][i]) | ||
else: | ||
self.profile[axis.lower()].append( | ||
int(stack[0].midpoints[axis][i] / TICK_S) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eventually we should make these numpy operations so we get some speed, but this will do for now.
We also need to make sure that we put lower and midpoint points in here, but that can also be a point for later.
TICK_S = 0.000001 | ||
|
||
|
||
class PmacTrajectory(Pmac, Flyable, Preparable): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please could you implement this as a TriggerLogic
to be nested within a StandardFlyer
? That will remove a bit of the boilerplate around kickoff
and complete
e2853f4
to
e7ba3aa
Compare
Completes #494 |
846a066
to
79d1086
Compare
With the recent changes using raw motors, I have had to pass to the PMAC which axes are on the brick. '{PMAC_PREFIX}:M{num}:CsAxis_RBV' and '{PMAC_PREFIX}:M{num}:CsPort_RBV' PVs don't exist unless there is a motor in the IOC for the associated M(num). |
self.CsAxis = epics_signal_r(str, f"{prefix}:CsAxis_RBV") | ||
self.CsPort = epics_signal_r(str, f"{prefix}:CsPort_RBV") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.CsAxis = epics_signal_r(str, f"{prefix}:CsAxis_RBV") | |
self.CsPort = epics_signal_r(str, f"{prefix}:CsPort_RBV") | |
self.cs_axis = epics_signal_r(str, f"{prefix}:CsAxis_RBV") | |
self.cs_port = epics_signal_r(str, f"{prefix}:CsPort_RBV") |
|
||
|
||
class PmacTrajInfo(BaseModel): | ||
spec: Spec = Field(default=None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spec: Spec = Field(default=None) | |
spec: Spec[PmacMotor | Literal["DURATION"]] = Field(default=None) |
So this makes me think we need to make duration special in scanspec
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This gives a pydantic error /workspaces/ophyd-async/src/ophyd_async/epics/pmac/_pmacTrajectory.py:20:11 - error: "__getitem__" method not defined on type "type" (reportIndexIssue)
async def profile_between_points( | ||
chunk: Frames[PmacMotor], | ||
gap: int, | ||
min_time: float = 0.002, | ||
min_interval: float = 0.002, | ||
): | ||
"""Make consistent time and velocity arrays for each axis | ||
|
||
Try to create velocity profiles for all axes that all arrive at | ||
'distance' in the same time period. The profiles will contain the | ||
following points:- | ||
|
||
in the following description acceleration can be -ve or +ve depending |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't all this in https://github.com/DiamondLightSource/velocity-profile/ now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so. I took this from https://github.com/DiamondLightSource/pymalcolm/blob/264ec4a319092a545853f41f89ddf1d21da56504/malcolm/modules/pmac/util.py#L222 which didn't get added to velocity-profile...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make these _pmac_io.py
and _pmac_trajectory.py
please?
70babfd
to
d53578a
Compare
291544c
to
df67a54
Compare
df67a54
to
71d53a8
Compare
Implements #427.
This implements a simple trajectory for a pmac. It is initialised with a PMAC, Coordinate System name and a list of pmac motors involved in the scan. On Prepare it takes a ScanSpec stack which it converts into a position array for each axis in the scan, a velocity array for each axis in the scan and one time array (shared by all axes in the scan). It adjusts the start of the scan so that there is enough time and distance for the motors to achieve their desired velocity for the first point of the scans, and stops the motors at the end of the scan.
A Pmac CS Motor class has also been defined, which takes a motor class and adds information about its Coordinate system.
It currently doesn't have the capability to turnaround or use velocity calculations.
The current test gives the pmac a line trajectory, mocking the settings of the SampleX axis on P45. This trajectory has been tested on the p45 hardware.