def generate_vectors(earth_vel):
        """
        Generate the velocity vectors.  This will calculate the magnitude and direction
        of the water.  If any of the data is marked bad in a bin, then the magnitude and
        direction will also be marked bad.

        Call this again and set the self.Magnitude and self.Direction when Bottom Track Velocity is
        available.

        :param earth_vel: Earth Velocities[bin][beam]
        :return: [magnitude], [direction]  List with a value for each bin
        """
        mag = []
        dir = []

        for bin_num in range(len(earth_vel)):
            # Calculate the magnitude and direction
            mag.append(
                Ensemble.calculate_magnitude(earth_vel[bin_num][0],
                                             earth_vel[bin_num][1],
                                             earth_vel[bin_num][2]))
            dir.append(
                Ensemble.calculate_direction(earth_vel[bin_num][0],
                                             earth_vel[bin_num][1]))

        return mag, dir
Exemplo n.º 2
0
    def pd0_beam_vel_mm_per_sec(self, pd0_beam_num: int):
        """
        Convert the Beam Velocity from m/s to mm/s.

        Also remap the Beam numbers to match PD0 beams.
        RTB and PD0 do not share the same Beam Order
        RTB BEAM 0,1,2,3 = PD0 BEAM 3,2,0,1

        :param pd0_beam_num: PD0 Beam number.
        :type pd0_beam_num: Integer
        :return: Velocity for the given PD0 beam, converted to mm/s for the beam.  The beam will be based on reordering for PD0
        :rtype: Velocity data for given beam.
        """

        if pd0_beam_num == 0 and pd0_beam_num <= self.NumBeams:
            if Ensemble.is_bad_velocity(self.BeamVelocity[2]):
                return -32768
            return round(self.BeamVelocity[2] * 1000.0 * -1.0)            # Convert to mm/s  PD0 0 - RTB 2

        if pd0_beam_num == 1 and pd0_beam_num <= self.NumBeams:
            if Ensemble.is_bad_velocity(self.BeamVelocity[3]):
                return -32768
            return round(self.BeamVelocity[3] * 1000.0 * -1.0)            # Convert to mm/s  PD0 1 - RTB 3

        if pd0_beam_num == 2 and pd0_beam_num <= self.NumBeams:
            if Ensemble.is_bad_velocity(self.BeamVelocity[1]):
                return -32768
            return round(self.BeamVelocity[1] * 1000.0 * -1.0)            # Convert to mm/s  PD0 2 - RTB 1

        if pd0_beam_num == 3 and pd0_beam_num <= self.NumBeams:
            if Ensemble.is_bad_velocity(self.BeamVelocity[0]):
                return -32768
            return round(self.BeamVelocity[0] * 1000.0 * -1.0)            # Convert to mm/s  PD0 3 - RTB 0

        return None
Exemplo n.º 3
0
    def remove_ship_speed(self, ens):
        """
        Store the last good bottom track velocity data.  Then use it to remove the ship
        speed from the Earth velocities and velocity vectors.
        :param ens: Ensemble data
        """
        if ens.IsBottomTrack and ens.IsEarthVelocity and ens.BottomTrack.NumBeams >= 3:
            # Check bottom track velocity for good data
            # If the bt velocity is not bad, then store it for next time
            # if it is bad, then use the previous good value
            # East
            if not Ensemble.is_bad_velocity(ens.BottomTrack.EarthVelocity[0]):
                self.prev_bt_east = ens.BottomTrack.EarthVelocity[0]

            # North
            if not Ensemble.is_bad_velocity(ens.BottomTrack.EarthVelocity[1]):
                self.prev_bt_north = ens.BottomTrack.EarthVelocity[1]

            # Vertical
            if not Ensemble.is_bad_velocity(ens.BottomTrack.EarthVelocity[2]):
                self.prev_bt_vert = ens.BottomTrack.EarthVelocity[2]

            # Remove the ship speed
            ens.EarthVelocity.remove_vessel_speed(bt_east=self.prev_bt_east,
                                                  bt_north=self.prev_bt_north,
                                                  bt_vert=self.prev_bt_vert)
Exemplo n.º 4
0
    def remove_vessel_speed(self, bt_east=0.0, bt_north=0.0, bt_vert=0.0):
        """
        Remove the vessel speed.  If the bottom track data is good and
        the velocity is good, then remove the vessel speed from the earth speed.

        The bottom track velocity is the vessel velocity.  You can also use GPS data as a backup.

        Calculate the East and North component from the GPS speed
        bt_east = Convert.ToSingle(speed * Math.Sin(MathHelper.DegreeToRadian(heading)));
        bt_north = Convert.ToSingle(speed * Math.Cos(MathHelper.DegreeToRadian(heading)));

        :param bt_east: Bottom Track East velocity
        :param bt_north: Bottom Track North velocity
        :param bt_vert: Bottom Track Vertical velocity
        :return:
        """
        # Remove the vessel speed
        for bin_num in range(len(self.Velocities)):
            if not Ensemble.is_bad_velocity(self.Velocities[bin_num][0]):
                self.Velocities[bin_num][0] = self.Velocities[bin_num][
                    0] + bt_east  # Remove vessel speed
            if not Ensemble.is_bad_velocity(self.Velocities[bin_num][1]):
                self.Velocities[bin_num][1] = self.Velocities[bin_num][
                    1] + bt_north  # Remove vessel speed
            if not Ensemble.is_bad_velocity(self.Velocities[bin_num][2]):
                self.Velocities[bin_num][2] = self.Velocities[bin_num][
                    2] + bt_vert  # Remove vessel speed

        # Generate the new vectors after removing the vessel speed
        self.generate_velocity_vectors()
