<commit_before>/*
Copyright (c) 2013-2018 Ubidots.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Developed and maintained by Jose Garcia and Cristian Arrieta for IoT Services
Inc
@jotathebest at github: https://github.com/jotathebest
@crisap94 at github: https://github.com/crisap94
*/

#include "UbiTcp.h"

/**************************************************************************
 * Overloaded constructors
 ***************************************************************************/

/**
 * @brief Construct a new UbiTCP object and power up the module
 *
 * @param token Users token
 * @param server Server addres
 * @param port Server port
 * @param user_agent Reference from the device being connected
 * @param apn GPRS network name
 * @param apnUser GPRS network username
 * @param apnPass GPRS network passwrod
 */
UbiTCP::UbiTCP(UbiToken token, UbiServer server, const int port, const char *user_agent, UbiApn apn, UbiApn apnUser,
               UbiApn apnPass) {

  _server = server;
  _user_agent = user_agent;
  _token = token;
  _port = port;
  _apn = apn;
  _apnUser = apnUser;
  _apnPass = apnPass;
  _client_tcp = GPRS::getInstance() == NULL ? new GPRS(RX, TX, BAUDRATE) : GPRS::getInstance();
}

/**************************************************************************
 * Destructor
 ***************************************************************************/

UbiTCP::~UbiTCP() {
  delete[] _server;
  delete[] _user_agent;
  delete[] _token;
  delete[] _apn;
  delete[] _apnUser;
  delete[] _apnPass;

  free(_client_tcp);
}

/**************************************************************************
 * Cloud Functions
 ***************************************************************************/

/**
 * @brief Post new data to ubidots server
 *
 * @param device_label Label of the device to be set or update in the ubidots
 * platform
 * @param device_name Device name to be published
 * @param payload Body to be processed by the Ubidot server
 * @return true The data was succesfully published
 * @return false Something went wrong with the sending.
 */
bool UbiTCP::sendData(const char *device_label, const char *device_name, char *payload) {

  if (!_preConnectionChecks()) {
    return false;
  }

  _client_tcp->send(payload);

  /* Parses the host answer, returns true if it is 'Ok' */
  char *response = (char *)malloc(sizeof(char) * MAX_BUFFER_SIZE);

  float value = _parseTCPAnswer("POST", response);
  free(response);
  if (value != ERROR_VALUE) {
    _client_tcp->close();
    _client_tcp->disconnect();
    return true;
  }

  return false;
}

/**
 * @brief Get request over TCP to Ubidots server
 *
 * @param device_label device label of the device
 * @param variable_label variable label to be fetched
 * @return float Response value from the server
 */
float UbiTCP::get(const char *device_label, const char *variable_label) {

  if (!_preConnectionChecks()) {
    return ERROR_VALUE;
  }

  if (_debug) {
    Serial.println(F("waiting to fetch..."));
  }

  uint16_t endpointLength = _endpointLength(device_label, variable_label);

  char *endpoint = (char *)malloc(sizeof(char) * endpointLength + 1);

  sprintf(endpoint, "%s|LV|%s|%s:%s|end HTTP/1.0\r\n\r\n", USER_AGENT, _token, device_label, variable_label);

  _client_tcp->send(endpoint, endpointLength);

  char *response = (char *)malloc(sizeof(char) * MAX_BUFFER_SIZE);
  float value = _parseTCPAnswer("POST", response);

  _client_tcp->close();
  _client_tcp->disconnect();

  delay(200);

  free(endpoint);
  free(response);

  return value;
}

/**************************************************************************
 * Auxiliar
 ***************************************************************************/

/**
 * @brief Do the initialization of the device
 *
 * @return true The device is responding to the commands
 * @return false The device is not reponding, probably bad Serial communication
 */
bool UbiTCP::_initGPRS() {

  _guaranteePowerOn();

  uint8_t attempts = 0;
  bool flag = true;
  if (_debug) {
    Serial.print("Start TCP Connection...\r\n");
    Serial.print("Initializing module...\r\n");
  }

  // use DHCP
  while (!_client_tcp->init() && attempts < 5) {
    delay(1000);
    attempts += 1;
    if (attempts == 5) {
      flag = false;
      break;
    }

    if (_debug) {
      Serial.print("Checking Serial connection...\r\n");
    }
  }

  return flag;
}

/**
 * @brief Extablish the phisical connection with the GPRS network
 *
 * @return true The network is accepted by the device
 * @return false The device could connect to it, probably the frequency
 */
bool UbiTCP::_isNetworkRegistered() {

  uint8_t attempts = 0;
  bool flag = true;

  // use DHCP
  while (!_client_tcp->isNetworkRegistered() && attempts < 5) {
    delay(1000);
    attempts += 1;
    if (attempts == 5) {
      flag = false;
      break;
    }

    if (_debug) {
      Serial.println(F("Network has not been registered yet!"));
    }
  }

  if (_debug) {
    Serial.println(F("Network has been registered into the module"));
  }

  return flag;
}

/**
 * @brief The GPRS establish a new connection with the service
 * TODO cast the _apn, _apnUser, _apnPass to the join method.
 *
 */
bool UbiTCP::_isJoinedToNetwork() {

  uint8_t attempts = 0;
  bool flag = true;
  bool isJoined = false;

  // use DHCP
  while (!isJoined && attempts < 5) {
    isJoined = _client_tcp->join(_F(_apn), _F(_apnUser), _F(_apnPass));
    delay(2000);
    attempts += 1;
    if (attempts == 1) {
      if (_debug) {
        Serial.print(F("Network status: "));
        Serial.println(isJoined ? F("Joined") : F("Not Joined"));
        Serial.println(F("Simcard Credentials"));
        Serial.print(F("Apn: "));
        Serial.println(_apn);
        Serial.print(F("User: "));
        Serial.println(_apnUser);
        Serial.print(F("Pass: "));
        Serial.println(_apnPass);
        Serial.println();
      }
    }

    if (_debug) {
      Serial.print(F("Trying to join to the network ["));
      Serial.print(attempts);
      Serial.println(F("]"));
    }

    if (attempts == 5) {
      if (_debug) {
        Serial.println(F("[ERROR] Couldn't join the network\n\n"));
      }
      flag = false;
      break;
    }
  }

  if (_debug && flag) {
    Serial.print(F("Network status: "));
    Serial.println(isJoined ? F("Joined") : F("Not Joined"));
    // successful DHCP
    Serial.print(F("IP Address is "));
    Serial.println(_client_tcp->getIPAddress());

    if (!_checkIpAddress()) {
      Serial.println(F("IpAddress NOT VALID!"));
      _isJoinedToNetwork();
    }
  }

  return flag;
}

/**
 * @brief Check the Octecs from the IP Address to make a valid request for the
 * IMEI, it splits the IP into tokes by the "." sparator, if there are 4 so it
 * is ok
 *
 * @return true There a valid connection to internet
 * @return false There is NOT a valid connection to internet
 */
bool UbiTCP::_checkIpAddress() {
  char *ipAddress = _client_tcp->getIPAddress();
  char *separator = ".";
  char *octet;

  uint8_t octecNumber = 0;
  octet = strtok(ipAddress, separator);

  while (octet != NULL) {
    octecNumber += 1;
    octet = strtok(NULL, separator);
  }

  if (octecNumber == 4) {
    return true;
  } else {
    return false;
  }
}

/**
 * @brief Establish connection with Ubidots Server
 *
 * @return true Succesfull connection
 * @return false Failed connection
 */
bool UbiTCP::_connectToServer() {
  if (!_client_tcp->connect(TCP, UBI_INDUSTRIAL, UBIDOTS_TCP_PORT)) {
    if (_debug) {
      Serial.println(F("Error Connecting to Ubidots server"));
      return false;
    }
  } else {
    _server_connected = true;
    if (_debug) {
      Serial.println(F("Connection to Ubidots server success!"));
    }
    return true;
  }
}

/**
 * @brief Calculate the lenght of the enpoint to be send over TCP to the server
 *
 * @param device_label device label of the device
 * @param variable_label variable label to be updated or fetched
 * @return uint16_t  Lenght of the enpoint
 */
uint16_t UbiTCP::_endpointLength(const char *device_label, const char *variable_label) {
  uint16_t endpointLength = strlen("|LV||:|end HTTP/1.0\r\n\r\n") + strlen(USER_AGENT) + strlen(_token) +
                            strlen(device_label) + strlen(variable_label);
  return endpointLength;
}

/**
 * @brief Parse the TCP host answer and saves it to the input char pointer.

 * @param request_type Type of request GET or POST
 * @param response [Mandatory] Buffer to contain the payload from the server
 * @return float Value from the response
 */
float UbiTCP::_parseTCPAnswer(const char *request_type, char *response) {
  int j = 0;

  if (_debug) {
    Serial.println(F("----------"));
    Serial.println(F("Server's response:"));
  }

  while (true) {
    int ret = _client_tcp->recv(response, MAX_BUFFER_SIZE);
    if (ret <= 0) {
      if (_debug) {
        Serial.println(F("fetch over..."));
      }
      break;
    }
    response[ret] = '\0';
    if (_debug) {
      Serial.print(F("Recv: "));
      Serial.print(ret);
      Serial.print(F(" bytes: "));
      Serial.println(response);
    }
  }

  if (_debug) {
    Serial.println(F("\n----------"));
  }

  response[j] = '\0';
  float result = ERROR_VALUE;

  // POST
  if (strcmp(request_type, "POST") == 0) {
    char *pch = strstr(response, "OK");
    if (pch != NULL) {
      result = 1;
    }
    return result;
  }

  // LV
  char *pch = strchr(response, '|');
  if (pch != NULL) {
    result = atof(pch + 1);
  }

  return result;
}

/**
 * @brief Enable the debug
 *
 * @param debug Makes available debug traces
 */
void UbiTCP::setDebug(bool debug) { _debug = debug; }

/**
 * @brief Checks if the socket is still opened with the Ubidots Server through
 * the attribute
 *
 * @return true Server Connected
 * @return false Server Obviusly not connected
 */
bool UbiTCP::serverConnected() { return _server_connected; }

/**
 * @brief Handle the proper behavior of the SIM900 before fetching or sending
 * data to Ubidots
 *
 * @return true Everything was fine
 * @return false something went wrong.
 */
bool UbiTCP::_preConnectionChecks() {

  if (!_initGPRS()) {
    return false;
  }

  if (!_isNetworkRegistered()) {
    return false;
  }

  if (!_isJoinedToNetwork()) {
    return false;
  }

  if (!_connectToServer()) {
    return false;
  }

  return true;
}

/**
 * @brief Check if the module is already powered on, if not then it tries to
 * power it forever.
 *
 */
void UbiTCP::_guaranteePowerOn() {

  if (_debug) {
    Serial.println(F("Cheking Power status SIM900 module"));
  }

  if (!_client_tcp->checkPowerUp()) {
    pinMode(SIM900_POWER_UP_PIN, OUTPUT);
    if (_debug) {
      Serial.println(F("Turning on the SIM900 Module"));
    }
    _client_tcp->powerUpDown();
    delay(2000);
    _guaranteePowerOn();
  } else {
    if (_debug) {
      Serial.println(F("SIM900 status: Powered On"));
    }
  }
}
<commit_msg>Adding more fixes<commit_after>/*
Copyright (c) 2013-2018 Ubidots.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Developed and maintained by Jose Garcia and Cristian Arrieta for IoT Services
Inc
@jotathebest at github: https://github.com/jotathebest
@crisap94 at github: https://github.com/crisap94
*/

#include "UbiTcp.h"

/**************************************************************************
 * Overloaded constructors
 ***************************************************************************/

