1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 """
21 Functions to convert numbers between fixed and floating point representation as
22 experienced by simulink users.
23 Permits conversion of 8, 16 and 32 bit representation of signed and unsigned
24 decimal numbers, with or without binary point.
25
26 INSTALLATION
27 ============
28
29 Package can be downloaded from the public git repo as::
30
31 $ git clone git://github.com/flyingfrog81/fixreal.git
32 $ python setup.py install
33
34 or installed automatically via pypi::
35
36 $ pip install fixreal
37
38
39 USAGE
40 =====
41
42 >>> import fixreal
43 >>> fixreal.real2fix(-0.9921875, fixreal.get_conv(8, 7, True))
44 129.0
45 >>> fixreal.real2fix(-3.96875, fixreal.get_conv(8, 5, True))
46 129.0
47 >>> fixreal.real2fix(-127, fixreal.get_conv(8, 0, True))
48 129.0
49 >>> fixreal.real2fix(1.0078125, fixreal.get_conv(8, 7, False))
50 129.0
51 >>> fixreal.real2fix(4.03125, fixreal.get_conv(8, 5, False))
52 129.0
53 >>> fixreal.real2fix(129, fixreal.get_conv(8, 0, False))
54 129.0
55 >>> fixreal.fix2real(0b10000001, fixreal.get_conv(8, 7, True))
56 -0.9921875
57 >>> fixreal.fix2real(0b10000001, fixreal.get_conv(8, 5, True))
58 -3.96875
59 >>> fixreal.fix2real(0b10000001, fixreal.get_conv(8, 0, True))
60 -127.0
61 >>> fixreal.fix2real(0b10000001, fixreal.get_conv(8, 7, False))
62 1.0078125
63 >>> fixreal.fix2real(0b10000001, fixreal.get_conv(8, 5, False))
64 4.03125
65 >>> fixreal.fix2real(0b10000001, fixreal.get_conv(8, 0, False))
66 129.0
67 >>> conv = fixreal.conv_from_name("fix_8_7")
68
69 @author: Marco Bartolini
70 @contact: marco.bartolini@gmail.com
71 @version: 0.8
72 """
73
74 import struct
75 import re
76
78 """
79 Raised when conversion types are conflicting
80 """
81 pass
82
83 -def get_conv(bits, bin_point, signed=False, scaling=1.0):
84 """
85 Creates a I{conversion structure} implented as a dictionary containing all parameters
86 needed to switch between number representations.
87 @param bits: the number of bits
88 @param bin_point: binary point position
89 @param signed: True if Fix, False if UFix
90 @param scaling: optional scaling to be applied after the conversion
91 @return: a conversion structure that can be applied in both directions of
92 conversion for the given specs.
93 """
94 conversion_t = {}
95 conversion_t["bits"] = bits
96 conversion_t["bin_point"] = bin_point
97 conversion_t["signed"] = signed
98 conversion_t["scaling"] = scaling
99 conversion_t["dec_step"] = 1.0 / (2 ** bin_point)
100
101 conversion_t["dec_mask"] = sum([2 ** i for i in range(bin_point)])
102 if bits == 8:
103 conversion_t["fmt"] = "B"
104 elif bits == 16:
105 conversion_t["fmt"] = "H"
106 elif bits == 32:
107 conversion_t["fmt"] = "I"
108 else:
109 raise ConversionError("numer of bits not supported: " + str(bits))
110 if signed:
111 _get_signed_params(conversion_t)
112 else:
113 _get_unsigned_params(conversion_t)
114 return conversion_t
115
117 """
118 Understand simulink syntax for fixed types and returns the proper
119 conversion structure.
120 @param name: the type name as in simulin (i.e. UFix_8_7 ... )
121 @raise ConversionError: When cannot decode the string
122 """
123 _match = re.match(r"^(?P<signed>u?fix)_(?P<bits>\d+)_(?P<binary>\d+)",
124 name, flags = re.I)
125 if not _match:
126 raise ConversionError("Cannot interpret name: " + name)
127 params = _match.groupdict()
128 if params['signed'] == 'fix':
129 signed = True
130 else:
131 signed = False
132 bits = int(params['bits'])
133 binary = int(params['binary'])
134 return get_conv(bits, binary, signed)
135
137 """
138 Fill the sign-dependent params of the conv structure in case of unsigned
139 conversion
140 @param conv: the structure to be filled
141 """
142 conv["sign_mask"] = 0
143 conv["int_min"] = 0
144 conv["int_mask"] = sum([2 ** i for i in range(conv["bin_point"],
145 conv["bits"])])
146 conv["int_max"] = sum([2 ** i for i in range(conv["bits"] -
147 conv["bin_point"])])
148
150 """
151 Fill the sign-dependent params of the conv structure in case of signed
152 conversion
153 @param conv: the structure to be filled
154 """
155 conv["sign_mask"] = 2 ** (conv["bits"] - 1)
156 conv["int_min"] = -1 * (2 ** (conv["bits"] - 1 - conv["bin_point"]))
157 conv["int_mask"] = sum([2 ** i
158 for i in range(conv["bin_point"], conv["bits"] - 1)])
159 conv["int_max"] = sum([2 ** i for i in range(conv["bits"] -
160 conv["bin_point"] - 1)])
161
163 """
164 Convert a 32 bit unsigned int register into the value it represents in its Fixed arithmetic form.
165 @param uval: the numeric unsigned value in simulink representation
166 @param conv: conv structure with conversion specs as generated by I{get_conv}
167 @return: the real number represented by the Fixed arithmetic defined in conv
168 @todo: Better error detection and management of unsupported operations and arguments
169 """
170 res = 0
171 int_val = ((uval & conv["int_mask"]) >> conv["bin_point"])
172 dec_val = conv["dec_step"] * (uval & conv["dec_mask"])
173 if conv["signed"] and (uval & conv["sign_mask"] > 0):
174 res = conv["int_min"] + int_val + dec_val
175 else:
176 res = int_val + dec_val
177 return (res / conv["scaling"])
178
179 -def bin2real(binary_string, conv, endianness="@"):
180 """
181 Converts a binary string representing a number to its Fixed arithmetic representation
182 @param binary_string: binary number in simulink representation
183 @param conv: conv structure containing conversion specs
184 @param endianness: optionally specify bytes endianness for unpacking
185 @return: the real number represented
186 @attention: The length of the binary string must match with the data type defined
187 in the conv structure, otherwise proper erros will be thrown by B{struct}
188 module.
189 """
190 data = struct.unpack(endianness + conv["fmt"], binary_string)[0]
191 return fix2real(data, conv)
192
194 """
195 Converts a binary stream into a sequence of real numbers
196 @param binary_stream: a binary string representing a sequence of numbers
197 @param conv: conv structure containing conversion specs
198 @param endianness: optionally specify bytes endianness for unpacking
199 @return: the list of real data represented
200 """
201 size = len(binary_stream) // (conv["bits"] // 8)
202 fmt = endianness + str(size) + conv["fmt"]
203 data = struct.unpack(fmt, binary_stream)
204 data = [fix2real(d, conv) for d in data]
205 return data
206
208 """
209 Convert a real number to its fixed representation so
210 that it can be written into a 32 bit register.
211 @param real: the real number to be converted into fixed representation
212 @param conv: conv structre with conversion specs
213 @return: the fixed representation of the real number
214 @raise ConverisonError: if conv structre can't handle the real number.
215 @todo: Better error detection and management of unsupported
216 operations and arguments
217 """
218 if not conv["signed"] and real < 0:
219 raise ConversionError("cannot convert " +
220 str(real) + " to unsigned representation")
221 if real < 0:
222 sign = 1
223 real = real - conv["int_min"]
224 else:
225 sign = 0
226 int_val, dec_val = divmod(abs(real), 1)
227 int_val = int(int_val)
228 int_val = int_val & (conv["int_mask"] >> conv["bin_point"])
229 val = int_val
230 dec = 0
231 dec = dec_val // conv["dec_step"]
232 if dec > conv["dec_mask"]:
233 dec = conv["dec_mask"]
234 dec_val = dec * conv["dec_step"]
235 val += dec_val
236 if (val - real) > (real - val + conv["dec_step"]):
237 dec -= 1
238 if sign == 1:
239 return conv["sign_mask"] + ((int_val << conv["bin_point"]) &
240 conv["int_mask"]) + dec
241 else:
242 return ((int_val << conv["bin_point"]) & conv["int_mask"]) + dec
243