Преобразование char to float

Tima123
Offline
Зарегистрирован: 29.01.2020

Здравствуйте, уважаемые форумчане! Возникла проблема с преобразованием массива char во float.

Использую atmega328p, к которой подключен GPS-приемник (код пишу на Atmel Studio 7.0). Через прерывание приемник-антенна передает микроконтроллеру NMEA-данные вот такие:

$GNRMC,173754.00,A,4314.25363,N,07649.87564,E,0.271,,00820,,,A*63
$GNVTG,,T,,,0.271,N,0.52,K,A*3E
$GGGA,173754.0,4314.25363,,07649.87564E,1,08,2.51,09.9,M,-48.0M,,*61
$GNGA,A,3,04,26,1,09,,,,,,,,3.52,2.51,2.7*14
$GNGSAA,3,66,76,6775,,,,,,,,,352,2.51,2.471D
$GPGSV,21,08,04,60,32,41,06,08,38,,07,03,250,09,23,301,2*72
$GPGSV2,2,08,16,52154,,26,55,07,27,29,05,06,24,31,24,01,25*79
$GLSV,2,1,08,6516,144,23,6682,134,23,6740,324,33,7524,038,28*62$GLGSV,2,2,8,76,76,021,1,77,44,226,83,00,319,,8,05,011,*63
$GNGLL,4314.5363,N,0764987564,E,17374.00,A,A*73

Я беру только одну строку $GNRMC:

$GNRMC,173754.00,A,4314.25363,N,07649.87564,E,0.271,,00820,,,A*63

Далее из этой строки мне нужно взять только значение 0.271 (это скорость в узлах).

И вот здесь возникает проблема

вот небольшой код

char speed[10];
float a;
float b;

int main() {
	
	USARTInit(MYUBRR);
	_delay_ms(2000);
	sei();
	
	while(1)
	{

                 cli();
	         for(int i=0;i<ind;i++)
	         send_Uart(buf[i]); // это для проверки, приходит ли строка.
	         ind=0;
		 if(stringReceived == 1)

		{
			for(int i=7;i<13;i++) // из строки GNRMC вытаскиваем значение скорости в узлах
	                  {
		              speed[i]=buf[i];     
	                  }
                           
                          a = atof(speed);
	                  b = a*1,852; // чтобы получить скорость в км/ч нужно домножить на коэффициент 
                          send_Uart(b);

		}
	}
	return 0;
}

В итоге результат. Вместо значения скорости рисует кракозябру.

В чем может быть проблема? Программу для вывода использую Terminal v1.9b

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017
Tima123
Offline
Зарегистрирован: 29.01.2020

Да я вытащить скорость могу, у меня не получается преобразовать ее в float чтобы потом произвести математические действия.

sadman41
Offline
Зарегистрирован: 19.10.2016

Не понимаю, что происходит в небольшом примере, однако строки в Си называются ASCIIZ именно потому, что в конце они затерминированны нулем. Без такой терминации любая строковая функция будет работать как бог на душу положит.

rkit
Offline
Зарегистрирован: 23.11.2016

atof()

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Код Вы привели неполностью (в таком виде он не то что не работает, а даже не компилируется), поэтому понять что Вы там вытаскиваете из строки невозможно.

Данные из строки Вы вытаскиваете скорее всего неправильно. Точнее сказать не могу, т.к. код неполный, но по тому, что я вижу, скорее всего результат должен быть случайным.

Какая именно религия мешает Вам напечатать переменную speed  перед  строкой №26, чтобы хоть видеть что Вы там собрались преобразовывать?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

rkit пишет:
atof()
Это у него уже есть. Ещё бы "a" правильно подготовить, чтобы "f" разумный получить.

Tima123
Offline
Зарегистрирован: 29.01.2020

Да код не полный, там просто много чего он делает, попытался сократить чтобы понятнее было. Вот еще

#define F_CPU 8000000UL
#define BAUD 9600
#define MYUBRR F_CPU/16/BAUD-1

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

char buf[100];

volatile char ind,flag,stringReceived;
char gnrmc[]={'$','G','N','R','M','C'};
	
ISR(USART_RX_vect)
{
	char ch=UDR0;
	buf[ind]=ch;
	ind++;
	if(ind<7)
	{
		if(buf[ind-1] != gnrmc[ind-1]) // $GNRMC
		ind=0;
	}
	
	if(ind>=66)
	stringReceived=1;
}