Exemplo n.º 5
0
    def pd0_range_cm(self, pd0_beam_num: int):
        """
        Convert the range from meters to centimeters.

        Remap the Beam numbers to match PD0 beams.
        RTB and PD0 do not share the same Beam Order
        RTB BEAM 0,1,2,3 = PD0 BEAM 3,2,0,1

        :param pd0_beam_num: PD0 Beam number.
        :type pd0_beam_num: Integer
        :return: Ranges for the given PD0 beam, converted to centimeters for the beam.  The beam will be based on reordering for PD0
        :rtype: Float - Range value.
        """

        if pd0_beam_num == 0 and pd0_beam_num <= self.NumBeams:
            if Ensemble.is_bad_velocity(self.Range[2]):
                return -32768
            return round(self.Range[2] * 100.0)            # PD0 0 - RTB 2

        if pd0_beam_num == 1 and pd0_beam_num <= self.NumBeams:
            if Ensemble.is_bad_velocity(self.Range[3]):
                return -32768
            return round(self.Range[3] * 100.0)           # PD0 1 - RTB 3

        if pd0_beam_num == 2 and pd0_beam_num <= self.NumBeams:
            if Ensemble.is_bad_velocity(self.Range[1]):
                return -32768
            return round(self.Range[1] * 100.0)           # PD0 2 - RTB 1

        if pd0_beam_num == 3 and pd0_beam_num <= self.NumBeams:
            if Ensemble.is_bad_velocity(self.Range[0]):
                return -32768
            return round(self.Range[0] * 100.0)            # PD0 3 - RTB 0

        return None
Exemplo n.º 6
0
    def encode_csv(self, dt, ss_code, ss_config, blank=0, bin_size=0):
        """
        Encode into CSV format.
        :param dt: Datetime object.
        :param ss_code: Subsystem code.
        :param ss_config: Subsystem Configuration
        :param blank: Blank or first bin position in meters.
        :param bin_size: Bin size in meters.
        :return: List of CSV lines.
        """
        str_result = []

        # Create the CSV strings
        str_result.append(Ensemble.gen_csv_line(dt, Ensemble.CSV_FIRST_PING_TIME, ss_code, ss_config, 0, 0, blank, bin_size, self.FirstPingTime))
        str_result.append(Ensemble.gen_csv_line(dt, Ensemble.CSV_LAST_PING_TIME, ss_code, ss_config, 0, 0, blank, bin_size, self.LastPingTime))
        str_result.append(Ensemble.gen_csv_line(dt, Ensemble.CSV_HEADING, ss_code, ss_config, 0, 0, blank, bin_size, self.Heading))
        str_result.append(Ensemble.gen_csv_line(dt, Ensemble.CSV_PITCH, ss_code, ss_config, 0, 0, blank, bin_size, self.Pitch))
        str_result.append(Ensemble.gen_csv_line(dt, Ensemble.CSV_ROLL, ss_code, ss_config, 0, 0, blank, bin_size, self.Roll))
        str_result.append(Ensemble.gen_csv_line(dt, Ensemble.CSV_WATER_TEMP, ss_code, ss_config, 0, 0, blank, bin_size, self.WaterTemp))
        str_result.append(Ensemble.gen_csv_line(dt, Ensemble.CSV_SYS_TEMP, ss_code, ss_config, 0, 0, blank, bin_size, self.SystemTemp))
        str_result.append(Ensemble.gen_csv_line(dt, Ensemble.CSV_PRESSURE, ss_code, ss_config, 0, 0, blank, bin_size, self.Pressure))
        str_result.append(Ensemble.gen_csv_line(dt, Ensemble.CSV_XDCR_DEPTH, ss_code, ss_config, 0, 0, blank, bin_size, self.TransducerDepth))
        str_result.append(Ensemble.gen_csv_line(dt, Ensemble.CSV_SOS, ss_code, ss_config, 0, 0, blank, bin_size, self.SpeedOfSound))

        return str_result
Exemplo n.º 7
0
def test_ones_compliment():
    value = 0x9E
    result = Ensemble.ones_complement(value)
    assert 0x61 == result

    value = 22
    result = Ensemble.oness_complement(value)
    assert 9 == result
