| 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 |