Skip to content
Open
Changes from 11 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3fc8d8b
changed directory name from ~/git/socs/socs/agents/starcam to ~/git/s…
tanaybhandarkar Nov 13, 2024
82252e7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 13, 2024
f7a0565
changes:
tanaybhandarkar Nov 20, 2024
04999b4
Merge branch 'starcamera' of https://github.com/simonsobs/socs into s…
tanaybhandarkar Nov 20, 2024
735850d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 20, 2024
0733d7a
changes
tanaybhandarkar Nov 20, 2024
3ea0cce
Merge branch 'starcamera' of https://github.com/simonsobs/socs into s…
tanaybhandarkar Nov 20, 2024
6465c4f
changes:
tanaybhandarkar Nov 20, 2024
10db58a
changes:
tanaybhandarkar Nov 20, 2024
824305f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 20, 2024
d2ca913
changes:
Nov 20, 2024
336557b
Fix docstrings and a few typos
Jan 15, 2025
a5a2f24
Change buffer size in get_astrom_data
Jan 27, 2025
03e2d58
Remove unnecessary lines
Jan 27, 2025
fe0c275
Change attribute names
Jan 27, 2025
401c61f
Correct Task docstrings and fix acq() timeout+typo
Feb 12, 2025
34bfdfc
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 12, 2025
e198836
Remove Returns: sections from agent operation dosctrings
Feb 12, 2025
21cc75d
Merge branch 'starcamera' of https://github.com/simonsobs/socs into s…
Feb 12, 2025
6d33d24
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 12, 2025
f2a9456
Touch up Docstrings
Feb 14, 2025
90bbfa9
Merge branch 'starcamera' of https://github.com/simonsobs/socs into s…
Feb 14, 2025
267a02c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 14, 2025
e789b4a
Fix typo
BrianJKoopman May 6, 2025
26988c5
Add agent to plugin list
BrianJKoopman May 6, 2025
7b5bc3a
Create docs page for starcam agent
BrianJKoopman May 6, 2025
f4fca40
Clean up documentation
BrianJKoopman May 6, 2025
f566702
Merge branch 'main' into starcamera
BrianJKoopman May 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
262 changes: 262 additions & 0 deletions socs/agents/starcam_lat/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
import argparse
import socket
import struct
import time
from os import environ

import txaio
from ocs import ocs_agent, site_config
from ocs.ocs_twisted import TimeoutLock


class StarcamHelper:

"""
CLASS to control and retrieve data from the starcamera

Args:
ip_address: IP address of the starcamera computer
port: port of the starcamera
"""

def __init__(self, ip_address, port, timeout=10):
self.ip = ip_address
self.port = port
self.server_addr = (self.ip, self.port)
self.comm = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.comm.connect(self.server_addr)
self.comm.settimeout(timeout)

def pack_and_send_cmds(self):
"""
pack_and_send_cmds()

**Process**
packs commands and parameters to be sent to starcamera and sends

**Return**
returns list of values sent
"""
Comment thread
BrianJKoopman marked this conversation as resolved.
Outdated
logodds = 1e8
latitude = -22.9586
longitude = -67.7875
height = 5200.0
exposure = 700
timelimit = 1
set_focus_to_amount = 0
auto_focus_bool = 1
start_focus = 0
end_focus = 0
step_size = 5
photos_per_focus = 3
infinity_focus_bool = 0
set_aperture_steps = 0
max_aperture_bool = 0
make_HP_bool = 0
use_HP_bool = 0
spike_limit_value = 3
dynamic_hot_pixels_bool = 1
r_smooth_value = 2
high_pass_filter_bool = 0
r_high_pass_filter_value = 10
centroid_search_border_value = 1
filter_return_image_bool = 0
n_sigma_value = 2
star_spacing_value = 15
values = [logodds,
latitude,
longitude,
height,
exposure,
timelimit,
set_focus_to_amount,
auto_focus_bool,
start_focus,
end_focus,
step_size,
photos_per_focus,
infinity_focus_bool,
set_aperture_steps,
max_aperture_bool,
make_HP_bool,
use_HP_bool,
spike_limit_value,
dynamic_hot_pixels_bool,
r_smooth_value,
high_pass_filter_bool,
r_high_pass_filter_value,
centroid_search_border_value,
filter_return_image_bool,
n_sigma_value,
star_spacing_value]
# Pack values into the command for the camera
self.cmds_for_camera = struct.pack('ddddddfiiiiiiiiiifffffffff',
*values)
# send commands to the camera
self.comm.sendto(self.cmds_for_camera, (self.ip, self.port))
print("Commands sent to camera")
# Return the list of values
return values

def get_astrom_data(self):
"""
get_astrom_data()

**Process**
receives and unpacks data from camera

**Return**
returns dictionary of unpacked data
"""
(scdata_raw, _) = self.comm.recvfrom(224)
Comment thread
BrianJKoopman marked this conversation as resolved.
Outdated
data = struct.unpack_from("dddddddddddddiiiiiiiiddiiiiiiiiiiiiiifiii",
scdata_raw)
keys = ['c_time',
'gmt',
'blob_num',
'obs_ra',
'astrom_ra',
'obs_dec',
'fr',
'ps',
'alt',
'az',
'ir',
'astrom_solve_time',
'camera_time']
# Create a dictionary of the unpacked data
astrom_data = [data[i] for i in range(len(keys))]
astrom_data_dict = {keys[i]: astrom_data[i] for i in range(len(keys))}
return astrom_data_dict

def close(self):
"""
close()

**Process**
closes the socket of the connection
"""
self.comm.close()


class StarcamAgent:

def __init__(self, agent, ip_address, port):
self.agent = agent
self.active = True
self.log = agent.log
self.job = None
Comment thread
BrianJKoopman marked this conversation as resolved.
Outdated
self.take_data = False
self.lock = TimeoutLock()
agg_params = {'frame_length': 60}
self.agent.register_feed("starcamera", record=True,
agg_params=agg_params, buffer_time=1)
try:
self.StarcamHelper = StarcamHelper(ip_address, port)
Comment thread
BrianJKoopman marked this conversation as resolved.
Outdated
except socket.timeout:
self.log.error("Starcamera connection has times out")
return False, "Timeout"
Comment on lines +154 to +169
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would still be useful to separate this part into an init operation that can be called from an ocs client (as hinted at in #335 (comment).) That would allow users to re-initialize the connection to the star camera without needing to completely restart the agent. That structure is in many of the other agents, one good example is the CryomechAgent:

@ocs_agent.param('auto_acquire', default=False, type=bool)
def init(self, session, params=None):
"""init(auto_acquire=False)
**Task** - Initializes the connection to the PTC.
Parameters:
auto_acquire (bool): Automatically start acq process after
initialization if True. Defaults to False.
"""
if self.initialized:
return True, "Already Initialized"
with self.lock.acquire_timeout(0, job='init') as acquired:
if not acquired:
self.log.warn("Could not start init because "
"{} is already running".format(self.lock.job))
return False, "Could not acquire lock."
# Establish connection to ptc
self.ptc = PTC(self.ip_address, port=self.port,
fake_errors=self.fake_errors)
# Test connection and display identifying info
try:
self.ptc.get_data()
except ConnectionError:
self.log.error("Could not establish connection to compressor.")
return False, "PTC agent initialization failed"
print("PTC Model:", self.ptc.model)
print("PTC Serial Number:", self.ptc.serial)
print("Software Revision is:", self.ptc.software_revision)
self.initialized = True
# Start data acquisition if requested
if params['auto_acquire']:
resp = self.agent.start('acq', params={})
self.log.info(f'Response from acq.start(): {resp[1]}')
return True, "PTC agent initialized"

I'd be happy to help with this, and to simultaneously set this up to use the new TCPInterface base class for the TCP connection to the star camera, as that will help make the agent robust against network issues.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, so that would mean there would be e.g. an "init" button on ocs-web. Cool! And added robustness for TCP connection would be great, the connection does tend to drop pretty frequently.

Thanks for offering that, let me know how best to proceed!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for offering that, let me know how best to proceed!

Just let me know when you're done making changes.


@ocs_agent.param('_')
def send_commands(self, session, params=None):
"""
send_commands()

**Process**
packs and sends camera+astrometry-related commands to starcam

**Return**
returns a touple with True/False and a string describing whether
or not a lock could be acquired and commands were sent to the sc
"""
Comment thread
BrianJKoopman marked this conversation as resolved.
Outdated
with self.lock.acquire_timeout(job='send_commands') as acquired:
if not acquired:
self.log.warn(f"Could not start Task because "
f"{self._lock.job} is already running")
return False, "Could not acquire lock"
self.log.info("Sending commands")
self.StarcamHelper.pack_and_send_cmds()
return True, "Sent commands to starcamera"

@ocs_agent.param('_')
def acq(self, session, params=None):
"""
acq()

**Process**
acquires data from starcam and publishes to feed

**Return**
once the acq() loop exits (wherein data is retrieved from
the camera and pulished), a touple with True/False and a string
describing whether or not the loop was exited after the end of
an acquisition.
"""
Comment thread
BrianJKoopman marked this conversation as resolved.
Outdated
if params is None:
params = {}
Comment thread
BrianJKoopman marked this conversation as resolved.
Outdated
with self.lock.acquire_timeout(timeout=100, job='acq') as acquired:
if not acquired:
self.log.warn("Could not start init because {} is already "
Comment thread
BrianJKoopman marked this conversation as resolved.
Outdated
"running".format(self.lock.job))
return False, "Could not acquire lock"
session.set_status('running')
self.log.info("Starting acquisition")
self.take_data = True
while self.take_data:
data = {
'timestamp': time.time(),
'block_name': 'astrometry',
'data': {}
}
# get astrometry data
astrom_data_dict = self.StarcamHelper.get_astrom_data()
# update the data dictionary+session and publish
data['data'].update(astrom_data_dict)
session.data.update(data['data'])
Comment thread
BrianJKoopman marked this conversation as resolved.
self.agent.publish_to_feed('starcamera', data)

return True, 'Acquisition exited cleanly'

def _stop_acq(self, session, params):
ok = False
if self.take_data:
session.set_status('stopping')
self.take_data = False
ok = True
# self.StarcamHelper.close()
Comment thread
BrianJKoopman marked this conversation as resolved.
Outdated
return (ok, {True: 'Requested process to stop',
False: 'Failed to request process stop.'}[ok])


def add_agent_args(parser=None):
if parser is None:
parser = argparse.ArgumentParser()
pgroup = parser.add_argument_group('Agent Options')
pgroup.add_argument("--ip-address", type=str,
help="IP address of starcam computer")
pgroup.add_argument("--port", default="8000", type=int,
help="Port of starcam computer")
return parser


def main(args=None):
# for logging
txaio.use_twisted()
txaio.make_logger()

# start logging
txaio.start_logging(level=environ.get("LOGLEVEL", "info"))

parser = add_agent_args()
args = site_config.parse_args(agent_class="StarcamAgent", parser=parser)
agent, runner = ocs_agent.init_site_agent(args)
starcam_agent = StarcamAgent(agent, ip_address=args.ip_address,
port=args.port)
agent.register_task('send_commands', starcam_agent.send_commands,
startup=True)
agent.register_process('acq', starcam_agent.acq, starcam_agent._stop_acq)
runner.run(agent, auto_reconnect=False)


if __name__ == '__main__':
main()