Bluetooth GATT: Set value and flag field correct - bluetooth

i have some problems by understanding the flag field in Bluetooth characteristics.
For example the heart rate measurement characteristic:
And its flags:
According to my understanding, the first part of the value must contain the flags.
For example 0x06 for:
Heart Rate Value Format is set to uint8
Sensor Contact detected = true
Sensor Contact Supported = true
The second part of the value is then byte(Heartrate).
In Python i fill the value like this:
value = []
value.append(dbus.Byte(0x06))
value.append(dbus.Byte(randint(90, 130)))
The whole thing also works perfectly. When I connect to the server with the app nRF connect I get all the info perfectly displayed with all the info.
Now about my problem:
I tried to implement the Weight Measurement Characteristic.
I want Weight in kg, BMI and height. So for my understanding i have to fill flag field with 0x08 for 00001000.
In Python it will look like this:
value = []
value.append(dbus.Byte(0x08))
value.append(dbus.Byte(randint(1, 13))) #weight
value.append(dbus.Byte(randint(1, 25))) #BMI
value.append(dbus.Byte(randint(1, 25))) #height
Now i get in nRF Connect App the message Invalid Data Syntax.
My Questions are:
How to handle with the resolution 0.0001? Value = Height/0.0001 or Height*0.0001?
What is meant by represented values M = 1, d=-1, ...?
Why is my Value in the second python code invalid?
Thank you very very much for your help!
I'm using bluez5.63/test/example-gatt-server.py for my Server!

The usual way to build the value for a characteristic is to use the Python struct library to pack that values into bytes.
The values sent in the characteristics are always bytes which can only represent integers. So to turn the height value to an integer it is saying that for every increment of 1 on the field value, the hight value goes up by 0.001. This means the decimal point needs to move 3 places to the right on the height value sent. So to send a height value of 0.001 you will actually send a value of 1. This means your messurment needs to be multiplied by value * 10**3 or if you prefer value / 0.001.
For weight it is similar but in addition to moving the decimal point you also have to change the value. This can be value / 0.005 or value * (1 / 5) * 10 ** 3
An example of how the python code might look:
import struct
weight_char = struct.Struct('<BHHH')
def pack_flags(units, timestamp, user_id, bmi_height):
flags = 0
for idx, _flag in enumerate((units, timestamp, user_id, bmi_height)):
flags |= _flag << idx
return flags
flags = pack_flags(False, False, False, True)
weight = 42.4 # example weight in KG
height = 1.49 # Height in meters
bmi = 20.1
value = weight_char.pack(flags,
int(weight * 0.2 * 10**3),
int(bmi * 10**1),
int(height * 10**3))
print(f"Value to send: {value.hex()}")
Which gives the output of:
Value to send: 082021c900d205

Related

What is the difference between cv2.addWeighted and numpy mean, in this case?

Suppose I have two OpenCV (python package cv2) loaded grayscale images img1 and img2, both of same dimensions. Now, I wish to take the mean of both img1 and img2. Here are two ways to do it:
# Method 1
mean = (img1 * 0.5) + (img2 * 0.5)
# Method 2
mean = cv2.addWeighted(img1,0.5,img2,0.5,0)
However, mean is visually different in both methods, when I display them using cv2.imshow. Why is this so?
I am glad that you have found a working solution to your problem, but this seems to be a workaround. The real reason for this behaviour lies somewhere else. The problem here is that mean = (img1 * 0.5) + (img2 * 0.5) is returning a matrix with float32 data type which contains values in range 0.0 - 255.0. You can verify this by using print mean.dtype. Since the new matrix values have been converted to float unintentionally, we can revert this operation by using (img_1 * 0.5 + img_2 * 0.5).astype("uint8"). In case of cv2.addWeighted() it automatically returns you a matrix of data type uint8 and all things would work fine.
My concern is with the conclusion that you have drawn:
The issue is that the cv2.imshow() method used to display images,
expects your image arrays to be normalized, i.e. in the range [0,1].
cv2.imshow() works just fine with range of [0-255] and [0.0-1.0], but the issue arises when you pass a matrix whose values are in range [0-255], but the dtype is float32 instead of uint8.
Answering my own question, to help others who get confused by this:
Both methods 1 and 2 yield the same result. You can verify this by writing the mean image to disk using cv2.imwrite. The issue is not with the methods.
The issue is that the cv2.imshow method used to display images, expects your image arrays to be normalized, i.e. in the range [0,1]. In my case, both the image arrays are 8-bit unsigned integers and so, its pixel values are in the range [0,255]. Since mean is an average of the two arrays, its pixel values are also in the range [0,255]. So when I passed mean to cv2.imshow, pixels having values greater than 1 were interpreted as having a value of 255, resulting in vastly different visuals.
The solution is to normalize mean before passing it to cv2.imshow:
# Method 1
mean = (img1 * 0.5) + (img2 * 0.5)
# Method 2
mean = cv2.addWeighted(img1,0.5,img2,0.5,0)
# Note that the division by 255 results in the image array values being squeezed to [0,1].
cv2.imshow("Averaged", mean/255.)

