net: lwm2m: fix float32/64 handling

During the initial work on LwM2M, the float32/64 code was
basically stubbed out.  Float32 sent only whole values and
float64 was completely broken.

Let's clean up the OMA TLV formatting code by moving the float
processing code into a separate file: lwm2m_util.c.

Then using public definitions for binary32 and binary64, let's
fix the processing code to correctly fill the float32_value_t
and float64_value_t types.

Signed-off-by: Michael Scott <mike@foundries.io>
This commit is contained in:
Michael Scott 2019-02-08 10:00:00 -08:00 committed by Anas Nashif
parent 90d1e6f6c6
commit d53a0855a1
5 changed files with 380 additions and 89 deletions

View file

@ -149,11 +149,14 @@ lwm2m_engine_user_cb_t lwm2m_firmware_get_update_cb(void);
* Example: 123.456 == val1: 123, val2:456000
* Example: 123.000456 = val1: 123, val2:456
*/
#define LWM2M_FLOAT32_DEC_MAX 1000000
typedef struct float32_value {
s32_t val1;
s32_t val2;
} float32_value_t;
#define LWM2M_FLOAT64_DEC_MAX 1000000000LL
typedef struct float64_value {
s64_t val1;
s64_t val2;

View file

@ -10,6 +10,7 @@ zephyr_library_sources(
lwm2m_obj_device.c
lwm2m_rw_plain_text.c
lwm2m_rw_oma_tlv.c
lwm2m_util.c
)
# LWM2M RD Client Support

View file

@ -72,6 +72,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#ifdef CONFIG_LWM2M_RD_CLIENT_SUPPORT
#include "lwm2m_rd_client.h"
#endif
#include "lwm2m_util.h"
enum {
OMA_TLV_TYPE_OBJECT_INSTANCE = 0,
@ -496,7 +497,6 @@ static size_t put_string(struct lwm2m_output_context *out,
return len;
}
/* use binary32 format */
static size_t put_float32fix(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
float32_value_t *value)
@ -504,57 +504,23 @@ static size_t put_float32fix(struct lwm2m_output_context *out,
struct tlv_out_formatter_data *fd;
size_t len;
struct oma_tlv tlv;
int e = 0;
s32_t val = 0;
s32_t v;
u8_t b[4];
/*
* TODO: Currently, there is no standard API for handling the decimal
* portion of the float32_value structure. In the future, we should use
* the value->val2 (decimal portion) to set the decimal mask and in the
* following binary float calculations.
*
* HACK BELOW: hard code the decimal mask to 0 (whole number)
*/
int bits = 0;
int ret;
u8_t b32[4];
fd = engine_get_out_user_data(out);
if (!fd) {
return 0;
}
v = value->val1;
if (v < 0) {
v = -v;
ret = lwm2m_f32_to_b32(value, b32, sizeof(b32));
if (ret < 0) {
LOG_ERR("float32 conversion error: %d", ret);
return 0;
}
while (v > 1) {
val = (val >> 1);
if (v & 1) {
val = val | (1L << 22);
}
v = (v >> 1);
e++;
}
/* convert to the thing we should have */
e = e - bits + 127;
if (value->val1 == 0) {
e = 0;
}
/* is this the right byte order? */
b[0] = (value->val1 < 0 ? 0x80 : 0) | (e >> 1);
b[1] = ((e & 1) << 7) | ((val >> 16) & 0x7f);
b[2] = (val >> 8) & 0xff;
b[3] = val & 0xff;
tlv_setup(&tlv, tlv_calc_type(fd->writer_flags),
tlv_calc_id(fd->writer_flags, path), 4);
len = oma_tlv_put(&tlv, out, b, false);
tlv_calc_id(fd->writer_flags, path), sizeof(b32));
len = oma_tlv_put(&tlv, out, b32, false);
return len;
}
@ -564,21 +530,24 @@ static size_t put_float64fix(struct lwm2m_output_context *out,
{
struct tlv_out_formatter_data *fd;
size_t len;
s64_t binary64 = 0, net_binary64 = 0;
struct oma_tlv tlv;
/* TODO */
u8_t b64[8];
int ret;
fd = engine_get_out_user_data(out);
if (!fd) {
return 0;
}
net_binary64 = sys_cpu_to_be64(binary64);
ret = lwm2m_f64_to_b64(value, b64, sizeof(b64));
if (ret < 0) {
LOG_ERR("float64 conversion error: %d", ret);
return 0;
}
tlv_setup(&tlv, tlv_calc_type(fd->writer_flags),
tlv_calc_id(fd->writer_flags, path),
sizeof(net_binary64));
len = oma_tlv_put(&tlv, out, (u8_t *)&net_binary64, false);
tlv_calc_id(fd->writer_flags, path), sizeof(b64));
len = oma_tlv_put(&tlv, out, b64, false);
return len;
}
@ -679,51 +648,37 @@ static size_t get_float32fix(struct lwm2m_input_context *in,
{
struct oma_tlv tlv;
size_t size = oma_tlv_get(&tlv, in, false);
int e;
s32_t val;
int sign = false;
int bits = 0;
u8_t values[4];
u8_t b32[4];
int ret;
if (size > 0) {
/* TLV needs to be 4 bytes */
if (buf_read(values, 4, CPKT_BUF_READ(in->in_cpkt),
if (tlv.length != 4) {
LOG_ERR("Invalid float32 length: %d", tlv.length);
/* dummy read */
while (tlv.length--) {
if (buf_read_u8(b32,
CPKT_BUF_READ(in->in_cpkt),
&in->offset) < 0) {
break;
}
}
return 0;
}
/* read b32 in network byte order */
if (buf_read(b32, tlv.length, CPKT_BUF_READ(in->in_cpkt),
&in->offset) < 0) {
/* TODO: Generate error? */
return 0;
}
/* sign */
sign = (values[0] & 0x80) != 0;
e = ((values[0] << 1) & 0xff) | (values[1] >> 7);
val = (((long)values[1] & 0x7f) << 16) |
(values[2] << 8) |
values[3];
e = e - 127 + bits;
/* e is the number of times we need to roll the number */
LOG_DBG("Actual e=%d", e);
e = e - 23;
LOG_DBG("E after sub %d", e);
val = val | 1L << 23;
if (e > 0) {
val = val << e;
} else {
val = val >> -e;
ret = lwm2m_b32_to_f32(b32, sizeof(b32), value);
if (ret < 0) {
LOG_ERR("binary32 conversion error: %d", ret);
return 0;
}
value->val1 = sign ? -val : val;
/*
* TODO: Currently, there is no standard API for handling the
* decimal portion of the float32_value structure. In the
* future, once that is settled, we should calculate
* value->val2 in the above float calculations.
*
* HACK BELOW: hard code the decimal value 0
*/
value->val2 = 0;
}
return size;
@ -734,14 +689,37 @@ static size_t get_float64fix(struct lwm2m_input_context *in,
{
struct oma_tlv tlv;
size_t size = oma_tlv_get(&tlv, in, false);
u8_t b64[8];
int ret;
if (size > 0) {
if (tlv.length != 8) {
LOG_ERR("invalid length: %u (not 8)", tlv.length);
LOG_ERR("invalid float64 length: %d", tlv.length);
/* dummy read */
while (tlv.length--) {
if (buf_read_u8(b64,
CPKT_BUF_READ(in->in_cpkt),
&in->offset) < 0) {
break;
}
}
return 0;
}
/* TODO */
/* read b64 in network byte order */
if (buf_read(b64, tlv.length, CPKT_BUF_READ(in->in_cpkt),
&in->offset) < 0) {
/* TODO: Generate error? */
return 0;
}
ret = lwm2m_b64_to_f64(b64, sizeof(b64), value);
if (ret < 0) {
LOG_ERR("binary64 conversion error: %d", ret);
return 0;
}
}
return size;

View file

@ -0,0 +1,289 @@
/*
* Copyright (c) 2019 Foundries.io
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <kernel.h>
#include "lwm2m_util.h"
#define SHIFT_LEFT(v, o, m) (((v) << (o)) & (m))
#define SHIFT_RIGHT(v, o, m) (((v) >> (o)) & (m))
/* convert from float32 to binary32 */
int lwm2m_f32_to_b32(float32_value_t *f32, u8_t *b32, size_t len)
{
s32_t e = -1, v, f = 0;
int i;
if (len != 4) {
return -EINVAL;
}
v = f32->val1;
/* sign handled later */
if (v < 0) {
v = -v;
}
/* add whole value to fraction */
while (v > 0) {
f >>= 1;
if (v & 1) {
f |= (1 << 23);
}
v >>= 1;
e++;
}
v = f32->val2;
/* sign handled later */
if (v < 0) {
v = -v;
}
/* add decimal to fraction */
i = e;
while (v > 0 && i < 23) {
v *= 2;
if (e < 0 && v < LWM2M_FLOAT32_DEC_MAX) {
/* handle -e */
e--;
continue;
} else if (v >= LWM2M_FLOAT32_DEC_MAX) {
v -= LWM2M_FLOAT32_DEC_MAX;
f |= 1 << (22 - i);
}
if (v == 0) {
break;
}
i++;
}
/* adjust exponent for bias */
e += 127;
memset(b32, 0, len);
/* sign: bit 31 */
b32[0] = f32->val1 < 0 ? 0x80 : 0;
/* exponent: bits 30-23 */
b32[0] |= e >> 1;
b32[1] = (e & 1) << 7;
/* fraction: bits 22-0 */
/* NOTE: ignore the "hidden" bit 23 in fraction */
b32[1] |= (f >> 16) & 0x7F;
b32[2] = (f >> 8) & 0xFF;
b32[3] = f & 0xFF;
return 0;
}
/* convert from float64 to binary64 */
int lwm2m_f64_to_b64(float64_value_t *f64, u8_t *b64, size_t len)
{
s64_t v, f = 0;
s32_t e = -1;
int i;
if (len != 8) {
return -EINVAL;
}
v = f64->val1;
/* sign handled later */
if (v < 0) {
v = -v;
}
/* add whole value to fraction */
while (v > 0) {
f >>= 1;
if (v & 1) {
f |= ((s64_t)1 << 52);
}
v >>= 1;
e++;
}
v = f64->val2;
/* sign handled later */
if (v < 0) {
v = -v;
}
/* add decimal to fraction */
i = e;
while (v > 0 && i < 52) {
v *= 2;
if (e < 0 && v < LWM2M_FLOAT64_DEC_MAX) {
/* handle -e */
e--;
continue;
} else if (v >= LWM2M_FLOAT64_DEC_MAX) {
v -= LWM2M_FLOAT64_DEC_MAX;
f |= (s64_t)1 << (51 - i);
}
if (v == 0) {
break;
}
i++;
}
/* adjust exponent for bias */
e += 1023;
memset(b64, 0, len);
/* sign: bit 63 */
b64[0] = f64->val1 < 0 ? 0x80 : 0;
/* exponent: bits 62-52 */
b64[0] |= (e >> 4);
b64[1] = ((e & 0xF) << 4);
/* fraction: bits 51-0 */
/* NOTE: ignore the "hidden" bit 52 in fraction */
b64[1] |= ((f >> 48) & 0xF);
b64[2] = (f >> 40) & 0xFF;
b64[3] = (f >> 32) & 0xFF;
b64[4] = (f >> 24) & 0xFF;
b64[5] = (f >> 16) & 0xFF;
b64[6] = (f >> 8) & 0xFF;
b64[7] = f & 0xFF;
return 0;
}
/* convert from binary32 to float32 */
int lwm2m_b32_to_f32(u8_t *b32, size_t len, float32_value_t *f32)
{
s32_t f, k, i, e;
bool sign = false;
if (len != 4) {
return -EINVAL;
}
f32->val1 = 0;
f32->val2 = 0;
/* calc sign: bit 31 */
sign = SHIFT_RIGHT(b32[0], 7, 0x1);
/* calc exponent: bits 30-23 */
e = SHIFT_LEFT(b32[0], 1, 0xFF);
e += SHIFT_RIGHT(b32[1], 7, 0x1);
/* remove bias */
e -= 127;
/* enable "hidden" fraction bit 23 which is always 1 */
f = ((s32_t)1 << 22);
/* calc fraction: bits 22-0 */
f += ((s32_t)(b32[1] & 0x7F) << 16);
f += ((s32_t)b32[2] << 8);
f += b32[3];
/* handle whole number */
if (e > -1) {
/* precision overflow */
if (e > 23) {
e = 23;
}
f32->val1 = (f >> (23 - e)) * (sign ? -1 : 1);
}
/* calculate the rest of the decimal */
k = LWM2M_FLOAT32_DEC_MAX;
/* account for -e */
while (e < -1) {
k /= 2;
e++;
}
for (i = 22 - e; i >= 0; i--) {
k /= 2;
if (f & (1 << i)) {
f32->val2 += k;
}
}
return 0;
}
/* convert from binary64 to float64 */
int lwm2m_b64_to_f64(u8_t *b64, size_t len, float64_value_t *f64)
{
s64_t f, k;
int i, e;
bool sign = false;
if (len != 8) {
return -EINVAL;
}
f64->val1 = 0LL;
f64->val2 = 0LL;
/* calc sign: bit 63 */
sign = SHIFT_RIGHT(b64[0], 7, 0x1);
/* get exponent: bits 62-52 */
e = SHIFT_LEFT((u16_t)b64[0], 4, 0x7F0);
e += SHIFT_RIGHT(b64[1], 4, 0xF);
/* remove bias */
e -= 1023;
/* enable "hidden" fraction bit 53 which is always 1 */
f = ((s64_t)1 << 52);
/* get fraction: bits 51-0 */
f += ((s64_t)(b64[1] & 0xF) << 48);
f += ((s64_t)b64[2] << 40);
f += ((s64_t)b64[3] << 32);
f += ((s64_t)b64[4] << 24);
f += ((s64_t)b64[5] << 16);
f += ((s64_t)b64[6] << 8);
f += b64[7];
/* handle whole number */
if (e > -1) {
/* precision overflow */
if (e > 52) {
e = 52;
}
f64->val1 = (f >> (52 - e)) * (sign ? -1 : 1);
}
/* calculate the rest of the decimal */
k = LWM2M_FLOAT64_DEC_MAX;
/* account for -e */
while (e < -1) {
k /= 2;
e++;
}
for (i = 51 - e; i >= 0; i--) {
k /= 2;
if (f & ((s64_t)1 << i)) {
f64->val2 += k;
}
}
return 0;
}

View file

@ -0,0 +1,20 @@
/*
* Copyright (c) 2018-2019 Foundries.io
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef LWM2M_UTIL_H_
#define LWM2M_UTIL_H_
#include <net/lwm2m.h>
/* convert float struct to binary format */
int lwm2m_f32_to_b32(float32_value_t *f32, u8_t *b32, size_t len);
int lwm2m_f64_to_b64(float64_value_t *f64, u8_t *b64, size_t len);
/* convert binary format to float struct */
int lwm2m_b32_to_f32(u8_t *b32, size_t len, float32_value_t *f32);
int lwm2m_b64_to_f64(u8_t *b64, size_t len, float64_value_t *f64);
#endif /* LWM2M_UTIL_H_ */