Skip to content

Commit c19fdac

Browse files
authored
Add float waveform support to the Python API (#166)
* Add float waveform support to the Python API Signed-off-by: Michael Johansen <michael.johansen@emerson.com> * Fix styleguide issue Signed-off-by: Michael Johansen <michael.johansen@emerson.com> * Address PR feedback. Fix docstring. Add float spectrum acceptance test. Fix acceptance test names. Fix unit tests. Signed-off-by: Michael Johansen <michael.johansen@emerson.com> --------- Signed-off-by: Michael Johansen <michael.johansen@emerson.com>
1 parent 2916add commit c19fdac

7 files changed

Lines changed: 227 additions & 28 deletions

File tree

poetry.lock

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ requires-poetry = '>=2.1,<3.0'
4040
[tool.poetry.dependencies]
4141
python = "^3.10"
4242
protobuf = {version=">=4.21"}
43-
ni-measurements-data-v1-client = { version = ">=1.1.0", allow-prereleases = true }
43+
ni-measurements-data-v1-client = { version = ">=1.2.0.dev0", allow-prereleases = true }
4444
ni-measurements-metadata-v1-client = { version = ">=1.0.0" }
45-
ni-protobuf-types = { version = ">=1.2.0", allow-prereleases = true }
45+
ni-protobuf-types = { version = ">=1.3.0.dev0", allow-prereleases = true }
4646
hightime = { version = ">=1.0.0" }
4747

4848
[tool.poetry.group.dev.dependencies]

src/ni/datastore/data/_data_store_client.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -227,12 +227,15 @@ def publish_measurement(
227227
228228
- Scalar: Single float, int, str or boolean
229229
- Vector: Array of float, int, str or boolean values
230-
- DoubleAnalogWaveform: Analog waveform with double precision
230+
- AnalogWaveform[np.float64]: Analog waveform with double precision
231+
- AnalogWaveform[np.float32]: Analog waveform with single precision
231232
- DoubleXYData: XY coordinate data with double precision
232-
- I16AnalogWaveform: Analog waveform with 16-bit integer precision
233-
- DoubleComplexWaveform: Complex waveform with double precision
234-
- I16ComplexWaveform: Complex waveform with 16-bit integer precision
235-
- DoubleSpectrum: Frequency spectrum data with double precision
233+
- AnalogWaveform[np.int16]: Analog waveform with 16-bit integer precision
234+
- ComplexWaveform[np.complex128]: Complex waveform with double precision
235+
- ComplexWaveform[np.complex64]: Complex waveform with single precision
236+
- ComplexWaveform[ComplexInt32DType]: Complex waveform with 16-bit integer precision
237+
- Spectrum[np.float64]: Frequency spectrum data with double precision
238+
- Spectrum[np.float32]: Frequency spectrum data with single precision
236239
- DigitalWaveform: Digital waveform data
237240
238241
step_id: The ID of the step associated with this measurement. This

src/ni/datastore/data/_grpc_conversion.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@
2525
from ni.protobuf.types.waveform_conversion import (
2626
digital_waveform_from_protobuf,
2727
digital_waveform_to_protobuf,
28+
float32_analog_waveform_from_protobuf,
29+
float32_analog_waveform_to_protobuf,
30+
float32_complex_waveform_from_protobuf,
31+
float32_complex_waveform_to_protobuf,
32+
float32_spectrum_from_protobuf,
33+
float32_spectrum_to_protobuf,
2834
float64_analog_waveform_from_protobuf,
2935
float64_analog_waveform_to_protobuf,
3036
float64_complex_waveform_from_protobuf,
@@ -241,6 +247,10 @@ def populate_publish_measurement_request_value(
241247
publish_request.double_analog_waveform.CopyFrom(
242248
float64_analog_waveform_to_protobuf(value)
243249
)
250+
elif value.dtype == np.float32:
251+
publish_request.float_analog_waveform.CopyFrom(
252+
float32_analog_waveform_to_protobuf(value)
253+
)
244254
elif value.dtype == np.int16:
245255
publish_request.i16_analog_waveform.CopyFrom(int16_analog_waveform_to_protobuf(value))
246256
else:
@@ -250,13 +260,19 @@ def populate_publish_measurement_request_value(
250260
publish_request.double_complex_waveform.CopyFrom(
251261
float64_complex_waveform_to_protobuf(value)
252262
)
263+
elif value.dtype == np.complex64:
264+
publish_request.float_complex_waveform.CopyFrom(
265+
float32_complex_waveform_to_protobuf(value)
266+
)
253267
elif value.dtype == ComplexInt32DType:
254268
publish_request.i16_complex_waveform.CopyFrom(int16_complex_waveform_to_protobuf(value))
255269
else:
256270
raise TypeError(f"Unsupported ComplexWaveform dtype: {value.dtype}")
257271
elif isinstance(value, Spectrum):
258272
if value.dtype == np.float64:
259273
publish_request.double_spectrum.CopyFrom(float64_spectrum_to_protobuf(value))
274+
elif value.dtype == np.float32:
275+
publish_request.float_spectrum.CopyFrom(float32_spectrum_to_protobuf(value))
260276
else:
261277
raise TypeError(f"Unsupported Spectrum dtype: {value.dtype}")
262278
elif isinstance(value, DigitalWaveform):
@@ -338,10 +354,16 @@ def convert_read_measurement_response_from_protobuf(
338354
return digital_waveform_from_protobuf(response.digital_waveform)
339355
elif read_data_type == "double_analog_waveform":
340356
return float64_analog_waveform_from_protobuf(response.double_analog_waveform)
357+
elif read_data_type == "float_analog_waveform":
358+
return float32_analog_waveform_from_protobuf(response.float_analog_waveform)
341359
elif read_data_type == "double_complex_waveform":
342360
return float64_complex_waveform_from_protobuf(response.double_complex_waveform)
361+
elif read_data_type == "float_complex_waveform":
362+
return float32_complex_waveform_from_protobuf(response.float_complex_waveform)
343363
elif read_data_type == "double_spectrum":
344364
return float64_spectrum_from_protobuf(response.double_spectrum)
365+
elif read_data_type == "float_spectrum":
366+
return float32_spectrum_from_protobuf(response.float_spectrum)
345367
elif read_data_type == "i16_analog_waveform":
346368
return int16_analog_waveform_from_protobuf(response.i16_analog_waveform)
347369
elif read_data_type == "i16_complex_waveform":

tests/acceptance/test_publish_measurement_and_read_data.py

Lines changed: 82 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,11 @@ def test___publish_xydata___read_measurement_value_returns_xydata(
8888
assert xydata == expected_xydata
8989

9090

91-
def test___publish_spectrum___read_measurement_value_returns_spectrum(
91+
def test___publish_float64_spectrum___read_measurement_value_returns_spectrum(
9292
acceptance_test_context: DataStoreContext,
9393
) -> None:
9494
with DataStoreClient() as data_store_client:
95-
step_id = _create_step(data_store_client, "spectrum")
95+
step_id = _create_step(data_store_client, "float64 spectrum")
9696
expected_spectrum = Spectrum.from_array_1d(
9797
array=[1.0, 10.0, 100.0],
9898
dtype=np.float64,
@@ -101,7 +101,7 @@ def test___publish_spectrum___read_measurement_value_returns_spectrum(
101101
)
102102

103103
published_measurement_id = data_store_client.publish_measurement(
104-
name="python publish spectrum",
104+
name="python publish float64 spectrum",
105105
value=expected_spectrum,
106106
step_id=step_id,
107107
)
@@ -113,19 +113,69 @@ def test___publish_spectrum___read_measurement_value_returns_spectrum(
113113
assert spectrum == expected_spectrum
114114

115115

116-
def test___publish_analog_waveform___read_measurement_value_returns_analog_waveform(
116+
def test___publish_float32_spectrum___read_measurement_value_returns_spectrum(
117117
acceptance_test_context: DataStoreContext,
118118
) -> None:
119119
with DataStoreClient() as data_store_client:
120-
step_id = _create_step(data_store_client, "analog waveform")
120+
step_id = _create_step(data_store_client, "float32 spectrum")
121+
expected_spectrum = Spectrum.from_array_1d(
122+
array=[1.0, 10.0, 100.0],
123+
dtype=np.float32,
124+
start_frequency=1.0,
125+
frequency_increment=1.0,
126+
)
127+
128+
published_measurement_id = data_store_client.publish_measurement(
129+
name="python publish spectrum float",
130+
value=expected_spectrum,
131+
step_id=step_id,
132+
)
133+
134+
published_measurement = data_store_client.get_measurement(published_measurement_id)
135+
spectrum = data_store_client.read_measurement_value(
136+
published_measurement, expected_type=Spectrum
137+
)
138+
assert spectrum == expected_spectrum
139+
140+
141+
def test___publish_float64_analog_waveform___read_measurement_value_returns_float64_analog_waveform(
142+
acceptance_test_context: DataStoreContext,
143+
) -> None:
144+
with DataStoreClient() as data_store_client:
145+
step_id = _create_step(data_store_client, "float64 analog waveform")
121146
expected_waveform = AnalogWaveform(
122147
sample_count=3,
123148
raw_data=np.array([1.0, 2.0, 3.0]),
124149
timing=Timing(SampleIntervalMode.NONE, time_offset=ht.timedelta()),
125150
)
126151

127152
published_measurement_id = data_store_client.publish_measurement(
128-
name="python publish analog waveform",
153+
name="python publish float64 analog waveform",
154+
value=expected_waveform,
155+
step_id=step_id,
156+
)
157+
158+
published_measurement = data_store_client.get_measurement(published_measurement_id)
159+
waveform = data_store_client.read_measurement_value(
160+
published_measurement, expected_type=AnalogWaveform
161+
)
162+
assert waveform == expected_waveform
163+
164+
165+
def test___publish_float32_analog_waveform___read_measurement_value_returns_float32_analog_waveform(
166+
acceptance_test_context: DataStoreContext,
167+
) -> None:
168+
with DataStoreClient() as data_store_client:
169+
step_id = _create_step(data_store_client, "float32 analog waveform")
170+
expected_waveform = AnalogWaveform(
171+
sample_count=3,
172+
dtype=np.float32,
173+
raw_data=np.array([1.0, 2.0, 3.0], dtype=np.float32),
174+
timing=Timing(SampleIntervalMode.NONE, time_offset=ht.timedelta()),
175+
)
176+
177+
published_measurement_id = data_store_client.publish_measurement(
178+
name="python publish float32 analog waveform",
129179
value=expected_waveform,
130180
step_id=step_id,
131181
)
@@ -159,17 +209,40 @@ def test___publish_digital_waveform___read_measurement_value_returns_digital_wav
159209
assert waveform == expected_waveform
160210

161211

162-
def test___publish_complex_waveform___read_measurement_value_returns_complex_waveform(
212+
def test___publish_float64_complex_waveform___read_measurement_value_returns_float64_complex_waveform(
213+
acceptance_test_context: DataStoreContext,
214+
) -> None:
215+
with DataStoreClient() as data_store_client:
216+
step_id = _create_step(data_store_client, "float64 complex waveform")
217+
expected_waveform = ComplexWaveform(
218+
10,
219+
timing=Timing(SampleIntervalMode.NONE, time_offset=ht.timedelta()),
220+
)
221+
published_measurement_id = data_store_client.publish_measurement(
222+
name="python publish float64 complex waveform",
223+
value=expected_waveform,
224+
step_id=step_id,
225+
)
226+
227+
published_measurement = data_store_client.get_measurement(published_measurement_id)
228+
waveform = data_store_client.read_measurement_value(
229+
published_measurement, expected_type=ComplexWaveform
230+
)
231+
assert waveform == expected_waveform
232+
233+
234+
def test___publish_float32_complex_waveform___read_measurement_value_returns_float32_complex_waveform(
163235
acceptance_test_context: DataStoreContext,
164236
) -> None:
165237
with DataStoreClient() as data_store_client:
166-
step_id = _create_step(data_store_client, "complex waveform")
238+
step_id = _create_step(data_store_client, "float32 complex waveform")
167239
expected_waveform = ComplexWaveform(
168240
10,
241+
dtype=np.complex64,
169242
timing=Timing(SampleIntervalMode.NONE, time_offset=ht.timedelta()),
170243
)
171244
published_measurement_id = data_store_client.publish_measurement(
172-
name="python publish complex waveform",
245+
name="python publish float32 complex waveform",
173246
value=expected_waveform,
174247
step_id=step_id,
175248
)

tests/unit/data/test_grpc_conversion.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,17 @@ def test___python_float64_analog_waveform___populate_measurement___measurement_u
186186
assert list(request.double_analog_waveform.y_data) == [0.0, 0.0, 0.0]
187187

188188

189+
def test___python_float32_analog_waveform___populate_measurement___measurement_updated_correctly() -> (
190+
None
191+
):
192+
wfm_obj = AnalogWaveform(3, np.float32)
193+
request = PublishMeasurementRequest()
194+
populate_publish_measurement_request_value(request, wfm_obj)
195+
196+
assert isinstance(request.float_analog_waveform, waveform_pb2.FloatAnalogWaveform)
197+
assert list(request.float_analog_waveform.y_data) == [0.0, 0.0, 0.0]
198+
199+
189200
def test___python_int16_analog_waveform___populate_measurement___measurement_updated_correctly() -> (
190201
None
191202
):
@@ -208,6 +219,17 @@ def test___python_float64_complex_waveform___populate_measurement___measurement_
208219
assert list(request.double_complex_waveform.y_data) == [0.0, 0.0, 0.0, 0.0]
209220

210221

222+
def test___python_float32_complex_waveform___populate_measurement___measurement_updated_correctly() -> (
223+
None
224+
):
225+
wfm_obj = ComplexWaveform(2, np.complex64)
226+
request = PublishMeasurementRequest()
227+
populate_publish_measurement_request_value(request, wfm_obj)
228+
229+
assert isinstance(request.float_complex_waveform, waveform_pb2.FloatComplexWaveform)
230+
assert list(request.float_complex_waveform.y_data) == [0.0, 0.0, 0.0, 0.0]
231+
232+
211233
def test___python_int16_complex_waveform___populate_measurement___measurement_updated_correctly() -> (
212234
None
213235
):
@@ -261,6 +283,20 @@ def test___python_float64_spectrum___populate_measurement___measurement_updated_
261283
assert request.double_spectrum.frequency_increment == 10.0
262284

263285

286+
def test___python_float32_spectrum___populate_measurement___measurement_updated_correctly() -> None:
287+
spectrum = Spectrum.from_array_1d(np.array([1.0, 2.0, 3.0]), dtype=np.float32)
288+
spectrum.start_frequency = 100.0
289+
spectrum.frequency_increment = 10.0
290+
291+
request = PublishMeasurementRequest()
292+
populate_publish_measurement_request_value(request, spectrum)
293+
294+
assert isinstance(request.float_spectrum, waveform_pb2.FloatSpectrum)
295+
assert list(request.float_spectrum.data) == [1.0, 2.0, 3.0]
296+
assert request.float_spectrum.start_frequency == 100.0
297+
assert request.float_spectrum.frequency_increment == 10.0
298+
299+
264300
def test___python_float64_xydata___populate_measurement___measurement_updated_correctly() -> None:
265301
xydata = XYData.from_arrays_1d(
266302
[1.0, 2.0], [3.0, 4.0], np.float64, x_units="Volts", y_units="Seconds"

0 commit comments

Comments
 (0)