text:
// File: request.cpp
// Authors: Marco Cecchi
//          Francesco Giacomini
//          Salvatore Monforte
// Copyright (c) Members of the EGEE Collaboration. 2009.
// See http://www.eu-egee.org/partners/ for details on the copyright holders.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//     http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <boost/shared_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/exception.hpp>

#include "glite/jdl/DAGAd.h"
#include "glite/jdl/JobAdManipulation.h"
#include "glite/jdl/PrivateAdManipulation.h"
#include "glite/jdl/ManipulationExceptions.h"

#include "glite/wms/common/configuration/Configuration.h"
#include "common/src/configuration/WMConfiguration.h"
#include "common/src/configuration/CommonConfiguration.h"
#include "common/src/configuration/NSConfiguration.h"
#include "glite/wms/common/utilities/wm_commands.h"
#include "glite/wms/common/utilities/input_reader.h"
#include "common/src/utilities/manipulation.h"
#include "glite/wms/common/logger/logger_utils.h"
#include "glite/wms/helper/exceptions.h"

#include "glite/wmsutils/exception/Exception.h"
#include "glite/jobid/JobId.h"

#include "purger/src/purger.h"
#include "glite/security/proxyrenewal/renewal.h"

#include "glite/lb/producer.h"

#include "submit_request.h"
#include "lb_utils.h"
#include "submission_utils.h"
#include "recovery.h"

namespace jobid = glite::jobid;
namespace jdl = glite::jdl;
namespace exception = glite::wmsutils::exception;
namespace utilities = glite::wms::common::utilities;
namespace configuration = glite::wms::common::configuration;
namespace fs = boost::filesystem;

namespace glite {
namespace wms {
namespace manager {
namespace server {

namespace {

  static int const EXIT_RETRY_NODE = 98;
  static int const EXIT_ABORT_NODE = 99;

  struct null_deleter { void operator()(void const *) const { } };

  void queue_new_submit_request(
    std::string name,
    jobid::JobId const jobid,
    classad::ClassAd& job_jdl,
    boost::shared_ptr<SubmitState> state
  ) {
    jdl::set_lb_sequence_code(job_jdl, "");
    RemoveNodeFromCollection cleanup(name, state);
    boost::shared_ptr<classad::ClassAd> fake_command_ad(
      new classad::ClassAd(
        utilities::submit_command_create(job_jdl)
      )
    );

    ContextPtr lb_context
      = create_context(jobid, state->m_x509_proxy, "");

    boost::shared_ptr<SubmitState> new_state(
      new SubmitState(
        fake_command_ad,
        "jobsubmit",
        jobid,
        "",
        state->m_x509_proxy,
        lb_context,
        cleanup,
        state->m_wm,
        state->m_events,
        state->m_jw_template
      )
    );
    state->m_events.schedule(
      SubmitProcessor(new_state),
      submit_priority
    );
  }

  class log_abort_for_child {
    ContextPtr m_context;
    std::string const m_reason;
  public:
    log_abort_for_child(ContextPtr context, std::string const& reason)
      : m_context(context), m_reason(reason) { }
    void operator()(char* id, edg_wll_JobStat& s) {
      if (is_submitted(JobStatusPtr(&s, null_deleter())) ||
        is_waiting(JobStatusPtr(&s, null_deleter()))) {

        jobid::JobId const _id(id);
        change_logging_job(m_context, EDG_WLL_SEQ_ABORT, _id);
        log_abort(m_context, m_reason);
      }
    }
  };

  void unrecoverable_collection(boost::shared_ptr<SubmitState> state, std::string msg)
  {
    JobStatusPtr cs(
      job_status(state->m_id, state->m_lb_context, children)
    );
    if (!cs) {
      Error(
        state->m_id.toString() << ": unable to retrieve collection status"
      );
      return;
    }
    if (!cs->children) {
      Error(state->m_id.toString() << ": unable to retrieve children information from jobstatus");
      return ;
    }

    char** i = cs->children;
    edg_wll_JobStat *j = cs->children_states;
    log_abort_for_child lafc(state->m_lb_context, msg);
    while (i < cs->children + cs->children_num && *i) {
      if (j && j->state != EDG_WLL_JOB_UNDEF) {
        lafc(*i, *j);
      } else {
        Error(state->m_id.toString() << ": unable to retrieve children information from jobstatus");
        *i = 0;
        continue;
      }
      ++i;
      ++j;
    }

    change_logging_job(state->m_lb_context, EDG_WLL_SEQ_ABORT, state->m_id);
  }

  void unrecoverable(
    boost::shared_ptr<SubmitState> state,
    std::string msg)
  {
    if (job_type(*state->m_ad) == collection) {
      unrecoverable_collection(state, state->m_id.toString() + " failed (" + msg + ')');
    }
    Error(state->m_id.toString() << " failed (" << msg << ')');
    log_abort(state->m_lb_context, msg);
    fs::path const cancel_token(get_cancel_token(state->m_id));
    ::unlink(cancel_token.native_file_string().c_str());
  }

  bool have_lbproxy()
  {
    configuration::Configuration const& config(
      *configuration::Configuration::instance()
    );
    return config.common()->lbproxy();
  }

  int get_expiry_period()
  {
    configuration::WMConfiguration const& wm_config
      = *configuration::Configuration::instance()->wm();
    return wm_config.expiry_period();
  }

  boost::tuple<
    int,
    int,
    std::vector<std::pair<std::string, int> >,
    std::vector<std::string>
  > retrieve_lb_info(
    jobid::JobId const& jobid,
    ContextPtr context
  )
  {
    // this can currently happen only in case of a resubmission
    // for a resubmission we need a few information stored in the LB:
    // - previous deep and shallow resubmissions
    // - previous matches
    // - original jdl

    LB_Events events(get_interesting_events(context, jobid));
    if (events.empty()) {
      Warning("Cannot retrieve interesting events for " << jobid.toString());
    }

    std::vector<std::pair<std::string, int> > const previous_matches = get_previous_matches(events);

    // in principle this warning should happen only if the req is a resubmit
    if (previous_matches.empty()) {
      Warning("cannot retrieve previous matches for " << jobid.toString());
    }

    std::vector<std::string> previous_matches_simple;
    for (std::vector<std::pair<std::string, int> >::const_iterator it = previous_matches.begin();
         it != previous_matches.end(); ++it) {
      previous_matches_simple.push_back(it->first);
    }

    boost::tuple<int, int> retries = get_retry_counts(events);
    return boost::make_tuple<
      int,
      int,
      std::vector<std::pair<std::string, int> >,
      std::vector<std::string>
    >(
      retries.get<0>(),
      retries.get<1>(),
      previous_matches,
      previous_matches_simple
    );
  }

  bool is_request_expired(
    boost::shared_ptr<classad::ClassAd> jdl,
    std::time_t first_processed)
  {
    bool exists = false;
    int expiry_time = 0;
    if (jdl) {
      expiry_time = jdl::get_expiry_time(*jdl, exists);
    }
    if (!exists) {
      return std::time(0) - first_processed > get_expiry_period();
    } else {
      return expiry_time < std::time(0);
    }
  }