Exemplo n.º 8
0
    def generate_csv_data_no_avg(self, ens):
        """
        Generate the CSV and Dataframe data from the ensemble.
        This is only called if the user is not averaging the data.
        :param ens: Ensemble data
        :return:
        """
        csv_rows = []

        dt = datetime.datetime.now()
        ss_code = 0
        ss_config = 0
        blank = 0.0
        bin_size = 0.0

        if ens.IsEnsembleData:
            dt = ens.EnsembleData.datetime()
            ss_code = ens.EnsembleData.SysFirmwareSubsystemCode
            ss_config = ens.EnsembleData.SubsystemConfig

        if ens.IsAncillaryData:
            blank = ens.AncillaryData.FirstBinRange
            bin_size = ens.AncillaryData.BinSize
            pressure = ens.AncillaryData.Pressure
            xdcr_depth = ens.AncillaryData.TransducerDepth

            # Pressure
            csv_rows.append([
                Ensemble.gen_csv_line(dt, Ensemble.CSV_PRESSURE, ss_code,
                                      ss_config, 0, 0, blank, bin_size,
                                      pressure)
            ])

            # Transducer Depth
            csv_rows.append([
                Ensemble.gen_csv_line(dt, Ensemble.CSV_XDCR_DEPTH, ss_code,
                                      ss_config, 0, 0, blank, bin_size,
                                      xdcr_depth)
            ])

        if ens.IsRangeTracking:
            for beam_num in range(len(ens.RangeTracking.Range)):
                # Range Tracking
                csv_rows.append([
                    Ensemble.gen_csv_line(dt, Ensemble.CSV_RT_RANGE, ss_code,
                                          ss_config, 0, beam_num, blank,
                                          bin_size,
                                          ens.RangeTracking.Range[beam_num])
                ])

        if ens.IsEarthVelocity:
            csv_rows += (ens.EarthVelocity.encode_csv(dt, ss_code, ss_config,
                                                      blank, bin_size))

        return csv_rows
Exemplo n.º 9
0
    def get_csv_data(self, data, awc_key, data_type, last_time):
        """
        Append the data to the CSV file.

        Ex:
        ["datetime", "data_type", "ss_code", "ss_config", "bin_num", "beam_num", "blank", "bin_size", "value"]
        2019/02/23 15:23:22.56, EARTH_VEL, 4, 1, 2, 2, 7.5, 1.245

        :param data: Data for all beams.
        :param awc_key: Key used to give the file an identifier for the subsystem and config.
        :param data_type: Data type to place into the csv line.
        :param last_time: Last time in the average.
        :return:
        """

        # Get the parameters for the data
        blank = self.awc_dict[awc_key].blank
        bin_size = self.awc_dict[awc_key].bin_size
        ss_code = self.awc_dict[awc_key].ss_code
        ss_config = self.awc_dict[awc_key].ss_config

        # Use the current time as a backup
        #dt_time = datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S:%f')
        dt_time = datetime.datetime.now()
        if last_time:
            dt_time = last_time

        # Get the data
        bin_num = 1
        beam_num = 0
        row_data = []

        # Go through each bin and add a line to the csv file
        for bin_data in data:
            if type(bin_data) == list:
                beam_num = 0
                for beam_data in bin_data:
                    val = beam_data
                    row_data.append([
                        (Ensemble.gen_csv_line(dt_time, data_type, ss_code,
                                               ss_config, bin_num, beam_num,
                                               blank, bin_size, val))
                    ])
                    beam_num += 1
            else:
                row_data.append([
                    (Ensemble.gen_csv_line(dt_time, data_type, ss_code,
                                           ss_config, bin_num, beam_num, blank,
                                           bin_size, bin_data))
                ])

            # Increment the bin number
            bin_num += 1

        return row_data
Exemplo n.º 10
0
    def accum_rt_range(self, awc):
        """
        Create the Range Tracking dataframe.  This takes all the information
        from the Range Tracking and creates a row in the dataframe for each bin,beam value.
        :param awc: Average data.
        :return:
        """
        # Convert the east array to df
        # params: vel_array, dt, ss_code, ss_config, blank, bin_size
        # DF Columns: Index, time_stamp, ss_code, ss_config, bin_num, beam_num, bin_depth, value
        df = Ensemble.array_beam_1d_to_df(
            awc[AverageWaterColumn.INDEX_RANGE_TRACK],
            awc[AverageWaterColumn.INDEX_LAST_TIME],
            awc[AverageWaterColumn.INDEX_SS_CODE],
            awc[AverageWaterColumn.INDEX_SS_CONFIG],
            awc[AverageWaterColumn.INDEX_FIRST_ENS_NUM],
            awc[AverageWaterColumn.INDEX_LAST_ENS_NUM])

        # Store the range results
        if self.df_rt_range.empty:
            self.df_rt_range = df
        else:
            self.df_rt_range = pd.concat([self.df_rt_range, df],
                                         ignore_index=True)

        # Average the Range Track Range
        avg_range = Ensemble.get_avg_range(
            awc[AverageWaterColumn.INDEX_RANGE_TRACK])

        # Create a dict entry
        dict_result = {}
        dict_result[0] = {
            'time_stamp': awc[AverageWaterColumn.INDEX_LAST_TIME],
            'ss_code': awc[AverageWaterColumn.INDEX_SS_CODE],
            'ss_config': awc[AverageWaterColumn.INDEX_SS_CONFIG],
            'bin_num': 0,
            'beam_num': 0,
            'bin_depth': avg_range,
            'first_ens_num': awc[AverageWaterColumn.INDEX_FIRST_ENS_NUM],
            'last_ens_num': awc[AverageWaterColumn.INDEX_LAST_ENS_NUM],
            'value': avg_range
        }

        # Create the dataframe from the dictionary
        # important to set the 'orient' parameter to "index" to make the keys as rows
        df = pd.DataFrame.from_dict(dict_result, "index")

        # Store the range results
        if self.df_avg_rt_range.empty:
            self.df_avg_rt_range = df
        else:
            self.df_avg_rt_range = pd.concat([self.df_avg_rt_range, df],
                                             ignore_index=True)
