예제 #1
0
    def process_observation(self, obs: Obs) -> Obs:
        z = obs.get_response_value('z')

        if not z:
            return obs

        d = obs.get_response_value('slopeDist')

        if not d:
            return obs

        k = 0.13  # Refraction coefficient.
        r = 6370000  # Earth radius.

        k_e = (d * d) / (2 * r)  # Correction of earth radius.
        k_r = k * k_e  # Correction of refraction.
        r = k_e - k_r

        self.logger.info('Updated height in observation "{}" of target "{}" '
                         'from {:3.4f} m to {:3.4f} m (refraction value: '
                         '{:3.5f} m)'.format(obs.get("name"),
                                             obs.get("target"), z, z + r, r))

        refraction = Obs.create_response_set('float', 'm', round(r, 6))
        z_new = Obs.create_response_set('float', 'm', round(z + r, 5))
        z_raw = Obs.create_response_set('float', 'm', z)

        obs.data['responseSets']['refraction'] = refraction
        obs.data['responseSets']['zRaw'] = z_raw
        obs.data['responseSets']['z'] = z_new

        return obs
예제 #2
0
    def process_observation(self, obs: Obs) -> Obs:
        if not self._is_valid_sensor_type(obs):
            # Only total stations are supported.
            return obs

        hz = obs.get_response_value('hz')
        v = obs.get_response_value('v')
        dist = obs.get_response_value('slopeDist')

        if None in [hz, v, dist]:
            return obs

        # Calculate the horizontal distance.
        dist_hz = math.sin(v) * dist

        if self._is_fixed_point(obs):
            # Add measured Hz and calculated Hz to the fixed point.
            self._update_fixed_point(obs)
            self.logger.debug(f'Updated fixed point of target '
                              f'"{obs.get("target")}"')

        self.logger.debug(
            'Starting polar transformation of target "{}" (Hz = '
            '{:3.5f} gon, V = {:3.5f} gon, dist = {:4.5f} m)'.format(
                obs.get("target"), rad_to_gon(hz), rad_to_gon(v), dist_hz))

        if self._is_adjustment_enabled:
            # Add the adjustment value to the horizontal direction.
            adj = self._get_adjustment_value()
            self.logger.info('Calculated adjustment value for polar '
                             'transformation ({:3.5f} gon)'.format(
                                 rad_to_gon(adj)))
            hz += adj

        # Calculate the coordinates of the observation.
        x, y, z = self.transform(self._view_point.get('x'),
                                 self._view_point.get('y'),
                                 self._view_point.get('z'),
                                 self._azimuth_point.get('x'),
                                 self._azimuth_point.get('y'), hz, v, dist_hz)

        self.logger.info('Transformed target "{}" (X = {:3.4f}, Y = {:3.4f}, '
                         'Z = {:3.4f})'.format(obs.get("target"), x, y, z))

        # Add to observation data set.
        response_sets = obs.get('responseSets')
        response_sets['x'] = Obs.create_response_set('float', 'm', round(x, 5))
        response_sets['y'] = Obs.create_response_set('float', 'm', round(y, 5))
        response_sets['z'] = Obs.create_response_set('float', 'm', round(z, 5))

        if self._is_adjustment_enabled:
            response_sets['hzAdjusted'] = Obs.create_response_set(
                'float', 'rad', round(hz, 16))

        return obs
예제 #3
0
    def _calculate_target_point(self, obs: Obs) -> Obs:
        """Calculates the coordinates of a target point and updates the
        given `Observation` object.

        Args:
            obs: `Observation` object.

        Returns:
            The `Observation` object with calculated coordinates.
        """
        hz = obs.get_response_value('hz')
        v = obs.get_response_value('v')
        dist = obs.get_response_value('slopeDist')

        if None in [hz, v, dist]:
            self.logger.warning(f'Hz, V, or distance missing in observation '
                                f'"{obs.get("name")}" of target '
                                f'"{obs.get("target")}"')
            return obs

        # Calculate the coordinates in the global system (X, Y, Z).
        x, y, z = self.calculate_point_coordinates(hz, v, dist,
                                                   self._view_point.get('x'),
                                                   self._view_point.get('y'),
                                                   self._view_point.get('z'),
                                                   self._a, self._o)

        self.logger.info('Calculated coordinates of target point "{}" '
                         '(X = {:4.5f}, Y = {:4.5f}, Z = {:4.5f})'.format(
                             obs.get("target"), x, y, z))

        # Do residual mismatch transformation.
        if self._is_residual:
            vx, vy = self._calculate_residual_mismatches(x, y)

            self.logger.debug('Calculated improvements for target point "{}" '
                              '(dX = {:4.5f} m, dY = {:4.5f} m)'.format(
                                  obs.get("target"), vx, vy))

            x += vx
            y += vy

            self.logger.debug('Updated coordinates of target point "{}" '
                              '(X = {:4.5f}, Y = {:4.5f})'.format(
                                  obs.get("target"), x, y))

        # Add response set.
        response_sets = obs.get('responseSets')
        response_sets['x'] = Obs.create_response_set('float', 'm', x)
        response_sets['y'] = Obs.create_response_set('float', 'm', y)
        response_sets['z'] = Obs.create_response_set('float', 'm', z)

        return obs
