Conversion Problems

Go To Last Post
20 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was working on a project of mine which calculates the sunrise and sunset everyday and has buttons and lcd to interact with it. The problem that I am facing is that I am able to calculate the sunrise and sunset accurately only if I hard code the coordinates (latitude, longitude and the timezone) into the microcontroller which defeats the purpose of having a lcd with interactable buttons. I noticed that I was able to enter and display the lats and longs on the lcd but then was not able to convert them from char to float (actually I was able to but it returns scrambled values). I know that this is kinda stupid to ask but I think that I am not able to convert the values properly. I am using a gcc to code an atmega16 in atmel studio, the chip is running at 8MHz internal clock. 

I am using this approach :- 

First of all let me tell you about the format I am using to enter the coordinates :-

It is like this for example :- coordinates for New Delhi are 28.7041 N & 77.1025 E latitude and longitude respectively.

Then use two buttons to increment / decrement single integer at a time first doing this for lats. and then longs. and timezone I will be hardcoding either way.

and then I would like to calculate and show the rise and set time on the lcd but the problem with me is

I first concatenate all these digits via two string buffers, first buffer is used to store the left two digits from the decimal point and the second buffer to store the 4 digits right from the decimal point, via sprintf 

and then store the single value in a buffer and then convert this buffer to a int and then dividing via 10000 to get the desired floating point number. 

I had tried to use atof but that doesn't work entirely the lcd shows ? in the place and atoi shows some scrambled values.

Below is the code I am using to calculate the sun times (It is working fine) :-

int sunrise(unsigned char isRise, int y, int m, int d, unsigned char isDST)
{
	float jday, newJday, timeUTC, newTimeUTC;
	int timeLocal;

	jday    = jDay(y, m, d);
	timeUTC = sunriseSetUTC(isRise, jday, latitude, longitude);

	// Advance the calculated time by a fraction of itself. I've no idea what the
	// purpose of this is.
	newJday    = jday + timeUTC / (60 * 24);
	newTimeUTC = sunriseSetUTC(isRise, newJday, latitude, longitude);

	if (!isnan(newTimeUTC)) {
		timeLocal  = (int) round(newTimeUTC + (timezone * 60));
		timeLocal += (isDST) ? 60 : 0;
		} else {
		// There is no sunrise or sunset, e.g. it's in the (ant)arctic.
		timeLocal = -1;
	}

	return timeLocal;
}

float sunriseSetUTC(unsigned char isRise, float jday, float latitude, float longitude)
{
	float t         = fractionOfCentury(jday);
	float eqTime    = equationOfTime(t);
	float solarDec  = sunDeclination(t);
	float hourAngle = hourAngleSunrise(latitude, solarDec);

	hourAngle = isRise ? hourAngle : -hourAngle;
	float delta   = longitude + radToDeg(hourAngle);
	float timeUTC = 720 - (4 * delta) - eqTime; // in minutes
	return timeUTC;
}

float fractionOfCentury(float jd)
{
	return (jd - 2531545) / 36525;
}

float radToDeg(float rad)
{
	//return 180 * rad / 3.14159265358979323846;//m.pi = pi
	return 180 * rad / M_PI;
}

float degToRad(float deg)
{
	return 3.14159265358979323846 * deg / 180;
}

float obliquityCorrection(float t)
{
	float e0    = meanObliquityOfEcliptic(t);
	float omega = 125.04 - 1934.136 * t;
	float e     = e0 + 0.00256 * cos(degToRad(omega));
	return e; // in degrees
}

float meanObliquityOfEcliptic(float t)
{
	float seconds = 21.448 - t * (46.8150 + t * (0.00059 - t * 0.001813));
	float e0      = 23 + (26 + (seconds / 60)) / 60;
	return e0; // in degrees
}

float eccentricityEarthOrbit(float t)
{
	float e = 0.016708634 - t * (0.000042037 + 0.0000001267 * t);
	return e; // unit-less
}

float geomMeanLongSun(float t)
{
	float L0 = 280.46646 + t * (36000.76983 + t * 0.0003032);
	while (L0 > 360) {
		L0 -= 360;
	}
	while (L0 < 0) {
		L0 += 360;
	}
	return L0; // in degrees
}

