@@ -208,6 +208,16 @@ def setTrigger(self, axis="X", pin=1, offset=0, period=1):
208208
209209 # {"task": "/motor_act", "stagescan": {"nStepsLine": 50, "dStepsLine": 1, "nTriggerLine": 1, "nStepsPixel": 50, "dStepsPixel": 1, "nTriggerPixel": 1, "delayTimeStep": 10, "stopped": 0, "nFrames": 50}}"}}
210210 def startStageScanning (self , nStepsLine = 100 , dStepsLine = 1 , nTriggerLine = 1 , nStepsPixel = 100 , dStepsPixel = 1 , nTriggerPixel = 1 , delayTimeStep = 10 , nFrames = 5 , isBlocking = False ):
211+ """Start a firmware-driven XY stage scan.
212+
213+ .. warning:: **Raw hardware-step values.**
214+ All step parameters (``dStepsLine``, ``dStepsPixel``) are forwarded
215+ to the firmware without applying the per-axis direction sign, because
216+ the scan API does not carry explicit axis labels and the firmware
217+ assigns axes internally. Callers that have configured an inverted
218+ axis via :meth:`setup_motor` must negate the relevant ``dSteps*``
219+ argument themselves before calling this method.
220+ """
211221 path = "/motor_act"
212222 payload = {
213223 "task" : path ,
@@ -335,6 +345,30 @@ def xyztTo1230(self, axis):
335345 axis = 0
336346 return axis
337347
348+ def signed_step_size (self , axis ):
349+ """Return stepSize * direction for an axis.
350+
351+ This combines the magnitude scale (µm/step) with the wiring-polarity
352+ sign into a single signed scalar, preserving the behaviour that existed
353+ before the direction field was introduced (when callers passed a
354+ negative stepSize to setup_motor() to flip polarity).
355+
356+ Parameters
357+ ----------
358+ axis : str or int
359+ Axis name (\" X\" /\" Y\" /\" Z\" /\" A\" ) or hardware index (0-3).
360+
361+ Returns
362+ -------
363+ float
364+ Positive if the axis is not inverted, negative if it is.
365+ ``phys = hw_steps * signed_step_size(axis)``
366+ ``hw_steps = phys / signed_step_size(axis)``
367+ """
368+ idx = self .xyztTo1230 (axis ) if isinstance (axis , str ) else int (axis )
369+ scales = (self .stepSizeA , self .stepSizeX , self .stepSizeY , self .stepSizeZ )
370+ return scales [idx ] * int (self .direction [idx ])
371+
338372 def cartesian2corexy (self , x , y ):
339373 # convert cartesian coordinates to coreXY coordinates
340374 # https://www.corexy.com/theory.html
@@ -432,6 +466,9 @@ def move_forever(self, speed=(0,0,0,0), is_stop=False, is_blocking=False):
432466 speed = (speed , speed , speed , speed )
433467 if len (speed )== 3 :
434468 speed = (* speed ,0 )
469+ # Apply per-axis direction sign so that a user-frame positive speed
470+ # moves the stage in the user-positive direction even on inverted axes.
471+ speed = tuple (int (speed [i ]) * int (self .direction [i ]) for i in range (4 ))
435472 '''
436473 {"task":"/motor_act",
437474 "motor":
@@ -826,21 +863,39 @@ def get_position(self, axis=None, timeout=1):
826863 return _position
827864
828865 def set_position (self , axis = 1 , position = 0 , timeout = 1 ):
829-
830866 '''
831- {"task":"/motor_act", "setpos": {"steppers": [{"stepperid":1, "posval": 0}]}}
867+ Tell the firmware what the current position register should be for an
868+ axis. ``position`` is in **user-frame physical units** (same frame as
869+ ``get_position()``). The direction sign and step size are applied
870+ internally so the firmware stores the corresponding hardware-step value:
871+
872+ hw_steps = round(position / signed_step_size(axis))
873+
874+ Special cases:
875+ - ``position=0`` always maps to hw 0 regardless of direction (used
876+ after homing to reset the step counter to zero).
877+ - ``set_motor()`` / ``set_motor_currentPosition()`` are lower-level
878+ helpers that accept raw hardware step values directly and do *not*
879+ apply the direction conversion.
880+
881+ Firmware payload: {"task":"/motor_act", "setpos": {"steppers": [{"stepperid":1, "posval": 0}]}}
832882 '''
833883 path = "/motor_act"
834- if type (axis ) != int :
884+ if not isinstance (axis , int ) :
835885 axis = self .xyztTo1230 (axis )
836886
887+ # Convert user-frame physical position to firmware hardware steps.
888+ ss = self .signed_step_size (axis )
889+ # Avoid division by zero for degenerate configurations.
890+ hw_position = int (round (position / ss )) if ss != 0 else 0
891+
837892 payload = {
838893 "task" : path ,
839894 "setpos" :{
840895 "steppers" : [
841896 {
842897 "stepperid" : axis ,
843- "posval" : int ( position )
898+ "posval" : hw_position
844899 }]
845900 }}
846901 r = self ._parent .post_json (path , payload , timeout = timeout )
0 commit comments