Source code for ssapy_toolkit.Time_Functions.get_times

import numpy as np
from astropy.time import Time
import astropy.units as u


[docs] def get_times( duration, freq=(1, 's'), t0=Time(0, format='gps'), tf=None, tm=None, # middle time (optional) ): """ Calculate a list of times spaced equally apart over a specified duration. Parameters ---------- duration : int, float, or tuple A duration in seconds (float/int), or a tuple like (30, 'day'). Must be non-negative. freq : int, float, or tuple A time step in seconds (float/int), or a tuple like (10, 'min'). Must be positive. t0 : str, float, int, or Time, optional Initial time (first element) if tf and tm are not provided. If str, interpreted as UTC. If numeric, interpreted as GPS seconds. tf : str, float, int, or Time, optional Final time (last element). If provided (and tm is None), the time array is built so that the last element equals tf and the span equals `duration` with spacing `freq`. tm : str, float, int, or Time, optional Middle time. If provided, the time array is built so that: - the total span is exactly `duration`, - tm is exactly the middle element, - the effective step is adjusted slightly from `freq` if needed. Returns ------- astropy.time.Time Time array of equally spaced times over the specified duration. """ # --- Helper to normalize times --- def _to_time_obj(t_in): if isinstance(t_in, Time): return t_in if isinstance(t_in, str): return Time(t_in, scale='utc') if isinstance(t_in, (float, int)): return Time(t_in, scale='utc', format='gps') return None # Normalize anchors t0_obj = _to_time_obj(t0) tf_obj = _to_time_obj(tf) if tf is not None else None tm_obj = _to_time_obj(tm) if tm is not None else None unit_dict = { 'second': 1, 'sec': 1, 's': 1, 'minute': 60, 'min': 60, 'hour': 3600, 'hr': 3600, 'h': 3600, 'day': 86400, 'd': 86400, 'week': 604800, 'month': 2630016, 'mo': 2630016, 'year': 31557600, 'yr': 31557600 } def to_seconds(value): if isinstance(value, (int, float)): return float(value) else: val, unit = value unit = unit.lower() if len(unit) > 1: unit = unit.rstrip('s') if unit not in unit_dict: raise ValueError( f'Error, {unit} is not a valid time unit. ' f'Valid options are: {", ".join(unit_dict.keys())}.' ) return float(val) * unit_dict[unit] dur_seconds = to_seconds(duration) freq_seconds = to_seconds(freq) if dur_seconds < 0: raise ValueError("duration must be non-negative.") if freq_seconds <= 0: raise ValueError("freq must be positive.") if t0_obj is None and tf_obj is None and tm_obj is None: raise ValueError("At least one of t0, tf, or tm must be provided.") # --- Case 1: tm is provided (middle time) --- if tm_obj is not None: if dur_seconds == 0: # Single time equal to tm return Time([tm_obj]) # Ideal number of intervals N_float = dur_seconds / freq_seconds # Choose an even integer N close to N_float so tm is exactly center N_candidate = int(round(N_float)) if N_candidate <= 0: N_candidate = 2 # minimal even positive if N_candidate % 2 != 0: # make it even by adjusting to nearby even integer if N_candidate < N_float: N = N_candidate + 1 else: N = N_candidate - 1 if N_candidate > 1 else 2 else: N = N_candidate # Ensure still positive if N <= 0: N = 2 # Effective step size and diagnostics effective_freq = dur_seconds / N if not np.isclose(effective_freq, freq_seconds): print( "get_times warning: adjusted frequency to keep duration exact and tm centered.\n" f" requested freq = {freq_seconds:.6f} s\n" f" effective freq = {effective_freq:.6f} s\n" f" intervals N = {N}\n" f" total span = {dur_seconds:.6f} s" ) timesteps = N + 1 half_span = dur_seconds / 2.0 # exact start_offset = -half_span end_offset = +half_span anchor = tm_obj # --- Case 2: tf is provided (end time) --- elif tf_obj is not None: if dur_seconds == 0: return Time([tf_obj]) timesteps = int(dur_seconds / freq_seconds) + 1 start_offset = -dur_seconds end_offset = 0.0 anchor = tf_obj # --- Case 3: default t0 (start time) --- else: if dur_seconds == 0: return Time([t0_obj]) timesteps = int(dur_seconds / freq_seconds) + 1 start_offset = 0.0 end_offset = dur_seconds anchor = t0_obj # Build offsets in days offsets = np.linspace(start_offset, end_offset, timesteps) / unit_dict['day'] * u.day times = anchor + offsets return times