class HeaderFormat: _taglen = 10 fmt = _HeaderFmt(tag=f'{_taglen:d}s', data_length='I') _fmt = ''.join(fmt) length = _calcsize(_fmt) @classmethod def pack(cls, b_tag, length): "Return a bytes object given a b_tag (bytes) and length (int)" taglen = cls._taglen if len(b_tag) > taglen: raise ValueError(f"Header tag {b_tag!r} is longer than {taglen}") return _pack(cls._fmt, b_tag, length) @classmethod def unpack(cls, bstr): "Unpack a _HeaderFmt object from the bytes string bstr." tag, length = _unpack(cls._fmt, bstr) tag = tag.replace(b'\x00', b'') return _HeaderFmt(tag, length) @classmethod def __repr__(cls): clsname = cls.__name__ return f'<{clsname}: fmt={cls.fmt}>'
def minimum_size(format): """ Return the minimum possible size of the given packed data format. """ if not format[:1] in b"@=<>!": format = b"!" + format return _calcsize(format.replace(b"$", b""))
def unpack(self, format_text): """Unpack bytes using struct modules format :param format_text: struct's module format :type format_text: :class:`str` :return data: result from :func:`struct.unpack_from` :rtype: :class:`tuple` """ data = _unpack_from(format_text, self.data, self.offset) self.offset += _calcsize(format_text) return data
class xmpyapi(object): # The start of the Unix 1970 epoch, expressed in terms of the # J1950 epoch. To be added to time.time() in order to obtain the # right value, or to be subtracted from self.integral before using # Unix time utilities for string time representation. EPOCH_DIFF = 631152000.0 # time.strftime() overflows on 32 bit machines past this time LONG_IS_32_BIT = (_calcsize('l') == 4) UNIX_32BIT_EPOCH_MAX = 2777068800.0 SECS_PER_MIN = 60.0 SECS_PER_HOUR = 3600.0 SECS_PER_DAY = 86400.0 SECS_PER_NON_LEAP_YEAR = 31536000.0 # J1950 is a not a leap year NCOLONS_MAP = { 0: [1], # ss.sss 1: [SECS_PER_HOUR, SECS_PER_MIN], # hh:mm 2: [SECS_PER_HOUR, SECS_PER_MIN, 1], # hh:mm:ss } # _parse_date() assumes that any instance of '%y/%Y' are at the # beginning of the format string to handle overflow/underflow # problems as well as date wrapping around 1950 vs 1970 for 2 # digit years. Currently VAX is out-of-luck for under/overflow # issues. TFORMAT = { STANDARD: ('%Y:%m:%d::%H:%M:%S', 'yyyy:mm:dd::hh:mm:ss.sss'), VAX: ('%d-%b-%Y:%H:%M:%S', 'dd-MON-yyyy::hh:mm:ss.sss'), EPOCH: ('%Y:', 'yyyy:sec_of_year.ssss'), NORAD: ('%y%j', 'yyDOY:frac_day'), TCR: ('%j:%H:%M:%S', 'DOY:hh:mm:ss.sss'), ACQ: ('%y.%j:%H:%M:%S', 'yy.DOY:hh:mm:ss.sss'), '_DATE': ('%Y:%m:%d', 'yyyy:mm:dd') } @staticmethod def now(): import time, math fractional, integral = math.modf(time.time()) # System time does not have more than microsecond precision. # Drop the bits that suggest otherwise return integral + xmpyapi.EPOCH_DIFF, round(fractional, 6) @staticmethod def time_format(time_str, fmt, warn): try: integral, fractional = xmpyapi._parse_time(time_str, fmt) except Exception, e: if warn == EXCEPTION: raise elif warn == WARNING: print 'WARNING:', e integral, fractional = 0.0, 0.0 return None, integral, fractional
def initial_size(format): """ Return the size of the given packed data format up to the first variable-length string. """ if format[:1] in b"@=<>!": byte_order = format[:1] format = format[1:] else: byte_order = b"!" index = format.find(b"$") if not index: return 0 elif index > 0: if b"$$" in format: raise error("invalid sequence in netstruct format") format = format[:index] return _calcsize(byte_order + format)
class c_long(_SimpleCData): _type_ = "l" _check_size(c_long) class c_ulong(_SimpleCData): _type_ = "L" _check_size(c_ulong) if _calcsize("i") == _calcsize("l"): # if int and long have the same size, make c_int an alias for c_long c_int = c_long c_uint = c_ulong else: class c_int(_SimpleCData): _type_ = "i" _check_size(c_int) class c_uint(_SimpleCData): _type_ = "I" _check_size(c_uint)
_type_ = "h" _check_size(c_short) class c_ushort(_SimpleCData): _type_ = "H" _check_size(c_ushort) class c_long(_SimpleCData): _type_ = "l" _check_size(c_long) class c_ulong(_SimpleCData): _type_ = "L" _check_size(c_ulong) if _calcsize("i") == _calcsize("l"): # if int and long have the same size, make c_int an alias for c_long c_int = c_long c_uint = c_ulong else: class c_int(_SimpleCData): _type_ = "i" _check_size(c_int) class c_uint(_SimpleCData): _type_ = "I" _check_size(c_uint) class c_float(_SimpleCData): _type_ = "f" _check_size(c_float)
_check_size(c_ushort) class c_long(_SimpleCData): _type_ = 'l' _check_size(c_long) class c_ulong(_SimpleCData): _type_ = 'L' _check_size(c_ulong) if _calcsize('i') == _calcsize('l'): c_int = c_long c_uint = c_ulong else: class c_int(_SimpleCData): _type_ = 'i' _check_size(c_int) class c_uint(_SimpleCData): _type_ = 'I' _check_size(c_uint)
def f(proc_handle: _types.HANDLE, base_addr: int): bs = read_mem(proc_handle, base_addr, _calcsize(tcode)) return _unpack(tcode, bs)[0]
from .enum import T_Enum as _Enum from struct import calcsize as _calcsize MD5_LEN: int = 16 SHA_LEN: int = 20 MAX8: int = 2**8 MAX16: int = 2**16 MAX32: int = 2**32 MAX64: int = 2**64 CTL_FMT: str = '!BBBBIIIII' CTL_LEN: int = _calcsize(CTL_FMT) BFD_PORT: int = 3784 class Diag(_Enum): NoDiag: int = 0 Expired: int = 1 EchoFailed: int = 2 NeighborDown: int = 3 FPReset: int = 4 PathDown: int = 5 ConPathDown: int = 6 AdminDown: int = 7 RConPathDown: int = 8 class SessionState(_Enum): AdminDown: int = 0
def read(self, filename): """Read a Zemax detector data file. File types are .ddr, .ddc, .ddp and .ddv. Can read data written using zSaveDetector for rectangular, color, polar or volume detectors. Parameters ---------- filename : str Filename of the detector data to read. Returns ------- Populated instance of DetectorData object. See the Zemax manual for more information on detector data files. The following attributes can be expected: type : int 1, 2, 3, or 4 for Detector Rectangle, Color, Polar, and Volume objects, respectively type_str : str 'Rectangle', 'Color', 'Polar' or 'Volume' depending on type lens_units : int 0, 1, 2, or 3 for mm, cm, in, or m respectively. lens_units_str: str 'mm', 'cm', 'in' or 'm', depending on lens_units. source_units: int 0, 1, or 2 for Watts, Lumens, or Joules, respectively. source_units_str: str 'W', 'lm' or 'J', depending on source_units. source_multiplier: int 0 through 9 for source unit multipliers femto through Tera, respectively. source_multiplier_val: float The source_multiplier as a floating point value. i_data: array of int integer data that is detector type specific (see below). d_data: array of float double precision that is detector type specific (see below). Data for Detector Rectangle and Detector Color objects: i_data[0], i_data[1]: number of x pixels, number of y pixels. i_data[2], i_data[3]: number of rays striking the spatial detector, number of rays striking the angular detector. d_data[0], d_data[1]: x half width of the detector, the y half width of the detector. d_data[2] - d_data[5]: angular x minimum, x maximum, y minimum, y maximum. Pixel Data for Rectangle Detectors inc_int_pos, inc_int_ang, coh_real, coh_imag, coh_amp : array of float These are the incoherent flux in position space, the incoherent flux in angle space, and the coherent real, imaginary, and summed amplitude in position space, respectively Pixel Data for Color Detectors pos_P, pos_X, pos_Y, pos_Z, ang_P, ang_X, ang_Y, ang_Z: array of float The first four are the position space power, tristimulus X, Y, and Z values, and the same 4 values for angle space. Data for Detector Polar Objects: i_data[0], i_data[1]: number of polar pixels, number of angular pixels. i_data[3]: number of rays striking the detector. d_data[0], d_data[1]: maximum angle of the detector, radial size of the detector. Pixel Data for Polar Detectors ang_P, ang_X, ang_Y, ang_Z These are the angular space power, tristimulus X, Y, and Z values. Data for Detector Volume Objects: i_data[0] - i_data[2]: number of x, y, and z direction pixels. d_data[0] - d_data[2]: half width of the detector in the x, y, and z directions, respectively. Pixel Data for Detector Volume Objects: inc_int_pos, inc_absorbed_flux : array of float These are the incoherent incident flux and the absorbed flux per voxel. Notes ----- Only reading of rectangular data has been tested. Check your results carefully. In particular, the pixel ordering may not be dealt with correctly. The detector type should conform to the file extension, .ddr, .ddc, .ddp, .ddv for rectangular, color, polar and volume detectors respectively. This is not enforced, but a warning is issued if this convention is violated. Binary data is unpacked as little-endian. Padding is dealt with manually, hopefully only occurring in the header between the integer (i_data) and double floating point (d_data) data (4 bytes). """ with open(filename, 'rb') as ddata: # Read the header and extract values to instance fields header_buffer = ddata.read(_calcsize('iiiii')) self.header_meta = _unpack('<iiiii', header_buffer) self.version = self.header_meta[0] self.type = self.header_meta[1] self.type_str = ('Unknown', 'Rectangle', 'Color', 'Polar', 'Volume', 'UnknownNew1', 'UnknownNew2')[self.type] self.lens_units = self.header_meta[2] self.lens_units_str = ('mm', 'cm', 'in', 'm')[self.lens_units] self.source_units = self.header_meta[3] self.source_units_str = ('W', 'lm', 'J')[self.source_units] self.source_multiplier = self.header_meta[4] self.source_multiplier_val = (1.e-15, 1.e-12, 1.e-9, 1.e-6, 1.e-3, 1., 1.e3, 1.e6, 1.e9, 1.e12, 1.e15)[self.source_multiplier] # Read the integer data (50 values) header_buffer = ddata.read(_calcsize('i' * 50)) self.i_data = _np.array(_unpack('<' + 'i' * 50, header_buffer)) # Ditch some padding, not sure why it occurs here, but documented as such in the Zemax manual. ddata.read(4) # Read the floating point (double) data (50 values) header_buffer = ddata.read(_calcsize('d' * 50)) self.d_data = _np.array(_unpack('<' + 'd' * 50, header_buffer)) # Read the Ray Trace Method header_buffer = ddata.read(_calcsize('i')) self.ray_trace_method = _unpack('<i', header_buffer)[0] if self.ray_trace_method >= 0: self.ray_trace_method_str = ( 'NotSet', 'LightningTrace', 'NSCRayTrace')[self.ray_trace_method] # Read the actual data, depending on the type of detector if self.type == 1: # Rectangle detector if _os.path.splitext(filename)[1].lower() != '.ddr': warnings.warn( 'File extension for rectangular detector data should be .ddr' ) self.num_x_pixels = self.i_data[0] self.num_y_pixels = self.i_data[1] self.num_pixels = self.num_x_pixels * self.num_y_pixels self.half_width_x = self.d_data[0] self.half_width_y = self.d_data[1] self.pixel_pitch_x = 2.0 * self.half_width_x / self.num_x_pixels self.pixel_pitch_y = 2.0 * self.half_width_y / self.num_y_pixels # There are 5 fields of double precision data for each pixel data_buffer = ddata.read(_calcsize('d' * 5) * self.num_pixels) pixel_data = _np.array( _unpack('<' + 'd' * (5 * self.num_pixels), data_buffer)).reshape(self.num_pixels, 5) self.inc_int_pos = _np.flipud(pixel_data[:, 0].reshape( self.num_y_pixels, self.num_x_pixels)) self.inc_int_ang = _np.flipud(pixel_data[:, 1].reshape( self.num_y_pixels, self.num_x_pixels)) self.coh_real = _np.flipud(pixel_data[:, 2].reshape( self.num_y_pixels, self.num_x_pixels)) self.coh_imag = _np.flipud(pixel_data[:, 3].reshape( self.num_y_pixels, self.num_x_pixels)) self.coh_amp = _np.flipud(pixel_data[:, 4].reshape( self.num_y_pixels, self.num_x_pixels)) elif self.type == 2: # Color detector if _os.path.splitext(filename)[1].lower() != '.ddc': warnings.warn( 'File extension for color detector data should be .ddc' ) self.num_x_pixels = self.i_data[0] self.num_y_pixels = self.i_data[1] self.num_pixels = self.num_x_pixels * self.num_y_pixels self.half_width_x = self.d_data[0] self.half_width_y = self.d_data[1] self.pixel_pitch_x = 2.0 * self.half_width_x / self.num_x_pixels self.pixel_pitch_y = 2.0 * self.half_width_y / self.num_y_pixels # There are 8 fields of double precision data for each pixel data_buffer = ddata.read(_calcsize('d' * 8) * self.num_pixels) pixel_data = _np.array( _unpack('<' + 'd' * (8 * self.num_pixels), data_buffer)).reshape(self.num_pixels, 8) self.pos_P = _np.flipud(pixel_data[:, 0].reshape( self.num_y_pixels, self.num_x_pixels)) self.pos_X = _np.flipud(pixel_data[:, 1].reshape( self.num_y_pixels, self.num_x_pixels)) self.pos_Y = _np.flipud(pixel_data[:, 2].reshape( self.num_y_pixels, self.num_x_pixels)) self.pos_Z = _np.flipud(pixel_data[:, 3].reshape( self.num_y_pixels, self.num_x_pixels)) self.ang_P = _np.flipud(pixel_data[:, 4].reshape( self.num_y_pixels, self.num_x_pixels)) self.ang_X = _np.flipud(pixel_data[:, 5].reshape( self.num_y_pixels, self.num_x_pixels)) self.ang_Y = _np.flipud(pixel_data[:, 6].reshape( self.num_y_pixels, self.num_x_pixels)) self.ang_Z = _np.flipud(pixel_data[:, 7].reshape( self.num_y_pixels, self.num_x_pixels)) elif self.type == 3: # Polar detector if _os.path.splitext(filename)[1].lower() != '.ddp': warnings.warn( 'File extension for polar detector data should be .ddp' ) self.num_x_pixels = self.i_data[0] self.num_y_pixels = self.i_data[1] self.num_pixels = self.num_x_pixels * self.num_y_pixels # There are 8 fields of double precision data for each pixel, # but the first four currently have no data. Zemax upgrades may change this. data_buffer = ddata.read(_calcsize('d' * 8) * self.num_pixels) pixel_data = _np.array( _unpack('<' + 'd' * (8 * self.num_pixels), data_buffer)).reshape(self.num_pixels, 8) self.ang_P = _np.flipud(pixel_data[:, 4].reshape( self.num_y_pixels, self.num_x_pixels)) self.ang_X = _np.flipud(pixel_data[:, 5].reshape( self.num_y_pixels, self.num_x_pixels)) self.ang_Y = _np.flipud(pixel_data[:, 6].reshape( self.num_y_pixels, self.num_x_pixels)) self.ang_Z = _np.flipud(pixel_data[:, 7].reshape( self.num_y_pixels, self.num_x_pixels)) elif self.type == 4: # Volume detector if _os.path.splitext(filename)[1].lower() != '.ddv': warnings.warn( 'File extension for volume detector data should be .ddv' ) self.num_x_pixels = self.i_data[0] self.num_y_pixels = self.i_data[1] self.num_z_pixels = self.i_data[2] self.num_pixels = self.num_x_pixels * self.num_y_pixels * self.num_z_pixels # There are 5 fields of double precision data for each pixel, # but only the first 2 currently have data. Zemax upgrades may change this. data_buffer = ddata.read(_calcsize('d' * 5) * self.num_pixels) pixel_data = _np.array( _unpack('<' + 'd' * (5 * self.num_pixels), data_buffer)).reshape(self.num_pixels, 5) self.inc_int_pos = _np.flipud(pixel_data[:, 0].reshape( self.num_x_pixels, self.num_y_pixels, self.num_z_pixels)) self.inc_absorbed_flux = _np.flipud(pixel_data[:, 1].reshape( self.num_y_pixels, self.num_x_pixels, self.num_z_pixels))