Exemplo n.º 11
0
    def decode(self, data):
        """
        Take the data bytearray.  Decode the data to populate
        the velocities.
        :param data: Bytearray for the dataset.
        """
        packet_pointer = Ensemble.GetBaseDataSize(self.name_len)

        for beam in range(self.element_multiplier):
            for bin_num in range(self.num_elements):
                self.Velocities[bin_num][beam] = Ensemble.GetFloat(packet_pointer, Ensemble().BytesInFloat, data)
                packet_pointer += Ensemble().BytesInFloat

        logging.debug(self.Velocities)
Exemplo n.º 12
0
    def decode(self, data):
        """
        Take the data bytearray.  Decode the data to populate
        the Good Beams.
        :param data: Bytearray for the dataset.
        """
        packet_pointer = Ensemble.GetBaseDataSize(self.name_len)

        for beam in range(self.element_multiplier):
            for bin_num in range(self.num_elements):
                self.GoodBeam[bin_num][beam] = Ensemble.GetInt32(packet_pointer, Ensemble().BytesInInt32, data)
                packet_pointer += Ensemble().BytesInInt32

        logging.debug(self.GoodBeam)
Exemplo n.º 13
0
    def calculate_magnitude(east, north, vertical):
        """
        Calculate the magnitude of the water current.
        :param east: Earth East Velocity
        :param north: Earth North Velocity
        :param vertical: Earth Vertical Velocity
        :return: Magnitude value
        """

        if not Ensemble.is_bad_velocity(east) and not Ensemble.is_bad_velocity(
                north) and not Ensemble.is_bad_velocity(vertical):
            return math.sqrt((east * east) + (north * north) +
                             (vertical * vertical))
        else:
            return Ensemble.BadVelocity
Exemplo n.º 14
0
    def accum_earth_vel(self, awc):
        """
        Create the Earth Velocity dataframe.  This takes all the information
        from the earth velocity and creates a row in the dataframe for each bin,beam value.
        :param awc: Average data.
        :return:
        """
        # Convert the east array to df
        # params: vel_array, dt, ss_code, ss_config, blank, bin_size
        # DF Columns: Index, time_stamp, ss_code, ss_config, bin_num, beam_num, bin_depth, value
        df = Ensemble.array_2d_to_df(
            awc[AverageWaterColumn.INDEX_EARTH],
            awc[AverageWaterColumn.INDEX_LAST_TIME],
            awc[AverageWaterColumn.INDEX_SS_CODE],
            awc[AverageWaterColumn.INDEX_SS_CONFIG],
            awc[AverageWaterColumn.INDEX_BLANK],
            awc[AverageWaterColumn.INDEX_BIN_SIZE],
            awc[AverageWaterColumn.INDEX_FIRST_ENS_NUM],
            awc[AverageWaterColumn.INDEX_LAST_ENS_NUM])

        # Store the results
        if self.df_earth.empty:
            self.df_earth = df
        else:
            self.df_earth = pd.concat([self.df_earth, df], ignore_index=True)
    def verify_ens_data(ens_data, ens_start=0):
        """
        This will check the checksum and verify it is correct.
        :param ens_data: Ensemble data.
        :param ens_start: Start location in the ens_data
        """
        try:
            # Ensemble Length
            ens_len = len(ens_data)

            # Verify at least the minimum number of bytes are available to verify the ensemble
            if ens_len <= Ensemble().HeaderSize + Ensemble().ChecksumSize:
                return False

            # Check Ensemble number
            ens_num = struct.unpack("I",
                                    ens_data[ens_start + 16:ens_start + 20])

            # Check ensemble size
            payload_size = struct.unpack(
                "I", ens_data[ens_start + 24:ens_start + 28])

            # Ensure the entire ensemble is in the buffer
            if ens_len >= ens_start + Ensemble(
            ).HeaderSize + payload_size[0] + Ensemble().ChecksumSize:

                # Check checksum
                checksum_loc = ens_start + Ensemble(
                ).HeaderSize + payload_size[0]
                checksum = struct.unpack(
                    "I", ens_data[checksum_loc:checksum_loc +
                                  Ensemble().ChecksumSize])

                # Calculate Checksum
                # Use only the payload for the checksum
                ens = ens_data[ens_start + Ensemble().HeaderSize:ens_start +
                               Ensemble().HeaderSize + payload_size[0]]
                calc_checksum = binascii.crc_hqx(ens, 0)

                # Verify checksum
                if checksum[0] == calc_checksum:
                    logging.debug(ens_num[0])
                    return True
                else:
                    logging.warning(
                        "Ensemble fails checksum. {:#04x} {:#04x}".format(
                            checksum[0], calc_checksum))
                    return False
            else:
                logging.warning("Incomplete ensemble.")
                return False

        except Exception as e:
            logging.error("Error verifying Ensemble.  " + str(e))
            return False

        return False
