File Coverage

blib/lib/Lab/Moose/Instrument/KeysightDSOS604A.pm
Criterion Covered Total %
statement 23 120 19.1
branch 0 14 0.0
condition 0 9 0.0
subroutine 8 33 24.2
pod 20 25 80.0
total 51 201 25.3


line stmt bran cond sub pod time code
1             package Lab::Moose::Instrument::KeysightDSOS604A;
2             $Lab::Moose::Instrument::KeysightDSOS604A::VERSION = '3.880';
3             #ABSTRACT: Keysight DSOS604A infiniium S-Series Oscilloscope.
4              
5 1     1   3910 use v5.20;
  1         4  
6              
7 1     1   6 use Moose;
  1         2  
  1         8  
8 1     1   7273 use MooseX::Params::Validate;
  1         2  
  1         9  
9 1     1   594 use Moose::Util::TypeConstraints qw/enum/;
  1         3  
  1         8  
10             use Lab::Moose::Instrument
11 1         102 qw/validated_getter validated_setter setter_params validated_channel_getter
12 1     1   560 validated_channel_setter/;
  1         4  
13 1     1   6 use Lab::Moose::Instrument::Cache;
  1         5  
  1         13  
14 1     1   770 use Carp 'croak';
  1         8  
  1         79  
15 1     1   8 use namespace::autoclean;
  1         6  
  1         11  