/**
 * @brief Construct a new UbiTCP object and power up the module
 *
 * @param token Users token
 * @param server Server addres
 * @param port Server port
 * @param user_agent Reference from the device being connected
 * @param apn GPRS network name
 * @param apnUser GPRS network username
 * @param apnPass GPRS network passwrod
 */
UbiTCP::UbiTCP(UbiToken token, UbiServer server, const int port, const char *user_agent, UbiApn apn, UbiApn apnUser,
               UbiApn apnPass) {

  _server = server;
  _user_agent = user_agent;
  _token = token;
  _port = port;
  _apn = apn;
  _apnUser = apnUser;
  _apnPass = apnPass;
  _client_tcp = GPRS::getInstance() == NULL ? new GPRS(RX, TX, BAUDRATE) : GPRS::getInstance();
}

/**************************************************************************
 * Destructor
 ***************************************************************************/

UbiTCP::~UbiTCP() {
  delete[] _server;
  delete[] _user_agent;
  delete[] _token;
  delete[] _apn;
  delete[] _apnUser;
  delete[] _apnPass;

  free(_client_tcp);
}

/**************************************************************************
 * Cloud Functions
 ***************************************************************************/

/**
 * @brief Post new data to ubidots server
 *
 * @param device_label Label of the device to be set or update in the ubidots
 * platform
 * @param device_name Device name to be published
 * @param payload Body to be processed by the Ubidot server
 * @return true The data was succesfully published
 * @return false Something went wrong with the sending.
 */
bool UbiTCP::sendData(const char *device_label, const char *device_name, char *payload) {
  if (_debug) {
    Serial.print("Start TCP Connection...\r\n");
  }

  if (!_preConnectionChecks()) {
    return false;
  }

  _client_tcp->send(payload);

  /* Parses the host answer, returns true if it is 'Ok' */
  char *response = (char *)malloc(sizeof(char) * MAX_BUFFER_SIZE);

  float value = _parseTCPAnswer("POST", response);
  free(response);
  if (value != ERROR_VALUE) {
    _client_tcp->close();
    _client_tcp->disconnect();
    return true;
  }

  return false;
}

/**
 * @brief Get request over TCP to Ubidots server
 *
 * @param device_label device label of the device
 * @param variable_label variable label to be fetched
 * @return float Response value from the server
 */
float UbiTCP::get(const char *device_label, const char *variable_label) {
  if (_debug) {
    Serial.print("Start TCP Connection...\r\n");
  }

  if (!_preConnectionChecks()) {
    return ERROR_VALUE;
  }

  if (_debug) {
    Serial.println(F("waiting to fetch..."));
  }

  uint16_t endpointLength = _endpointLength(device_label, variable_label);

  char *endpoint = (char *)malloc(sizeof(char) * endpointLength + 1);

  sprintf(endpoint, "%s|LV|%s|%s:%s|end HTTP/1.0\r\n\r\n", USER_AGENT, _token, device_label, variable_label);

  _client_tcp->send(endpoint, endpointLength);
  free(endpoint);

  char *response = (char *)malloc(sizeof(char) * MAX_BUFFER_SIZE);
  float value = _parseTCPAnswer("POST", response);

  free(response);

  _client_tcp->close();
  _client_tcp->disconnect();

  delay(200);

  return value;
}

/**************************************************************************
 * Auxiliar
 ***************************************************************************/

/**
 * @brief Do the initialization of the device
 *
 * @return true The device is responding to the commands
 * @return false The device is not reponding, probably bad Serial communication
 */
bool UbiTCP::_initGPRS() {

  if (!isInitiatedModule) {
    uint8_t attempts = 0;
    isInitiatedModule = true;
    if (_debug) {
      Serial.print(F("Initializing module...\r\n"));
    }

    // use DHCP
    while (!_client_tcp->init() && attempts < 5) {
      delay(500);
      attempts += 1;
      if (attempts == 5) {
        isInitiatedModule = false;
      }
      if (_debug) {
        Serial.print(F("Checking Serial connection ["));
        Serial.print(attempts);
        Serial.println(F("]"));
      }
    }
  }

  return isInitiatedModule;
}

/**
 * @brief Extablish the phisical connection with the GPRS network
 *
 * @return true The network is accepted by the device
 * @return false The device could connect to it, probably the frequency
 */
bool UbiTCP::_isNetworkRegistered() {
  if (!isNetwowrkRegistered) {
    if (_debug) {
      Serial.println(F("Registering Network into the module"));
    }
    uint8_t attempts = 0;
    isNetwowrkRegistered = true;

    // use DHCP
    while (!_client_tcp->isNetworkRegistered() && attempts < 5) {
      delay(500);
      yield();
      attempts += 1;
      if (attempts == 5) {
        isNetwowrkRegistered = false;
      }

      if (_debug) {
        Serial.print(F("Trying to register the network ["));
        Serial.print(attempts);
        Serial.println(F("]"));
      }
    }

    if (_debug && !isNetwowrkRegistered) {
      Serial.println(F("[ERROR] Couldn't register into the network\n\n"));
    }

    if (_debug && isNetwowrkRegistered) {
      Serial.println(F("Network has been registered into the module"));
    }
  }

  return isNetwowrkRegistered;
}

/**
 * @brief The GPRS establish a new connection with the service
 * TODO cast the _apn, _apnUser, _apnPass to the join method.
 *
 */
bool UbiTCP::_isJoinedToNetwork() {

  uint8_t attempts = 0;
  bool flag = true;
  bool isJoined = false;

  // use DHCP
  while (!isJoined && attempts < 5) {
    isJoined = _client_tcp->join(_F(_apn), _F(_apnUser), _F(_apnPass));
    delay(2000);
    attempts += 1;
    if (attempts == 1) {
      if (_debug) {
        Serial.print(F("Network status: "));
        Serial.println(isJoined ? F("Joined") : F("Not Joined"));
        Serial.println(F("Simcard Credentials"));
        Serial.print(F("Apn: "));
        Serial.println(_apn);
        Serial.print(F("User: "));
        Serial.println(_apnUser);
        Serial.print(F("Pass: "));
        Serial.println(_apnPass);
        Serial.println();
      }
    }

    if (_debug) {
      Serial.print(F("Trying to join to the network ["));
      Serial.print(attempts);
      Serial.println(F("]"));
    }

    if (attempts == 5) {
      if (_debug) {
        Serial.println(F("[ERROR] Couldn't join the network\n\n"));
      }
      flag = false;
      break;
    }
  }

  if (_debug && flag) {
    Serial.print(F("Network status: "));
    Serial.println(isJoined ? F("Joined") : F("Not Joined"));
    // successful DHCP
    Serial.print(F("IP Address is "));
    Serial.println(_client_tcp->getIPAddress());

    if (!_checkIpAddress()) {
      Serial.println(F("IpAddress NOT VALID!"));
      _isJoinedToNetwork();
    }
  }

  return flag;
}

/**
 * @brief Check the Octecs from the IP Address to make a valid request for the
 * IMEI, it splits the IP into tokes by the "." sparator, if there are 4 so it
 * is ok
 *
 * @return true There a valid connection to internet
 * @return false There is NOT a valid connection to internet
 */
bool UbiTCP::_checkIpAddress() {
  char *ipAddress = _client_tcp->getIPAddress();
  char *separator = ".";
  char *octet;

  uint8_t octecNumber = 0;
  octet = strtok(ipAddress, separator);

  while (octet != NULL) {
    octecNumber += 1;
    octet = strtok(NULL, separator);
  }

  if (octecNumber == 4) {
    return true;
  } else {
    return false;
  }
}

/**
 * @brief Establish connection with Ubidots Server
 *
 * @return true Succesfull connection
 * @return false Failed connection
 */
bool UbiTCP::_connectToServer() {
  if (!_client_tcp->connect(TCP, UBI_INDUSTRIAL, UBIDOTS_TCP_PORT)) {
    if (_debug) {
      Serial.println(F("Error Connecting to Ubidots server"));
      _server_connected = false;
      return false;
    }
  } else {
    _server_connected = true;
    if (_debug) {
      Serial.println(F("Connection to Ubidots server success!"));
    }
    return true;
  }
}

/**
 * @brief Calculate the lenght of the enpoint to be send over TCP to the server
 *
 * @param device_label device label of the device
 * @param variable_label variable label to be updated or fetched
 * @return uint16_t  Lenght of the enpoint
 */
uint16_t UbiTCP::_endpointLength(const char *device_label, const char *variable_label) {
  uint16_t endpointLength = strlen("|LV||:|end HTTP/1.0\r\n\r\n") + strlen(USER_AGENT) + strlen(_token) +
                            strlen(device_label) + strlen(variable_label);
  return endpointLength;
}

/**
 * @brief Parse the TCP host answer and saves it to the input char pointer.

 * @param request_type Type of request GET or POST
 * @param response [Mandatory] Buffer to contain the payload from the server
 * @return float Value from the response
 */
float UbiTCP::_parseTCPAnswer(const char *request_type, char *response) {
  int j = 0;

  if (_debug) {
    Serial.println(F("----------"));
    Serial.println(F("Server's response:"));
  }

  while (true) {
    int ret = _client_tcp->recv(response, MAX_BUFFER_SIZE);
    if (ret <= 0) {
      if (_debug) {
        Serial.println(F("fetch over..."));
      }
      break;
    }
    response[ret] = '\0';
    if (_debug) {
      Serial.print(F("Recv: "));
      Serial.print(ret);
      Serial.print(F(" bytes: "));
      Serial.println(response);
    }
  }

  if (_debug) {
    Serial.println(F("\n----------"));
  }

  response[j] = '\0';
  float result = ERROR_VALUE;

  // POST
  if (strcmp(request_type, "POST") == 0) {
    char *pch = strstr(response, "OK");
    if (pch != NULL) {
      result = 1;
    }
    return result;
  }

  // LV
  char *pch = strchr(response, '|');
  if (pch != NULL) {
    result = atof(pch + 1);
  }

  return result;
}

/**
 * @brief Enable the debug
 *
 * @param debug Makes available debug traces
 */
void UbiTCP::setDebug(bool debug) { _debug = debug; }

/**
 * @brief Checks if the socket is still opened with the Ubidots Server through
 * the attribute
 *
 * @return true Server Connected
 * @return false Server Obviusly not connected
 */
bool UbiTCP::serverConnected() { return _server_connected; }

/**
 * @brief Handle the proper behavior of the SIM900 before fetching or sending
 * data to Ubidots
 *
 * @return true Everything was fine
 * @return false something went wrong.
 */
bool UbiTCP::_preConnectionChecks() {

  if (_guaranteePowerOn()) {
    return false;
  }

  if (!_initGPRS()) {
    return false;
  }

  if (!_isNetworkRegistered()) {
    return false;
  }

  if (!_isJoinedToNetwork()) {
    return false;
  }

  if (!_connectToServer()) {
    return false;
  }

  return true;
}

/**
 * @brief Check if the module is already powered on, if not then it tries to
 * power it forever.
 *
 */
bool UbiTCP::_guaranteePowerOn() {

  if (!isPoweredOn) {
    if (_debug) {
      Serial.println(F("Cheking Power status SIM900 module"));
    }
    if (!_client_tcp->checkPowerUp()) {
      pinMode(SIM900_POWER_UP_PIN, OUTPUT);
      if (_debug) {
        Serial.println(F("Turning on the SIM900 Module"));
      }
      _client_tcp->powerUpDown();
      delay(2000);
      _guaranteePowerOn();
    } else {
      isPoweredOn = true;
      if (_debug) {
        Serial.println(F("SIM900 status: Powered On"));
      }
    }
  }
}
<|endoftext|>


<commit_before>/**
 * @file visibility
 */

#include "visibility.h"
#include "forecast_time.h"
#include "level.h"
#include "logger.h"
#include "plugin_factory.h"
#include "util.h"
#include <boost/lexical_cast.hpp>
#include <cmath>

