├── 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 | ![Python](https://img.shields.io/badge/Python%20Support-2.7%2C%203.4%2C%203.5%2C%203.6%2C%203.7-green.svg) 4 | ![License](https://img.shields.io/badge/License-BSD%203-orange.svg) 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 | --------------------------------------------------------------------------------