| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Lab::Moose::Instrument::KeysightB2901A; | 
| 2 |  |  |  |  |  |  | $Lab::Moose::Instrument::KeysightB2901A::VERSION = '3.881'; | 
| 3 |  |  |  |  |  |  | #ABSTRACT: Agilent/Keysight B2901A voltage/current sourcemeter. | 
| 4 |  |  |  |  |  |  |  | 
| 5 | 2 |  |  | 2 |  | 2913 | use v5.20; | 
|  | 2 |  |  |  |  | 8 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 2 |  |  | 2 |  | 12 | use Moose; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 17 |  | 
| 9 | 2 |  |  | 2 |  | 15786 | use MooseX::Params::Validate; | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 22 |  | 
| 10 |  |  |  |  |  |  | use Lab::Moose::Instrument | 
| 11 | 2 |  |  | 2 |  | 1054 | qw/validated_getter validated_setter setter_params/; | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 136 |  | 
| 12 | 2 |  |  | 2 |  | 596 | use Lab::Moose::Instrument::Cache; | 
|  | 2 |  |  |  |  | 6 |  | 
|  | 2 |  |  |  |  | 28 |  | 
| 13 | 2 |  |  | 2 |  | 1675 | use Carp; | 
|  | 2 |  |  |  |  | 7 |  | 
|  | 2 |  |  |  |  | 149 |  | 
| 14 | 2 |  |  | 2 |  | 17 | use namespace::autoclean; | 
|  | 2 |  |  |  |  | 6 |  | 
|  | 2 |  |  |  |  | 14 |  | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | extends 'Lab::Moose::Instrument'; | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | has [qw/max_units_per_second max_units_per_step min_units max_units/] => | 
| 19 |  |  |  |  |  |  | ( is => 'ro', isa => 'Num', required => 1 ); | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | has source_level_timestamp => ( | 
| 22 |  |  |  |  |  |  | is       => 'rw', | 
| 23 |  |  |  |  |  |  | isa      => 'Num', | 
| 24 |  |  |  |  |  |  | init_arg => undef, | 
| 25 |  |  |  |  |  |  | ); | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | has verbose => ( | 
| 28 |  |  |  |  |  |  | is      => 'ro', | 
| 29 |  |  |  |  |  |  | isa     => 'Bool', | 
| 30 |  |  |  |  |  |  | default => 1 | 
| 31 |  |  |  |  |  |  | ); | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | sub BUILD { | 
| 34 | 1 |  |  | 1 | 0 | 6 | my $self = shift; | 
| 35 |  |  |  |  |  |  |  | 
| 36 | 1 |  |  |  |  | 11 | $self->clear(); | 
| 37 | 1 |  |  |  |  | 10 | $self->cls(); | 
| 38 |  |  |  |  |  |  | } | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | around default_connection_options => sub { | 
| 41 |  |  |  |  |  |  | my $orig     = shift; | 
| 42 |  |  |  |  |  |  | my $self     = shift; | 
| 43 |  |  |  |  |  |  | my $options  = $self->$orig(); | 
| 44 |  |  |  |  |  |  | my $usb_opts = { | 
| 45 |  |  |  |  |  |  | vid => 0x0957, pid => 0x8b18,    # Agilent vid! | 
| 46 |  |  |  |  |  |  | reset_device => 0 | 
| 47 |  |  |  |  |  |  | , # Problem of the B2901A: https://community.keysight.com/thread/36706 | 
| 48 |  |  |  |  |  |  | }; | 
| 49 |  |  |  |  |  |  | $options->{USB} = $usb_opts; | 
| 50 |  |  |  |  |  |  | $options->{'VISA::USB'} = $usb_opts; | 
| 51 |  |  |  |  |  |  | return $options; | 
| 52 |  |  |  |  |  |  | }; | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | sub source_function_query { | 
| 56 | 2 |  |  | 2 | 0 | 1589 | my ( $self, %args ) = validated_getter( \@_ ); | 
| 57 |  |  |  |  |  |  |  | 
| 58 | 2 |  |  |  |  | 1010 | my $value = $self->query( command => "SOUR:FUNC:MODE?", %args ); | 
| 59 | 2 |  |  |  |  | 7 | $value =~ s/["']//g; | 
| 60 | 2 |  |  |  |  | 8 | return $self->cached_source_function($value); | 
| 61 |  |  |  |  |  |  | } | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | sub source_function { | 
| 64 | 2 |  |  | 2 | 0 | 5127 | my ( $self, $value, %args ) = validated_setter( \@_ ); | 
| 65 | 2 |  |  |  |  | 14 | $self->write( command => "SOUR:FUNC:MODE $value", %args ); | 
| 66 | 2 |  |  |  |  | 12 | $self->cached_source_function($value); | 
| 67 |  |  |  |  |  |  | } | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | # Concurrent sense is always ON for the B2901A | 
| 70 |  |  |  |  |  |  | sub sense_function_concurrent_query { | 
| 71 | 1 |  |  | 1 | 0 | 4 | return 1; | 
| 72 |  |  |  |  |  |  | } | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | sub sense_function_concurrent { | 
| 75 | 0 |  |  | 0 | 0 | 0 | croak "Concurrent sense is always ON"; | 
| 76 |  |  |  |  |  |  | } | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  | sub set_level { | 
| 80 | 1 |  |  | 1 | 1 | 18 | my ( $self, $value, %args ) = validated_setter( | 
| 81 |  |  |  |  |  |  | \@_, | 
| 82 |  |  |  |  |  |  | value => { isa => 'Num' }, | 
| 83 |  |  |  |  |  |  | ); | 
| 84 |  |  |  |  |  |  |  | 
| 85 | 1 |  |  |  |  | 40 | return $self->linear_step_sweep( | 
| 86 |  |  |  |  |  |  | to => $value, verbose => $self->verbose, | 
| 87 |  |  |  |  |  |  | %args | 
| 88 |  |  |  |  |  |  | ); | 
| 89 |  |  |  |  |  |  | } | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | sub get_measurement { | 
| 93 | 0 |  |  | 0 | 1 | 0 | my ( $self, %args ) = validated_getter( \@_ ); | 
| 94 | 0 |  |  |  |  | 0 | my $meas = $self->query( command => ':MEAS?', %args ); | 
| 95 | 0 |  |  |  |  | 0 | my $elements = $self->query( command => ':FORM:ELEM:SENS?' ); | 
| 96 | 0 |  |  |  |  | 0 | my @elements    = split /,/, $elements; | 
| 97 | 0 |  |  |  |  | 0 | my @meas_values = split /,/, $meas; | 
| 98 | 0 |  |  |  |  | 0 | my %result = map { $_ => shift @meas_values } @elements; | 
|  | 0 |  |  |  |  | 0 |  | 
| 99 | 0 |  |  |  |  | 0 | return \%result; | 
| 100 |  |  |  |  |  |  | } | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | # | 
| 103 |  |  |  |  |  |  | # Aliases for Lab::XPRESS::Sweep API | 
| 104 |  |  |  |  |  |  | # | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | sub cached_level { | 
| 108 | 1 |  |  | 1 | 1 | 11 | my $self = shift; | 
| 109 | 1 |  |  |  |  | 8 | return $self->cached_source_level(@_); | 
| 110 |  |  |  |  |  |  | } | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | sub get_level { | 
| 114 | 1 |  |  | 1 | 1 | 4 | my $self = shift; | 
| 115 | 1 |  |  |  |  | 7 | return $self->source_level_query(@_); | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | sub set_voltage { | 
| 120 | 0 |  |  | 0 | 1 |  | my $self  = shift; | 
| 121 | 0 |  |  |  |  |  | my $value = shift; | 
| 122 | 0 |  |  |  |  |  | return $self->set_level( value => $value ); | 
| 123 |  |  |  |  |  |  | } | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | with qw( | 
| 126 |  |  |  |  |  |  | Lab::Moose::Instrument::Common | 
| 127 |  |  |  |  |  |  | Lab::Moose::Instrument::SCPI::Sense::Function::Concurrent | 
| 128 |  |  |  |  |  |  | Lab::Moose::Instrument::SCPI::Sense::Protection | 
| 129 |  |  |  |  |  |  | Lab::Moose::Instrument::SCPI::Sense::Range | 
| 130 |  |  |  |  |  |  | Lab::Moose::Instrument::SCPI::Sense::NPLC | 
| 131 |  |  |  |  |  |  | Lab::Moose::Instrument::SCPI::Source::Function | 
| 132 |  |  |  |  |  |  | Lab::Moose::Instrument::SCPI::Source::Level | 
| 133 |  |  |  |  |  |  | Lab::Moose::Instrument::SCPI::Source::Range | 
| 134 |  |  |  |  |  |  | Lab::Moose::Instrument::LinearStepSweep | 
| 135 |  |  |  |  |  |  | ); | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | after source_level => sub { | 
| 138 |  |  |  |  |  |  | my $self = shift; | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | # B2901A (with GPIB) accepts "SOUR:VOLT:LEV" in a faster rate than | 
| 141 |  |  |  |  |  |  | # it can set the value. Slow it down by doing a query after each set. | 
| 142 |  |  |  |  |  |  | $self->source_level_query(); | 
| 143 |  |  |  |  |  |  | }; | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | __PACKAGE__->meta()->make_immutable(); | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | 1; | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | __END__ | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | =pod | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | =encoding UTF-8 | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | =head1 NAME | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | Lab::Moose::Instrument::KeysightB2901A - Agilent/Keysight B2901A voltage/current sourcemeter. | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | =head1 VERSION | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | version 3.881 | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | use Lab::Moose; | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | my $source = instrument( | 
| 168 |  |  |  |  |  |  | type => 'KeysightB2901A', | 
| 169 |  |  |  |  |  |  | connection_type => 'LinuxGPIB', | 
| 170 |  |  |  |  |  |  | connection_options => {gpib_address => 15}, | 
| 171 |  |  |  |  |  |  | # mandatory protection settings | 
| 172 |  |  |  |  |  |  | max_units_per_step => 0.001, # max step is 1mV/1mA | 
| 173 |  |  |  |  |  |  | max_units_per_second => 0.01, | 
| 174 |  |  |  |  |  |  | min_units => -10, | 
| 175 |  |  |  |  |  |  | max_units => 10, | 
| 176 |  |  |  |  |  |  | ); | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | ### Sourcing | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | # Source voltage | 
| 182 |  |  |  |  |  |  | $source->source_function(value => 'VOLT'); | 
| 183 |  |  |  |  |  |  | $source->source_range(value => 210); | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | # Step-sweep to new level. | 
| 186 |  |  |  |  |  |  | # Stepsize and speed is given by (max|min)_units* settings. | 
| 187 |  |  |  |  |  |  | $source->set_level(value => 9); | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | # Get current level from device cache (without sending a query to the | 
| 190 |  |  |  |  |  |  | # instrument): | 
| 191 |  |  |  |  |  |  | my $level = $source->cached_level(); | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | ### Measurement | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | The B2901A provides a concurrent SENSE subsystem. See also L<Lab::Moose::Instrument::SCPI::Sense::Function::Concurrent>. | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | # Measure current | 
| 198 |  |  |  |  |  |  | $source->sense_function_on(value => ['CURR']); | 
| 199 |  |  |  |  |  |  | $source->sense_function(value => 'CURR'); | 
| 200 |  |  |  |  |  |  | # Set measurement range to 100nA | 
| 201 |  |  |  |  |  |  | $source->sense_range(value => 100e-9); | 
| 202 |  |  |  |  |  |  | # Use measurement integration time of 2 NPLC | 
| 203 |  |  |  |  |  |  | $source->sense_nplc(value => 2); | 
| 204 |  |  |  |  |  |  | # Set compliance limit to 10nA | 
| 205 |  |  |  |  |  |  | $source->sense_protection(value => 10e-9); | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | # Get measurement sample | 
| 208 |  |  |  |  |  |  | my $sample = $source->get_measurement(); | 
| 209 |  |  |  |  |  |  | my $current = $sample->{CURR}; | 
| 210 |  |  |  |  |  |  | # print all entries in sample (Voltage, Current, Resistance, Timestamp): | 
| 211 |  |  |  |  |  |  | use Data::Dumper; | 
| 212 |  |  |  |  |  |  | print Dumper $sample; | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | =head1 NOTES | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | There are problems with the USB connection: | 
| 217 |  |  |  |  |  |  | L<https://community.keysight.com/thread/36706>. GPIB works fine. | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | =head1 METHODS | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | Used roles: | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | =over | 
| 224 |  |  |  |  |  |  |  | 
| 225 |  |  |  |  |  |  | =item L<Lab::Moose::Instrument::Common> | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | =item L<Lab::Moose::Instrument::SCPI::Sense::Function::Concurrent> | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | =item L<Lab::Moose::Instrument::SCPI::Sense::Protection> | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | =item L<Lab::Moose::Instrument::SCPI::Sense::Range> | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | =item L<Lab::Moose::Instrument::SCPI::Sense::NPLC> | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | =item L<Lab::Moose::Instrument::SCPI::Source::Function> | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | =item L<Lab::Moose::Instrument::SCPI::Source::Level> | 
| 238 |  |  |  |  |  |  |  | 
| 239 |  |  |  |  |  |  | =item L<Lab::Moose::Instrument::SCPI::Source::Range> | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | =item L<Lab::Moose::Instrument::LinearStepSweep> | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | =back | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | =head2 set_level | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | $source->set_level(value => $new_level); | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | Go to new level. Sweep with multiple steps if the distance between current and | 
| 250 |  |  |  |  |  |  | new level is larger than C<max_units_per_step>. | 
| 251 |  |  |  |  |  |  |  | 
| 252 |  |  |  |  |  |  | =head2 get_measurement | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | my $sample = $source->get_measurement(); | 
| 255 |  |  |  |  |  |  | my $current = $sample->{CURR}; | 
| 256 |  |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | Do new measurement and return sample hashref of measured elements. | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | =head2 cached_level | 
| 260 |  |  |  |  |  |  |  | 
| 261 |  |  |  |  |  |  | my $current_level = $source->cached_level(); | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | Get current value from device cache. | 
| 264 |  |  |  |  |  |  |  | 
| 265 |  |  |  |  |  |  | =head2 get_level | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | my $current_level = $source->get_level(); | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | Query current level. | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | =head2 set_voltage | 
| 272 |  |  |  |  |  |  |  | 
| 273 |  |  |  |  |  |  | $source->set_voltage($value); | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | For XPRESS voltage sweep. Equivalent to C<< set_level(value => $value) >>. | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  | This software is copyright (c) 2023 by the Lab::Measurement team; in detail: | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  | Copyright 2018-2019  Simon Reinhardt | 
| 282 |  |  |  |  |  |  | 2020       Andreas K. Huettel | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | This is free software; you can redistribute it and/or modify it under | 
| 286 |  |  |  |  |  |  | the same terms as the Perl 5 programming language system itself. | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | =cut |