void send_Uart(char c)
{
	while(!(UCSR0A & (1<<UDRE0)));
	UDR0 = c;
}

int main() {
	
	USARTInit(MYUBRR);
	_delay_ms(2000);
	sei();
	
	while(1)
	{

             cli();
	     for(int i=0;i<ind;i++)
	     send_Uart(buf[i]); // это для проверки, приходит ли строка.
	     ind=0;
	     if(stringReceived == 1)

	{
		for(int i=7;i<13;i++) // из строки GNRMC вытаскиваем значение скорости в узлах
	             {
		         speed[i]=buf[i];     
	              }
                           
                       a = atof(speed);
	               b = a*1,852; // чтобы получить скорость в км/ч нужно домножить на коэффициент 
                       send_Uart(b);

		}
	}
	return 0;
}

 

Tima123
Offline
Зарегистрирован: 29.01.2020

там где цикл for вот такое значение for(int i=46;i<51;i++)

Tima123
Offline
Зарегистрирован: 29.01.2020

А зачем ее снова объявлять переменную speed, тем более перед ним записываются данные в это массив?

Tima123
Offline
Зарегистрирован: 29.01.2020

Я дико извиняюсь за такой код. Сейчас сделаю его понятнее вам.

#define F_CPU 8000000UL
#define BAUD 9600
#define MYUBRR F_CPU/16/BAUD-1

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

char buf[100];
char speed[10];
float a;
float b;

volatile char ind,flag,stringReceived;
char gnrmc[]={'$','G','N','R','M','C'};
	
ISR(USART_RX_vect)
{
	char ch=UDR0;
	buf[ind]=ch;
	ind++;
	if(ind<7)
	{
		if(buf[ind-1] != gnrmc[ind-1]) // $GNRMC
		ind=0;
	}
	
	if(ind>=66)
	stringReceived=1;
}

void send_Uart(char c)
{
	while(!(UCSR0A & (1<<UDRE0)));
	UDR0 = c;
}

int main() {
	
	USARTInit(MYUBRR);
	_delay_ms(2000);
	sei();
	
	while(1)
	{

             cli();
	     for(int i=0;i<ind;i++)
	     send_Uart(buf[i]); // это для проверки, приходит ли строка.
	     ind=0;
	     if(stringReceived == 1)

	{
	      for(int i=46;i<51;i++) // из строки GNRMC вытаскиваем значение скорости в узлах
	          {
		         speed[i]=buf[i];     
	          }
                           
                       a = atof(speed);
	               b = a*1,852; // чтобы получить скорость в км/ч нужно домножить на коэффициент 
                       send_Uart(b);

		}
	}
	return 0;
}

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Tima123 пишет:

...
float b;
///
void send_Uart(char c)
...
send_Uart(b);
...

Клоунада очередная???

sadman41
Offline
Зарегистрирован: 19.10.2016

Там ещё строка #54 доставляет, но, видимо, в Студии свои законы физики и логики.

Tima123
Offline
Зарегистрирован: 29.01.2020

wdrakula пишет:

Tima123 пишет:

...
float b;
///
void send_Uart(char c)
...
send_Uart(b);
...

Клоунада очередная???

В смысле?

Tima123
Offline
Зарегистрирован: 29.01.2020

wdrakula пишет:

Tima123 пишет:

...
float b;
///
void send_Uart(char c)
...
send_Uart(b);
...

Клоунада очередная???

Кажется я догадываюсь)

Tima123
Offline
Зарегистрирован: 29.01.2020

sadman41 пишет:
Там ещё строка #54 доставляет, но, видимо, в Студии свои законы физики и логики.

А что не так с 54 строкой?

sadman41
Offline
Зарегистрирован: 19.10.2016

А, там ещё и cli приделано. Когда в буфер нормальные байты успевают попадать - не понимаю.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Tima123 пишет:

А зачем ее снова объявлять переменную speed, тем более перед ним записываются данные в это массив?

А я разве говорил "объявлять"? Я говорил

ЕвгенийП пишет:
напечатать переменную speed  перед  строкой №26, чтобы хоть видеть что Вы там собрались преобразовывать?

Напечатайте и посмотрите что в ней.

1. Вы неправильно готовите строку для atof (напечатайте её и убедитесь)

2. Вы уверены, что функции send_Uart можно передавать float и она адекватно переведёт Ваш float обратно в строку для печати?