Retreiving neighbors with geohash algorithm?

I am looking at a pythonic implementation of this top rated accepted answer on GIS SE - Using geohash for proximity searches? and I am unable to retrieve any matches for my geohash query. Here is the approach I have tried so far.
To run this Minimum Verifiable Complete Example(MVCE) you need to download the following files - geohash int
and sortedlist python and install the python sortedlist via pip. You also need to have the latest version of Cython installed on your machine so as to wrap the C functionality of geohash-int(Note I am only wrapping what is necessary for this MVCE).
geohash_test.py
# GeoHash is my Cython wrapper of geohash-int C package
from geo import GeoHash
from sortedcontainers import SortedList
import numpy as np
def main():
# Bounding coordinates of my grid.
minLat = 27.401436
maxLat = 62.54858
minLo = -180.0
maxLo = 179.95000000000002
latGrid = np.arange(minLat,maxLat,0.05)
lonGrid = np.arange(minLo,maxLo,0.05)
geoHash = GeoHash()
# Create my own data set of points with a resolution of
# 0.05 in the latitude and longitude direction.
gridLon,gridLat = np.meshgrid(lonGrid,latGrid)
grid_points = np.c_[gridLon.ravel(),gridLat.ravel()]
sl = SortedList()
#Store my grid points in the best resolution possible i.e. 52(First step in accepted answer)
for grid_point in grid_points:
lon = grid_point[0]
lat = grid_point[1]
geohash = geoHash.encode(lat,lon,52)
bitsOriginal = geohash["bits"]
sl.add(bitsOriginal)
#Derive the minimum and maximum value for the range query from method below
minValue,maxValue = getMinMaxForQueryGeoHash(geoHash)
# Do the actual range query with a sorted list
it = sl.irange(minValue,maxValue,inclusive=(False,False))
print(len(list(it)))
def getMinMaxForQueryGeoHash(geoHash):
lonTest = 172.76843
latTest = 61.560745
#Query geohash encoded at resolution 26 because my search area
# is around 10 kms.(Step 2 and 3 in accepted answer)
queryGeoHash = geoHash.encode(latTest,lonTest,26)
# Step 4 is getting the neighbors for query geohash
neighbors = geoHash.get_neighbors(queryGeoHash)
bitsList = []
for key,value in neighbors.items():
bitsList.append(value["bits"])
#Step 5 from accepted answer
bitsList.append(queryGeoHash["bits"])
# Step 6 We need 1 to all the neighbors
newList = [x+1 for x in bitsList]
joinedList = bitsList + newList
#Step 7 Left bit shift this to 52
newList2 = [x <<26 for x in joinedList]
#Return min and max value to main method
minValue = min(newList2)
maxValue = max(newList2)
return minValue,maxValue
main()
If one were to write this out as a pseudocode here is what I am doing
Given my bounding box which is a grid I store it in the highest resolution possible by computing the geohash for each latitude and longitude(this happens to be bit depth 52)
I add the geohash to a sorted list
Then I would like to do a range query by specifying a search radius of 10 kms for a specific query coordinate
From the accepted answer to do this you need the min and max value for a query geohash
I calculate the min and max value in the method getMinMaxForQueryGeoHash
Calculate the query geohash at bit depth 26(this is the radius of 10 kms)
Calculate the neighbors of the query geohash and create the 18 member array
The 18 members are the 8 neighbors returned from the C method plus the original query geohash and the remaining 9 are obtained by adding 1 to this array
Then left bit shift this array by 26 and return the min and max value to the irange query of sorted list.
Bit shift = 52(maximum resolution) - query geohash precision(26) = 26
But that query returns me a NULL. Could somebody explain where I am going wrong ?
using your jargon: for a MVCE, you not need a complex two-languages implementations. There are a lot of simple good implementations of Geohash, some in 100% Python (example). All them use the Morton Curve (example).
Conclusion: try to plug-and-play a pure-Python implementation, first test encode/decode, them test the use of neighbors(geohash) function.

Translating Pseudocode steps into Python algorithm

