Beispiel #1
0
    def _is_target_point_visible(self, target_point_trans, c_periscope):
        """
        Return True if the ``target_point_trans`` is visible from the current view device.
        """
        ## Target point in the focal point coord system
        t_pt_foc = get_translation(c_periscope.focal_point_trans_inv, target_point_trans)

        ## Calculate angle to target w.r.t to periscope view axis
        h_ang = atan2(t_pt_foc[0], t_pt_foc[2])
        rad_in_plane = sqrt(t_pt_foc[0] * t_pt_foc[0] + t_pt_foc[2] * t_pt_foc[2])
        v_ang = atan2(-t_pt_foc[1], rad_in_plane)

        ## Transform to project forward from the focal point to be slightly in front of the glass
        lens_dist = translate([0.0, 0.0, c_periscope.focal_distance * self.focal_length_multiplier, 1.0])

        ## Check if within the periscope view frustrum
        if c_periscope.is_ray_within_frustrum(h_ang, v_ang):
            ## Calculate the lens offset point
            tran_hor = rotation_about_vector(c_periscope.lens_hor_axis, h_ang)
            tran_vert = rotation_about_vector(mul(tran_hor, c_periscope.lens_hor_axis), v_ang)
            lens_point = get_translation(c_periscope.focal_point_trans, tran_vert, tran_hor, lens_dist)

            ## Actually do the intersection here
            t = self.b_tree.get_line_intersection(lens_point, get_translation(target_point_trans))

            if t < 0:
                ## Visible
                return True
        ## Not visible
        return False
    def post_process(self):
        """
        Generate the metrics from the stored raw ray trace results.
        """
        data = self.traverse_results
        ## Check raw traverse results for continguous regions of no ground coverage.
        none_runs = [
            len(list(grp)) for k, grp in itertools.groupby(data, lambda x: x)
            if k is None
        ]

        if none_runs and max(none_runs) >= self.contiguous:
            ## Fail because there is a contiguous region larger than allowed
            fwd = "Fail"
            aft = "Fail"
            no_fire_area = "Fail"
        else:
            trav_cen = get_translation(self.weapon.trans_trav)

            angles = np.array([x[0] for x in data if x[1] is not None])
            muz_pts = np.array([x[1][0] for x in data if x[1] is not None])
            gnd_pts = np.array([x[1][1] for x in data if x[1] is not None])

            ## Distance along the ground
            gnd_dist = np.sqrt((trav_cen[0] - gnd_pts[:, 0])**2 +
                               (trav_cen[2] - gnd_pts[:, 2])**2)

            ## Calculate the minimum shoot distance over contiguous regions
            mod_dist = grey_erosion(gnd_dist, self.contiguous, mode="wrap")

            ## Calculate an area metric (A = 0.5 * a.b.sin(c_ang))
            num_tris = len(angles)
            no_fire_area = 0.0
            for i in xrange(num_tris):
                next_ang = (angles[0] +
                            2 * pi) if i + 1 >= num_tris else angles[i + 1]
                c_ang = next_ang - angles[i]
                a = gnd_dist[i]
                b = gnd_dist[(i + 1) % num_tris]
                no_fire_area += 0.5 * a * b * sin(c_ang)

            ## Calculate min forward and aft shoot distance.
            fwd = np.max(mod_dist[np.where((angles >= 3 * pi / 2)
                                           | (angles <= pi / 2))])
            aft = np.max(mod_dist[np.where((angles <= 3 * pi / 2)
                                           & (angles >= pi / 2))])

            self.post_process_2d(angles, gnd_dist, mod_dist, no_fire_area, fwd,
                                 aft)

            if self.show_3d:
                self.post_process_3d(muz_pts, gnd_pts)

        ## Write results to json file and echo to log.
        out = {
            "min_fire_dist_fore_180": fwd,
            "min_fire_dist_aft_180": aft,
            "no_fire_area": no_fire_area
        }
        tba.write_results(out)