3. Где, когда и при каких обстоятельствах ind получает начальное значение?

Т.е. сначала Вы неправильно формируете строку для перевода во float, а птом Вы этот float неправильно печатаете.

Tima123
Offline
Зарегистрирован: 29.01.2020

ЕвгенийП пишет:

Tima123 пишет:

А зачем ее снова объявлять переменную speed, тем более перед ним записываются данные в это массив?

А я разве говорил "объявлять"? Я говорил

ЕвгенийП пишет:
напечатать переменную speed  перед  строкой №26, чтобы хоть видеть что Вы там собрались преобразовывать?

Напечатайте и посмотрите что в ней.

1. Вы неправильно готовите строку для atof (напечатайте её и убедитесь)

2. Вы уверены, что функции send_Uart можно передавать float и она адекватно переведёт Ваш float обратно в строку для печати?

3. Где, когда и при каких обстоятельствах ind получает начальное значение?

Т.е. сначала Вы неправильно формируете строку для перевода во float, а птом Вы этот float неправильно печатаете.

Ну вот смотрите я поставлю два вывода для speed (один в цикле, другой после)

вот так:

for(int i=46;i<51;i++) // из строки GNRMC вытаскиваем значение скорости в узлах
{
   speed[i]=buf[i];
   send_Uart(speed[i]);   
}

send_Uart_str("speed:");
send_Uart_str(speed);

где send_Uart_str будет:

void send_Uart_str(char *s)
{
	while (*s != 0) send_Uart(*s++);
}

Результат на программе Terminal:

Tima123
Offline
Зарегистрирован: 29.01.2020

ЕвгенийП пишет:

Tima123 пишет:

А зачем ее снова объявлять переменную speed, тем более перед ним записываются данные в это массив?

А я разве говорил "объявлять"? Я говорил

ЕвгенийП пишет:
напечатать переменную speed  перед  строкой №26, чтобы хоть видеть что Вы там собрались преобразовывать?

Напечатайте и посмотрите что в ней.

1. Вы неправильно готовите строку для atof (напечатайте её и убедитесь)

2. Вы уверены, что функции send_Uart можно передавать float и она адекватно переведёт Ваш float обратно в строку для печати?

3. Где, когда и при каких обстоятельствах ind получает начальное значение?

Т.е. сначала Вы неправильно формируете строку для перевода во float, а птом Вы этот float неправильно печатаете.

 

2. По поводу float и send_Uart. Да я понял теперь что нужно сделать отдельный вывод для float, спасибо.

3. По поводу ind. А он здесь причем? Интересует то совсем другое. Я его обнуляю. Потому что потом снова пойдет счет по прерыванию, где снова будет ловится строка GNRMC.

sadman41
Offline
Зарегистрирован: 19.10.2016

Вы нам специально мозги канифолите? В send_Uart_str() что передаёте? А буквы свои куда пишете?

Tima123
Offline
Зарегистрирован: 29.01.2020

sadman41 пишет:
Вы нам специально мозги канифолите? В send_Uart_str() что передаёте? А буквы свои куда пишете?

Я сделал два вывода, один внутри цикла, другой вне цикла, для проверки, как просил Евгений П. Вывожу значение скорости

sadman41
Offline
Зарегистрирован: 19.10.2016

Чем отличается *speed от speed[46] ?

Tima123
Offline
Зарегистрирован: 29.01.2020

sadman41 пишет:
Чем отличается *speed от speed[46] ?

Одна берется из массива char speed, другая из массива char buf

Tima123
Offline
Зарегистрирован: 29.01.2020

Tima123 пишет:

sadman41 пишет:
Чем отличается *speed от speed[46] ?

Одна берется из массива char speed, другая из массива char buf

там не speed[46], там диапазон с 46 до 51 ячейки массива buf[100]

sadman41
Offline
Зарегистрирован: 19.10.2016

Отличаются чем эти две записи? Суть их какова? Пока этого не выясните - нет никакого смысла разбирать строку.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Вы опять привели код не полностью! Избавьтесь от этой дебильной привычки! Ну, трудно же работать!

Вот Вы написали

Tima123 пишет:

for(int i=46;i<51;i++) // из строки GNRMC вытаскиваем значение скорости в узлах
{
   speed[i]=buf[i]; // Это типа speed[46] = buf[46] ????????
   send_Uart(speed[i]);   
}

send_Uart_str("speed:");
send_Uart_str(speed);