I'm entirely new to programming and I'm supposed to turn pseudocode into a Python algorithm for a class assignment. I've tested mine algorithm (if you can even call it that) a few too many times and keep coming up with error messages. Any suggestions or resources that might be able to help would be greatly appreciated!
Pseudocode Order:
Declare Real radius
Declare Real area
Display “ Enter value for radius : “
Input radius
Set area = 3.14 * radius * radius
Display Area
Attempted Code:
radius = 1.0
Area = 1.0
print(" Enter value for radius : ")
radius = input(" Enter value for radius : ")
Area = 3.14 * radius * radius
print(Area)
and the error:
TypeError: can't multiply sequence by non-int of type 'float'
input() returns a string, thus your TypeError. You tried to multiply a string by a float.
Updated Code here:
radius = 1.0
print("Enter value for radius : ")
radius = input()
print(type(radius))
Area = 3.14 * (float(radius) * float(radius))
print(Area)
Output:
Enter value for radius :
5
<class 'str'>
78.5
The best way to do this is:
import math
radius = input("Enter a radius: ")
area = math.pi * radius ** 2
print("The area is: " + str(area) + "cm squared.")
A few things happen here:
On the first line we import the math module, which contains a bunch of values (like π) and lots of methods (like tan). For more on modules, take a look here.
On the second line, we ask for the radius. Note that unlike lower level programming languages, we don't have to initialise it. Python figures out that it is an float (a decimal) by itself. EDIT: If you are using python 2, you do have to cast, just as Damien pointed out, by using radius = float(input("Enter an area: ))
On line three we set the area equal to πr^2. We call the math.pi value, which is very precise, then we multiply that by r ^ 2 (in python if we want to a to the power of b we write a ** b)
On line 4 we print the area as a String. Note that we have to cast the float area to be a string using the str() function. This is basically Java's easy way to print anything that isn't a string as a string (a collection of characters).
Hope that helps!
Well, I will add some explain to this:
radius = 1.0 #this is not mandatory, you can create the variable and assign the value in the same moment
area = 1.0
radius = float(input(" Enter value for radius : ")) #here is so important to convert the input into a float, that's the other error you had
area = 3.14 * radius * radius t isn't working
print(area)

MODBUS-tk Read floating point values from slave in the master

I have created modbus slave to write data to the registers.
I am able to write both float values and integer values from the slave side.
In the modbus master I am able to access only the integer values but not able to read float values.
I went through this https://github.com/ljean/modbus-tk/issues/72 but that didn't solve my problem.
For the integer values reading I can use the below code and read the values.
master = modbus_tcp.TcpMaster()
master.set_timeout(time_out_period)
result = master.execute(slave = 100, function_code = 3 , starting_address = 0, quantity_of_x = 25)
But for the float values I used both the above and below code.
master = modbus_tcp.TcpMaster()
master.set_timeout(time_out_period)
result = master.execute(slave = 100, function_code = 3 , starting_address = 0, quantity_of_x = 25 , data_format='>f')
I get error while reading the float as,
unpack requires a bytes object of length 4
The quantity of x should be a multiple of 2. Because the float requires two 16 bit registers or words so if you want 25 it should be 50.
You also need to provide the correct data format reflective of how many individual float values(below are big endian) are trying to be unpacked;
1 float
logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 2, data_format='>f'))
2 floats
logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 4, data_format='>ff'))
3 floats
logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 6, data_format='>fff'))
It's easy, using Numpy.
For example:
import numpy as np
# Sample registers to read
start_address = 0
items = 10
# Get the reply from slave
reply = master.execute(1, cst.READ_HOLDING_REGISTERS, start_address, items*2)
# Convert the reply to Numpy array of type int16
d16 = np.array(reply, dtype=np.int16)
# Convert to an array of type float32
f32 = d16.view(dtype = np.float32)

Un/pack additional set of UV coordinates into a 32bit RGBA field

I'm modding a game called Mount&Blade, currently trying to implement lightmapping through custom shaders.
As the in-game format doesn't allows more than one UV map per model and I need to carry the info of a second, non-overlapping parametrization somewhere, a field of four uints (RGBA, used for per-vertex coloring) is my only possibility.
At first thought about just using U,V=R,G but the precision isn't good enough.
Now I'm trying to encode them with the maximum precision available, using two fields (16bit) per coordinate. Snip of my Python exporter:
def decompose(a):
a=int(a*0xffff) #fill the entire range to get the maximum precision
aa =(a&0xff00)>>8 #decompose the first half and save it as an 8bit uint
ab =(a&0x00ff) #decompose the second half
return aa,ab
def compose(na,nb):
return (na<<8|nb)/0xffff
I'd like to know how to do the second part (composing, or unpacking it) in HLSL (DX9, shader model 2.0). Here's my try, close, but doesn't works:
//compose UV from n=(na<<8|nb)/0xffff
float2 thingie = float2(
float( ((In.Color.r*255.f)*256.f)+
(In.Color.g*255.f) )/65535.f,
float( ((In.Color.b*255.f)*256.f)+
(In.Color.w*255.f) )/65535.f
);
//sample the lightmap at that position
Output.RGBColor = tex2D(MeshTextureSamplerHQ, thingie);
Any suggestion or ingenious alternative is welcome.
Remember to normalize aa and ab after you decompose a.
Something like this:
(u1, u2) = decompose(u)
(v1, v2) = decompose(v)
color.r = float(u1) / 255.f
color.g = float(u2) / 255.f
color.b = float(v1) / 255.f
color.a = float(v2) / 255.f
The pixel shader:
float2 texc;
texc.x = (In.Color.r * 256.f + In.Color.g) / 257.f;
texc.y = (In.Color.b * 256.f + In.Color.a) / 257.f;

Resources