Пример #1
0
def lf_lfc(time, reflectogram, id_dir, freq, time_cat, intensity_cat, cos_cat):
    '''
    This function is used to calculate LF and LFC
    '''
    # bi-directional intensityes for LF
    intensity_cos2 = np.multiply(intensity_cat, cos_cat**2)
    # log.info(cos_cat**2)
    reflecto_cos2 = reflectogram_hist(time, time_cat, intensity_cos2)
    # bi-directional intensityes for LFC
    intensity_cosabs = np.multiply(intensity_cat, np.abs(cos_cat))
    reflecto_cosabs = reflectogram_hist(time, time_cat, intensity_cosabs)

    ## The next two lines get direct sound without any information from source and receiver
    LF = np.zeros(freq.size, dtype = np.float32)
    LFC = np.zeros(freq.size, dtype = np.float32)
    jf = 0
    # log.info("time dir: {}".format(time[id_dir]))
    id_0to80 = np.where(time <= 0.080 + time[id_dir])
    # id_0to800 = np.where(time[id_0to80[0]] >= time[id_0to80[0]])
    # log.info("time 0-80: {}".format(time[id_0to800[0]]))
    id_5to80 = np.where(time[id_0to80[0]] >= 0.005 + time[id_dir])
    # log.info("time 5-80: {}".format(time[id_5to80[0]]))
    for jref, ref in enumerate(reflectogram):
        np.seterr(divide = 'ignore')
        try:
            refcos2 = reflecto_cos2[jf,:]
            LF[jref] = 100 * np.sum(refcos2[id_5to80[0]]) / np.sum(ref[id_0to80[0]])
            refcosabs = reflecto_cosabs[jf,:]
            LFC[jref] = 100 * np.sum(refcosabs[id_5to80[0]]) / np.sum(ref[id_0to80[0]])
        except:
            log.info("I could not calculate LF and LFC for the {}.".format(freq[jf])+
                "[Hz] frequency band. Try to use more rays or"+
                "extend the length of h(t) of the simmulation.")
        jf=+1
    return LF, LFC
Пример #2
0
    def start(self):
        # loop through every single ray
        for src_rays in self.rays:
            log.info(src_rays.nrays)
            progress = tqdm(np.ndindex((src_rays.nrays, src_rays.niters)))
            for (rayid, rayit) in progress:
                if src_rays.length[rayid] != src_rays.niters:
                    continue
                plane, rhit = self.ray_x_planes(r0=src_rays.ris[rayid, rayit],
                                                rd=src_rays.rds[rayid, rayit])

                try:
                    src_rays.ris[rayid, rayit + 1] = rhit
                except IndexError:
                    # trying to add an element beyond the array size. Using a
                    # try/except block is faster than using an if statement
                    pass
                else:
                    # compute the new direction but first check if this plane
                    # is part of the bounding box
                    if plane.bbox:
                        src_rays.length[rayid] = rayit + 2
                    rd = src_rays.rds[rayid, rayit]
                    n = plane.normal
                    rr = -2 * np.dot(rd, n) * n + rd
                    src_rays.rds[rayid, rayit + 1] = rr
Пример #3
0
 def __init__(self, geo_cfg, alpha, s):
     '''
     Set up the room geometry from the .dae file
     Geometry consists of: Volume, Total ara and an
     array of plane objects. Each plane object will be
     processed in a c++ class and have the following att:
     - name (string)
     - bounding box (bool)
     - list of vertices (Eigen<double> - Nvert x 3)
     - normal (Eigen<double> - 1 x 3)
     - vert_x - 2D polygon x coord (Eigen<double> - 1 x Nvert)
     - vert_y - 2D polygon y coord (Eigen<double> - 1 x Nvert)
     - nig - 2D normal components index (Eigen<int> - 1 x 2)
     - area (double)
     - centroid (Eigen<double> - 1 x 3)
     - alpha - absorption coefficient (Eigen<double> - 1 x Nfreq)
     - s - scattering coefficient (double)
     '''
     # toml file
     daepath = geo_cfg['room']  # path to .dae
     # Load an array of plane objects
     self.planes = []  # A list of planes (object with attributes)
     mesh = co.Collada(daepath)
     for obj in mesh.scene.objects('geometry'):  #loop every obj
         for triset in obj.primitives():  #loop every primitives
             # First if excludes the non-triangles objects
             if type(triset) != co.triangleset.BoundTriangleSet:
                 log.info('Warning: non-supported primitive ignored!')
                 continue
             # Loop trhough all triangles
             for jp, tri in enumerate(triset):
                 # Define a single plane object
                 name = '{}-{}'.format(obj.original.name, jp)
                 vertices = np.array(tri.vertices)
                 normal = np.float32(tri.normals[0] /
                                     np.linalg.norm(tri.normals[0]))
                 vert_x, vert_y, normal_nig = vert_2d(normal, vertices)
                 area = np.float64(triangle_area(vertices))
                 centroid = np.float32(triangle_centroid(vertices))
                 alpha_v = np.float32(alpha[jp])
                 ################### cpp plane class #################
                 plane = ra_cpp.Planecpp(name, False, vertices, normal,
                                         vert_x, vert_y, normal_nig, area,
                                         centroid, alpha_v, s[jp])
                 ################### py plane class ################
                 # plane = PyPlane(name, False, vertices, normal,
                 #     vert_x, vert_y, normal_nig, area, centroid,
                 #     alpha[jp], s[jp])
                 # Append plane object
                 self.planes.append(plane)
     # total area and volume
     self.total_area = total_area(self.planes)
     self.volume = volume(self.planes)