  bool shallow_resubmission_is_enabled(boost::shared_ptr<classad::ClassAd> jdl)
  {
    return server::shallow_resubmission_is_enabled(*jdl);
  }
}

SubmitProcessor::SubmitProcessor(boost::shared_ptr<SubmitState> state)
  : m_state(state)
{ }

void SubmitProcessor::postpone(
  std::string const& reason,
  int match_retry_period
)
{
  if (!reason.empty()) {
    Info("postponing " << m_state->m_id.toString() << " (" << reason << ')');
    log_pending(m_state->m_lb_context, reason);
  }
  m_state->m_events.schedule_at(
    *this,
    std::time(0) + match_retry_period,
    submit_priority
  );
}

void SubmitProcessor::operator()()
{
  Debug("considering (re)submit of " << m_state->m_id.toString());

  bool proxy_expired = true;
  try {
    proxy_expired = is_proxy_expired(m_state->m_id);
  } catch(MissingProxy const& e) {
    std::string const error_msg(
      "X509 proxy not found or I/O error (" + std::string(e.what()) + ')'
    );
    unrecoverable(m_state, error_msg);
    return;
  } catch(InvalidProxy const& e) {
    std::string const error_msg(
      "invalid X509 proxy (" + std::string(e.what()) + ')'
    );
    unrecoverable(m_state, error_msg);
    return;
  }

  if (proxy_expired) {
    unrecoverable(m_state, "proxy expired");
    return;
  }

  if (is_request_expired(m_state->m_ad, m_state->m_created_at)) {
    unrecoverable(m_state, "request expired");
    return;
  }

  job_type_t job_type_ = job_type(*m_state->m_ad);
  try {
    if (job_type_ == dag) {

      static std::string const na = "notapplicable";
      if (SubmitState::START == m_state->m_last_checkpointing) {
        log_match(m_state->m_lb_context, "dagman");
        // the wm now manages the DAG in its entirety,
        // so the LB state machine must be someway tricked
        log_enqueued_start(m_state->m_lb_context, na);
        log_enqueued_ok(m_state->m_lb_context, na, na);
        //log_dequeued(m_state->m_lb_context, na);
        log_transfer_start(m_state->m_lb_context, "L&B");
        log_transfer_ok(
          m_state->m_lb_context,
          "L&B",
          na,
          "DAG managed by WM"
        );
        log_running(m_state->m_lb_context, "localhost");
      }

      jdl::DAGAd dagad(*m_state->m_ad);
      jdl::DAGAd::Graph_t dag = dagad.graph();
      jdl::DAGAd::VertexIterator v_i, v_end;
      // - the recovery mechanism is by design included in the processing of the request,
      // unlike what happens with other job types
      // - starting from LB 2.1, querying the LBProxy will also return terminated jobs
      // allowing to check for 'green nodes'
      // in this first phase, similar to what happens with the WM recovery, an assessment
      // is done about each node's status
      for (boost::tie(v_i, v_end) = boost::vertices(dag); v_i != v_end; ++v_i) {

        // find classad by graph vertex name
        std::string node_name = get(boost::vertex_name, dag)[*v_i];
        jdl::DAGAd::node_iterator classad_node_it = dagad.find(node_name);
        jdl::DAGNodeInfo const& classad_info = classad_node_it->second;
        classad::ClassAd node_jdl(
          *classad_info.description_ad()
        );
        jobid::JobId const node_id(jdl::get_edg_jobid(node_jdl));
        JobStatusPtr status(
          job_status(node_id, m_state->m_lb_context)
        );

        Debug(node_name << " id " << node_id.toString() << " has status " << status->state);
        // let's see if the job has reached a terminal state (green or red)
        if (is_aborted(status) || is_cancelled(status)) {
          get(boost::vertex_color, dag)[*v_i] = boost::red_color;
        } else if (is_done(status)) {

          try {
            LB_Events events(
              get_recovery_events(m_state->m_lb_context, node_id)
            );
            if (is_last_done(events, EDG_WLL_DONE_OK)) {
              get(boost::vertex_color, dag)[*v_i] = boost::green_color;
            } else {
              // done/failed is not a terminal state
              get(boost::vertex_color, dag)[*v_i] = boost::gray_color;
            }
          } catch (LB_QueryFailed const& e) {
            std::string const error_msg(" unable to retrieve recovery events");
            Error(m_state->m_id.toString() << error_msg);
            return;
          } catch (LB_QueryTemporarilyFailed const& e) {
            std::string const error_msg(
              " unable to retrieve recovery events (transient error)"
            );
            Error(m_state->m_id.toString() << error_msg);
            m_state->m_events.schedule_at(
              *this,
              std::time(0) + match_retry_period(),
              submit_priority
              );
          }
        } else if (is_submitted(status)) {
          get(boost::vertex_color, dag)[*v_i] = boost::white_color; // not yet started
        } else { // not in terminal state
          get(boost::vertex_color, dag)[*v_i] = boost::gray_color;
        }
      } // end 'recovery'

      // second phase, plan submission of pending nodes or terminate
      // with some status (ok/aborted)
      bool completed_dag = true;
      for (boost::tie(v_i, v_end) = boost::vertices(dag); v_i != v_end; ++v_i) {
        if (get(boost::vertex_color, dag)[*v_i] == boost::gray_color) {

          completed_dag = false;
        } else if (get(boost::vertex_color, dag)[*v_i] == boost::white_color) {
          // still unprocessed request
          completed_dag = false;
          jdl::DAGAd::InEdgeIterator in_i, in_end;
          boost::tie(in_i, in_end) = boost::in_edges(*v_i, dag);
          if (in_i == in_end) {
            // job can be run straightaway
            std::string node_name = get(boost::vertex_name, dag)[*v_i];
            jdl::DAGAd::node_iterator classad_node_it = dagad.find(node_name);
            jdl::DAGNodeInfo const& classad_info = classad_node_it->second;
            classad::ClassAd node_jdl(
              *classad_info.description_ad()
            );
            jobid::JobId const node_id(jdl::get_edg_jobid(node_jdl));
            try {
              Debug("queueing new submit for " << node_name << " as it has no parents");
              queue_new_submit_request(node_name, node_id, node_jdl, m_state);
            } catch (CannotCreateLBContext const& e) {
              Error(e.what() << ": cannot create request for " << node_id.toString());
              get(boost::vertex_color, dag)[*v_i] = boost::red_color;
              continue;
            }
            get(boost::vertex_color, dag)[*v_i] = boost::gray_color;
          } else { // node has in-edges
            bool preceding_jobs_are_green = true;
            for (
              boost::tie(in_i, in_end) = boost::in_edges(*v_i, dag);
              in_i != in_end;
              ++in_i
            ) {
              jdl::DAGAd::Vertex src = boost::source(*in_i, dag);
              if (get(boost::vertex_color, dag)[src] == boost::red_color) {
                get(boost::vertex_color, dag)[*v_i] = boost::red_color;

                std::string node_name = get(boost::vertex_name, dag)[*v_i];
                jdl::DAGAd::node_iterator classad_node_it = dagad.find(node_name);
                jdl::DAGNodeInfo const& classad_info = classad_node_it->second;
                classad::ClassAd node_jdl(
                  *classad_info.description_ad()
                );
                jdl::set_lb_sequence_code(node_jdl, "");
                RemoveNodeFromCollection cleanup(node_name, m_state);
                boost::shared_ptr<classad::ClassAd> fake_command_ad(
                  new classad::ClassAd(
                    utilities::submit_command_create(node_jdl)
                  )
                );
                jobid::JobId const node_id(jdl::get_edg_jobid(node_jdl));
                ContextPtr lb_context
                  = create_context(node_id, m_state->m_x509_proxy, "");

                boost::shared_ptr<SubmitState> new_state(
                  new SubmitState(
                    fake_command_ad,
                    "jobsubmit",
                    node_id,
                    "",
                    m_state->m_x509_proxy,
                    lb_context,
                    cleanup,
                    m_state->m_wm,
                    m_state->m_events,
                    m_state->m_jw_template
                  )
                );

                unrecoverable(new_state, "parents have aborted");
                preceding_jobs_are_green = false;
                break;
              } else if (get(boost::vertex_color, dag)[src] == boost::white_color
                || get(boost::vertex_color, dag)[src] == boost::gray_color
              ) {
                preceding_jobs_are_green = false;
              }
            }
            if (preceding_jobs_are_green) {
              std::string node_name = get(boost::vertex_name, dag)[*v_i];
              jdl::DAGAd::node_iterator classad_node_it = dagad.find(node_name);
              jdl::DAGNodeInfo const& classad_info = classad_node_it->second;
              classad::ClassAd node_jdl(
                *classad_info.description_ad()
              );
              jobid::JobId const node_id(jdl::get_edg_jobid(node_jdl));
              try {
                Debug("queueing new submit for " << node_name << " as it has terminal parents");
                queue_new_submit_request(node_name, node_id, node_jdl, m_state);
              } catch (CannotCreateLBContext const& e) {
                Error(e.what() << ": cannot create request for " << node_id.toString());
                get(boost::vertex_color, dag)[*v_i] = boost::red_color;
              }
            }
          }
        }
      }

      if (!completed_dag) {
        m_state->m_events.schedule_at(
          *this,
          std::time(0) + match_retry_period(),
          submit_priority
          );
        m_state->m_last_checkpointing = SubmitState::PENDING;
      } else {
        bool allgreen = true;
        for (boost::tie(v_i, v_end) = boost::vertices(dag); v_i != v_end; ++v_i) {
          if (get(boost::vertex_color, dag)[*v_i] != boost::green_color) {
            allgreen = false;
            break;
          }
        }
        if (allgreen) {
          log_done_ok(m_state->m_lb_context, "DAG successfully completed", 0);
        } else {
          log_abort(m_state->m_lb_context, "DAG completed with failed jobs");
        }
      }
    } else if (job_type_ == collection) {

      if (SubmitState::START == m_state->m_last_checkpointing) {
        Debug(m_state->m_id.toString() << ", started delivering collection");
        m_state->m_wm.submit_collection(
          *m_state->m_ad,
          m_state->m_lb_context,
          m_state->m_collection_pending,
          m_state->m_jw_template
        );
      }
      if (m_state->m_collection_pending.empty()) {

        Info(m_state->m_id.toString() << ", collection delivered");
      } else {

        if (SubmitState::START == m_state->m_last_checkpointing) {
          Info(m_state->m_id.toString() << ", considering pending collection");
          // manage all pending jobs as normal jobs
          jdl::DAGAd dagad(*m_state->m_ad);
          jdl::DAGAd::node_iterator first_node, last_node;
          boost::tie(first_node, last_node) = dagad.nodes();
          std::set<std::string>::iterator first =
            m_state->m_collection_pending.begin();
          std::set<std::string>::iterator const last =
            m_state->m_collection_pending.end();
          for ( ; first != last; ++first) {

            std::string node_name = *first;
            jdl::DAGAdNodeIterator it = dagad.find(node_name);
            assert(it != last_node);
            jdl::DAGNodeInfo const& node_info = it->second;
            classad::ClassAd node_jdl(
              *node_info.description_ad()
            );
            jobid::JobId const node_id(jdl::get_edg_jobid(node_jdl));
            try {
              queue_new_submit_request(node_name, node_id, node_jdl, m_state);
            } catch (CannotCreateLBContext const& e) {
              Error(e.what() << ": cannot create request for " << node_id.toString());
            }
          } // for each child job

          m_state->m_last_checkpointing = SubmitState::PENDING;
          m_state->m_events.schedule_at(
            *this,
            std::time(0) + match_retry_period(),
            submit_priority
          );
        }
      }
    } else if (job_type_ == single) {

      if (SubmitState::LIMBO == m_state->m_last_checkpointing) {
        try {
          JobStatusPtr status(job_status(m_state->m_id));
          ContextPtr context(m_state->m_lb_context);
          LB_Events events(get_recovery_events(context, m_state->m_id));

          if (in_limbo(status, events)) {
            postpone(" waiting in limbo", match_retry_period());
          } else {
            m_state->m_last_checkpointing = SubmitState::START;
            Debug(m_state->m_id.toString() << " exited from limbo!");
          }
        } catch(LB_QueryFailed&) {
          std::string const error_msg(" unable to retrieve recovery events");
          Error(m_state->m_id.toString() << error_msg);
          return;
        }
      }

      int deep_count, shallow_count;
      std::vector<std::pair<std::string, int> > previous_matches;
      std::vector<std::string> previous_matches_simple;
      if (m_state->m_command == "jobresubmit") {

        boost::tie(
          deep_count,
          shallow_count,
          previous_matches,
          previous_matches_simple
        )
          = retrieve_lb_info(m_state->m_id, m_state->m_lb_context);

        std::string const job_ad_str(
          get_original_jdl(m_state->m_lb_context, m_state->m_id)
        );
        if (job_ad_str.empty()) {
          throw CannotRetrieveJDL();
        }

        std::auto_ptr<classad::ClassAd> job_ad(
          glite::wmsutils::classads::parse_classad(job_ad_str)
        );

        m_state->m_ad = job_ad;
        job_ad.release();

        jdl::set_edg_previous_matches(*m_state->m_ad, previous_matches_simple);
        jdl::set_edg_previous_matches_ex(*m_state->m_ad, previous_matches);
      }

      fs::path const cancel_token(get_cancel_token(m_state->m_id));
      if (fs::exists(cancel_token)) {
        // this check also addresses a resubmit
        // coming in after the job has been cancelled
        log_cancelled(m_state->m_lb_context);
        log_done_cancelled(m_state->m_lb_context);
        purger::Purger thePurger(have_lbproxy());
        thePurger.force_dag_node_removal();
        thePurger(m_state->m_id);

        return;
      }

      fs::directory_iterator end;
      fs::path base_path(
        (*configuration::Configuration::instance()).ns()->sandbox_staging_path()
      );
      base_path /= fs::path(utilities::get_reduced_part(m_state->m_id), fs::native);
      base_path /= fs::path(utilities::to_filename(m_state->m_id), fs::native);
      std::string last_token_file;
      boost::regex const filter(
        (*configuration::Configuration::instance()).wm()->token_file()
        + "_.*"
      );
      bool token_found = false;
      try {
        for (fs::directory_iterator it(base_path); it != end; ++it) {
          if (boost::regex_match(it->leaf(), filter)) {
            if (!token_found) {
              last_token_file = it->string();
              token_found = true;
            } else {
              unrecoverable(
                m_state,
                it->string() +
                ": at least two shallow resubmit tokens are present at the same time!"
              );
            }
          }
        }
      } catch (fs::filesystem_error const& e) {
        Error(e.what());
        return;
      }

      int replans_count = 0;
      if (token_found && !last_token_file.empty()) {
        replans_count = int(boost::lexical_cast<float>(
          last_token_file.substr(
            last_token_file.rfind('_') + 1
          ).c_str()
        ));
        bool nothrow = false;
        jdl::set_replans_count(*m_state->m_ad, replans_count, nothrow);
        Debug("found token number " << replans_count << " for job " << m_state->m_id.toString());
      }

      fs::path const token_file(get_reallyrunning_token(m_state->m_id, replans_count));
      fs::path const token_tmp_file(
        token_file.native_file_string() + '.', // so as to work always with a lexical_cast to float
        fs::native
      );
      std::string token_file_str(token_file.native_file_string());
      std::string token_tmp_file_str(token_tmp_file.native_file_string());
      if (m_state->m_command == "jobresubmit") {
        if (shallow_resubmission_is_enabled(m_state->m_ad)) {

          int res = ::rename(token_file_str.c_str(), token_tmp_file_str.c_str());
          if (res && errno != ENOENT) {
            Error("cannot rename token "
              + token_file.native_file_string()
              + " (error " + boost::lexical_cast<std::string>(errno) + ')'
            );
            throw CannotCreateToken();
          }

          if (fs::exists(token_tmp_file)) {
            server::check_shallow_count(*m_state->m_ad, shallow_count);
            log_resubmission_shallow(
              m_state->m_lb_context,
              token_file_str
            );
          } else {
            server::check_deep_count(*m_state->m_ad, deep_count);
            log_resubmission_deep(
              m_state->m_lb_context,
              token_file_str
            );
            create_token(token_tmp_file);
          }
        } else { // shallow not enabled

          server::check_deep_count(*m_state->m_ad, deep_count);
          log_resubmission_deep(m_state->m_lb_context);
        }
      }

      bool is_replan = false;
      if (m_state->m_command == "jobsubmit") {
        bool has_replans_count = false;
        int jdl_replans_count = jdl::get_replans_count(*m_state->m_ad, has_replans_count);
        if (!has_replans_count) {
          jdl_replans_count = 0;
        }
        if (jdl_replans_count > 0 && replans_count == jdl_replans_count) {
          Debug("this is a reschedule for job " << m_state->m_id.toString());
          is_replan = true;
        } else if (jdl_replans_count > 0 && replans_count > jdl_replans_count) {
          // another reschedule is on the way (on very rare race conditions)
          return; // present request can be safely dropped
        } else {
          if (shallow_resubmission_is_enabled(m_state->m_ad)) {
            if (!fs::exists(token_tmp_file)) { // as a 'reallyrunning' token,
                                           // it must be created anyhow

              create_token(token_tmp_file);
            }
          }
        }
      }

      if (
        !is_replan // the token has already been managed by the replanner
        && shallow_resubmission_is_enabled(m_state->m_ad)
      ) {
        if (::rename(token_tmp_file_str.c_str(), token_file_str.c_str()))
        {
          // ENOENT would be an error as well, unlike the previous case
          Error("cannot rename temporary token "
            + token_tmp_file_str
            + " (error " + boost::lexical_cast<std::string>(errno) + ')'
          );
          throw CannotCreateToken();
        }
      }

      std::string const jobid(m_state->m_id.toString());
      configuration::Configuration const& config(
        *configuration::Configuration::instance()
      );
      std::string maradona(config.ns()->sandbox_staging_path());
      maradona += "/";
      maradona += utilities::get_reduced_part(jobid);
      maradona += "/";
      maradona += utilities::to_filename(jobid);
      maradona += "/Maradona.output";
      ::unlink(maradona.c_str());

      m_state->m_wm.submit(
        *m_state->m_ad,
        m_state->m_lb_context,
        m_state->m_jw_template,
        is_replan
      );

      Info(m_state->m_id.toString() << " delivered");
    } else {
      Error("Unknown job type for job: " << m_state->m_id.toString());
    }
  } catch (std::invalid_argument const& e) {
    unrecoverable(m_state, e.what());
  } catch (jdl::ManipulationException const& e) {
    unrecoverable(m_state, e.what());
  } catch (exception::Exception const& e) {
    unrecoverable(m_state, e.what());
  } catch (LB_QueryFailed const& e) {
    unrecoverable(m_state, " failed (LB query failed)");
  } catch (LB_QueryTemporarilyFailed const& e) {
    postpone("LB query temporarily failed", match_retry_period());
  } catch (HitMaxRetryCount& e) {
    unrecoverable(
      m_state,
      "hit max retry count ("
        + boost::lexical_cast<std::string>(e.count()) + ')'
    );
  } catch (HitJobRetryCount& e) {
    unrecoverable(
      m_state,
      "hit job retry count ("
        + boost::lexical_cast<std::string>(e.count()) + ')'
    );
  } catch (HitMaxShallowCount& e) {
    unrecoverable(
      m_state,
      "hit shallow max retry count ("
        + boost::lexical_cast<std::string>(e.count()) + ')'
    );
  } catch (HitJobShallowCount& e) {
    unrecoverable(
      m_state,
      "hit job shallow retry count ("
        + boost::lexical_cast<std::string>(e.count()) + ')'
    );
  } catch (CannotRetrieveJDL const& e) {
    postpone("cannot retrieve jdl", match_retry_period());
  } catch (helper::HelperError const& e) {
    postpone(e.what(), match_retry_period());
  } catch (CannotCreateToken&) {
    postpone("cannot create token for shallow resubmission", match_retry_period());
  } catch (CannotDeliverFatal& e) {
    unrecoverable(m_state, " cannot deliver request");
  } catch (CannotCreateLBContext const& e) {
    unrecoverable(m_state, std::string(e.what()) + ": ignoring request");
  } catch (std::exception const&e) {
    unrecoverable(m_state, e.what());
  } catch (...) {
    unrecoverable(m_state, "unknown exception caught handling submit request");
  }
}

}}}}