Beispiel #3
0
    def post_process_2d(self, angles, gnd_dist, mod_dist, area, fwd, aft):
        """
        Save a 2d figure to illustrate the no-fire region and the metrics.
        """
        ## Setup polar plot with textual labels rather than numerical
        ax = vehicle_polar_axes()

        ## Plot a line for the raw data and a filled region for the processed data.
        ax.plot(angles, gnd_dist, color="r", alpha=0.3)
        ax.fill(angles, mod_dist, color="r", alpha=0.5)

        ## Show semi-circles for the fore and aft closest point metrics.
        for offset, val in [(0, fwd), (pi, aft)]:
            phi = np.linspace(offset - pi / 2, offset + pi / 2, 180)
            r = np.ones_like(phi) * val
            ax.fill(phi, r, color="k", alpha=0.1)
            ax.plot(phi, r, color="k", linewidth=2)

        ## Add the metrics text.
        ax.text(-pi / 4, fwd + 2, "{}[m]".format(round(fwd, 1)), va="bottom")
        ax.text(pi + pi / 4, aft + 2, "{}[m]".format(round(aft, 1)), va="top")
        ax.text(0.0, fwd + 10, "No Fire Area = {}[m2]".format(int(area)), ha="center", color="r")

        ## Add the vehicle as a bounding box.
        trav_cen = get_translation(self.weapon.trans_trav)
        l_r = trav_cen[0] - np.min(self.nodes[:, 0]), trav_cen[0] - np.max(self.nodes[:, 0])
        f_b = trav_cen[2] - np.min(self.nodes[:, 2]), trav_cen[2] - np.max(self.nodes[:, 2])

        veh = np.array([(atan2(x, y), sqrt(x * x + y * y)) for x, y in itertools.product(l_r, f_b)])
        ax.fill(veh[[0, 1, 3, 2, 0], 0], veh[[0, 1, 3, 2, 0], 1], color="k", alpha=0.4)

        plt.savefig("field_of_fire_pic.png")
Beispiel #4
0
    def post_process(self):
        """
        Generate the metrics from the stored raw ray trace results.
        """
        data = self.traverse_results
        ## Check raw traverse results for continguous regions of no ground coverage.
        none_runs = [len(list(grp)) for k, grp in itertools.groupby(data, lambda x:x) if k is None]

        if none_runs and max(none_runs) >= self.contiguous:
            ## Fail because there is a contiguous region larger than allowed
            fwd = "Fail"
            aft = "Fail"
            no_fire_area = "Fail"
        else:
            trav_cen = get_translation(self.weapon.trans_trav)

            angles = np.array([x[0] for x in data if x[1] is not None])
            muz_pts = np.array([x[1][0] for x in data if x[1] is not None])
            gnd_pts = np.array([x[1][1] for x in data if x[1] is not None])

            ## Distance along the ground
            gnd_dist = np.sqrt((trav_cen[0] - gnd_pts[:, 0]) ** 2 +
                               (trav_cen[2] - gnd_pts[:, 2]) ** 2)

            ## Calculate the minimum shoot distance over contiguous regions
            mod_dist = grey_erosion(gnd_dist, self.contiguous, mode="wrap")

            ## Calculate an area metric (A = 0.5 * a.b.sin(c_ang))
            num_tris = len(angles)
            no_fire_area = 0.0
            for i in xrange(num_tris):
                next_ang = (angles[0] + 2 * pi) if i + 1 >= num_tris else angles[i+1]
                c_ang = next_ang - angles[i]
                a = gnd_dist[i]
                b = gnd_dist[(i + 1) % num_tris]
                no_fire_area += 0.5 * a * b * sin(c_ang)

            ## Calculate min forward and aft shoot distance.
            fwd = np.max(mod_dist[np.where((angles >= 3 * pi / 2) | (angles <= pi / 2))])
            aft = np.max(mod_dist[np.where((angles <= 3 * pi / 2) & (angles >= pi / 2))])

            self.post_process_2d(angles, gnd_dist, mod_dist, no_fire_area, fwd, aft)

            if self.show_3d:
                self.post_process_3d(muz_pts, gnd_pts)

        ## Write results to json file and echo to log.
        out = {
                  "min_fire_dist_fore_180" : fwd,
                  "min_fire_dist_aft_180" : aft,
                  "no_fire_area" : no_fire_area
              }
        tba.write_results(out)
