<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Antenna Position Measurement
Author
Step1:  Locating the third reference point
The distances from reference point 2 to the other two reference points (0,1) are measured (in mm)
Step2:  Now a least squares estimator is used to work out the x-y coordinates of the third reference point (x2)
Step3:  Finding the antennas
This is done by measuring the distance from each antenna to the three reference points x0, x1 and x2.
Step4:  The following are the measured distances from [x1, x0, x2] from the reference points in millimeters. Note that their order must be the same as the order of the variable called 'reference_points'. In this case, they are x1,x0,x2.
Step5:  Plot the Initial Guess Points
Initial Guesses are from JSON queried from the telescope API. These are converted to millimeters.
Step6:  Criteria for Optimality
The function below is minimized when the positions (in variable x) are consistent with the measured distances m[i,j]. The initial value of this function is more than 3 million.
Note that the x input is a 1D vector of with 48 entries as [p0.x, p0.y, p1.x, p1.y]
Step7:  The optimized positions are now known. The final value of the function is 32. Far closer to zero than 3 million!
We can recover the x,y coordinates by reshaping the array
Step8:  The API expects 3D coordinates (with a z value which is zero in this case). Therefore we add a column of zeros.
<ASSISTANT_TASK:>
Python Code:
import numpy as np
from scipy.optimize import minimize 
x0 = [0,0]
x1 = [0, 2209]
Explanation: Antenna Position Measurement
Author: Tim Molteno. tim@elec.ac.nz.
The antennas are laid out on tiles, and these tiles are placed on site. Once this is done, a survey is needed to refine the positions of each antenna in the array.
Three reference posts are placed. The first is at the centre of the array, the second approximately 2.5 meters due north. The first and second post defines the $y$ axis. The third post is placed approximately 2.5 meters east of the centre, and reasonably close to at right angles to the $y$ axis.
The first reference point, x0, has coordinates (0,0). The second reference point, x1, has coordinates (0, y) and the third is not known, but must be established by measurement.
All measurements are made from the height of the antennas on the reference points.
End of explanation
d_0_2 = 2047
d_1_2 = 3020
Explanation: Locating the third reference point
The distances from reference point 2 to the other two reference points (0,1) are measured (in mm)
End of explanation
def dist(a,b):
    return np.sqrt((a[0]-b[0])**2 + (a[1]-b[1])**2)
def f(x):
    return (dist(x0, x) - d_0_2)**2 + (dist(x1, x) - d_1_2)**2
initial_guess = [2047, 0]
res = minimize(f, initial_guess)
x2 = res.x
reference_points = [x1, x0, x2]
reference_points
Explanation: Now a least squares estimator is used to work out the x-y coordinates of the third reference point (x2)
End of explanation
n_ant = 24
m = np.zeros((24,3))
Explanation: Finding the antennas
This is done by measuring the distance from each antenna to the three reference points x0, x1 and x2.
End of explanation
m[0,:] = [1563, 855, 2618]
m[1,:] = [1407, 825, 2355]
m[2,:] = [1750, 765, 2644]
m[3,:] = [839, 1373, 2416]
m[4,:] = [1151, 1422, 2986]
m[5,:] = [842, 1410, 2662]
m[6,:] = [2527, 1119, 929]
m[7,:] = [2274, 1200, 915]
m[8,:] = [2715, 1261, 824]
m[9,:] = [1684, 1064, 1457]
m[10,:] = [2238, 546, 1501]
m[11,:] = [1834, 805, 1493]
m[12,:] = [3320, 1111, 2370]
m[13,:] = [3385, 1192, 2131]
m[14,:] = [3446, 1247, 2555]
m[15,:] = [3063, 1048, 1531]
m[16,:] = [2760, 550, 2096]
m[17,:] = [2873, 784, 1689]
m[18,:] = [2342, 934, 2979]
m[19,:] = [2638, 1142, 3179]
m[20,:] = [2186, 993, 3020]
m[21,:] = [3130, 1260, 3140]
m[22,:] = [2545, 565, 2544]
m[23,:] = [2942, 1000, 2891]
Explanation: The following are the measured distances from [x1, x0, x2] from the reference points in millimeters. Note that their order must be the same as the order of the variable called 'reference_points'. In this case, they are x1,x0,x2.
End of explanation
import requests
import json
pos_url = "https://tart.elec.ac.nz/signal/api/v1/imaging/antenna_positions"
def get_data(path):
    server = "https://tart.elec.ac.nz/signal"
    r = requests.get('{}/{}'.format(server, path))
    return json.loads(r.text)
def get_pos():
    return np.array(get_data('api/v1/imaging/antenna_positions'))
current_pos = get_pos()
current_pos
initial_guess = np.zeros(2*n_ant)
for i in range(n_ant):
    initial_guess[2*i:2*i+2] = current_pos[i][0:2]*1000
    #print(current_pos[i][0:2]*1000)
initial_guess
pos_i = current_pos*1000
import matplotlib.pyplot as plt
plt.scatter(pos_i[:,0], pos_i[:,1])
plt.xlim(-2000,2000)
plt.ylim(-2000,2000)
plt.show()
Explanation: Plot the Initial Guess Points
Initial Guesses are from JSON queried from the telescope API. These are converted to millimeters.
End of explanation
def f(x):
    ret = 0
    for i in range(n_ant):
        for j in range(3):
            p = [x[2*i],x[2*i+1]]
            ret += (dist(reference_points[j], p) - m[i,j])**2
    return ret
print(f(initial_guess))
res = minimize(f, initial_guess)
res
Explanation: Criteria for Optimality
The function below is minimized when the positions (in variable x) are consistent with the measured distances m[i,j]. The initial value of this function is more than 3 million.
Note that the x input is a 1D vector of with 48 entries as [p0.x, p0.y, p1.x, p1.y]
End of explanation
pos = res.x.reshape((24,2))
pos
plt.scatter(pos[:,0], pos[:,1], color='red')
plt.scatter(pos_i[:,0], pos_i[:,1], color='blue')
plt.xlim(-2000,2000)
plt.ylim(-2000,2000)
plt.grid(True)
plt.show()
Explanation: The optimized positions are now known. The final value of the function is 32. Far closer to zero than 3 million!
We can recover the x,y coordinates by reshaping the array
End of explanation
result = np.zeros((n_ant, 3))
result[:,:-1] = np.round(pos/1000.0, 3)
result
json_result = {}
json_result["antenna_positions"] = result.tolist()
print(json.dumps(json_result, indent=4, separators=(',', ': ')))
Explanation: The API expects 3D coordinates (with a z value which is zero in this case). Therefore we add a column of zeros.
End of explanation
<END_TASK>