Exemplo n.º 16
0
    def avg_range(self):
        """
        Return the average range (depth to the bottom).  This will determine the good values
        for the range and average them together.

        :return: Average range.
        """
        # Average the range
        return Ensemble.get_avg_range(self.Range)
Exemplo n.º 17
0
    def encode_csv(self, dt, ss_code, ss_config, blank=0, bin_size=0):
        """
        Encode into CSV format.
        :param dt: Datetime object.
        :param ss_code: Subsystem code.
        :param ss_config: Subsystem Configuration
        :param blank: Blank or first bin position in meters.
        :param bin_size: Bin size in meters.
        :return: List of CSV lines.
        """
        str_result = []

        # Create the CSV strings
        for beams in range(len(self.Range)):
            str_result.append(
                Ensemble.gen_csv_line(dt, Ensemble.CSV_RT_RANGE, ss_code,
                                      ss_config, 0, beams, blank, bin_size,
                                      self.Range[beams]))

        for beams in range(len(self.Pings)):
            str_result.append(
                Ensemble.gen_csv_line(dt, Ensemble.CSV_RT_PINGS, ss_code,
                                      ss_config, 0, beams, blank, bin_size,
                                      self.Pings[beams]))

        for beams in range(len(self.BeamVelocity)):
            str_result.append(
                Ensemble.gen_csv_line(dt, Ensemble.CSV_RT_BEAM_VEL, ss_code,
                                      ss_config, 0, beams, blank, bin_size,
                                      self.BeamVelocity[beams]))

        for beams in range(len(self.InstrumentVelocity)):
            str_result.append(
                Ensemble.gen_csv_line(dt, Ensemble.CSV_RT_INSTR_VEL, ss_code,
                                      ss_config, 0, beams, blank, bin_size,
                                      self.InstrumentVelocity[beams]))

        for beams in range(len(self.EarthVelocity)):
            str_result.append(
                Ensemble.gen_csv_line(dt, Ensemble.CSV_RT_EARTH_VEL, ss_code,
                                      ss_config, 0, beams, blank, bin_size,
                                      self.EarthVelocity[beams]))

        return str_result
Exemplo n.º 18
0
    def encode(self):
        """
        Encode the data into RTB format.
        :return:
        """
        result = []

        # Generate header
        result += Ensemble.generate_header(self.ds_type, self.num_elements,
                                           self.element_multiplier, self.image,
                                           self.name_len, self.Name)

        # Add the data
        for beam in range(self.element_multiplier):
            for bin_num in range(self.num_elements):
                val = self.Correlation[bin_num][beam]
                result += Ensemble.float_to_bytes(val)

        return result
Exemplo n.º 19
0
    def encode(self):
        """
        Encode the data into RTB format.
        :return:
        """
        result = []

        self.num_elements = (8 * int(
            self.NumBeams)) + 1  # 8 is the number of list plus 1 for NumBeams

        # Generate header
        result += Ensemble.generate_header(self.ds_type, self.num_elements,
                                           self.element_multiplier, self.image,
                                           self.name_len, self.Name)

        # Add the data
        result += Ensemble.float_to_bytes(self.NumBeams)

        for beam in range(len(self.SNR)):
            result += Ensemble.float_to_bytes(self.SNR[beam])

        for beam in range(len(self.Range)):
            result += Ensemble.float_to_bytes(self.Range[beam])

        for beam in range(len(self.Pings)):
            result += Ensemble.float_to_bytes(self.Pings[beam])

        for beam in range(len(self.Amplitude)):
            result += Ensemble.float_to_bytes(self.Amplitude[beam])

        for beam in range(len(self.Correlation)):
            result += Ensemble.float_to_bytes(self.Correlation[beam])

        for beam in range(len(self.BeamVelocity)):
            result += Ensemble.float_to_bytes(self.BeamVelocity[beam])

        for beam in range(len(self.InstrumentVelocity)):
            result += Ensemble.float_to_bytes(self.InstrumentVelocity[beam])

        for beam in range(len(self.EarthVelocity)):
            result += Ensemble.float_to_bytes(self.EarthVelocity[beam])

        return result