Пример #4
0
 def load_dae_geometry(self, daepath):
     planes = []
     mesh = co.Collada(daepath)
     for obj in mesh.scene.objects('geometry'):
         for triset in obj.primitives():
             if type(triset) != co.triangleset.BoundTriangleSet:
                 log.info('Warning: non-supported primitive ignored!')
                 continue
             for i, tri in enumerate(triset):
                 plane = Plane(name='{}-{}'.format(obj.original.name, i),
                               vertices=tri.vertices,
                               normal=tri.normals[0] /
                               np.linalg.norm(tri.normals[0]))
                 planes.append(plane)
     return planes
Пример #5
0
 def dump(self):
     log.info('number of escaped rays: {}/{}={}%'.format(
         self.n_escaped_rays, self.rays[0].nrays,
         np.around(100 * self.n_escaped_rays / self.rays[0].nrays,
                   decimals=2)))
     rays = []
     for r in self.rays:
         rays.append({'ris': r.ris.tolist(), 'length': r.length.tolist()})
     sim_json = {'rays': rays}
     json.dump(
         sim_json,
         codecs.open('sim.json', 'w', encoding='utf-8'),
         separators=(',', ':'),
         sort_keys=True,
         # indent=4
     )
Пример #6
0
def process_results(Dt, ht_length, freq, sources, receivers):
    '''
    This function process all the relevant source-receiver data, such as:
    reflectogram, decay and acoustical parameters. Each receiver
    will be appended to each source to store the results of
    each source-receiver (vs. time or vs. frequency) pair.
    '''
    log.info("processing results...")
    time_bins = np.arange(0.0, 1.2 * ht_length, Dt)
    sou = []
    for s in sources:
        rec = [] #SRPairRec()
        for jrec, r in enumerate(receivers):
            rec.append(RecResults(s, jrec, time_bins, freq))
        sou.append(SouResults(rec, time_bins, freq))
    return sou
Пример #7
0
def main():
    time_start = time.time()
    sim = Simulation(cfgfile='simulation.toml')
    log.info('Starting simulation...')
    sim.start()
    log.info('Simulation over.\nDumping data now...')
    sim.dump()
    log.info('All data has been dumped!')
    log.info('Terminated in: %.4f s' % (time.time() - time_start))
Пример #8
0
def ts(time, reflectogram, id_dir, freq):
    '''
    This function is used to calculate T30 by curve fitting the decay from -5 dB to -25dB
    '''
    ## The next two lines get direct sound without any information from source and receiver
    Ts = np.zeros(freq.size, dtype = np.float32)
    jf = 0
    for jref, ref in enumerate(reflectogram):
        np.seterr(divide = 'ignore')
        try:
            Ts[jref] = 1000 * (np.sum(np.multiply(time[id_dir:], ref[id_dir:]))/np.sum(ref[id_dir:]) - time[id_dir])
        except:
            log.info("I could not calculate Ts for the {}.".format(freq[jf])+
                "[Hz] frequency band. Try to use more rays or"+
                "extend the length of h(t) of the simmulation.")
        jf=+1
    return Ts