Beispiel #5
0
    def _is_target_point_visible(self, target_point_trans, c_periscope):
        """
        Return True if the ``target_point_trans`` is visible from the current view device.
        """
        ## Target point in the focal point coord system
        t_pt_foc = get_translation(c_periscope.focal_point_trans_inv,
                                   target_point_trans)

        ## Calculate angle to target w.r.t to periscope view axis
        h_ang = atan2(t_pt_foc[0], t_pt_foc[2])
        rad_in_plane = sqrt(t_pt_foc[0] * t_pt_foc[0] +
                            t_pt_foc[2] * t_pt_foc[2])
        v_ang = atan2(-t_pt_foc[1], rad_in_plane)

        ## Transform to project forward from the focal point to be slightly in front of the glass
        lens_dist = translate([
            0.0, 0.0,
            c_periscope.focal_distance * self.focal_length_multiplier, 1.0
        ])

        ## Check if within the periscope view frustrum
        if c_periscope.is_ray_within_frustrum(h_ang, v_ang):
            ## Calculate the lens offset point
            tran_hor = rotation_about_vector(c_periscope.lens_hor_axis, h_ang)
            tran_vert = rotation_about_vector(
                mul(tran_hor, c_periscope.lens_hor_axis), v_ang)
            lens_point = get_translation(c_periscope.focal_point_trans,
                                         tran_vert, tran_hor, lens_dist)

            ## Actually do the intersection here
            t = self.b_tree.get_line_intersection(
                lens_point, get_translation(target_point_trans))

            if t < 0:
                ## Visible
                return True
        ## Not visible
        return False
    def post_process_2d(self, angles, gnd_dist, mod_dist, area, fwd, aft):
        """
        Save a 2d figure to illustrate the no-fire region and the metrics.
        """
        ## Setup polar plot with textual labels rather than numerical
        ax = vehicle_polar_axes()

        ## Plot a line for the raw data and a filled region for the processed data.
        ax.plot(angles, gnd_dist, color="r", alpha=0.3)
        ax.fill(angles, mod_dist, color="r", alpha=0.5)

        ## Show semi-circles for the fore and aft closest point metrics.
        for offset, val in [(0, fwd), (pi, aft)]:
            phi = np.linspace(offset - pi / 2, offset + pi / 2, 180)
            r = np.ones_like(phi) * val
            ax.fill(phi, r, color="k", alpha=0.1)
            ax.plot(phi, r, color="k", linewidth=2)

        ## Add the metrics text.
        ax.text(-pi / 4, fwd + 2, "{}[m]".format(round(fwd, 1)), va="bottom")
        ax.text(pi + pi / 4, aft + 2, "{}[m]".format(round(aft, 1)), va="top")
        ax.text(0.0,
                fwd + 10,
                "No Fire Area = {}[m2]".format(int(area)),
                ha="center",
                color="r")

        ## Add the vehicle as a bounding box.
        trav_cen = get_translation(self.weapon.trans_trav)
        l_r = trav_cen[0] - np.min(self.nodes[:, 0]), trav_cen[0] - np.max(
            self.nodes[:, 0])
        f_b = trav_cen[2] - np.min(self.nodes[:, 2]), trav_cen[2] - np.max(
            self.nodes[:, 2])

        veh = np.array([(atan2(x, y), sqrt(x * x + y * y))
                        for x, y in itertools.product(l_r, f_b)])
        ax.fill(veh[[0, 1, 3, 2, 0], 0],
                veh[[0, 1, 3, 2, 0], 1],
                color="k",
                alpha=0.4)

        plt.savefig("field_of_fire_pic.png")
