@@ -13,7 +13,7 @@ def analytic_transform(
1313 x : Union [list , np .ndarray , xr .DataArray , pd .Series ],
1414 boundary : Optional [str ] = "mirror" ,
1515) -> np .ndarray :
16- """returns the analytic part of a real-valued signal or of a complex-valued
16+ """Return the analytic part of a real-valued signal or of a complex-valued
1717 signal. To obtain the anti-analytic part of a complex-valued signal apply analytic_transform
1818 to the conjugate of the input. Analytic_transform removes the mean of the input signals.
1919
@@ -86,3 +86,69 @@ def analytic_transform(
8686 z = z [int (m0 + 1 ) - 1 : int (2 * m0 + 1 ) - 1 ]
8787
8888 return z
89+
90+
91+ def rotary_transform (
92+ u : Union [list , np .ndarray , xr .DataArray , pd .Series ],
93+ v : Union [list , np .ndarray , xr .DataArray , pd .Series ],
94+ boundary : Optional [str ] = "mirror" ,
95+ ) -> Tuple [np .ndarray , np .ndarray ]:
96+ """Return time-domain rotary components time series (zp,zn) from Cartesian components time series (u,v).
97+ Note that zp and zn are both analytic time series which implies that the complex-valued time series
98+ u+1j*v is recovered by zp+np.conj(zn). The mean of the original complex signal is split evenly between
99+ the two rotary components.
100+
101+ If up is the analytic transform of u, and vp the analytic transform of v, then the counterclockwise and
102+ clockwise components are defined by zp = 0.5*(up+1j*vp), zp = 0.5*(up-1j*vp).
103+ See as an example Lilly and Olhede (2010), doi: 10.1109/TSP.2009.2031729.
104+
105+ Parameters
106+ ----------
107+ u : np.ndarray
108+ Real-valued signal, first Cartesian component (zonal, east-west)
109+ v : np.ndarray
110+ Real-valued signal, second Cartesian component (meridional, north-south)
111+ boundary : str, optional ["mirror", "zeros", "periodic"] optionally specifies the
112+ boundary condition to be imposed at the edges of the time series for the underlying analytic
113+ transform. Default is "mirror".
114+
115+ Returns
116+ -------
117+ zp : np.ndarray
118+ Time-domain complex-valued positive (counterclockwise) rotary component.
119+ zn : np.ndarray
120+ Time-domain complex-valued negative (clockwise) rotary component.
121+
122+ Examples
123+ --------
124+
125+ To obtain the rotary components of a real-valued signal:
126+ >>> zp, zn = rotary_transform(u,v)
127+
128+ Raises
129+ ------
130+ ValueError
131+ If the input arrays do not have the same shape.
132+
133+ See Also
134+ --------
135+ :func:`analytic_transform`
136+ """
137+ # to implement: input one complex argument instead of two real arguments
138+ # u and v arrays must have the same shape or list length.
139+ if type (u ) == list and type (v ) == list :
140+ if not len (u ) == len (v ):
141+ raise ValueError ("u and v must have the same length." )
142+ else :
143+ if not u .shape == v .shape :
144+ raise ValueError ("u and v must have the same shape." )
145+
146+ muv = np .mean (u ) + 1j * np .mean (v )
147+
148+ if muv == xr .DataArray :
149+ muv = muv .to_numpy ()
150+
151+ up = analytic_transform (u , boundary = boundary )
152+ vp = analytic_transform (v , boundary = boundary )
153+
154+ return 0.5 * (up + 1j * vp ) + 0.5 * muv , 0.5 * (up - 1j * vp ) + 0.5 * np .conj (muv )
0 commit comments