max_stars_repo_name: italiangrid/wms

max_issues_repo_name: italiangrid/wms

max_forks_repo_name: italiangrid/wms


text:
#include <boost/log/trivial.hpp>

#include <string>

using std::string;

#include "rinexNavWrite.hpp"
#include "streamTrace.hpp"
#include "navigation.hpp"
#include "biasSINEX.hpp"
#include "constants.hpp"
#include "station.hpp"
#include "common.hpp"
#include "gTime.hpp"
#include "rinex.hpp"

/* constants/macros ----------------------------------------------------------*/

#define MAXRNXLEN   (16*MAXOBSTYPE+4)   /* max rinex record length */
#define MAXPOSHEAD  1024                /* max head line position */
#define MINFREQ_GLO -7                  /* min frequency number glonass */
#define MAXFREQ_GLO 13                  /* max frequency number glonass */

const double ura_eph[]=
{
	/* ura values (ref [3] 20.3.3.3.1.1) */
	2.4, 3.4, 4.85, 6.85, 9.65, 13.65, 24, 48, 96, 192, 384, 768, 1536, 3072, 6144, 0
};

/* set string without tail space ---------------------------------------------*/
void setstr(char *dst, const char *src, int n)
{
	char *p=dst;
	const char *q=src;
	while (*q&&q<src+n) *p++=*q++;
	*p--='\0';
	while (p>=dst&&*p==' ') *p--='\0';
}

/* adjust time considering week handover -------------------------------------*/
GTime adjweek(GTime t, GTime t0)
{
	double tt = t - t0;
	if (tt < -302400)		return t + 604800.0;
	if (tt > +302400)		return t - 604800.0;
							return t;
}

/* adjust time considering week handover -------------------------------------*/
GTime adjday(GTime t, GTime t0)
{
	double tt = t - t0;
	if (tt < -43200.0)		return t + 86400.0;
	if (tt > +43200.0)		return t - 86400.0;
							return t;
}

/* ura value (m) to ura index ------------------------------------------------*/
int uraindex(double value)
{
	int i;
	for (i = 0; i < 15; i++)
		if (ura_eph[i] >= value)
			break;
	return i;
}

/*------------------------------------------------------------------------------
* input rinex functions
*-----------------------------------------------------------------------------*/

/* convert rinex obs type ver.2 -> ver.3 -------------------------------------*/
void convcode(
	double		ver,
	int			sys,
	const char*	str,
	char*		type)
{
	strcpy(type,"   ");

	if      (!strcmp(str,"P1"))
	{
		/* ver.2.11 GPS L1PY,GLO L2P */
		if      (sys==+E_Sys::GPS) sprintf(type,"%c1W",'C');
		else if (sys==+E_Sys::GLO) sprintf(type,"%c1P",'C');
	}
	else if (!strcmp(str,"P2"))
	{
		/* ver.2.11 GPS L2PY,GLO L2P */
		if      (sys==+E_Sys::GPS) sprintf(type,"%c2W",'C');
		else if (sys==+E_Sys::GLO) sprintf(type,"%c2P",'C');
	}
	else if (!strcmp(str,"C1"))
	{
		/* ver.2.11 GPS L1C,GLO L1C/A */
		if      (ver>=2.12) ; /* reject C1 for 2.12 */
		else if (sys==+E_Sys::GPS) sprintf(type,"%c1C",'C');
		else if (sys==+E_Sys::GLO) sprintf(type,"%c1C",'C');
		else if (sys==+E_Sys::GAL) sprintf(type,"%c1X",'C'); /* ver.2.12 */
		else if (sys==+E_Sys::QZS) sprintf(type,"%c1C",'C');
		else if (sys==+E_Sys::SBS) sprintf(type,"%c1C",'C');
	}
	else if (!strcmp(str,"C2"))
	{
		if (sys==+E_Sys::GPS)
		{
			if (ver>=2.12) sprintf(type,"%c2W",'C'); /* L2P(Y) */
			else           sprintf(type,"%c2X",'C'); /* L2C */
		}
		else if (sys==+E_Sys::GLO) sprintf(type,"%c2C",'C');
		else if (sys==+E_Sys::QZS) sprintf(type,"%c2X",'C');
		else if (sys==+E_Sys::BDS) sprintf(type,"%c1X",'C'); /* ver.2.12 B1 */
	}
	else if (ver>=2.12&&str[1]=='A')
	{
		/* ver.2.12 L1C/A */
		if      (sys==+E_Sys::GPS) sprintf(type,"%c1C",str[0]);
		else if (sys==+E_Sys::GLO) sprintf(type,"%c1C",str[0]);
		else if (sys==+E_Sys::QZS) sprintf(type,"%c1C",str[0]);
		else if (sys==+E_Sys::SBS) sprintf(type,"%c1C",str[0]);
	}
	else if (ver>=2.12&&str[1]=='B')
	{
		/* ver.2.12 GPS L1C */
		if      (sys==+E_Sys::GPS) sprintf(type,"%c1X",str[0]);
		else if (sys==+E_Sys::QZS) sprintf(type,"%c1X",str[0]);
	}
	else if (ver>=2.12&&str[1]=='C')
	{
		/* ver.2.12 GPS L2C */
		if      (sys==+E_Sys::GPS) sprintf(type,"%c2X",str[0]);
		else if (sys==+E_Sys::QZS) sprintf(type,"%c2X",str[0]);
	}
	else if (ver>=2.12&&str[1]=='D')
	{
		/* ver.2.12 GLO L2C/A */
		if      (sys==+E_Sys::GLO) sprintf(type,"%c2C",str[0]);
	}
	else if (ver>=2.12&&str[1]=='1')
	{
		/* ver.2.12 GPS L1PY,GLO L1P */
		if      (sys==+E_Sys::GPS) sprintf(type,"%c1W",str[0]);
		else if (sys==+E_Sys::GLO) sprintf(type,"%c1P",str[0]);
		else if (sys==+E_Sys::GAL) sprintf(type,"%c1X",str[0]); /* tentative */
		else if (sys==+E_Sys::BDS) sprintf(type,"%c1X",str[0]); /* extension */
	}
	else if (ver<2.12&&str[1]=='1')
	{
		if      (sys==+E_Sys::GPS) sprintf(type,"%c1C",str[0]);
		else if (sys==+E_Sys::GLO) sprintf(type,"%c1C",str[0]);
		else if (sys==+E_Sys::GAL) sprintf(type,"%c1X",str[0]); /* tentative */
		else if (sys==+E_Sys::QZS) sprintf(type,"%c1C",str[0]);
		else if (sys==+E_Sys::SBS) sprintf(type,"%c1C",str[0]);
	}
	else if (str[1]=='2')
	{
		if      (sys==+E_Sys::GPS) sprintf(type,"%c2W",str[0]);
		else if (sys==+E_Sys::GLO) sprintf(type,"%c2P",str[0]);
		else if (sys==+E_Sys::QZS) sprintf(type,"%c2X",str[0]);
		else if (sys==+E_Sys::BDS) sprintf(type,"%c1X",str[0]); /* ver.2.12 B1 */
	}
	else if (str[1]=='5')
	{
		if      (sys==+E_Sys::GPS) sprintf(type,"%c5X",str[0]);
		else if (sys==+E_Sys::GAL) sprintf(type,"%c5X",str[0]);
		else if (sys==+E_Sys::QZS) sprintf(type,"%c5X",str[0]);
		else if (sys==+E_Sys::SBS) sprintf(type,"%c5X",str[0]);
	}
	else if (str[1]=='6')
	{
		if      (sys==+E_Sys::GAL) sprintf(type,"%c6X",str[0]);
		else if (sys==+E_Sys::QZS) sprintf(type,"%c6X",str[0]);
		else if (sys==+E_Sys::BDS) sprintf(type,"%c6X",str[0]); /* ver.2.12 B3 */
	}
	else if (str[1]=='7')
	{
		if      (sys==+E_Sys::GAL) sprintf(type,"%c7X",str[0]);
		else if (sys==+E_Sys::BDS) sprintf(type,"%c7X",str[0]); /* ver.2.12 B2 */
	}
	else if (str[1]=='8')
	{
		if      (sys==+E_Sys::GAL) sprintf(type,"%c8X",str[0]);
	}

	BOOST_LOG_TRIVIAL(debug)
	<< "convcode: ver=" << ver
	<< " sys="		<< sys
	<< " type= "	<< str
	<< " -> "		<< type;
}