Пример #9
0
    def __init__(self, source, jrec, time_bins, freq):
        start_time = time.time()
        # Time concatenation
        # time_cat = np.array(ra_cpp._time_cat(
        #     source.rays, source.reccrossdir[jrec].time_dir, jrec), dtype = np.float32)
        time_cat = np.array(ra_cpp._time_cat(
            source.rays, source.reccrossdir[jrec].time_dir, jrec,
            source.reccrossdir[jrec].size_of_time), dtype = np.float32)

        # log.info("time cat size: {}.".format(len(time_cat)))
        # log.info("size_of_tme: {}.".format(source.reccrossdir[jrec].size_of_time))
        # sort time vector
        # id_sorted_time = np.argsort(time_cat)
        # time_sorted = time_cat[id_sorted_time]
        # concatenate sound intensities
        # start_time = time.time()
        intensity_cat = np.array(ra_cpp._intensity_cat(
            source.rays, source.reccrossdir[jrec].i_dir,
            jrec, time_cat.size), dtype = np.float32)
        
        # Cossine concatenation
        cos_cat = np.array(ra_cpp._cos_cat(
            source.rays, source.reccrossdir[jrec].cos_dir, jrec,
            source.reccrossdir[jrec].size_of_time), dtype = np.float32)
        # log.info(" {} seconds to concatenate intensity (c++).".format(time.time() - start_time))
        # sort intensity
        # intensity_sorted = intensity_cat[:, id_sorted_time]
        # reflectogram
        self.reflectogram = reflectogram_hist(time_bins, time_cat, intensity_cat)
        # self.reflectogram = reflectogram_hist(time_bins, time_sorted, intensity_sorted)
        self.decay = decay_curve(self.reflectogram)
        log.info(" {} seconds to calc reflectogram (c++).".format(time.time() - start_time))

        # Calculate the direct sound id
        direct_sound_idarr = np.nonzero(self.reflectogram[0,:])
        id_dir = direct_sound_idarr[0]
        # Calculate acoustical parameters
        self.EDT = edt(time_bins, self.decay, id_dir[0], freq)
        self.T20 = t20(time_bins, self.decay, id_dir[0], freq)
        self.T30 = t30(time_bins, self.decay, id_dir[0], freq)
        self.C80 = c80(time_bins, self.reflectogram, id_dir[0], freq)
        self.D50 = d50(time_bins, self.reflectogram, id_dir[0], freq)
        self.Ts = ts(time_bins, self.reflectogram, id_dir[0], freq)
        self.G = g_db(self.reflectogram, source.power_lin, freq)
        self.LF, self.LFC = lf_lfc(time_bins, self.reflectogram, id_dir[0], freq, time_cat, intensity_cat, cos_cat)
Пример #10
0
def g_db(reflectogram, Wlin, freq):
    '''
    This function is used to calculate T30 by curve fitting the decay from -5 dB to -25dB
    '''
    ## The next two lines get direct sound without any information from source and receiver
    G = np.zeros(freq.size, dtype = np.float32) - np.inf
    jf = 0
    for jref, ref in enumerate(reflectogram):
        np.seterr(divide = 'ignore')
        try:
            G[jref] = 10.0 * np.log10(np.sum(ref)) -\
                10.0 * np.log10(Wlin[jref] / (4.0 * np.pi * 100.0))
        except:
            log.info("I could not calculate G for the {}.".format(freq[jf])+
                "[Hz] frequency band. Try to use more rays or"+
                "extend the length of h(t) of the simmulation.")
        jf=+1
    return G
Пример #11
0
def d50(time, reflectogram, id_dir, freq):
    '''
    This function is used to calculate T30 by curve fitting the decay from -5 dB to -25dB
    '''
    ## The next two lines get direct sound without any information from source and receiver
    D50 = np.zeros(freq.size, dtype = np.float32)-0.1
    jf = 0
    for jref, ref in enumerate(reflectogram):
        np.seterr(divide = 'ignore')
        try:
            id_to_sum = np.where(time <= 0.050 + time[id_dir])
            D50[jref] = 100 * np.sum(ref[id_to_sum[0]]) / np.sum(ref)
        except:
            log.info("I could not calculate D50 for the {}.".format(freq[jf])+
                "[Hz] frequency band. Try to use more rays or"+
                "extend the length of h(t) of the simmulation.")
        jf=+1
    return D50
Пример #12
0
def t30(time, decay, id_dir, freq):
    '''
    This function is used to calculate T30 by curve fitting the decay from -5 dB to -25dB
    '''
    ## The next two lines get direct sound without any information from source and receiver
    T30 = np.zeros(freq.size, dtype = np.float32)
    jf = 0
    for jdec, dec in enumerate(decay):
        np.seterr(divide = 'ignore')
        decdB = 10 * np.log10(dec[id_dir:]/
            np.amax(dec[id_dir:]))
        # log.info(time.shape)
        id_to_fit = np.where((decdB < -5.0) & (decdB > -35.0))
        try:
            p = np.polyfit(time[id_to_fit], decdB[id_to_fit], 1)
            T30[jdec] = -60 / p[0]
        except:
            log.info("I could not calculate T30 for the {}.".format(freq[jf])+
                "[Hz] frequency band. Try to use more rays or"+
                "extend the length of h(t) of the simmulation.")
        jf=+1
    return T30
