Thu, 13 Sep 2018 14:15:34 +0300
Merge from 151:d89be5efc39c
ulalume3@64 | 1 | """ Command line tool to convert Licel binary files to SCC NetCDF format. |
ulalume3@64 | 2 | """ |
ulalume3@64 | 3 | import argparse |
ulalume3@64 | 4 | import glob |
ulalume3@64 | 5 | import importlib |
ulalume3@64 | 6 | import logging |
ulalume3@64 | 7 | import os |
ulalume3@64 | 8 | import sys |
ulalume3@64 | 9 | |
ulalume3@64 | 10 | from ..licel_depol import LicelCalibrationMeasurement |
ulalume3@67 | 11 | from ..__init__ import __version__ |
ulalume3@64 | 12 | |
i@103 | 13 | logger = logging.getLogger(__name__) |
i@103 | 14 | |
ulalume3@64 | 15 | |
i@101 | 16 | def create_custom_class(custom_netcdf_parameter_path, use_id_as_name=False, temperature=25., pressure=1020., |
i@101 | 17 | licel_timezone='UTC'): |
ulalume3@64 | 18 | """ This funtion creates a custom LicelLidarMeasurement subclass, |
ulalume3@64 | 19 | based on the input provided by the users. |
ulalume3@64 | 20 | |
ulalume3@64 | 21 | Parameters |
ulalume3@64 | 22 | ---------- |
i@103 | 23 | custom_netcdf_parameter_path : str |
ulalume3@64 | 24 | The path to the custom channels parameters. |
i@103 | 25 | use_id_as_name : bool |
ulalume3@64 | 26 | Defines if channels names are descriptive or transient digitizer IDs. |
i@103 | 27 | temperature : float |
ulalume3@64 | 28 | The ground temperature in degrees C (default 25.0). |
i@103 | 29 | pressure : float |
ulalume3@64 | 30 | The ground pressure in hPa (default: 1020.0). |
i@101 | 31 | licel_timezone : str |
i@101 | 32 | String describing the timezone according to the tz database. |
ulalume3@64 | 33 | |
ulalume3@64 | 34 | Returns |
ulalume3@64 | 35 | ------- |
ulalume3@64 | 36 | CustomLidarMeasurement: |
ulalume3@64 | 37 | A custom sub-class of LicelLidarMeasurement |
ulalume3@64 | 38 | """ |
ulalume3@64 | 39 | |
ulalume3@64 | 40 | custom_netcdf_parameters = read_settings_file(custom_netcdf_parameter_path) |
ulalume3@64 | 41 | |
ulalume3@64 | 42 | class CustomLidarMeasurement(LicelCalibrationMeasurement): |
ulalume3@64 | 43 | extra_netcdf_parameters = custom_netcdf_parameters |
ulalume3@64 | 44 | |
ulalume3@69 | 45 | def __init__(self, plus45_files=None, minus45_files=None): |
i@101 | 46 | super(CustomLidarMeasurement, self).__init__(plus45_files, minus45_files, use_id_as_name, |
i@101 | 47 | licel_timezone=licel_timezone) |
ulalume3@64 | 48 | |
ulalume3@92 | 49 | def set_PT(self): |
ulalume3@64 | 50 | ''' Sets the pressure and temperature at station level. This is used if molecular_calc parameter is |
ulalume3@64 | 51 | set to 0 (i.e. use US Standard atmosphere). |
ulalume3@64 | 52 | |
ulalume3@64 | 53 | The results are stored in the info dictionary. |
ulalume3@64 | 54 | ''' |
ulalume3@64 | 55 | |
ulalume3@64 | 56 | self.info['Temperature'] = temperature |
ulalume3@64 | 57 | self.info['Pressure'] = pressure |
ulalume3@64 | 58 | |
ulalume3@64 | 59 | return CustomLidarMeasurement |
ulalume3@64 | 60 | |
ulalume3@64 | 61 | |
ulalume3@64 | 62 | def read_settings_file(settings_path): |
ulalume3@64 | 63 | """ Read the settings file. |
ulalume3@64 | 64 | |
ulalume3@64 | 65 | The file should contain python code.""" |
ulalume3@64 | 66 | if not os.path.isfile(settings_path): |
ulalume3@64 | 67 | logging.error("The provided settings path does not correspond to a file.") |
ulalume3@64 | 68 | sys.exit(1) |
ulalume3@64 | 69 | |
ulalume3@64 | 70 | dirname, basename = os.path.split(settings_path) |
ulalume3@64 | 71 | sys.path.append(dirname) |
ulalume3@64 | 72 | |
ulalume3@64 | 73 | module_name, _ = os.path.splitext(basename) |
ulalume3@64 | 74 | settings = importlib.import_module(module_name) |
ulalume3@64 | 75 | return settings |
ulalume3@64 | 76 | |
ulalume3@64 | 77 | |
ulalume3@64 | 78 | def main(): |
ulalume3@64 | 79 | # Define the command line argument |
ulalume3@68 | 80 | parser = argparse.ArgumentParser(description="A program to convert Licel binary files from depolarization calibration measurements to the SCC NetCDF format.") |
ulalume3@64 | 81 | parser.add_argument("parameter_file", help="The path to a parameter file linking licel and SCC channels.") |
ulalume3@69 | 82 | parser.add_argument("plus45_string", help="Search string for plus 45 degree files") |
ulalume3@69 | 83 | parser.add_argument("minus45_string", help="Search string for minus 45 degree files") |
ulalume3@64 | 84 | parser.add_argument("-i", '--id_as_name', |
ulalume3@64 | 85 | help="Use transient digitizer ids as channel names, instead of descriptive names", |
ulalume3@64 | 86 | action="store_true") |
ulalume3@64 | 87 | parser.add_argument("-m", "--measurement_id", help="The new measurement id", default=None) |
ulalume3@64 | 88 | parser.add_argument("-n", "--measurement_number", |
ulalume3@64 | 89 | help="The measurement number for the date from 00 to 99. Used if no id is provided", |
ulalume3@64 | 90 | default="00") |
ulalume3@64 | 91 | parser.add_argument("-t", "--temperature", type=float, |
ulalume3@64 | 92 | help="The temperature (in C) at lidar level, required if using US Standard atmosphere", |
ulalume3@64 | 93 | default="25") |
ulalume3@64 | 94 | parser.add_argument("-p", "--pressure", type=float, |
ulalume3@64 | 95 | help="The pressure (in hPa) at lidar level, required if using US Standard atmosphere", |
ulalume3@64 | 96 | default="1020") |
i@101 | 97 | parser.add_argument('--licel_timezone', help="String describing the timezone according to the tz database.", |
i@101 | 98 | default="UTC", dest="licel_timezone", |
i@101 | 99 | ) |
ulalume3@64 | 100 | # Verbosity settings from http://stackoverflow.com/a/20663028 |
ulalume3@64 | 101 | parser.add_argument('-d', '--debug', help="Print dubuging information.", action="store_const", |
ulalume3@64 | 102 | dest="loglevel", const=logging.DEBUG, default=logging.INFO, |
ulalume3@64 | 103 | ) |
ulalume3@64 | 104 | parser.add_argument('-s', '--silent', help="Show only warning and error messages.", action="store_const", |
ulalume3@64 | 105 | dest="loglevel", const=logging.WARNING |
ulalume3@64 | 106 | ) |
victor@106 | 107 | parser.add_argument('-D', '--dark_measurements', help="Location of files containing dark measurements. Use relative path and filename wildcars, see 'files' parameter for example.", |
victor@106 | 108 | default="", dest="dark_files" |
victor@106 | 109 | ) |
ulalume3@67 | 110 | parser.add_argument('--version', help="Show current version.", action='store_true') |
ulalume3@64 | 111 | |
ulalume3@64 | 112 | args = parser.parse_args() |
ulalume3@64 | 113 | |
ulalume3@64 | 114 | # Get the logger with the appropriate level |
ulalume3@64 | 115 | logging.basicConfig(format='%(levelname)s: %(message)s', level=args.loglevel) |
ulalume3@64 | 116 | logger = logging.getLogger(__name__) |
ulalume3@64 | 117 | |
ulalume3@64 | 118 | #coloredlogs.install(fmt='%(levelname)s: %(message)s', level=args.loglevel) |
ulalume3@64 | 119 | |
ulalume3@67 | 120 | if args.version: |
ulalume3@67 | 121 | print("Version: %s" % __version__) |
ulalume3@67 | 122 | sys.exit(0) |
ulalume3@67 | 123 | |
ulalume3@64 | 124 | # Get a list of files to convert |
ulalume3@64 | 125 | plus45_files = glob.glob(args.plus45_string) |
ulalume3@64 | 126 | minus45_files = glob.glob(args.minus45_string) |
ulalume3@64 | 127 | |
ulalume3@64 | 128 | if len(plus45_files)==0 or len(minus45_files)==0: |
ulalume3@64 | 129 | logger.error("No files found when searching for %s and %s." % (plus45_files, minus45_files)) |
ulalume3@64 | 130 | sys.exit(1) |
ulalume3@64 | 131 | |
ulalume3@64 | 132 | # Read the files |
ulalume3@64 | 133 | logger.info("Reading {0} files from {1}".format(len(plus45_files), args.plus45_string)) |
ulalume3@64 | 134 | logger.info("Reading {0} files from {1}".format(len(minus45_files), args.minus45_string)) |
ulalume3@64 | 135 | |
ulalume3@64 | 136 | CustomLidarMeasurement = create_custom_class(args.parameter_file, args.id_as_name, args.temperature, |
i@101 | 137 | args.pressure, args.licel_timezone) |
ulalume3@64 | 138 | |
ulalume3@64 | 139 | measurement = CustomLidarMeasurement(plus45_files, minus45_files) |
victor@106 | 140 | |
victor@106 | 141 | # Get a list of files containing dark measurements |
victor@106 | 142 | if args.dark_files != "": |
victor@106 | 143 | dark_files = glob.glob(args.dark_files) |
victor@106 | 144 | |
victor@106 | 145 | if dark_files: |
victor@106 | 146 | logger.debug("Using %s as dark measurements files!" % ', '.join(dark_files)) |
victor@106 | 147 | measurement.dark_measurement = CustomLidarMeasurement(dark_files, dark_files) |
victor@106 | 148 | else: |
victor@106 | 149 | logger.warning('No dark measurement files found when searching for %s. Will not use any dark measurements.' % args.dark_files) |
ulalume3@64 | 150 | |
ulalume3@64 | 151 | try: |
ulalume3@64 | 152 | measurement = measurement.subset_by_scc_channels() |
ulalume3@64 | 153 | except ValueError as err: |
ulalume3@64 | 154 | logging.error(err) |
ulalume3@64 | 155 | sys.exit(1) |
ulalume3@64 | 156 | |
ulalume3@64 | 157 | # Save the netcdf |
ulalume3@64 | 158 | logger.info("Saving netcdf") |
ulalume3@64 | 159 | measurement.set_measurement_id(args.measurement_id, args.measurement_number) |
ulalume3@92 | 160 | measurement.save_as_SCC_netcdf() |
ulalume3@64 | 161 | logger.info("Created file %s" % measurement.scc_filename) |