/* decode obs header ---------------------------------------------------------*/
void decode_obsh(
	std::istream& 					inputStream,
	string&							line,
	double							ver,
	int&							tsys,
	map<E_Sys, vector<CodeType>>&	sysCodeTypes,
	nav_t&							nav,
	RinexStation*					sta)
{
	double del[3];
	int i,j,n,prn,fcn;
	const char *p;
	char* buff	= &line[0];
	char* label	= buff+60;

//     BOOST_LOG_TRIVIAL(debug)
// 	<< "decode_obsh: ver=" << ver;

	if      (strstr(label,"MARKER NAME"         ))
	{
		if (sta)
		{
			sta->id				.assign(buff,		60);
		}
	}
	else if (strstr(label,"MARKER NUMBER"       ))
	{
		if (sta)
		{
			sta->marker			.assign(buff,		20);
		}
	}
//     else if (strstr(label,"MARKER TYPE"         )) ; /* ver.3 */
//     else if (strstr(label,"OBSERVER / AGENCY"   )) ;
	else if (strstr(label,"REC # / TYPE / VERS" ))
	{
		if (sta)
		{
			sta->recSerial		.assign(buff,		20);
			sta->recType		.assign(buff+20,	20);
			sta->recFWVersion	.assign(buff+40,	20);
		}
	}
	else if (strstr(label,"ANT # / TYPE"        ))
	{
		if (sta)
		{
			sta->antSerial		.assign(buff,		20);
			sta->antDesc		.assign(buff+20,	20);
		}
	}
	else if (strstr(label,"APPROX POSITION XYZ" ))
	{
		if (sta)
		{
			for (i=0,j=0;i<3;i++,j+=14)
				sta->pos[i] = str2num(buff,j,14);
		}
	}
	else if (strstr(label,"ANTENNA: DELTA H/E/N"))
	{
		if (sta)
		{
			for (i=0,j=0;i<3;i++,j+=14)
				del[i] = str2num(buff,j,14);

			sta->del[2] = del[0]; /* h */
			sta->del[0] = del[1]; /* e */
			sta->del[1] = del[2]; /* n */
		}
	}
//     else if (strstr(label,"ANTENNA: DELTA X/Y/Z")) ; /* opt ver.3 */
//     else if (strstr(label,"ANTENNA: PHASECENTER")) ; /* opt ver.3 */
//     else if (strstr(label,"ANTENNA: B.SIGHT XYZ")) ; /* opt ver.3 */
//     else if (strstr(label,"ANTENNA: ZERODIR AZI")) ; /* opt ver.3 */
//     else if (strstr(label,"ANTENNA: ZERODIR XYZ")) ; /* opt ver.3 */
//     else if (strstr(label,"CENTER OF MASS: XYZ" )) ; /* opt ver.3 */
	else if (strstr(label,"SYS / # / OBS TYPES" ))
	{
		/* ver.3 */
		//get system from code letter
		char code[]	= "x00";
		code[0]		= buff[0];

		SatSys Sat(code);

		if (Sat.sys == +E_Sys::NONE)
		{
			BOOST_LOG_TRIVIAL(debug)
			<< "invalid system code: sys=" << code[0];

			return;
		}

		n = (int) str2num(buff, 3, 3);

		for (int j = 0, k = 7; j < n; j++, k += 4)
		{
			if (k > 58)
			{
				//more on the next line

				if (!std::getline(inputStream, line))
					break;

				buff = &line[0];
				k = 7;
			}

			CodeType codeType;
			codeType.type = buff[k];

			char code[] = "Lxx";
			code[1] = buff[k+1];
			code[2] = buff[k+2];
			if	( (Sat.sys == +E_Sys::BDS)
				&&(code[1] == '2'))
			{
				/* change beidou B1 code: 3.02 draft -> 3.02 */
				code[1] = '1';
			}
			try
			{
				codeType.code = E_ObsCode::_from_string(code);
			}
			catch (...)
			{
				BOOST_LOG_TRIVIAL(debug)
				<< "invalid obs code: " << code;
			}

			sysCodeTypes[Sat.sys].push_back(codeType);
		}

		/* if unknown code in ver.3, set default code */
// 		for (auto& codeType : sysCodeTypes[Sat.sys])
// 		{
//             if (tobs[i][j][2])
// 				continue;
//
//             if (!(p = strchr(frqcodes, tobs[i][j][1])))
// 				continue;
//
				/* default codes for unknown code */
//     			const char *defcodes[] =
//    			{
//         			"CWX   ",   /* GPS: L125___ */
//         			"CC    ",   /* GLO: L12____ */
//         			"X XXXX",   /* GAL: L1_5678 */
//         			"CXXX  ",   /* QZS: L1256__ */
//         			"C X   ",   /* SBS: L1_5___ */
//         			"X  XX "    /* BDS: L1__67_ */
//     			};
//             	tobs[i][j][2] = defcodes[i][(int)(p - frqcodes)];
//
//             	BOOST_LOG_TRIVIAL(debug)
// 				<< "set default for unknown code: sys=" << buff[0]
// 				<< " code=" << tobs[i][j];
//         }
	}
//     else if (strstr(label,"WAVELENGTH FACT L1/2")) ; /* opt ver.2 */
//     else if (strstr(label,"# / TYPES OF OBSERV" ))
// 	{ /* ver.2 */
//         n=(int)str2num(buff,0,6);
//         for (i=nt=0,j=10;i<n;i++,j+=6)
// 		{
//             if (j>58)
// 			{
//                 if (!fgets(buff,MAXRNXLEN,fp))
// 					break;
//                 j=10;
//             }
//
//             if (nt>=MAXOBSTYPE-1)
// 				continue;
//
//             if (ver<=2.99)
// 			{
//                 setstr(str,buff+j,2);
//                 convcode(ver,E_Sys::GPS,str,tobs[0][nt]);
//                 convcode(ver,E_Sys::GLO,str,tobs[1][nt]);
//                 convcode(ver,E_Sys::GAL,str,tobs[2][nt]);
//                 convcode(ver,E_Sys::QZS,str,tobs[3][nt]);
//                 convcode(ver,E_Sys::SBS,str,tobs[4][nt]);
//                 convcode(ver,E_Sys::BDS,str,tobs[5][nt]);
//             }
//             nt++;
//         }
//         *tobs[0][nt]='\0';
//     }
//     else if (strstr(label,"SIGNAL STRENGTH UNIT")) ; /* opt ver.3 */
//     else if (strstr(label,"INTERVAL"            )) ; /* opt */
	else if (strstr(label,"TIME OF FIRST OBS"   ))
	{
		if      (!strncmp(buff+48,"GPS",3)) tsys=TSYS_GPS;
		else if (!strncmp(buff+48,"GLO",3)) tsys=TSYS_UTC;
		else if (!strncmp(buff+48,"GAL",3)) tsys=TSYS_GAL;
		else if (!strncmp(buff+48,"QZS",3)) tsys=TSYS_QZS; /* ver.3.02 */
		else if (!strncmp(buff+48,"BDT",3)) tsys=TSYS_CMP; /* ver.3.02 */
	}
//     else if (strstr(label,"TIME OF LAST OBS"    )) ; /* opt */
//     else if (strstr(label,"RCV CLOCK OFFS APPL" )) ; /* opt */
//     else if (strstr(label,"SYS / DCBS APPLIED"  )) ; /* opt ver.3 */
//     else if (strstr(label,"SYS / PCVS APPLIED"  )) ; /* opt ver.3 */
//     else if (strstr(label,"SYS / SCALE FACTOR"  )) ; /* opt ver.3 */
//     else if (strstr(label,"SYS / PHASE SHIFTS"  )) ; /* ver.3.01 */
	else if (strstr(label,"GLONASS SLOT / FRQ #"))
	{
		/* ver.3.02 */
		for (i=0,p=buff+4;i<8;i++,p+=8)
		{
			if (sscanf(p,"R%2d %2d",&prn,&fcn)<2)
				continue;

			if (1 <= prn
				&&prn <= MAXPRNGLO)
			{
				nav.glo_fcn[prn-1] = fcn+8;
			}

		}
	}
	else if (strstr(label,"GLONASS COD/PHS/BIS" ))
	{
		/* ver.3.02 */
		for (i=0,p=buff;i<4;i++,p+=13)
		{
			if      (strncmp(p+1,"C1C",3)) nav.glo_cpbias[0]=str2num(p,5,8);
			else if (strncmp(p+1,"C1P",3)) nav.glo_cpbias[1]=str2num(p,5,8);
			else if (strncmp(p+1,"C2C",3)) nav.glo_cpbias[2]=str2num(p,5,8);
			else if (strncmp(p+1,"C2P",3)) nav.glo_cpbias[3]=str2num(p,5,8);
		}
	}
	else if (strstr(label,"LEAP SECONDS"        ))
	{
		/* opt */
		nav.leaps=(int)str2num(buff,0,6);
	}
//     else if (strstr(label,"# OF SALTELLITES"    )) ; /* opt */
//     else if (strstr(label,"PRN / # OF OBS"      )) ; /* opt */
}

/* decode nav header ---------------------------------------------------------*/
void decode_navh(
	string&	line,
	nav_t&	nav)
{
	int i,j;
	char*	buff	= &line[0];
	char*	label	= buff + 60;

	BOOST_LOG_TRIVIAL(debug)
	<< "decode_navh:";

	if      (strstr(label,"ION ALPHA"           ))	{ /* opt ver.2 */  for (i=0,j=2;i<4;i++,j+=12) nav.ion_gps[i]	= str2num(buff,j,12);  }
	else if (strstr(label,"ION BETA"            ))	{ /* opt ver.2 */  for (i=0,j=2;i<4;i++,j+=12) nav.ion_gps[i+4]	= str2num(buff,j,12);  }
	else if (strstr(label,"DELTA-UTC: A0,A1,T,W"))
	{
		/* opt ver.2 */
		for (i=0,j=3;i<2;i++,j+=19)	nav.utc_gps[i]=str2num(buff,j,19);
		for (;i<4;i++,j+=9)			nav.utc_gps[i]=str2num(buff,j,9);
	}
	else if (strstr(label,"IONOSPHERIC CORR"    ))
	{
		/* opt ver.3 */
		if		(!strncmp(buff,"GPSA",4))		{ for (i=0,j=5; i<4; i++,j+=12) nav.ion_gps[i]		= str2num(buff,j,12); }
		else if (!strncmp(buff,"GPSB",4)) 		{ for (i=0,j=5; i<4; i++,j+=12) nav.ion_gps[i+4]	= str2num(buff,j,12); }
		else if (!strncmp(buff,"GAL" ,3))		{ for (i=0,j=5; i<4; i++,j+=12) nav.ion_gal[i]		= str2num(buff,j,12); }
		else if (!strncmp(buff,"QZSA",4)) 		{ for (i=0,j=5; i<4; i++,j+=12) nav.ion_qzs[i]		= str2num(buff,j,12); }
		else if (!strncmp(buff,"QZSB",4))		{ for (i=0,j=5; i<4; i++,j+=12) nav.ion_qzs[i+4]	= str2num(buff,j,12); }
		else if (!strncmp(buff,"BDSA",4)) 		{ for (i=0,j=5; i<4; i++,j+=12) nav.ion_cmp[i]		= str2num(buff,j,12); }
		else if (!strncmp(buff,"BDSB",4))		{ for (i=0,j=5; i<4; i++,j+=12) nav.ion_cmp[i+4]	= str2num(buff,j,12); }
	}
	else if (strstr(label,"TIME SYSTEM CORR"    ))
	{
		/* opt ver.3 */
		if (!strncmp(buff,"GPUT",4))
		{
			nav.utc_gps[0] = str2num(buff, 5,17);
			nav.utc_gps[1] = str2num(buff,22,16);
			nav.utc_gps[2] = str2num(buff,38, 7);
			nav.utc_gps[3] = str2num(buff,45, 5);
		}
		else if (!strncmp(buff,"GLUT",4))
		{
			nav.utc_glo[0] = str2num(buff, 5,17);
			nav.utc_glo[1] = str2num(buff,22,16);
		}
		else if (!strncmp(buff,"GAUT",4))
		{
			/* v.3.02 */
			nav.utc_gal[0] = str2num(buff, 5,17);
			nav.utc_gal[1] = str2num(buff,22,16);
			nav.utc_gal[2] = str2num(buff,38, 7);
			nav.utc_gal[3] = str2num(buff,45, 5);
		}
		else if (!strncmp(buff,"QZUT",4))
		{
			/* v.3.02 */
			nav.utc_qzs[0] = str2num(buff, 5,17);
			nav.utc_qzs[1] = str2num(buff,22,16);
			nav.utc_qzs[2] = str2num(buff,38, 7);
			nav.utc_qzs[3] = str2num(buff,45, 5);
		}
		else if (!strncmp(buff,"BDUT",4))
		{
			/* v.3.02 */
			nav.utc_cmp[0] = str2num(buff, 5,17);
			nav.utc_cmp[1] = str2num(buff,22,16);
			nav.utc_cmp[2] = str2num(buff,38, 7);
			nav.utc_cmp[3] = str2num(buff,45, 5);
		}
		else if (!strncmp(buff,"SBUT",4))
		{
			/* v.3.02 */
			nav.utc_cmp[0] = str2num(buff, 5,17);
			nav.utc_cmp[1] = str2num(buff,22,16);
			nav.utc_cmp[2] = str2num(buff,38, 7);
			nav.utc_cmp[3] = str2num(buff,45, 5);
		}
	}
	else if (strstr(label,"LEAP SECONDS"        ))
	{
		/* opt */
		nav.leaps=(int)str2num(buff,0,6);
	}
}
/* decode gnav header --------------------------------------------------------*/
void decode_gnavh(
	string&	line,
	nav_t&	nav)
{
	char*	buff	= &line[0];
	char*	label	= buff + 60;

	BOOST_LOG_TRIVIAL(debug)
	<< "decode_gnavh:";

	if      (strstr(label,"CORR TO SYTEM TIME"  )) ; /* opt */
	else if (strstr(label,"LEAP SECONDS"        ))
	{
		/* opt */
		nav.leaps=(int)str2num(buff,0,6);
	}
}

