Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion Config/sequencerd.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,9 @@ TCS_OFFSET_RATE_RA=45 # TCS offset rate for RA ("MRATE") in arcse
TCS_OFFSET_RATE_DEC=45 # TCS offset rate for DEC ("MRATE") in arcsec per second
TCS_SETTLE_TIMEOUT=20 # seconds to wait for telescope to settle (for "TRACKING_STABLY")
TCS_SETTLE_STABLE=1.5 # time in fractional seconds (to 0.1) that TCS must report TRACKING before it is really stable
TCS_AUTO_ONTARGET=no # yes: TCS ?ONTARGET=1 + tracking + sep < TCS_AUTO_ONTARGET_SEP (2 consecutive 1 Hz reads) satisfies the ontarget wait; operator press always works
TCS_AUTO_ONTARGET=yes # yes: TCS ?ONTARGET=1 + tracking + sep < TCS_AUTO_ONTARGET_SEP + cass ring within TCS_AUTO_ONTARGET_CASS (2 consecutive 1 Hz reads) satisfies the ontarget wait; operator press always works
TCS_AUTO_ONTARGET_SEP=5.0 # arcsec: max |telescope-target| for auto-ontarget
TCS_AUTO_ONTARGET_CASS=0.05 # deg: max |TCS cass angle - commanded ring angle| for auto-ontarget
TCS_DOMEAZI_READY=10 # max degrees azimuth that dome and telescope can differ before ready to observe
TCS_PREAUTH_TIME=10 # seconds before end of exposure to notify TCS of next target's coords (0 to disable)

