This repository has been archived by the owner on Feb 12, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathantenna.py
275 lines (210 loc) · 8.58 KB
/
antenna.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
import math
import os
import logging
import Adafruit_PCA9685
from imu_client import ImuClient
from gps_client import GPSClient
from unmanned_aerial_vehicule import UnmannedAerialVehicule
from yaw_servo import YawServo
from pitch_servo import PitchServo
from manual_override import ManualOverride
import time
class Antenna():
# Hardcoded antenna position. Useful when GPS is unavailable
SATELLITE_DISH_DEFAULT_LATITUDE = 45.4946532
SATELLITE_DISH_DEFAULT_LONGITUDE = -73.5627038
SATELLITE_DISH_DEFAULT_ALTITUDE = 0
# Usually it's 60Hz but in this case we want it to go to 100Hz
PMW_FREQUENCY = 100 # Hz
# Hardcoded magnetic declination
MAGNETIC_DECLINATION = -14.46667
# Alma airport magnetic declination
# MAGNETIC_DECLINATION = -16.41667
def __init__(self, use_internal_gps=False):
""" Constructor """
logging.info(
'Started Antenna tracking system. Beginning initialization...')
self.ready = False
# Initialize magnetic declination
self.declination = self.MAGNETIC_DECLINATION
# Initialize GPS values
self.alt = self.SATELLITE_DISH_DEFAULT_ALTITUDE
self.lat = self.SATELLITE_DISH_DEFAULT_LATITUDE
self.lon = self.SATELLITE_DISH_DEFAULT_LONGITUDE
# Initialize UAV values
self.uav_alt = 0
self.uav_lon = 0
self.uav_lat = 0
# Initialize inertial measurement values
self.roll = 0
self.pitch = 0
self.yaw = 0
# Initialize target values
self.wyaw = 0
self.wpitch = 0
# Initialize IMU
self.imu = ImuClient()
self.imu.start()
self.imu_latency = 0
# Setup UAV
self.uav = UnmannedAerialVehicule()
self.uav.start()
if use_internal_gps:
logging.info('Fetching internal GPS')
self.gps_client = GPSClient()
self.gps_client.GPS_coordinate_avg(5)
self.alt = self.gps_client.alt
self.lat = self.gps_client.lat
self.lon = self.gps_client.lon
logging.info("Longitude: {}".format(self.lon))
logging.info("Latitude: {}".format(self.lat))
logging.info("Altitude: {}".format(self.alt))
self.is_pwm_ready = False
# Setup pwm
try:
self._pwm = Adafruit_PCA9685.PCA9685()
self._pwm.set_pwm_freq(self.PMW_FREQUENCY)
self.is_pwm_ready = True
except IOError:
logging.error(
"PWM module failed to initialize. Please check if the adafruit \
hat is plugged in the Raspberry Pi GPIO pins.")
# Init servos
self.yaw_servo = YawServo(-180, 180, 1.1, 1.9, 100, 0)
self.pitch_servo = PitchServo(0, 90, 1.1, 1.9, 100, 1)
# Setup manual override
# self.manual_override = ManualOverride()
# self.manual_override.start()
# Health check on every component
if self.imu.ready and self.uav.ready and self.is_pwm_ready:
self.ready = True
if self.ready:
logging.info(
'Antenna tracking system successfully started. Ctrl-C to stop...')
# Initialize deadzone
self._read_imu(5)
self.update_imu()
self._bearing_offset = self.yaw
def update_imu(self):
""" Get newest values from imu module """
self.pitch = self.imu.pitch
self.yaw = self.imu.yaw
self.imu_latency = self.imu.latency
def _read_imu(self, n):
""" Get average next nth values from imu """
self.imu.read_imu_n_time(n)
def close(self):
""" Stop IMU module thread """
self.imu.kill = True
""" Stop UAV module thread """
self.uav.close()
""" Stop manual override thread """
# self.manual_override.close()
if not self.is_pwm_ready:
return
# Set yaw servo to neutral position (will stop the servo movement)
self.tick_yaw = 614
self._pwm.set_pwm(self.yaw_servo.channel, 0, self.tick_yaw)
# Set pitch servo to neutral position (will stop the servo movement)
self.tick_pitch = 614
self._pwm.set_pwm(self.pitch_servo.channel, 0, self.tick_pitch)
def update_target_orientation(self):
# Transfer UAV coordinates into the antenna
self.uav_alt = self.uav.alt
self.uav_lat = self.uav.lat
self.uav_lon = self.uav.lon
self.update_imu()
self.update_yaw_from_gps()
self.update_pitch_from_gps()
self.update_angle_offset()
# Manual override of servos
########
# TODO #
########
# Update yaw servo
self.tick_yaw = self.yaw_servo.refresh(
self.wyaw, self.yaw)
self._pwm.set_pwm(self.yaw_servo.channel, 0, self.tick_yaw)
# Update pitch servo
self.tick_pitch = self.pitch_servo.refresh(
self.wpitch, self.pitch)
self._pwm.set_pwm(self.pitch_servo.channel, 0, self.tick_pitch)
def update_angle_offset(self):
""" Update yaw and wanted yaw with bearing offset angle """
self.yaw = self._calculate_bearing_offset(
self.yaw, self._bearing_offset)
self.wyaw = self._calculate_bearing_offset(
self.wyaw, self._bearing_offset)
def update_yaw_from_gps(self):
""" Update wanted yaw """
self.wyaw = self._calculate_bearing(
self.lat, self.lon, self.uav_lat, self.uav_lon)
# Apply correction using magnetic declination angle
self.wyaw = self.wyaw - self.declination
def update_pitch_from_gps(self):
""" Update wanted pitch """
self.wpitch = self._calculate_pitch(
self.lat, self.lon, self.alt, self.uav_lat, self.uav_lon, self.uav_alt)
def _calculate_pitch(self, lat_sat, long_sat, alt_sat, lat_drone, long_drone, alt_drone):
""" Calculate the pitch using haversine formula """
R = 6371000
lat_sat = math.radians(lat_sat)
lat_drone = math.radians(lat_drone)
long_sat = math.radians(long_sat)
long_drone = math.radians(long_drone)
delta_long = long_drone - long_sat
delta_lat = lat_drone - lat_sat
delta_alt = alt_drone - alt_sat
a = math.pow(math.sin(delta_lat / 2), 2) + math.cos(lat_sat) * \
math.cos(lat_drone) * math.pow(math.sin(delta_long / 2), 2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
d = R * c
pitch_angle = math.atan2(delta_alt, d)
pitch_angle = math.degrees(pitch_angle)
return pitch_angle
def _calculate_bearing(self, lat_sat, long_sat, lat_drone, long_drone):
""" Calculate the bearing based on antenna and uav gps coordinates"""
lat_sat = math.radians(lat_sat)
lat_drone = math.radians(lat_drone)
long_sat = math.radians(long_sat)
long_drone = math.radians(long_drone)
delta_long = long_drone - long_sat
delta_lat = lat_drone - lat_sat
y = math.sin(delta_long) * math.cos(lat_drone)
x = math.cos(lat_sat) * math.sin(lat_drone) - \
math.sin(lat_sat) * math.cos(lat_drone) * math.cos(delta_long)
bearing_initial = math.degrees(math.atan2(y, x))
return bearing_initial
def _calculate_bearing_offset(self, angle, bearingangleoffset):
""" Calculate the bearing offset used to apply a deadzone.
It makes the antenna easier to operate because we don't
have to deal with tangled wires.
"""
newbearing = angle
newbearing -= bearingangleoffset
if newbearing > 180:
newbearing -= 360
if newbearing < -180:
newbearing += 360
return newbearing
def print_current_data(self):
os.system("clear")
print("[UAV]")
print("\tLatitude\t" + str(self.uav_lat))
print("\tLongitude\t" + str(self.uav_lon))
print("\tAltitude\t" + str(self.uav_alt))
print("\tLatency\t\t" + str(self.uav.latency) + "ms")
print("[Antenna]")
print("\tLatitude\t" + str(self.lat))
print("\tLongitude\t" + str(self.lon))
print("\tAltitude\t" + str(self.alt) + "m")
print("[IMU]")
print("\tYaw\t\t" + str(self.yaw))
print("\tWanted yaw\t" + str(self.wyaw))
print("\tPitch\t\t" + str(self.pitch))
print("\tWanted pitch\t" + str(self.wpitch))
print("\tLatency\t\t" +
"{0:.2f}".format(round(self.imu_latency, 2)) + "ms")
print("[Servos]")
print("\tYaw tick\t" + str(self.tick_yaw))
print("\tPitch tick\t" + str(self.tick_pitch))