| 260 | } |
| 261 | |
| 262 | void PulseCollection::InitializeTrajectory() |
| 263 | { |
| 264 | if (pulses.empty()) |
| 265 | throw pdal_error("PulseCollection: no pulses for Solve"); |
| 266 | |
| 267 | double tstart = std::floor((m_timeMin - m_timeOrigin) / m_args.tblock); |
| 268 | int num = int(std::ceil((m_timeMax - m_timeOrigin) / m_args.tblock) - tstart) - 1; |
| 269 | if (num < 1) |
| 270 | throw pdal_error("PulseCollection: no time interval for Solve"); |
| 271 | tstart *= m_args.tblock; |
| 272 | |
| 273 | traj = SplineFit3(num, m_args.tblock, tstart); |
| 274 | attitude = SplineFit2(num, m_args.tblock, tstart); |
| 275 | |
| 276 | for (int i = 0; i <= num; ++i) |
| 277 | { |
| 278 | traj.missing[i] = !EstimatedPositionVelocity(tstart + i * m_args.tblock, |
| 279 | traj.r[i], traj.v[i]); |
| 280 | traj.v[i] *= m_args.tblock; |
| 281 | } |
| 282 | |
| 283 | if (!traj.fillmissing(true)) |
| 284 | throw pdal_error("PulseCollection: too few pulses for initial estimate of trajectory"); |
| 285 | |
| 286 | for (int i = 0; i <= num; ++i) |
| 287 | { |
| 288 | int im = (std::max)(0, i - 1); |
| 289 | int ip = (std::min)(num, i + 1); |
| 290 | |
| 291 | // atan(dx, dy) to give clockwise from north convention |
| 292 | attitude.r[i] = Eigen::Vector2d(std::atan2(traj.r[ip](0) - traj.r[im](0), |
| 293 | traj.r[ip](1) - traj.r[im](1)), |
| 294 | std::isnan(m_args.fixedpitch) ? 0.0 : degreesToRadians(m_args.fixedpitch)); |
| 295 | attitude.v[i] = Eigen::Vector2d::Zero(); |
| 296 | } |
| 297 | // Make sure heading doesn't jump around |
| 298 | double ang0 = attitude.r[0](0); |
| 299 | for (int i = 1; i <= num; ++i) |
| 300 | { |
| 301 | double ang1 = attitude.r[i](0); |
| 302 | attitude.r[i](0) = ang0 + normalizeRadians(ang1 - ang0); |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | void PulseCollection::Solve() |
| 307 | { |
nothing calls this directly
no test coverage detected