or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

area-detectors.mdcore-framework.mdepics-integration.mdflyers-continuous-scanning.mdindex.mdmotors-positioners.mdsimulation-testing.mdspecialized-devices.md

flyers-continuous-scanning.mddocs/

0

# Flyers and Continuous Scanning

1

2

Interfaces and implementations for continuous scanning and fly scanning where data is collected while motors are in motion or during continuous acquisitions. The flyer interface enables coordinated data collection from multiple devices during uninterrupted operations.

3

4

## Capabilities

5

6

### Flyer Interface

7

8

The base protocol for all flyer devices, defining the standard workflow for continuous data collection operations.

9

10

```python { .api }

11

class FlyerInterface:

12

"""

13

Interface for flyer devices that collect data continuously during motion.

14

15

The flyer protocol involves four key operations:

16

1. kickoff() - Start the continuous operation

17

2. complete() - Wait for or signal completion

18

3. collect() - Retrieve collected data as events

19

4. describe_collect() - Provide metadata about collected data

20

"""

21

def kickoff(self):

22

"""

23

Start the flyer operation.

24

25

Returns:

26

StatusBase: Status indicating when flying has started

27

"""

28

29

def complete(self):

30

"""

31

Wait for flying to complete or signal completion.

32

33

Can be either a query (returns immediately if done) or

34

a command (initiates completion sequence).

35

36

Returns:

37

StatusBase: Status tracking completion

38

"""

39

40

def collect(self):

41

"""

42

Retrieve collected data as generator of proto-events.

43

44

Yields:

45

dict: Events with keys {'time', 'timestamps', 'data'}

46

- time (float): UNIX timestamp for event

47

- timestamps (dict): Per-signal timestamps

48

- data (dict): Per-signal data values

49

"""

50

51

def describe_collect(self):

52

"""

53

Describe the format of data returned by collect().

54

55

Returns:

56

dict: Nested dictionary with stream names as keys,

57

signal descriptions as values

58

"""

59

60

def collect_tables(self):

61

"""

62

Alternative data collection format (proposed).

63

64

Returns:

65

Iterable: Data organized as tables rather than events

66

"""

67

```

68

69

### Area Detector Time Series Collection

70

71

Flyer implementation for area detector time series data collection, enabling continuous acquisition of waveform data with timestamps.

72

73

```python { .api }

74

class AreaDetectorTimeseriesCollector(Device):

75

"""

76

Collects time series data from area detectors during fly scans.

77

78

This flyer collects waveform data and timestamps from area detector

79

time series plugins, supporting pause/resume functionality.

80

81

Parameters:

82

- prefix (str): EPICS PV prefix for time series records

83

- stream_name (str): Name for the data stream (default: 'primary')

84

"""

85

def __init__(self, prefix, *, stream_name='primary', **kwargs): ...

86

87

# Component signals

88

control: EpicsSignal # TSControl - acquisition control

89

num_points: EpicsSignal # TSNumPoints - number of points to collect

90

cur_point: EpicsSignalRO # TSCurrentPoint - current point counter

91

waveform: EpicsSignalRO # TSTotal - collected waveform data

92

waveform_ts: EpicsSignalRO # TSTimestamp - waveform timestamps

93

94

def kickoff(self):

95

"""Start time series collection."""

96

97

def complete(self):

98

"""Wait for collection to finish."""

99

100

def collect(self):

101

"""Yield time series data as events."""

102

103

def describe_collect(self):

104

"""Describe time series data format."""

105

106

def pause(self):

107

"""Pause data collection."""

108

109

def resume(self):

110

"""Resume data collection."""

111

```

112

113

### Waveform Collection

114

115

General-purpose waveform collector for devices that provide time-stamped waveform data.

116

117

