Closed bgottula closed 1 year ago
I realized that the current approach may require some inter-process communication for synchronization to make the script fully automated. The bright star is held at the center of the main camera by means of a subprocess which performs a spiral search followed by bringing the star to the center of the camera once it is found. While that subprocess continues running to hold the star at camera center, a still image is taken with the guidescope camera for plate solving. The user provides the synchronization event by answering a prompt when the star is centered in the main camera preview window. Without user interaction, the subprocess will need to somehow inform the parent process that the star is centered, which will indicate to the parent script that it's time to proceed.
I think the logic to detect when the star is centered already exists, but normally that logic is used to terminate the controller. In this case we don't want the controller to stop; we instead want it to inform another process that this condition has been satisfied.
One approach would be to use multiprocessing
which allows for synchronization primitives like multiprocessing.Event
to be shared between processes. For this to work, I think __main__.py
would need to be refactored such that there is a function handle that can be passed to multiprocessing.Process
with configuration passed in by function arguments rather than command line arguments.
I attempted to implement the multiprocessing
approach but ran into a number of challenges:
__main__.py
, so I had to move the contents of that over to track.py
. Not a huge deal; doesn't really change anything.sys.argv
of the parent process is inherited by the child process, naturally, so had to add an optional args
parameter to track.main()
so it could take arguments passed to it via the multiprocessing.Process()
constructor. Also not a huge deal. Still need to fix logging startup message so it doesn't log misleading information when sys.argv
is ignored.click
raises an exception in the child process. I don't understand why. The child process clearly has access to stdout
since logging still works; maybe it doesn't inherit access to stdin
? Ended up just commenting out the single yes/no prompt about stale model params figuring I'd deal with that later.stdout
in identical pairs. This does not seem to affect log messages written to files.spawn
startup method for multiprocessing
here to get around a couple of these weird annoyances, but that breaks point
which expects the fork
method. And for whatever reason it's impossible to use more than one method in a single program. Since I'm not willing to rewrite multiprocessing
usage in point
I'm stuck with the fork
method.The more I use it the more disenchanted I become with multiprocessing
. It seems like it's built on a bunch of fragile hacks that only work for trivial use cases. All I need is for one process to send a one-way signal to another process. Maybe I'd be better off reverting to subprocess.run
. I can achieve the desired inter-process communication by passing the child process the PID of the parent process so it can just send a signal like SIGUSR1
. The parent can wait for that signal with signal.sigtimedwait()
. Not the most elegant, but compared with multiprocessing
's hideous warts it might look rather attractive.
I just realized that the approach I'm using in the control
module to determine if the mount is converged on the target only checks if the separation angle between the mount position and the target position is small, but this isn't the right criterion.
Problem: How to specify how close to center of frame is close enough.
What should determine this:
Target
object ideally should not decide this, since it has no idea of the requirements or application. Perhaps it would be okay if it has a reasonable default, but depending on defaults to be correct for a given application is fragile.main()
function should not decide this for the same reason.align_guidescope
program, since that code is aware of the objective.What units should be used for the threshold:
Angle
.How:
align_guidescope
could calculate the threshold angle as a fraction of the main camera frame height, since ultimately the objective is to keep the target object reasonably close to the center of this camera's field of view. However, normally align_guidescope
does not have direct access to the main camera so it doesn't know about its field of view.
subprocess
that will then re-open that same camera. Then the problem becomes how to construct this object. This might require awkwardly parsing arguments from a different config file (the one passed to the subprocess)...ugh. I've worked hard to avoid making the main() function of these programs responsible for constructing most objects directly. In fact normally the main() functions don't interact with cameras directly at all, but it seems like I need to violate that separation of concerns in this case.align_guidescope
doesn't need to do elaborate contortions to compute it. The more I think about it, the more this seems like the right approach. Ultimately the accuracy of the guidescope alignment should be configurable and not hard-coded anywhere.The problem I'm dealing with now is that the program arguments I'm adding to track's main()
function are very specific to CameraTarget
and do not work for any other target. So when I add a program argument specifying the separation angle below which SIGUSR1
should be sent I have to write an awkward and lengthy help message that points out how it only applies under very limited circumstances and so on.
I realized that I should just move these program arguments into the target
module under the camera
subparser, and furthermore the code that actually sends the signal can then just be moved into the CameraTarget
class eliminating the need for any callbacks from main()
. That seems like it ought to be much cleaner and avoids cluttering up main()
with these concerns.
target.add_program_arguments()
main()
to targets.CameraTarget
After sleeping on it, I've changed my mind about the previous comment. I'd rather main()
be responsible for unusual side effects like sending signals to other processes, even if it clutters up main()
a bit, such that none of the Target
objects including CameraTarget
need to be directly involved in inter-process communication concerns.
This is the best I can think of for now. It's not super pretty, but it will get the job done and I think the coupling introduced is as minimal as it can be while solving the problem.
The basics have been implemented, but still need to be tested outside since I don't have a way to do a full integrated test of align_guidescope
indoors.
Thinking through some checks that could be automated:
align_guidescope
has existed with manual checks I've never seen it mark the wrong location. I don't think any additional automated checks are needed. I already trust Astrometry.net to work in other contexts like initial alignment.
The
align_guidescope
program currently requires some user interaction to confirm that it's doing the right thing, but this prevents more complete automation.