/* decode geo nav header -----------------------------------------------------*/
void decode_hnavh(
	string&	line,
	nav_t&	nav)
{
	char*	buff	= &line[0];
	char*	label	= buff + 60;

	BOOST_LOG_TRIVIAL(debug)
	<< "decode_hnavh:";

	if      (strstr(label, "CORR TO SYTEM TIME"  )) ; /* opt */
	else if (strstr(label, "D-UTC A0,A1,T,W,S,U" )) ; /* opt */
	else if (strstr(label, "LEAP SECONDS"        ))
	{
		/* opt */
		nav.leaps= (int)str2num(buff,0,6);
	}
}

/* read rinex header ---------------------------------------------------------*/
int readrnxh(
	std::istream& 					inputStream,
	double&							ver,
	char&							type,
	E_Sys&							sys,
	int&							tsys,
	map<E_Sys, vector<CodeType>>&	sysCodeTypes,
	nav_t&							nav,
	RinexStation*					sta)
{
	string	line;
	int 	i		= 0;
	int 	block	= 0;

//     BOOST_LOG_TRIVIAL(debug)
// 	<< "readrnxh:";

	ver		= 2.10;
	type	= ' ';
	sys		= E_Sys::GPS;
	tsys	= TSYS_GPS;

	char sysChar = '\0';
	int typeOffset = 20;
	int sysCharOffset = 40;

	while (std::getline(inputStream, line))
	{
		char*	buff	= &line[0];
		char*	label	= buff + 60;

		if (line.length() <= 60)
			continue;
		else if (strstr(label,"RINEX VERSION / TYPE"))
		{
			ver		= str2num(buff,0,9);

			type	= buff[typeOffset];

			sysChar = buff[sysCharOffset];

			// possible error in generation by one manufacturer. This hack gets around it
			if(ver==3.04 && type == ' ')
			{
				typeOffset		+= 1;
				sysCharOffset	+= 2;
				type	= buff[typeOffset];

				sysChar = buff[sysCharOffset];

			}

			/* satellite system */
			switch (sysChar)
			{
				case ' ':
				case 'G': sys = E_Sys::GPS;  tsys = TSYS_GPS; break;
				case 'R': sys = E_Sys::GLO;  tsys = TSYS_UTC; break;
				case 'E': sys = E_Sys::GAL;  tsys = TSYS_GAL; break; /* v.2.12 */
				case 'S': sys = E_Sys::SBS;  tsys = TSYS_GPS; break;
				case 'J': sys = E_Sys::QZS;  tsys = TSYS_QZS; break; /* v.3.02 */
				case 'C': sys = E_Sys::BDS;  tsys = TSYS_CMP; break; /* v.2.12 */
				case 'M': sys = E_Sys::NONE; tsys = TSYS_GPS; break; /* mixed */
				default :
					BOOST_LOG_TRIVIAL(debug)
					<< "unsupported satellite system: " << sysChar;

					break;
			}
			continue;
		}
		else if (strstr(label,"PGM / RUN BY / DATE"))
			continue;
		else if (strstr(label,"COMMENT"))
		{ /* opt */

			/* read cnes wl satellite fractional bias */
			if	( strstr(buff,"WIDELANE SATELLITE FRACTIONAL BIASES")
				||strstr(buff,"WIDELANE SATELLITE FRACTIONNAL BIASES"))
			{
				block=1;
			}
			else if (block)
			{
				double bias;
				SatSys Sat;

				/* cnes/cls grg clock */
				if (!strncmp(buff,"WL",2)&&(Sat=SatSys(buff+3), Sat)&&
					sscanf(buff+40,"%lf",&bias)==1)
				{
					nav.satNavMap[Sat].wlbias = bias;
				}
				/* cnes ppp-wizard clock */
				else if ((Sat=SatSys(buff+1), Sat)&&sscanf(buff+6,"%lf",&bias)==1)
				{
					nav.satNavMap[Sat].wlbias = bias;
				}
			}
			continue;
		}
		/* file type */
		switch (type)
		{
			case 'O': decode_obsh(inputStream, line, ver, tsys, sysCodeTypes, nav, sta); break;
			case 'N': decode_navh			(  line,nav); break;
			case 'G': decode_gnavh			(  line,nav); break;
			case 'H': decode_hnavh			(  line,nav); break;
			case 'J': decode_navh 			(  line,nav); break; /* extension */
			case 'L': decode_navh 			(  line,nav); break; /* extension */
		}
		if (strstr(label,"END OF HEADER"))
			return 1;

		if (++i>=MAXPOSHEAD
			&&type==' ')
		{
			break; /* no rinex file */
		}
	}
	return 0;
}
/* decode obs epoch ----------------------------------------------------------*/
int decode_obsepoch(
	std::istream& 		inputStream,
	string&				line,
	double				ver,
	GTime*				time,
	int&				flag,
	vector<SatSys>&		sats)

{
	int n;
	char*	buff	= &line[0];

//     BOOST_LOG_TRIVIAL(debug)
// 	<< "decode_obsepoch: ver=" << ver;

	if	(ver <= 2.99)
	{
		/* ver.2 */
		n		= (int) str2num(buff, 29, 3);
		if (n <= 0)
			return 0;

		/* epoch flag: 3:new site,4:header info,5:external event */
		flag	= (int) str2num(buff, 28, 1);

		if	( flag >= 3
			&&flag <= 5)
		{
			return n;
		}

		if (str2time(buff, 0, 26, *time))
		{
			BOOST_LOG_TRIVIAL(debug)
			<< "rinex obs invalid epoch: epoch=" << buff;
			return 0;
		}

		int j = 32;
		for (int i = 0; i < n; i++, j+=3)
		{
			if (j >= 68)
			{
				//more on the next line
				if (!std::getline(inputStream, line))
					break;

				buff = &line[0];

				j = 32;
			}

			sats.push_back(SatSys(buff + j));
		}
	}
	else
	{
		/* ver.3 */
		n		= (int) str2num(buff, 32, 3);
		if (n <= 0)
		{
			return 0;
		}

		flag	= (int) str2num(buff, 31, 1);

		if	( flag >= 3
			&&flag <= 5)
			return n;

		if	( buff[0] != '>'
			||str2time(buff, 1, 28, *time))
		{
			BOOST_LOG_TRIVIAL(debug)
			<< "rinex obs invalid epoch: epoch=" << buff;
			return 0;
		}
	}

//     BOOST_LOG_TRIVIAL(debug)
// 	<< "decode_obsepoch: time=" << time->to_string(3)
// 	<< " flag=" << flag;

	return n;
}

/* decode obs data -----------------------------------------------------------*/
int decode_obsdata(
	std::istream& 					inputStream,
	string&		line,
	double		ver,
	map<E_Sys, vector<CodeType>>& sysCodeTypes,
	RawObs&		obs,
	SatSys*		v2SatSys_ptr)
{
	char		satid[8]	= "";
	int			stat		= 1;
	char*		buff		= &line[0];

//     BOOST_LOG_TRIVIAL(debug)
// 	<< "decode_obsdata: ver=" << ver;

	if (ver > 2.99)
	{
		/* ver.3 */
		strncpy(satid, buff, 3);
		obs.Sat = SatSys(satid);
	}
	else
	{
		obs.Sat = *v2SatSys_ptr;
	}

	if (!obs.Sat)
	{
		BOOST_LOG_TRIVIAL(debug)
		<< "decode_obsdata: unsupported sat sat=" << satid;

		stat = 0;
	}

	vector<CodeType>& codeTypes = sysCodeTypes[obs.Sat.sys];

	int j;
	if (ver <= 2.99)	j = 0;
	else				j = 3;

	if (!stat)
		return 0;

	for (auto& codeType : codeTypes)
	{
//         if	( ver	<= 2.99
// 			&&j		>= 80)
// 		{
// 			/* ver.2 */
//             if (!fgets(buff,MAXRNXLEN,fp))
// 				break;
//             j = 0;
//         }

		E_FType ft		= ftypes[codeType.code];

		RawSig* rawSig = nullptr;
		list<RawSig>& sigList = obs.SigsLists[ft];

		for (auto& sig : sigList)
		{
			if (sig.code == codeType.code)
			{
				rawSig = &sig;
				break;
			}
		}
		if (rawSig == nullptr)
		{
			RawSig raw;
			raw.code = codeType.code;

			sigList.push_back(raw);
			rawSig = &sigList.back();
		}

		double val = str2num(buff, j,		14);
		double lli = str2num(buff, j+14,	1);
		lli = (unsigned char) lli & 0x03;

// 		val += shift;// todo aaron, phase shift needed
		RawSig& sig = *rawSig;
		switch (codeType.type)
		{
			case 'C': sig.P		= val; 									break;
			case 'L': sig.L		= val; 				sig.LLI = lli;  	break;
			case 'D': sig.D		= val;                        			break;
			case 'S': sig.snr	= val * 4 + 0.5;   						break;
		}

		j += 16;
	}

//     BOOST_LOG_TRIVIAL(debug)
// 	<< "decode_obsdata: time=" << obs.time.to_string(0)
// 	<< " sat=" << obs.Sat.id();

	return 1;
}

/* read rinex obs data body --------------------------------------------------*/
int readrnxobsb(
	std::istream& 					inputStream,
	double							ver,
	map<E_Sys, vector<CodeType>>&	sysCodeTypes,
	int&							flag,
	ObsList&						obsList)
{
	GTime			time = {};
	string			line;
	int				i		= 0;
	vector<SatSys>	sats;
	int				nSats = 0;	//cant replace with sats.size()

	/* read record */
	while (std::getline(inputStream, line))
	{
		/* decode obs epoch */
		if (i == 0)
		{
			nSats = decode_obsepoch(inputStream, line, ver, &time, flag, sats);
			if (nSats <= 0)
			{
				continue;
			}
		}
		else if ( flag <= 2
				||flag == 6)
		{
			RawObs rawObs = {};

			rawObs.time	= time;

			/* decode obs data */
			bool pass = decode_obsdata(inputStream, line, ver, sysCodeTypes, rawObs, &sats[i]);
			if	(pass)
			{
				/* save obs data */
				obsList.push_back(rawObs);
			}
		}

		if (++i > nSats)
			return obsList.size();
	}

	return -1;
}