Beispiel #7
0
    def trace_rays(self):
        """
        Trace the rays to calculate the raw hit points for each device.
        """
        for p, periscope in enumerate(self.periscopes):

            hit = []
            hor_fan = []
            hor_fan.append(get_translation(periscope.trans_glass))

            dist_ver = get_translation(
                periscope.trans_glass)[1] - self.z_ground

            ## Sweep around the global up direction to find horizontal view percent
            for i, ang_hor in enumerate(self.hor_sweep):
                ## Global transform to target point
                tran_hor_world = rotation_about_vector(self.up_direction,
                                                       ang_hor)

                target_point_trans = mul(
                    self.tran_veh, tran_hor_world,
                    translate(np.array([self.far_dist, 0, 0])),
                    translate(np.array([0, dist_ver, 0])))

                if self._is_target_point_visible(target_point_trans,
                                                 periscope):
                    self.target_points_horizon[i] += 2**p
                    hor_fan.append(get_translation(target_point_trans))
                else:
                    hor_fan.append(get_translation(periscope.trans_glass))

            self.hor_fans.append(hor_fan)

            ## Find the highest visible point in front of the vehicle
            max_uplook = 50
            accuracy = 0.001
            self.uplook[p] = 0.0
            tran_hor_world = rotation_about_vector(self.up_direction, pi)
            upper_uplook = max_uplook
            lower_uplook = 0.0

            while (upper_uplook - lower_uplook) > accuracy:
                ## Global transform to target point
                height = (upper_uplook + lower_uplook) * 0.5

                target_point_trans = mul(
                    self.tran_veh, tran_hor_world,
                    translate(np.array([0.0, height, 50.0])))

                if self._is_target_point_visible(target_point_trans,
                                                 periscope):
                    self.uplook[p] = height
                    lower_uplook = height
                else:
                    upper_uplook = height

            if self.uplook[p] > 0.0:
                hit.extend((get_translation(target_point_trans),
                            get_translation(target_point_trans),
                            get_translation(periscope.trans_glass)))

            ## Find the closest visible ground point fore and aft
            max_radius = 2e6
            self.fore_aft[p] = [max_radius, max_radius]
            for i, rot in enumerate([pi, 0.0]):
                tran_hor_world = rotation_about_vector(self.up_direction, rot)
                upper_radius = max_radius
                lower_radius = 0.0

                while (upper_radius - lower_radius) > accuracy:
                    ## Global transform to target point
                    radius = (upper_radius + lower_radius) * 0.5

                    target_point_trans = mul(
                        self.tran_veh, tran_hor_world,
                        translate(np.array([0, 0, radius])))

                    if self._is_target_point_visible(target_point_trans,
                                                     periscope):
                        self.fore_aft[p][i] = radius
                        upper_radius = radius
                    else:
                        lower_radius = radius

                if self.fore_aft[p][i] < max_radius * 0.5:
                    hit.extend((get_translation(target_point_trans),
                                get_translation(target_point_trans),
                                get_translation(periscope.trans_glass)))

            self.hit.append(hit)
    def trace_rays(self):
        """
        Trace the rays to calculate the raw hit points for each device.
        """
        wep = self.weapon
        z_rot = np.array([0, 0, 1, 1])
        tran_traverse = np.eye(4)
        tran_elevation = np.eye(4)
        elev_angle = -wep.max_depr
        for rot in self.hor_sweep:
            logging.info("Scanning angle={}".format(rot))

            ## Rotate the weapon by traverse angle.
            tran_traverse = rotation_about_vector(z_rot, rot, tran_traverse)
            tran_elev_point = mul(wep.trans_trav, tran_traverse,
                                  wep.elev_from_trav)
            elev_point = get_translation(tran_elev_point)

            first = True
            good_shot = None
            while True:
                ## Rotate the weapon up or down by elevation angle.
                tran_elevation = rotation_about_vector(z_rot, elev_angle,
                                                       tran_elevation)
                muz_point = get_translation(
                    mul(tran_elev_point, tran_elevation, wep.muz_from_elev))

                ## Find out where shot would hit ground (or None if it won't hit the ground).
                gnd_hit = ray_plane_intersection(elev_point, muz_point,
                                                 self.up_direction,
                                                 self.ground)

                ## Shot either 1) wouldn't hit the ground, 2) hit the ground or 3) hit the vehicle.
                if gnd_hit is not None:
                    ## Test if shot line cleared the vehicle.
                    t = self.b_tree.get_line_intersection(muz_point, gnd_hit)
                    shot = "cleared" if t < 0 else "collide"
                else:
                    ## Shot above the horizon
                    shot = "above_horizon"

                ## On first shot at this traverse angle determine if need to elevate or depress aim.
                if first:
                    first = False
                    if shot == "collide":
                        ## Need to try raising elevation
                        elev_change = self.incr_elev
                    elif shot == "cleared":
                        ## Need to try lowering elevation, but store because this might be the best.
                        elev_change = -self.incr_elev
                        good_shot = (muz_point, gnd_hit)
                    else:
                        elev_change = -self.incr_elev
                else:
                    if elev_change > 0.0:
                        ## elevation was being raised to find clearance
                        if shot == "cleared":
                            ## This is the closest shot possible, store it and stop looking.
                            good_shot = (muz_point, gnd_hit)
                            break
                        elif shot == "above_horizon" or elev_angle > wep.max_elev:
                            ## Missed the ground or exceeded weapon elevation.
                            break
                    else:
                        ## elevation was being lower to find a closer shot
                        if shot == "collide" or elev_angle < -wep.max_depr:
                            ## Must have already stored the best shot previously.
                            break
                        else:
                            ## Store this shot but keep looking.
                            good_shot = (muz_point, gnd_hit)

                elev_angle += elev_change

            if good_shot is not None:
                self.traverse_results.append((rot, good_shot))
            else:
                self.traverse_results.append((rot, None))
