def test_orientations_from_view_up(): """Create `Orientations` from view and up vectors.""" # test with single view and up vectors view = [1, 0, 0] up = [0, 1, 0] Orientations.from_view_up(view, up) # test with multiple view and up vectors views = [[1, 0, 0], [0, 0, 1]] ups = [[0, 1, 0], [0, 1, 0]] Orientations.from_view_up(views, ups) # provided as numpy ndarrays views = np.atleast_2d(views).astype(np.float64) ups = np.atleast_2d(ups).astype(np.float64) Orientations.from_view_up(views, ups) # provided as Coordinates views = Coordinates(views[:, 0], views[:, 1], views[:, 2]) ups = Coordinates(ups[:, 0], ups[:, 1], ups[:, 2]) Orientations.from_view_up(views, ups) # number of views to ups N:1 views = [[1, 0, 0], [0, 0, 1]] ups = [[0, 1, 0]] Orientations.from_view_up(views, ups) # number of views to ups 1:N views = [[1, 0, 0]] ups = [[0, 1, 0], [0, 1, 0]] Orientations.from_view_up(views, ups) # number of views to ups M:N with raises(ValueError): views = [[1, 0, 0], [0, 0, 1], [0, 0, 1]] ups = [[0, 1, 0], [0, 1, 0]] Orientations.from_view_up(views, ups)
def test_getter_with_degrees(): """Test if getter return correct values also in degrees""" coords = Coordinates(0, 1, 0) sph = coords.get_sph(unit="deg") npt.assert_allclose(sph, np.atleast_2d([90, 90, 1])) cyl = coords.get_cyl(unit="deg") npt.assert_allclose(cyl, np.atleast_2d([90, 0, 1]))
def test_coordinates_init_default_convention_and_unit(): """Test initialization with the default convention and untit.""" # get list of available coordinate systems coords = Coordinates() systems = coords._systems() # test constructor with all systems, units, and convention=None for domain in systems: Coordinates(0, 0, 0, domain)
def test_coordinates_init_val_and_weights(): """Test initialization with weights.""" # correct number of weights coords = Coordinates([1, 2], 0, 0, weights=[.5, .5]) assert isinstance(coords, Coordinates) npt.assert_allclose(coords.weights, [.5, .5]) # incorrect number of weights with raises(AssertionError): Coordinates([1, 2], 0, 0, weights=.5)
def test_getitem(): """Test getitem with different parameters.""" # test without weights coords = Coordinates([1, 2], 0, 0) new = coords[0] assert isinstance(new, Coordinates) npt.assert_allclose(new.get_cart(), np.atleast_2d([1, 0, 0])) # test with weights coords = Coordinates([1, 2], 0, 0, weights=[.1, .9]) new = coords[0] assert isinstance(new, Coordinates) npt.assert_allclose(new.get_cart(), np.atleast_2d([1, 0, 0])) assert new.weights == np.array(.1) # test with 3D array coords = Coordinates([[1, 2, 3, 4, 5], [2, 3, 4, 5, 6]], 0, 0) new = coords[0:1] assert isinstance(new, Coordinates) assert new.cshape == (1, 5) # test if sliced object stays untouched coords = Coordinates([0, 1], [0, 1], [0, 1]) new = coords[0] new.set_cart(2, 2, 2) assert coords.cshape == (2, ) npt.assert_allclose(coords.get_cart()[0], np.array([0, 0, 0]))
def test_csize(): """Test the csize attribute.""" # 0 points coords = Coordinates() assert coords.csize == 0 # two points coords = Coordinates([1, 0], 1, 1) assert coords.csize == 2 # 6 points in two dimensions coords = Coordinates([[1, 2, 3], [4, 5, 6]], 1, 1) assert coords.csize == 6
def test_cdim(): """Test the csim attribute.""" # empty coords = Coordinates() assert coords.cdim == 0 # 2D points coords = Coordinates([1, 0], 1, 1) assert coords.cdim == 1 # 3D points coords = Coordinates([[1, 2, 3], [4, 5, 6]], 1, 1) assert coords.cdim == 2
def test_cshape(): """Test the cshape attribute.""" # empty coords = Coordinates() assert coords.cshape == (0, ) # 2D points coords = Coordinates([1, 0], [1, 1], [0, 1]) assert coords.cshape == (2, ) # 3D points coords = Coordinates([[1, 2, 3], [4, 5, 6]], 1, 1) assert coords.cshape == (2, 3)
def test_coordinates_init_default_convention(): """Test initialization with the default convention.""" # get list of available coordinate systems coords = Coordinates() systems = coords._systems() # test constructor with all systems, units, and convention=None for domain in systems: convention = list(systems[domain])[0] for unit in systems[domain][convention]['units']: Coordinates(0, 0, 0, domain, unit=unit[0][0:3])
def test___eq___differInUnit_notEqual(): coordinates = Coordinates([1, 1], [1, 1], [1, 1], convention='top_colat', domain='sph', unit='rad') actual = Coordinates([1, 1], [1, 1], [1, 1], convention='top_colat', domain='sph', unit='deg') is_equal = coordinates == actual assert not is_equal
def test_coordinates_init_val_and_system(): """Test initialization with all available coordinate systems.""" # get list of available coordinate systems coords = Coordinates() systems = coords._systems() # test constructor with all systems for domain in systems: for convention in systems[domain]: for unit in systems[domain][convention]['units']: Coordinates(0, 0, 0, domain, convention, unit[0][0:3])
def test_show(): """Test if possible calls of show() pass.""" coords = Coordinates([-1, 0, 1], 0, 0) # show without mask coords.show() # show with mask as list coords.show([1, 0, 1]) # show with mask as ndarray coords.show(np.array([1, 0, 1], dtype=bool)) # test assertion with raises(AssertionError): coords.show(np.array([1, 0], dtype=bool)) plt.close("all")
def test_setter_and_getter_with_conversion(): """Test conversion between coordinate systems using the default unit.""" # get list of available coordinate systems coords = Coordinates() systems = coords._systems() # test points contained in system definitions points = [ 'positive_x', 'positive_y', 'positive_z', 'negative_x', 'negative_y', 'negative_z' ] # test setter and getter with all systems and default unit for domain_in in list(systems): for convention_in in list(systems[domain_in]): for domain_out in list(systems): for convention_out in list(systems[domain_out]): for point in points: # for debugging print(f"{domain_in}({convention_in}) -> " f"{domain_out}({convention_out}): {point}") # in and out points p_in = systems[domain_in][convention_in][point] p_out = systems[domain_out][convention_out][point] # empty object c = Coordinates() # --- set point --- eval(f"c.set_{domain_in}(p_in[0], p_in[1], p_in[2], \ '{convention_in}')") # check point p = c._points npt.assert_allclose(p.flatten(), p_in, atol=1e-15) # --- test without conversion --- p = eval(f"c.get_{domain_out}('{convention_out}')") # check internal and returned point npt.assert_allclose(c._points.flatten(), p_in, atol=1e-15) npt.assert_allclose(p.flatten(), p_out, atol=1e-15) # check if system was converted assert c._system["domain"] == domain_in assert c._system["convention"] == convention_in # --- test with conversion --- p = eval(f"c.get_{domain_out}('{convention_out}', \ convert=True)") # check point npt.assert_allclose(p.flatten(), p_out, atol=1e-15) # check if system was converted assert c._system["domain"] == domain_out assert c._system["convention"] == convention_out
def sphericalvoronoi(): """ SphericalVoronoi object. """ points = np.array([[0, 0, 1], [0, 0, -1], [1, 0, 0], [0, 1, 0], [0, -1, 0], [-1, 0, 0]]) sampling = Coordinates(points[:, 0], points[:, 1], points[:, 2]) return SphericalVoronoi(sampling)
def _decode(cls, obj_dict): """Decode object based on its respective `_encode` counterpart.""" sampling = Coordinates(obj_dict['points'][:, 0], obj_dict['points'][:, 1], obj_dict['points'][:, 2], domain='cart') return cls(sampling, center=obj_dict['center'])
def test_assertion_for_getter(): """Test assertion for empty Coordinates objects""" coords = Coordinates() with raises(ValueError, match="Object is empty"): coords.get_cart() with raises(ValueError, match="Object is empty"): coords.get_sph() with raises(ValueError, match="Object is empty"): coords.get_cyl()
def test_coordinate_names(): """Test if units agree across coordinates that appear more than once""" # get all coordinate systems c = Coordinates() systems = c._systems() # get unique list of coordinates and their properties coords = {} # loop across domains and conventions for domain in systems: for convention in systems[domain]: # loop across coordinates for cc, coord in enumerate( systems[domain][convention]['coordinates']): # units of the current coordinate cur_units = [ u[cc] for u in systems[domain][convention]['units'] ] # add coordinate to coords if coord not in coords: coords[coord] = {} coords[coord]['domain'] = [domain] coords[coord]['convention'] = [convention] coords[coord]['units'] = [cur_units] else: coords[coord]['domain'].append(domain) coords[coord]['convention'].append(convention) coords[coord]['units'].append(cur_units) # check if units agree across coordinates that appear more than once for coord in coords: # get unique first entry units = coords[coord]['units'].copy() units_ref, idx = np.unique(units[0], True) units_ref = units_ref[idx] for cc in range(1, len(units)): # get next entry for comparison units_test, idx = np.unique(units[cc], True) units_test = units_test[idx] # compare assert all(units_ref == units_test), \ f"'{coord}' has units {units_ref} in "\ f"{coords[coord]['domain'][0]} "\ f"({coords[coord]['convention'][0]}) but units {units_test} "\ f"in {coords[coord]['domain'][cc]} "\ f"({coords[coord]['convention'][cc]})"
def test_inverse_rotation(): """Test the inverse rotation.""" xyz = np.concatenate((np.ones((2, 4, 1)), np.zeros( (2, 4, 1)), np.zeros((2, 4, 1))), -1) c = Coordinates(xyz[..., 0].copy(), xyz[..., 1].copy(), xyz[..., 2].copy()) c.rotate('z', 90) c.rotate('z', 90, inverse=True) npt.assert_allclose(c.get_cart(), xyz, atol=1e-15)
def test__systems(): """Test completeness of internal representation of coordinate systems.""" # get all coordinate systems coords = Coordinates() systems = coords._systems() # check object type assert isinstance(systems, dict) # check completeness of systems for domain in systems: for convention in systems[domain]: assert "description_short" in systems[domain][convention], \ f"{domain} ({convention}) is missing entry 'description_short'" assert "coordinates" in systems[domain][convention], \ f"{domain} ({convention}) is missing entry 'coordinates'" assert "units" in systems[domain][convention], \ f"{domain} ({convention}) is missing entry 'units'" assert "description" in systems[domain][convention], \ f"{domain} ({convention}) is missing entry 'description'" assert "positive_x" in systems[domain][convention], \ f"{domain} ({convention}) is missing entry 'positive_x'" assert "positive_y" in systems[domain][convention], \ f"{domain} ({convention}) is missing entry 'positive_y'" assert "negative_x" in systems[domain][convention], \ f"{domain} ({convention}) is missing entry 'negative_'" assert "negative_y" in systems[domain][convention], \ f"{domain} ({convention}) is missing entry 'negative_y'" assert "positive_z" in systems[domain][convention], \ f"{domain} ({convention}) is missing entry 'positive_z'" assert "negative_z" in systems[domain][convention], \ f"{domain} ({convention}) is missing entry 'negative_z'" for coord in systems[domain][convention]['coordinates']: assert coord in systems[domain][convention], \ f"{domain} ({convention}) is missing entry '{coord}'" assert systems[domain][convention][coord][0] in \ ["unbound", "bound", "cyclic"], \ f"{domain} ({convention}), {coord}[0] must be 'unbound', "\ "'bound', or 'cyclic'."
def test_coordinates_init_val(): """Test initializing Coordinates with values of different type and size.""" # test input: scalar c1 = 1 # test input: 2 element vectors c2 = [1, 2] # list c3 = np.asarray(c2) # flat np.array c4 = np.atleast_2d(c2) # row vector np.array c5 = np.transpose(c4) # column vector np.array # test input: 3 element vector c6 = [1, 2, 3] # test input: 2D matrix c7 = np.array([[1, 2, 3], [1, 2, 3]]) # test input: 3D matrix c8 = np.array([[[1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3]]]) # tests that have to path # input scalar coordinate Coordinates(c1, c1, c1) # input list of coordinates Coordinates(c2, c2, c2) # input scalar and lists Coordinates(c1, c2, c2) # input flat np.arrays Coordinates(c3, c3, c3) # input non flat vectors Coordinates(c3, c4, c5) # input 2D data Coordinates(c1, c1, c7) # input 3D data Coordinates(c1, c1, c8) # tests that have to fail with raises(AssertionError): Coordinates(c2, c2, c6) with raises(AssertionError): Coordinates(c6, c6, c7) with raises(AssertionError): Coordinates(c2, c2, c8)
def test_orientations_show(views, ups, positions, orientations): """ Visualize orientations via `Orientations.show()` with and without `positions`. """ # default orientation Orientations().show() # single vectors no position view = [1, 0, 0] up = [0, 1, 0] orientation_single = Orientations.from_view_up(view, up) orientation_single.show() # with position position = Coordinates(0, 1, 0) orientation_single.show(position) # multiple vectors no position orientations.show() # with matching number of positions orientations.show(positions) # select what to show orientations.show(show_views=False) orientations.show(show_ups=False) orientations.show(show_rights=False) orientations.show(show_views=False, show_ups=False) orientations.show(show_views=False, show_rights=False) orientations.show(show_ups=False, show_rights=False) orientations.show(positions=positions, show_views=False, show_ups=False) # with positions provided as Coordinates positions = np.asarray(positions) positions = Coordinates(positions[:, 0], positions[:, 1], positions[:, 2]) orientations.show(positions) # with non-matching positions positions = Coordinates(0, 1, 0) with raises(ValueError): orientations.show(positions)
def test_multiple_getter_with_conversion(): """Test output of 500 random sequential conversions.""" # test N successive coordinate conversions N = 500 # get list of available coordinate systems coords = Coordinates() systems = coords._systems() # get reference points in cartesian coordinate system points = [ 'positive_x', 'positive_y', 'positive_z', 'negative_x', 'negative_y', 'negative_z' ] pts = np.array([systems['cart']['right'][point] for point in points]) # init the system coords.set_cart(pts[:, 0], pts[:, 1], pts[:, 2]) # list of domains domains = list(systems) for ii in range(N): # randomly select a coordinate system domain = domains[np.random.randint(len(domains))] conventions = list(systems[domain]) convention = conventions[np.random.randint(len(conventions))] # convert points to selected system pts = eval(f"coords.get_{domain}('{convention}', convert=True)") # get the reference ref = np.array( [systems[domain][convention][point] for point in points]) # check npt.assert_allclose(pts, ref, atol=1e-15) # print print(f"Tolerance met in iteration {ii}")
def test_get_nearest_cart(): """Tests returns of get_nearest_cart.""" # test only 1D case since most of the code from self.get_nearest_k is used x = np.arange(6) coords = Coordinates(x, 0, 0) i, m = coords.get_nearest_cart(2.5, 0, 0, 1.5) npt.assert_allclose(i, np.array([1, 2, 3, 4])) npt.assert_allclose(m, np.array([0, 1, 1, 1, 1, 0])) # test search with empty results i, m = coords.get_nearest_cart(2.5, 0, 0, .1) assert len(i) == 0 npt.assert_allclose(m, np.array([0, 0, 0, 0, 0, 0])) # test out of range parameters with raises(AssertionError): coords.get_nearest_cart(1, 0, 0, -1)
def coordinates(): """ Coordinates object. """ return Coordinates([0, 1], [2, 3], [4, 5])
def test___eq___differInConvention_notEqual(): coordinates = Coordinates(domain='sph', convention='top_elev') actual = Coordinates(domain='sph', convention='front') assert not coordinates == actual
def test_orientations_from_view_up_show_coordinate_system_change( views, ups, positions): """ Create `Orientations` from view and up vectors in the spherical domain as well as in the carteesian domain, and visualize both to compare them manually by eye. """ # Carteesian: Visualize to manually validate orientations views = np.asarray(views) ups = np.asarray(ups) views = Coordinates(views[:, 0], views[:, 1], views[:, 2]) ups = Coordinates(ups[:, 0], ups[:, 1], ups[:, 2]) positions = np.asarray(positions) positions = Coordinates(positions[:, 0], positions[:, 1], positions[:, 2]) orient_from_cart = Orientations.from_view_up(views, ups) orient_from_cart.show(positions) # Convert to spherical: And again visualize to manually validate views.get_sph(convert=True) ups.get_sph(convert=True) positions.get_sph(convert=True) orient_from_sph = Orientations.from_view_up(views, ups) orient_from_sph.show(positions) # Check if coordinate system has not been changed by orientations assert views._system['domain'] == 'sph', ( "Coordinate system has been changed by Orientations.") assert ups._system['domain'] == 'sph', ( "Coordinate system has been changed by Orientations.") assert positions._system['domain'] == 'sph', ( "Coordinate system has been changed by Orientations.show().")
def read_sofa(filename): """ Import a SOFA file as :py:class:`~pyfar.classes.audio.Signal` object. Parameters ---------- filename : string, Path Input SOFA file (cf. [#]_, [#]_). Returns ------- signal : Signal :py:class:`~pyfar.classes.audio.Signal` object containing the data stored in `SOFA_Object.Data.IR`. `cshape` is equal to ``(number of measurements, number of receivers)``. source_coordinates : Coordinates Coordinates object containing the data stored in `SOFA_object.SourcePosition`. The domain, convention and unit are automatically matched. receiver_coordinates : Coordinates Coordinates object containing the data stored in `SOFA_object.RecevierPosition`. The domain, convention and unit are automatically matched. Notes ----- * This function is based on the python-sofa [#]_. * Currently, only SOFA files of `DataType` ``FIR`` are supported. References ---------- .. [#] https://www.sofaconventions.org .. [#] “AES69-2015: AES Standard for File Exchange-Spatial Acoustic Data File Format.”, 2015. .. [#] https://github.com/spatialaudio/python-sofa """ sofafile = sofa.Database.open(filename) # Check for DataType if sofafile.Data.Type == 'FIR': domain = 'time' data = np.asarray(sofafile.Data.IR) sampling_rate = sofafile.Data.SamplingRate.get_values() # Check for units if sofafile.Data.SamplingRate.Units != 'hertz': raise ValueError( "SamplingRate:Units" "{sofafile.Data.SamplingRate.Units} is not supported.") else: raise ValueError("DataType {sofafile.Data.Type} is not supported.") signal = Signal(data, sampling_rate, domain=domain) # Source s_values = sofafile.Source.Position.get_values() s_domain, s_convention, s_unit = _sofa_pos(sofafile.Source.Position.Type) source_coordinates = Coordinates( s_values[:, 0], s_values[:, 1], s_values[:, 2], domain=s_domain, convention=s_convention, unit=s_unit) # Receiver r_values = sofafile.Receiver.Position.get_values() r_domain, r_convention, r_unit = _sofa_pos(sofafile.Receiver.Position.Type) receiver_coordinates = Coordinates( r_values[:, 0], r_values[:, 1], r_values[:, 2], domain=r_domain, convention=r_convention, unit=r_unit) return signal, source_coordinates, receiver_coordinates
def test___eq___differInShComment_notEqual(): coordinates = Coordinates(1, 2, 3, comment="Madre mia!") actual = Coordinates(1, 2, 3, comment="Oh my woooooosh!") assert not coordinates == actual
def test___eq___differInShOrder_notEqual(): coordinates = Coordinates(1, 2, 3, sh_order=2) actual = Coordinates(1, 2, 3, sh_order=8) assert not coordinates == actual
def test___eq___differInWeigths_notEqual(): coordinates = Coordinates(1, 2, 3, weights=.5) actual = Coordinates(1, 2, 3, weights=0.0) assert not coordinates == actual