예제 #1
0
    def fill_state(self):
        state = {}
        state.update({"time": self.time_mgr.get_sec()})
        #state.update({"packjpeg": self.packjpeg})
        #state.update({"daymode": self.daymode})
        if self.websock_randid != 0:
            state.update({"rand_id": self.websock_randid})
        if self.img is not None and self.cam_err == False:
            state.update({"expo_code": self.expo_code})
        elif self.img is None:
            state.update({"expo_code": star_finder.EXPO_NO_IMG})
        elif self.cam_err:
            state.update({"expo_code": star_finder.EXPO_CAMERA_ERR})
        stable_solution = self.stable_solution()
        if stable_solution is not None:
            stable_solution.get_pole_coords()  # update time
            state.update({"solution": stable_solution.to_jsonobj()})
            state.update({"star_x": stable_solution.Polaris.cx})
            state.update({"star_y": stable_solution.Polaris.cy})
            state.update({"pole_x": stable_solution.x})
            state.update({"pole_y": stable_solution.y})
            state.update({"rotation": stable_solution.get_rotation()})
            state.update({
                "polaris_ra":
                (stable_solution.polaris_ra_dec[0] * 360.0) / 24.0
            })
            state.update({"pix_per_deg": stable_solution.pix_per_deg})
        else:
            state.update({"solution": False})
        if self.stars is not None:
            star_list = self.stars
            if len(star_list) > 50:
                star_list = blobstar.sort_brightness(star_list)[0:50]
            state.update({"stars": blobstar.to_jsonobj(star_list)})
        state.update({"polar_clock": self.time_mgr.get_angle()})

        state.update({"max_stars": self.max_stars})

        if self.extdisp is not None:
            state.update({"oled_avail": self.extdisp.oled_ok})
            state.update({"gps_avail": self.extdisp.gps_ok})

        # diagnostic info
        state.update({"frm_cnt": self.frm_cnt})
        if self.debug:
            state.update({"diag_cnt": self.diag_cnt})
            state.update({"diag_dur_all": self.dur_all})
            state.update({"diag_dur_sol": self.solu_dur})
            state.update({"diag_mem_alloc": gc.mem_alloc()})
            state.update({"diag_mem_free": gc.mem_free()})
        if self.img_stats is not None:
            state.update({"img_mean": self.img_stats.mean()})
            state.update({"img_stdev": self.img_stats.stdev()})
            state.update({"img_max": self.img_stats.max()})
            state.update({"img_min": self.img_stats.min()})
        return state
    def handle_getstate(self, client_stream, req, headers, content):
        if self.debug:
            print("handle_getstate")
        self.handle_query(client_stream, req, reply=False, save=False)
        state = {}
        state.update({"time": self.time_mgr.get_sec()})
        state.update({"packjpeg": self.packjpeg})
        state.update({"daymode": self.daymode})
        if self.img is not None and self.cam_err == False:
            state.update({"expo_code": self.expo_code})
        elif self.img is None:
            state.update({"expo_code": star_finder.EXPO_NO_IMG})
        elif self.cam_err:
            state.update({"expo_code": star_finder.EXPO_CAMERA_ERR})
        stable_solution = self.stable_solution()
        if stable_solution is not None:
            stable_solution.get_pole_coords()  # update time
            state.update({"solution": stable_solution.to_jsonobj()})
            state.update({"star_x": stable_solution.Polaris.cx})
            state.update({"star_y": stable_solution.Polaris.cy})
            state.update({"pole_x": stable_solution.x})
            state.update({"pole_y": stable_solution.y})
            state.update({"rotation": stable_solution.get_rotation()})
            state.update({
                "polaris_ra":
                (stable_solution.polaris_ra_dec[0] * 360.0) / 24.0
            })
            state.update({"pix_per_deg": stable_solution.pix_per_deg})
        else:
            state.update({"solution": False})
        if self.stars is not None:
            star_list = self.stars
            if len(star_list) > 50:
                star_list = blobstar.sort_brightness(star_list)[0:50]
            state.update({"stars": blobstar.to_jsonobj(star_list)})
        state.update({"polar_clock": self.time_mgr.get_angle()})

        state.update({"max_stars": self.max_stars})

        # diagnostic info
        state.update({"frm_cnt": self.frm_cnt})
        if self.debug:
            state.update({"diag_cnt": self.diag_cnt})
            state.update({"diag_dur_all": self.dur_all})
            state.update({"diag_dur_sol": self.solu_dur})
            state.update({"diag_mem_alloc": gc.mem_alloc()})
            state.update({"diag_mem_free": gc.mem_free()})
        if self.img_stats is not None:
            state.update({"img_mean": self.img_stats.mean()})
            state.update({"img_stdev": self.img_stats.stdev()})
            state.update({"img_max": self.img_stats.max()})
            state.update({"img_min": self.img_stats.min()})

        json_str = ujson.dumps(state)
        client_stream.write(
            captive_portal.default_reply_header(
                content_type="application/json", content_length=len(json_str))
            + json_str)
        client_stream.close()
        if self.use_leds:
            red_led.on()