#include "fetcher.h"
#include "hitool.h"
#include "neons.h"

using namespace std;
using namespace himan;
using namespace himan::plugin;

const double defaultVis = 40000;

// Lumi- tai räntäsateen intensiteetin "tehostus" [mm/h]
// Tällä siis tarkoitus saada mallin heikotkin lumisadeintensiteetit huonontamaan näkyvyyttä
const double pseudoRR = 0.13;

// Raja-arvo merkittävälle sumupilven määrälle [0..1]
const double stLimit = 0.55;

// Sumupilven max korkeus [m], 305m = 1000ft
const double stMaxH = 305.;

const himan::params PFParams({himan::param("PRECFORM2-N"), himan::param("PRECFORM-N")});
const himan::params RHParam({himan::param("RH-PRCNT"), himan::param("RH-0TO1")});
const himan::param RRParam(himan::param("RRR-KGM2"));
const himan::params NParam({himan::param("N-PRCNT"), himan::param("N-0TO1")});

// ..and their levels
const himan::level NLevel(himan::kHeight, 0, "HEIGHT");
const himan::level RHLevel(himan::kHeight, 2, "HEIGHT");

double VisibilityInRain(double stN, double stH, double RR, double RH, int PF);
double VisibilityInMist(double stN, double stH, double RR, double RH);

visibility::visibility()
{
	itsClearTextFormula = "<algorithm>";
	itsLogger = logger("visibility");
}

void visibility::Process(std::shared_ptr<const plugin_configuration> conf)
{
	Init(conf);

	SetParams({param("VV2-M", 407)});

	Start();
}

/*
 * Calculate()
 *
 * This function does the actual calculation.
 */

void visibility::Calculate(shared_ptr<info> myTargetInfo, unsigned short threadIndex)
{
	auto myThreadedLogger = logger("visibilityThread #" + boost::lexical_cast<string>(threadIndex));

	forecast_time forecastTime = myTargetInfo->Time();
	level forecastLevel = myTargetInfo->Level();
	forecast_type forecastType = myTargetInfo->ForecastType();

	myThreadedLogger.Info("Calculating time " + static_cast<string>(forecastTime.ValidDateTime()) + " level " +
	                      static_cast<string>(forecastLevel));

	info_t RHInfo = Fetch(forecastTime, RHLevel, RHParam, forecastType, false);
	info_t PFInfo = Fetch(forecastTime, NLevel, PFParams, forecastType, false);
	info_t RRInfo = Fetch(forecastTime, NLevel, RRParam, forecastType, false);

	if (!RRInfo || !RHInfo || !PFInfo)
	{
		myThreadedLogger.Warning("Skipping step " + boost::lexical_cast<string>(forecastTime.Step()) + ", level " +
		                         static_cast<string>(forecastLevel));
		return;
	}

	double RHScale = 1;

	if (itsConfiguration->SourceProducer().Id() == 1 || itsConfiguration->SourceProducer().Id() == 199 ||
	    itsConfiguration->SourceProducer().Id() == 4)
	{
		RHScale = 100;
	}

	auto h = GET_PLUGIN(hitool);

	h->Configuration(itsConfiguration);
	h->Time(myTargetInfo->Time());
	h->ForecastType(myTargetInfo->ForecastType());

	// Alle 304m (1000ft) sumupilven (max) määrä [0..1]
	auto stN = h->VerticalMaximum(NParam, 0, stMaxH);

	// Stratus <15m (~0-50ft)
	auto st15 = h->VerticalAverage(NParam, 0, 15);

	// Stratus 15-45m (~50-150ft)
	auto st45 = h->VerticalAverage(NParam, 15, 45);

	// Sumupilven korkeus [m]
	auto stH = h->VerticalHeightGreaterThan(NParam, 0, stMaxH, stLimit);

	// Jos sumupilveä ohut kerros (vain) ~alimmalla mallipinnalla, jätetään alin kerros huomioimatta
	// (ehkä mieluummin ylempää keskiarvo, jottei tällöin mahdollinen ylempi st-kerros huononna näkyvyyttä liikaa?)
	auto stHup = h->VerticalHeightGreaterThan(NParam, 25, stMaxH, stLimit);
	auto stNup = h->VerticalAverage(NParam, 15, stMaxH);

	assert(stH.size() == stHup.size());
	assert(st15.size() == stH.size());

	for (size_t i = 0; i < stH.size(); i++)
	{
		if (st15[i] > stLimit && st45[i] < stLimit)
		{
			stN[i] = stNup[i];
			stH[i] = stHup[i];
		}
	}

	string deviceType = "CPU";

	auto& target = VEC(myTargetInfo);

	for (auto&& tup : zip_range(target, VEC(PFInfo), VEC(RRInfo), VEC(RHInfo), stN, stH))
	{
		double& result = tup.get<0>();
		double PF = tup.get<1>();
		double RR = tup.get<2>();
		double RH = tup.get<3>();
		double stratN = tup.get<4>();
		double stratH = tup.get<5>();

		if (RR == kFloatMissing || RH == kFloatMissing)
		{
			continue;
		}

		assert(stratN <= 1.0);
		assert(RR < 50);

		stratN *= 100;  // Note! Cloudiness is scaled to percents

		RH *= RHScale;

		assert(RH < 102.);

		double visPre = defaultVis;

		if (RR > 0)
		{
			visPre = VisibilityInRain(stratN, stratH, RR, RH, PF);
		}

		double visMist = VisibilityInMist(stratN, stratH, RR, RH);

		// Choose lower visibility to be the end result

		result = fmin(visMist, visPre);
	}

	myThreadedLogger.Info("[" + deviceType +
	                      "] Missing values: " + boost::lexical_cast<string>(myTargetInfo->Data().MissingCount()) +
	                      "/" + boost::lexical_cast<string>(myTargetInfo->Data().Size()));
}

double VisibilityInRain(double stN, double stH, double RR, double RH, int PF)
{
	double visPre = defaultVis;

	// Näkyvyyden utuisuuskerroin sateessa 2m suhteellisen kosteuden perusteella [0,85...8,5, kun 100%<rh<10%]
	// (jos rh<85%, näkyvyyttä parannetaan)

	const double RHpre = 85. / RH;

	// Näkyvyyden utuisuuskerroin sateessa matalan sumupilven (alle 1000ft) määrän perusteella [1...0,85, kun
	// 50<N<100%]
	// Alle 50% sumupilven määrä ei siis huononna näkyvyyttä sateessa

	const double stNpre = (stN >= 50) ? log(50) / log(stN) : 1;

	// Näkyvyyden utuisuuskerroin sateessa matalan sumupilvikorkeuden perusteella [0,68...1, kun stH=12...152m]
	// Yli 152m (500ft) korkeudella oleva sumupilvi ei siis huononna näkyvyyttä sateessa

	const double stHpre = (stH != kFloatMissing && stH < 152) ? pow((stH / 152), 0.15) : 1;

	assert(PF != kFloatMissing);

	switch (PF)
	{
		case 0:
		case 4:
			// Tihku (tai jäätävä tihku)
			// Nakyvyys intensiteetin perusteella
			visPre = 1. / RR * 500;
			break;

		case 1:
		case 5:
			// Vesisade (tai jäätävä vesisade)
			// Nakyvyys intensiteetin perusteella
			// (kaava antaa ehkä turhan huonoja <4000m näkyvyyksiä reippaassa RR>4 vesisateessa)
			visPre = 1 / RR * 6000 + 2000;
			break;

		case 3:
			// Snow
			// Näkyvyys intensiteetin perusteella
			visPre = 1 / (RR + pseudoRR) * 1400;
			break;

		case 2:
			// Sleet
			// Näkyvyys intensiteetin perusteella
			visPre = 1 / (RR + pseudoRR) * 2000;
			break;
		default:
			throw runtime_error("New unhandled precipitation form value: " + to_string(PF));
	}

	// Mahdollinen lisähuononnus utuisuuden perusteella
	visPre = visPre * RHpre * stNpre * stHpre;

	return fmin(visPre, defaultVis);
}

double VisibilityInMist(double stN, double stH, double RR, double RH)
{
	double visMist = defaultVis;

	// Näkyvyyden utuisuuskerroin udussa/sumussa sumupilven määrän perusteella [7,5...0,68, kun stN = 0...100%]
	const double stNmist = 75. / (stN + 10);

	// Nakyvyyden utuisuuskerroin udussa/sumussa sumupilvikorkeuden perusteella [ 0,47...1, kun par500 = 50...999ft]
	const double stHmist = (stH != kFloatMissing && stH < 305) ? pow((stH / 0.3048 / 1000), 0.25) : 1;

	// Nakyvyys udussa/sumussa, lasketaan myos heikossa sateessa (tarkoituksena tasoittaa suuria
	// nakyvyysgradientteja sateen reunalla)
	// (ehka syyta rajata vain tilanteisiin, jossa sateen perusteella saatu nakyvyys oli viela >8000?)

	if ((RR < 0.5 || RR == kFloatMissing) && RH > 80)
	{
		// Näkyvyys udussa/sumussa
		// Yksinkertaistetty kaava, eli lasketaan samalla tavalla riippumatta siitä, onko pakkasta vai ei
		// (pakkaset eri tavalla voisi olla parempi tapa)

		// Alkuarvo suht. kosteuden perusteella [700-31400m, kun 100<=RH<80]
		visMist = pow((101 - RH), 1.25) * 700;
		// Lisamuokkaus sumupilven maaran ja korkeuden perusteella
		visMist = visMist * stNmist * stHmist;
	}

	return fmin(visMist, defaultVis);
}
<commit_msg>Fix problem where visibility crashed because precipitation was found but form was missing.<commit_after>/**
 * @file visibility
 */

#include "visibility.h"
#include "forecast_time.h"
#include "level.h"
#include "logger.h"
#include "plugin_factory.h"
#include "util.h"

#include "fetcher.h"
#include "hitool.h"

using namespace std;
using namespace himan;
using namespace himan::plugin;

const double defaultVis = 40000;

// Lumi- tai räntäsateen intensiteetin "tehostus" [mm/h]
// Tällä siis tarkoitus saada mallin heikotkin lumisadeintensiteetit huonontamaan näkyvyyttä
const double pseudoRR = 0.13;

// Raja-arvo merkittävälle sumupilven määrälle [0..1]
const double stLimit = 0.55;

// Sumupilven max korkeus [m], 305m = 1000ft
const double stMaxH = 305.;

const himan::params PFParams({himan::param("PRECFORM2-N"), himan::param("PRECFORM-N")});
const himan::params RHParam({himan::param("RH-PRCNT"), himan::param("RH-0TO1")});
const himan::params RRParam({himan::param("RR-1-MM"), himan::param("RRR-KGM2")});
const himan::params NParam({himan::param("N-PRCNT"), himan::param("N-0TO1")});

// ..and their levels
const himan::level NLevel(himan::kHeight, 0, "HEIGHT");
const himan::level RHLevel(himan::kHeight, 2, "HEIGHT");

double VisibilityInRain(double stN, double stH, double RR, double RH, int PF);
double VisibilityInMist(double stN, double stH, double RR, double RH);

visibility::visibility()
{
	itsClearTextFormula = "<algorithm>";
	itsLogger = logger("visibility");
}

void visibility::Process(std::shared_ptr<const plugin_configuration> conf)
{
	Init(conf);

	SetParams({param("VV2-M", 407)});

	Start();
}

/*
 * Calculate()
 *
 * This function does the actual calculation.
 */