float geomMeanAnomalySun(float t)
{
	float M = 357.52911 + t * (35999.05029 - 0.0001537 * t);
	return M; // in degrees
}
float equationOfTime(float t)
{
	float epsilon = obliquityCorrection(t);
	float l0      = geomMeanLongSun(t);
	float e       = eccentricityEarthOrbit(t);
	float m       = geomMeanAnomalySun(t);

	float y = tan(degToRad(epsilon) / 2);
	y *= y;

	float sin2l0 = sin(2.0 * degToRad(l0));
	float sinm   = sin(degToRad(m));
	float cos2l0 = cos(2.0 * degToRad(l0));
	float sin4l0 = sin(4.0 * degToRad(l0));
	float sin2m  = sin(2.0 * degToRad(m));

	float 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
}

float sunDeclination(float t)
{
	float e      = obliquityCorrection(t);
	float lambda = sunApparentLong(t);

	float sint  = sin(degToRad(e)) * sin(degToRad(lambda));
	float theta = radToDeg(asin(sint));
	return theta; // in degrees
}

float hourAngleSunrise(float lat, float solarDec)
{
	float latRad = degToRad(lat);
	float sdRad  = degToRad(solarDec);
	float HAarg  = (cos(degToRad(90.833)) / (cos(latRad) * cos(sdRad)) - tan(latRad) * tan(sdRad));
	float HA     = acos(HAarg);
	return HA; // in radians (for sunset, use -HA)
}

float sunApparentLong(float t)
{
	float o      = sunTrueLong(t);
	float omega  = 125.04 - 1934.136 * t;
	float lambda = o - 0.00569 - 0.00478 * sin(degToRad(omega));
	return lambda; // in degrees
}

float sunTrueLong(float t)
{
	float l0 = geomMeanLongSun(t);
	float c  = sunEqOfCenter(t);
	float O  = l0 + c;
	return O; // in degrees
}

float sunEqOfCenter(float t)
{
	float m     = geomMeanAnomalySun(t);
	float mrad  = degToRad(m);
	float sinm  = sin(mrad);
	float sin2m = sin(mrad * 2);
	float sin3m = sin(mrad * 3);
	float C = sinm * (1.914602 - t * (0.004817 + 0.000014 * t)) + sin2m * (0.019993 - 0.000101 * t) + sin3m * 0.000289;
	return C; // in degrees
}

float jDay(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);
	return floor(365.25 * (year + 4716)) + floor(30.6001 * (month + 1)) +
	day + B - 1524.5;
}

Please inform me if more info is required...

This topic has a solution.

anshumaan kumar

Last Edited: Mon. Jun 27, 2022 - 04:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Display showing ? where float (%f) should be means you haven't read about libprint_flt.a in the user manual. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You should initialize timeLocal otherwise sunrise may return an undefined value depending on the result of isnan.

C: i = "told you so";

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Display showing ? where float (%f) should be means you haven't read about libprint_flt.a in the user manual. 

You saying that I should read how to use sprintf ? I knew that double and float in gcc are the same that is why I used %f format specifier for my double value but it doesn't show anything but a question mark on the lcd. 

 

cpluscon wrote:

You should initialize timeLocal otherwise sunrise may return an undefined value depending on the result of isnan.

Good Sir, doesn't this count for the initialization??

int timeLocal;

 

 

anshumaan kumar

Last Edited: Mon. May 16, 2022 - 07:14 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Let me explain a little bit more with the code that I am actually using :-

I need coordinates in this fashion :- For example 29.8472

now to achieve this I had done this for now:-

I declare 6 unsigned integers and two buffers to store them 

first buffer will hold the left two digits from the decimal point and the second buffer will hold the right 4 digits from the decimal point:- like this :-

The values are incremented and decremented via two buttons:-

//used chars cause I will not require value after 9 for each field
unsigned char field;
unsigned char value;
unsigned char lat_1, lat_2, lat_3, lat_4, lat_5, lat_6;
char line1[16], latbuffer_1[7], latbuffer_2[7];

//VALUE MODIFICATION
if(set_button_pressed)
{
    //increment the field
    if(field == 7) field = 0;
    else field++;
}
if(increment_button_pressed)
{
    if(value == 9) value = 0;
    else value++;
}

if(decrement_button_pressed)
{
    if(value == 0) value = 9;
    else value--;
}

sprintf(latbuffer_1,"%01d%01d", lat_1, lat_2);
sprintf(latbuffer_2,"%01d%01d%01d%01d", lat_3, lat_4, lat_5, lat_6);
//used this method to print so that it is easy to convert
//could have used this but refrained from this case atoi would have discard the digits after the decimal point
//sprintf(some_buffer, "%d%d.%d%d%d%d", lat_1, lat_2, lat_3, lat_4, lat_5, lat_6);
lcd.print(latbuffer_1);
lcd.print(".");
lcd.print(latbuffer_2);
switch(field)
{
    case 0;
    lat_1 = value;
    break;

    case 1:
    lat_2 = value;
    break;
    //you get the idea what I am doing
}

