"""Define functions to fix measurement errors."""
import logging
from pathlib import Path
import numpy as np
from multipac_testbench.instruments.electric_field.helper import (
load_rf_calibration_files,
read_e_field_probe_calibration,
)
from multipac_testbench.multipactor_test.loader import load, save
from multipac_testbench.util.transfer_functions import (
field_probe,
field_probe_inv,
)
from multipac_testbench.util.types import FIELD_PROBES
from numpy.typing import NDArray
[docs]
def fix_wrong_e_field_calibration(
freq_mhz: float,
g_probe_new: dict[str, float],
rack_calibration_folder: Path,
filepath_bad: Path,
filepath_new: Path | None = None,
g_probe_bad: dict[str, float] | None = None,
a_rack_bad: dict[str, float] | None = None,
b_rack_bad: dict[str, float] | None = None,
sep: str = ",",
**kwargs,
) -> None:
"""Recover proper electric field measurements.
Use this when the attenuation and/or the rf rack data in LabView was
incorrect.
Parameters
----------
freq_mhz :
Test frequency in :unit:`MHz`.
g_probe_new :
Correct attenuation at current frequency for every field probe. Keys
are the names of the field probes, *eg* ``"E1"``, ``"E2"``, etc.
rack_calibration_folder :
Folder holding all the rack calibration files. It should look like:
.. code-block::
outputs
├── E1_fit_calibration.csv
├── E2_fit_calibration.csv
├── E3_fit_calibration.csv
├── E4_fit_calibration.csv
├── E5_fit_calibration.csv
├── E6_fit_calibration.csv
└── E7_fit_calibration.csv
filepath_bad :
Path to the original data file, generally ``XLSX`` file.
filepath_new :
Path to the new corrected data file, generally ``CSV`` file.
g_probe_bad, a_rack_bad, b_rack_bad :
Dictionaries linking every field probe to the electric field probe
parameters as found in ``filepath_bad``. If not provided, should be
read from ``filepath_bad`` directly.
sep :
Column delimiter in input and output ``CSV`` files.
Raises
------
NotImplementedError
When the bad electric field probes parameters are not provided and
they should be read from ``filepath_bad`` directly.
"""
data, _ = load(filepath_bad, sep=sep)
if not all((g_probe_bad, a_rack_bad, b_rack_bad)):
g_probe_bad, a_rack_bad, b_rack_bad = read_e_field_probe_calibration(
data
)
assert g_probe_bad is not None
assert a_rack_bad is not None
assert b_rack_bad is not None
a_rack_new, b_rack_new = load_rf_calibration_files(
rack_calibration_folder, freq_mhz
)
for probe in FIELD_PROBES:
col = f"NI9205_{probe}"
if col not in data:
continue
v_acqui = field_probe_inv(
data[col].to_numpy(),
g_probe_bad[probe],
a_rack_bad[probe],
b_rack_bad[probe],
)
v_coax = field_probe(
v_acqui, g_probe_new[probe], a_rack_new[probe], b_rack_new[probe]
)
data[col] = v_coax
if filepath_new is None:
logging.warning(
"filepath_new was not given. I will overwrite the original data "
"file. Is it ok for you? y/[n]"
)
answer = input()
if answer not in ("y", "Y"):
logging.info("Returning without overwritting.")
return
filepath_new = filepath_bad
save(filepath_new, data, sep=sep, **kwargs)
[docs]
def fix_power_channel_b(
p_bad: NDArray[np.float64], k_fix: float, alpha_fix: float
) -> NDArray[np.float64]:
r"""Fix power measured on channel B.
Transfer function proposed by M. Vénière.
.. math::
P_\mathrm{ok} =
k_\mathrm{fix} \times P_\mathrm{bad}^{\alpha_\mathrm{fix}}
Parameters
----------
p_bad :
Power measured on channel B in :unit:`W`.
k_fix :
Fix slope constant.
alpha_fix :
Fix exponent constant.
Returns
-------
Fixed power in :unit:`W`.
"""
return k_fix * p_bad**alpha_fix