Пример #13
0
def run(cfgs):
    ##### Setup algorithm controls ########
    # FIXME: use pathlib instead of string concatenation
    controls = AlgControls(cfgs['sim_cfg']['controls'])

    ##### Setup air properties ########
    air = AirProperties(cfgs['sim_cfg']['air'])
    air_m = air.air_absorption(controls.freq)

    ##### Setup materials #############
    alpha_list = load_matdata_from_mat(cfgs['sim_cfg']['material'])
    alpha, s = get_alpha_s(cfgs['sim_cfg']['geometry'],
                           cfgs['mat_cfg']['material'], alpha_list)

    ##### Setup Geometry ###########
    geo = GeometryMat(cfgs['sim_cfg']['geometry'], alpha, s)
    # geo.plot_mat_room(normals = 'on')
    # geo = Geometry('simulation.toml', alpha, s)
    # geo.plot_dae_room(normals = 'on')

    ##### Statistical theory ############
    res_stat = StatisticalMat(geo, controls.freq, air.c0, air_m)
    res_stat.t60_sabine()
    res_stat.t60_eyring()
    # res_stat.t60_kutruff(gamma=0.4)
    # res_stat.t60_araup()
    # res_stat.t60_fitzroy()
    # res_stat.t60_milsette()
    # res_stat.plot_t60()

    ##### ray's initial direction ########
    rays_i_v = RayInitialDirections()
    # rays_i_v.single_ray([0.0, -1.0, 0.0])#([0.7236, -0.5257, 0.4472])
    # rays_i_v.isotropic_rays(controls.Nrays) # 15
    # mat = spio.loadmat('ra/vin_matlab.mat')
    # rays_i_v.vinit = mat['vin']
    rays_i_v.random_rays(controls.Nrays)
    # rays_i_v.single_ray(rays_i_v.vinit[41])
    log.info("The number of rays is {}.".format(rays_i_v.Nrays))

    ##### Setup receiver, reccross and reccrossdir ########
    receivers, reccross, reccrossdir = setup_receivers(
        cfgs['sim_cfg']['receivers'])
    #### Allocate some memory in python for geometrical ray tracing ########################
    # Estimate max reflection order
    N_max_ref = math.ceil(1.5 * air.c0 * controls.ht_length * \
        (geo.total_area / (4 * geo.volume)))
    # Allocate according to max reflection order
    rays = ray_initializer(rays_i_v, N_max_ref, controls.transition_order,
                           reccross)

    ##### Setup sources - ray history goes inside source object ########
    sources = setup_sources(cfgs['sim_cfg']['sources'], rays, reccrossdir)

    ############# Now - the calculations ####################
    ############### direct sound ############################
    sources = ra_cpp._direct_sound(sources, receivers,
                                   controls.rec_radius_init, geo.planes,
                                   air.c0, rays_i_v.vinit)

    ############### ray tracing ##############
    sources = ra_cpp._raytracer_main(
        controls.ht_length, controls.allow_scattering,
        controls.transition_order, controls.rec_radius_init,
        controls.alow_growth, controls.rec_radius_final, sources, receivers,
        geo.planes, air.c0, rays_i_v.vinit)

    ######## Calculate intensities ###################
    sources = ra_cpp._intensity_main(controls.rec_radius_init, sources, air.c0,
                                     air.m, res_stat.alphas_mtx)

    ########### Process reflectograms and acoustical parameters #####################
    sou = process_results(controls.Dt, controls.ht_length, controls.freq,
                          sources, receivers)

    ########## Statistics ##########################################################
    stats = SRStats(sou)
    ######## some plotting ##############################
    # sou[0].plot_single_reflecrogram(band = 4, jrec = 2)
    # plt.show()
    # sou[0].plot_single_reflecrogram(band = 4, jrec = 1)
    sou[0].plot_decays()
    # sou[0].plot_edt()
    # sou[0].plot_t20()
    # sou[0].plot_t30()
    # sou[0].plot_c80()
    # sou[0].plot_d50()
    # sou[0].plot_ts()
    # sou[0].plot_g()
    # sou[0].plot_lf()
    # sou[0].plot_lfc()
    # log.info(sources[0].rays[0].refpts_hist)

    # geo.plot_raypath(sources[0].coord, sources[0].rays[0].refpts_hist,  # <-- sources[0].rays[0].refpts_hist
    #     receivers)

    # log.info(sources[0].reccrossdir[0].cos_dir)
    ############# Save trial #########################
    # import pickle
    # path = '/home/eric/dev/ra/data/legacy/ptb_studio_ph3/'
    # pkl_fname_res = 'ptb_studio_ph3_open'        # simulation results name
    # with open(path+pkl_fname_res+'.pkl', 'wb') as output:
    #     pickle.dump(res_stat, output, pickle.HIGHEST_PROTOCOL)
    #     pickle.dump(sou, output, pickle.HIGHEST_PROTOCOL)
    #     pickle.dump(stats, output, pickle.HIGHEST_PROTOCOL)
    # pickle.dump(geo, output, pickle.HIGHEST_PROTOCOL)

    # pickle.dump(sources, output, pickle.HIGHEST_PROTOCOL)