```python { .api }

118

class WaveformCollector(Device):

119

"""

120

Collects waveform data with timestamps during fly scans.

121

122

Used with timestamp devices and other waveform-generating hardware.

123

Supports configurable data interpretation (time vs indices).

124

125

Parameters:

126

- prefix (str): EPICS PV prefix for waveform records

127

- stream_name (str): Name for the data stream

128

"""

129

def __init__(self, prefix, *, stream_name='primary', **kwargs): ...

130

131

# Component signals

132

select: EpicsSignal # Sw-Sel - waveform selection

133

reset: EpicsSignal # Rst-Sel - reset control

134

waveform_count: EpicsSignalRO # Val:TimeN-I - point count

135

waveform: EpicsSignalRO # Val:Time-Wfrm - waveform data

136

waveform_nord: EpicsSignalRO # Val:Time-Wfrm.NORD - number of points

137

data_is_time: Signal # Configure data interpretation

138

139

def kickoff(self):

140

"""Start waveform collection."""

141

142

def complete(self):

143

"""Wait for waveform collection to finish."""

144

145

def collect(self):

146

"""Yield waveform data as events."""

147

148

def describe_collect(self):

149

"""Describe waveform data format."""

150

```

151

152

### Monitor-Based Flying

153

154

Mixin class that implements flyer functionality by monitoring device attributes during continuous operations.

155

156

```python { .api }

157

class MonitorFlyerMixin:

158

"""

159

Flyer mixin that monitors specified attributes during flight.

160

161

This mixin enables any device to act as a flyer by subscribing to

162

attribute changes and collecting the monitored data.

163

164

Parameters:

165

- monitor_attrs (list): Signal attribute names to monitor during flight

166

- stream_names (dict): Mapping of attributes to stream names

167

- pivot (bool): Data organization mode:

168

- False: Single event with arrays of all values

169

- True: Separate events for each value/timestamp pair

170

"""

171

def __init__(self, *, monitor_attrs=None, stream_names=None, pivot=False, **kwargs): ...

172

173

def kickoff(self):

174

"""

175

Start monitoring specified attributes.

176

177

Subscribes to attribute changes and begins collecting data.

178

179

Returns:

180

StatusBase: Completed status (monitoring starts immediately)

181

"""

182

183

def complete(self):

184

"""

185

Signal that monitoring should stop.

186

187

Returns:

188

StatusBase: Status tracking monitor completion

189

"""

190

191

def collect(self):

192

"""

193

Yield collected monitor data as events.

194

195

Data organization depends on pivot setting:

196

- pivot=False: Single event with all collected data arrays

197

- pivot=True: One event per monitored value/timestamp pair

198

199

Yields:

200

dict: Events containing monitored attribute data

201

"""

202

203

def describe_collect(self):

204

"""

205

Describe monitored data format.

206

207

Returns:

208

dict: Description of monitored signals by stream name

209

"""

210

211

def pause(self):

212

"""Pause monitoring (unsubscribe from attributes)."""

213

214

def resume(self):

215

"""Resume monitoring (resubscribe to attributes)."""

216

```

217

218

### Simulation and Testing Flyers

219

220

Mock flyer implementations for testing and development without requiring hardware.

221

222

```python { .api }

223

class TrivialFlyer(Device):

224

"""

225

Simple flyer that returns empty data for testing.

226

227

Complies with flyer API but generates no actual data.

228

Used for testing flyscan protocols and timing.

229

"""

230

def __init__(self, **kwargs): ...

231

232

def kickoff(self):

233

"""Return completed status immediately."""

234

235

def complete(self):

236

"""Return completed status immediately."""

237

238

def collect(self):

239

"""Yield 100 empty events for testing."""

240

241

class MockFlyer(Device):

242

"""

243

Comprehensive mock flyer for testing flyscan APIs.

244

245

Simulates realistic flyer behavior with threaded execution,

246

motor movement, and detector data collection.

247

248

Parameters:

249

- detector (Device): Detector device to collect from

250

- motor (Device): Motor device to move during scan

251

- start (float): Starting motor position

252

- stop (float): Ending motor position

253

- num_points (int): Number of data points to collect

254

"""

255

def __init__(self, detector, motor, *, start=0, stop=1, num_points=10, **kwargs): ...

256

257

def kickoff(self):

258

"""Start threaded motor movement and data collection."""

259

260

def complete(self):

261

"""Wait for scan completion."""

262

263

def collect(self):

264

"""Yield collected motor and detector data."""

265

```

