File Coverage

blib/lib/Lab/Moose/Instrument/ZI_MFLI.pm
Criterion Covered Total %
statement 45 141 31.9
branch 1 4 25.0
condition n/a
subroutine 14 56 25.0
pod 27 32 84.3
total 87 233 37.3


line stmt bran cond sub pod time code
1             package Lab::Moose::Instrument::ZI_MFLI;
2             $Lab::Moose::Instrument::ZI_MFLI::VERSION = '3.900';
3             #ABSTRACT: Zurich Instruments MFLI Lock-in Amplifier
4              
5 3     3   1949 use v5.20;
  3         10  
6              
7 3     3   18 use Moose;
  3         39  
  3         24  
8 3     3   20483 use MooseX::Params::Validate;
  3         8  
  3         22  
9 3     3   1925 use Lab::Moose::Instrument::Cache;
  3         8  
  3         18  
10 3     3   1776 use Carp;
  3         8  
  3         190  
11 3     3   30 use namespace::autoclean;
  3         8  
  3         17  
12 3     3   220 use Moose::Util::TypeConstraints 'enum';
  3         8  
  3         28  
13 3     3   1429 use Lab::Moose::Instrument qw/validated_setter validated_getter/;
  3         8  
  3         180  
14 3     3   21 use Lab::Moose::Instrument::Cache;
  3         6  
  3         25  
15             use constant {
16 3         9744 ZI_LIST_NODES_RECURSIVE => 1,
17             ZI_LIST_NODES_ABSOLUTE => 2,
18 3     3   1894 };
  3         7  
