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 45 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion scos_actions/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "9.0.0"
__version__ = "10.0.0"
39 changes: 25 additions & 14 deletions scos_actions/actions/acquire_sea_data_product.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,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 @@ -161,6 +162,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 @@ -643,9 +645,16 @@ def capture_iq(self, params: dict) -> dict:
duration_ms = utils.get_parameter(DURATION_MS, params)
nskip = utils.get_parameter(NUM_SKIP, params)
num_samples = int(params[SAMPLE_RATE] * duration_ms * 1e-3)
if logger.getEffectiveLevel() == logging.DEBUG:
for key, value in params.items():
logger.debug(f"param {key}={value}")
self.cal_adjust = True
if CAL_ADJUST in params:
self.cal_adjust = utils.get_parameter(CAL_ADJUST, params)
logger.debug(f"cal_adjust={self.cal_adjust}")
# Collect IQ data
measurement_result = self.sensor.acquire_time_domain_samples(
num_samples, nskip, cal_params=params
num_samples, nskip, cal_params=params, cal_adjust=self.cal_adjust
)
# Store some metadata with the IQ
measurement_result.update(params)
Expand Down Expand Up @@ -802,7 +811,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 @@ -1143,7 +1153,14 @@ def create_capture_segment(
datetime=measurement_result["capture_time"],
duration=measurement_result[DURATION_MS],
overload=measurement_result["overload"],
sensor_calibration=ntia_sensor.Calibration(
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=self.sensor.sensor_calibration_data["datetime"],
gain=round(measurement_result["applied_calibration"]["gain"], 3),
noise_figure=round(
Expand All @@ -1153,17 +1170,11 @@ def create_capture_segment(
self.sensor.sensor_calibration_data["temperature"], 1
),
reference=measurement_result["reference"],
),
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 "compression_point" in measurement_result["applied_calibration"]:
capture_segment.sensor_calibration.compression_point = measurement_result[
"applied_calibration"
]["compression_point"]
)
if "compression_point" in measurement_result["applied_calibration"]:
capture_segment.sensor_calibration.compression_point = (
measurement_result["applied_calibration"]["compression_point"]
)
self.sigmf_builder.add_capture(capture_segment)

def get_sigmf_builder(
Expand Down
2 changes: 0 additions & 2 deletions scos_actions/actions/calibrate_y_factor.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,14 +231,12 @@ def __call__(self, sensor: Sensor, schedule_entry: dict, task_id: int):
logger.debug(f"cal_params: {cal_params}")
cal_data = dict()
last_cal_datetime = get_datetime_str_now()
clock_rate_lookup_by_sample_rate = []
self.sensor.sensor_calibration = SensorCalibration(
calibration_parameters=cal_params,
calibration_data=cal_data,
calibration_reference=onboard_cal_reference,
file_path=Path(env("ONBOARD_CALIBRATION_FILE")),
last_calibration_datetime=last_cal_datetime,
clock_rate_lookup_by_sample_rate=clock_rate_lookup_by_sample_rate,
sensor_uid=sensor_uid,
)
elif self.sensor.sensor_calibration.file_path == env(
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 @@ -59,15 +59,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
7 changes: 4 additions & 3 deletions scos_actions/actions/interfaces/measurement_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,16 @@ def get_calibration(self, measurement_result: dict) -> ntia_sensor.Calibration:
noise_figure=round(
measurement_result["applied_calibration"]["noise_figure"], 3
),
temperature=round(
self.sensor.sensor_calibration_data["temperature"], 1
),
reference=measurement_result["reference"],
)
if "compression_point" in measurement_result["applied_calibration"]:
cal_meta.compression_point = measurement_result["applied_calibration"][
"compression_point"
]
if "temperature" in self.sensor.sensor_calibration_data:
cal_meta.temperature = round(
self.sensor.sensor_calibration_data["temperature"], 1
)
return cal_meta

def create_metadata(
Expand Down
4 changes: 2 additions & 2 deletions scos_actions/actions/sync_gps.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ def __init__(self, parameters: dict):
def __call__(self, sensor: Sensor, schedule_entry: dict, task_id: int):
logger.debug("Syncing to GPS")
self.sensor = sensor
dt = self.sensor.gps.get_gps_time()
dt = self.sensor.gps.get_gps_time(self.sensor)
date_cmd = ["date", "-s", "{:}".format(dt.strftime("%Y/%m/%d %H:%M:%S"))]
subprocess.check_output(date_cmd, shell=True)
logger.info(f"Set system time to GPS time {dt.ctime()}")

location = sensor.gps.get_location()
location = sensor.gps.get_location(self.sensor)
if location is None:
raise RuntimeError("Unable to synchronize to GPS")

Expand Down
8 changes: 0 additions & 8 deletions scos_actions/calibration/sensor_calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,8 @@ class provides an implementation for the update method to allow calibration
"""

last_calibration_datetime: str
clock_rate_lookup_by_sample_rate: List[Dict[str, float]]
sensor_uid: str

def get_clock_rate(self, sample_rate: Union[float, int]) -> Union[float, int]:
"""Find the clock rate (Hz) using the given sample_rate (samples per second)"""
for mapping in self.clock_rate_lookup_by_sample_rate:
if mapping["sample_rate"] == sample_rate:
return mapping["clock_frequency"]
return sample_rate

def update(
self,
params: dict,
Expand Down
1 change: 0 additions & 1 deletion scos_actions/calibration/tests/test_calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ def test_to_and_from_json(self, tmp_path: Path):
"testing",
tmp_path / "testing.json",
"dt_str",
[],
"uid",
)
sensor_cal.to_json()
Expand Down
22 changes: 0 additions & 22 deletions scos_actions/calibration/tests/test_sensor_calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,6 @@ def setup_calibration_file(self, tmp_path: Path):
cal_data["sensor_uid"] = "SAMPLE_CALIBRATION"
cal_data["calibration_reference"] = "TESTING"

# Add SR/CF lookup table
cal_data["clock_rate_lookup_by_sample_rate"] = []
for sr in self.sample_rates:
cr = sr
while cr <= 40e6:
cr *= 2
cr /= 2
cal_data["clock_rate_lookup_by_sample_rate"].append(
{"sample_rate": int(sr), "clock_frequency": int(cr)}
)

# Create the JSON architecture for the calibration data
cal_data["calibration_data"] = {}
cal_data["calibration_parameters"] = ["sample_rate", "frequency", "gain"]
Expand Down Expand Up @@ -151,7 +140,6 @@ def test_sensor_calibration_dataclass_fields(self):
# Note: does not check field order
assert fields == {
"last_calibration_datetime": str,
"clock_rate_lookup_by_sample_rate": List[Dict[str, float]],
"sensor_uid": str,
}

Expand All @@ -167,13 +155,6 @@ def test_field_validator(self):
[], {}, False, Path(""), datetime.datetime.now(), [], "uid"
)

def test_get_clock_rate(self):
"""Test the get_clock_rate method"""
# Test getting a clock rate by sample rate
assert self.sample_cal.get_clock_rate(10e6) == 40e6
# If there isn't an entry, the sample rate should be returned
assert self.sample_cal.get_clock_rate(-999) == -999

def test_get_calibration_dict_exact_match_lookup(self):
calibration_datetime = get_datetime_str_now()
calibration_params = ["sample_rate", "frequency"]
Expand All @@ -187,7 +168,6 @@ def test_get_calibration_dict_exact_match_lookup(self):
calibration_reference="testing",
file_path=Path(""),
last_calibration_datetime=calibration_datetime,
clock_rate_lookup_by_sample_rate=[],
sensor_uid="TESTING",
)
cal_data = cal.get_calibration_dict({"sample_rate": 100.0, "frequency": 200.0})
Expand All @@ -206,7 +186,6 @@ def test_get_calibration_dict_within_range(self):
calibration_reference="testing",
file_path=Path("test_calibration.json"),
last_calibration_datetime=calibration_datetime,
clock_rate_lookup_by_sample_rate=[],
sensor_uid="TESTING",
)

Expand Down Expand Up @@ -234,7 +213,6 @@ def test_update(self):
calibration_reference="testing",
file_path=test_cal_path,
last_calibration_datetime=calibration_datetime,
clock_rate_lookup_by_sample_rate=[],
sensor_uid="TESTING",
)
action_params = {"sample_rate": 100.0, "frequency": 200.0}
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ stepped_frequency_time_domain_iq:
nskip: 15.36e4
calibration_adjust: False
classification: UNCLASSIFIED
rf_path: antenna
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ single_frequency_time_domain_iq:
nskip: 15.36e4
calibration_adjust: False
classification: UNCLASSIFIED
rf_path: antenna
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ single_frequency_fft:
nskip: 15.36e4
calibration_adjust: False
classification: UNCLASSIFIED
rf_path: antenna
1 change: 1 addition & 0 deletions scos_actions/configs/actions/test_survey_iq_action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ stepped_frequency_time_domain_iq:
- 10000
nskip: 15.36e4
calibration_adjust: False
rf_path: antenna
32 changes: 18 additions & 14 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 @@ -31,6 +23,18 @@ def init(
return yaml_actions, yaml_test_actions


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"
):
yaml_actions, yaml_test_actions = init()
actions.update(yaml_actions)
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)
6 changes: 4 additions & 2 deletions scos_actions/hardware/gps_iface.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@

class GPSInterface(ABC):
@abstractmethod
def get_location(self, timeout_s=1):
def get_location(
self, sensor: "scos_actions.hardware.sensor.Sensor", timeout_s: float = 1
):
pass

@abstractmethod
def get_gps_time(self):
def get_gps_time(self, sensor: "scos_actions.hardware.sensor.Sensor"):
pass
5 changes: 3 additions & 2 deletions scos_actions/hardware/mocks/mock_gps.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@


class MockGPS(GPSInterface):
def get_location(timeout_s=1):

def get_location(self, sensor, timeout_s=1):
logger.warning("Using mock GPS!")
return 39.995118, -105.261572, 1651.0

def get_gps_time(self):
def get_gps_time(self, sensor):
logger.warning("Using mock GPS!")
return datetime.now()
Loading
Loading