void visibility::Calculate(shared_ptr<info> myTargetInfo, unsigned short threadIndex)
{
	auto myThreadedLogger = logger("visibilityThread #" + to_string(threadIndex));

	forecast_time forecastTime = myTargetInfo->Time();
	level forecastLevel = myTargetInfo->Level();
	forecast_type forecastType = myTargetInfo->ForecastType();

	myThreadedLogger.Info("Calculating time " + static_cast<string>(forecastTime.ValidDateTime()) + " level " +
	                      static_cast<string>(forecastLevel));

	info_t RHInfo = Fetch(forecastTime, RHLevel, RHParam, forecastType, false);
	info_t PFInfo = Fetch(forecastTime, NLevel, PFParams, forecastType, false);
	info_t RRInfo = Fetch(forecastTime, NLevel, RRParam, forecastType, false);

	if (!RRInfo || !RHInfo || !PFInfo)
	{
		myThreadedLogger.Warning("Skipping step " + to_string(forecastTime.Step()) + ", level " +
		                         static_cast<string>(forecastLevel));
		return;
	}

	double RHScale = 1;

	if (itsConfiguration->SourceProducer().Id() == 1 || itsConfiguration->SourceProducer().Id() == 199 ||
	    itsConfiguration->SourceProducer().Id() == 4)
	{
		RHScale = 100;
	}

	auto h = GET_PLUGIN(hitool);

	h->Configuration(itsConfiguration);
	h->Time(myTargetInfo->Time());
	h->ForecastType(myTargetInfo->ForecastType());

	// Alle 304m (1000ft) sumupilven (max) määrä [0..1]
	auto stN = h->VerticalMaximum(NParam, 0, stMaxH);

	// Stratus <15m (~0-50ft)
	auto st15 = h->VerticalAverage(NParam, 0, 15);

	// Stratus 15-45m (~50-150ft)
	auto st45 = h->VerticalAverage(NParam, 15, 45);

	// Sumupilven korkeus [m]
	auto stH = h->VerticalHeightGreaterThan(NParam, 0, stMaxH, stLimit);

	// Jos sumupilveä ohut kerros (vain) ~alimmalla mallipinnalla, jätetään alin kerros huomioimatta
	// (ehkä mieluummin ylempää keskiarvo, jottei tällöin mahdollinen ylempi st-kerros huononna näkyvyyttä liikaa?)
	auto stHup = h->VerticalHeightGreaterThan(NParam, 25, stMaxH, stLimit);
	auto stNup = h->VerticalAverage(NParam, 15, stMaxH);

	assert(stH.size() == stHup.size());
	assert(st15.size() == stH.size());

	for (size_t i = 0; i < stH.size(); i++)
	{
		if (st15[i] > stLimit && st45[i] < stLimit)
		{
			stN[i] = stNup[i];
			stH[i] = stHup[i];
		}
	}

	string deviceType = "CPU";

	auto& target = VEC(myTargetInfo);

	for (auto&& tup : zip_range(target, VEC(PFInfo), VEC(RRInfo), VEC(RHInfo), stN, stH))
	{
		double& result = tup.get<0>();
		double PF = tup.get<1>();
		double RR = tup.get<2>();
		double RH = tup.get<3>();
		double stratN = tup.get<4>();
		double stratH = tup.get<5>();

		if (RR == kFloatMissing || RH == kFloatMissing)
		{
			continue;
		}

		assert(stratN <= 1.0);
		assert(RR < 50);

		stratN *= 100;  // Note! Cloudiness is scaled to percents

		RH *= RHScale;

		assert(RH < 102.);

		double visPre = defaultVis;

		if (RR > 0)
		{
			visPre = VisibilityInRain(stratN, stratH, RR, RH, static_cast<int>(PF));
		}

		double visMist = VisibilityInMist(stratN, stratH, RR, RH);

		// Choose lower visibility to be the end result

		result = fmin(visMist, visPre);
	}

	myThreadedLogger.Info("[" + deviceType + "] Missing values: " + to_string(myTargetInfo->Data().MissingCount()) +
	                      "/" + to_string(myTargetInfo->Data().Size()));
}

double VisibilityInRain(double stN, double stH, double RR, double RH, int PF)
{
	double visPre = defaultVis;

	// Näkyvyyden utuisuuskerroin sateessa 2m suhteellisen kosteuden perusteella [0,85...8,5, kun 100%<rh<10%]
	// (jos rh<85%, näkyvyyttä parannetaan)

	const double RHpre = 85. / RH;

	// Näkyvyyden utuisuuskerroin sateessa matalan sumupilven (alle 1000ft) määrän perusteella [1...0,85, kun
	// 50<N<100%]
	// Alle 50% sumupilven määrä ei siis huononna näkyvyyttä sateessa

	const double stNpre = (stN >= 50) ? log(50) / log(stN) : 1;

	// Näkyvyyden utuisuuskerroin sateessa matalan sumupilvikorkeuden perusteella [0,68...1, kun stH=12...152m]
	// Yli 152m (500ft) korkeudella oleva sumupilvi ei siis huononna näkyvyyttä sateessa

	const double stHpre = (stH != kFloatMissing && stH < 152) ? pow((stH / 152), 0.15) : 1;

	assert(PF != kFloatMissing);

	switch (PF)
	{
		case 0:
		case 4:
			// Tihku (tai jäätävä tihku)
			// Nakyvyys intensiteetin perusteella
			visPre = 1. / RR * 500;
			break;

		case 1:
		case 5:
			// Vesisade (tai jäätävä vesisade)
			// Nakyvyys intensiteetin perusteella
			// (kaava antaa ehkä turhan huonoja <4000m näkyvyyksiä reippaassa RR>4 vesisateessa)
			visPre = 1 / RR * 6000 + 2000;
			break;

		case 3:
			// Snow
			// Näkyvyys intensiteetin perusteella
			visPre = 1 / (RR + pseudoRR) * 1400;
			break;

		case 2:
			// Sleet
			// Näkyvyys intensiteetin perusteella
			visPre = 1 / (RR + pseudoRR) * 2000;
			break;
		default:
			throw runtime_error("New unhandled precipitation form value: " + to_string(PF));
	}

	// Mahdollinen lisähuononnus utuisuuden perusteella
	visPre = visPre * RHpre * stNpre * stHpre;

	return fmin(visPre, defaultVis);
}

double VisibilityInMist(double stN, double stH, double RR, double RH)
{
	double visMist = defaultVis;

	// Näkyvyyden utuisuuskerroin udussa/sumussa sumupilven määrän perusteella [7,5...0,68, kun stN = 0...100%]
	const double stNmist = 75. / (stN + 10);

	// Nakyvyyden utuisuuskerroin udussa/sumussa sumupilvikorkeuden perusteella [ 0,47...1, kun par500 = 50...999ft]
	const double stHmist = (stH != kFloatMissing && stH < 305) ? pow((stH / 0.3048 / 1000), 0.25) : 1;

	// Nakyvyys udussa/sumussa, lasketaan myos heikossa sateessa (tarkoituksena tasoittaa suuria
	// nakyvyysgradientteja sateen reunalla)
	// (ehka syyta rajata vain tilanteisiin, jossa sateen perusteella saatu nakyvyys oli viela >8000?)

	if ((RR < 0.5 || RR == kFloatMissing) && RH > 80)
	{
		// Näkyvyys udussa/sumussa
		// Yksinkertaistetty kaava, eli lasketaan samalla tavalla riippumatta siitä, onko pakkasta vai ei
		// (pakkaset eri tavalla voisi olla parempi tapa)

		// Alkuarvo suht. kosteuden perusteella [700-31400m, kun 100<=RH<80]
		visMist = pow((101 - RH), 1.25) * 700;
		// Lisamuokkaus sumupilven maaran ja korkeuden perusteella
		visMist = visMist * stNmist * stHmist;
	}

	return fmin(visMist, defaultVis);
}
<|endoftext|>


<commit_before><commit_msg>Hrm, it's hard explaining, and indeed thinking about what's going on in here :-) Think this is a better representation.<commit_after><|endoftext|>


<commit_before>// $Id$

#include <base/logstream.h>
#include <lac/solver_control.h>

/*----------------------- SolverControl ---------------------------------*/

SolverControl::SolverControl (const unsigned int maxiter,
			      const double tolerance,
			      const bool log_history,
			      const bool log_result)
		:
		maxsteps(maxiter),
		tol(tolerance),
		lvalue(1.e300),
		lstep(0),
		log_history(log_history),
		log_result(log_result)
{};

SolverControl::~SolverControl()
{};

SolverControl::State
SolverControl::check (const unsigned int step,
		      const double check_value)
{
  if (log_history)
    deallog << "Check " << step << "\t" << check_value << endl;

  lstep  = step;
  lvalue = check_value;
  if (step>=maxsteps)
    {
      if (log_result)
	deallog << "Failure step " << step
		<< " value " << check_value << endl;
      return failure;
    }
  if (check_value <= tol)
    {
      if (log_result)
	deallog << "Convergence step " << step
		<< " value " << check_value << endl;
      return success;
    }
  return iterate;
};

double
SolverControl::last_value() const
{
  return lvalue;
};

unsigned int
SolverControl::last_step() const
{
  return lstep;
};

/*----------------------- ReductionControl ---------------------------------*/

ReductionControl::ReductionControl(const unsigned int n,
				   const double tol,
				   const double red,
				   const bool log_history,
				   const bool log_result)
		:
		SolverControl (n, tol, log_history, log_result),
		reduce(red)
{};

ReductionControl::~ReductionControl()
{};

double
ReductionControl::initial_value() const {
  return initial_val;
};

SolverControl::State
ReductionControl::check (const unsigned int step,
			 const double check_value)
{
  if (step==0)
    {
      initial_val = check_value;
      reduced_tol = check_value * reduce;
    };

  if (check_value < reduced_tol)
    {
      if (log_result)
	deallog << "Convergence step " << step
		<< " value " << check_value << endl;
      return success;
    }
  else
    return SolverControl::check(step, check_value);
};

<commit_msg>Output of starting residual<commit_after>// $Id$

#include <base/logstream.h>
#include <lac/solver_control.h>

/*----------------------- SolverControl ---------------------------------*/

SolverControl::SolverControl (const unsigned int maxiter,
			      const double tolerance,
			      const bool log_history,
			      const bool log_result)
		:
		maxsteps(maxiter),
		tol(tolerance),
		lvalue(1.e300),
		lstep(0),
		log_history(log_history),
		log_result(log_result)
{};

SolverControl::~SolverControl()
{};

SolverControl::State
SolverControl::check (const unsigned int step,
		      const double check_value)
{
  if (log_history)
    deallog << "Check " << step << "\t" << check_value << endl;

  lstep  = step;
  lvalue = check_value;

  if ((step==0) && log_result)
    {
      deallog << "Starting value " << check_value << endl;
    }

  if (step>=maxsteps)
    {
      if (log_result)
	deallog << "Failure step " << step
		<< " value " << check_value << endl;
      return failure;
    }
  if (check_value <= tol)
    {
      if (log_result)
	deallog << "Convergence step " << step
		<< " value " << check_value << endl;
      return success;
    }
  return iterate;
};

double
SolverControl::last_value() const
{
  return lvalue;
};

unsigned int
SolverControl::last_step() const
{
  return lstep;
};

/*----------------------- ReductionControl ---------------------------------*/

ReductionControl::ReductionControl(const unsigned int n,
				   const double tol,
				   const double red,
				   const bool log_history,
				   const bool log_result)
		:
		SolverControl (n, tol, log_history, log_result),
		reduce(red)
{};

ReductionControl::~ReductionControl()
{};

double
ReductionControl::initial_value() const {
  return initial_val;
};

