forked from ufo-kit/uca-andor
-
Notifications
You must be signed in to change notification settings - Fork 1
/
uca-andor-camera.c
2456 lines (2091 loc) · 98.7 KB
/
uca-andor-camera.c
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
/*
* Copyright (C) 2014-2017 Karlsruhe Institute of Technology
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this library; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110, USA
*/
/** WARNING:
* This plugin has been tested under the following configuration:
* Ubuntu 16.04 LTS
* Kernel version = 4.4.0
* Camera 'Andor Neo' ; model 'DC-152Q-FOO-FI' ; Ser.No = SCC-1837
* Internal firmware version = V3
* There is no warranty that this plugin will be compatible under any other configuration, especially when using newer firmware version.
*/
#include <uca/uca-camera.h>
#include <gio/gio.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include "atcore.h"
#include "atutility.h"
#include "uca-andor-camera.h"
#include "uca-andor-enums.h"
/**
* atutility:
* Additional utilities functions provided by Andor.
* WARNING: Some functions in this header use C++ syntaxe: it is needed to comment them manually in order to the plugin to compile.s
*/
#define UCA_ANDOR_CAMERA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UCA_TYPE_ANDOR_CAMERA, UcaAndorCameraPrivate))
static void uca_andor_initable_iface_init(GInitableIface *iface);
G_DEFINE_TYPE_WITH_CODE (UcaAndorCamera, uca_andor_camera, UCA_TYPE_CAMERA,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, uca_andor_initable_iface_init))
#define WAIT_BUFFER_TIMEOUT 10000 /* Time allowed for the camera to return buffer before raising error; original = 10000 (10s) */
#define MARGIN 0.01 /* Framerate margin used when setting framerate at max transfer rate (frame_rate = max_interface_transfer_rate - MARGIN) */
#define INTERNAL_MEMORY 3981262199 /* Estimated memory according to experimental tests made on actual camera (should be 4GB) */
/* Available values for check_access function's parameters */
#define CHECK_ACCESS_WRITE 0
#define CHECK_ACCESS_READ 1
#define CHECK_ACCESS_WARN TRUE
#define CHECK_ACCESS_SILENT FALSE
/* METADATA memory length values */
#define METADATA_CID_SIZE 4 /* Length in Bytes of each 'CID' field in METADATA (CID = Chunk / Block Identifier) */
#define METADATA_LENGTH_SIZE 4 /* Length in Bytes of each 'length' field in METADATA (contain length of the block just above in memory)*/
#define METADATA_TIMESTAMP_SIZE 8 /* Length in Bytes of 'Timestamp' field in METADATA */
/* METADATA CID values (CID = Chunk / Block Identifier) */
#define METADATA_CID_FRAMEDATA 0 /* CID of block containing actual frame's raw data (+padding) */
#define METADATA_CID_TICKS 1 /* CID of block containing internal timestamp clock at exposure start */
#define METADATA_CID_FRAMEINFO 7 /* CID of block containing frame's pixel encoding + AOI stride + AOI height + AOI width */
/* Helper to compare wide char string equality */
#define WC_STR_EQUAL(wcs1, wcs2) (wcscmp(wcs1, wcs2) == 0)
/**
* UcaAndorCameraError
* @ANDOR_NOERROR:
* @UCA_ANDOR_CAMERA_ERROR_LIBANDOR_INIT:
* @UCA_ANDOR_CAMERA_ERROR_LIBANDOR_GENERAL:
* @UCA_ANDOR_CAMERA_ERROR_UNSUPPORTED:
* @UCA_ANDOR_CAMERA_ERROR_INVALID_MODE:
* @UCA_ANDOR_CAMERA_ERROR_MODE_NOT_AVAILABLE:
* @UCA_ANDOR_CAMERA_ERROR_ENUM_OUT_OF_RANGE:
*
* This enumerated is currently not used : when error detected and GError is available, just set by default LIBANDOR_GENERAL
*/
/**
* UcaAndorCameraSPAGC:
* @UCA_ANDOR_CAMERA_SPAGC_11BIT_HIGH_CAPACITY: set the feature to '11bit (high well capacity)'
* @UCA_ANDOR_CAMERA_SPAGC_11BIT_LOW_NOISE: set the feature to '11bit (low noise)'
* @UCA_ANDOR_CAMERA_SPAGC_11BIT_HIGH_CAPACITY: set the feature to '16bit (low noise & high well capacity)'
*/
/**
* UcaAndorCameraShutteringMode:
* @UCA_ANDOR_CAMERA_SHUTTERING_MODE_ROLLING: when reading out pixels, each row
* is read successively, 2 rows at a time, starting at the middle of ROI (one
* row going up while the other goes down) -> make acquisition faster with less
* noise but can result in image distortion
* @UCA_ANDOR_CAMERA_SHUTTERING_MODE_GLOBAL: when reading out pixels, every rows
* are read simultaneously
*/
/**
* UcaAndorCameraPixelReadoutRate:
* @UCA_ANDOR_CAMERA_PIXEL_READOUT_RATE_100MHZ: 100 MHz (x2 = 200 if Shutter mode is 'Rolling')
* @UCA_ANDOR_CAMERA_PIXEL_READOUT_RATE_200MHZ: 200 MHz (x2 = 400 if Shutter mode is 'Rolling')
* @UCA_ANDOR_CAMERA_PIXEL_READOUT_RATE_280MHZ: 280 MHz (x2 = 560 if Shutter mode is 'Rolling')
*/
/**
* UcaAndorCameraFanSpeed:
* @UCA_ANDOR_CAMERA_FAN_SPEED_OFF: fan off (internal heat sink warms up to 45
* degC, then fan is automatically set to ON to cool it down)
* @UCA_ANDOR_CAMERA_FAN_SPEED_LOW: low speed (low noise due to vibrations)
* @UCA_ANDOR_CAMERA_FAN_SPEED_ON: high speed (highest heat sink cooling efficienty)
*/
typedef enum {
CAMERATYPE_UNKNOWN,
CAMERATYPE_NEO_55_CL3,
CAMERATYPE_MARANA_4B_11,
CAMERATYPE_MARANA_4B_6,
N_CAMERATYPES,
} andor_camera_type;
static andor_camera_type
get_camera_type_from_string(const gchar *model)
{
if (g_strstr_len(model, -1, "NEO"))
return CAMERATYPE_NEO_55_CL3;
if (g_strstr_len(model, -1, "MARANA")) {
if (g_strstr_len(model, -1, "11"))
return CAMERATYPE_MARANA_4B_11;
return CAMERATYPE_MARANA_4B_6;
}
return CAMERATYPE_UNKNOWN;
}
typedef struct {
andor_camera_type type;
int trigger_mode_internal_index;
int trigger_mode_software_index;
int trigger_mode_external_index;
int pixel_rate_100mhz_index;
int pixel_rate_180mhz_index;
int pixel_rate_200mhz_index;
int pixel_rate_280mhz_index;
int pixel_rate_310mhz_index;
int pixel_rate_uca_default;
int spagc_11bit_hc_index;
int spagc_11bit_ln_index;
int spagc_12bit_index;
int spagc_16bit_index;
int spagc_uca_default;
gboolean has_internal_memory;
guint64 internal_memory_size;
gboolean has_global_shutter;
} andor_features_entry;
static andor_features_entry andor_features_map[] = {
/* 2017-09: Neo camera trigger enum definition differs from documentation */
{CAMERATYPE_NEO_55_CL3,
0, 4, 6,
1, -1, 2, 3, -1, UCA_ANDOR_CAMERA_PIXEL_READOUT_RATE_100MHZ,
0, 1, -1, 2, UCA_ANDOR_CAMERA_SPAGC_11BIT_LOW_NOISE,
TRUE, 3981262199, TRUE},
/* 2022-04: Marana camera seems to match documentation */
{CAMERATYPE_MARANA_4B_11,
0, 1, 2,
0, -1, 1, -1, -1, UCA_ANDOR_CAMERA_PIXEL_READOUT_RATE_100MHZ,
-1, -1, 0, 1, UCA_ANDOR_CAMERA_SPAGC_16BIT,
FALSE, 0, FALSE},
{CAMERATYPE_MARANA_4B_6,
0, 1, 2,
-1, -1, -1, 0, 1, UCA_ANDOR_CAMERA_PIXEL_READOUT_RATE_310MHZ,
-1, -1, 0, 1, UCA_ANDOR_CAMERA_SPAGC_16BIT,
FALSE, 0, FALSE},
{CAMERATYPE_UNKNOWN,
-1, -1, -1,
-1, -1, -1, -1, -1, UCA_ANDOR_CAMERA_PIXEL_READOUT_RATE_100MHZ,
-1, -1, -1, -1, UCA_ANDOR_CAMERA_SPAGC_16BIT,
FALSE, 0, FALSE},
};
static andor_features_entry
get_andor_features (andor_camera_type type) {
int i;
for (i = 0; i < N_CAMERATYPES; i++) {
andor_features_entry entry = andor_features_map[i];
if (entry.type == type)
return entry;
}
return get_andor_features(CAMERATYPE_UNKNOWN);
}
struct _UcaAndorCameraPrivate {
andor_features_entry features;
guint camera_number;
AT_H handle;
GError *construct_error;
gchar* name;
gchar* model;
gchar* bitdepth;
gchar* pixel_encoding;
gchar* temperature_status;
guint64 aoi_left;
guint64 aoi_top;
guint64 aoi_width;
guint64 aoi_height;
guint64 aoi_stride;
guint64 sensor_width;
guint64 sensor_height;
guint64 num_buffers;
guint64 frame_count;
guint64 accumulate_count;
guint64 timestamp_clock;
guint64 timestamp_clock_frequency;
gdouble pixel_width;
gdouble pixel_height;
gdouble frame_rate;
gdouble exp_time;
gdouble sensor_temperature;
gdouble target_sensor_temperature;
gdouble calculated_bytes_per_pixel;
gdouble frame_rate_max;
gdouble frame_rate_min;
gdouble max_interface_transfer_rate;
gint max_frame_capacity;
gboolean is_sim_cam;
gboolean is_cam_acquiring;
gboolean full_aoi_control;
gboolean vertically_centre_aoi;
gboolean fast_aoi_frame_rate_enable;
gboolean sensor_cooling;
gboolean spurious_noise_filter;
gboolean static_blemish_correction;
gboolean overlap;
gboolean metadata;
UcaCameraTriggerSource trigger_mode;
UcaAndorCameraSPAGC simple_pre_amp_gain_control;
UcaAndorCameraShutteringMode shuttering_mode;
UcaAndorCameraPixelReadoutRate pixel_readout_rate;
UcaAndorCameraFanSpeed fan_speed;
UcaAndorCameraAOIBinning aoi_binning;
UcaAndorCameraCycleMode cycle_mode;
AT_WC* pixel_encoding_wchar; /* pixel encoding under AT_WC* format needed to pass it into AT_ConvertBuffer */
AT_U8* image_buffer;
AT_U8* aligned_buffer;
AT_64 image_size; /* full image (frame + padding + metadata if enabled) memory size in bytes */
guint frame_timeout;
/* Variables used to handle calculation of frame number */
gint last_frame_number;
gint last_frame_clock;
gint frame_number;
};
static gint andor_overrideables [] = {
PROP_NAME,
PROP_EXPOSURE_TIME,
PROP_ROI_X,
PROP_ROI_Y,
PROP_ROI_WIDTH,
PROP_ROI_HEIGHT,
PROP_SENSOR_WIDTH,
PROP_SENSOR_HEIGHT,
PROP_SENSOR_PIXEL_WIDTH,
PROP_SENSOR_PIXEL_HEIGHT,
PROP_IS_RECORDING,
PROP_SENSOR_BITDEPTH,
PROP_HAS_CAMRAM_RECORDING,
PROP_HAS_STREAMING,
PROP_TRIGGER_SOURCE,
PROP_NUM_BUFFERS,
0,
};
enum {
PROP_ROI_STRIDE = N_BASE_PROPERTIES,
PROP_SENSOR_TEMPERATURE,
PROP_TARGET_SENSOR_TEMPERATURE,
PROP_FAN_SPEED,
PROP_CYCLE_MODE,
PROP_FRAMERATE,
PROP_PIXEL_ENCODING,
PROP_SIMPLE_PRE_AMP_GAIN_CONTROL,
PROP_SHUTTERING_MODE,
PROP_FRAMERATE_MAX,
PROP_FRAMERATE_MIN,
PROP_MAX_INTERFACE_TRANSFER_RATE,
PROP_IMAGE_SIZE,
PROP_MAX_FRAME_CAPACITY,
PROP_FAST_AOI_FRAMERATE_ENABLE,
PROP_PIXEL_READOUT_RATE,
PROP_VERTICALLY_CENTRE_AOI,
PROP_SENSOR_COOLING,
PROP_TEMPERATURE_STATUS,
PROP_SPURIOUS_NOISE_FILTER,
PROP_STATIC_BLEMISH_CORRECTION,
PROP_OVERLAP,
PROP_FRAME_COUNT,
PROP_ACCUMULATE_COUNT,
PROP_AOI_BINNING,
PROP_TIMESTAMP_CLOCK,
PROP_TIMESTAMP_CLOCK_FREQUENCY,
PROP_METADATA,
PROP_FRAME_GRABBER_TIMEOUT,
N_PROPERTIES
};
static GParamSpec *andor_properties [N_PROPERTIES] = { NULL, };
/**
* Return a string containing the error corresponding to the error number
* returned from andor camera. The array matches the AT errors defined in the
* SDK's file 'atcore.h' and in the documentation.
*/
static const gchar *
identify_andor_error (int error)
{
static const gchar *errors[] = {
/* atcore.h errors */
"No error ... (identify_andor_error function has been called for a bad reason, please fix)",
"Camera Handle uninitialized",
"Feature is not implemented for this camera",
"Feature is read only",
"Feature is currently not readable",
"Feature is currently not writable / Command is not currently executable",
"Value is either out of range or unavailable",
"Index is currently not available",
"Index is not implemented on this camera",
"String value exceed maximum allowed length",
"Connection or Disconnection error",
"No Internal Event or Internal Error",
"Invalid handle",
"Waiting for buffer timed out",
"Input buffer queue reached maximum capacity",
"Queued buffer / returned frame size conflict",
"A queued buffer was not aligned on an 8-byte boundary",
"An error has occurred while communicating with hardware",
"Index / String is not currently available",
"Index / String is not implemented on this camera",
"Passed feature = NULL",
"Passed handle = NULL",
"Feature not implemented",
"Readable not set",
"Readonly not set",
"Writable not set",
"Min value = NULL",
"Max value = NULL",
"Function returned NULL value",
"Function returned NULL string",
"Feature index count = NULL",
"Available not set",
"Passed string lenght = NULL",
"EvCallBack parameter = NULL",
"Pointer to queue = NULL",
"Wait pointer = NULL",
"Pointer size = NULL",
"No memory allocated for current action",
"Unable to connect, device already in use",
"Device not found",
/* this is error number 40 but we pretend it's 100, so substract 60 */
"The software was not able to retrieve data from the card or camera fast enough to avoid the internal hardware buffer bursting",
/* atutility.h errors */
/* this is error number 41 but we pretend it's 1002, so substract 961 */
"Invalid output pixel encoding",
"Invalid input pixel encoding",
"Input buffer does not include metadata",
"Corrupted metadata",
"Metadata not found",
};
if (error <= 39)
return errors[error];
if (error == 100)
return errors[error - 60];
if (error >= 1002 && error <= 1006)
return errors[error - 961];
return "Unknown error...";
}
static gboolean
check_access (UcaAndorCameraPrivate *priv, const AT_WC* feature, int access, gboolean warn)
/**
* Check access of the feature passed in parameters :
* - check if implemented
* - if access = CHECK_ACCESS_READ check if readable
* - if access = CHECK_ACCESS_WRITE check if read_only then if writable
* Return TRUE if access is OK, return FALSE if not
* - if warn = CHECK_ACCESS_WARN will display warning if access is not allowed/available or if an error occur
* - if warn = CHECK_ACCESS_SILENT will display warning only if an error occur
* Does not display error when read access fail if camera = SIMCAM (to not overload output... but errors are still displayed if its for writting)
*/
{
int errnum;
AT_BOOL testbool;
errnum = AT_IsImplemented (priv->handle, feature, &testbool);
if (!errnum && testbool){
if (access == CHECK_ACCESS_READ) {
errnum = AT_IsReadable (priv->handle, feature, &testbool);
if (!errnum && !testbool) {
if (warn) g_warning ("READ ACCESS ERROR: feature '%S' is currently not readable", feature);
return FALSE;
}
else if (errnum) {
g_warning ("Check access failed for '%S': AT_IsReadable returned error: %s (%d)",feature, identify_andor_error(errnum), errnum);
return FALSE;
}
}
else if (access == CHECK_ACCESS_WRITE) {
errnum = AT_IsReadOnly (priv->handle, feature, &testbool);
if (!errnum && testbool) {
if (warn) g_warning ("WRITE ACCESS ERROR: feature '%S' is read only", feature);
return FALSE;
}
else if (errnum) {
g_warning ("Check access failed for '%S': AT_IsReadOnly returned error: %s (%d)",feature,identify_andor_error(errnum), errnum);
return FALSE;
}
errnum = AT_IsWritable (priv->handle, feature, &testbool);
if (!errnum && !testbool) {
if (warn) g_warning ("WRITE ACCESS ERROR: feature '%S' is currently not writable", feature);
return FALSE;
}
else if (errnum) {
g_warning ("Check access failed for '%S': AT_IsWritable returned error: %s (%d)",feature,identify_andor_error(errnum), errnum);
return FALSE;
}
}
else {
g_warning("Could not check access '%d' for feature '%S', access not recognised", access, feature);
}
return TRUE;
}
else if (!errnum && !testbool){
if (warn && !(priv->is_sim_cam && access==CHECK_ACCESS_READ) ) /* Disable 'not implemented' display if camera is SIMCAM and checking READ access */
g_warning ("ACCESS ERROR: feature '%S' is not implemeted on camera '%s'", feature, priv->name);
return FALSE;
}
else {
g_warning ("Check access failed for '%S': AT_IsImplemented returned error: %s (%d)",feature,identify_andor_error(errnum), errnum);
return FALSE;
}
return FALSE; /* by default .. should not be encountered */
}
static gboolean
is_marana (UcaAndorCameraPrivate *priv)
{
return priv->features.type == CAMERATYPE_MARANA_4B_11 || priv->features.type == CAMERATYPE_MARANA_4B_6;
}
static void
estimate_max_frame_capacity (UcaAndorCameraPrivate *priv)
/**
* Calculate the estimated max number of frames that the camera's internal memory can store with current parameters.
* NOTE : this is an estimation! (Estimating in 11-bit is quite accurate but in 16-bit it is very pessimistic
*/
{
if (!priv->features.has_internal_memory)
{
priv->max_frame_capacity = 0.0;
return;
}
AT_64 max_width = 0, max_height = 0;
AT_GetIntMax(priv->handle, L"AOIWidth", &max_width);
AT_GetIntMax(priv->handle, L"AOIHeight", &max_height);
gfloat memory = priv->features.internal_memory_size; /* estimated experimentaly on camera (4GB) */
gfloat fullAOISize = priv->calculated_bytes_per_pixel * max_width * max_height; /* maximum size of frames when using max ROI size */
gfloat temp = priv->frame_rate_max * memory / (fullAOISize * (priv->frame_rate_max - priv->max_interface_transfer_rate));
priv->max_frame_capacity = (gint) temp;
/* If result < 0, it means that no frames are stored in the memory even at
* max framerate in this case, max_frame_capacity is set to +inf (in fact:
* int-type's maximum) */
if (temp < 0)
priv->max_frame_capacity = G_MAXINT;
}
static gboolean
write_integer (UcaAndorCameraPrivate *priv, const AT_WC* property, guint64 value)
{
int error;
AT_64 max=0, min=0;
if (!check_access (priv, property, CHECK_ACCESS_WRITE, CHECK_ACCESS_WARN))
return FALSE;
error = AT_GetIntMax (priv->handle, property, &max);
if (error) {
g_warning ("Could not read maximum allowable '%S'value: %s (%d)", property, identify_andor_error (error), error);
return FALSE;
}
error = AT_GetIntMin (priv->handle, property, &min);
if (error) {
g_warning ("Could not read minimum allowable '%S' value: %s (%d)", property, identify_andor_error (error), error);
return FALSE;
}
#ifdef ZERO_BASED_ROI
if (WC_STR_EQUAL(property, L"AOILeft") || WC_STR_EQUAL(property, L"AOITop")) {
min--;
max--;
}
#endif
if ((value < min) || (value > max)) {
g_warning ("Value %d is out of range for feature %S: current range is [%d ; %d]", (int) value, property, (int) min, (int) max);
return FALSE;
}
#ifdef ZERO_BASED_ROI
if (WC_STR_EQUAL(property, L"AOILeft") || WC_STR_EQUAL(property, L"AOITop")) {
value++;
}
#endif
error = AT_SetInt (priv->handle, property, value);
if (error != AT_SUCCESS) {
g_warning ("Could not write integer '%S': %s (%d)", property, identify_andor_error (error), error);
return FALSE;
}
return TRUE;
}
static gboolean
read_integer (UcaAndorCameraPrivate *priv, const AT_WC* property, guint64 *value)
{
guint64 temp;
if (!check_access (priv, property, CHECK_ACCESS_READ, CHECK_ACCESS_WARN))
return FALSE;
int error = AT_GetInt (priv->handle, property, (AT_64 *) &temp);
if (error != AT_SUCCESS) {
g_warning ("Could not read integer '%S': %s (%d)", property, identify_andor_error (error), error);
return FALSE;
}
#ifdef ZERO_BASED_ROI
if (WC_STR_EQUAL(property, L"AOILeft") || WC_STR_EQUAL(property, L"AOITop")) {
temp--;
}
#endif
*value = temp;
return TRUE;
}
static gboolean
write_double (UcaAndorCameraPrivate *priv, const AT_WC* property, double value)
{
int error;
double max=0, min=0;
if (!check_access (priv, property, CHECK_ACCESS_WRITE, CHECK_ACCESS_WARN))
return FALSE;
error = AT_GetFloatMax (priv->handle, property, &max);
if (error) {
g_warning ("Could not read maximum allowable '%S'value: %s (%d)", property, identify_andor_error (error), error);
return FALSE;
}
error = AT_GetFloatMin (priv->handle, property, &min);
if (error) {
g_warning ("Could not read minimum allowable '%S' value: %s (%d)", property, identify_andor_error (error), error);
return FALSE;
}
if ((value < min) || (value > max)) {
g_warning ("Value %f is out of range for feature %S: current range is [%f ; %f]", (float) value, property, (float) min, (float) max);
return FALSE;
}
error = AT_SetFloat (priv->handle, property, value);
if (error != AT_SUCCESS) {
g_warning ("Could not write double '%S': %s (%d)", property, identify_andor_error (error), error);
return FALSE;
}
return TRUE;
}
static gboolean
read_double (UcaAndorCameraPrivate *priv, const AT_WC* property, double *value)
{
double temp;
if (!check_access (priv, property, CHECK_ACCESS_READ, CHECK_ACCESS_WARN))
return FALSE;
int error = AT_GetFloat (priv->handle, property, &temp);
if (error != AT_SUCCESS) {
g_warning ("Could not read double '%S': %s (%d)", property, identify_andor_error (error), error);
return FALSE;
}
*value = temp;
return TRUE;
}
static gboolean
read_double_max (UcaAndorCameraPrivate *priv, const AT_WC* property, double *value)
{
double temp;
if (!check_access (priv, property, CHECK_ACCESS_READ, CHECK_ACCESS_WARN))
return FALSE;
int error = AT_GetFloatMax (priv->handle, property, &temp);
if (error != AT_SUCCESS) {
g_warning ("Could not read double max of '%S': %s (%d)", property, identify_andor_error (error), error);
return FALSE;
}
*value = temp;
return TRUE;
}
static gboolean
read_double_min (UcaAndorCameraPrivate *priv, const AT_WC* property, double *value)
{
double temp;
if (!check_access (priv, property, CHECK_ACCESS_READ, CHECK_ACCESS_WARN))
return FALSE;
int error = AT_GetFloatMin (priv->handle, property, &temp);
if (error != AT_SUCCESS) {
g_warning ("Could not read double min of '%S': %s (%d)", property, identify_andor_error (error), error);
return FALSE;
}
*value = temp;
return TRUE;
}
static gboolean
write_enum_index (UcaAndorCameraPrivate *priv, const AT_WC* property, int value)
{
int count;
if (!check_access (priv, property, CHECK_ACCESS_WRITE, CHECK_ACCESS_WARN))
return FALSE;
int error = AT_GetEnumCount (priv->handle, property, &count);
if (error != AT_SUCCESS) {
g_warning ("Cannot read enum count '%S': %s (%d)", property, identify_andor_error (error), error);
return FALSE;
}
if (value >= count || value < 0) {
g_warning ("Enumeration value (%d) out of range [0, %i] for feature '%S'", value, count - 1, property);
return FALSE;
}
error = AT_SetEnumIndex (priv->handle, property, value);
if (error != AT_SUCCESS) {
g_warning ("Could not set enum '%S': %s (%d)", property, identify_andor_error (error), error);
return FALSE;
}
return TRUE;
}
static gboolean
read_enum_index (UcaAndorCameraPrivate *priv, const AT_WC* property, int *value)
{
int temp;
if (!check_access (priv, property, CHECK_ACCESS_READ, CHECK_ACCESS_WARN))
return FALSE;
int error = AT_GetEnumIndex (priv->handle, property, &temp);
if (error != AT_SUCCESS) {
g_warning ("Could not read index '%S': %s (%d)", property, identify_andor_error (error), error);
return FALSE;
}
*value = temp;
return TRUE;
}
static gboolean
write_string (UcaAndorCameraPrivate *priv, const AT_WC *property, const gchar *value)
{
AT_WC* wide_value;
size_t len;
gboolean result;
if (!check_access (priv, property, CHECK_ACCESS_WRITE, CHECK_ACCESS_WARN))
return FALSE;
result = TRUE;
len = strlen (value);
wide_value = (AT_WC *)g_malloc0 ((len + 1) * sizeof (AT_WC));
mbstowcs (wide_value, value, len);
int error = AT_SetEnumString (priv->handle, property, wide_value);
if (error != AT_SUCCESS) {
g_warning ("Could not write string '%s' to '%S': %s (%d)", value, property, identify_andor_error (error), error);
result = FALSE;
}
g_free (wide_value);
return result;
}
static gboolean
read_string (UcaAndorCameraPrivate *priv, const AT_WC *property, gchar **value)
{
AT_WC* wide_value;
int index, error;
gboolean result = TRUE;
if (!check_access (priv, property, CHECK_ACCESS_READ, CHECK_ACCESS_WARN))
return FALSE;
error = AT_GetEnumIndex (priv->handle, property, &index);
if (error != AT_SUCCESS) {
g_warning ("Could not read index for '%S': %s (%d)", property, identify_andor_error (error), error);
return FALSE;
}
wide_value = (AT_WC *)g_malloc0 (1023 * sizeof (AT_WC));
error = AT_GetEnumStringByIndex (priv->handle, property, index, wide_value, 1023);
if (error != AT_SUCCESS) {
g_warning ("Could not read string '%S': %s (%d)", property, identify_andor_error (error), error);
g_free (wide_value);
result = FALSE;
}
*value = (gchar *)g_malloc0 ((wcslen (wide_value) + 1) * sizeof (gchar));
wcstombs (*value, wide_value, wcslen (wide_value));
g_free (wide_value);
return result;
}
static gboolean
read_boolean (UcaAndorCameraPrivate *priv, const AT_WC *property, gboolean *value)
{
gboolean temp;
if (!check_access (priv, property, CHECK_ACCESS_READ, CHECK_ACCESS_WARN))
return FALSE;
int error = AT_GetBool (priv->handle, property, (AT_BOOL *) &temp);
if (error != AT_SUCCESS) {
g_warning ("Could not read boolean '%S': %s (%d)", property, identify_andor_error (error), error);
return FALSE;
}
*value = temp;
return TRUE;
}
static gboolean
write_boolean (UcaAndorCameraPrivate *priv, const AT_WC* property, gboolean value)
{
if (!check_access (priv, property, CHECK_ACCESS_WRITE, CHECK_ACCESS_WARN))
return FALSE;
int error = AT_SetBool (priv->handle, property, value);
if (error != AT_SUCCESS) {
g_warning ("Could not write boolean '%S': %s (%d)", property, identify_andor_error (error), error);
return FALSE;
}
return TRUE;
}
/**
* Function used for extracting bitdeph value (uint) from andor's returned
* string assume that the string contain an unique number of 1 or 2 numeral(s)
* (nothing else!)
*/
static gint64
extract_uint_from_string (const gchar* string)
{
GRegex *regex;
GMatchInfo *info;
gint64 result = 0;
regex = g_regex_new ("(\\d{1,2})", (GRegexCompileFlags)0, (GRegexMatchFlags)0, NULL);
if (g_regex_match (regex, string, (GRegexMatchFlags)0, &info)) {
gchar *word = g_match_info_fetch (info, 0);
result = atoi (word);
g_free (word);
g_match_info_free (info);
goto extract_uint_from_string_cleanup;
}
g_warning ("Could not extract BitDepth uint from returned string '%s', returned value: 0 by default", string);
extract_uint_from_string_cleanup:
g_regex_unref (regex);
return result;
}
static void
calculate_frame_number (UcaAndorCameraPrivate *priv, AT_64 timestamp)
/**
* Calculate and return the frame number since beginning of acquisition according to user's parameters :
* - if trigger source = AUTO: measure delta time between each frame and according to framerate, calculate the new frame number
* can be used to ensure that no frame has been missed during recording
* WARNING: this is an approximation, if delta time does not perfectly match the framerate, the number set is a truncation of what has been calculated
*
* - if trigger source = SOFTWARE or EXTERNAL: frame_number is incremented each time grab function is used... but there is no warranty that no frame has been missed
* because the plugin does not have access to the framerate used for the experiment
* NOTE: this function does not check if metadata is enabled, it should not be called if this is not the case
*/
{
switch (priv->trigger_mode) {
case UCA_CAMERA_TRIGGER_SOURCE_AUTO:
if (priv->last_frame_number == 0) {
priv->last_frame_number = 1;
priv->last_frame_clock = timestamp;
priv->frame_number = 1;
}
else {
double temp_float = ((double)(timestamp - priv->last_frame_clock)/(double)priv->timestamp_clock_frequency)
* priv->frame_rate / priv->accumulate_count;
priv->frame_number = priv->last_frame_number + (gint) temp_float;
priv->last_frame_number = priv->frame_number;
priv->last_frame_clock = timestamp;
}
return;
default:
priv->frame_number++;
return;
}
}
static void
add_time_to_frame (AT_64 timestamp, AT_U8 *data, int frame_number)
/**
* Overwrite the first 28 Bytes of the picture (14 pixels at 2 Bytes/pixel) with frame number and timestamp raw value :
* - pixels 0 to 3 (4 pixels): frame number coded in BCD-packed: each pixel contains 2 digit (going from highest power to lowest) on the last 8 bits eg :
* _______________ _______________
* | (0) | (0) | | digi0 | digi1 |
* 1 Byte 1 Byte [...] (if number = 1042, digi[] = [1,0,4,2])
* |----------------1 pixel----------------|
*
* - pixels 4 to 13 (10 pixels): timestamp in binary (64 bits) converted into BCD-packed (20 digits) following the same process.
* - WARNING: this function assumes that the frame_number has maximum 8 digits
* NOTE: this function does not check if metadata is enabled, it should not be called if this is not the case
*/
{
unsigned short temp1, temp2;
AT_64 pow=1e7, offset=0;
/* Write frame_number on pixels no:0-3 (bytes no:0-7) */
for (int i=0; i<4; i++) { /* Naviguate through 4 first pixels */
temp1 = ((frame_number-offset) - ((frame_number-offset) % pow)) / pow; /* retrieve 1st digit */
pow /= 10;
temp2 = ((frame_number-offset) - ((frame_number-offset) % pow)) / pow - (temp1*10); /* retrieve 2nd digit */
offset += ((temp1*10) + temp2) * pow; /* suppress digits that have been grabbed from number */
pow /= 10;
*data = (unsigned short) (temp1*16 + temp2); /* write digits into pixel (pixel size = unsigned short size = 2 bytes) */
data += 2; /* Move through the memory */
}
/* Write timestamp on pixels no:4-13 (bytes no:8-27) */
pow = 1e17;
offset = 0;
/* We "cheat" for the first iteration (first pixel) because we cannot set pow = 2e19 (overflow of AT_64 format (unsigned long)) (FIXME: can we?)*/
temp1 = ((timestamp-offset) - ((timestamp-offset) % (int)1e19)) / 1e19;
temp2 = ((timestamp-offset) - ((timestamp-offset) % (int)1e18)) / 1e18 - (temp1*10);
offset += ((temp1*10) + temp2) * pow;
*data = (unsigned short) (temp1*16 + temp2);
data += 2;
for (int i=0; i<9; i++) {
temp1 = ((timestamp-offset) - ((timestamp-offset) % pow)) / pow;
pow /= 10;
temp2 = ((timestamp-offset) - ((timestamp-offset) % pow)) / pow - (temp1*10);
offset += ((temp1*10) + temp2) * pow;
pow /= 10;
*data = (unsigned short) (temp1*16 + temp2);
data += 2;
}
}
static int
convert_and_concatenate_buffer (UcaAndorCameraPrivate *priv, AT_U8 *input_buffer, gpointer data)
/**
* In the specific case where METADATA is used, convert buffer into correct pixel encoding + remove padding + remove METADATA from data + overwrite 4 first pixels with
* timestamp clock value retrieved from metadata.
* TODO: for now, it assumes that METADATA and Timestamp are enabled while FrameInfo is disabled, we should check that in the future to be more
* reliable and if we want to improve (currently not possible because features described in documentation are not implemented on actual camera)
* NOTE: this function does not check if metadata is enabled, it should not be called if this is not the case
*/
{
int error_number, ticks_offset, ticks_cid, framedata_size, framedata_cid, framedata_offset;
AT_U8 *end_metadata, *temp_buffer;
AT_64 timestamp;
/* Extracting timestamp from metadata */
end_metadata = input_buffer + priv->image_size; /* Metadata has to be read starting from end of data memory space */
ticks_cid = *(int *)(end_metadata - METADATA_LENGTH_SIZE - METADATA_CID_SIZE); /* Get first block's CID (which should be Ticks) */
if (ticks_cid != METADATA_CID_TICKS) { /* We are not in Tick block -> unsupported */
g_warning ("Metadata format error: espected reading 'Tick' block (of CID = 1) but got CID = %d instead", ticks_cid);
return 1005;
}
ticks_offset = METADATA_LENGTH_SIZE + METADATA_CID_SIZE + METADATA_TIMESTAMP_SIZE; /* offset from end of metadata to begin of 'Ticks' block's memory space */
timestamp = *(AT_64 *)(end_metadata - ticks_offset); /* Get the value of timestamp */
/* Converting buffer to frame stripped from padding and metadata */
framedata_size = *(int *)(end_metadata - ticks_offset - METADATA_LENGTH_SIZE); /* Get the second block's size in bytes (which should be FrameData) */
framedata_cid = *(int *)(end_metadata - ticks_offset - METADATA_LENGTH_SIZE - METADATA_CID_SIZE); /* Get second block's CID (which should be 'Frame Data') */
if (framedata_cid != METADATA_CID_FRAMEDATA) {
g_warning ("Metadata format error: espected reading 'FrameData' block (of CID = 0) but got CID = %d instead", framedata_cid);
return 1005;
}
/* Remember NOT to count CID_SIZE because it is already included in framedata_size (the length block contain data length + cid length) */
framedata_offset = ticks_offset + METADATA_LENGTH_SIZE + framedata_size;
temp_buffer = (AT_U8 *)(end_metadata - framedata_offset);
error_number = AT_ConvertBuffer (temp_buffer, (AT_U8 *) data, priv->aoi_width, priv->aoi_height, priv->aoi_stride, priv->pixel_encoding_wchar, L"Mono16");
if (error_number)
return error_number;
/* Concatenating timestamp onto frame */
calculate_frame_number(priv, timestamp);
add_time_to_frame(timestamp, (AT_U8 *) data, priv->frame_number);
return 0;
}
GQuark
uca_andor_camera_error_quark (void)
{
return g_quark_from_static_string ("uca-andor-camera-error-quark");
}
gboolean
check_error (int error_number, const char* message, GError **error)
{
if (error_number != AT_SUCCESS) {
g_set_error (error, UCA_ANDOR_CAMERA_ERROR, UCA_ANDOR_CAMERA_ERROR_LIBANDOR_GENERAL,
"Andor error '%s': %s (%i)", message, identify_andor_error (error_number), error_number);
return FALSE;
}
return TRUE;
}