/* read rinex obs ------------------------------------------------------------*/
int readrnxobs(
	std::istream& 					inputStream,
	double							ver,
	int								tsys,
	map<E_Sys, vector<CodeType>>&	sysCodeTypes,
	ObsList&						obsList)
{
	int flag = 0;
	int stat = 0;

//     BOOST_LOG_TRIVIAL(debug)
// 	<< "readrnxobs: ver=" << ver << " tsys=" << tsys;

	/* read rinex obs data body */
	int n = readrnxobsb(inputStream, ver, sysCodeTypes, flag, obsList);

	if	( n >= 0
		&&stat >= 0)
	{
		for (auto& obs : obsList)
		{
			/* utc -> gpst */
			if (tsys == TSYS_UTC)
				obs.time = utc2gpst(obs.time);
		}

		stat = 1;
	}

//     BOOST_LOG_TRIVIAL(debug)
// 	<< "readrnxobs: nobs=" << obsList.size()
// 	<< " stat=" << stat;

	return stat;
}

/* decode ephemeris ----------------------------------------------------------*/
int decode_eph(
	double ver,
	SatSys Sat,
	GTime toc,
	double *data,
	Eph *eph)
{
	Eph eph0 = {};

//     BOOST_LOG_TRIVIAL(debug)
// 	<< "decode_eph: ver=" << ver << " sat=" << Sat.id();

	int sys = Sat.sys;

	if	( sys != +E_Sys::GPS
		&&sys != +E_Sys::GAL
		&&sys != +E_Sys::QZS
		&&sys != +E_Sys::BDS)
	{
		BOOST_LOG_TRIVIAL(debug)
		<< "ephemeris error: invalid satellite sat=" << Sat.id();

		return 0;
	}
	*eph=eph0;

	eph->Sat=Sat;
	eph->toc=toc;

	eph->f0=data[0];
	eph->f1=data[1];
	eph->f2=data[2];

	eph->A=SQR(data[10]); eph->e=data[ 8]; eph->i0  =data[15]; eph->OMG0=data[13];
	eph->omg =data[17]; eph->M0 =data[ 6]; eph->deln=data[ 5]; eph->OMGd=data[18];
	eph->idot=data[19]; eph->crc=data[16]; eph->crs =data[ 4]; eph->cuc =data[ 7];
	eph->cus =data[ 9]; eph->cic=data[12]; eph->cis =data[14];

	if	( sys == +E_Sys::GPS
		||sys == +E_Sys::QZS)
	{
		eph->iode=(int)data[ 3];      /* IODE */
		eph->iodc=(int)data[26];      /* IODC */
		eph->toes=     data[11];      /* toe (s) in gps week */
		eph->week=(int)data[21];      /* gps week */
		eph->toe=adjweek(gpst2time(eph->week,data[11]),toc);
		eph->ttr=adjweek(gpst2time(eph->week,data[27]),toc);

		eph->code=(int)data[20];      /* GPS: codes on L2 ch */
		eph->svh =(E_Svh)data[24];      /* sv health */
		eph->sva=uraindex(data[23]);  /* ura (m->index) */
		eph->flag=(int)data[22];      /* GPS: L2 P data flag */

		eph->tgd[0]=   data[25];      /* TGD */
		eph->fit   =   data[28];      /* fit interval */

		decomposeTGDBias(Sat, eph->tgd[0]);
	}
	else if (sys==+E_Sys::GAL)
	{
		/* GAL ver.3 */
		eph->iode=(int)data[ 3];      /* IODnav */
		eph->toes=     data[11];      /* toe (s) in galileo week */
		eph->week=(int)data[21];      /* gal week = gps week */
		eph->toe=adjweek(gpst2time(eph->week,data[11]),toc);
		eph->ttr=adjweek(gpst2time(eph->week,data[27]),toc);

		eph->code=(int)data[20];      /* data sources */
									/* bit 0 set: I/NAV E1-B */
									/* bit 1 set: F/NAV E5a-I */
									/* bit 2 set: F/NAV E5b-I */
									/* bit 8 set: af0-af2 toc are for E5a.E1 */
									/* bit 9 set: af0-af2 toc are for E5b.E1 */
		eph->svh =(E_Svh)data[24];      /* sv health */
									/* bit     0: E1B DVS */
									/* bit   1-2: E1B HS */
									/* bit     3: E5a DVS */
									/* bit   4-5: E5a HS */
									/* bit     6: E5b DVS */
									/* bit   7-8: E5b HS */
		eph->sva = sisaToSva(data[23]);
		//eph->sva =uraindex(data[23]); /* ura (m->index) */

		eph->tgd[0]=   data[25];      /* BGD E5a/E1 */
		eph->tgd[1]=   data[26];      /* BGD E5b/E1 */

		decomposeBGDBias(Sat, eph->tgd[0], eph->tgd[1]);
	}
	else if (sys==+E_Sys::BDS)
	{
		/* BeiDou v.3.02 */
		eph->toc=bdt2gpst(eph->toc);  /* bdt -> gpst */
		eph->iode=(int)data[ 3];      /* AODE */
		eph->iodc=(int)data[28];      /* AODC */
		eph->toes=     data[11];      /* toe (s) in bdt week */
		eph->week=(int)data[21];      /* bdt week */
		eph->toe=bdt2gpst(bdt2time(eph->week,data[11])); /* bdt -> gpst */
		eph->ttr=bdt2gpst(bdt2time(eph->week,data[27])); /* bdt -> gpst */
		eph->toe=adjweek(eph->toe,toc);
		eph->ttr=adjweek(eph->ttr,toc);

		eph->svh =(E_Svh)data[24];      /* satH1 */
		eph->sva=uraindex(data[23]);  /* ura (m->index) */

		eph->tgd[0]=   data[25];      /* TGD1 B1/B3 */
		eph->tgd[1]=   data[26];      /* TGD2 B2/B3 */
	}

	if (eph->iode<0||1023<eph->iode)
	{
		BOOST_LOG_TRIVIAL(debug)
		<< "rinex nav invalid: sat=" << Sat.id() << " iode=" << eph->iode;
	}

	if (eph->iodc<0||1023<eph->iodc)
	{
		BOOST_LOG_TRIVIAL(debug)
		<< "rinex nav invalid: sat=" << Sat.id() << " iodc=" << eph->iodc;
	}
	return 1;
}

/* decode glonass ephemeris --------------------------------------------------*/
int decode_geph(double ver, SatSys Sat, GTime toc, double *data,
					Geph *geph)
{
	Geph geph0={};
	GTime tof;
	double tow,tod;
	int week,dow;

//     BOOST_LOG_TRIVIAL(debug)
// 	<< "decode_geph: ver=" << ver << " sat=" << Sat.id();

	if (Sat.sys!=+E_Sys::GLO)
	{
		BOOST_LOG_TRIVIAL(debug)
		<< "glonass ephemeris error: invalid satellite sat=" << Sat.id();

		return 0;
	}
	*geph=geph0;

	geph->Sat=Sat;

	/* toc rounded by 15 min in utc */
	tow=time2gpst(toc,&week);
	toc=gpst2time(week,floor((tow+450.0)/900.0)*900);
	dow=(int)floor(tow/86400.0);

	/* time of frame in utc */
	tod=ver<=2.99?data[2]:fmod(data[2],86400.0); /* tod (v.2), tow (v.3) in utc */
	tof=gpst2time(week,tod+dow*86400.0);
	tof=adjday(tof,toc);

	geph->toe=utc2gpst(toc);   /* toc (gpst) */
	geph->tof=utc2gpst(tof);   /* tof (gpst) */

	/* iode = tb (7bit), tb =index of UTC+3H within current day */
	geph->iode=(int)(fmod(tow+10800.0,86400.0)/900.0+0.5);

	geph->taun= data[0];       /* taun */
	geph->gamn= data[1];       /* +gamman */

	geph->pos[0]=data[3]*1E3; geph->pos[1]=data[7]*1E3; geph->pos[2]=data[11]*1E3;
	geph->vel[0]=data[4]*1E3; geph->vel[1]=data[8]*1E3; geph->vel[2]=data[12]*1E3;
	geph->acc[0]=data[5]*1E3; geph->acc[1]=data[9]*1E3; geph->acc[2]=data[13]*1E3;

	geph->svh=(E_Svh)data[ 6];
	geph->frq=(int)data[10];
	geph->age=(int)data[14];

	/* some receiver output >128 for minus frequency number */
	if (geph->frq >  128)
		geph->frq -= 256;

	if (geph->frq<MINFREQ_GLO||MAXFREQ_GLO<geph->frq)
	{
		BOOST_LOG_TRIVIAL(debug)
		<< "rinex gnav invalid freq: sat=" << Sat << " fn=" << geph->frq;
	}
	return 1;
}
/* decode geo ephemeris ------------------------------------------------------*/
int decode_seph(double ver, SatSys Sat, GTime toc, double *data,
					Seph *seph)
{
	Seph seph0 = {};
	int week;

//     BOOST_LOG_TRIVIAL(debug)
// 	<< "decode_seph: ver=" << ver << " sat=" << Sat.id();

	if (Sat.sys!=+E_Sys::SBS)
	{
		BOOST_LOG_TRIVIAL(debug)
		<< "geo ephemeris error: invalid satellite sat=" << Sat.id();

		return 0;
	}
	*seph=seph0;

	seph->Sat=Sat;
	seph->t0 =toc;

	time2gpst(toc,&week);
	seph->tof=adjweek(gpst2time(week,data[2]),toc);

	seph->af0=data[0];
	seph->af1=data[1];

	seph->pos[0]=data[3]*1E3; seph->pos[1]=data[7]*1E3; seph->pos[2]=data[11]*1E3;
	seph->vel[0]=data[4]*1E3; seph->vel[1]=data[8]*1E3; seph->vel[2]=data[12]*1E3;
	seph->acc[0]=data[5]*1E3; seph->acc[1]=data[9]*1E3; seph->acc[2]=data[13]*1E3;

	seph->svh=(E_Svh)data[6];
	seph->sva=uraindex(data[10]);

	return 1;
}

/* read rinex navigation data body -------------------------------------------*/
int readrnxnavb(
	std::istream& 	inputStream,
	double		ver,
	E_Sys		sys,
	int&		type,
	Eph*		eph,
	Geph*		geph,
	Seph*		seph)
{
	GTime toc;
	double data[64];
	int i=0,j,prn,sp=3;
	string	line;
	char id[8]="";
	char *p;

//     BOOST_LOG_TRIVIAL(debug)
// 	<< "readrnxnavb: ver=" << ver << " sys=" << sys;

	SatSys Sat = {};
	while (std::getline(inputStream, line))
	{
		char*	buff	= &line[0];

		if (i==0)
		{
			/* decode satellite field */
			if (ver>=3.0||sys==+E_Sys::GAL||sys==+E_Sys::QZS)
			{
				/* ver.3 or GAL/QZS */
				strncpy(id,buff,3);
				Sat=SatSys(id);
				sp=4;
				if (ver>=3.0) sys=Sat.sys;
			}
			else
			{
				prn=(int)str2num(buff,0,2);
				Sat.sys = sys;
				Sat.prn = prn;
			}
			/* decode toc field */
			if (str2time(buff+sp,0,19,toc))
			{
//                 BOOST_LOG_TRIVIAL(debug)
// 				<< "rinex nav toc error: " << buff;

				return 0;
			}
			/* decode data fields */
			for (j=0,p=buff+sp+19;j<3;j++,p+=19)
			{
				data[i++]=str2num(p,0,19);
			}
		}
		else
		{
			/* decode data fields */
			for (j=0,p=buff+sp;j<4;j++,p+=19)
			{
				data[i++]=str2num(p,0,19);
			}
			/* decode ephemeris */
			if		(sys==+E_Sys::GLO&&i>=15)	{ type = 1;	return decode_geph(ver,Sat,toc,data,geph);  }
			else if	(sys==+E_Sys::SBS&&i>=15) 	{ type = 2;	return decode_seph(ver,Sat,toc,data,seph);  }
			else if	(i>=31)						{ type = 0;	return decode_eph(ver,Sat,toc,data,eph);    }
		}
	}
	return -1;
}