SolverControl::State
ReductionControl::check (const unsigned int step,
			 const double check_value)
{
  if (step==0)
    {
      initial_val = check_value;
      reduced_tol = check_value * reduce;
    };

  if (check_value < reduced_tol)
    {
      if (log_result)
	deallog << "Convergence step " << step
		<< " value " << check_value << endl;
      return success;
    }
  else
    return SolverControl::check(step, check_value);
};

<|endoftext|>


<commit_before>/*
 *  This file is part of PlantLight application.
 *
 * 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.
 *
 */

/*
 * Built for ATMega328P 1Mhz, using AVR Pololu programmer.
 * VERSION 2.0b004
 */

#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#include <Arduino.h>
#include <Timezone.h>
#include <BH1750FVI.h>
#include <DS3232RTC.h>

#include "PowerUtils.h"
#include "TimeUtils.h"

#define SERIAL_BAUD          9600       // Serial baud per second

// Pins
#define RTC_INT_SQW             4       // INT/SQW pin from RTC
#define RELAY_SW_OUT            5       // Relay out pin

#define ENABLE_H               16       // Enable hour (default)
#define DISABLE_H              23       // Disable hour (default)
#define ENA_DIS_MIN             0       // Enable and disable minute (default)

#define POLLING_INT             3       // Polling interval (default)       [s]
#define SAMPLE_COUNT           10       // Light sample count (average measurement)
#define MAX_SAMPLE_COUNT       50       // Max light sample count
#define LUX_TH                 10       // Lux threshold
#define LUX_TH_HIST             5       // Lux threshold (hysteresis compensation)

//#define RTC_CONTROL 0x0E                // TODO remove
//#define RTC_STATUS 0x0F

// ################################# Constants ################################

// ################################# Variables ################################

TwoWire bus;                            // TinyWireM instance (I2C bus)
//BH1750FVI BH1750(bus);                  // Light sensor instance
DS3232RTC RTC(bus);                     // RTC clock instance

static float luxSamples[MAX_SAMPLE_COUNT] = {};
static uint8_t readCounter = 0;
static boolean relayState = false;
static struct tm utcTime, localTime;

//CET Time Zone (Rome, Berlin) -> UTC/GMT + 1
const TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120};   // Central European Summer Time
const TimeChangeRule CET  = {"CET ", Last, Sun, Oct, 3, 60};    // Central European Standard Time
Timezone TZ(CEST, CET);                                         // Constructor to build object from DST Rules
TimeChangeRule *tcr;                                            // Pointer to the time change rule, use to get TZ abbreviations

// Options data
typedef struct {
  int8_t hhE, mmE, hhD, mmD;        // Time of day to enable or disable light
  uint8_t poll;                     // Polling interval when enabled       [s]
  uint8_t sampleCount;              // Light sample count (average measurement)
  uint8_t luxTh;                    // Lux threshold
  uint8_t luxThHys;                 // Lux threshold (hysteresis compensation)
} options;                          // Options structure

options opt;                        // Global application options

// ############################################################################
// Sensor reading functions
// ############################################################################

/*
 * Sample from light sensor.
 *
 * Return: the current mobile average value.
 */
float sample() {
  float tmpLux = 0.0;

  // Shift left values in sample array
  for (int i = 0; i < opt.sampleCount - 1; ++i) {
    luxSamples[i] = luxSamples[i + 1];
    tmpLux += luxSamples[i];
  }

  //luxSamples[opt.sampleCount - 1] = BH1750.getLightIntensity();  // TODO Get lux value

  luxSamples[opt.sampleCount - 1] = 9.0;

  tmpLux += luxSamples[opt.sampleCount - 1];

  if (readCounter < opt.sampleCount) {
    readCounter++;
  }

  return tmpLux / readCounter;
}

/*
 * Cleans the mobile average array.
 */
void cleanLuxArray() {
  readCounter = 0;
  for (int i = 0; i < opt.sampleCount; ++i) {
    luxSamples[i] = 0.0;
  }
}

/*
 * Checks the light condition to switch the relay on or off.
 *
 * Return: true to turn on the relay.
 */
boolean checkLightCond(float lux) {
  int low = 0, high = 0;
  for (int i = opt.sampleCount / 2 + 1 ; i < opt.sampleCount - 1; ++i) {
    if (luxSamples[i] <= LUX_TH) {
      low++;
    } else {
      high++;
    }
  }

  if (readCounter >= opt.sampleCount / 2 + 1) {
    // Turn light on
    if (high <= 2 && lux <= LUX_TH) {
      return true;
    }

    // Turn light off
    if (relayState && lux <= LUX_TH + LUX_TH_HIST) {
      return true;
    } else {
      return false;
    }
  }
  return false;
}

boolean checkEnable(const struct tm &now) {
  int minE = opt.hhE * 60 + opt.mmE;
  int minD = opt.hhD * 60 + opt.mmD;
  int nowM = now.tm_hour * 60 + now.tm_min;

  if (minE == minD) {
    return true;
  } else if (minE < minD) {
    return (minE <= nowM && nowM < minD);
  } else {
    return (minE <= nowM || nowM < minD);
  }
}

#ifdef DEBUG
void printLuxArray() {
  for (int i = 0; i < opt.sampleCount; ++i) {
    Serial.print(luxSamples[i]);
    Serial.print(" ");
  }
}
#endif

// ############################################################################
// AVR specific functions
// ############################################################################

/*
 *  PCINT Interrupt Service Routine
 */
ISR(PCINT2_vect) {
  // Don't do anything here but we must include this
  // block of code otherwise the interrupt calls an
  // uninitialized interrupt handler.
}

/*
 * Set various power reduction options
 */
static void powerReduction() {
  // Disable digital input buffer on ADC pins
  DIDR0 = (1 << ADC5D) | (1 << ADC4D) | (1 << ADC3D) | (1 << ADC2D) | (1 << ADC1D) | (1 << ADC0D);

  // Disable digital input buffer on Analog comparator pins
  DIDR1 |= (1 << AIN1D) | (1 << AIN0D);

  // Disable Analog Comparator interrupt
  ACSR &= ~(1 << ACIE);

  // Disable Analog Comparator
  ACSR |= (1 << ACD);

  // Disable unused peripherals to save power
  // Disable ADC (ADC must be disabled before shutdown)
  ADCSRA &= ~(1 << ADEN);

  // Enable power reduction register except:
  // - USART0: for serial communications
  // - TWI module: for I2C communications
  // - TIMER0: for millis()
  PRR = 0xFF & (~(1 << PRUSART0)) & (~(1 << PRTWI)) & (~(1 << PRTIM0));
}

void setup() {
  uint8_t retcode;

  Serial.begin(SERIAL_BAUD);

  // I2C begin() is called BEFORE sensors library "begin" methods:
  // it is called just once for all the sensors.
  bus.begin();

  // Setup pins modes
  pinMode(RELAY_SW_OUT, OUTPUT);
  pinMode(RTC_INT_SQW, INPUT_PULLUP);

  // RTC connection check
  if ((retcode = RTC.checkCon()) != 0) {
    Serial.print("RTC err: ");
    Serial.println(retcode);

    // Exit application code to infinite loop
    exit(retcode);
  }

  // Default options load
  opt.hhE         = 21;//ENABLE_H;
  opt.mmE         = 0;//ENA_DIS_MIN;
  opt.hhD         = 21;//DISABLE_H;
  opt.mmD         = 1;//ENA_DIS_MIN;
  opt.poll        = POLLING_INT;
  opt.sampleCount = SAMPLE_COUNT;
  opt.luxTh       = LUX_TH;
  opt.luxThHys    = LUX_TH_HIST;

  if (opt.sampleCount > MAX_SAMPLE_COUNT) {
    opt.sampleCount = MAX_SAMPLE_COUNT;
  }

  // Real time clock set: UTC time!
  makeTime(&utcTime, 2018, 11, 28, 20, 59, 55);
  RTC.write(&utcTime);
  //RTC.read(&utcTime);
  TZ.toLocal(&utcTime, &localTime, &tcr);

//#ifdef DEBUG
//  printTime(&utcTime, "UTC");
//  printTime(&localTime, tcr->abbrev);
//#endif

  // Set alarm to the RTC clock in UTC format!!
  RTC.setAlarm(ALM2_MATCH_HOURS, opt.mmE, opt.hhE, 0);
  // Clear the alarm flag
  RTC.alarm(ALARM_2);
  // Set the alarm interrupt
  RTC.alarmInterrupt(ALARM_2, true);

  // Light Sensor initialization
//  BH1750.powerOn();
//  BH1750.setMode(BH1750_CONTINUOUS_HIGH_RES_MODE_2);
//  BH1750.setMtreg(200);                 // Set measurement time register to high value
//  delay(100);
//  BH1750.sleep();                       // Send light sensor to sleep

  // Power settings
  powerReduction();

  // Interrupt configuration
  PCMSK2 |= _BV(PCINT20);               // Pin change mask: listen to portD bit 4 (D4) (RTC_INT_SQW)
  PCMSK2 |= _BV(PCINT16);               // Pin change mask: listen to portD bit 0 (D0) (Serial RX)
  PCICR  |= _BV(PCIE2);                 // Enable PCINT interrupt on portD
}

void loop() {

  RTC.read(&utcTime);
  TZ.toLocal(&utcTime, &localTime, &tcr);

#ifdef DEBUG
  printTime(&utcTime, "UTC");
  printTime(&localTime, tcr->abbrev);
#endif

//  TODO if (checkEnable(localTime)) {
  if (checkEnable(utcTime)) {
    RTC.setAlarm(ALM1_MATCH_SECONDS, (utcTime.tm_sec + opt.poll) % 60, 0, 0, 0);
    // Set the alarm interrupt
    RTC.alarmInterrupt(ALARM_1, true);

//    // One time mode: the sensor reads and goes into sleep mode autonomously
//    BH1750.wakeUp(BH1750_ONE_TIME_HIGH_RES_MODE_2);
//
//    // Wait for the sensor to be fully awake.
//    delay(500);

    float lux = sample();
    relayState = checkLightCond(lux);

    digitalWrite(RELAY_SW_OUT, relayState);

#ifdef DEBUG
    printLuxArray();
    Serial.print("= ");
    Serial.print(lux);
    Serial.print(" ");
    if (relayState)
      Serial.println("ON");
    else Serial.println("OFF");
#endif
  } else {
//    TODO set next alarm
    makeTime(&utcTime, 2018, 11, 28, 20, 59, 54);
    RTC.write(&utcTime);

    // Reset the alarm interrupt
    RTC.alarmInterrupt(ALARM_1, false);

    cleanLuxArray();
    digitalWrite(RELAY_SW_OUT, false);
    relayState = false;
  }

#ifdef DEBUG
  Serial.println("Sleeping...");
  Serial.println();
  delay(500);
#endif
  systemSleep(SLEEP_MODE_PWR_DOWN);     // Send the unit to sleep

  RTC.alarm(ALARM_1);                   // Necessary to reset the alarm flag on RTC!
  RTC.alarm(ALARM_2);                   // Necessary to reset the alarm flag on RTC!
}
<commit_msg>Added "set time mode", function to read date from serial, moved RTC interrupt to external INT0.<commit_after>/*
 *  This file is part of PlantLight application.
 *
 * 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.
 *
 */

/*
 * Built for ATMega328P 1Mhz, using AVR Pololu programmer.
 * VERSION 2.0b004
 */

#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#include <Arduino.h>
#include <Timezone.h>
#include <BH1750FVI.h>
#include <DS3232RTC.h>

#include "PowerUtils.h"
#include "TimeUtils.h"

#define SERIAL_BAUD          9600       // Serial baud per second

// Pins
#define RTC_INT_SQW             2       // INT/SQW pin from RTC: external interrupt
#define RELAY_SW_OUT            5       // Relay output pin
#define SET_TIME_IN             4       // Set time mode input pin: pin change interrupt
// TODO time mode LED

