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