Skip to content

Commit 1525fa3

Browse files
Merge pull request #14 from NTIA/fix-status-parser
Fix status parser for direct streaming mode
2 parents 17c88d5 + 4f31eb9 commit 1525fa3

File tree

4 files changed

+141
-45
lines changed

4 files changed

+141
-45
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ repos:
1919
rev: v2.34.0
2020
hooks:
2121
- id: pyupgrade
22+
args: ["--py3-plus"]
2223
- repo: https://github.com/pycqa/isort
23-
rev: 5.10.1
24+
rev: 5.12.0
2425
hooks:
2526
- id: isort
2627
name: isort (python)

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ tasks. These "helper functions" include:
9595
- `IQBLK_Acquire()`
9696
- `IQBLK_Configure()`
9797
- `SPECTRUM_Acquire()`
98-
- `IQSTREAM_StatusParser()`
98+
- `IQSTREAMFileInfo_StatusParser()`
99+
- `IQSTREAMIQInfo_StatusParser()`
99100
- `IQSTREAM_Tempfile()`
100101
- `IQSTREAM_Tempfile_NoConfig()`
101102
- `DEVICE_SearchAndConnect()`
@@ -108,7 +109,18 @@ To read more about these functions, check their docstrings with `help()`.
108109
Known issues exist in the underlying Tektronix RSA API for Linux, and therefore this
109110
wrapper is limited in certain ways. The list of known issues is provided by Tektronix in
110111
the [Tektronix RSA API for Linux release notes](https://download.tek.com/software/supporting_files/ReleaseNotes_1_0_0014_64bit_066207701.txt)
111-
(up-to-date as of version 1.0.0014):
112+
(up-to-date as of version 1.0.0014).
113+
114+
### TODO: Update this section after resolving
115+
116+
Additionally, a known issue exists with parsing IQ streaming status data structures.
117+
There appears to be a discrepancy between the documented status message encoding scheme
118+
and the implemented encoding scheme. In its current implementation, this API wrapper has
119+
been tested to ensure that ADC overrange events are properly flagged when using
120+
`IQSTREAM_Tempfile`, `IQSTREAM_Tempfile_NoConfig` or `IQSTREAM_Acquire` methods. Buffer
121+
overflow warnings and errors should work, but have not been tested. The USB data
122+
discontinuity status is unable to be parsed. Unknown IQ stream status codes are treated
123+
as errors and handled as configured in `IQSTREAM_StatusParser`.
112124

113125
## Development
114126

src/rsa_api/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from .rsa_api import *
22

3-
__version__ = "1.3.0"
3+
__version__ = "1.3.1"

src/rsa_api/rsa_api.py

Lines changed: 124 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@
2626
200 # Max. characters in frequency reference user setting string
2727
)
2828
_DEVINFO_MAX_STRLEN = 19 # Datetime substring length in user setting string
29-
_IQSTREAM_MAX_TRIGGERCOUNT = (
30-
100 # Max. number of trigger events that can be reported by IQSTREAM_GetIQData
31-
)
29+
3230
# ENUMERATION TUPLES
3331

3432
_DEV_EVENT = ("OVERRANGE", "TRIGGER", "1PPS")
@@ -121,8 +119,8 @@ class _IQStreamFileInfo(Structure):
121119
class _IQStreamIQInfo(Structure):
122120
_fields_ = [
123121
("timestamp", c_uint64),
124-
("triggerCount", c_int32),
125-
("triggerIndices", c_int32 * _IQSTREAM_MAX_TRIGGERCOUNT),
122+
("triggerCount", c_int),
123+
("triggerIndices", POINTER(c_int)),
126124
("scaleFactor", c_double),
127125
("acqStatus", c_uint32),
128126
]
@@ -135,7 +133,7 @@ class RSAError(Exception):
135133
def __init__(self, err_txt=""):
136134
self.err_txt = err_txt
137135
err = "RSA Error: {}".format(self.err_txt)
138-
super(RSAError, self).__init__(err)
136+
super().__init__(err)
139137

140138