//until this everything works

now comes the tricky part or should I say tricky for me cause what was supposed to be the simplest calculation one can do I was not able to get the results I wanted although the logic seems right to me

These are the conversions that I had done:-

strcat(latbuffer_1, latbuffer_2);

latitude = atoi(latbuffer_1) / 10000;

sprintf(line1, "%d", latitude);

lcd.print(line1);
//this usually gives scrambled values :(

Now I could have done something else too like used atof which produces a "?" on the lcd or could have used strtoll but I think this method was quite easy and should give me what I want but still nothing.

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A specific library needs to be linked to print floats. Otherwise ? is printed. This is done to save flash as most people do not need to print floating point numbers. 
 

regards
Greg

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anshumaan wrote:

latitude = atoi(latbuffer_1) / 10000;

For gcc compiler targetting an 8bit AVR, the size of an int is only 16bits, -32768 to 32767.

You don't get many 10,000s in a 16 bit int!

 

Anshumaan wrote:
or could have used strtoll

strtol would be suficeint (a long being 32 bits)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

 

gregd99 wrote:
A specific library needs to be linked to print floats. Otherwise ? is printed.
As I said in #2 you need to read the user manual. In post #2 I was rushed and on my phone so couldn't give specifics but now I can point you towards:

 

https://www.nongnu.org/avr-libc/...

 

Note the stuff about:

 

-Wl,-u,vfprintf -lprintf_flt -lm

 

If you are using Studio 7 then it already has some support to help with this.

 

If I build and run this in the simulator...

 

 

it has only printed '?' where my %f should have appeared. That is because GCC has three libraries for printf:

 

minimal

standard

float

 

By default it uses the "standard" library. That can print most things but as it's only about 700 bytes it cannot print floating point. There is a "minimal" lib but that is even more limited than standard and is intended for AVRs that have very little flash. To get %f to work you have to use the float supporting copy of vfprintf() (that is the routine at the core of printf/sprintf).

 

To do that two things must be done in Studio 7. First you have to tell the code to link against the libprintf_flt.a file.

 

You do that here:

 

 

That is you right-click on the line that says "Libraries" in the solution explorer then choose this option to "Add library". That leads to:

 

 

As I have already done select the line that shows libprintf_flt and tick it.

 