#define ENABLE_H               16       // Enable hour (default)
#define DISABLE_H              23       // Disable hour (default)
#define ENA_DIS_MIN             0       // Enable and disable minute (default)

#define POLLING_INT             3       // Polling interval (default)       [s]
#define SAMPLE_COUNT           10       // Light sample count (average measurement)
#define MAX_SAMPLE_COUNT       50       // Max light sample count
#define LUX_TH                 10       // Lux threshold
#define LUX_TH_HIST             5       // Lux threshold (hysteresis compensation)

//#define RTC_CONTROL 0x0E                // TODO remove
//#define RTC_STATUS 0x0F

// ################################# Constants ################################

// ################################# Variables ################################

TwoWire bus;                            // TinyWireM instance (I2C bus)
//BH1750FVI BH1750(bus);                  // Light sensor instance
DS3232RTC RTC(bus);                     // RTC clock instance

static float luxSamples[MAX_SAMPLE_COUNT] = {};
static uint8_t readCounter = 0;
static boolean relayState = false;
volatile boolean setTimeMode = false;
static struct tm utcTime, localTime;

//CET Time Zone (Rome, Berlin) -> UTC/GMT + 1
const TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120};   // Central European Summer Time
const TimeChangeRule CET  = {"CET ", Last, Sun, Oct, 3, 60};    // Central European Standard Time
Timezone TZ(CEST, CET);                                         // Constructor to build object from DST Rules
TimeChangeRule *tcr;                                            // Pointer to the time change rule, use to get TZ abbreviations

// Options data
typedef struct {
  int8_t hhE, mmE, hhD, mmD;        // Time of day to enable or disable light
  uint8_t poll;                     // Polling interval when enabled       [s]
  uint8_t sampleCount;              // Light sample count (average measurement)
  uint8_t luxTh;                    // Lux threshold
  uint8_t luxThHys;                 // Lux threshold (hysteresis compensation)
} options;                          // Options structure

options opt;                        // Global application options

// ############################################################################
// Sensor reading functions
// ############################################################################

/*
 * Sample from light sensor.
 *
 * Return: the current mobile average value.
 */
float sample() {
  float tmpLux = 0.0;

  // Shift left values in sample array
  for (int i = 0; i < opt.sampleCount - 1; ++i) {
    luxSamples[i] = luxSamples[i + 1];
    tmpLux += luxSamples[i];
  }

  //luxSamples[opt.sampleCount - 1] = BH1750.getLightIntensity();  // TODO Get lux value

  luxSamples[opt.sampleCount - 1] = 9.0;

  tmpLux += luxSamples[opt.sampleCount - 1];

  if (readCounter < opt.sampleCount) {
    readCounter++;
  }

  return tmpLux / readCounter;
}

/*
 * Cleans the mobile average array.
 */
void cleanLuxArray() {
  readCounter = 0;
  for (int i = 0; i < opt.sampleCount; ++i) {
    luxSamples[i] = 0.0;
  }
}

/*
 * Checks the light condition to switch the relay on or off.
 *
 * Return: true to turn on the relay.
 */
boolean checkLightCond(float lux) {
  int low = 0, high = 0;
  for (int i = opt.sampleCount / 2 + 1 ; i < opt.sampleCount - 1; ++i) {
    if (luxSamples[i] <= LUX_TH) {
      low++;
    } else {
      high++;
    }
  }

  if (readCounter >= opt.sampleCount / 2 + 1) {
    // Turn light on
    if (high <= 2 && lux <= LUX_TH) {
      return true;
    }

    // Turn light off
    if (relayState && lux <= LUX_TH + LUX_TH_HIST) {
      return true;
    } else {
      return false;
    }
  }
  return false;
}

boolean checkEnable(const struct tm &now) {
  int minE = opt.hhE * 60 + opt.mmE;
  int minD = opt.hhD * 60 + opt.mmD;
  int nowM = now.tm_hour * 60 + now.tm_min;

  if (minE == minD) {
    return true;
  } else if (minE < minD) {
    return (minE <= nowM && nowM < minD);
  } else {
    return (minE <= nowM || nowM < minD);
  }
}

#ifdef DEBUG
void printLuxArray() {
  for (int i = 0; i < opt.sampleCount; ++i) {
    Serial.print(luxSamples[i]);
    Serial.print(" ");
  }
}
#endif

/**
 * Set the date and time by entering the following on the serial input:
 * year,month,day,hour,minute,second,
 *
 * Where
 *  year can be two or four digits,
 *  month is 1-12,
 *  day is 1-31,
 *  hour is 0-23, and
 *  minute and second are 0-59.
 *
 * Entering the final comma delimiter (after "second") will avoid a TODO
 * one-second timeout and will allow the RTC to be set more accurately.
 *
 * No validity checking is done, invalid values or incomplete syntax
 * in the input will result in an incorrect RTC setting.
 */
// TODO exit from serial (quit?)
boolean setTimeSerial() {
  int16_t YYYY;                 // Year in 4 digit format
  int8_t MM, DD, hh, mm, ss;
  time_t t;

  // Check for input to set the RTC, minimum length is 12, i.e. yy,m,d,h,m,s
  if (Serial.available() >= 12) {
    int y = Serial.parseInt();
    if (y >= 100 && y < 1000) {
      Serial.println(F("Error: Year must be two digits or four digits!"));
    } else {
      if (y >= 1000) {
        YYYY = y;
      } else {
        // (y < 100)
        YYYY = y + 2000;
      }

      MM = Serial.parseInt();
      DD = Serial.parseInt();
      hh = Serial.parseInt();
      mm = Serial.parseInt();
      ss = Serial.parseInt();

      t = makeTime(YYYY, MM, DD, hh, mm, ss);

      // Use the time_t value to ensure correct weekday is set
      RTC.set(t);

      // Set UTC application time
      memset((void*) &utcTime, 0, sizeof(utcTime));
      gmtime_r(&t, &utcTime);

      // Convert UTC time in local time
      TZ.toLocal(&utcTime, &localTime, &tcr);

      Serial.println(F("RTC set to: "));
      printTime(&utcTime, "UTC");
      printTime(&localTime, tcr->abbrev);
      Serial.println();

      // Dump any extraneous input
      while (Serial.available() > 0)
        Serial.read();
    }
  return true;
  }
return false;
}

// ############################################################################
// AVR specific functions
// ############################################################################

/*
 *  PCINT Interrupt Service Routines
 */
ISR(PCINT2_vect) {
  setTimeMode = true;
}

/*
 *  INT0 Interrupt Service Routines
 *
 *  External interrupt on PD2
 */
ISR(INT0_vect) {
  // Don't do anything here but we must include this
  // block of code otherwise the interrupt calls an
  // uninitialized interrupt handler.
}

/*
 * Set various power reduction options
 */
static void powerReduction() {
  // Disable digital input buffer on ADC pins
  DIDR0 = (1 << ADC5D) | (1 << ADC4D) | (1 << ADC3D) | (1 << ADC2D) | (1 << ADC1D) | (1 << ADC0D);

  // Disable digital input buffer on Analog comparator pins
  DIDR1 |= (1 << AIN1D) | (1 << AIN0D);

  // Disable Analog Comparator interrupt
  ACSR &= ~(1 << ACIE);

  // Disable Analog Comparator
  ACSR |= (1 << ACD);

  // Disable unused peripherals to save power
  // Disable ADC (ADC must be disabled before shutdown)
  ADCSRA &= ~(1 << ADEN);

  // Enable power reduction register except:
  // - USART0: for serial communications
  // - TWI module: for I2C communications
  // - TIMER0: for millis()
  PRR = 0xFF & (~(1 << PRUSART0)) & (~(1 << PRTWI)) & (~(1 << PRTIM0));
}

void setup() {
  uint8_t retcode;

  Serial.begin(SERIAL_BAUD);

  // I2C begin() is called BEFORE sensors library "begin" methods:
  // it is called just once for all the sensors.
  bus.begin();

  // Setup pins modes
  pinMode(RELAY_SW_OUT, OUTPUT);
  pinMode(RTC_INT_SQW, INPUT_PULLUP);
  pinMode(SET_TIME_IN, INPUT_PULLUP);

  // RTC connection check
  if ((retcode = RTC.checkCon()) != 0) {
    Serial.print("RTC err: ");
    Serial.println(retcode);

    // Exit application code to infinite loop
    exit(retcode);
  }

  // Default options load
  opt.hhE         = 21;//ENABLE_H;
  opt.mmE         = 0;//ENA_DIS_MIN;
  opt.hhD         = 21;//DISABLE_H;
  opt.mmD         = 1;//ENA_DIS_MIN;
  opt.poll        = 20; // TODO POLLING_INT;
  opt.sampleCount = SAMPLE_COUNT;
  opt.luxTh       = LUX_TH;
  opt.luxThHys    = LUX_TH_HIST;

  if (opt.sampleCount > MAX_SAMPLE_COUNT) {
    opt.sampleCount = MAX_SAMPLE_COUNT;
  }

  // Real time clock set: UTC time!
  //makeTime(&utcTime, 2018, 11, 28, 12, 59, 55);
  //RTC.write(&utcTime);
  RTC.read(&utcTime);
  TZ.toLocal(&utcTime, &localTime, &tcr);

#ifdef DEBUG
  printTime(&utcTime, "UTC");
  printTime(&localTime, tcr->abbrev);
#endif

  // Set alarm to the RTC clock in UTC format!!
  RTC.setAlarm(ALM2_MATCH_HOURS, opt.mmE, opt.hhE, 0);
  // Clear the alarm flag
  RTC.alarm(ALARM_2);
  // Set the alarm interrupt
  RTC.alarmInterrupt(ALARM_2, true);

  // Light Sensor initialization
//  BH1750.powerOn();
//  BH1750.setMode(BH1750_CONTINUOUS_HIGH_RES_MODE_2);
//  BH1750.setMtreg(200);                 // Set measurement time register to high value
//  delay(100);
//  BH1750.sleep();                       // Send light sensor to sleep

  // Power settings
  powerReduction();

  // Interrupt configuration
  PCMSK2 |= _BV(PCINT20);               // Pin change mask: listen to portD bit 4 (D4) (SET_TIME_IN)
  PCICR  |= _BV(PCIE2);                 // Enable PCINT interrupt on portD

  EICRA |= _BV(ISC01);                  // Set INT0 to trigger on falling edge (RTC_INT_SQW)
  EIMSK |= _BV(INT0);                   // Turns on INT0
}

void loop() {

  RTC.read(&utcTime);
  TZ.toLocal(&utcTime, &localTime, &tcr);

#ifdef DEBUG
  printTime(&utcTime, "UTC");
  printTime(&localTime, tcr->abbrev);
#endif

  if (setTimeMode) {
    // ############################# SET TIME MODE ############################
    Serial.println("Wake");
    delay(500);
    if (setTimeSerial()) {
      // TODO set alarm
      setTimeMode = false;
    }
  } else {
    //  TODO if (checkEnable(localTime)) {
    if (checkEnable(utcTime)) {
      RTC.setAlarm(ALM1_MATCH_SECONDS, (utcTime.tm_sec + opt.poll) % 60, 0, 0, 0);
      // Clear the alarm flag
      RTC.alarm(ALARM_1);
      // Set the alarm interrupt
      RTC.alarmInterrupt(ALARM_1, true);

      //    // One time mode: the sensor reads and goes into sleep mode autonomously
      //    BH1750.wakeUp(BH1750_ONE_TIME_HIGH_RES_MODE_2);
      //
      //    // Wait for the sensor to be fully awake.
      //    delay(500);

      float lux = sample();
      relayState = checkLightCond(lux);

      digitalWrite(RELAY_SW_OUT, relayState);

#ifdef DEBUG
      printLuxArray();
      Serial.print("= ");
      Serial.print(lux);
      Serial.print(" ");
      if (relayState)
        Serial.println("ON");
      else Serial.println("OFF");
#endif
    } else {
      // Reset the alarm interrupt
      RTC.alarmInterrupt(ALARM_1, false);

      cleanLuxArray();
      digitalWrite(RELAY_SW_OUT, false);
      relayState = false;
    }

#ifdef DEBUG
    Serial.println("Sleeping...");
    Serial.println();
    delay(100);
#endif
    systemSleep(SLEEP_MODE_PWR_DOWN);     // Send the unit to sleep

    RTC.alarm(ALARM_1);                   // Necessary to reset the alarm flag on RTC!
    RTC.alarm(ALARM_2);                   // Necessary to reset the alarm flag on RTC!
  }
}
<|endoftext|>