/* read rinex nav/gnav/geo nav -----------------------------------------------*/
int readrnxnav(
	std::istream& 	inputStream,
	double			ver,
	E_Sys			sys,
	nav_t&			nav)
{
	Eph		eph;
	Geph	geph;
	Seph	seph;
	int		stat;
	int		type;

//     BOOST_LOG_TRIVIAL(debug)
// 	<< "readrnxnav: ver=" << ver << " sys=" << sys;

	/* read rinex navigation data body */
	while ((stat = readrnxnavb(inputStream, ver, sys, type, &eph, &geph, &seph)) >= 0)
	{
		/* add ephemeris to navigation data */
		if (stat)
		{
			switch (type)
			{
				case 1 : nav.gephMap[geph.Sat][eph.toe] = geph;	break;
				case 2 : nav.sephMap[seph.Sat][eph.toe] = seph;	break;
				default: nav.ephMap [eph.Sat] [eph.toe] = eph;	break;
			}
		}
	}
	return	( nav.ephMap .size() > 0
			||nav.gephMap.size() > 0
			||nav.sephMap.size() > 0);
}

/* read rinex clock ----------------------------------------------------------*/
int readrnxclk(
	std::istream& 	inputStream,
	double			ver,
	nav_t&			nav)
{
//     trace(3,"readrnxclk: index=%d\n", index);

	static int index = 0;
	index++;
	string line;

	typedef struct
	{
		short offset;
		short length;
	} ClkStruct;

	ClkStruct typ = {0,2};
	ClkStruct as  = {3,3};
	ClkStruct ar  = {3,4};
	ClkStruct tim = {8,26};
	ClkStruct clk = {40,19};
	ClkStruct std = {60,19};

	/* special case for 3.04 rnx with 9 char AR names */
	if (ver == 3.04)
	{
		ar.length  += 5;
		tim.offset += 5;
		clk.offset += 5;
		std.offset += 5;
	}

	while (std::getline(inputStream, line))
	{
		char*	buff	= &line[0];

		GTime time;
		if (str2time(buff, tim.offset, tim.length, time))
		{
//             trace(2,"rinex clk invalid epoch: %34.34s\n", buff);
			continue;
		}

		string type(buff + typ.offset,typ.length);

		string idString;
		if		(type == "AS")	{	idString.assign(buff + as.offset, as.length);	}
		else if	(type == "AR")	{	idString.assign(buff + ar.offset, ar.length);	}
		else 						continue;

		Pclk preciseClock = {};

		preciseClock.clk	= str2num(buff, clk.offset, clk.length);
		preciseClock.std	= str2num(buff, std.offset, std.length);
		preciseClock.time	= time;
		preciseClock.index	= index;

		nav.pclkMap[idString].push_back(preciseClock);
	}

	return nav.pclkMap.size() > 0;
}

/* read rinex file -----------------------------------------------------------*/
int readrnx(
	std::istream& 					inputStream,
	char&							type,
	ObsList&						obsList,
	nav_t&							nav,
	RinexStation*					sta,
	double&							ver,
	E_Sys&							sys,
	int&							tsys,
	map<E_Sys, vector<CodeType>>&	sysCodeTypes)
{
//     BOOST_LOG_TRIVIAL(debug)
// 	<< "readrnxfp: flag=" << flag
// 	<< " index=" << index;

	// read rinex header if at beginning of file
	if (inputStream.tellg() == 0)
	{
		bool pass = readrnxh(inputStream, ver, type, sys, tsys, sysCodeTypes, nav, sta);

		return pass;
	}

	/* read rinex body */
	switch (type)
	{
		case 'O': return readrnxobs(inputStream, ver, tsys, sysCodeTypes, obsList);
		case 'N': return readrnxnav(inputStream, ver, sys      , 	nav);
		case 'G': return readrnxnav(inputStream, ver, E_Sys::GLO, 	nav);
		case 'H': return readrnxnav(inputStream, ver, E_Sys::SBS, 	nav);
		case 'J': return readrnxnav(inputStream, ver, E_Sys::QZS, 	nav); /* extension */
		case 'L': return readrnxnav(inputStream, ver, E_Sys::GAL, 	nav); /* extension */
		case 'C': return readrnxclk(inputStream, ver,				nav);
	}

	BOOST_LOG_TRIVIAL(debug)
	<< "unsupported rinex type ver=" << ver << " type=" << type;

	return 0;
}

ext: cpp

max_stars_repo_name: HiTMonitor/ginan

max_issues_repo_name: HiTMonitor/ginan

max_forks_repo_name: HiTMonitor/ginan


text:
//          Copyright Dean Michael Berris 2008.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_NETWORK_PROTOCOL_HTTP_TRAITS_REQUEST_METHODS_IPP
#define BOOST_NETWORK_PROTOCOL_HTTP_TRAITS_REQUEST_METHODS_IPP

#include <boost/network/tags.hpp>

namespace boost {
namespace network {
namespace http {

template <>
struct request_methods<tags::http_default_8bit_tcp_resolve> {
  static char const* head() {
    static char const* const HEAD = "HEAD";
    return HEAD;
  };

  static char const* get() {
    static char const* const GET = "GET";
    return GET;
  };

  static char const* put() {
    static char const* const PUT = "PUT";
    return PUT;
  };

  static char const* post() {
    static char const* const POST = "POST";
    return POST;
  };

  static char const* delete_() {
    static char const* const DELETE_ = "DELETE";
    return DELETE_;
  };
};

}  // namespace http

}  // namespace network

}  // namespace boost

#endif  // BOOST_NETWORK_PROTOCOL_HTTP_TRAITS_REQUEST_METHODS_IPP

ext: ipp

max_stars_repo_name: yhager/cpp-netlib

max_issues_repo_name: yhager/cpp-netlib

max_forks_repo_name: yhager/cpp-netlib


text:
#ifndef MPLLIBS_METAMONAD_V1_METAFUNCTION_HPP
#define MPLLIBS_METAMONAD_V1_METAFUNCTION_HPP

//    Copyright Abel Sinkovics (abel@sinkovics.hu) 2012.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <mpllibs/metamonad/v1/impl/metafunction_body.hpp>
#include <mpllibs/metamonad/v1/impl/expand_arg_usage.hpp>
#include <mpllibs/metamonad/v1/impl/expand_arg_usage_with_na.hpp>
#include <mpllibs/metamonad/v1/impl/curried_call.hpp>
#include <mpllibs/metamonad/v1/tmp_value.hpp>

#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/size.hpp>

#ifdef MPLLIBS_V1_METAFUNCTION
#  error MPLLIBS_V1_METAFUNCTION already defined
#endif
#define MPLLIBS_V1_METAFUNCTION(name, args) \
  template <BOOST_PP_SEQ_FOR_EACH_I(MPLLIBS_V1_EXPAND_ARG_USAGE, ~, args)> \
  struct BOOST_PP_CAT(name, ___impl); \
  \
  template < \
    BOOST_PP_SEQ_FOR_EACH_I(MPLLIBS_V1_EXPAND_ARG_USAGE_WITH_NA, ~, args) \
  > \
  struct name : \
    BOOST_PP_CAT( \
      mpllibs::metamonad::v1::impl::curried_call, \
      BOOST_PP_SEQ_SIZE(args) \
    )<BOOST_PP_CAT(name, ___impl), BOOST_PP_SEQ_ENUM(args)> \
  {}; \
  \
  template <BOOST_PP_SEQ_FOR_EACH_I(MPLLIBS_V1_EXPAND_ARG_USAGE, ~, args)> \
  struct BOOST_PP_CAT(name, ___impl) : MPLLIBS_V1_METAFUNCTION_BODY

#endif

ext: hpp


text:
#include <cmath>
#include <vector>
#include <string>
#include "multi_lidar_calib/common.h"
#include "multi_lidar_calib/tic_toc.h"
#include <nav_msgs/Odometry.h>
#include <opencv/cv.h>
#include <pcl_conversions/pcl_conversions.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/io/io.h>
#include <pcl/io/pcd_io.h>
#include <pcl/io/obj_io.h>
#include <pcl/PolygonMesh.h>
#include <pcl/point_cloud.h>
#include <pcl/io/vtk_lib_io.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/sample_consensus/ransac.h>
#include <pcl/sample_consensus/sac_model_plane.h>
#include <pcl/sample_consensus/sac_model_sphere.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/sample_consensus/sac_model_perpendicular_plane.h>
#include <pcl/filters/passthrough.h>    ///直通滤波相关
#include <ros/ros.h>
#include <sensor_msgs/Imu.h>
#include <sensor_msgs/PointCloud2.h>
#include <tf/transform_datatypes.h>
#include <tf/transform_broadcaster.h>

 #include <ceres/ceres.h>
 #include "lidarFactor.hpp"

#include <pcl/features/vfh.h>
#include <pcl/features/normal_3d.h>
#include <boost/thread/thread.hpp>
#include <pcl/common/common_headers.h>
#include <pcl/io/pcd_io.h>
#include <pcl/console/parse.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <sstream>
#include<vector>

#include<mutex>

#define PI 3.1415926

using std::atan2;
using std::cos;
using std::sin;

double lidar_z_height = -1;

ros::Publisher  pubFittedPlane;

double parameters[7] = {0, 0, 0, 1, 0, 0, 0}; // 激光雷达间相对位姿关系

Eigen::Map<Eigen::Quaterniond> q_to_be_optimized(parameters);
Eigen::Map<Eigen::Vector3d> t_to_be_optimized(parameters + 4);

void planeFitting(pcl::PointCloud<pcl::PointXYZ>::Ptr extracted_cloud, pcl::ModelCoefficients::Ptr & coefficients)
{

    pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
    pcl::SACSegmentation<pcl::PointXYZ> seg;
    seg.setOptimizeCoefficients (true);
    seg.setModelType (pcl::SACMODEL_PLANE);
    seg.setMethodType (pcl::SAC_RANSAC);
    seg.setDistanceThreshold (0.01);
     seg.setInputCloud(extracted_cloud);
    seg.segment (*inliers, *coefficients);

    // // ax + by + cz + d = 0，其中法向量为(a, b, c)
    // std::cout<<"平面参数："<<std::endl;
    // std::cout<<"a："<<coefficients->values[0]<<std::endl;
    // std::cout<<"b："<<coefficients->values[1]<<std::endl;
    // std::cout<<"c："<<coefficients->values[2]<<std::endl;
    // std::cout<<"d："<<coefficients->values[3]<<std::endl;
    double a,b,c,d;
    a =   coefficients->values[0];
    b =  coefficients->values[1];
    c =  coefficients->values[2];
    d =  coefficients->values[3];
    // double numerator = fabs(  a*x_c+b*y_c+c*z_c+d  );
    // double denominator = std::sqrt(  a*a+b*b+c*c );
    // double distanceToArea = numerator/ denominator;
    pcl::PointCloud<pcl::PointXYZ>::Ptr fittedPlaneCloud(new pcl::PointCloud<pcl::PointXYZ>);
    for (int i=0; i<inliers->indices.size();i++)
    {
        int ind = inliers->indices[i];
         fittedPlaneCloud->points.push_back(  extracted_cloud->points[ind]  );
    }

    sensor_msgs::PointCloud2 fittedPlaneCloudMsg;
    pcl::toROSMsg(*fittedPlaneCloud, fittedPlaneCloudMsg);
    fittedPlaneCloudMsg.header.stamp = ros::Time::now();
    fittedPlaneCloudMsg.header.frame_id = "/rslidar";
    pubFittedPlane.publish(fittedPlaneCloudMsg);

}