Beispiel #9
0
    def trace_rays(self):
        """
        Trace the rays to calculate the raw hit points for each device.
        """
        wep = self.weapon
        z_rot = np.array([0, 0, 1, 1])
        tran_traverse = np.eye(4)
        tran_elevation = np.eye(4)
        elev_angle = -wep.max_depr
        for rot in self.hor_sweep:
            logging.info("Scanning angle={}".format(rot))

            ## Rotate the weapon by traverse angle.
            tran_traverse = rotation_about_vector(z_rot, rot, tran_traverse)
            tran_elev_point = mul(wep.trans_trav, tran_traverse, wep.elev_from_trav)
            elev_point = get_translation(tran_elev_point)

            first = True
            good_shot = None
            while True:
                ## Rotate the weapon up or down by elevation angle.
                tran_elevation = rotation_about_vector(z_rot, elev_angle, tran_elevation)
                muz_point = get_translation(mul(tran_elev_point, tran_elevation, wep.muz_from_elev))

                ## Find out where shot would hit ground (or None if it won't hit the ground).
                gnd_hit = ray_plane_intersection(elev_point,
                                                 muz_point,
                                                 self.up_direction,
                                                 self.ground)

                ## Shot either 1) wouldn't hit the ground, 2) hit the ground or 3) hit the vehicle.
                if gnd_hit is not None:
                    ## Test if shot line cleared the vehicle.
                    t = self.b_tree.get_line_intersection(muz_point, gnd_hit)
                    shot = "cleared" if t < 0 else "collide"
                else:
                    ## Shot above the horizon
                    shot = "above_horizon"

                ## On first shot at this traverse angle determine if need to elevate or depress aim.
                if first:
                    first = False
                    if shot == "collide":
                        ## Need to try raising elevation
                        elev_change = self.incr_elev
                    elif shot == "cleared":
                        ## Need to try lowering elevation, but store because this might be the best.
                        elev_change = -self.incr_elev
                        good_shot = (muz_point, gnd_hit)
                    else:
                        elev_change = -self.incr_elev
                else:
                    if elev_change > 0.0:
                        ## elevation was being raised to find clearance
                        if shot == "cleared":
                            ## This is the closest shot possible, store it and stop looking.
                            good_shot = (muz_point, gnd_hit)
                            break
                        elif shot == "above_horizon" or elev_angle > wep.max_elev:
                            ## Missed the ground or exceeded weapon elevation.
                            break
                    else:
                        ## elevation was being lower to find a closer shot
                        if shot == "collide" or elev_angle < -wep.max_depr:
                            ## Must have already stored the best shot previously.
                            break
                        else:
                            ## Store this shot but keep looking.
                            good_shot = (muz_point, gnd_hit)

                elev_angle += elev_change


            if good_shot is not None:
                self.traverse_results.append((rot, good_shot))
            else:
                self.traverse_results.append((rot, None))
