├── Calculations
├── A.png
├── W.png
├── b.png
├── l.png
├── Cd.png
├── DSl.png
├── sl.png
├── Xhat.png
└── Elevations:Variance.png
├── Matrices
├── ATWA Matrix.png
├── (ATWA)^-1 Matrix.png
├── Design (A) Matrix.png
├── Weight (Wd) Matrix.png
├── Double Differencing (D) Matrix.png
├── Single Differencing (S) Matrix.png
├── Covariance matrix of observations (cl) Matrix.png
└── covariance matrix of the double differences (Cd) Matrix.png
├── Diagrams
├── DD_Diagram_aid.png
├── Method_Flowchart.png
├── Satellite_Angles.png
└── Satellite_Elevation.png
├── Vectors
├── Degrees Vector.png
├── Radians Vector.png
├── Variances Vector.png
├── Observed - Computed Vector.png
├── Double differences (Dsl) Vector.png
├── Vector of observations (l) Vector.png
└── Vector of single differences (sl) Vector.png
├── Graphs
└── Z Scores of Observed - Computed.png
├── Todo.py
├── Quality_Assessment.py
├── Original_Data
└── GNSS_Data_(text).txt
├── Computations.py
├── Epoch2.py
├── Epoch3.py
├── main.py
└── README.md
/Calculations/A.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Calculations/A.png
--------------------------------------------------------------------------------
/Calculations/W.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Calculations/W.png
--------------------------------------------------------------------------------
/Calculations/b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Calculations/b.png
--------------------------------------------------------------------------------
/Calculations/l.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Calculations/l.png
--------------------------------------------------------------------------------
/Calculations/Cd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Calculations/Cd.png
--------------------------------------------------------------------------------
/Calculations/DSl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Calculations/DSl.png
--------------------------------------------------------------------------------
/Calculations/sl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Calculations/sl.png
--------------------------------------------------------------------------------
/Calculations/Xhat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Calculations/Xhat.png
--------------------------------------------------------------------------------
/Matrices/ATWA Matrix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Matrices/ATWA Matrix.png
--------------------------------------------------------------------------------
/Diagrams/DD_Diagram_aid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Diagrams/DD_Diagram_aid.png
--------------------------------------------------------------------------------
/Vectors/Degrees Vector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Vectors/Degrees Vector.png
--------------------------------------------------------------------------------
/Vectors/Radians Vector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Vectors/Radians Vector.png
--------------------------------------------------------------------------------
/Vectors/Variances Vector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Vectors/Variances Vector.png
--------------------------------------------------------------------------------
/Diagrams/Method_Flowchart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Diagrams/Method_Flowchart.png
--------------------------------------------------------------------------------
/Diagrams/Satellite_Angles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Diagrams/Satellite_Angles.png
--------------------------------------------------------------------------------
/Matrices/(ATWA)^-1 Matrix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Matrices/(ATWA)^-1 Matrix.png
--------------------------------------------------------------------------------
/Matrices/Design (A) Matrix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Matrices/Design (A) Matrix.png
--------------------------------------------------------------------------------
/Diagrams/Satellite_Elevation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Diagrams/Satellite_Elevation.png
--------------------------------------------------------------------------------
/Matrices/Weight (Wd) Matrix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Matrices/Weight (Wd) Matrix.png
--------------------------------------------------------------------------------
/Calculations/Elevations:Variance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Calculations/Elevations:Variance.png
--------------------------------------------------------------------------------
/Vectors/Observed - Computed Vector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Vectors/Observed - Computed Vector.png
--------------------------------------------------------------------------------
/Graphs/Z Scores of Observed - Computed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Graphs/Z Scores of Observed - Computed.png
--------------------------------------------------------------------------------
/Matrices/Double Differencing (D) Matrix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Matrices/Double Differencing (D) Matrix.png
--------------------------------------------------------------------------------
/Matrices/Single Differencing (S) Matrix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Matrices/Single Differencing (S) Matrix.png
--------------------------------------------------------------------------------
/Vectors/Double differences (Dsl) Vector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Vectors/Double differences (Dsl) Vector.png
--------------------------------------------------------------------------------
/Vectors/Vector of observations (l) Vector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Vectors/Vector of observations (l) Vector.png
--------------------------------------------------------------------------------
/Vectors/Vector of single differences (sl) Vector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Vectors/Vector of single differences (sl) Vector.png
--------------------------------------------------------------------------------
/Matrices/Covariance matrix of observations (cl) Matrix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Matrices/Covariance matrix of observations (cl) Matrix.png
--------------------------------------------------------------------------------
/Todo.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | # TODO: Label all units
4 | # TODO: Redraw elevations drawing
5 | # TODO Fix Cl Matrix - Needs to be wider
6 | # TODO: Give the vectors clearer labels - maybe some hand calculations
--------------------------------------------------------------------------------
/Matrices/covariance matrix of the double differences (Cd) Matrix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThomasJames/GNSS_Double_Differencing/HEAD/Matrices/covariance matrix of the double differences (Cd) Matrix.png
--------------------------------------------------------------------------------
/Quality_Assessment.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | from statistics import mean
3 | from scipy.stats import zscore
4 | import seaborn as sns
5 |
6 |
7 | x = [4929605.54130538, 4929605.5410819, 4929605.53911663]
8 | y = [-29123.82730074, -29123.82767789, -29123.82739046]
9 | z = [4033603.93206799, 4033603.93099312, 4033603.93271809]
10 |
11 |
12 | # Get the mean of the 3 epochs.
13 | x = mean(x)
14 | y = mean(y)
15 | z = mean(z)
16 |
17 |
18 | # collect the residuals from the three epochs
19 | b = [9.29757378e-01, 2.52782310e-04, 9.89184036e-01, 2.90583952e-01, 9.64209316e-01, -1.13994574e-01,7.92398563e-02,
20 | 0.94013581, 0.00273867, 0.97883069, 0.28919151, 0.97179478, -0.10708197, 0.08768272, 0.93938436, 0.01976735,
21 | 0.98053831, 0.29998728, 0.97263572, -0.10390881, 0.06579685]
22 |
23 | # Get the Z-Scores of the b vector
24 | b_z = zscore(b)
25 |
26 | # Plot the Z-Scores
27 | sns.distplot(b_z, bins=200, kde=False)
28 | plt.xlabel("Z Scores")
29 | plt.ylabel("Frequency")
30 | plt.title("Z Scores of Observed - Computed")
31 | plt.savefig("Graphs/Z Scores of Observed - Computed")
32 | plt.show()
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/Original_Data/GNSS_Data_(text).txt:
--------------------------------------------------------------------------------
1 | Data from Leica receivers and choke-ring antennas placed ~94.4m apart.
2 | Polytechnic University of Valencia (UPV) calibration baseline.
3 |
4 |
5 | ECEF BASE (Pillar 1A) POSITION (m): (assumed as phase centre)
6 | -----------------------------------
7 | 4929635.440
8 | -29041.877
9 | 4033567.846
10 |
11 |
12 | ECEF ROVER (Pillar 3A) POSITION (m): (a good approximation of phase centre)
13 | ------------------------------------
14 | 4929605.542
15 | -29123.828
16 | 4033603.932
17 |
18 | BASE: PL1A3201.16o (Pillar 1A)
19 | ROVER: PL3A3201.16o (Pillar 3A)
20 |
21 |
22 | BASE OBSERVATIONS (Pillar 1A) C1C (metres), L1C (L1 cycles)
23 | -----------------------------
24 | > 2016 11 15 22 19 5
25 | G10 23726969.123 124686036.295
26 | G12 20647534.024 108503516.027
27 | G13 23087780.798 121327099.499
28 | G15 21346539.664 112176830.803
29 | G17 23379753.757 122861442.012
30 | G18 23217805.737 122010370.583
31 | G19 22181729.713 116565751.296
32 | G24 20436699.926 107395596.426
33 | > 2016 11 15 22 19 6
34 | G10 23726938.331 124685874.438
35 | G12 20647192.450 108501721.043
36 | G13 23088426.022 121330490.145
37 | G15 21346983.784 112179164.661
38 | G17 23380013.060 122862804.668
39 | G18 23218019.358 122011493.172
40 | G19 22181749.153 116565853.440
41 | G24 20436639.939 107395281.178
42 | > 2016 11 15 22 19 7
43 | G10 23726907.612 124685712.984
44 | G12 20646850.932 108499926.342
45 | G13 23089071.250 121333880.840
46 | G15 21347427.948 112181498.752
47 | G17 23380272.423 122864167.659
48 | G18 23218233.033 122012615.974
49 | G19 22181768.684 116565956.073
50 | G24 20436579.966 107394966.011
51 |
52 | ROVER OBSERVATIONS (Pillar 3A) C1C (metres), L1C (L1 cycles)
53 | ------------------------------
54 | > 2016 11 15 22 19 5
55 | G10 23726881.094 124685588.685
56 | G12 20647514.655 108503447.644
57 | G13 23087860.345 121327512.345
58 | G15 21346576.786 112177022.660
59 | G17 23379790.820 122861635.973
60 | G18 23217736.821 122010019.631
61 | G19 22181785.598 116566080.299
62 | G24 20436682.002 107395502.123
63 | > 2016 11 15 22 19 6
64 | G10 23726850.239 124685426.535
65 | G12 20647173.023 108501652.350
66 | G13 23088505.515 121330902.710
67 | G15 21347020.856 112179356.269
68 | G17 23380050.064 122862998.307
69 | G18 23217950.391 122011141.955
70 | G19 22181804.968 116566182.098
71 | G24 20436621.970 107395186.650
72 | > 2016 11 15 22 19 7
73 | G10 23726819.469 124685264.860
74 | G12 20646831.462 108499857.433
75 | G13 23089150.708 121334293.217
76 | G15 21347464.989 112181690.196
77 | G17 23380309.383 122864361.034
78 | G18 23218164.027 122012264.591
79 | G19 22181824.450 116566284.483
80 | G24 20436561.968 107394871.335
81 |
82 |
83 | ECEF SATELLITE POSITIONS X, Y, Z (m) (already corrected for earth rotation during signal travel time)
84 | ----------------------------
85 | 2016 11 15 22 19 5
86 | G10 4634093.207 -19899701.050 16933747.321
87 | G12 22559170.178 -8979632.676 10377257.530
88 | G13 23277536.897 12575815.276 -2029027.200
89 | G15 25950462.808 2443858.353 5881092.070
90 | G17 5785091.956 16827408.400 20125597.869
91 | G18 13564948.214 -21357948.777 8232124.013
92 | G19 12262838.101 17165601.305 15682863.092
93 | G24 15569244.807 -1039249.482 21443791.252
94 |
95 | 2016 11 15 22 19 6
96 | G10 4635830.310 -19900984.252 16931753.611
97 | G12 22558227.890 -8978660.026 10380129.659
98 | G13 23277208.631 12575940.142 -2032215.535
99 | G15 25951110.653 2444379.287 5878128.296
100 | G17 5782765.465 16826578.194 20126966.693
101 | G18 13565919.388 -21358354.010 8229256.854
102 | G19 12260892.186 17164934.342 15685108.806
103 | G24 15569832.291 -1036572.061 21443516.954
104 |
105 | 2016 11 15 22 19 7
106 | G10 4637567.153 -19902267.390 16929759.540
107 | G12 22557285.375 -8977687.092 10383001.564
108 | G13 23276880.010 12576064.854 -2035403.827
109 | G15 25951758.170 2444900.089 5875164.399
110 | G17 5780438.765 16825748.070 20128335.104
111 | G18 13566890.289 -21358759.047 8226389.520
112 | G19 12258945.969 17164267.376 15687354.176
113 | G24 15570419.916 -1033894.709 21443242.199
114 |
115 |
116 | STANDARD DEVIATIONS
117 | -----------------------------------------------------
118 |
119 | L1C standard deviation (s): 0.003
120 | L1C variance for satellite at elevation angle E: s*s/sin(E)
121 |
122 | ----------------------
123 | NOTE: Satellite G24 has the highest elevation: 71 degrees.
124 |
125 | Phase ambiguities for each epoch and each phase measurement:
126 |
127 | 2016 11 15 22 19 5
128 |
129 | Before ambig resolution:
130 | 4929605.364
131 | -29123.817
132 | 4033603.867
133 | 12.564 (G24-G10)
134 | 34.873 (G24-G12)
135 | -3.838 (G24-G13)
136 | -4.170 (G24-G15)
137 | 1.538 (G24-G17)
138 | 11.324 (G24-G18)
139 | 34.352 (G24-G19)
140 |
141 | After ambig resolution (LAMBDA):
142 | 4929605.542
143 | -29123.828
144 | 4033603.932
145 | 12.000 (G24-G10)
146 | 35.000 (G24-G12)
147 | -4.000 (G24-G13)
148 | -4.000 (G24-G15)
149 | 1.000 (G24-G17)
150 | 11.000 (G24-G18)
151 | 34.000 (G24-G19)
152 |
153 | 2016 11 15 22 19 6
154 |
155 | Before ambig resolution:
156 | 4929605.363
157 | -29123.817
158 | 4033603.866
159 | 12.560 (G24-G10)
160 | 34.866 (G24-G12)
161 | -3.842 (G24-G13)
162 | -4.168 (G24-G15)
163 | 1.553 (G24-G17)
164 | 11.325 (G24-G18)
165 | 34.345 (G24-G19)
166 |
167 | After ambig resolution (LAMBDA):
168 | 4929605.541
169 | -29123.828
170 | 4033603.931
171 | 12.000 (G24-G10)
172 | 35.000 (G24-G12)
173 | -4.000 (G24-G13)
174 | -4.000 (G24-G15)
175 | 1.000 (G24-G17)
176 | 11.000 (G24-G18)
177 | 34.000 (G24-G19)
178 |
179 | 2016 11 15 22 19 7
180 |
181 | Before ambig resolution:
182 | 4929605.358
183 | -29123.821
184 | 4033603.865
185 | 12.580 (G24-G10)
186 | 34.879 (G24-G12)
187 | -3.850 (G24-G13)
188 | -4.172 (G24-G15)
189 | 1.541 (G24-G17)
190 | 11.371 (G24-G18)
191 | 34.342 (G24-G19)
192 |
193 | After ambig resolution (LAMBDA):
194 | 4929605.540
195 | -29123.828
196 | 4033603.933
197 | 12.000 (G24-G10)
198 | 35.000 (G24-G12)
199 | -4.000 (G24-G13)
200 | -4.000 (G24-G15)
201 | 1.000 (G24-G17)
202 | 11.000 (G24-G18)
203 | 34.000 (G24-G19)
--------------------------------------------------------------------------------
/Computations.py:
--------------------------------------------------------------------------------
1 | from numpy import transpose, linalg
2 | import matplotlib.pyplot as plt
3 | import seaborn as sns
4 | from math import sqrt, sin, degrees, acos, radians
5 | from typing import List
6 |
7 |
8 | class DD:
9 | """"
10 | ref_station - Earth centric Cartesian Coordinates of the reference station [X, Y, Z]
11 | corresponding_sat - Earth centric cartesian Coordinates of the corresponding station [X, Y, Z]
12 | sat_ref - Satellite reference of cartesian Coordinates of the reference station [X, Y, Z]
13 | brrs - Base receiver to reference satellite
14 | rrrs - Reference receiver to reference satellite
15 | brcs - Base receiver to corresponding satellite
16 | rrcs - Reference receiver to corresponding satellite
17 | c = speed of light in vacuum (299792458.0 ms-1) - Set to default
18 | f = signal frequency (L1: 1575.42MHz, L2: 1227.6MHz) either L1 or L2 can be True
19 | λ=𝑐/𝑓 - Wavelength calculated from c and f
20 | """""
21 |
22 | def __init__(self, ref_station: List[float] = None,
23 | rov_station: List[float] = None,
24 | corresponding_sat: List[float] = None,
25 | sat_ref: List[float] = None,
26 | L1: bool = True,
27 | L2: bool = False,
28 | observed: float = None):
29 |
30 | # Speed of light m/s
31 | c: float = 299792458.0
32 |
33 | # Signal frequency of L1 (MHz)
34 | L1_f: float = 1575.42 * 1000000
35 |
36 | # Signal frequency of L2
37 | L2_f: float = 1227.6 * 1000000
38 |
39 | # Set to True by default
40 | if L1:
41 | wl = c / L1_f
42 |
43 | if L2:
44 | wl = c / L2_f
45 |
46 | # Error check the arguments
47 | assert len(ref_station) == len(rov_station) == len(corresponding_sat) == len(sat_ref)
48 | assert L1 != L2
49 |
50 | # Compute ranges from satellite coordinates
51 | brrs: float = self.distance(ref_station, sat_ref)
52 | rrrs: float = self.distance(rov_station, sat_ref)
53 | brcs: float = self.distance(ref_station, corresponding_sat)
54 | rrcs: float = self.distance(rov_station, corresponding_sat)
55 |
56 | # Initialise the class variables
57 | self.x_1A = ref_station[0]
58 | self.y_1A = ref_station[1]
59 | self.z_1A = ref_station[2]
60 | self.x_3A = rov_station[0]
61 | self.y_3A = rov_station[1]
62 | self.z_3A = rov_station[2]
63 | self.x_s = corresponding_sat[0]
64 | self.y_s = corresponding_sat[1]
65 | self.z_s = corresponding_sat[2]
66 | self.x_s_ref = sat_ref[0]
67 | self.y_s_ref = sat_ref[1]
68 | self.z_s_ref = sat_ref[2]
69 | self.wl = wl
70 | self.brrs = brrs
71 | self.rrrs = rrrs
72 | self.brcs = brcs
73 | self.rrcs = rrcs
74 | self.observed = observed
75 |
76 | def x_diff(self) -> float:
77 | return float(1 / self.wl * \
78 | (
79 | (self.x_3A - self.x_s) /
80 | (sqrt(((self.x_s - self.x_3A) ** 2) +
81 | ((self.y_s - self.y_3A) ** 2) +
82 | ((self.z_s - self.z_3A) ** 2)))
83 | -
84 | (self.x_3A - self.x_s_ref) /
85 | (sqrt(((self.x_s_ref - self.x_3A) ** 2) +
86 | ((self.y_s_ref - self.y_3A) ** 2) +
87 | ((self.z_s_ref - self.z_3A) ** 2)))))
88 |
89 | def y_diff(self) -> float:
90 | return float((1 / self.wl * \
91 | (
92 | (self.y_3A - self.y_s) /
93 | (sqrt(((self.x_s - self.x_3A) ** 2) +
94 | ((self.y_s - self.y_3A) ** 2) +
95 | ((self.z_s - self.z_3A) ** 2)))
96 | -
97 | (self.y_3A - self.y_s_ref) /
98 | (sqrt(((self.x_s_ref - self.x_3A) ** 2) +
99 | ((self.y_s_ref - self.y_3A) ** 2) +
100 | ((self.z_s_ref - self.z_3A) ** 2))))))
101 |
102 | def z_diff(self) -> float:
103 | return float(1 / self.wl * \
104 | (
105 | (self.z_3A - self.z_s) /
106 | (sqrt(((self.x_s - self.x_3A) ** 2) +
107 | ((self.y_s - self.y_3A) ** 2) +
108 | ((self.z_s - self.z_3A) ** 2)))
109 | -
110 | (self.z_3A - self.z_s_ref) /
111 | (sqrt(((self.x_s_ref - self.x_3A) ** 2) +
112 | ((self.y_s_ref - self.y_3A) ** 2) +
113 | ((self.z_s_ref - self.z_3A) ** 2)))))
114 |
115 | def calc_b_vector(self) -> float:
116 | # observed - The vector of measured quantities
117 | o = self.observed
118 |
119 | # Computed
120 | c = (1 / self.wl) * (self.brrs - self.rrrs - self.brcs + self.rrcs)
121 | return o - c
122 |
123 | def distance(self, point_1: List[float], point_2: List[float]) -> float:
124 | """""
125 | Find the difference between two points given sets of [X, Y, Z] coordinates.
126 | """""
127 | return sqrt((point_2[0] - point_1[0]) ** 2 +
128 | (point_2[1] - point_1[1]) ** 2 +
129 | (point_2[2] - point_1[2]) ** 2)
130 |
131 |
132 | class Variance:
133 |
134 | def __init__(self, sat_coords: List[float],
135 | receiver_coords: List[float],
136 | L1: bool == True):
137 | if L1:
138 | l1_std = 0.003
139 |
140 | assert len(sat_coords) == len(receiver_coords)
141 |
142 | self.l1_std = l1_std
143 | self.sat_coords = sat_coords
144 | self.receiver_coords = receiver_coords
145 |
146 | def elevation_calculator(self) -> float:
147 | """"
148 | This method calculates the satellite angle of elevation in the following stages:
149 | Calculates the distance of receiver to the satellite (m) using pythagoras theorem.
150 | Calculates the distance between the earth center and the satellite (m) using pythagoras theorem.
151 | Calculates the distance between the earth center and the receiver (m) using pythagoras theorem.
152 | These ranges make up a scalene triangle, where all ranges are known.
153 | The low of cosines is used to calculate the angle about the receiver in degrees.
154 | 90 is subtracted from this angle to get the local elevation angle.
155 | """""
156 |
157 | # Extract ECEF distances (m)
158 | x_s, y_s, z_s = self.sat_coords[0], self.sat_coords[1], self.sat_coords[2]
159 | x_r, y_r, z_r = self.receiver_coords[0], self.receiver_coords[1], self.receiver_coords[2]
160 |
161 | # Distance from receiver to satellite (m)
162 | r_s = sqrt((x_s - x_r) ** 2 + (y_s - y_r) ** 2 + (z_s - z_r) ** 2)
163 |
164 | # Distance from earth center to satellite (m)
165 | ec_s = sqrt((sqrt(x_s ** 2 + y_s ** 2)) ** 2 + z_s ** 2)
166 |
167 | # Distance from earth center to receiver (m)
168 | ec_r = sqrt((sqrt(x_r ** 2 + y_r ** 2)) ** 2 + z_r ** 2)
169 |
170 | # Angle from the local horizontal to the satellite (m)
171 | angle_radians = radians((degrees(acos((ec_r ** 2 + r_s ** 2 - ec_s ** 2) / (2 * ec_r * r_s)))) - 90)
172 |
173 | return angle_radians
174 |
175 | def variance(self):
176 | """""
177 | This method then calculates the variance of the satellite at the calculated angle.
178 | returns the variance as a float
179 | """""
180 | # Variance (uncertainty associated with the satellite) (m)
181 | variance = (self.l1_std ** 2) / (sin(self.elevation_calculator()))
182 |
183 | return variance
184 |
185 |
186 | class MatrixOperations:
187 | """"
188 | Matrix Operations
189 |
190 | D - Double differencing matrix
191 | S - Single differencing matrix
192 | Cl - Covariance matrix of observations
193 | A - Design matrix
194 | W - Weight matrix
195 | b - B (innovation vector?)
196 | """""
197 |
198 | def __init__(self, D=None, S=None, Cl=None, A=None, W=None, b=None):
199 | self.D = D
200 | self.S = S
201 | self.Cl = Cl
202 | self.A = A
203 | self.W = W
204 | self.b = b
205 |
206 | def Cd_calculator(self):
207 | try:
208 | return (((self.D.dot(self.S)).dot(self.Cl)).dot(transpose(self.S))).dot(transpose(self.D))
209 | except IOError:
210 | print("Cd_calculator failed")
211 |
212 | def calculate_x_hat(self):
213 | try:
214 | return \
215 | ((linalg.inv((transpose(self.A).dot(self.W)).dot(self.A))).dot(transpose(self.A))
216 | .dot(self.W)).dot(self.b)
217 | except IOError:
218 | print("Calculate_x_hat failed")
219 |
220 | def ATWA(self):
221 | try:
222 | return ((transpose(self.A)).dot(self.W)).dot(self.A)
223 | except IOError:
224 | print("ATWA failed")
225 |
226 |
227 | def matrix_heatmap(matrix, name: str) -> None:
228 | sns.heatmap(matrix,
229 | annot=True,
230 | xticklabels=False,
231 | yticklabels=False,
232 | cmap="Blues",
233 | cbar=False, )
234 | plt.title(f"{name} Matrix")
235 | plt.savefig(f"Matrices/{name} Matrix")
236 | plt.show()
237 |
238 |
239 | def vector_heatmap(matrix, name: str):
240 | sns.heatmap(matrix,
241 | annot=True,
242 | xticklabels=False,
243 | yticklabels=False,
244 | cmap="Blues",
245 | cbar=False, )
246 | plt.title(f"{name} Matrix")
247 | plt.savefig(f"Vectors/{name} Vector")
248 | plt.show()
249 |
250 |
251 | def flipped_vector_heatmap(data, name: str):
252 | vec2 = data.reshape(data.shape[0], 1)
253 | ax = sns.heatmap((vec2),
254 | annot=True,
255 | xticklabels=False,
256 | yticklabels=False,
257 | cmap="Blues",
258 | cbar=False,
259 | fmt='g')
260 | plt.title(f"{name} Vector")
261 | plt.savefig(f"Vectors/{name} Vector")
262 | plt.show()
263 |
--------------------------------------------------------------------------------
/Epoch2.py:
--------------------------------------------------------------------------------
1 | from math import sqrt, cos, sin, degrees, acos
2 | import numpy as np
3 | from numpy import transpose, linalg
4 | from Computations import DD, Variance, matrix_heatmap, vector_heatmap, flipped_vector_heatmap, MatrixOperations
5 | import matplotlib.pyplot as plt
6 | import seaborn as sns
7 | from typing import List
8 |
9 | """
10 | GOAL: Calculate the coordinates of the reference antenna (ARP) of the roving receiver
11 |
12 | Try to get your answer close to the figures for 3A. The nominal coordinates given mean you do not need to iterate the
13 | least squares solution, you should converge on the answer with on round of matrix inversion
14 | The data contains 1 of the three epochs of phase and pseudorange observations measured on a calibration baseline in
15 | valencia, spain.
16 | The sensors used are geodetic quality receivers using choke ring antennas.
17 | Pillar 1A is treated as the reference receiver.
18 | Pillar 3A is treated as the monument.
19 | """
20 |
21 | # X, Y, Z ECEF coordinates for the phase center of the receiver
22 | pillar_1A_base = np.array([[4929635.440], [-29041.877], [4033567.846]]) # Reference receiver
23 |
24 | # Trying to reproduce these coordinates
25 | pillar_3A_rover = np.array([[4929605.400], [-29123.700], [4033603.800]]) # Monument
26 | distance_between_receivers = 94.4 # Measured in meters / Approximate
27 | l1_SD = 0.003
28 |
29 | """
30 | ECEF coordinates (m) of the satellite phase centers when they transmitted the signals measured at each epoch.
31 | """
32 |
33 | # Make a note of the L1 Wl
34 | wl = 0.19029367
35 |
36 | # ECEF SATELLITE POSITIONS X, Y, Z (m) (already corrected for earth rotation during signal travel time)
37 | # 2016 11 15 22 19 6
38 |
39 | G10 = [4635830.310, -19900984.252, 16931753.611]
40 | G12 = [22558227.890, -8978660.026, 10380129.659]
41 | G13 = [23277208.631, 12575940.142, -2032215.535]
42 | G15 = [25951110.653, 2444379.287, 5878128.296]
43 | G17 = [ 5782765.465, 16826578.194, 20126966.693]
44 | G18 = [13565919.388, -21358354.010, 8229256.854]
45 | G19 = [12260892.186, 17164934.342, 15685108.806]
46 | G24 = [15569832.291, -1036572.061, 21443516.954]
47 |
48 | """
49 | Use double differenced phase measurements, from the first epoch of data only 2016_11_15_22_19_5
50 | TO compute the precise coordinates of the pillar 3A sensor phase center.
51 | These are pseudo range measurements
52 | """
53 |
54 | # BASE OBSERVATIONS (Pillar 1A) C1C (metres), L1C (L1 cycles)
55 | # 2016_11_15_22_19_5
56 |
57 | G24_base_obs = [20436639.939, 107395281.178]
58 | G19_base_obs = [22181749.153, 116565853.440]
59 | G18_base_obs = [23218019.358, 122011493.172]
60 | G17_base_obs = [23380013.060, 122862804.668]
61 | G15_base_obs = [21346983.784, 112179164.661]
62 | G13_base_obs = [23088426.022, 121330490.145]
63 | G12_base_obs = [20647192.450, 108501721.043]
64 | G10_base_obs = [23726938.331, 124685874.438]
65 |
66 | # ROVER OBSERVATIONS (Pillar 3A) C1C (metres), L1C (L1 cycles)
67 | # 2016 11 15 22 19 6 (Second Epoch)
68 | # meters, cycles
69 |
70 | G24_rover_obs = [20436621.970, 107395186.650]
71 | G19_rover_obs = [22181804.968, 116566182.098]
72 | G18_rover_obs = [23217950.391, 122011141.955]
73 | G17_rover_obs = [23380050.064, 122862998.307]
74 | G15_rover_obs = [21347020.856, 112179356.269]
75 | G13_rover_obs = [23088505.515, 121330902.710]
76 | G12_rover_obs = [20647173.023, 108501652.350]
77 | G10_rover_obs = [23726850.239, 124685426.535]
78 |
79 | # At the first epoch we have 16 raw phase observations in cycles.
80 | l = np.transpose([
81 | G24_base_obs[1],
82 | G24_rover_obs[1],
83 | G19_base_obs[1],
84 | G19_rover_obs[1],
85 | G18_base_obs[1],
86 | G18_rover_obs[1],
87 | G17_base_obs[1],
88 | G17_rover_obs[1],
89 | G15_base_obs[1],
90 | G15_rover_obs[1],
91 | G13_base_obs[1],
92 | G13_rover_obs[1],
93 | G12_base_obs[1],
94 | G12_rover_obs[1],
95 | G10_base_obs[1],
96 | G10_rover_obs[1]])
97 |
98 | """
99 | Phase Ambiguity terms (N) for each measurement, before and after ambiguity resolution
100 | Use integer terms in computations
101 | 2016 11 15 22 19 5
102 | G24 is a reference satellite - and has the highest
103 | """
104 |
105 | # Phase ambiguities for each epoch and each phase measurement:
106 | before_ambiguity_resolution = np.array([[4929605.364], [-29123.817], [4033603.867]])
107 | G24toG19_before = 34.345
108 | G24toG18_before = 1.325
109 | G24toG17_before = 1.553
110 | G24toG15_before = -4.168
111 | G24toG13_before = -3.842
112 | G24toG12_before = 34.866
113 | G24toG10_before = 12.560
114 |
115 | after_ambiguity_resolution = np.array([[4929605.542], [-29123.828], [4033603.932]])
116 | G24toG19_after = 34.000
117 | G24toG18_after = 11.000
118 | G24toG17_after = 1.000
119 | G24toG15_after = -4.000
120 | G24toG13_after = -4.000
121 | G24toG12_after = 35.000
122 | G24toG10_after = 12.000
123 |
124 | a_a_r = [G24toG19_after,
125 | G24toG18_after,
126 | G24toG17_after,
127 | G24toG15_after,
128 | G24toG13_after,
129 | G24toG12_after,
130 | G24toG10_after]
131 |
132 | G24_base_var = Variance(sat_coords=G24, receiver_coords=pillar_1A_base, L1=True)
133 | G24_rover_var = Variance(sat_coords=G24, receiver_coords=pillar_3A_rover, L1=True)
134 | G19_base_var = Variance(sat_coords=G19, receiver_coords=pillar_1A_base, L1=True)
135 | G19_rover_var = Variance(sat_coords=G19, receiver_coords=pillar_3A_rover, L1=True)
136 | G18_base_var = Variance(sat_coords=G18, receiver_coords=pillar_1A_base, L1=True)
137 | G18_rover_var = Variance(sat_coords=G18, receiver_coords=pillar_3A_rover, L1=True)
138 | G17_base_var = Variance(sat_coords=G17, receiver_coords=pillar_1A_base, L1=True)
139 | G17_rover_var = Variance(sat_coords=G17, receiver_coords=pillar_3A_rover, L1=True)
140 | G15_base_var = Variance(sat_coords=G15, receiver_coords=pillar_1A_base, L1=True)
141 | G15_rover_var = Variance(sat_coords=G15, receiver_coords=pillar_3A_rover, L1=True)
142 | G13_base_var = Variance(sat_coords=G13, receiver_coords=pillar_1A_base, L1=True)
143 | G13_rover_var = Variance(sat_coords=G13, receiver_coords=pillar_3A_rover, L1=True)
144 | G12_base_var = Variance(sat_coords=G12, receiver_coords=pillar_1A_base, L1=True)
145 | G12_rover_var = Variance(sat_coords=G12, receiver_coords=pillar_3A_rover, L1=True)
146 | G10_base_var = Variance(sat_coords=G10, receiver_coords=pillar_1A_base, L1=True)
147 | G10_rover_var = Variance(sat_coords=G10, receiver_coords=pillar_3A_rover, L1=True)
148 |
149 | elevations_radians = np.array([
150 | [G24_base_var.elevation_calculator()],
151 | [G24_rover_var.elevation_calculator()],
152 | [G19_base_var.elevation_calculator()],
153 | [G19_rover_var.elevation_calculator()],
154 | [G18_base_var.elevation_calculator()],
155 | [G18_rover_var.elevation_calculator()],
156 | [G17_base_var.elevation_calculator()],
157 | [G17_rover_var.elevation_calculator()],
158 | [G15_base_var.elevation_calculator()],
159 | [G15_rover_var.elevation_calculator()],
160 | [G13_base_var.elevation_calculator()],
161 | [G13_rover_var.elevation_calculator()],
162 | [G12_base_var.elevation_calculator()],
163 | [G12_rover_var.elevation_calculator()],
164 | [G10_base_var.elevation_calculator()],
165 | [G10_rover_var.elevation_calculator()]])
166 |
167 | satelltie_names = np.array([["Base to G19"],
168 | ["Rover to G19"],
169 | ["Base to G18"],
170 | ["Rover to G18"],
171 | ["Base to G17"],
172 | ["Rover to G17"],
173 | ["Base to G15"],
174 | ["Rover to G15"],
175 | ["Base to G13"],
176 | ["Rover to G13"],
177 | ["Base to G12"],
178 | ["Rover to G12"],
179 | ["Base to G10"],
180 | ["Rover to G10"],
181 | ["Base to G24"],
182 | ["Rover to G24"]])
183 |
184 | # Elevations in degrees
185 | elevations_degrees = np.array([degrees(x) for x in elevations_radians])
186 |
187 | variance_vector = np.array([
188 | [G24_base_var.variance()],
189 | [G24_rover_var.variance()],
190 | [G19_base_var.variance()],
191 | [G19_rover_var.variance()],
192 | [G18_base_var.variance()],
193 | [G18_rover_var.variance()],
194 | [G17_base_var.variance()],
195 | [G17_rover_var.variance()],
196 | [G15_base_var.variance()],
197 | [G15_rover_var.variance()],
198 | [G13_base_var.variance()],
199 | [G13_rover_var.variance()],
200 | [G12_base_var.variance()],
201 | [G12_rover_var.variance()],
202 | [G10_base_var.variance()],
203 | [G10_rover_var.variance()]])
204 |
205 | # 16 x 8: Differencing matrix
206 | S = np.array([[1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
207 | [0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
208 | [0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
209 | [0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0],
210 | [0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0],
211 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0],
212 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0],
213 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1]])
214 |
215 | D = np.array([[1, -1, 0, 0, 0, 0, 0, 0],
216 | [1, 0, -1, 0, 0, 0, 0, 0],
217 | [1, 0, 0, -1, 0, 0, 0, 0],
218 | [1, 0, 0, 0, -1, 0, 0, 0],
219 | [1, 0, 0, 0, 0, -1, 0, 0],
220 | [1, 0, 0, 0, 0, 0, -1, 0],
221 | [1, 0, 0, 0, 0, 0, 0, -1]])
222 |
223 | if __name__ == "__main__":
224 |
225 | flipped_vector_heatmap(variance_vector, "Variances")
226 | flipped_vector_heatmap(elevations_radians, "Radians")
227 | flipped_vector_heatmap(elevations_degrees, "Degrees")
228 |
229 | """
230 | l vector - The vector of raw observations
231 | """
232 |
233 | flipped_vector_heatmap(l, "Vector of observations (l)")
234 |
235 | """
236 | For purposes of single differencing and double differencing.
237 | The D and S are created.
238 | D DIM: 7 x 8
239 | S DIM: 8 X 16
240 | """
241 |
242 | matrix_heatmap(S, "Single Differencing (S)")
243 |
244 | matrix_heatmap(D, "Double Differencing (D)")
245 |
246 | """
247 | SINGLE DIFFERENCING
248 | As receiver 1A and 3A are relatively close together (9.4m)
249 | The ionspheric and tropospheric terms cancel out
250 | Therefore we can calculate receiver-receiver single difference (RRSD)
251 | sl = vector of receiver-receiver single differences
252 | DIM: 8 x 1
253 | """
254 |
255 | sl = S.dot(l)
256 |
257 | flipped_vector_heatmap(sl, "Vector of single differences (sl)")
258 |
259 | """
260 | DOUBLE DIFFERENCING
261 |
262 | We choose the satellite with the highest elevation as the reference satellite.
263 | This will be G24 - This satellite will have the least noise and multipath
264 |
265 | Dsl = vector of double differences
266 | DIM: 7 x 1
267 | """
268 |
269 | Dsl = D.dot(sl)
270 |
271 | """
272 | Subtract the corresponding ambiguity resolved phase ambiguity term.
273 | """
274 | DD_s_p_a = []
275 | for i in range(len(Dsl)):
276 | DD_s_p_a.append(Dsl[i] - a_a_r[i])
277 |
278 | flipped_vector_heatmap(Dsl, "Double differences (Dsl)")
279 |
280 | """
281 | Covariance matrix of the observation vector.
282 | This is an identity matrix scaled by the variance.
283 | It is assumed that the variance of each raw phase observation has the same l1 standard deviation is 0.003.
284 | Therefore variance is this value squared.
285 | UNITS: Cycles
286 | DIM: 16 x 16
287 | """
288 |
289 | cl = np.eye(16, 16) * (1 / wl * variance_vector) # back to cycles
290 | matrix_heatmap(cl, "Covariance matrix of observations (cl)")
291 |
292 | """
293 | Obtain the covariance matrix of the double differences.
294 | This is because we require Wd - The weight matrix of the double difference vector. (The inverse of cd)
295 | Apply gauss's propagation of error law: y = Qx, Cx to work out Cd
296 | Where Q is the matrix operator - No uncertainties associated
297 | Where x is stochastic quantity
298 | Where y is stochastic
299 | Where Cx is the covariance matrix of x and is known
300 |
301 | d = DSl
302 | Cl Exists and is known.
303 |
304 | Use the formula: Cd = DSCl (DS)^T
305 |
306 | DIM: 7 x 7
307 | """
308 |
309 | Cd = MatrixOperations(D=D, S=S, Cl=cl)
310 | Cd = Cd.Cd_calculator()
311 | matrix_heatmap(Cd, "covariance matrix of the double differences (Cd)")
312 |
313 | """
314 | Calculate the weight matrix of the double differences.
315 |
316 | Wd = Cd^-1
317 | DIM: 7 x 7
318 | """
319 |
320 | Wd = linalg.inv(Cd)
321 | matrix_heatmap(Wd, "Weight (Wd)")
322 |
323 | """
324 |
325 | """
326 | G24G19 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G19, sat_ref=G24,
327 | observed=DD_s_p_a[0])
328 |
329 | G24G18 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G18, sat_ref=G24,
330 | observed=DD_s_p_a[1])
331 |
332 | G24G17 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G17, sat_ref=G24,
333 | observed=DD_s_p_a[2])
334 |
335 | G24G15 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G15, sat_ref=G24,
336 | observed=DD_s_p_a[3])
337 |
338 | G24G13 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G13, sat_ref=G24,
339 | observed=DD_s_p_a[4])
340 |
341 | G24G12 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G12, sat_ref=G24,
342 | observed=DD_s_p_a[5])
343 |
344 | G24G10 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G10, sat_ref=G24,
345 | observed=DD_s_p_a[6])
346 |
347 | """
348 | Calculate the b vector:
349 | This is the observed double differencing measurements - the computed.
350 | """
351 | b = np.array([[G24G19.calc_b_vector()],
352 | [G24G18.calc_b_vector()],
353 | [G24G17.calc_b_vector()],
354 | [G24G15.calc_b_vector()],
355 | [G24G13.calc_b_vector()],
356 | [G24G12.calc_b_vector()],
357 | [G24G10.calc_b_vector()]])
358 |
359 | vector_heatmap(b, "Observed - Computed")
360 |
361 | # Populate the design matrix
362 | A = np.array([[G24G19.x_diff(), G24G19.y_diff(), G24G19.z_diff()],
363 | [G24G18.x_diff(), G24G18.y_diff(), G24G18.z_diff()],
364 | [G24G17.x_diff(), G24G17.y_diff(), G24G17.z_diff()],
365 | [G24G15.x_diff(), G24G15.y_diff(), G24G15.z_diff()],
366 | [G24G13.x_diff(), G24G13.y_diff(), G24G13.z_diff()],
367 | [G24G12.x_diff(), G24G12.y_diff(), G24G12.z_diff()],
368 | [G24G10.x_diff(), G24G10.y_diff(), G24G10.z_diff()]])
369 |
370 | matrix_heatmap(A, "Design (A)")
371 |
372 | """
373 | Output the ATWA matrix
374 | """
375 | atwa = MatrixOperations(A=A, W=Wd)
376 | atwa = atwa.ATWA()
377 | matrix_heatmap(atwa, "ATWA")
378 |
379 | """
380 | Output the (ATWA)^-1 matrix
381 | """
382 | inverse_atwa = linalg.inv(atwa)
383 |
384 | matrix_heatmap(inverse_atwa, "(ATWA)^-1")
385 |
386 | ATWb = (transpose(A).dot(Wd)).dot(b)
387 |
388 | x_hat = inverse_atwa.dot(ATWb)
389 |
390 | x, y, z = x_hat
391 | print(x)
392 | print(y)
393 | print(z)
394 |
395 | print(f"Updated Cooridnates: {pillar_3A_rover[0] + x} actual coordinates: {after_ambiguity_resolution[0]}")
396 | print(f"Updated Cooridnates: {pillar_3A_rover[1] + y} actual coordinates: {after_ambiguity_resolution[1]}")
397 | print(f"Updated Cooridnates: {pillar_3A_rover[2] + z} actual coordinates: {after_ambiguity_resolution[2]}")
398 |
399 |
400 | # Quality Assessment
401 | # Distance between nominal reciever and reference receiver
402 | def distance(point_1: List[float], point_2: List[float]) -> float:
403 | """""
404 | Find the difference between two points given sets of [X, Y, Z] coordinates.
405 | """""
406 | return sqrt((point_2[0] - point_1[0]) ** 2 +
407 | (point_2[1] - point_1[1]) ** 2 +
408 | (point_2[2] - point_1[2]) ** 2)
409 |
410 |
411 | print(distance(pillar_1A_base, pillar_3A_rover))
412 | print(distance(pillar_1A_base, pillar_3A_rover + x_hat))
413 |
414 |
415 |
416 |
--------------------------------------------------------------------------------
/Epoch3.py:
--------------------------------------------------------------------------------
1 | from math import sqrt, cos, sin, degrees, acos
2 | import numpy as np
3 | from numpy import transpose, linalg
4 | from Computations import DD, Variance, matrix_heatmap, vector_heatmap, flipped_vector_heatmap, MatrixOperations
5 | import matplotlib.pyplot as plt
6 | import seaborn as sns
7 | from typing import List
8 |
9 | """
10 | GOAL: Calculate the coordinates of the reference antenna (ARP) of the roving receiver
11 |
12 | Try to get your answer close to the figures for 3A. The nominal coordinates given mean you do not need to iterate the
13 | least squares solution, you should converge on the answer with on round of matrix inversion
14 | The data contains 1 of the three epochs of phase and pseudorange observations measured on a calibration baseline in
15 | valencia, spain.
16 | The sensors used are geodetic quality receivers using choke ring antennas.
17 | Pillar 1A is treated as the reference receiver.
18 | Pillar 3A is treated as the monument.
19 | """
20 |
21 | # X, Y, Z ECEF coordinates for the phase center of the receiver
22 | pillar_1A_base = np.array([[4929635.440], [-29041.877], [4033567.846]]) # Reference receiver
23 |
24 | # Trying to reproduce these coordinates
25 | pillar_3A_rover = np.array([[4929605.400], [-29123.700], [4033603.800]]) # Monument
26 | distance_between_receivers = 94.4 # Measured in meters / Approximate
27 | l1_SD = 0.003
28 |
29 | """
30 | ECEF coordinates (m) of the satellite phase centers when they transmitted the signals measured at each epoch.
31 | """
32 |
33 | # Make a note of the L1 Wl
34 | wl = 0.19029367
35 |
36 | # ECEF SATELLITE POSITIONS X, Y, Z (m) (already corrected for earth rotation during signal travel time)
37 | # 2016 11 15 22 19 6
38 |
39 | G10 = [4637567.153, -19902267.390, 16929759.540]
40 | G12 = [22557285.375 , -8977687.092 , 10383001.564]
41 | G13 = [23276880.010 , 12576064.854 , -2035403.827]
42 | G15 = [25951758.170 , 2444900.089 , 5875164.399]
43 | G17 = [5780438.765, 16825748.070, 20128335.104]
44 | G18 = [13566890.289 ,-21358759.047 , 8226389.520]
45 | G19 = [12258945.969 , 17164267.376 , 15687354.176]
46 | G24 = [15570419.916 , -1033894.709 , 21443242.199]
47 |
48 | """
49 | Use double differenced phase measurements, from the first epoch of data only 2016_11_15_22_19_5
50 | TO compute the precise coordinates of the pillar 3A sensor phase center.
51 | These are pseudo range measurements
52 | """
53 |
54 | # BASE OBSERVATIONS (Pillar 1A) C1C (metres), L1C (L1 cycles)
55 | # 2016_11_15_22_19_7
56 |
57 | G24_base_obs = [20436579.966, 107394966.011]
58 | G19_base_obs = [22181768.684, 116565956.073]
59 | G18_base_obs = [23218233.033, 122012615.974]
60 | G17_base_obs = [23380272.423, 122864167.659]
61 | G15_base_obs = [21347427.948, 112181498.752]
62 | G13_base_obs = [23089071.250, 121333880.840]
63 | G12_base_obs = [20646850.932, 108499926.342]
64 | G10_base_obs = [23726907.612, 124685712.984]
65 |
66 | # ROVER OBSERVATIONS (Pillar 3A) C1C (metres), L1C (L1 cycles)
67 | # 2016 11 15 22 19 7 (Third Epoch)
68 | # meters, cycles
69 |
70 | G24_rover_obs = [20436561.968, 107394871.335]
71 | G19_rover_obs = [22181824.450, 116566284.483]
72 | G18_rover_obs = [23218164.027, 122012264.591]
73 | G17_rover_obs = [23380309.383, 122864361.034]
74 | G15_rover_obs = [21347464.989, 112181690.196]
75 | G13_rover_obs = [23089150.708, 121334293.217]
76 | G12_rover_obs = [20646831.462, 108499857.433]
77 | G10_rover_obs = [23726819.469, 124685264.860]
78 |
79 | # At the first epoch we have 16 raw phase observations in cycles.
80 | l = np.transpose([
81 | G24_base_obs[1],
82 | G24_rover_obs[1],
83 | G19_base_obs[1],
84 | G19_rover_obs[1],
85 | G18_base_obs[1],
86 | G18_rover_obs[1],
87 | G17_base_obs[1],
88 | G17_rover_obs[1],
89 | G15_base_obs[1],
90 | G15_rover_obs[1],
91 | G13_base_obs[1],
92 | G13_rover_obs[1],
93 | G12_base_obs[1],
94 | G12_rover_obs[1],
95 | G10_base_obs[1],
96 | G10_rover_obs[1]])
97 |
98 | """
99 | Phase Ambiguity terms (N) for each measurement, before and after ambiguity resolution
100 | Use integer terms in computations
101 | 2016 11 15 22 19 5
102 | G24 is a reference satellite - and has the highest
103 | """
104 |
105 | # Phase ambiguities for each epoch and each phase measurement:
106 | before_ambiguity_resolution = np.array([[4929605.364], [-29123.817], [4033603.867]])
107 | G24toG19_before = 34.342
108 | G24toG18_before = 1.371
109 | G24toG17_before = 1.541
110 | G24toG15_before = -4.172
111 | G24toG13_before = -3.842
112 | G24toG12_before = 34.879
113 | G24toG10_before = 12.580
114 |
115 | after_ambiguity_resolution = np.array([[4929605.542], [-29123.828], [4033603.932]])
116 | G24toG19_after = 34.000
117 | G24toG18_after = 11.000
118 | G24toG17_after = 1.000
119 | G24toG15_after = -4.000
120 | G24toG13_after = -4.000
121 | G24toG12_after = 35.000
122 | G24toG10_after = 12.000
123 |
124 | a_a_r = [G24toG19_after,
125 | G24toG18_after,
126 | G24toG17_after,
127 | G24toG15_after,
128 | G24toG13_after,
129 | G24toG12_after,
130 | G24toG10_after]
131 |
132 | G24_base_var = Variance(sat_coords=G24, receiver_coords=pillar_1A_base, L1=True)
133 | G24_rover_var = Variance(sat_coords=G24, receiver_coords=pillar_3A_rover, L1=True)
134 | G19_base_var = Variance(sat_coords=G19, receiver_coords=pillar_1A_base, L1=True)
135 | G19_rover_var = Variance(sat_coords=G19, receiver_coords=pillar_3A_rover, L1=True)
136 | G18_base_var = Variance(sat_coords=G18, receiver_coords=pillar_1A_base, L1=True)
137 | G18_rover_var = Variance(sat_coords=G18, receiver_coords=pillar_3A_rover, L1=True)
138 | G17_base_var = Variance(sat_coords=G17, receiver_coords=pillar_1A_base, L1=True)
139 | G17_rover_var = Variance(sat_coords=G17, receiver_coords=pillar_3A_rover, L1=True)
140 | G15_base_var = Variance(sat_coords=G15, receiver_coords=pillar_1A_base, L1=True)
141 | G15_rover_var = Variance(sat_coords=G15, receiver_coords=pillar_3A_rover, L1=True)
142 | G13_base_var = Variance(sat_coords=G13, receiver_coords=pillar_1A_base, L1=True)
143 | G13_rover_var = Variance(sat_coords=G13, receiver_coords=pillar_3A_rover, L1=True)
144 | G12_base_var = Variance(sat_coords=G12, receiver_coords=pillar_1A_base, L1=True)
145 | G12_rover_var = Variance(sat_coords=G12, receiver_coords=pillar_3A_rover, L1=True)
146 | G10_base_var = Variance(sat_coords=G10, receiver_coords=pillar_1A_base, L1=True)
147 | G10_rover_var = Variance(sat_coords=G10, receiver_coords=pillar_3A_rover, L1=True)
148 |
149 | elevations_radians = np.array([
150 | [G24_base_var.elevation_calculator()],
151 | [G24_rover_var.elevation_calculator()],
152 | [G19_base_var.elevation_calculator()],
153 | [G19_rover_var.elevation_calculator()],
154 | [G18_base_var.elevation_calculator()],
155 | [G18_rover_var.elevation_calculator()],
156 | [G17_base_var.elevation_calculator()],
157 | [G17_rover_var.elevation_calculator()],
158 | [G15_base_var.elevation_calculator()],
159 | [G15_rover_var.elevation_calculator()],
160 | [G13_base_var.elevation_calculator()],
161 | [G13_rover_var.elevation_calculator()],
162 | [G12_base_var.elevation_calculator()],
163 | [G12_rover_var.elevation_calculator()],
164 | [G10_base_var.elevation_calculator()],
165 | [G10_rover_var.elevation_calculator()]])
166 |
167 | satelltie_names = np.array([["Base to G19"],
168 | ["Rover to G19"],
169 | ["Base to G18"],
170 | ["Rover to G18"],
171 | ["Base to G17"],
172 | ["Rover to G17"],
173 | ["Base to G15"],
174 | ["Rover to G15"],
175 | ["Base to G13"],
176 | ["Rover to G13"],
177 | ["Base to G12"],
178 | ["Rover to G12"],
179 | ["Base to G10"],
180 | ["Rover to G10"],
181 | ["Base to G24"],
182 | ["Rover to G24"]])
183 |
184 | # Elevations in degrees
185 | elevations_degrees = np.array([degrees(x) for x in elevations_radians])
186 |
187 | variance_vector = np.array([
188 | [G24_base_var.variance()],
189 | [G24_rover_var.variance()],
190 | [G19_base_var.variance()],
191 | [G19_rover_var.variance()],
192 | [G18_base_var.variance()],
193 | [G18_rover_var.variance()],
194 | [G17_base_var.variance()],
195 | [G17_rover_var.variance()],
196 | [G15_base_var.variance()],
197 | [G15_rover_var.variance()],
198 | [G13_base_var.variance()],
199 | [G13_rover_var.variance()],
200 | [G12_base_var.variance()],
201 | [G12_rover_var.variance()],
202 | [G10_base_var.variance()],
203 | [G10_rover_var.variance()]])
204 |
205 | # 16 x 8: Differencing matrix
206 | S = np.array([[1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
207 | [0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
208 | [0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
209 | [0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0],
210 | [0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0],
211 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0],
212 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0],
213 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1]])
214 |
215 | D = np.array([[1, -1, 0, 0, 0, 0, 0, 0],
216 | [1, 0, -1, 0, 0, 0, 0, 0],
217 | [1, 0, 0, -1, 0, 0, 0, 0],
218 | [1, 0, 0, 0, -1, 0, 0, 0],
219 | [1, 0, 0, 0, 0, -1, 0, 0],
220 | [1, 0, 0, 0, 0, 0, -1, 0],
221 | [1, 0, 0, 0, 0, 0, 0, -1]])
222 |
223 | if __name__ == "__main__":
224 |
225 | flipped_vector_heatmap(variance_vector, "Variances")
226 | flipped_vector_heatmap(elevations_radians, "Radians")
227 | flipped_vector_heatmap(elevations_degrees, "Degrees")
228 |
229 | """
230 | l vector - The vector of raw observations
231 | """
232 |
233 | flipped_vector_heatmap(l, "Vector of observations (l)")
234 |
235 | """
236 | For purposes of single differencing and double differencing.
237 | The D and S are created.
238 | D DIM: 7 x 8
239 | S DIM: 8 X 16
240 | """
241 |
242 | matrix_heatmap(S, "Single Differencing (S)")
243 |
244 | matrix_heatmap(D, "Double Differencing (D)")
245 |
246 | """
247 | SINGLE DIFFERENCING
248 | As receiver 1A and 3A are relatively close together (9.4m)
249 | The ionspheric and tropospheric terms cancel out
250 | Therefore we can calculate receiver-receiver single difference (RRSD)
251 | sl = vector of receiver-receiver single differences
252 | DIM: 8 x 1
253 | """
254 |
255 | sl = S.dot(l)
256 |
257 | flipped_vector_heatmap(sl, "Vector of single differences (sl)")
258 |
259 | """
260 | DOUBLE DIFFERENCING
261 |
262 | We choose the satellite with the highest elevation as the reference satellite.
263 | This will be G24 - This satellite will have the least noise and multipath
264 |
265 | Dsl = vector of double differences
266 | DIM: 7 x 1
267 | """
268 |
269 | Dsl = D.dot(sl)
270 |
271 | """
272 | Subtract the corresponding ambiguity resolved phase ambiguity term.
273 | """
274 | DD_s_p_a = []
275 | for i in range(len(Dsl)):
276 | DD_s_p_a.append(Dsl[i] - a_a_r[i])
277 |
278 | flipped_vector_heatmap(Dsl, "Double differences (Dsl)")
279 |
280 | """
281 | Covariance matrix of the observation vector.
282 | This is an identity matrix scaled by the variance.
283 | It is assumed that the variance of each raw phase observation has the same l1 standard deviation is 0.003.
284 | Therefore variance is this value squared.
285 | UNITS: Cycles
286 | DIM: 16 x 16
287 | """
288 |
289 | cl = np.eye(16, 16) * (1 / wl * variance_vector) # back to cycles
290 | matrix_heatmap(cl, "Covariance matrix of observations (cl)")
291 |
292 | """
293 | Obtain the covariance matrix of the double differences.
294 | This is because we require Wd - The weight matrix of the double difference vector. (The inverse of cd)
295 | Apply gauss's propagation of error law: y = Qx, Cx to work out Cd
296 | Where Q is the matrix operator - No uncertainties associated
297 | Where x is stochastic quantity
298 | Where y is stochastic
299 | Where Cx is the covariance matrix of x and is known
300 |
301 | d = DSl
302 | Cl Exists and is known.
303 |
304 | Use the formula: Cd = DSCl (DS)^T
305 |
306 | DIM: 7 x 7
307 | """
308 |
309 | Cd = MatrixOperations(D=D, S=S, Cl=cl)
310 | Cd = Cd.Cd_calculator()
311 | matrix_heatmap(Cd, "covariance matrix of the double differences (Cd)")
312 |
313 | """
314 | Calculate the weight matrix of the double differences.
315 |
316 | Wd = Cd^-1
317 | DIM: 7 x 7
318 | """
319 |
320 | Wd = linalg.inv(Cd)
321 | matrix_heatmap(Wd, "Weight (Wd)")
322 |
323 | """
324 |
325 | """
326 | G24G19 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G19, sat_ref=G24,
327 | observed=DD_s_p_a[0])
328 |
329 | G24G18 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G18, sat_ref=G24,
330 | observed=DD_s_p_a[1])
331 |
332 | G24G17 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G17, sat_ref=G24,
333 | observed=DD_s_p_a[2])
334 |
335 | G24G15 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G15, sat_ref=G24,
336 | observed=DD_s_p_a[3])
337 |
338 | G24G13 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G13, sat_ref=G24,
339 | observed=DD_s_p_a[4])
340 |
341 | G24G12 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G12, sat_ref=G24,
342 | observed=DD_s_p_a[5])
343 |
344 | G24G10 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G10, sat_ref=G24,
345 | observed=DD_s_p_a[6])
346 |
347 | """
348 | Calculate the b vector:
349 | This is the observed double differencing measurements - the computed.
350 | """
351 | b = np.array([[G24G19.calc_b_vector()],
352 | [G24G18.calc_b_vector()],
353 | [G24G17.calc_b_vector()],
354 | [G24G15.calc_b_vector()],
355 | [G24G13.calc_b_vector()],
356 | [G24G12.calc_b_vector()],
357 | [G24G10.calc_b_vector()]])
358 |
359 | vector_heatmap(b, "Observed - Computed")
360 |
361 | # Populate the design matrix
362 | A = np.array([[G24G19.x_diff(), G24G19.y_diff(), G24G19.z_diff()],
363 | [G24G18.x_diff(), G24G18.y_diff(), G24G18.z_diff()],
364 | [G24G17.x_diff(), G24G17.y_diff(), G24G17.z_diff()],
365 | [G24G15.x_diff(), G24G15.y_diff(), G24G15.z_diff()],
366 | [G24G13.x_diff(), G24G13.y_diff(), G24G13.z_diff()],
367 | [G24G12.x_diff(), G24G12.y_diff(), G24G12.z_diff()],
368 | [G24G10.x_diff(), G24G10.y_diff(), G24G10.z_diff()]])
369 |
370 | matrix_heatmap(A, "Design (A)")
371 |
372 | """
373 | Output the ATWA matrix
374 | """
375 | atwa = MatrixOperations(A=A, W=Wd)
376 | atwa = atwa.ATWA()
377 | matrix_heatmap(atwa, "ATWA")
378 |
379 | """
380 | Output the (ATWA)^-1 matrix
381 | """
382 | inverse_atwa = linalg.inv(atwa)
383 |
384 | matrix_heatmap(inverse_atwa, "(ATWA)^-1")
385 |
386 | ATWb = (transpose(A).dot(Wd)).dot(b)
387 |
388 | x_hat = inverse_atwa.dot(ATWb)
389 |
390 | x, y, z = x_hat
391 | print(x)
392 | print(y)
393 | print(z)
394 |
395 | print(f"Updated Cooridnates: {pillar_3A_rover[0] + x} actual coordinates: {after_ambiguity_resolution[0]}")
396 | print(f"Updated Cooridnates: {pillar_3A_rover[1] + y} actual coordinates: {after_ambiguity_resolution[1]}")
397 | print(f"Updated Cooridnates: {pillar_3A_rover[2] + z} actual coordinates: {after_ambiguity_resolution[2]}")
398 |
399 |
400 | # Quality Assessment
401 | # Distance between nominal reciever and reference receiver
402 | def distance(point_1: List[float], point_2: List[float]) -> float:
403 | """""
404 | Find the difference between two points given sets of [X, Y, Z] coordinates.
405 | """""
406 | return sqrt((point_2[0] - point_1[0]) ** 2 +
407 | (point_2[1] - point_1[1]) ** 2 +
408 | (point_2[2] - point_1[2]) ** 2)
409 |
410 |
411 | print(distance(pillar_1A_base, pillar_3A_rover))
412 | print(distance(pillar_1A_base, pillar_3A_rover + x_hat))
413 |
414 |
415 |
416 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | from math import sqrt, cos, sin, degrees, acos
2 | import numpy as np
3 | from numpy import transpose, linalg
4 | from Computations import DD, Variance, matrix_heatmap, vector_heatmap, flipped_vector_heatmap, MatrixOperations
5 | import matplotlib.pyplot as plt
6 | import seaborn as sns
7 | from typing import List
8 |
9 | """
10 | GOAL: Calculate the coordinates of the reference antenna (ARP) of the roving receiver
11 |
12 | Try to get your answer close to the figures for 3A. The nominal coordinates given mean you do not need to iterate the
13 | least squares solution, you should converge on the answer with on round of matrix inversion
14 | The data contains 1 of the three epochs of phase and pseudorange observations measured on a calibration baseline in
15 | valencia, spain.
16 | The sensors used are geodetic quality receivers using choke ring antennas.
17 | Pillar 1A is treated as the reference receiver.
18 | Pillar 3A is treated as the monument.
19 | """
20 |
21 | # X, Y, Z ECEF coordinates for the phase center of the receiver
22 | pillar_1A_base = np.array([[4929635.440], [-29041.877], [4033567.846]]) # Reference receiver
23 |
24 | # Trying to reproduce these coordinates
25 | pillar_3A_rover = np.array([[4929605.400], [-29123.700], [4033603.800]]) # Monument
26 | distance_between_receivers = 94.4 # Measured in meters / Approximate
27 | l1_SD = 0.003
28 |
29 | """
30 | ECEF coordinates (m) of the satellite phase centers when they transmitted the signals measured at each epoch.
31 | """
32 |
33 | # Make a note of the L1 Wl
34 | wl = 0.19029367
35 |
36 | # ECEF SATELLITE POSITIONS X, Y, Z (m) (already corrected for earth rotation during signal travel time)
37 | # 2016 11 15 22 19 5
38 |
39 | G10 = [4634093.207, -19899701.050, 16933747.321]
40 | G12 = [22559170.178, -8979632.676, 10377257.530]
41 | G13 = [23277536.897, 12575815.276, -2029027.200]
42 | G15 = [25950462.808, 2443858.353, 5881092.070]
43 | G17 = [5785091.956, 16827408.400, 20125597.869]
44 | G18 = [13564948.214, -21357948.777, 8232124.013]
45 | G19 = [12262838.101, 17165601.305, 15682863.092]
46 | G24 = [15569244.807, -1039249.482, 21443791.252]
47 |
48 | """
49 | Use double differenced phase measurements, from the first epoch of data only 2016_11_15_22_19_5
50 | TO compute the precise coordinates of the pillar 3A sensor phase center.
51 | These are pseudo range measurements
52 | """
53 |
54 | # BASE OBSERVATIONS (Pillar 1A) C1C (metres), L1C (L1 cycles)
55 | # 2016_11_15_22_19_5
56 |
57 | G24_base_obs = [20436699.926, 107395596.426]
58 | G19_base_obs = [22181729.713, 116565751.296]
59 | G18_base_obs = [23217805.737, 122010370.583]
60 | G17_base_obs = [23379753.757, 122861442.012]
61 | G15_base_obs = [21346539.664, 112176830.803]
62 | G13_base_obs = [23087780.798, 121327099.499]
63 | G12_base_obs = [20647534.024, 108503516.027]
64 | G10_base_obs = [23726969.123, 124686036.295]
65 |
66 | # ROVER OBSERVATIONS (Pillar 3A) C1C (metres), L1C (L1 cycles)
67 | # 2016 11 15 22 19 5 (First Epoch)
68 | # meters, cycles
69 |
70 | G24_rover_obs = [20436682.002, 107395502.123]
71 | G19_rover_obs = [22181785.598, 116566080.299]
72 | G18_rover_obs = [23217736.821, 122010019.631]
73 | G17_rover_obs = [23379790.820, 122861635.973]
74 | G15_rover_obs = [21346576.786, 112177022.660]
75 | G13_rover_obs = [23087860.345, 121327512.345]
76 | G12_rover_obs = [20647514.655, 108503447.644]
77 | G10_rover_obs = [23726881.094, 124685588.685]
78 |
79 | # At the first epoch we have 16 raw phase observations in cycles.
80 | l = np.transpose([
81 | G24_base_obs[1],
82 | G24_rover_obs[1],
83 | G19_base_obs[1],
84 | G19_rover_obs[1],
85 | G18_base_obs[1],
86 | G18_rover_obs[1],
87 | G17_base_obs[1],
88 | G17_rover_obs[1],
89 | G15_base_obs[1],
90 | G15_rover_obs[1],
91 | G13_base_obs[1],
92 | G13_rover_obs[1],
93 | G12_base_obs[1],
94 | G12_rover_obs[1],
95 | G10_base_obs[1],
96 | G10_rover_obs[1]])
97 |
98 | """
99 | Phase Ambiguity terms (N) for each measurement, before and after ambiguity resolution
100 | Use integer terms in computations
101 | 2016 11 15 22 19 5
102 | G24 is a reference satellite - and has the highest
103 | """
104 |
105 | # Phase ambiguities for each epoch and each phase measurement:
106 | before_ambiguity_resolution = np.array([[4929605.364], [-29123.817], [4033603.867]])
107 | G24toG19_before = 34.352
108 | G24toG18_before = 11.324
109 | G24toG17_before = 1.538
110 | G24toG15_before = -4.170
111 | G24toG13_before = -3.838
112 | G24toG12_before = 34.873
113 | G24toG10_before = 12.564
114 |
115 | after_ambiguity_resolution = np.array([[4929605.542], [-29123.828], [4033603.932]])
116 | G24toG19_after = 34.000
117 | G24toG18_after = 11.000
118 | G24toG17_after = 1.000
119 | G24toG15_after = -4.000
120 | G24toG13_after = -4.000
121 | G24toG12_after = 35.000
122 | G24toG10_after = 12.000
123 |
124 | a_a_r = [G24toG19_after,
125 | G24toG18_after,
126 | G24toG17_after,
127 | G24toG15_after,
128 | G24toG13_after,
129 | G24toG12_after,
130 | G24toG10_after]
131 |
132 | G24_base_var = Variance(sat_coords=G24, receiver_coords=pillar_1A_base, L1=True)
133 | G24_rover_var = Variance(sat_coords=G24, receiver_coords=pillar_3A_rover, L1=True)
134 | G19_base_var = Variance(sat_coords=G19, receiver_coords=pillar_1A_base, L1=True)
135 | G19_rover_var = Variance(sat_coords=G19, receiver_coords=pillar_3A_rover, L1=True)
136 | G18_base_var = Variance(sat_coords=G18, receiver_coords=pillar_1A_base, L1=True)
137 | G18_rover_var = Variance(sat_coords=G18, receiver_coords=pillar_3A_rover, L1=True)
138 | G17_base_var = Variance(sat_coords=G17, receiver_coords=pillar_1A_base, L1=True)
139 | G17_rover_var = Variance(sat_coords=G17, receiver_coords=pillar_3A_rover, L1=True)
140 | G15_base_var = Variance(sat_coords=G15, receiver_coords=pillar_1A_base, L1=True)
141 | G15_rover_var = Variance(sat_coords=G15, receiver_coords=pillar_3A_rover, L1=True)
142 | G13_base_var = Variance(sat_coords=G13, receiver_coords=pillar_1A_base, L1=True)
143 | G13_rover_var = Variance(sat_coords=G13, receiver_coords=pillar_3A_rover, L1=True)
144 | G12_base_var = Variance(sat_coords=G12, receiver_coords=pillar_1A_base, L1=True)
145 | G12_rover_var = Variance(sat_coords=G12, receiver_coords=pillar_3A_rover, L1=True)
146 | G10_base_var = Variance(sat_coords=G10, receiver_coords=pillar_1A_base, L1=True)
147 | G10_rover_var = Variance(sat_coords=G10, receiver_coords=pillar_3A_rover, L1=True)
148 |
149 | elevations_radians = np.array([
150 | [G24_base_var.elevation_calculator()],
151 | [G24_rover_var.elevation_calculator()],
152 | [G19_base_var.elevation_calculator()],
153 | [G19_rover_var.elevation_calculator()],
154 | [G18_base_var.elevation_calculator()],
155 | [G18_rover_var.elevation_calculator()],
156 | [G17_base_var.elevation_calculator()],
157 | [G17_rover_var.elevation_calculator()],
158 | [G15_base_var.elevation_calculator()],
159 | [G15_rover_var.elevation_calculator()],
160 | [G13_base_var.elevation_calculator()],
161 | [G13_rover_var.elevation_calculator()],
162 | [G12_base_var.elevation_calculator()],
163 | [G12_rover_var.elevation_calculator()],
164 | [G10_base_var.elevation_calculator()],
165 | [G10_rover_var.elevation_calculator()]])
166 |
167 | satelltie_names = np.array([["Base to G19"],
168 | ["Rover to G19"],
169 | ["Base to G18"],
170 | ["Rover to G18"],
171 | ["Base to G17"],
172 | ["Rover to G17"],
173 | ["Base to G15"],
174 | ["Rover to G15"],
175 | ["Base to G13"],
176 | ["Rover to G13"],
177 | ["Base to G12"],
178 | ["Rover to G12"],
179 | ["Base to G10"],
180 | ["Rover to G10"],
181 | ["Base to G24"],
182 | ["Rover to G24"]])
183 |
184 | # Elevations in degrees
185 | elevations_degrees = np.array([degrees(x) for x in elevations_radians])
186 |
187 | variance_vector = np.array([
188 | [G24_base_var.variance()],
189 | [G24_rover_var.variance()],
190 | [G19_base_var.variance()],
191 | [G19_rover_var.variance()],
192 | [G18_base_var.variance()],
193 | [G18_rover_var.variance()],
194 | [G17_base_var.variance()],
195 | [G17_rover_var.variance()],
196 | [G15_base_var.variance()],
197 | [G15_rover_var.variance()],
198 | [G13_base_var.variance()],
199 | [G13_rover_var.variance()],
200 | [G12_base_var.variance()],
201 | [G12_rover_var.variance()],
202 | [G10_base_var.variance()],
203 | [G10_rover_var.variance()]])
204 |
205 | # 16 x 8: Differencing matrix
206 | S = np.array([[1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
207 | [0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
208 | [0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
209 | [0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0],
210 | [0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0],
211 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0],
212 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0],
213 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1]])
214 |
215 | D = np.array([[1, -1, 0, 0, 0, 0, 0, 0],
216 | [1, 0, -1, 0, 0, 0, 0, 0],
217 | [1, 0, 0, -1, 0, 0, 0, 0],
218 | [1, 0, 0, 0, -1, 0, 0, 0],
219 | [1, 0, 0, 0, 0, -1, 0, 0],
220 | [1, 0, 0, 0, 0, 0, -1, 0],
221 | [1, 0, 0, 0, 0, 0, 0, -1]])
222 |
223 |
224 | if __name__ == "__main__":
225 |
226 | flipped_vector_heatmap(variance_vector, "Variances")
227 | flipped_vector_heatmap(elevations_radians, "Radians")
228 | flipped_vector_heatmap(elevations_degrees, "Degrees")
229 |
230 | """
231 | l vector - The vector of raw observations
232 | """
233 |
234 | flipped_vector_heatmap(l, "Vector of observations (l)")
235 |
236 | """
237 | For purposes of single differencing and double differencing.
238 | The D and S are created.
239 | D DIM: 7 x 8
240 | S DIM: 8 X 16
241 | """
242 |
243 | matrix_heatmap(S, "Single Differencing (S)")
244 |
245 | matrix_heatmap(D, "Double Differencing (D)")
246 |
247 | """
248 | SINGLE DIFFERENCING
249 | As receiver 1A and 3A are relatively close together (9.4m)
250 | The ionspheric and tropospheric terms cancel out
251 | Therefore we can calculate receiver-receiver single difference (RRSD)
252 | sl = vector of receiver-receiver single differences
253 | DIM: 8 x 1
254 | """
255 |
256 | sl = S.dot(l)
257 |
258 | flipped_vector_heatmap(sl, "Vector of single differences (sl)")
259 |
260 | """
261 | DOUBLE DIFFERENCING
262 |
263 | We choose the satellite with the highest elevation as the reference satellite.
264 | This will be G24 - This satellite will have the least noise and multipath
265 |
266 | Dsl = vector of double differences
267 | DIM: 7 x 1
268 | """
269 |
270 | Dsl = D.dot(sl)
271 |
272 | """
273 | Subtract the corresponding ambiguity resolved phase ambiguity term.
274 | """
275 | DD_s_p_a = []
276 | for i in range(len(Dsl)):
277 | DD_s_p_a.append(Dsl[i] - a_a_r[i])
278 |
279 | flipped_vector_heatmap(Dsl, "Double differences (Dsl)")
280 |
281 | """
282 | Covariance matrix of the observation vector.
283 | This is an identity matrix scaled by the variance.
284 | It is assumed that the variance of each raw phase observation has the same l1 standard deviation is 0.003.
285 | Therefore variance is this value squared.
286 | UNITS: Cycles
287 | DIM: 16 x 16
288 | """
289 |
290 | cl = np.eye(16, 16) * (1 / wl * variance_vector) # (1/wl to convert from cycles to meters)
291 | matrix_heatmap(cl, "Covariance matrix of observations (cl)")
292 |
293 | """
294 | Obtain the covariance matrix of the double differences.
295 | This is because we require Wd - The weight matrix of the double difference vector. (The inverse of cd)
296 | Apply gauss's propagation of error law: y = Qx, Cx to work out Cd
297 | Where Q is the matrix operator - No uncertainties associated
298 | Where x is stochastic quantity
299 | Where y is stochastic
300 | Where Cx is the covariance matrix of x and is known
301 |
302 | d = DSl
303 | Cl Exists and is known.
304 |
305 | Use the formula: Cd = DSCl (DS)^T
306 |
307 | DIM: 7 x 7
308 | """
309 |
310 | Cd = MatrixOperations(D=D, S=S, Cl=cl)
311 | Cd = Cd.Cd_calculator()
312 | matrix_heatmap(Cd, "covariance matrix of the double differences (Cd)")
313 |
314 | """
315 | Calculate the weight matrix of the double differences.
316 |
317 | Wd = Cd^-1
318 | DIM: 7 x 7
319 | """
320 |
321 | Wd = linalg.inv(Cd)
322 | matrix_heatmap(Wd, "Weight (Wd)")
323 |
324 | """
325 |
326 | """
327 | G24G19 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G19, sat_ref=G24,
328 | observed=DD_s_p_a[0])
329 |
330 | G24G18 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G18, sat_ref=G24,
331 | observed=DD_s_p_a[1])
332 |
333 | G24G17 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G17, sat_ref=G24,
334 | observed=DD_s_p_a[2])
335 |
336 | G24G15 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G15, sat_ref=G24,
337 | observed=DD_s_p_a[3])
338 |
339 | G24G13 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G13, sat_ref=G24,
340 | observed=DD_s_p_a[4])
341 |
342 | G24G12 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G12, sat_ref=G24,
343 | observed=DD_s_p_a[5])
344 |
345 | G24G10 = DD(ref_station=pillar_1A_base, rov_station=pillar_3A_rover, corresponding_sat=G10, sat_ref=G24,
346 | observed=DD_s_p_a[6])
347 |
348 | """
349 | Calculate the b vector:
350 | This is the observed double differencing measurements - the computed.
351 | """
352 | b = np.array([[G24G19.calc_b_vector()],
353 | [G24G18.calc_b_vector()],
354 | [G24G17.calc_b_vector()],
355 | [G24G15.calc_b_vector()],
356 | [G24G13.calc_b_vector()],
357 | [G24G12.calc_b_vector()],
358 | [G24G10.calc_b_vector()]])
359 |
360 | vector_heatmap(b, "Observed - Computed")
361 |
362 | # Populate the design matrix
363 | A = np.array([[G24G19.x_diff(), G24G19.y_diff(), G24G19.z_diff()],
364 | [G24G18.x_diff(), G24G18.y_diff(), G24G18.z_diff()],
365 | [G24G17.x_diff(), G24G17.y_diff(), G24G17.z_diff()],
366 | [G24G15.x_diff(), G24G15.y_diff(), G24G15.z_diff()],
367 | [G24G13.x_diff(), G24G13.y_diff(), G24G13.z_diff()],
368 | [G24G12.x_diff(), G24G12.y_diff(), G24G12.z_diff()],
369 | [G24G10.x_diff(), G24G10.y_diff(), G24G10.z_diff()]])
370 |
371 | matrix_heatmap(A, "Design (A)")
372 |
373 | """
374 | Output the ATWA matrix
375 | """
376 | atwa = MatrixOperations(A=A, W=Wd)
377 | atwa = atwa.ATWA()
378 | matrix_heatmap(atwa, "ATWA")
379 |
380 | """
381 | Output the (ATWA)^-1 matrix
382 | """
383 | inverse_atwa = linalg.inv(atwa)
384 |
385 | matrix_heatmap(inverse_atwa, "(ATWA)^-1")
386 |
387 | ATWb = (transpose(A).dot(Wd)).dot(b)
388 |
389 | x_hat = inverse_atwa.dot(ATWb)
390 |
391 | x, y, z = x_hat
392 | print(x)
393 | print(y)
394 | print(z)
395 |
396 | print(f"Updated Cooridnates: {pillar_3A_rover[0] + x} actual coordinates: {after_ambiguity_resolution[0]}")
397 | print(f"Updated Cooridnates: {pillar_3A_rover[1] + y} actual coordinates: {after_ambiguity_resolution[1]}")
398 | print(f"Updated Cooridnates: {pillar_3A_rover[2] + z} actual coordinates: {after_ambiguity_resolution[2]}")
399 |
400 |
401 | # Quality Assessment
402 | # Distance between nominal reciever and reference receiver
403 | def distance(point_1: List[float], point_2: List[float]) -> float:
404 | """""
405 | Find the difference between two points given sets of [X, Y, Z] coordinates.
406 | """""
407 | return sqrt((point_2[0] - point_1[0]) ** 2 +
408 | (point_2[1] - point_1[1]) ** 2 +
409 | (point_2[2] - point_1[2]) ** 2)
410 |
411 |
412 | print(distance(pillar_1A_base, pillar_3A_rover))
413 | print(distance(pillar_1A_base, pillar_3A_rover + x_hat))
414 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 📡 GNSS Double Differencing 📡
2 |
3 | Double Differencing: Main method of high precision commercial positioning. This method requires two high grade GNSS receivers. In this case a reference, and a (static or kinematic) rover receiver.
4 |
5 | ## Scenario
6 |
7 | Phase and pseudorange observations measured on a calibration baseline in Valencia, Spain. The sensors used
8 | are geodetic quality Leica receivers using choke ring antennas. The receiver on pillar 1A is treated as
9 | the reference receiver, with the following known ECEF coordinates in metres (XYZ)T
10 | for the phase
11 |
12 | centre:
13 | 4929635.440
14 | -29041.877
15 | 4033567.846
16 |
17 | Use the following nominal coordinates for the phase centre of the sensor on pillar 3A:
18 | 4929605.400
19 | -29123.700
20 | 4033603.800
21 |
22 | Use double differenced phase measurements, from the first epoch of data only (2016 11 15 22 19
23 | 5), to compute the precise coordinates of the pillar 3A sensor phase centre
24 |
25 |
26 |
27 | ## Prerequisites
28 |
29 | ``` Python 3```
30 |
31 | The following libraries must be installed:
32 |
33 | ```
34 | pip install numpy
35 | pip install matplotlib
36 | pip install seaborn
37 | ```
38 |
39 | You can find the computations [here](https://github.com/ThomasJames/GNSS_Double_Differencing/blob/master/Computations.py)
40 |
41 | ### Clone this repository:
42 |
43 | ```
44 | $ git clone https://github.com/ThomasJames/GNSS_Double_Differencing
45 | ```
46 |
47 | ## Data
48 |
49 | The original [text file](https://github.com/ThomasJames/GNSS_Data_(text).txt) is in this repository, however the data has been organised and processed into [python](https://github.com/ThomasJames/GNSS_Double_Differencing/Data.py) variables.
50 |
51 | ## Method
52 |
53 |
54 |
55 | Satellite G24 has the highest elevation of apprixmatley 71 degrees. This satellite is used as the reference satellite.
56 |
57 | It is important to initially calculate the elevation angles of each satelite. The error of a satellite is inversely proportial to elevation with respect to a local horizon. Low elevation satellites produce less reliable results, and this needs to be taken into account when formulating a weight matrix.
58 |
59 |
60 |
61 |
62 | ### Computations of elevations and variances:
63 |
64 | This method calculates the satellite angle of elevation in the following stages:
65 | Calculates the distance of receiver to the satellite (m) using pythagoras theorem.
66 | Calculates the distance between the earth center and the satellite (m) using pythagoras theorem.
67 | Calculates the distance between the earth center and the receiver (m) using pythagoras theorem.
68 | These ranges make up a scalene triangle, where all ranges/sides are known.
69 | The low of cosines is used to calculate the angle about the receiver in degrees.
70 | 90 is subtracted from this angle to get the local elevation angle.
71 | The method then calculates the variance of the satellite at the calculated angle.
72 | returns the variance as a float
73 |
74 |
75 |
76 | The code to achieve this is here:
77 | ```
78 | def elevation_variance_calculator(self) -> float:
79 |
80 | """"
81 | This method calculates the satellite angle of elevation in the following stages:
82 | Calculates the distance of receiver to the satellite (m) using pythagoras theorem.
83 | Calculates the distance between the earth center and the satellite (m) using pythagoras theorem.
84 | Calculates the distance between the earth center and the receiver (m) using pythagoras theorem.
85 | These ranges make up a scalene triangle, where all ranges are known.
86 | The low of cosines is used to calculate the angle about the receiver in degrees.
87 | 90 is subtracted from this angle to get the local elevation angle.
88 | """""
89 |
90 | # Extract ECEF distances (m)
91 | x_s, y_s, z_s = self.sat_coords[0], self.sat_coords[1], self.sat_coords[2]
92 | x_r, y_r, z_r = self.receiver_coords[0], self.receiver_coords[1], self.receiver_coords[2]
93 |
94 | # Distance from receiver to satellite (m)
95 | r_s = sqrt((x_s - x_r)**2 + (y_s - y_r)**2 + (z_s - z_r)**2)
96 |
97 | # Distance from earth center to satellite (m)
98 | ec_s = sqrt((sqrt(x_s ** 2 + y_s ** 2)) ** 2 + z_s ** 2)
99 |
100 | # Distance from earth center to receiver (m)
101 | ec_r = sqrt((sqrt(x_r ** 2 + y_r ** 2)) ** 2 + z_r ** 2)
102 |
103 | # Angle from the local horizontal to the satellite (m)
104 | angle = radians((degrees(acos((ec_r ** 2 + r_s ** 2 - ec_s ** 2) / (2 * ec_r * r_s)))) - 90)
105 |
106 | return angle
107 |
108 | def variance(self):
109 | """""
110 | This method then calculates the variance of the satellite at the calculated angle.
111 | returns the variance as a float
112 | """""
113 | # Variance (uncertainty associated with the satellite) (m)
114 | variance = (self.l1_std ** 2) / (sin(self.elevation_variance_calculator()))
115 |
116 | return variance
117 |
118 |
119 | ```
120 | Satellites ordered top to bottom: G24, G19, G18, G17, G15, G13, G12, G10
121 |
122 | 

123 |
124 | (Elevation angles are representative)
125 |
126 |
127 |
128 | ### l (Observations) vector
129 |
130 | This is the vector of raw observations.
131 | UNITS: L1C (L1 cycles)
132 |
133 |
134 |
135 |
136 |
137 |
138 | ### S (Single differencing) Matrix
139 |
140 | This matrix is used to generate a vector of single differences.
141 |
142 |
143 |
144 | ### Sl (Vector of single differences)
145 |
146 | The dot product of the differencing matrix (S) and the vector of observations (l) generates the vector of single differences.
147 |
148 |
149 |
150 | The following code was used compute this:
151 | ```
152 | sl = S.dot(l)
153 | ```
154 | UNITS: L1C (L1 cycles)
155 |
156 |
157 | ### D (Doube differencing) Matrix
158 |
159 | This matrix is used to generate values for the double differences of the observations matrix.
160 |
161 |
162 |
163 | ### DSl (Vector of Double Differences of Observations)
164 |
165 | The dot product of the double differencing matrix (D) and the vector of single differences generates the vector of double differences of the observations
166 |
167 |
168 |
169 | The following code was used to compute this.
170 |
171 | UNITS: L1C (L1 cycles)
172 |
173 |
174 |
175 | Code:
176 | ```
177 | Dsl = D.dot(sl)
178 | ```
179 |
180 |
181 |
182 | ### b (Observed - Computed)
183 |
184 | The observed - computed (b) matrix was calculated in the following steps:
185 |
186 | 1. The phase ambiguity term (N) was subtracted from the double differenced vector of double differences (DSl):
187 | In the code, this has been stored into the variable ``` DD_s_p_a``` This value is expressed in cycles
188 |
189 | 2. The range terms are computed from the satellite coordinates. These are then used to generate a value of the computed measurement. These are multiplied by 1/wavelength to convert from meters into cycles.
190 |
191 | 3. The observed measurement is subtracted from the computed measurement.
192 |
193 |
194 |
195 | The code to achieve this is here:
196 | ```
197 | # This function is used to compute satelite - receiver ranges.
198 | def distance(point_1: List[float], point_2: List[float]) -> float:
199 | return sqrt((point_2[0] - point_1[0])**2 +
200 | (point_2[1] - point_1[1])**2 +
201 | (point_2[2] - point_1[2])**2)
202 |
203 | """
204 | wl - Wavelength
205 | brrs - Base receiver to reference satellite
206 | rrrs - Reference receiver to reference satellite
207 | brcs - Base receiver to corresponding satellite
208 | rrcs - Reference receiver to corresponding satellite
209 | DD_s_p_a - Vector of double differences, after phase ambiguity term subtracted.
210 | """
211 | brrs: floa = distance(ref_station, sat_ref)
212 | rrrs = distance(rov_station, sat_ref)
213 | brcs = distance(ref_station, corresponding_sat)
214 | rrcs = distance(rov_station, corresponding_sat)
215 |
216 | def calc_b_vector(wl: float, DD_s_p_a: float, brrs: float, rrrs: float, brcs: float, rrcs: float) -> float:
217 | # observed - The vector of measured quantities
218 | o = DD_s_p_a
219 |
220 | # Computed
221 | c = (1 / wl) * (brrs - rrrs - brcs + rrcs)
222 | return o - c
223 |
224 | ```
225 |
226 | UNITS: meters
227 |
228 |
229 |
230 | ### Cl (Observations covariance) Matrix
231 |
232 | The covarince matrix of observations is calculated in the following steps:
233 |
234 | Step 1: A 16 x 16 Identity matrix is generated.
235 |
236 | Step 2: This matrix is multiplied by the vector of variances (Computed from satellite elevations)
237 |
238 | This following code was used to make this computations:
239 | ```
240 | cl = np.eye(16, 16) * (1 / wl * variance_vector) # Convert to meters
241 | ```
242 |
243 | UNITS: Meters
244 |
245 |
246 |
247 | ### Cd (Covariance) Matrix
248 |
249 |
250 |
251 | ```
252 | def Cd_calculator(D, S, Cl):
253 | return (((D.dot(S)).dot(Cl)).dot(transpose(S))).dot(transpose(D))
254 | ```
255 | UNITS: Meters
256 |
257 |
258 |
259 | ### Wd (Weight) Matrix
260 |
261 |
262 |
263 | The code to compute this is here:
264 | ```
265 | Wd = linalg.inv(Cd)
266 | ```
267 |
268 | UNITS: Meters
269 |
270 |
271 |
272 | ### A (Design) Martix
273 |
274 | The design matrix was populated with the partial derivitives of the double difference observation equations with respect to the unknowns.
275 |
276 | UNITS: Meters
277 |
278 |
279 |
280 |
281 | NOTE: Phase ambiguity terms are not included.
282 |
283 | ### The following code was used to perform these computations.
284 |
285 | ```
286 | class DD:
287 | """"
288 | ref_station - Earth centric Cartesian Coordinates of the reference station [X, Y, Z]
289 | corresponding_sat - Earth centric cartesian Coordinates of the corresponding station [X, Y, Z]
290 | sat_ref - Satellite reference of cartesian Coordinates of the reference station [X, Y, Z]
291 | c = speed of light in vacuum (299792458.0 ms-1) - Set to default
292 | f = signal frequency (L1: 1575.42MHz, L2: 1227.6MHz) either L1 or L2 can be True
293 | λ=𝑐/𝑓 - Wavelength calculated from c and f
294 | """""
295 |
296 | def __init__(self, ref_station: List[float] = None,
297 | rov_station: List[float] = None,
298 | corresponding_sat: List[float] = None,
299 | sat_ref: List[float] = None,
300 | L1: bool = True,
301 | L2: bool = False,
302 | observed: float = None):
303 |
304 | # Speed of light m/s
305 | c: float = 299792458.0
306 |
307 | # Signal frequency of L1 (MHz)
308 | L1_f: float = 1575.42 * 1000000
309 |
310 | # Signal frequency of L2
311 | L2_f: float = 1227.6 * 1000000
312 |
313 | # Set to True by default
314 | if L1:
315 | wl = c / L1_f
316 |
317 | if L2:
318 | wl = c / L2_f
319 |
320 | # Error check the arguments
321 | assert len(ref_station) == len(rov_station) == len(corresponding_sat) == len(sat_ref)
322 | assert L1 != L2
323 |
324 | # Initialise the class variables
325 | self.x_1A = ref_station[0]
326 | self.y_1A = ref_station[1]
327 | self.z_1A = ref_station[2]
328 | self.x_3A = rov_station[0]
329 | self.y_3A = rov_station[1]
330 | self.z_3A = rov_station[2]
331 | self.x_s = corresponding_sat[0]
332 | self.y_s = corresponding_sat[1]
333 | self.z_s = corresponding_sat[2]
334 | self.x_s_ref = sat_ref[0]
335 | self.y_s_ref = sat_ref[1]
336 | self.z_s_ref = sat_ref[2]
337 | self.wl = wl
338 | self.observed = observed
339 |
340 | def x_diff(self) -> float:
341 | return float(1 / self.wl * \
342 | (
343 | (self.x_3A - self.x_s) /
344 | (sqrt(((self.x_s - self.x_3A) ** 2) +
345 | ((self.y_s - self.y_3A) ** 2) +
346 | ((self.z_s - self.z_3A) ** 2)))
347 | -
348 | (self.x_3A - self.x_s_ref) /
349 | (sqrt(((self.x_s_ref - self.x_3A) ** 2) +
350 | ((self.y_s_ref - self.y_3A) ** 2) +
351 | ((self.z_s_ref - self.z_3A) ** 2)))))
352 |
353 | def y_diff(self) -> float:
354 | return float((1 / self.wl * \
355 | (
356 | (self.y_3A - self.y_s) /
357 | (sqrt(((self.x_s - self.x_3A) ** 2) +
358 | ((self.y_s - self.y_3A) ** 2) +
359 | ((self.z_s - self.z_3A) ** 2)))
360 | -
361 | (self.y_3A - self.y_s_ref) /
362 | (sqrt(((self.x_s_ref - self.x_3A) ** 2) +
363 | ((self.y_s_ref - self.y_3A) ** 2) +
364 | ((self.z_s_ref - self.z_3A) ** 2))))))
365 |
366 | def z_diff(self) -> float:
367 | return float(1 / self.wl * \
368 | (
369 | (self.z_3A - self.z_s) /
370 | (sqrt(((self.x_s - self.x_3A) ** 2) +
371 | ((self.y_s - self.y_3A) ** 2) +
372 | ((self.z_s - self.z_3A) ** 2)))
373 | -
374 | (self.z_3A - self.z_s_ref) /
375 | (sqrt(((self.x_s_ref - self.x_3A) ** 2) +
376 | ((self.y_s_ref - self.y_3A) ** 2) +
377 | ((self.z_s_ref - self.z_3A) ** 2)))))
378 |
379 |
380 | ```
381 |
382 |
383 |
384 | ### Calculating the updates:
385 |
386 |
387 |
388 | ### ATWA Matrix
389 |
390 | UNITS: Meters
391 |
392 | ```
393 | def ATWA(A, W):
394 | return ((transpose(A)).dot(W)).dot(A)
395 | ```
396 |
397 |
398 |
399 | ### (ATWA)^-1 Matrix
400 |
401 | UNITS: Meters
402 |
403 | ```
404 | linalg.inv(atwa)
405 | ```
406 |
407 |
408 |
409 | ## RESULT
410 |
411 | ### EPOCH 1
412 | ##### X update = 0.14130538m
413 | ##### Y update = -0.12730074m
414 | ##### Z update = 0.13206799m
415 |
416 | ##### Updated X Cooridnates: 4929605.54130538
417 | ##### Updated Y Cooridnates: -29123.82730074
418 | ##### Updated Z Cooridnates: 4033603.93206799
419 |
420 | ### EPOCH 2
421 | ##### X update = 0.1410819m
422 | ##### Y update = -0.12767789m
423 | ##### Z update = 0.13099312m
424 |
425 | ##### Updated X Cooridnates: 4929605.5410819
426 | ##### Updated Y Cooridnates: -29123.82767789
427 | ##### Updated Z Cooridnates: 4033603.93099312
428 |
429 | ### EPOCH 3
430 | ##### X update = 0.13911663m
431 | ##### Y update = -0.12739046m
432 | ##### Z update = 0.13271809m
433 |
434 | ##### Updated X Cooridnates: 4929605.53911663
435 | ##### Updated Y Cooridnates: -29123.82739046
436 | ##### Updated Z Cooridnates: 4033603.93271809
437 |
438 | ## Position Solution as an average of 3 Epochs.
439 |
440 | ##### Averaged X Cooridnates: 4929605.540501303
441 | ##### Averaged Y Cooridnates: -29123.82745636333
442 | ##### Averaged Z Cooridnates: 4033603.9319264
443 |
444 |
445 | ## Quality Assessment:
446 |
447 | The distance between the two pillars is approximatley 94.4m.
448 |
449 | Distance between Pillar 1A and Nominal coordinates for Pillar 3A:
450 | 94.287m
451 |
452 | Distance between Pillar 1A and updated coordinates for Pillar 3A
453 | 94.403m
454 |
455 | This shows a 0.116 change in range.
456 |
457 | #### Residuals:
458 |
459 | Observed - Computed residuals are all within 99% confidence intervals for all three epochs.
460 |
461 |
462 |
463 |
464 |
465 |
466 |
--------------------------------------------------------------------------------