| 147 |
148 |
| 148 m = self.__class__() # Create an object of the same type as this one. |
149 m = self.__class__() # Create an object of the same type as this one. |
| 149 for (channel_name, channel) in self.channels.items(): |
150 for (channel_name, channel) in self.channels.items(): |
| 150 m.channels[channel_name] = channel.subset_by_time(start_time, stop_time) |
151 m.channels[channel_name] = channel.subset_by_time(start_time, stop_time) |
| 151 m.update() |
152 m.update() |
| |
153 return m |
| |
154 |
| |
155 def subset_by_bins(self, b_min = 0, b_max = None): |
| |
156 """Remove some height bins from the file. This could be needed to |
| |
157 remove aquisition artifacts at the start or the end of the files. |
| |
158 """ |
| |
159 |
| |
160 m = self.__class__() # Create an object of the same type as this one. |
| |
161 |
| |
162 for (channel_name, channel) in self.channels.items(): |
| |
163 m.channels[channel_name] = channel.subset_by_bins(b_min, b_max) |
| |
164 |
| |
165 m.update() |
| |
166 |
| 152 return m |
167 return m |
| 153 |
168 |
| 154 def r_plot(self): |
169 def r_plot(self): |
| 155 #Make a basic plot of the data. |
170 #Make a basic plot of the data. |
| 156 #Should include some dictionary with params to make plot stable. |
171 #Should include some dictionary with params to make plot stable. |
| 388 dt = stop_time - start_time |
403 dt = stop_time - start_time |
| 389 t_mean = start_time + dt / 2 |
404 t_mean = start_time + dt / 2 |
| 390 return t_mean |
405 return t_mean |
| 391 |
406 |
| 392 |
407 |
| 393 class Lidar_channel: |
408 class LidarChannel: |
| 394 |
409 |
| 395 def __init__(self,channel_parameters): |
410 def __init__(self, channel_parameters): |
| 396 c = 299792458 #Speed of light |
411 c = 299792458 # Speed of light |
| 397 self.wavelength = channel_parameters['name'] |
412 self.wavelength = channel_parameters['name'] |
| 398 self.name = str(self.wavelength) |
413 self.name = str(self.wavelength) |
| 399 self.binwidth = float(channel_parameters['binwidth']) # in microseconds |
414 self.binwidth = float(channel_parameters['binwidth']) # in microseconds |
| 400 self.data = {} |
415 self.data = {} |
| 401 self.resolution = self.binwidth * c / 2 |
416 self.resolution = self.binwidth * c / 2 |
| 402 self.z = np.arange(len(channel_parameters['data'])) * self.resolution + self.resolution/2.0 # Change: add half bin in the z |
417 self.z = np.arange(len(channel_parameters['data'])) * self.resolution + self.resolution/2.0 # Change: add half bin in the z |
| 403 self.points = len(channel_parameters['data']) |
418 self.points = len(channel_parameters['data']) |
| 404 self.rc = [] |
419 self.rc = [] |
| 405 self.duration = 60 |
420 self.duration = 60 |
| 406 |
421 |
| 407 def calculate_rc(self): |
422 def calculate_rc(self): |
| 408 background = np.mean(self.matrix[:,4000:], axis = 1) #Calculate the background from 30000m and above |
423 background = np.mean(self.matrix[:,4000:], axis = 1) # Calculate the background from 30000m and above |
| 409 self.rc = (self.matrix.transpose()- background).transpose() * (self.z **2) |
424 self.rc = (self.matrix.transpose()- background).transpose() * (self.z **2) |
| 410 |
425 |
| 411 |
426 |
| 412 def update(self): |
427 def update(self): |
| 413 self.start_time = min(self.data.keys()) |
428 self.start_time = min(self.data.keys()) |
| 440 |
455 |
| 441 subset_time = time_array[condition] |
456 subset_time = time_array[condition] |
| 442 subset_data = dict([(c_time, self.data[c_time]) for c_time in subset_time]) |
457 subset_data = dict([(c_time, self.data[c_time]) for c_time in subset_time]) |
| 443 |
458 |
| 444 #Create a list with the values needed by channel's __init__() |
459 #Create a list with the values needed by channel's __init__() |
| 445 parameters_values = {'name': self.wavelength, |
460 parameter_values = {'name': self.wavelength, |
| 446 'binwidth': self.binwidth, |
461 'binwidth': self.binwidth, |
| 447 'data': subset_data[subset_time[0]],} |
462 'data': subset_data[subset_time[0]],} |
| 448 |
463 |
| 449 c = Lidar_channel(parameters_values) |
464 # We should use __class__ instead of class name, so that this works properly |
| |
465 # with subclasses |
| |
466 # Ex: c = self.__class__(parameters_values) |
| |
467 # This does not currently work with Licel files though |
| |
468 c = LidarChannel(parameter_values) |
| 450 c.data = subset_data |
469 c.data = subset_data |
| 451 c.update() |
470 c.update() |
| 452 return c |
471 return c |
| 453 |
472 |
| 454 |
473 def subset_by_bins(self, b_min = 0, b_max = None): |
| |
474 """Remove some height bins from the file. This could be needed to |
| |
475 remove aquisition artifacts at the start or the end of the files. |
| |
476 """ |
| |
477 |
| |
478 subset_data = {} |
| |
479 |
| |
480 for ctime, cdata in self.data.items(): |
| |
481 subset_data[ctime] = cdata[b_min:b_max] |
| |
482 |
| |
483 #Create a list with the values needed by channel's __init__() |
| |
484 parameters_values = {'name': self.wavelength, |
| |
485 'binwidth': self.binwidth, |
| |
486 'data': subset_data[subset_data.keys()[0]],} # We just need any array with the correct length |
| |
487 |
| |
488 c = LidarChannel(parameters_values) |
| |
489 c.data = subset_data |
| |
490 c.update() |
| |
491 return c |
| |
492 |
| 455 def profile(self,dtime, signal_type = 'rc'): |
493 def profile(self,dtime, signal_type = 'rc'): |
| 456 t, idx = self._nearest_dt(dtime) |
494 t, idx = self._nearest_dt(dtime) |
| 457 if signal_type == 'rc': |
495 if signal_type == 'rc': |
| 458 data = self.rc |
496 data = self.rc |
| 459 else: |
497 else: |
| 510 else: |
548 else: |
| 511 if show_plot: |
549 if show_plot: |
| 512 plt.show() |
550 plt.show() |
| 513 #plt.close() ??? |
551 #plt.close() ??? |
| 514 |
552 |
| 515 def draw_plot(self,ax1, cmap = plt.cm.jet, signal_type = 'rc', zoom = [0,12000,0,-1], z0 = None, cmap_label = 'a.u.', cb_format = None, vmin = 0, vmax = 1.3 * 10 ** 7): |
553 def draw_plot(self,ax1, cmap = plt.cm.jet, signal_type = 'rc', |
| |
554 zoom = [0,12000,0,-1], z0 = None, |
| |
555 add_colorbar = True, cmap_label = 'a.u.', cb_format = None, |
| |
556 vmin = 0, vmax = 1.3 * 10 ** 7): |
| 516 |
557 |
| 517 if signal_type == 'rc': |
558 if signal_type == 'rc': |
| 518 if len(self.rc) == 0: |
559 if len(self.rc) == 0: |
| 519 self.calculate_rc() |
560 self.calculate_rc() |
| 520 data = self.rc |
561 data = self.rc |
| 559 #vmin = data[:,10:400].max() * 0.1, |
600 #vmin = data[:,10:400].max() * 0.1, |
| 560 vmax = vmax, |
601 vmax = vmax, |
| 561 #vmax = data[:,10:400].max() * 0.9, |
602 #vmax = data[:,10:400].max() * 0.9, |
| 562 extent = [ts1,ts2,self.z[zoom[0]]/1000.0 + z0/1000., self.z[hmax_idx]/1000.0 + z0/1000.], |
603 extent = [ts1,ts2,self.z[zoom[0]]/1000.0 + z0/1000., self.z[hmax_idx]/1000.0 + z0/1000.], |
| 563 ) |
604 ) |
| 564 |
605 |
| 565 if cb_format: |
606 if add_colorbar: |
| 566 cb1 = plt.colorbar(im1, format = cb_format) |
607 if cb_format: |
| 567 else: |
608 cb1 = plt.colorbar(im1, format = cb_format) |
| 568 cb1 = plt.colorbar(im1) |
609 else: |
| 569 cb1.ax.set_ylabel(cmap_label) |
610 cb1 = plt.colorbar(im1) |
| 570 |
611 cb1.ax.set_ylabel(cmap_label) |
| 571 # Make the ticks of the colorbar smaller, two points smaller than the default font size |
612 |
| 572 cb_font_size = mpl.rcParams['font.size'] - 2 |
613 # Make the ticks of the colorbar smaller, two points smaller than the default font size |
| 573 for ticklabels in cb1.ax.get_yticklabels(): |
614 cb_font_size = mpl.rcParams['font.size'] - 2 |
| 574 ticklabels.set_fontsize(cb_font_size) |
615 for ticklabels in cb1.ax.get_yticklabels(): |
| 575 cb1.ax.yaxis.get_offset_text().set_fontsize(cb_font_size) |
616 ticklabels.set_fontsize(cb_font_size) |
| 576 |
617 cb1.ax.yaxis.get_offset_text().set_fontsize(cb_font_size) |
| |
618 |
| |
619 |
| |
620 def draw_plot_new(self, ax1, cmap = plt.cm.jet, signal_type = 'rc', |
| |
621 zoom = [0, 12000, 0, None], z0 = None, |
| |
622 add_colorbar = True, cmap_label = 'a.u.', |
| |
623 cb_format = None, power_limits = (-2, 2), |
| |
624 date_labels = False, |
| |
625 vmin = 0, vmax = 1.3 * 10 ** 7): |
| |
626 |
| |
627 if signal_type == 'rc': |
| |
628 if len(self.rc) == 0: |
| |
629 self.calculate_rc() |
| |
630 data = self.rc |
| |
631 else: |
| |
632 data = self.matrix |
| |
633 |
| |
634 hmax_idx = self.index_at_height(zoom[1]) |
| |
635 hmin_idx = self.index_at_height(zoom[0]) |
| |
636 |
| |
637 # If z0 is given, then the plot is a.s.l. |
| |
638 if z0: |
| |
639 ax1.set_ylabel('Altitude a.s.l. [km]') |
| |
640 else: |
| |
641 ax1.set_ylabel('Altitude a.g.l. [km]') |
| |
642 z0 = 0 |
| |
643 |
| |
644 ax1.set_xlabel('Time UTC [hh:mm]') |
| |
645 #y axis in km, xaxis /2 to make 30s measurements in minutes. Only for 1064 |
| |
646 #dateFormatter = mpl.dates.DateFormatter('%H.%M') |
| |
647 #hourlocator = mpl.dates.HourLocator() |
| |
648 |
| |
649 |
| |
650 if date_labels: |
| |
651 dayFormatter = mpl.dates.DateFormatter('%H:%M\n%d/%m/%Y') |
| |
652 daylocator = mpl.dates.AutoDateLocator(minticks = 3, maxticks = 8, interval_multiples=True) |
| |
653 ax1.axes.xaxis.set_major_formatter(dayFormatter) |
| |
654 ax1.axes.xaxis.set_major_locator(daylocator) |
| |
655 else: |
| |
656 hourFormatter = mpl.dates.DateFormatter('%H:%M') |
| |
657 hourlocator = mpl.dates.AutoDateLocator(minticks = 3, maxticks = 8, interval_multiples=True) |
| |
658 ax1.axes.xaxis.set_major_formatter(hourFormatter) |
| |
659 ax1.axes.xaxis.set_major_locator(hourlocator) |
| |
660 |
| |
661 |
| |
662 # Get the values of the time axis |
| |
663 dt = datetime.timedelta(seconds = self.duration) |
| |
664 |
| |
665 time_cut = self.time[zoom[2]:zoom[3]] |
| |
666 time_last = time_cut[-1] + dt # The last element needed for pcolormesh |
| |
667 |
| |
668 time_all = time_cut + (time_last,) |
| |
669 |
| |
670 t_axis = mpl.dates.date2num(time_all) |
| |
671 |
| |
672 # Get the values of the z axis |
| |
673 z_cut = self.z[hmin_idx:hmax_idx] - self.resolution / 2. |
| |
674 z_last = z_cut[-1] + self.resolution |
| |
675 |
| |
676 z_axis = np.append(z_cut, z_last) / 1000. + z0 / 1000. # Convert to km |
| |
677 |
| |
678 # Plot |
| |
679 im1 = ax1.pcolormesh(t_axis, z_axis, data.T[hmin_idx:hmax_idx, zoom[2]:zoom[3]], |
| |
680 cmap = cmap, |
| |
681 vmin = vmin, |
| |
682 vmax = vmax, |
| |
683 ) |
| |
684 |
| |
685 if add_colorbar: |
| |
686 if cb_format: |
| |
687 cb1 = plt.colorbar(im1, format = cb_format) |
| |
688 else: |
| |
689 cb_formatter = ScalarFormatter() |
| |
690 cb_formatter.set_powerlimits(power_limits) |
| |
691 cb1 = plt.colorbar(im1, format = cb_formatter) |
| |
692 cb1.ax.set_ylabel(cmap_label) |
| |
693 |
| |
694 # Make the ticks of the colorbar smaller, two points smaller than the default font size |
| |
695 cb_font_size = mpl.rcParams['font.size'] - 2 |
| |
696 for ticklabels in cb1.ax.get_yticklabels(): |
| |
697 ticklabels.set_fontsize(cb_font_size) |
| |
698 cb1.ax.yaxis.get_offset_text().set_fontsize(cb_font_size) |
| |
699 |
| 577 def index_at_height(self, height): |
700 def index_at_height(self, height): |
| 578 idx = np.array(np.abs(self.z - height).argmin()) |
701 idx = np.array(np.abs(self.z - height).argmin()) |
| 579 if idx.size > 1: |
702 if idx.size > 1: |
| 580 idx =idx[0] |
703 idx =idx[0] |
| 581 return idx |
704 return idx |