19              
20             extends 'Lab::Moose::Instrument::Zhinst';
21              
22              
23             # FIXME: warn/croak on AUTO freq, bw, ...
24              
25             has num_demods => (
26             is => 'ro',
27             isa => 'Int',
28             builder => '_get_num_demods',
29             lazy => 1,
30             init_arg => undef,
31             );
32              
33             my %oscillator_arg
34             = ( oscillator => { isa => 'Lab::Moose::PosInt', optional => 1 } );
35              
36             my %sigin_arg = ( sigin => { isa => 'Lab::Moose::PosInt' } );
37              
38             has oscillator => (
39             is => 'ro',
40             isa => 'Lab::Moose::PosInt',
41             default => 0
42             );
43              
44             sub _get_oscillator {
45 4     4   10 my $self = shift;
46 4         10 my %args = @_;
47 4         9 my $osc = delete $args{oscillator};
48 4 50       16 if ( not defined $osc ) {
49 4         175 $osc = $self->oscillator();
50             }
51 4         11 $osc;
52             }
53              
54             sub _get_num_demods {
55 0     0   0 my $self = shift;
56 0         0 my $nodes = $self->list_nodes(
57             path => '/',
58             mask => ZI_LIST_NODES_ABSOLUTE | ZI_LIST_NODES_RECURSIVE
59             );
60              
61 0         0 my @demods = $nodes =~ m{^/dev\w+/demods/[0-9]+/}gmi;
62             @demods = map {
63 0         0 my $s = $_;
  0         0  
64 0         0 $s =~ m{/([0-9]+)/$};
65 0         0 $1;
66             } @demods;
67 0         0 my %hash = map { $_ => 1 } @demods;
  0         0  
68 0         0 @demods = keys %hash;
69 0 0       0 if ( @demods == 0 ) {
70 0         0 croak "did not find any demods";
71             }
72 0         0 return ( @demods + 0 );
73             }
74              
75              
76             cache frequency => ( getter => 'get_frequency' );
77              
78             sub get_frequency {
79 2     2 1 27 my ( $self, %args ) = validated_hash(
80             \@_,
81             %oscillator_arg,
82             );
83              
84 2         580 my $osc = $self->_get_oscillator(%args);
85              
86 2         72 return $self->cached_frequency(
87             $self->get_value(
88             path => $self->device() . "/oscs/$osc/freq",
89             type => 'D'
90             )
91             );
92              
93             }
94              
95             sub get_frq {
96 0     0 1 0 my $self = shift;
97 0         0 return $self->get_frequency(@_);
98             }
99              
100              
101              
102             sub set_frequency {
103 2     2 1 1410 my ( $self, $value, %args ) = validated_setter(
104             \@_,
105             %oscillator_arg,
106             value => { isa => 'Num' },
107             );
108 2         14 my $osc = $self->_get_oscillator(%args);
109 2         72 return $self->cached_frequency(
110             $self->sync_set_value(
111             path => $self->device() . "/oscs/$osc/freq", type => 'D',
112             value => $value
113             )
114             );
115              
116             }
117              
118              
119             sub set_frq {
120 0     0 1 0 my $self = shift;
121 0         0 return $self->set_frequency(@_);
122             }
123              
124              
125             cache voltage_sens => ( getter => 'voltage_sens' );
126              
127             sub get_voltage_sens {
128 0     0 1 0 my $self = shift;
129 0         0 my ($sigin) = validated_list(
130             \@_,
131             %sigin_arg,
132             );
133              
134 0         0 return $self->cached_voltage_sens(
135             $self->get_value(
136             path => $self->device() . "/sigins/$sigin/range",
137             type => 'D'
138             )
139             );
140             }
141              
142              
143             sub set_voltage_sens {
144 0     0 1 0 my ( $self, $value, %args ) = validated_setter(
145             \@_,
146             value => { isa => 'Num' },
147             %sigin_arg,
148             );
149 0         0 my $sigin = delete $args{sigin};
150 0         0 return $self->cached_voltage_sens(
151             $self->sync_set_value(
152             path => $self->device() . "/sigins/$sigin/range",
153             type => 'D',
154             value => $value
155             )
156             );
157             }
158              
159              
160             # add methods for various "ON/OFF" properties of the inputs
161             for my $sigin_arg (qw/diff ac imp50 float autorange on/) {
162             my $meta = __PACKAGE__->meta();
163             my $set_function = "set_sigin_$sigin_arg";
164             my $get_function = "get_sigin_$sigin_arg";
165              
166             # create setter function
167             $meta->add_method(
168             $set_function => sub {
169 0     0   0 my ( $self, $value, %args ) = validated_setter(
        0      
        0      
        0      
        0      
        0      
170             \@_,
171             value => { isa => enum( [ 0, 1 ] ) },
172             %sigin_arg,
173             );
174 0         0 my $sigin = delete $args{sigin};
175 0         0 return $self->sync_set_value(
176             path => $self->device() . "/sigins/$sigin/$sigin_arg",
177             type => 'I',
178             value => $value
179             );
180             }
181             );
182              
183             # create getter function
184             $meta->add_method(
185             $get_function => sub {
186 0     0   0 my $self = shift;
        0      
        0      
        0      
        0      
        0      
187 0         0 my ($sigin) = validated_list(
188             \@_,
189             %sigin_arg,
190             );
191              
192 0         0 return $self->get_value(
193             path => $self->device() . "/sigins/$sigin/$sigin_arg",
194             type => 'I',
195             );
196             }
197             );
198             }
199              
200             sub set__sens {
201 0     0 0 0 my ( $self, $value, %args ) = validated_setter(
202             \@_,
203             value => { isa => 'Num' },
204             %sigin_arg,
205             );
206 0         0 my $sigin = delete $args{sigin};
207 0         0 return $self->cached_voltage_sens(
208             $self->sync_set_value(
209             path => $self->device() . "/sigins/$sigin/range",
210             type => 'D',
211             value => $value
212             )
213             );
214             }
215              
216              
217             cache current_sens => ( getter => 'get_current_sens' );
218              
219             sub get_current_sens {
220 0     0 1 0 my $self = shift;
221 0         0 return $self->cached_current_sens(
222             $self->get_value(
223             path => $self->device() . "/currins/0/range",
224             type => 'D'
225             )
226             );
227             }
228              
229              
230             sub set_current_sens {
231 0     0 1 0 my ( $self, $value, %args ) = validated_setter(
232             \@_,
233             value => { isa => 'Num' },
234             );
235              
236 0         0 return $self->cached_current_sens(
237             $self->sync_set_value(
238             path => $self->device() . "/currins/0/range",
239             type => 'D',
240             value => $value
241             )
242             );
243             }
244              
245             sub set_sigin_diff {
246             my ( $self, $value, %args ) = validated_setter(
247             \@_,
248             value => { isa => enum( [ 0, 1 ] ) },
249             %sigin_arg,
250             );
251             my $sigin = delete $args{sigin};
252              
253             return $self->sync_set_value(
254             path => $self->device() . "/sigins/$sigin/diff",
255             type => 'I',
256             value => $value
257             );
258             }
259              
260              
261             cache amplitude_range => ( getter => 'get_amplitude_range' );
262              
263             sub get_amplitude_range {
264 0     0 1 0 my $self = shift;
265 0         0 return $self->cached_amplitude_range(
266             $self->get_value(
267             path => $self->device() . "/sigouts/0/range",
268             type => 'D'
269             )
270             );
271             }
272              
273              
274             sub set_amplitude_range {
275 0     0 1 0 my ( $self, $value, %args ) = validated_setter(
276             \@_,
277             value => { isa => 'Num' }
278             );
279              
280 0         0 return $self->cached_amplitude_range(
281             $self->sync_set_value(
282             path => $self->device() . "/sigouts/0/range",
283             type => 'D',
284             value => $value
285             )
286             );
287             }
288              
289              
290             sub set_output_status {
291 0     0 1 0 my ( $self, $value, %args ) = validated_setter(
292             \@_,
293             value => { isa => enum( [ 0, 1 ] ) },
294             );
295              
296 0         0 return $self->sync_set_value(
297             path => $self->device() . "/sigouts/0/on",
298             type => 'I',
299             value => $value,
300             );
301             }
302              
303             cache offset_voltage => ( getter => 'get_offset_voltage' );
304              
305              
306             sub get_offset_voltage {
307 0     0 1 0 my $self = shift;
308 0         0 return $self->cached_offset_voltage(
309             $self->get_value(
310             path => $self->device() . "/sigouts/0/offset",
311             type => 'D'
312             )
313             );
314             }
315              
316              
317             sub set_offset_voltage {
318 0     0 1 0 my ( $self, $value, %args ) = validated_setter(
319             \@_,
320             value => { isa => 'Num' }
321             );
322 0         0 return $self->cached_offset_voltage(
323             $self->sync_set_value(
324             path => $self->device() . "/sigouts/0/offset",
325             type => 'D',
326             value => $value
327             )
328             );
329             }
330              
331              
332             sub set_offset_status {
333 0     0 1 0 my ( $self, $value, %args ) = validated_setter(
334             \@_,
335             value => { isa => enum( [ 0, 1 ] ) },
336             );
337              
338 0         0 return $self->sync_set_value(
339             path => $self->device() . "/sigouts/0/add",
340             type => 'I',
341             value => $value,
342             );
343             }
344              
345             #
346             # compatibility with XPRESS::Sweep::Voltage sweep
347             #
348              
349             sub get_level {
350 0     0 0 0 my $self = shift;
351 0         0 return $self->get_offset_voltage();
352             }
353              
354             sub sweep_to_level {
355 0     0 0 0 my $self = shift;
356 0         0 my ( $target, $time, $stepwidth ) = @_;
357 0         0 $self->set_offset_voltage( value => $target );
358             }
359              
360             sub config_sweep {
361 0     0 0 0 croak "ZI_MFLI only supports step/list sweep with 'jump => 1'";
362             }
363              
364             sub set_voltage {
365 0     0 0 0 my $self = shift;
366 0         0 my $value = shift;
367 0         0 $self->set_offset_voltage( value => $value );
368             }
369              
370             my %adcselect_signals = (
371             0 => 'sigin1',
372             1 => 'currin1',
373             2 => 'trigger1',
374             3 => 'trigger2',
375             4 => 'auxout1',
376             5 => 'auxout2',
377             6 => 'auxout3',
378             7 => 'auxout4',
379             8 => 'auxin1',
380             9 => 'auxin2',
381             174 => 'constant_input',
382             );
383             my %adcselect_signals_revers = reverse %adcselect_signals;
384             my @adcselect_signals = values %adcselect_signals;
385              
386             #
387             # Demodulators
388             #
389              
390              
391             sub set_input {
392 0     0 1 0 my ( $self, $value, %args ) = validated_setter(
393             \@_,
394             value => { isa => enum( [@adcselect_signals] ) },
395             demod => { isa => 'Int' },
396             );
397              
398 0         0 $value = $adcselect_signals_revers{$value};
399 0         0 my $demod = delete $args{demod};
400 0         0 $self->sync_set_value(
401             path => $self->device() . "/demods/$demod/adcselect",
402             type => 'I', value => $value
403             );
404             }
405              
406             sub get_input {
407 0     0 1 0 my $self = shift;
408 0         0 my ($demod) = validated_list(
409             \@_, demod => { isa => 'Int' },
410             );
411 0         0 my $v = $self->get_value(
412             path => $self->device() . "/demods/$demod/adcselect",
413             type => 'I'
414             );
415 0         0 return $adcselect_signals{$v};
416             }
417              
418              
419             cache phase => ( getter => 'get_phase', index_arg => 'demod' );
420              
421             sub get_phase {
422 0     0 1 0 my $self = shift;
423 0         0 my ($demod) = validated_list(
424             \@_,
425             demod => { isa => 'Int' },
426             );
427              
428 0         0 return $self->cached_phase(
429             demod => $demod,
430             value => $self->get_value(
431             path => $self->device() . "/demods/$demod/phaseshift",
432             type => 'D'
433             )
434             );
435             }
436              
437              
438             sub set_phase {
439 0     0 1 0 my ( $self, $value, %args ) = validated_setter(
440             \@_,
441             value => { isa => 'Num' },
442             demod => { isa => 'Int' },
443             );
444 0         0 my $demod = delete $args{demod};
445 0         0 return $self->cached_phase(
446             demod => $demod,
447             value => $self->sync_set_value(
448             path => $self->device() . "/demods/$demod/phaseshift",
449             type => 'D', value => $value
450             )
451             );
452             }
453              
454              
455             cache tc => ( getter => 'get_tc', index_arg => 'demod' );
456              
457             sub get_tc {
458 0     0 1 0 my $self = shift;
459 0         0 my ($demod) = validated_list(
460             \@_,
461             demod => { isa => 'Int' },
462             );
463 0         0 return $self->cached_tc(
464             demod => $demod,
465             value => $self->get_value(
466             path => $self->device() . "/demods/$demod/timeconstant",
467             type => 'D'
468             )
469             );
470             }
471              
472              
473             sub set_tc {
474 0     0 1 0 my ( $self, $value, %args ) = validated_setter(
475             \@_,
476             value => { isa => 'Num' },
477             demod => { isa => 'Int' },
478             );
479 0         0 my $demod = delete $args{demod};
480 0         0 return $self->cached_tc(
481             demod => $demod,
482             value => $self->sync_set_value(
483             path => $self->device() . "/demods/$demod/timeconstant",
484             type => 'D',
485             value => $value
486             )
487             );
488             }
489              
490              
491             cache order => ( getter => 'get_order', index_arg => 'demod' );
492              
493             sub get_order {
494 0     0 1 0 my $self = shift;
495 0         0 my ($demod) = validated_list(
496             \@_,
497             demod => { isa => 'Int' },
498             );
499 0         0 return $self->cached_order(
500             demod => $demod,
501             value => $self->get_value(
502             path => $self->device() . "/demods/$demod/order",
503             type => 'I'
504             )
505             );
506             }
507              
508              
509             sub set_order {
510 0     0 1 0 my ( $self, $value, %args ) = validated_setter(
511             \@_,
512             value => { isa => 'Int' },
513             demod => { isa => 'Int' },
514             );
515 0         0 my $demod = delete $args{demod};
516 0         0 return $self->cached_order(
517             demod => $demod,
518             value => $self->sync_set_value(
519             path => $self->device() . "/demods/$demod/order", type => 'I',
520             value => $value
521             )
522             );
523             }
524              
525              
526             cache amplitude => ( getter => 'get_amplitude' );
527              
528             sub get_amplitude {
529 0     0 1 0 my ( $self, %args ) = validated_getter(
530             \@_,
531             demod => { isa => 'Int' },
532             );
533              
534 0         0 my $demod = delete $args{demod};
535 0         0 return $self->cached_amplitude(
536             $self->get_value(
537             path => $self->device() . "/sigouts/0/amplitudes/$demod",
538             type => 'D'
539             )
540             );
541             }
542              
543              
544             sub set_amplitude {
545 0     0 1 0 my ( $self, $value, %args ) = validated_setter(
546             \@_,
547             value => { isa => 'Num' },
548             demod => { isa => 'Int' },
549             );
550 0         0 my $demod = delete $args{demod};
551 0         0 return $self->cached_amplitude(
552             $self->sync_set_value(
553             path => $self->device() . "/sigouts/0/amplitudes/$demod",
554             type => 'D',
555             value => $value
556             )
557             );
558             }
559              
560              
561             sub get_amplitude_rms {
562 0     0 1 0 my $self = shift;
563 0         0 my $value = $self->get_amplitude(@_);
564 0         0 return $value / sqrt(2);
565             }
566              
567             sub set_amplitude_rms {
568 0     0 1 0 my $self = shift;
569 0         0 my %args = @_;
570 0         0 $args{value} *= sqrt(2);
571 0         0 return $self->set_amplitude(%args);
572             }
573              
574             #
575             # Output commands
576             #
577              
578              
579             sub get_xy {
580 1     1 1 3 my $self = shift;
581 1         8 my ($demod) = validated_list(
582             \@_,
583             demod => { isa => 'Int' },
584             );
585 1         460 my $demod_sample = $self->get_value(
586             path => $self->device() . "/demods/$demod/sample",
587             type => 'Demod'
588             );
589              
590 1         10 return { x => $demod_sample->{x}, y => $demod_sample->{y} };
591             }
592              
593             __PACKAGE__->meta()->make_immutable();
594              
595             1;
596              
597             __END__
598              
599             =pod
600              
601             =encoding UTF-8
602              
603             =head1 NAME
604              
605             Lab::Moose::Instrument::ZI_MFLI - Zurich Instruments MFLI Lock-in Amplifier
606              
607             =head1 VERSION
608              
609             version 3.900
610              
611             =head1 SYNOPSIS
612              
613             use Lab::Moose;
614              
615             my $mfli = instrument(
616             type => 'ZI_MFLI',
617             connection_type => 'Zhinst',
618             oscillator => 1, # 0 is default
619             connection_options => {
620             host => '132.188.12.13',
621             port => 8004, # Note: The HF2LI uses port 8005.
622             });
623              
624             $mfli->set_frequency(value => 10000);
625              
626             # Set time constants of first two demodulators to 0.5 sec:
627             $mfli->set_tc(demod => 0, value => 0.5);
628             $mfli->set_tc(demod => 1, value => 0.5);
629              
630             # Read out demodulators:
631             my $xy_0 = $mfli->get_xy(demod => 0);
632             my $xy_1 = $mfli->get_xy(demod => 1);
633             say "x_0, y_0: ", $xy_0->{x}, ", ", $xy_0->{y};
634              
635             =head1 METHODS
636              
637             If the MFLI has the Impedance Analyzer option, calling some of the following
638             setter options might be without effect. E.g. if the B<Bandwith Control> option
639             of the Impedance Analyzer module is set, manipulating the time constant with
640             C<set_tc> will not work.
641              
642             =head2 get_frequency
643              
644             # Get oscillator frequency of default oscillator.
645             my $freq = $mfli->get_frequency();
646              
647              
648             my $freq = $mfli->get_frequency(oscillator => ...);
649              
650             =head2 get_frq
651              
652             Alias for L</get_frequency>.
653              
654             =head2 set_frequency
655              
656             $mfli->set_frequency(value => 10000);
657              
658             Set oscillator frequency.
659              
660             =head2 set_frq
661              
662             Alias for L</set_frequency>.
663              
664             =head2 get_voltage_sens
665              
666             my $sens = $mfli->get_voltage_sens(sigin => 0);
667              
668             Get sensitivity (range) of voltage input.
669              
670             =head2 set_voltage_sens
671              
672             $mfli->set_voltage_sens(value => 1, sigin => 0);
673              
674             Set sensitivity (range) of voltage input.
675              
676             =head2 set_sigin_diff, set_sigin_ac, set_sigin_imp50, set_sigin_float, set_sigin_autorange, set_sigin_on
677              
678             These all take either C<0> or C<1> as value:
679              
680             $mfli->set_sigin_ac(sigin => 0, value => 0); # No AC coupling for first input
681             $mfli->set_sigin_imp50(sigin => 0, value => 1); # Use 50 Ohm input impedance for first input
682              
683             =head2 get_sigin_diff, get_sigin_ac, get_sigin_imp50, get_sigin_float, get_sigin_autorange, get_sigin_on
684              
685             These all return either C<0> or C<1> as value:
686              
687             say $mfli->get_sigin_ac(sigin => 0); # Does the first input use AC coupling?
688             $mfli->get_sigin_imp50(sigin => 0); # Is the impedance of the first input 50 Ohms?
689              
690             =head2 get_current_sens
691              
692             my $sens = $mfli->get_current_sens();
693              
694             Get sensitivity (range) of current input.
695              
696             =head2 set_current_sens
697              
698             $mfli->set_current_sens(value => 100e-6);
699              
700             Set sensitivity (range) of current input.
701              
702             =head2 get_amplitude_range
703              
704             my $amplitude_range = $mfli->get_amplitude_range();
705              
706             Get range of voltage output.
707              
708             =head2 set_amplitude_range
709              
710             $mfli->set_amplitude_range(value => 1);
711              
712             Set amplitude of voltage output.
713              
714             =head2 set_output_status
715              
716             $mfli->set_output_status(value => 1); # Enable output
717             $mfli->set_output_status(value => 0); # Disable output
718              
719             =head2 get_offset_voltage
720              
721             my $offset = $mfli->get_offset_voltage();
722              
723             Get DC offset.
724              
725             =head2 set_offset_voltage
726              
727             $mfli->set_offset_voltage(value => 1e-3);
728              
729             Set DC offset.
730              
731             =head2 set_offset_status
732              
733             $mfli->set_offset_status(value => 1); # Enable offset voltage
734             $mfli->set_offset_status(value => 0); # Disable offset voltage
735              
736             =head2 set_input/get_input
737              
738             $mfli->set_input(demod => 0, value => 'CurrIn1');
739             my $signal = $mfli->get_input(demod => 0);
740              
741             Valid inputs: currin1, trigger1, trigger2, auxout1, auxout2, auxout3, auxout4, auxin1, auxin2, constant_input
742              
743             t
744              
745             =head2 get_phase
746              
747             my $phase = $mfli->get_phase(demod => 0);
748              
749             Get demodulator phase shift.
750              
751             =head2 set_phase
752              
753             $mfli->set_phase(demod => 0, value => 10);
754              
755             Set demodulator phase.
756              
757             =head2 get_tc
758              
759             my $tc = $mfli->get_tc(demod => 0);
760              
761             Get demodulator time constant.
762              
763             =head2 set_tc
764              
765             $mfli->set_tc(demod => 0, value => 0.5);
766              
767             Set demodulator time constant.
768              
769             =head2 get_order
770              
771             my $order = $mfli->get_order(demod => 0);
772              
773             Get demodulator filter order.
774              
775             =head2 set_order
776              
777             $mfli->set_order(demod => 0, order => 4);
778              
779             Set demodulator filter order.
780              
781             =head2 get_amplitude
782              
783             # set amplitude for default oscillator
784             my $amplitude = $mfli->get_amplitude(demod => ...);
785              
786             Get peak amplitude of voltage output.
787              
788             =head2 set_amplitude
789              
790             $mfli->set_amplitude(value => ..., demod => ...);
791              
792             Set peak amplitude of voltage output.
793              
794             =head2 get_amplitude_rms/set_amplitude_rms
795              
796             Get/Set root mean square value of amplitude. These are wrappers around get_amplitude/set_amplitude and divide/multiply the peak amplitude with sqrt(2).
797              
798             =head2 get_xy
799              
800             my $xy_0 = $mfli->get_xy(demod => 0);
801             my $xy_1 = $mfli->get_xy(demod => 1);
802            
803             printf("x: %g, y: %g\n", $xy_0->{x}, $xy_0->{y});
804              
805             Get demodulator X and Y output measurement values.
806              
807             =head1 COPYRIGHT AND LICENSE
808              
809             This software is copyright (c) 2023 by the Lab::Measurement team; in detail:
810              
811             Copyright 2017 Andreas K. Huettel, Simon Reinhardt
812             2019 Simon Reinhardt
813             2020 Andreas K. Huettel, Simon Reinhardt
814              
815              
816             This is free software; you can redistribute it and/or modify it under
817             the same terms as the Perl 5 programming language system itself.
818              
819             =cut