mirror of
https://github.com/slsdetectorgroup/aare.git
synced 2026-02-18 15:28:40 +01:00
added parsing of period and exptime
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
#include <chrono>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using json = nlohmann::json;
|
||||
@@ -84,6 +85,9 @@ class RawMasterFile {
|
||||
size_t m_bitdepth{};
|
||||
uint8_t m_quad = 0;
|
||||
|
||||
std::chrono::nanoseconds m_exptime{0};
|
||||
std::chrono::nanoseconds m_period{0};
|
||||
|
||||
xy m_geometry{};
|
||||
xy m_udp_interfaces_per_module{1, 1};
|
||||
|
||||
@@ -140,6 +144,9 @@ class RawMasterFile {
|
||||
|
||||
ScanParameters scan_parameters() const;
|
||||
|
||||
std::chrono::nanoseconds exptime() const { return m_exptime; }
|
||||
std::chrono::nanoseconds period() const { return m_period; }
|
||||
|
||||
private:
|
||||
void parse_json(const std::filesystem::path &fpath);
|
||||
void parse_raw(const std::filesystem::path &fpath);
|
||||
|
||||
@@ -85,5 +85,13 @@ void define_raw_master_file_bindings(py::module &m) {
|
||||
.def_property_readonly("quad", &RawMasterFile::quad)
|
||||
.def_property_readonly("scan_parameters",
|
||||
&RawMasterFile::scan_parameters)
|
||||
.def_property_readonly("roi", &RawMasterFile::roi);
|
||||
.def_property_readonly("roi", &RawMasterFile::roi)
|
||||
.def_property_readonly("exptime", [](RawMasterFile &self) {
|
||||
double seconds = std::chrono::duration<double>(self.exptime()).count();
|
||||
return seconds;
|
||||
})
|
||||
.def_property_readonly("period", [](RawMasterFile &self) {
|
||||
double seconds = std::chrono::duration<double>(self.period()).count();
|
||||
return seconds;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -206,6 +206,12 @@ void RawMasterFile::parse_json(const std::filesystem::path &fpath) {
|
||||
|
||||
m_max_frames_per_file = j["Max Frames Per File"];
|
||||
|
||||
m_exptime = string_to<std::chrono::nanoseconds>(
|
||||
j["Exptime"].get<std::string>());
|
||||
|
||||
m_period = string_to<std::chrono::nanoseconds>(
|
||||
j["Period"].get<std::string>());
|
||||
|
||||
// Not all detectors write the bitdepth but in case
|
||||
// its not there it is 16
|
||||
try {
|
||||
@@ -431,6 +437,10 @@ void RawMasterFile::parse_raw(const std::filesystem::path &fpath) {
|
||||
m_pixels_x = std::stoi(value.substr(0, pos));
|
||||
} else if (key == "Total Frames") {
|
||||
m_total_frames_expected = std::stoi(value);
|
||||
} else if(key == "Exptime"){
|
||||
m_exptime = string_to<std::chrono::nanoseconds>(value);
|
||||
} else if(key == "Period"){
|
||||
m_period = string_to<std::chrono::nanoseconds>(value);
|
||||
} else if (key == "Dynamic Range") {
|
||||
m_bitdepth = std::stoi(value);
|
||||
} else if (key == "Quad") {
|
||||
|
||||
@@ -194,7 +194,9 @@ TEST_CASE("Parse a master file in .raw format", "[.integration]") {
|
||||
// Total Frames : 100
|
||||
REQUIRE(f.total_frames_expected() == 100);
|
||||
// Exptime : 100us
|
||||
REQUIRE(f.exptime() == std::chrono::microseconds(100));
|
||||
// Period : 4ms
|
||||
REQUIRE(f.period() == std::chrono::milliseconds(4));
|
||||
// Ten Giga : 1
|
||||
// ADC Mask : 0xffffffff
|
||||
// Analog Flag : 1
|
||||
@@ -302,8 +304,10 @@ TEST_CASE("Read eiger master file", "[.integration]") {
|
||||
// },
|
||||
// "Dynamic Range": 32,
|
||||
// "Ten Giga": 0,
|
||||
// "Exptime": "5s",
|
||||
// "Exptime": "5s",
|
||||
REQUIRE(f.exptime() == std::chrono::seconds(5));
|
||||
// "Period": "1s",
|
||||
REQUIRE(f.period() == std::chrono::seconds(1));
|
||||
// "Threshold Energy": -1,
|
||||
// "Sub Exptime": "2.62144ms",
|
||||
// "Sub Period": "2.62144ms",
|
||||
|
||||
@@ -237,4 +237,40 @@ template <> DACIndex string_to(const std::string &arg) {
|
||||
}
|
||||
|
||||
|
||||
std::string remove_unit(std::string &str) {
|
||||
auto it = str.begin();
|
||||
while (it != str.end()) {
|
||||
if (std::isalpha(*it)) {
|
||||
// Check if this is scientific notation (e or E followed by optional sign and digits)
|
||||
if (((*it == 'e' || *it == 'E') && (it + 1) != str.end())) {
|
||||
auto next = it + 1;
|
||||
// Skip optional sign
|
||||
if (*next == '+' || *next == '-') {
|
||||
++next;
|
||||
}
|
||||
// Check if followed by at least one digit
|
||||
if (next != str.end() && std::isdigit(*next)) {
|
||||
// This is scientific notation, continue scanning
|
||||
it = next;
|
||||
while (it != str.end() && std::isdigit(*it)) {
|
||||
++it;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Not scientific notation, this is the start of the unit
|
||||
break;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
auto pos = it - str.begin();
|
||||
auto unit = str.substr(pos);
|
||||
str.erase(it, end(str));
|
||||
// Strip trailing whitespace
|
||||
while (!str.empty() && std::isspace(str.back())) {
|
||||
str.pop_back();
|
||||
}
|
||||
return unit;
|
||||
}
|
||||
|
||||
} // namespace aare
|
||||
@@ -8,10 +8,46 @@
|
||||
|
||||
#include "aare/defs.hpp" //enums
|
||||
|
||||
#include <chrono> //time conversions
|
||||
|
||||
namespace aare {
|
||||
|
||||
|
||||
std::string remove_unit(std::string &str);
|
||||
|
||||
template <typename T>
|
||||
T string_to(const std::string &t, const std::string &unit) {
|
||||
double tval{0};
|
||||
try {
|
||||
tval = std::stod(t);
|
||||
} catch (const std::invalid_argument &e) {
|
||||
throw std::runtime_error("Could not convert string to time");
|
||||
}
|
||||
|
||||
using std::chrono::duration;
|
||||
using std::chrono::duration_cast;
|
||||
if (unit == "ns") {
|
||||
return duration_cast<T>(duration<double, std::nano>(tval));
|
||||
} else if (unit == "us") {
|
||||
return duration_cast<T>(duration<double, std::micro>(tval));
|
||||
} else if (unit == "ms") {
|
||||
return duration_cast<T>(duration<double, std::milli>(tval));
|
||||
} else if (unit == "s" || unit.empty()) {
|
||||
return duration_cast<T>(std::chrono::duration<double>(tval));
|
||||
} else {
|
||||
throw std::runtime_error(
|
||||
"Invalid unit in conversion from string to std::chrono::duration");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if T has a constructor that takes a string, lets use it.
|
||||
template <class T> T string_to(const std::string &arg) { return T{arg}; }
|
||||
// template <class T> T string_to(const std::string &arg) { return T{arg}; }
|
||||
template <typename T> T string_to(const std::string &arg) {
|
||||
std::string tmp{arg};
|
||||
auto unit = remove_unit(tmp);
|
||||
return string_to<T>(tmp, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a string to DetectorType
|
||||
@@ -45,4 +81,10 @@ template <> FrameDiscardPolicy string_to(const std::string &arg);
|
||||
*/
|
||||
template <> DACIndex string_to(const std::string &arg);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace aare
|
||||
@@ -144,4 +144,123 @@ TEST_CASE("DACIndex string to enum") {
|
||||
REQUIRE(string_to<aare::DACIndex>("temp_slowadc") == aare::DACIndex::SLOW_ADC_TEMP);
|
||||
|
||||
REQUIRE_THROWS(string_to<aare::DACIndex>("invalid_dac"));
|
||||
}
|
||||
|
||||
TEST_CASE("Remove unit from string") {
|
||||
using aare::remove_unit;
|
||||
|
||||
// Test basic numeric value with unit
|
||||
{
|
||||
std::string input = "123.45 V";
|
||||
std::string unit = remove_unit(input);
|
||||
REQUIRE(unit == "V");
|
||||
REQUIRE(input == "123.45");
|
||||
}
|
||||
|
||||
// Test integer value with unit
|
||||
{
|
||||
std::string input = "42 Hz";
|
||||
std::string unit = remove_unit(input);
|
||||
REQUIRE(unit == "Hz");
|
||||
REQUIRE(input == "42");
|
||||
}
|
||||
|
||||
// Test negative value with unit
|
||||
{
|
||||
std::string input = "-50.5 mV";
|
||||
std::string unit = remove_unit(input);
|
||||
REQUIRE(unit == "mV");
|
||||
REQUIRE(input == "-50.5");
|
||||
}
|
||||
|
||||
// Test value with no unit (only numbers)
|
||||
{
|
||||
std::string input = "123.45";
|
||||
std::string unit = remove_unit(input);
|
||||
REQUIRE(unit == "");
|
||||
REQUIRE(input == "123.45");
|
||||
}
|
||||
|
||||
// Test value with only unit (letters at start)
|
||||
{
|
||||
std::string input = "kHz";
|
||||
std::string unit = remove_unit(input);
|
||||
REQUIRE(unit == "kHz");
|
||||
REQUIRE(input == "");
|
||||
}
|
||||
|
||||
// Test with multiple word units
|
||||
{
|
||||
std::string input = "100 degrees Celsius";
|
||||
std::string unit = remove_unit(input);
|
||||
REQUIRE(unit == "degrees Celsius");
|
||||
REQUIRE(input == "100");
|
||||
}
|
||||
|
||||
// Test with scientific notation
|
||||
{
|
||||
std::string input = "1.23e-5 A";
|
||||
std::string unit = remove_unit(input);
|
||||
REQUIRE(unit == "A");
|
||||
REQUIRE(input == "1.23e-5");
|
||||
}
|
||||
|
||||
// Another test with scientific notation
|
||||
{
|
||||
std::string input = "-4.56E6 m/s";
|
||||
std::string unit = remove_unit(input);
|
||||
REQUIRE(unit == "m/s");
|
||||
REQUIRE(input == "-4.56E6");
|
||||
}
|
||||
|
||||
// Test with scientific notation uppercase
|
||||
{
|
||||
std::string input = "5.67E+3 Hz";
|
||||
std::string unit = remove_unit(input);
|
||||
REQUIRE(unit == "Hz");
|
||||
REQUIRE(input == "5.67E+3");
|
||||
}
|
||||
|
||||
// Test with leading zeros
|
||||
{
|
||||
std::string input = "00123 ohm";
|
||||
std::string unit = remove_unit(input);
|
||||
REQUIRE(unit == "ohm");
|
||||
REQUIRE(input == "00123");
|
||||
}
|
||||
|
||||
// Test with leading zeros no space
|
||||
{
|
||||
std::string input = "00123ohm";
|
||||
std::string unit = remove_unit(input);
|
||||
REQUIRE(unit == "ohm");
|
||||
REQUIRE(input == "00123");
|
||||
}
|
||||
|
||||
// Test empty string
|
||||
{
|
||||
std::string input = "";
|
||||
std::string unit = remove_unit(input);
|
||||
REQUIRE(unit == "");
|
||||
REQUIRE(input == "");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Conversions from time string to chrono durations") {
|
||||
using namespace std::chrono;
|
||||
using aare::string_to;
|
||||
|
||||
REQUIRE(string_to<nanoseconds>("100 ns") == nanoseconds(100));
|
||||
REQUIRE(string_to<nanoseconds>("1s") == nanoseconds(1000000000));
|
||||
REQUIRE(string_to<microseconds>("200 us") == microseconds(200));
|
||||
REQUIRE(string_to<milliseconds>("300 ms") == milliseconds(300));
|
||||
REQUIRE(string_to<seconds>("5 s") == seconds(5));
|
||||
|
||||
REQUIRE(string_to<nanoseconds>("1.5 us") == nanoseconds(1500));
|
||||
REQUIRE(string_to<microseconds>("2.5 ms") == microseconds(2500));
|
||||
REQUIRE(string_to<milliseconds>("3.5 s") == milliseconds(3500));
|
||||
|
||||
REQUIRE(string_to<seconds>("2") == seconds(2)); // No unit defaults to seconds
|
||||
|
||||
REQUIRE_THROWS(string_to<seconds>("10 min")); // Unsupported unit
|
||||
}
|
||||
Reference in New Issue
Block a user