--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atmospheric_lidar/diva.py Thu Dec 14 22:47:33 2017 +0200 @@ -0,0 +1,171 @@ +""" This is a class for experimenting with the new DIVA / EARLINET NetCDF file format. + +In the long run, this should be places as a method in BaseLidarMeasurement class. For now it is kept +separately not to interfere with normal development. +""" +import netCDF4 as netcdf +import pyyaml +import datetime +import os + +from .generic import BaseLidarMeasurement + + +class DivaOutput(BaseLidarMeasurement): + + def save_as_diva(self, output_path, parameter_file): + """ Save the current data in the 'draft' DIVA format. """ + + with open(parameter_file, 'r') as f: + parameters = pyyaml.load(f) + + global_parameters = parameters['global_parameters'] # Shortcut + + iso_date = datetime.datetime.utcnow().isoformat() + python_file_name = os.path.basename(__file__) + + with netcdf.Dataset(output_path, 'w', format="NETCDF4") as f: + + # Global attributes + f.title = global_parameters['title'] + f.source = global_parameters['source'] + f.institution = global_parameters['institution'] + f.references = global_parameters['references'] + f.location = global_parameters['location'] + f.data_version = global_parameters['data_version'] + f.conversion_date = iso_date + f.comment = global_parameters['comment'] + f.Conventions = global_parameters['Conventions'] + f.history = global_parameters['history'].format(date=iso_date, file=python_file_name) + f.featureType = "timeSeriesProfile" + + # Top level variables + latitude = f.createVariable('latitude', datatype='f4') + latitude.standard_name = 'latitude' + latitude.long_name = 'system latitude' + latitude.units = 'degrees_north' + + longitude = f.createVariable('longitude', datatype='f4') + longitude.standard_name = 'longitude' + longitude.long_name = 'system longitude' + longitude.units = 'degrees_east' + + laser_angle = f.createVariable('laser_zenith_angle', datatype='f4') + laser_angle.standard_name = 'sensor_zenith_angle' + laser_angle.long_name = 'zenith angle of emitted laser' + laser_angle.units = 'degree' + + laser_angle = f.createVariable('laser_zenith_angle', datatype='f4') + laser_angle.long_name = 'zenith angle of emitted laser' + laser_angle.units = 'degrees' + + altitude = f.createVariable('altitude', datatype='f4') + altitude.standard_name = 'altitude' + altitude.long_name = 'system altitude' + altitude.units = 'm' + + + # Create a separate group for each channel + f = f.createGroup("channel_532nmP") # This should be changed to a meaningful channel name + f.data_type = self.data_type + + f.createDimension('profile', len(self.time)) + f.createDimension('range', len(self.range)) + + id = f.createVariable('channel_id', 'i4') + id.cf_role = "trajectory_id" + + latitude = f.createVariable('latitude', datatype='f8', dimensions=('profile',), zlib=True) + latitude.long_name = 'aircraft GPS latitude' + latitude.units = 'degrees_north' + latitude.standard_name = 'latitude' + + longitude = f.createVariable('longitude', datatype='f8', dimensions=('profile',), zlib=True) + longitude.long_name = 'aircraft GPS longitude' + longitude.units = 'degrees_east' + longitude.standard_name = 'longitude' + + altitude = f.createVariable('altitude', datatype='f4', dimensions=('profile',), zlib=True) + altitude.long_name = 'aircraft GPS altitude' + altitude.units = 'm' + altitude.standard_name = 'altitude' + + time = f.createVariable('time', datatype='f8', dimensions=('profile',), + zlib=True) + time.long_name = 'starting time of record' + time.units = "seconds since 1970-01-01 00:00:00" + time.standard_name = "time" + + bin_range = f.createVariable('range', datatype='f4', dimensions=('range',), + zlib=True) # Use name 'bin_range' to avoid conflict with built-in range + bin_range.long_name = 'range from instrument' + bin_range.units = "m" + bin_range.positive = "down" + bin_range.axis = 'Z' # This is strictly not correct, as it does not correspond to geophysical variable. Still it can be usefull for quick plotting. + + pitch = f.createVariable('pitch', datatype='f4', dimensions=('profile',), zlib=True) + pitch.long_name = 'aircraft pitch' + pitch.units = 'degree' + pitch.standard_name = 'platform_pitch_angle' + pitch.coordinates = "time longitude latitude" + + roll = f.createVariable('roll', datatype='f4', dimensions=('profile',), zlib=True) + roll.long_name = 'aircraft roll' + roll.units = 'degree' + roll.standard_name = 'platform_roll_angle' + roll.coordinates = "time longitude latitude" + + heading = f.createVariable('heading', datatype='f4', dimensions=('profile',), zlib=True) + heading.long_name = 'aircraft heading' + heading.units = 'degree' + heading.standard_name = 'platform_course' + heading.coordinates = "time longitude latitude" + + hv = f.createVariable('hv', datatype='f4', dimensions=('profile',), zlib=True) + hv.long_name = 'detector high voltage' + hv.units = 'V' + hv.coordinates = "time longitude latitude" + + averaging_time = f.createVariable('averaging_time', datatype='f4', dimensions=('profile',), zlib=True) + averaging_time.long_name = 'averaging time' + averaging_time.units = 's' + averaging_time.coordinates = "time longitude latitude" + + emission_wl = f.createVariable('emission_wavelength', datatype='f8', dimensions=('profile',), + zlib=True) + emission_wl.long_name = 'emission wavelength in vacuum' + emission_wl.units = 'nm' + emission_wl.standard_name = 'radiation_wavelength' + emission_wl.coordinates = "time longitude latitude" + + pulses = f.createVariable('pulses', datatype='i4', dimensions=('profile',), + zlib=True) + pulses.long_name = 'laser pulses per record' + pulses.coordinates = "time longitude latitude" + + signal = f.createVariable('signal', datatype='i4', dimensions=('profile', 'range'), + zlib=True) + signal.long_name = 'signal' + signal.units = "counts" + signal.missing_value = -1 + signal.coordinates = "time longitude latitude range" + + # Assign channel attributes + f.channel_name = 'L_532nmP' + + # Assign variables + id[:] = 1 + latitude[:] = self.latitude[:] + longitude[:] = self.longitude[:] + altitude[:] = self.gps_altitude[:] + pitch[:] = self.pitch[:] + roll[:] = self.roll[:] + heading[:] = self.heading_angle[:] + hv[:] = self.high_voltage[:] + averaging_time[:] = self.averaging_time[:] + emission_wl[:] = self.emission_wavelength_vacuum[:] + pulses[:] = self.pulses[:] + time[:] = self.time[:] + bin_range[:] = self.range[:] + signal[:] = self.signal[:] +