예제 #4
0
    def _update_fixed_point(self, obs: Obs) -> None:
        """Adds horizontal direction, vertical angle, and slope distance
        of the observation to a fixed point.

        Args:
            obs: `Observation` object.
        """
        hz = obs.get_response_value('hz')
        v = obs.get_response_value('v')
        dist = obs.get_response_value('slopeDist')

        if None in [hz, v, dist]:
            return

        # Calculate the coordinates of the fixed point if the Helmert
        # transformation has been done already. Otherwise, use the datum from
        # the configuration.
        fixed_point = self._fixed_points.get(obs.get('target'))

        if self._is_ready():
            # Calculate fixed point coordinates.
            x, y, z = self.calculate_point_coordinates(
                hz, v, dist, self._view_point.get('x'),
                self._view_point.get('y'), self._view_point.get('z'), self._a,
                self._o)

            self.logger.info('Calculated coordinates of fixed point "{}" '
                             '(X = {:3.5f}, Y = {:3.5f}, Z = {:3.5f})'.format(
                                 obs.get("target"), x, y, z))
        else:
            # Get the coordinates of the fixed point from the configuration.
            x = fixed_point.get('x')
            y = fixed_point.get('y')
            z = fixed_point.get('z')

        # Update the values.
        fixed_point['hz'] = hz
        fixed_point['v'] = v
        fixed_point['dist'] = dist
        fixed_point['lastUpdate'] = time.time()

        self.logger.debug(f'Updated fixed point of target '
                          f'"{obs.get("target")}"')

        # Add global Cartesian coordinates of the fixed point to the
        # observation.
        response_sets = obs.get('responseSets')
        response_sets['x'] = Obs.create_response_set('float', 'm', x)
        response_sets['y'] = Obs.create_response_set('float', 'm', y)
        response_sets['z'] = Obs.create_response_set('float', 'm', z)
예제 #5
0
    def _fire(self, c: int) -> None:
        """Creates a new observation with the given counter value.

        Args:
            c: The counter value.
        """
        obs = Observation()
        gpio = 'gpio{}'.format(self._pin)

        response_sets = {
            gpio: Observation.create_response_set('int', 'none', c)
        }

        obs.set('enabled', False)
        obs.set('name', 'interruptCount')
        obs.set('nextReceiver', 0)
        obs.set('nid', self._node_manager.node.id)
        obs.set('onetime', False)
        obs.set('portName', f'GPIO{self._pin}')
        obs.set('pid', self._project_manager.project.id)
        obs.set('receivers', [self._receiver])
        obs.set('responseSets', response_sets)
        obs.set('sensorName', self._sensor_name)
        obs.set('sensorType', 'gpio')
        obs.set('sleepTime', 0.0)
        obs.set('target', gpio)
        obs.set('timestamp', str(arrow.utcnow()))

        self.publish_observation(obs)