<commit_before>#include <stdio.h>
#include <math.h>

#include "mylib/mymath.hpp"

int main(int argc, char** argv)
{
  unsigned long int n;
  if (argc!=2 || sscanf(argv[1],"%lu",&n)!=1) {
    fprintf(stderr,"Usage:\n%s integration_steps\n\n\n",argv[0]);
  }

  // Integration limits.
  const double global_a=1E-5;
  const double global_b=1;

  const double y=integral(integrand, n, global_a, global_b);

  const double y_exact=4*(pow(global_b,0.25)-pow(global_a,0.25));
  printf("Result=%lf Exact=%lf Difference=%lf\n", y, y_exact, y-y_exact);

  return 0;
}
<commit_msg>Fix running integral_seq w/o args<commit_after>#include <stdio.h>
#include <math.h>

#include "mylib/mymath.hpp"

int main(int argc, char** argv)
{
  unsigned long int n;
  if (argc!=2 || sscanf(argv[1],"%lu",&n)!=1) {
    fprintf(stderr,"Usage:\n%s integration_steps\n\n\n",argv[0]);
    return 1;
  }

  // Integration limits.
  const double global_a=1E-5;
  const double global_b=1;

  const double y=integral(integrand, n, global_a, global_b);

  const double y_exact=4*(pow(global_b,0.25)-pow(global_a,0.25));
  printf("Result=%lf Exact=%lf Difference=%lf\n", y, y_exact, y-y_exact);

  return 0;
}
<|endoftext|>


<commit_before>#include <stdio.h>
#include <math.h>

#include "mylib/mymath.hpp"

int main(int argc, char** argv)
{
  unsigned long int n;
  if (argc!=2 || sscanf(argv[1],"%lu",&n)!=1)  return 1;

  // Integration limits.
  const double global_a=1E-5;
  const double global_b=1;

  const double y=integral(integrand, n, global_a, global_b);

  const double y_exact=4*(pow(global_b,0.25)-pow(global_a,0.25));
  printf("Result=%lf Exact=%lf Difference=%lf\n", y, y_exact, y-y_exact);

  return 0;
}
<commit_msg>Improved help message for integral_seq<commit_after>#include <stdio.h>
#include <math.h>

#include "mylib/mymath.hpp"

