Metadata-Version: 2.1
Name: nirfmxspecan
Version: 25.8.0
Summary: Python APIs for interacting with NI RFmx SpecAn Product
Home-page: https://github.com/ni/nirfmx-python
License: MIT
Keywords: rfmx,nirfmx,nirfmxspecan
Author: NI
Author-email: opensource@ni.com
Maintainer: Thangam V
Maintainer-email: thangam.v@emerson.com
Requires-Python: >=3.9,<4.0
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Manufacturing
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Provides-Extra: dev
Requires-Dist: fasteners (>=0.19)
Requires-Dist: hightime (>=0.2.2)
Requires-Dist: nirfmxinstr (==25.8.0)
Requires-Dist: numpy (>=1.22) ; python_version >= "3.9" and python_version < "3.12" and implementation_name != "pypy"
Requires-Dist: numpy (>=1.26) ; python_version >= "3.12" and python_version < "3.13" and implementation_name != "pypy"
Requires-Dist: numpy (>=2.1) ; python_version >= "3.10" and python_version < "3.11" and implementation_name == "pypy"
Requires-Dist: numpy (>=2.1) ; python_version >= "3.13" and python_version < "4.0" and implementation_name != "pypy"
Requires-Dist: numpy (>=2.3) ; python_version >= "3.11" and python_version < "4.0" and implementation_name == "pypy"
Project-URL: Documentation, https://nirfmx-python-nirfmxspecan.readthedocs-hosted.com/
Project-URL: Repository, https://github.com/ni/nirfmx-python
Description-Content-Type: text/markdown

| **Info**      | Contains Python APIs for interacting with NI RFmx Products. |
| :------------ | :-----------------------------------------------------------|
| **Author**    | National Instruments                                        |

# Table of Contents

