From 7fdb6596e8e88933fab33a4318d9692f93011c20 Mon Sep 17 00:00:00 2001 From: Oliver Bock <oliver.bock@aei.mpg.de> Date: Wed, 23 Jul 2008 10:04:19 +0200 Subject: [PATCH] Added new Starsphere engine to visualize the Radio Pulsar Search * For the time being we just added textual search parameters * Still to come: FFT spectrum in upper right corner --- src/framework/GraphicsEngineFactory.cpp | 5 +- src/framework/GraphicsEngineFactory.h | 20 +-- src/starsphere/EinsteinRadioAdapter.cpp | 122 +++++++++++++++++ src/starsphere/EinsteinRadioAdapter.h | 173 ++++++++++++++++++++++++ src/starsphere/StarsphereRadio.cpp | 153 +++++++++++++++++++++ src/starsphere/StarsphereRadio.h | 166 +++++++++++++++++++++++ 6 files changed, 629 insertions(+), 10 deletions(-) create mode 100644 src/starsphere/EinsteinRadioAdapter.cpp create mode 100644 src/starsphere/EinsteinRadioAdapter.h create mode 100644 src/starsphere/StarsphereRadio.cpp create mode 100644 src/starsphere/StarsphereRadio.h diff --git a/src/framework/GraphicsEngineFactory.cpp b/src/framework/GraphicsEngineFactory.cpp index 3186f4c..539047f 100644 --- a/src/framework/GraphicsEngineFactory.cpp +++ b/src/framework/GraphicsEngineFactory.cpp @@ -37,11 +37,14 @@ AbstractGraphicsEngine * GraphicsEngineFactory::createInstance( case EinsteinS5R3: return new StarsphereS5R3(); break; + case EinsteinRadio: + return new StarsphereRadio(); + break; default: return NULL; } break; default: - return NULL; + return NULL; } } diff --git a/src/framework/GraphicsEngineFactory.h b/src/framework/GraphicsEngineFactory.h index b6bcdc0..c66e7ca 100644 --- a/src/framework/GraphicsEngineFactory.h +++ b/src/framework/GraphicsEngineFactory.h @@ -23,6 +23,7 @@ #include "AbstractGraphicsEngine.h" #include "StarsphereS5R3.h" +#include "StarsphereRadio.h" /** * \addtogroup framework Framework @@ -41,34 +42,35 @@ class GraphicsEngineFactory public: /// Destructor virtual ~GraphicsEngineFactory(); - + /// Identifiers of supported graphics engines enum Engines { Starsphere = 1 }; - + /// Identifiers of supported science applications enum Applications { - EinsteinS5R3 = 53 + EinsteinS5R3 = 53, + EinsteinRadio = 42 }; - + /** * \brief Instantiates a new graphics engine - * + * * Use this method to create a new grahics engine instance. However, please make * that you use only sensible combinations of \c engine and \c application (you * should know them). - * + * * \param engine The identifier of the requested graphics engine * \param application The identifier of the requested science application support - * + * * \return The pointer to the new engine instance - * + * * \see Engines * \see Applications */ static AbstractGraphicsEngine * createInstance(Engines engine, Applications application); - + private: /// Contructor (private since this a purely static factory) GraphicsEngineFactory(); diff --git a/src/starsphere/EinsteinRadioAdapter.cpp b/src/starsphere/EinsteinRadioAdapter.cpp new file mode 100644 index 0000000..b1522bd --- /dev/null +++ b/src/starsphere/EinsteinRadioAdapter.cpp @@ -0,0 +1,122 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Bock * + * oliver.bock[AT]aei.mpg.de * + * * + * This file is part of Einstein@Home. * + * * + * Einstein@Home is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published * + * by the Free Software Foundation, version 2 of the License. * + * * + * Einstein@Home is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Einstein@Home. If not, see <http://www.gnu.org/licenses/>. * + * * + ***************************************************************************/ + +#include "EinsteinRadioAdapter.h" + +const string EinsteinRadioAdapter::SharedMemoryIdentifier = "EinsteinRadio"; + +EinsteinRadioAdapter::EinsteinRadioAdapter(BOINCClientAdapter *boincClient) +{ + this->boincClient = boincClient; + + m_WUSkyPosRightAscension = 0.0; + m_WUSkyPosDeclination = 0.0; + m_WUFractionDone = 0.0; + m_WUCPUTime = 0.0; +} + +EinsteinRadioAdapter::~EinsteinRadioAdapter() +{ +} + +void EinsteinRadioAdapter::refresh() +{ + boincClient->refresh(); + parseApplicationInformation(); +} + +void EinsteinRadioAdapter::parseApplicationInformation() +{ + // get updated application information + string info = boincClient->applicationInformation(); + + // do we have any data? + if(info.length() > 0) { + + // parse data into members + // TODO: this is soon going to be replaced by true XML parsing! + if(4 != sscanf(info.c_str(), + "<graphics_info>\n" + " <fraction_done>%lf</fraction_done>\n" + " <cpu_time>%lf</cpu_time>\n" + " <skypos_rac>%lf</skypos_rac>\n" + " <skypos_dec>%lf</skypos_dec>\n" + " <dispersion>%lf</dispersion>\n", + " <orb_radius>%lf</orb_radius>\n", + " <orb_period>%lf</orb_period\n", + " <orb_phase>%lf</orb_phase>\n", + &m_WUFractionDone, + &m_WUCPUTime, + &m_WUSkyPosRightAscension, + &m_WUSkyPosDeclination, + &m_WUDispersionMeasure, + &m_WUTemplateOrbitalRadius, + &m_WUTemplateOrbitalPeriod, + &m_WUTemplateOrbitalPhase)) + { + cerr << "Incompatible shared memory data encountered!" << endl; + } + else { + // convert radians to degrees + m_WUSkyPosRightAscension *= 180/PI; + m_WUSkyPosDeclination *= 180/PI; + } + } +} + +double EinsteinRadioAdapter::wuSkyPosRightAscension() const +{ + return m_WUSkyPosRightAscension; +} + +double EinsteinRadioAdapter::wuSkyPosDeclination() const +{ + return m_WUSkyPosDeclination; +} + +double EinsteinRadioAdapter::wuDispersionMeasure() const +{ + return m_WUDispersionMeasure; +} + +double EinsteinRadioAdapter::wuTemplateOrbitalRadius() const +{ + return m_WUTemplateOrbitalRadius; +} + +double EinsteinRadioAdapter::wuTemplateOrbitalPeriod() const +{ + return m_WUTemplateOrbitalPeriod; +} + +double EinsteinRadioAdapter::wuTemplateOrbitalPhase() const +{ + return m_WUTemplateOrbitalPhase; +} + +double EinsteinRadioAdapter::wuFractionDone() const +{ + return m_WUFractionDone; +} + +double EinsteinRadioAdapter::wuCPUTime() const +{ + return m_WUCPUTime; +} diff --git a/src/starsphere/EinsteinRadioAdapter.h b/src/starsphere/EinsteinRadioAdapter.h new file mode 100644 index 0000000..0d0a514 --- /dev/null +++ b/src/starsphere/EinsteinRadioAdapter.h @@ -0,0 +1,173 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Bock * + * oliver.bock[AT]aei.mpg.de * + * * + * This file is part of Einstein@Home. * + * * + * Einstein@Home is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published * + * by the Free Software Foundation, version 2 of the License. * + * * + * Einstein@Home is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Einstein@Home. If not, see <http://www.gnu.org/licenses/>. * + * * + ***************************************************************************/ + +#ifndef EINSTEINRADIOADAPTER_H_ +#define EINSTEINRADIOADAPTER_H_ + +#include <string> + +#include "BOINCClientAdapter.h" + +using namespace std; + +#define PI 3.14159265 + +/** + * \addtogroup starsphere Starsphere + * @{ + */ + +/** + * \brief Adapter class which facilitates communications with the \b Einstein\@Home Radio Pulsar application + * + * This adapter class can be used to query the \b Einstein\@Home Radio Pulsar application + * for informational data about the current work unit like search details and progress. + * + * \author Oliver Bock\n + * Max-Planck-Institute for Gravitational Physics\n + * Hannover, Germany + */ +class EinsteinRadioAdapter +{ +public: + /** + * \brief Constructor + * + * \param boincClient Pointer to the parent BOINC client adapter instance + */ + EinsteinRadioAdapter(BOINCClientAdapter* boincClient); + + /// Destructor + virtual ~EinsteinRadioAdapter(); + + /** + * \brief Refreshes dynamic data (e.g. search information) + * + * You want to call this method periodically to refresh any volatile application information + * + * \see AbstractGraphicsEngine::refreshBOINCInformation + */ + void refresh(); + + /** + * \brief Retrieves the right ascension of the currently searched sky position + * + * \return The right ascension (in degrees) + */ + double wuSkyPosRightAscension() const; + + /** + * \brief Retrieves the declination of the currently searched sky position + * + * \return The right ascension (in degrees) + */ + double wuSkyPosDeclination() const; + + /** + * \brief Retrieves the dispersion measure of the currently searched sky position + * + * \return The dispersion measure + */ + double wuDispersionMeasure() const; + + /** + * \brief Retrieves the projected orbital radius of the currently active template + * + * \return The projected orbital radius + */ + double wuTemplateOrbitalRadius() const; + + /** + * \brief Retrieves the orbital period of the currently active template + * + * \return The orbital period of the currently active template + */ + double wuTemplateOrbitalPeriod() const; + + /** + * \brief Retrieves the initial orbital phase of the currently active template + * + * \return The initial orbital phase of the currently active template + */ + double wuTemplateOrbitalPhase() const; + + /** + * \brief Retrieves the completion fraction of the currently active work unit + * + * \return The completion fraction (range 0-1) + */ + double wuFractionDone() const; + + /** + * \brief Retrieves the amount of CPU time consumed for the currently active work unit + * during the active session + * + * \return The accumulated CPU time consumed during this work unit session (in seconds) + */ + double wuCPUTime() const; + + /// The identifier of the Einstein\@Home Radio Pulsar science application's shared memory area + static const string SharedMemoryIdentifier; + +private: + + /** + * \brief Parses science application specific information into local attributes + * + * The information is usually transferred via a shared memory area + * which is handled by the parent generic BOINC client adapter. + * + * \see boincClient + */ + void parseApplicationInformation(); + + /// Pointer to the (parent) BOINC client adapter + BOINCClientAdapter *boincClient; + + /// Right ascension of the currently searched sky position (in degrees) + double m_WUSkyPosRightAscension; + + /// Declination of the currently searched sky position (in degrees) + double m_WUSkyPosDeclination; + + /// Dispersion measure of the currently searched sky position + double m_WUDispersionMeasure; + + /// Projected orbital radius of the currently active template + double m_WUTemplateOrbitalRadius; + + /// Orbital period of the currently active template + double m_WUTemplateOrbitalPeriod; + + /// Initial orbital phase of the currently active template + double m_WUTemplateOrbitalPhase; + + /// The completion fraction of the active work unit + double m_WUFractionDone; + + /// Amount of CPU time consumed for the work unit during the active session + double m_WUCPUTime; +}; + +/** + * @} + */ + +#endif /*EINSTEINRADIOADAPTER_H_*/ diff --git a/src/starsphere/StarsphereRadio.cpp b/src/starsphere/StarsphereRadio.cpp new file mode 100644 index 0000000..b9feeb1 --- /dev/null +++ b/src/starsphere/StarsphereRadio.cpp @@ -0,0 +1,153 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Bock * + * oliver.bock[AT]aei.mpg.de * + * * + * This file is part of Einstein@Home. * + * * + * Einstein@Home is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published * + * by the Free Software Foundation, version 2 of the License. * + * * + * Einstein@Home is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Einstein@Home. If not, see <http://www.gnu.org/licenses/>. * + * * + ***************************************************************************/ + +#include "StarsphereRadio.h" + +StarsphereRadio::StarsphereRadio() : Starsphere(), m_EinsteinAdapter(&m_BoincAdapter) +{ + m_WUDispersionMeasureValue = -1.0; +} + +StarsphereRadio::~StarsphereRadio() +{ +} + +void StarsphereRadio::initialize(const int width, const int height, const Resource *font, const bool recycle) +{ + Starsphere::initialize(width, height, font, recycle); + + // check whether we initialize the first time or have to recycle (required for windoze) + if(!recycle) { + + // adjust HUD config + m_YOffsetMedium = 15; + m_XStartPosRight = width - 125; + m_YStartPosBottom = 100; + m_Y1StartPosBottom = m_YStartPosBottom - m_YOffsetMedium; + m_Y2StartPosBottom = m_Y1StartPosBottom - m_YOffsetMedium; + m_Y3StartPosBottom = m_Y2StartPosBottom - m_YOffsetMedium; + m_Y4StartPosBottom = m_Y3StartPosBottom - m_YOffsetMedium; + m_Y5StartPosBottom = m_Y4StartPosBottom - m_YOffsetMedium; + m_Y6StartPosBottom = m_Y5StartPosBottom - m_YOffsetMedium; + } +} + +void StarsphereRadio::resize(const int width, const int height) +{ + Starsphere::resize(width, height); + + // adjust HUD config + m_XStartPosRight = width - 125; +} + +void StarsphereRadio::refreshBOINCInformation() +{ + // call base class implementation + Starsphere::refreshLocalBOINCInformation(); + + // update local/specific content + m_EinsteinAdapter.refresh(); + + // prepare conversion buffer + stringstream buffer; + buffer.precision(2); + buffer.setf(ios::fixed, ios::floatfield); + buffer.fill('0'); + buffer.setf(ios::right, ios::adjustfield); + + // store content required for our HUD (search info) + if(m_CurrentRightAscension != m_EinsteinAdapter.wuSkyPosRightAscension()) { + // we've got a new position, update search marker and HUD + m_CurrentRightAscension = m_EinsteinAdapter.wuSkyPosRightAscension(); + m_RefreshSearchMarker = true; + buffer << "Ascension: " << fixed << m_CurrentRightAscension << " deg" << ends; + m_WUSkyPosRightAscension = buffer.str(); + buffer.str(""); + } + + if(m_CurrentDeclination != m_EinsteinAdapter.wuSkyPosDeclination()) { + // we've got a new position, update search marker and HUD + m_CurrentDeclination = m_EinsteinAdapter.wuSkyPosDeclination(); + m_RefreshSearchMarker = true; + buffer << "Declination: " << fixed << m_CurrentDeclination << " deg" << ends; + m_WUSkyPosDeclination = buffer.str(); + buffer.str(""); + } + + if(m_WUDispersionMeasureValue != m_EinsteinAdapter.wuDispersionMeasure()) { + // we've got a new dispersion measure, update HUD + m_WUDispersionMeasureValue = m_EinsteinAdapter.wuSkyPosDeclination(); + buffer << "Dispersion: " << fixed << m_WUDispersionMeasureValue << ends; + m_WUDispersionMeasure = buffer.str(); + buffer.str(""); + } + + // update the following information every time (no need to check first) + + buffer << "Orb. Radius: " << fixed << m_EinsteinAdapter.wuTemplateOrbitalRadius() << ends; + m_WUTemplateOrbitalRadius = buffer.str(); + buffer.str(""); + + buffer << "Orb. Period: " << fixed << m_EinsteinAdapter.wuTemplateOrbitalPeriod() << ends; + m_WUTemplateOrbitalPeriod = buffer.str(); + buffer.str(""); + + buffer << "Orb. Phase: " << fixed << m_EinsteinAdapter.wuTemplateOrbitalPhase() << ends; + m_WUTemplateOrbitalPhase = buffer.str(); + buffer.str(""); + + buffer << "WU Completed: " << fixed << m_EinsteinAdapter.wuFractionDone() * 100 << " %" << ends; + m_WUPercentDone = buffer.str(); + buffer.str(""); + + // show WU's total CPU time (previously accumulated + current session) + const double cputime = m_BoincAdapter.wuCPUTimeSpent() + m_EinsteinAdapter.wuCPUTime(); + const int hrs = cputime / 3600; + const int min = (cputime - hrs*3600) / 60; + const int sec = cputime - (hrs*3600 + min*60); + + buffer << "WU CPU Time: " << right << setw(2) << hrs << ":" + << right << setw(2) << min << ":" + << right << setw(2) << sec << ends; + + m_WUCPUTime = buffer.str(); +} + +void StarsphereRadio::renderSearchInformation() +{ + // left info block + m_FontHeader->draw(m_XStartPosLeft, m_YStartPosBottom, "BOINC Information"); + m_FontText->draw(m_XStartPosLeft, m_Y1StartPosBottom, m_UserName.c_str()); + m_FontText->draw(m_XStartPosLeft, m_Y2StartPosBottom, m_TeamName.c_str()); + m_FontText->draw(m_XStartPosLeft, m_Y3StartPosBottom, m_UserCredit.c_str()); + m_FontText->draw(m_XStartPosLeft, m_Y4StartPosBottom, m_UserRACredit.c_str()); + m_FontText->draw(m_XStartPosLeft, m_Y5StartPosBottom, m_WUPercentDone.c_str()); + m_FontText->draw(m_XStartPosLeft, m_Y6StartPosBottom, m_WUCPUTime.c_str()); + + // right info block + m_FontHeader->draw(m_XStartPosRight, m_YStartPosBottom, "Search Information"); + m_FontText->draw(m_XStartPosRight, m_Y1StartPosBottom, m_WUSkyPosRightAscension.c_str()); + m_FontText->draw(m_XStartPosRight, m_Y2StartPosBottom, m_WUSkyPosDeclination.c_str()); + m_FontText->draw(m_XStartPosRight, m_Y3StartPosBottom, m_WUDispersionMeasure.c_str()); + m_FontText->draw(m_XStartPosRight, m_Y4StartPosBottom, m_WUTemplateOrbitalRadius.c_str()); + m_FontText->draw(m_XStartPosRight, m_Y5StartPosBottom, m_WUTemplateOrbitalPeriod.c_str()); + m_FontText->draw(m_XStartPosRight, m_Y6StartPosBottom, m_WUTemplateOrbitalPhase.c_str()); + +} diff --git a/src/starsphere/StarsphereRadio.h b/src/starsphere/StarsphereRadio.h new file mode 100644 index 0000000..ba76e01 --- /dev/null +++ b/src/starsphere/StarsphereRadio.h @@ -0,0 +1,166 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Bock * + * oliver.bock[AT]aei.mpg.de * + * * + * This file is part of Einstein@Home. * + * * + * Einstein@Home is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published * + * by the Free Software Foundation, version 2 of the License. * + * * + * Einstein@Home is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Einstein@Home. If not, see <http://www.gnu.org/licenses/>. * + * * + ***************************************************************************/ + +#ifndef STARSPHERERADIO_H_ +#define STARSPHERERADIO_H_ + +#include <sstream> + +#include "Starsphere.h" +#include "EinsteinRadioAdapter.h" + +using namespace std; + +/** + * \addtogroup starsphere Starsphere + * @{ + */ + +/** + * \brief Specialized rendering engine for the Pulsar Search science run + * + * This class comprises the specialized parts of the Starsphere rendering engine. + * The main differences stem from the fact that most science runs differ in their + * search configuration and parameters. Thus the parameters exposed by the HUD + * (head-up display) are positioned and rendered here. For the time being the + * "BOINC Information" are top-aligned to the "Search Parameters", hence they're + * also positioned and rendered here. + * + * \author Oliver Bock\n + * Max-Planck-Institute for Gravitational Physics\n + * Hannover, Germany + */ +class StarsphereRadio : public Starsphere +{ +public: + /// Default contructor + StarsphereRadio(); + + /// Destructor + virtual ~StarsphereRadio(); + + /** + * \brief This method is called to initialize the engine + * + * As this method overrides its parent's implementation, it calls Starsphere::initialize() + * first in order to "add" the specialized parts afterwards. + * + * \param width The current width of the display surface + * \param height The current height of the display surface + * \param font A pointer to a Resource object containing TTF font faces for text rendering + * \param recycle This flag indicates whether we initialize (FALSE) or reinitialize (TRUE) the context + */ + virtual void initialize(const int width, const int height, const Resource *font, const bool recycle = false); + + /** + * \brief This method is called when the windowing system encounters a window resize event + * + * As this method overrides its parent's implementation, it calls Starsphere::resize() + * first in order to "add" the specialized parts afterwards. + * + * \param width The new width of the display surface + * \param height The new height of the display surface + */ + void resize(const int width, const int height); + + /** + * \brief This method is called when the BOINC client information should be updated + * + * This method implements AbstractGraphicsEngine::refreshBOINCInformation() and calls + * Starsphere::refreshLocalBOINCInformation() first and "adds" the specialized + * parts afterwards. + * + * \see AbstractGraphicsEngine::refreshBOINCInformation() + * \see Starsphere::refreshLocalBOINCInformation() + */ + void refreshBOINCInformation(); + +private: + /** + * \brief Render science run specific search information + * + * For this specific implementation this also includes the "BOINC Statistics" + * as it is top-aligned to the "Search Information". + */ + void renderSearchInformation(); + + /// Specialized BOINC client adapter instance for information retrieval + EinsteinRadioAdapter m_EinsteinAdapter; + + /// Formatted string copy of the current WU's search parameter "Right-Ascension" (degrees) + string m_WUSkyPosRightAscension; + + /// Formatted string copy of the current WU's search parameter "Declination" (degrees) + string m_WUSkyPosDeclination; + + /// Formatted string copy of the current WU's search parameter "Dispersion measure" + string m_WUDispersionMeasure; + + /// Local value copy of the current WU's search parameter "Dispersion measure" + double m_WUDispersionMeasureValue; + + /// Formatted string copy of the current template's search parameter "Projected orbital radius" + string m_WUTemplateOrbitalRadius; + + /// Formatted string copy of the current template's search parameter "Orbital period" + string m_WUTemplateOrbitalPeriod; + + /// Formatted string copy of the current template's search parameter "Initial orbital phase" + string m_WUTemplateOrbitalPhase; + + /// Formatted string copy of the current WU's search parameter "Percent done" + string m_WUPercentDone; + + /// Formatted string copy of the current WU's search parameter "CPU Time" + string m_WUCPUTime; + + /// HUD configuration setting (line offset for medium sized font) + GLfloat m_YOffsetMedium; + + /// HUD configuration setting (horizontal start position for the right part) + GLfloat m_XStartPosRight; + + /// HUD configuration setting (vertical start position for the bottom part, header) + GLfloat m_YStartPosBottom; + + /// HUD configuration setting (vertical start position for the bottom part, line 1) + GLfloat m_Y1StartPosBottom; + + /// HUD configuration setting (vertical start position for the bottom part, line 2) + GLfloat m_Y2StartPosBottom; + + /// HUD configuration setting (vertical start position for the bottom part, line 3) + GLfloat m_Y3StartPosBottom; + + /// HUD configuration setting (vertical start position for the bottom part, line 4) + GLfloat m_Y4StartPosBottom; + + /// HUD configuration setting (vertical start position for the bottom part, line 5) + GLfloat m_Y5StartPosBottom; + + /// HUD configuration setting (vertical start position for the bottom part, line 6) + GLfloat m_Y6StartPosBottom; +}; + +/** + * @} + */ + +#endif /*STARSPHERES5R3_H_*/ -- GitLab