// Copyright (c) FIRST and other WPILib contributors. // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. #include "sysid/analysis/JSONConverter.h" #include #include #include #include #include #include #include #include #include #include "sysid/Util.h" #include "sysid/analysis/AnalysisManager.h" #include "sysid/analysis/AnalysisType.h" // Sizes of the arrays for new sysid data. static constexpr size_t kDrivetrainSize = 9; static constexpr size_t kGeneralSize = 4; // Indices for the old data. static constexpr size_t kTimestampCol = 0; static constexpr size_t kLVoltsCol = 3; static constexpr size_t kRVoltsCol = 4; static constexpr size_t kLPosCol = 5; static constexpr size_t kRPosCol = 6; static constexpr size_t kLVelCol = 7; static constexpr size_t kRVelCol = 8; static wpi::json GetJSON(std::string_view path, wpi::Logger& logger) { std::error_code ec; wpi::raw_fd_istream input{path, ec}; if (ec) { throw std::runtime_error(fmt::format("Unable to read: {}", path)); } wpi::json json; input >> json; WPI_INFO(logger, "Read frc-characterization JSON from {}", path); return json; } std::string sysid::ConvertJSON(std::string_view path, wpi::Logger& logger) { wpi::json ojson = GetJSON(path, logger); auto type = sysid::analysis::FromName(ojson.at("test").get()); auto factor = ojson.at("unitsPerRotation").get(); auto unit = ojson.at("units").get(); wpi::json json; for (auto&& key : AnalysisManager::kJsonDataKeys) { if (type == analysis::kDrivetrain) { // Get the old data; create a vector for the new data; reserve the // appropriate size for the new data. auto odata = ojson.at(key).get>>(); std::vector> data; data.reserve(odata.size()); // Transfer the data. for (auto&& pt : odata) { data.push_back(std::array{ pt[kTimestampCol], pt[kLVoltsCol], pt[kRVoltsCol], pt[kLPosCol], pt[kRPosCol], pt[kLVelCol], pt[kRVelCol], 0.0, 0.0}); } json[key] = data; } else { // Get the old data; create a vector for the new data; reserve the // appropriate size for the new data. auto odata = ojson.at(key).get>>(); std::vector> data; data.reserve(odata.size()); // Transfer the data. for (auto&& pt : odata) { data.push_back(std::array{ pt[kTimestampCol], pt[kLVoltsCol], pt[kLPosCol], pt[kLVelCol]}); } json[key] = data; } } json["units"] = unit; json["unitsPerRotation"] = factor; json["test"] = type.name; json["sysid"] = true; // Write the new file with "_new" appended to it. path.remove_suffix(std::string_view{".json"}.size()); std::string loc = fmt::format("{}_new.json", path); sysid::SaveFile(json.dump(2), std::filesystem::path{loc}); WPI_INFO(logger, "Wrote new JSON to: {}", loc); return loc; } std::string sysid::ToCSV(std::string_view path, wpi::Logger& logger) { wpi::json json = GetJSON(path, logger); auto type = sysid::analysis::FromName(json.at("test").get()); auto factor = json.at("unitsPerRotation").get(); auto unit = json.at("units").get(); std::string_view abbreviation = GetAbbreviation(unit); std::error_code ec; // Naming: {sysid-json-name}(Test, Units).csv path.remove_suffix(std::string_view{".json"}.size()); std::string loc = fmt::format("{} ({}, {}).csv", path, type.name, unit); wpi::raw_fd_ostream outputFile{loc, ec}; if (ec) { throw std::runtime_error("Unable to write to: " + loc); } fmt::print(outputFile, "Timestamp (s),Test,"); if (type == analysis::kDrivetrain || type == analysis::kDrivetrainAngular) { fmt::print( outputFile, "Left Volts (V),Right Volts (V),Left Position ({0}),Right " "Position ({0}),Left Velocity ({0}/s),Right Velocity ({0}/s),Gyro " "Position (deg),Gyro Rate (deg/s)\n", abbreviation); } else { fmt::print(outputFile, "Volts (V),Position({0}),Velocity ({0}/s)\n", abbreviation); } outputFile << "\n"; for (auto&& key : AnalysisManager::kJsonDataKeys) { if (type == analysis::kDrivetrain || type == analysis::kDrivetrainAngular) { auto tempData = json.at(key).get>>(); for (auto&& pt : tempData) { fmt::print(outputFile, "{},{},{},{},{},{},{},{},{},{}\n", pt[0], // Timestamp key, // Test pt[1], pt[2], // Left and Right Voltages pt[3] * factor, pt[4] * factor, // Left and Right Positions pt[5] * factor, pt[6] * factor, // Left and Right Velocity pt[7], pt[8] // Gyro Position and Velocity ); } } else { auto tempData = json.at(key).get>>(); for (auto&& pt : tempData) { fmt::print(outputFile, "{},{},{},{},{}\n", pt[0], // Timestamp, key, // Test pt[1], // Voltage pt[2] * factor, // Position pt[3] * factor // Velocity ); } } } outputFile.flush(); WPI_INFO(logger, "Wrote CSV to: {}", loc); return loc; }