def set_laser(dvc = 0, laser_on = True, laser_intensity = 1000, Errors = {}, verbosity = _VERBOSITY): """ Turn laser ON (3 modulation types: 7Hz (1), 28 Hz (2) and 255 Hz (3)) or OFF (0) and set laser intensity. Args: :dvc: | Device handle. :laser_on: | 0: OFF, >0: ON -> 1: PWM 7Hz, 2: PWM 28 Hz, 3: 255 Hz, optional | True (>0): turn laser on to select measurement area; False (0): turn off. | (Can only be ON when "spd" is not in out.split(",")) :laser_intensity: | 1000.0, optional | Laser intensity in ‰ (pro-mille). :Errors: | Dict with error messages. :verbosity: | 1, optional | 0: no printed error message output. Returns: :Errors: | Dict with error messages. """ try: Errors["SetLaserIntensity"] = None # Open device if not already opened! if not _check_dvc_open(dvc): dvc_was_open = False dvc, Errors = dvc_open(dvc = dvc, Errors = Errors, out = "dvc,Errors", verbosity = verbosity) else: dvc_was_open = True # Set laser intensity and modulation: if laser_intensity > 1000: laser_intensity = 1000 if np.int(laser_on) not in [0,1,2,3]: laser_on = 3 if (bool(laser_on) == True) & (_check_dvc_open(dvc)): dwError = jtc.JETI_SetLaserIntensity(dvc, DWORD(laser_intensity), DWORD(np.int(laser_on))) if dwError != 0: if (verbosity == 1): print("Could not set pilot laser intensity and/or modulation. Error code = {}".format(dwError)) Errors["SetLaserIntensity"] = dwError elif (bool(laser_on) == False) & (_check_dvc_open(dvc)): dwError = jtc.JETI_SetLaserIntensity(dvc, DWORD(np.int(laser_intensity)), DWORD(0)) if dwError != 0: if (verbosity == 1): print("Could not turn OFF pilot laser. Error code = {}".format(dwError)) Errors["SetLaserIntensity"] = dwError # Toggle laser status: Errors = set_laser_status(dvc, status = bool(laser_on), out = "Errors", Errors = Errors, verbosity = verbosity) except: Errors["SetLaserIntensity"] = "set_laser() fails." finally: dvc, Errors = dvc_close(dvc, Errors = Errors, close_device = (dvc_was_open == False), out = "dvc,Errors", verbosity = verbosity) return Errors
def read_spectral_radiance(dvc, wlstart = 360, wlend = 830, wlstep = 1, out = "spd,Errors", Errors = {}, verbosity = _VERBOSITY): """ Read measured spectral radiance (W/m².sr.nm) from device. Args: :dvc: | Device handle (of class ctypes). :wlstart: | 360 or Int, optional | Start wavelength in nm. (min = 350 nm) :wlend: | 830 or Int, optional | Start wavelength in nm. (max = 1000 nm) :out: | "status,Errors", optional | Requested return. :Errors: | Dict with error messages. :verbosity: | 1, optional | 0: no printed error message output. Returns: :spd: | ndarray with wavelengths (1st row) and spectral radiance (2nd row; nan's if error). :Errors: | Dict with error messages. """ out = out.replace(' ','') # Get wavelength range: wls = np.arange(np.int(wlstart), np.int(wlend)+np.int(wlstep), np.int(wlstep), dtype=np.float32) # Initialize spd filled with nan's: spd = np.vstack((wls, np.nan*np.ones(wls.shape))) # try: Errors["SpecRadEx"] = None # Convert measurement parameters to ctypes: dwBeg = DWORD(np.int(wlstart)) # wavelength start in nm dwEnd = DWORD(np.int(wlend)) # wavelength end in nm # create buffer for spectral radiance data: fSprad = (FLOAT * wls.shape[0])() # get pointer to start of spectral radiance dwError = jtre.JETI_SpecRadEx(dvc, dwBeg, dwEnd, ctypes.byref(fSprad)) Errors["SpecRadEx"] = dwError if (dwError != 0): if (verbosity == 1): print("Could not read spectral radiance data from device. Error code = {}".format(dwError)) else: # Read spectral radiance from buffer: Sprad= np.frombuffer(fSprad, np.float32) # Overwrite 2nd row of spd array with measured spectral radiance values: spd[1,:] = Sprad # except: # Errors["SpecRadEx"] = "read_spectral_radiance() fails." # finally: # Generate requested return: if out == "spd,Errors": return spd, Errors elif out == "spd": return spd elif out == "Errors": return Errors else: raise Exception("Requested output error.")
def start_meas(dvc, Tint = 0.0, autoTint_max = _TINT_MAX, Nscans = 1, wlstep = 1, Errors = {}, out = "Tint,Errors", verbosity = _VERBOSITY): """ Start measurement on already opened device. Args: :dvc: | Device handle (of class ctypes). :Tint: | 0 or Float, optional | Integration time in seconds. (if 0: find best integration time). :autoTint_max: | Limit Tint to this value when Tint = 0. :Nscans: | 1 or Int, optional | Number of scans to average. :wlstep: | 1 or Int, optional | Wavelength step size in nm. :out: | "Tint,Errors", optional | Requested return. :Errors: | Dict with error messages. :verbosity: | 1, optional | 0: no printed error message output. Returns: :Tint: | Integration time (limited to max possible time allowed by device) :Errors: | Dict with error messages. """ out = out.replace(' ','') try: Errors["MeasureEx"] = None # Find minimum integration time for connected device and re-set global variable _TINT_MIN (to avoid having to call the function a second time for this device): global _TINT_MIN if _TINT_MIN is None: _TINT_MIN, Errors = get_min_integration_time(dvc, out = "MinTint,Errors", Errors = Errors, verbosity = verbosity) if autoTint_max is None: autoTint_max = _TINT_MAX # Limit integration time to max value: Tint = _limit_Tint(Tint, Tint_min = _TINT_MIN, Tint_max = _TINT_MAX) autoTint_max = _limit_Tint(autoTint_max, Tint_min = _TINT_MIN, Tint_max = _TINT_MAX) # For automated Tint: if Tint == 0: MaxTint,Errors = get_max_auto_integration_time(dvc, out = "MaxTint,Errors", Errors = Errors, verbosity = verbosity) Errors = set_max_auto_integration_time(dvc, MaxTint = autoTint_max, out = "Errors", Errors = Errors, verbosity = verbosity) # Convert measurement parameters to ctypes: fTint = FLOAT(Tint*1000) # integration time (seconds -> milliseconds) wAver = WORD(np.int(Nscans)) # number of scans to average dwStep = DWORD(np.int(wlstep)) # wavelength step in nm # Start measurement: dwError = jtre.JETI_MeasureEx(dvc, fTint, wAver, dwStep) Errors["MeasureEx"] = dwError if (dwError != 0): if (verbosity == 1): print("Could not start measurement. Error code = {}".format(dwError)) except: Errors["MeasureEx"] = "start_meas() fails." finally: if out == "Tint,Errors": return Tint, Errors elif out == "Errors": return Errors elif out == "Tint": return Tint else: raise Exception("Requested output error.")
def get_spd(dvc = 0, Tint = 0.0, autoTint_max = _TINT_MAX, Nscans = 1, wlstep = 1, wlstart = 360, wlend = 830, twait = _TWAIT_STATUS, out = "spd", close_device = True, laser_on = 0, laser_intensity = 1000, verbosity = _VERBOSITY): """ Measure spectral radiance (W/nm.sr.m²). Args: :dvc: | 0 or Int or ctypes.wintypes.LP_c_ulong, optional | Number of the spectrometer device to load (0 = 1st) or handle (ctypes) to pre_initialized device. :Tint: | 0 or Float, optional | Integration time in seconds. (if 0: find best integration time, but < autoTint_max). :autoTint_max: | Limit Tint to this value when Tint = 0. :Nscans: | 1 or Int, optional | Number of scans to average. :wlstep: | 1 or Int, optional | Wavelength step size in nm. :wlstart: | 360 or Int, optional | Start wavelength in nm. (min = 350 nm) :wlend: | 830 or Int, optional | Start wavelength in nm. (max = 1000 nm) :twait: | 0.1 or Float, optional | Time in seconds to wait before checking status of device. | (If 0: wait :Tint: seconds, unless :Tint: == 0, then wait _TWAIT_STATUS seconds) :out: | "spd" [",dvc, Errors"], optional | Requested return. If "spd" in out.split(","):do spectral measurement, else: initialize dvc handle [and turn laser ON or OFF]. :close_device: | True or False, optional | Close device at the end of the measurement. | If 'dvc' not in out.split(','): always close!!! :laser_on: | 0: OFF, >0: ON -> 1: PWM 7Hz, 2: PWM 28 Hz, 3: 255 Hz, optional | True (>0): turn laser on to select measurement area; False (0): turn off. | (Can only be ON when "spd" is not in out.split(",") | if Tint is None) :laser_intensity: | 1000.0, optional | Laser intensity in ‰ (pro-mille). :verbosity: | 1, optional | 0: no printed error message output. Returns: :returns: | spd [,dvc, Errors] (as specified in :out:) | - "spd": ndarray with wavelengths (1st row) and spectral radiance (2nd row). | - "dvc": ctypes handle to device (if open) or nan (if closed). | - "Errors": dict with error message returned by device during various steps of the spectral measurement process. """ # Initialize dict with errors messages for each of the different measurement steps: Errors = {} Errors["get_spd"] = None out = out.replace(' ','') # Get wavelength range: wls = np.arange(np.int(wlstart), np.int(wlend)+np.int(wlstep), np.int(wlstep), dtype=np.float32) # Initialize spd filled with nan's: spd = np.vstack((wls, np.nan*np.ones(wls.shape))) try: # Initialize device : dvc, Errors = dvc_open(dvc = dvc, Errors = Errors, out = "dvc,Errors", verbosity = verbosity) if (_check_dvc_open(dvc)) & (("spd" in out.split(",")) & (Tint is not None)): # Turn off laser before starting measurement: Errors = set_laser(dvc = dvc, laser_on = False, laser_intensity = laser_intensity, Errors = Errors, verbosity = verbosity) # Start measurement: Tint, Errors = start_meas(dvc, Tint = Tint, autoTint_max = autoTint_max, Nscans = Nscans, wlstep = wlstep, Errors = Errors, out = "Tint, Errors", verbosity = verbosity) # wait until measurement is finished (check intermediate status every twait seconds): status, Errors = wait_until_meas_is_finished(dvc, Tint = Tint, twait = twait, out = "status,Errors", Errors = Errors, verbosity = verbosity) if status == False: # Read measured spectral radiance from device: spd, Errors = read_spectral_radiance(dvc, wlstart = wlstart, wlend = wlend, wlstep = wlstep, out = "spd,Errors", Errors = Errors, verbosity = verbosity) elif (("spd" not in out.split(",")) | (Tint is None)): # only dvc handle was requested or to turn laser ON. Errors = set_laser(dvc = dvc, laser_on = laser_on, laser_intensity = laser_intensity, Errors = Errors, verbosity = verbosity) # Close device: dvc, Errors = dvc_close(dvc, Errors = Errors, close_device = (close_device) | ('dvc' not in out.split(',')), out = "dvc,Errors", verbosity = verbosity) Errors["get_spd"] = int(np.sum([int(bool(x)) for x in Errors.values() if (x is not None)]) > 0) except: Errors["get_spd"] = "get_spd fails." finally: # Generate requested return: if out == "spd": return spd elif out == "dvc": return dvc elif out == "Errors": return Errors elif out == "spd,Errors": return spd, Errors elif out == "spd,dvc": return spd, dvc elif out == "spd,Errors,dvc": return spd, Errors, dvc elif out == "spd,dvc,Errors": return spd, dvc, Errors else: raise Exception("Requested output error.")
def get_pixel_coordinates(jab, jab_ranges=None, jab_deltas=None, limit_grid_radius=0): """ Get pixel coordinates corresponding to array of jab color coordinates. Args: :jab: | ndarray of color coordinates :jab_ranges: | None or ndarray, optional | Specifies the pixelization of color space. | (ndarray.shape = (3,3), with first axis: J,a,b, and second axis: min, max, delta) :jab_deltas: | float or ndarray, optional | Specifies the sampling range. | A float uses jab_deltas as the maximum Euclidean distance to select samples around each pixel center. A ndarray of 3 deltas, uses a city block sampling around each pixel center. :limit_grid_radius: | 0, optional | A value of zeros keeps grid as specified by axr,bxr. | A value > 0 only keeps (a,b) coordinates within :limit_grid_radius: Returns: :returns: | gridp, idxp, jabp, samplenrs, samplesIDs | - :gridp: ndarray with coordinates of all pixel centers. | - :idxp: list[int] with pixel index for each non-empty pixel | - :jabp: ndarray with center color coordinates of non-empty pixels | - :samplenrs: list[list[int]] with sample numbers belong to each | non-empty pixel | - :sampleIDs: summarizing list, | with column order: 'idxp, jabp, samplenrs' """ if jab_deltas is None: jab_deltas = np.array([_VF_DELTAR, _VF_DELTAR, _VF_DELTAR]) if jab_ranges is None: jab_ranges = np.vstack( ([0, 100, jab_deltas[0] ], [-_VF_MAXR, _VF_MAXR + jab_deltas[1], jab_deltas[1]], [-_VF_MAXR, _VF_MAXR + jab_deltas[2], jab_deltas[2]])) # Get pixel grid: gridp = generate_grid(jab_ranges=jab_ranges, limit_grid_radius=limit_grid_radius) # determine pixel coordinates of each sample in jab: samplesIDs = [] for idx in range(gridp.shape[0]): # get pixel coordinates: jp = gridp[idx, 0] ap = gridp[idx, 1] bp = gridp[idx, 2] #Cp = np.sqrt(ap**2+bp**2) if type(jab_deltas) == np.ndarray: sampleID = np.where( ((np.abs(jab[..., 0] - jp) <= jab_deltas[0] / 2) & (np.abs(jab[..., 1] - ap) <= jab_deltas[1] / 2) & (np.abs(jab[..., 2] - bp) <= jab_deltas[2] / 2))) else: sampleID = np.where( (np.sqrt((jab[..., 0] - jp)**2 + (jab[..., 1] - ap)**2 + (jab[..., 2] - bp)**2) <= jab_deltas / 2)) if (sampleID[0].shape[0] > 0): samplesIDs.append( np.hstack((idx, np.array([jp, ap, bp]), sampleID[0]))) idxp = [np.int(samplesIDs[i][0]) for i in range(len(samplesIDs))] jabp = np.vstack([samplesIDs[i][1:4] for i in range(len(samplesIDs))]) samplenrs = [ np.array(samplesIDs[i][4:], dtype=int).tolist() for i in range(len(samplesIDs)) ] return gridp, idxp, jabp, samplenrs, samplesIDs
jabt, jabr = out # jabt,jabr = data['bjabt'],data['bjabr'] _jab_test, _jab_ref = jabt.copy(), jabr.copy() close_gamut = True # make 3d for easy looping: _test_original_shape = _jab_test.shape if len(_test_original_shape) < 3: _jab_test = _jab_test[:, None] _jab_ref = _jab_ref[:, None] #initialize Jabt, Jabr, binnr, DEi; _test_shape = list(_jab_test.shape) if nhbins is not None: _nhbins = np.int(nhbins) _test_shape[0] = _nhbins + close_gamut * 1 else: _nhbins = nhbins _test_shape[0] = _test_shape[0] + close_gamut * 1 _jabt = np.zeros(_test_shape) _jabr = _jabt.copy() _binnr = _jab_test[..., 0].copy() _DEi = _jabt[..., 0].copy() # Store all samples (for output of potentially scaled coordinates): # if ('jabti' in out) | ('jabri' in out): _jabti = _jab_test.copy() _jabri = _jab_ref.copy() # Loop over axis 1:
def read_ldt_lamp_data(filename, multiplier=1.0, normalize='I0'): """ Read in LDT files. Args: :filename: | Filename of LDT file. :multiplier: | 1.0, optional | Scaler for candela values. :verbosity: | 0, optional | Display messages while reading file. :normalize: | 'I0', optional | If 'I0': normalize LID to intensity at (theta,phi) = (0,0) | If 'max': normalize to max = 1. Returns: :LDT: dict with LDT file data. | | dict_keys( | ['filename', 'version', 'manufacturer', 'Ityp','Isym', | 'Mc', 'Dc', 'Ng', 'name', Dg', 'cct/cri', 'tflux', 'lumens_per_lamp', | 'candela_mult', 'tilt', lamps_num', | 'cangles', 'tangles','candela_values', 'candela_2d', | 'intensity', 'theta', 'values', 'phi', 'map', 'Iv0'] | ) """ LDT = {'filename': filename} LDT['version'] = None with open(filename) as file: c = 0 cangles = [] tangles = [] candela_values = [] for line in file: if c == 0: # manufacturer LDT['manufacturer'] = line.rstrip() elif c == 1: # type indicator: 1: point with symm. around vert. axis, 2: line luminaire, 3: point with other symm. if np.float(line) == 1.0: LDT['Ityp'] = 'point source with symm. around vert. axis' elif np.float(line) == 2.0: LDT['Ityp'] = 'line luminaire' elif np.float(line) == 3.0: LDT['Ityp'] = 'point source with other symm.' elif c == 2: # symm. indicator if np.float(line) == 0.0: LDT['Isym'] = (0, 'no symmetry') elif np.float(line) == 1.0: LDT['Isym'] = (1, 'symmetry about the vertical axis') elif np.float(line) == 2.0: LDT['Isym'] = (2, 'symmetry to plane C0-C180') elif np.float(line) == 3.0: LDT['Isym'] = (3, 'symmetry to plane C90-C270') elif np.float(line) == 4.0: LDT['Isym'] = ( 4, 'symmetry to plane C0-C180 and to plane C90-C270') elif c == 3: # Number Mc of C-planes between 0 and 360 degrees LDT['Mc'] = np.float(line) elif c == 4: # Distance Dc between C-planes (Dc = 0 for non-equidistantly available C-planes) LDT['Dc'] = np.float(line) elif c == 5: # Number Ng of luminous intensities in each C-plane LDT['Ng'] = np.float(line) elif c == 6: # Distance Dg between luminous intensities per C-plane (Dg = 0 for non-equidistantly available luminous intensities in C-planes) LDT['Dg'] = np.float(line) elif c == 8: # luminaire name LDT['name'] = line.rstrip() elif c == 23: # conversion factor LDT['candela_mult'] = np.float(line) elif c == 24: # Tilt angle LDT['tilt'] = np.float(line) elif c == 26: # number of lamps LDT['lamps_num'] = np.float(line) elif c == 28: # total luminous flux LDT['tflux'] = np.float(line) LDT['lumens_per_lamp'] = LDT['tflux'] elif c == 29: # cct/cri LDT['cct/cri'] = line.rstrip() elif (c >= 42) & (c <= (42 + LDT['Mc'] - 1)): # start of C-angles cangles.append(np.float(line)) elif (c >= 42 + LDT['Mc']) & ( c <= (42 + LDT['Mc'] + LDT['Ng'] - 1)): # start of t-angles tangles.append(np.float(line)) elif (c >= (42 + LDT['Mc'] + LDT['Ng'])) & ( c <= (42 + LDT['Mc'] + LDT['Ng'] + LDT['Mc'] * LDT['Ng'] - 1)): candela_values.append(np.float(line)) c += 1 candela_values = np.array(candela_values) LDT['candela_values'] = np.array(candela_values) candela_2d = np.array(candela_values).reshape((-1, np.int(LDT['Ng']))) LDT['h_angs'] = np.array(cangles)[:candela_2d.shape[0]] LDT['v_angs'] = np.array(tangles) LDT['candela_2d'] = np.array(candela_2d) # normalize candela values to max = 1 or I0 = 1: LDT = _normalize_candela_2d(LDT, normalize=normalize, multiplier=multiplier) # complete lid to full theta[0-180] and phi [0-360] LDT = _complete_ldt_lid(LDT, Isym=LDT['Isym'][0]) LDT['Iv0'] = LDT['intensity'] / 1000 * LDT['tflux'] #lid in cd/klm return LDT