- [Table of Contents](#table-of-contents)
- [About](#about)
  - [Documentation](#documentation)
  - [Operating System Support](#operating-system-support)
- [Installation](#installation)
- [License](#license)
- [Support and Feedback](#support-and-feedback)
- [Example](#example)


# About

The **nirfmx-python** repository generates Python bindings (Application Programming Interface)
for interacting with the NI RFmx Products.

**nirfmx-python** follows [Python Software Foundation](https://devguide.python.org/#status-of-python-branches)
support policy for different versions.

The following products are supported:

* RFmx Instr (Python module: nirfmxinstr)
* RFmx SpecAn (Python module: nirfmxspecan)
* RFmx Bluetooth (Python module: nirfmxbluetooth)
* RFmx WLAN (Python module: nirfmxwlan)


## RFmx Instr Python API Status

| Item                         | Details                      |
|------------------------------|-------------------------------|
| **Driver Version Tested**    | [2025 Q4](http://www.ni.com/downloads/)                      |
| **PyPI Package Version**     | v19.1                        |
| **Supported Python Versions**| Python 3.9+ (64-bit)    |
| **Documentation**            | [RFmx Instr Docs](https://nirfmx-python-nirfmxinstr.readthedocs-hosted.com/en/latest/) |


## RFmx SpecAn Python API Status

| Item                         | Details                      |
|------------------------------|-------------------------------|
| **Driver Version Tested**    | [2025 Q4](http://www.ni.com/downloads/)                      |
| **PyPI Package Version**     | v19.1                        |
| **Supported Python Versions**| Python 3.9+ (64-bit)    |
| **Documentation**            | [RFmx SpecAn Docs](https://nirfmx-python-nirfmxspecan.readthedocs-hosted.com/en/latest/) |

## RFmx Bluetooth Python API Status

| Item                         | Details                      |
|------------------------------|-------------------------------|
| **Driver Version Tested**    | [2025 Q4](http://www.ni.com/downloads/)                      |
| **PyPI Package Version**     | v19.1                        |
| **Supported Python Versions**| Python 3.9+ (64-bit)    |
| **Documentation**            | [RFmx Bluetooth Docs](https://nirfmx-python-nirfmxbluetooth.readthedocs-hosted.com/en/latest/) |

## RFmx WLAN Python API Status

| Item                         | Details                      |
|------------------------------|-------------------------------|
| **Driver Version Tested**    | [2025 Q4](http://www.ni.com/downloads/)                      |
| **PyPI Package Version**     | v19.1                        |
| **Supported Python Versions**| Python 3.9+ (64-bit)    |
| **Documentation**            | [RFmx WLAN Docs](https://nirfmx-python-nirfmxwlan.readthedocs-hosted.com/en/latest/) |


## Documentation

You can find the latest API documentation for the **nirfmx-python** package
on [Read the Docs](https://nirfmx-python-nirfmxinstr.readthedocs-hosted.com/en/latest/)

Refer to the [NI RFmx User Manual](https://www.ni.com/docs/en-US/bundle/rfmx/page/user-manual-welcome.html)
for an overview of NI RFmx, system requirements, troubleshooting, key concepts, etc.


## Operating System Support

**nirfmx-python** supports Windows systems where the supported drivers are 
installed. Refer to [NI Hardware and Operating System Compatibility](https://www.ni.com/r/hw-support) for 
which versions of the driver support your hardware on a given operating system.


# Installation

You can use [pip](http://pypi.python.org/pypi/pip) to download
[nirfmxinstr](https://pypi.org/project/nirfmxinstr/), [nirfmxspecan](https://pypi.org/project/nirfmxspecan/), [nirfmxbluetooth](https://pypi.org/project/nirfmxbluetooth/), [nirfmxwlan](https://pypi.org/project/nirfmxwlan/)
and install it.
    
    $ python -m pip install nirfmxinstr
    $ python -m pip install nirfmxspecan
    $ python -m pip install nirfmxbluetooth
    $ python -m pip install nirfmxwlan

# Upgrade

You can use [pip](http://pypi.python.org/pypi/pip) to upgrade
[nirfmxinstr](https://pypi.org/project/nirfmxinstr/), [nirfmxspecan](https://pypi.org/project/nirfmxspecan/) packages using following commands:
    
    $ python -m pip install nirfmxinstr --upgrade
    $ python -m pip install nirfmxspecan --upgrade


# License

This project is licensed under the MIT License. While the source code is not publicly released,
the license permits binary distribution with attribution.

**Note:** This Python driver depends on several third-party components that are subject to separate
commercial licenses. Users are responsible for ensuring they have the appropriate rights and licenses
to use those dependencies in their environments.


# Support and Feedback

For support with Python API, hardware, the driver runtime or any other questions,
please visit [NI Community Forums](https://forums.ni.com/).


# Examples

<details> 
<summary> <strong> RFmxSpecAn Example </strong></summary>

```python
import nirfmxinstr
import nirfmxspecan
import numpy

instr_session = None
specan = None

try:
    # Open a RFmx Session
    instr_session = nirfmxinstr.Session(resource_name="RFSA", option_string="")

    # Configure RFmx Session
    instr_session.configure_frequency_reference(selector_string="",
    frequency_reference_source="OnboardClock", frequency_reference_frequency=10.0e+6)

    # Create SpecAn Signal
    specan = instr_session.get_specan_signal_configuration()

    # Configure SpecAn Signal
    specan.set_selected_ports(selector_string="", value="")
    specan.configure_frequency(selector_string="", center_frequency=1e+9)
    specan.configure_reference_level(selector_string="", reference_level=0.0)
    specan.configure_external_attenuation(selector_string="", external_attenuation=0.0)

    # Select Spectrum Measurement
    specan.select_measurements(selector_string="",
      measurements=nirfmxspecan.MeasurementTypes.SPECTRUM, enable_all_traces=True)

    # Configure Spectrum Measurement
    specan.spectrum.configuration.configure_span(selector_string="", span=1.0e+6)
    specan.spectrum.configuration.configure_measurement_method(selector_string="",
      measurement_method=nirfmxspecan.SpectrumMeasurementMethod.NORMAL)

    error_code = specan.initiate(selector_string="", result_name="")

    # Retrieve Results
    spectrum = numpy.empty(0, dtype=numpy.float32)
    x0, dx, _ = specan.spectrum.results.fetch_spectrum(selector_string="", timeout=10.0, 
      spectrum=spectrum)
    peak_amplitude, peak_frequency, frequency_resolution, error_code = (
      specan.spectrum.results.fetch_measurement(selector_string="", timeout=10.0))

    # Print Results
    print(f"Peak Amplitude (dBm)             {peak_amplitude}")
    print(f"Peak Frequency (Hz)              {peak_frequency}")

except Exception as e:
    print("ERROR: " + str(e))

finally:
    # Dispose Signal & Session
    if specan is not None:
        specan.dispose()
        specan = None
    if instr_session is not None:
        instr_session.close()
        instr_session = None
```
</details>

<details>
<summary><strong> RFmxBluetooth Example </strong></summary>

```python
import nirfmxinstr
import nirfmxbluetooth
import numpy

instr_session = None
bt_signal = None

try:
    # Create a new RFmx Session
    instr_session = nirfmxinstr.Session(resource_name="RFSA", option_string="")

    # Get BT signal configuration
    bt_signal = instr_session.get_bt_signal_configuration()

    # Configure frequency reference
    instr_session.configure_frequency_reference(selector_string="",
      frequency_reference_source="OnboardClock", frequency_reference_frequency=10e6)

    # Configure RF settings
    bt_signal.configure_rf(selector_string="", center_frequency=2.402e9,
      reference_level=0.00, external_attenuation=0.0)

    # Configure trigger
    bt_signal.configure_iq_power_edge_trigger(
        selector_string="",
        iq_power_edge_trigger_source="0",
        iq_power_edge_trigger_slope=nirfmxbluetooth.IQPowerEdgeTriggerSlope.RISING,
        iq_power_edge_trigger_level=-20.0,
        trigger_delay=0.0,
        trigger_minimum_quiet_time_mode=nirfmxbluetooth.TriggerMinimumQuietTimeMode.AUTO,
        trigger_minimum_quiet_time_duration=100e-6,
        iq_power_edge_trigger_level_type=nirfmxbluetooth.IQPowerEdgeTriggerLevelType.RELATIVE,
        enable_trigger=True
    )

    # Configure packet settings
    bt_signal.configure_packet_type(selector_string="",
      packet_type=nirfmxbluetooth.PacketType.PACKET_TYPE_DH1)
    bt_signal.configure_data_rate(selector_string="", data_rate=1000000)
    bt_signal.configure_payload_length(selector_string="",
      payload_length_mode=nirfmxbluetooth.PayloadLengthMode.AUTO, payload_length=10)

    # Select measurements
    bt_signal.select_measurements(selector_string="",
      measurements=nirfmxbluetooth.MeasurementTypes.ACP, enable_all_traces=True)

    # Configure ACP measurement
    bt_signal.acp.configuration.configure_burst_synchronization_type(selector_string="",
      burst_synchronization_type=nirfmxbluetooth.AcpBurstSynchronizationType.PREAMBLE)
    bt_signal.acp.configuration.configure_averaging(selector_string="",
      averaging_enabled=nirfmxbluetooth.AcpAveragingEnabled.FALSE, averaging_count=10)
    bt_signal.acp.configuration.configure_offset_channel_mode(selector_string="",
      offset_channel_mode=nirfmxbluetooth.AcpOffsetChannelMode.SYMMETRIC)

    number_of_offsets = 5
    channel_number = 0
    offset_channel_mode = nirfmxbluetooth.AcpOffsetChannelMode.SYMMETRIC

    if offset_channel_mode == nirfmxbluetooth.AcpOffsetChannelMode.SYMMETRIC:
        bt_signal.acp.configuration.configure_number_of_offsets(selector_string="",
          number_of_offsets)
    elif offset_channel_mode == nirfmxbluetooth.AcpOffsetChannelMode.INBAND:
        bt_signal.configure_channel_number(selector_string="", channel_number)

    # Initiate measurement
    error_code = bt_signal.initiate(selector_string="", result_name="")

    # Retrieve results
    measurement_status, error_code = bt_signal.acp.results.fetch_measurement_status(
      selector_string="", timeout=10.0)
    print(f"Measurement Status: {measurement_status}")

    reference_channel_power, error_code = bt_signal.acp.results.fetch_reference_channel_power(
      selector_string="", timeout=10.0)
    print(f"Reference Channel Power (dBm): {reference_channel_power}")

    (
        lower_absolute_power,
        upper_absolute_power,
        lower_relative_power,
        upper_relative_power,
        lower_margin,
        upper_margin,
        error_code
    ) = bt_signal.acp.results.fetch_offset_measurement_array(selector_string="", timeout=10.0)

    # Fetch traces
    limit_with_exception_mask = numpy.empty(0, dtype=numpy.float32)
    limit_without_exception_mask = numpy.empty(0, dtype=numpy.float32)
    x0_mask, dx_mask, error_code = bt_signal.acp.results.fetch_mask_trace(
        selector_string="", 
        timeout=10.0, 
        limit_with_exception_mask=limit_with_exception_mask, 
        limit_without_exception_mask=limit_without_exception_mask
    )

    absolute_power_trace = numpy.empty(0, dtype=numpy.float32)
    x0_abs, dx_abs, error_code = bt_signal.acp.results.fetch_absolute_power_trace(
        selector_string="", 
        timeout=10.0, 
        absolute_power=absolute_power_trace
    )

    spectrum = numpy.empty(0, dtype=numpy.float32)
    x0_spec, dx_spec, error_code = bt_signal.acp.results.fetch_spectrum(
        selector_string="", timeout=10.0, spectrum=spectrum
    )

    # Print Results
    print("------------------ACP------------------")
    print(f"Measurement Status                 : {measurement_status}")
    print(f"Reference Channel Power (dBm)      : {reference_channel_power}")
    print()

    print("------------------Offset Measurements------------------")
    for i in range(len(lower_absolute_power)):
        print(f"Offset {i}")
        print(f"Lower Absolute Powers (dBm)        : {lower_absolute_power[i]}")
        print(f"Upper Absolute Powers (dBm)        : {upper_absolute_power[i]}")
        print(f"Lower Relative Powers (dB)         : {lower_relative_power[i]}")
        print(f"Upper Relative Powers (dB)         : {upper_relative_power[i]}")
        print(f"Lower Margin (dB)                  : {lower_margin[i]}")
        print(f"Upper Margin (dB)                  : {upper_margin[i]}")
        print()

except Exception as e:
    print("ERROR: " + str(e))

finally:
    # Close Session
    if bt_signal is not None:
        bt_signal.dispose()
        bt_signal = None
    if instr_session is not None:
        instr_session.close()
        instr_session = None
```
</details>

<details>
<summary> <strong> RFmxWLAN Example </strong></summary>

```python
import nirfmxinstr
import nirfmxwlan
import numpy

instr_session = None
wlan_signal = None

try:
    # Create a new RFmx Session
    instr_session = nirfmxinstr.Session(resource_name="RFSA", option_string="")

    # Get WLAN Signal
    wlan_signal = instr_session.get_wlan_signal_configuration()
    
    # Configure measurement
    instr_session.configure_frequency_reference(
        selector_string="", frequency_reference_source="PXI_CLK",
        frequency_reference_frequency=10e6
    )
    wlan_signal.configure_frequency(selector_string="", center_frequency=2.412e9)
    wlan_signal.configure_reference_level(selector_string="", reference_level=0.0)
    wlan_signal.configure_external_attenuation(selector_string="", external_attenuation=0.0)
    wlan_signal.configure_iq_power_edge_trigger(
        selector_string="",
        iq_power_edge_source="0",
        iq_power_edge_slope=nirfmxwlan.IQPowerEdgeTriggerSlope.RISING_SLOPE,
        iq_power_edge_level=-20.0,
        trigger_delay=0.0,
        trigger_min_quiet_time_mode=nirfmxwlan.TriggerMinimumQuietTimeMode.AUTO,
        trigger_min_quiet_time_duration=5.0e-6,
        iq_power_edge_level_type=nirfmxwlan.IQPowerEdgeTriggerLevelType.RELATIVE,
        enable_trigger=True,
    )
    wlan_signal.configure_standard(selector_string="",
      standard=nirfmxwlan.Standard.STANDARD_802_11_AG)
    wlan_signal.configure_channel_bandwidth(selector_string="", channel_bandwidth=20e6)
    wlan_signal.select_measurements(selector_string="",
      measurement=nirfmxwlan.MeasurementTypes.SEM, enable_all_traces=True)
    wlan_signal.sem.configuration.configure_mask_type(selector_string="",
      mask_type=nirfmxwlan.SemMaskType.STANDARD)
    wlan_signal.sem.configuration.configure_averaging(
        selector_string="", 
        averaging_enabled=nirfmxwlan.SemAveragingEnabled.FALSE, 
        averaging_count=10, 
        averaging_type=nirfmxwlan.SemAveragingType.RMS
    )
    wlan_signal.sem.configuration.configure_sweep_time(selector_string="",
      sweep_time_auto=nirfmxwlan.SemSweepTimeAuto.TRUE, sweep_time_interval=1.0e-3)
    wlan_signal.sem.configuration.configure_span(selector_string="",
      span_auto=nirfmxwlan.SemSpanAuto.TRUE, span=66.0e6)

    error_code = wlan_signal.initiate(selector_string="", result_name="")

    # Retrieve results
    measurement_status, error_code = wlan_signal.sem.results.fetch_measurement_status(
      selector_string="", timeout=10.0)

    absolute_power, relative_power, error_code = (
        wlan_signal.sem.results.fetch_carrier_measurement(selector_string="", timeout=10.0)
    )

    (
        lower_offset_measurement_status,
        lower_offset_margin,
        lower_offset_margin_frequency,
        lower_offset_margin_absolute_power,
        lower_offset_margin_relative_power,
        error_code,
    ) = wlan_signal.sem.results.fetch_lower_offset_margin_array(selector_string="", timeout=10.0)

    (
        upper_offset_measurement_status,
        upper_offset_margin,
        upper_offset_margin_frequency,
        upper_offset_margin_absolute_power,
        upper_offset_margin_relative_power,
        error_code,
    ) = wlan_signal.sem.results.fetch_upper_offset_margin_array(selector_string="", timeout=10.0)

    spectrum = numpy.empty(0, dtype=numpy.float32)
    composite_mask = numpy.empty(0, dtype=numpy.float32)
    x0, dx, error_code = wlan_signal.sem.results.fetch_spectrum(selector_string="", timeout=10.0,
      spectrum=spectrum, composite_mask=composite_mask)

    # Print Results
    print(f"Measurement Status                          : {measurement_status}")
    print(f"Carrier Absolute Power (dBm)                : {absolute_power}\n")

    print("----------Lower Offset Measurements----------\n")
    for i in range(len(lower_offset_margin)):
        print(f"Offset {i}")
        print(f"Measurement Status              : {lower_offset_measurement_status[i]}")
        print(f"Margin (dB)                     : {lower_offset_margin[i]}")
        print(f"Margin Frequency (Hz)           : {lower_offset_margin_frequency[i]}")
        print(f"Margin Absolute Power (dBm)     : {lower_offset_margin_absolute_power[i]}\n")

    print("\n----------Upper Offset Measurements----------\n")
    for i in range(len(upper_offset_margin)):
        print(f"Offset {i}")
        print(f"Measurement Status              : {upper_offset_measurement_status[i]}")
        print(f"Margin (dB)                     : {upper_offset_margin[i]}")
        print(f"Margin Frequency (Hz)           : {upper_offset_margin_frequency[i]}")
        print(f"Margin Absolute Power (dBm)     : {upper_offset_margin_absolute_power[i]}\n")

except Exception as e:
    print("ERROR: " + str(e))

finally:
    # Close session
    if wlan_signal is not None:
        wlan_signal.dispose()
        wlan_signal = None
    if instr_session is not None:
        instr_session.close()
        instr_session = None
```
</details>
