Skip to content

Acu add sin el nod - #1072

Merged
mhasself merged 13 commits into
mainfrom
ACU_add_sin_el_nod
Oct 19, 2026
Merged

mhasself merged 13 commits into
mainfrom
ACU_add_sin_el_nod

Conversation

@mjrand

@mjrand mjrand commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Added the gen_sin_el_nod scan generator function into the ACU drivers.

Description

Added a new generator function for generating sinusoidal elevation nods into the ACU agent. This generator will allow us to generate sine wave el nods that do not move in azimuth. This is basically a replica of type3 scan behavior with no azimuth movement.

Motivation and Context

LAT ISO review requested sinusoidal elevation nods with no azimuth movement.

How Has This Been Tested?

I've simmed the gen_sin_el_nod driver function output with success. The generator function should work as intended.
image

I have not yet tested the changes to the agent generate_scan function or tested an movement on the LAT. This needs to be tested before PR approval.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.

@mjrand
mjrand requested a review from mhasself June 9, 2026 21:28
@mjrand mjrand self-assigned this Jun 9, 2026
@mhasself

Copy link
Copy Markdown
Member

Thanks Michael. I recommend that you move this to "draft" PR until we finalize design and test it on a real system.

Initial reactions:

  • Why should this be a new "scan type" for generate_scan rather than a new, stand-alone process / task? The arguments for this functionality are a tiny subset of the full az scan functionality. It's not like someone would decide, one day, to do an el-nod everywhere that they previously were doing a scan!
  • In modern application, num_batches and batch_size are deprecated. Let's not introduce new usages ... i.e., they can be removed from your driver function (and just hard-code the current default at the top of the function).
  • Is there a control on step_time relative to the el_freq? Make sure step_time gives you "enough" points. Fourier limit is 4 but I would think we'd want more like 16.
  • The generator sets group_flag=0, which means a "stop" can cause the track to terminate at ~any point. That might "work" but it would be best to tune group_flag such that stops will always occur at the bottom of the dip (which is also the starting point).
  • To support the preceding point, it might be helpful to tweak step_time such that it divides the oscillation period evenly. Then just generate 1 cycle at a time and yield it (another reason to drop the "batch_size" notion). Can discuss more elsewhere. But basically set new_step_time = (el_freq*round((el_freq*step_time)**-1))**-1.

@mjrand
mjrand marked this pull request as draft June 10, 2026 21:08
@mjrand

mjrand commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Aaaah I didn't even know there was a draft PR feature... that's super helpful! (I am not great at git... lol). Changed.

  1. There's really no reason other than I wanted to reuse the generate_scan architecture and make it really easy to call from with sorunlib. It should be somewhat easy to make its own process/task. I'll write that in. I wasn't super attached to keeping it in generate_scan instead of making its own process.
  2. Yeah the notes on batches makes sense to me. I can rewrite this to always yield out whole number nods. That should also prevent us from stopping the elevation on a non-zero velocity.... which opens up a question of if we need to also write a smooth-stop generator for elevation (I think we probably do), but that's outside the scope of this PR.
  3. I think step_size = 0.1 is probably good enough for all nods that aren't hilariously high frequency. I think there is a check for this in plan_scan... but it should probably just be in this function calculated like you say.
  4. Agreed we should probably yield whole number nods.
  5. Agreed.

I think rewriting the driver to yield whole number of nods and then rewriting the agent process will be good. One of the worries I had with the current implementation into generate_scan was how plan_scan would interact with az_vel=0. I think it would work... but I think if we make a generate_el_nod function or whatever it could just start at the current az instead of having to run-up to az_vel=0. I'll try this implementation and see how it would work.

@mjrand