А speed как объявлен? Также, как и раньше?

char speed[10];

(если бы Вы привели код полностью - вопроса бы не было!)

Т.е. Вы в переменную у которой всего 10 элементов (с 0-го по 9-ый), пишете в позиции 46 и далее, а потом печатаете её с 0-ой позиции? И Вас удивляет, что там что-то не то печатается? А почему там должно быть "то"?

Tima123
Offline
Зарегистрирован: 29.01.2020

ЕвгенийП пишет:

Вы опять привели код не полностью! Избавьтесь от этой дебильной привычки! Ну, трудно же работать!

Вот Вы написали

Tima123 пишет:

for(int i=46;i<51;i++) // из строки GNRMC вытаскиваем значение скорости в узлах
{
   speed[i]=buf[i]; // Это типа speed[46] = buf[46] ????????
   send_Uart(speed[i]);   
}

send_Uart_str("speed:");
send_Uart_str(speed);

А speed как объявлен? Также, как и раньше?

char speed[10];

(если бы Вы привели код полностью - вопроса бы не было!)

Т.е. Вы в переменную у которой всего 10 элементов (с 0-го по 9-ый), пишете в позиции 46 и далее, а потом печатаете её с 0-ой позиции? И Вас удивляет, что там что-то не то печатается? А почему там должно быть "то"?

Да, все верно. Я учел все замечания, теперь вроде что-то получается. Хотя все равно придется допиливать)

Tima123
Offline
Зарегистрирован: 29.01.2020

Господа-программисты, спасибо вам всем за помощь. Не зря есть пословица что "в споре рождается истина". Я учел все ваши замечания и что-то уже исправляется,и это очень радует. До идеала конечно далеко, но буду доделывать)).

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Tima123 пишет:

"в споре рождается истина". 

А что, был какой-то спор? Не заметил.

Tima123 пишет:

что-то уже исправляется

Не думаю.

Вы изначально чрезмерно усложнили код, а теперь геройски пытаетесь его исправить.

Вот, допустим, что Вы поняли что не так с формирование переменной speed. Давайте пойдём чуть дальше.

Скажите мне, а вот вообще нафига попу гармонь Вам эта переменная speed? Вот просто на-фи-га?

Если Вы уже нашли начала интересующего Вас числа в массиве buf, кто Вам мешает преобразовать его прямо оттуда? Зачем его копировать в какой-то speed? Функции atof глубоко плевать, что идёт в строке после числа - она выбирает только число, а на остальное кладёт.

Чтобы убедиться в этом, достаточно запустить простой проверочный скетч (обратите внимание, я привожу полный скетч. который Вы можете легко скопировать к себе и запустить без лишних телодвижений. Поступайте всегда также - это просто уважение к собеседнику)

#define printIt(x) do { Serial.print(#x "=");Serial.println(x); } while (false)

void setup(void) {
	Serial.begin(57600);
	printIt (atof("3.14"));
	printIt (atof("3.14!!!"));
	printIt (atof("3.14-da-raz"));
	printIt (atof("3.14,2.71"));
	printIt (atof("3.14.15"));
	printIt (atof("3.14 or something like that"));
}

void loop(void){}

результат

atof("3.14")=3.14
atof("3.14!!!")=3.14
atof("3.14-da-raz")=3.14
atof("3.14,2.71")=3.14
atof("3.14.15")=3.14
atof("3.14 or something like that")=3.14
BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Я надеюсь, что "учел все замечания, теперь вроде что-то получается", это как минимум:

byte stepchar = 0;

for ( byte i = 46; i < 51; i++) {
   speed [stepchar++] = buf [i];
   send_Uart(speed[i]);   
   speed [stepchar] = '\0';
}

Или нет? Или я тут вообще как пятое колесо?

sadman41
Offline
Зарегистрирован: 19.10.2016

6-ю строку из цикла вынести можно. Ну, и понять, что скорость не всегда начинается с 46-й позиции. И вообще - в atof передать адрес позиции в буфере.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

BOOM пишет:
это как минимум:
Прочитайте мой пост #29

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Не дочитал, но моя версия кода ТС подойдет для "копирования части строки".

А еще ТС и всем остальным кто еще не пользуется (или не знает): компилятор игнорирует пробелы, не стесняйтесь и используйте их в коде - так код становится на много более читабельным.

Tima123
Offline
Зарегистрирован: 29.01.2020

[/quote]

