ARINC429 Word Construction - python

Just as a preamble I am using python 3 and the bitstring library.
So Arinc429 words are 32 bit data words.
Bits 1-8 are used to store the label. Say for example I want the word to set the latitude, according to the label docs, set latitude is set to the octal
041
I can model this in python by doing:
label = BitArray(oct='041')
print(label.bin)
>> 000100001
The next two bits can be used to send a source, or extend the label by giving an equipment ID. Equipment IDs are given in hex, the one I wish to use is
002
So again, I add it to a new BitArray object and convert it to binary
>> 000000010
Next comes the data field which spans from bits 11-29. Say I want to set the latitude to the general area of London (51.5072). This is where I'm getting stuck as floats can only be 32/64 bits long.
There are 2 other parts of the word, but before I go there I am just wondering, if I am going along the right track, or way off how you would construct such a word?
Thanks.

I think you're on the right track, but you need to either know or decide the format for your data field.
If the 19 bits you want to represent a float are documented somewhere as being a float then look how that conversion is done (as that's not at all a standard number of bits for a floating point number). If those bits are free-form and you can choose both the encode and decode then just pick something appropriate.
There is a standard for 16-bit floats which is occasionally used, but if you only want to represent a latitude I'd go for something simpler. As it can only got from 0 to 360 just scale that to an integer from 0 to 2^19 and store the integer.
So 51.5072 becomes (51.5072/360*(2**19)) = 75012
Then store this as a unsigned integer
> latitude = BitArray(uint=75012, length=19)
This gives you a resolution of about 0.0007 degrees, which is the best you can hope for. To convert back:
> latitude.uint*360.0/2**19
51.50665283203125

Related

How to convert RGB percentage to RGB values?

Given the RGB percentages of a color, e.g. "wild watermelon", (99%, 42%, 52%), the right RGB values are (252, 108, 133).
But when I tried
rgb_percent = ('99%', '42%','52%')
rgb_values = tuple(int(int(s.strip('%')) * 2.55) for s in rgb_percent)
print(rgb_values)
[out]:
(252, 107, 132)
The color looks similar but they are not the right values. How to convert RGB percentage to RGB values correctly?
Should I be rounding up/down them instead or casting the values to integer? This seems closer but not the expected values still:
tuple(int(round(int(s.strip('%')) * 2.55, 0)) for s in rgb_percent)
[out]:
(252, 107, 133)
You cannot get exactly the same values, because you round numbers. 100 and 255 are not multiples. Your percentage RGB seems to be all integer, so you lost at minimum 1 bit of information (and possibly also 2 bits). So you cannot expect exact values on all cases. (You cannot map 100 numbers into 256 values, and get all 256 values).
But the good: you do not need it (usually). Your eyes are not so sensible (and we cannot see all combination of RGB with 256x256x256 values: our eyes can distinguish a lot less colours, so on some case you will see no differences, and on some, it is just barely noticeable). In addition some monitor panel trick displaying only 64 different shades per channel (and few people notice it). Consider that if you took data from a JPEG image, you get an additional conversion (data are not stored as RGB data, but as YCC).
To get the best choice, you need to know the original algorithm (do they round or trunc? or you should have a larger example test, and check the differences between different algorithms (e.g. how many cases are precise, how many are just 1 off, 2 off, 3 off).
Note: you have 3 possibilities: round(), math.floor(), and math.ceil(). I would avoid using int: according Python documentation, it seems the result depend on the system standard library (it could be truncated, or rounded). Note: math.trunc() and math.floor() gives the same results for positive (or zero) numbers.
In general, I would prefer round: it gives expected results for 0% and 100% (ceil is similar for these extreme values).
PS: there is not "correct RGB values". Historically RGB were given as numbers from 0 to 100 (and possibly as floating point number), or from 0.0 to 1.0. Just HTML pushed for 0 to 255, but it causes problems if you have a screens which can display more colours. And probably for RGB you mean R*G*B*, so not a linear RGB, but a OETF corrected RGB).
And no: there is not reputable sources. Nobody follows standards, so do not expect people which converted the colour channel (e.g. to 99%) are using exact the same method (we programmers are lazy). And standards tend to uses floating points. Note: internally a program may use floating point numbers, so in a colour picker you may get different colours with same displayed %-values, depending on how you input the colour. Or do you refer to a specific standard which specifies %-values? We cannot agree if black is 0,0,0 or 16,16,16 (e.g. MPEG-2, or TVs, and other limited range RGB devices), or how do do or interpret the gamma correction (ok: recent and less-recent standards are more precise, but most programs didn't get it). Also when reading your "RGB" values, in past I would assume it would be sRGB (so colour space of web and Microsoft), but since few years Apple use a different colour space (Apple P3), and other are following extending sRGB. Chaos. So just test different methods (and use your eyes to check quality).
Instead of rediscovering the nature of different color codes I researched the packages and find one usable and detailed package for this. When we look at the code of this package similar problems have been mentioned and solved. With this package with the help of bit reverse engineering I found this way:
#pip install webcolors #to install
import webcolors as wco
hex = wco.rgb_percent_to_hex(('99%','42%','52%'))
wco.hex_to_rgb(hex)
Resut is IntegerRGB(red=252, green=107, blue=133).
Although there is no rgb_percent_to_rgb function instead of this I converted percentage values to HEX and then converted them to values.
Yes, round up before casting them to int values but remember float values aren't precise so you won't always get the expected output

Convert two raw values to 32-bit IEEE floating point number

I am attempting to decode some data from a Shark 100 Power Meter via TCP modbus. I have successfully pulled down the registers that I need, and am left with two raw values from the registers like so:
[17138, 59381]
From the manual, I know that I need to convert these two numbers into a 32bit IEEE floating-point number. I also know from the manual that "The lower-addressed register is the
high order half (i.e., contains the exponent)." The first number in the list shown above is the lower-addressed register.
Using Python (any library will do if needed), how would I take these two values and make them into a 32 bit IEEE floating point value.
I have tried to use various online converters and calculators to figure out a non-programmatic way to do this, however, anything I have tried gets me a result that is way out of bounds (I am reading volts in this case so the end result should be around 120-122 from the supplied values above).
Update for Python 3.6+ (f-strings).
I am not sure why the fill in #B.Go's answer was only 2. Also, since the byte order was big-endian, I hardcoded it as such.
import struct
a = 17138
b = 59381
struct.unpack('>f', bytes.fromhex(f"{a:0>4x}" + f"{b:0>4x}"))[0]
Output: 121.45304107666016
The following code works:
import struct
a=17138
b=59381
struct.unpack('!f', bytes.fromhex('{0:02x}'.format(a) + '{0:02x}'.format(b)))
It gives
(121.45304107666016,)
Adapted from Convert hex to float and Integer to Hexadecimal Conversion in Python
I read in the comments, and #Sanju had posted this link: https://github.com/riptideio/pymodbus/blob/master/examples/common/modbus_payload.py
For anyone using pymodbus, the BinaryPayloadDecoder is useful as it's built in. It's very easy to pass a result.registers, as shown in the example. Also, it has a logging integrated, so you can help debug why a conversion isn't working (ex: wrong endianness).
As such, I made a working example for this question (using pymodbus==2.3.0):
from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder
a = 17138
b = 59381
registers = [a, b]
decoder = BinaryPayloadDecoder.fromRegisters(registers, byteorder=Endian.Big)
decoder.decode_32bit_float() # type: float
Output: 121.45304107666016

RFID Unique serial number conversion

My USB-RFID reader receives a 24 bit ID from the card. I can convert this ID into 8 bit/16bit format. But how can I convert it into 40 bit format?
Example for one tag:
24 bit decimal format (as I get from the reader): 0005966009
8,16 bit binary format (converted via Python): 01011011, 0000100010111001
8,16 bit decimal format (converted via Python): 91, 2233
40 bit decimal format (provided by the manufacturer): 455272499385
How can I get that 40 bit number from the 24 bit number?
Tag standard: unique, 125 kHz
Screenshot from manufacturer's system:
No, in general it's impossible to turn a 24 bit number into a 40 bit number. Such a conversion would imply that you add 16 bits of extra information to the 24 bit value. This extra information won't just magically appear out of nowhere.
In your case, the numbers are
24 bit format: 0005966009dec = 5B08B9hex
8+16 bit format: 091dec & 02233dec = 5Bhex & 08B9hex
40 bit format: 455272499385dec = 6A005B08B9hex
Thus, all three numbers contain 24 common bits (5B08B9hex). The 40 bit number has an additional prefix value of 6A00hex.
Since you did not reveal what RFID system and what tags you are using (other than that they are operating on 125 khz), it's impossible to tell if that prefix is some standard prefix that is the same for all tags or if that prefix changes for every tag (or every manufacturer, customer, etc.)
If the prefix is the same for all your tags, you could easily use the value 6A00hex as the upper 16 bits of each tag serial number. However, if the prefix may be different for different tags, then there is no other way than to get a reader that reads that full 40 bit serial number.
Nevertheless, if all your readers only read the 24 bit value, I don't see why you would even want to use the whole 40 bit value. Even if you already have a database that contains the whole 40 bit value, you could easily trim that value to a 24 bit value (as long as the lower 24 bits are (sufficiently) unique across all your tags).

Python plotting

I have a question about the plotting. I want to plot some data between ranges :
3825229325678980.0786812569752124806963380417361932
and
3825229325678980.078681262584097479512892231994772
but I get the following error:
Attempting to set identical bottom==top results
in singular transformations; automatically expanding.
bottom=3.82522932568e+15, top=3.82522932568e+15
How should I increase the decimal points here to solve the problem?
The difference between your min and max value is less than the precision an eps of a double (~1e-15).
Basically using a 4-byte floating point representation you can not distinguish between the two numbers.
I suggest to remove all the integer digits from your data and represent only the decimal part. The integer part is only a big constant that you can always add later.
It might be easiest to scale your data to provide a range that looks less like zero.

how to pack and unpack data using python 2.5

I know there are mutliple questions relating to my question but i'm having difficulty understating how pack & unpack works.
for example using struct.pack("!B",14) gives me value of x0e basically this is a one-byte binary.
how can i create a four-byte binary? for example struct.pack("!B",104277) should generate 0x00019755 but i cannot get this code struct.pack("!B",104277) to output 0x00019755
Background details of my problem
I'm trying to create a "Type-4 High-resolution grayscale fingerprint image" record which is part of the NIST standard. The first value of Type-4 record is Length of the record (LEN) in this case its 104277 bits, but the standard specifies that the LEN should be represented as four-byte binary. The sample data I have contains this value 0x00019755 which already has been converted to four-byte binary thus complies with standard.
reference links:
http://code.beckjohnson.com/NistRecordTypes.html
http://www.nist.gov/itl/csd/biometrics/ansi-nist.cfm
As specified in ยง7.3.2.2. Format Characters of the Python manual, the format code for an unsigned 4-byte long is L.
struct.pack("!L", 104277)

Categories

Resources