Thu, 16 Feb 2017 16:35:42 +0200
Added logging and collored logging through CLI.
ioannis@48 | 1 | """ Command line tool to convert Licel binary files to SCC NetCDF format. |
ioannis@48 | 2 | """ |
ioannis@55 | 3 | import argparse |
ioannis@55 | 4 | import glob |
ioannis@55 | 5 | import importlib |
ioannis@55 | 6 | import logging |
ioannis@48 | 7 | import os |
ioannis@48 | 8 | import sys |
ioannis@55 | 9 | |
ioannis@55 | 10 | import coloredlogs |
ioannis@48 | 11 | |
ioannis@48 | 12 | from ..licel import LicelLidarMeasurement |
ioannis@48 | 13 | |
ioannis@48 | 14 | |
ioannis@48 | 15 | def create_custom_class(custom_netcdf_parameter_path, use_id_as_name=False, temperature=25., pressure=1020.): |
ioannis@48 | 16 | """ This funtion creates a custom LicelLidarMeasurement subclass, |
ioannis@48 | 17 | based on the input provided by the users. |
ioannis@48 | 18 | |
ioannis@48 | 19 | Parameters |
ioannis@48 | 20 | ---------- |
ioannis@48 | 21 | custom_netcdf_parameter_path: str |
ioannis@48 | 22 | The path to the custom channels parameters. |
ioannis@48 | 23 | use_id_as_name: bool |
ioannis@48 | 24 | Defines if channels names are descriptive or transient digitizer IDs. |
ioannis@48 | 25 | temperature: float |
ioannis@48 | 26 | The ground temperature in degrees C (default 25.0). |
ioannis@48 | 27 | pressure: float |
ioannis@48 | 28 | The ground pressure in hPa (default: 1020.0). |
ioannis@48 | 29 | |
ioannis@48 | 30 | Returns |
ioannis@48 | 31 | ------- |
ioannis@48 | 32 | CustomLidarMeasurement: |
ioannis@48 | 33 | A custom sub-class of LicelLidarMeasurement |
ioannis@48 | 34 | """ |
ioannis@48 | 35 | |
ioannis@48 | 36 | custom_netcdf_parameters = read_settings_file(custom_netcdf_parameter_path) |
ioannis@48 | 37 | |
ioannis@48 | 38 | class CustomLidarMeasurement(LicelLidarMeasurement): |
ioannis@48 | 39 | extra_netcdf_parameters = custom_netcdf_parameters |
ioannis@48 | 40 | |
ioannis@48 | 41 | def __init__(self, filelist=None): |
ioannis@48 | 42 | super(CustomLidarMeasurement, self).__init__(filelist, use_id_as_name) |
ioannis@48 | 43 | |
ioannis@48 | 44 | def get_PT(self): |
ioannis@48 | 45 | ''' Sets the pressure and temperature at station level. This is used if molecular_calc parameter is |
ioannis@48 | 46 | set to 0 (i.e. use US Standard atmosphere). |
ioannis@48 | 47 | |
ioannis@48 | 48 | The results are stored in the info dictionary. |
ioannis@48 | 49 | ''' |
ioannis@48 | 50 | |
ioannis@48 | 51 | self.info['Temperature'] = temperature |
ioannis@48 | 52 | self.info['Pressure'] = pressure |
ioannis@48 | 53 | |
ioannis@48 | 54 | return CustomLidarMeasurement |
ioannis@48 | 55 | |
ioannis@48 | 56 | |
ioannis@48 | 57 | def read_settings_file(settings_path): |
ioannis@48 | 58 | """ Read the settings file. |
ioannis@48 | 59 | |
ioannis@48 | 60 | The file should contain python code.""" |
ioannis@48 | 61 | if not os.path.isfile(settings_path): |
ioannis@48 | 62 | raise IOError("The provided settings path does not correspond to a file.") |
ioannis@48 | 63 | |
ioannis@48 | 64 | dirname, basename = os.path.split(settings_path) |
ioannis@48 | 65 | sys.path.append(dirname) |
ioannis@48 | 66 | |
ioannis@48 | 67 | module_name, _ = os.path.splitext(basename) |
ioannis@48 | 68 | settings = importlib.import_module(module_name) |
ioannis@48 | 69 | return settings |
ioannis@48 | 70 | |
ioannis@48 | 71 | |
ioannis@48 | 72 | def main(): |
ioannis@48 | 73 | # Define the command line argument |
ioannis@48 | 74 | parser = argparse.ArgumentParser(description="A program to convert LICEL binary files to the SCC NetCDF format.") |
ioannis@48 | 75 | parser.add_argument("parameter_file", help="The path to a parameter file linking licel and SCC channels.") |
ioannis@48 | 76 | parser.add_argument("directory", nargs='?', help="Directory containing licel files (default '.')", default='.') |
ioannis@55 | 77 | parser.add_argument("search_string", nargs='?', help="Search string for files in directory (default '*.*')", |
ioannis@55 | 78 | default="*.*") |
ioannis@48 | 79 | parser.add_argument("-i", '--id_as_name', |
ioannis@48 | 80 | help="Use transient digitizer ids as channel names, instead of descriptive names", |
ioannis@48 | 81 | action="store_true") |
ioannis@48 | 82 | parser.add_argument("-m", "--measurement_id", help="The new measurement id", default=None) |
ioannis@48 | 83 | parser.add_argument("-n", "--measurement_number", |
ioannis@48 | 84 | help="The measurement number for the date from 00 to 99. Used if no id is provided", |
ioannis@48 | 85 | default="00") |
ioannis@48 | 86 | parser.add_argument("-t", "--temperature", type=float, |
ioannis@48 | 87 | help="The temperature (in C) at lidar level, required if using US Standard atmosphere", |
ioannis@48 | 88 | default="25") |
ioannis@48 | 89 | parser.add_argument("-p", "--pressure", type=float, |
ioannis@48 | 90 | help="The pressure (in hPa) at lidar level, required if using US Standard atmosphere", |
ioannis@48 | 91 | default="1020") |
ioannis@55 | 92 | # Verbosity settings from http://stackoverflow.com/a/20663028 |
ioannis@55 | 93 | parser.add_argument('-d', '--debug', help="Print dubuging information.", action="store_const", |
ioannis@55 | 94 | dest="loglevel", const=logging.DEBUG, default=logging.INFO, |
ioannis@55 | 95 | ) |
ioannis@55 | 96 | parser.add_argument('-s', '--silent', help="Show processing information.", action="store_const", |
ioannis@55 | 97 | dest="loglevel", const=logging.WARNING |
ioannis@55 | 98 | ) |
ioannis@55 | 99 | |
ioannis@48 | 100 | args = parser.parse_args() |
ioannis@48 | 101 | |
ioannis@55 | 102 | # Get the logger with the appropriate level |
ioannis@55 | 103 | #logging.basicConfig(format='%(levelname)s: %(message)s', level=args.loglevel) |
ioannis@55 | 104 | logger = logging.getLogger(__name__) |
ioannis@55 | 105 | |
ioannis@55 | 106 | coloredlogs.install(fmt='%(levelname)s: %(message)s', level=args.loglevel) |
ioannis@55 | 107 | |
ioannis@48 | 108 | # Get a list of files to convert |
ioannis@48 | 109 | search_str = os.path.join(args.directory, args.search_string) |
ioannis@48 | 110 | files = glob.glob(search_str) |
ioannis@48 | 111 | |
ioannis@48 | 112 | if files: |
ioannis@48 | 113 | # Read the files |
ioannis@55 | 114 | logger.info("Reading {0} files from {1}".format(len(files), args.directory)) |
ioannis@48 | 115 | CustomLidarMeasurement = create_custom_class(args.parameter_file, args.id_as_name, args.temperature, |
ioannis@48 | 116 | args.pressure) |
ioannis@48 | 117 | measurement = CustomLidarMeasurement(files) |
ioannis@55 | 118 | try: |
ioannis@55 | 119 | measurement = measurement.subset_by_scc_channels() |
ioannis@55 | 120 | except ValueError as err: |
ioannis@55 | 121 | logging.error(err) |
ioannis@55 | 122 | sys.exit(1) |
ioannis@48 | 123 | # Save the netcdf |
ioannis@55 | 124 | logger.info("Saving netcdf") |
ioannis@48 | 125 | measurement.set_measurement_id(args.measurement_id, args.measurement_number) |
ioannis@48 | 126 | measurement.save_as_netcdf() |
ioannis@55 | 127 | logger.info("Created file %s" % measurement.scc_filename) |
ioannis@48 | 128 | else: |
ioannis@55 | 129 | logger.error("No files found when searching for %s." % search_str) |
ioannis@55 | 130 | sys.exit(1) |