@@ -129,3 +129,184 @@ def get_galvo_status(self, timeout=1):
129129
130130 return self ._parent .post_json (path , payload , timeout = timeout )
131131
132+ def set_arbitrary_points (self , points , laser_trigger = "AUTO" , timeout = 1 ):
133+ """
134+ Start arbitrary point scanning with custom XY coordinate list
135+
136+ Each point in the list specifies an X, Y coordinate, a dwell time,
137+ and optionally a laser intensity.
138+ The scanner will loop through the points continuously.
139+
140+ Args:
141+ points: List of dicts with keys:
142+ - 'x' (0-4095): X DAC coordinate
143+ - 'y' (0-4095): Y DAC coordinate
144+ - 'dwell_us' (int): Dwell time in microseconds
145+ - 'laser_intensity' (0-255, optional): Per-point laser intensity
146+ Maximum 265 points.
147+ laser_trigger: Trigger mode - "AUTO" (HIGH during dwell, LOW during movement),
148+ "HIGH" (force always on), "LOW" (force always off),
149+ "CONTINUOUS" (HIGH during entire scan)
150+ timeout: Request timeout in seconds (default: 1)
151+
152+ Example:
153+ >>> points = [
154+ ... {"x": 1024, "y": 2048, "dwell_us": 500, "laser_intensity": 128},
155+ ... {"x": 1500, "y": 2100, "dwell_us": 1000, "laser_intensity": 255},
156+ ... {"x": 2000, "y": 2500, "dwell_us": 250}
157+ ... ]
158+ >>> galvo.set_arbitrary_points(points)
159+ >>> galvo.set_arbitrary_points(points, laser_trigger="CONTINUOUS")
160+ """
161+ if len (points ) > 265 :
162+ raise ValueError ("Maximum 265 points supported" )
163+ if len (points ) == 0 :
164+ raise ValueError ("At least 1 point required" )
165+
166+ # Validate points
167+ for i , pt in enumerate (points ):
168+ if not all (k in pt for k in ('x' , 'y' , 'dwell_us' )):
169+ raise ValueError (f"Point { i } missing required keys (x, y, dwell_us)" )
170+ if pt ['x' ] < 0 or pt ['x' ] > 4095 :
171+ raise ValueError (f"Point { i } x={ pt ['x' ]} out of range (0-4095)" )
172+ if pt ['y' ] < 0 or pt ['y' ] > 4095 :
173+ raise ValueError (f"Point { i } y={ pt ['y' ]} out of range (0-4095)" )
174+ if 'laser_intensity' in pt :
175+ if pt ['laser_intensity' ] < 0 or pt ['laser_intensity' ] > 255 :
176+ raise ValueError (f"Point { i } laser_intensity={ pt ['laser_intensity' ]} out of range (0-255)" )
177+
178+ path = '/galvo_act'
179+ payload = {
180+ "task" : path ,
181+ #"laser_trigger": laser_trigger,
182+ "points" : points
183+ }
184+
185+ return self ._parent .post_json (path , payload , timeout = timeout )
186+
187+ def stop_arbitrary_points (self , timeout = 1 ):
188+ """
189+ Stop arbitrary point scanning
190+
191+ Args:
192+ timeout: Request timeout in seconds (default: 1)
193+
194+ Example:
195+ >>> galvo.stop_arbitrary_points()
196+ """
197+ path = '/galvo_act'
198+ payload = {"task" :"/galvo_act" , "points" :[{"x" :0 ,"y" :0 ,"dwell_us" :1000 }]} # Single point at origin with long dwell to effectively stop scanning
199+
200+ return self ._parent .post_json (path , payload , timeout = timeout )
201+
202+ def pause_arbitrary_points (self , timeout = 1 ):
203+ """
204+ Pause arbitrary point scanning (keeps current index)
205+
206+ Args:
207+ timeout: Request timeout in seconds (default: 1)
208+
209+ Example:
210+ >>> galvo.pause_arbitrary_points()
211+ """
212+ path = '/galvo_act'
213+ payload = {
214+ "task" : path ,
215+ "pause_points" : True
216+ }
217+
218+ return self ._parent .post_json (path , payload , timeout = timeout )
219+
220+ def resume_arbitrary_points (self , timeout = 1 ):
221+ """
222+ Resume arbitrary point scanning from paused position
223+
224+ Args:
225+ timeout: Request timeout in seconds (default: 1)
226+
227+ Example:
228+ >>> galvo.resume_arbitrary_points()
229+ """
230+ path = '/galvo_act'
231+ payload = {
232+ "task" : path ,
233+ "resume_points" : True
234+ }
235+
236+ return self ._parent .post_json (path , payload , timeout = timeout )
237+
238+ def set_trigger_mode (self , mode = "AUTO" , timeout = 1 ):
239+ """
240+ Set laser trigger mode override
241+
242+ Args:
243+ mode: Trigger mode - "AUTO", "HIGH", "LOW", "CONTINUOUS"
244+ timeout: Request timeout in seconds (default: 1)
245+
246+ Example:
247+ >>> galvo.set_trigger_mode("HIGH") # Force laser on
248+ >>> galvo.set_trigger_mode("LOW") # Force laser off
249+ >>> galvo.set_trigger_mode("AUTO") # Restore automatic control
250+ """
251+ path = '/galvo_act'
252+ payload = {
253+ "task" : path ,
254+ "laser_trigger" : mode
255+ }
256+
257+ return self ._parent .post_json (path , payload , timeout = timeout )
258+
259+ def generate_circle_points (self , center_x = 2048 , center_y = 2048 , radius = 1000 ,
260+ num_points = 32 , dwell_us = 100 ):
261+ """
262+ Generate points along a circle for arbitrary point scanning
263+
264+ Args:
265+ center_x: Circle center X (default: 2048)
266+ center_y: Circle center Y (default: 2048)
267+ radius: Circle radius in DAC units (default: 1000)
268+ num_points: Number of points around circle (default: 32)
269+ dwell_us: Dwell time per point in microseconds (default: 100)
270+
271+ Returns:
272+ list: List of point dicts suitable for set_arbitrary_points()
273+
274+ Example:
275+ >>> points = galvo.generate_circle_points(radius=500, num_points=64)
276+ >>> galvo.set_arbitrary_points(points)
277+ """
278+ points = []
279+ for i in range (num_points ):
280+ angle = 2.0 * np .pi * i / num_points
281+ x = int (center_x + radius * np .cos (angle ))
282+ y = int (center_y + radius * np .sin (angle ))
283+ x = max (0 , min (4095 , x ))
284+ y = max (0 , min (4095 , y ))
285+ points .append ({"x" : x , "y" : y , "dwell_us" : dwell_us })
286+ return points
287+
288+ def generate_grid_points (self , x_min = 500 , x_max = 3500 , y_min = 500 , y_max = 3500 ,
289+ nx = 4 , ny = 4 , dwell_us = 500 ):
290+ """
291+ Generate a grid of points for arbitrary point scanning
292+
293+ Args:
294+ x_min, x_max: X range (default: 500-3500)
295+ y_min, y_max: Y range (default: 500-3500)
296+ nx, ny: Number of grid points per axis (default: 4x4)
297+ dwell_us: Dwell time per point in microseconds (default: 500)
298+
299+ Returns:
300+ list: List of point dicts suitable for set_arbitrary_points()
301+
302+ Example:
303+ >>> points = galvo.generate_grid_points(nx=8, ny=8, dwell_us=1000)
304+ >>> galvo.set_arbitrary_points(points)
305+ """
306+ points = []
307+ for iy in range (ny ):
308+ y = int (y_min + (y_max - y_min ) * iy / max (1 , ny - 1 )) if ny > 1 else (y_min + y_max ) // 2
309+ for ix in range (nx ):
310+ x = int (x_min + (x_max - x_min ) * ix / max (1 , nx - 1 )) if nx > 1 else (x_min + x_max ) // 2
311+ points .append ({"x" : x , "y" : y , "dwell_us" : dwell_us })
312+ return points
0 commit comments