16              
17             extends 'Lab::Moose::Instrument';
18              
19             has input_impedance => (
20             is => 'ro',
21             isa => enum( [qw/DC DC50 DCFifty LFR1 LFR2/]),
22             default => 'DC50'
23             );
24              
25             has instrument_nselect => (
26             is => 'ro',
27             isa => 'Int',
28             default => 1
29             );
30              
31             has waveform_format => (
32             is => 'rw',
33             isa => enum([qw/ASCii BINary BYTE WORD FLOat/]),
34             default => 'FLOat'
35             );
36              
37             around default_connection_options => sub {
38             my $orig = shift;
39             my $self = shift;
40             my $options = $self->$orig();
41             my $usb_opts = { vid => 0x2a8d, pid => 0x9045,
42             reset_device => 0 # Same as with the B2901A
43             };
44             $options->{USB} = $usb_opts;
45             $options->{'VISA::USB'} = $usb_opts;
46             return $options;
47             };
48              
49              
50             # It is recommended to use the :PDER? query instead of the standard *OPC? query
51             # on this oscilloscope. This is because *OPC? returns after the previous
52             # commands are parsed, not after the previous commands are executed completely.
53             # :PDER? does just this, like you would expect from the standard SCPI *OPC?
54             # query. See the programming manual page 209 for more information.
55              
56             # around opc_query => sub {
57             # my ( $self, %args ) = validated_getter( \@_ );
58             # return $self->query( command => ':PDER?', %args );
59             # };
60              
61             sub BUILD {
62 0     0 0   my $self = shift;
63 0           $self->clear();
64 0           $self->cls();
65 0           $self->write(command => ":CHANnel".$self->instrument_nselect.":DISPlay ON");
66 0           $self->write(command => ":WAVeform:FORMat ".$self->waveform_format);
67 0           $self->write(command => ":WAVeform:BYTeorder LSBFirst" );
68 0           $self->write(command => ":WAVeform:SOURce CHANnel".$self->instrument_nselect);
69 0           $self->write(command => ":WAVeform:STReaming ON" );
70 0           $self->timebase_reference(value => 'LEFT');
71 0           $self->timebase_ref_perc(value => 5);
72 0           $self->channel_input(channel => $self->instrument_nselect, parameter => $self->input_impedance);
73 0           $self->write(command => ":TRIGger:EDGE:SOURce CHANnel".$self->instrument_nselect);
74 0           $self->write(command => ":TRIGger:EDGE:SLOPe POSitive");
75 0           $self->write(command => ":MEASure:CLEar");
76              
77             }
78              
79             sub get_default_channel {
80 0     0 0   my $self = shift;
81 0           return $self->instrument_nselect;
82             }
83              
84             ###
85             ### DEBUGGING
86             ###
87              
88             sub read_error {
89 0     0 0   my ( $self, %args ) = validated_getter( \@_ );
90 0           return $self->query( command => ":SYSTem:ERRor? STRing", %args );
91             }
92              
93             sub system_debug {
94 0     0 0   my ( $self, $output, $filename, %args ) = validated_setter( \@_,
95             output => { isa => enum( [qw/FILE SCReen FileSCReen/]) },
96             filename => { isa => 'Str' }
97             );
98 0           $self->write( command => ":SYSTem:DEBug ON,$output,\"$filename\",CREate", %args );
99             }
100              
101             sub disable_debug {
102 0     0 0   my ( $self, %args ) = validated_getter( \@_);
103 0           $self->write( command => ":SYSTem:DEBug OFF", %args );
104             }
105              
106             ###
107             ### MEASURE
108             ###
109              
110              
111             sub save_measurement {
112 0     0 1   my ( $self, $value, %args ) = validated_setter(
113             \@_,
114             value => { isa => 'Str' }
115             );
116 0           $self->write( command => ":DISK:SAVE:MEASurements \"$value\"", %args );
117             }
118              
119              
120             sub measure_vpp {
121 0     0 1   my ( $self, $channel, %args ) = validated_getter( \@_ );
122              
123 0           return $self->query( command => ":MEASure:VPP? CHANnel${channel}", %args );
124             }
125              
126             ###
127             ### SAVE TO DISK
128             ###
129              
130              
131             sub save_waveform {
132 0     0 1   my ( $self, $channel, %args ) = validated_channel_getter( \@_,
133             filename => { isa => 'Str'},
134             format => { isa => enum( [qw/BIN CSV INTernal TSV TXT H5 H5INt MATlab/])}
135             );
136             my ( $source, $filename, $format)
137 0           = delete @args{qw/source filename format/};
138              
139 0           $self->write( command => ":DISK:SAVE:WAVeform CHANnel${channel},\"$filename\",$format,ON", %args );
140             }
141              
142              
143             sub save_measurements {
144 0     0 1   my ( $self, %args ) = validated_getter( \@_,
145             filename => { isa => 'Str'}
146             );
147 0           my $filename = delete $args{'filename'};
148              
149 0           $self->write( command => ":DISK:SAVE:MEASurements \"$filename\"", %args );
150             }
151              
152             ###
153             ### TRIGGER
154             ###
155              
156              
157             sub force_trigger {
158 0     0 1   my ( $self, %args ) = validated_getter( \@_ );
159 0           $self->write( command => ":TRIGger:FORCe", %args );
160             }
161              
162              
163             sub trigger_level {
164 0     0 1   my ( $self, $channel, $value, %args ) = validated_channel_setter( \@_ );
165              
166 0           $self->write( command => ":TRIGger:LEVel CHANnel${channel},$value", %args );
167             }
168              
169             ###
170             ### ACQUIRE
171             ###
172              
173              
174             sub acquire_mode {
175 0     0 1   my ( $self, $value, %args ) = validated_setter(
176             \@_,
177             value => { isa => enum( [qw/ETIMe RTIMe PDETect HRESolution SEGMented SEGPdetect SEGHres/])},
178             );
179 0           $self->write( command => ":ACQuire:MODE $value", %args );
180             }
181              
182              
183             sub acquire_hres {
184 0     0 1   my ( $self, $value, %args ) = validated_setter(
185             \@_,
186             value => { isa => 'Int', default => 0},
187             );
188 0 0 0       if ($value == 0){
    0          
189 0           $self->write( command => ":ACQuire:HRESolution AUTO", %args );
190             } elsif ($value >= 11 and $value <= 16){
191 0           $self->write( command => ":ACQuire:HRESolution BITF$value", %args );
192             } else {
193 0           croak "The Bit resolution can be 0 (for an automatic choice) or between 11 and 16";
194             }
195              
196             }
197              
198              
199             sub acquire_points {
200 0     0 1   my ( $self, $value, %args ) = validated_setter(
201             \@_,
202             value => { isa => 'Lab::Moose::PosNum' }
203             );
204 0           $self->write( command => ":ACQuire:POINts:ANALog $value", %args );
205             }
206              
207             ###
208             ### TIMEBASE
209             ###
210              
211              
212             sub timebase_range {
213 0     0 1   my ( $self, $value, %args ) = validated_setter(
214             \@_,
215             value => { isa => 'Lab::Moose::PosNum' }
216             );
217              
218 0           $self->write( command => ":TIMebase:RANGe $value", %args );
219             }
220              
221              
222             sub timebase_reference {
223 0     0 1   my ( $self, $value, %args ) = validated_setter(
224             \@_,
225             value => { isa => enum( [qw/LEFT CENTer RIGHt/]) }
226             );
227              
228 0           $self->write( command => ":TIMebase:REFerence $value", %args );
229             }
230              
231              
232             sub timebase_ref_perc {
233 0     0 1   my ( $self, $value, %args ) = validated_setter(
234             \@_,
235             value => { isa => 'Num' }
236             );
237 0 0 0       if ($value > 100 || $value < 0){
238 0           croak "The offset percentage must be between 0 and 100";
239             };
240              
241 0           $self->write( command => ":TIMebase:REFerence:PERCent $value", %args );
242             }
243              
244              
245             sub timebase_clock {
246 0     0 1   my ( $self, $value, %args ) = validated_setter(
247             \@_,
248             value => { isa => enum( [qw/ON 1 OFF 0 HFRequency/]) }
249             );
250              
251 0           $self->write( command => ":TIMebase:REFClock $value", %args );
252             }
253              
254             ###
255             ### WAVEFORM
256             ###
257              
258              
259             sub get_waveform {
260 0     0 1   my ( $self, $channel, %args ) = validated_channel_getter( \@_);
261 0 0 0       if ($channel < 1 or $channel > 4){
262 0           croak "The available channels are 1,2,3 and 4";
263             }
264             # Capture a waveform after the next trigger event
265 0           $self->write(command => ":DIGitize CHANnel${channel}");
266 0           $self->opc_query();
267             # Query some parameters
268 0           my $yOrg = $self->query(command => ":WAVeform:YORigin?");
269 0           my $yInc = $self->query(command => ":WAVeform:YINCrement?");
270 0           my $xOrg = $self->query(command => ":WAVeform:XORigin?");
271 0           my $xInc = $self->query(command => ":WAVeform:XINCrement?");
272 0           my $points = $self->query(command => ":ACQuire:POINts:ANALog?");
273             # Compute the required data size in bits depending on the waveform format
274 0           my $format = $self->query(command => ":WAVeform:FORMat?");
275 0           my $fbits;
276 0 0         if ($format eq 'BYTE') { $fbits = 8; } elsif ($format eq 'WORD') { $fbits = 16; }
  0 0          
  0 0          
277 0           elsif ($format eq 'FLOat') { $fbits = 32; } else { $fbits = 64; }
  0            
278             # The read length is the amount of acquired points times the bit count plus
279             # a small buffer of 128 bits
280 0           my @data = ( split /,/, $self->query(
281             command => ":WAVeform:DATA?",
282             read_length => $points*$fbits+128
283             ));
284             # Wait for the data download to complete
285 0           $self->opc_query();
286             # Turn on the display for visual feedback
287 0           $self->write(command => ":CHANnel${channel}:DISPlay ON");
288             # Rescale the voltage values
289 0           foreach (0..@data-1) {$data[$_] = $data[$_]*$yInc+$yOrg;}
  0            
290             # Compute the time axis corresponding to each voltage value
291 0           my @times;
292 0           foreach (1..@data) {@times[$_-1] = $_*$xInc+$xOrg}
  0            
293             # Return a data block containing both the time and voltage values
294 0           return [\@times, \@data];
295             }
296              
297              
298             sub set_waveform_format {
299 0     0 1   my ( $self, $value, %args ) = validated_setter(
300             \@_,
301             value => { isa => enum( [qw/ASCii BINary BYTE WORD FLOat/]) }
302             );
303              
304 0           $self->write( command => ":WAVeform:FORMat $value", %args );
305             }
306              
307              
308             sub waveform_source {
309 0     0 1   my ( $self, $channel, %args ) = validated_channel_getter( \@_, );
310              
311 0           $self->write( command => ":WAVeform:SOURce CHANnel${channel}", %args );
312             }
313              
314             ###
315             ### CHANNEL
316             ###
317              
318              
319             sub channel_input {
320 0     0 1   my ( $self, $channel, %args ) = validated_channel_getter(
321             \@_,
322             parameter => { isa => enum( [qw/DC DC50 DCFifty LFR1 LFR2/])}
323             );
324 0           my $parameter = delete $args{'parameter'};
325              
326 0           $self->write( command => ":CHANnel${channel}:INPut $parameter", %args );
327             }
328              
329              
330             sub channel_differential {
331 0     0 1   my ( $self, $channel, %args ) = validated_channel_getter(
332             \@_,
333             mode => { isa => 'Bool'}
334             );
335 0           my $mode = delete $args{'mode'};
336              
337 0           $self->write( command => ":CHANnel${channel}:DIFFerential $mode", %args );
338             }
339              
340              
341             sub channel_range {
342 0     0 1   my ( $self, $channel, %args ) = validated_channel_getter(
343             \@_,
344             range => { isa => 'Num'}
345             );
346 0           my $range = delete $args{'range'};
347              
348 0           $self->write( command => ":CHANnel${channel}:RANGe $range", %args );
349             }
350              
351             sub channel_offset {
352 0     0 1   my ( $self, $channel, %args ) = validated_channel_getter(
353             \@_,
354             offset => { isa => 'Num'}
355             );
356 0           my $offset = delete $args{'offset'};
357              
358 0           $self->write( command => ":CHANnel${channel}:OFFSet $offset", %args );
359             }
360              
361             with qw(
362             Lab::Moose::Instrument::Common
363             );
364              
365             __PACKAGE__->meta()->make_immutable();
366              
367             1;
368              
369             __END__
370              
371             =pod
372              
373             =encoding UTF-8
374              
375             =head1 NAME
376              
377             Lab::Moose::Instrument::KeysightDSOS604A - Keysight DSOS604A infiniium S-Series Oscilloscope.
378              
379             =head1 VERSION
380              
381             version 3.880
382              
383             =head1 SYNOPSIS
384              
385             use Lab::Moose;
386              
387             my $source = instrument(
388             type => 'KeysightDSOS604A',
389             input_impedance => ...,
390             instrument_nselect => ...,
391             waveform_format => ...
392             );
393              
394             =over 4
395              
396             =item * C<input_impedance> specifies the default input input impedance. See channel_input for more information
397              
398             =item * C<instrument_nselect> specifies the default input channel
399              
400             =item * C<waveform_format> specifies the default format for waveform data. See set_waveform_format for more information
401              
402             =back
403              
404             Most commands accept a C<channel> argument which can be 1,2,3 or 4.
405              
406             =head2 save_measurement
407              
408             $keysight->save_measurement(value => 'C:\Users\Administrator\Documents\Results\my_measurement');
409              
410             Save all current measurements on screen to the specified path.
411              
412             =head2 measure_vpp
413              
414             $keysight->measure_vpp(channel => 1);
415              
416             Query the Vpp voltage of a specified source.
417              
418             =head2 save_waveform
419              
420             $keysight->save_waveform(source => 'CHANnel1', filename => 'C:\Users\Administrator\Documents\Results\data2306_c1_5',format => 'CSV');
421              
422             Save the waveform currently displayed on screen. C<source> can be a channel, function,
423             histogram, etc, C<filename> specifies the path the waveform is saved to and format can be
424             C<BIN CSV INTernal TSV TXT H5 H5INt MATlab>.
425              
426             The following file name extensions are used for the different formats:
427              
428             =over 4
429              
430             =item * BIN = file_name.bin
431              
432             =item * CSV (comma separated values) = file_name.csv
433              
434             =item * INTernal = file_name.wfm
435              
436             =item * TSV (tab separated values) = file_name.tsv
437              
438             =item * TXT = file_name.txt
439              
440             =item * H5 (HDF5) = file_name.h5
441              
442             In the H5 format, data is saved as floats. In this case, the data values are actual
443             vertical values and do not need to be multiplied by the Y increment value.
444              
445             =item * H5INt (HDF5) = file_name.h5
446              
447             In the H5INt format, data is saved as integers. In this case, data values are
448             quantization values and need to be multiplied by the Y increment value and
449             added to the Y origin value to get the actual vertical values.
450              
451             =item * MATlab (MATLAB data format) = file_name.mat
452              
453             =back
454              
455             =head2 save_measurements
456              
457             $keysight->save_measurements(filename => 'C:\Users\Administrator\Documents\Results\my_measurements');
458              
459             Save all measurements on-screen to a file.
460              
461             =head2 force_trigger
462              
463             $keysight->force_trigger();
464              
465             Force a trigger event by command.
466              
467             =head2 trigger_level
468              
469             $keysight->trigger_level(channel => 1, value => 0.1);
470              
471             Set the global trigger to a specified channel with a trigger level in volts.
472              
473             =head2 acquire_mode
474              
475             $keysight->acquire_mode(value => 'HRESolution');
476              
477             Allowed values: C<ETIMe, RTIMe, PDETect, HRESolution, SEGMented, SEGPdetect, SEGHres>
478              
479             See the programming manual on page 243 for more information on the different
480             acquisation modes. The default is RTIMe.
481              
482             =head2 acquire_hres
483              
484             $keysight->acquire_hres(value => 'BITF16');
485              
486             Specify the minimum resolution for the High Resolution acquisition mode.
487              
488             =head2 acquire_points
489              
490             $keysight->acquire_points(value => 40000);
491              
492             Specify the amount of data points collected within an acquisition window. Using
493             this command adjusts the sample rate automatically.
494              
495             =head2 timebase_range
496              
497             $keysight->timebase_range(value => 0.00022);
498              
499             Manually adjust the Oscilloscopes time scale on the x-axis. The timebase range
500             specifies the time interval on-screen.
501              
502             =head2 timebase_reference
503              
504             $keysight->timebase_reference(value => 'LEFT');
505              
506             Specify where the time origin is on the display. By default it is centered.
507             Allowed values: C<LEFT CENTer RIGHt>
508              
509             =head2 timebase_ref_perc
510              
511             $keysight->timebase_ref_perc(value => 15);
512              
513             Shift the time origin by 0% to 100% in the opposite direction than C<timebase_reference>,
514             100% would shift the origin from left to right or the other way around.
515              
516             =head2 timebase_clock
517              
518             $keysight->timebase_clock(value => 'OFF')
519              
520             Enable or disable the Oscilloscopes 10 MHz REF IN BNC input (ON or OFF) or the
521             100MHz REF IN SMA input (HFRequency or OFF). When either option is enabled, the
522             the external reference input is used as a reference clock for the Oscilloscopes
523             horizonal scale instead of the internal reference clock.
524              
525             =head2 get_waveform
526              
527             $keysight->get_waveform(channel => 1);
528              
529             Query the waveform on any channel. When executing this subroutine the oscilloscope
530             waits for a trigger event, acquires a full waveform and returns an array reference
531             containing the scaled time and voltage axis in the form of [\@time, \@voltage].
532              
533             This acquisition method is called Blocking Synchronisation and should only be
534             used if the oscilloscope is certain to trigger, for example when measuring a
535             periodically oscillating signal. For more information see the programming manual
536             on page 211 and following.
537              
538             =head2 set_waveform_format
539              
540             $keysight->set_waveform_format(value => 'WORD');
541              
542             This command controls how the data is formatted when it is sent from
543             the oscilloscope, and pertains to all waveforms. The default format is FLOat.
544             The possible formats are:
545              
546             =over 4
547              
548             =item * ASCii
549              
550             ASCii-formatted data consists of waveform data values converted to the currently
551             selected units, such as volts, and are output as a string of ASCII characters with
552             each value separated from the next value by a comma.
553              
554             =item * BYTE
555              
556             BYTE data is formatted as signed 8-bit integers.
557              
558             =item * WORD
559              
560             WORD-formatted data is transferred as signed 16-bit integers in two bytes.
561              
562             =item * BINary
563              
564             BINary will return a binary block of (8-byte) uint64 values.
565              
566             =item * FLOat
567              
568             FLOat will return a binary block of (4-byte) single-precision floating-point values.
569              
570             =back
571              
572             For more information on these formats see the programming manual on page 1564.
573              
574             =head2 waveform_source
575              
576             $keysight->waveform_source(channel => 1);
577              
578             Select an input channel for the acquired waveform.
579              
580             =head2 channel_input
581              
582             $keysight->channel_input(channel => 'CHANnel1', parameter => 'DC50');
583              
584             C<parameter> can be either
585              
586             =over 4
587              
588             =item * C<DC> — DC coupling, 1 MΩ impedance.
589              
590             =item * C<DC50> | DCFifty — DC coupling, 50Ω impedance.
591              
592             =item * C<AC> — AC coupling, 1 MΩ impedance.
593              
594             =item * C<LFR1> | LFR2 — AC 1 MΩ input impedance.
595              
596             =back
597              
598             When no probe is attached, the coupling for each channel can be C<AC, DC, DC50> or
599             C<DCFifty>. If you have an 1153A probe attached, the valid parameters are C<DC, LFR1>
600             and C<LFR2> (low-frequency reject). See the programming manual on page 347 for more
601             information.
602              
603             =head2 channel_differential
604              
605             $keysight->channel_differential(channel => 1, mode => 1);
606              
607             Turns on or off differential mode. C<mode> is a boolean value, where 0 is
608             false and everything else is true.
609              
610             =head2 channel_range/channel_offset
611              
612             $keysight->channel_range(channel => 1, range => 1);
613             $keysight->channel_offset(channel => 1, offset => 0.2);
614              
615             Allows for manual adjustment of the oscilloscopes vertical voltage range and
616             -offset for a specific channel. Differential mode is turned on automatically
617             on execution. C<range> and C<offset> parameters are in volts.
618              
619             =head1 COPYRIGHT AND LICENSE
620              
621             This software is copyright (c) 2023 by the Lab::Measurement team; in detail:
622              
623             Copyright 2021 Andreas K. Huettel, Fabian Weinelt, Simon Reinhardt
624              
625              
626             This is free software; you can redistribute it and/or modify it under
627             the same terms as the Perl 5 programming language system itself.
628              
629             =cut