Expand Down
57 changes: 43 additions & 14 deletions sequencerd/sequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2368,11 +2368,15 @@ namespace Sequencer {
* lives only while that wait is open (auto_ontarget_active).
* Acknowledges when two consecutive 1 Hz reads show
* ?ONTARGET==1 AND motion tracking AND |telescope-commanded| <
* tcs_auto_ontarget_sep. All three predicates are required:
* the flag alone is position-agnostic, the separation alone
* cannot see a sub-threshold hop in progress, and motion alone
* says nothing about settling. The coords are the SCOPE-frame
* position move_to_target actually commanded. A wrong
* tcs_auto_ontarget_sep AND |TCS cass angle - commanded ring
* angle| < tcs_auto_ontarget_cass. All four are required: the
* flag alone is position-agnostic, the separation alone cannot
* see a sub-threshold hop in progress, motion alone says
* nothing about settling, and none of them covers the cass
* ring, which turns on its own after RINGGO (ACAM's goal is
* computed from the cass angle at the acquire, ~8.5" per
* degree of ring still to go). The coords and ring angle are
* what move_to_target actually commanded. A wrong
* acknowledgment is self-limiting -- ACAM cannot solve a field
* that is not there and the acquisition retry machinery
* handles the failure -- so the conditions make wrong acks
Expand All @@ -2382,16 +2386,18 @@ namespace Sequencer {
* cancel_flag, and a concurrent cancel must always win
* against this automatic path.
*/
void Sequence::dothread_auto_ontarget( double tgt_ra_deg, double tgt_dec_deg, unsigned gen ) {
void Sequence::dothread_auto_ontarget( double tgt_ra_deg, double tgt_dec_deg, double tgt_cass_deg, unsigned gen ) {
const std::string function("Sequencer::Sequence::dothread_auto_ontarget");
std::stringstream message;

if ( std::isnan(tgt_ra_deg) || std::isnan(tgt_dec_deg) ) {
logwrite( function, "ERROR invalid commanded coords; auto-ontarget sits this wait out" );
if ( std::isnan(tgt_ra_deg) || std::isnan(tgt_dec_deg) || std::isnan(tgt_cass_deg) ) {
logwrite( function, "ERROR invalid commanded coords or ring angle; auto-ontarget sits this wait out" );
return;
}

int consecutive = 0;
int64_t ring_wait_since_us = 0; // when everything but the ring first held
bool ring_warned = false;

// gen retires this watcher the moment a newer one is launched, so one
// still sleeping from an earlier move can never acknowledge a new one.
Expand All @@ -2412,6 +2418,8 @@ namespace Sequencer {
bool fresh = false;
bool tracking = false;
double sep_as = NAN;
double dcass_deg = NAN;
double cass_now = NAN;
{
std::lock_guard<std::mutex> lock( tcsd_mtx );
fresh = this->tcsinfo.is_fresh();
Expand All @@ -2422,11 +2430,31 @@ namespace Sequencer {
double dra_as = dra_deg * cos( tgt_dec_deg * M_PI / 180.0 ) * 3600.0;
double ddec_as = ( this->tcsinfo.dec_d - tgt_dec_deg ) * 3600.0;
sep_as = std::hypot( dra_as, ddec_as );
// the cass ring against the angle RINGGO was sent, either side of the wrap
cass_now = this->tcsinfo.cassangle;
dcass_deg = std::fabs( std::remainder( cass_now - tgt_cass_deg, 360.0 ) );
}
}

const bool all_but_ring = fresh && flag_on && tracking && ( sep_as < this->tcs_auto_ontarget_sep );
const bool ring_ok = ( dcass_deg < this->tcs_auto_ontarget_cass );

// On target with only the ring missing for 20 s (a refused RINGGO, a ring
// parked off the command, an unreadable angle): say so once, so the
// operator knows the wait needs a press.
if ( all_but_ring && !ring_ok ) {
if ( ring_wait_since_us == 0 ) ring_wait_since_us = get_time_us();
else if ( !ring_warned && get_time_us() - ring_wait_since_us > 20000000 ) {
ring_warned = true;
message.str(""); message << "telescope on target but the cass ring reads " << cass_now
<< " deg, commanded " << tgt_cass_deg
<< " deg: press ontarget once the ring is in place";
this->broadcast.warning( function, message.str() );
}
}
}
else if ( !all_but_ring ) ring_wait_since_us = 0;

if ( !fresh || !flag_on || !tracking ||
!( sep_as < this->tcs_auto_ontarget_sep ) ) { consecutive=0; continue; }
if ( !all_but_ring || !ring_ok ) { consecutive=0; continue; }

if ( ++consecutive < 2 ) continue; // debounce: two reads in a row

Expand All @@ -2447,7 +2475,8 @@ namespace Sequencer {

message.str(""); message << "TCS reports on-target (?ONTARGET=1, tracking, sep="
<< std::fixed << std::setprecision(1) << sep_as
<< " arcsec x2) -- sending ontarget";
<< " arcsec, ring within " << std::setprecision(3) << dcass_deg
<< " deg x2) -- sending ontarget";
this->broadcast.notice( function, message.str() );
return;
}
Expand Down Expand Up @@ -2614,7 +2643,7 @@ namespace Sequencer {

this->broadcast.notice( function, "waiting for TCS operator to send \"ontarget\" signal" );

// AUTO-ONTARGET (config TCS_AUTO_ONTARGET, default no): the TCS itself can
// AUTO-ONTARGET (config TCS_AUTO_ONTARGET, default yes): the TCS itself can
// satisfy this wait through the same ontarget() the operator's press uses.
// The watcher lives exactly as long as this wait (auto_ontarget_active).
//
Expand All @@ -2625,7 +2654,7 @@ namespace Sequencer {
// database coordinates differ by the pointmode focal-plane offset.
// ontarget_gen was taken, retiring older watchers, before the COORDS.
std::thread( &Sequencer::Sequence::dothread_auto_ontarget, this,
ra_out, dec_out, ontarget_gen ).detach();
ra_out, dec_out, angle_out, ontarget_gen ).detach();
}

while ( !this->cancel_flag.load() && !this->is_ontarget.load() ) {
Expand Down
5 changes: 3 additions & 2 deletions sequencerd/sequence.h
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ namespace Sequencer {
/** @brief satisfies the TCSOP wait when the TCS itself reports arrival
* (config TCS_AUTO_ONTARGET); coords are the commanded SCOPE
* frame position in degrees; see sequence.cpp */
void dothread_auto_ontarget( double tgt_ra_deg, double tgt_dec_deg, unsigned gen );
void dothread_auto_ontarget( double tgt_ra_deg, double tgt_dec_deg, double tgt_cass_deg, unsigned gen );
std::atomic<bool> auto_ontarget_active{false}; ///< true only while move_to_target's TCSOP wait is open
std::atomic<unsigned> auto_ontarget_gen{0}; ///< launch generation; a watcher retires when a newer one is launched
std::mutex ontarget_ack_mtx; ///< a watcher's acknowledgment vs a new move retiring watchers and clearing is_ontarget
Expand All @@ -429,8 +429,9 @@ namespace Sequencer {
double tcs_domeazi_ready; ///< max degrees azimuth that dome and telescope can differ before ready to observe
double tcs_preauth_time; ///< seconds before end of exposure to notify TCS of next target's coords (0 to disable)
double offset_settle_sec; ///< sec to wait after a target offset before exposing (config OFFSET_SETTLE_SEC; 0 disables)
bool tcs_auto_ontarget = false; ///< TCS ?ONTARGET may satisfy the TCSOP wait (config TCS_AUTO_ONTARGET; default no)
bool tcs_auto_ontarget = true; ///< TCS ?ONTARGET may satisfy the TCSOP wait (config TCS_AUTO_ONTARGET; default yes)
double tcs_auto_ontarget_sep = 5.0; ///< max |telescope-target| arcsec for auto-ontarget (config TCS_AUTO_ONTARGET_SEP)
double tcs_auto_ontarget_cass = 0.05; ///< max |TCS cass angle - commanded ring angle| deg for auto-ontarget (config TCS_AUTO_ONTARGET_CASS)

/// std::mutex tcs_ontarget_mtx;
/// std::condition_variable tcs_ontarget_cv;
Expand Down
15 changes: 15 additions & 0 deletions sequencerd/sequencer_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,21 @@ namespace Sequencer {
applied++;
}

// TCS_AUTO_ONTARGET_CASS
if (config.param[entry] == "TCS_AUTO_ONTARGET_CASS") {
try {
this->sequence.tcs_auto_ontarget_cass = std::stod( config.arg[entry] );
}
catch (const std::exception &e) {
message.str(""); message << "ERROR parsing TCS_AUTO_ONTARGET_CASS: " << e.what();
logwrite( function, message.str() );
return ERROR;
}
message.str(""); message << "SEQUENCERD:config:" << config.param[entry] << "=" << config.arg[entry];
logwrite( function, message.str() );
applied++;
}

// TCS_AUTO_ONTARGET_SEP
if (config.param[entry] == "TCS_AUTO_ONTARGET_SEP") {
try {
Expand Down