int main(int argc, char** argv)
{
  unsigned long int n;
  if (argc!=2 || sscanf(argv[1],"%lu",&n)!=1) {
    fprintf(stderr,"Usage:\n%s integration_steps\n\n\n",argv[0]);

  // Integration limits.
  const double global_a=1E-5;
  const double global_b=1;

  const double y=integral(integrand, n, global_a, global_b);

  const double y_exact=4*(pow(global_b,0.25)-pow(global_a,0.25));
  printf("Result=%lf Exact=%lf Difference=%lf\n", y, y_exact, y-y_exact);

  return 0;
}
<|endoftext|>


<commit_before>/*
 *
 * MIT License
 *
 * Copyright (c) 2016-2018 The Cats Project
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#ifndef CATS_CORECAT_DATA_ARRAY_HPP
#define CATS_CORECAT_DATA_ARRAY_HPP

#include <cstddef>

#include <memory>
#include <utility>

#include "Allocator/DefaultAllocator.hpp"
#include "../Util/Iterator.hpp"

namespace Cats {
namespace Corecat {
inline namespace Data {

template <typename T>
class ArrayView;

template <typename T, typename A = DefaultAllocator>
class Array {

public:

    using Type = T;
    using AllocatorType = A;

    using Iterator = Type*;
    using ConstIterator = const Type*;
    using ReverseIterator = Corecat::ReverseIterator<Iterator>;
    using ConstReverseIterator = Corecat::ReverseIterator<ConstIterator>;

    using ArrayViewType = ArrayView<T>;
    using ConstArrayViewType = ArrayView<const T>;

private:

    A allocator;
    Type* data = nullptr;
    std::size_t size = 0;
    std::size_t capacity = 0;

public:

    Array() = default;
    Array(std::size_t size_) { resize(size_); }
    Array(const Array& src) {

        data = static_cast<T*>(allocator.allocate(src.size * sizeof(T)));
        size = src.size;
        capacity = size;
        std::size_t i = 0;
        try {

            for(; i < size; ++i) new(data + i) T(src.data[i]);

        } catch(...) {

            for(std::size_t j = 0; j < i; ++j) data[j].~T();
            allocator.deallocate(data, capacity * sizeof(T));
            throw;

        }

    }
    Array(Array&& src) noexcept { swap(src); }
    ~Array() {

        clear();
        if(data) allocator.deallocate(data, capacity * sizeof(T));

    }

    Array& operator =(const Array& src) {

        if(src.size > capacity) {

            T* newData = static_cast<T*>(allocator.allocate(src.size * sizeof(T)));
            std::size_t i = 0;
            try {

                for(; i < src.size; ++i) new(newData + i) T(src.data[i]);

            } catch(...) {

                for(std::size_t j = 0; j < i; ++j) newData[j].~T();
                allocator.deallocate(newData, src.size * sizeof(T));
                throw;

            }
            if(data) {

                for(std::size_t i = 0; i < size; ++i) data[i].~T();
                allocator.deallocate(data, capacity * sizeof(T));

            }
            data = newData;
            size = src.size;
            capacity = size;

        } else if(src.size > size) {

            std::size_t i = 0;
            for(; i < size; ++i) data[i] = src.data[i];
            try {

                for(; i < src.size; ++i) new(data + i) T(src.data[i]);

            } catch(...) {

                for(std::size_t j = size; j < i; ++j) data[j].~T();
                throw;

            }
            size = src.size;

        } else {

            std::size_t i = 0;
            for(; i < src.size; ++i) data[i] = src.data[i];
            for(; i < size; ++i) data[i].~T();
            size = src.size;

        }
        return *this;

    }
    Array& operator =(Array&& src) noexcept { swap(src); return *this; }

    operator ConstArrayViewType() const noexcept { return getView(); }
    operator ArrayViewType() noexcept { return getView(); }

    const Type& operator [](std::size_t index) const noexcept { return data[index]; }
    Type& operator [](std::size_t index) noexcept { return data[index]; }

    const Type* getData() const noexcept { return data; }
    Type* getData() noexcept { return data; }
    std::size_t getSize() const noexcept { return size; }
    std::size_t getCapacity() const noexcept { return capacity; }

    ConstArrayViewType getView() const noexcept { return {data, size}; }
    ArrayViewType getView() noexcept { return {data, size}; }

    bool isEmpty() const noexcept { return !size; }

    void clear() noexcept { resize(0); }

    void reserve(std::size_t capacity_) {

        if(capacity >= capacity_) return;
        T* data_ = static_cast<T*>(allocator.allocate(capacity_ * sizeof(T)));
        std::size_t i = 0;
        try {

            for(; i < size; ++i) new(data_ + i) T(data[i]);

        } catch(...) {

            for(std::size_t j = 0; j < i; ++j) data[j].~T();
            allocator.deallocate(data_, capacity_ * sizeof(T));
            throw;

        }
        if(data) {

            for(std::size_t i = 0; i < size; ++i) data[i].~T();
            allocator.deallocate(data, capacity * sizeof(T));

        }
        data = data_;
        capacity = capacity_;

    }

    void resize(std::size_t size_) {

        if(size_ < size) {

            for(std::size_t i = size_ - 1; i != size_ - 1; --i)
                data[i].~T();

        } else if(size_ > size) {

            if(size_ <= capacity) {

                std::size_t i = size;
                try {

                    for(; i < size_; ++i) new(data + i) T();

                } catch(...) {

                    for(std::size_t j = 0; j < i; ++j) data[j].~T();
                    throw;

                }

            } else {

                T* data_ = static_cast<T*>(allocator.allocate(size_ * sizeof(T)));
                std::size_t i = 0;
                try {

                    for(; i < size; ++i) new(data_ + i) T(data[i]);
                    for(; i < size_; ++i) new(data_ + i) T();

                } catch(...) {

                    for(std::size_t j = 0; j < i; ++j) data_[j].~T();
                    allocator.deallocate(data_, size_ * sizeof(T));
                    throw;

                }
                if(data) {

                    for(std::size_t i = 0; i < size; ++i) data[i].~T();
                    allocator.deallocate(data, capacity * sizeof(T));

                }
                data = data_;
                capacity = size_;

            }

        }
        size = size_;

    }

    template <typename... Arg>
    void push(Arg&&... arg) {

        if(size < capacity) {

            new(data + size) T(std::forward<Arg>(arg)...);

        } else {

            std::size_t newSize = size + 1;
            T* newData = static_cast<T*>(allocator.allocate(newSize * sizeof(T)));
            std::size_t i = 0;
            try {

                for(; i < size; ++i) new(newData + i) T(data[i]);
                new(newData + size) T(std::forward<Arg>(arg)...);

            } catch(...) {

                for(std::size_t j = 0; j < i; ++j) newData[j].~T();
                allocator.deallocate(newData, newSize * sizeof(T));
                throw;

            }
            if(data) {

                for(std::size_t i = 0; i < size; ++i) data[i].~T();
                allocator.deallocate(data, capacity * sizeof(T));

            }
            data = newData;
            capacity = newSize;

        }
        ++size;

    }

    void pop() noexcept {

        if(!size) return;
        data[--size].~T();

    }

    void swap(Array& src) noexcept {

        std::swap(allocator, src.allocator);
        std::swap(data, src.data);
        std::swap(size, src.size);
        std::swap(capacity, src.capacity);

    }

    Iterator begin() const noexcept { return data; }
    Iterator end() const noexcept { return data + size; }

    ConstIterator cbegin() const noexcept { return begin(); }
    ConstIterator cend() const noexcept { return end(); }

    ReverseIterator rbegin() const noexcept { return ReverseIterator(end()); }
    ReverseIterator rend() const noexcept { return ReverseIterator(begin()); }

    ConstReverseIterator crbegin() const noexcept { return ConstReverseIterator(cend()); }
    ConstReverseIterator crend() const noexcept { return ConstReverseIterator(cbegin()); }

};

template <typename T>
class ArrayView {

public:

    using Type = T;

    using Iterator = Type*;
    using ConstIterator = const Type*;
    using ReverseIterator = Corecat::ReverseIterator<Iterator>;
    using ConstReverseIterator = Corecat::ReverseIterator<ConstIterator>;

private:

    Type* data = nullptr;
    std::size_t size = 0;

public:

    ArrayView() = default;
    ArrayView(std::nullptr_t) noexcept {}
    ArrayView(Type* data_, std::size_t size_) noexcept : data(data_), size(size_) {}
    template <std::size_t S>
    ArrayView(Type (&array)[S]) noexcept : data(array), size(S) {}
    ArrayView(std::initializer_list<T> list) noexcept : data(list.begin()), size(list.size()) {}
    ArrayView(const ArrayView& src) = default;

    ArrayView& operator =(const ArrayView& src) = default;

    operator ArrayView<const Type>() noexcept { return {data, size}; }

    Type& operator [](std::size_t index) const noexcept { return data[index]; }

    Type* getData() const noexcept { return data; }
    void setData(T* data_, std::size_t size_) noexcept { data = data_, size = size_; }
    std::size_t getSize() const noexcept { return size; }
    void setSize(std::size_t size_) noexcept { size = size_; }

    bool isEmpty() const noexcept { return !size; }

    ArrayView slice(std::ptrdiff_t beginPos) const noexcept { return slice(beginPos, size); }
    ArrayView slice(std::ptrdiff_t beginPos, std::ptrdiff_t endPos) const noexcept {

        if(beginPos < 0) beginPos = std::max<std::ptrdiff_t>(beginPos + size, 0);
        else beginPos = std::min<std::ptrdiff_t>(beginPos, size);
        if(endPos < 0) endPos = std::max<std::ptrdiff_t>(endPos + size, 0);
        else endPos = std::min<std::ptrdiff_t>(endPos, size);
        if(beginPos < endPos) return {data + beginPos, std::size_t(endPos - beginPos)};
        else return {};

    }

    Iterator begin() const noexcept { return data; }
    Iterator end() const noexcept { return data + size; }

    ConstIterator cbegin() const noexcept { return begin(); }
    ConstIterator cend() const noexcept { return end(); }

    ReverseIterator rbegin() const noexcept { return ReverseIterator(end()); }
    ReverseIterator rend() const noexcept { return ReverseIterator(begin()); }

    ConstReverseIterator crbegin() const noexcept { return ConstReverseIterator(cend()); }
    ConstReverseIterator crend() const noexcept { return ConstReverseIterator(cbegin()); }

};

}
}
}

#endif
<commit_msg>Update Array<commit_after>/*
 *
 * MIT License
 *
 * Copyright (c) 2016-2018 The Cats Project
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#ifndef CATS_CORECAT_DATA_ARRAY_HPP
#define CATS_CORECAT_DATA_ARRAY_HPP

#include <cstddef>

#include <memory>
#include <utility>

#include "Allocator/DefaultAllocator.hpp"
#include "../Util/Iterator.hpp"

namespace Cats {
namespace Corecat {
inline namespace Data {

template <typename T>
class ArrayView;

template <typename T, typename A = DefaultAllocator>
class Array {

public:

    using Type = T;
    using AllocatorType = A;

    using Iterator = Type*;
    using ConstIterator = const Type*;
    using ReverseIterator = Corecat::ReverseIterator<Iterator>;
    using ConstReverseIterator = Corecat::ReverseIterator<ConstIterator>;

    using ArrayViewType = ArrayView<T>;
    using ConstArrayViewType = ArrayView<const T>;

private:

    A allocator;
    Type* data = nullptr;
    std::size_t size = 0;
    std::size_t capacity = 0;

public:

    Array() = default;
    Array(std::size_t size_) { resize(size_); }
    Array(const Array& src) {

        data = static_cast<T*>(allocator.allocate(src.size * sizeof(T)));
        size = src.size;
        capacity = size;
        std::size_t i = 0;
        try {

            for(; i < size; ++i) new(data + i) T(src.data[i]);

        } catch(...) {

            for(std::size_t j = 0; j < i; ++j) data[j].~T();
            allocator.deallocate(data, capacity * sizeof(T));
            throw;

        }

    }
    Array(Array&& src) noexcept { swap(src); }
    ~Array() {

        clear();
        if(data) allocator.deallocate(data, capacity * sizeof(T));

    }

    Array& operator =(const Array& src) {

        if(src.size > capacity) {

            T* newData = static_cast<T*>(allocator.allocate(src.size * sizeof(T)));
            std::size_t i = 0;
            try {

                for(; i < src.size; ++i) new(newData + i) T(src.data[i]);

            } catch(...) {

                for(std::size_t j = 0; j < i; ++j) newData[j].~T();
                allocator.deallocate(newData, src.size * sizeof(T));
                throw;

            }
            if(data) {

                for(std::size_t i = 0; i < size; ++i) data[i].~T();
                allocator.deallocate(data, capacity * sizeof(T));

            }
            data = newData;
            size = src.size;
            capacity = size;

        } else if(src.size > size) {

            std::size_t i = 0;
            for(; i < size; ++i) data[i] = src.data[i];
            try {

                for(; i < src.size; ++i) new(data + i) T(src.data[i]);

            } catch(...) {

                for(std::size_t j = size; j < i; ++j) data[j].~T();
                throw;

            }
            size = src.size;

        } else {

            std::size_t i = 0;
            for(; i < src.size; ++i) data[i] = src.data[i];
            for(; i < size; ++i) data[i].~T();
            size = src.size;

        }
        return *this;

    }
    Array& operator =(Array&& src) noexcept { swap(src); return *this; }

    operator ConstArrayViewType() const noexcept { return getView(); }
    operator ArrayViewType() noexcept { return getView(); }

    const Type& operator [](std::size_t index) const noexcept { return data[index]; }
    Type& operator [](std::size_t index) noexcept { return data[index]; }

    const Type* getData() const noexcept { return data; }
    Type* getData() noexcept { return data; }
    std::size_t getSize() const noexcept { return size; }
    std::size_t getCapacity() const noexcept { return capacity; }

    ConstArrayViewType getView() const noexcept { return {data, size}; }
    ArrayViewType getView() noexcept { return {data, size}; }

    bool isEmpty() const noexcept { return !size; }

    void clear() noexcept { resize(0); }

    void reserve(std::size_t capacity_) {

        if(capacity >= capacity_) return;
        T* data_ = static_cast<T*>(allocator.allocate(capacity_ * sizeof(T)));
        std::size_t i = 0;
        try {

            for(; i < size; ++i) new(data_ + i) T(data[i]);

        } catch(...) {

            for(std::size_t j = 0; j < i; ++j) data[j].~T();
            allocator.deallocate(data_, capacity_ * sizeof(T));
            throw;

        }
        if(data) {

            for(std::size_t i = 0; i < size; ++i) data[i].~T();
            allocator.deallocate(data, capacity * sizeof(T));

        }
        data = data_;
        capacity = capacity_;

    }

    void resize(std::size_t size_) {

        if(size_ < size) {

            for(std::size_t i = size_ - 1; i != size_ - 1; --i)
                data[i].~T();

        } else if(size_ > size) {

            if(size_ <= capacity) {

                std::size_t i = size;
                try {

                    for(; i < size_; ++i) new(data + i) T();

                } catch(...) {

                    for(std::size_t j = 0; j < i; ++j) data[j].~T();
                    throw;

                }

            } else {

                T* data_ = static_cast<T*>(allocator.allocate(size_ * sizeof(T)));
                std::size_t i = 0;
                try {

                    for(; i < size; ++i) new(data_ + i) T(data[i]);
                    for(; i < size_; ++i) new(data_ + i) T();

                } catch(...) {

                    for(std::size_t j = 0; j < i; ++j) data_[j].~T();
                    allocator.deallocate(data_, size_ * sizeof(T));
                    throw;

                }
                if(data) {

                    for(std::size_t i = 0; i < size; ++i) data[i].~T();
                    allocator.deallocate(data, capacity * sizeof(T));

                }
                data = data_;
                capacity = size_;

            }

        }
        size = size_;

    }

    template <typename... Arg>
    void push(Arg&&... arg) {

        if(size < capacity) {

            new(data + size) T(std::forward<Arg>(arg)...);

        } else {

            std::size_t newSize = size + 1;
            T* newData = static_cast<T*>(allocator.allocate(newSize * sizeof(T)));
            std::size_t i = 0;
            try {

                for(; i < size; ++i) new(newData + i) T(data[i]);
                new(newData + size) T(std::forward<Arg>(arg)...);

            } catch(...) {

                for(std::size_t j = 0; j < i; ++j) newData[j].~T();
                allocator.deallocate(newData, newSize * sizeof(T));
                throw;

            }
            if(data) {

                for(std::size_t i = 0; i < size; ++i) data[i].~T();
                allocator.deallocate(data, capacity * sizeof(T));

            }
            data = newData;
            capacity = newSize;

        }
        ++size;

    }

    void pop() noexcept {

        if(!size) return;
        data[--size].~T();

    }

    void swap(Array& src) noexcept {

        std::swap(allocator, src.allocator);
        std::swap(data, src.data);
        std::swap(size, src.size);
        std::swap(capacity, src.capacity);

    }

    Iterator begin() const noexcept { return data; }
    Iterator end() const noexcept { return data + size; }

    ConstIterator cbegin() const noexcept { return begin(); }
    ConstIterator cend() const noexcept { return end(); }

    ReverseIterator rbegin() const noexcept { return ReverseIterator(end()); }
    ReverseIterator rend() const noexcept { return ReverseIterator(begin()); }

    ConstReverseIterator crbegin() const noexcept { return ConstReverseIterator(cend()); }
    ConstReverseIterator crend() const noexcept { return ConstReverseIterator(cbegin()); }

};

template <typename T>
class ArrayView {

public:

    using Type = T;

    using Iterator = Type*;
    using ConstIterator = const Type*;
    using ReverseIterator = Corecat::ReverseIterator<Iterator>;
    using ConstReverseIterator = Corecat::ReverseIterator<ConstIterator>;

private:

    Type* data = nullptr;
    std::size_t size = 0;

public:

    ArrayView() = default;
    ArrayView(std::nullptr_t) noexcept {}
    ArrayView(Type* data_, std::size_t size_) noexcept : data(data_), size(size_) {}
    template <std::size_t S>
    ArrayView(Type (&array)[S]) noexcept : data(array), size(S) {}
    ArrayView(std::initializer_list<T> list) noexcept : data(list.begin()), size(list.size()) {}
    ArrayView(const ArrayView& src) = default;

    ArrayView& operator =(const ArrayView& src) = default;

    operator ArrayView<const Type>() noexcept { return {data, size}; }

    Type& operator [](std::size_t index) const noexcept { return data[index]; }

    Type* getData() const noexcept { return data; }
    void setData(T* data_, std::size_t size_) noexcept { data = data_, size = size_; }
    std::size_t getSize() const noexcept { return size; }
    void setSize(std::size_t size_) noexcept { size = size_; }

    bool isEmpty() const noexcept { return !size; }

    ArrayView slice(std::ptrdiff_t beginPos) const noexcept { return slice(beginPos, size); }
    ArrayView slice(std::ptrdiff_t beginPos, std::ptrdiff_t endPos) const noexcept {

        if(beginPos < 0) beginPos = std::max<std::ptrdiff_t>(beginPos + size, 0);
        else beginPos = std::min<std::ptrdiff_t>(beginPos, size);
        if(endPos < 0) endPos = std::max<std::ptrdiff_t>(endPos + size, 0);
        else endPos = std::min<std::ptrdiff_t>(endPos, size);
        if(beginPos < endPos) return {data + beginPos, std::size_t(endPos - beginPos)};
        else return {};

    }

    void swap(ArrayView& src) noexcept {

        std::swap(data, src.data);
        std::swap(size, src.size);

    }

    Iterator begin() const noexcept { return data; }
    Iterator end() const noexcept { return data + size; }

    ConstIterator cbegin() const noexcept { return begin(); }
    ConstIterator cend() const noexcept { return end(); }

    ReverseIterator rbegin() const noexcept { return ReverseIterator(end()); }
    ReverseIterator rend() const noexcept { return ReverseIterator(begin()); }

    ConstReverseIterator crbegin() const noexcept { return ConstReverseIterator(cend()); }
    ConstReverseIterator crend() const noexcept { return ConstReverseIterator(cbegin()); }

};

}
}
}

#endif
<|endoftext|>