sofar 1.1.4__py2.py3-none-any.whl → 1.2.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. sofar/__init__.py +4 -4
  2. sofar/io.py +18 -10
  3. sofar/sofa.py +42 -25
  4. sofar/sofa_conventions/conventions/AnnotatedEmitterAudio_0.2.csv +46 -0
  5. sofar/sofa_conventions/conventions/AnnotatedEmitterAudio_0.2.json +353 -0
  6. sofar/sofa_conventions/conventions/AnnotatedReceiverAudio_0.2.csv +46 -0
  7. sofar/sofa_conventions/conventions/AnnotatedReceiverAudio_0.2.json +353 -0
  8. sofar/sofa_conventions/conventions/deprecated/AnnotatedEmitterAudio_0.1.csv +46 -0
  9. sofar/sofa_conventions/conventions/deprecated/AnnotatedEmitterAudio_0.1.json +351 -0
  10. sofar/sofa_conventions/conventions/deprecated/AnnotatedReceiverAudio_0.1.csv +46 -0
  11. sofar/sofa_conventions/conventions/deprecated/AnnotatedReceiverAudio_0.1.json +351 -0
  12. sofar/sofa_conventions/conventions/deprecated/SingleTrackedAudio_0.1.csv +47 -0
  13. sofar/sofa_conventions/conventions/deprecated/SingleTrackedAudio_0.1.json +366 -0
  14. sofar/sofa_conventions/conventions/deprecated/SingleTrackedAudio_0.2.csv +51 -0
  15. sofar/sofa_conventions/conventions/deprecated/SingleTrackedAudio_0.2.json +397 -0
  16. sofar/sofa_conventions/rules/deprecations.json +2 -1
  17. sofar/sofa_conventions/rules/rules.json +21 -2
  18. sofar/sofa_conventions/rules/upgrade.json +36 -0
  19. sofar/sofastream.py +296 -0
  20. sofar/update_conventions.py +108 -85
  21. sofar/utils.py +1 -1
  22. {sofar-1.1.4.dist-info → sofar-1.2.0.dist-info}/LICENSE +4 -1
  23. sofar-1.2.0.dist-info/METADATA +93 -0
  24. {sofar-1.1.4.dist-info → sofar-1.2.0.dist-info}/RECORD +34 -19
  25. {sofar-1.1.4.dist-info → sofar-1.2.0.dist-info}/WHEEL +1 -1
  26. tests/conftest.py +27 -0
  27. tests/test_io.py +9 -5
  28. tests/test_sofa.py +1 -1
  29. tests/test_sofa_upgrade_conventions.py +10 -1
  30. tests/test_sofa_verify.py +1 -1
  31. tests/test_sofastream.py +126 -0
  32. tests/test_utils.py +18 -8
  33. sofar-1.1.4.dist-info/METADATA +0 -91
  34. {sofar-1.1.4.dist-info → sofar-1.2.0.dist-info}/AUTHORS.rst +0 -0
  35. {sofar-1.1.4.dist-info → sofar-1.2.0.dist-info}/top_level.txt +0 -0
sofar/__init__.py CHANGED
@@ -1,13 +1,12 @@
1
1
  # -*- coding: utf-8 -*-
2
-
3
- """Top-level package for sofar."""
4
-
5
2
  __author__ = """The pyfar developers"""
6
3
  __email__ = 'info@pyfar.org'
7
- __version__ = '1.1.4'
4
+ __version__ = '1.2.0'
8
5
 
9
6
  from .sofa import Sofa
10
7
 
8
+ from .sofastream import SofaStream
9
+
11
10
  from .io import read_sofa, read_sofa_as_netcdf, write_sofa
12
11
 
