Skip to content

Commit b9d53c9

Browse files
committed
fix ship trajectory by using geod.fwd_intermediate() to get all points across the shortest path on globe
1 parent de9508d commit b9d53c9

1 file changed

Lines changed: 70 additions & 58 deletions

File tree

src/virtualship/expedition/simulate_schedule.py

Lines changed: 70 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def __init__(self, projection: pyproj.Geod, expedition: Expedition) -> None:
116116
self._next_ship_underwater_st_time = self._time
117117

118118
def simulate(self) -> ScheduleOk | ScheduleProblem:
119-
# TODO: instrument config mapping (as introduced in #269) should be helpful for refactoring here...
119+
# TODO: instrument config mapping (as introduced in #269) should be helpful for refactoring here (i.e. #236)...
120120

121121
for wp_i, waypoint in enumerate(self._expedition.schedule.waypoints):
122122
# sail towards waypoint
@@ -140,6 +140,7 @@ def simulate(self) -> ScheduleOk | ScheduleProblem:
140140

141141
# wait while measurements are being done
142142
self._progress_time_stationary(time_passed)
143+
143144
return ScheduleOk(self._time, self._measurements_to_simulate)
144145

145146
def _progress_time_traveling_towards(self, location: Location) -> None:
@@ -150,90 +151,101 @@ def _progress_time_traveling_towards(self, location: Location) -> None:
150151
self._projection,
151152
)
152153
end_time = self._time + time_to_reach
154+
distance_to_move = ship_speed_meter_per_second * time_to_reach.total_seconds()
155+
153156
# note all ADCP measurements
154157
if self._expedition.instruments_config.adcp_config is not None:
155-
location = self._location
156-
time = self._time
157-
while self._next_adcp_time <= end_time:
158-
time_to_sail = self._next_adcp_time - time
159-
distance_to_move = (
160-
ship_speed_meter_per_second * time_to_sail.total_seconds()
161-
)
162-
geodfwd: tuple[float, float, float] = self._projection.fwd(
163-
lons=location.lon,
164-
lats=location.lat,
165-
az=azimuth1,
166-
dist=distance_to_move,
167-
)
168-
location = Location(latitude=geodfwd[1], longitude=geodfwd[0])
169-
time = time + time_to_sail
170-
158+
adcp_times, adcp_lons, adcp_lats = self._get_underway_measurements(
159+
self._expedition.instruments_config.adcp_config,
160+
azimuth1,
161+
distance_to_move,
162+
time_to_reach,
163+
)
164+
165+
for time, lon, lat in zip(adcp_times, adcp_lons, adcp_lats, strict=False):
166+
location = Location(latitude=lat, longitude=lon)
171167
self._measurements_to_simulate.adcps.append(
172168
Spacetime(location=location, time=time)
173169
)
174170

175-
self._next_adcp_time = (
176-
self._next_adcp_time
177-
+ self._expedition.instruments_config.adcp_config.period
178-
)
179-
180171
# note all ship underwater ST measurements
181172
if self._expedition.instruments_config.ship_underwater_st_config is not None:
182-
location = self._location
183-
time = self._time
184-
while self._next_ship_underwater_st_time <= end_time:
185-
time_to_sail = self._next_ship_underwater_st_time - time
186-
distance_to_move = (
187-
ship_speed_meter_per_second * time_to_sail.total_seconds()
188-
)
189-
geodfwd: tuple[float, float, float] = self._projection.fwd(
190-
lons=location.lon,
191-
lats=location.lat,
192-
az=azimuth1,
193-
dist=distance_to_move,
194-
)
195-
location = Location(latitude=geodfwd[1], longitude=geodfwd[0])
196-
time = time + time_to_sail
197-
198-
self._measurements_to_simulate.ship_underwater_sts.append(
173+
st_times, st_lons, st_lats = self._get_underway_measurements(
174+
self._expedition.instruments_config.ship_underwater_st_config,
175+
azimuth1,
176+
distance_to_move,
177+
time_to_reach,
178+
)
179+
180+
for time, lon, lat in zip(st_times, st_lons, st_lats, strict=False):
181+
location = Location(latitude=lat, longitude=lon)
182+
self._measurements_to_simulate.adcps.append(
199183
Spacetime(location=location, time=time)
200184
)
201185

202-
self._next_ship_underwater_st_time = (
203-
self._next_ship_underwater_st_time
204-
+ self._expedition.instruments_config.ship_underwater_st_config.period
205-
)
206-
207186
self._time = end_time
208187
self._location = location
209188

189+
def _get_underway_measurements(
190+
self,
191+
underway_instrument_config,
192+
azimuth,
193+
distance_to_move,
194+
time_to_reach: timedelta,
195+
):
196+
"""Get the times and locations of measurements between a waypoint (or the start) and the next waypoint, for underway instruments."""
197+
period = underway_instrument_config.period
198+
npts = (time_to_reach.total_seconds() / period.total_seconds()) + 1
199+
times = [self._time + i * period for i in range(1, int(npts) + 1)]
200+
201+
geodfwd = self._projection.fwd_intermediate(
202+
lon1=self._location.lon,
203+
lat1=self._location.lat,
204+
azi1=azimuth,
205+
npts=npts,
206+
del_s=distance_to_move / npts,
207+
return_back_azimuth=False,
208+
)
209+
210+
return times, geodfwd.lons, geodfwd.lats
211+
210212
def _progress_time_stationary(self, time_passed: timedelta) -> None:
211213
end_time = self._time + time_passed
212214

213-
# note all ADCP measurements
215+
# note all ADCP measurements (stationary at wp)
214216
if self._expedition.instruments_config.adcp_config is not None:
215-
while self._next_adcp_time <= end_time:
217+
adcp_times = self._get_underway_stationary_times(
218+
self._expedition.instruments_config.adcp_config, time_passed
219+
)
220+
221+
for time in adcp_times:
216222
self._measurements_to_simulate.adcps.append(
217-
Spacetime(self._location, self._next_adcp_time)
218-
)
219-
self._next_adcp_time = (
220-
self._next_adcp_time
221-
+ self._expedition.instruments_config.adcp_config.period
223+
Spacetime(location=self._location, time=time)
222224
)
223225

224-
# note all ship underwater ST measurements
226+
# note all underwater ST measurements (stationary at wp)
225227
if self._expedition.instruments_config.ship_underwater_st_config is not None:
226-
while self._next_ship_underwater_st_time <= end_time:
228+
st_times = self._get_underway_stationary_times(
229+
self._expedition.instruments_config.ship_underwater_st_config,
230+
time_passed,
231+
)
232+
for time in st_times:
227233
self._measurements_to_simulate.ship_underwater_sts.append(
228-
Spacetime(self._location, self._next_ship_underwater_st_time)
229-
)
230-
self._next_ship_underwater_st_time = (
231-
self._next_ship_underwater_st_time
232-
+ self._expedition.instruments_config.ship_underwater_st_config.period
234+
Spacetime(location=self._location, time=time)
233235
)
234236

235237
self._time = end_time
236238

239+
def _get_underway_stationary_times(self, underway_instrument_config, time_passed):
240+
npts = (
241+
time_passed.total_seconds()
242+
/ underway_instrument_config.period.total_seconds()
243+
) + 1
244+
return [
245+
self._time + i * underway_instrument_config.period
246+
for i in range(1, int(npts) + 1)
247+
]
248+
237249
def _make_measurements(self, waypoint: Waypoint) -> timedelta:
238250
# if there are no instruments, there is no time cost
239251
if waypoint.instrument is None:

0 commit comments

Comments
 (0)