-
Notifications
You must be signed in to change notification settings - Fork 0
/
wfs.py
3458 lines (3163 loc) · 174 KB
/
wfs.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# -*- coding: utf-8 -*-
"""Wrapper for interfacing with the Thorlabs Wavefront Sensor (WFS)."""
import ctypes
from ctypes.util import find_library
import logging.config
import os
import yaml
from vi import Vi
__version__ = '0.5.0'
__author__ = 'David Amrhein'
__email__ = 'davea50@gmail.com'
def setup_logging(path='logging.yaml', level=logging.INFO, env_key='LOG_CFG'):
"""Setup logging configuration.
Uses logging.yaml for the default configuration.
Args:
path:
level:
env_key:
"""
path = path
value = os.getenv(env_key, None)
if value:
path = value
if os.path.exists(path):
with open(path, 'rt') as f:
config = yaml.load(f.read())
logging.config.dictConfig(config)
else:
logging.basicConfig(level=level)
class WFS(object):
"""Thorlabs Shack-Hartmann Wavefront Sensor Interface."""
# Constants declared in WFS.h header file
# Buffers
WFS_BUFFER_SIZE = 256 # General buffer size
WFS_ERR_DESCR_BUFFER_SIZE = 512 # Buffer size for error messages
# Error/Warning Codes
# max errors from camera driver
MAX_CAM_DRIVER_ERRORS = 2000 # camera driver errors in range 1 ... MAX_CAM_DRIVER_ERRORS
MAX_WFS_DEVICES = 5 # max 5 cams at the same time connected
MAX_MLA_CALS = 7 # max. 7 MLA cals per device
# Offsets
WFS_ERROR = (-2147483647 - 1) # 0x80000000, -2147483648
WFS_INSTR_WARNING_OFFSET = 0x3FFC0900 # 1073481984
WFS_INSTR_ERROR_OFFSET = WFS_ERROR + 0x3FFC0900 # 0xBFFC0900, -1074001664
# WFS Driver Error Codes; error texts defined in WFS_ErrorMessage()
WFS_SUCCESS = 0
WFS_ERROR_PARAMETER1 = WFS_ERROR + 0x3FFC0001 # -1074003967
WFS_ERROR_PARAMETER2 = WFS_ERROR + 0x3FFC0002 # -1074003966
WFS_ERROR_PARAMETER3 = WFS_ERROR + 0x3FFC0003 # -1074003965
WFS_ERROR_PARAMETER4 = WFS_ERROR + 0x3FFC0004 # -1074003964
WFS_ERROR_PARAMETER5 = WFS_ERROR + 0x3FFC0005 # -1074003963
WFS_ERROR_PARAMETER6 = WFS_ERROR + 0x3FFC0006 # -1074003962
WFS_ERROR_PARAMETER7 = WFS_ERROR + 0x3FFC0007 # -1074003961
WFS_ERROR_PARAMETER8 = WFS_ERROR + 0x3FFC0008 # -1074003960
WFS_ERROR_PARAMETER9 = WFS_ERROR + 0x3FFC0009 # -1074003959
WFS_ERROR_API_ID_NOT_SUPPORTED = 0x00000504
WFS_ERROR_NO_SENSOR_CONNECTED = WFS_INSTR_ERROR_OFFSET + 0x00 # -1074001664
WFS_ERROR_OUT_OF_MEMORY = WFS_INSTR_ERROR_OFFSET + 0x01 # -1074001663
WFS_ERROR_INVALID_HANDLE = WFS_INSTR_ERROR_OFFSET + 0x02 # -1074001662
WFS_ERROR_CAM_NOT_CONFIGURED = WFS_INSTR_ERROR_OFFSET + 0x03 # -1074001661
WFS_ERROR_PIXEL_FORMAT = WFS_INSTR_ERROR_OFFSET + 0x04 # -1074001660
WFS_ERROR_EEPROM_CHECKSUM = WFS_INSTR_ERROR_OFFSET + 0x05 # -1074001659
WFS_ERROR_EEPROM_CAL_DATA = WFS_INSTR_ERROR_OFFSET + 0x06 # -1074001658
WFS_ERROR_OLD_REF_FILE = WFS_INSTR_ERROR_OFFSET + 0x07 # -1074001657
WFS_ERROR_NO_REF_FILE = WFS_INSTR_ERROR_OFFSET + 0x08 # -1074001656
WFS_ERROR_CORRUPT_REF_FILE = WFS_INSTR_ERROR_OFFSET + 0x09 # -1074001655
WFS_ERROR_WRITE_FILE = WFS_INSTR_ERROR_OFFSET + 0x0a # -1074001654
WFS_ERROR_INSUFF_SPOTS_FOR_ZERNFIT = WFS_INSTR_ERROR_OFFSET + 0x0b # -1074001653
WFS_ERROR_TOO_MANY_SPOTS_FOR_ZERNFIT = WFS_INSTR_ERROR_OFFSET + 0x0c # -1074001652
WFS_ERROR_FOURIER_ORDER = WFS_INSTR_ERROR_OFFSET + 0x0d # -1074001651
WFS_ERROR_NO_RECON_DEVIATIONS = WFS_INSTR_ERROR_OFFSET + 0x0e # -1074001650
WFS_ERROR_NO_PUPIL_DEFINED = WFS_INSTR_ERROR_OFFSET + 0x0f # -1074001649
WFS_ERROR_WRONG_PUPIL_DIA = WFS_INSTR_ERROR_OFFSET + 0x10 # -1074001648
WFS_ERROR_WRONG_PUPIL_CTR = WFS_INSTR_ERROR_OFFSET + 0x11 # -1074001647
WFS_ERROR_INVALID_CAL_DATA = WFS_INSTR_ERROR_OFFSET + 0x12 # -1074001646
WFS_ERROR_INTERNAL_REQUIRED = WFS_INSTR_ERROR_OFFSET + 0x13 # -1074001645
WFS_ERROR_ROC_RANGE = WFS_INSTR_ERROR_OFFSET + 0x14 # -1074001644
WFS_ERROR_NO_USER_REFERENCE = WFS_INSTR_ERROR_OFFSET + 0x15 # -1074001643
WFS_ERROR_AWAITING_TRIGGER = WFS_INSTR_ERROR_OFFSET + 0x16 # -1074001642
WFS_ERROR_NO_HIGHSPEED = WFS_INSTR_ERROR_OFFSET + 0x17 # -1074001641
WFS_ERROR_HIGHSPEED_ACTIVE = WFS_INSTR_ERROR_OFFSET + 0x18 # -1074001640
WFS_ERROR_HIGHSPEED_NOT_ACTIVE = WFS_INSTR_ERROR_OFFSET + 0x19 # -1074001639
WFS_ERROR_HIGHSPEED_WINDOW_MISMATCH = WFS_INSTR_ERROR_OFFSET + 0x1a # -1074001638
WFS_ERROR_NOT_SUPPORTED = WFS_INSTR_ERROR_OFFSET + 0x1b # -1074001637
# returned from non-exported functions
WFS_ERROR_SPOT_TRUNCATED = WFS_INSTR_ERROR_OFFSET + 0x1b # -1074001637
WFS_ERROR_NO_SPOT_DETECTED = WFS_INSTR_ERROR_OFFSET + 0x1c # -1074001636
WFS_ERROR_TILT_CALCULATION = WFS_INSTR_ERROR_OFFSET + 0x1d # -1074001635
# WFS Driver Warning Codes
WFS_WARNING = WFS_INSTR_WARNING_OFFSET + 0x00 # 1073481984
WFS_WARN_NSUP_ID_QUERY = 0x3FFC0101 # 1073479937
WFS_WARN_NSUP_RESET = 0x3FFC0102 # 1073479938
WFS_WARN_NSUP_SELF_TEST = 0x3FFC0103 # 1073479939
WFS_WARN_NSUP_ERROR_QUERY = 0x3FFC0104 # 1073479940
WFS_WARN_NSUP_REV_QUERY = 0x3FFC0105 # 1073479941
WFS_WARNING_CODES = {WFS_WARN_NSUP_ID_QUERY: b'Identification query not supported!',
WFS_WARN_NSUP_RESET: b'Reset not supported!',
WFS_WARN_NSUP_SELF_TEST: b'Self-test not supported!',
WFS_WARN_NSUP_ERROR_QUERY: b'Error query not supported!',
WFS_WARN_NSUP_REV_QUERY: b'Instrument revision query not supported!'}
# Driver Status reporting (lower 24 bits)
WFS_STATBIT_CON = 0x00000001 # USB connection lost, set by driver
WFS_STATBIT_PTH = 0x00000002 # Power too high (cam saturated)
WFS_STATBIT_PTL = 0x00000004 # Power too low (low cam digits)
WFS_STATBIT_HAL = 0x00000008 # High ambient light
WFS_STATBIT_SCL = 0x00000010 # Spot contrast too low
WFS_STATBIT_ZFL = 0x00000020 # Zernike fit failed because of not enough detected spots
WFS_STATBIT_ZFH = 0x00000040 # Zernike fit failed because of too much detected spots
WFS_STATBIT_ATR = 0x00000080 # Camera is still awaiting a trigger
WFS_STATBIT_CFG = 0x00000100 # Camera is configured, ready to use
WFS_STATBIT_PUD = 0x00000200 # Pupil is defined
WFS_STATBIT_SPC = 0x00000400 # Number of spots or pupil or aoi has been changed
WFS_STATBIT_RDA = 0x00000800 # Reconstructed spot deviations available
WFS_STATBIT_URF = 0x00001000 # User reference data available
WFS_STATBIT_HSP = 0x00002000 # Camera is in Highspeed Mode
WFS_STATBIT_MIS = 0x00004000 # Mismatched centroids in Highspeed Mode
WFS_STATBIT_LOS = 0x00008000 # Low number of detected spots, warning: reduced Zernike accuracy
WFS_STATBIT_FIL = 0x00010000 # Pupil is badly filled with spots, warning: reduced Zernike accuracy
WFS_DRIVER_STATUS = {WFS_STATBIT_CON: b'USB connection lost, set by driver',
WFS_STATBIT_PTH: b'Power too high (cam saturated)',
WFS_STATBIT_PTL: b'Power too low (low cam digits)',
WFS_STATBIT_HAL: b'High ambient light',
WFS_STATBIT_SCL: b'Spot contrast too low',
WFS_STATBIT_ZFL: b'Zernike fit failed because of not enough detected spots',
WFS_STATBIT_ZFH: b'Zernike fit failed because of too many detected spots',
WFS_STATBIT_ATR: b'Camera is still awaiting a trigger',
WFS_STATBIT_CFG: b'Camera is configured, ready to use',
WFS_STATBIT_PUD: b'Pupil is defined',
WFS_STATBIT_SPC: b'Number of spots or pupil or aoi has been changed',
WFS_STATBIT_RDA: b'Reconstructed spot deviations available',
WFS_STATBIT_URF: b'User reference data available',
WFS_STATBIT_HSP: b'Camera is in Highspeed Mode',
WFS_STATBIT_MIS: b'Mismatched centroids in Highspeed Mode',
WFS_STATBIT_LOS: b'Low number of detected spots, warning: reduced Zernike accuracy',
WFS_STATBIT_FIL: b'Pupil is badly filled with spots, warning: reduced Zernike accuracy'}
# Timeout
# * 10 ms = 24 hours, given to is_SetTimeout, after that time is_IsVideoFinish returns 'finish' without error
WFS_TRIG_TIMEOUT = 100 * 60 * 60 * 24 # 8640000
WFS_TIMEOUT_CAPTURE_NORMAL = 5.0 # in seconds
WFS_TIMEOUT_CAPTURE_TRIGGER = 0.1 # in seconds, allow fast return of functions WFS_TakeSpotfieldImage...
WFS10_TIMEOUT_CAPTURE_NORMAL = 4000 # in ms, allow 500 ms exposure time + reserve
WFS10_TIMEOUT_CAPTURE_TRIGGER = 100 # in ms, allow fast return of functions WFS_TakeSpotfieldImage...
WFS20_TIMEOUT_CAPTURE_NORMAL = 4000 # in ms, allow 84 ms exposure time + reserve
WFS20_TIMEOUT_CAPTURE_TRIGGER = 100 # in ms, allow fast return of functions WFS_TakeSpotfieldImage...
# Exported constants
WFS_TRUE = 1
WFS_FALSE = 0
# Defines for WFS camera
EXPOSURE_MANUAL = 0
EXPOSURE_AUTO = 1
MASTER_GAIN_MIN_WFS = 1.0 # real gain factor, not 0 ... 100% percent
MASTER_GAIN_MIN_WFS10 = 1.5 # 1.0 prevents ADC from saturation on overexposure
MASTER_GAIN_MIN_WFS20 = 1.0
MASTER_GAIN_MAX_WFS20 = 1.0
MASTER_GAIN_MAX_WFS30 = 24.0
MASTER_GAIN_MAX_WFS40 = 4.0
MASTER_GAIN_MAX = 13.66
MASTER_GAIN_MAX_DISPLAY = 5.0 # dark signal is too noisy for higher amplification
MASTER_GAIN_EXPONENT = 38.26 # based on natural logarithm
MASTER_GAIN_EXPONENT_WFS30 = 31.465
MASTER_GAIN_FACTOR_WFS40 = 33.333
NOISE_LEVEL_MIN = 0 # level for cutting spotfield
NOISE_LEVEL_MAX = 256
BLACK_LEVEL_MIN = 0
BLACK_LEVEL_MAX = 255
BLACK_LEVEL_WFS_DEF = 100 # lower values causes problems with auto-exposure and trigger (WFS)
BLACK_LEVEL_WFS10_DEF = 100 # for cam shifted to 0 ... +15
BLACK_LEVEL_WFS20_DEF = 0
BLACK_LEVEL_WFS30_DEF = 0
BLACK_LEVEL_WFS40_DEF = 0
# Pixel format defines
PIXEL_FORMAT_MONO8 = 0
PIXEL_FORMAT_MONO16 = 1
# pre-defined image sizes for WFS150/300 instruments
CAM_RES_1280 = 0 # 1280x1024
CAM_RES_1024 = 1 # 1024x1024
CAM_RES_768 = 2 # 768x768
CAM_RES_512 = 3 # 512x512
CAM_RES_320 = 4 # 320x320 smallest!
CAM_RES_MAX_IDX = 4
# pre-defined image sizes for WFS10 instruments
CAM_RES_WFS10_640 = 0 # 640x480
CAM_RES_WFS10_480 = 1 # 480x480
CAM_RES_WFS10_360 = 2 # 360x360
CAM_RES_WFS10_260 = 3 # 260x260
CAM_RES_WFS10_180 = 4 # 180x180 smallest!
CAM_RES_WFS10_MAX_IDX = 4
# pre-defined image sizes for WFS20 instruments
CAM_RES_WFS20_1440 = 0 # 1440x1080
CAM_RES_WFS20_1080 = 1 # 1080x1080
CAM_RES_WFS20_768 = 2 # 768x768
CAM_RES_WFS20_512 = 3 # 512x512
CAM_RES_WFS20_360 = 4 # 360x360 smallest!
CAM_RES_WFS20_720_BIN2 = 5 # 720x540, binning 2x2
CAM_RES_WFS20_540_BIN2 = 6 # 540x540, binning 2x2
CAM_RES_WFS20_384_BIN2 = 7 # 384x384, binning 2x2
CAM_RES_WFS20_256_BIN2 = 8 # 256x256, binning 2x2
CAM_RES_WFS20_180_BIN2 = 9 # 180x180, binning 2x2
CAM_RES_WFS20_MAX_IDX = 9
CAM_MAX_PIX_X = 1440
CAM_MAX_PIX_Y = 1080
# pre-defined image sizes for WFS30 instruments
CAM_RES_WFS30_1936 = 0 # 1936x1216
CAM_RES_WFS30_1216 = 1 # 1216x1216
CAM_RES_WFS30_1024 = 2 # 1024x1024
CAM_RES_WFS30_768 = 3 # 768x768
CAM_RES_WFS30_512 = 4 # 512x512
CAM_RES_WFS30_360 = 5 # 360x360 smallest!
CAM_RES_WFS30_968_SUB2 = 6 # 968x608, subsampling 2x2
CAM_RES_WFS30_608_SUB2 = 7 # 608x608, subsampling 2x2
CAM_RES_WFS30_512_SUB2 = 8 # 512x512, subsampling 2x2
CAM_RES_WFS30_384_SUB2 = 9 # 384x384, subsampling 2x2
CAM_RES_WFS30_256_SUB2 = 10 # 256x256, subsampling 2x2
CAM_RES_WFS30_180_SUB2 = 11 # 180x180, subsampling 2x2
CAM_RES_WFS30_MAX_IDX = 11
# pre-defined image sizes for WFS40 instruments
CAM_RES_WFS40_2048 = 0 # 2048x2048
CAM_RES_WFS40_1536 = 1 # 1536x1536
CAM_RES_WFS40_1024 = 2 # 1024x1024
CAM_RES_WFS40_768 = 3 # 768x768
CAM_RES_WFS40_512 = 4 # 512x512
CAM_RES_WFS40_360 = 5 # 360x360 smallest!
CAM_RES_WFS40_1024_SUB2 = 6 # 1024x1024, subsampling 2x2
CAM_RES_WFS40_768_SUB2 = 7 # 608x608, subsampling 2x2
CAM_RES_WFS40_512_SUB2 = 8 # 512x512, subsampling 2x2
CAM_RES_WFS40_384_SUB2 = 9 # 384x384, subsampling 2x2
CAM_RES_WFS40_256_SUB2 = 10 # 256x256, subsampling 2x2
CAM_RES_WFS40_180_SUB2 = 11 # 180x180, subsampling 2x2
CAM_RES_WFS40_MAX_IDX = 11
# Hardware trigger modes
WFS_HW_TRIGGER_OFF = 0
WFS_HW_TRIGGER_HL = 1
WFS_HW_TRIGGER_LH = 2
WFS_SW_TRIGGER = 3
WFS_TRIGGER_MODE_MIN = WFS_HW_TRIGGER_OFF
WFS_TRIGGER_MODE_MAX = WFS_SW_TRIGGER
# Averaging
AVERAGE_COUNT_MAX = 256
# Pupil
PUPIL_DIA_MIN_MM = 0.5 # for coarse check only
PUPIL_DIA_MAX_MM = 12.0
PUPIL_CTR_MIN_MM = -8.0
PUPIL_CTR_MAX_MM = 8.0
# Wavefront types
WAVEFRONT_MEAS = 0
WAVEFRONT_REC = 1
WAVEFRONT_DIFF = 2
# Max number of detectable spots
MAX_SPOTS_X = 80 # WFS30: 1936 * 5.86 / 150 = 75.6; WFS40: 2048 * 5.5 / 150 = 75.1
MAX_SPOTS_Y = 80 # WFS40: 2048 * 5.5 / 150 = 75.1
# MAX_SPOTS_X = 47 # WFS20: 1440*5/150 - 1 = 47
# MAX_SPOTS_Y = 35 # WFS20: 1080*5/150 - 1 = 35
# Reference
WFS_REF_INTERNAL = 0
WFS_REF_USER = 1
# Spots ref types
WFS_REF_TYPE_REL = 0
WFS_REF_TYPE_ABS = 1
# Spherical reference
ROC_CAL_MIN_MM = 100.0
ROC_CAL_MAX_MM = 5000.0
# Zernike polynomials
MIN_NUMDOTS_FIT = 5
MAX_NUMDOTS_FIT = MAX_SPOTS_X * MAX_SPOTS_Y # max number of spots used for Zernike fit
MAX_NUM_ROWS_B = 2 * MAX_NUMDOTS_FIT + 1 # max number of rows in Matrix containing x and y deviations and a piston
ZERNIKE_ORDERS_AUTO = 0
MIN_ZERNIKE_ORDERS = 2
MAX_ZERNIKE_ORDERS = 10
MIN_ZERNIKE_MODES = 6 # min number of modes in Zernike fit
MAX_ZERNIKE_MODES = 66 # max number of modes in Zernike fit
ZERNIKE_WARNING_LEVEL = 1.3 # 30% more spots than desired Zernike modes should be detected for good accuracy
# for conversion pixel - mm
NOT_CENTERED = 0
CENTERED = 1
def __init__(self):
setup_logging()
self.log_wfs = logging.getLogger('WFS')
self.lib = self.find_wfs_library()
self.adapt_centroids = Vi.int32(0)
self.allow_auto_exposure = Vi.int32(1)
self.array_centroid_x = Vi.array_float(self.MAX_SPOTS_X, self.MAX_SPOTS_Y)
self.array_centroid_y = Vi.array_float(self.MAX_SPOTS_X, self.MAX_SPOTS_Y)
self.array_deviations_x = Vi.array_float(self.MAX_SPOTS_X, self.MAX_SPOTS_Y)
self.array_deviations_y = Vi.array_float(self.MAX_SPOTS_X, self.MAX_SPOTS_Y)
self.array_diameter_x = Vi.array_float(self.MAX_SPOTS_X, self.MAX_SPOTS_Y)
self.array_diameter_y = Vi.array_float(self.MAX_SPOTS_X, self.MAX_SPOTS_Y)
self.array_image_buffer = Vi.array_uint8(self.CAM_MAX_PIX_X, self.CAM_MAX_PIX_Y)
self.array_image_buffer_ref = Vi.array_uint8(self.WFS_BUFFER_SIZE)
self.array_intensity = Vi.array_float(self.MAX_SPOTS_X, self.MAX_SPOTS_Y)
self.array_line_max = Vi.array_float(self.CAM_MAX_PIX_X)
self.array_line_min = Vi.array_float(self.CAM_MAX_PIX_X)
self.array_line_selected = Vi.array_float(self.CAM_MAX_PIX_X)
self.array_reference_x = Vi.array_float(self.MAX_SPOTS_X, self.MAX_SPOTS_Y)
self.array_reference_y = Vi.array_float(self.MAX_SPOTS_X, self.MAX_SPOTS_Y)
self.array_scale_x = Vi.array_float(self.MAX_SPOTS_X)
self.array_scale_y = Vi.array_float(self.MAX_SPOTS_Y)
self.array_wavefront = Vi.array_float(self.MAX_SPOTS_X, self.MAX_SPOTS_Y)
self.array_wavefront_wave = Vi.array_float(self.MAX_SPOTS_X, self.MAX_SPOTS_Y)
self.array_wavefront_xy = Vi.array_float(self.MAX_SPOTS_Y, self.MAX_SPOTS_X)
self.array_wavefront_yx = Vi.array_float(self.MAX_SPOTS_X, self.MAX_SPOTS_Y)
self.array_zernike_orders_um = Vi.array_float((self.MAX_ZERNIKE_ORDERS + 1))
self.array_zernike_reconstructed = Vi.array_uint8(self.MAX_ZERNIKE_MODES + 1)
self.array_zernike_um = Vi.array_float(self.MAX_ZERNIKE_MODES + 1)
self.average_count = Vi.int32(1)
self.average_data_ready = Vi.int32(0)
self.aoi_center_x_mm = Vi.real64(0)
self.aoi_center_y_mm = Vi.real64(0)
self.aoi_size_x_mm = Vi.real64(0) # 0 is full sensor size
self.aoi_size_y_mm = Vi.real64(0) # 0 is full sensor size
self.beam_centroid_x_mm = Vi.real64(0)
self.beam_centroid_y_mm = Vi.real64(0)
self.beam_diameter_x_mm = Vi.real64(0)
self.beam_diameter_y_mm = Vi.real64(0)
self.black_level_offset_actual = Vi.int32(0)
self.black_level_offset_set = Vi.int32(100)
self.calculate_diameters = Vi.int32(1)
self.cam_pitch_um = Vi.real64(0)
self.cam_resolution_index = Vi.int32(0)
self.cam_resolution_factor = Vi.int32(1)
self.cam_resolution_x = Vi.int32(0)
self.cam_resolution_y = Vi.int32(0)
self.cancel_wavefront_tilt = Vi.int32(1)
self.device_id = Vi.int32(0)
self.device_status = Vi.int32(0)
self.diameter_max = Vi.real64(0)
self.diameter_mean = Vi.real64(0)
self.diameter_min = Vi.real64(0)
self.do_spherical_reference = Vi.int32(0)
self.dynamic_noise_cut = Vi.int32(1)
self.error_code = Vi.int32(0)
self.error_message = Vi.char(self.WFS_ERR_DESCR_BUFFER_SIZE)
self.exposure_time_actual = Vi.real64(0)
self.exposure_time_increment = Vi.real64(0.005)
self.exposure_time_max = Vi.real64(83.3479995727539)
self.exposure_time_min = Vi.real64(0.01)
self.exposure_time_set = Vi.real64(0.0793666666667)
self.firmware_revision = Vi.char(self.WFS_BUFFER_SIZE)
self.fit_error_mean = Vi.real64(0)
self.fit_error_stdev = Vi.real64(0)
self.fourier_j0 = Vi.real64(0)
self.fourier_j45 = Vi.real64(0)
self.fourier_m = Vi.real64(0)
self.fourier_orders = Vi.int32(2) # 2, 4, 6 only valid settings
self.grid_correction_0 = Vi.real64(0)
self.grid_correction_45 = Vi.real64(0)
self.grid_correction_pitch = Vi.real64(0)
self.grid_correction_rotation = Vi.real64(0)
self.highspeed_mode = Vi.int32(0)
self.id_query = Vi.boolean(0)
self.intensity_limit = Vi.int32(1)
self.intensity_max = Vi.int32(0)
self.intensity_min = Vi.int32(0)
self.intensity_mean = Vi.real64(0)
self.intensity_rms = Vi.real64(0)
self.in_use = Vi.int32(0)
self.instrument_count = Vi.int32(0)
self.instrument_driver_revision = Vi.char(self.WFS_BUFFER_SIZE)
self.instrument_handle = Vi.session(Vi.NULL)
self.instrument_index = Vi.int32(0)
self.instrument_name_wfs = Vi.char(self.WFS_BUFFER_SIZE)
self.lenslet_focal_length_um = Vi.real64(0)
self.lenslet_pitch_um = Vi.real64(0)
self.limit_to_pupil = Vi.int32(0)
self.line = Vi.int32(0)
self.manufacturer_name = Vi.char(self.WFS_BUFFER_SIZE)
self.master_gain_actual = Vi.real64(0)
self.master_gain_max = Vi.real64(1)
self.master_gain_min = Vi.real64(1)
self.master_gain_set = Vi.real64(1)
self.mla_count = Vi.int32(1)
self.mla_index = Vi.int32(0)
self.mla_name = Vi.char(self.WFS_BUFFER_SIZE)
self.optometric_axis = Vi.real64(0)
self.optometric_cylinder = Vi.real64(0)
self.optometric_sphere = Vi.real64(0)
self.pixel_format = Vi.int32(self.PIXEL_FORMAT_MONO8)
self.pupil_center_x_mm = Vi.real64(0)
self.pupil_center_y_mm = Vi.real64(0)
self.pupil_diameter_x_mm = Vi.real64(5.4) # Max diameter without clipping edges
self.pupil_diameter_y_mm = Vi.real64(5.4) # Max diameter without clipping edges
self.reference_index = Vi.int32(0)
self.reset_device = Vi.boolean(0)
self.resource_name = Vi.rsrc(self.WFS_BUFFER_SIZE, b'USB::0x1313::0x0000::1')
self.roc_mm = Vi.real64(0)
self.rolling_reset = Vi.int32(0)
self.saturated_pixels_percent = Vi.real64(0)
self.serial_number_camera = Vi.char(self.WFS_BUFFER_SIZE)
self.serial_number_wfs = Vi.char(self.WFS_BUFFER_SIZE)
self.spot_offset_x = Vi.real64(0)
self.spot_offset_y = Vi.real64(0)
self.spot_ref_type = Vi.int32(0)
self.spotfield_columns = Vi.int32(0)
self.spotfield_rows = Vi.int32(0)
self.spots_x = Vi.int32(0)
self.spots_y = Vi.int32(0)
self.subtract_offset = Vi.int32(0)
self.test_result = Vi.int16(0)
self.test_message = Vi.char(self.WFS_BUFFER_SIZE)
self.trigger_delay_actual = Vi.int32(0)
self.trigger_delay_increment = Vi.int32(1)
self.trigger_delay_max = Vi.int32(699000)
self.trigger_delay_min = Vi.int32(0)
self.trigger_delay_set = Vi.int32(0)
self.trigger_mode = Vi.int32(0)
self.wavefront_diff = Vi.real64(0)
self.wavefront_max = Vi.real64(0)
self.wavefront_mean = Vi.real64(0)
self.wavefront_min = Vi.real64(0)
self.wavefront_rms = Vi.real64(0)
self.wavefront_type = Vi.int32(0)
self.wavefront_weighted_rms = Vi.real64(0)
self.wavelength = Vi.real64(532)
self.zernike_orders = Vi.int32(4) # 0=Auto, 2, 3, 4, 5, 6, 7, 8, 9, 10
self.zernike_modes = Vi.int32(15)
self.window_count_x = Vi.int32(0)
self.window_count_y = Vi.int32(0)
self.window_size_x = Vi.int32(0)
self.window_size_y = Vi.int32(0)
# self.window_start_position_x = Vi.int32(0)
# self.window_start_position_y = Vi.int32(0)
self.window_start_position_x = Vi.array_float(self.MAX_SPOTS_X)
self.window_start_position_y = Vi.array_float(self.MAX_SPOTS_Y)
# NAME = {Index: (x_res, y_res, sub_factor)}
self.cam_res_WFS150 = {0: (1280, 1024, 1),
1: (1024, 1024, 1),
2: (768, 768, 1),
3: (512, 512, 1),
4: (320, 320, 1)}
self.cam_res_WFS10 = {0: (640, 480, 1),
1: (480, 480, 1),
2: (360, 360, 1),
3: (260, 260, 1),
4: (180, 180, 1)}
self.cam_res_WFS20 = {0: (1440, 1080, 1),
1: (1080, 1080, 1),
2: (768, 768, 1),
3: (512, 512, 1),
4: (360, 360, 1),
5: (720, 540, 2),
6: (540, 540, 2),
7: (384, 384, 2),
8: (256, 256, 2),
9: (180, 180, 2)}
self.cam_res_WFS30 = {0: (1936, 1216, 1),
1: (1216, 1216, 1),
2: (1024, 1024, 1),
3: (768, 768, 1),
4: (512, 512, 1),
5: (360, 360, 1),
6: (968, 968, 2),
7: (608, 608, 2),
8: (512, 512, 2),
9: (384, 384, 2),
10: (256, 256, 2),
11: (180, 180, 2)}
self.cam_res_WFS40 = {0: (2048, 2048, 1),
1: (1536, 1536, 1),
2: (1024, 1024, 1),
3: (768, 768, 1),
4: (512, 512, 1),
5: (360, 360, 1),
6: (1024, 1024, 2),
7: (768, 768, 2),
8: (512, 512, 2),
9: (384, 384, 2),
10: (256, 256, 2),
11: (180, 180, 2)}
self.cam_res_id = {'WFS150': self.cam_res_WFS150,
'WFS10': self.cam_res_WFS10,
'WFS20': self.cam_res_WFS20,
'WFS30': self.cam_res_WFS30,
'WFS40': self.cam_res_WFS40}
# Zernike Order: Zernike Modes
self.zernike_modes_per_order = {2: 6,
3: 10,
4: 15,
5: 21,
6: 28,
7: 36,
8: 45,
9: 55,
10: 66}
def find_wfs_library(self):
"""Find and load the WFS .dll in the system.
Returns:
ctypes.windll.LoadLibrary(WFS_32/64.dll)
"""
bitness = ctypes.sizeof(ctypes.c_void_p) * 8 # =32 on x86, =64 on x64
if bitness is 64:
self.log_wfs.critical('64 bit Windows has OSError Access Violations')
raise ImportError('64 bit Windows has OSError Access Violations')
lib = find_library(f'WFS_{bitness}')
if lib is None:
self.log_wfs.critical(f'WFS_{bitness}.dll not found')
raise ImportError(f'WFS_{bitness}.dll not found')
_lib_wfs = ctypes.windll.LoadLibrary(lib)
self.log_wfs.debug(f'{lib} loaded')
return _lib_wfs
# WFS Functions
def _init(self, resource_name=None, id_query=None, reset_device=None):
"""Initializes the instrument driver session.
Each time this function is invoked an unique session is opened.
It Performs the following initialization actions:
(1) Opens a session to the Default Resource Manager resource
and a session to the selected device using the Resource
Name.
(2) Performs an identification query on the Instrument.
(3) Resets the instrument to a known state.
(4) Sends initialization commands to the instrument.
(5) Returns an instrument handle which is used to differentiate
between different sessions of this instrument driver.
Args:
resource_name (Vi.rsrc(int, str)): This parameter specifies the
interface of the device that is to be initialized. The
resource name has to follow the syntax:
"USB::0x1313::0x0000::" followed by the Device ID.
The Device ID can be gotten with the function
_get_instrument_list_info E.g. "USB::0x1313::0x0000::1"
id_query (Vi.boolean(int)): Performs an In-System
Verification. Checks if the resource matches the vendor
and product id.
reset_device (Vi.boolean(int)): Performs Reset operation
and places the instrument in a pre-defined reset state.
Returns:
status (Vi.status(int)): Operational return status.
Contains either a completion code or an error code.
Instrument driver specific codes that may be returned
in addition to the VISA error codes defined in VPP-4.3
and vendor specific codes, are as follows.
Completion Codes:
WFS_SUCCESS: Initialization successful
WFS_WARN_NSUP_ID_QUERY: Identification query not
supported
WFS_WARN_NSUP_RESET: Reset not supported
Error Codes:
VI_ERROR_FAIL_ID_QUERY: Instrument identification
query failed
instrument_handle (Vi.session(int)): This parameter returns
an instrument handle that is used in all subsequent
calls to distinguish between different sessions of this
instrument driver.
"""
if resource_name is not None:
try:
self.resource_name = Vi.rsrc(self.WFS_BUFFER_SIZE, resource_name)
except (TypeError, ValueError):
self.resource_name = resource_name
if id_query is not None:
try:
self.id_query = Vi.boolean(id_query)
except ValueError:
self.id_query = id_query
if reset_device is not None:
try:
self.reset_device = Vi.boolean(reset_device)
except ValueError:
self.reset_device = reset_device
status = self.lib.WFS_init(self.resource_name,
self.id_query,
self.reset_device,
ctypes.byref(self.instrument_handle))
self.log_wfs.info(f'Init: {self.instrument_handle.value}')
self.log_wfs.info(f'Resource Name: {self.resource_name.value.decode()}')
self.log_wfs.info(f'ID Query: {self.id_query.value}')
self.log_wfs.info(f'Reset Device: {self.reset_device.value}')
self._error_message(status)
return status, self.instrument_handle.value
def _get_status(self):
"""Get the device status of the Wavefront Sensor instrument.
Returns:
status (Vi.status(int)): This value shows the status code
returned by the function call. For Status Codes see
function _error_message.
device_status (Vi.int32(int)): This parameter returns the
device status of the Wavefront Sensor instrument.
Lower 24 bits are used.
"""
self.device_status = ctypes.c_ubyte(0)
status = self.lib.WFS_GetStatus(self.instrument_handle,
ctypes.byref(self.device_status))
self.log_wfs.debug(f'Get Status: {self.instrument_handle.value}')
self.log_wfs.info(f'Device Status: {self.device_status.value}')
if self.device_status.value in self.WFS_DRIVER_STATUS:
self.log_wfs.info(f'Device Status: {self.WFS_DRIVER_STATUS[self.device_status.value].decode()}')
else:
self.log_wfs.info('Device Status: OK/Unknown')
self._error_message(status)
return status, self.device_status.value
def _close(self):
"""Closes the instrument driver session.
Note: The instrument must be reinitialized to use it again.
Returns:
status (Vi.status(int)): This value shows the status code
returned by the function call. For Status Codes see
function _error_message.
"""
status = self.lib.WFS_close(self.instrument_handle)
self.log_wfs.info(f'Close: {self.instrument_handle.value}')
self.instrument_handle.value = Vi.NULL
self._error_message(status)
return status
# Configuration Functions
def _get_instrument_info(self):
"""Get information about the instrument names and serials.
This function returns the following information about the
opened instrument:
- Driver Manufacturer Name
- Instrument Name
- Instrument Serial Number
- Camera Serial Number
Returns:
status (Vi.status(int)): This value shows the status code
returned by the function call. For Status Codes see
function _error_message.
manufacturer_name (Vi.char(int)): This parameter returns
the Manufacturer Name of this instrument driver.
Note: The string must contain at least WFS_BUFFER_SIZE
(256) elements (char(WFS_BUFFER_SIZE)).
instrument_name_wfs (Vi.char(int)): This parameter returns
the Instrument Name of the WFS.
Note: The string must contain at least WFS_BUFFER_SIZE
(256) elements (char(WFS_BUFFER_SIZE)).
serial_number_wfs (Vi.char(int)): This parameter returns
the Serial Number of the WFS.
Note: The string must contain at least WFS_BUFFER_SIZE
(256) elements (char(WFS_BUFFER_SIZE)).
serial_number_camera (Vi.char(int)): This parameter returns
the Serial Number of the camera body the WFS is based
on.
Note: The string must contain at least WFS_BUFFER_SIZE
(256) elements (char(WFS_BUFFER_SIZE)).
"""
status = self.lib.WFS_GetInstrumentInfo(self.instrument_handle,
self.manufacturer_name,
self.instrument_name_wfs,
self.serial_number_wfs,
self.serial_number_camera)
self.log_wfs.debug(f'Get Instrument Info: {self.instrument_handle.value}')
self.log_wfs.info(f'Manufacturer Name: {self.manufacturer_name.value.decode()}')
self.log_wfs.info(f'Instrument Name WFS: {self.instrument_name_wfs.value.decode()}')
self.log_wfs.info(f'Serial Number WFS: {self.serial_number_wfs.value.decode()}')
self.log_wfs.info(f'Serial Number Camera: {self.serial_number_camera.value.decode()}')
self._error_message(status)
return (status, self.manufacturer_name.value, self.instrument_name_wfs.value,
self.serial_number_wfs.value, self.serial_number_camera.value)
def _configure_cam(self, cam_resolution_index=None, pixel_format=None):
"""Configure the WFS camera resolution and max spots in X and Y.
This function configures the WFS instrument's camera resolution
and returns the maximum number of detectable spots in X and Y
direction. The result depends on the selected microlens array
in function _select_mla()
Note: This function is not available in Highspeed Mode!
Args:
cam_resolution_index (Vi.int32(int)): This parameter
selects the camera resolution in pixels. Only the
following pre-defined settings are supported:
For WFS150/300 instruments:
Index Resolution
0 1280x1024
1 1024x1024
2 768x768
3 512x512
4 320x320
For WFS10 instruments:
Index Resolution
0 640x480
1 480x480
2 360x360
3 260x260
4 180x180
For WFS20 instruments:
Index Resolution
0 1440x1080
1 1080x1080
2 768x768
3 512x512
4 360x360
5 720x540, bin2
6 540x540, bin2
7 384x384, bin2
8 256x256, bin2
9 180x180, bin2
For WFS30 instruments:
Index Resolution
0 1936x1216
1 1216x1216
2 1024x1024
3 768x768
4 512x512
5 360x360
6 968x608, sub2
7 608x608, sub2
8 512x512, sub2
9 384x384, sub2
10 256x256, sub2
11 180x180, sub2
For WFS40 instruments:
Index Resolution
0 2048x2048
1 1536x1536
2 1024x1024
3 768x768
4 512x512
5 360x360
6 1024x1024, sub2
7 768x768, sub2
8 512x512, sub2
9 384x384, sub2
10 256x256, sub2
11 180x180, sub2
pixel_format (Vi.int32(int)): This parameter selects the
bit width per pixel of the returned camera image.
Thorlabs WFS instruments currently support only 8 bit
format.
Returns:
status (Vi.status(int)): This value shows the status code
returned by the function call. For Status Codes see
function _error_message.
spots_x (Vi.int32(int)): This parameter returns the number
of spots which can be detected in X direction, based
on the selected camera resolution and Microlens Array
in function _select_mla.
spots_y (Vi.int32(int)): This parameter returns the number
of spots which can be detected in Y direction, based
on the selected camera resolution and Microlens Array
in function _select_mla.
"""
if cam_resolution_index is not None:
try:
self.cam_resolution_index = Vi.int32(cam_resolution_index)
except ValueError:
self.cam_resolution_index = cam_resolution_index
if pixel_format is not None:
try:
self.pixel_format = Vi.int32(pixel_format)
except ValueError:
self.pixel_format = pixel_format
status = self.lib.WFS_ConfigureCam(self.instrument_handle,
self.pixel_format,
self.cam_resolution_index,
ctypes.byref(self.spots_x),
ctypes.byref(self.spots_y))
cam_id = self.instrument_name_wfs.value.decode().split('-', 1)[0] # Remove text after '-'
self.cam_resolution_x.value = self.cam_res_id[cam_id][self.cam_resolution_index.value][0]
self.cam_resolution_y.value = self.cam_res_id[cam_id][self.cam_resolution_index.value][1]
self.cam_resolution_factor.value = self.cam_res_id[cam_id][self.cam_resolution_index.value][2]
self.log_wfs.debug(f'Configure Cam: {self.instrument_handle.value}')
self.log_wfs.info(f'Pixel Format: {self.pixel_format.value}')
self.log_wfs.info(f'Camera Resolution Index: {self.cam_resolution_index.value}')
self.log_wfs.info(f'Camera Resolution X: {self.cam_resolution_x.value}')
self.log_wfs.info(f'Camera Resolution Y: {self.cam_resolution_y.value}')
self.log_wfs.info(f'Camera Resolution Factor: {self.cam_resolution_factor.value}')
self.log_wfs.info(f'Spots X: {self.spots_x.value}')
self.log_wfs.info(f'Spots Y: {self.spots_y.value}')
self._error_message(status)
return status, self.spots_x.value, self.spots_y.value
def _set_highspeed_mode(self, highspeed_mode=None, adapt_centroids=None, subtract_offset=None,
allow_auto_exposure=None):
"""Set the WFS to use Highspeed mode.
This function activates/deactivates the camera's Highspeed Mode
for WFS10/WFS20 instruments. When activated, the camera
calculates the spot centroid positions internally and sends the
result to the WFS driver instead of sending raw spotfield
images.
Note: There is no camera image available when Highspeed Mode is
activated! Highspeed Mode is not available for
WFS150/WFS300/WFS30/WFS40 instruments!
Args:
highspeed_mode (Vi.int32(int)): This parameter determines
if the camera's Highspeed Mode is switched on or off.
adapt_centroids (Vi.int32(int)): When Highspeed Mode is
selected, this parameter determines if the centroid
positions measured in Normal Mode should be used to
adapt the spot search windows for Highspeed Mode.
Otherwise, a rigid grid based on reference spot
positions is used in Highspeed Mode.
subtract_offset (Vi.int32(int)): This parameter defines an
offset level for Highspeed Mode only. All camera pixels
will be subtracted by this level before the centroids
are being calculated, which increases accuracy.
Valid range: 0 ... 255
Note: The offset is only valid in Highspeed Mode and
must not set too high to clear the spots within the
camera image!
allow_auto_exposure (Vi.int32(int)): When Highspeed Mode is
selected, this parameter determines if the camera
should also calculate the image saturation in order
enable the auto exposure feature using function
_take_spotfield_image_auto_exposure() instead of
_take_spotfield_image(). This option leads to a
somewhat reduced measurement speed when enabled.
Returns:
status (Vi.status(int)): This value shows the status code
returned by the function call. For Status Codes see
function _error_message.
"""
if highspeed_mode is not None:
try:
self.highspeed_mode = Vi.int32(highspeed_mode)
except ValueError:
self.highspeed_mode = highspeed_mode
if adapt_centroids is not None:
try:
self.adapt_centroids = Vi.int32(adapt_centroids)
except ValueError:
self.adapt_centroids = adapt_centroids
if subtract_offset is not None:
try:
self.subtract_offset = Vi.int32(subtract_offset)
except ValueError:
self.subtract_offset = subtract_offset
if allow_auto_exposure is not None:
try:
self.allow_auto_exposure = Vi.int32(allow_auto_exposure)
except ValueError:
self.allow_auto_exposure = allow_auto_exposure
status = self.lib.WFS_SetHighspeedMode(self.instrument_handle,
self.highspeed_mode,
self.adapt_centroids,
self.subtract_offset,
self.allow_auto_exposure)
self.log_wfs.debug(f'Set Highspeed Mode: {self.instrument_handle.value}')
self.log_wfs.info(f'Highspeed Mode: {self.highspeed_mode.value}')
self.log_wfs.info(f'Adapt Centroids: {self.adapt_centroids.value}')
self.log_wfs.info(f'Subtract Offset: {self.subtract_offset.value}')
self.log_wfs.info(f'Allow Auto Exposure: {self.allow_auto_exposure.value}')
self._error_message(status)
return status
def _get_highspeed_windows(self):
"""Get the data from spot detection in Highspeed Mode.
This function returns data of the spot detection windows valid
in Highspeed Mode. Window size and positions depend on options
passed to function _set_highspeed_mode().
Note: This function is only available when Highspeed Mode is
activated!
Returns:
status (Vi.status(int)): This value shows the status code
returned by the function call. For Status Codes see
function _error_message.
window_count_x (Vi.int32(int)): This parameter returns the
number of spot windows in X direction.
window_count_y (Vi.int32(int)): This parameter returns the
number of spot windows in Y direction.
window_size_x (Vi.int32(int)): This parameter returns the
size in pixels of spot windows in X direction.
window_size_y (Vi.int32(int)): This parameter returns the
size in pixels of spot windows in Y direction.
window_start_position_x (Vi.int32(int)): This parameter
returns a one-dimensional array containing the start
positions in pixels for spot windows in X direction.
The required array size is MAX_SPOTS_X.
Note: Window Stop X = Windows Start X + Windows Size X
window_start_position_y (Vi.int32(int)):This parameter
returns a one-dimensional array containing the start
positions in pixels for spot windows in Y direction.
The required array size is MAX_SPOTS_Y.
Note: Window Stop Y = Windows Start Y + Windows Size Y
"""
status = self.lib.WFS_GetHighspeedWindows(self.instrument_handle,
ctypes.byref(self.window_count_x),
ctypes.byref(self.window_count_y),
ctypes.byref(self.window_size_x),
ctypes.byref(self.window_size_y),
self.window_start_position_x,
self.window_start_position_y)
columns = self.spots_x.value
rows = self.spots_y.value
self.log_wfs.debug(f'Get Highspeed Windows: {self.instrument_handle.value}')
self.log_wfs.info(f'Window Count X: {self.window_count_x.value}')
self.log_wfs.info(f'Window Count Y: {self.window_count_y.value}')
self.log_wfs.info(f'Window Size X: {self.window_size_x.value}')
self.log_wfs.info(f'Window Size Y: {self.window_size_y.value}')
self.log_wfs.debug('Window Start Position X:\n' +
' '.join([f'{item:12.8}' for item in self.window_start_position_x[:columns]]))
self.log_wfs.debug('Window Start Position Y:\n' +
' '.join([f'{item:12.8}' for item in self.window_start_position_y[:rows]]))
self._error_message(status)
return (status, self.window_count_x.value, self.window_count_y.value, self.window_size_x.value,
self.window_size_y.value, self.window_start_position_x, self.window_start_position_y)
def _check_highspeed_centroids(self):
"""Check if measured spots are in calculation in Highspeed Mode.
This function checks if the actual measured spot centroid
positions are within the calculation windows in Highspeed Mode.
Possible error: WFS_ERROR_HIGHSPEED_WINDOW_MISMATCH
If this error occurs, measured centroids are not reliable for
wavefront interrogation because the appropriated spots are
truncated.
Note: This function is only available when Highspeed Mode is
activated!
Returns:
status (Vi.status(int)): This value shows the status code
returned by the function call. For Status Codes see
function _error_message.
"""
status = self.lib.WFS_CheckHighspeedCentroids(self.instrument_handle)
self.log_wfs.debug(f'Check Highspeed Centroids: {self.instrument_handle.value}')
self._error_message(status)
return status
def _get_exposure_time_range(self):
"""Get the exposure time range in ms based on camera resolution.
This function returns the available exposure range of the WFS
camera in ms. The range may depend on the camera resolution
set by function _configure_cam.
Returns:
status (Vi.status(int)): This value shows the status code
returned by the function call. For Status Codes see
function _error_message.
exposure_time_min (Vi.real64(float)): This parameter
returns the minimal exposure time of the WFS camera in
ms.
exposure_time_min (Vi.real64(float)): This parameter
returns the maximal exposure time of the WFS camera in
ms.
exposure_time_min (Vi.real64(float)): This parameter
returns the smallest possible increment of the
exposure time in ms.
"""
status = self.lib.WFS_GetExposureTimeRange(self.instrument_handle,
ctypes.byref(self.exposure_time_min),
ctypes.byref(self.exposure_time_max),
ctypes.byref(self.exposure_time_increment))
self.log_wfs.debug(f'Get Exposure Time Range: {self.instrument_handle.value}')
self.log_wfs.info(f'Exposure Time Minimum (ms): {self.exposure_time_min.value}')
self.log_wfs.info(f'Exposure Time Maximum (ms): {self.exposure_time_max.value}')
self.log_wfs.info(f'Exposure Time Increment (ms): {self.exposure_time_increment.value}')
self._error_message(status)