I had a question like this before, but now i rewrited my code and tried this again.
My goal is to calculate the Sun rise and the Sun set times based on the microcontroller's location. For this to work, I'm using a public API which will tell me the Longitude and Latitude data based on the device's public IP. The controller's time is synced using NTP and an external RTC module.
I have created a wrapper class for this calculations so it can be used by other components of my program.
The problem is that the calculated times are shifting upwards. When the system first calculates the times it is minus one hour from the actual times of my location. If i calculate it every 5 sec it will eventually reach the real times, after like 10 min or so.
Here is a sample calculation of just a couple of seconds
**********
[SUN_TIMES] - Millis: 5326
[SUN_TIMES] - SunRise: 4:39
[SUN_TIMES] - SunSet: 18:32
**********
**********
[SUN_TIMES] - Millis: 10335
[SUN_TIMES] - SunRise: 4:39
[SUN_TIMES] - SunSet: 18:32
**********
**********
[SUN_TIMES] - Millis: 15335
[SUN_TIMES] - SunRise: 4:39
[SUN_TIMES] - SunSet: 18:32
**********
**********
[SUN_TIMES] - Millis: 20335
[SUN_TIMES] - SunRise: 4:39
[SUN_TIMES] - SunSet: 18:33
**********
**********
[SUN_TIMES] - Millis: 25335
[SUN_TIMES] - SunRise: 4:39
[SUN_TIMES] - SunSet: 18:33
**********
**********
[SUN_TIMES] - Millis: 30335
[SUN_TIMES] - SunRise: 4:39
[SUN_TIMES] - SunSet: 18:33
**********
**********
[SUN_TIMES] - Millis: 35335
[SUN_TIMES] - SunRise: 4:40
[SUN_TIMES] - SunSet: 18:33
**********
**********
[SUN_TIMES] - Millis: 40335
[SUN_TIMES] - SunRise: 4:40
[SUN_TIMES] - SunSet: 18:33
**********
**********
[SUN_TIMES] - Millis: 45342
[SUN_TIMES] - SunRise: 4:40
[SUN_TIMES] - SunSet: 18:33
**********
**********
[SUN_TIMES] - Millis: 50341
[SUN_TIMES] - SunRise: 4:40
[SUN_TIMES] - SunSet: 18:33
**********
**********
[SUN_TIMES] - Millis: 55345
[SUN_TIMES] - SunRise: 4:40
[SUN_TIMES] - SunSet: 18:33
**********
**********
[SUN_TIMES] - Millis: 60346
[SUN_TIMES] - SunRise: 4:40
[SUN_TIMES] - SunSet: 18:33
**********
**********
[SUN_TIMES] - Millis: 65346
[SUN_TIMES] - SunRise: 4:40
[SUN_TIMES] - SunSet: 18:33
**********
**********
[SUN_TIMES] - Millis: 70346
[SUN_TIMES] - SunRise: 4:40
[SUN_TIMES] - SunSet: 18:33
**********
**********
[SUN_TIMES] - Millis: 75346
[SUN_TIMES] - SunRise: 4:40
[SUN_TIMES] - SunSet: 18:33
**********
As you can see it started on 5326 millisec, calculated 18:32 PM for the sun set.
On 75346 millisec it shifted one minute. It's about 70 seconds and more then one minute offset. And if i left it it will goes upper and upper as time passes. What did i miss?
My current sun rise is 5:38AM and the set is 19:30
At 857999 millis the calculation prints the following:
**********
[SUN_TIMES] - Millis: 857999
[SUN_TIMES] - SunRise: 4:53
[SUN_TIMES] - SunSet: 18:47
**********
sunTimes.h
#include <Arduino.h>
#include <ArduinoJson.h>
#include <utilities/globals.h>
#include <utilities/networkSystem.h>
#include <utilities/fileSystem.h>
#include <utilities/timeSystem.h>
#include <HTTPClient.h>
/*
these includes mostly for the http request response processing.
Like JSON for arduino and things like that. It does not important for the
calculation
*/
#define SUN_TIMES_DEBUG true
#define GEO_RESP_SIZE 1500
class sunTimes {
private:
const char* ipStackURL = "http://api.ipstack.com/check?access_key=0f4ce7d93a2ed67e7435d4227cc0e931&fields=main";
boolean gotGeoData = false;
// Various helper functions for the calculation.
// These calculations adapted from the following open source C project
// Link by Mike Chirico http://souptonuts.sourceforge.net/code/sunrise.c.html
double degToRad(double angleDeg);
double radToDeg(double angleRad);
double calcMeanObliquityOfEcliptic(double t);
double calcGeomMeanLongSun(double t);
double calcObliquityCorrection(double t);
double calcEccentricityEarthOrbit(double t);
double calcGeomMeanAnomalySun(double t);
double calcEquationOfTime(double t);
double calcTimeJulianCent(double jd);
double calcSunTrueLong(double t);
double calcSunApparentLong(double t);
double calcSunDeclination(double t);
double calcHourAngleSunrise(double lat, double solarDec);
double calcHourAngleSunset(double lat, double solarDec);
double calcJD(int year, int month, int day);
double calcJDFromJulianCent(double t);
double calcSunEqOfCenter(double t);
double calcSunriseUTC(double JD, double latitude, double longitude);
double calcSunsetUTC(double JD, double latitude, double longitude);
public:
void getGeoData();
void calcSunTimes();
};
sunTimes.cpp
#include <utilities/sunTimes.h>
void sunTimes::getGeoData(){
// First check if we already got geolocation data.
// No need to get it again.
if( fileSys.config.gotGeoData ){ return; }
HTTPClient http;
http.begin( ipStackURL );
int httpResponseCode = http.GET();
// Check the HTTP response code.
if (httpResponseCode <= 0) {
#if SUN_TIMES_DEBUG
Serial.printf("[SUN_TIMES] - Server not reachable. Error: %d\n",httpResponseCode);
#endif
return;
}
// Try to deserialize the response stream. It should be a JSON string.
SpiRamJsonDocument doc(GEO_RESP_SIZE);
DeserializationError error = deserializeJson(doc,http.getString());
http.end();
if(error){
#if SUN_TIMES_DEBUG
Serial.printf("[SUN_TIMES] - Can't deserialize geo response. Error: %s\n", error.c_str() );
#endif
return;
}
// Check the deserialized response message.
if( doc["success"] == false ){
const char* errorMSG = doc["error"]["info"].as<const char*>();
#if SUN_TIMES_DEBUG
Serial.printf("[SUN_TIMES] - Response error: %s\n", errorMSG);
#endif
return;
}
// Get the data from the JSON response.
fileSys.config.publicIP.fromString(doc["ip"].as<const char*>());
strncpy(fileSys.config.city, doc["city"], sizeof(fileSys.config.city));
fileSys.config.latitude = doc["latitude"].as<float>();
fileSys.config.longitude = doc["longitude"].as<float>();
#if SUN_TIMES_DEBUG
Serial.printf("[SUN_TIMES] - Latitude: %.6f\n",fileSys.config.latitude);
Serial.printf("[SUN_TIMES] - Longitude: %.6f\n",fileSys.config.longitude);
#endif
fileSys.config.gotGeoData = true;
calcSunTimes();
}
void sunTimes::calcSunTimes(){
// First we need to check if we already got latitude data.
if( !fileSys.config.gotGeoData ){ return; }
time_t seconds;
time_t tseconds;
struct tm *ptm = NULL;
struct tm tm;
float JD = calcJD( hsh_timeSystem.getYear(),
hsh_timeSystem.getMonth(),
hsh_timeSystem.getDayOfMonth());
tm.tm_year = hsh_timeSystem.getYear() - 1900;
tm.tm_mon = hsh_timeSystem.getMonth() - 1;
tm.tm_mday = hsh_timeSystem.getDayOfMonth();
tm.tm_hour = hsh_timeSystem.getHour();
tm.tm_min = hsh_timeSystem.getMinute();
tm.tm_sec = hsh_timeSystem.getSecond();
seconds = mktime(&tm);
int delta;
ptm = gmtime(&seconds);
delta = ptm->tm_hour;
// Calc sunRise
tseconds = seconds;
seconds = seconds + calcSunriseUTC(JD, fileSys.config.latitude, -fileSys.config.longitude) * 60;
seconds = seconds - delta * 3600;
ptm = gmtime(&seconds);
// Is winter time.
int offsetToAdd = 1;
if( fileSys.config.dst ){ offsetToAdd = 0; }
int calculatedYear = ptm->tm_year + 1900;
if( calculatedYear == hsh_timeSystem.getYear() ){
fileSys.config.sunRiseHour = ptm->tm_hour + offsetToAdd;
fileSys.config.sunRiseMinute = ptm->tm_min;
}
// Calc sunSet
seconds = tseconds;
seconds += calcSunsetUTC(JD, fileSys.config.latitude, -fileSys.config.longitude) * 60;
seconds = seconds - delta * 3600;
ptm = gmtime(&seconds);
calculatedYear = ptm->tm_year + 1900;
if( calculatedYear == hsh_timeSystem.getYear() ){
fileSys.config.sunSetHour = ptm->tm_hour + offsetToAdd;
fileSys.config.sunSetMinute = ptm->tm_min;
}
#if SUN_TIMES_DEBUG
Serial.println("*********** ***********");
Serial.printf("[SUN_TIMES] - Millis: %lu\n",millis() );
Serial.printf("[SUN_TIMES] - SunRise: %d:%d\n",fileSys.config.sunRiseHour,fileSys.config.sunRiseMinute);
Serial.printf("[SUN_TIMES] - SunSet: %d:%d\n",fileSys.config.sunSetHour,fileSys.config.sunSetMinute);
Serial.println("*********** ***********");
#endif
// Make the config file with the new data.
fileSys.makeConfig();
}
sunTimesCalc.cpp
// Various helper functions for the calculation.
// These calculations adapted from the following open source C project
// Link by Mike Chirico http://souptonuts.sourceforge.net/code/sunrise.c.html
#include <utilities/sunTimes.h>
/* Convert degree angle to radians */
double sunTimes::degToRad(double angleDeg) {
return (PI * angleDeg / 180.0);
}
double sunTimes::radToDeg(double angleRad) {
return (180.0 * angleRad / PI);
}
double sunTimes::calcMeanObliquityOfEcliptic(double t) {
double seconds = 21.448 - t * (46.8150 + t * (0.00059 - t * (0.001813)));
double e0 = 23.0 + (26.0 + (seconds / 60.0)) / 60.0;
return e0; // in degrees
}
double sunTimes::calcGeomMeanLongSun(double t) {
double L = 280.46646 + t * (36000.76983 + 0.0003032 * t);
while ((int)L > 360) {
L -= 360.0;
}
while (L < 0) {
L += 360.0;
}
return L; // in degrees
}
double sunTimes::calcObliquityCorrection(double t) {
double e0 = calcMeanObliquityOfEcliptic(t);
double omega = 125.04 - 1934.136 * t;
double e = e0 + 0.00256 * cos(degToRad(omega));
return e; // in degrees
}
double sunTimes::calcEccentricityEarthOrbit(double t) {
double e = 0.016708634 - t * (0.000042037 + 0.0000001267 * t);
return e; // unitless
}
double sunTimes::calcGeomMeanAnomalySun(double t) {
double M = 357.52911 + t * (35999.05029 - 0.0001537 * t);
return M; // in degrees
}
double sunTimes::calcEquationOfTime(double t) {
double epsilon = calcObliquityCorrection(t);
double l0 = calcGeomMeanLongSun(t);
double e = calcEccentricityEarthOrbit(t);
double m = calcGeomMeanAnomalySun(t);
double y = tan(degToRad(epsilon) / 2.0);
y *= y;
double sin2l0 = sin(2.0 * degToRad(l0));
double sinm = sin(degToRad(m));
double cos2l0 = cos(2.0 * degToRad(l0));
double sin4l0 = sin(4.0 * degToRad(l0));
double sin2m = sin(2.0 * degToRad(m));
double Etime = y * sin2l0 - 2.0 * e * sinm + 4.0 * e * y * sinm * cos2l0 - 0.5 * y * y * sin4l0 - 1.25 * e * e * sin2m;
return radToDeg(Etime) * 4.0; // in minutes of time
}
double sunTimes::calcTimeJulianCent(double jd) {
double T = (jd - 2451545.0) / 36525.0;
return T;
}
double sunTimes::calcSunTrueLong(double t) {
double l0 = calcGeomMeanLongSun(t);
double c = calcSunEqOfCenter(t);
double O = l0 + c;
return O; // in degrees
}
double sunTimes::calcSunApparentLong(double t) {
double o = calcSunTrueLong(t);
double omega = 125.04 - 1934.136 * t;
double lambda = o - 0.00569 - 0.00478 * sin(degToRad(omega));
return lambda; // in degrees
}
double sunTimes::calcSunDeclination(double t) {
double e = calcObliquityCorrection(t);
double lambda = calcSunApparentLong(t);
double sint = sin(degToRad(e)) * sin(degToRad(lambda));
double theta = radToDeg(asin(sint));
return theta; // in degrees
}
double sunTimes::calcHourAngleSunrise(double lat, double solarDec) {
double latRad = degToRad(lat);
double sdRad = degToRad(solarDec);
double HA = (acos(cos(degToRad(90.833)) / (cos(latRad) * cos(sdRad)) - tan(latRad) * tan(sdRad)));
return HA; // in radians
}
double sunTimes::calcHourAngleSunset(double lat, double solarDec) {
double latRad = degToRad(lat);
double sdRad = degToRad(solarDec);
double HA = (acos(cos(degToRad(90.833)) / (cos(latRad) * cos(sdRad)) - tan(latRad) * tan(sdRad)));
return -HA; // in radians
}
double sunTimes::calcJD(int year, int month, int day) {
if (month <= 2) {
year -= 1;
month += 12;
}
int A = floor(year / 100);
int B = 2 - A + floor(A / 4);
double JD = floor(365.25 * (year + 4716)) + floor(30.6001 * (month + 1)) + day + B - 1524.5;
return JD;
}
double sunTimes::calcJDFromJulianCent(double t) {
double JD = t * 36525.0 + 2451545.0;
return JD;
}
double sunTimes::calcSunEqOfCenter(double t) {
double m = calcGeomMeanAnomalySun(t);
double mrad = degToRad(m);
double sinm = sin(mrad);
double sin2m = sin(mrad + mrad);
double sin3m = sin(mrad + mrad + mrad);
double C = sinm * (1.914602 - t * (0.004817 + 0.000014 * t)) + sin2m * (0.019993 - 0.000101 * t) + sin3m * 0.000289;
return C; // in degrees
}
double sunTimes::calcSunriseUTC(double JD, double latitude, double longitude) {
double t = calcTimeJulianCent(JD);
double eqTime = calcEquationOfTime(t);
double solarDec = calcSunDeclination(t);
double hourAngle = calcHourAngleSunrise(latitude, solarDec);
double delta = longitude - radToDeg(hourAngle);
double timeDiff = 4 * delta; // in minutes of time
double timeUTC = 720 + timeDiff - eqTime; // in minutes
double newt = calcTimeJulianCent(calcJDFromJulianCent(t) + timeUTC / 1440.0);
eqTime = calcEquationOfTime(newt);
solarDec = calcSunDeclination(newt);
hourAngle = calcHourAngleSunrise(latitude, solarDec);
delta = longitude - radToDeg(hourAngle);
timeDiff = 4 * delta;
timeUTC = 720 + timeDiff - eqTime;
return timeUTC;
}
double sunTimes::calcSunsetUTC(double JD, double latitude, double longitude) {
double t = calcTimeJulianCent(JD);
double eqTime = calcEquationOfTime(t);
double solarDec = calcSunDeclination(t);
double hourAngle = calcHourAngleSunset(latitude, solarDec);
double delta = longitude - radToDeg(hourAngle);
double timeDiff = 4 * delta; // in minutes of time
double timeUTC = 720 + timeDiff - eqTime; // in minutes
double newt = calcTimeJulianCent(calcJDFromJulianCent(t) + timeUTC / 1440.0);
eqTime = calcEquationOfTime(newt);
solarDec = calcSunDeclination(newt);
hourAngle = calcHourAngleSunset(latitude, solarDec);
delta = longitude - radToDeg(hourAngle);
timeDiff = 4 * delta;
cc timeUTC = 720 + timeDiff - eqTime;
return timeUTC;
}
File system is used to store the calculated data and the longitude and latitude because of the limited API requests.
If you comment out the file system related things it should work.
#include <utilities/sunTimes.h>
sunTimes sunSys;
long lastSunTest = 0;
char ssid[32] = "Network";
char password[32] = "password";
#define WIFI_TIMEOUT 5000
#define WIFI_DEBUG true
/*
This function will wait for a wifi to connect.
If the connection takes more then WIFI_TIMEOUT it will return
false, otherwise returns true.
*/
boolean connectWifi(){
WiFi.begin(ssid, password);
long wifiConnStartMS = millis();
#if WIFI_DEBUG
Serial.printf("\nConnecting to: %s...",ssid);
#endif
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(200);
if( millis() - wifiConnStartMS >= WIFI_TIMEOUT ){
#if WIFI_DEBUG
Serial.println("\nTimeout on wifi connection. Wrong credentials?");
#endif
return false;
}
}
#if WIFI_DEBUG
Serial.printf("\nWiFi Connected to: %s\n",ssid);
#endif
return true;
}
void setup() {
Serial.begin(115200);
if( connectWifi() ){
sunSys.getGeoData();
}
}
void loop() {
if( millis() - lastSunTest >= 5000 ){
lastSunTest = millis();
sunSys.calcSunTimes();
}
}
I can confirm that the latitude and longitude which the API gave me is correct.
The time is also correct.
Sorry for the long question, i know every detail matters and I'm desperate.
EDIT: I let the calculation run every 5 sec. Suddenly when it reached the correct suntimes it shifted back one hour. And started all over again.
Here is a sample:
**********
[SUN_TIMES] - Millis: 3403180
[SUN_TIMES] - SunRise: 5:36
[SUN_TIMES] - SunSet: 19:29
**********
**********
[SUN_TIMES] - Millis: 3408181
[SUN_TIMES] - SunRise: 5:36
[SUN_TIMES] - SunSet: 19:29
**********
**********
[SUN_TIMES] - Millis: 3413181
[SUN_TIMES] - SunRise: 4:36
[SUN_TIMES] - SunSet: 18:29
**********
**********
[SUN_TIMES] - Millis: 3418182
[SUN_TIMES] - SunRise: 4:36
[SUN_TIMES] - SunSet: 18:29
**********
**********
[SUN_TIMES] - Millis: 3423183
[SUN_TIMES] - SunRise: 4:36
[SUN_TIMES] - SunSet: 18:29
**********
As you can see on 3413181 millis it shifted back which is roughly 56 minute.
EDIT 2 : I have found an other way to compute this. This way it isn't shifting anywhere on it's own, but it is plus one hour for some reason. I did not wanted to paste the code here so i created files on pastebin.com
sunTimes_NewCalc.cpp
sunTimes_New.cpp
sunTimes.h
EDIT3: I created an ESP32 simulation to demonstrate the problem.
Just adjust the time in the t_Config struct and let it run. You can check it out at this link: https://wokwi.com/projects/340791620186145362
I have found an open source library to address my issue.
I could not figure out what was my code problem so i started to search for libraries. I did not want to use library, i rather use my own with inspired code, but i had no choise.
Here is the library ( thanks for the author ) link.
My final calculation function simplified to this:
void sunTimes::calcSunTimes(){
Dusk2Dawn cycle( fileSys.config.latitude, fileSys.config.longitude, fileSys.config.timeZone );
int year = hsh_timeSystem.getYear(),
month = hsh_timeSystem.getMonth(),
day = hsh_timeSystem.getDayOfMonth();
int sunRise = cycle.sunrise(year, month, day, fileSys.config.dst);
int sunSet = cycle.sunset(year, month, day, fileSys.config.dst);
fileSys.config.sunRiseHour = cycle.getHour_fromMin(sunRise);
fileSys.config.sunRiseMinute = cycle.getMin_fromMin(sunRise);
fileSys.config.sunSetHour = cycle.getHour_fromMin(sunSet);
fileSys.config.sunSetMinute = cycle.getMin_fromMin(sunSet);
fileSys.makeConfig();
#if SUN_TIMES_DEBUG
Serial.println("*********** ***********");
Serial.printf("[SUN_TIMES] - Millis: %lu\n",millis() );
Serial.printf("[SUN_TIMES] - SunRise: %02d:%02d\n",fileSys.config.sunRiseHour,fileSys.config.sunRiseMinute);
Serial.printf("[SUN_TIMES] - SunSet: %02d:%02d\n",fileSys.config.sunSetHour,fileSys.config.sunSetMinute);
Serial.println("*********** ***********");
#endif
}
In the following days i will compare my code to this lib's code and i will check whats went wrong. ( I suspect that it was the timeZone ) Thank you if you read my question.
Related
for example, the JDK method java.lang.Integer.numberOfLeadingZeros(int):
public static int numberOfLeadingZeros(int i) {
// HD, Figure 5-6
if (i == 0)
return 32;
int n = 1;
if (i >>> 16 == 0) { n += 16; i <<= 16; }
if (i >>> 24 == 0) { n += 8; i <<= 8; }
if (i >>> 28 == 0) { n += 4; i <<= 4; }
if (i >>> 30 == 0) { n += 2; i <<= 2; }
n -= i >>> 31;
return n;
}
what does the code comment 'HD, Figure 5-6' mean?
HD = Hacker's Delight. See the the javadoc:
Implementation note: The implementations of the "bit twiddling" methods (such as highestOneBit and numberOfTrailingZeros) are based on material from Henry S. Warren, Jr.'s Hacker's Delight, (Addison Wesley, 2002).
There are also such comments in java.lang.Long and java.lang.Math.
For example, the addExactmethod in java.lang.Math:
public static int addExact(int x, int y) {
int r = x + y;
// HD 2-12 Overflow iff both arguments have the opposite sign of the result
if (((x ^ r) & (y ^ r)) < 0) {
throw new ArithmeticException("integer overflow");
}
return r;
}
For information of Hacker's Delight we can also refer to: http://hackersdelight.org/
I've solved an lp using CPLEX callable library (in VS2010). The lp is the following:
Maximize
obj: x1 + 2 x2 + 3 x3
Subject To
c1: - x1 + x2 + x3 <= 20
c2: x1 - 3 x2 + x3 <= 30
Bounds
0 <= x1 <= 40
End
The code is given beneath. Now I would like to make it an MIP (additional integrality constraints on the x's). I tried to do so by changing status = CPXlpopt (env, lp); into status = CPXmipopt (env, lp);. This does not work and I get the error 3003: not a mixed-integer problem. Does anybody know what I am missing here?
int main ()
{
/* Declare and allocate space for the variables and arrays where we
will store the optimization results including the status, objective
value, variable values, dual values, row slacks and variable
reduced costs. */
int solstat;
double objval;
double *x = NULL;
double *pi = NULL;
double *slack = NULL;
double *dj = NULL;
CPXENVptr env = NULL;
CPXLPptr lp = NULL;
int status = 0;
int i, j;
int cur_numrows, cur_numcols;
/* Initialize the CPLEX environment */
env = CPXopenCPLEX (&status);
/* Turn on output to the screen */
status = CPXsetintparam (env, CPX_PARAM_SCRIND, CPX_ON);
/* Turn on data checking */
status = CPXsetintparam (env, CPX_PARAM_DATACHECK, CPX_ON);
/* Create the problem. */
lp = CPXcreateprob (env, &status, "lpex1");
/* Now populate the problem with the data. */
#define NUMROWS 2
#define NUMCOLS 3
#define NUMNZ 6
/* To populate by column, we first create the rows, and then add the columns. */
int status = 0;
double obj[NUMCOLS];
double lb[NUMCOLS];
double ub[NUMCOLS];
char *colname[NUMCOLS];
int matbeg[NUMCOLS];
int matind[NUMNZ];
double matval[NUMNZ];
double rhs[NUMROWS];
char sense[NUMROWS];
char *rowname[NUMROWS];
CPXchgobjsen (env, lp, CPX_MAX); /* Problem is maximization */
/* Now create the new rows. First, populate the arrays. */
rowname[0] = "c1";
sense[0] = 'L';
rhs[0] = 20.0;
rowname[1] = "c2";
sense[1] = 'L';
rhs[1] = 30.0;
status = CPXnewrows (env, lp, NUMROWS, rhs, sense, NULL, rowname);
if ( status ) goto TERMINATE;
/* Now add the new columns. First, populate the arrays. */
obj[0] = 1.0; obj[1] = 2.0; obj[2] = 3.0;
matbeg[0] = 0; matbeg[1] = 2; matbeg[2] = 4;
matind[0] = 0; matind[2] = 0; matind[4] = 0;
matval[0] = -1.0; matval[2] = 1.0; matval[4] = 1.0;
matind[1] = 1; matind[3] = 1; matind[5] = 1;
matval[1] = 1.0; matval[3] = -3.0; matval[5] = 1.0;
lb[0] = 0.0; lb[1] = 0.0; lb[2] = 0.0;
ub[0] = 40.0; ub[1] = CPX_INFBOUND; ub[2] = CPX_INFBOUND;
colname[0] = "x1"; colname[1] = "x2"; colname[2] = "x3";
status = CPXaddcols (env, lp, NUMCOLS, NUMNZ, obj, matbeg, matind, matval, lb, ub, colname);
/* Optimize the problem and obtain solution. */
status = CPXlpopt (env, lp);
cur_numrows = CPXgetnumrows (env, lp);
cur_numcols = CPXgetnumcols (env, lp);
x = (double *) malloc (cur_numcols * sizeof(double));
slack = (double *) malloc (cur_numrows * sizeof(double));
dj = (double *) malloc (cur_numcols * sizeof(double));
pi = (double *) malloc (cur_numrows * sizeof(double));
status = CPXsolution (env, lp, &solstat, &objval, x, pi, slack, dj);
/* Write the output to the screen. */
printf ("\nSolution status = %d\n", solstat);
printf ("Solution value = %f\n\n", objval);
for (i = 0; i < cur_numrows; i++) {
printf ("Row %d: Slack = %10f Pi = %10f\n", i, slack[i], pi[i]);
}
for (j = 0; j < cur_numcols; j++) {
printf ("Column %d: Value = %10f Reduced cost = %10f\n",
j, x[j], dj[j]);
}
/* Finally, write a copy of the problem to a file. */
status = CPXwriteprob (env, lp, "lpex1.lp", NULL);
/* Free up the solution */
... (additional code to free up the solution)...
return(status)
}
In your code, you are not declaring any decision variables to be integer. That's why cplex is complaining when you try to solve your problem using a MIP solver. You are doing column-wise modeling and CPXaddcols doesn't have have a parameter for the variable type, but you can use CPXcopyctype or CPXchgctype. Since you the bounds on your decision variables are all greater than 1, you are looking for the 'I' variable type, instead of 'B' for binary.
char *ctype;
ctype = (char *) malloc(cur_numcols * sizeof(char);
for (j = 0; j < cur_numcols; j++) {
ctype[j] = 'I';
}
status = CPXcopyctype(env, lp, ctype);
/* verify status */
status = CPXmipopt (env, lp);
/* verify status */
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I'm looking a while for a decent piece of code to use in my app, in one of those algorithms.
I found this example: http://rosettacode.org/wiki/K-d_tree#C
But when I put the code in xcode, I get an errors, for example:
"use of undeclared identifier", "expected ';' at the end of declaration".
I guess a header file is missing?
I copied the code from the link and made a minor edit which moved
"swap" from being an inline nested function to a static function.
Compiled with "gcc -C99 file.c" and it compiled ok. So, no, it doesn't
need some include file. Maybe you mis pasted it.
If you are happy with this answer, you could accept it. Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#define MAX_DIM 3
struct kd_node_t{
double x[MAX_DIM];
struct kd_node_t *left, *right;
};
inline double
dist(struct kd_node_t *a, struct kd_node_t *b, int dim)
{
double t, d = 0;
while (dim--) {
t = a->x[dim] - b->x[dim];
d += t * t;
}
return d;
}
static void swap(struct kd_node_t *x, struct kd_node_t *y) {
double tmp[MAX_DIM];
memcpy(tmp, x->x, sizeof(tmp));
memcpy(x->x, y->x, sizeof(tmp));
memcpy(y->x, tmp, sizeof(tmp));
}
/* see quickselect method */
struct kd_node_t*
find_median(struct kd_node_t *start, struct kd_node_t *end, int idx)
{
if (end <= start) return NULL;
if (end == start + 1)
return start;
struct kd_node_t *p, *store, *md = start + (end - start) / 2;
double pivot;
while (1) {
pivot = md->x[idx];
swap(md, end - 1);
for (store = p = start; p < end; p++) {
if (p->x[idx] < pivot) {
if (p != store)
swap(p, store);
store++;
}
}
swap(store, end - 1);
/* median has duplicate values */
if (store->x[idx] == md->x[idx])
return md;
if (store > md) end = store;
else start = store;
}
}
struct kd_node_t*
make_tree(struct kd_node_t *t, int len, int i, int dim)
{
struct kd_node_t *n;
if (!len) return 0;
if ((n = find_median(t, t + len, i))) {
i = (i + 1) % dim;
n->left = make_tree(t, n - t, i, dim);
n->right = make_tree(n + 1, t + len - (n + 1), i, dim);
}
return n;
}
/* global variable, so sue me */
int visited;
void nearest(struct kd_node_t *root, struct kd_node_t *nd, int i, int dim,
struct kd_node_t **best, double *best_dist)
{
double d, dx, dx2;
if (!root) return;
d = dist(root, nd, dim);
dx = root->x[i] - nd->x[i];
dx2 = dx * dx;
visited ++;
if (!*best || d < *best_dist) {
*best_dist = d;
*best = root;
}
/* if chance of exact match is high */
if (!*best_dist) return;
if (++i >= dim) i = 0;
nearest(dx > 0 ? root->left : root->right, nd, i, dim, best, best_dist);
if (dx2 >= *best_dist) return;
nearest(dx > 0 ? root->right : root->left, nd, i, dim, best, best_dist);
}
#define N 1000000
#define rand1() (rand() / (double)RAND_MAX)
#define rand_pt(v) { v.x[0] = rand1(); v.x[1] = rand1(); v.x[2] = rand1(); }
int main(void)
{
int i;
struct kd_node_t wp[] = {
{{2, 3}}, {{5, 4}}, {{9, 6}}, {{4, 7}}, {{8, 1}}, {{7, 2}}
};
struct kd_node_t this = {{9, 2}};
struct kd_node_t *root, *found, *million;
double best_dist;
root = make_tree(wp, sizeof(wp) / sizeof(wp[1]), 0, 2);
visited = 0;
found = 0;
nearest(root, &this, 0, 2, &found, &best_dist);
printf(">> WP tree\nsearching for (%g, %g)\n"
"found (%g, %g) dist %g\nseen %d nodes\n\n",
this.x[0], this.x[1],
found->x[0], found->x[1], sqrt(best_dist), visited);
million = calloc(N, sizeof(struct kd_node_t));
srand(time(0));
for (i = 0; i < N; i++) rand_pt(million[i]);
root = make_tree(million, N, 0, 3);
rand_pt(this);
visited = 0;
found = 0;
nearest(root, &this, 0, 3, &found, &best_dist);
printf(">> Million tree\nsearching for (%g, %g, %g)\n"
"found (%g, %g, %g) dist %g\nseen %d nodes\n",
this.x[0], this.x[1], this.x[2],
found->x[0], found->x[1], found->x[2],
sqrt(best_dist), visited);
/* search many random points in million tree to see average behavior.
tree size vs avg nodes visited:
10 ~ 7
100 ~ 16.5
1000 ~ 25.5
10000 ~ 32.8
100000 ~ 38.3
1000000 ~ 42.6
10000000 ~ 46.7 */
int sum = 0, test_runs = 100000;
for (i = 0; i < test_runs; i++) {
found = 0;
visited = 0;
rand_pt(this);
nearest(root, &this, 0, 3, &found, &best_dist);
sum += visited;
}
printf("\n>> Million tree\n"
"visited %d nodes for %d random findings (%f per lookup)\n",
sum, test_runs, sum/(double)test_runs);
// free(million);
return 0;
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Convert decimal to fraction in Objective-C?
I'm trying to make an simple app for converting decimal form to fraction form. The decimal value is set to a UISliderĀ“s value but how should I get the fraction in fraction form? Should I declare it with double or float and how should I tell the app to print it out in fraction form?
You want to return a fractional approximation from a real number? e.g. 0.3333 ~ "1/3". You could try experimenting with this:
#import <Foundation/Foundation.h>
char out[20];
static char *fractApprox(double r, long d) {
double atof();
int atoi();
void exit();
long m[2][2];
double x, startx;
long maxden;
long ai;
startx = x = r;
maxden = d;
/* initialize matrix */
m[0][0] = m[1][1] = 1;
m[0][1] = m[1][0] = 0;
/* loop finding terms until denom gets too big */
while (m[1][0] * ( ai = (long)x ) + m[1][1] <= maxden) {
long t;
t = m[0][0] * ai + m[0][1];
m[0][1] = m[0][0];
m[0][0] = t;
t = m[1][0] * ai + m[1][1];
m[1][1] = m[1][0];
m[1][0] = t;
if(x==(double)ai) break; // AF: division by zero
x = 1/(x - (double) ai);
if(x>(double)0x7FFFFFFF) break; // AF: representation failure
}
ai = (maxden - m[1][1]) / m[1][0];
m[0][0] = m[0][0] * ai + m[0][1];
m[1][0] = m[1][0] * ai + m[1][1];
sprintf(out, "%ld/%ld",m[0][0],m[1][0]);
return out;
}
int main(int argc, const char * argv[])
{
#autoreleasepool {
printf("%s",fractApprox(0.342343, 999));
}
return 0;
}
Prints 329/961 to the console.
Credits to David Eppstein, UC Irvine for the algorithm in C
What you need to convert a decimal to fraction is GCD. For example:
0.535
0.535 => 535/1000
GCD(535, 1000) => 5
(535/5) / (1000/5) => 107/200.
I am seeking an example of using Audio Queue Services.
I would like to create a sound using a mathematical equation and then hear it.
Here's my code for generating sound from a function. I'm assuming you know how to use AudioQueue services, set up an AudioSession, and properly start and stop an audio output queue.
Here's a snippet for setting up and starting an output AudioQueue:
// Get the preferred sample rate (8,000 Hz on iPhone, 44,100 Hz on iPod touch)
size = sizeof(sampleRate);
err = AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareSampleRate, &size, &sampleRate);
if (err != noErr) NSLog(#"AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate) error: %d", err);
//NSLog (#"Current hardware sample rate: %1.0f", sampleRate);
BOOL isHighSampleRate = (sampleRate > 16000);
int bufferByteSize;
AudioQueueBufferRef buffer;
// Set up stream format fields
AudioStreamBasicDescription streamFormat;
streamFormat.mSampleRate = sampleRate;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
streamFormat.mBitsPerChannel = 16;
streamFormat.mChannelsPerFrame = 1;
streamFormat.mBytesPerPacket = 2 * streamFormat.mChannelsPerFrame;
streamFormat.mBytesPerFrame = 2 * streamFormat.mChannelsPerFrame;
streamFormat.mFramesPerPacket = 1;
streamFormat.mReserved = 0;
// New output queue ---- PLAYBACK ----
if (isPlaying == NO) {
err = AudioQueueNewOutput (&streamFormat, AudioEngineOutputBufferCallback, self, nil, nil, 0, &outputQueue);
if (err != noErr) NSLog(#"AudioQueueNewOutput() error: %d", err);
// Enqueue buffers
//outputFrequency = 0.0;
outputBuffersToRewrite = 3;
bufferByteSize = (sampleRate > 16000)? 2176 : 512; // 40.5 Hz : 31.25 Hz
for (i=0; i<3; i++) {
err = AudioQueueAllocateBuffer (outputQueue, bufferByteSize, &buffer);
if (err == noErr) {
[self generateTone: buffer];
err = AudioQueueEnqueueBuffer (outputQueue, buffer, 0, nil);
if (err != noErr) NSLog(#"AudioQueueEnqueueBuffer() error: %d", err);
} else {
NSLog(#"AudioQueueAllocateBuffer() error: %d", err);
return;
}
}
// Start playback
isPlaying = YES;
err = AudioQueueStart(outputQueue, nil);
if (err != noErr) { NSLog(#"AudioQueueStart() error: %d", err); isPlaying= NO; return; }
} else {
NSLog (#"Error: audio is already playing back.");
}
Here's the part that generates the tone:
// AudioQueue output queue callback.
void AudioEngineOutputBufferCallback (void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
AudioEngine *engine = (AudioEngine*) inUserData;
[engine processOutputBuffer:inBuffer queue:inAQ];
}
- (void) processOutputBuffer: (AudioQueueBufferRef) buffer queue:(AudioQueueRef) queue {
OSStatus err;
if (isPlaying == YES) {
[outputLock lock];
if (outputBuffersToRewrite > 0) {
outputBuffersToRewrite--;
[self generateTone:buffer];
}
err = AudioQueueEnqueueBuffer(queue, buffer, 0, NULL);
if (err == 560030580) { // Queue is not active due to Music being started or other reasons
isPlaying = NO;
} else if (err != noErr) {
NSLog(#"AudioQueueEnqueueBuffer() error %d", err);
}
[outputLock unlock];
} else {
err = AudioQueueStop (queue, NO);
if (err != noErr) NSLog(#"AudioQueueStop() error: %d", err);
}
}
-(void) generateTone: (AudioQueueBufferRef) buffer {
if (outputFrequency == 0.0) {
memset(buffer->mAudioData, 0, buffer->mAudioDataBytesCapacity);
buffer->mAudioDataByteSize = buffer->mAudioDataBytesCapacity;
} else {
// Make the buffer length a multiple of the wavelength for the output frequency.
int sampleCount = buffer->mAudioDataBytesCapacity / sizeof (SInt16);
double bufferLength = sampleCount;
double wavelength = sampleRate / outputFrequency;
double repetitions = floor (bufferLength / wavelength);
if (repetitions > 0.0) {
sampleCount = round (wavelength * repetitions);
}
double x, y;
double sd = 1.0 / sampleRate;
double amp = 0.9;
double max16bit = SHRT_MAX;
int i;
SInt16 *p = buffer->mAudioData;
for (i = 0; i < sampleCount; i++) {
x = i * sd * outputFrequency;
switch (outputWaveform) {
case kSine:
y = sin (x * 2.0 * M_PI);
break;
case kTriangle:
x = fmod (x, 1.0);
if (x < 0.25)
y = x * 4.0; // up 0.0 to 1.0
else if (x < 0.75)
y = (1.0 - x) * 4.0 - 2.0; // down 1.0 to -1.0
else
y = (x - 1.0) * 4.0; // up -1.0 to 0.0
break;
case kSawtooth:
y = 0.8 - fmod (x, 1.0) * 1.8;
break;
case kSquare:
y = (fmod(x, 1.0) < 0.5)? 0.7: -0.7;
break;
default: y = 0; break;
}
p[i] = y * max16bit * amp;
}
buffer->mAudioDataByteSize = sampleCount * sizeof (SInt16);
}
}
Something to watch out for is that your callback will be called on a non-main thread, so you have to practice thread safety with locks, mutexs, or other techniques.
This is a version using C# of the same sample from #lucius
void SetupAudio ()
{
AudioSession.Initialize ();
AudioSession.Category = AudioSessionCategory.MediaPlayback;
sampleRate = AudioSession.CurrentHardwareSampleRate;
var format = new AudioStreamBasicDescription () {
SampleRate = sampleRate,
Format = AudioFormatType.LinearPCM,
FormatFlags = AudioFormatFlags.LinearPCMIsSignedInteger | AudioFormatFlags.LinearPCMIsPacked,
BitsPerChannel = 16,
ChannelsPerFrame = 1,
BytesPerFrame = 2,
BytesPerPacket = 2,
FramesPerPacket = 1,
};
var queue = new OutputAudioQueue (format);
var bufferByteSize = (sampleRate > 16000)? 2176 : 512; // 40.5 Hz : 31.25 Hz
var buffers = new AudioQueueBuffer* [numBuffers];
for (int i = 0; i < numBuffers; i++){
queue.AllocateBuffer (bufferByteSize, out buffers [i]);
GenerateTone (buffers [i]);
queue.EnqueueBuffer (buffers [i], null);
}
queue.OutputCompleted += (object sender, OutputCompletedEventArgs e) => {
queue.EnqueueBuffer (e.UnsafeBuffer, null);
};
queue.Start ();
return true;
}
This is the tone generator:
void GenerateTone (AudioQueueBuffer *buffer)
{
// Make the buffer length a multiple of the wavelength for the output frequency.
uint sampleCount = buffer->AudioDataBytesCapacity / 2;
double bufferLength = sampleCount;
double wavelength = sampleRate / outputFrequency;
double repetitions = Math.Floor (bufferLength / wavelength);
if (repetitions > 0)
sampleCount = (uint)Math.Round (wavelength * repetitions);
double x, y;
double sd = 1.0 / sampleRate;
double amp = 0.9;
double max16bit = Int16.MaxValue;
int i;
short *p = (short *) buffer->AudioData;
for (i = 0; i < sampleCount; i++) {
x = i * sd * outputFrequency;
switch (outputWaveForm) {
case WaveForm.Sine:
y = Math.Sin (x * 2.0 * Math.PI);
break;
case WaveForm.Triangle:
x = x % 1.0;
if (x < 0.25)
y = x * 4.0; // up 0.0 to 1.0
else if (x < 0.75)
y = (1.0 - x) * 4.0 - 2.0; // down 1.0 to -1.0
else
y = (x - 1.0) * 4.0; // up -1.0 to 0.0
break;
case WaveForm.Sawtooth:
y = 0.8 - (x % 1.0) * 1.8;
break;
case WaveForm.Square:
y = ((x % 1.0) < 0.5)? 0.7: -0.7;
break;
default: y = 0; break;
}
p[i] = (short)(y * max16bit * amp);
}
buffer->AudioDataByteSize = sampleCount * 2;
}
}
You also want these definitions:
enum WaveForm {
Sine, Triangle, Sawtooth, Square
}
WaveForm outputWaveForm;
const float outputFrequency = 220;
High level: use AVAudioPlayer https://github.com/hollance/AVBufferPlayer
Med level: audio queues trailsinthesand.com/exploring-iphone-audio-part-1/ gets you going nicely. NOTE: I removed the http so the old link could be there, but it does direct to a bad site, so it apparently has changed.
Low level: alternatively, you can drop down a level and do it with audio units: http://cocoawithlove.com/2010/10/ios-tone-generator-introduction-to.html