Mon, 06 Nov 2017 16:02:11 +0200
Merge from 81:2b484f310d0d
--- a/atmospheric_lidar/__init__.py Mon Nov 06 16:01:35 2017 +0200 +++ b/atmospheric_lidar/__init__.py Mon Nov 06 16:02:11 2017 +0200 @@ -1,1 +1,1 @@ -__version__ = '0.2.8' \ No newline at end of file +__version__ = '0.2.10' \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atmospheric_lidar/adam2016_depol.py Mon Nov 06 16:02:11 2017 +0200 @@ -0,0 +1,5 @@ +from licel_depol import LicelCalibrationMeasurement +import adam2016_depolarization_parameters + +class ADAM2017CalibrationMeasurement(LicelCalibrationMeasurement): + extra_netcdf_parameters = adam2016_depolarization_parameters
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atmospheric_lidar/adam2016_depolarization_parameters.py Mon Nov 06 16:02:11 2017 +0200 @@ -0,0 +1,116 @@ +general_parameters = \ +{'System': '\'ADAM 2012-2017\'', + 'Call sign': 'is', + 'Laser_Pointing_Angle': 0, + 'Molecular_Calc': 0, + 'Latitude_degrees_north': 45.8, + 'Longitude_degrees_east': 8.6, + 'Altitude_meter_asl': 209.0} # This should be float + +# Change to channel_parameters to use all channels. For testing I define only photon counting channels below. +channel_parameters = \ +{'BC1_p45': {'channel_ID': 647, + 'Background_Low': 45000, + 'Background_High': 60000, + 'Laser_Shots': 1501, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'Pol_Calib_Range_Min': 2500, + 'Pol_Calib_Range_Max': 4000}, + 'BC2_p45': {'channel_ID': 649, + 'Background_Low': 45000, + 'Background_High': 60000, + 'Laser_Shots': 1501, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'Pol_Calib_Range_Min': 2500, + 'Pol_Calib_Range_Max': 4000}, + 'BC1_m45': {'channel_ID': 646, + 'Background_Low': 45000, + 'Background_High': 60000, + 'Laser_Shots': 1501, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'Pol_Calib_Range_Min': 2500, + 'Pol_Calib_Range_Max': 4000}, + 'BC2_m45': {'channel_ID': 648, + 'Background_Low': 45000, + 'Background_High': 60000, + 'Laser_Shots': 1501, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'Pol_Calib_Range_Min': 2500, + 'Pol_Calib_Range_Max': 4000}, + } + +""" +{'00532.p_an_p45': {'channel_ID': 383, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3000, + 'LR_Input': 1, + 'DAQ_Range': 100.0, + 'Depolarization_Factor': 0, + 'Pol_Calib_Range_Min': 1000, + 'Pol_Calib_Range_Max': 3000}, + '00532.p_ph_p45': {'channel_ID': 378, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3000, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'Depolarization_Factor': 0, + 'Pol_Calib_Range_Min': 1000, + 'Pol_Calib_Range_Max': 3000}, + '00532.s_an_p45': {'channel_ID': 385 , + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3000, + 'LR_Input': 1, + 'DAQ_Range': 20.0, + 'Pol_Calib_Range_Min': 1000, + 'Pol_Calib_Range_Max': 3000}, + '00532.s_ph_p45': {'channel_ID': 380, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3000, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'Pol_Calib_Range_Min': 1000, + 'Pol_Calib_Range_Max': 3000}, + '00532.p_an_m45': {'channel_ID': 384, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3000, + 'LR_Input': 1, + 'DAQ_Range': 100.0, + 'Depolarization_Factor': 0, + 'Pol_Calib_Range_Min': 1000, + 'Pol_Calib_Range_Max': 3000}, + '00532.p_ph_m45': {'channel_ID': 379, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3000, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'Depolarization_Factor': 0, + 'Pol_Calib_Range_Min': 1000, + 'Pol_Calib_Range_Max': 3000}, + '00532.s_an_m45': {'channel_ID': 386, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3000, + 'LR_Input': 1, + 'DAQ_Range': 20.0, + 'Pol_Calib_Range_Min': 1000, + 'Pol_Calib_Range_Max': 3000}, + '00532.s_ph_m45': {'channel_ID': 382, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3000, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'Pol_Calib_Range_Min': 1000, + 'Pol_Calib_Range_Max': 3000}, + } +"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atmospheric_lidar/adam2017.py Mon Nov 06 16:02:11 2017 +0200 @@ -0,0 +1,16 @@ +from licel import LicelLidarMeasurement + +import adam2017_netcdf_parameters + +class ADAM2017LidarMeasurement(LicelLidarMeasurement): + extra_netcdf_parameters = adam2017_netcdf_parameters + + def _get_scc_mandatory_channel_variables(self): + channel_variables = \ + {'Background_Low': (('channels',), 'd'), + 'Background_High': (('channels',), 'd'), + 'LR_Input': (('channels',), 'i'), + 'DAQ_Range': (('channels',), 'd'), + 'First_Signal_Rangebin': (('channels',), 'i') + } + return channel_variables
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atmospheric_lidar/adam2017_depol.py Mon Nov 06 16:02:11 2017 +0200 @@ -0,0 +1,5 @@ +from licel_depol import LicelCalibrationMeasurement +import adam2017_depolarization_parameters + +class ADAM2017CalibrationMeasurement(LicelCalibrationMeasurement): + extra_netcdf_parameters = adam2017_depolarization_parameters
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atmospheric_lidar/adam2017_depolarization_parameters.py Mon Nov 06 16:02:11 2017 +0200 @@ -0,0 +1,116 @@ +general_parameters = \ +{'System': '\'ADAM May 2017\'', + 'Call sign': 'is', + 'Laser_Pointing_Angle': 0, + 'Molecular_Calc': 0, + 'Latitude_degrees_north': 45.8, + 'Longitude_degrees_east': 8.6, + 'Altitude_meter_asl': 209.0} # This should be float + +# Change to channel_parameters to use all channels. For testing I define only photon counting channels below. +channel_parameters = \ +{'00532.p_ph_p45': {'channel_ID': 929, + 'Background_Low': 45000, + 'Background_High': 60000, + 'Laser_Shots': 1201, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'Pol_Calib_Range_Min': 2000, + 'Pol_Calib_Range_Max': 3000}, + '00532.s_ph_p45': {'channel_ID': 931, + 'Background_Low': 45000, + 'Background_High': 60000, + 'Laser_Shots': 1201, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'Pol_Calib_Range_Min': 2000, + 'Pol_Calib_Range_Max': 3000}, + '00532.p_ph_m45': {'channel_ID': 928, + 'Background_Low': 45000, + 'Background_High': 60000, + 'Laser_Shots': 1201, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'Pol_Calib_Range_Min': 2000, + 'Pol_Calib_Range_Max': 3000}, + '00532.s_ph_m45': {'channel_ID': 930, + 'Background_Low': 45000, + 'Background_High': 60000, + 'Laser_Shots': 1201, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'Pol_Calib_Range_Min': 2000, + 'Pol_Calib_Range_Max': 3000}, + } + +""" +{'00532.p_an_p45': {'channel_ID': 383, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3000, + 'LR_Input': 1, + 'DAQ_Range': 100.0, + 'Depolarization_Factor': 0, + 'Pol_Calib_Range_Min': 1000, + 'Pol_Calib_Range_Max': 3000}, + '00532.p_ph_p45': {'channel_ID': 378, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3000, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'Depolarization_Factor': 0, + 'Pol_Calib_Range_Min': 1000, + 'Pol_Calib_Range_Max': 3000}, + '00532.s_an_p45': {'channel_ID': 385 , + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3000, + 'LR_Input': 1, + 'DAQ_Range': 20.0, + 'Pol_Calib_Range_Min': 1000, + 'Pol_Calib_Range_Max': 3000}, + '00532.s_ph_p45': {'channel_ID': 380, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3000, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'Pol_Calib_Range_Min': 1000, + 'Pol_Calib_Range_Max': 3000}, + '00532.p_an_m45': {'channel_ID': 384, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3000, + 'LR_Input': 1, + 'DAQ_Range': 100.0, + 'Depolarization_Factor': 0, + 'Pol_Calib_Range_Min': 1000, + 'Pol_Calib_Range_Max': 3000}, + '00532.p_ph_m45': {'channel_ID': 379, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3000, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'Depolarization_Factor': 0, + 'Pol_Calib_Range_Min': 1000, + 'Pol_Calib_Range_Max': 3000}, + '00532.s_an_m45': {'channel_ID': 386, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3000, + 'LR_Input': 1, + 'DAQ_Range': 20.0, + 'Pol_Calib_Range_Min': 1000, + 'Pol_Calib_Range_Max': 3000}, + '00532.s_ph_m45': {'channel_ID': 382, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3000, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'Pol_Calib_Range_Min': 1000, + 'Pol_Calib_Range_Max': 3000}, + } +"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atmospheric_lidar/adam2017_netcdf_parameters.py Mon Nov 06 16:02:11 2017 +0200 @@ -0,0 +1,89 @@ +general_parameters = \ +{'System': '\'ADAM May 2017\'', + 'Call sign': 'is', + 'Laser_Pointing_Angle': 0, + 'Molecular_Calc': 0, + 'Latitude_degrees_north': 45.8, + 'Longitude_degrees_east': 8.6, + 'Altitude_meter_asl': 209.0} # This should be float + +channel_parameters = \ +{'01064.o_an': {'channel_ID': 917, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 500.0, + 'First_Signal_Rangebin': 9}, + '00532.p_an': {'channel_ID': 918, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 500.0, + 'First_Signal_Rangebin': 9}, +'00532.p_ph': {'channel_ID': 919, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'First_Signal_Rangebin': 0}, + '00532.s_an': {'channel_ID': 920, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 500.0, + 'First_Signal_Rangebin': 9}, + '00532.s_ph': {'channel_ID': 921, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'First_Signal_Rangebin': 0}, + '00607.o_an': {'channel_ID': 922, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 500.0, + 'First_Signal_Rangebin': 9}, + '00607.o_ph': {'channel_ID': 923, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input':1, + 'DAQ_Range':500.0, + 'First_Signal_Rangebin': 0}, + '00355.o_an': {'channel_ID': 924, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 500.0, + 'First_Signal_Rangebin': 8}, + '00355.o_ph': {'channel_ID': 925, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'First_Signal_Rangebin': 0}, + '00387.o_an': {'channel_ID': 926, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 500.0, + 'First_Signal_Rangebin': 9}, + '00387.o_ph': {'channel_ID': 927, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'First_Signal_Rangebin': 0}, +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atmospheric_lidar/adam2017_netcdf_parameters_2.py Mon Nov 06 16:02:11 2017 +0200 @@ -0,0 +1,89 @@ +general_parameters = \ +{'System': '\'ADAM May 2017\'', + 'Call sign': 'is', + 'Laser_Pointing_Angle': 0, + 'Molecular_Calc': 0, + 'Latitude_degrees_north': 45.8, + 'Longitude_degrees_east': 8.6, + 'Altitude_meter_asl': 209.0} # This should be float + +channel_parameters = \ +{'01064.o_an': {'channel_ID': 917, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 100.0, + 'First_Signal_Rangebin': 9}, + '00532.p_an': {'channel_ID': 918, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 100.0, + 'First_Signal_Rangebin': 9}, +'00532.p_ph': {'channel_ID': 919, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'First_Signal_Rangebin': 0}, + '00532.s_an': {'channel_ID': 920, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 100.0, + 'First_Signal_Rangebin': 9}, + '00532.s_ph': {'channel_ID': 921, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'First_Signal_Rangebin': 0}, + '00607.o_an': {'channel_ID': 922, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 100.0, + 'First_Signal_Rangebin': 9}, + '00607.o_ph': {'channel_ID': 923, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input':1, + 'DAQ_Range':100.0, + 'First_Signal_Rangebin': 0}, + '00355.o_an': {'channel_ID': 924, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 100.0, + 'First_Signal_Rangebin': 8}, + '00355.o_ph': {'channel_ID': 925, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'First_Signal_Rangebin': 0}, + '00387.o_an': {'channel_ID': 926, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 100.0, + 'First_Signal_Rangebin': 9}, + '00387.o_ph': {'channel_ID': 927, + 'Background_Low': 50000, + 'Background_High': 60000, + 'Laser_Shots': 3001, + 'LR_Input': 1, + 'DAQ_Range': 0, + 'First_Signal_Rangebin': 0}, +} +
--- a/atmospheric_lidar/generic.py Mon Nov 06 16:01:35 2017 +0200 +++ b/atmospheric_lidar/generic.py Mon Nov 06 16:02:11 2017 +0200 @@ -334,12 +334,20 @@ temp_v = f.createVariable(channel_var, variable_type, ('channels',)) for n, channel in enumerate(channels): temp_v[n] = params.channel_parameters[channel][channel_var] - - # Write the values of fixed channel parameters - for (var, t) in channel_variables.iteritems(): - temp_v = f.createVariable(var, t[1], t[0]) + + # Write the values of fixed channel parameters: + fill_value = -9999 + for param in self._get_provided_extra_parameters(): + if param in channel_variables.keys(): + temp_v = f.createVariable(param, channel_variables[param][1], channel_variables[param][0]) + else: + temp_v = f.createVariable(param, 'd', ('channels',), fill_value = fill_value) + for (channel, n) in zip(channels, range(len(channels))): - temp_v[n] = params.channel_parameters[channel][var] + try: + temp_v[n] = params.channel_parameters[channel][param] + except KeyError: # The parameter was not provided for this channel so we mask the value. + temp_v[n] = fill_value # Write the id_timescale values temp_id_timescale = f.createVariable('id_timescale', 'i', ('channels',)) @@ -403,6 +411,34 @@ 'DAQ_Range': (('channels',), 'd'), } return channel_variables + + def _get_provided_extra_parameters(self): + # When looking for non-mandatory channel parameters, ignore the following parameter names: + ignore = ['channel_ID', 'channel string ID', 'Depolarization_Factor', 'Laser_Shots'] + + channels = self.channels.keys() + params = self.extra_netcdf_parameters.channel_parameters + mandatory = self._get_scc_mandatory_channel_variables() + + # Get all the provided extra parameters (both mandatory and optional): + provided_extra_parameters = [] + for (channel, n) in zip(channels, range(len(channels))): + # Check all the mandatory parameters are provided for each of the channels: + for var in mandatory.keys(): + if var not in params[channel].keys(): + raise ValueError ("Mandatory parameter '{0}' not provided for channel {1}!".format( + var, + channel + )) + + provided_extra_parameters.extend(params[channel].keys()) + + provided_extra_parameters = set(provided_extra_parameters) + # Discard certain parameter names: + for param in ignore: + provided_extra_parameters.discard(param) + + return provided_extra_parameters def add_dark_measurements_to_netcdf(self, f, channels): @@ -592,13 +628,14 @@ prof = np.mean(self.matrix, axis=0) return prof - def plot(self, signal_type='rc', filename=None, zoom=[0, 12000, 0, -1], show_plot=True, cmap=plt.cm.jet, z0=None, + def plot(self, figsize=(8, 4), signal_type='rc', zoom=[0, 12000, 0, -1], show_plot=True, cmap=plt.cm.jet, z0=None, title=None, vmin=0, vmax=1.3 * 10 ** 7, figsize=(8, 4)): # if filename is not None: # matplotlib.use('Agg') fig = plt.figure(figsize=figsize) ax1 = fig.add_subplot(111) + self.draw_plot(ax1, cmap=cmap, signal_type=signal_type, zoom=zoom, z0=z0, vmin=vmin, vmax=vmax) if title: @@ -606,13 +643,8 @@ else: ax1.set_title("%s signal - %s" % (signal_type.upper(), self.name)) - if filename is not None: - pass - # plt.savefig(filename) - else: - if show_plot: - plt.show() - # plt.close() ??? + if show_plot: + plt.show() def draw_plot(self, ax1, cmap=plt.cm.jet, signal_type='rc', zoom=[0, 12000, 0, -1], z0=None,
--- a/atmospheric_lidar/licel_depol.py Mon Nov 06 16:01:35 2017 +0200 +++ b/atmospheric_lidar/licel_depol.py Mon Nov 06 16:02:11 2017 +0200 @@ -7,14 +7,14 @@ class LicelCalibrationMeasurement(LicelLidarMeasurement): - def __init__(self, plus45_files=None, minus45_files=None): + def __init__(self, plus45_files=None, minus45_files=None, use_id_as_name=False): # Setup the empty class - super(LicelCalibrationMeasurement, self).__init__() + super(LicelCalibrationMeasurement, self).__init__(use_id_as_name=use_id_as_name) self.plus45_files = plus45_files self.minus45_files = minus45_files - if (plus45_files and minus45_files): + if plus45_files and minus45_files: self.check_equal_length() self.read_channel_data() @@ -39,10 +39,10 @@ def read_channel_data(self): # Read plus and minus 45 measurements - self.plus45_measurement = LicelLidarMeasurement(self.plus45_files) + self.plus45_measurement = LicelLidarMeasurement(self.plus45_files, self.use_id_as_name) self.plus45_measurement.rename_channel(suffix='_p45') - self.minus45_measurement = LicelLidarMeasurement(self.minus45_files) + self.minus45_measurement = LicelLidarMeasurement(self.minus45_files, self.use_id_as_name) self.minus45_measurement.rename_channel(suffix='_m45') # Combine them in this object @@ -109,14 +109,6 @@ self.update() - def subset_by_netcdf_channels(self): - """ - Subset the measurement based on the available netcdf channels in the parameters file. - """ - channels = self.extra_netcdf_parameters.channel_parameters.keys() - new_measurement = self.subset_by_channels(channels) - return new_measurement - def subset_photoncounting(self): """ Subset photoncounting channels. @@ -125,7 +117,7 @@ new_measurement = self.subset_by_channels(ph_channels) return new_measurement - def _get_scc_channel_variables(self): + def _get_scc_mandatory_channel_variables(self): """ Get a list of variables to put in the SCC. @@ -138,8 +130,7 @@ A dictionary with channel variable specifications. """ channel_variables = \ - {'channel_ID': (('channels',), 'i'), - 'Background_Low': (('channels',), 'd'), + {'Background_Low': (('channels',), 'd'), 'Background_High': (('channels',), 'd'), 'LR_Input': (('channels',), 'i'), 'DAQ_Range': (('channels',), 'd'),
--- a/atmospheric_lidar/rali_depolarization_parameters.py Mon Nov 06 16:01:35 2017 +0200 +++ b/atmospheric_lidar/rali_depolarization_parameters.py Mon Nov 06 16:02:11 2017 +0200 @@ -77,80 +77,4 @@ 'DAQ_Range': 0, 'Pol_Calib_Range_Min': 1000, 'Pol_Calib_Range_Max': 3000}, - } - - -channel_parameters_full = \ -{ '00532.p_an_p45': {'channel_ID': 383, - 'Background_Low': 50000, - 'Background_High': 60000, - 'Laser_Shots': 3000, - 'LR_Input': 1, - 'DAQ_Range': 100.0, - 'Depolarization_Factor': 0, - 'Pol_Calib_Range_Min': 1000, - 'Pol_Calib_Range_Max': 3000}, - '00532.p_ph_p45': {'channel_ID': 378, - 'Background_Low': 50000, - 'Background_High': 60000, - 'Laser_Shots': 3000, - 'LR_Input': 1, - 'DAQ_Range': 0, - 'Depolarization_Factor': 0, - 'Pol_Calib_Range_Min': 1000, - 'Pol_Calib_Range_Max': 3000}, - '00532.s_an_p45': {'channel_ID': 385 , - 'Background_Low': 50000, - 'Background_High': 60000, - 'Laser_Shots': 3000, - 'LR_Input': 1, - 'DAQ_Range': 20.0, - 'Depolarization_Factor': 0.115, - 'Pol_Calib_Range_Min': 1000, - 'Pol_Calib_Range_Max': 3000}, - '00532.s_ph_p45': {'channel_ID': 380, - 'Background_Low': 50000, - 'Background_High': 60000, - 'Laser_Shots': 3000, - 'LR_Input': 1, - 'DAQ_Range': 0, - 'Depolarization_Factor': 0.115, - 'Pol_Calib_Range_Min': 1000, - 'Pol_Calib_Range_Max': 3000}, - '00532.p_an_m45': {'channel_ID': 384, - 'Background_Low': 50000, - 'Background_High': 60000, - 'Laser_Shots': 3000, - 'LR_Input': 1, - 'DAQ_Range': 100.0, - 'Depolarization_Factor': 0, - 'Pol_Calib_Range_Min': 1000, - 'Pol_Calib_Range_Max': 3000}, - '00532.p_ph_m45': {'channel_ID': 379, - 'Background_Low': 50000, - 'Background_High': 60000, - 'Laser_Shots': 3000, - 'LR_Input': 1, - 'DAQ_Range': 0, - 'Depolarization_Factor': 0, - 'Pol_Calib_Range_Min': 1000, - 'Pol_Calib_Range_Max': 3000}, - '00532.s_an_m45': {'channel_ID': 386, - 'Background_Low': 50000, - 'Background_High': 60000, - 'Laser_Shots': 3000, - 'LR_Input': 1, - 'DAQ_Range': 20.0, - 'Depolarization_Factor': 0.115, - 'Pol_Calib_Range_Min': 1000, - 'Pol_Calib_Range_Max': 3000}, - '00532.s_ph_m45': {'channel_ID': 382, - 'Background_Low': 50000, - 'Background_High': 60000, - 'Laser_Shots': 3000, - 'LR_Input': 1, - 'DAQ_Range': 0, - 'Depolarization_Factor': 0.115, - 'Pol_Calib_Range_Min': 1000, - 'Pol_Calib_Range_Max': 3000}, - } + } \ No newline at end of file
--- a/atmospheric_lidar/scripts/licel2scc.py Mon Nov 06 16:01:35 2017 +0200 +++ b/atmospheric_lidar/scripts/licel2scc.py Mon Nov 06 16:02:11 2017 +0200 @@ -9,6 +9,7 @@ from ..licel import LicelLidarMeasurement +from ..__init__ import __version__ def create_custom_class(custom_netcdf_parameter_path, use_id_as_name=False, temperature=25., pressure=1020.): @@ -73,9 +74,8 @@ # Define the command line argument parser = argparse.ArgumentParser(description="A program to convert Licel binary files to the SCC NetCDF format.") parser.add_argument("parameter_file", help="The path to a parameter file linking licel and SCC channels.") - parser.add_argument("directory", nargs='?', help="Directory containing licel files (default '.')", default='.') - parser.add_argument("search_string", nargs='?', help="Search string for files in directory (default '*.*')", - default="*.*") + parser.add_argument("files", nargs='?', help="Location of licel files. Use relative path and filename wildcards. (default './*.*')", + default="./*.*") parser.add_argument("-i", '--id_as_name', help="Use transient digitizer ids as channel names, instead of descriptive names", action="store_true") @@ -96,6 +96,7 @@ parser.add_argument('-s', '--silent', help="Show only warning and error messages.", action="store_const", dest="loglevel", const=logging.WARNING ) + parser.add_argument('--version', help="Show current version.", action='store_true') args = parser.parse_args() @@ -105,13 +106,16 @@ #coloredlogs.install(fmt='%(levelname)s: %(message)s', level=args.loglevel) + if args.version: + print("Version: %s" % __version__) + sys.exit(0) + # Get a list of files to convert - search_str = os.path.join(args.directory, args.search_string) - files = glob.glob(search_str) + files = glob.glob(args.files) if files: # Read the files - logger.info("Reading {0} files from {1}".format(len(files), args.directory)) + logger.info("Reading {0} files from {1}".format(len(files), os.path.abspath(os.path.dirname(args.files)))) CustomLidarMeasurement = create_custom_class(args.parameter_file, args.id_as_name, args.temperature, args.pressure) measurement = CustomLidarMeasurement(files)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atmospheric_lidar/scripts/licel2scc_depol.py Mon Nov 06 16:02:11 2017 +0200 @@ -0,0 +1,140 @@ +""" Command line tool to convert Licel binary files to SCC NetCDF format. +""" +import argparse +import glob +import importlib +import logging +import os +import sys + + +from ..licel_depol import LicelCalibrationMeasurement +from ..__init__ import __version__ + + +def create_custom_class(custom_netcdf_parameter_path, use_id_as_name=False, temperature=25., pressure=1020.): + """ This funtion creates a custom LicelLidarMeasurement subclass, + based on the input provided by the users. + + Parameters + ---------- + custom_netcdf_parameter_path: str + The path to the custom channels parameters. + use_id_as_name: bool + Defines if channels names are descriptive or transient digitizer IDs. + temperature: float + The ground temperature in degrees C (default 25.0). + pressure: float + The ground pressure in hPa (default: 1020.0). + + Returns + ------- + CustomLidarMeasurement: + A custom sub-class of LicelLidarMeasurement + """ + + custom_netcdf_parameters = read_settings_file(custom_netcdf_parameter_path) + + class CustomLidarMeasurement(LicelCalibrationMeasurement): + extra_netcdf_parameters = custom_netcdf_parameters + + def __init__(self, plus45_files=None, minus45_files=None): + super(CustomLidarMeasurement, self).__init__(plus45_files, minus45_files, use_id_as_name) + + def get_PT(self): + ''' Sets the pressure and temperature at station level. This is used if molecular_calc parameter is + set to 0 (i.e. use US Standard atmosphere). + + The results are stored in the info dictionary. + ''' + + self.info['Temperature'] = temperature + self.info['Pressure'] = pressure + + return CustomLidarMeasurement + + +def read_settings_file(settings_path): + """ Read the settings file. + + The file should contain python code.""" + if not os.path.isfile(settings_path): + logging.error("The provided settings path does not correspond to a file.") + sys.exit(1) + + dirname, basename = os.path.split(settings_path) + sys.path.append(dirname) + + module_name, _ = os.path.splitext(basename) + settings = importlib.import_module(module_name) + return settings + + +def main(): + # Define the command line argument + parser = argparse.ArgumentParser(description="A program to convert Licel binary files from depolarization calibration measurements to the SCC NetCDF format.") + parser.add_argument("parameter_file", help="The path to a parameter file linking licel and SCC channels.") + parser.add_argument("plus45_string", help="Search string for plus 45 degree files") + parser.add_argument("minus45_string", help="Search string for minus 45 degree files") + parser.add_argument("-i", '--id_as_name', + help="Use transient digitizer ids as channel names, instead of descriptive names", + action="store_true") + parser.add_argument("-m", "--measurement_id", help="The new measurement id", default=None) + parser.add_argument("-n", "--measurement_number", + help="The measurement number for the date from 00 to 99. Used if no id is provided", + default="00") + parser.add_argument("-t", "--temperature", type=float, + help="The temperature (in C) at lidar level, required if using US Standard atmosphere", + default="25") + parser.add_argument("-p", "--pressure", type=float, + help="The pressure (in hPa) at lidar level, required if using US Standard atmosphere", + default="1020") + # Verbosity settings from http://stackoverflow.com/a/20663028 + parser.add_argument('-d', '--debug', help="Print dubuging information.", action="store_const", + dest="loglevel", const=logging.DEBUG, default=logging.INFO, + ) + parser.add_argument('-s', '--silent', help="Show only warning and error messages.", action="store_const", + dest="loglevel", const=logging.WARNING + ) + parser.add_argument('--version', help="Show current version.", action='store_true') + + args = parser.parse_args() + + # Get the logger with the appropriate level + logging.basicConfig(format='%(levelname)s: %(message)s', level=args.loglevel) + logger = logging.getLogger(__name__) + + #coloredlogs.install(fmt='%(levelname)s: %(message)s', level=args.loglevel) + + if args.version: + print("Version: %s" % __version__) + sys.exit(0) + + # Get a list of files to convert + plus45_files = glob.glob(args.plus45_string) + minus45_files = glob.glob(args.minus45_string) + + if len(plus45_files)==0 or len(minus45_files)==0: + logger.error("No files found when searching for %s and %s." % (plus45_files, minus45_files)) + sys.exit(1) + + # Read the files + logger.info("Reading {0} files from {1}".format(len(plus45_files), args.plus45_string)) + logger.info("Reading {0} files from {1}".format(len(minus45_files), args.minus45_string)) + + CustomLidarMeasurement = create_custom_class(args.parameter_file, args.id_as_name, args.temperature, + args.pressure) + + measurement = CustomLidarMeasurement(plus45_files, minus45_files) + + try: + measurement = measurement.subset_by_scc_channels() + except ValueError as err: + logging.error(err) + sys.exit(1) + + # Save the netcdf + logger.info("Saving netcdf") + measurement.set_measurement_id(args.measurement_id, args.measurement_number) + measurement.save_as_netcdf() + logger.info("Created file %s" % measurement.scc_filename)
--- a/readme.rst Mon Nov 06 16:01:35 2017 +0200 +++ b/readme.rst Mon Nov 06 16:02:11 2017 +0200 @@ -7,6 +7,12 @@ The package provides a single command line tool, called licel2scc that can convert Licel binary files to the EARLINET's Single Calculus Chain NetCDF format. +Installation +============ + +The easiest way to install this module is from the python package index using ``pip``:: + + pip install atmospheric-lidar Using it as a Licel to SCC converter ==================================== @@ -20,19 +26,15 @@ Command line interface ---------------------- -The usage of the ``licel2scc`` program is described bellow:: +The usage of the ``licel2scc`` program is described below:: - usage: licel2scc [-h] [-i] [-m MEASUREMENT_ID] [-n MEASUREMENT_NUMBER] - [-t TEMPERATURE] [-p PRESSURE] [-d] [-s] - parameter_file [directory] [search_string] - - A program to convert LICEL binary files to the SCC NetCDF format. + A program to convert Licel binary files to the SCC NetCDF format. positional arguments: parameter_file The path to a parameter file linking licel and SCC channels. - directory Directory containing licel files (default '.') - search_string Search string for files in directory (default '*.*') + files Location of licel files. Use relative path and + filename wildcards. (default './*.*') optional arguments: -h, --help show this help message and exit @@ -51,7 +53,39 @@ using US Standard atmosphere -d, --debug Print dubuging information. -s, --silent Show only warning and error messages. + --version Show current version. +Similarly, the ``licel2scc-depol`` program can be used to convert +Licel files from Delta45 depolarization calibration measurements:: + + A program to convert Licel binary files from depolarization calibration + measurements to the SCC NetCDF format. + + positional arguments: + parameter_file The path to a parameter file linking licel and SCC + channels. + plus45_string Search string for plus 45 degree files (default '*.*') + minus45_string Search string for minus 45 degree files (default + '*.*') + + optional arguments: + -h, --help show this help message and exit + -i, --id_as_name Use transient digitizer ids as channel names, instead + of descriptive names + -m MEASUREMENT_ID, --measurement_id MEASUREMENT_ID + The new measurement id + -n MEASUREMENT_NUMBER, --measurement_number MEASUREMENT_NUMBER + The measurement number for the date from 00 to 99. + Used if no id is provided + -t TEMPERATURE, --temperature TEMPERATURE + The temperature (in C) at lidar level, required if + using US Standard atmosphere + -p PRESSURE, --pressure PRESSURE + The pressure (in hPa) at lidar level, required if + using US Standard atmosphere + -d, --debug Print dubuging information. + -s, --silent Show only warning and error messages. + --version Show current version. Usage in python code --------------------
--- a/setup.py Mon Nov 06 16:01:35 2017 +0200 +++ b/setup.py Mon Nov 06 16:02:11 2017 +0200 @@ -54,6 +54,7 @@ "sphinx", ], entry_points={ - 'console_scripts': ['licel2scc = atmospheric_lidar.scripts.licel2scc:main'], + 'console_scripts': ['licel2scc = atmospheric_lidar.scripts.licel2scc:main', + 'licel2scc-depol = atmospheric_lidar.scripts.licel2scc_depol:main'], }, )