Thu, 02 Mar 2017 10:20:58 +0200
Use id.
binietoglou@38 | 1 | import datetime |
binietoglou@38 | 2 | |
binietoglou@38 | 3 | import numpy as np |
binietoglou@38 | 4 | |
binietoglou@38 | 5 | from licel import LicelLidarMeasurement |
binietoglou@38 | 6 | |
binietoglou@38 | 7 | |
binietoglou@38 | 8 | class LicelCalibrationMeasurement(LicelLidarMeasurement): |
binietoglou@38 | 9 | |
ulalume3@69 | 10 | def __init__(self, plus45_files=None, minus45_files=None, use_id_as_name=False): |
binietoglou@38 | 11 | # Setup the empty class |
ulalume3@69 | 12 | super(LicelCalibrationMeasurement, self).__init__(use_id_as_name=use_id_as_name) |
binietoglou@38 | 13 | |
binietoglou@38 | 14 | self.plus45_files = plus45_files |
binietoglou@38 | 15 | self.minus45_files = minus45_files |
binietoglou@38 | 16 | |
ulalume3@69 | 17 | if plus45_files and minus45_files: |
binietoglou@38 | 18 | self.check_equal_length() |
binietoglou@38 | 19 | |
binietoglou@38 | 20 | self.read_channel_data() |
binietoglou@38 | 21 | self.update() |
binietoglou@38 | 22 | |
binietoglou@38 | 23 | def update(self): |
binietoglou@38 | 24 | """ Correct timescales after each update. |
binietoglou@38 | 25 | """ |
binietoglou@38 | 26 | super(LicelCalibrationMeasurement, self).update() |
binietoglou@38 | 27 | self.correct_timescales() |
binietoglou@38 | 28 | |
binietoglou@38 | 29 | def check_equal_length(self): |
binietoglou@38 | 30 | """ |
binietoglou@38 | 31 | Check if input time series have equal lengths. |
binietoglou@38 | 32 | """ |
binietoglou@38 | 33 | len_plus = len(self.plus45_files) |
binietoglou@38 | 34 | len_minus = len(self.minus45_files) |
binietoglou@38 | 35 | if len_plus != len_minus: |
binietoglou@38 | 36 | raise self.UnequalMeasurementLengthError( |
binietoglou@38 | 37 | "Input timeseries have different length: %s vs %s." % (len_plus, len_minus)) |
binietoglou@38 | 38 | |
binietoglou@38 | 39 | def read_channel_data(self): |
binietoglou@38 | 40 | |
binietoglou@38 | 41 | # Read plus and minus 45 measurements |
binietoglou@38 | 42 | self.plus45_measurement = LicelLidarMeasurement(self.plus45_files) |
binietoglou@38 | 43 | self.plus45_measurement.rename_channel(suffix='_p45') |
binietoglou@38 | 44 | |
binietoglou@38 | 45 | self.minus45_measurement = LicelLidarMeasurement(self.minus45_files) |
binietoglou@38 | 46 | self.minus45_measurement.rename_channel(suffix='_m45') |
binietoglou@38 | 47 | |
binietoglou@38 | 48 | # Combine them in this object |
binietoglou@38 | 49 | self.channels = {} |
binietoglou@38 | 50 | self.channels.update(self.plus45_measurement.channels) |
binietoglou@38 | 51 | self.channels.update(self.minus45_measurement.channels) |
binietoglou@38 | 52 | |
binietoglou@38 | 53 | def correct_timescales(self): |
binietoglou@38 | 54 | self.check_timescales_are_two() |
binietoglou@38 | 55 | self.combine_scales() |
binietoglou@38 | 56 | |
binietoglou@38 | 57 | def check_timescales_are_two(self): |
binietoglou@38 | 58 | no_timescales = len(self.variables['Raw_Data_Start_Time']) |
binietoglou@38 | 59 | if no_timescales != 2: |
binietoglou@38 | 60 | raise self.WrongNumberOfTimescalesError("Wrong number of timescales: %s instead of 2." % no_timescales) |
binietoglou@38 | 61 | |
binietoglou@38 | 62 | def combine_scales(self): |
binietoglou@38 | 63 | start_times, end_times = self.get_ordered_timescales() |
binietoglou@38 | 64 | new_start_time = start_times[0] |
binietoglou@38 | 65 | new_stop_time = end_times[1] |
binietoglou@38 | 66 | self.variables['Raw_Data_Start_Time'] = [new_start_time, ] |
binietoglou@38 | 67 | self.variables['Raw_Data_Stop_Time'] = [new_stop_time, ] |
binietoglou@38 | 68 | self.reset_timescale_id() |
binietoglou@38 | 69 | |
binietoglou@38 | 70 | def reset_timescale_id(self): |
binietoglou@38 | 71 | """ |
binietoglou@38 | 72 | Set all timescales to 0 |
binietoglou@38 | 73 | :return: |
binietoglou@38 | 74 | """ |
binietoglou@38 | 75 | timescale_dict = self.variables['id_timescale'] |
binietoglou@38 | 76 | self.variables['id_timescale'] = dict.fromkeys(timescale_dict, 0) |
binietoglou@38 | 77 | |
binietoglou@38 | 78 | def get_ordered_timescales(self): |
binietoglou@38 | 79 | scale_start_1, scale_start_2 = self.variables['Raw_Data_Start_Time'] |
binietoglou@38 | 80 | scale_end_1, scale_end_2 = self.variables['Raw_Data_Stop_Time'] |
binietoglou@38 | 81 | |
binietoglou@38 | 82 | if scale_start_1[0] > scale_start_2[0]: |
binietoglou@38 | 83 | scale_start_1, scale_start_2 = scale_start_2, scale_start_1 |
binietoglou@38 | 84 | |
binietoglou@38 | 85 | if scale_end_1[0] > scale_end_2[0]: |
binietoglou@38 | 86 | scale_end_1, scale_end_2 = scale_end_2, scale_end_1 |
binietoglou@38 | 87 | |
binietoglou@38 | 88 | return (scale_start_1, scale_start_2), (scale_end_1, scale_end_2) |
binietoglou@38 | 89 | |
binietoglou@38 | 90 | def add_fake_measurements(self, no_profiles, variation=0.1): |
binietoglou@38 | 91 | """ |
binietoglou@38 | 92 | Add a number of fake measurements. This is done to allow testing with single analog profiles. |
binietoglou@38 | 93 | |
binietoglou@38 | 94 | Adds a predefined variation in each new profile. |
binietoglou@38 | 95 | """ |
binietoglou@38 | 96 | duration = self.info['duration'] |
binietoglou@38 | 97 | for channel_name, channel in self.channels.items(): |
binietoglou@38 | 98 | base_time = channel.data.keys()[0] |
binietoglou@38 | 99 | base_data = channel.data[base_time] |
binietoglou@38 | 100 | |
binietoglou@38 | 101 | for n in range(no_profiles): |
binietoglou@38 | 102 | random_variation = base_data * (np.random.rand(len(base_data)) * 2 - 1) * variation |
binietoglou@38 | 103 | |
binietoglou@38 | 104 | new_time = base_time + n * duration |
binietoglou@38 | 105 | new_data = channel.data[base_time].copy() + random_variation |
binietoglou@38 | 106 | if 'ph' in channel_name: |
binietoglou@38 | 107 | new_data = new_data.astype('int') |
binietoglou@38 | 108 | channel.data[new_time] = new_data |
binietoglou@38 | 109 | |
binietoglou@38 | 110 | self.update() |
binietoglou@38 | 111 | |
binietoglou@38 | 112 | def subset_photoncounting(self): |
binietoglou@38 | 113 | """ |
binietoglou@38 | 114 | Subset photoncounting channels. |
binietoglou@38 | 115 | """ |
binietoglou@38 | 116 | ph_channels = [channel for channel in self.channels.keys() if 'ph' in channel] |
binietoglou@38 | 117 | new_measurement = self.subset_by_channels(ph_channels) |
binietoglou@38 | 118 | return new_measurement |
binietoglou@38 | 119 | |
binietoglou@43 | 120 | def _get_scc_channel_variables(self): |
binietoglou@43 | 121 | """ |
binietoglou@43 | 122 | Get a list of variables to put in the SCC. |
binietoglou@43 | 123 | |
binietoglou@43 | 124 | It can be overridden e.g. in the depolarization product class. |
binietoglou@43 | 125 | |
binietoglou@43 | 126 | Returns |
binietoglou@43 | 127 | ------- |
binietoglou@43 | 128 | |
binietoglou@43 | 129 | channel_variables: dict |
binietoglou@43 | 130 | A dictionary with channel variable specifications. |
binietoglou@43 | 131 | """ |
binietoglou@43 | 132 | channel_variables = \ |
binietoglou@43 | 133 | {'channel_ID': (('channels',), 'i'), |
binietoglou@43 | 134 | 'Background_Low': (('channels',), 'd'), |
binietoglou@43 | 135 | 'Background_High': (('channels',), 'd'), |
binietoglou@43 | 136 | 'LR_Input': (('channels',), 'i'), |
binietoglou@43 | 137 | 'DAQ_Range': (('channels',), 'd'), |
binietoglou@43 | 138 | 'Pol_Calib_Range_Min': (('channels',), 'd'), |
binietoglou@43 | 139 | 'Pol_Calib_Range_Max': (('channels',), 'd'), |
binietoglou@43 | 140 | } |
binietoglou@43 | 141 | return channel_variables |
binietoglou@43 | 142 | |
binietoglou@38 | 143 | class UnequalMeasurementLengthError(RuntimeError): |
binietoglou@38 | 144 | """ Raised when the plus and minus files have different length. |
binietoglou@38 | 145 | """ |
binietoglou@38 | 146 | pass |
binietoglou@38 | 147 | |
binietoglou@38 | 148 | class WrongNumberOfTimescalesError(RuntimeError): |
binietoglou@38 | 149 | """ Raised when timescales are not two. |
binietoglou@38 | 150 | """ |
binietoglou@38 | 151 | pass |