#include "fgc/ScanGrid.h" #include #include #include #include namespace fgc { namespace { std::string trim(const std::string& s) { size_t b = s.find_first_not_of(" \t\r\n"); if (b == std::string::npos) return ""; size_t e = s.find_last_not_of(" \t\r\n"); return s.substr(b, e - b + 1); } std::vector parseDoubleList(const std::string& csv) { std::vector out; size_t start = 0; while (start <= csv.size()) { size_t comma = csv.find(',', start); std::string tok = trim(csv.substr(start, comma == std::string::npos ? std::string::npos : comma - start)); if (!tok.empty()) out.push_back(std::stod(tok)); if (comma == std::string::npos) break; start = comma + 1; } return out; } } // namespace std::vector parseScanCsv(const std::string& text) { std::vector pts; std::istringstream in(text); std::string line; int lineno = 0; while (std::getline(in, line)) { ++lineno; std::string t = trim(line); if (t.empty() || t[0] == '#') continue; size_t comma = t.find(','); if (comma == std::string::npos) throw std::runtime_error("scan grid line " + std::to_string(lineno) + ": expected 'yaw_deg,pitch_deg', got '" + t + "'"); try { ScanPoint p; p.yaw_deg = std::stod(trim(t.substr(0, comma))); p.pitch_deg = std::stod(trim(t.substr(comma + 1))); pts.push_back(p); } catch (const std::exception&) { throw std::runtime_error("scan grid line " + std::to_string(lineno) + ": non-numeric coordinate in '" + t + "'"); } } return pts; } std::vector generateScanGrid(const ScanConfig& cfg) { std::vector pts; std::vector pitches = parseDoubleList(cfg.pitch_levels); if (pitches.empty()) pitches.push_back(0.0); int n = cfg.yaw_intervals; if (n < 1) n = 1; // pitch-major, yaw inner (min -> max); the runtime cursor ping-pongs the whole list. for (double pitch : pitches) { for (int i = 0; i < n; ++i) { double frac = n > 1 ? static_cast(i) / (n - 1) : 0.0; ScanPoint p; p.yaw_deg = cfg.yaw_min_deg + frac * (cfg.yaw_max_deg - cfg.yaw_min_deg); p.pitch_deg = pitch; pts.push_back(p); } } return pts; } ScanGrid ScanGrid::fromConfig(const ScanConfig& cfg) { if (!cfg.grid_file.empty()) { std::ifstream f(cfg.grid_file); if (!f) throw std::runtime_error("cannot open scan grid file: " + cfg.grid_file); std::stringstream buf; buf << f.rdbuf(); auto pts = parseScanCsv(buf.str()); if (pts.empty()) throw std::runtime_error("scan grid file has no waypoints: " + cfg.grid_file); return ScanGrid(std::move(pts)); } return ScanGrid(generateScanGrid(cfg)); } void ScanGrid::advance() { if (points_.size() <= 1) return; if (forward_) { if (index_ + 1 < points_.size()) { ++index_; } else { forward_ = false; --index_; } } else { if (index_ > 0) { --index_; } else { forward_ = true; ++index_; } } } } // namespace fgc