예제 #6
0
    def process_observation(self, obs: Obs) -> Obs:
        # Calculate the serial measurement of an observation in two faces.
        hz_0 = obs.get_response_value('hz0')
        hz_1 = obs.get_response_value('hz1')

        v_0 = obs.get_response_value('v0')
        v_1 = obs.get_response_value('v1')

        dist_0 = obs.get_response_value('slopeDist0')
        dist_1 = obs.get_response_value('slopeDist1')

        if None in [hz_0, hz_1, v_0, v_1, dist_0, dist_1]:
            return obs

        # Calculate new Hz, V, and slope distance.
        hz = hz_0 + hz_1

        if hz_0 > hz_1:
            hz += math.pi
        else:
            hz -= math.pi

        hz /= 2

        v = ((2 * math.pi) + (v_0 - v_1)) / 2
        dist = (dist_0 + dist_1) / 2

        # Save the calculated values.
        response_sets = obs.get('responseSets')
        response_sets['hz'] = Obs.create_response_set('float', 'rad', hz)
        response_sets['v'] = Obs.create_response_set('float', 'rad', v)
        response_sets['slopeDist'] = Obs.create_response_set('float' 'm', dist)

        self.logger.debug(f'Calculated serial measurement with two faces for '
                          f'observation "{obs.get("name")}" of target '
                          f'"{obs.get("target")}"')
        return obs
예제 #7
0
    def process_observation(self, obs: Observation) -> Observation:
        for name, properties in self._config.items():
            response_set = obs.get('responseSets').get(name)

            if not response_set:
                continue

            source_value = response_set.get('value')
            source_unit = response_set.get('unit')

            if not source_value or not source_unit:
                continue

            if source_unit != properties.get('sourceUnit'):
                self.logger.warning(f'Unit "{source_unit}" of response '
                                    f'"{name}" in observation '
                                    f'"{obs.get("name")}" of target '
                                    f'"{obs.get("target")}" does not match '
                                    f'"{properties.get("sourceUnit")}"')
                continue

            if properties.get('conversionType') == 'scale':
                target_value = self.scale(float(source_value),
                                          properties.get('scalingValue'))
                target_unit = properties.get('targetUnit')

                self.logger.info(
                    'Converted response "{}" in observation "{}" '
                    'of target "{}" from {:.4f} {} to {:.4f} {}'.format(
                        name, obs.get("name"), obs.get("target"), source_value,
                        source_unit, target_value, target_unit))

                response_set = Observation.create_response_set(
                    'float', target_unit, round(target_value, 5))

                obs.data['responseSets'][name] = response_set

        return obs
예제 #8
0
 def test_create_response_test(self, observation: Observation) -> None:
     response_set = observation.create_response_set('test', 'none', 0.0)
     assert response_set == {'type': 'test', 'unit': 'none', 'value': 0.0}
예제 #9
0
    def process_observation(self, obs: Obs) -> Obs:
        sensor_type = obs.get('sensorType')

        # Update atmospheric data if sensor is a weather station.
        if SensorType.is_weather_station(sensor_type):
            self._update_meteorological_data(obs)
            return obs

        # Check if sensor is of type "total station".
        if not SensorType.is_total_station(sensor_type):
            self.logger.warning(f'Sensor type "{sensor_type}" not supported')
            return obs

        # Check if atmospheric data has been set.
        if None in [self.temperature, self.pressure, not self.humidity]:
            self.logger.warning('Missing temperature, air pressure, or '
                                'humidity')
            return obs

        # Check the age of the atmospheric data.
        if self.last_update - time.time() > self._max_age:
            self.logger.warning(f'Atmospheric data is older than '
                                f'{int(self._max_age / 3600)} hour(s)')

        # Reduce the slope distance of the EDM measurement.
        dist = obs.get_response_value(self._distance_name)

        if dist is None:
            self.logger.error(f'No distance set in observation '
                              f'"{obs.get("name")}" of target '
                              f'"{obs.get("target")}"')
            return obs

        d_dist_1 = 0
        d_dist_2 = 0

        response_sets = obs.get('responseSets')

        # Calculate the atmospheric reduction of the distance.
        if self._is_atmospheric_correction:
            c = self.get_atmospheric_correction(self._temperature,
                                                self._pressure, self._humidity)
            d_dist_1 = dist * c * math.pow(10, -6)

            rs = Obs.create_response_set('float', 'none', round(c, 5))
            response_sets['atmosphericPpm'] = rs

        # Calculate the sea level reduction of the distance.
        if self._is_sea_level_correction:
            d_dist_2 = self.get_sea_level_correction(self._sensor_height)

            rs = Obs.create_response_set('float', 'm', round(d_dist_2, 5))
            response_sets['seaLevelDelta'] = rs

        # Add corrected distance to the observation set.
        if d_dist_1 != 0 or d_dist_2 != 0:
            r_dist = dist + d_dist_1 + d_dist_2
            self.logger.info('Reduced distance from {:0.5f} m to {:0.5f} m '
                             '(correction value: {:0.5f} m)'.format(
                                 dist, r_dist, d_dist_1 + d_dist_2))
            rs = Obs.create_response_set('float', 'm', round(r_dist, 5))

            response_sets[self._distance_name + 'Raw'] =\
                response_sets.get(self._distance_name)
            response_sets[self._distance_name] = rs

        return obs
