atmospheric_lidar/diva.py

changeset 166
ab21bd91bc09
parent 165
392a714d12a2
child 167
0604a628fb1e
equal deleted inserted replaced
165:392a714d12a2 166:ab21bd91bc09
182 polarizer_angle = g.createVariable('polarizer_angle', datatype='f4', dimensions=('profile', ), zlib=True) 182 polarizer_angle = g.createVariable('polarizer_angle', datatype='f4', dimensions=('profile', ), zlib=True)
183 polarizer_angle.long_name = 'polarizer angle in respect to laser plane of polarization' 183 polarizer_angle.long_name = 'polarizer angle in respect to laser plane of polarization'
184 polarizer_angle.units = 'degree' 184 polarizer_angle.units = 'degree'
185 polarizer_angle.comments = 'Optional' 185 polarizer_angle.comments = 'Optional'
186 186
187 if not channel.is_analog: 187 if channel.is_photon_counting:
188 dead_time_model = g.createVariable('dead_time_model', datatype='b') 188 dead_time_model = g.createVariable('dead_time_model', datatype='b')
189 dead_time_model.long_name = 'optimal dead time model of detection system' 189 dead_time_model.long_name = 'optimal dead time model of detection system'
190 dead_time_model.flag_values = '0b 1b 2b' 190 dead_time_model.flag_values = '0b 1b 2b'
191 dead_time_model.flag_meanings = 'paralyzable non_paralyzable other' 191 dead_time_model.flag_meanings = 'paralyzable non_paralyzable other'
192 192
422 422
423 Unlike other classes in this module, it does not inherit from BasicLidarMeasurement. This is done 423 Unlike other classes in this module, it does not inherit from BasicLidarMeasurement. This is done
424 to avoid all the burden of backward compatibility. In the future this could be hosted also as a separte moduel. 424 to avoid all the burden of backward compatibility. In the future this could be hosted also as a separte moduel.
425 """ 425 """
426 426
427 def __init__(self, file_path, import_now=True): 427 def __init__(self, file_path, header_only=False):
428 """ 428 """
429 This is run when creating a new object. 429 This is run when creating a new object.
430 430
431 Parameters 431 Parameters
432 ---------- 432 ----------
433 file_path : str 433 file_path : str
434 Paths to the input netCDF file. 434 Paths to the input netCDF file.
435 import_now : bool 435 header_only : bool
436 If True, the file is imported when the object is created. 436 If True, channel info are not loaded.
437 """ 437 """
438 self.file_path = file_path 438 self.file_path = file_path
439 self.file_name = os.path.basename(file_path) 439 self.file_name = os.path.basename(file_path)
440 440
441 if import_now: 441 self.import_file(file_path, header_only)
442 self.import_file(file_path) 442
443 443 def import_file(self, header_only):
444 def import_file(self):
445 """ Import data from a single DIVA file. 444 """ Import data from a single DIVA file.
446 """ 445 """
447 446
448 logger.debug('Importing file {0}'.format(self.file_name)) 447 logger.debug('Importing file {0}'.format(self.file_name))
449 448
475 474
476 self.meteo_time = ancillary.variables['time'][:] 475 self.meteo_time = ancillary.variables['time'][:]
477 self.air_temperature_kelvin = ancillary.variable['air_temperature'][:] 476 self.air_temperature_kelvin = ancillary.variable['air_temperature'][:]
478 self.air_pressure_hpa = ancillary.variable['air_pressure'][:] 477 self.air_pressure_hpa = ancillary.variable['air_pressure'][:]
479 478
479 self.available_channels = []
480 for group_name, group in input_file.groups.items(): 480 for group_name, group in input_file.groups.items():
481 channel_name = group_name[8:] # Remove 'channel_' prefix 481 channel_name = group_name[8:] # Remove 'channel_' prefix
482 self.channels[channel_name] = DivaChannel(channel_name, group) 482 self.available_channels.append(channel_name)
483 483
484 if not header_only:
485 self.channels[channel_name] = DivaChannel(channel_name, group)
486
487 def import_channel(self, channel_name):
488 """ Import a specific channel. """
489 if channel_name not in self.available_channels:
490 raise ValueError('Channel {0} not available. Should be one of {1}'.format(channel_name, self.available_channels))
491
492 group_name = 'channel_{0}'.format(channel_name)
493
494 with netcdf.Dataset(self.file_path) as input_file:
495 group = input_file.groups[group_name]
496 self.channels[channel_name] = DivaChannel(channel_name, group)
484 497
485 498
486 class DivaChannel(object): 499 class DivaChannel(object):
487 500
488 def __init__(self, channel_name, group): 501 def __init__(self, channel_name, group):
493 channel_name : str 506 channel_name : str
494 Name of the group 507 Name of the group
495 group : netCDF4.Group object 508 group : netCDF4.Group object
496 An open netcdf group to initialize. 509 An open netcdf group to initialize.
497 """ 510 """
498 self.group_name = channel_name 511 self.channel_name = channel_name
499 512
500 self. 513 self.long_name = group.long_name
501 514 self.detector_manufacturer = group.detector_manufacturer
515 self.detector_model = group.detector_model
516 self.daq_manufacturer = group.daq_manufacturer
517 self.daq_model = group.daq_model
518
519 self.number_of_profiles = len(group.dimensions['profile'])
520 self.number_of_bins = len(group.dimensions['range'])
521 self.channel_id = group.variables['channel_id'][:]
522 self.laser_repetition_rate = group.variables['laser_repetition_rate'][:]
523
524 self.emission_energy_mJ = group.variables['emission_energy'][:]
525 self.emission_polarization_flag = group.variables['emission_polarization'][:]
526 self.emission_polarization = self._flag_to_polarization(self.emission_polarization_flag)
527 self.field_of_view = group.variables['fov'][:]
528 self.field_of_view_comment = group.variables['fov'].comment
529
530 self.detector_type_flag = group.variables['detector_type'][:]
531 self.detector_type = self._flag_to_detector_type(self.detector_type_flag)
532
533 self.detection_mode_flag = group.variables['detection_mode'][:]
534 self.detection_mode = self._flag_to_detector_type(self.detection_mode_flag)
535
536 self.detection_wavelength_nm = group.variables['detection_wavelength'][:]
537 self.detection_fwhm = group.variables['detection_fwhm'][:]
538
539 self.detection_polarization_flag = group.variables['detection_polarization']
540 self.detection_polariation = self._flag_to_detection_polarization(self.detection_polarization_flag)
541
542 self.polarizer_angle_degrees = group.variables['polarizer_angle'][:]
543
544 if self.is_photon_counting:
545 self.dead_time_model_flag = group.variables['dead_time_model'][:]
546 self.dead_time_model = self._flag_to_dead_time_model(self.dead_time_model_flag)
547
548 self.dead_time = group.variables['dead_time'][:]
549 self.dead_time_source = group.variables['dead_time'].comment
550 self.discriminator = group.variables['discriminator'][:]
551
552 if self.is_analog:
553 self.adc_bits = group.variables['adc_bits'][:]
554 self.adc_range = group.variables['adc_range'][:]
555
556 self.bin_length_ns = group.variables['bin_length'][:]
557 self.detector_voltage = group.variables['detector_voltage'][:]
558 self.pulses = group.variables['pulses'][:]
559 self.nd_filter_od = group.variables['nd_filter_od'][:]
560 self.trigger_delay_ns = group.variables['trigger_delay'][:]
561 self.time_since_epoch = group.variables['time'][:]
562
563 self.time = [datetime.datetime.utcfromtimestamp(t) for t in self.time_since_epoch]
564 self.bin_time_ns = group.variables['bin_time'][:]
565
566 self.signal = group.variables['signal'][:]
567 self.signal_units = group.variables['signal'].units
568
569 signal_stddev_var = group.variables.pop('signal_stddev', None)
570
571 if signal_stddev_var:
572 self.signal_stddev = signal_stddev_var[:]
573 else:
574 self.signal_stddev = None
575
576 def _flag_to_polarization(self, flag):
577 """ Convert polarization flag to str"""
578 if flag not in [0, 1, 2]:
579 logger.warning('Polarization flag has unrecognized value: {0}'.format(flag))
580 return ""
581
582 values = {0: 'linear',
583 1: 'circular',
584 2: 'None'}
585
586 return values[flag]
587
588 def _flag_to_detector_type(self, flag):
589 """ Convert detector type flag to str"""
590 if flag not in [0, 1]:
591 logger.warning('Detector type flag has unrecognized value: {0}'.format(flag))
592 return ""
593
594 values = {0: 'PMT',
595 1: 'APD',}
596
597 return values[flag]
598
599 def _flag_to_detection_mode(self, flag):
600 """ Convert detector type flag to str"""
601 if flag not in [0, 1]:
602 logger.warning('Detection mode flag has unrecognized value: {0}'.format(flag))
603 return ""
604
605 values = {0: 'analog',
606 1: 'photon counting'}
607
608 return values[flag]
609
610 def _flag_to_detection_polarization(self, flag):
611 """ Convert detector type flag to str"""
612 if flag not in [0, 1, 2]:
613 logger.warning('Detection polarization flag has unrecognized value: {0}'.format(flag))
614 return ""
615
616 values = {0: 'linear',
617 1: 'circular',
618 2: 'total',}
619
620 return values[flag]
621
622 def _flag_to_dead_time_model(self, flag):
623 """ Convert detector type flag to str"""
624 if flag not in [0, 1, 2]:
625 logger.warning('Dead time model flag has unrecognized value: {0}'.format(flag))
626 return ""
627
628 values = {0: 'paralyzable',
629 1: 'non_paralyzable',
630 2: 'other', }
631
632 return values[flag]
633
634 @property
635 def is_analog(self):
636 return self.detection_mode_flag==0
637
638 @property
639 def is_photon_counting(self):
640 return self.detection_mode_flag==1

mercurial