-
Notifications
You must be signed in to change notification settings - Fork 1
/
lib.h
287 lines (249 loc) · 8.52 KB
/
lib.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
//
// lib.h
// rwchcd
//
// (C) 2016-2017,2019-2020 Thibaut VARENE
// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
//
/**
* @file
* Basic functions used throughout the program, API.
*/
#ifndef rwchcd_lib_h
#define rwchcd_lib_h
#include <assert.h>
#include <limits.h> // CHAR_BIT
#include "rwchcd.h"
#include "timekeep.h"
// NB: we rely on the fact that gcc sign-extends
#define sign(x) ((x>>(sizeof(x)*CHAR_BIT-1))|1) ///< -1 if x<0, 1 if x>=0
#define zerosign(x) ((x>>(sizeof(x)*CHAR_BIT-1))|(!!x)) ///< -1 if x<0, 1 if x>0, 0 if x==0
/** Temperature integral data */
struct s_temp_intgrl {
tempdiff_t integral; ///< integral value in temp_t * seconds
temp_t last_thrsh; ///< temperature threshold for integral calculation
temp_t last_temp; ///< last recorded temperature value
timekeep_t last_time; ///< last recorded temperature time
};
/** Temperature derivative data */
struct s_temp_deriv {
tempdiff_t derivative; ///< derivative value in temp_t / timekeep_t
temp_t last_temp; ///< last recorded temperature value
timekeep_t last_time; ///< last recorded temperature time
};
float temp_to_celsius(const temp_t temp);
temp_t temp_expw_mavg(const temp_t filtered, const temp_t new_sample, const timekeep_t tau, const timekeep_t dt);
tempdiff_t temp_lin_deriv(struct s_temp_deriv * const deriv, const temp_t new_temp, const timekeep_t new_time, const timekeep_t tau);
tempdiff_t temp_thrs_intg(struct s_temp_intgrl * const intgrl, const temp_t thrsh, const temp_t new_temp, const timekeep_t new_time,
const tempdiff_t tlow_jacket, const tempdiff_t thigh_jacket);
bool lib_runmode_is_changedown(const enum e_runmode prev_runmode, const enum e_runmode new_runmode);
/**
* Convert kelvin value to internal temp_t format (Kelvin * KPRECISION).
* @note The preprocessor will do the right thing whether kelvin is a float or a native integer type.
* @param kelvin temp value in Kelvin
*/
#define kelvin_to_temp(kelvin) (temp_t)((kelvin) * KPRECISION)
/**
* Convert celsius value to internal temp_t format (Kelvin * KPRECISION).
* @note The preprocessor will do the right thing whether celsius is a float or a native integer type.
* @param celsius temp value in Celsius
*/
#define celsius_to_temp(celsius) kelvin_to_temp((celsius) + 273)
/**
* Convert temperature from internal format to integer Kelvin (rounded)
* @param temp temp value as temp_t
*/
#define temp_to_ikelvin(temp) (int)((temp + KPRECISION/2)/KPRECISION)
/**
* Convert temperature from internal format to integer Kelvin (floored)
* @param temp temp value as temp_t
*/
#define temp_to_ikelvind(temp) ((temp)/KPRECISION)
/**
* Convert a temperature delta (in Kelvin) to internal type.
* @note The preprocessor will do the right thing whether delta is a float or a native integer type.
* @param delta the delta value to be converted
*/
#define deltaK_to_temp(delta) (temp_t)((delta) * KPRECISION)
#define deltaK_to_tempdiff(delta) (tempdiff_t)((delta) * KPRECISION)
/**
* Convert delta from internal to Kelvin value.
* @note Ensure this is only used in non-fast code path (dbgmsg, config handling...).
* @param dtemp the internal delta value to be converted
*/
#define temp_to_deltaK(dtemp) ((float)((float)dtemp/KPRECISION))
/**
* Calculate the minimum time interval to use with temp_expw_mavg() for a given
* tau.
* This function 'ceils' the return value.
* @param tau target tau
* @return minimum usable time interval
*/
__attribute__((const, always_inline)) static inline timekeep_t expw_mavg_dtmin(const timekeep_t tau)
{
return ((((KPRECISION*tau)/(KPRECISION-1)) * 2 / KPRECISION) + 1);
}
/**
* Validate a temperature value
* @param temp the value to validate
* @return validation result
*/
__attribute__((const, always_inline)) static inline int validate_temp(const temp_t temp)
{
if ((temp <= RWCHCD_TEMPMIN) || (temp >= RWCHCD_TEMPMAX))
return (-EINVALID);
else
return (ALL_OK);
}
/**
* Reset an integral
* @param intgrl data to reset
*/
__attribute__((always_inline)) static inline void reset_intg(struct s_temp_intgrl * const intgrl)
{
assert(intgrl);
intgrl->integral = 0;
intgrl->last_thrsh = 0;
intgrl->last_temp = 0;
intgrl->last_time = 0;
}
/**
* Branchless saturated u32 add.
* If the result is smaller than either of the initial values (checking either is enough), overflow occurred => return all bits set.
* @param a first operand
* @param b second operand
* @return saturated a+b
*/
__attribute__((always_inline)) static inline uint32_t lib_satadd_u32(uint32_t a, uint32_t b)
{
uint32_t c = a + b;
c |= (uint32_t)-(c < a);
return (c);
}
/**
* Branchless saturated u32 sub.
* The logic is the inverse from add: we only care for underflow (overflow is not possible).
* Unsigned sub is a 2-complement add, the test is similar to add, the bitmask is simply inverted so that underflow zeroes out, otherwise we AND with all bits set (-1).
* @param a first operand
* @param b second operand
* @return saturated a-b
*/
__attribute__((always_inline)) static inline uint32_t lib_satsub_u32(uint32_t a, uint32_t b)
{
uint32_t c = a - b;
c &= (uint32_t)-(c <= a);
return (c);
}
/**
* Branchless saturated u32 mul.
* Use an intermediary 64bit var for result to check for overflow, AND with all bits set (-1) if overflow occured
* @param a first operand
* @param b second operand
* @return saturated a*b
*/
__attribute__((always_inline)) static inline uint32_t lib_satmul_u32(uint32_t a, uint32_t b)
{
uint32_t hi, lo;
uint64_t temp = a;
temp *= b;
hi = (uint32_t)(temp >> 32); // carry - i.e. overflow
lo = (uint32_t)temp;
return (lo | (uint32_t)-!!hi);
}
/**
* Branchless satured s32 add.
* Over/underflow can only happen if both operands have the same sign: then if result has opposite sign to the arguments, over/underflow occured.
* Note: INT32_MIN = INT32_MAX + 1
* @param a first operand
* @param b second operand
* @return saturated a+b
*/
__attribute__((always_inline)) static inline int32_t lib_satadd_s32(int32_t a, int32_t b)
{
uint32_t ua = (uint32_t)a;
uint32_t ub = (uint32_t)b;
uint32_t uc = ua + ub;
ua = (ua >> 31) + INT32_MAX; // calculate saturated result using sign of first operand *without* changing it
// compiler should resolve the following block branchless through a conditional move
if ((int32_t)(~(ua ^ ub) & (ua ^ uc)) < 0)
uc = ua;
return ((int32_t)uc);
}
/**
* Branchless satured s32 sub.
* Over/underflow can only happen if both operands have opposite sign: then if result has opposite sign to the first arg, over/underflow occured.
* Note: INT32_MIN = INT32_MAX + 1
* @param a first operand
* @param b second operand
* @return saturated a-b
*/
__attribute__((always_inline)) static inline int32_t lib_satsub_s32(int32_t a, int32_t b)
{
uint32_t ua = (uint32_t)a;
uint32_t ub = (uint32_t)b;
uint32_t uc = ua - ub;
ua = (ua >> 31) + INT32_MAX;
if ((int32_t)((ua ^ ub) & (ua ^ uc)) < 0)
uc = ua;
return ((int32_t)uc);
}
/**
* Fixed-point signed 32bit multiplication.
* @param a first operand
* @param b second operand
* @param scale fixed-point scaling factor
* @return signed fixed-point a*b
*/
__attribute__((always_inline)) static inline int32_t lib_fpmul_s32(const int32_t a, const int32_t b, const uint32_t scale)
{
int64_t temp = a;
temp *= b;
temp /= scale;
return ((int32_t)temp);
}
/**
* Fixed-point unsigned 32bit multiplication.
* @param a first operand
* @param b second operand
* @param scale fixed-point scaling factor
* @return unsigned fixed-point a*b
*/
__attribute__((always_inline)) static inline uint32_t lib_fpmul_u32(const uint32_t a, const uint32_t b, const uint32_t scale)
{
uint64_t temp = a;
temp *= b;
temp /= scale;
return ((uint32_t)temp);
}
/**
* Fixed-point signed 32bit division.
* @param n numerator
* @param d denominator
* @param scale fixed-point scaling factor
* @return signed fixed-point n/d
*/
__attribute__((always_inline)) static inline int32_t lib_fpdiv_s32(const int32_t n, const int32_t d, const uint32_t scale)
{
int64_t temp = n;
temp *= scale;
temp /= d;
return ((int32_t)temp);
}
/**
* Fixed-point unsigned 32bit division.
* @param n numerator
* @param d denominator
* @param scale fixed-point scaling factor
* @return unsigned fixed-point n/d
*/
__attribute__((always_inline)) static inline uint32_t lib_fpdiv_u32(const uint32_t n, const uint32_t d, const uint32_t scale)
{
uint64_t temp = n;
temp *= scale;
temp /= d;
return ((uint32_t)temp);
}
#define LIB_DERIV_FPDEC 0x8000
#define temp_expw_deriv_mul(_a, _b) lib_fpmul_s32(_a, _b, LIB_DERIV_FPDEC)
#define temp_expw_deriv_val(_deriv) ((_deriv)->derivative)
#endif /* rwchcd_lib_h */