├── setup.cfg
├── SOFASonix
├── ss_db.db
├── __init__.py
├── SOFASonixError.py
├── SOFATemplate.py
├── SOFASonixField.py
└── SOFASonix.py
├── LICENSE
├── setup.py
├── README.md
├── .GITIGNORE
└── Templates
├── GeneralTF_1.0_1.0.py
├── GeneralFIR_1.0_1.0.py
├── GeneralFIRE_1.0_1.0.py
├── SimpleFreeFieldTF_1.0_1.0.py
├── SimpleFreeFieldHRIR_1.0_1.0.py
├── SimpleFreeFieldSOS_1.0_1.0.py
├── SingleRoomDRIR_1.0_0.3.py
├── MultiSpeakerBRIR_1.0_0.3.py
├── MusicalInstrumentDirectivity_1.0_1.0.py
├── SimpleHeadphoneIR_1.0_0.2.py
└── FreeFieldDirectivityTF_1.0_0.1.py
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.md
--------------------------------------------------------------------------------
/SOFASonix/ss_db.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OneBadNinja/SOFASonix/HEAD/SOFASonix/ss_db.db
--------------------------------------------------------------------------------
/SOFASonix/__init__.py:
--------------------------------------------------------------------------------
1 | from .SOFASonix import SOFASonix as SOFAFile
2 | from .SOFATemplate import SOFATemplate as TemplateGenerator
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2018 I.Laghidze (developer@artisan-one.com)
2 |
3 | Redistribution and use in source and binary forms, with or without modification, are permitted
4 | provided that the following conditions are met:
5 |
6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions
7 | and the following disclaimer.
8 |
9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of
10 | conditions and the following disclaimer in the documentation and/or other materials provided
11 | with the distribution.
12 |
13 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to
14 | endorse or promote products derived from this software without specific prior written permission.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
17 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
23 | OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 |
3 | with open("README.md", "r") as fh:
4 | long_description = fh.read()
5 |
6 | setuptools.setup(
7 | name="SOFASonix",
8 | version="1.0.8",
9 | author='Ioseb Laghidze',
10 | author_email='developer@artisan-one.com',
11 | description="A Lightweight Python API for the AES69-2015 Spatially Oriented Format for Acoustics (SOFA) File Format",
12 | long_description=long_description,
13 | long_description_content_type="text/markdown",
14 | url='https://github.com/OneBadNinja/SOFASonix/',
15 | packages=setuptools.find_packages(),
16 | classifiers=[
17 | 'Development Status :: 4 - Beta',
18 | 'Intended Audience :: Developers',
19 | 'Intended Audience :: Science/Research',
20 | 'Topic :: Software Development :: Build Tools',
21 | 'License :: OSI Approved :: BSD License',
22 | 'Operating System :: OS Independent',
23 | 'Programming Language :: Python :: 2.7',
24 | 'Programming Language :: Python :: 3',
25 | 'Programming Language :: Python :: 3.4',
26 | 'Programming Language :: Python :: 3.5',
27 | 'Programming Language :: Python :: 3.6',
28 | 'Programming Language :: Python :: 3.7',
29 | 'Programming Language :: Python :: 3.8',
30 | ],
31 | python_requires='>=2.7',
32 | license='BSD 3-Clause',
33 | keywords=['SOFA', 'Spatially Oriented Format For Acoustics',
34 | 'Audio', '3D Audio', 'Binaural', 'Transaural', 'Acoustics'],
35 | package_data={
36 | # Include database file
37 | 'SOFASonix': ['*.db'],
38 | },
39 | install_requires=[
40 | 'netCDF4',
41 | 'numpy',
42 | 'pandas',
43 | ]
44 | )
45 |
--------------------------------------------------------------------------------
/SOFASonix/SOFASonixError.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (c) 2018, I.Laghidze
5 | #
6 | # All rights reserved.
7 | #
8 | # Redistribution and use in source and binary forms, with or without
9 | # modification, are permitted provided that the following conditions are met:
10 | #
11 | # * Redistributions of source code must retain the above copyright notice,
12 | # this list of conditions and the following disclaimer.
13 | # * Redistributions in binary form must reproduce the above copyright
14 | # notice, this list of conditions and the following disclaimer in the
15 | # documentation and/or other materials provided with the distribution.
16 | # * Neither the name of SOFASonix nor the names of its contributors
17 | # may be used to endorse or promote products derived from this software
18 | # without specific prior written permission.
19 | #
20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | #
32 | # =============================================================================
33 | #
34 | # File: SOFASonixError.py
35 | # Project: SOFASonix
36 | # Author: I.Laghidze
37 | # License: BSD 3
38 | #
39 | # =============================================================================
40 |
41 |
42 | class SOFAError(Exception):
43 | pass
44 |
45 |
46 | class SOFAFieldError(Exception):
47 | pass
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 
4 | 
5 |
6 | SOFASonix is an advanced Python API for the Spatially-Oriented Format for Acoustics (SOFA Format) as defined by AES69-2015 - www.sofaconventions.org.
7 |
8 | SOFASonix was initially developed as a supplementary tool for usage alongside an undergraduate project at Southampton University's Institute of Sound and Vibration Research (ISVR). The enhanced and refined version of this is now the official SOFASonix API.
9 |
10 | ## Features
11 |
12 | - Support for the majority of [standardised and stable SOFA conventions](#supportedConventions).
13 | - Reading/Writing of SOFA Files for each of the supported conventions.
14 | - Built-in **convention-specific validation** to ensure correct file format for export!
15 | - Powered by the lightweight and powerful SQLite3, providing support for different versions of each convention.
16 | - Simple syntax for quickly generating / reading SOFA files.
17 |
18 | ## Installation
19 |
20 | SOFASonix can be installed as a PyPI package using
21 |
22 | ```
23 | pip install sofasonix
24 | ```
25 |
26 | ## Supported Conventions
27 |
28 | The following conventions are currently supported by SOFASonix for reading and writing, as per the [SOFAConventions website](https://www.sofaconventions.org/mediawiki/index.php/SOFA_conventions):
29 |
30 | |Convention|Version|SOFAConventionsVersion|
31 | |---|---|---|
32 | |GeneralFIR|1.0|1.0|
33 | |GeneralTF|1.0|1.0|
34 | ||0.5|0.1|
35 | |SimpleFreeFieldHRIR|1.0|1.0|
36 | ||0.6|0.4|
37 | ||0.5|0.3|
38 | |GeneralFIRE|1.0|1.0|
39 | ||0.6|0.1|
40 | |MultiSpeakerBRIR|1.0|0.3|
41 | ||0.6|0.2|
42 | ||0.6|0.1|
43 | |SimpleHeadphoneIR|1.0|0.2|
44 | ||0.6|0.1|
45 | ||0.5|0.1|
46 | |SimpleFreeFieldTF|1.0|1.0|
47 | ||0.6|0.4|
48 | |SimpleFreeFieldSOS|1.0|1.0|
49 | |SingleRoomDRIR|1.0|0.3|
50 | ||0.5|0.1|
51 | |MusicalInstrumentDirectivity|1.0|1.0|
52 | |FreeFieldDirectivityTF|1.0|0.1|
53 |
54 | **Note:** While some of these versions are deprecated according to [SOFAConventions](http://www.sofaconventions.org), they have been included to maintain backwards compatibility for both older SOFA files and older applications that may be using them. It is always recommended to use the latest versions for generating files for storage.
55 |
56 | ## Documentation
57 | SOFASonix documentation and example usage is available over at the [SOFASonix Wiki page](https://github.com/OneBadNinja/SOFASonix/wiki).
58 |
59 | ## Templates
60 | SOFASonix supports templates for each convention for easily creating SOFA files. These templates are autogenerated with the provided TemplateGenerator. The TemplateGenerator may be used to create python templates for any convention (and version combinations) supported by SOFASonix. To learn more, refer to the [SOFASonix WIKI page](https://github.com/OneBadNinja/SOFASonix/wiki).
61 |
62 | ## Contribute
63 | You may contribute any improvements and fixes via regular pull-requests. These will be reviewed as per standard procedure before integration.
64 |
65 | ## License
66 | SOFASonix is licensed under BSD-3 - see the LICENSE.md file for details. Alternatively you may visit https://opensource.org/licenses/BSD-3-Clause
67 |
68 | ## Authors
69 | I.G Laghidze (Founder) - developer@artisan-one.com
70 |
71 |
--------------------------------------------------------------------------------
/.GITIGNORE:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/osx,linux,python,windows
3 | # Edit at https://www.gitignore.io/?templates=osx,linux,python,windows
4 |
5 | ### Linux ###
6 | *~
7 |
8 | # temporary files which can be created if a process still has a handle open of a deleted file
9 | .fuse_hidden*
10 |
11 | # KDE directory preferences
12 | .directory
13 |
14 | # Linux trash folder which might appear on any partition or disk
15 | .Trash-*
16 |
17 | # .nfs files are created when an open file is removed but is still being accessed
18 | .nfs*
19 |
20 | ### OSX ###
21 | # General
22 | .DS_Store
23 | .AppleDouble
24 | .LSOverride
25 |
26 | # Icon must end with two \r
27 | Icon
28 |
29 | # Thumbnails
30 | ._*
31 |
32 | # Files that might appear in the root of a volume
33 | .DocumentRevisions-V100
34 | .fseventsd
35 | .Spotlight-V100
36 | .TemporaryItems
37 | .Trashes
38 | .VolumeIcon.icns
39 | .com.apple.timemachine.donotpresent
40 |
41 | # Directories potentially created on remote AFP share
42 | .AppleDB
43 | .AppleDesktop
44 | Network Trash Folder
45 | Temporary Items
46 | .apdisk
47 |
48 | ### Python ###
49 | # Byte-compiled / optimized / DLL files
50 | __pycache__/
51 | *.py[cod]
52 | *$py.class
53 |
54 | # C extensions
55 | *.so
56 |
57 | # Distribution / packaging
58 | .Python
59 | build/
60 | develop-eggs/
61 | dist/
62 | downloads/
63 | eggs/
64 | .eggs/
65 | lib/
66 | lib64/
67 | parts/
68 | sdist/
69 | var/
70 | wheels/
71 | share/python-wheels/
72 | *.egg-info/
73 | .installed.cfg
74 | *.egg
75 | MANIFEST
76 |
77 | # PyInstaller
78 | # Usually these files are written by a python script from a template
79 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
80 | *.manifest
81 | *.spec
82 |
83 | # Installer logs
84 | pip-log.txt
85 | pip-delete-this-directory.txt
86 |
87 | # Unit test / coverage reports
88 | htmlcov/
89 | .tox/
90 | .nox/
91 | .coverage
92 | .coverage.*
93 | .cache
94 | nosetests.xml
95 | coverage.xml
96 | *.cover
97 | .hypothesis/
98 | .pytest_cache/
99 |
100 | # Translations
101 | *.mo
102 | *.pot
103 |
104 | # Django stuff:
105 | *.log
106 | local_settings.py
107 | db.sqlite3
108 |
109 | # Flask stuff:
110 | instance/
111 | .webassets-cache
112 |
113 | # Scrapy stuff:
114 | .scrapy
115 |
116 | # Sphinx documentation
117 | docs/_build/
118 |
119 | # PyBuilder
120 | target/
121 |
122 | # Jupyter Notebook
123 | .ipynb_checkpoints
124 |
125 | # IPython
126 | profile_default/
127 | ipython_config.py
128 |
129 | # pyenv
130 | .python-version
131 |
132 | # celery beat schedule file
133 | celerybeat-schedule
134 |
135 | # SageMath parsed files
136 | *.sage.py
137 |
138 | # Environments
139 | .env
140 | .venv
141 | env/
142 | venv/
143 | ENV/
144 | env.bak/
145 | venv.bak/
146 |
147 | # Spyder project settings
148 | .spyderproject
149 | .spyproject
150 |
151 | # Rope project settings
152 | .ropeproject
153 |
154 | # mkdocs documentation
155 | /site
156 |
157 | # mypy
158 | .mypy_cache/
159 | .dmypy.json
160 | dmypy.json
161 |
162 | # Pyre type checker
163 | .pyre/
164 |
165 | ### Python Patch ###
166 | .venv/
167 |
168 | ### Windows ###
169 | # Windows thumbnail cache files
170 | Thumbs.db
171 | ehthumbs.db
172 | ehthumbs_vista.db
173 |
174 | # Dump file
175 | *.stackdump
176 |
177 | # Folder config file
178 | [Dd]esktop.ini
179 |
180 | # Recycle Bin used on file shares
181 | $RECYCLE.BIN/
182 |
183 | # Windows Installer files
184 | *.cab
185 | *.msi
186 | *.msix
187 | *.msm
188 | *.msp
189 |
190 | # Windows shortcuts
191 | *.lnk
192 |
193 | # End of https://www.gitignore.io/api/osx,linux,python,windows
194 |
--------------------------------------------------------------------------------
/Templates/GeneralTF_1.0_1.0.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (c) 2018, I.Laghidze
5 | #
6 | # All rights reserved.
7 | #
8 | # Redistribution and use in source and binary forms, with or without
9 | # modification, are permitted provided that the following conditions are met:
10 | #
11 | # * Redistributions of source code must retain the above copyright notice,
12 | # this list of conditions and the following disclaimer.
13 | # * Redistributions in binary form must reproduce the above copyright
14 | # notice, this list of conditions and the following disclaimer in the
15 | # documentation and/or other materials provided with the distribution.
16 | # * Neither the name of SOFASonix nor the names of its contributors
17 | # may be used to endorse or promote products derived from this software
18 | # without specific prior written permission.
19 | #
20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | #
32 | # =============================================================================
33 | #
34 | # File: GeneralTF_1.0_1.0.py
35 | # Project: SOFASonix
36 | # Author: I.Laghidze
37 | # License: BSD 3
38 | #
39 | # =============================================================================
40 |
41 | from SOFASonix import SOFAFile
42 | import numpy as np
43 |
44 | """
45 | =============================== Initial Config ================================
46 | """
47 |
48 | # Create SOFAFile object with the latest GeneralTF convention
49 | sofa = SOFAFile("GeneralTF", sofaConventionsVersion=1.0, version=1.0)
50 |
51 | # Set dimensions
52 | sofa._M = 100
53 | sofa._N = 1024
54 | sofa._R = 2
55 |
56 | # View parameters of convention
57 | sofa.view()
58 |
59 |
60 | """
61 | =============================== Attributes ====================================
62 | """
63 |
64 | # ----- Mandatory attributes -----
65 | sofa.GLOBAL_AuthorContact = ""
66 | sofa.GLOBAL_Comment = ""
67 | sofa.GLOBAL_License = "No license provided, ask the author for permission"
68 | sofa.GLOBAL_Organization = ""
69 | sofa.GLOBAL_RoomType = "free field"
70 | sofa.GLOBAL_DateCreated = "2019-06-07 20:32:05"
71 | sofa.GLOBAL_DateModified = "2019-06-07 20:32:05"
72 | sofa.GLOBAL_Title = ""
73 | sofa.N_Units = "hertz"
74 | sofa.ListenerPosition_Type = "cartesian"
75 | sofa.ListenerPosition_Units = "metre"
76 | sofa.ReceiverPosition_Type = "cartesian"
77 | sofa.ReceiverPosition_Units = "metre"
78 | sofa.SourcePosition_Type = "spherical"
79 | sofa.SourcePosition_Units = "degree, degree, metre"
80 | sofa.EmitterPosition_Type = "cartesian"
81 | sofa.EmitterPosition_Units = "metre"
82 |
83 | # ----- Non-Mandatory attributes -----
84 | sofa.GLOBAL_ApplicationName = None
85 | sofa.GLOBAL_ApplicationVersion = None
86 | sofa.GLOBAL_History = None
87 | sofa.GLOBAL_References = None
88 | sofa.GLOBAL_Origin = None
89 | sofa.N_LongName = None
90 |
91 |
92 | """
93 | =============================== Double Variables ==============================
94 | """
95 |
96 | # ----- Mandatory double variables -----
97 |
98 | # Needs dimensions N
99 | sofa.N = np.zeros(1)
100 |
101 | # Needs dimensions IC or MC
102 | sofa.ListenerPosition = np.zeros(1)
103 |
104 | # Needs dimensions rCI or rCM
105 | sofa.ReceiverPosition = np.zeros(1)
106 |
107 | # Needs dimensions IC or MC
108 | sofa.SourcePosition = np.zeros(1)
109 |
110 | # Needs dimensions eCI or eCM
111 | sofa.EmitterPosition = np.zeros(1)
112 |
113 | # Needs dimensions mRn
114 | sofa.Data_Real = np.zeros(1)
115 |
116 | # Needs dimensions MRN
117 | sofa.Data_Imag = np.zeros(1)
118 |
119 | # ----- Non-mandatory double variables -----
120 |
121 |
122 | """
123 | =============================== Export ========================================
124 | """
125 |
126 | # Save file upon completion
127 | sofa.export("filename")
128 |
--------------------------------------------------------------------------------
/Templates/GeneralFIR_1.0_1.0.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (c) 2018, I.Laghidze
5 | #
6 | # All rights reserved.
7 | #
8 | # Redistribution and use in source and binary forms, with or without
9 | # modification, are permitted provided that the following conditions are met:
10 | #
11 | # * Redistributions of source code must retain the above copyright notice,
12 | # this list of conditions and the following disclaimer.
13 | # * Redistributions in binary form must reproduce the above copyright
14 | # notice, this list of conditions and the following disclaimer in the
15 | # documentation and/or other materials provided with the distribution.
16 | # * Neither the name of SOFASonix nor the names of its contributors
17 | # may be used to endorse or promote products derived from this software
18 | # without specific prior written permission.
19 | #
20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | #
32 | # =============================================================================
33 | #
34 | # File: GeneralFIR_1.0_1.0.py
35 | # Project: SOFASonix
36 | # Author: I.Laghidze
37 | # License: BSD 3
38 | #
39 | # =============================================================================
40 |
41 | from SOFASonix import SOFAFile
42 | import numpy as np
43 |
44 | """
45 | =============================== Initial Config ================================
46 | """
47 |
48 | # Create SOFAFile object with the latest GeneralFIR convention
49 | sofa = SOFAFile("GeneralFIR", sofaConventionsVersion=1.0, version=1.0)
50 |
51 | # Set dimensions
52 | sofa._M = 100
53 | sofa._N = 1024
54 | sofa._R = 2
55 |
56 | # View parameters of convention
57 | sofa.view()
58 |
59 |
60 | """
61 | =============================== Attributes ====================================
62 | """
63 |
64 | # ----- Mandatory attributes -----
65 | sofa.GLOBAL_AuthorContact = ""
66 | sofa.GLOBAL_Comment = ""
67 | sofa.GLOBAL_License = "No license provided, ask the author for permission"
68 | sofa.GLOBAL_Organization = ""
69 | sofa.GLOBAL_RoomType = "free field"
70 | sofa.GLOBAL_DateCreated = "2019-06-07 20:32:04"
71 | sofa.GLOBAL_DateModified = "2019-06-07 20:32:04"
72 | sofa.GLOBAL_Title = ""
73 | sofa.ListenerPosition_Type = "cartesian"
74 | sofa.ListenerPosition_Units = "metre"
75 | sofa.ReceiverPosition_Type = "cartesian"
76 | sofa.ReceiverPosition_Units = "metre"
77 | sofa.SourcePosition_Type = "spherical"
78 | sofa.SourcePosition_Units = "degree, degree, metre"
79 | sofa.EmitterPosition_Type = "cartesian"
80 | sofa.EmitterPosition_Units = "metre"
81 | sofa.Data_SamplingRate_Units = "hertz"
82 |
83 | # ----- Non-Mandatory attributes -----
84 | sofa.GLOBAL_ApplicationName = None
85 | sofa.GLOBAL_ApplicationVersion = None
86 | sofa.GLOBAL_History = None
87 | sofa.GLOBAL_References = None
88 | sofa.GLOBAL_Origin = None
89 |
90 |
91 | """
92 | =============================== Double Variables ==============================
93 | """
94 |
95 | # ----- Mandatory double variables -----
96 |
97 | # Needs dimensions IC or MC
98 | sofa.ListenerPosition = np.zeros(1)
99 |
100 | # Needs dimensions rCI or rCM
101 | sofa.ReceiverPosition = np.zeros(1)
102 |
103 | # Needs dimensions IC or MC
104 | sofa.SourcePosition = np.zeros(1)
105 |
106 | # Needs dimensions eCI or eCM
107 | sofa.EmitterPosition = np.zeros(1)
108 |
109 | # Needs dimensions mRn
110 | sofa.Data_IR = np.zeros(1)
111 |
112 | # Needs dimensions I
113 | sofa.Data_SamplingRate = np.zeros(1)
114 |
115 | # Needs dimensions IR or MR
116 | sofa.Data_Delay = np.zeros(1)
117 |
118 | # ----- Non-mandatory double variables -----
119 |
120 |
121 | """
122 | =============================== Export ========================================
123 | """
124 |
125 | # Save file upon completion
126 | sofa.export("filename")
127 |
--------------------------------------------------------------------------------
/Templates/GeneralFIRE_1.0_1.0.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (c) 2018, I.Laghidze
5 | #
6 | # All rights reserved.
7 | #
8 | # Redistribution and use in source and binary forms, with or without
9 | # modification, are permitted provided that the following conditions are met:
10 | #
11 | # * Redistributions of source code must retain the above copyright notice,
12 | # this list of conditions and the following disclaimer.
13 | # * Redistributions in binary form must reproduce the above copyright
14 | # notice, this list of conditions and the following disclaimer in the
15 | # documentation and/or other materials provided with the distribution.
16 | # * Neither the name of SOFASonix nor the names of its contributors
17 | # may be used to endorse or promote products derived from this software
18 | # without specific prior written permission.
19 | #
20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | #
32 | # =============================================================================
33 | #
34 | # File: GeneralFIRE_1.0_1.0.py
35 | # Project: SOFASonix
36 | # Author: I.Laghidze
37 | # License: BSD 3
38 | #
39 | # =============================================================================
40 |
41 | from SOFASonix import SOFAFile
42 | import numpy as np
43 |
44 | """
45 | =============================== Initial Config ================================
46 | """
47 |
48 | # Create SOFAFile object with the latest GeneralFIRE convention
49 | sofa = SOFAFile("GeneralFIRE", sofaConventionsVersion=1.0, version=1.0)
50 |
51 | # Set dimensions
52 | sofa._M = 100
53 | sofa._N = 1024
54 | sofa._R = 2
55 | sofa._E = 4
56 |
57 | # View parameters of convention
58 | sofa.view()
59 |
60 |
61 | """
62 | =============================== Attributes ====================================
63 | """
64 |
65 | # ----- Mandatory attributes -----
66 | sofa.GLOBAL_AuthorContact = ""
67 | sofa.GLOBAL_Comment = ""
68 | sofa.GLOBAL_License = "No license provided, ask the author for permission"
69 | sofa.GLOBAL_Organization = ""
70 | sofa.GLOBAL_RoomType = "free field"
71 | sofa.GLOBAL_DateCreated = "2019-06-07 20:32:05"
72 | sofa.GLOBAL_DateModified = "2019-06-07 20:32:05"
73 | sofa.GLOBAL_Title = ""
74 | sofa.ListenerPosition_Type = "cartesian"
75 | sofa.ListenerPosition_Units = "metre"
76 | sofa.ReceiverPosition_Type = "cartesian"
77 | sofa.ReceiverPosition_Units = "metre"
78 | sofa.SourcePosition_Type = "spherical"
79 | sofa.SourcePosition_Units = "degree, degree, metre"
80 | sofa.EmitterPosition_Type = "cartesian"
81 | sofa.EmitterPosition_Units = "metre"
82 | sofa.Data_SamplingRate_Units = "hertz"
83 |
84 | # ----- Non-Mandatory attributes -----
85 | sofa.GLOBAL_ApplicationName = None
86 | sofa.GLOBAL_ApplicationVersion = None
87 | sofa.GLOBAL_History = None
88 | sofa.GLOBAL_References = None
89 | sofa.GLOBAL_Origin = None
90 |
91 |
92 | """
93 | =============================== Double Variables ==============================
94 | """
95 |
96 | # ----- Mandatory double variables -----
97 |
98 | # Needs dimensions IC or MC
99 | sofa.ListenerPosition = np.zeros(1)
100 |
101 | # Needs dimensions rCI or rCM
102 | sofa.ReceiverPosition = np.zeros(1)
103 |
104 | # Needs dimensions IC or MC
105 | sofa.SourcePosition = np.zeros(1)
106 |
107 | # Needs dimensions eCI or eCM
108 | sofa.EmitterPosition = np.zeros(1)
109 |
110 | # Needs dimensions mREn
111 | sofa.Data_IR = np.zeros(1)
112 |
113 | # Needs dimensions I
114 | sofa.Data_SamplingRate = np.zeros(1)
115 |
116 | # Needs dimensions IRE or MRE
117 | sofa.Data_Delay = np.zeros(1)
118 |
119 | # ----- Non-mandatory double variables -----
120 |
121 |
122 | """
123 | =============================== Export ========================================
124 | """
125 |
126 | # Save file upon completion
127 | sofa.export("filename")
128 |
--------------------------------------------------------------------------------
/Templates/SimpleFreeFieldTF_1.0_1.0.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (c) 2018, I.Laghidze
5 | #
6 | # All rights reserved.
7 | #
8 | # Redistribution and use in source and binary forms, with or without
9 | # modification, are permitted provided that the following conditions are met:
10 | #
11 | # * Redistributions of source code must retain the above copyright notice,
12 | # this list of conditions and the following disclaimer.
13 | # * Redistributions in binary form must reproduce the above copyright
14 | # notice, this list of conditions and the following disclaimer in the
15 | # documentation and/or other materials provided with the distribution.
16 | # * Neither the name of SOFASonix nor the names of its contributors
17 | # may be used to endorse or promote products derived from this software
18 | # without specific prior written permission.
19 | #
20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | #
32 | # =============================================================================
33 | #
34 | # File: SimpleFreeFieldTF_1.0_1.0.py
35 | # Project: SOFASonix
36 | # Author: I.Laghidze
37 | # License: BSD 3
38 | #
39 | # =============================================================================
40 |
41 | from SOFASonix import SOFAFile
42 | import numpy as np
43 |
44 | """
45 | =============================== Initial Config ================================
46 | """
47 |
48 | # Create SOFAFile object with the latest SimpleFreeFieldTF convention
49 | sofa = SOFAFile("SimpleFreeFieldTF", sofaConventionsVersion=1.0, version=1.0)
50 |
51 | # Set dimensions
52 | sofa._M = 100
53 | sofa._N = 1024
54 |
55 | # View parameters of convention
56 | sofa.view()
57 |
58 |
59 | """
60 | =============================== Attributes ====================================
61 | """
62 |
63 | # ----- Mandatory attributes -----
64 | sofa.GLOBAL_AuthorContact = ""
65 | sofa.GLOBAL_License = "No license provided, ask the author for permission"
66 | sofa.GLOBAL_ListenerShortName = ""
67 | sofa.GLOBAL_Organization = ""
68 | sofa.GLOBAL_RoomType = "free field"
69 | sofa.GLOBAL_DateCreated = "2019-06-07 20:32:05"
70 | sofa.GLOBAL_DateModified = "2019-06-07 20:32:05"
71 | sofa.GLOBAL_Title = ""
72 | sofa.GLOBAL_DatabaseName = ""
73 | sofa.ListenerPosition_Type = "cartesian"
74 | sofa.ListenerPosition_Units = "metre"
75 | sofa.ListenerView_Type = "cartesian"
76 | sofa.ListenerView_Units = "metre"
77 | sofa.ReceiverPosition_Type = "cartesian"
78 | sofa.ReceiverPosition_Units = "metre"
79 | sofa.SourcePosition_Type = "spherical"
80 | sofa.SourcePosition_Units = "degree, degree, metre"
81 | sofa.EmitterPosition_Type = "cartesian"
82 | sofa.EmitterPosition_Units = "metre"
83 |
84 | # ----- Non-Mandatory attributes -----
85 | sofa.GLOBAL_ApplicationName = None
86 | sofa.GLOBAL_ApplicationVersion = None
87 | sofa.GLOBAL_Comment = None
88 | sofa.GLOBAL_History = None
89 | sofa.GLOBAL_References = None
90 | sofa.GLOBAL_Origin = None
91 | sofa.N_LongName = None
92 | sofa.N_Units = None
93 |
94 |
95 | """
96 | =============================== Double Variables ==============================
97 | """
98 |
99 | # ----- Mandatory double variables -----
100 |
101 | # Needs dimensions N
102 | sofa.N = np.zeros(1)
103 |
104 | # Needs dimensions IC or MC
105 | sofa.ListenerPosition = np.zeros(1)
106 |
107 | # Needs dimensions IC or MC
108 | sofa.ListenerUp = np.zeros(1)
109 |
110 | # Needs dimensions IC or MC
111 | sofa.ListenerView = np.zeros(1)
112 |
113 | # Needs dimensions rCI or rCM
114 | sofa.ReceiverPosition = np.zeros(1)
115 |
116 | # Needs dimensions IC or MC
117 | sofa.SourcePosition = np.zeros(1)
118 |
119 | # Needs dimensions eCI or eCM
120 | sofa.EmitterPosition = np.zeros(1)
121 |
122 | # Needs dimensions mRn
123 | sofa.Data_Real = np.zeros(1)
124 |
125 | # Needs dimensions MRN
126 | sofa.Data_Imag = np.zeros(1)
127 |
128 | # ----- Non-mandatory double variables -----
129 |
130 |
131 | """
132 | =============================== Export ========================================
133 | """
134 |
135 | # Save file upon completion
136 | sofa.export("filename")
137 |
--------------------------------------------------------------------------------
/Templates/SimpleFreeFieldHRIR_1.0_1.0.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (c) 2018, I.Laghidze
5 | #
6 | # All rights reserved.
7 | #
8 | # Redistribution and use in source and binary forms, with or without
9 | # modification, are permitted provided that the following conditions are met:
10 | #
11 | # * Redistributions of source code must retain the above copyright notice,
12 | # this list of conditions and the following disclaimer.
13 | # * Redistributions in binary form must reproduce the above copyright
14 | # notice, this list of conditions and the following disclaimer in the
15 | # documentation and/or other materials provided with the distribution.
16 | # * Neither the name of SOFASonix nor the names of its contributors
17 | # may be used to endorse or promote products derived from this software
18 | # without specific prior written permission.
19 | #
20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | #
32 | # =============================================================================
33 | #
34 | # File: SimpleFreeFieldHRIR_1.0_1.0.py
35 | # Project: SOFASonix
36 | # Author: I.Laghidze
37 | # License: BSD 3
38 | #
39 | # =============================================================================
40 |
41 | from SOFASonix import SOFAFile
42 | import numpy as np
43 |
44 | """
45 | =============================== Initial Config ================================
46 | """
47 |
48 | # Create SOFAFile object with the latest SimpleFreeFieldHRIR convention
49 | sofa = SOFAFile("SimpleFreeFieldHRIR", sofaConventionsVersion=1.0, version=1.0)
50 |
51 | # Set dimensions
52 | sofa._M = 100
53 | sofa._N = 1024
54 |
55 | # View parameters of convention
56 | sofa.view()
57 |
58 |
59 | """
60 | =============================== Attributes ====================================
61 | """
62 |
63 | # ----- Mandatory attributes -----
64 | sofa.GLOBAL_AuthorContact = ""
65 | sofa.GLOBAL_License = "No license provided, ask the author for permission"
66 | sofa.GLOBAL_Organization = ""
67 | sofa.GLOBAL_RoomType = "free field"
68 | sofa.GLOBAL_DateCreated = "2019-06-07 20:32:05"
69 | sofa.GLOBAL_DateModified = "2019-06-07 20:32:05"
70 | sofa.GLOBAL_Title = ""
71 | sofa.GLOBAL_DatabaseName = ""
72 | sofa.GLOBAL_ListenerShortName = ""
73 | sofa.ListenerPosition_Type = "cartesian"
74 | sofa.ListenerPosition_Units = "metre"
75 | sofa.ListenerView_Type = "cartesian"
76 | sofa.ListenerView_Units = "metre"
77 | sofa.ReceiverPosition_Type = "cartesian"
78 | sofa.ReceiverPosition_Units = "metre"
79 | sofa.SourcePosition_Type = "spherical"
80 | sofa.SourcePosition_Units = "degree, degree, metre"
81 | sofa.EmitterPosition_Type = "cartesian"
82 | sofa.EmitterPosition_Units = "metre"
83 | sofa.Data_SamplingRate_Units = "hertz"
84 |
85 | # ----- Non-Mandatory attributes -----
86 | sofa.GLOBAL_ApplicationName = None
87 | sofa.GLOBAL_ApplicationVersion = None
88 | sofa.GLOBAL_Comment = None
89 | sofa.GLOBAL_History = None
90 | sofa.GLOBAL_References = None
91 | sofa.GLOBAL_Origin = None
92 |
93 |
94 | """
95 | =============================== Double Variables ==============================
96 | """
97 |
98 | # ----- Mandatory double variables -----
99 |
100 | # Needs dimensions IC or MC
101 | sofa.ListenerPosition = np.zeros(1)
102 |
103 | # Needs dimensions IC or MC
104 | sofa.ListenerUp = np.zeros(1)
105 |
106 | # Needs dimensions IC or MC
107 | sofa.ListenerView = np.zeros(1)
108 |
109 | # Needs dimensions rCI or rCM
110 | sofa.ReceiverPosition = np.zeros(1)
111 |
112 | # Needs dimensions IC or MC
113 | sofa.SourcePosition = np.zeros(1)
114 |
115 | # Needs dimensions eCI or eCM
116 | sofa.EmitterPosition = np.zeros(1)
117 |
118 | # Needs dimensions mRn
119 | sofa.Data_IR = np.zeros(1)
120 |
121 | # Needs dimensions I
122 | sofa.Data_SamplingRate = np.zeros(1)
123 |
124 | # Needs dimensions IR or MR
125 | sofa.Data_Delay = np.zeros(1)
126 |
127 | # ----- Non-mandatory double variables -----
128 |
129 |
130 | """
131 | =============================== Export ========================================
132 | """
133 |
134 | # Save file upon completion
135 | sofa.export("filename")
136 |
--------------------------------------------------------------------------------
/Templates/SimpleFreeFieldSOS_1.0_1.0.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (c) 2018, I.Laghidze
5 | #
6 | # All rights reserved.
7 | #
8 | # Redistribution and use in source and binary forms, with or without
9 | # modification, are permitted provided that the following conditions are met:
10 | #
11 | # * Redistributions of source code must retain the above copyright notice,
12 | # this list of conditions and the following disclaimer.
13 | # * Redistributions in binary form must reproduce the above copyright
14 | # notice, this list of conditions and the following disclaimer in the
15 | # documentation and/or other materials provided with the distribution.
16 | # * Neither the name of SOFASonix nor the names of its contributors
17 | # may be used to endorse or promote products derived from this software
18 | # without specific prior written permission.
19 | #
20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | #
32 | # =============================================================================
33 | #
34 | # File: SimpleFreeFieldSOS_1.0_1.0.py
35 | # Project: SOFASonix
36 | # Author: I.Laghidze
37 | # License: BSD 3
38 | #
39 | # =============================================================================
40 |
41 | from SOFASonix import SOFAFile
42 | import numpy as np
43 |
44 | """
45 | =============================== Initial Config ================================
46 | """
47 |
48 | # Create SOFAFile object with the latest SimpleFreeFieldSOS convention
49 | sofa = SOFAFile("SimpleFreeFieldSOS", sofaConventionsVersion=1.0, version=1.0)
50 |
51 | # Set dimensions
52 | sofa._M = 100
53 | sofa._N = 1024
54 |
55 | # View parameters of convention
56 | sofa.view()
57 |
58 |
59 | """
60 | =============================== Attributes ====================================
61 | """
62 |
63 | # ----- Mandatory attributes -----
64 | sofa.GLOBAL_AuthorContact = ""
65 | sofa.GLOBAL_License = "No license provided, ask the author for permission"
66 | sofa.GLOBAL_Organization = ""
67 | sofa.GLOBAL_RoomType = "free field"
68 | sofa.GLOBAL_DateCreated = "2019-06-07 20:32:05"
69 | sofa.GLOBAL_DateModified = "2019-06-07 20:32:05"
70 | sofa.GLOBAL_Title = ""
71 | sofa.GLOBAL_DatabaseName = ""
72 | sofa.GLOBAL_ListenerShortName = ""
73 | sofa.ListenerPosition_Type = "cartesian"
74 | sofa.ListenerPosition_Units = "metre"
75 | sofa.ListenerView_Type = "cartesian"
76 | sofa.ListenerView_Units = "metre"
77 | sofa.ReceiverPosition_Type = "cartesian"
78 | sofa.ReceiverPosition_Units = "metre"
79 | sofa.SourcePosition_Type = "spherical"
80 | sofa.SourcePosition_Units = "degree, degree, metre"
81 | sofa.EmitterPosition_Type = "cartesian"
82 | sofa.EmitterPosition_Units = "metre"
83 | sofa.Data_SamplingRate_Units = "hertz"
84 |
85 | # ----- Non-Mandatory attributes -----
86 | sofa.GLOBAL_ApplicationName = None
87 | sofa.GLOBAL_ApplicationVersion = None
88 | sofa.GLOBAL_Comment = None
89 | sofa.GLOBAL_History = None
90 | sofa.GLOBAL_References = None
91 | sofa.GLOBAL_Origin = None
92 |
93 |
94 | """
95 | =============================== Double Variables ==============================
96 | """
97 |
98 | # ----- Mandatory double variables -----
99 |
100 | # Needs dimensions IC or MC
101 | sofa.ListenerPosition = np.zeros(1)
102 |
103 | # Needs dimensions IC or MC
104 | sofa.ListenerUp = np.zeros(1)
105 |
106 | # Needs dimensions IC or MC
107 | sofa.ListenerView = np.zeros(1)
108 |
109 | # Needs dimensions rCI or rCM
110 | sofa.ReceiverPosition = np.zeros(1)
111 |
112 | # Needs dimensions IC or MC
113 | sofa.SourcePosition = np.zeros(1)
114 |
115 | # Needs dimensions eCI or eCM
116 | sofa.EmitterPosition = np.zeros(1)
117 |
118 | # Needs dimensions mRn
119 | sofa.Data_SOS = np.zeros(1)
120 |
121 | # Needs dimensions I
122 | sofa.Data_SamplingRate = np.zeros(1)
123 |
124 | # Needs dimensions IR or MR
125 | sofa.Data_Delay = np.zeros(1)
126 |
127 | # ----- Non-mandatory double variables -----
128 |
129 |
130 | """
131 | =============================== Export ========================================
132 | """
133 |
134 | # Save file upon completion
135 | sofa.export("filename")
136 |
--------------------------------------------------------------------------------
/Templates/SingleRoomDRIR_1.0_0.3.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (c) 2018, I.Laghidze
5 | #
6 | # All rights reserved.
7 | #
8 | # Redistribution and use in source and binary forms, with or without
9 | # modification, are permitted provided that the following conditions are met:
10 | #
11 | # * Redistributions of source code must retain the above copyright notice,
12 | # this list of conditions and the following disclaimer.
13 | # * Redistributions in binary form must reproduce the above copyright
14 | # notice, this list of conditions and the following disclaimer in the
15 | # documentation and/or other materials provided with the distribution.
16 | # * Neither the name of SOFASonix nor the names of its contributors
17 | # may be used to endorse or promote products derived from this software
18 | # without specific prior written permission.
19 | #
20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | #
32 | # =============================================================================
33 | #
34 | # File: SingleRoomDRIR_1.0_0.3.py
35 | # Project: SOFASonix
36 | # Author: I.Laghidze
37 | # License: BSD 3
38 | #
39 | # =============================================================================
40 |
41 | from SOFASonix import SOFAFile
42 | import numpy as np
43 |
44 | """
45 | =============================== Initial Config ================================
46 | """
47 |
48 | # Create SOFAFile object with the latest SingleRoomDRIR convention
49 | sofa = SOFAFile("SingleRoomDRIR", sofaConventionsVersion=0.3, version=1.0)
50 |
51 | # Set dimensions
52 | sofa._M = 100
53 | sofa._N = 1024
54 | sofa._R = 2
55 |
56 | # View parameters of convention
57 | sofa.view()
58 |
59 |
60 | """
61 | =============================== Attributes ====================================
62 | """
63 |
64 | # ----- Mandatory attributes -----
65 | sofa.GLOBAL_AuthorContact = ""
66 | sofa.GLOBAL_Comment = ""
67 | sofa.GLOBAL_License = "No license provided, ask the author for permission"
68 | sofa.GLOBAL_Organization = ""
69 | sofa.GLOBAL_RoomType = "reverberant"
70 | sofa.GLOBAL_DateCreated = "2019-06-07 20:32:05"
71 | sofa.GLOBAL_DateModified = "2019-06-07 20:32:05"
72 | sofa.GLOBAL_Title = ""
73 | sofa.GLOBAL_DatabaseName = ""
74 | sofa.GLOBAL_RoomDescription = ""
75 | sofa.ListenerPosition_Type = "cartesian"
76 | sofa.ListenerPosition_Units = "metre"
77 | sofa.ListenerView_Type = "cartesian"
78 | sofa.ReceiverPosition_Type = "cartesian"
79 | sofa.ReceiverPosition_Units = "metre"
80 | sofa.SourcePosition_Type = "cartesian"
81 | sofa.SourcePosition_Units = "metre"
82 | sofa.SourceView_Type = "cartesian"
83 | sofa.EmitterPosition_Type = "cartesian"
84 | sofa.EmitterPosition_Units = "metre"
85 | sofa.Data_SamplingRate_Units = "hertz"
86 |
87 | # ----- Non-Mandatory attributes -----
88 | sofa.GLOBAL_ApplicationName = None
89 | sofa.GLOBAL_ApplicationVersion = None
90 | sofa.GLOBAL_History = None
91 | sofa.GLOBAL_References = None
92 | sofa.GLOBAL_Origin = None
93 |
94 |
95 | """
96 | =============================== Double Variables ==============================
97 | """
98 |
99 | # ----- Mandatory double variables -----
100 |
101 | # Needs dimensions IC or MC
102 | sofa.ListenerPosition = np.zeros(1)
103 |
104 | # Needs dimensions IC or MC
105 | sofa.ListenerUp = np.zeros(1)
106 |
107 | # Needs dimensions IC or MC
108 | sofa.ListenerView = np.zeros(1)
109 |
110 | # Needs dimensions rCI or rCM
111 | sofa.ReceiverPosition = np.zeros(1)
112 |
113 | # Needs dimensions IC or MC
114 | sofa.SourcePosition = np.zeros(1)
115 |
116 | # Needs dimensions IC or MC
117 | sofa.SourceUp = np.zeros(1)
118 |
119 | # Needs dimensions IC or MC
120 | sofa.SourceView = np.zeros(1)
121 |
122 | # Needs dimensions eCI or eCM
123 | sofa.EmitterPosition = np.zeros(1)
124 |
125 | # Needs dimensions mRn
126 | sofa.Data_IR = np.zeros(1)
127 |
128 | # Needs dimensions I
129 | sofa.Data_SamplingRate = np.zeros(1)
130 |
131 | # Needs dimensions IR or MR
132 | sofa.Data_Delay = np.zeros(1)
133 |
134 | # ----- Non-mandatory double variables -----
135 |
136 |
137 | """
138 | =============================== Export ========================================
139 | """
140 |
141 | # Save file upon completion
142 | sofa.export("filename")
143 |
--------------------------------------------------------------------------------
/Templates/MultiSpeakerBRIR_1.0_0.3.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (c) 2018, I.Laghidze
5 | #
6 | # All rights reserved.
7 | #
8 | # Redistribution and use in source and binary forms, with or without
9 | # modification, are permitted provided that the following conditions are met:
10 | #
11 | # * Redistributions of source code must retain the above copyright notice,
12 | # this list of conditions and the following disclaimer.
13 | # * Redistributions in binary form must reproduce the above copyright
14 | # notice, this list of conditions and the following disclaimer in the
15 | # documentation and/or other materials provided with the distribution.
16 | # * Neither the name of SOFASonix nor the names of its contributors
17 | # may be used to endorse or promote products derived from this software
18 | # without specific prior written permission.
19 | #
20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | #
32 | # =============================================================================
33 | #
34 | # File: MultiSpeakerBRIR_1.0_0.3.py
35 | # Project: SOFASonix
36 | # Author: I.Laghidze
37 | # License: BSD 3
38 | #
39 | # =============================================================================
40 |
41 | from SOFASonix import SOFAFile
42 | import numpy as np
43 |
44 | """
45 | =============================== Initial Config ================================
46 | """
47 |
48 | # Create SOFAFile object with the latest MultiSpeakerBRIR convention
49 | sofa = SOFAFile("MultiSpeakerBRIR", sofaConventionsVersion=0.3, version=1.0)
50 |
51 | # Set dimensions
52 | sofa._M = 100
53 | sofa._N = 1024
54 | sofa._R = 2
55 | sofa._E = 4
56 |
57 | # View parameters of convention
58 | sofa.view()
59 |
60 |
61 | """
62 | =============================== Attributes ====================================
63 | """
64 |
65 | # ----- Mandatory attributes -----
66 | sofa.GLOBAL_AuthorContact = ""
67 | sofa.GLOBAL_Comment = ""
68 | sofa.GLOBAL_License = "No license provided, ask the author for permission"
69 | sofa.GLOBAL_Organization = ""
70 | sofa.GLOBAL_RoomType = "reverberant"
71 | sofa.GLOBAL_DateCreated = "2019-06-07 20:32:05"
72 | sofa.GLOBAL_DateModified = "2019-06-07 20:32:05"
73 | sofa.GLOBAL_Title = ""
74 | sofa.GLOBAL_DatabaseName = ""
75 | sofa.GLOBAL_ListenerShortName = ""
76 | sofa.ListenerPosition_Type = "cartesian"
77 | sofa.ListenerPosition_Units = "metre"
78 | sofa.ListenerView_Type = "cartesian"
79 | sofa.ListenerView_Units = "metre"
80 | sofa.ReceiverPosition_Type = "cartesian"
81 | sofa.ReceiverPosition_Units = "metre"
82 | sofa.SourcePosition_Type = "spherical"
83 | sofa.SourcePosition_Units = "degree, degree, metre"
84 | sofa.EmitterPosition_Type = "cartesian"
85 | sofa.EmitterPosition_Units = "metre"
86 | sofa.Data_SamplingRate_Units = "hertz"
87 |
88 | # ----- Non-Mandatory attributes -----
89 | sofa.GLOBAL_ApplicationName = None
90 | sofa.GLOBAL_ApplicationVersion = None
91 | sofa.GLOBAL_History = None
92 | sofa.GLOBAL_References = None
93 | sofa.GLOBAL_Origin = None
94 | sofa.GLOBAL_RoomDescription = None
95 | sofa.EmitterView_Type = None
96 | sofa.EmitterView_Units = None
97 |
98 |
99 | """
100 | =============================== Double Variables ==============================
101 | """
102 |
103 | # ----- Mandatory double variables -----
104 |
105 | # Needs dimensions IC or MC
106 | sofa.ListenerPosition = np.zeros(1)
107 |
108 | # Needs dimensions IC or MC
109 | sofa.ListenerUp = np.zeros(1)
110 |
111 | # Needs dimensions IC or MC
112 | sofa.ListenerView = np.zeros(1)
113 |
114 | # Needs dimensions rCI or rCM
115 | sofa.ReceiverPosition = np.zeros(1)
116 |
117 | # Needs dimensions IC or MC
118 | sofa.SourcePosition = np.zeros(1)
119 |
120 | # Needs dimensions eCI or eCM
121 | sofa.EmitterPosition = np.zeros(1)
122 |
123 | # Needs dimensions mREn
124 | sofa.Data_IR = np.zeros(1)
125 |
126 | # Needs dimensions I
127 | sofa.Data_SamplingRate = np.zeros(1)
128 |
129 | # Needs dimensions IRE or MRE
130 | sofa.Data_Delay = np.zeros(1)
131 |
132 | # ----- Non-mandatory double variables -----
133 |
134 | # Needs dimensions ECI or ECM
135 | sofa.EmitterUp = None
136 |
137 | # Needs dimensions ECI or ECM
138 | sofa.EmitterView = None
139 |
140 |
141 | """
142 | =============================== Export ========================================
143 | """
144 |
145 | # Save file upon completion
146 | sofa.export("filename")
147 |
--------------------------------------------------------------------------------
/Templates/MusicalInstrumentDirectivity_1.0_1.0.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (c) 2018, I.Laghidze
5 | #
6 | # All rights reserved.
7 | #
8 | # Redistribution and use in source and binary forms, with or without
9 | # modification, are permitted provided that the following conditions are met:
10 | #
11 | # * Redistributions of source code must retain the above copyright notice,
12 | # this list of conditions and the following disclaimer.
13 | # * Redistributions in binary form must reproduce the above copyright
14 | # notice, this list of conditions and the following disclaimer in the
15 | # documentation and/or other materials provided with the distribution.
16 | # * Neither the name of SOFASonix nor the names of its contributors
17 | # may be used to endorse or promote products derived from this software
18 | # without specific prior written permission.
19 | #
20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | #
32 | # =============================================================================
33 | #
34 | # File: MusicalInstrumentDirectivity_1.0_1.0.py
35 | # Project: SOFASonix
36 | # Author: I.Laghidze
37 | # License: BSD 3
38 | #
39 | # =============================================================================
40 |
41 | from SOFASonix import SOFAFile
42 | import numpy as np
43 |
44 | """
45 | =============================== Initial Config ================================
46 | """
47 |
48 | # Create SOFAFile object with the latest MusicalInstrumentDirectivity convention
49 | sofa = SOFAFile("MusicalInstrumentDirectivity", sofaConventionsVersion=1.0, version=1.0)
50 |
51 | # Set dimensions
52 | sofa._M = 100
53 | sofa._N = 1024
54 | sofa._R = 2
55 | sofa._E = 4
56 |
57 | # View parameters of convention
58 | sofa.view()
59 |
60 |
61 | """
62 | =============================== Attributes ====================================
63 | """
64 |
65 | # ----- Mandatory attributes -----
66 | sofa.GLOBAL_AuthorContact = ""
67 | sofa.GLOBAL_Comment = ""
68 | sofa.GLOBAL_License = "No license provided, ask the author for permission"
69 | sofa.GLOBAL_Organization = ""
70 | sofa.GLOBAL_RoomType = "free field"
71 | sofa.GLOBAL_DateCreated = "2019-06-07 20:32:05"
72 | sofa.GLOBAL_DateModified = "2019-06-07 20:32:05"
73 | sofa.GLOBAL_Title = ""
74 | sofa.GLOBAL_InstrumentType = ""
75 | sofa.GLOBAL_InstrumentManufacturer = ""
76 | sofa.GLOBAL_Musician = ""
77 | sofa.GLOBAL_MusicianPosition = ""
78 | sofa.N_LongName = "frequency"
79 | sofa.N_Units = "hertz"
80 | sofa.ListenerPosition_Type = "cartesian"
81 | sofa.ListenerPosition_Units = "metre"
82 | sofa.ReceiverPosition_Type = "cartesian"
83 | sofa.ReceiverPosition_Units = "metre"
84 | sofa.SourcePosition_Type = "spherical"
85 | sofa.SourcePosition_Units = "degree, degree, metre"
86 | sofa.SourcePosition_Definition = ""
87 | sofa.SourceView_Type = "spherical"
88 | sofa.SourceView_Units = "degree, degree, metre"
89 | sofa.SourceView_Definition = ""
90 | sofa.SourceUp_Type = "spherical"
91 | sofa.SourceUp_Units = "degree, degree, metre"
92 | sofa.SourceUp_Definition = ""
93 | sofa.EmitterPosition_Type = "cartesian"
94 | sofa.EmitterPosition_Units = "degree, degree, metre"
95 |
96 | # ----- Non-Mandatory attributes -----
97 | sofa.GLOBAL_ApplicationName = None
98 | sofa.GLOBAL_ApplicationVersion = None
99 | sofa.GLOBAL_History = None
100 | sofa.GLOBAL_References = None
101 | sofa.GLOBAL_Origin = None
102 | sofa.EmitterDescription = None
103 | sofa.TuningFrequency_LongName = None
104 | sofa.TuningFrequency_Units = None
105 |
106 |
107 | """
108 | =============================== Double Variables ==============================
109 | """
110 |
111 | # ----- Mandatory double variables -----
112 |
113 | # Needs dimensions N or NE
114 | sofa.N = np.zeros(1)
115 |
116 | # Needs dimensions IC
117 | sofa.ListenerPosition = np.zeros(1)
118 |
119 | # Needs dimensions rCI
120 | sofa.ReceiverPosition = np.zeros(1)
121 |
122 | # Needs dimensions IC
123 | sofa.SourcePosition = np.zeros(1)
124 |
125 | # Needs dimensions IC
126 | sofa.SourceView = np.zeros(1)
127 |
128 | # Needs dimensions IC
129 | sofa.SourceUp = np.zeros(1)
130 |
131 | # Needs dimensions eCI
132 | sofa.EmitterPosition = np.zeros(1)
133 |
134 | # Needs dimensions mREn
135 | sofa.Data_Real = np.zeros(1)
136 |
137 | # Needs dimensions MREN
138 | sofa.Data_Imag = np.zeros(1)
139 |
140 | # ----- Non-mandatory double variables -----
141 |
142 | # Needs dimensions E
143 | sofa.EmitterMidiNote = None
144 |
145 | # Needs dimensions I or E
146 | sofa.TuningFrequency = None
147 |
148 |
149 | """
150 | =============================== Export ========================================
151 | """
152 |
153 | # Save file upon completion
154 | sofa.export("filename")
155 |
--------------------------------------------------------------------------------
/Templates/SimpleHeadphoneIR_1.0_0.2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (c) 2018, I.Laghidze
5 | #
6 | # All rights reserved.
7 | #
8 | # Redistribution and use in source and binary forms, with or without
9 | # modification, are permitted provided that the following conditions are met:
10 | #
11 | # * Redistributions of source code must retain the above copyright notice,
12 | # this list of conditions and the following disclaimer.
13 | # * Redistributions in binary form must reproduce the above copyright
14 | # notice, this list of conditions and the following disclaimer in the
15 | # documentation and/or other materials provided with the distribution.
16 | # * Neither the name of SOFASonix nor the names of its contributors
17 | # may be used to endorse or promote products derived from this software
18 | # without specific prior written permission.
19 | #
20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | #
32 | # =============================================================================
33 | #
34 | # File: SimpleHeadphoneIR_1.0_0.2.py
35 | # Project: SOFASonix
36 | # Author: I.Laghidze
37 | # License: BSD 3
38 | #
39 | # =============================================================================
40 |
41 | from SOFASonix import SOFAFile
42 | import numpy as np
43 |
44 | """
45 | =============================== Initial Config ================================
46 | """
47 |
48 | # Create SOFAFile object with the latest SimpleHeadphoneIR convention
49 | sofa = SOFAFile("SimpleHeadphoneIR", sofaConventionsVersion=0.2, version=1.0)
50 |
51 | # Set dimensions
52 | sofa._M = 100
53 | sofa._N = 1024
54 |
55 | # View parameters of convention
56 | sofa.view()
57 |
58 |
59 | """
60 | =============================== Attributes ====================================
61 | """
62 |
63 | # ----- Mandatory attributes -----
64 | sofa.GLOBAL_AuthorContact = ""
65 | sofa.GLOBAL_Comment = ""
66 | sofa.GLOBAL_License = "No license provided, ask the author for permission"
67 | sofa.GLOBAL_Organization = ""
68 | sofa.GLOBAL_RoomType = "free field"
69 | sofa.GLOBAL_DateCreated = "2019-06-07 20:32:05"
70 | sofa.GLOBAL_DateModified = "2019-06-07 20:32:05"
71 | sofa.GLOBAL_Title = ""
72 | sofa.GLOBAL_DatabaseName = ""
73 | sofa.GLOBAL_ListenerShortName = ""
74 | sofa.GLOBAL_ListenerDescription = ""
75 | sofa.GLOBAL_SourceDescription = ""
76 | sofa.GLOBAL_SourceManufacturer = ""
77 | sofa.GLOBAL_SourceModel = ""
78 | sofa.GLOBAL_SourceURI = ""
79 | sofa.GLOBAL_ReceiverDescription = ""
80 | sofa.GLOBAL_EmitterDescription = ""
81 | sofa.ListenerPosition_Type = "cartesian"
82 | sofa.ListenerPosition_Units = "metre"
83 | sofa.ReceiverPosition_Type = "cartesian"
84 | sofa.ReceiverPosition_Units = "metre"
85 | sofa.SourcePosition_Type = "spherical"
86 | sofa.SourcePosition_Units = "degree, degree, metre"
87 | sofa.EmitterPosition_Type = "cartesian"
88 | sofa.EmitterPosition_Units = "metre"
89 | sofa.Data_SamplingRate_Units = "hertz"
90 |
91 | # ----- Non-Mandatory attributes -----
92 | sofa.GLOBAL_ApplicationName = None
93 | sofa.GLOBAL_ApplicationVersion = None
94 | sofa.GLOBAL_History = None
95 | sofa.GLOBAL_References = None
96 | sofa.GLOBAL_Origin = None
97 |
98 |
99 | """
100 | =============================== String Variables ==============================
101 | """
102 |
103 | # ----- Mandatory string variables -----
104 |
105 | # ----- Non-mandatory string variables -----
106 |
107 | # Needs dimensions MS
108 | sofa.ReceiverDescription = None
109 |
110 | # Needs dimensions MS
111 | sofa.SourceManufacturer = None
112 |
113 | # Needs dimensions MS
114 | sofa.SourceModel = None
115 |
116 | # Needs dimensions MS
117 | sofa.EmitterDescription = None
118 |
119 |
120 | """
121 | =============================== Double Variables ==============================
122 | """
123 |
124 | # ----- Mandatory double variables -----
125 |
126 | # Needs dimensions IC or MC
127 | sofa.ListenerPosition = np.zeros(1)
128 |
129 | # Needs dimensions rCI or rCM
130 | sofa.ReceiverPosition = np.zeros(1)
131 |
132 | # Needs dimensions IC or MC
133 | sofa.SourcePosition = np.zeros(1)
134 |
135 | # Needs dimensions eCI or eCM
136 | sofa.EmitterPosition = np.zeros(1)
137 |
138 | # Needs dimensions mRn
139 | sofa.Data_IR = np.zeros(1)
140 |
141 | # Needs dimensions I
142 | sofa.Data_SamplingRate = np.zeros(1)
143 |
144 | # Needs dimensions IR or MR
145 | sofa.Data_Delay = np.zeros(1)
146 |
147 | # ----- Non-mandatory double variables -----
148 |
149 | # Needs dimensions M
150 | sofa.MeasurementDate = None
151 |
152 |
153 | """
154 | =============================== Export ========================================
155 | """
156 |
157 | # Save file upon completion
158 | sofa.export("filename")
159 |
--------------------------------------------------------------------------------
/Templates/FreeFieldDirectivityTF_1.0_0.1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (c) 2018, I.Laghidze
5 | #
6 | # All rights reserved.
7 | #
8 | # Redistribution and use in source and binary forms, with or without
9 | # modification, are permitted provided that the following conditions are met:
10 | #
11 | # * Redistributions of source code must retain the above copyright notice,
12 | # this list of conditions and the following disclaimer.
13 | # * Redistributions in binary form must reproduce the above copyright
14 | # notice, this list of conditions and the following disclaimer in the
15 | # documentation and/or other materials provided with the distribution.
16 | # * Neither the name of SOFASonix nor the names of its contributors
17 | # may be used to endorse or promote products derived from this software
18 | # without specific prior written permission.
19 | #
20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | #
32 | # =============================================================================
33 | #
34 | # File: FreeFieldDirectivityTF_1.0_0.1.py
35 | # Project: SOFASonix
36 | # Author: I.Laghidze
37 | # License: BSD 3
38 | #
39 | # =============================================================================
40 |
41 | from SOFASonix import SOFAFile
42 | import numpy as np
43 |
44 | """
45 | =============================== Initial Config ================================
46 | """
47 |
48 | # Create SOFAFile object with the latest FreeFieldDirectivityTF convention
49 | sofa = SOFAFile("FreeFieldDirectivityTF", sofaConventionsVersion=0.1, version=1.0)
50 |
51 | # Set dimensions
52 | sofa._M = 100
53 | sofa._N = 1024
54 | sofa._R = 2
55 | sofa._E = 4
56 |
57 | # View parameters of convention
58 | sofa.view()
59 |
60 |
61 | """
62 | =============================== Attributes ====================================
63 | """
64 |
65 | # ----- Mandatory attributes -----
66 | sofa.GLOBAL_AuthorContact = ""
67 | sofa.GLOBAL_Comment = ""
68 | sofa.GLOBAL_License = "No license provided, ask the author for permission"
69 | sofa.GLOBAL_Organization = ""
70 | sofa.GLOBAL_RoomType = "free field"
71 | sofa.GLOBAL_DateCreated = "2019-06-07 20:32:05"
72 | sofa.GLOBAL_DateModified = "2019-06-07 20:32:05"
73 | sofa.GLOBAL_Title = ""
74 | sofa.GLOBAL_InstrumentType = ""
75 | sofa.GLOBAL_InstrumentManufacturer = ""
76 | sofa.GLOBAL_Musician = ""
77 | sofa.ListenerPosition_Type = "cartesian"
78 | sofa.ListenerPosition_Units = "metre"
79 | sofa.ListenerView_Type = "spherical"
80 | sofa.ListenerView_Units = "degree, degree, metre"
81 | sofa.ListenerUp_Type = "spherical"
82 | sofa.ListenerUp_Units = "degree, degree, metre"
83 | sofa.ReceiverPosition_Type = "cartesian"
84 | sofa.ReceiverPosition_Units = "metre"
85 | sofa.SourcePosition_Type = "cartesian"
86 | sofa.SourcePosition_Units = "metre"
87 | sofa.SourcePosition_Reference = ""
88 | sofa.SourceView_Type = "spherical"
89 | sofa.SourceView_Units = "degree, degree, metre"
90 | sofa.SourceView_Reference = ""
91 | sofa.SourceUp_Type = "spherical"
92 | sofa.SourceUp_Units = "degree, degree, metre"
93 | sofa.SourceUp_Reference = ""
94 | sofa.EmitterPosition_Type = "cartesian"
95 | sofa.EmitterPosition_Units = "degree, degree, metre"
96 | sofa.N_LongName = "frequency"
97 | sofa.N_Units = "Hertz"
98 |
99 | # ----- Non-Mandatory attributes -----
100 | sofa.GLOBAL_ApplicationName = None
101 | sofa.GLOBAL_ApplicationVersion = None
102 | sofa.GLOBAL_History = None
103 | sofa.GLOBAL_References = None
104 | sofa.GLOBAL_Origin = None
105 | sofa.MIDINoteDescription = None
106 |
107 |
108 | """
109 | =============================== Double Variables ==============================
110 | """
111 |
112 | # ----- Mandatory double variables -----
113 |
114 | # Needs dimensions N
115 | sofa.N = np.zeros(1)
116 |
117 | # Needs dimensions IC or MC
118 | sofa.ListenerPosition = np.zeros(1)
119 |
120 | # Needs dimensions IC or MC
121 | sofa.ListenerView = np.zeros(1)
122 |
123 | # Needs dimensions IC or MC
124 | sofa.ListenerUp = np.zeros(1)
125 |
126 | # Needs dimensions rCI or rCM
127 | sofa.ReceiverPosition = np.zeros(1)
128 |
129 | # Needs dimensions IC or MC
130 | sofa.SourcePosition = np.zeros(1)
131 |
132 | # Needs dimensions IC or MC
133 | sofa.SourceView = np.zeros(1)
134 |
135 | # Needs dimensions IC or MC
136 | sofa.SourceUp = np.zeros(1)
137 |
138 | # Needs dimensions eCI
139 | sofa.EmitterPosition = np.zeros(1)
140 |
141 | # Needs dimensions mRn
142 | sofa.Data_Real = np.zeros(1)
143 |
144 | # Needs dimensions MRN
145 | sofa.Data_Imag = np.zeros(1)
146 |
147 | # ----- Non-mandatory double variables -----
148 |
149 | # Needs dimensions I or M
150 | sofa.MIDINote = None
151 |
152 | # Needs dimensions I or M
153 | sofa.TuningFrequency = None
154 |
155 |
156 | """
157 | =============================== Export ========================================
158 | """
159 |
160 | # Save file upon completion
161 | sofa.export("filename")
162 |
--------------------------------------------------------------------------------
/SOFASonix/SOFATemplate.py:
--------------------------------------------------------------------------------
1 | from .SOFASonix import SOFASonix
2 |
3 |
4 | def SOFATemplate(convention,
5 | sofaConventionsVersion=False,
6 | version=False,
7 | path=False,
8 | useNone=True,
9 | testMode=False):
10 | # Create SOFAFile
11 | sofa = SOFASonix(convention, sofaConventionsVersion, version)
12 | convention = sofa.global_sofaconventions
13 |
14 | # Obtain parameters and sort by attributes, doubles and strings
15 | attributes = sofa.flatten()
16 |
17 | # Extract doubles.
18 | doubles = {k: attributes.pop(k) for k in list(attributes.keys())
19 | if attributes[k].isType("double")}
20 |
21 | # Extract strings
22 | strings = {k: attributes.pop(k) for k in list(attributes.keys())
23 | if attributes[k].isType("string")}
24 |
25 | # Create file and begin writing
26 | if(path):
27 | name = "{}.py".format(path)
28 | else:
29 | name = "{}_{}_{}.py".format(convention, sofa.GLOBAL_Version,
30 | sofa.GLOBAL_SOFAConventionsVersion)
31 | file = open(name, "w")
32 |
33 | # Write Licensing Info
34 | file.write("""#!/usr/bin/env python
35 | # -*- coding: utf-8 -*-
36 | #
37 | # Copyright (c) 2018, I.Laghidze
38 | #
39 | # All rights reserved.
40 | #
41 | # Redistribution and use in source and binary forms, with or without
42 | # modification, are permitted provided that the following conditions are met:
43 | #
44 | # * Redistributions of source code must retain the above copyright notice,
45 | # this list of conditions and the following disclaimer.
46 | # * Redistributions in binary form must reproduce the above copyright
47 | # notice, this list of conditions and the following disclaimer in the
48 | # documentation and/or other materials provided with the distribution.
49 | # * Neither the name of SOFASonix nor the names of its contributors
50 | # may be used to endorse or promote products derived from this software
51 | # without specific prior written permission.
52 | #
53 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
54 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
55 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
56 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
57 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
58 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
59 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
60 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
61 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
62 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
63 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64 | #
65 | # =============================================================================
66 | #
67 | # File: {}
68 | # Project: SOFASonix
69 | # Author: I.Laghidze
70 | # License: BSD 3
71 | #
72 | # =============================================================================
73 |
74 | """.format(name.split("/")[-1]))
75 |
76 |
77 | headerString = """from SOFASonix import SOFAFile
78 | import numpy as np
79 |
80 | \"""
81 | =============================== Initial Config ================================
82 | \"""
83 |
84 | # Create SOFAFile object with the latest {} convention
85 | sofa = SOFAFile("{}", sofaConventionsVersion={}, version={})
86 | """.format(convention, convention,
87 | sofa.GLOBAL_SOFAConventionsVersion, sofa.GLOBAL_Version)
88 |
89 | file.write(headerString)
90 | file.write("\n# Set dimensions\n")
91 |
92 | # Default dimensions
93 | dims = {"M": 100, "N": 1024, "R": 2, "E": 4}
94 |
95 | for dim in dims:
96 | if(not sofa.dims[dim]["ro"]):
97 | file.write("sofa._{} = {}\n".format(dim, dims[dim]))
98 |
99 | # Add view parameters code
100 | file.write("""
101 | # View parameters of convention
102 | sofa.view()
103 | """)
104 |
105 | # Create attributes separator
106 | file.write("""\n\n\"\"\"
107 | =============================== Attributes ====================================
108 | \"\"\"\n""")
109 |
110 | # Generate mandatory attributes
111 | file.write("\n# ----- Mandatory attributes -----\n")
112 | for key, ai in attributes.items():
113 | if(not ai.isReadOnly() and ai.isRequired()):
114 | if(ai.value):
115 | file.write("sofa.{} = \"{}\"".format(ai.getShorthandName(),
116 | ai.value))
117 | else:
118 | if(testMode):
119 | file.write("sofa.{} = \"TestMode\"".format(ai.getShorthandName()))
120 | else:
121 | file.write("sofa.{} = \"\"".format(ai.getShorthandName()))
122 | file.write("\n")
123 |
124 | # Generate non-mandatory attributes
125 | file.write("\n# ----- Non-Mandatory attributes -----\n")
126 | for key, ai in attributes.items():
127 | if(not ai.isReadOnly() and not ai.isRequired()):
128 | if(useNone):
129 | file.write("sofa.{} = None".format(ai.getShorthandName(),
130 | ai.value))
131 | else:
132 | if(ai.value):
133 | file.write("sofa.{} = \"{}\"".format(ai.getShorthandName(),
134 | ai.value))
135 | else:
136 | if(testMode):
137 | file.write("sofa.{} = \"TestMode\"".format(ai.getShorthandName()))
138 | else:
139 | file.write("sofa.{} = \"\"".format(ai.getShorthandName()))
140 | file.write("\n")
141 |
142 | if(strings):
143 | # Create strings separator
144 | file.write("""\n\n\"""
145 | =============================== String Variables ==============================
146 | \"""\n""")
147 |
148 | # Generate mandatory strings
149 | file.write("\n# ----- Mandatory string variables -----\n")
150 | for key, si in strings.items():
151 | if(not si.isReadOnly() and si.isRequired()):
152 | file.write("\n# Needs dimensions {}\n".format(" or ".join(si.dimensions)))
153 | if(testMode):
154 | dims = ", ".join(["sofa._{}".format(i) for i in si.dimensions[0]])
155 | file.write("sofa.{} = np.zeros(({}))".format(si.getShorthandName(), dims))
156 | else:
157 | file.write("sofa.{} = np.zeros(1)".format(si.getShorthandName()))
158 | file.write("\n")
159 |
160 | # Generate non-mandatory strings
161 | file.write("\n# ----- Non-mandatory string variables -----\n")
162 | for key, si in strings.items():
163 | if(not si.isReadOnly() and not si.isRequired()):
164 | file.write("\n# Needs dimensions {}\n".format(" or ".join(si.dimensions)))
165 | if(useNone):
166 | file.write("sofa.{} = None".format(si.getShorthandName()))
167 | else:
168 | if(testMode):
169 | dims = ", ".join(["sofa._{}".format(i) for i in si.dimensions[0]])
170 | file.write("sofa.{} = np.zeros(({}))".format(si.getShorthandName(), dims))
171 | else:
172 | file.write("sofa.{} = np.zeros(1)".format(si.getShorthandName()))
173 | file.write("\n")
174 |
175 | # Create doubles separator
176 | file.write("""\n\n\"""
177 | =============================== Double Variables ==============================
178 | \"""\n""")
179 |
180 | # Generate mandatory doubles
181 | file.write("\n# ----- Mandatory double variables -----\n")
182 | for key, di in doubles.items():
183 | if(not di.isReadOnly() and di.isRequired()):
184 | file.write("\n# Needs dimensions {}\n".format(" or ".join(di.dimensions)))
185 | if(testMode):
186 | dims = ", ".join(["sofa._{}".format(i) for i in di.dimensions[0]])
187 | file.write("sofa.{} = np.zeros(({}))".format(di.getShorthandName(), dims))
188 | else:
189 | file.write("sofa.{} = np.zeros(1)".format(di.getShorthandName()))
190 | file.write("\n")
191 |
192 | # Generate non-mandatory doubles
193 | file.write("\n# ----- Non-mandatory double variables -----\n")
194 | for key, di in doubles.items():
195 | if(not di.isReadOnly() and not di.isRequired()):
196 | file.write("\n# Needs dimensions {}\n".format(" or ".join(di.dimensions)))
197 | if(useNone):
198 | file.write("sofa.{} = None".format(di.getShorthandName()))
199 | else:
200 | if(testMode):
201 | dims = ", ".join(["sofa._{}".format(i) for i in di.dimensions[0]])
202 | file.write("sofa.{} = np.zeros(({}))".format(di.getShorthandName(), dims))
203 | else:
204 | file.write("sofa.{} = np.zeros(1)".format(di.getShorthandName()))
205 | file.write("\n")
206 |
207 | # Create export separator
208 | file.write("""\n\n\"""
209 | =============================== Export ========================================
210 | \"""\n""")
211 | # Finally, create save file statement
212 | file.write("""
213 | # Save file upon completion
214 | sofa.export("filename")\n""")
215 |
--------------------------------------------------------------------------------
/SOFASonix/SOFASonixField.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (c) 2018, I.Laghidze
5 | #
6 | # All rights reserved.
7 | #
8 | # Redistribution and use in source and binary forms, with or without
9 | # modification, are permitted provided that the following conditions are met:
10 | #
11 | # * Redistributions of source code must retain the above copyright notice,
12 | # this list of conditions and the following disclaimer.
13 | # * Redistributions in binary form must reproduce the above copyright
14 | # notice, this list of conditions and the following disclaimer in the
15 | # documentation and/or other materials provided with the distribution.
16 | # * Neither the name of SOFASonix nor the names of its contributors
17 | # may be used to endorse or promote products derived from this software
18 | # without specific prior written permission.
19 | #
20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | #
32 | # =============================================================================
33 | #
34 | # File: SOFASonixField.py
35 | # Project: SOFASonix
36 | # Author: I.Laghidze
37 | # License: BSD 3
38 | #
39 | # =============================================================================
40 |
41 | from .SOFASonixError import SOFAFieldError, SOFAError
42 | import numpy as np
43 |
44 |
45 | class SOFASonixField:
46 | def __init__(self, parent, name, pclass, units, params):
47 | self.name = name
48 | self.parameter_class = pclass
49 | self.parent = parent
50 | self.units = units
51 | # Set parameters
52 | for key, value in params.items():
53 | setattr(self, key, value)
54 | # Create parameter for string data type
55 | # to store padded null valued arrays
56 | if(self.isType("string")):
57 | self.paddedValue = value
58 |
59 | def isType(self, type_str):
60 | return self.type.lower() == str(type_str).lower()
61 |
62 | def inClass(self, class_str):
63 | return self.parameter_class.lower() == str(class_str).lower()
64 |
65 | def isReadOnly(self):
66 | return True if self.ro else False
67 |
68 | def isRequired(self):
69 | return True if self.required else False
70 |
71 | def isTimestamp(self):
72 | return True if self.timestamp else False
73 |
74 | def isEmpty(self):
75 | size = len(self.value) if self.isType("attribute") or\
76 | self.isType("string") else self.value.size
77 | return True if size == 0 else False
78 |
79 | def checkRequiredStatus(self):
80 | if(self.isEmpty() and self.isRequired()):
81 | raise SOFAFieldError(("'{}' is a required parameter."
82 | ).format(self.name))
83 |
84 | def checkRequirements(self):
85 | if(self.requires):
86 | if(self.requires["type"] == "regular"):
87 | # Check if all required fields have been filled in
88 | for field in self.requires["fields"]:
89 | if(self.parent.getParam(field, True).isEmpty()):
90 | raise SOFAFieldError(("'{}' requires '{}' to be "
91 | "filled in"
92 | ).format(self.name, field))
93 |
94 | elif(self.requires["type"] == "conditional"):
95 | for value, fields in self.requires["fields"].items():
96 | if(self.value.lower() == value.lower()):
97 | for field in fields:
98 | if(self.parent.getParam(field, True).isEmpty()):
99 | raise SOFAFieldError(("'{}' requires '{}' to "
100 | "be filled in if its "
101 | "value is {}"
102 | ).format(self.name,
103 | field,
104 | self.value))
105 | else:
106 | raise SOFAFieldError(("Invalid requirements type for "
107 | "'{}'").format(self.name))
108 |
109 | def _matchUnitsHelper(self, values, restrictions):
110 | passed = False
111 | for valueSet in restrictions:
112 | if(len(valueSet) == len(values)):
113 | zipValues = zip(values, valueSet)
114 | # Assume a positive match
115 | match = all([v in self.units[c]
116 | for v, c in zipValues])
117 | if(match):
118 | # Validation has passed
119 | passed = True
120 | break
121 | return passed
122 |
123 | def _matchDims(self):
124 | if((self.isType("double") or self.isType("string"))
125 | and self.inClass("__unclassed") and not self.dimensions):
126 | # Try to match dimensions
127 | dimensions = ""
128 | for num in self.value.shape:
129 | for dim, dimParams in self.parent.dims.items():
130 | if(dimParams["value"] == num):
131 | dimensions += dim
132 | # Only one dimension may correspond to each shape value
133 | break
134 | if(len(dimensions) != len(self.value.shape)):
135 | raise SOFAError("Unable to import correct dimensions "
136 | "for parameter {}".format(self.name))
137 | else:
138 | self.dimensions = [dimensions]
139 |
140 | def checkValueConstraints(self):
141 | if(self.value_restrictions):
142 | if(self.value_restrictions["type"] == "regular"):
143 | if(self.value.lower() not in
144 | self.value_restrictions["values"]):
145 | raise SOFAFieldError(("'{}' accepts only the following "
146 | "values:{}\nYou have: '{}'"
147 | ).format(self.name,
148 | "\n- ".join(
149 | self.
150 | value_restrictions
151 | ["values"]),
152 | self.value))
153 |
154 | elif(self.value_restrictions["type"] == "units"):
155 | # Obtain restricted valueSets
156 | restrictions = self.value_restrictions["values"]
157 | # Split units by commas
158 | values = [i.strip() for i in self.value.split(",")]
159 | lengths = [len(i) for i in restrictions]
160 |
161 | # Valid units allowed - generate error message if needed
162 | validUnitSets = []
163 | for valueSet in restrictions:
164 | validUnitSet = [self.units[v][0] for v in valueSet]
165 | validUnitSets.append(", ".join(validUnitSet))
166 | errormsg = ("Units for '{}' can only be one of the following:"
167 | "{}").format(self.name, "\n- ".join(validUnitSets))
168 |
169 | # Check whether the correct # of units have been supplied
170 | if(len(values) not in lengths):
171 | raise SOFAFieldError(errormsg)
172 | else:
173 | # Check if units are matched
174 | passed = self._matchUnitsHelper(values, restrictions)
175 | if(not passed):
176 | raise SOFAFieldError(errormsg)
177 |
178 | elif(self.value_restrictions["type"] == "conditional_units"):
179 | # Obtain the parameter to check for the condition
180 | externalParam = self.parent.getParam(self.value_restrictions
181 | ["conditional_field"],
182 | True)
183 | try:
184 | # See if the parameter value has units to be matched
185 | idx = [i.lower() for i in self.value_restrictions["values"]
186 | ].index(externalParam.value.lower())
187 | key = list(self.value_restrictions["values"])[idx]
188 |
189 | # Obtain restricted value sets
190 | restrictions = self.value_restrictions["values"][key]
191 |
192 | # Split units by commas
193 | values = [i.strip() for i in self.value.split(",")]
194 | lengths = [len(i) for i in restrictions]
195 |
196 | # Valid units allowed - generate error message if needed
197 | validUnitSets = []
198 | for valueSet in restrictions:
199 | validUnitSet = [self.units[v][0] for v in valueSet]
200 | validUnitSets.append(", ".join(validUnitSet))
201 | errormsg = ("Units for '{}' can only be one of the "
202 | "following if '{}' is supplied as a value for"
203 | "'{}':{}"
204 | ).format(self.name,
205 | "\n- ".join(validUnitSets),
206 | key,
207 | externalParam.name)
208 |
209 | # Check whether the correct # of units have been supplied
210 | if(len(values) not in lengths):
211 | raise SOFAFieldError(errormsg)
212 | else:
213 | # Check if units are matched
214 | passed = self._matchUnitsHelper(values, restrictions)
215 | if(not passed):
216 | raise SOFAFieldError(errormsg)
217 |
218 | # If value of field is not in conditional_units, ignore
219 | except Exception:
220 | pass
221 |
222 | else:
223 | raise SOFAFieldError(("Invalid value_restrictions type for "
224 | "'{}'").format(self.name))
225 |
226 | def checkDimensionsLength(self, prospective=False):
227 | value = prospective if(type(prospective) == np.ndarray
228 | and prospective.size) else self.value
229 | shapeLength = len(value.shape)
230 | # Get dimension length
231 | dimensionsLength = len(self.dimensions[0])
232 |
233 | if(shapeLength != dimensionsLength):
234 | raise SOFAFieldError(("'{}' is expecting a {}-dimensional input "
235 | "({})\nYou supplied: {} dimensions"
236 | ).format(self.name, dimensionsLength,
237 | self.dimensions, shapeLength))
238 |
239 | def checkDimensions(self):
240 | if(self.isType("double") or self.isType("string")):
241 | # Check if not force-input without dimensions matching
242 | if(self.dimensions):
243 | # Check dimensions length check
244 | self.checkDimensionsLength()
245 |
246 | # Perform dimensions check
247 | match = False
248 | for dSet in self.dimensions:
249 | values = [self.parent.getDim(dim) for
250 | dim in dSet]
251 | if(list(self.value.shape) == values):
252 | match = True
253 | break
254 | # If dimensions are not matched, raise exception
255 | if(not match):
256 | raise SOFAFieldError(("Incorrect dimensions for '{}'\n"
257 | "Required: {}\n"
258 | "Current: {}"
259 | ).format(self.name, self.dimensions,
260 | list(self.value.shape)))
261 |
262 | def getDimensions(self):
263 | for dimlist in self.dimensions:
264 | dims = [self.parent.getDim(d_i) for d_i in dimlist]
265 | if(dims == list(self.value.shape)):
266 | return [i.upper() for i in dimlist]
267 | raise ValueError("Cannot get dimensions")
268 |
269 | def getShorthandName(self):
270 | return self.name.replace(":", "_").replace(".", "_")
271 |
--------------------------------------------------------------------------------
/SOFASonix/SOFASonix.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (c) 2018, I.Laghidze
5 | #
6 | # All rights reserved.
7 | #
8 | # Redistribution and use in source and binary forms, with or without
9 | # modification, are permitted provided that the following conditions are met:
10 | #
11 | # * Redistributions of source code must retain the above copyright notice,
12 | # this list of conditions and the following disclaimer.
13 | # * Redistributions in binary form must reproduce the above copyright
14 | # notice, this list of conditions and the following disclaimer in the
15 | # documentation and/or other materials provided with the distribution.
16 | # * Neither the name of SOFASonix nor the names of its contributors
17 | # may be used to endorse or promote products derived from this software
18 | # without specific prior written permission.
19 | #
20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | #
32 | # =============================================================================
33 | #
34 | # File: SOFASonix.py
35 | # Project: SOFASonix
36 | # Author: I.Laghidze
37 | # License: BSD 3
38 | #
39 | # =============================================================================
40 |
41 | import netCDF4
42 | import numpy as np
43 | import json
44 | import sqlite3
45 | import pandas as pd
46 | import datetime
47 | import os
48 | import gc
49 | from .SOFASonixField import SOFASonixField
50 | from .SOFASonixError import SOFAError, SOFAFieldError
51 |
52 |
53 | class SOFASonix(object):
54 | APIName = "SOFASonix"
55 | API_VERSION_MAJOR = 1
56 | API_VERSION_MINOR = 0
57 | API_VERSION_PATCH = 8
58 | DBFile = "ss_db.db"
59 |
60 | def __init__(self, conv,
61 | sofaConventionsVersion=False,
62 | version=False,
63 | load=False,
64 | verbose=True,
65 | **dims):
66 |
67 | # Create DB Path
68 | if(os.path.isabs(SOFASonix.DBFile)):
69 | self.dbpath = SOFASonix.DBFile
70 | else:
71 | try:
72 | cwdpath = os.path.dirname(os.path.realpath(__file__))
73 | except NameError:
74 | cwdpath = os.path.dirname(os.path.realpath('__file__'))
75 | finally:
76 | self.dbpath = "{}/{}".format(cwdpath, SOFASonix.DBFile)
77 |
78 | # Set verbose
79 | self.verbose = True if verbose else False
80 |
81 | # Return convention data if valid params supplied.
82 | self.convention = self._getConvention(conv, sofaConventionsVersion,
83 | version)
84 | self.modified = False # Check whether convention has been modified
85 |
86 | # Get dimensions
87 | self.dims = json.loads(self.convention.pop("dimensions"))
88 |
89 | # Override dimensions if provided
90 | [self.setDim(dim.upper(), dims[dim]) for dim in dims]
91 |
92 | # Get convention parameters
93 | self.params = self._getParams(self.convention["id"])
94 |
95 | # Assign application information
96 | self.getParam("GLOBAL:APIName",
97 | True).value = SOFASonix.APIName
98 | self.getParam("GLOBAL:APIVersion",
99 | True).value = SOFASonix.version()
100 |
101 | # Check if creating new SOFA file or loading an existing one
102 | if(load is False):
103 | # Attempt to automatically fill date fields if they exist
104 | try:
105 | # Date timestamps
106 | self.getParam("GLOBAL:DateCreated", True).value = self._time()
107 | # Date modified - Updated on SAVE
108 | self.getParam("GLOBAL:DateModified", True).value = self._time()
109 | except SOFAFieldError:
110 | pass
111 |
112 | def __setattr__(self, name, value):
113 | # Quick set dimensions if supplied.
114 | if (name[0] == "_"):
115 | self.setDim(name.strip("_"), value)
116 | else:
117 | # Quick set parameters if supplied.
118 | params = {i.getShorthandName().lower(): i
119 | for i in self.flatten().values()}
120 | nameTrim = name.lower()
121 |
122 | if(hasattr(self, "params") and nameTrim in params):
123 | param = params[nameTrim]
124 | # Perform shorthand removal if value is None
125 | if(value is None):
126 | self.deleteParam(param.name)
127 | else:
128 | self.setParam(param.name, value)
129 | # Otherwise assign normally
130 | else:
131 | super(SOFASonix, self).__setattr__(name, value)
132 |
133 | def __getattr__(self, name):
134 | error = ("'{}' object has no attribute '{}'"
135 | ).format(self.__class__.__name__, name)
136 | # Retrieve dimensiojs if exists
137 | if(name[0] == "_" and "dims" in self.__dict__):
138 | try:
139 | return self.getDim(name.strip("_"))
140 | except Exception:
141 | pass
142 | # Retrieve parameter if exists.
143 | elif("params" in self.__dict__):
144 | params = [i.replace(":", "_").replace(".",
145 | "_").lower() for i in self.flatten()]
146 | nameTrim = name.lower()
147 | if(nameTrim in params):
148 | return self.getParam(list(self.flatten().keys())
149 | [params.index(nameTrim)])
150 | raise AttributeError(error)
151 |
152 | def _time(self):
153 | return datetime.datetime.now().replace(
154 | microsecond=0).isoformat().replace("T", " ")
155 |
156 | @staticmethod
157 | def version():
158 | return "{}.{}.{}".format(SOFASonix.API_VERSION_MAJOR,
159 | SOFASonix.API_VERSION_MINOR,
160 | SOFASonix.API_VERSION_PATCH)
161 |
162 | def _getData(self, query):
163 | db = None
164 | try:
165 | db = sqlite3.connect(self.dbpath)
166 | cursor = db.cursor()
167 | cursor.execute(query)
168 | data = cursor.fetchall()
169 | except Exception as e:
170 | if(db is not None):
171 | db.rollback()
172 | raise e
173 | finally:
174 | db.close()
175 | return data
176 |
177 | def _updateStrings(self):
178 | # Get all string parameter values
179 | strings = {k: i for k, i in self.flatten().items() if
180 | (i.isType("string")
181 | and not i.isEmpty())}
182 | stringSizes = [len(i.value) for i in strings.items()]
183 |
184 | # Get the largest string value and update dimension S
185 | if(len(stringSizes)):
186 | self.setDim("S", max(stringSizes)[-1])
187 |
188 | # Pad all string arrays
189 | for k in strings:
190 | amountToPad = self.getDim("S") - strings[k].value.shape[-1]
191 | if(amountToPad > 0):
192 | # Pad the appropriate amount of null characters to each string
193 | padamount = np.zeros((len(strings[k].shape), 2),
194 | dtype=np.int64)
195 | padamount[-1][-1] = amountToPad
196 | self.getParam(k, True).paddedValue = np.pad(strings[k].value,
197 | amountToPad,
198 | 'constant')
199 |
200 | def _getConvention(self, conv, sofaConventionsVersion,
201 | version):
202 | conventionData = self._getData("""SELECT convention_names.name as name,
203 | conventions.id,
204 | version,
205 | spec_version,
206 | standard,
207 | dimensions,
208 | data_group.name
209 |
210 | FROM conventions
211 |
212 | INNER JOIN data_group
213 | on conventions.datagroup=
214 | data_group.id
215 | INNER JOIN convention_names
216 | on conventions.convention=
217 | convention_names.id
218 |
219 | WHERE
220 | lower(convention_names.name)
221 | ='{}'
222 | """.format(conv.lower()))
223 |
224 | # If supplied convention is in the database, check versions.
225 | if(conventionData):
226 | conventionName = conventionData[0][0]
227 | if(sofaConventionsVersion and version):
228 | try:
229 | convention = conventionData[np.argwhere(
230 | np.array([i[2] + i[3] for i in conventionData])
231 | == sofaConventionsVersion + version)[0][0]]
232 | except Exception:
233 | raise SOFAError(("Incompatible SOFAConventionsVersion ({})"
234 | " and spec version ({}) supplied. The "
235 | "following pairs are available for "
236 | "'{}':\n\n{}"
237 | ).format(sofaConventionsVersion,
238 | version,
239 | conventionName,
240 | "\n".join([("- Version: {}"
241 | ", Spec Version: "
242 | "{}").format(i[2],
243 | i[3])
244 | for i in
245 | conventionData])))
246 | elif(sofaConventionsVersion):
247 | try:
248 | # Retrieve row indices with the supplied convention version
249 | versionIndices = np.argwhere(np.array([i[2] for i in
250 | conventionData])
251 | == sofaConventionsVersion
252 | )[:, 0]
253 |
254 | # Strip invalid convention entries
255 | validConventionData = [conventionData[i] for i
256 | in versionIndices]
257 |
258 | # Get the convention data with the latest specVersion
259 | convention = validConventionData[
260 | np.argmax([i[3] for i in validConventionData])]
261 |
262 | except Exception:
263 | available = np.unique([i[2] for i
264 | in conventionData]).astype(str)
265 | raise SOFAError("Invalid SOFAConventionsVersion ({}). The "
266 | "following SOFAConventionsVersion values "
267 | "are available for '{}':\n\n- {}".format(
268 | sofaConventionsVersion,
269 | conventionName,
270 | "\n- ".join(available)))
271 |
272 | elif(version):
273 | try:
274 | # Retrieve row indices with the supplied convention version
275 | specVersionIndices = np.argwhere(np.array([i[3] for i in
276 | conventionData])
277 | == version)[:, 0]
278 |
279 | # Strip invalid convention entries
280 | validConventionData = [conventionData[i] for i
281 | in specVersionIndices]
282 |
283 | # Get the convention ID with the latest convention version
284 | convention = validConventionData[
285 | np.argmax([i[2] for i in validConventionData])]
286 |
287 | except Exception:
288 | available = np.unique([i[3] for i
289 | in conventionData]).astype(str)
290 | raise SOFAError("Invalid spec version ({}). The following"
291 | " spec versions are available for "
292 | "'{}':\n\n- {}".format(
293 | version,
294 | conventionName,
295 | "\n- ".join(available)))
296 | else:
297 | # Assign latest SOFA version and Convention Version
298 | convention = conventionData[np.argmax([i[2] + i[3]
299 | for i in conventionData])]
300 |
301 | # Otherwise raise a ValueError and display available conventions.
302 | else:
303 | conventions = [k[0] for k in
304 | self._getData("SELECT name FROM convention_names")]
305 | raise SOFAError(("Convention '{}' not found in conventions. "
306 | "Please supply one of the following "
307 | "conventions:\n\n- {}"
308 | ).format(conv, "\n- ".join(conventions)))
309 | keys = ["name", "id", "SOFAConventionsVersion", "spec_version",
310 | "standard",
311 | "dimensions", "data_group"]
312 | return dict(zip(keys, convention))
313 |
314 | def _getUnits(self):
315 | units = dict(self._getData("""SELECT name, aliases FROM units"""))
316 |
317 | # Unserialize unit lists and add name
318 | for key, unitSet in units.items():
319 | units[key] = json.loads(unitSet)
320 | units[key].append(key)
321 | return units
322 |
323 | def _getParams(self, convention_id):
324 | parameters = self._getData("""SELECT field.name,
325 | value,
326 | ro,
327 | required,
328 | dimensions,
329 | description,
330 | parameter_type.name,
331 | parameter_class.name,
332 | properties
333 | FROM field
334 | INNER JOIN parameter_type
335 | on field.type = parameter_type.id
336 | INNER JOIN parameter_class
337 | on field.field_class
338 | = parameter_class.id
339 | WHERE convention={}
340 | """.format(int(convention_id)))
341 |
342 | units = self._getUnits()
343 |
344 | parsedParams = {}
345 | paramKeys = ["name", "value", "ro", "required", "dimensions",
346 | "description", "type", "class", "properties"]
347 | for i in parameters:
348 | pi = dict(zip(paramKeys, i))
349 | pc = pi.pop("class")
350 |
351 | # Add parameter class as dictionary category
352 | if(pc not in parsedParams):
353 | parsedParams[pc] = {}
354 |
355 | # Unserialize data where necessary
356 | if(pi["type"] in ["double", "string"]):
357 | pi["value"] = np.array(json.loads(pi["value"])) \
358 | if pi["value"] else np.array([])
359 |
360 | pi["dimensions"] = json.loads(pi["dimensions"])
361 | properties = json.loads(pi.pop("properties"))
362 |
363 | # Create a SOFASonixField object
364 | name = pi.pop("name")
365 | fieldParams = {}
366 | fieldParams.update(pi)
367 | fieldParams.update(properties)
368 | parsedParams[pc][name] = SOFASonixField(self, name, pc, units,
369 | fieldParams)
370 |
371 | return parsedParams
372 |
373 | def attributes(self):
374 | for key, parameter in self.flatten().items():
375 | if (not parameter.isReadOnly() and
376 | parameter.isType("attribute") and
377 | not parameter.isTimestamp()):
378 | value = parameter.value
379 | text = "Parameter: '{}' | Required: {}\n".format(
380 | key, "Yes" if parameter.isRequired() else "No")
381 | if (value):
382 | text += ("Current value (leave "
383 | "blank to preserve): \n{}\n").format(value)
384 | paramInput = input(text)
385 | if(paramInput == "" and value):
386 | continue
387 | else:
388 | self.setParam(key, str(paramInput))
389 |
390 | def flatten(self):
391 | flat = {}
392 | if(hasattr(self, "params")):
393 | {flat.update(category) for category in self.params.values()}
394 | return flat
395 |
396 | @staticmethod
397 | def load(file, verbose=True):
398 | gc.collect()
399 | raw = netCDF4.Dataset(file, "r", "NETCDF4")
400 | # Try to find a convention
401 | try:
402 | convention = raw.SOFAConventions
403 | version = float(raw.SOFAConventionsVersion)
404 | specversion = float(raw.Version)
405 | except Exception:
406 | raise SOFAError("Invalid SOFA file. No convention specified.")
407 |
408 | # Create a convention file.
409 | sofa = SOFASonix(convention, version, specversion, load=True,
410 | verbose=verbose)
411 |
412 | # Set dimensions if applicable
413 | for dim in raw.dimensions:
414 | if(dim in sofa.dims.keys()):
415 | sofa.setDim(dim, len(raw.dimensions[dim]), force=True)
416 |
417 | # Populate with datasets and attributes - single dimension (sufficient)
418 | for key in raw.variables:
419 | # Empty check
420 | if(raw[key].shape is not None):
421 | dat = np.array(raw[key][:].tolist())
422 | sofa.setParam(key, dat, force=True)
423 | # Check for attributes
424 | for attr in raw[key].ncattrs():
425 | attribute = getattr(raw[key], attr)
426 | if(attribute):
427 | paramName = "{}:{}".format(key, attr)
428 | try:
429 | sofa.setParam(paramName,
430 | attribute,
431 | force=True)
432 | except Exception as e:
433 | raise Exception(e)
434 |
435 | # Match dimension strings for unclassed params
436 | if("__unclassed" in sofa.params):
437 | for param in sofa.params["__unclassed"].values():
438 | param._matchDims()
439 |
440 | # Now set global attributes
441 | for attr in raw.ncattrs():
442 | # Empty check
443 | attribute = getattr(raw, attr)
444 | if(attribute):
445 | try:
446 | sofa.setParam("GLOBAL:{}".format(attr),
447 | attribute,
448 | force=True)
449 | except Exception as e:
450 | raise Exception(e)
451 |
452 | # If modified (foreign parameters), add modified to convention name
453 | if(sofa.modified):
454 | sofa.getParam("GLOBAL:SOFAConventions").value += " (modified)"
455 |
456 | # Close h5py file and return SOFASonix object
457 | raw.close()
458 | del raw
459 | return sofa
460 |
461 | def getDim(self, dim):
462 | dim = dim.upper()
463 | if(dim in self.dims):
464 | return self.dims[dim]["value"]
465 | else:
466 | raise SOFAError("Dimension '{}' does not exist")
467 |
468 | def setDim(self, dim, value, force=False):
469 | dim = dim.upper()
470 | if(dim in self.dims):
471 | if (self.dims[dim]["ro"] and force is False):
472 | raise SOFAError("Dimension '{}' is read-only.".format(dim))
473 | else:
474 | error = ("Invalid dimensions for '{}'. Please"
475 | " supply a positive integer").format(dim)
476 | try:
477 | numeric = int(value)
478 | if(numeric < 0):
479 | raise SOFAError(error)
480 | self.dims[dim]["value"] = numeric
481 | except Exception:
482 | raise SOFAError(error)
483 | else:
484 | raise SOFAError("Dimension '{}' doesn't exist".format(dim))
485 |
486 | def getParam(self, key, obj=False):
487 | params = self.flatten()
488 | if key in params:
489 | if(obj):
490 | return params[key]
491 | else:
492 | return params[key].value
493 | else:
494 | raise SOFAFieldError("Field '{}' was not found".format(key))
495 |
496 | def setParam(self, key, value, force=False):
497 | params = self.flatten()
498 | if key in params:
499 | param = params[key]
500 | if(not param.isReadOnly() or force):
501 | # Attribute
502 | if(param.isType("attribute")):
503 | param.value = str(value)
504 | # Dealing with STRING (placeholder)
505 | elif(param.isType("string")):
506 | if(type(value) in [list, np.ndarray]):
507 | length = len(value) if type(value) == list\
508 | else value.size
509 | if(length):
510 | if(type(value) == list):
511 | # Check if all elements are strings
512 | allStrings = all([type(v) == str
513 | for v in value])
514 | if(not allStrings):
515 | raise SOFAFieldError(("Submitted list data"
516 | " for '{}' must only"
517 | " contain strings"
518 | ).format(key))
519 |
520 | # Create a numpy array with nullspaces
521 | maxS = max(map(len, value))
522 | # Create an array to store the values
523 | stringArray = np.zeros((len(value), maxS),
524 | dtype=np.string_)
525 | # Append values
526 | for i in value:
527 | iv = list(i)
528 | stringArray[:len(iv)] = iv
529 |
530 | # Store string array
531 | param.value = stringArray
532 | param.paddedValue = stringArray
533 |
534 | # Otherwise assume a character array
535 | else:
536 | if(value.dtype != "S1"):
537 | raise SOFAFieldError(("Array submitted for"
538 | " parameter '{}'"
539 | " must be a "
540 | "character array of "
541 | "dtype 'S1'"
542 | ).format(key))
543 | param.value = value
544 | param.paddedValue = stringArray
545 |
546 | else:
547 | raise SOFAFieldError(("Empty array or list "
548 | "submitted for parameter "
549 | "'{}'").format(key))
550 | # Update dimension S and pad all character arrays
551 | self._updateStrings()
552 | else:
553 | raise SOFAFieldError(("Parameter '{}' of type string "
554 | "must either have a list of "
555 | "strings or a numpy character"
556 | "array.").format(key))
557 | # Double
558 | elif(param.isType("double")):
559 | if(type(value) in [list, np.ndarray]):
560 | value = np.array(value)
561 | # Check if array is numeric
562 | if(np.issubdtype(value.dtype, np.number)):
563 | # Check number of dimensions
564 | param.checkDimensionsLength(value)
565 |
566 | # Add value if length has passed
567 | param.value = value
568 |
569 | # Check if parameter defines dimensions
570 | for i, dim in enumerate(param.dimensions[0]):
571 | # If lowercase, define dim (assumption [0])
572 | if(dim.lower() == dim):
573 | if(self.verbose):
574 | print(("Setting dimension '{}' from "
575 | "parameter '{}'"
576 | ).format(dim, param.name))
577 | # Bypass any exceptions if dim is RO
578 | try:
579 | self.setDim(dim, value.shape[i],
580 | force=force)
581 | except Exception:
582 | pass
583 | else:
584 | raise SOFAFieldError(("Parameter '{}' of type "
585 | "'double' cannot contain "
586 | "non-numeric list data."
587 | ).format(key))
588 | else:
589 | raise SOFAFieldError(("Parameter '{}' is of type "
590 | "'double'. Only python lists "
591 | "and numpy arrays are valid "
592 | "inputs.").format(key))
593 | else:
594 | raise SOFAFieldError("Invalid parameter type for '{}'"
595 | .format(key))
596 | else:
597 | raise SOFAFieldError(("Cannot edit parameter '{}'. "
598 | "Read only.").format(key))
599 | # Force insertion of foreign parameter -- use for load ONLY
600 | elif force:
601 | if(self.verbose):
602 | print("Inserting foreign parameter: '{}'".format(key))
603 | # Python2 fix
604 | try:
605 | basetype = unicode
606 | except NameError:
607 | basetype = str
608 |
609 | # Find out parameter type
610 | if(type(value) in [str, basetype, bytes]):
611 | inputType = "attribute"
612 | inputDims = 0
613 | else:
614 | # Disable dims check unless _matchDims() is called on param
615 | inputDims = 0
616 | try:
617 | value = np.array(value)
618 | except Exception:
619 | raise SOFAFieldError("""Invalid parameter input. Please "
620 | "insert a valid string, list or "
621 | "numpy array.""")
622 | # Check if character array or numerical
623 | if(np.issubdtype(value.dtype, np.string_)):
624 | inputType = "string"
625 | else:
626 | inputType = "double"
627 | # If an unclassed variable is introduced for the first time
628 | if("__unclassed" not in self.params):
629 | self.params["__unclassed"] = {}
630 | # Also append (modified) to convention name (switched off)
631 | self.modified = False
632 | params = {"type": inputType,
633 | "value": value,
634 | "properties": {"requires": 0,
635 | "value_restrictions": 0},
636 | "required": 0,
637 | "dimensions": inputDims,
638 | "ro": 0}
639 | properties = params.pop("properties")
640 | fieldParams = {}
641 | fieldParams.update(params)
642 | fieldParams.update(properties)
643 | self.params["__unclassed"][key] = SOFASonixField(self, key,
644 | "__unclassed",
645 | self._getUnits(),
646 | fieldParams)
647 |
648 | else:
649 | raise SOFAError("Invalid parameter supplied for convention: '{}'"
650 | .format(self.convention["name"]))
651 |
652 | def deleteParam(self, param):
653 | deleteList = []
654 | # Search for parameters to remove
655 | for category in self.params.keys():
656 | for field in self.params[category].values():
657 | # Remove parameter
658 | if(field.name == param):
659 | if(field.isRequired()):
660 | raise SOFAFieldError("Parameter '{}' is a required"
661 | " parameter and cannot be "
662 | "removed!".format(field.name))
663 | else:
664 | deleteList.append([category, field.name])
665 | if(self.verbose):
666 | print("Removed '{}'".format(field.name))
667 | # Remove any associated attributes if applicable
668 | if(field.name.startswith("{}:".format(param))):
669 | if(field.isRequired()):
670 | print("WARNING: Associated attribute '{}' is a "
671 | "required parameter and cannot "
672 | "be automatically removed!".format(field.name))
673 | else:
674 | if(self.verbose):
675 | print("Removed associated attribute '{}'"
676 | .format(field.name))
677 | deleteList.append([category, field.name])
678 | if(len(deleteList)):
679 | for pair in deleteList:
680 | del self.params[pair[0]][pair[1]]
681 | else:
682 | print("No parameter '{}' found to delete.".format(param))
683 |
684 | def validate(self, category=False):
685 | params = self.params[category].items() if category else\
686 | self.flatten().items()
687 | for key, param in params:
688 | # Run required check]
689 | param.checkRequiredStatus()
690 |
691 | if(not param.isEmpty()):
692 | param.checkDimensions()
693 | param.checkRequirements()
694 | param.checkValueConstraints()
695 |
696 | def export(self, filename):
697 | # Perform field-by-field validation
698 | for category in self.params:
699 | self.validate(category)
700 |
701 | # Set new DateModified value if it exists
702 | try:
703 | self.getParam("GLOBAL:DateModified", True).value = self._time()
704 | except SOFAFieldError:
705 | pass
706 |
707 | # Create file and attempt saving.
708 | file = netCDF4.Dataset("{}.sofa".format(filename), "w",
709 | format="NETCDF4")
710 |
711 | try:
712 | # Create dimensions
713 | for dim in self.dims.keys():
714 | file.createDimension(dim, self.dims[dim]["value"])
715 |
716 | attributes = self.flatten()
717 | # Extract doubles.
718 | doubles = {k: attributes.pop(k) for k in list(attributes.keys())
719 | if attributes[k].isType("double")}
720 |
721 | # Extract strings
722 | strings = {k: attributes.pop(k) for k in list(attributes.keys())
723 | if attributes[k].isType("string")}
724 |
725 | # Create all doubles first.
726 | for key, element in doubles.items():
727 | if(not element.isEmpty()):
728 | var = file.createVariable(key, "f8",
729 | element.getDimensions())
730 | var[:] = element.value
731 |
732 | # Create strings
733 | for key, element in strings.items():
734 | if(not element.isEmpty()):
735 | var = file.createVariable(key, "S1",
736 | element.getDimensions())
737 | var[:] = element.paddedValue
738 |
739 | # Create attributes
740 | for key, element in attributes.items():
741 | variable, attrname = key.split(":") if (":" in key)\
742 | else ["global", key]
743 | # For global attributes, create in root
744 | if(key in self.params["global"] or
745 | variable.lower() == "global"):
746 | setattr(file, attrname, element.value)
747 | # Otherwise create the attribute within the variable
748 | else:
749 | setattr(file[variable], attrname, element.value)
750 | file.close()
751 | except Exception:
752 | # Close file if errors encountered and re-raise exception.
753 | file.close()
754 | raise
755 |
756 | def view(self):
757 | cols = ["Shorthand", "Type", "Value", "RO", "M", "Dims"]
758 | rows = []
759 | params = self.flatten()
760 | for i in params:
761 | pi = params[i]
762 | value = "{} Array".format(pi.value.shape) if \
763 | (pi.isType("double") or pi.isType("string")) else pi.value
764 | rows.append([".{}".format(pi.getShorthandName()),
765 | pi.type[0].upper(),
766 | value,
767 | pi.isReadOnly(),
768 | pi.isRequired(),
769 | str(pi.dimensions) if pi.dimensions else "_"])
770 | pd.set_option('display.max_colwidth', 40)
771 | pd.set_option('display.expand_frame_repr', False)
772 |
773 | df = pd.DataFrame(rows, columns=cols)
774 | print(df)
775 |
--------------------------------------------------------------------------------