>>> wonky_points = signal(np.array([0,1,2,3])*1.1+1.1) * >>> np.exp(np.array([0,1,2,3]) * 1.1 * 2j * np.pi * np.fft.fftfreq(4)[1])
what's going on here is the product of two complex sinusoids. >>> abs(zero_based_wonky_points), np.angle(zero_based_wonky_points)*180//np.pi (array([1., 1., 1., 1.]), array([ 0., -63., -126., 170.])) Just looking it, it seems like a solution would be to scale the angles. The internet confirms that the product of two complex numbers has the sum of their angles. I'm immediately having difficulty confirming that: >>> np.angle(np.exp(np.array([30, 40]) / 360 * 2j * np.pi)) * 180 // np.pi array([29., 40.]) >>> np.product(np.angle(np.exp(np.array([30, 40]) / 360 * 2j * np.pi))) * 180 >>> // np.pi 20.0 >>> (0.8660254 +0.5j) * (0.76604444+0.64278761j) (0.342020137568776+0.939692617065294j) >>> np.angle((0.8660254 +0.5j) * (0.76604444+0.64278761j)) * 180 // np.pi 70.0 It looks like that is true, and np.product simply doesn't do what I wanted it to. Ok, so if the angles of the products of these sinusoids are increasing by a constant, this means the sums of the angles of the parts are increasing by a constant. Multiplying the signal sinusoid, by the fourier sinusoid, adds their angles together. I'll look at this again: >>> zero_based_wonky_points = signal(np.array([0,1,2,3])*1.1) * >>> np.exp(np.array([0,1,2,3]) * 1.1 * 2j * np.pi * np.fft.fftfreq(4)[1]) >>> abs(zero_based_wonky_points), np.angle(zero_based_wonky_points)*180//np.pi (array([1., 1., 1., 1.]), array([ 0., -63., -126., 170.])) >>> np.angle(signal(np.array([0,1,2,3])*1.1)) * 180 // np.pi array([ 0., -162., 36., -127.]) >>> np.angle(np.exp(np.array([0,1,2,3]) * 1.1 * 2j * np.pi * >>> np.fft.fftfreq(4)[1])) * 180 // np.pi array([ 0., 99., -162., -64.]) The fourier signal is advancing at half the rate of the measured signal. 0652 When the measured signal is at -162, the fourier signal is at 90. When the measured signal is at 36, the fourier signal is at -162. >>> -162 - 0 -162 >>> 36 - (-162+360) -162 >>> -127 - 36 -163 Because the sampling is offset, the measured signal is advancing by -162 degrees every sample. Meanwhile, the fourier signal is advancing by 99 degrees: >>> 99 - 0 99 >>> -162 - (99 - 360) 99 >>> -64 - -162 98 I'm missing something: why does the angular rate of the fourier signal not appear to be half the angular rate of the measured signal? Some aliasing effect? Oh, actually it is: >>> 99 * 2 198 >>> -162 + 360 198 So the faster signal is advancing by 198 degrees/sample, and the slower signal is advancing by 99 degrees/sample. When we multiply them, we get a sequence of unit vectors that advance by -63 degrees/sample. This is the same as +297 degrees, which is 198 degrees + 99 degrees: >>> -63+360 297 >>> 99+198 297 When the signals align, the faster frequency advances by 180 degrees, and the sum advances by 0 degrees. The slower frequency advances by 90 degrees, and the sum advances by 270 degrees, which results in it hitting -90, 180, and 90 with modulo 360. If the samples are aligned, the sum advances by 270 degrees. Here, in the disalignment, the sum instead advances by 297 degrees. Performing a transform on this row that converts 270 degree angle advancements to 297 degree advancements could help. Further considering of combinations of these could help figure out a formula for that transform. But is it even reasonable to scale a sequence of complex numbers such that they express a rotation at a different rate? I'm thinking that could be done a further complex multiplication. 0704 If these all advance at 297 degrees, then maybe I could divide them by appropriate exponentiated complex numbers to make them align to 0 degrees, then multiply them again to bring them into 270 degrees. Not sure. I'm thinking of how to design an exponent that increases by 297 degrees each time. I know I just wrote this to display it, but I'm still confused around it. If I want the complex number in the base of the exponent, then the number I would use would be the one at 297 degrees. >>> np.angle(np.exp(297 * np.pi / -180j) ** np.array([0,1,2,3])) * 180 // np.pi array([ 0., -64., -127., 170.]) I'm using power operator to sort my thoughts out, but of course it would be more efficient to simply multiply the angle before taking the complex sin with the exp function. Another question is, where does this angle 297 come from? This must relate to the frequency difference of the original data. That puts all the parts together. There was something I wanted to consider to ensure the approach had a reasonable likelihood of being valid. >>> zero_based_wonky_points = signal(np.array([0,1,2,3])*1.1) * >>> np.exp(np.array([0,1,2,3]) * 1.1 * 2j * np.pi * np.fft.fftfreq(4)[1]) >>> abs(zero_based_wonky_points), np.angle(zero_based_wonky_points)*180//np.pi (array([1., 1., 1., 1.]), array([ 0., -63., -126., 170.])) >>> np.angle(signal(np.array([0,1,2,3])*1.1)) * 180 // np.pi array([ 0., -162., 36., -127.]) >>> np.angle(np.exp(np.array([0,1,2,3]) * 1.1 * 2j * np.pi * >>> np.fft.fftfreq(4)[1])) * 180 // np.pi array([ 0., 99., -162., -64.]) It looks like it's quite possible to adjust the phase offsets of the results of multiplying a unit signal by a unit fourier component so as to recover destructive interference. In reality there are multiple signals at once, multiple components at once, and notable the signal does not have unit magnitude. A first test could be to give the signal a different magnitude, and see what the result looks like. >>> zero_based_wonky_points = signal(np.array([0,1,2,3])*1.1)*1.1 * >>> np.exp(np.array([0,1,2,3]) * 1.1 * 2j * np.pi * np.fft.fftfreq(4)[1]) >>> abs(zero_based_wonky_points), np.angle(zero_based_wonky_points) * 180 // >>> np.pi (array([1.1, 1.1, 1.1, 1.1]), array([ 0., -63., -126., 170.])) It looks like magnitude is retained intact, so that much is great. Then, real data would have more than one signal going on at once, and more than one fourier component going on at once. 0712 What does that mean? Well, for one thing I could try a further combination. I can imagine in my mind that changing the fourier frequency here, or the signal frequency, will just change the angle advancement, and can be similarly countered. What if there are multiple signals? Here, there is just a DC component and a single signal. It seems it could be reasonable to look briefly at 6-valued data, so as to consider the interference of 2 disaligned signals at once. It seems like that might require a more complex transform. The fftfreqs of 6-valued data have 3 different absolute frequencies in them, in addition to the DC offset. I imagine the negative frequencies have an arithmetic equivalence, but it's a little more confusing for me to consider what that is. 0714