Paparazzi UAS  v7.0_unstable
Paparazzi is a free software Unmanned Aircraft System.
stabilization_attitude_rc_setpoint.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2012-2013 Felix Ruess <felix.ruess@gmail.com>
3  *
4  * This file is part of paparazzi.
5  *
6  * paparazzi is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * paparazzi is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with paparazzi; see the file COPYING. If not, write to
18  * the Free Software Foundation, 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
27 #include "generated/airframe.h"
28 
30 #include "state.h"
33 #include "mcu_periph/sys_time.h"
34 
35 #ifndef STABILIZATION_ATTITUDE_DEADBAND_A
36 #define STABILIZATION_ATTITUDE_DEADBAND_A 0
37 #endif
38 
39 #ifndef STABILIZATION_ATTITUDE_DEADBAND_E
40 #define STABILIZATION_ATTITUDE_DEADBAND_E 0
41 #endif
42 
49 #ifndef COORDINATED_TURN_AIRSPEED
50 #define COORDINATED_TURN_AIRSPEED 12.0
51 #endif
52 
53 #define YAW_DEADBAND_EXCEEDED(_rc) \
54  (rc->values[RADIO_YAW] > STABILIZATION_ATTITUDE_DEADBAND_R || \
55  rc->values[RADIO_YAW] < -STABILIZATION_ATTITUDE_DEADBAND_R)
56 
57 static int32_t get_rc_roll(struct RadioControl *rc)
58 {
59  const int32_t max_rc_phi = (int32_t) ANGLE_BFP_OF_REAL(STABILIZATION_ATTITUDE_SP_MAX_PHI);
60  int32_t roll = rc->values[RADIO_ROLL];
61  DeadBand(roll, STABILIZATION_ATTITUDE_DEADBAND_A);
62  return roll * max_rc_phi / (MAX_PPRZ - STABILIZATION_ATTITUDE_DEADBAND_A);
63 }
64 
65 static int32_t get_rc_pitch(struct RadioControl *rc)
66 {
67  const int32_t max_rc_theta = (int32_t) ANGLE_BFP_OF_REAL(STABILIZATION_ATTITUDE_SP_MAX_THETA);
68  int32_t pitch = rc->values[RADIO_PITCH];
69  DeadBand(pitch, STABILIZATION_ATTITUDE_DEADBAND_E);
70  return pitch * max_rc_theta / (MAX_PPRZ - STABILIZATION_ATTITUDE_DEADBAND_E);
71 }
72 
73 static int32_t get_rc_yaw(struct RadioControl *rc)
74 {
75  const int32_t max_rc_r = (int32_t) ANGLE_BFP_OF_REAL(STABILIZATION_ATTITUDE_SP_MAX_R);
76  int32_t yaw = rc->values[RADIO_YAW];
77  DeadBand(yaw, STABILIZATION_ATTITUDE_DEADBAND_R);
78  return yaw * max_rc_r / (MAX_PPRZ - STABILIZATION_ATTITUDE_DEADBAND_R);
79 }
80 
81 static float get_rc_roll_f(struct RadioControl *rc)
82 {
83  int32_t roll = rc->values[RADIO_ROLL];
84  DeadBand(roll, STABILIZATION_ATTITUDE_DEADBAND_A);
85  return roll * STABILIZATION_ATTITUDE_SP_MAX_PHI / (MAX_PPRZ - STABILIZATION_ATTITUDE_DEADBAND_A);
86 }
87 
88 static float get_rc_pitch_f(struct RadioControl *rc)
89 {
90  int32_t pitch = rc->values[RADIO_PITCH];
91  DeadBand(pitch, STABILIZATION_ATTITUDE_DEADBAND_E);
92  return pitch * STABILIZATION_ATTITUDE_SP_MAX_THETA / (MAX_PPRZ - STABILIZATION_ATTITUDE_DEADBAND_E);
93 }
94 
95 static inline float get_rc_yaw_f(struct RadioControl *rc)
96 {
97  int32_t yaw = rc->values[RADIO_YAW];
98  DeadBand(yaw, STABILIZATION_ATTITUDE_DEADBAND_R);
99  return yaw * STABILIZATION_ATTITUDE_SP_MAX_R / (MAX_PPRZ - STABILIZATION_ATTITUDE_DEADBAND_R);
100 }
101 
103 {
104  float_quat_identity(&rc_sp->rc_quat);
106  rc_sp->care_free_heading = 0.f;
107  rc_sp->transition_theta_offset = 0.f;
108  rc_sp->last_ts = 0.f;
109 }
110 
119 void stabilization_attitude_read_rc_setpoint(struct AttitudeRCInput *rc_sp, bool in_flight, bool in_carefree,
120  bool coordinated_turn, struct RadioControl *rc)
121 {
122  // FIXME: remove me, do in quaternion directly
123  // is currently still needed, since the yaw setpoint integration is done in eulers
124  stabilization_attitude_read_rc_setpoint_eulers_f(rc_sp, in_flight, in_carefree, coordinated_turn, rc);
125 
126  struct FloatQuat q_rp_cmd;
128 
129  /* get current heading */
130  const struct FloatVect3 zaxis = {0., 0., 1.};
131  struct FloatQuat q_yaw;
132 
133  //Care Free mode
134  if (in_carefree) {
135  //care_free_heading has been set to current psi when entering care free mode.
136  float_quat_of_axis_angle(&q_yaw, &zaxis, rc_sp->care_free_heading);
137  } else {
138  float_quat_of_axis_angle(&q_yaw, &zaxis, stateGetNedToBodyEulers_f()->psi);
139  }
140 
141  /* roll/pitch commands applied to to current heading */
142  struct FloatQuat q_rp_sp;
143  float_quat_comp(&q_rp_sp, &q_yaw, &q_rp_cmd);
144  float_quat_normalize(&q_rp_sp);
145 
146  if (in_flight) {
147  /* get current heading setpoint */
148  struct FloatQuat q_yaw_sp;
149  float_quat_of_axis_angle(&q_yaw_sp, &zaxis, rc_sp->rc_eulers.psi);
150 
151  /* rotation between current yaw and yaw setpoint */
152  struct FloatQuat q_yaw_diff;
153  float_quat_comp_inv(&q_yaw_diff, &q_yaw_sp, &q_yaw);
154 
155  /* compute final setpoint with yaw */
156  float_quat_comp_norm_shortest(&rc_sp->rc_quat, &q_rp_sp, &q_yaw_diff);
157  } else {
158  QUAT_COPY(rc_sp->rc_quat, q_rp_sp);
159  }
160 }
161 
170  bool in_carefree, bool coordinated_turn, struct RadioControl *rc)
171 {
172  // FIXME: remove me, do in quaternion directly
173  // is currently still needed, since the yaw setpoint integration is done in eulers
174  stabilization_attitude_read_rc_setpoint_eulers_f(rc_sp, in_flight, in_carefree, coordinated_turn, rc);
175 
176  const struct FloatVect3 zaxis = {0., 0., 1.};
177 
178  struct FloatQuat q_rp_cmd;
180 
181  if (in_flight) {
182  /* get current heading setpoint */
183  struct FloatQuat q_yaw_sp;
184  float_quat_of_axis_angle(&q_yaw_sp, &zaxis, rc_sp->rc_eulers.psi);
185  float_quat_comp(&rc_sp->rc_quat, &q_yaw_sp, &q_rp_cmd);
186  } else {
187  struct FloatQuat q_yaw;
188  float_quat_of_axis_angle(&q_yaw, &zaxis, stateGetNedToBodyEulers_f()->psi);
189 
190  /* roll/pitch commands applied to to current heading */
191  struct FloatQuat q_rp_sp;
192  float_quat_comp(&q_rp_sp, &q_yaw, &q_rp_cmd);
193  float_quat_normalize(&q_rp_sp);
194 
195  QUAT_COPY(rc_sp->rc_quat, q_rp_sp);
196  }
197 }
198 
201 {
203  rc_sp->rc_quat = *stateGetNedToBodyQuat_f();
204 }
205 
208 {
210 }
211 
212 /* This is a different way to obtain yaw. It will not switch when going beyond 90 degrees pitch.
213  However, when rolling more then 90 degrees in combination with pitch it switches. For a
214  transition vehicle this is better as 90 degrees pitch will occur, but more than 90 degrees roll probably not. */
216 {
217  struct Int32Eulers *att = stateGetNedToBodyEulers_i();
219  if (abs(att->phi) < INT32_ANGLE_PI_2) {
220  int32_t sin_theta;
221  PPRZ_ITRIG_SIN(sin_theta, att->theta);
222  heading = att->psi - INT_MULT_RSHIFT(sin_theta, att->phi, INT32_TRIG_FRAC);
223  } else if (ANGLE_FLOAT_OF_BFP(att->theta) > 0) {
224  heading = att->psi - att->phi;
225  } else {
226  heading = att->psi + att->phi;
227  }
228  return heading;
229 }
230 
232 {
233  struct FloatEulers *att = stateGetNedToBodyEulers_f();
234  float heading;
235  if (fabsf(att->phi) < M_PI / 2) {
236  heading = att->psi - sinf(att->theta) * att->phi;
237  } else if (att->theta > 0) {
238  heading = att->psi - att->phi;
239  } else {
240  heading = att->psi + att->phi;
241  }
242  return heading;
243 }
244 
245 
256  bool in_carefree, bool coordinated_turn, struct RadioControl *rc)
257 {
258  struct Int32Eulers sp_i;
259  EULERS_BFP_OF_REAL(sp_i, rc_sp->rc_eulers);
260 
261  sp_i.phi = get_rc_roll(rc);
262  sp_i.theta = get_rc_pitch(rc);
263 
264  if (in_flight) {
265  /* calculate dt for yaw integration */
266  float dt = get_sys_time_float() - rc_sp->last_ts;
267  /* make sure nothing drastically weird happens, bound dt to 0.5sec */
268  Bound(dt, 0, 0.5);
269 
270  /* do not advance yaw setpoint if within a small deadband around stick center or if throttle is zero */
272  sp_i.psi += get_rc_yaw(rc) * dt;
273  INT32_ANGLE_NORMALIZE(sp_i.psi);
274  }
275  if (coordinated_turn) {
276  //Coordinated turn
277  //feedforward estimate angular rotation omega = g*tan(phi)/v
278  int32_t omega;
279  const int32_t max_phi = ANGLE_BFP_OF_REAL(RadOfDeg(60.0));
280  if (abs(sp_i.phi) < max_phi) {
281  omega = ANGLE_BFP_OF_REAL(9.81 / COORDINATED_TURN_AIRSPEED * tanf(ANGLE_FLOAT_OF_BFP(sp_i.phi)));
282  } else { //max 60 degrees roll
283  omega = ANGLE_BFP_OF_REAL(9.81 / COORDINATED_TURN_AIRSPEED * 1.72305 * ((sp_i.phi > 0) - (sp_i.phi < 0)));
284  }
285 
286  sp_i.psi += omega * dt;
287  }
288 #ifdef STABILIZATION_ATTITUDE_SP_PSI_DELTA_LIMIT
289  // Make sure the yaw setpoint does not differ too much from the real yaw
290  // to prevent a sudden switch at 180 deg
291  const int32_t delta_limit = ANGLE_BFP_OF_REAL(STABILIZATION_ATTITUDE_SP_PSI_DELTA_LIMIT);
292 
294 
295  int32_t delta_psi = sp_i.psi - heading;
296  INT32_ANGLE_NORMALIZE(delta_psi);
297  if (delta_psi > delta_limit) {
298  sp_i.psi = heading + delta_limit;
299  } else if (delta_psi < -delta_limit) {
300  sp_i.psi = heading - delta_limit;
301  }
302  INT32_ANGLE_NORMALIZE(sp_i.psi);
303 #endif
304  //Care Free mode
305  if (in_carefree) {
306  //care_free_heading has been set to current psi when entering care free mode.
307  int32_t cos_psi;
308  int32_t sin_psi;
309  int32_t temp_theta;
310  int32_t care_free_delta_psi_i;
311 
312  care_free_delta_psi_i = sp_i.psi - ANGLE_BFP_OF_REAL(rc_sp->care_free_heading);
313 
314  INT32_ANGLE_NORMALIZE(care_free_delta_psi_i);
315 
316  PPRZ_ITRIG_SIN(sin_psi, care_free_delta_psi_i);
317  PPRZ_ITRIG_COS(cos_psi, care_free_delta_psi_i);
318 
319  temp_theta = INT_MULT_RSHIFT(cos_psi, sp_i.theta, INT32_ANGLE_FRAC) - INT_MULT_RSHIFT(sin_psi, sp_i.phi,
321  sp_i.phi = INT_MULT_RSHIFT(cos_psi, sp_i.phi, INT32_ANGLE_FRAC) - INT_MULT_RSHIFT(sin_psi, sp_i.theta, INT32_ANGLE_FRAC);
322 
323  sp_i.theta = temp_theta;
324  }
325  } else { /* if not flying, use current yaw as setpoint */
326  sp_i.psi = stateGetNedToBodyEulers_i()->psi;
327  }
328 
329  /* update timestamp for dt calculation */
330  rc_sp->last_ts = get_sys_time_float();
331  EULERS_FLOAT_OF_BFP(rc_sp->rc_eulers, sp_i);
332  return sp_i;
333 }
334 
335 
346  bool in_carefree, bool coordinated_turn, struct RadioControl *rc)
347 {
348  rc_sp->rc_eulers.phi = get_rc_roll_f(rc);
349  rc_sp->rc_eulers.theta = get_rc_pitch_f(rc);
350 
351  if (in_flight) {
352  /* calculate dt for yaw integration */
353  float dt = get_sys_time_float() - rc_sp->last_ts;
354  /* make sure nothing drastically weird happens, bound dt to 0.5sec */
355  Bound(dt, 0, 0.5);
356 
357  /* do not advance yaw setpoint if within a small deadband around stick center or if throttle is zero */
359  rc_sp->rc_eulers.psi += get_rc_yaw_f(rc) * dt;
360  FLOAT_ANGLE_NORMALIZE(rc_sp->rc_eulers.psi);
361  }
362  if (coordinated_turn) {
363  //Coordinated turn
364  //feedforward estimate angular rotation omega = g*tan(phi)/v
365  float omega;
366  const float max_phi = RadOfDeg(60.0);
367  if (fabsf(rc_sp->rc_eulers.phi) < max_phi) {
368  omega = 9.81 / COORDINATED_TURN_AIRSPEED * tanf(rc_sp->rc_eulers.phi);
369  } else { //max 60 degrees roll
370  omega = 9.81 / COORDINATED_TURN_AIRSPEED * 1.72305 * ((rc_sp->rc_eulers.phi > 0) - (rc_sp->rc_eulers.phi < 0));
371  }
372 
373  rc_sp->rc_eulers.psi += omega * dt;
374  }
375 #ifdef STABILIZATION_ATTITUDE_SP_PSI_DELTA_LIMIT
376  // Make sure the yaw setpoint does not differ too much from the real yaw
377  // to prevent a sudden switch at 180 deg
379 
380  float delta_psi = rc_sp->rc_eulers.psi - heading;
381  FLOAT_ANGLE_NORMALIZE(delta_psi);
382  if (delta_psi > STABILIZATION_ATTITUDE_SP_PSI_DELTA_LIMIT) {
383  rc_sp->rc_eulers.psi = heading + STABILIZATION_ATTITUDE_SP_PSI_DELTA_LIMIT;
384  } else if (delta_psi < -STABILIZATION_ATTITUDE_SP_PSI_DELTA_LIMIT) {
385  rc_sp->rc_eulers.psi = heading - STABILIZATION_ATTITUDE_SP_PSI_DELTA_LIMIT;
386  }
387  FLOAT_ANGLE_NORMALIZE(rc_sp->rc_eulers.psi);
388 #endif
389  //Care Free mode
390  if (in_carefree) {
391  //care_free_heading has been set to current psi when entering care free mode.
392  float cos_psi;
393  float sin_psi;
394  float temp_theta;
395 
396  float care_free_delta_psi_f = rc_sp->rc_eulers.psi - rc_sp->care_free_heading;
397 
398  FLOAT_ANGLE_NORMALIZE(care_free_delta_psi_f);
399 
400  sin_psi = sinf(care_free_delta_psi_f);
401  cos_psi = cosf(care_free_delta_psi_f);
402 
403  temp_theta = cos_psi * rc_sp->rc_eulers.theta - sin_psi * rc_sp->rc_eulers.phi;
404  rc_sp->rc_eulers.phi = cos_psi * rc_sp->rc_eulers.phi - sin_psi * rc_sp->rc_eulers.theta;
405 
406  rc_sp->rc_eulers.theta = temp_theta;
407  }
408  } else { /* if not flying, use current yaw as setpoint */
409  rc_sp->rc_eulers.psi = stateGetNedToBodyEulers_f()->psi;
410  }
411 
412  /* update timestamp for dt calculation */
413  rc_sp->last_ts = get_sys_time_float();
414  return rc_sp->rc_eulers;
415 }
416 
417 
424 {
425  /* orientation vector describing simultaneous rotation of roll/pitch */
426  struct FloatVect3 ov;
427  ov.x = get_rc_roll_f(rc);
428  ov.y = get_rc_pitch_f(rc);
429  ov.z = 0.0;
430 
431  /* quaternion from that orientation vector */
433 }
434 
442 {
443  /* only non-zero entries for roll quaternion */
444  float roll2 = get_rc_roll_f(rc) / 2.0f;
445  float qx_roll = sinf(roll2);
446  float qi_roll = cosf(roll2);
447 
448  //An offset is added if in forward mode
449  /* only non-zero entries for pitch quaternion */
450  float pitch2 = (theta_offset + get_rc_pitch_f(rc)) / 2.0f;
451  float qy_pitch = sinf(pitch2);
452  float qi_pitch = cosf(pitch2);
453 
454  /* only multiply non-zero entries of float_quat_comp(q, &q_roll, &q_pitch) */
455  q->qi = qi_roll * qi_pitch;
456  q->qx = qx_roll * qi_pitch;
457  q->qy = qi_roll * qy_pitch;
458  q->qz = qx_roll * qy_pitch;
459 }
460 
float phi
in radians
float theta
in radians
float psi
in radians
static void float_quat_normalize(struct FloatQuat *q)
#define FLOAT_ANGLE_NORMALIZE(_a)
void float_quat_of_axis_angle(struct FloatQuat *q, const struct FloatVect3 *uv, float angle)
Quaternion from unit vector and angle.
static void float_quat_identity(struct FloatQuat *q)
initialises a quaternion to identity
void float_quat_comp_norm_shortest(struct FloatQuat *a2c, struct FloatQuat *a2b, struct FloatQuat *b2c)
Composition (multiplication) of two quaternions with normalization.
void float_quat_comp_inv(struct FloatQuat *a2b, struct FloatQuat *a2c, struct FloatQuat *b2c)
Composition (multiplication) of two quaternions.
#define FLOAT_EULERS_ZERO(_e)
void float_quat_of_orientation_vect(struct FloatQuat *q, const struct FloatVect3 *ov)
Quaternion from orientation vector.
void float_quat_comp(struct FloatQuat *a2c, struct FloatQuat *a2b, struct FloatQuat *b2c)
Composition (multiplication) of two quaternions.
euler angles
Roation quaternion.
#define EULERS_BFP_OF_REAL(_ei, _ef)
Definition: pprz_algebra.h:715
#define QUAT_COPY(_qo, _qi)
Definition: pprz_algebra.h:596
#define EULERS_FLOAT_OF_BFP(_ef, _ei)
Definition: pprz_algebra.h:709
int32_t phi
in rad with INT32_ANGLE_FRAC
int32_t psi
in rad with INT32_ANGLE_FRAC
int32_t theta
in rad with INT32_ANGLE_FRAC
#define INT_MULT_RSHIFT(_a, _b, _r)
#define ANGLE_BFP_OF_REAL(_af)
#define INT32_TRIG_FRAC
#define INT32_ANGLE_PI_2
#define INT32_ANGLE_FRAC
#define ANGLE_FLOAT_OF_BFP(_ai)
#define INT32_ANGLE_NORMALIZE(_a)
euler angles
static struct Int32Eulers * stateGetNedToBodyEulers_i(void)
Get vehicle body attitude euler angles (int).
Definition: state.h:1125
static struct FloatEulers * stateGetNedToBodyEulers_f(void)
Get vehicle body attitude euler angles (float).
Definition: state.h:1143
static struct FloatQuat * stateGetNedToBodyQuat_f(void)
Get vehicle body attitude quaternion (float).
Definition: state.h:1131
#define MAX_PPRZ
Definition: paparazzi.h:8
#define PPRZ_ITRIG_SIN(_s, _a)
#define PPRZ_ITRIG_COS(_c, _a)
Generic interface for radio control modules.
pprz_t values[RADIO_CONTROL_NB_CHANNEL]
Definition: radio_control.h:67
Some helper functions to check RC sticks.
#define THROTTLE_STICK_DOWN_FROM_RC(_rc)
General attitude stabilization interface for rotorcrafts.
void stabilization_attitude_reset_care_free_heading(struct AttitudeRCInput *rc_sp)
reset the heading for care-free mode to current heading
int32_t stabilization_attitude_get_heading_i(void)
Get attitude heading as int (avoiding jumps)
static int32_t get_rc_yaw(struct RadioControl *rc)
static float get_rc_pitch_f(struct RadioControl *rc)
#define COORDINATED_TURN_AIRSPEED
Airspeed that will be used in the turning speed calculation (m/s).
static int32_t get_rc_pitch(struct RadioControl *rc)
static int32_t get_rc_roll(struct RadioControl *rc)
void stabilization_attitude_reset_rc_setpoint(struct AttitudeRCInput *rc_sp)
reset to current state
void stabilization_attitude_rc_setpoint_init(struct AttitudeRCInput *rc_sp)
Init rc input.
void stabilization_attitude_read_rc_setpoint_earth_bound(struct AttitudeRCInput *rc_sp, bool in_flight, bool in_carefree, bool coordinated_turn, struct RadioControl *rc)
Read attitude setpoint from RC as quaternion in earth bound frame.
void stabilization_attitude_read_rc_roll_pitch_earth_quat_f(struct FloatQuat *q, float theta_offset, struct RadioControl *rc)
Read roll/pitch command from RC as quaternion.
void stabilization_attitude_read_rc_roll_pitch_quat_f(struct FloatQuat *q, struct RadioControl *rc)
Read roll/pitch command from RC as quaternion.
void stabilization_attitude_read_rc_setpoint(struct AttitudeRCInput *rc_sp, bool in_flight, bool in_carefree, bool coordinated_turn, struct RadioControl *rc)
Read attitude setpoint from RC as quaternion Interprets the stick positions as axes.
#define STABILIZATION_ATTITUDE_DEADBAND_A
#define STABILIZATION_ATTITUDE_DEADBAND_E
struct Int32Eulers stabilization_attitude_read_rc_setpoint_eulers(struct AttitudeRCInput *rc_sp, bool in_flight, bool in_carefree, bool coordinated_turn, struct RadioControl *rc)
Read attitude setpoint from RC as euler angles Only the euler format is updated and returned.
static float get_rc_roll_f(struct RadioControl *rc)
#define YAW_DEADBAND_EXCEEDED(_rc)
struct FloatEulers stabilization_attitude_read_rc_setpoint_eulers_f(struct AttitudeRCInput *rc_sp, bool in_flight, bool in_carefree, bool coordinated_turn, struct RadioControl *rc)
Read attitude setpoint from RC as float euler angles Only the euler format is updated and returned.
float stabilization_attitude_get_heading_f(void)
Get attitude heading as float (avoiding jumps)
static float get_rc_yaw_f(struct RadioControl *rc)
Read an attitude setpoint from the RC.
struct FloatEulers rc_eulers
RC input in eulers (needed even for quat for yaw integration)
struct FloatQuat rc_quat
RC input in quaternion.
float care_free_heading
care_free heading
float transition_theta_offset
pitch offset for hybrids, add when in forward mode
Attitude (and Rate) Remote Control input.
API to get/set the generic vehicle states.
Architecture independent timing functions.
static float get_sys_time_float(void)
Get the time in seconds since startup.
Definition: sys_time.h:138
int int32_t
Typedef defining 32 bit int type.
Definition: vl53l1_types.h:83
float heading
Definition: wedgebug.c:258