Не думаю.

Вы изначально чрезмерно усложнили код, а теперь геройски пытаетесь его исправить.

Вот, допустим, что Вы поняли что не так с формирование переменной speed. Давайте пойдём чуть дальше.

Скажите мне, а вот вообще нафига попу гармонь Вам эта переменная speed? Вот просто на-фи-га?

Если Вы уже нашли начала интересующего Вас числа в массиве buf, кто Вам мешает преобразовать его прямо оттуда? Зачем его копировать в какой-то speed? Функции atof глубоко плевать, что идёт в строке после числа - она выбирает только число, а на остальное кладёт.

[/quote]

так мне же нужно с ним проделать дофига математических операций. Сначала домножить на коэффициент 1.852, а после еще и округлить, а затем передать через uart

Tima123
Offline
Зарегистрирован: 29.01.2020

ЕвгенийП пишет:

Tima123 пишет:

Tima123 пишет:

Чтобы убедиться в этом, достаточно запустить простой проверочный скетч (обратите внимание, я привожу полный скетч. который Вы можете легко скопировать к себе и запустить без лишних телодвижений. Поступайте всегда также - это просто уважение к собеседнику)

#define printIt(x) do { Serial.print(#x "=");Serial.println(x); } while (false)

void setup(void) {
	Serial.begin(57600);
	printIt (atof("3.14"));
	printIt (atof("3.14!!!"));
	printIt (atof("3.14-da-raz"));
	printIt (atof("3.14,2.71"));
	printIt (atof("3.14.15"));
	printIt (atof("3.14 or something like that"));
}

void loop(void){}

результат

atof("3.14")=3.14
atof("3.14!!!")=3.14
atof("3.14-da-raz")=3.14
atof("3.14,2.71")=3.14
atof("3.14.15")=3.14
atof("3.14 or something like that")=3.14

Спасибо, нужно будет попробовать.

Tima123
Offline
Зарегистрирован: 29.01.2020

BOOM пишет:

Я надеюсь, что "учел все замечания, теперь вроде что-то получается", это как минимум:

byte stepchar = 0;

for ( byte i = 46; i < 51; i++) {
   speed [stepchar++] = buf [i];
   send_Uart(speed[i]);   
   speed [stepchar] = '\0';
}

Или нет? Или я тут вообще как пятое колесо?

Нет, все верно. Я примерно так сделал, только чуть по-другому.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Tima123 пишет:

так мне же нужно с ним проделать дофига математических операций. Сначала домножить на коэффициент 1.852, а после еще и округлить, а затем передать через uart

Вы невнимательно читаете, для программиста это неприемлемо.

Я Вам говорил, "нафига попу гармонь Вам эта переменная speed? Вот просто на-фи-га?". Вам нужно делать операции, а при чём тут промежуточная строковая переменная speed? Вы с ней собрались операции проделывать? Или таки с уже преобразованным во float значением? А если с преобразованным, так и преобразовывайте его прямо из буфера безо всякой промежуточной строки - нафига она Вам? Чтобы всё усложнить, и расходовать лишнюю память и время исполнения? Больше она не нужна ни для чего.

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Да он уже сделал, сойдёт. Но так то да - зачем и так малую память использовать для непонятных манипуляций? Сразу в растудыть ее и жарить на углях уже шомпол с мясом, а не отдельные куски )))

Tima123
Offline
Зарегистрирован: 29.01.2020

ЕвгенийП пишет:

Tima123 пишет:

так мне же нужно с ним проделать дофига математических операций. Сначала домножить на коэффициент 1.852, а после еще и округлить, а затем передать через uart

Вы невнимательно читаете, для программиста это неприемлемо.

Я Вам говорил, "нафига попу гармонь Вам эта переменная speed? Вот просто на-фи-га?". Вам нужно делать операции, а при чём тут промежуточная строковая переменная speed? Вы с ней собрались операции проделывать? Или таки с уже преобразованным во float значением? А если с преобразованным, так и преобразовывайте его прямо из буфера безо всякой промежуточной строки - нафига она Вам? Чтобы всё усложнить, и расходовать лишнюю память и время исполнения? Больше она не нужна ни для чего.

Я вас понял, вы имеете ввиду сразу миновать переменную speed, создав переменную float и сразу закинуть туда преобразованное значение с массива buf. Да, это хорошая идея, скорее всего так и сделаю. Но чтобы не запутаться пока сделал так.