If you now built the code it still would not work. I may well have told it to link with libprintf_flt.a here but the fact is that any program you build in avr-gcc already links with a lib called libc.a (it's the one that holds things like strcpy, malloc, printf, etc) and that already contains a version of the vfprintf() function. So, while libprintf_flt.a also contains another version of vfprintf() too, one that knows how to handle "%f", the one from libc.a will get linked in as the linker will always come to that one first when it's looking for a suitable vfprintf() to use.

 

So you need to tell the linker "if you have found a vfprintf in the standard libraries I want you to forget that before you consider linking with these other libraries like lbprintf_flt.a". There is a -u option (undefine) that can be passed to the linker to do this. Studio 7 has an easy to use check-box for this:

 

 

So that is the complete solution. Under the "General" section of "AVR/GNU Linker" project options there is a tick pox for "Use vprintf library" (actually the caption is a bit wrong it is really "don't use standard vfprintf library"). As set before, the "Libraries" in the Solution now lists both the standard libm.a (one day Atmel/Microchip will catch up with the fact that that one doesn't really need to be there!) and the added libprintf_flt.a with the floating point support.

 

So now...

 

 

QUOD ERAT DEMONSTRANDUM!

 

By the way note that when I just built this I got:

 

 

If I switch off the stuff I just did to link with libprintf_flt.a then:

 

 

So that shows the "cost" of having %f printing support. It has gone up from 1,684 to 3,166 bytes - that is an additional 1,482 bytes. This is the reason that the support for %f is not switched on all the time. Most people using 8bit micros would not choose to use float so it would be unwise for the "big version" to be the default choice.

 

Last Edited: Mon. May 16, 2022 - 09:38 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why do you even need floating point?

 

The earth's equatorial diameter is 12,756km. It's circumference is therefore 40,074km.

 

By my reckoning that means that a uint_16t has a resolution of 611m. Or, put into time terms, a uint16_t has a resolution of 1.3 seconds.

 

Just how accurate do you think sunset and sunrise times need to be?

 

[E2A]

Unless you're crazy you'll be displaying the sunset/sunrise times rounded to the nearest minute. You can do that with 11 bits.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

Last Edited: Mon. May 16, 2022 - 09:03 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

gregd99 wrote:

A specific library needs to be linked to print floats. Otherwise ? is printed. This is done to save flash as most people do not need to print floating point numbers. 
 

woah didn't see that coming. I was always taught in my C classes that printf and sprintf will support all format specifiers well that is because they were running C on PCs while I am trying to create something on a 8 bit micro. Thanks. 

MrKendo wrote:
strtol would be suficeint (a long being 32 bits)

 Indeed sir, but when I was not able to use even atoi() I was kinda baffled by that.Cause they are just simple functions to work with. I become stressed when the most basic things didn't seem to work as they are supposed to be. Thansks for the advice.

 

Clawson : As I said in #2 you need to read the user manual. In #2 I was rushed and on my phone so couldn't give specifics but now I can point you towards

Indeed never would I have known this. Thanks for the detailed tutorial. Thanks Clawson.

clawson wrote:
So that shows the "cost" of having %f printing support. It has gone up from 1,684 to 3,166 bytes - that is an additional 1,482 bytes. This is the reason that the support for %f is not switched on all the time. Most people using 8bit micros would not choose to use float so it would be unwise for the "big version" to be the default choice.

 Yes you are right although I knew that working with floats is kinda messy considering I have only 8 bit micros at the time.

 

Brian Fairchild wrote:
Just how accurate do you think sunset and sunrise times need to be?

Nothing super accurate even if I miss by a few minutes it is not a big deal. 

Can you help me. 

These are the things that I desire :-

I require floating point because the sunrise and sunset calculation function which seems to work fine require floating points as input.

Didn't want to actually display coordinates (floating points) on the lcd. I printed them cause the function then was calculating very wrong values I just checked what was being fed into the function.

I wanted to use method of coordinate entry format (23.7902 N) cause my parents would be able to use it effectively it is most readable for them. 

anshumaan kumar

Last Edited: Mon. May 16, 2022 - 09:23 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I see that your code requires latitude as a float. I also see Mr Lawson has provided with excellent instructions on enabling floating point support. Where he gets the time to create these mini tutorials I'll never understand. (Perhaps he's obtained Hermione Granger's Time-Turner)

 

So; for editing the latitude, I would say forget dealing with integers it only adds complexity. Instead use the WYSIWYG approach and directly edit the string you are displaying then use atof to convert it.

 

This is a complete example that demonstrates how I personally do this simple editing.  BTW: This code works in the Simulator.

 

#include <avr/io.h>

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

enum keypress {NONE, UP_BUTTON, DOWN_BUTTON, SET_BUTTON};

float latitude;

uint8_t field;
char latbuffer[7];

/**
 * @param	keypress
 * @return	Boolean - Editing in Progress.
 */
bool edit_latitude (uint8_t keypress)
{
	bool bUserIsEditing = true;
	char value = latbuffer[field];

	switch (keypress) {

		case NONE:
			sprintf(latbuffer, "%07.4f", latitude);
			break;

		case SET_BUTTON:
			//increment the field
			if (field == 6) {
				field = 0;
				bUserIsEditing = false;
			}
			else
				field++;

			/* Skip the decimal point */
			if (latbuffer[field] == '.')
				field++;
			break;

		case UP_BUTTON:
			if (value == '9') value = '0';
			else              value++;
			latbuffer[field] = value;
			break;

		case DOWN_BUTTON:
			if (value == '0') value = '9';
			else              value--;
			latbuffer[field] = value;
			break;
	};

	lcd.print(latbuffer);
	latitude = atof(latbuffer);

	return bUserIsEditing;
}

int main (void)
{
    /* This lot is purely for testing */
	field = 0;
	edit_latitude(NONE);	// This "None KeyPress" will initialise the latitude editor.
	edit_latitude(UP_BUTTON);
	edit_latitude(SET_BUTTON);
	edit_latitude(SET_BUTTON);
	edit_latitude(DOWN_BUTTON);
	edit_latitude(SET_BUTTON);
	edit_latitude(SET_BUTTON);
	edit_latitude(SET_BUTTON);
	edit_latitude(DOWN_BUTTON);

	edit_latitude(NONE);
	edit_latitude(NONE);

	while (1);
}

 

Last Edited: Mon. May 16, 2022 - 11:21 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:
Where he gets the time to create these mini tutorials I'll never understand.
Post took about 2..3 minutes - I write a lot of manuals/Confluence content in my day job so I can VERY quickly capture relevant screenshots to show a sequence of operation - almost as fast as I could explain it if I was actually showing someone live.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anshumaan wrote:
Good Sir, doesn't this count for the initialization??
No, but your indentation threw me.  I see now that timeStamp gets set to -1 for isnan. Still, it's probably good practice to initialize it explicitly in the declaration.

	if (!isnan(newTimeUTC)) {
		timeLocal  = (int) round(newTimeUTC + (timezone * 60));
		timeLocal += (isDST) ? 60 : 0;
		} else {
		// There is no sunrise or sunset, e.g. it's in the (ant)arctic.
		timeLocal = -1;
	}

C: i = "told you so";

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Brian Fairchild wrote:
Why do you even need floating point?
Indeed

Brian Fairchild wrote:
The earth's equatorial diameter is 12,756km. It's circumference is therefore 40,074km.
Floating point to initialize Great-circle navigation though FSF AVR GCC has 64-bit arithmetic.

 


GitHub - johnmcfarlane/cnl: A Compositional Numeric Library for C++

[end of first paragraph]

You can try out CNL on Compiler Explorer here.

due to The Embedded Muse 361 - Tools and Tips by Jack Ganssle

CNL on AVR in Compiler Explorer generated one compile error "awhile" ago (haven't tried CNL on FSF AVR GCC 12.1)

 

Fixed-Point Support - avr-gcc - GCC Wiki

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anshumaan wrote:
I wanted to use method of coordinate entry format (23.7902 N) cause my parents would be able to use it effectively it is most readable for them. 
Embedded systems may be provisioned via smart phone or feature phone though your design would be more organic.

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

N.Winterbottom wrote:
So; for editing the latitude, I would say forget dealing with integers it only adds complexity. Instead use the WYSIWYG approach and directly edit the string you are displaying then use atof to convert it.

Thanks good sir that was something out of the blue. Really I was first doing the very same thing but then was stuck in the floating point problem and got stuck there thanks for pointing me out this properly how to do everything like they are supposed to be.

 

clawson wrote:

Post took about 2..3 minutes - I write a lot of manuals/Confluence content in my day job so I can VERY quickly capture relevant screenshots to show a sequence of operation - almost as fast as I could explain it if I was actually showing someone live.

Practise makes the man perfect. Really he made a very detailed tutorial even for a very beginner. Thanks again Clawson.

 

cpluscon wrote:

Still, it's probably good practice to initialize it explicitly in the declaration.

Thank you sir these are the things you will not learn in any class. 

gchapman wrote:

Embedded systems may be provisioned via smart phone or feature phone though your design would be more organic.

I could have connected a mobile phone for all the calculation easily could have done that but in this case the problem is that the user(my parents) are not be able to use applications they felt uncomfortable. My father worked in an electronic display company and that is why he is most comfortable in using the character lcd, I had made them all kinds of things with displays like automatic watering system with time fed via buttons and lcd and many things like that.

 

Thanks everyone for replying I will try to complete this and then inform you all. Thank again.

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You could deal with integers, and since sprintf is being used already use it for everything that needs conversions (if you use it once, may as well use it all the time)-

https://godbolt.org/z/WEjhhKxjx

(compiled for pc, but same idea applies to avr using snprintf/lcd/buffer as needed)

convert the int32_t to float as needed later for the calculations, but probably then no need to bring in the float version of printf.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Thanks  N.Winterbottom for showing me how to use the What you see what you get approach correctly. Thanks for that.

And also I thank Clawson for showing me this wonderful tutorial on how to enable the libraries. I also thank Curtvm for showing me even another approach to the problem 

and also others for giving me their time. I thank thee. 

By you people's grace I was able to complete my project and gifted it to my father on his birthday. I thank all of you and also marked the post with the relevant solution. 

Also sorry for replying soo late I forgot at that moment.

Thanks again guys.

Message to the mods. You can close this post as it has achieved its purpose :) 

 

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anshumaan wrote:
Message to the mods. You can close this post as it has achieved its purpose :) 
Well we don't ever really "close" threads as such - in three years time someone may have something useful to add when they find the thread, it solves their own problem but they have an additional observation to make.

 

However a thread is usually considered "complete" when one of the posts has been marked as solution (as has happened in this thread)

 

Moderator.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for another good piece of knowledge about this forum. 

anshumaan kumar