예제 #10
0
    def _calculate_view_point(self, obs: Obs) -> Union[Obs, None]:
        """Calculates the view point by doing a 2D Helmert transformation.

        Args:
            obs: `Observation` object. Needed for port and sensor information.

        Returns:
            New `Observation` object with view point coordinates.
        """
        sum_local_x = sum_local_y = sum_local_z = 0  # [x], [y], [z].
        sum_global_x = sum_global_y = sum_global_z = 0  # [X], [Y], [Z].
        num_fixed_points = len(self._fixed_points)  # n.

        # Calculate the centroid coordinates of the view point.
        for name, fixed_point in self._fixed_points.items():
            hz = fixed_point.get('hz')  # Horizontal direction.
            v = fixed_point.get('v')  # Vertical angle.
            dist = fixed_point.get('dist')  # Distance (slope or reduced).

            if None in [hz, v, dist]:
                self.logger.warning(f'Hz, V, or distance missing in fixed '
                                    f'point "{name}"')
                return

            # Calculate Cartesian coordinates out of polar coordinates.
            local_x, local_y, local_z = self.get_cartesian_coordinates(
                hz, v, dist)

            # Store local coordinates in the fixed point dictionary.
            fixed_point['localX'] = local_x
            fixed_point['localY'] = local_y
            fixed_point['localZ'] = local_z

            # Coordinates in the global system (X, Y, Z).
            global_x = fixed_point.get('x')
            global_y = fixed_point.get('y')
            global_z = fixed_point.get('z')

            if None in [global_x, global_y, global_z]:
                self.logger.error(f'Undefined fixed point "{name}"')

            # Sums of the coordinates.
            sum_local_x += local_x
            sum_local_y += local_y
            sum_local_z += local_z

            sum_global_x += global_x
            sum_global_y += global_y
            sum_global_z += global_z

        # Coordinates of the centroids.
        local_centroid_x = sum_local_x / num_fixed_points  # x_s.
        local_centroid_y = sum_local_y / num_fixed_points  # y_s.

        global_centroid_x = sum_global_x / num_fixed_points  # X_s.
        global_centroid_y = sum_global_y / num_fixed_points  # Y_s.

        # Calculate transformation parameters.
        o_1 = o_2 = 0
        a_1 = a_2 = 0

        for name, fixed_point in self._fixed_points.items():
            local_x = fixed_point.get('localX')
            local_y = fixed_point.get('localY')

            global_x = fixed_point.get('x')
            global_y = fixed_point.get('y')

            # Reduced coordinates of the centroids.
            r_local_centroid_x = local_x - local_centroid_x
            r_local_centroid_y = local_y - local_centroid_y

            r_global_centroid_x = global_x - global_centroid_x
            r_global_centroid_y = global_y - global_centroid_y

            # o = [ x_i * Y_i - y_i * X_i ] * [ x_i^2 + y_i^2 ]^-1
            o_1 += (r_local_centroid_x * r_global_centroid_y) -\
                (r_local_centroid_y * r_global_centroid_x)
            o_2 += math.pow(r_local_centroid_x, 2) +\
                math.pow(r_local_centroid_y, 2)

            # a = [ x_i * X_i + y_i * Y_i ] * [ x_i^2 + y_i^2 ]^-1
            a_1 += (r_local_centroid_x * r_global_centroid_x) +\
                (r_local_centroid_y * r_global_centroid_y)
            a_2 += math.pow(r_local_centroid_x, 2) +\
                math.pow(r_local_centroid_y, 2)

        self._o = o_1 / o_2 if o_2 != 0 else 0  # Parameter o.
        self._a = a_1 / a_2 if a_2 != 0 else 0  # Parameter a.

        # Calculate the coordinates of the view point:
        # Y_0 = Y_s - a * y_s - o * x_s
        # X_0 = X_s - a * x_s + o * y_s
        # Z_0 = ([Z] - [z]) / n
        self._view_point['x'] = (global_centroid_x -
                                 (self._a * local_centroid_x) +
                                 (self._o * local_centroid_y))
        self._view_point['y'] = (global_centroid_y -
                                 (self._a * local_centroid_y) -
                                 (self._o * local_centroid_x))
        self._view_point['z'] = (sum_global_z - sum_local_z) / num_fixed_points

        self.logger.info('Calculated coordinates of view point "{}" '
                         '(X = {:4.5f}, Y = {:4.5f}, Z = {:4.5f})'.format(
                             self._view_point.get('target'),
                             self._view_point.get('x'),
                             self._view_point.get('y'),
                             self._view_point.get('z')))

        # Calculate the standard deviations.
        sum_wx = sum_wy = 0  # [W_x], [W_y].
        sum_wx_wx = sum_wy_wy = sum_wz_wz = 0  # [W_x^2], [W_y^2], [W_z^2].

        for name, fixed_point in self._fixed_points.items():
            local_x = fixed_point.get('localX')
            local_y = fixed_point.get('localY')
            local_z = fixed_point.get('localZ')

            global_x = fixed_point.get('x')
            global_y = fixed_point.get('y')
            global_z = fixed_point.get('z')

            view_point_x = self._view_point.get('x')
            view_point_y = self._view_point.get('y')
            view_point_z = self._view_point.get('z')

            wx_i = ((-1 * view_point_x) - (self._a * local_x) +
                    (self._o * local_y) + global_x)
            wy_i = ((-1 * view_point_y) - (self._a * local_y) -
                    (self._o * local_x) + global_y)

            sum_wx += wx_i
            sum_wy += wy_i

            sum_wx_wx += wx_i * wx_i
            sum_wy_wy += wy_i * wy_i
            sum_wz_wz += math.pow(view_point_z - (global_z - local_z), 2)

        # Sum of discrepancies should be 0, i.e., [W_x] = [W_y] = 0.
        r_sum_wx = abs(round(sum_wx, 5))
        r_sum_wy = abs(round(sum_wy, 5))

        if r_sum_wx != 0 or r_sum_wy != 0:
            self.logger.warning(f'Calculated coordinates of view point '
                                f'"{self._view_point.get("target")}" '
                                f'are inaccurate ([Wx] = {r_sum_wx}, '
                                f'[Wy] = {r_sum_wy})')

        # Standard deviations.
        sx = math.sqrt((sum_wx_wx + sum_wy_wy) / ((2 * num_fixed_points) - 4))
        sy = sx
        sz = math.sqrt(sum_wz_wz / (num_fixed_points - 1))

        self.logger.debug(
            'Calculated standard deviations '
            '(sX = {:1.5f} m, sY = {:1.5f} m, sZ = {:1.5f} m)'.format(
                sx, sy, sz))

        # Scale factor.
        m = math.sqrt((self._a * self._a) + (self._o * self._o))
        self.logger.debug('Calculated scale factor (m = {:1.5f})'.format(m))

        # Create response sets for the view point.
        response_sets = {
            'x': Obs.create_response_set('float', 'm', self._view_point['x']),
            'y': Obs.create_response_set('float', 'm', self._view_point['y']),
            'z': Obs.create_response_set('float', 'm', self._view_point['z']),
            'stdDevX': Obs.create_response_set('float', 'm', sx),
            'stdDevY': Obs.create_response_set('float', 'm', sy),
            'stdDevZ': Obs.create_response_set('float', 'm', sz),
            'scaleFactor': Obs.create_response_set('float', 'm', m)
        }

        # Create `Observation` instance for the view point.
        view_point = Obs()
        view_point.set('name', 'getViewPoint')
        view_point.set('nextReceiver', 0)
        view_point.set('nid', self._node_manager.node.id)
        view_point.set('portName', obs.get('portName'))
        view_point.set('pid', self._project_manager.project.id)
        view_point.set('receivers', self._view_point.get('receivers'))
        view_point.set('responseSets', response_sets)
        view_point.set('sensorName', obs.get('sensorName'))
        view_point.set('sensorType', obs.get('sensorType'))
        view_point.set('target', self._view_point.get('target'))
        view_point.set('timestamp', str(arrow.utcnow()))

        # Return the `Observation` object of the view point.
        return view_point