mjrand commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Alright I reverted generate_scan and made generate_sin_el_nod. I started it as a copy of generate_scan and then tried to prune back as many things weren't used or weren't necessary. This meant pruning plan_scan and some other misc variables like wait_to_start (which I couldn't find used in generate_scan either...). I also fixed up the generate_sin_el_nod drivers function to only yield full el nods and to always divide them into 16 points (which can be changed...). This means step_time is determined in the driver and doesn't need to be callable in the agent.

@mhasself mhasself left a comment

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! More notes ...

Comment thread socs/agents/acu/agent.py Resolved
Comment thread socs/agents/acu/agent.py Resolved
Comment thread socs/agents/acu/agent.py Resolved
Comment thread socs/agents/acu/agent.py Resolved
Comment thread socs/agents/acu/drivers.py Resolved
Comment thread socs/agents/acu/drivers.py Resolved
Comment thread socs/agents/acu/drivers.py Resolved
Comment thread socs/agents/acu/drivers.py Resolved
@mjrand
mjrand force-pushed the ACU_add_sin_el_nod branch from c8dee4c to 6db658d Compare July 1, 2026 01:03
@mjrand

mjrand commented Jul 3, 2026 •

Copy link
Copy Markdown
Contributor Author

I had to change part of the _run_track function to get el nods to work properly. The previous iteration was only and explicitly checking that the azimuth axis was in ProgramTrack mode. This meant that these el nods that only put the el axis into track_axes was failing because the az axis was in in stop mode. Now _run_track should check that every axis in track_axes (which are the axes we expect to be in ProgramTrack mode) are in ProgramTrack mode. This should benefit type2 and 3 scans too which should be checking that the el axis didn't fail but currently don't.

I also added some comments into the abort checks to make it clear what we're checking for abort mode.

@mjrand

mjrand commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

I've tested this current commit on satp5 and generated a few nods with varying num_nods and it worked as expected. Here is num_nods=10 el_freq=0.1 , el_depth=-3 starting at el=60.

image

The free upload positions plot shows that we're uploading full nods at once too:
image

@mhasself mhasself left a comment

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 -- those plots are quite convincing! Looking good but a few comments.

Comment thread socs/agents/acu/agent.py Outdated
Comment on lines +2447 to +2451
wait_to_start (float): number of seconds to wait before
starting a scan, in the case that start_time is None.
The default is to compute a minimum time based on the
scan parameters and the ACU ramp-up algorithm; this is
typically 5-10 seconds.

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.

not a declared arg

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've removed it because I can't seem to see if its used for anything beyond the initial plan and move to scan start (which isn't used in this el nod function).

Comment thread socs/agents/acu/agent.py Resolved
Comment thread socs/agents/acu/agent.py Resolved
Comment thread socs/agents/acu/agent.py Resolved
Comment thread socs/agents/acu/agent.py Resolved
Comment thread socs/agents/acu/agent.py Resolved
Comment thread socs/agents/acu/agent.py Resolved
Comment thread socs/agents/acu/agent.py Resolved
@mjrand
mjrand force-pushed the ACU_add_sin_el_nod branch from 748cf3f to b215786 Compare July 7, 2026 01:37

@mhasself mhasself left a comment

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.

Last batch of comments (?). Mostly cosmetic but not all!

Comment thread socs/agents/acu/drivers.py Outdated
Comment on lines +1073 to +1074
def check_completed_nods():
return num_nods is None or t * el_freq < num_nods

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.

This is vulnerable to rounding error. Instead of using t, just count the nods and check that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changed! Now each loop of generating a nod counts with a num_completed_nod variable

Comment thread socs/agents/acu/agent.py Outdated
Comment on lines +2458 to +2462
Note that all parameters are optional except for
el_depth and el_freq. If only those two parameters
are passed, the Process will nod with that depth and frequency,
with the azimuth axis held in Stop, indefinitely (until
Process .stop method is called)..

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.

In fact only el_depth is mandatory. The indefinitely bit is accurately communicated through the arg num_nods. The write-up here makes it sound like "will nod ... held in Stop" is only the case if one only passes those two parameters. Which is not the case. I think you should just mention here that the az axis is held in Stop.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed up wording to be a bit more clear w.r.t. the az axis. Removed reference to el_freq being mandatory.

Comment thread socs/agents/acu/agent.py Outdated
Comment on lines +2485 to +2486
if el_depth == 0:
raise ValueError("El depth must not be equal to 0 for el nod!")

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.

Why not? If zero is not ok ... is 0.0001 ok? Replace with a minimum depth.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Replaced. I chose a super arbitrary value of abs(el_depth) < 0.1 as the condition. I'm not really sure what the real physical minimum should be. Maybe values less than 0.1 work or maybe values a bit higher than 0.1 don't work? I'm not sure without testing. Maybe we need to check this.

Comment thread socs/agents/acu/agent.py Outdated
if el_depth == 0:
raise ValueError("El depth must not be equal to 0 for el nod!")

# Could probably use a condition for if the el endpoints are too close?

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.

Yes, see above...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed comment as it is resolved by changing the condition

Comment thread socs/agents/acu/agent.py Outdated
Comment on lines +2453 to +2455
constant-velocity parts of the motion. The default is
None, which will cause an appropriate value to be
chosen automatically (typically 0.1 to 1.0).

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.

Default is not None.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed. Default is properly commented as 0.1 seconds

Comment thread socs/agents/acu/agent.py Outdated
Comment on lines +2496 to +2499
scan_params = {k: params.get(k) for k in [
'num_nods', 'start_time', 'step_time']
if params.get(k) is not None}
step_time = scan_params['step_time']

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.

Careful here -- if indeed params['step_time'] could be None, then this bit will fail with KeyError.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

changed to step_time = 0.1 if 'step_time' not in scan_params.keys() else scan_params['step_time']

Comment thread socs/agents/acu/agent.py Outdated
for ax in track_axes:
# Convert between track axes names and status field names.
track_axes_names = {'az': 'Az', 'el': 'El'} # There's probably a better way to convert these.
if current_modes[track_axes_names[ax]] == 'ProgramTrack':

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.

You could just use current_modes[ax.capitalize()].

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed!

Comment thread socs/agents/acu/drivers.py Outdated
start_time=None,
wait_to_start=10.,
step_time=0.05,
batch_size=500):

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.

drop batch_size arg

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed

Comment thread socs/agents/acu/drivers.py Outdated

def generate_sin_el_nod(az, el_endpoint1, el_endpoint2,
el_freq=.15,
num_batches=None,

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.

drop num_batches arg

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed

@mjrand

mjrand commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Sorry I fell behind on this for a month due to some personal complications. I've resolved many of the lingering comments. One comment in particular about the minimum el_depth may need some further thinking. I set the minimum el_depth to 0.1 degrees rather arbitrarily. We may need to test what the minimum el_depth really is on the platforms (or just pick a value we know no one will ever try to nod smaller than...)

@mhasself mhasself left a comment

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.

Sorry for not getting this sooner. Looks great. Two minor comments. I tested this on the simulator and it is working well.

Comment thread socs/agents/acu/agent.py Resolved
Comment thread socs/agents/acu/drivers.py Outdated
arbitrarily long sin el nods.

Parameters:
el_endpoint1 (float): elevation endpoint for the scan start

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.

Missing "az" in the param list.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added!

Comment thread socs/agents/acu/drivers.py Outdated
Comment on lines +1014 to +1015
el_endpoint2 (float): second elevation endpoint of the scan. For
constant az scans, this must be equal to el_endpoint1.

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.

Remove "for constant az scans..." part

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

el_endpoint2 description is fixed.

@mhasself
mhasself marked this pull request as ready for review October 7, 2026 13:22
@mjrand
mjrand force-pushed the ACU_add_sin_el_nod branch from 7ca952c to 5aae3b4 Compare October 14, 2026 03:45

@mhasself mhasself left a comment

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 -- last few typos (I'll fix those) and we're good to go.

Comment thread socs/agents/acu/agent.py Resolved
Comment thread socs/agents/acu/agent.py Resolved
Comment thread socs/agents/acu/agent.py Resolved
Co-authored-by: Matthew Hasselfield <mhasself@users.noreply.github.com>
@mhasself

Copy link
Copy Markdown
Member

@BrianJKoopman This is ready for final review if you want to take a look. I can do the merge (to clean up the commit message).

@BrianJKoopman BrianJKoopman left a comment

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.

Took a very quick look. A few docstring/default inconsistencies to fix, otherwise if @mhasself is happy with it, that's good enough for me.

Comment thread socs/agents/acu/agent.py Resolved
Comment thread socs/agents/acu/drivers.py Outdated
wait_to_start (float): number of seconds to wait between
start_time and when the scan actually starts. Default is 10 seconds.
step_time (float): time between points on the constant-velocity
parts of the motion. Default value is 1.0 seconds. Minimum value is

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.

The default appears to be 0.05 above.

Comment thread socs/agents/acu/drivers.py Outdated
el_endpoint2 (float): second elevation endpoint of the scan.
Can be greater than el_endpoint1 for a positive el nod or
less than el_endpoint2 for a negative el nod.
el_freq(float): frequency of the elevation nods in Hz.

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.

State default.

Comment thread socs/agents/acu/agent.py Resolved
Comment thread socs/agents/acu/drivers.py Resolved
Comment thread socs/agents/acu/drivers.py Resolved
Co-authored-by: Matthew Hasselfield <mhasself@users.noreply.github.com>
@mhasself
mhasself merged commit f4ee7d7 into main Oct 19, 2026
13 checks passed
@mhasself
mhasself deleted the ACU_add_sin_el_nod branch October 19, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent: acu enhancement New feature or request.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants