API reference¶
This reference is auto-generated from the source code in the pypogs GitHub repository. General instructions are avilable at the pypogs ReadTheDocs website.
High-level control of pypogs core¶
pypogs.Systemis the main instance for interfacing with and controlling the pypogs core. It allows the user to create and manage the hardware, tracking threads, targets etc.pypogs.Alignmentmanages the alignment and location of the system, including coordinate transformations.pypogs.Targetmanages the target and start/end times.
This is Free and Open-Source Software originally written by Gustav Pettersson at ESA.
- License:
Copyright 2019 the European Space Agency
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
-
class
pypogs.System(data_folder=None, debug_folder=None)¶ Instantiate and control pypogs functionality and hardware.
The first step is always to create a System instance. The hardware and all other classes are accessible as properties of the System (e.g.
coarse_camera,control_loop_thread) and should be created by asking the System instance to do so.Note
Tables of Earth’s rotation must be downloaded to do coordinate transforms. Calling
update_databases()will attempt to download these from the internet (if expired). To facilitate offline use of pypogs, these will otherwise not be automatically downloaded unless strictly necessary. If you use pypogs offline do this update every few months to keep the coordinate transforms accurate.Example
import pypogs sys = pypogs.System()
- Add and control hardware:
There are five possible hardware instances:
mount(pypogs.Mount)star_camera(pypogs.Camera)coarse_camera(pypogs.Camera)fine_camera(pypogs.Camera)receiver(pypogs.Receiver)
They should be added to the System by using an add method, e.g.
add_mount()formount, and may be removed by the respectiveclear_mount(). One special case is if the star and coarse cameras are the same physical camera, then useadd_star_camera_from_coarse()to copy thecoarse_cameraobject tostar_camera. After adding, see the respective class documentation for controlling the settings.- Example:
sys.add_coarse_camera(model='ptgrey', identity='18285284') sys.add_star_camera_from_coarse() sys.add_fine_camera(model='ptgrey', identity='18285254') sys.coarse_camera.exposure_time = 100 # milliseconds sys.coarse_camera.frame_rate = 2 # hertz sys.coarse_camera is sys.star_camera # returns True
- Settings for closed loop tracking:
When a coarse or fine Camera object is added, a corresponding tracking thread (e.g.
coarse_track_threadforcoarse_camera) is also created. This is apypogs.TrackingThreadobject which also holds apypogs.SpotTrackerobject (accessible viacoarse_track_thread.spot_tracker). The parameters for these (see respective documentation) are used to set up the detection for tracking. For best performance it is critical to set up these well.Further, a
pypogs.ControlLoopThreadobject is available ascontrol_loop_threadand defines the feedback parameters used for closed loop tracking (e.g. gains, modes, switching logic).- Example:
sys.coarse_track_thread.feedforward_threshold = 10 sys.coarse_track_thread.spot_tracker.bg_subtract_mode = 'global_median' sys.control_loop_thread.CCL_P = 1
- Set location and alignment:
alignmentreferences thepypogs.Alignmentinstance (auto created) used to determine and calibrate the location, alignment, and mount corrections. See the class documentation for how to set location and alignments. To do auto-alignment, use thedo_auto_star_alignment()method (requires a star camera).If your mount has built in alignment (and/or is physically aligned to the earth) you may call
alignment.set_alignment_enu()to set the telescope alignment to East, North, Up (ENU) coordinates, which will also disable the corrections done in pypogs. ENU is the traditional astronomical coordinate system for altitude (elevation) and azimuth telescopes, measured as degrees above the horizon and degrees away from north (towards east) respectively.- Example:
sys.alignment.set_location_lat_lon(lat=52.2155, lon=4.4194, height=45) sys.do_auto_star_alignment()
- Set target:
targetreferences thepypogs.Targetinstance (auto created) which holds the target and (optional) tracking start and end times. The target may be set directly to an astropy SkyCoord or a skyfield EarthSatellite bytarget.set_target()or these can be created by pypogs by e.g. callingtarget.set_target_from_tle()with a Two Line Element (TLE) for a satellite ortarget.set_target_from_ra_dec()with right ascension and declination (in decimal degrees) for a star.- Example satellite:
tle = ['1 28647U 05016B 19180.65078896 .00000014 00000-0 19384-4 0 9991',\ '2 28647 56.9987 238.9694 0122260 223.0550 136.0876 15.05723818 6663'] sys.target.set_target_from_tle(tle)
- Example star:
sys.target.set_target_from_ra_dec(37.95456067, 89.26410897) # Polaris
- Control tracking:
- You are now ready! Just call
start_tracking()and thenstop_tracking()when you are finished. - Release hardware:
- Before exiting, it’s strongly recommended to call
deinitialize()to release the hardware resources.
Parameters: - data_folder (pathlib.Path, optional) – The folder for data saving. If None (the default) the folder pypogs/data will be used/created.
- debug_folder (pathlib.Path, optional) – The folder for debug logging. If None (the default) the folder pypogs/debug will be used/created.
-
add_coarse_camera(model=None, identity=None, name='CoarseCamera', auto_init=True)¶ Create and set the coarse camera. Calls pypogs.Camera constructor with name=’CoarseCamera’ and the given arguments.
Parameters: - model (str, optional) – The model used to determine the correct hardware API. Supported: ‘ptgrey’ for PointGrey/FLIR Machine Vision cameras (using Spinnaker/PySpin APIs).
- identity (str, optional) – String identifying the device. For ptgrey this is ‘serial number’ as a string.
- name (str, optional) – Name for the device, defaults to ‘CoarseCamera’.
- auto_init (bool, optional) – If both model and identity are given when creating the Camera and auto_init is True (the default), Camera.initialize() will be called after creation.
-
add_coarse_camera_from_star()¶ Set the coarse camera to be the current star camera object.
-
add_fine_camera(model=None, identity=None, name='FineCamera', auto_init=True)¶ Create and set the fine camera. Calls pypogs.Camera constructor with name=’FineCamera’ and the given arguments.
Parameters: - model (str, optional) – The model used to determine the correct hardware API. Supported: ‘ptgrey’ for PointGrey/FLIR Machine Vision cameras (using Spinnaker/PySpin APIs).
- identity (str, optional) – String identifying the device. For ptgrey this is ‘serial number’ as a string. name (str, optional): Name for the device, defaults to ‘FineCamera’.
- auto_init (bool, optional) – If both model and identity are given when creating the Camera and auto_init is True (the default), Camera.initialize() will be called after creation.
-
add_mount(*args, **kwargs)¶ Create and set a pypogs.Mount for System.mount. Arguments passed to constructor.
Parameters: - model (str, optional) – The model used to determine the the hardware control interface. Supported: ‘celestron’ for Celestron NexStar and Orion/SkyWatcher SynScan (all the same) hand controller communication over serial.
- identity (str or int, optional) – String or int identifying the device. For model celestron this can either be a string with the serial port (e.g. ‘COM3’ on Windows or ‘/dev/ttyUSB0’ on Linux) or an int with the index in the list of available ports to use (e.g. identity=0 i if only one serial device is connected.)
- name (str, optional) – Name for the device.
- auto_init (bool, optional) – If both model and identity are given when creating the Mount and auto_init is True (the default), Mount.initialize() will be called after creation.
-
add_receiver(*args, **kwargs)¶ Create and set a pypogs.Receiver for the system. Arguments passed to constructor.
Parameters: - model (str, optional) – The model used to determine the correct hardware API. Supported: ‘ni_daq’ for National Instruments DAQ cards (tested on USB-6211).
- identity (str, optional) – String identifying the device and input. For ni_daq this is ‘device/input’ eg. ‘Dev1/ai1’ for device ‘Dev1’ and analog input 1; only differential input is supported for ni_daq.
- name (str, optional) – Name for the device.
- save_path (pathlib.Path, optional) – Save path to set. See Receiver.save_path for details.
- auto_init (bool, optional) – If both model and identity are given when creating the Receiver and auto_init is True (the default), Receiver.initialize() will be called after creation.
-
add_star_camera(model=None, identity=None, name='StarCamera', auto_init=True)¶ Create and set the star camera. Calls pypogs.Camera constructor with name=’StarCamera’ and the given arguments.
Parameters: - model (str, optional) – The model used to determine the correct hardware API. Supported: ‘ptgrey’ for PointGrey/FLIR Machine Vision cameras (using Spinnaker/PySpin APIs).
- identity (str, optional) – String identifying the device. For ptgrey this is ‘serial number’ as a string.
- name (str, optional) – Name for the device, defaults to ‘StarCamera’.
- auto_init (bool, optional) – If both model and identity are given when creating the Camera and auto_init is True (the default), Camera.initialize() will be called after creation.
-
add_star_camera_from_coarse()¶ Set the star camera to be the current coarse camera object.
-
clear_coarse_camera()¶ Set the coarse camera to None.
-
clear_fine_camera()¶ Set the fine camera to None.
-
clear_mount()¶ Set the mount to None.
-
clear_receiver()¶ Set the receiver to None.
-
clear_star_camera()¶ Set the star camera to None.
-
deinitialize()¶ Deinitialise camera, mount, and receiver if they are initialised.
-
do_alignment_test(max_trials=2, rate_control=True)¶ Move to 40 positions spread across the sky and measure the alignment errors.
Data is saved to CSV file named with the current time and ‘_System_align_test_hemisp.csv’.
Parameters: - max_trials (int, optional) – Maximum attempts to take each image and solve the position. Default 2.
- rate_control (bool, optional) – If True (the default) rate control (see pypogs.Mount) is used.
-
do_auto_star_alignment(max_trials=1, rate_control=True)¶ Do the auto star alignment procedure by taking eight star images across the sky.
Will call System.Alignment.set_alignment_from_observations() with the captured images.
Parameters: - max_trials (int, optional) – Maximum attempts to take each image and solve the position. Default 1.
- rate_control (bool, optional) – If True (the default) rate control (see pypogs.Mount) is used.
-
get_alt_az_of_target(times=None, time_step=0.1)¶ Get the corrected altitude and azimuth angles and rates of the target from the current alignment.
Parameters: - times (astropy.time.Time, optional) – The time(s) to calculate for. If None (the default) the current time is used. If time array the calculation is done for each time in the array.
- time_step (float, optional) – The time step in seconds used to calculate the angular rates (default .1).
Returns: Nx2 array with altitude and azimuth angles in degrees. numpy.ndarray: Nx2 array with altitude and azimuth rates in degrees per second.
Return type: numpy.ndarray
-
get_itrf_direction_of_target(times=None)¶ Get direction (unit vector) in ITRF from the telescope position to the target.
Parameters: times (astropy.time.Time, optional) – The time(s) to calculate for. If None (the default) the current time is used. If time array the calculation is done for each time in the array. Returns: Shape (3,) if single time, shape (3,N) if array time. Return type: numpy.ndarray
-
initialize()¶ Initialise cameras, mount, and receiver if they are not already.
-
slew_to_target(time=None, block=True, rate_control=True)¶ Slew the mount to the defined target.
Parameters: - time (astropy.time.Time, optional) – The time to calculate target position. If None (the default) the current time is used.
- block (bool, optional) – If True (the default), execution is blocked until move finishes.
- rate_control (bool, optional) – If True (the default) rate control (see pypogs.Mount) is used.
-
start_tracking()¶ Track the target, using closed loop feedback if defined.
The target will be tracked between the start and end times defined in the target. To stop manually call System.stop_tracking().
-
stop()¶ Stop all tasks.
-
static
update_databases()¶ Download and update Skyfield and Astropy databases (of earth rotation).
-
alignment¶ Get the system alignment object.
Type: pypogs.Alignment
-
coarse_camera¶ Get or set the coarse camera. Will create System.coarse_track_thread if not existing.
Type: pypogs.Camera
-
coarse_track_thread¶ Get the coarse tracking thread.
Type: pypogs.TrackingThread
-
control_loop_thread¶ Get the system control loop thread.
Type: System.ControlLoopThread
-
data_folder¶ Get or set the path for data saving. Will create folder if not existing.
Type: pathlib.Path
-
debug_folder¶ Get or set the path for debug logging. Will create folder if not existing.
Type: pathlib.Path
-
fine_camera¶ Get or set the fine camera. Will create System.fine_track_thread if not existing.
Type: pypogs.Camera
-
fine_track_thread¶ Get the fine tracking thread.
Type: pypogs.TrackingThread
-
is_busy¶ Returns True if system is doing some control task.
Type: bool
-
is_init¶ Returns True if all attached hardware (cameras, mount, and receiver) are initialised.
Type: bool
-
mount¶ Get or set the mount.
Type: pypogs.Mount
-
receiver¶ Get or set a pypogs.Receiver for the telescope.
-
star_camera¶ Get or set the star camera.
Type: pypogs.Camera
-
target¶ Get the system target object.
Type: pypogs.Alignment
-
class
pypogs.Alignment(data_folder=None, debug_folder=None)¶ Alignment and location of a telescope and coordinate transforms.
Location is the position of the telescope on Earth. It is provided as latitude, longitude, height relative to a reference ellipsoid. WGS84 (the GPS ellipsoid) is used in pypogs (and almost everywhere else).
Alignment refers to the different coordinate frames in use to get the telescope to point in the correct direction. A direction in some coordinate frame can either be represented as a cartesian unit vector or as two angles: altitude and azimuth. In the former case they are appended by _xyz and in the latter by _altaz. There are four coordinate frames to consider, ITRF, ENU, MNT, and COM, see below for descriptions.
The fundamental coordinates used are ITRF_xyz unit vectors. They give direction in an earth fixed cartesian frame. This can often be called the ECEF (Earth-Centered Earth-Fixed) or ECR (Earth-Centered Rotating) frame. pypogs uses external packages (Astropy and Skyfield) to get directions in ITRF_xyz, but the other three are managed here.
If using auto-align, all you really need to know is the ITRF or ENU direction you want the telescope to point towards, use this class to get COM_altaz, and send that to the mount.
Note
If you have a ITRF position vector (e.g. from centre of earth to a satellite) use Alignment.get_itrf_relative_from_position() to get the unit vector direction from the telescope location or pass the optional argument position=True when converting from ITRF_xyz.
If you have a telescope with internal alignment and corrective terms such that the communicated values with the mount are in traditional ENU_altaz coordinates, set the location and then call Alignment.set_alignment_enu(). This will make MNT coincide with the present ENU frame and disable corrections.
To understand this class deeply, one needs to understand the different coordinate systems that are used. They are:
- ITRF: International Terrestrial Reference Frame is the common ECEF (Earth-Centered Earth-Fixed) cartesian coordinate frame. This gives a position in (x, y, z) where the coordinates rotate along with Earth, so a point on Earth always has the same coordinates. (0, 0, 0) is the centre of Earth. This is the “base” coordinate frame in this class which everything else is referenced to.
- ENU: East North Up is the local tangent plane coordinate frame and depends on the location of the telescope. The coordinates may be described in their cartesian components (e, n, u) but are more commonly referenced by the two angles ENU altitude (degrees above the horizon) and ENU azimuth (degrees rotation east from north). ENU_altaz is the traditional astronomical coordinate frame seen e.g. in star charts. To transform into this frame the location must be known.
- MNT: Mount is the local coordinate system of the telescope mount. This is another cartesian coordinate where the coordinate axes coincide with the telescope mount axes. In particular, MNT c is the azimuth rotation axis of the gimbal, MNT a is the altitude rotation axis of the gimbal (when the gibals are at the zero position), and MNT b completes the right hand system (pointing along the telescope boresight). The coordinates are commonly expressed as MNT altitude (degrees above the plane normal to the azimuth rotation axis) and MNT azimuth (degrees rotation around the azimuth axis). Traditionally, telescopes are physically aligned such that MNT and ENU coincide, but we relax this harsh constraint via software!
- COM: Commanded is the angles which must be send to the mount to actually end up at the desired MNT_altaz. The nonperpendicularity (Cnp), vertical deflection (Cvd), and altitude zero offset (Alt0) of the physical mount must be corrected for to get better than about 1000 arcseconds pointing. In an ideal mount COM and MNT coincide.
Parameters: - data_folder (pathlib.Path, optional) – The folder for data saving. If None (the default) the folder pypogs/data will be used/created.
- debug_folder (pathlib.Path, optional) – The folder for debug logging. If None (the default) the folder pypogs/debug will be used/created.
-
get_com_altaz_from_enu_altaz(enu_altaz)¶ Transform the given ENU AltAz coordinate to COM AltAz.
Parameters: enu_altaz (numpy array-like) – Size 2 or shape (2,N). Returns: Shape (2,) if single input, shape (2,N) if array input. Return type: numpy.ndarray
-
get_com_altaz_from_itrf_xyz(itrf_xyz, position=False)¶ Transform the given ITRF xyz coordinates to COM AltAz.
Parameters: - itrf_xyz (numpy array-like) – Size 3 or shape (3,N).
- position (bool, optional) – If True, itrf_xyz is treated as the position in ITRF we want to observe, and the ENU AltAz required to see it from our location is returned. If False (the default), itrf_xyz is treated as a vector with the desired direction to point in ITRF.
Returns: Shape (2,) if single input, shape (2,N) if array input.
Return type: numpy.ndarray
-
get_com_altaz_from_mnt_altaz(mnt_altaz)¶ Transform the given MNT AltAz coordinate to COM AltAz.
Parameters: mnt_altaz (numpy array-like) – Size 2 or shape (2,N). Returns: Shape (2,) if single input, shape (2,N) if array input. Return type: numpy.ndarray
-
get_enu_altaz_from_com_altaz(com_altaz)¶ Transform the given COM AltAz coordinate to ENU AltAz.
Parameters: mnt_altaz (numpy array-like) – Size 2 or shape (2,N). Returns: Shape (2,) if single input, shape (2,N) if array input. Return type: numpy.ndarray
-
get_enu_altaz_from_itrf_xyz(itrf_xyz, position=False)¶ Transform the given ITRF xyz coordinates to ENU AltAz.
Parameters: - itrf_xyz (numpy array-like) – Size 3 or shape (3,N).
- position (bool, optional) – If True, itrf_xyz is treated as the position in ITRF we want to observe, and the ENU AltAz required to see it from our location is returned. If False (the default), itrf_xyz is treated as a vector with the desired direction to point in ITRF.
Returns: Shape (2,) if single input, shape (2,N) if array input.
Return type: numpy.ndarray
-
get_enu_altaz_from_mnt_altaz(mnt_altaz)¶ Transform the given MNT AltAz coordinate to ENU AltAz.
Parameters: mnt_altaz (numpy array-like) – Size 2 or shape (2,N). Returns: Shape (2,) if single input, shape (2,N) if array input. Return type: numpy.ndarray
-
get_itrf_relative_from_position(itrf_pos)¶ Get the vector in ITRF pointing from the telescope to the given position. Not normalised.
-
get_itrf_xyz_from_enu_altaz(enu_altaz)¶ Transform the given ENU AltAz coordinate to ITRF xyz. May be numpy array-like.
Parameters: enu_altaz (numpy array-like) – Size 2 or shape (2,N). Returns: Shape (3,) if single input, shape (3,N) if array input. Return type: numpy.ndarray
-
get_itrf_xyz_from_mnt_altaz(mnt_altaz)¶ Transform the given MNT AltAz coordinate to ITRF xyz. May be numpy array-like.
Parameters: mnt_altaz (numpy array-like) – Size 2 or shape (2,N). Returns: Shape (3,) if single input, shape (3,N) if array input. Return type: numpy.ndarray
-
get_location_itrf()¶ numpy.ndarray: Get the location in ITRF x (m), y (m), z (m) as shape (3,) array.
-
get_location_lat_lon_height()¶ tuple of float: Get the location in latitude (deg), longitude (deg), height (m) above ellipsoid.
-
get_mnt_altaz_from_com_altaz(com_altaz)¶ Transform the given COM AltAz coordinate to MNT AltAz.
Parameters: mnt_altaz (numpy array-like) – Size 2 or shape (2,N). Returns: Shape (2,) if single input, shape (2,N) if array input. Return type: numpy.ndarray
-
get_mnt_altaz_from_enu_altaz(enu_altaz)¶ Transform the given ENU AltAz coordinate to MNT AltAz.
Parameters: enu_altaz (numpy array-like) – Size 2 or shape (2,N). Returns: Shape (2,) if single input, shape (2,N) if array input. Return type: numpy.ndarray
-
get_mnt_altaz_from_itrf_xyz(itrf_xyz, position=False)¶ Transform the given ITRF xyz coordinates to MNT AltAz.
Parameters: - itrf_xyz (numpy array-like) – Size 3 or shape (3,N).
- position (bool, optional) – If True, itrf_xyz is treated as the position in ITRF we want to observe, and the ENU AltAz required to see it from our location is returned. If False (the default), itrf_xyz is treated as a vector with the desired direction to point in ITRF.
Returns: Shape (2,) if single input, shape (2,N) if array input.
Return type: numpy.ndarray
-
set_alignment_enu()¶ Set the MNT frame equal to the current ENU frame and zero all correction terms.
Note
If the location is changed after set_alignment_enu() the MNT frame will not be updated automatically to coincide with the new ENU.
-
set_alignment_from_observations(obs_data, alt0=None, Cvd=None, Cnp=None)¶ Use star camera/plate solving observations to set the alignment and mount correction terms.
The observation data must be a list containing eight tuples, each from observing these specific COM AltAz coordinates in order: (40,-135), (60,-135), (60,-45), (40,-45), (40,45), (60,45), (60,135), (40,135). Each tuple in the list must be a 5-tuple with: (Right Ascension (deg), Declination (deg), timestamp (astropy Time), COM Alt (deg), COM Az (deg)) for the measurement. Set Right Ascension and Declination to None if the measurement failed.
This method also solves for the mount correction terms alt0, Cvd, Cnp. If some of these are well known pass the argument to use that value instead of solving for it. You may also pass zeros to disable the corrections.
Parameters: - obs_data (list of tuple) – See specifics above. The correct sequence is generated from System.do_auto_star_alignment().
- alt0 (float, optional) – Altitude offset (deg). If None (default) it will be solved for.
- Cvd (float, optional) – Vertical deflecion coefficient. If None (default) it will be solved for.
- Cnp (float, optional) – Axes nonperpendicularity (deg). If None (default) it will be solved for.
-
set_location_itrf(x, y, z)¶ Set the location via ITRF position x (m), y (m), z (m).
-
set_location_lat_lon(lat, lon, height=0)¶ Set the location via latitude (deg), longitude (deg), height (m, default 0) above ellipsoid.
-
data_folder¶ Get or set the path for data saving. Will create folder if not existing.
Type: pathlib.Path
-
debug_folder¶ Get or set the path for debug logging. Will create folder if not existing.
Type: pathlib.Path
-
is_aligned¶ Returns True if telescope has location.
Type: bool
-
is_located¶ Returns True if telescope has location.
Type: bool
-
class
pypogs.Target¶ Target to track and start and end times.
Two types of targets are supported, Astropy SkyCoord (any deep space object) and Skyfield EarthSatellite (an Earth orbiting satellite).
The target can be set by the property Target.target_object to one of the supported types. It can also be created from the right ascension and declination (for deep sky) or the Two Line Element (TLE) for a satellite. See Target.set_target_from_ra_dec() and Target.set_target_from_tle() respectively.
You may also give a start and end time (e.g. useful for satellite rise and set times) when creating the target or by the method Target.set_start_end_time().
With a target set, get the ITRF_xyz coordinates at your preferred times with Target.get_target_itrf_xyz().
Note
- If the target is a SkyCoord, the ITRF coordinates will be a unit vector in the direction of the target.
- If the target is an EarthSatellite, the ITRF coordinates will be an absolute position (in metres) from the centre of Earth to the satellite.
-
clear_start_end_time()¶ Set both start and end time to None.
-
get_short_string()¶ Get a short descriptive string of the target.
Returns: str
-
get_target_itrf_xyz(times=None)¶ Get the ITRF_xyz position vector from the centre of Earth to the EarthSatellite (in metres) or the ITRF_xyz unit vector to the SkyCoord.
Parameters: times (Astropy Time) – Single or array Time for when to calculate the vector. Returns: Shape (3,) if single input, shape (3,N) if array input. Return type: numpy.ndarray
-
get_tle_raw()¶ Get the TLE of the target.
Returns: tuple of str Raises: AssertionError– If the current target is not a Skyfield EarthSatellite.
-
set_start_end_time(start_time=None, end_time=None)¶ Set the start and end times.
Parameters: - start_time (astropy Time, optional) – The start time to set.
- end_time (astropy Time, optional) – The end time to set.
-
set_target_deep_by_name(name, start_time=None, end_time=None)¶ Use Astropy name lookup for setting a SkyCoord deep sky target.
Parameters: - name (str) – Name to search for.
- start_time (astropy Time, optional) – The start time to set.
- end_time (astropy Time, optional) – The end time to set.
-
set_target_from_ra_dec(ra, dec, start_time=None, end_time=None)¶ Create an Astropy SkyCoord and set as the target.
Parameters: - ra (float) – Right ascension in decimal degrees.
- dec (float) – Declination in decimal degrees.
- start_time (astropy Time, optional) – The start time to set.
- end_time (astropy Time, optional) – The end time to set.
-
set_target_from_tle(tle, start_time=None, end_time=None)¶ Create a Skyfield EarthSatellite and set as the target.
Parameters: - tle (tuple or str) – 2-tuple with the two TLE rows or a string with newline character between lines.
- start_time (astropy Time, optional) – The start time to set.
- end_time (astropy Time, optional) – The end time to set.
-
end_time¶ Get or set the tracking end time.
Type: astropy Time or None
-
has_target¶ Returns True if a target is set.
Type: bool
-
start_time¶ Get or set the tracking start time.
Type: astropy Time or None
-
target_object¶ Get or set the target object.
Type: Astropy SkyCoord or Skyfield EarthSatellite or None
Feedback and tracking algorithms¶
pypogs.ControlLoopThreadis the main control loop for pypyogs. It manages the transition- logic and feedback loop.
pypogs.TrackingThreadis the target tracking loop. One is created for each camera to read arriving images. It provides the current error etc. to the pypogs.ControlLoopThread.pypogs.SpotTrackeris attached to each pypogs.TrackingThread and implements the spot detection and tracking.
This is Free and Open-Source Software originally written by Gustav Pettersson at ESA.
- License:
Copyright 2019 the European Space Agency
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
-
class
pypogs.ControlLoopThread(parent, data_folder=None, debug_folder=None)¶ Run a control loop for satellite tracking.
This thread holds a reference to the parent pypogs.System instance and takes control of all devices. Set up this thread with your desired tracking parameters (such as gains, frequency, feedback maximum rates, etc.) and then call ControlLoopThread.start(). The thread runs with a maximum frequency defined by max_frequency, it defaults to 5 Hz. The target (and start and end times if desired) must be defined in the parent.target object.
Upon reaching a transition criteria, the tracking mode will be changed (e.g. open-loop to closed-loop) automatically either to a “better” or “worse” mode. The available modes and their parameters are defined below:
OL: Open-loop control. The mount angles are read out and the control signal is based on the difference between the mount angles and the target angles. Gains OL_P (proportional, default 1.0), OL_I (integral, default 10 seconds), and rate limit OL_speed_limit (default 3600 arcsec/s) apply.
CCL: Coarse closed-loop control. The measured position from the coarse camera, returned by parent.coarse_track_thread.track_x_y, is used (scaled to degrees) for the control signal. Gains CCL_P (proportional, default .5), CCL_I (integral, default 10 s), and rate limit CCL_speed_limit (default 180 arcsec/s) apply. The transition OL > CCL requires the following:
- parent.coarse_track_thread exists and has a track
- aforementioned track standard deviation is below CCL_transition_th (default 100 arcsec)
FCL: Fine closed-loop control. The measured position from the fine camera, returned by parent.fine_track_thread.track_x_y, is used (scaled to degrees) for the control signal. Gains FCL_P (proportional, default 1.0), FCL_I (integral, default 10 s), and rate limit FCL_speed_limit (default 180 arcsec/s) apply. The transition CCL > FCL requires the following:
- parent.fine_track_thread exists and has a track
- aforementioned track standard deviation is below FCL_transition_th (default 30 arcsec)
CTFSP: Coarse-to-fine spiral acquisition. Only available if CTFSP_enable is set to True. (defaults to False). It is recommended to use this only to automatically find the intercamera alignment, and then disable again for fastest possible acquisition. Enabling CTFSP has several effects:
- When in CCL, the transition CCL > FCL will not occur as described above. Instead, when in CCL and the parent.coarse_track_thread.rms_error falls below CTFSP_transition_th (default 20 arcsec), the transition CCL > CTFSP occurs.
- When in CTFSP, CCL tracking parameters are used. If CTFSP_zero_integral = True is set (defaults to False) the integral term is set to zero.
- When in CTFSP, the parent.coarse_track_thread.spot_tracker goal is supplemented by the goal offset with a spiral which has arms spaced by CTFSP_spacing (default 100 arcsec), at a rate of CTFSP_speed (default 50 arcsec/s), until a radius CTFSP_max_radius (default 500 arcsec) is reached, upon which the mode is changed back to CCL and the spiral offset is deleted.
- When in CTFSP, the transition CTFSP > FCL happens immediately when parent.fine_track_thread exists and has a track.
- When in FCL, if CTFSP_auto_update_CCL_goal = True, the parent.coarse_track_thread.spot_tracker goal is updated with the current track position, effectively calibrating the intercamera alignment. This only happens when parent.fine_track_thread.rms_error is below CTFSP_auto_update_CCL_goal_th (default 10 arcsec). If CTFSP_disable_after_goal_update is True (the default), CTFSP mode will be disabled automatically after successful alignment.
The availability of each mode can be controlled by setting CCL_enable, FCL_enable, CTFSP_enable to True or False, OL mode may not be disabled.
Note
Integral windup protection is achieved by two means:
- The integral term is reset if the control signal desired is greater than the speed limit (disable by reset_integral_if_saturated=False).
- The integral term may grow (in magnitude) by a maximum value of integral_max_add (default 36 arcsec) per loop. (It may shrink by a maximum value of integral_max_subtract, default 360 arcsec).
Tracking data is saved to a .csv file in the data_folder. The filenames are auto-generated with the start time (as an ISO timestamp) and _ControlLoopThread.csv.
Parameters: - parent (pypogs.System) – The System to control.
- data_folder (pathlib.Path, optional) – The folder for data saving. If None (the default) the folder pypogs/data will be used/created.
- debug_folder (pathlib.Path, optional) – The folder for debug logging. If None (the default) the folder pypogs/debug will be used/created.
-
start()¶ Starts the control loop in a background thread. Must have target and be aligned, located, and initialised.
-
stop()¶ Stop the thread.
-
CCL_I¶ Get or set Coarse Closed-Loop integral time (1/gain) in seconds.
Type: float
-
CCL_P¶ Get or set Coarse Closed-Loop proportional gain.
Type: float
-
CCL_enable¶ Get or set if Coarse Closed-Loop is allowed.
Type: bool
-
CCL_speed_limit¶ Get or set Coarse Closed-Loop speed limit (arcsec per second).
Type: float
-
CCL_transition_th¶ Get or set the coarse track SD required to transition OL to CCL (arcsec).
Type: float
-
CTFSP_auto_update_CCL_goal¶ Get or set if CTFSP should update the CCL goal (i.e. save the alignment).
Type: bool
-
CTFSP_auto_update_CCL_goal_th¶ Get or set the required FCL RMSE to auto update the CCL goal in arcseconds.
Type: float
-
CTFSP_delay¶ Get or set the delay from starting CTFSP mode until spiraling begins in seconds.
Type: float
-
CTFSP_disable_after_goal_update¶ Get or set if CTFSP should be automatically disabled when the alignment is finished.
Type: bool
-
CTFSP_disable_integral¶ Get or set if CTFSP should disable (zero) the integral term.
Type: bool
-
CTFSP_enable¶ Get or set if Coarse-to-Fine Spiral auto-alignment is allowed. Disabled by default.
It is recommended to only enable this when necessary for alignment, and then disable for faster acquisitions.
Type: bool
-
CTFSP_max_radius¶ Get or set the maximum (reset) radius for Coarse-to-Fine Spiral auto-alignment in arcseconds.
Type: float
-
CTFSP_ramp¶ Get or set the CTFSP speed ramp up in seconds.
Type: float
-
CTFSP_spacing¶ Get or set the spacing between arms in Coarse-to-Fine Spiral auto-alignment in arcseconds.
Type: float
-
CTFSP_speed¶ Get or set the speed in Coarse-to-Fine Spiral auto-alignment in arcsec per second.
Type: float
-
CTFSP_transition_th¶ Get or set the CCL RMSE required to start Coarse-to-Fine Spiral auto-alignment in arcseconds.
Type: float
-
FCL_I¶ Get or set Fine Closed-Loop integral time (1/gain) in seconds.
Type: float
-
FCL_P¶ Get or set Fine Closed-Loop proportional gain.
Type: float
-
FCL_enable¶ Get or set if Fine Closed-Loop is allowed.
Type: bool
-
FCL_speed_limit¶ Get or set Fine Closed-Loop speed limit (arcsec per second).
Type: float
-
FCL_transition_th¶ Get or set the fine track SD required to transition OL to CCL (arcsec).
Type: float
-
OL_I¶ Get or set Open-Loop integral time (1/gain) in seconds.
Type: float
-
OL_P¶ Get or set Open-Loop proportional gain.
Type: float
-
OL_goal_offset_x_y¶ Get or set the open-loop goal offset (arcseconds).
Type: tuple of float
-
OL_goal_x_y¶ Get or set the open-loop goal (arcseconds).
Type: tuple of float
-
OL_speed_limit¶ Get or set Open-Loop speed limit (arcsec per second).
Type: float
-
available_properties¶ Get the available tracking parameters (e.g. gains).
Type: tuple of str
-
data_folder¶ Get or set the path for data saving. Will create folder if not existing.
Type: pathlib.Path
-
debug_folder¶ Get or set the path for debug logging. Will create folder if not existing.
Type: pathlib.Path
-
integral_max_add¶ Get or set the maximum error (arcseconds) allowed to be added (in magnitude) to the integral.
Type: float
-
integral_max_subtract¶ Get or set the maximum error (arcseconds) allowed to be subtracted (in magnitude) to the integral.
Type: float
-
integral_min_rate¶ Get or set the minimum target rate (arcsec per second) required to enable the integral term.
Type: float
-
is_running¶ Returns True if control thread is running.
Type: bool
-
max_frequency¶ Get or set the maximum frequency in hertz.
Type: float or None
-
name¶ Get or set a name for the thread. Default ‘ControlLoopThread’.
Type: str
-
reset_integral_if_saturated¶ Get or set if the integral term should be reset (zeroed) if control output is at the speed limit.
Type: bool
-
state_cache¶ Get dictionary with last cached state.
Type: dict
-
class
pypogs.TrackingThread(camera=None, spot_tracker=None, name=None, image_folder=None, img_save_frequency=1.0, data_folder=None, debug_folder=None)¶ Run a thread to track a point on a camera.
This thread should be given a pypogs.Camera object and a pypogs.SpotTracker object. An empty SpotTracker will be created and attached if not given as input. The camera acquisition properties are set directly in the Camera and the spot detection parameters are set directly in the SpotTracker. They may be accessed (and set) by TrackingThread.camera and TrackingThread.spot_tracker respectively. If an image_folder is given to the TrackingThread the images read are saved (as .tiff) to the path, the maximum saving frequency can be limited by setting img_save_frequency (default 1 Hz) to avoid saving massive amounts of data.
The TrackingThread will update on every image event received from the Camera, so the frequency is controlled by the Camera.frame_rate. If the thread is already busy, the image will be ignored and a frame drop warning logged.
After setting up and starting the thread, read TrackingThread.track_alt_az to get the last measured position (error from the goal) of the satellite being tracked. See note below for coordinate definition. The tracker keeps an estimate of the mean and standard deviation of position, signal sum, and signal area which may be used to keep the track. Please read the SpotTracker (in this module) documentation carefully to set up the spot detection and tracking parameters.
The TrackingThread can also pass feed-forward information to the SpotTracker to compensate for the movement of the telescope on the position of the target. The SpotTracker mean position will be moved at a speed of TrackingThread.feedforward_rate (tuple of floats; arcsec per second) but only if the step change is greater than TrackingThread.feedforward_threshold (default 10 arcsec). It is also possible to move the SpotTracker goal offset by a speed defined by TrackingThread.goal_offset_rate (arcsec per second) to follow a moving goal offset (i.e. during spiralling).
Note
The TrackingThread has two distinct coordinate systems. The alt_az system measures positions relative to the defined goal and is (1) derotated with the Camera.rotation parameter and (2) switched (i.e. x and y becomes az and alt respectively) in order to coincide with the control loop’s alt_az system compared to the SpotTracker x and y positions. However, many parameters (e.g. the goal and anything appended by _absolute) are in the same x_y coordinates of the SpotTracker without any derotation. All distance measurements are scaled by the Camera.plate_scale such that the outputs are directly in arcseconds.
Parameters: - camera (pypogs.Camera, optional) – The Camera object to read from.
- spot_tracker (pypogs.SpotTracker, optional) – The SpotTracker used to detect and track from the images. If this is not supplied an empty SpotTracker will be created.
- name (str, optional) – A name for the TrackingThread.
- image_folder (pathlib.Path, optional) – The folder for data saving. If None (the default) images will not be saved.
- img_save_frequency (float, optional) – Maximum frequency for saving images. If None every image will be saved (this may use a lot of disk space and computer resources).
- data_folder (pathlib.Path, optional) – The folder for data saving. If None (the default) the folder pypogs/data will be used/created.
- debug_folder (pathlib.Path, optional) – The folder for debug logging. If None (the default) the folder pypogs/debug will be used/created.
Example
# Create a camera instance (see pypogs.camera.Camera) cam = pypogs.Camera(model='ptgrey', identity='18285284', name='CoarseCam') # Create a TrackingThread instance tt = pypogs.TrackingThread(camera=cam, name='CoarseTrackThread') # Set up tracking parameters (see SpotTracker in this module for details) tt.spot_tracker.max_search_radius = 500 tt.spot_tracker.min_search_radius = 100 tt.spot_tracker.position_sigma = 5 # (Optional) set up a directory for image saving at .5 Hz tt.image_folder = Path('./tracking_images') tt.img_save_frequency = .5 # Start the tracker tt.start() # Wait for a while time.sleep(2) # Read the position print(tt.track_alt_az) # Stop the tracker tt.stop() # Deinitialise the camera cam.deinitialize()
-
start()¶ Starts the tracking in a background thread. Must have Camera and SpotTracker.
-
stop()¶ Stop the thread.
-
auto_acquire_track¶ Get or set if tracks should be automatically acquired.
Type: bool
-
camera¶ Get or set the Camera object for the tracking thread.
Type: pypogs.Camera
-
data_folder¶ Get or set the path for data saving. Will create folder if not existing.
Type: pathlib.Path
-
debug_folder¶ Get or set the path for debug logging. Will create folder if not existing.
Type: pathlib.Path
-
feedforward_rate¶ Get or set the speed (arcsec/sec) with which the SpotTracker mean position should be moved.
Type: “tuple of float
-
feedforward_threshold¶ Get or set the minimum feedforward step (arcsec).
Type: float
-
frequency¶ Get or set the target loop frequency in Hz. Must be greater than zero.
Type: float
-
frequency_actual¶ Get the actual loop frequency (for the last update) in Hz.
Type: float
-
goal_offset_rate¶ Get or set the speed (arcsec/sec) with which the SpotTracker goal offset position should be moved.
Type: “tuple of float
-
goal_offset_x_y¶ Set temporary (cleared on lost track) offset from the defined goal.
Type: tuple of float
-
goal_x_y¶ Get or set the goal (in absolute x and y relative to image centre) the track is measured against.
Defaults to (0,0)
Type: tuple of float
-
has_track¶ Returns true if there is a track. Returns None if not running.
Type: bool
-
image_folder¶ Get or set the path for saving images. Will create folder if not existing.
The saving frequency can be limited by setting TrackingThread.img_save_frequency
Type: pathlib.Path
-
img_save_frequency¶ Get or set the maximum frequency to save images. If None (the default) all images will be saved.
Type: float
-
is_running¶ Returns True if the thread is running.
Type: bool
-
mean_alt_az¶ Get the mean (smoothed) alt and az position relative to the goal (derotated). None if no track.
Smoothing determined by SpotTracker.smoothing_parameter
Type: tuple of float
-
mean_x_y_absolute¶ Get the absolute mean x and y position (relative to image centre). None if no track.
Smoothing determined by SpotTracker.smoothing_parameter
Type: tuple of float
-
name¶ Get or set a name for the thread
Type: str
-
pos_search_rad¶ Get or set the current search radius.
Type: float
-
pos_search_x_y¶ Get or set the current search position. Will also set search radius to max_search_radius.
Type: tuple of float
-
rms_error¶ Get the total smoothed RMS error relative to the goal. None if no track.
Smoothing determined by SpotTracker.rmse_smoothing_parameter
Type: float
-
spot_tracker¶ Get or set the SpotTracker object.
Type: SpotTracker
-
track_alt_az¶ Get the latest alt and az position relative to the goal (derotated). None if no track or latest update failed.
Type: tuple of float
-
track_sd¶ Get the total standard deviation of the track. None if no track.
Type: float
-
track_x_y_absolute¶ Get the latest absolute x and y position (relative to image centre). None if no track or latest update failed.
Type: tuple of float
-
class
pypogs.SpotTracker(name=None, data_folder=None, debug_folder=None)¶ Class for detecting and tracking spots (i.e. satellites) from a stream or series of images.
Typically this class is not used directly, but attached to a TrackingThread (in this module).
The tracker works by keeping an estimate of the mean and standard deviation (SD) of position, signal sum, and signal area and searching for spots which fall within sigma SDs away from the mean. By default the position and signal sum are used with 5-sigma limits to keep the track. To change the limits see e.g SpotTracker.sigma_position; setting any of these to None will disable using that parameter to discern tracks. If more than one spot falls within the track limits the spot closest in position to the mean position will be used. It is mandatory to set a reasonable maximum search radius (default 1000 arcsec). This will also be used to initialise the position SD estimate. It is recommended to set a reasonable minimum search radius (default None) to at least one pixel’s width. It is also possible to set maximum and minimum limits on the signal sum and area SD estimates, otherwise it will be limited to between zero and the mean value of the respective signal.
You may define a goal position against which the output should be measured. By default this is (0,0) which is the centre of the image. The units for all positions will be in the units you provide via plate_scale (default 1 arcsec/pixel) to SpotTracker.update_from_image(). The goal and properties ending in _absolute are always measured relative to image centre.
The estimators use exponentially weighted moving mean and variance. SpotTracker.smoothing_parameter (default 10) defines how samples are weighted and roughly corresponds to the number of samples to average.
If SpotTracker.success_to_start_track (default 3) spots are found in a row a new track will be established. If SpotTracker.fails_to_drop_track (default 10) failures occur in a row the track is dropped. If an update fails during tracking the tracking parameters will be penalised by SpotTracker.failure_sd_penalty (default 25%) to account for drift in the mean.
A performance metric, the root-mean-squared error (RMSE), is provided which measures the position error relative to the goal (instead of the mean, as the SD does). By default this uses SpotTracker.smoothing_parameter with exponential averaging, but SpotTracker.rmse_smoothing_parameter may be defined to control this individually.
You may elect to use your own spot detection system and directly update the tracker with position with SpotTracker.update_from_observation(). If this is done, the tracker will accept the updated position without checking if it falls within the defined tracking range.
Note
The SpotTracker uses position information in (x,y) coordinates where (0,0) is the centre of the image, x increases to the right and y increases upwards. When images are used to update the tracker (via SpotTracker.update_from_image()) a plate scale should be supplied such that the output is in useful units, i.e. arcseconds.
Parameters: - name (str, optional) – A name for the SpotTracker.
- data_folder (pathlib.Path, optional) – The folder for data saving. If None (the default) the folder pypogs/data will be used/created. TODO! Currently does nothing
- debug_folder (pathlib.Path, optional) – The folder for debug logging. If None (the default) the folder pypogs/debug will be used/created.
-
change_mean_relative(dx, dy)¶ Add an offset to SpotTracker.mean_x_y positions. Useful for feed-forward of control signals.
Parameters: - dx (float) – Amount to add in x.
- dy (float) – Amount to add in y.
-
penalize_track(percentage=25)¶ Scale the search radius, sum SD, and areaSD used for tracking by the given percentage.
-
update_from_image(img, plate_scale=1)¶ Update the SpotTracker from a new image.
Will use tetra3.extract_star_positions() to find spots in the image with the extraction parameters which have been set in the SpotTracker. A spot falling within the current tracking range will be searched for and, if found, the tracker will be updated. If not found, the estimator SD will be penalised by SpotTracker.failure_penalty (default 10%).
Parameters: - img (numpy.ndarray) – The image to update with.
- plate_scale (float, optional) – Image plate scale (typically arcseconds per pixel).
- to 1 (Defaults) –
Returns: - True if has_track and the provided image was successfully used to update the
tracker.
Return type: bool
-
update_from_observation(x, y, summ, area)¶ Update with new observed data for the track. Will not check any tracking validity (see update_from_image()).
If any of x, y, summ, area are None it will be interpreted as a failed track cycle and nothing updated.
-
active_crop_enable¶ Get or set if active cropping should be used.
If enabled and the SpotTracker has a track, the incoming image will be automatically cropped to only contain the search region (to speed up computation). This may have a slight effect on background subtraction and SD estimators.
Type: bool
-
active_crop_padding¶ Get or set the number of pixels to pad on each side of the search region for active cropping.
Type: int
-
area_max_sd¶ Get or set the maximum (and initialisation) for signal area standard deviation estimator.
Type: float
-
area_min_sd¶ Get or set the minimum for signal area standard deviation estimator.
Type: float
-
area_sigma¶ Get or set the signal area standard deviation threshold.
Set None to disable tracking in spot area.
Type: float or None
-
auto_acquire_track¶ Get or set if tracks should be automatically acquired.
Type: bool
-
available_properties¶ Get the available tracking parameters (e.g. gains).
Type: tuple of str
-
bg_subtract_mode¶ Get or set the background subtraction mode.
- Allowed values:
- global_median: Subtract the global median value.
- local_median: Subtract the median in a SpotTracker.filtsize wide window around each pixel.
- global_mean: Subtract the global mean value.
- local_mean: Subtract the median in a SpotTracker.filtsize wide window around each pixel.
Type: str
-
binary_open¶ Get or set if binary opening (with 1-connected structure element) should be used to clean the thresholded image.
Type: bool
-
centroid_window¶ Get or set the second round centroiding window size.
If None, the centroid will be calculated from the spot region in the thresholded image. If set, a square window of the given size will be used to recalculate the centroid.
Type: int or None
-
crop¶ Get or set the cropping of incoming images. If None no cropping is applied.
- Can be set in two ways:
- 2-tuple: The image is cropped to this (width, height) number of pixels in the centre.
- 4-tuple: The image is cropped to this (width, height, offset_right, offset_down)
Type: tuple of int or or None
-
data_folder¶ Get or set the path for data saving. Will create folder if not existing.
Type: pathlib.Path
-
debug_folder¶ Get or set the path for debug logging. Will create folder if not existing.
Type: pathlib.Path
-
downsample¶ Get or set downsampling (binning) of incoming images.
Note
If a received image’s shape can not be divided by this factor an error will be thrown!
Type: int or None
-
fails_to_drop¶ Get or set the number of successive failed extractions after which a track is dropped.
Type: int
-
failure_sd_penalty¶ Get or set the penalty in percent to give to the standard deviation on a failed extraction.
Type: float or None
-
filtsize¶ Get or set the filter size (width and height) used for local background subtraction and SD estimate.
Type: int
-
goal_offset_x_y¶ Get or set the tracking offset from the goal.
Type: tuple of float
-
goal_x_y¶ Get or set the tracking goal in absolute x and y.
Type: tuple of float
-
has_track¶ Returns True if there currently is a track.
Type: bool
-
image_sigma_th¶ Get or set the number of standard deviations of image noise used for image thresholding.
If SpotTracker.image_th is set (not None), this value will be ignored.
Type: float
-
image_th¶ Get or set the image thresholding value.
If this value is set (not None), the image standard deviation estimator and image_sigma_th will be ignored and the provided value used directly.
Type: int or None
-
max_search_radius¶ Get or set the maximum search radius. Also sets initial standard deviation. Must be set; default 1000.
Type: float
-
mean_area¶ Get the mean (smoothed) signal area. None if no track.
Smoothing determined by SpotTracker.smoothing_parameter
Type: float
-
mean_sum¶ Get the mean (smoothed) signal sum. None if no track.
Smoothing determined by SpotTracker.smoothing_parameter
Type: float
-
mean_x_y¶ Get the mean (smoothed) x and y position relative to the goal. None if no track.
Smoothing determined by SpotTracker.smoothing_parameter
Type: tuple of float
-
mean_x_y_absolute¶ Get the absolute mean x and y position (relative to image centre). None if no track.
Smoothing determined by SpotTracker.smoothing_parameter
Type: tuple of float
-
min_search_radius¶ Get or set the minimum search radius. Also sets initial standard deviation.
Type: float or None
-
name¶ Get or set a name for the SpotTracker
Type: str
-
pos_search_rad¶ Get or set the current search radius.
Type: float
-
pos_search_x_y¶ Get or set the x y position (absolute) where we search for track.
Will clear tracker if set.
Type: tuple of float or None
-
position_sigma¶ Get or set the number of standard deviations the search radius should be. Set None to disable tracking in position.
Type: float or None
-
rms_error¶ Get the total smoothed RMS error relative to the goal. None if no track.
Smoothing determined by SpotTracker.rmse_smoothing_parameter
Type: float
-
rmse_smoothing_parameter¶ Get or set the smoothing parameter. It roughly corresponds to the number of samples to average.
This parameter is used for estimating the RMS Error relative to the goal position.
If not set, SpotTracker.smoothing_parameter is used
Exponential smoothing is used. rmse_smoothing_parameter is the inverse of ‘alpha’. Each smoothed value s is defined from the measurements x by:
rmse[n]**2 = (1-alpha) * (rmse[n-1]**2 + alpha*((x[n]-goal_x)**2 + (y[n]-goal_y)**2))rmse[0]**2 = (x[0]-goal_x)**2 + (y[0]-goal_y)**2
Type: float
-
sd_area¶ Get the signal area standard deviation. None if no track.
Smoothing determined by SpotTracker.smoothing_parameter
Type: float
-
sd_sum¶ Get the signal sum standard deviation. None if no track.
Smoothing determined by SpotTracker.smoothing_parameter
Type: float
-
sigma_mode¶ Get or set the mode used to calculate the image sigma (standard deviation) after background subtraction.
This value multiplied with SpotTracker.image_sigma_th will be used to threshold the image. If SpotTracker.image_th is not None (default is None), that will be used as the threshold instead, and sigma_mode and image_sigma_th have no effect.
- Allowed values:
- global_median_abs: Use the median of the absolute value of the image and multiply by 1.48.
- local_median_abs: As above, but in a SpotTracker.filtsize wide window around each pixel.
- global_root_square: Use the root mean square of the image.
- local_root_square: As above, but in a SpotTracker.filtsize wide window around each pixel.
Type: str
-
smoothing_parameter¶ Get or set the smoothing parameter. It roughly corresponds to the number of samples to average.
This parameter is used for estimating the mean and standard deviation of the position, sum, and area.
The total RMS Error smoothing can be set independently by SpotTracker.rmse_smoothing_parameter. If not, this value is used there as well.
Exponential smoothing is used. smoothing_parameter is the inverse of ‘alpha’. Each smoothed value s is defined from the measurements x by:
mean[n] = alpha*x[n] + (1-alpha)*mean[n-1]mean[0] = x[0]sd[n]**2 = (1-alpha)*(sd[n-1]**2 + alpha*(x[n]-mean[n-1])**2)
The SD estimator is initialised to given maximum values max_search_radius, sum_max_sd, area_max_sd if available, otherwise it is set to the magnitude of the first given value.
Type: int or float
-
spot_max_area¶ Get or set the maximum number of pixels in the thresholded image which constitute a star.
Type: int or None
-
spot_max_axis_ratio¶ Get or set the maximum ratio of major to minor axes for a region to constitute a star.
The major and minor axes are calculated from second moments.
Type: float or None
-
spot_max_sum¶ Get or set the maximum sum of values in a region to constitute a star.
Type: float or None
-
spot_min_area¶ Get or set the minimum number of pixels in the thresholded image which constitute a star.
Type: int or None
-
spot_min_sum¶ Get or set the minimum sum of values in a region to constitute a star.
Type: float or None
-
successes_to_track¶ Get or set the number of successive successful extractions to start a track.
Type: int
-
sum_max_sd¶ Get or set the maximum (and initialisation) for signal sum standard deviation estimator.
Type: float or None
-
sum_min_sd¶ Get or set the minimum for signal sum standard deviation estimator.
Type: float or None
-
sum_sigma¶ Get or set the sigmal sum standard deviation threshold. Set None to disable tracking in sum.
Type: float or None
-
track_area¶ Get the latest signal area. None if no track or latest update failed.
Type: float
-
track_sd¶ Get the total standard deviation of the track. None if no track.
Type: float
-
track_sum¶ Get the latest signal sum. None if no track or latest update failed.
Type: float
-
track_x_y¶ Get the latest x and y position relative to the goal. None if no track or latest update failed.
Type: tuple of float
-
track_x_y_absolute¶ Get the latest absolute x and y position (relative to image centre). None if no track or latest update failed.
Type: tuple of float
Hardware interfaces¶
- Current hardware support:
pypogs.Camera: ‘ptgrey’ for FLIR (formerly Point Grey) machine vision cameras. Requires Spinnaker API and PySpin, see the installation instructions. Tested with Blackfly S USB3 model BFS-U3-31S4M.pypogs.Mount: ‘celestron’ for Celestron, Orion and SkyWatcher telescopes (using NexStar serial protocol). No additional packages required. Tested with Celestron model CPC800.pypogs.Receiver: ‘ni_daq’ for National Instruments DAQ data acquisition cards. Requires NI-DAQmx API and nidaqmx, see the installation instructions. Tested with NI DAQ model USB-6211.
This is Free and Open-Source Software originally written by Gustav Pettersson at ESA.
- License:
Copyright 2019 the European Space Agency
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
-
class
pypogs.Camera(model=None, identity=None, name=None, auto_init=True, debug_folder=None)¶ Control acquisition and receive images from a camera.
To initialise a Camera a model (determines hardware interface) and identity (identifying the specific device) must be given. If both are given to the constructor the Camera will be initialised immediately (unless auto_init=False is passed). Manually initialise with a call to Camera.initialize(); release hardware with a call to Camera.deinitialize().
After the Camera is initialised, acquisition properties (e.g. exposure_time and frame_rate) may be set and images received. The Camera also supports event-driven acquisition, see Camera.add_event_callback(), where new images are automatically passed on to the desired functions.
Parameters: - model (str, optional) – The model used to determine the correct hardware API. Supported: ‘ptgrey’ for PointGrey/FLIR Machine Vision cameras (using Spinnaker and PySpin).
- identity (str, optional) – String identifying the device. For model ptgrey this is ‘serial number’ as a string.
- name (str, optional) – Name for the device.
- auto_init (bool, optional) – If both model and identity are given when creating the Camera and auto_init is True (the default), Camera.initialize() will be called after creation.
- debug_folder (pathlib.Path, optional) – The folder for debug logging. If None (the default) the folder pypogs/debug will be used/created.
Example
# Create instance and set parameters (will auto initialise) cam = pypogs.Camera(model='ptgrey', identity='18285284', name='CoarseCam') cam.gain = 0 #decibel cam.exposure_time = 100 #milliseconds cam.frame_rate_auto = True # Start acquisition cam.start() # Wait for a while time.sleep(2) # Read the latest image img = cam.get_latest_image() # Stop the acquisition cam.stop() # Release the hardware cam.deinitialize()
-
add_event_callback(method)¶ Add a method to be called when a new image shows up.
The method should have the signature (image, timestamp, *args, **kwargs) where:
- image (numpy.ndarray): The image data as a 2D numpy array.
- timestamp (datetime.datetime): UTC timestamp when the image event occurred (i.e. when the capture finished).
- *args, **kwargs should be allowed for forward compatibility.
The callback should not be used for computations, make sure the method returns as fast as possible.
Parameters: method – The method to be called, with signature (image, timestamp, *args, **kwargs).
-
deinitialize()¶ De-initialise the device and release hardware resources. Will stop the acquisition if it is running.
-
get_latest_image()¶ Get latest image in the cache immediately. Camera must be running.
Returns: 2d array with image data. Return type: numpy.ndarray
-
get_new_image(timeout=10)¶ Get an image guaranteed to be started after calling this method. Camera does not have to be running.
Parameters: timeout (float) – Maximum time (seconds) to wait for the image before raising TimeoutError. Returns: 2d array with image data. Return type: numpy.ndarray
-
get_next_image(timeout=10)¶ Get the next image to be completed. Camera does not have to be running.
Parameters: timeout (float) – Maximum time (seconds) to wait for the image before raising TimeoutError. Returns: 2d array with image data. Return type: numpy.ndarray
-
initialize()¶ Initialise (make ready to start) the device. The model and identity must be defined.
-
remove_event_callback(method)¶ Remove method from event callbacks.
-
start()¶ Start the acquisition. Device must be initialised.
-
stop()¶ Stop the acquisition.
-
available_properties¶ Get all the available properties (settings) supported by this device.
Type: tuple of str
-
binning¶ Number of pixels to bin in each dimension (e.g. 2 gives 2x2 binning). Bins by summing.
Setting will stop and restart camera if running. Will scale size_readout to show the same sensor area.
Type: int
-
debug_folder¶ Get or set the path for debug logging. Will create folder if not existing.
Type: pathlib.Path
-
exposure_time¶ Get or set the camera expsure time in ms. Will set auto exposure time to False.
Type: float
-
exposure_time_auto¶ Get or set automatic exposure time. If True the exposure time will be continuously updated.
Type: bool
-
exposure_time_limit¶ Get the minimum and maximum expsure time in ms supported.
Type: tuple of float
-
flip_x¶ Get or set if the image X-axis should be flipped. Default is False.
Type: bool
-
flip_y¶ Get or set if the image Y-axis should be flipped. Default is False.
Type: bool
-
frame_rate¶ Get or set the camera frame rate in Hz. Will set auto frame rate to False.
Type: float
-
frame_rate_auto¶ Get or set automatic frame rate. If True camera will run as fast as possible.
Type: bool
-
frame_rate_limit¶ Get the minimum and maximum frame rate in Hz supported.
Type: tuple of float
-
gain¶ Get or set the camera gain in dB. Will set auto frame rate to False.
Type: float
-
gain_auto¶ Get or set automatic gain. If True the gain will be continuously updated.
Type: bool
-
gain_limit¶ Get the minimum and maximum gain in dB supported.
Type: tuple of float
-
identity¶ Get or set the device and/or input. Model must be defined first.
- For model ptgrey this is the serial number as a string
- Must set before initialising the device and may not be changed for an initialised device.
Type: str
-
is_init¶ True if the device is initialised (and therefore ready to start).
Type: bool
-
is_running¶ True if device is currently acquiring data.
Type: bool
-
model¶ Get or set the device model.
- Supported:
- ‘ptgrey’ for FLIR/Point Grey cameras (using Spinnaker/PySpin SDKs).
- This will determine which hardware API that is used.
- Must set before initialising the device and may not be changed for an initialised device.
Type: str
-
name¶ Get or set the name.
Type: str
-
plate_scale¶ Get or set the plate scale of the Camera in arcsec per pixel.
This will not affect anything in this class but is used elsewhere. Set this to the physical pixel plate scale before any binning. When getting the plate scale it will be scaled by the binning factor.
Type: float
-
rotate_90¶ Get or set how many times the image should be rotated by 90 degrees. Applied after flip_x and flip_y.
Type: int
-
rotation¶ Get or set the camera rotation relative to the horizon in degrees.
This does not affect the received images, but is used elsewhere. Use rotate_90 first to keep this rotation small.
Type: float
-
size_max¶ Get the maximum allowed readout size (width, height) in pixels.
Type: tuple of int
-
size_readout¶ Get or set the number of pixels read out (width, height). Will automatically center.
This applies after binning, i.e. this is the size the output image will be.
Setting will stop and restart camera if running.
Type: tuple of int
-
class
pypogs.Mount(model=None, identity=None, name=None, auto_init=True, debug_folder=None)¶ Control a telescope gimbal mount.
To initialise a Mount a model (determines hardware interface) and identity (identifying the specific device) must be given. If both are given to the constructor the Mount will be initialised immediately (unless auto_init=False is passed). Manually initialise with a call to Mount.initialize(); release hardware with a call to Mount.deinitialize().
After the Mount is initialised, the gimbal angles and rates may be read and commanded. Several properties (e.g maximum angles and rates) may be set.
Parameters: - model (str, optional) – The model used to determine the the hardware control interface. Supported: ‘celestron’ for Celestron NexStar and Orion/SkyWatcher SynScan (all the same) hand controller communication over serial.
- identity (str or int, optional) – String or int identifying the device. For model celestron this can either be a string with the serial port (e.g. ‘COM3’ on Windows or ‘/dev/ttyUSB0’ on Linux) or an int with the index in the list of available ports to use (e.g. identity=0 i if only one serial device is connected.)
- name (str, optional) – Name for the device.
- auto_init (bool, optional) – If both model and identity are given when creating the Mount and auto_init is True (the default), Mount.initialize() will be called after creation.
- debug_folder (pathlib.Path, optional) – The folder for debug logging. If None (the default) the folder pypogs/logs will be used/created.
Example
# Create instance (will auto initialise) mount = pypogs.Mount(model='celestron', identity='COM3', name='CPC800') # Move to position mount.move_to_alt_az(30, 10) #degrees; by default blocks until finished # Set gimbal rates mount.set_rate_alt_az(0, -1.5) #degrees per second # Wait for a while time.sleep(2) # Stop moving mount.stop() # Disconnect from the mount mount.deinitialize()
Note
The Mount class allows two modes of control for moving to positions. The default is rate_control=True, where this class will continuously send rate commands until the desired position is reached. It is possible to use the internal motion controller in the mount by passing rate_control=False. However, it is slow and implements backlash compensation. In our testing the accuracy difference is negligible so the default is recommended.
-
static
degrees_to_0_360(number)¶ float: Convert angle (degrees) to range [0, 360).
-
static
degrees_to_n180_180(number)¶ float: Convert angle (degrees) to range (-180, 180]
-
deinitialize()¶ De-initialise the device and release hardware (serial port). Will stop the mount if it is moving.
-
get_alt_az()¶ Get the current alt and azi angles of the mount.
Returns: the (altitude, azimuth) angles of the mount in degrees (-180, 180]. Return type: tuple of float
-
initialize()¶ Initialise (make ready to start) the device. The model and identity must be defined.
-
static
list_available_ports()¶ List the available serial port names and descriptions.
Returns: (device, description) for each available serial port (see serial.tools.list_ports). Return type: list of tuple
-
move_home(block=True, rate_control=True)¶ Move to the position defined by Mount.home_alt_az.
Parameters: - block (bool, optional) – If True (the default) the call to this method will block until the move is finished.
- rate_control (bool, optional) – If True (the default) the rate of the mount will be controlled until position is reached, if False the position command will be sent to the mount for execution.
-
move_to_alt_az(alt, azi, block=True, rate_control=True)¶ Move the mount to the given position. Must be initialised.
Parameters: - alt (float) – Altitude angle (degrees).
- azi (float) – Azimuth angle (degrees).
- block (bool, optional) – If True (the default) the call to this method will block until the move is finished.
- rate_control (bool, optional) – If True (the default) the rate of the mount will be controlled until position is reached, if False the position command will be sent to the mount for execution.
-
set_rate_alt_az(alt, azi)¶ Set the mount slew rate. Must be initialised.
Parameters: - alt (float) – Altitude rate (degrees per second).
- azi (float) – Azimuth rate (degrees per second).
-
stop()¶ Stop moving.
-
wait_for_move_to(timeout=120)¶ Wait for mount to finish move.
Parameters: timeout (int, optional) – Maximum time (seconds) to wait before raising TimeoutError. Default 120.
-
alt_limit¶ Get or set the altitude limits (degrees) where the mount can safely move. May be set to None. Default (-5, 95). Not enforced when slewing (set_rate) the mount.
Type: tuple of float
-
available_properties¶ Get all the available properties (settings) supported by this device.
Type: tuple of str
-
azi_limit¶ Get or set the azimuth limits (degrees) where the mount can safely move. May be set to None. Default (None, None). Not enforced when slewing (set_rate) the mount.
Type: tuple of float
-
debug_folder¶ Get or set the path for debug logging. Will create folder if not existing.
Type: pathlib.Path
-
home_alt_az¶ Get or set the home position (altitude, azimuth) in degrees. Default (0, 0)
Type: tuple of float
-
identity¶ Get or set the device and/or input. Model must be defined first.
- For model celestron this can either be a string with the serial port (e.g. ‘COM3’ on Windows or ‘/dev/ttyUSB0’ on Linux) or an int with the index in the list of available ports to use (e.g. identity=0 i if only one serial device is connected.)
- Must set before initialising the device and may not be changed for an initialised device.
Raises: AssertionError– if unable to connect to and verify identity of the mount.Type: str
-
is_init¶ True if the device is initialised (and therefore ready to control).
Type: bool
-
is_moving¶ Returns True if the mount is currently moving.
-
max_rate¶ Get or set the max slew rate (degrees per second) for the axes (altitude, azimith). Default (4.0, 4.0).
If a scalar is set, both axes’ rates will be set to this value.
Type: tuple of float
-
model¶ Get or set the device model.
- Supported:
- ‘celestron’ for Celestron NexStar and Orion/SkyWatcher SynScan hand controllers over serial.
- This will determine which hardware interface is used.
- Must set before initialising the device and may not be changed for an initialised device.
Type: str
-
name¶ Get or set the name.
Type: str
-
state_cache¶ Get cache with the current state of the Mount. Updates on calls to get_alt_az() and set_rate_alt_az().
- Keys:
- azi: float, alt: float, azi_rate: float, alt_rate: float
Type: dict
-
zero_altitude¶ Get or set the zero altitude angle (degrees). Default 0.
Normally the mount is initialised with the telescope level. In this case zero_altitude is 0. However, if the mount is e.g. initialised with the telescope pointing straight up, zero_altitude must be set to +90.
Type: float
-
class
pypogs.Receiver(model=None, identity=None, name=None, auto_init=True, data_folder=None, debug_folder=None)¶ Control acquisition and read received power from a photodetector.
To initialise a Receiver a model (determines hardware interface) and identity (identifying the specific device) must be given. If both are given to the constructor the Receiver will be initialised immediately (unless auto_init=False is passed). Manually initialise with a call to Receiver.initialize(); release hardware with a call to Receiver.deinitialize().
The raw data can be saved to a file by specifying data_folder (filenames are auto-generated). While the acquisition is running the instantaneous (last measurement) and (exponentially) smoothed power can be read.
Parameters: - model (str, optional) – The model used to determine the correct hardware API. Supported: ‘ni_daq’ for National Instruments DAQ cards (tested on USB-6211).
- identity (str, optional) – String identifying the device and input. For ni_daq this is ‘device/input’ eg. ‘Dev1/ai1’ for device ‘Dev1’ and analog input 1; only differential input is supported for ni_daq.
- name (str, optional) – Name for the device.
- auto_init (bool, optional) – If both model and identity are given when creating the Receiver and auto_init is True (the default), Receiver.initialize() will be called after creation.
- data_folder (pathlib.Path, optional) – The folder for data saving. If None (the default) no data will be saved.
- debug_folder (pathlib.Path, optional) – The folder for debug logging. If None (the default) the folder pypogs/debug will be used/created.
Example
# Create instance and set parameters (will auto initialise) rec = pypogs.Receiver(model='ni_daq', identity='Dev1/ai1', name='PhotoDiode') rec.sample_rate = 1000 #Samples per second rec.smoothing_parameter = 100 #number of samples to smooth over rec.measurement_range = (-10, 10) #Volts for ni_daq # Add a save path (filenames are auto-generated) rec.data_folder = pathlib.Path('./datafolder') # Start acquisition rec.start() # Wait for a while time.sleep(2) # Read the smooth and instantaneous powers print('Smoothed power is: ' + str(rec.smooth_power)) print('Instant power is: ' + str(rec.instant_power)) # Stop the acquisition rec.stop()
-
deinitialize()¶ De-initialise the device. Will stop the acquisition if it is running.
-
initialize()¶ Initialise (make ready to start) the device. The model and identity must be defined.
-
start()¶ Start the acquisition. Device must be initialised. Data will only be saved if data_folder is set.
-
stop()¶ Stop the acquisition. Will ensure all data in the buffer is read before stopping.
-
available_properties¶ Get all the available properties (settings) supported by this device.
Type: tuple of str
-
data_folder¶ Get or set the path for data saving. Will create folder if not existing.
Type: pathlib.Path
-
identity¶ Get or set the device and/or input. Model must be defined first.
- For model ni_daq this is ‘device/input’ eg. ‘Dev1/ai1’ for device ‘Dev1’ and analog input 1. Only differential input is supported for NI DAQ.
- Must set before initialising the device and may not be changed for an initialised device.
Type: str
-
instant_power¶ Get the latest raw measurement.
Type: float
-
is_init¶ True if the device is initialised (and therefore ready to start).
Type: bool
-
is_running¶ True if device is currently acquiring data.
Type: bool
-
measurement_range¶ Get or set the measurement range (lower_limit, upper_limit).
- If given as a scalar the range will be set to +- the supplied value.
Type: tuple, int or float
-
model¶ Get or set the device model.
- Supported:
- ‘ni_daq’ for National Instruments DAQ devices (e.g. USB-6211).
- This will determine which hardware API that is used.
- Must set before initialising the device and may not be changed for an initialised device.
Type: str
-
name¶ Get or set the name.
Type: str
-
sample_rate¶ Get or set the sample rate (in Hz) of the device. Must initialise the device first.
Type: int or float
-
smooth_power¶ Get the current smoothed measurement (see smoothing_parameter).
Type: float
-
smoothing_parameter¶ Get or set the smoothing parameter. It roughly corresponds to the number of samples to average.
Exponential smoothing is used. smoothing_parameter is the inverse of ‘alpha’. Each smoothed value s is defined from the measurements x by:
s[n] = alpha*x[n] + (1-alpha)*s[n-1]; s[0] = x[0]
Type: int or float