Exemplo n.º 20
0
    def run(self):
        """
        Find the start of an ensemble.  Then find the end of the ensemble.
        Then remove the ensemble from the buffer and process the raw data.
        :return:
        """

        while self.thread_alive:
            # Lock to look for the data
            self.thread_lock.acquire()

            # Create a buffer to hold the ensemble
            bin_ens_list = []
            timeout = 0

            # Wait for the next ensemble added to the buffer
            self.thread_lock.wait()

            # Look for first 16 bytes of header
            ens_start = self.buffer.find(self.DELIMITER)

            # Verify enough data is in the buffer for an ensemble header
            while ens_start >= 0 and len(
                    self.buffer) > Ensemble().HeaderSize + ens_start:
                # Decode the Ensemble
                bin_ens = self.decode_ensemble(ens_start)

                # Add the data to the list or count for timeout
                if len(bin_ens) > 0:
                    # Add it to the list
                    bin_ens_list.append(bin_ens)
                else:
                    # Timeout if we are only getting bad data
                    timeout += 1
                    if timeout > 5:
                        logging.warning("Find good ensemble timeout")
                        break

                # Search if there is a new start location
                ens_start = self.buffer.find(self.DELIMITER)

            self.thread_lock.release()

            # If data was found for an ensemble
            # Process the ensemble binary data
            for ens in bin_ens_list:
                if len(ens) > 0:
                    # Decode data
                    ensemble = BinaryCodec.decode_data_sets(ens)

                    if ensemble:
                        # Publish the ensemble
                        self.process_ensemble(ensemble)
                else:
                    logging.debug("No Ensemble data found")
Exemplo n.º 21
0
def test_generate_header():

    value_type = 10             # Float
    num_elements = 30           # 30 bins
    element_multiplier = 4      # 4 Beams
    imag = 0                    # NOT USED
    name_length = 8             # Length of name
    name = "E000001\0"          # Beam Vel name

    header = Ensemble.generate_header(value_type,
                                      num_elements,
                                      element_multiplier,
                                      imag,
                                      name_length,
                                      name)

    # Value type
    assert 0xA == header[0]
    assert 0x0 == header[1]
    assert 0x0 == header[2]
    assert 0x0 == header[3]

    # Num Elements
    assert 0x1E == header[4]
    assert 0x0 == header[5]
    assert 0x0 == header[6]
    assert 0x0 == header[7]

    # Element Multiplier
    assert 0x4 == header[8]
    assert 0x0 == header[9]
    assert 0x0 == header[10]
    assert 0x0 == header[11]

    # Imag
    assert 0x0 == header[12]
    assert 0x0 == header[13]
    assert 0x0 == header[14]
    assert 0x0 == header[15]

    # Name Length
    assert 0x8 == header[16]
    assert 0x0 == header[17]
    assert 0x0 == header[18]
    assert 0x0 == header[19]

    # Name
    assert ord('E') == header[20]
    assert ord('0') == header[21]
    assert ord('0') == header[22]
    assert ord('0') == header[23]
    assert ord('0') == header[24]
    assert ord('0') == header[25]
    assert ord('1') == header[26]
    assert ord('\0') == header[27]
Exemplo n.º 22
0
def test_generate_header():

    value_type = 10             # Float
    num_elements = 25           # 25 elements
    element_multiplier = 1      # no multiplier
    imag = 0                    # NOT USED
    name_length = 8             # Length of name
    name = "E000014\0"          # System Setup name

    header = Ensemble.generate_header(value_type,
                                      num_elements,
                                      element_multiplier,
                                      imag,
                                      name_length,
                                      name)

    # Value type
    assert 0xA == header[0]
    assert 0x0 == header[1]
    assert 0x0 == header[2]
    assert 0x0 == header[3]

    # Num Elements
    assert 0x19 == header[4]
    assert 0x0 == header[5]
    assert 0x0 == header[6]
    assert 0x0 == header[7]

    # Element Multiplier
    assert 0x1 == header[8]
    assert 0x0 == header[9]
    assert 0x0 == header[10]
    assert 0x0 == header[11]

    # Imag
    assert 0x0 == header[12]
    assert 0x0 == header[13]
    assert 0x0 == header[14]
    assert 0x0 == header[15]

    # Name Length
    assert 0x8 == header[16]
    assert 0x0 == header[17]
    assert 0x0 == header[18]
    assert 0x0 == header[19]

    # Name
    assert ord('E') == header[20]
    assert ord('0') == header[21]
    assert ord('0') == header[22]
    assert ord('0') == header[23]
    assert ord('0') == header[24]
    assert ord('1') == header[25]
    assert ord('4') == header[26]
    assert ord('\0') == header[27]
Exemplo n.º 23
0
    def calculate_direction(east, north):
        """
        Calculate the direction of the water current.
        This will return a value between 0 and 360.
        :param east: Earth East Velocity
        :param north: Earth North Velocity
        :return: Direction of the water
        """
        if not Ensemble.is_bad_velocity(east) and not Ensemble.is_bad_velocity(
                north):
            bin_dir = (math.atan2(east, north)) * (180.0 / math.pi)

            # The range is -180 to 180
            # This moves it to 0 to 360
            if bin_dir < 0.0:
                bin_dir = 360.0 + bin_dir

            return bin_dir
        else:
            return Ensemble.BadVelocity