예제 #3
0
    def solve(self, polaris_ra_dec=(2.960856, 89.349278)):

        # polaris_ra_dec default to values at Jan 1 2020
        # supply new values according to current date
        self.polaris_ra_dec = polaris_ra_dec
        self.x = None
        self.y = None
        self.lam_umi = None
        self.solu_time = 0
        self.solu_time = int(round(pyb.millis() // 1000))

        if len(self.star_list) < SCORE_REQUIRED:
            return False  # impossible to have a solution if not enough stars

        # Polaris is the brightest object in the potential field of view, so it's faster to start with it
        brite_sorted = blobstar.sort_brightness(self.star_list)
        # limit the search for the first few possibilities
        if len(brite_sorted) > self.search_limit:
            brite_sorted = brite_sorted[0:self.search_limit]

        # iterate through all posibilities, brightest first
        for i in brite_sorted:
            # we are guessing "i" is Polaris for this iteration
            i.score_list = []
            i.score = 0
            i.penalty = 0
            i.rotation = 0
            i.rot_angi_sum = 0
            i.rot_angj_sum = 0
            i.rot_dist_sum = 0
            i.pix_calibration = []
            i.lam_umi = None
            ang_tol = 4

            for j in self.star_list:
                j.set_ref_star(
                    i
                )  # this is required for all entries in the list, so that sort_dist can work
                # set_ref_star also computes the vector to the ref star and caches the result
            dist_sorted = blobstar.sort_dist(
                self.star_list)  # sorted closest-to-Polaris first

            if self.debug:
                print("center star (%.1f , %.1f)" % (i.cx, i.cy))
                dbgi = 0
                for dbg in dist_sorted:
                    print("[%u]: (%.1f , %.1f) -> (%.1f , %.1f)" %
                          (dbgi, dbg.cx, dbg.cy, dbg.ref_star_dist,
                           dbg.ref_star_angle))
                    dbgi += 1

            rot_ang = None  # without a known reference angle, use the first angle we encounter to establish a reference angle
            # rot_ang is set after the match is made

            # these are used for the penalizing later
            max_dist = 0
            min_brite = -1

            idx_tbl = 0
            idx_blobs_start = 1  # start at [1] because [0] is supposed to be Polaris
            len_tbl = len(STARS_NEAR_POLARIS)
            len_blobs = len(dist_sorted)
            while idx_tbl < len_tbl:

                # skip stars that might have too similar of a vector distance if the reference angle is not established yet
                # it is unlikely that this logic is actually useful in real life
                if rot_ang is None and idx_tbl >= 1:
                    if abs(STARS_NEAR_POLARIS[idx_tbl][1] -
                           STARS_NEAR_POLARIS[idx_tbl - 1][1]) <= 2:
                        idx_tbl += 1
                        continue

                idx_blobs = idx_blobs_start  # previous blobs (closer-to-Polaris) will be ignored
                while idx_blobs < len_blobs:
                    k = dist_sorted[idx_blobs]
                    match = False
                    if dist_match(k.ref_star_dist,
                                  STARS_NEAR_POLARIS[idx_tbl][1]):

                        if self.debug:
                            print("dist matched [%s , %u] %.1f %.1f %.1f" %
                                  (STARS_NEAR_POLARIS[idx_tbl][0], idx_blobs,
                                   STARS_NEAR_POLARIS[idx_tbl][1],
                                   k.ref_star_dist,
                                   abs(k.ref_star_dist -
                                       STARS_NEAR_POLARIS[idx_tbl][1])))

                        if rot_ang is None:
                            # without a known reference angle, use the first angle we encounter to establish a reference angle
                            # rot_ang is set after the match is made
                            match = True
                            if self.debug:
                                print(
                                    "first angle match [%s , %u] %.1f %.1f %.1f"
                                    %
                                    (STARS_NEAR_POLARIS[idx_tbl][0], idx_blobs,
                                     STARS_NEAR_POLARIS[idx_tbl][2],
                                     k.ref_star_angle,
                                     angle_diff(
                                         k.ref_star_angle,
                                         STARS_NEAR_POLARIS[idx_tbl][2])))
                        else:
                            adj_ang = ang_normalize(
                                STARS_NEAR_POLARIS[idx_tbl][2] + rot_ang)
                            if angle_match(k.ref_star_angle,
                                           adj_ang,
                                           tol=ang_tol):
                                match = True
                                if ang_tol > 1:
                                    ang_tol -= 1
                                if self.debug:
                                    print("angle matched ", end="")
                            else:
                                if self.debug:
                                    print("angle match failed ", end="")
                            if self.debug:
                                print(
                                    "[%s , %u] %.1f %.1f %.1f %.1f %.1f" %
                                    (STARS_NEAR_POLARIS[idx_tbl][0], idx_blobs,
                                     STARS_NEAR_POLARIS[idx_tbl][2],
                                     k.ref_star_angle,
                                     angle_diff(k.ref_star_angle,
                                                adj_ang), rot_ang, adj_ang))
                    if match:
                        # each match is a further star, which means more precise angle
                        # compute (and update) the weighted average of the angle offset
                        rot_ang = angle_diff(k.ref_star_angle,
                                             STARS_NEAR_POLARIS[idx_tbl][2])
                        unitvector = [
                            math.cos(math.radians(rot_ang)),
                            math.sin(math.radians(rot_ang))
                        ]
                        i.rot_angi_sum += unitvector[0] * k.ref_star_dist
                        i.rot_angj_sum += unitvector[1] * k.ref_star_dist
                        i.rot_dist_sum += k.ref_star_dist
                        rot_ang = math.degrees(
                            math.atan2(i.rot_angj_sum / i.rot_dist_sum,
                                       i.rot_angi_sum / i.rot_dist_sum))
                        i.rotation = rot_ang

                        if STARS_NEAR_POLARIS[idx_tbl][0] == "* lam UMi":
                            i.lam_umi = k

                        if k.ref_star_dist > max_dist:
                            max_dist = k.ref_star_dist  # establishes maximum matching area
                        if k.brightness < min_brite or min_brite < 0:
                            min_brite = k.brightness  # establishes minimum matching brightness

                        # measured vs supposed distances may be different, track the differences
                        # this will account for distortion and focus-breathing
                        i.pix_calibration.append(
                            k.ref_star_dist / STARS_NEAR_POLARIS[idx_tbl][1])

                        # all previous (closer-to-Polaris) entries to be ignored on the next loop
                        idx_blobs_start = idx_blobs  # doing this will prevent potential out-of-order matches

                        #i.score_list.append(STARS_NEAR_POLARIS[idx_tbl][0]) # save the name to the list of matches (score)
                        i.score_list.append(k)

                        if self.debug:
                            print("score %u , new rotation %.1f" %
                                  (len(i.score_list), rot_ang))

                    idx_blobs += 1
                idx_tbl += 1

            # penalty function is optional
            if ENABLE_PENALTY:
                # go through all blobs again to see if we should penalize for mystery stars
                # if a star is brighter than some of the stars we've been able to match against
                # then it's a mystery star, and makes the solution less confident
                idx_blobs = 1
                while idx_blobs < len_blobs:
                    k = dist_sorted[idx_blobs]
                    if k.ref_star_dist < max_dist and k.brightness > min_brite:
                        # within the area and also brighter than expected
                        # does it match an entry in the table? (some of the table entries were ignored previously, so we have to do the whole check again)
                        in_database = False
                        idx_tbl = 0
                        len_tbl = len(STARS_NEAR_POLARIS)
                        while idx_tbl < len_tbl:
                            if dist_match(
                                    k.ref_star_dist,
                                    STARS_NEAR_POLARIS[idx_tbl]
                                [1]) and angle_match(
                                    k.ref_star_angle,
                                    ang_normalize(
                                        STARS_NEAR_POLARIS[idx_tbl][2] +
                                        rot_ang)):
                                in_database = True
                                break
                            idx_tbl += 1

                        if in_database == False:
                            is_hot = False
                            # check if it's a hot pixel
                            for hp in self.hot_pixels:
                                d = math.sqrt(((k.cx - hp[0])**2) +
                                              ((k.cy - hp[1])**2))
                                if d < 2.0:
                                    is_hot = True
                                    break
                            if is_hot == False:
                                i.penalty += 1
                                if self.debug:
                                    print("penalty (%.1f , %.1f)" %
                                          (k.cx, k.cy))
                    idx_blobs += 1
                # calculate score accounting for penalty
                i.score = len(i.score_list) - i.penalty

        # end of the for loop that goes from brightest to dimmest
        # each entry of that list will now have a "score" (number of matches)
        # find the one that has the most matches
        score_sorted = sorted(brite_sorted, key=sort_score_func, reverse=True)
        self.star_list = None  # garbage collect
        if score_sorted[0].score < SCORE_REQUIRED:
            return False  # not enough matches, no solution

        self.solved = True
        # store the solution states
        self.Polaris = score_sorted[0]
        self.rotation = score_sorted[0].rotation
        self.rotation = ang_normalize(self.rotation +
                                      180.0)  # everything needs to be flipped
        self.stars_matched = score_sorted[0].score_list  # for debug purposes
        self.penalty = score_sorted[0].penalty
        self.lam_umi = score_sorted[0].lam_umi
        dist_calibration = 0
        for i in score_sorted[0].pix_calibration:
            dist_calibration += i
        dist_calibration /= len(score_sorted[0].pix_calibration)
        self.pix_per_deg = PIXELS_PER_DEGREE * dist_calibration
        return True