int main(int argc, char **argv)
{

    ros::init(argc, argv, "multi_lidar_calibration");
	ros::NodeHandle nh;
    pubFittedPlane = nh.advertise<sensor_msgs::PointCloud2>("/fitted_plane", 100);

    for (int iterCount = 0; iterCount < 10; iterCount++)
    {

            ceres::LossFunction *loss_function = new ceres::HuberLoss(0.1);
            ceres::LocalParameterization *q_parameterization =
                new ceres::EigenQuaternionParameterization();
            ceres::Problem::Options problem_options;

            ceres::Problem problem(problem_options);
            problem.AddParameterBlock(parameters, 4, q_parameterization);
            problem.AddParameterBlock(parameters + 4, 3);

            for (int k=0; k<36; k++)
            {

                std::stringstream ss;
                std::string filename_pt0 = "/media/mjy/Samsung_T5/linux/DX/data/0812forcalib/pcd_select/";
                std::string filename_pt1 = "/media/mjy/Samsung_T5/linux/DX/data/0812forcalib/pcd_select/";
                ss << k+1;
                std::string num = ss.str();
                filename_pt0.append(num);
                filename_pt0.append("_0.pcd");
                filename_pt1.append(num);
                filename_pt1.append("_1.pcd");

                std::cout<<"Extracting "<<k<<" pointcloud from "<<  filename_pt0<<std::endl;
                std::cout<<"Extracting "<<k<<" pointcloud from "<<  filename_pt1<<std::endl;
                pcl::PointCloud<pcl::PointXYZ>::Ptr cloud0(new pcl::PointCloud<pcl::PointXYZ>);
                pcl::PointCloud<pcl::PointXYZ>::Ptr cloud1(new pcl::PointCloud<pcl::PointXYZ>);

                if (pcl::io::loadPCDFile<pcl::PointXYZ> (filename_pt0, *cloud0) == -1)
                    {
                    PCL_ERROR ("Couldn't read PCD file \n");
                    }
                if (pcl::io::loadPCDFile<pcl::PointXYZ> (filename_pt1, *cloud1) == -1)
                    {
                    PCL_ERROR ("Couldn't read PCD file \n");
                    }

                pcl::ModelCoefficients::Ptr coefficients0 (new pcl::ModelCoefficients);
                planeFitting(cloud0, coefficients0);
                pcl::ModelCoefficients::Ptr coefficients1 (new pcl::ModelCoefficients);
                planeFitting(cloud1,coefficients1);

                // 两平面法线的夹角作为一个损失值
                Eigen::Vector3d norm0(coefficients0->values[0], coefficients0->values[1], coefficients0->values[2]);
                Eigen::Vector3d norm1(coefficients1->values[0], coefficients1->values[1], coefficients1->values[2]);
                ceres::CostFunction *cost_function;
                cost_function = LidarNormFactor::Create(norm0, norm1);
                problem.AddResidualBlock(cost_function, loss_function, parameters, parameters + 4);

                TicToc t_solver;
                ceres::Solver::Options options;
                options.linear_solver_type = ceres::DENSE_QR;
                options.max_num_iterations = 10;
                options.minimizer_progress_to_stdout = false;
                options.check_gradients = false;
                options.gradient_check_relative_precision = 1e-4;
                ceres::Solver::Summary summary;
                ceres::Solve(options, &problem, &summary);

                sleep(0.2);

            }

    }

    printf("result q %f %f %f %f result t %f %f %f\n", parameters[3], parameters[0], parameters[1], parameters[2],parameters[4], parameters[5], parameters[6]);
    std::cout<<q_to_be_optimized.matrix()<<std::endl;

    return 0;
}


text:
/*
@copyright Louis Dionne 2015
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/assert.hpp>
#include <boost/hana/ext/std/integer_sequence.hpp>
#include <boost/hana/ext/std/integral_constant.hpp>
#include <boost/hana/tuple.hpp>

#include <laws/base.hpp>
#include <laws/comparable.hpp>
#include <laws/foldable.hpp>
#include <laws/iterable.hpp>
#include <laws/searchable.hpp>

#include <cstddef>
#include <type_traits>
#include <utility>
using namespace boost::hana;

using T = int;
using U = long long;

int main() {
    auto sequences = make<tuple_tag>(
        std::integer_sequence<T>{},
        std::integer_sequence<U>{},

        std::integer_sequence<T, 0>{},
        std::integer_sequence<U, 0>{},

        std::integer_sequence<T, 0, 1>{},
        std::integer_sequence<U, 0, 1>{},

        std::integer_sequence<T, 1, 0, 2>{},
        std::integer_sequence<U, 1, 0, 2>{}
    );
    (void)sequences;

#if BOOST_HANA_TEST_PART == 1
    //////////////////////////////////////////////////////////////////////////
    // Comparable
    //////////////////////////////////////////////////////////////////////////
    {
        // equal
        {
            BOOST_HANA_CONSTANT_CHECK(equal(
                std::integer_sequence<T>{},
                std::integer_sequence<U>{}
            ));
            BOOST_HANA_CONSTANT_CHECK(not_(equal(
                std::integer_sequence<T, 0>{},
                std::integer_sequence<U>{}
            )));
            BOOST_HANA_CONSTANT_CHECK(not_(equal(
                std::integer_sequence<T>{},
                std::integer_sequence<U, 0>{}
            )));

            BOOST_HANA_CONSTANT_CHECK(equal(
                std::integer_sequence<T, 0>{},
                std::integer_sequence<U, 0>{}
            ));
            BOOST_HANA_CONSTANT_CHECK(not_(equal(
                std::integer_sequence<T, 0>{},
                std::integer_sequence<U, 0, 1>{}
            )));
            BOOST_HANA_CONSTANT_CHECK(not_(equal(
                std::integer_sequence<T, 0, 2>{},
                std::integer_sequence<U, 0, 1>{}
            )));

            BOOST_HANA_CONSTANT_CHECK(equal(
                std::integer_sequence<T, 0, 1, 2, 3>{},
                std::integer_sequence<U, 0, 1, 2, 3>{}
            ));
            BOOST_HANA_CONSTANT_CHECK(not_(equal(
                std::integer_sequence<T, 0, 1, 2, 3, 5>{},
                std::integer_sequence<U, 0, 1, 2, 3>{}
            )));
        }

        // laws
        test::TestComparable<ext::std::integer_sequence_tag>{sequences};
    }

#elif BOOST_HANA_TEST_PART == 2
    //////////////////////////////////////////////////////////////////////////
    // Foldable
    //////////////////////////////////////////////////////////////////////////
    {
        // unpack
        {
            test::_injection<0> f{};

            BOOST_HANA_CONSTANT_CHECK(equal(
                unpack(std::integer_sequence<T>{}, f),
                f()
            ));

            BOOST_HANA_CONSTANT_CHECK(equal(
                unpack(std::integer_sequence<T, 0>{}, f),
                f(std::integral_constant<T, 0>{})
            ));

            BOOST_HANA_CONSTANT_CHECK(equal(
                unpack(std::integer_sequence<T, 0, 1>{}, f),
                f(std::integral_constant<T, 0>{}, std::integral_constant<T, 1>{})
            ));

            BOOST_HANA_CONSTANT_CHECK(equal(
                unpack(std::integer_sequence<T, 0, 1, 2>{}, f),
                f(std::integral_constant<T, 0>{}, std::integral_constant<T, 1>{},
                  std::integral_constant<T, 2>{})
            ));

            BOOST_HANA_CONSTANT_CHECK(equal(
                unpack(std::integer_sequence<T, 0, 1, 2, 3>{}, f),
                f(std::integral_constant<T, 0>{}, std::integral_constant<T, 1>{},
                  std::integral_constant<T, 2>{}, std::integral_constant<T, 3>{})
            ));
        }

        // laws
        test::TestFoldable<ext::std::integer_sequence_tag>{sequences};
    }

#elif BOOST_HANA_TEST_PART == 3
    //////////////////////////////////////////////////////////////////////////
    // Iterable
    //////////////////////////////////////////////////////////////////////////
    {
        // front
        {
            BOOST_HANA_CONSTANT_CHECK(equal(
                front(std::index_sequence<0>{}),
                std::integral_constant<std::size_t, 0>{}
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                front(std::index_sequence<0, 1>{}),
                std::integral_constant<std::size_t, 0>{}
            ));
        }

        // is_empty
        {
            BOOST_HANA_CONSTANT_CHECK(is_empty(std::index_sequence<>{}));
            BOOST_HANA_CONSTANT_CHECK(not_(is_empty(std::index_sequence<0>{})));
            BOOST_HANA_CONSTANT_CHECK(not_(is_empty(std::index_sequence<1>{})));
        }

        // tail
        {
            BOOST_HANA_CONSTANT_CHECK(equal(
                tail(std::index_sequence<0>{}),
                std::index_sequence<>{}
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                tail(std::index_sequence<0, 1>{}),
                std::index_sequence<1>{}
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                tail(std::index_sequence<0, 1, 2>{}),
                std::index_sequence<1, 2>{}
            ));
        }

        // laws
        test::TestIterable<ext::std::integer_sequence_tag>{sequences};
    }

#elif BOOST_HANA_TEST_PART == 4
    //////////////////////////////////////////////////////////////////////////
    // Searchable
    //////////////////////////////////////////////////////////////////////////
    {
        auto sequences = make<tuple_tag>(
            std::integer_sequence<T>{},
            std::integer_sequence<T, 2>{},
            std::integer_sequence<T, 3>{},
            std::integer_sequence<T, 3, 4>{},
            std::integer_sequence<T, 3, 4, 5>{}
        );

        auto keys = make<tuple_tag>(
            std::integral_constant<T, 3>{}, std::integral_constant<U, 4>{}
        );

        // laws
        test::TestSearchable<ext::std::integer_sequence_tag>{sequences, keys};
    }
#endif
}

max_stars_repo_name: qicosmos/hana

max_issues_repo_name: qicosmos/hana

max_forks_repo_name: qicosmos/hana


text:
//==================================================================================================
/*!
  @file

  @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)

**/
//==================================================================================================
#ifndef BOOST_SIMD_META_CARDINAL_OF_HPP_INCLUDED
#define BOOST_SIMD_META_CARDINAL_OF_HPP_INCLUDED

#include <boost/simd/config.hpp>
#include <boost/core/ignore_unused.hpp>
#include <boost/config.hpp>
#include <boost/simd/detail/nsm.hpp>

namespace boost { namespace simd
{
  namespace tt = nsm::type_traits;
  /*!
    @ingroup group-api

    Computes the register width required to store an instance of any type @c T.

    @par Models:

    @intconst

    @par Semantic:

    For any given cv-qualified type @c T,

    @code
    using r = cardinal_of<T>::type;
    @endcode

    is equivalent to:

    @code
    using r = sd::integral_constant<std::size_t,sizeof(T)/sizeof(boost::dispatch::scalar_of_t<T>)>;
    @endcode

    @tparam T Type which register size is to be computed.

    @see boost::simd::cardinal
  **/
  template<typename T>
  struct  cardinal_of
#if !defined(DOXYGEN_ONLY)
        : tt::integral_constant<std::size_t,1ULL>
#endif
  {};

#if !defined(DOXYGEN_ONLY)
  template<typename T>  struct cardinal_of<T&>                : cardinal_of<T> {};
  template<typename T>  struct cardinal_of<T&&>               : cardinal_of<T> {};
  template<typename T>  struct cardinal_of<T const>           : cardinal_of<T> {};
  template<typename T>  struct cardinal_of<volatile T>        : cardinal_of<T> {};
  template<typename T>  struct cardinal_of<volatile T const>  : cardinal_of<T> {};
#endif

  /*!
    @ingroup group-api
    Eager short-cut for cardinal_of
  **/
  template<typename T>  using cardinal_of_t = typename cardinal_of<T>::type;

  /*!
    @ingroup group-api

    Computes the register size of any given value v.

    @par Semantic:

    For any instance @c v of type @c T,

    @code
    auto r = cardinal(v)
    @endcode

    is equivalent to:

    @code
    auto r = cardinal_of<T>::type{};
    @endcode

    @param v Value which register size is to be computed
    @return An @intconst containing v register size.

    @see boost::simd::cardinal_of
  **/
  template<typename T> BOOST_FORCEINLINE cardinal_of_t<T> cardinal(T&& v) BOOST_NOEXCEPT
  {
    boost::ignore_unused(v);
    return {};
  }
} }

#endif

max_stars_repo_name: SylvainCorlay/pythran

max_issues_repo_name: SylvainCorlay/pythran

max_forks_repo_name: SylvainCorlay/pythran