Exemplo n.º 24
0
    def accum_mag_dir(self, awc):
        """
        Create the Magnitude dataframe.  This takes all the information
        from the Magnitude and creates a row in the dataframe for each bin,beam value.
        :param awc: Average data.
        :return:
        """
        # Convert the east array to df
        # params: vel_array, dt, ss_code, ss_config, blank, bin_size
        # DF Columns: Index, time_stamp, ss_code, ss_config, bin_num, beam_num, bin_depth, value
        df_mag = Ensemble.array_1d_to_df(
            awc[AverageWaterColumn.INDEX_MAG],
            awc[AverageWaterColumn.INDEX_LAST_TIME],
            awc[AverageWaterColumn.INDEX_SS_CODE],
            awc[AverageWaterColumn.INDEX_SS_CONFIG],
            awc[AverageWaterColumn.INDEX_BLANK],
            awc[AverageWaterColumn.INDEX_BIN_SIZE],
            awc[AverageWaterColumn.INDEX_FIRST_ENS_NUM],
            awc[AverageWaterColumn.INDEX_LAST_ENS_NUM])

        # Store the mag results
        if self.df_mag.empty:
            self.df_mag = df_mag
        else:
            self.df_mag = pd.concat([self.df_mag, df_mag], ignore_index=True)

        df_dir = Ensemble.array_1d_to_df(
            awc[AverageWaterColumn.INDEX_DIR],
            awc[AverageWaterColumn.INDEX_LAST_TIME],
            awc[AverageWaterColumn.INDEX_SS_CODE],
            awc[AverageWaterColumn.INDEX_SS_CONFIG],
            awc[AverageWaterColumn.INDEX_BLANK],
            awc[AverageWaterColumn.INDEX_BIN_SIZE],
            awc[AverageWaterColumn.INDEX_FIRST_ENS_NUM],
            awc[AverageWaterColumn.INDEX_LAST_ENS_NUM])

        # Store the dir results
        if self.df_dir.empty:
            self.df_dir = df_dir
        else:
            self.df_dir = pd.concat([self.df_dir, df_dir], ignore_index=True)
Exemplo n.º 25
0
def test_generate_header():

    value_type = 50  # Byte
    num_elements = 11  # 17 elements
    element_multiplier = 1  # no multiplier
    imag = 0  # NOT USED
    name_length = 8  # Length of name
    name = "E000011\0"  # Ancillary name

    header = Ensemble.generate_header(value_type, num_elements,
                                      element_multiplier, imag, name_length,
                                      name)

    # Value type
    assert 0x32 == header[0]
    assert 0x0 == header[1]
    assert 0x0 == header[2]
    assert 0x0 == header[3]

    # Num Elements
    assert 0x0B == header[4]
    assert 0x0 == header[5]
    assert 0x0 == header[6]
    assert 0x0 == header[7]

    # Element Multiplier
    assert 0x1 == header[8]
    assert 0x0 == header[9]
    assert 0x0 == header[10]
    assert 0x0 == header[11]

    # Imag
    assert 0x0 == header[12]
    assert 0x0 == header[13]
    assert 0x0 == header[14]
    assert 0x0 == header[15]

    # Name Length
    assert 0x8 == header[16]
    assert 0x0 == header[17]
    assert 0x0 == header[18]
    assert 0x0 == header[19]

    # Name
    assert ord('E') == header[20]
    assert ord('0') == header[21]
    assert ord('0') == header[22]
    assert ord('0') == header[23]
    assert ord('0') == header[24]
    assert ord('1') == header[25]
    assert ord('1') == header[26]
    assert ord('\0') == header[27]
def test_generate_header():

    value_type = 20  # Int
    num_elements = 19  # 19 elements
    element_multiplier = 1  # no multiplier
    imag = 0  # NOT USED
    name_length = 8  # Length of name
    name = "E000008\0"  # Ensemble Dataset name

    header = Ensemble.generate_header(value_type, num_elements,
                                      element_multiplier, imag, name_length,
                                      name)

    # Value type
    assert 0x14 == header[0]
    assert 0x0 == header[1]
    assert 0x0 == header[2]
    assert 0x0 == header[3]

    # Num Elements
    assert 0x13 == header[4]
    assert 0x0 == header[5]
    assert 0x0 == header[6]
    assert 0x0 == header[7]

    # Element Multiplier
    assert 0x1 == header[8]
    assert 0x0 == header[9]
    assert 0x0 == header[10]
    assert 0x0 == header[11]

    # Imag
    assert 0x0 == header[12]
    assert 0x0 == header[13]
    assert 0x0 == header[14]
    assert 0x0 == header[15]

    # Name Length
    assert 0x8 == header[16]
    assert 0x0 == header[17]
    assert 0x0 == header[18]
    assert 0x0 == header[19]

    # Name
    assert ord('E') == header[20]
    assert ord('0') == header[21]
    assert ord('0') == header[22]
    assert ord('0') == header[23]
    assert ord('0') == header[24]
    assert ord('0') == header[25]
    assert ord('8') == header[26]
    assert ord('\0') == header[27]