266

267

## Usage Examples

268

269

### Basic Flyer Operation

270

271

```python

272

from ophyd import Device

273

from ophyd.flyers import MonitorFlyerMixin

274

275

# Create a flyer device that monitors motor position

276

class MotorFlyer(MonitorFlyerMixin, Device):

277

def __init__(self, motor, **kwargs):

278

self.motor = motor

279

super().__init__(

280

monitor_attrs=['motor.position'],

281

stream_names={'motor.position': 'primary'},

282

**kwargs

283

)

284

285

# Use the flyer

286

motor_flyer = MotorFlyer(my_motor, name='motor_flyer')

287

288

# Standard flyer workflow

289

kickoff_status = motor_flyer.kickoff() # Start monitoring

290

# ... motor moves or other operations happen ...

291

complete_status = motor_flyer.complete() # Signal completion

292

wait(complete_status) # Wait for completion

293

294

# Collect the data

295

data_events = list(motor_flyer.collect())

296

data_description = motor_flyer.describe_collect()

297

```

298

299

### Area Detector Time Series

300

301

```python

302

from ophyd.flyers import AreaDetectorTimeseriesCollector

303

304

# Create time series collector

305

ts_collector = AreaDetectorTimeseriesCollector(

306

'XF:DET:TS:',

307

name='ts_collector',

308

stream_name='detector_timeseries'

309

)

310

311

# Configure collection

312

ts_collector.num_points.set(1000) # Collect 1000 points

313

314

# Execute flyer protocol

315

kickoff_status = ts_collector.kickoff()

316

wait(kickoff_status) # Wait for acquisition to start

317

318

complete_status = ts_collector.complete()

319

wait(complete_status) # Wait for acquisition to finish

320

321

# Retrieve time series data

322

events = list(ts_collector.collect())

323

for event in events:

324

print(f"Time: {event['time']}")

325

print(f"Data: {event['data']}")

326

```

327

328

### Monitor-Based Multi-Device Flying

329

330

```python

331

# Monitor multiple devices during a scan

332

class MultiDeviceFlyer(MonitorFlyerMixin, Device):

333

def __init__(self, motor, detector, **kwargs):

334

self.motor = motor

335

self.detector = detector

336

super().__init__(

337

monitor_attrs=['motor.position', 'detector.value'],

338

stream_names={

339

'motor.position': 'primary',

340

'detector.value': 'detector_stream'

341

},

342

pivot=True, # Separate events for each reading

343

**kwargs

344

)

345

346

flyer = MultiDeviceFlyer(my_motor, my_detector, name='multi_flyer')

347

348

# Use with bluesky plans

349

from bluesky.plans import fly

350

from bluesky import RunEngine

351

352

RE = RunEngine()

353

RE(fly([flyer])) # Execute fly plan with the flyer

354

```

355

356

## Integration with Bluesky

357

358

Flyers integrate seamlessly with Bluesky's experiment orchestration:

359

360

- **Run Engine Compatibility**: All flyers work with Bluesky's RunEngine

361

- **Event Model**: Data follows Bluesky's event model for analysis pipeline integration

362

- **Stream Organization**: Multiple data streams allow flexible data organization

363

- **Status Protocol**: Uses ophyd's StatusBase for proper asynchronous operation tracking

364

- **Pause/Resume**: Supports experiment pause/resume functionality where implemented

365

366

Flyers enable sophisticated data collection scenarios including fly scans, continuous monitoring, and coordinated multi-device acquisition during uninterrupted motion or operations.