Beispiel #10
0
    def trace_rays(self):
        """
        Trace the rays to calculate the raw hit points for each device.
        """
        for p, periscope in enumerate(self.periscopes):

            hit = []
            hor_fan = []
            hor_fan.append(get_translation(periscope.trans_glass))

            dist_ver = get_translation(periscope.trans_glass)[1] - self.z_ground

            ## Sweep around the global up direction to find horizontal view percent
            for i, ang_hor in enumerate(self.hor_sweep):
                ## Global transform to target point
                tran_hor_world = rotation_about_vector(self.up_direction, ang_hor)

                target_point_trans = mul(
                    self.tran_veh,
                    tran_hor_world,
                    translate(np.array([self.far_dist, 0, 0])),
                    translate(np.array([0, dist_ver, 0])),
                )

                if self._is_target_point_visible(target_point_trans, periscope):
                    self.target_points_horizon[i] += 2 ** p
                    hor_fan.append(get_translation(target_point_trans))
                else:
                    hor_fan.append(get_translation(periscope.trans_glass))

            self.hor_fans.append(hor_fan)

            ## Find the highest visible point in front of the vehicle
            max_uplook = 50
            accuracy = 0.001
            self.uplook[p] = 0.0
            tran_hor_world = rotation_about_vector(self.up_direction, pi)
            upper_uplook = max_uplook
            lower_uplook = 0.0

            while (upper_uplook - lower_uplook) > accuracy:
                ## Global transform to target point
                height = (upper_uplook + lower_uplook) * 0.5

                target_point_trans = mul(self.tran_veh, tran_hor_world, translate(np.array([0.0, height, 50.0])))

                if self._is_target_point_visible(target_point_trans, periscope):
                    self.uplook[p] = height
                    lower_uplook = height
                else:
                    upper_uplook = height

            if self.uplook[p] > 0.0:
                hit.extend(
                    (
                        get_translation(target_point_trans),
                        get_translation(target_point_trans),
                        get_translation(periscope.trans_glass),
                    )
                )

            ## Find the closest visible ground point fore and aft
            max_radius = 2e6
            self.fore_aft[p] = [max_radius, max_radius]
            for i, rot in enumerate([pi, 0.0]):
                tran_hor_world = rotation_about_vector(self.up_direction, rot)
                upper_radius = max_radius
                lower_radius = 0.0

                while (upper_radius - lower_radius) > accuracy:
                    ## Global transform to target point
                    radius = (upper_radius + lower_radius) * 0.5

                    target_point_trans = mul(self.tran_veh, tran_hor_world, translate(np.array([0, 0, radius])))

                    if self._is_target_point_visible(target_point_trans, periscope):
                        self.fore_aft[p][i] = radius
                        upper_radius = radius
                    else:
                        lower_radius = radius

                if self.fore_aft[p][i] < max_radius * 0.5:
                    hit.extend(
                        (
                            get_translation(target_point_trans),
                            get_translation(target_point_trans),
                            get_translation(periscope.trans_glass),
                        )
                    )

            self.hit.append(hit)