Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sea 178 test actions loading #110

Merged
merged 46 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
99ecc9f
only update test actions if using scos-actions mock sigan
jhazentia Feb 5, 2024
d13baee
change configure preselector logic
jhazentia Feb 6, 2024
9829efc
configurable cal adjust for data product
jhazentia Feb 6, 2024
835e649
debug log messages
jhazentia Feb 6, 2024
db5ae40
only add sensor cal to capture if sensor cal in result
jhazentia Feb 6, 2024
d9e6f88
only set cal datetime if cal_adjust
jhazentia Feb 6, 2024
002ec32
check for cal data before gettting cal datetime, memory debug
jhazentia Feb 9, 2024
e070087
memory debug
jhazentia Feb 9, 2024
c054376
log messages for debug
jhazentia Feb 9, 2024
ea2eefe
comment out debug
jhazentia Feb 9, 2024
7f79201
remove debug code
jhazentia Feb 16, 2024
322d096
use cal adjust in sea data product action, don't get cal datetime if …
jhazentia Feb 20, 2024
08d72e0
fix cal_adjust bugs
jhazentia Feb 27, 2024
b36f36d
add constructor to mock gps to match actual gps
jhazentia Feb 28, 2024
bda63db
fix tests
jhazentia Feb 28, 2024
062e6ce
autoformat code, always add logger to test actions
jhazentia Feb 28, 2024
3eaf79e
use test data product action from scos-tekrsa
jhazentia Feb 29, 2024
a70ce1b
fix bug in multi freq IQ action, small change to mock sigan recompute…
jhazentia Feb 29, 2024
0d26e5b
remove "calibration_datetime" from metadata, replaced by Calibration …
jhazentia Mar 4, 2024
5696802
fix m4s graph name
jhazentia Mar 4, 2024
a683cf8
revert previous change
jhazentia Mar 4, 2024
74cac26
autoformat, fix format problem
jhazentia Mar 5, 2024
adb5383
update formatting to prevent bad autoformat
jhazentia Mar 5, 2024
f7f08b3
Merge branch 'master' of https://github.com/NTIA/scos-actions into SE…
jhazentia Mar 7, 2024
ab657a9
fix m4s action duration in metadata
jhazentia Mar 8, 2024
c56da56
add plugin_name property to sigan iface
jhazentia Mar 8, 2024
01c2764
add docstring
jhazentia Mar 11, 2024
9c0718a
round to convert to int for m4s duration
jhazentia Mar 11, 2024
9e600f8
increment version, autoformat
jhazentia Mar 11, 2024
91e3eff
pass sigan for gps through method calls instead of constructor
jhazentia Mar 27, 2024
d06098f
check if CAL_ADJUST param exists, slight logging improvement
jhazentia Mar 27, 2024
7ca107a
Merge branch 'master' of https://github.com/NTIA/scos-actions into SE…
jhazentia Mar 27, 2024
3c37524
fix error from merge
jhazentia Mar 27, 2024
8d205e5
add rf_path to test actions
jhazentia Apr 4, 2024
b43b962
autoformat
jhazentia Apr 4, 2024
0a8621a
only check compression_point if cal_adjust
jhazentia Apr 4, 2024
a632242
autoformat
jhazentia Apr 4, 2024
93a32d0
change GPS methods to use sensor, add type hints
jhazentia Apr 5, 2024
d310202
fix circular import
jhazentia Apr 5, 2024
876b6a6
move sensor overload check from scos-usrp to sensor class
jhazentia Apr 9, 2024
9f1b040
remove clock_rate_lookup_by_sample_rate, remove unused import
jhazentia Apr 11, 2024
6638bfd
only add temperature to cal metadata if it exists
jhazentia Apr 11, 2024
8806dec
only load yaml actions if using scos_actions mock sigan
jhazentia Apr 15, 2024
a58f0eb
change version to 10.0.0
jhazentia Apr 17, 2024
a050817
fix bug in sync gps action
jhazentia Apr 24, 2024
7c0454d
Address feedback
jhazentia Apr 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.0
rev: v3.15.1
hooks:
- id: pyupgrade
args: ["--py38-plus"]
Expand All @@ -30,12 +30,12 @@ repos:
types: [file, python]
args: ["--profile", "black", "--filter-files", "--gitignore"]
- repo: https://github.com/psf/black
rev: 23.12.1
rev: 24.2.0
hooks:
- id: black
types: [file, python]
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.38.0
rev: v0.39.0
hooks:
- id: markdownlint
types: [file, markdown]
Expand Down
1 change: 1 addition & 0 deletions sample_debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
This is a sample file showing how an action be created and called for debugging purposes
using a mock signal analyzer.
"""

import json

from scos_actions.actions.acquire_single_freq_fft import SingleFrequencyFftAcquisition
Expand Down
2 changes: 1 addition & 1 deletion scos_actions/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "8.0.1"
__version__ = "9.0.0"
34 changes: 22 additions & 12 deletions scos_actions/actions/acquire_sea_data_product.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
DURATION_MS = "duration_ms"
NUM_SKIP = "nskip"
PFP_FRAME_PERIOD_MS = "pfp_frame_period_ms"
CAL_ADJUST = "calibration_adjust"

# Constants
DATA_TYPE = np.half
Expand Down Expand Up @@ -158,6 +159,7 @@ def __init__(
- 10.0 * np.log10(sample_rate_Hz * fft_size) # PSD scaling
+ 20.0 * np.log10(window_ecf) # Window energy correction
)
self.cal_adjust = True

def run(self, iq: ray.ObjectRef) -> np.ndarray:
"""
Expand Down Expand Up @@ -630,14 +632,20 @@ def capture_iq(self, params: dict) -> dict:
nskip = utils.get_parameter(NUM_SKIP, params)
num_samples = int(params[SAMPLE_RATE] * duration_ms * 1e-3)
# Collect IQ data
self.cal_adjust = True
for key, value in params.items():
logger.debug(f"param {key}={value}")
self.cal_adjust = utils.get_parameter(CAL_ADJUST, params)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to either check if the CAL_ADJUST param exists or catch the exception if it doesn't and set it to true.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d06098f