13
12
  from .utils import (list_conventions,
@@ -18,6 +17,7 @@ from .update_conventions import update_conventions
18
17
 
19
18
 
20
19
  __all__ = ['Sofa',
20
+ 'SofaStream',
21
21
  'update_conventions',
22
22
  'list_conventions',
23
23
  'read_sofa',
sofar/io.py CHANGED
@@ -9,13 +9,16 @@ import sofar as sf
9
9
  from .utils import _verify_convention_and_version, _atleast_nd
10
10
 
11
11
 
12
- def read_sofa(filename, verify=True, verbose=True):
12
+ def read_sofa(filename, verify='auto', verbose=True):
13
13
  """
14
14
  Read SOFA file from disk and convert it to SOFA object.
15
15
 
16
16
  Numeric data is returned as floats or numpy float arrays unless they have
17
17
  missing data, in which case they are returned as numpy masked arrays.
18
18
 
19
+ If you want to read only parts of the data of a large sofa file use
20
+ :class:`SofaStream` instead.
21
+
19
22
  Parameters
20
23
  ----------
21
24
  filename : str
@@ -24,7 +27,9 @@ def read_sofa(filename, verify=True, verbose=True):
24
27
  Verify and update the SOFA object by calling :py:func:`~Sofa.verify`.
25
28
  This helps to find potential errors in the default values and is thus
26
29
  recommended. If reading a file does not work, try to call `Sofa` with
27
- ``verify=False``. The default is ``True``.
30
+ ``verify=False``. The default is ``'auto'`` defaults to ``True`` for
31
+ stable conventions with versions of 1.0 or higher and to ``False``
32
+ otherwise.
28
33
  verbose : bool, optional
29
34
  Print the names of detected custom variables and attributes. The
30
35
  default is ``True``
@@ -39,7 +44,7 @@ def read_sofa(filename, verify=True, verbose=True):
39
44
 
40
45
  1. Missing dimensions are appended when writing the SOFA object to disk.
41
46
  E.g., if ``sofa.Data_IR`` is of shape (1, 2) it is written as an array
42
- of shape (1, 2, 1) because the SOFA standard AES69-2020 defines it as a
47
+ of shape (1, 2, 1) because the SOFA standard AES69 defines it as a
43
48
  three dimensional array with the dimensions (`M: measurements`,
44
49
  `R: receivers`, `N: samples`)
45
50
  2. When reading data from a SOFA file, array data is always returned as
@@ -48,7 +53,7 @@ def read_sofa(filename, verify=True, verbose=True):
48
53
  after writing and reading to and from disk.
49
54
  3. One dimensional arrays with only one element will be converted to scalar
50
55
  values. E.g. ``sofa.Data_SamplingRate`` is stored as an array of shape
51
- (1, ) inside SOFA files (according to the SOFA standard AES69-2020) but
56
+ (1, ) inside SOFA files (according to the SOFA standard AES69) but
52
57
  will be a scalar inside SOFA objects after reading from disk.
53
58
  """
54
59
 
@@ -90,7 +95,7 @@ def read_sofa_as_netcdf(filename):
90
95
 
91
96
  1. Missing dimensions are appended when writing the SOFA object to disk.
92
97
  E.g., if ``sofa.Data_IR`` is of shape (1, 2) it is written as an array
93
- of shape (1, 2, 1) because the SOFA standard AES69-2020 defines it as a
98
+ of shape (1, 2, 1) because the SOFA standard AES69 defines it as a
94
99
  three dimensional array with the dimensions (`M: measurements`,
95
100
  `R: receivers`, `N: samples`)
96
101
  2. When reading data from a SOFA file, array data is always returned as
@@ -99,7 +104,7 @@ def read_sofa_as_netcdf(filename):
99
104
  after writing and reading to and from disk.
100
105
  3. One dimensional arrays with only one element will be converted to scalar
101
106
  values. E.g. ``sofa.Data_SamplingRate`` is stored as an array of shape
102
- (1, ) inside SOFA files (according to the SOFA standard AES69-2020) but
107
+ (1, ) inside SOFA files (according to the SOFA standard AES69) but
103
108
  will be a scalar inside SOFA objects after reading from disk.
104
109
  """
105
110
  return _read_netcdf(filename, False, False, mode="netcdf")
@@ -200,6 +205,10 @@ def _read_netcdf(filename, verify, verbose, mode):
200
205
  "----------------------------------\n"
201
206
  f"{', '.join(custom)}"))
202
207
 
208
+ # set default for verify
209
+ if verify == 'auto':
210
+ verify = True if parse(version) >= parse('1.0') else False
211
+
203
212
  # update api
204
213
  if verify:
205
214
  try:
@@ -234,7 +243,7 @@ def write_sofa(filename: str, sofa: sf.Sofa, compression=4):
234
243
 
235
244
  1. Missing dimensions are appended when writing the SOFA object to disk.
236
245
  E.g., if ``sofa.Data_IR`` is of shape (1, 2) it is written as an array
237
- of shape (1, 2, 1) because the SOFA standard AES69-2020 defines it as a
246
+ of shape (1, 2, 1) because the SOFA standard AES69 defines it as a
238
247
  three dimensional array with the dimensions (`M: measurements`,
239
248
  `R: receivers`, `N: samples`)
240
249
  2. When reading data from a SOFA file, array data is always returned as
@@ -243,7 +252,7 @@ def write_sofa(filename: str, sofa: sf.Sofa, compression=4):
243
252
  after writing and reading to and from disk.
244
253
  3. One dimensional arrays with only one element will be converted to scalar
245
254
  values. E.g. ``sofa.Data_SamplingRate`` is stored as an array of shape
246
- (1, ) inside SOFA files (according to the SOFA standard AES69-2020) but
255
+ (1, ) inside SOFA files (according to the SOFA standard AES69) but
247
256
  will be a scalar inside SOFA objects after reading from disk.
248
257
  """
249
258
  _write_sofa(filename, sofa, compression, verify=True)
@@ -325,8 +334,7 @@ def _write_sofa(filename: str, sofa: sf.Sofa, compression=4, verify=True):
325
334
  if dtype == "f8":
326
335
  tmp_var[:] = value
327
336
  else:
328
- tmp_var[:] = stringtochar(value)
329
- tmp_var._Encoding = "ascii"
337
+ tmp_var[:] = stringtochar(value, encoding='utf-8')
330
338
 
331
339
  # write variable attributes
332
340
  sub_keys = [k for k in all_keys if k.startswith(f"{key}_")]
sofar/sofa.py CHANGED
@@ -5,6 +5,7 @@ from datetime import datetime
5
5
  import platform
6
6
  import numpy as np
7
7
  import warnings
8
+ from packaging.version import parse
8
9
  from copy import deepcopy
9
10
  import sofar as sf
10
11
  from .utils import (_nd_newaxis, _atleast_nd, _get_conventions,
@@ -30,7 +31,9 @@ class Sofa():
30
31
  Verify the SOFA object by calling :py:func:`~Sofa.verify`. This helps
31
32
  to find potential errors in the default values and is thus recommended
32
33
  If creating a file does not work, try to call `Sofa` with
33
- ``verify=False``. The default is ``True``.
34
+ ``verify=False``. The default ``'auto'`` defaults to ``True`` for
35
+ stable conventions with versions of 1.0 or higher and to ``False``
36
+ otherwise.
34
37
 
35
38
  Returns
36
39
  -------
@@ -60,7 +63,7 @@ class Sofa():
60
63
  ``sofa.Data_IR`` is converted to a numpy array of shape (1, 2)
61
64
  2. Missing dimensions are appended when writing the SOFA object to disk,
62
65
  i.e., ``sofa.Data_IR`` is written as an array of shape (1, 2, 1) because
63
- the SOFA standard AES69-2020 defines it as a three dimensional array
66
+ the SOFA standard AES69 defines it as a three dimensional array
64
67
  with the dimensions (`M: measurements`, `R: receivers`, `N: samples`)
65
68
  3. When reading data from a SOFA file, array data is always returned as
66
69
  numpy arrays and singleton trailing dimensions are discarded (numpy
@@ -68,7 +71,7 @@ class Sofa():
68
71
  after writing and reading to and from disk.
69
72
  4. One dimensional arrays with only one element will be converted to scalar
70
73
  values. E.g. ``sofa.Data_SamplingRate`` is stored as an array of shape
71
- (1, ) inside SOFA files (according to the SOFA standard AES69-2020) but
74
+ (1, ) inside SOFA files (according to the SOFA standard AES69) but
72
75
  will be a scalar inside SOFA objects after reading from disk.
73
76
 
74
77
 
@@ -85,7 +88,7 @@ class Sofa():
85
88
  _read_only_attr = []
86
89
 
87
90
  def __init__(self, convention, mandatory=False, version="latest",
88
- verify=True):
91
+ verify='auto'):
89
92
  """See class docstring"""
90
93
 
91
94
  # get convention
@@ -100,11 +103,24 @@ class Sofa():
100
103
  # add attributes with default values
101
104
  self._convention_to_sofa(mandatory)
102
105
 
106
+ # set default for verify
107
+ version = \
108
+ self._convention['GLOBAL_SOFAConventionsVersion']['default']
109
+ if verify == 'auto':
110
+ verify = True if parse(version) >= parse('1.0') else False
111
+
103
112
  # add and update the API
104
113
  # (mandatory=False can not be verified because some conventions
105
114
  # have default values that have optional variables as dependencies)
106
115
  if verify and not mandatory:
107
116
  self.verify(mode="read")
117
+ # warning for preliminary conventions if verification is bypassed
118
+ elif parse(version) < parse('1.0'):
119
+ warnings.warn(UserWarning((
120
+ f"Detected preliminary conventions version {version}. "
121
+ "Upgrade data to version >= 1.0 if possible. Preliminary "
122
+ "conventions might change in the future, which could "
123
+ "invalidate data that was written before the changes.")))
108
124
 
109
125
  self.protected = True
110
126
  else:
@@ -168,7 +184,7 @@ class Sofa():
168
184
  M
169
185
  number of measurements
170
186
  N
171
- number of samles, frequencies, SOS coefficients
187
+ number of samples, frequencies, SOS coefficients
172
188
  (depending on self.GLOBAL_DataType)
173
189
  R
174
190
  Number of receivers or SH coefficients
@@ -188,7 +204,7 @@ class Sofa():
188
204
  # Check if the dimensions can be updated
189
205
  self._update_dimensions()
190
206
 
191
- # get verbose description for dimesion N
207
+ # get verbose description for dimension N
192
208
  if self.GLOBAL_DataType.startswith("FIR"):
193
209
  N_verbose = "samples"
194
210
  elif self.GLOBAL_DataType.startswith("TF"):
@@ -392,7 +408,7 @@ class Sofa():
392
408
  console.
393
409
  issue_handling : str, optional
394
410
  Defines how issues detected during verification of the SOFA object
395
- are handeled (see :py:func:`~sofar.sofar.Sofa.verify`)
411
+ are handled (see :py:func:`~sofar.sofar.Sofa.verify`)
396
412
 
397
413
  ``'raise'``
398
414
  Warnings and errors are raised if issues are detected
@@ -403,7 +419,7 @@ class Sofa():
403
419
  ``'ignore'``
404
420
  Issues are ignored, i.e., not raised, printed, or returned.
405
421
 
406
- The default is ``print'``.
422
+ The default is ``'print'``.
407
423
  """
408
424
 
409
425
  # update the private attribute `_convention` to make sure the required
@@ -538,7 +554,7 @@ class Sofa():
538
554
  # add numeric data
539
555
  sofa.add_variable("Temperature", 25.1, "double", "MI")
540
556
 
541
- # add GLOBAL and Variable attribtue
557
+ # add GLOBAL and Variable attribute
542
558
  sofa.add_entry(
543
559
  "GLOBAL_DateMeasured", "8.08.2021", "attribute", None)
544
560
  sofa.add_entry(
@@ -569,7 +585,7 @@ class Sofa():
569
585
  import sofar as sf
570
586
  sofa = sf.Sofa("GeneralTF")
571
587
 
572
- # add GLOBAL and Variable attribtue
588
+ # add GLOBAL and Variable attribute
573
589
  sofa.add_attribute("GLOBAL_DateMeasured", "8.08.2021")
574
590
  sofa.add_attribute("Data_Real_Units", "Pascal")
575
591
 
@@ -695,7 +711,7 @@ class Sofa():
695
711
  setattr(self, key, value)
696
712
  self.protected = True
697
713
 
698
- def upgrade_convention(self, target=None, verify=True):
714
+ def upgrade_convention(self, target=None, verify='auto'):
699
715
  """
700
716
  Upgrade Sofa data to newer conventions.
701
717
 
@@ -713,7 +729,9 @@ class Sofa():
713
729
  conventions to which the data can be updated.
714
730
  verify : bool, optional
715
731
  Flag to specify if the data should be verified after the upgrade
716
- using :py:func:`~Sofa.verify`. The default is ``True``.
732
+ using :py:func:`~Sofa.verify`. The default ``'auto'`` defaults to
733
+ ``True`` for stable conventions with versions of 1.0 or higher and
734
+ to ``False`` otherwise.
717
735
 
718
736
  Returns
719
737
  -------
@@ -745,7 +763,6 @@ class Sofa():
745
763
 
746
764
  # check for upgrades --------------------------------------------------
747
765
  if is_deprecated:
748
- version_matched = False
749
766
  # check if upgrade is available for this convention
750
767
  if convention_current not in upgrade:
751
768
  print((f"Convention {convention_current} v{version_current} is"
@@ -755,7 +772,6 @@ class Sofa():
755
772
  # check if upgrade is available for this version
756
773
  for from_to in upgrade[convention_current]["from_to"]:
757
774
  if version_current in from_to[0]:
758
- version_matched = True
759
775
  targets = from_to[1]
760
776
 
761
777
  if target in targets:
@@ -773,9 +789,6 @@ class Sofa():
773
789
  print(upgrades)
774
790
  return targets
775
791
  break
776
- if not version_matched:
777
- print((f"Convention {convention_current} v{version_current} is"
778
- " outdated but is missing upgrade rules"))
779
792
  else:
780
793
  print((f"Convention {convention_current} v{version_current} "
781
794
  "is up to date"))
@@ -856,6 +869,10 @@ class Sofa():
856
869
  if upgrade["message"] is not None:
857
870
  print(upgrade["message"])
858
871
 
872
+ # set default for verify
873
+ if verify == 'auto':
874
+ version = self.GLOBAL_SOFAConventionsVersion
875
+ verify = True if parse(version) >= parse('1.0') else False
859
876
  if verify:
860
877
  self.verify()
861
878
 
@@ -905,7 +922,7 @@ class Sofa():
905
922
  Parameters
906
923
  ----------
907
924
  issue_handling : str, optional
908
- Defines how detected issues are handeled
925
+ Defines how detected issues are handled
909
926
 
910
927
  ``'raise'``
911
928
  Warnings and errors are raised if issues are detected
@@ -1006,7 +1023,7 @@ class Sofa():
1006
1023
 
1007
1024
  elif dtype == "string":
1008
1025
  # multiple checks needed because sofar does not force the user
1009
- # to initally pass data as numpy arrays
1026
+ # to initially pass data as numpy arrays
1010
1027
  if not isinstance(value, (str, np.ndarray)):
1011
1028
  current_error += (f"- {key} must be string or numpy array "
1012
1029
  f"but is {type(value)}\n")
@@ -1099,7 +1116,7 @@ class Sofa():
1099
1116
  current_error = ""
1100
1117
  for key in keys:
1101
1118
 
1102
- # AES69-2020 Sec. 4.7.1
1119
+ # AES69 Sec. 4.7.1
1103
1120
  if key.startswith("PRIVATE") or key.startswith("API"):
1104
1121
  current_error += "- " + key + "\n"
1105
1122
  if (key.startswith("GLOBAL") and not key.startswith("GLOBAL_")) or\
@@ -1214,7 +1231,7 @@ class Sofa():
1214
1231
  [self._api[d.upper()] if d != "S" else 1 for d in dim])
1215
1232
 
1216
1233
  # get shape for comparison to correct length by cropping and
1217
- # appending singelton dimensions if required
1234
+ # appending singleton dimensions if required
1218
1235
  shape_compare = shape_act[:len(shape_ref)]
1219
1236
  for _ in range(len(shape_ref) - len(shape_compare)):
1220
1237
  shape_compare += (1, )
@@ -1293,7 +1310,7 @@ class Sofa():
1293
1310
  for key_dep, ref_dep in items:
1294
1311
 
1295
1312
  if key_dep == "_dimensions":
1296
- # requires specific dimension(s) to have a vertain size
1313
+ # requires specific dimension(s) to have a certain size
1297
1314
  for dim in rules[key]["specific"][test]["_dimensions"]:
1298
1315
  # possible sizes
1299
1316
  dim_ref = \
@@ -1390,7 +1407,7 @@ class Sofa():
1390
1407
  @staticmethod
1391
1408
  def _verify_value(test, ref, unit_aliases, key):
1392
1409
  """
1393
- Check a value agains the SOFA standard for Sofa.verify()
1410
+ Check a value against the SOFA standard for Sofa.verify()
1394
1411
 
1395
1412
  Parameters
1396
1413
  ----------
@@ -1462,7 +1479,7 @@ class Sofa():
1462
1479
  or not isinstance(ref[0], str):
1463
1480
  raise TypeError("ref must be a list of length 1 containing a str")
1464
1481
 
1465
- # Following the SOFA standard AES69-2020, units may be separated by
1482
+ # Following the SOFA standard AES69, units may be separated by
1466
1483
  # `, ` (comma and space), `,` (comma only), and ` ` (space only).
1467
1484
  # (regexp ', ?' matches ', ' and ',')
1468
1485
  units_ref = re.split(', ?', ref[0])
@@ -1500,7 +1517,7 @@ class Sofa():
1500
1517
  -------
1501
1518
  reference_units : str
1502
1519
  """
1503
- # Following the SOFA standard AES69-2020, units may be separated by
1520
+ # Following the SOFA standard AES69, units may be separated by
1504
1521
  # `, ` (comma and space), `,` (comma only), and ` ` (space only).
1505
1522
  # (regexp ', ?' matches ', ' and ',')
1506
1523
  units_test = re.split(', ?| ', test.lower())
@@ -0,0 +1,46 @@
1
+ Name Default Flags Dimensions Type Comment
2
+ GLOBAL:Conventions SOFA rm attribute
3
+ GLOBAL:Version 2.1 rm attribute
4
+ GLOBAL:SOFAConventions AnnotatedEmitterAudio rm attribute
5
+ GLOBAL:SOFAConventionsVersion 0.2 rm attribute
6
+ GLOBAL:APIName rm attribute
7
+ GLOBAL:APIVersion rm attribute
8
+ GLOBAL:ApplicationName attribute
9
+ GLOBAL:ApplicationVersion attribute
10
+ GLOBAL:AuthorContact m attribute
11
+ GLOBAL:Comment attribute
12
+ GLOBAL:DataType Audio rm attribute
13
+ GLOBAL:History attribute
14
+ GLOBAL:License No license provided, ask the author for permission m attribute
15
+ GLOBAL:Organization m attribute
16
+ GLOBAL:References attribute
17
+ GLOBAL:RoomType free field m attribute
18
+ GLOBAL:Origin attribute
19
+ GLOBAL:DateCreated m attribute
20
+ GLOBAL:DateModified m attribute
21
+ GLOBAL:Title m attribute
22
+ ListenerPosition [0 0 0] m IC, MC double Position of the head. IC if not tracked, MC if tracked.
23
+ ListenerPosition:Type cartesian m attribute
24
+ ListenerPosition:Units metre m attribute
25
+ ReceiverPosition [0 0.09 0; 0 -0.09 0] m rC, rCM double Position of the ears. RC if not tracked, RCM if tracked.
26
+ ReceiverPosition:Type cartesian m attribute
27
+ ReceiverPosition:Units metre m attribute
28
+ SourcePosition [0 0 0] m IC, MC double Position of the virtual ensemble. IC if not tracked, MC if tracked.
29
+ SourcePosition:Type cartesian m attribute
30
+ SourcePosition:Units metre m attribute
31
+ EmitterPosition [0 0 0] m eC, eCM double Position of the virtual source(s). eC if not tracked, eCM if tracked.
32
+ EmitterPosition:Type cartesian m attribute
33
+ EmitterPosition:Units metre m attribute
34
+ ListenerUp [0 0 1] m IC, MC double Must be of the same dimensionality as ListenerView.
35
+ ListenerView [1 0 0] m IC, MC double Orientation of the head. IC if not tracked, MC if tracked.
36
+ ListenerView:Type cartesian m attribute
37
+ ListenerView:Units metre m attribute
38
+ Data.Emitter [0 0] m In, En double audio data at the emitter(s); n=number of audio samples
39
+ Data.SamplingRate 44100 m I double
40
+ Data.SamplingRate:Units hertz m attribute
41
+ M 0 m m double Time stamp of the measurements in M, defines the size of M.
42
+ M:LongName time m attribute Narrative name for M
43
+ M:Units second m attribute Units used for M
44
+ Response {''} I, C, S string the subject’s response
45
+ Response:Type attribute type depends on the dimension
46
+ Response:LongName attribute narrative description of the response type