package Lab::Moose::Instrument::HP8596E;
$Lab::Moose::Instrument::HP8596E::VERSION = '3.930';
#ABSTRACT: HP8596E Spectrum Analyzer

use v5.20;


use PDL::Core qw/pdl cat nelem/;

use Moose;
use Moose::Util::TypeConstraints;
use MooseX::Params::Validate;
use Lab::Moose::Instrument qw/
    timeout_param
    precision_param
    validated_getter
    validated_setter
    validated_channel_getter
    validated_channel_setter
    validated_no_param_setter
    /;
use Lab::Moose::Instrument::Cache;
use Carp;
use namespace::autoclean;

extends 'Lab::Moose::Instrument';

with 'Lab::Moose::Instrument::SpectrumAnalyzer', qw(
    Lab::Moose::Instrument::Common

    Lab::Moose::Instrument::SCPI::Format

    Lab::Moose::Instrument::SCPI::Sense::Bandwidth
    Lab::Moose::Instrument::SCPI::Sense::Frequency
    Lab::Moose::Instrument::SCPI::Sense::Sweep
    Lab::Moose::Instrument::SCPI::Sense::Power
    Lab::Moose::Instrument::SCPI::Display::Window
    Lab::Moose::Instrument::SCPI::Unit

    Lab::Moose::Instrument::SCPI::Initiate

    Lab::Moose::Instrument::SCPIBlock

);

sub BUILD {
    my $self = shift;

    # limitation of hardware
    $self->capable_to_query_number_of_X_points_in_hardware(0);
    $self->capable_to_set_number_of_X_points_in_hardware(0);
    $self->hardwired_number_of_X_points(401);

    #$self->clear();
    $self->cls();
}


sub validate_trace_param {
    my ( $self, $trace ) = @_;
    if ( $trace < 1 || $trace > 3 ) {
        confess "trace has to be in (1..3)";
    }
    # convert trace number to name 1->A, 2->B, ...
    if ( $trace == 1 ) {
        $trace = 'A';
    }
    elsif ( $trace == 2 ) {
        $trace = 'B';
    }
    elsif ( $trace == 3 ) {
        $trace = 'C';
    }
    return $trace;
}

##### This device predates creation of SCPI commands (introduced in 1999), so we fake them

sub idn {
    my ( $self, %args ) = validated_getter( \@_ );
    return $self->query( command => '*ID?', %args );
}

sub cls {
    my ( $self, %args ) = validated_no_param_setter( \@_ );
    return $self->write( command => 'CLS', %args );
}


### Sense:Frequency emulation
sub sense_frequency_start_query {
    my ( $self, $channel, %args ) = validated_channel_getter( \@_ );

    return $self->cached_sense_frequency_start(
        $self->query( command => "FA?", %args ) );
}

sub sense_frequency_start {
    my ( $self, $channel, $value, %args ) = validated_channel_setter( \@_ );
    $self->write(
        command => sprintf( "FA %.17g", $value ),
        %args
    );
    $self->cached_sense_frequency_start($value);
}

sub sense_frequency_stop_query {
    my ( $self, $channel, %args ) = validated_channel_getter( \@_ );

    return $self->cached_sense_frequency_stop(
        $self->query( command => "FB?", %args ) );
}

sub sense_frequency_stop {
    my ( $self, $channel, $value, %args ) = validated_channel_setter( \@_ );
    $self->write(
        command => sprintf( "FB %.17g", $value ),
        %args
    );
    $self->cached_sense_frequency_stop($value);
}

### Sense:Power emulation

sub sense_power_rf_attenuation_query {
    my ( $self, %args ) = validated_getter( \@_ );

    return $self->cached_sense_power_rf_attenuation(
        $self->query( command => "AT?", %args ) );
}

sub sense_power_rf_attenuation {
    my ( $self, $value, %args ) = validated_setter( \@_ );

    $self->write( command => "AT $value", %args );
    $self->cached_sense_power_rf_attenuation($value);
}

### Sense:Sweep:Points emulation


sub sense_sweep_points_query {
    confess(
        "sub sense_sweep_points_query is not implemented by hardware, we should not be here"
    );
}

sub sense_sweep_points {
    confess(
        "sub sense_sweep_points is not implemented by hardware, we should not be here"
    );
}

### Sense:Sweep:Count  emulation

sub sense_sweep_count_query {
    my ( $self, $channel, %args ) = validated_channel_getter( \@_ );
    return $self->cached_sense_sweep_count(1);      # hardwired
}

sub sense_sweep_count {
    my ( $self, $channel, $value, %args ) = validated_channel_setter( \@_ );

    $value = 1;                                     # hard wired
    $self->cached_sense_sweep_count($value);
}

### Sense:Bandwidth:Resolution emulation

sub sense_bandwidth_resolution_query {
    my ( $self, $channel, %args ) = validated_channel_getter( \@_ );

    return $self->cached_sense_bandwidth_resolution(
        $self->query( command => "RB?", %args ) );
}