# class Simulation():
#     def __init__(self,):
#         self.cfgs = {}
#         self.sources = []
#         self.receivers = []
#         self.geometry = {}

#     def set_configs(self, cfgs):
#         self.cfgs = cfgs['sim_cfg']
#         self.mat_cfg = cfgs['mat_cfg']
#         self.controls = AlgControls(self.cfgs['controls'])
#         self.air = AirProperties(self.cfgs['air'])
#         self.air_m = self.air.air_absorption(self.controls.freq)

#     def set_sources(self, srcs):
#         '''
#         Parameters:
#         ----------
#             srcs: list of Source's
#         '''
#         # self.rays_i_v = RayInitialDirections()
#         # self.rays_i_v.random_rays(self.controls.Nrays)
#         # log.info("The number of rays is {}.".format(self.rays_i_v.Nrays))

#         # c0 = self.air.c0
#         # htl = self.controls.ht_length
#         # area = self.geo.total_area
#         # volume = self.geo.volume
#         # N_max_ref = math.ceil(1.5 * c0 * htl * area / (4 * volume))
#         # rays = ray_initializer(
#         #     self.rays_i_v, N_max_ref, self.controls.transition_order,
#         #     self.reccross
#         # )
#         # self.sources = setup_sources(
#         #     self.cfgs['sources'], rays, self.reccrossdir
#         # )

#     def set_receivers(self, rcvrs):
#         '''
#         Parameters:
#         -----------
#             rcvrs: list of Receiver`s
#         '''
#         new_rcvrs = []
#         for r in rcvrs:
#             r.append({
#                 'position': r.coord,
#                 'orientation': r.orientation
#             })
#         self.receivers, self.reccross, self.reccrossdir = setup_receivers(
#             new_rcvrs
#         )

#         # self.receivers, self.reccross, self.reccrossdir = setup_receivers(
#         #     self.cfgs['receivers']
#         # )

#     def set_geometry(self, geom):
#         '''
#         Parameters:
#         -----------
#             geom: list of dicts with the following parameters: 'name',
#             'vertices', 'normal', alpha, s.
#         '''
#         # to be populated
#         # ...
#         pass

#         # cfgs = self.cfgs
#         # alpha_list = load_matdata_from_mat(cfgs['material'])
#         # alpha, s = get_alpha_s(
#         #     cfgs['geometry'], self.mat_cfg['material'], alpha_list
#         # )
#         # self.geo = GeometryMat(cfgs['geometry'], alpha, s)

#     def run(self, state):
#         '''
#         Parameters:
#         ----------
#         Return:
#         -------
#             dict with the following structure:
#             {
#                 'rays':,
#                 ''
#             }
#         '''
#         res_stat = StatisticalMat(
#             self.geo, self.controls.freq, self.air.c0, self.air_m
#         )
#         res_stat.t60_sabine()
#         res_stat.t60_eyring()
#         srcs, rcvrs = self.sources, self.receivers
#         srcs = ra_cpp._direct_sound(
#             srcs, rcvrs, self.controls.rec_radius_init,
#             self.geo.planes, self.air.c0, self.rays_i_v.vinit
#         )
#         ctls = self.controls
#         srcs = ra_cpp._raytracer_main(
#             ctls.ht_length, ctls.allow_scattering, ctls.transition_order,
#             ctls.rec_radius_init, ctls.alow_growth, ctls.rec_radius_final, srcs,
#             rcvrs, self.geo.planes, self.air.c0, self.rays_i_v.vinit
#         )
#         srcs = ra_cpp._intensity_main(ctls.rec_radius_init,
#             srcs, self.air.c0, self.air.m, res_stat.alphas_mtx)
#         sou = process_results(ctls.Dt, ctls.ht_length,
#             ctls.freq, srcs, rcvrs)
#         stats = SRStats(sou)

#     def stats(self,):
#         pass

#     def save(self,):
#         '''
#         Return:
#         ------
#             a dict with the state of the simulation.
#         '''
#         pass