diff --git a/CMakeLists.txt b/CMakeLists.txt index e43e461..8a2c93c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,6 +152,10 @@ target_link_libraries( PUBLIC cxxopts::cxxopts ${ASIBFM700_LIB} ) +set(ASIBFM700_PCM_FIT_APP asibfm700_pcm_fit) +add_executable(${ASIBFM700_PCM_FIT_APP} asibfm700_pcm_fit.cpp) +target_link_libraries(${ASIBFM700_PCM_FIT_APP} PUBLIC cxxopts::cxxopts mcc) + # include(CMakePrintHelpers) # cmake_print_properties( # TARGETS ${ASIBFM700_LIB} diff --git a/asibfm700_pcm_fit.cpp b/asibfm700_pcm_fit.cpp new file mode 100644 index 0000000..e45b4b3 --- /dev/null +++ b/asibfm700_pcm_fit.cpp @@ -0,0 +1,182 @@ +#include +#include +#include +#include + +#include + +#include +#include + +static constexpr mcc::MccMountType MOUNT_TYPE{mcc::MccMountType::CROSSAXIS_TYPE}; + +// #include "asibfm700_configfile.h" + +int main(int argc, char* argv[]) +{ + /* COMMANDLINE OPTS */ + cxxopts::Options options(argv[0], "Astrosib (c) FM700 mount PCM fitter\n"); + + options.allow_unrecognised_options(); + + options.add_options()("h,help", "Print usage"); + + options.add_options()("v,verbose", "Verbose output"); + + options.add_options()("input_file", "Input encoder-celestial coordinate pairs file", + cxxopts::value()->default_value("pcm_coords.dat")); + + options.add_options()("c,config", "Mount configuration filename (by default use of hardcoded one)", + cxxopts::value()->default_value("")); + + options.positional_help("[input_encoder-celestial_pair_filename]"); + options.parse_positional({"input_file"}); + + mcc::impl::MccPCMFitter pcm_fitter; + + try { + auto opt_result = options.parse(argc, argv); + + if (opt_result["help"].count() || argc == 1) { + std::println("{}", options.help()); + + std::println( + "[input_encoder-celestial_pair_filename] - Input encoder-celestial coordinate pairs filename. " + "It must be in the format: ENCODER_HA ENCODER_DEC ENCODER_EPOCH RA_ICRS DEC_ICRS TEMP(C) PRESSURE(hPa) " + "HUMIDITY([0-1])", + options.help()); + + return 0; + } + + bool verbose = false; + if (opt_result["verbose"].count()) { + verbose = true; + } + + std::ifstream fst; + + auto fname = opt_result["input_file"].as(); + fst.open(fname); + if (!fst.is_open()) { + std::println("Cannot open input file {}", fname); + return 2; + } + + size_t sz; + double temp, press, humi; + std::optional num; + std::string str; + std::string_view sv; + std::array tokens; + + mcc::impl::MccCelestialCoordEpoch ep; + mcc::impl::MccAngleX enc_ha; + mcc::impl::MccAngleY enc_dec; + mcc::impl::MccAngleRA_ICRS ra; + mcc::impl::MccAngleDEC_ICRS dec; + + mcc::impl::MccSkyPoint sp; + mcc::impl::MccError err; + + while (!fst.eof()) { + std::getline(fst, str); + + sv = mcc::utils::trimSpaces(str); + if (!sv.size()) { // an empty string + continue; + } + if (sv[0] == '#') { // comment + continue; + } + + auto toks = std::views::split(sv, std::string_view(" ")) | + std::views::filter([](auto const& r) { return std::ranges::size(r); }); + + sz = std::ranges::distance(toks.begin(), toks.end()); + if (sz < 8) { + std::println("Invalid input file format! Number of tokens must be at least 8: {}", str); + return 3; + } + + size_t i = 0; + for (auto const& t : toks) { + tokens[i++] = {t.begin(), t.end()}; + } + + // degrees or sexagesimal hours representations + enc_ha = {tokens[0], mcc::impl::mcc_hms}; + + // degrees or sexagesimal degrees representations + enc_dec = {tokens[1]}; + + if (!ep.fromCharRange(tokens[2])) { + std::println("Invalid input file format! Invalid encoder coordinates epoch representation: {}", + tokens[2]); + return 4; + } + + // degrees or sexagesimal hours representations + ra = {tokens[3], mcc::impl::mcc_hms}; + + // degrees or sexagesimal degrees representations + dec = {tokens[4]}; + + num = mcc::utils::numFromStr(tokens[5]); + if (!num) { + std::println("Invalid input file format! Non-numeric representation of temperature: {}", tokens[5]); + return 5; + } + temp = num.value(); + + num = mcc::utils::numFromStr(tokens[6]); + if (!num) { + std::println("Invalid input file format! Non-numeric representation of pressure: {}", tokens[6]); + return 6; + } + press = num.value(); + + num = mcc::utils::numFromStr(tokens[7]); + if (!num) { + std::println("Invalid input file format! Non-numeric representation of humidity: {}", tokens[7]); + return 7; + } + humi = num.value(); + + mcc::impl::MccSkyPoint::cctEngine.updateMeteoERFA( + {.temperature = temp, .humidity = humi, .pressure = press}); + + sp.from(mcc::impl::MccSkyRADEC_ICRS{ra, dec}); + + err = pcm_fitter.addPoint(sp, mcc::impl::MccGenXY{enc_ha, enc_dec, ep}); + if (err) { + std::println("An error occured: {}", err.message()); + return 8; + } + + if (verbose) { + std::println("{} {} {:.5f} {} {} {:.1f} {:.2f} {:.2f}", enc_ha.sexagesimal(true), + enc_dec.sexagesimal(), ep.MJD(), ra.sexagesimal(true), dec.sexagesimal(), temp, press, + humi); + } + } + + fst.close(); + + } catch (cxxopts::exceptions::parsing& ex) { + std::println("An error occured while parsing input options: {}", ex.what()); + + return 1; + } catch (std::exception& ex) { + std::println("An exception occured: {}", ex.what()); + + return 10; + + } catch (...) { + std::println("An unhandled exception occured!"); + + return 100; + } + + return 0; +} \ No newline at end of file