logger.debug(f"cal_adjust={self.cal_adjust}")
measurement_result = self.sensor.signal_analyzer.acquire_time_domain_samples(
num_samples, nskip
num_samples, nskip, cal_adjust=self.cal_adjust
)
# Store some metadata with the IQ
measurement_result.update(params)
measurement_result[
"sensor_cal"
] = self.sensor.signal_analyzer.sensor_calibration_data
if self.cal_adjust:
measurement_result["sensor_cal"] = (
self.sensor.signal_analyzer.sensor_calibration_data
)
toc = perf_counter()
logger.debug(
f"IQ Capture ({duration_ms} ms @ {(params[FREQUENCY]/1e6):.1f} MHz) completed in {toc-tic:.2f} s."
Expand Down Expand Up @@ -786,7 +794,8 @@ def capture_diagnostics(
"scos_sensor_version": SCOS_SENSOR_GIT_TAG,
"scos_actions_version": SCOS_ACTIONS_VERSION,
"scos_sigan_plugin": ntia_diagnostics.ScosPlugin(
name="scos_tekrsa", version=self.sensor.signal_analyzer.plugin_version
name=self.sensor.signal_analyzer.plugin_name,
version=self.sensor.signal_analyzer.plugin_version,
),
"preselector_api_version": PRESELECTOR_API_VERSION,
"sigan_firmware_version": self.sensor.signal_analyzer.firmware_version,
Expand Down Expand Up @@ -1125,19 +1134,20 @@ def create_capture_segment(
datetime=measurement_result["capture_time"],
duration=measurement_result[DURATION_MS],
overload=measurement_result["overload"],
sensor_calibration=ntia_sensor.Calibration(
datetime=measurement_result["sensor_cal"]["datetime"],
gain=round(measurement_result["sensor_cal"]["gain"], 3),
noise_figure=round(measurement_result["sensor_cal"]["noise_figure"], 3),
temperature=round(measurement_result["sensor_cal"]["temperature"], 1),
reference=DATA_REFERENCE_POINT,
),
sigan_settings=ntia_sensor.SiganSettings(
reference_level=self.sensor.signal_analyzer.reference_level,
attenuation=self.sensor.signal_analyzer.attenuation,
preamp_enable=self.sensor.signal_analyzer.preamp_enable,
),
)
if self.cal_adjust:
capture_segment.sensor_calibration = ntia_sensor.Calibration(
datetime=measurement_result["sensor_cal"]["datetime"],
gain=round(measurement_result["sensor_cal"]["gain"], 3),
noise_figure=round(measurement_result["sensor_cal"]["noise_figure"], 3),
temperature=round(measurement_result["sensor_cal"]["temperature"], 1),
reference=DATA_REFERENCE_POINT,
)
self.sigmf_builder.add_capture(capture_segment)

def get_sigmf_builder(
Expand Down
5 changes: 1 addition & 4 deletions scos_actions/actions/acquire_single_freq_fft.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,6 @@ def execute(self, schedule_entry: dict, task_id: int) -> dict:
# Save measurement results
measurement_result["data"] = m4s_result
measurement_result.update(self.parameters)
measurement_result[
"calibration_datetime"
] = self.sensor.signal_analyzer.sensor_calibration_data["datetime"]
measurement_result["task_id"] = task_id
measurement_result["classification"] = self.classification

Expand All @@ -191,7 +188,7 @@ def execute(self, schedule_entry: dict, task_id: int) -> dict:
sample_start=0,
start_time=measurement_result["capture_time"],
center_frequency_Hz=self.frequency_Hz,
duration_ms=int(self.num_samples / sample_rate_Hz),
duration_ms=round((self.num_samples / sample_rate_Hz) * 1000),
overload=measurement_result["overload"],
sigan_settings=sigan_settings,
)
Expand Down
3 changes: 0 additions & 3 deletions scos_actions/actions/acquire_single_freq_tdomain_iq.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,6 @@ def execute(self, schedule_entry: dict, task_id: int) -> dict:
measurement_result.update(self.parameters)
measurement_result["end_time"] = end_time
measurement_result["task_id"] = task_id
measurement_result[
"calibration_datetime"
] = self.sensor.signal_analyzer.sensor_calibration_data["datetime"]
measurement_result["classification"] = self.classification
sigan_settings = self.get_sigan_settings(measurement_result)
logger.debug(f"sigan settings:{sigan_settings}")
Expand Down
6 changes: 3 additions & 3 deletions scos_actions/actions/acquire_stepped_freq_tdomain_iq.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,9 @@ def __call__(self, sensor: Sensor, schedule_entry: dict, task_id: int):
sensor_cal["compression_point"] = sensor_cal.pop(
"1db_compression_point"
)
capture_segment.sensor_calibration = ntia_sensor.Calibration(
**sensor_cal
)
capture_segment.sensor_calibration = ntia_sensor.Calibration(
**sensor_cal
)
measurement_result["capture_segment"] = capture_segment

self.create_metadata(measurement_result, recording_id)
Expand Down
19 changes: 10 additions & 9 deletions scos_actions/actions/interfaces/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,16 @@ def configure_sigan(self, params: dict):

def configure_preselector(self, params: dict):
preselector = self.sensor.preselector
if self.PRESELECTOR_PATH_KEY in params:
path = params[self.PRESELECTOR_PATH_KEY]
logger.debug(f"Setting preselector RF path: {path}")
preselector.set_state(path)
elif self.sensor.has_configurable_preselector:
# Require the RF path to be specified if the sensor has a preselector.
raise ParameterException(
f"No {self.PRESELECTOR_PATH_KEY} value specified in the YAML config."
)
if self.sensor.has_configurable_preselector:
if self.PRESELECTOR_PATH_KEY in params:
path = params[self.PRESELECTOR_PATH_KEY]
logger.debug(f"Setting preselector RF path: {path}")
preselector.set_state(path)
else:
# Require the RF path to be specified if the sensor has a preselector.
raise ParameterException(
f"No {self.PRESELECTOR_PATH_KEY} value specified in the YAML config."
)
else:
# No preselector in use, so do not require an RF path
pass
Expand Down
6 changes: 0 additions & 6 deletions scos_actions/actions/interfaces/measurement_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,6 @@ def create_metadata(
self.sigmf_builder.set_classification(measurement_result["classification"])
except KeyError:
logger.warning(warning_str.format("classification"))
try:
self.sigmf_builder.set_last_calibration_time(
measurement_result["calibration_datetime"]
)
except KeyError:
logger.warning(warning_str.format("calibration_datetime"))
try:
cap = measurement_result["capture_segment"]
logger.debug(f"Adding capture:{cap}")
Expand Down
5 changes: 2 additions & 3 deletions scos_actions/actions/tests/test_sync_gps.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@ def callback(sender, **kwargs):

location_action_completed.connect(callback)
action = test_actions["test_sync_gps"]
sensor = Sensor(
signal_analyzer=MockSignalAnalyzer(), capabilities={}, gps=MockGPS()
)
sigan = MockSignalAnalyzer()
sensor = Sensor(signal_analyzer=sigan, capabilities={}, gps=MockGPS(sigan))
if sys.platform == "linux":
action(sensor, SYNC_GPS, 1)
assert _latitude
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
nasctn_sea_data_product:
name: test_nasctn_sea_data_product
name: test_SEA_CBRS_Measure_Baseline
rf_path: antenna
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this same rf_path: antenna param should be added to all of the test actions to allow them to work with a configurable preselector or without a preselector. Currently, they will all fail on a sensor if it has a configurable preselector.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8d205e5

calibration_adjust: False
# IIR filter settings
iir_apply: True
iir_gpass_dB: 0.1 # Max passband ripple below unity gain
iir_gstop_dB: 40 # Minimum stopband attenuation
iir_pb_edge_Hz: 5e6 # Passband edge frequency
iir_sb_edge_Hz: 5.008e6 # Stopband edge frequency
# Mean/Max FFT settings
fft_size: 175
# FFT settings
nffts: 320e3
fft_window_type: flattop # See scipy.signal.get_window for supported input
# PFP frame
pfp_frame_period_ms: 10
# APD downsampling settings
apd_bin_size_dB: 0.5 # Set to 0 or negative for no downsampling
apd_min_bin_dBm: -180
apd_bin_size_dB: 1.0 # Set to 0 or negative for no downsampling
apd_max_bin_dBm: -30
apd_min_bin_dBm: -180
# Time domain power statistics settings
td_bin_size_ms: 10
# Round all power results to X decimal places
round_to_places: 2
# Sigan Settings
preamp_enable: True
reference_level: -25
Expand Down
28 changes: 16 additions & 12 deletions scos_actions/discover/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,10 @@
from scos_actions.actions.monitor_sigan import MonitorSignalAnalyzer
from scos_actions.actions.sync_gps import SyncGps
from scos_actions.discover.yaml import load_from_yaml
from scos_actions.settings import ACTION_DEFINITIONS_DIR
from scos_actions.settings import ACTION_DEFINITIONS_DIR, SIGAN_CLASS, SIGAN_MODULE

actions = {
"logger": Logger(),
}
test_actions = {
"test_sync_gps": SyncGps(parameters={"name": "test_sync_gps"}),
"test_monitor_sigan": MonitorSignalAnalyzer(
parameters={"name": "test_monitor_sigan"}
),
"logger": Logger(),
}
actions = {"logger": Logger()}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we remove logger from the actions? Not a big deal either way.

test_actions = {"logger": Logger()}


def init(
Expand All @@ -33,4 +25,16 @@ def init(

yaml_actions, yaml_test_actions = init()
actions.update(yaml_actions)
test_actions.update(yaml_test_actions)
if (
SIGAN_MODULE == "scos_actions.hardware.mocks.mock_sigan"
and SIGAN_CLASS == "MockSignalAnalyzer"
):
test_actions.update(
{
"test_sync_gps": SyncGps(parameters={"name": "test_sync_gps"}),
"test_monitor_sigan": MonitorSignalAnalyzer(
parameters={"name": "test_monitor_sigan"}
),
}
)
test_actions.update(yaml_test_actions)
2 changes: 1 addition & 1 deletion scos_actions/discover/tests/test_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"""

sigan = MockSignalAnalyzer()
gps = MockGPS()
gps = MockGPS(sigan)


def test_load_from_yaml_existing():
Expand Down
3 changes: 3 additions & 0 deletions scos_actions/hardware/mocks/mock_gps.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@


class MockGPS(GPSInterface):
def __init__(self, sigan):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need a sigan?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 91e3eff

self.sigan = sigan

def get_location(timeout_s=1):
logger.warning("Using mock GPS!")
return 39.995118, -105.261572, 1651.0
Expand Down
10 changes: 9 additions & 1 deletion scos_actions/hardware/mocks/mock_sigan.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
"""Mock a signal analyzer for testing."""

import logging
from collections import namedtuple
from typing import Optional

import numpy as np

from scos_actions import __package__ as SCOS_ACTIONS_NAME
from scos_actions import __version__ as SCOS_ACTIONS_VERSION
from scos_actions.calibration.calibration import Calibration
from scos_actions.hardware.sigan_iface import SignalAnalyzerInterface
Expand Down Expand Up @@ -64,6 +66,7 @@ def __init__(
self._capture_time = None
self._is_available = True
self._plugin_version = SCOS_ACTIONS_VERSION
self._plugin_name = SCOS_ACTIONS_NAME
self._firmware_version = "1.2.3"
self._api_version = "v1.2.3"

Expand All @@ -84,6 +87,11 @@ def is_available(self):
def plugin_version(self):
return self._plugin_version

@property
def plugin_name(self) -> str:
"""Returns the current package name of scos-actions."""
return self._plugin_name

@property
def firmware_version(self):
return self._firmware_version
Expand Down Expand Up @@ -212,7 +220,7 @@ def update_calibration(self, params):
def recompute_sensor_calibration_data(self, cal_args: list) -> None:
if self.sensor_calibration is not None:
self.sensor_calibration_data.update(
self._sensor_calibration.get_calibration_dict(cal_args)
self.sensor_calibration.get_calibration_dict(cal_args)
)
else:
logger.warning("Sensor calibration does not exist.")
Expand Down
6 changes: 6 additions & 0 deletions scos_actions/hardware/sigan_iface.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ def plugin_version(self) -> str:
"""Returns the version of the SCOS plugin defining this interface."""
pass

@property
@abstractmethod
def plugin_name(self) -> str:
"""Returns the name of the SCOS plugin defining this interface."""
pass

@property
def firmware_version(self) -> str:
"""Returns the version of the signal analyzer firmware."""
Expand Down
10 changes: 5 additions & 5 deletions scos_actions/hardware/tests/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@


def test_sensor():
sensor = Sensor(
signal_analyzer=MockSignalAnalyzer(), capabilities={}, gps=MockGPS()
)
sigan = MockSignalAnalyzer()
sensor = Sensor(signal_analyzer=sigan, capabilities={}, gps=MockGPS(sigan))
assert sensor is not None
assert sensor.signal_analyzer is not None
assert sensor.gps is not None
Expand All @@ -23,8 +22,9 @@ def test_set_get_preselector():


def test_set_get_gps():
gps = MockGPS()
sensor = Sensor(signal_analyzer=MockSignalAnalyzer(), capabilities={})
sigan = MockSignalAnalyzer()
gps = MockGPS(sigan)
sensor = Sensor(signal_analyzer=sigan, capabilities={})
sensor.gps = gps
assert sensor.gps == gps

Expand Down
5 changes: 0 additions & 5 deletions scos_actions/metadata/sigmf_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,11 +426,6 @@ def add_annotation(self, start_index, length, annotation_md):
start_index=start_index, length=length, metadata=annotation_md
)

def set_last_calibration_time(self, last_cal_time):
self.sigmf_md.set_global_field(
"ntia-sensor:calibration_datetime", last_cal_time
)

def add_to_global(self, key, value):
self.sigmf_md.set_global_field(key, value)

Expand Down
4 changes: 4 additions & 0 deletions scos_actions/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
logger.debug(f"scos-actions: RUNNING_TESTS:{RUNNING_TESTS}")
FQDN = env("FQDN", None)
logger.debug(f"scos-actions: FQDN:{FQDN}")
SIGAN_MODULE = env.str("SIGAN_MODULE", default="scos_actions.hardware.mocks.mock_sigan")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would only set the defaults for these when running tests.

logger.debug(f"scos-actions: SIGAN_MODULE:{SIGAN_MODULE}")
SIGAN_CLASS = env.str("SIGAN_CLASS", default="MockSignalAnalyzer")
logger.debug(f"scos-actions: SIGAN_CLASS:{SIGAN_CLASS}")
SIGAN_POWER_SWITCH = env("SIGAN_POWER_SWITCH", default=None)
logger.debug(f"scos-actions: SIGAN_POWER_SWITCH:{SIGAN_POWER_SWITCH}")
SIGAN_POWER_CYCLE_STATES = env("SIGAN_POWER_CYCLE_STATES", default=None)
Expand Down