sub sense_bandwidth_resolution {
    my ( $self, $channel, $value, %args ) = validated_channel_setter(
        \@_,
        value => { isa => 'Num' }
    );
    $self->write(
        command => sprintf( "RB %.17g", $value ),
        %args
    );
    $self->cached_sense_bandwidth_resolution($value);
}

sub sense_bandwidth_video_query {
    my ( $self, $channel, %args ) = validated_channel_getter( \@_ );

    return $self->cached_sense_bandwidth_video(
        $self->query( command => "VB?", %args ) );
}

sub sense_bandwidth_video {
    my ( $self, $channel, $value, %args ) = validated_channel_setter(
        \@_,
        value => { isa => 'Num' }
    );
    $self->write(
        command => sprintf( "VB %.17g", $value ),
        %args
    );
    $self->cached_sense_bandwidth_video($value);
}

### Sense:Sweep:Time

sub sense_sweep_time_query {
    my ( $self, $channel, %args ) = validated_channel_getter( \@_ );
    return $self->cached_sense_sweep_time(
        $self->query( command => "ST?", %args ) );
}

sub sense_sweep_time {
    my ( $self, $channel, $value, %args ) = validated_channel_setter( \@_ );

    $self->write( command => "ST $value", %args );
    $self->cached_sense_sweep_time($value);
}

### Display:Window:Trace:Y:Scale:Rlevel

sub display_window_trace_y_scale_rlevel_query {
    my ( $self, $channel, %args ) = validated_channel_getter( \@_ );

    return $self->cached_display_window_trace_y_scale_rlevel(
        $self->query( command => "RL?", %args ) );
}

sub display_window_trace_y_scale_rlevel {
    my ( $self, $channel, $value, %args ) = validated_channel_setter( \@_ );
    $self->write(
        command => sprintf( "RL %.17g", $value ),
        %args
    );
    $self->cached_display_window_trace_y_scale_rlevel($value);
}

### Unit:Power

sub unit_power_query {
    my ( $self, %args ) = validated_getter( \@_ );

    return $self->cached_unit_power(
        $self->query( command => "AUNITS?", %args ) );
}

sub unit_power {
    my ( $self, $channel, $value, %args ) = validated_channel_setter( \@_ );

    # allowed values are DBM, DBMV, DBUV, V, W
    $self->write(
        command => sprintf( "AUNITS %s", $value ),
        %args
    );
    $self->cached_unit_power($value);
}

### Trace/Data emulation
sub get_traceY {
    # grab what is on display for a given trace
    my ( $self, %args ) = validated_hash(
        \@_,
        timeout_param(),
        precision_param(),
        trace => { isa => 'Int', default => 1 },
    );

    my $precision = delete $args{precision};
    my $trace     = delete $args{trace};

    $trace = $self->validate_trace_param($trace);


    # 'TDF P' switches output format to the human readable (ascii)
    # number representation. Numbers are separated by commas
    my $reply = $self->query(
        command => "TDF P; TR$trace?",
        %args
    );
    my @dat = split( /,/, $reply );

    return pdl @dat;

}

__PACKAGE__->meta()->make_immutable();

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Lab::Moose::Instrument::HP8596E - HP8596E Spectrum Analyzer

=head1 VERSION

version 3.930

=head1 SYNOPSIS

 my $data = $hp->get_spectrum(trace=>1, timeout => 10);

=head1 Driver for HP8596E series spectrum analyzers

=head1 METHODS

=head2 validate_trace_param

Validates or applies hardware friendly  aliases to trace parameter.
Trace has to be in (1..3).

=head1 Missing SCPI functionality

HP8596E has no Sense:Sweep:Points implementation

=head2 sense_sweep_points_query

=head2 sense_sweep_points

=head1 NAME

Lab::Moose::Instrument::HP8596E - HP Spectrum Analyzer

=head1 VERSION

version 3.621

=head1 METHODS

This driver implements the following high-level method:

=head2 get_spectrum

 $data = $hp->get_spectrum(timeout => 10, trace => 2);

Perform a single sweep and return the resulting spectrum as a 2D PDL:

 [
  [freq1,  freq2,  freq3,  ...,  freqN],
  [power1, power2, power3, ..., powerN],
 ]

I.e. the first dimension runs over the sweep points.

This method accepts a hash with the following options:

=over

=item B<timeout>

timeout for the sweep operation. If this is not given, use the connection's
default timeout.

=item B<trace>

number of the trace (1..3). Defaults to 1.

=back

=head1 SEE ALSO

This driver modeled closely to RS_FSV (see L<Lab::Moose::Instrument::RS_FSV>) and should perform similar functions.

In particular commands from the following SCPI subsystems are implemented

L<Lab::Moose::Instrument::SCPI::Sense::Frequency>,
L<Lab::Moose::Instrument::SCPI::Sense::Sweep>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2025 by the Lab::Measurement team; in detail:

  Copyright 2018       Eugeniy E. Mikhailov


This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