Exemplo n.º 27
0
 def avg_range(self):
     # Average the ranges
     #range_count = 0
     #range_accum = 0.0
     #for beam in range(int(self.NumBeams)):
     #    if self.Range[beam] > 0.0:
     #        range_count += 1
     #        range_accum += self.Range[beam]
     #if range_count > 0:
     #    return range_accum / range_count
     #else:
     #    return 0.0
     return Ensemble.get_avg_range(self.Range)
    def encode_csv(self, dt, ss_code, ss_config, blank, bin_size):
        """
        Encode into CSV format.
        :param dt: Datetime object.
        :param ss_code: Subsystem code.
        :param ss_config: Subsystem Configuration
        :param blank: Blank or first bin position in meters.
        :param bin_size: Bin size in meters.
        :return: List of CSV lines.
        """
        str_result = []

        for beam in range(self.element_multiplier):
            for bin_num in range(self.num_elements):
                # Get the value
                val = self.Velocities[bin_num][beam]

                # Create the CSV string
                str_result.append([
                    Ensemble.gen_csv_line(dt, Ensemble.CSV_EARTH_VEL, ss_code,
                                          ss_config, bin_num, beam, blank,
                                          bin_size, val)
                ])

        # Generate Magnitude and Direction CSV
        for bin_num in range(self.num_elements):
            mag = self.Magnitude[bin_num]
            dir = self.Direction[bin_num]
            str_result.append([
                Ensemble.gen_csv_line(dt, Ensemble.CSV_MAG, ss_code, ss_config,
                                      bin_num, 0, blank, bin_size, mag)
            ])
            str_result.append([
                Ensemble.gen_csv_line(dt, Ensemble.CSV_DIR, ss_code, ss_config,
                                      bin_num, 0, blank, bin_size, dir)
            ])

        return str_result
Exemplo n.º 29
0
    def avg_range(self, rt):
        """
        Average the Range Tracking data given.
        This will verify the number of beams
        is the same between ensembles.  If any ensembles have a different
        number of beams, then a exception will be thrown.

        This will not average the data if the data is BAD VELOCITY.

        :param rt:  Range Tracking data from each ensemble.
        :return: Average of all the Range Tracking in the all the ensembles.
        """
        # Determine number of beams
        num_beams = 0
        avg_accum = []
        avg_count = []
        avg_range = None

        for ens_range in rt:
            temp_num_beams = len(ens_range)

            # Verify the bins and beams has not changed
            if num_beams == 0:
                num_beams = temp_num_beams
            elif num_beams != temp_num_beams:
                logging.error(
                    "Number of beams is not consistent between ensembles")
                self.thread_lock.release()
                raise Exception(
                    "Number of beams is not consistent between ensembles")

            # Create the average lists
            if num_beams != 0 and len(avg_accum) == 0:
                avg_accum = [0 for beam in range(num_beams)]
                avg_count = [0 for beam in range(num_beams)]
                avg_range = [0 for beam in range(num_beams)]

            # Accumulate the data
            for beam in range(len(ens_range)):
                if not Ensemble.is_bad_velocity(ens_range[beam]):
                    avg_accum[beam] += ens_range[beam]  # Accumulate range
                    avg_count[beam] += 1  # Count good data

        # Average the data accumulate
        for beam in range(len(avg_accum)):
            if avg_count[beam] > 0:  # Verify data was accumulated
                avg_range[beam] = avg_accum[beam] / avg_count[
                    beam]  # Average data

        return avg_range
Exemplo n.º 30
0
    def avg_mag_dir(self, data):
        """
        Average the magnitude or direction data given.
        This will verify the number of bins
        is the same between ensembles.  If any ensembles have a different
        number of bins, then a exception will be thrown.

        This will not average the data if the data is BAD VELOCITY.

        :param data:  Magnitude or direction data from each ensemble.
        :return: Average of all the velocities in the all the ensembles.
        """
        # Determine number of bins and beams
        num_bins = 0
        avg_accum = []
        avg_count = []
        avg_vel = None

        for ens_data in data:
            temp_num_bins = len(ens_data)
            if num_bins == 0:
                num_bins = temp_num_bins
            elif num_bins != temp_num_bins:
                logging.error(
                    "Number of bins is not consistent between ensembles")
                self.thread_lock.release()
                raise Exception(
                    "Number of bins is not consistent between ensembles")

            # Create the average lists
            if num_bins != 0 and len(avg_accum) == 0:
                avg_accum = [0 for b in range(num_bins)]
                avg_count = [0 for b in range(num_bins)]
                avg_vel = [0 for b in range(num_bins)]

            # Accumulate the data
            for ens_bin in range(len(ens_data)):
                if not Ensemble.is_bad_velocity(ens_data[ens_bin]):
                    avg_accum[ens_bin] += ens_data[
                        ens_bin]  # Accumulate velocity
                    avg_count[ens_bin] += 1  # Count good data

        # Average the data accumulate
        for ens_bin in range(len(avg_accum)):
            if avg_count[ens_bin] > 0:  # Verify data was accumulated
                avg_vel[ens_bin] = avg_accum[ens_bin] / avg_count[
                    ens_bin]  # Average data

        return avg_vel