# HG changeset patch # User Iannis # Date 1513167578 -7200 # Node ID ef3b6f838da1d5d83d16c1fe0c55fe62865dbef5 # Parent b1f3eb51c3bf4b7247afdd1f5f3d5c3c4e321f93 Some documentation to the licel.py file. diff -r b1f3eb51c3bf -r ef3b6f838da1 atmospheric_lidar/licel.py --- a/atmospheric_lidar/licel.py Tue Dec 12 17:24:04 2017 +0200 +++ b/atmospheric_lidar/licel.py Wed Dec 13 14:19:38 2017 +0200 @@ -16,27 +16,46 @@ class LicelFile: - def __init__(self, filename, use_id_as_name=False, licel_timezone="UTC"): - self.filename = filename + """ A class representing a single binary Licel file. """ + def __init__(self, file_path, use_id_as_name=False, licel_timezone="UTC"): + """ + This is run when creating a new object. + + Parameters + ---------- + file_path : str + The path to the Licel file. + use_id_as_name : bool + If True, the transient digitizer name (e.g. BT0) is used as a channel + name. If False, a more descriptive name is used (e.g. '01064.o_an'). + licel_timezone : str + The timezone of dates found in the Licel files. Should match the available + timezones in the TZ database. + """ + self.filename = file_path self.use_id_as_name = use_id_as_name self.start_time = None self.stop_time = None self.licel_timezone = licel_timezone - self.import_file(filename) + self._import_file(file_path) self.calculate_physical() def calculate_physical(self): + """ Calculate physical quantities from raw data for all channels in the file. """ for channel in self.channels.itervalues(): channel.calculate_physical() - def import_file(self, filename): - """Imports a licel file. - Input: filename - Output: object """ - + def _import_file(self, file_path): + """ Read the content of the Licel file. + + Parameters + ---------- + file_path : str + The path to the Licel file. + """ channels = {} - with open(filename, 'rb') as f: + with open(file_path, 'rb') as f: self.read_header(f) @@ -50,7 +69,7 @@ b = np.fromfile(f, 'b', 1) if (a[0] != 13) | (b[0] != 10): - logging.warning("No end of line found after record. File could be corrupt: %s" % filename) + logging.warning("No end of line found after record. File could be corrupt: %s" % file_path) channel = LicelFileChannel(current_channel_info, raw_data, self.duration(), use_id_as_name=self.use_id_as_name) @@ -65,10 +84,13 @@ self.channels = channels def read_header(self, f): - """ Read the header of a open file f. + """ Read the header of an open Licel file. - Returns raw_info and channel_info. Updates some object properties. """ - + Parameters + ---------- + f : file-like object + An open file object. + """ # Read the first 3 lines of the header raw_info = {} channel_info = [] @@ -124,13 +146,35 @@ self.channel_info = channel_info def duration(self): - """ Return the duration of the file. """ + """ Return the duration of the file. + + Returns + ------- + : float + The duration of the file in seconds. + """ dt = self.stop_time - self.start_time return dt.seconds class LicelFileChannel: + """ A class representing a single channel found in a single Licel file.""" def __init__(self, raw_info=None, raw_data=None, duration=None, use_id_as_name=False): + """ + This is run when creating a new object. + + Parameters + ---------- + raw_info : dict + A dictionary containing raw channel information. + raw_data : dict + An array with raw channel data. + duration : float + Duration of the file, in seconds + use_id_as_name : bool + If True, the transient digitizer name (e.g. BT0) is used as a channel + name. If False, a more descriptive name is used (e.g. '01064.o_an'). + """ self.raw_info = raw_info self.raw_data = raw_data self.duration = duration @@ -138,6 +182,14 @@ @property def wavelength(self): + """ Property describing the nominal wavelength of the channel. + + Returns + ------- + : int or None + The integer value describing the wavelength. If no raw_info have been provided, + returns None. + """ if self.raw_info is not None: wave_str = self.raw_info['Wavelength'] wavelength = wave_str.split('.')[0] @@ -147,13 +199,18 @@ @property def channel_name(self): - ''' + """ Construct the channel name adding analog photon info to avoid duplicates If use_id_as_name is True, the channel name will be the transient digitizer ID (e.g. BT01). This could be useful if the lidar system has multiple telescopes, so the descriptive name is not unique. - ''' + + Returns + ------- + channel_name : str + The channel name + """ if self.use_id_as_name: channel_name = self.raw_info['ID'] else: @@ -161,7 +218,20 @@ channel_name = "%s_%s" % (self.raw_info['Wavelength'], acquisition_type) return channel_name - def analog_photon_string(self, analog_photon_number): + @staticmethod + def analog_photon_string(analog_photon_number): + """ Convert the analog/photon flag found in the Licel file to a proper sting. + + Parameters + ---------- + analog_photon_number : int + 0 or 1 indicating analog or photon counting channel. + + Returns + ------- + string : str + 'an' or 'ph' string, for analog or photon-counting channel respectively. + """ if analog_photon_number == '0': string = 'an' else: @@ -169,6 +239,13 @@ return string def calculate_physical(self): + """ Calculate physically-meaningful data from raw channel data: + + * In case of analog signals, the data are converted to mV. + * In case of photon counting signals, data are stored as number of photons. + + In addition, some ancillary variables are also calculated (z, dz, number_of_bins). + """ data = self.raw_data number_of_shots = float(self.raw_info['NShots']) @@ -202,9 +279,6 @@ class LicelLidarMeasurement(BaseLidarMeasurement): - ''' - - ''' raw_info = {} # Keep the raw info from the files durations = {} # Keep the duration of the files laser_shots = []