141139
class RSA:
@@ -2188,7 +2186,7 @@ def IQSTREAM_Tempfile_NoConfig(
21882186
IQ data, with each element in the form (I + j*Q)
21892187
iq_status : str (optional)
21902188
The status string for the IQ capture, as defined in
2191-
the documentation for IQSTREAM_StatusParser().
2189+
the documentation for IQSTREAMFileInfo_StatusParser().
21922190
"""
21932191
# Configuration parameters
21942192
dest = _IQS_OUT_DEST[3] # Split SIQ format
@@ -2224,7 +2222,7 @@ def IQSTREAM_Tempfile_NoConfig(
22242222

22252223
# Check acquisition status
22262224
file_info = self.IQSTREAM_GetDiskFileInfo()
2227-
iq_status = self.IQSTREAM_StatusParser(file_info, not return_status)
2225+
iq_status = self.IQSTREAMFileInfo_StatusParser(file_info, not return_status)
22282226

22292227
self.DEVICE_Stop()
22302228

@@ -2277,7 +2275,7 @@ def IQSTREAM_Tempfile(
22772275
IQ data, with each element in the form (I + j*Q)
22782276
iq_status : str (optional)
22792277
The status code for the IQ capture, as defined in
2280-
the documentation for IQSTREAM_StatusParser().
2278+
the documentation for IQSTREAMFileInfo_StatusParser().
22812279
"""
22822280
# Configuration parameters
22832281
dest = _IQS_OUT_DEST[3] # Split SIQ format
@@ -2316,7 +2314,7 @@ def IQSTREAM_Tempfile(
23162314

23172315
# Check acquisition status
23182316
file_info = self.IQSTREAM_GetDiskFileInfo()
2319-
iq_status = self.IQSTREAM_StatusParser(file_info, not return_status)
2317+
iq_status = self.IQSTREAMFileInfo_StatusParser(file_info, not return_status)
23202318

23212319
self.DEVICE_Stop()
23222320

@@ -2337,27 +2335,26 @@ def IQSTREAM_Tempfile(
23372335
return iq_data
23382336

23392337
@staticmethod
2340-
def IQSTREAM_StatusParser(
2341-
iq_stream_info: Union[_IQStreamFileInfo, _IQStreamIQInfo], exit: bool = True
2342-
):
2343-
"""
2344-
Parse _IQStreamFileInfo or _IQStreamIQInfo to get acquisition status.
2345-
2346-
Depending on the 'exit' parameter, this method will either raise an
2347-
error, or return a status string. Possible values for the
2348-
returned status indicator are:
2349-
2350-
status | Definition
2351-
-------------------
2352-
0 | No error
2353-
1 | Input overrange.
2354-
2 | USB data stream discontinuity.
2355-
3 | Input buffer > 75% full.
2356-
4 | Input buffer overflow. IQ Stream processing
2357-
| too slow. Data loss has occurred.
2358-
5 | Output buffer > 75% full.
2359-
6 | Output buffer overflow. File writing
2360-
| too slow. Data loss has occurred.
2338+
def IQSTREAMFileInfo_StatusParser(
2339+
iq_stream_info: _IQStreamFileInfo, exit: bool = True
2340+
) -> Union[None, str]:
2341+
"""
2342+
Parse an _IQStreamFileInfo struct to get the acquisition status.
2343+
2344+
Depending on the ``exit`` parameter, this method will either raise an
2345+
error or return a status string. Possible values for the
2346+
returned status string (when ``exit`` is False):
2347+
2348+
- No error
2349+
- Input overrange.
2350+
- USB data stream discontinuity.
2351+
- Input buffer > 75% full.
2352+
- Input buffer overflow. IQ Stream processing
2353+
too slow. Data loss has occurred.
2354+
- Output buffer > 75% full.
2355+
- Output buffer overflow. File writing
2356+
too slow. Data loss has occurred.
2357+
- Invalid status code returned. Some always-zero bits are nonzero.
23612358
23622359
In the case of multiple status codes being returned, the status
23632360
string will contain all returned status strings, separated by line
@@ -2368,8 +2365,10 @@ def IQSTREAM_StatusParser(
23682365
iq_stream_info : _IQStreamFileInfo
23692366
The IQ streaming status information structure.
23702367
exit : bool
2371-
If True, raise an exception for any error status in the IQ stream.
2372-
If False, return a flag representing the error, without raising an exception.
2368+
If True, raise an exception for any error or warning status in the
2369+
IQ stream. Return None if there is no error or warning.
2370+
If False, return a string indicating the status, without raising
2371+
an exception.
23732372
23742373
Returns
23752374
-------
@@ -2379,21 +2378,22 @@ def IQSTREAM_StatusParser(
23792378
Raises
23802379
------
23812380
RSAError
2382-
If errors have occurred during IQ streaming, and exit is True.
2381+
If errors or warnings have occurred during IQ streaming, and
2382+
``exit`` is True.
23832383
"""
23842384
status = iq_stream_info.acqStatus
2385+
status_str = ""
23852386

23862387
# Handle no error case
23872388
if status == 0:
23882389
if exit:
23892390
return
23902391
else:
2391-
return "No error."
2392+
status_str += "No error."
23922393
else:
23932394
# Construct status string if status != 0
2394-
status_str = ""
23952395
if bool(status & 0x10000): # mask bit 16
2396-
status_str += "Input overrange\n."
2396+
status_str += "Input overrange.\n"
23972397
if bool(status & 0x20000): # mask bit 17
23982398
status_str += "USB data stream discontinuity.\n"
23992399
if bool(status & 0x40000): # mask bit 18
@@ -2406,12 +2406,95 @@ def IQSTREAM_StatusParser(
24062406
if bool(status & 0x200000): # mask bit 21
24072407
status_str += "Output buffer overflow. File writing too slow, "
24082408
status_str += "data loss has occurred.\n"
2409+
if bool(status & 0xFFC00000):
2410+
status_str += (
2411+
"Invalid status code returned. Some always-zero bits are nonzero."
2412+
)
24092413
if exit:
24102414
# Raise error with full string if configured
24112415
raise RSAError(status_str)
2416+
return status_str
2417+
2418+
@staticmethod
2419+
def IQSTREAMIQInfo_StatusParser(
2420+
iq_stream_info: _IQStreamIQInfo, exit: bool = True
2421+
) -> Union[None, str]:
2422+
"""
2423+
Parse an _IQStreamIQInfo struct to get the acquisition status.
2424+
2425+
Depending on the ``exit`` parameter, this method will either raise an
2426+
error or return a status string. Possible values for the
2427+
returned status string (when ``exit`` is False):
2428+
2429+
- No error
2430+
- Input overrange.
2431+
- USB data stream discontinuity.
2432+
- Input buffer > 75% full.
2433+
- Input buffer overflow. IQ Stream processing
2434+
too slow. Data loss has occurred.
2435+
- Output buffer > 75% full.
2436+
- Output buffer overflow. File writing
2437+
too slow. Data loss has occurred.
2438+
- Invalid status code returned. Some always-zero bits are nonzero.
2439+
2440+
In the case of multiple status codes being returned, the status
2441+
string will contain all returned status strings, separated by line
2442+
breaks.
2443+
2444+
Parameters
2445+
----------
2446+
iq_stream_info : _IQStreamIQInfo
2447+
The IQ streaming status information structure.
2448+
exit : bool
2449+
If True, raise an exception for any error or warning status in the
2450+
IQ stream. Return None if there is no error or warning.
2451+
If False, return a string indicating the status, without raising
2452+
an exception.
2453+
2454+
Returns
2455+
-------
2456+
status: str
2457+
A string containing all returned status messages.
2458+
2459+
Raises
2460+
------
2461+
RSAError
2462+
If errors or warnings have occurred during IQ streaming, and
2463+
``exit`` is True.
2464+
"""
2465+
status = iq_stream_info.acqStatus
2466+
status_str = ""
2467+
2468+
# Handle no error case
2469+
if status == 0:
2470+
if exit:
2471+
return
24122472
else:
2413-
# Or just return the status string
2414-
return status_str
2473+
status_str += "No error."
2474+
else:
2475+
# Construct status string if status != 0
2476+
if bool(status & 0x10000): # mask bit 16
2477+
status_str += "Input overrange.\n"
2478+
if bool(status & 0x20000): # mask bit 17
2479+
status_str += "USB data stream discontinuity.\n"
2480+
if bool(status & 0x40000): # mask bit 18
2481+
status_str += "Input buffer > 75{} full.\n".format("%")
2482+
if bool(status & 0x80000): # mask bit 19
2483+
status_str += "Input buffer overflow. IQStream processing too"
2484+
status_str += " slow, data loss has occurred.\n"
2485+
if bool(status & 0x100000): # mask bit 20
2486+
status_str += "Output buffer > 75{} full.\n".format("%")
2487+
if bool(status & 0x200000): # mask bit 21
2488+
status_str += "Output buffer overflow. File writing too slow, "
2489+
status_str += "data loss has occurred.\n"
2490+
if bool(status & 0xFFC00000):
2491+
status_str += (
2492+
"Invalid status code returned. Some always-zero bits are nonzero."
2493+
)
2494+
if exit:
2495+
# Raise error with full string if configured
2496+
raise RSAError(status_str)
2497+
return f"{status_str}, {status=}"
24152498

24162499
def SPECTRUM_Acquire(
24172500
self, trace: str = "Trace1", trace_points: int = 801, timeout_msec: int = 50
@@ -2564,7 +2647,7 @@ def IQSTREAM_Acquire(
25642647
IQ data, with each element in the form (I + j*Q)
25652648
iq_status : str (optional)
25662649
The status string for the IQ capture, as defined in
2567-
the documentation for IQSTREAM_StatusParser().
2650+
the documentation for IQSTREAMIQInfo_StatusParser().
25682651
"""
25692652
dest = _IQS_OUT_DEST[0] # Client
25702653
dtype = _IQS_OUT_DTYPE[0] # Single
@@ -2618,7 +2701,7 @@ def IQSTREAM_Acquire(
26182701
assert len(iqdata) == iq_samples_requested
26192702

26202703
if return_status:
2621-
iq_status = self.IQSTREAM_StatusParser(iqinfo, not return_status)
2704+
iq_status = self.IQSTREAMIQInfo_StatusParser(iqinfo, not return_status)
26222705
return iqdata, iq_status
26232706
else:
26242707
return iqdata

0 commit comments

Comments
 (0)