Emaxx Navigation Group / decawave_networking
Committer:
jdawkins
Date:
Thu Jun 01 17:58:50 2017 +0000
Revision:
7:bdfcc94be056
Parent:
5:0a6df7e647f3
Child:
8:a69bdc60d93e
fixing stuff;

Who changed what in which revision?

UserRevisionLine numberNew contents of line
jdawkins 0:bd42ff91c25b 1 #include "decawave_network.h"
jdawkins 0:bd42ff91c25b 2
jdawkins 0:bd42ff91c25b 3 DecaWaveNetwork::DecaWaveNetwork(DW1000& DW) : dw(DW)
jdawkins 0:bd42ff91c25b 4 {
jdawkins 0:bd42ff91c25b 5 isAnchor = true;
jdawkins 0:bd42ff91c25b 6 overflow = false;
jdawkins 0:bd42ff91c25b 7 address = 0;
jdawkins 0:bd42ff91c25b 8 rxTimestamp = 0;
jdawkins 0:bd42ff91c25b 9 timediffRec = 0;
jdawkins 0:bd42ff91c25b 10 timediffSend = 0;
jdawkins 7:bdfcc94be056 11 curr_mode = 1;
jdawkins 0:bd42ff91c25b 12 for (int i = 0; i < MAX_NODES; i++)
jdawkins 0:bd42ff91c25b 13 acknowledgement[i] = true;
jdawkins 0:bd42ff91c25b 14
jdawkins 0:bd42ff91c25b 15 dw.setCallbacks(this, &DecaWaveNetwork::callbackRX, &DecaWaveNetwork::callbackTX);
jdawkins 0:bd42ff91c25b 16
jdawkins 0:bd42ff91c25b 17 LocalTimer.start();
jdawkins 2:fb2268373246 18 //checkConn.attach(this,&DecaWaveNetwork::checkConnectivity, 10.0);
jdawkins 0:bd42ff91c25b 19 dw.startRX();
jdawkins 0:bd42ff91c25b 20 }
jdawkins 0:bd42ff91c25b 21
jdawkins 0:bd42ff91c25b 22 void DecaWaveNetwork::callbackRX()
jdawkins 0:bd42ff91c25b 23 {
jdawkins 0:bd42ff91c25b 24 int n = dw.getFramelength();
jdawkins 0:bd42ff91c25b 25 dw.readRegister(DW1000_RX_BUFFER, 0, (uint8_t*)&receivedFrame, n);
jdawkins 0:bd42ff91c25b 26
jdawkins 0:bd42ff91c25b 27
jdawkins 0:bd42ff91c25b 28
jdawkins 0:bd42ff91c25b 29 if (receivedFrame.destination == address){
jdawkins 0:bd42ff91c25b 30 switch (receivedFrame.type) {
jdawkins 0:bd42ff91c25b 31 case PING:
jdawkins 0:bd42ff91c25b 32 rxTimestamp = dw.getRXTimestamp();
jdawkins 0:bd42ff91c25b 33 receiverTimestamps[receivedFrame.source][0] = rxTimestamp; //Save the first timestamp on the receiving node/anchor (T_rp)
jdawkins 0:bd42ff91c25b 34 sendDelayedAnswer(receivedFrame.source, ANCHOR_RESPONSE, rxTimestamp);
jdawkins 0:bd42ff91c25b 35 break;
jdawkins 0:bd42ff91c25b 36 case ANCHOR_RESPONSE:
jdawkins 0:bd42ff91c25b 37 rxTimestamp = dw.getRXTimestamp();
jdawkins 0:bd42ff91c25b 38 senderTimestamps[receivedFrame.source][1] = rxTimestamp; //Save the second timestamp on the sending node/beacon (T_rr)
jdawkins 0:bd42ff91c25b 39 sendDelayedAnswer(receivedFrame.source, 3, rxTimestamp);
jdawkins 0:bd42ff91c25b 40 break;
jdawkins 0:bd42ff91c25b 41 case BEACON_RESPONSE:
jdawkins 0:bd42ff91c25b 42 rxTimestamp = dw.getRXTimestamp();
jdawkins 0:bd42ff91c25b 43 receiverTimestamps[receivedFrame.source][2] = rxTimestamp; //Save the third timestamp on the receiving node/anchor (T_rf)
jdawkins 0:bd42ff91c25b 44
jdawkins 0:bd42ff91c25b 45 correctReceiverTimestamps(receivedFrame.source); //Correct the timestamps for the case of a counter overflow
jdawkins 0:bd42ff91c25b 46 //calculation of the summand on the receiving node/anchor
jdawkins 0:bd42ff91c25b 47 timediffRec = - 2*receiverTimestamps[receivedFrame.source][1] + receiverTimestamps[receivedFrame.source][0] + receiverTimestamps[receivedFrame.source][2];
jdawkins 0:bd42ff91c25b 48 sendTransferFrame(receivedFrame.source, timediffRec );
jdawkins 0:bd42ff91c25b 49 break;
jdawkins 0:bd42ff91c25b 50 case TRANSFER_FRAME:
jdawkins 0:bd42ff91c25b 51 //calculation of the summand on the sending node/beacon
jdawkins 0:bd42ff91c25b 52 timediffSend = 2 * senderTimestamps[receivedFrame.source][1] - senderTimestamps[receivedFrame.source][0] - senderTimestamps[receivedFrame.source][2];
jdawkins 0:bd42ff91c25b 53 //calculation of the resulting sum of all four ToFs.
jdawkins 0:bd42ff91c25b 54 tofs[receivedFrame.source] = receivedFrame.signedTime + timediffSend;
jdawkins 0:bd42ff91c25b 55 acknowledgement[receivedFrame.source] = true;
jdawkins 0:bd42ff91c25b 56 break;
jdawkins 0:bd42ff91c25b 57 default :
jdawkins 0:bd42ff91c25b 58 break;
jdawkins 0:bd42ff91c25b 59 }
jdawkins 0:bd42ff91c25b 60 } else{
jdawkins 0:bd42ff91c25b 61
jdawkins 0:bd42ff91c25b 62 memcpy(&mavlink_buffer,&receivedFrame,n);
jdawkins 0:bd42ff91c25b 63 // int i;
jdawkins 0:bd42ff91c25b 64
jdawkins 0:bd42ff91c25b 65 /* for(int i=0; i<n; i++) {
jdawkins 0:bd42ff91c25b 66 printf("%X",mavlink_buffer[i]);
jdawkins 0:bd42ff91c25b 67 }
jdawkins 0:bd42ff91c25b 68 printf("\r\n");*/
jdawkins 0:bd42ff91c25b 69 } // end else
jdawkins 0:bd42ff91c25b 70
jdawkins 0:bd42ff91c25b 71
jdawkins 0:bd42ff91c25b 72 parseMavlinkMsg(n);
jdawkins 0:bd42ff91c25b 73
jdawkins 0:bd42ff91c25b 74 dw.startRX();
jdawkins 0:bd42ff91c25b 75 }
jdawkins 0:bd42ff91c25b 76
jdawkins 0:bd42ff91c25b 77 void DecaWaveNetwork::callbackTX()
jdawkins 0:bd42ff91c25b 78 {
jdawkins 0:bd42ff91c25b 79 switch (rangingFrame.type) {
jdawkins 0:bd42ff91c25b 80 case PING:
jdawkins 0:bd42ff91c25b 81 senderTimestamps[rangingFrame.destination][0] = dw.getTXTimestamp(); //Save the first timestamp on the sending node/beacon (T_sp)
jdawkins 0:bd42ff91c25b 82 break;
jdawkins 0:bd42ff91c25b 83 case ANCHOR_RESPONSE:
jdawkins 0:bd42ff91c25b 84 receiverTimestamps[rangingFrame.destination][1] = dw.getTXTimestamp(); //Save the second timestamp on the receiving node/anchor (T_sr)
jdawkins 0:bd42ff91c25b 85 break;
jdawkins 0:bd42ff91c25b 86 case BEACON_RESPONSE:
jdawkins 0:bd42ff91c25b 87 senderTimestamps[rangingFrame.destination][2] = dw.getTXTimestamp(); //Save the third timestamp on the sending node/beacon (T_sr)
jdawkins 0:bd42ff91c25b 88 correctSenderTimestamps(rangingFrame.destination); //Correct the timestamps for the case of a counter overflow
jdawkins 0:bd42ff91c25b 89 break;
jdawkins 0:bd42ff91c25b 90 default:
jdawkins 0:bd42ff91c25b 91 break;
jdawkins 0:bd42ff91c25b 92 }
jdawkins 0:bd42ff91c25b 93
jdawkins 0:bd42ff91c25b 94 }
jdawkins 0:bd42ff91c25b 95
jdawkins 0:bd42ff91c25b 96 /**
jdawkins 0:bd42ff91c25b 97 * Get the distance to the Anchor with address @param destination.
jdawkins 0:bd42ff91c25b 98 *
jdawkins 0:bd42ff91c25b 99 * @param destination The address of the anchor
jdawkins 0:bd42ff91c25b 100 */
jdawkins 0:bd42ff91c25b 101 void DecaWaveNetwork::requestRanging(uint8_t destination)
jdawkins 0:bd42ff91c25b 102 {
jdawkins 0:bd42ff91c25b 103 acknowledgement[destination] = false;
jdawkins 0:bd42ff91c25b 104 float time_before = LocalTimer.read();
jdawkins 0:bd42ff91c25b 105
jdawkins 0:bd42ff91c25b 106 sendPingFrame(destination);
jdawkins 0:bd42ff91c25b 107
jdawkins 0:bd42ff91c25b 108 while(!acknowledgement[destination] && (LocalTimer.read() < time_before + 0.02f)); // wait for succeeding ranging or timeout
jdawkins 0:bd42ff91c25b 109
jdawkins 0:bd42ff91c25b 110 roundtriptimes[destination] = LocalTimer.read() - time_before;
jdawkins 0:bd42ff91c25b 111
jdawkins 0:bd42ff91c25b 112 if(acknowledgement[destination]) {
jdawkins 0:bd42ff91c25b 113 distances[destination] = calibratedDistance(destination);
jdawkins 0:bd42ff91c25b 114 } else {
jdawkins 0:bd42ff91c25b 115 distances[destination] = -1;
jdawkins 0:bd42ff91c25b 116 }
jdawkins 0:bd42ff91c25b 117 }
jdawkins 0:bd42ff91c25b 118
jdawkins 0:bd42ff91c25b 119 inline float DecaWaveNetwork::calibratedDistance(uint8_t destination)
jdawkins 0:bd42ff91c25b 120 {
jdawkins 0:bd42ff91c25b 121
jdawkins 0:bd42ff91c25b 122 float rawDistance = (tofs[destination] * 300 * TIMEUNITS_TO_US / 4);
jdawkins 0:bd42ff91c25b 123
jdawkins 0:bd42ff91c25b 124 // Least Squares calibration parameters determined from dynamic data on quadrotor with Optitrack
jdawkins 0:bd42ff91c25b 125 float calibDistance = 0.9710*rawDistance - 0.5075;
jdawkins 0:bd42ff91c25b 126
jdawkins 0:bd42ff91c25b 127 // Calibration for Nucleo 0 (and 1)
jdawkins 0:bd42ff91c25b 128
jdawkins 0:bd42ff91c25b 129 // if (this->address == 1) rawDistance+= 10;
jdawkins 0:bd42ff91c25b 130 // switch(destination){
jdawkins 0:bd42ff91c25b 131 // case 2:
jdawkins 0:bd42ff91c25b 132 // return rawDistance * 0.9754 - 0.5004;
jdawkins 0:bd42ff91c25b 133 // case 3:
jdawkins 0:bd42ff91c25b 134 // return rawDistance * 0.9759 - 0.4103;
jdawkins 0:bd42ff91c25b 135 // case 4:
jdawkins 0:bd42ff91c25b 136 // return rawDistance * 0.9798 - 0.5499;
jdawkins 0:bd42ff91c25b 137 // case 5:
jdawkins 0:bd42ff91c25b 138 // return rawDistance * 0.9765 - 0.5169;
jdawkins 0:bd42ff91c25b 139 // }
jdawkins 0:bd42ff91c25b 140
jdawkins 0:bd42ff91c25b 141 return calibDistance;
jdawkins 0:bd42ff91c25b 142
jdawkins 0:bd42ff91c25b 143 }
jdawkins 0:bd42ff91c25b 144
jdawkins 0:bd42ff91c25b 145 void DecaWaveNetwork::requestRangingAll()
jdawkins 0:bd42ff91c25b 146 {
jdawkins 0:bd42ff91c25b 147 for (int i = 1; i <= 4; i++) { // Request ranging to all anchors
jdawkins 0:bd42ff91c25b 148 requestRanging(i);
jdawkins 0:bd42ff91c25b 149 }
jdawkins 0:bd42ff91c25b 150 }
jdawkins 0:bd42ff91c25b 151
jdawkins 0:bd42ff91c25b 152 void DecaWaveNetwork::sendPingFrame(uint8_t destination)
jdawkins 0:bd42ff91c25b 153 {
jdawkins 0:bd42ff91c25b 154 rangingFrame.source = address;
jdawkins 0:bd42ff91c25b 155 rangingFrame.destination = destination;
jdawkins 0:bd42ff91c25b 156 rangingFrame.type = PING;
jdawkins 0:bd42ff91c25b 157 dw.sendFrame((uint8_t*)&rangingFrame, sizeof(rangingFrame));
jdawkins 0:bd42ff91c25b 158 }
jdawkins 0:bd42ff91c25b 159
jdawkins 0:bd42ff91c25b 160 void DecaWaveNetwork::sendTransferFrame(uint8_t destination, int timeDiffsReceiver)
jdawkins 0:bd42ff91c25b 161 {
jdawkins 0:bd42ff91c25b 162 transferFrame.source = address;
jdawkins 0:bd42ff91c25b 163 transferFrame.destination = destination;
jdawkins 0:bd42ff91c25b 164 transferFrame.type = TRANSFER_FRAME;
jdawkins 0:bd42ff91c25b 165 transferFrame.signedTime = timeDiffsReceiver; //cast the time difference
jdawkins 0:bd42ff91c25b 166 dw.sendFrame((uint8_t*)&transferFrame, sizeof(transferFrame));
jdawkins 0:bd42ff91c25b 167 }
jdawkins 0:bd42ff91c25b 168
jdawkins 0:bd42ff91c25b 169 void DecaWaveNetwork::sendDelayedAnswer(uint8_t destination, uint8_t type, uint64_t rxTimestamp)
jdawkins 0:bd42ff91c25b 170 {
jdawkins 0:bd42ff91c25b 171
jdawkins 0:bd42ff91c25b 172 rangingFrame.source = address;
jdawkins 0:bd42ff91c25b 173 rangingFrame.destination = destination;
jdawkins 0:bd42ff91c25b 174 rangingFrame.type = type;
jdawkins 0:bd42ff91c25b 175
jdawkins 0:bd42ff91c25b 176 if(rxTimestamp + ANSWER_DELAY_TIMEUNITS > MMRANGING_2POWER40)
jdawkins 0:bd42ff91c25b 177 dw.sendDelayedFrame((uint8_t*)&rangingFrame, sizeof(rangingFrame), rxTimestamp + ANSWER_DELAY_TIMEUNITS - MMRANGING_2POWER40);
jdawkins 0:bd42ff91c25b 178 else
jdawkins 0:bd42ff91c25b 179 dw.sendDelayedFrame((uint8_t*)&rangingFrame, sizeof(rangingFrame), rxTimestamp + ANSWER_DELAY_TIMEUNITS);
jdawkins 0:bd42ff91c25b 180 }
jdawkins 0:bd42ff91c25b 181
jdawkins 0:bd42ff91c25b 182 void DecaWaveNetwork::correctReceiverTimestamps(uint8_t source)
jdawkins 0:bd42ff91c25b 183 {
jdawkins 0:bd42ff91c25b 184
jdawkins 0:bd42ff91c25b 185 if(receiverTimestamps[source][0] > receiverTimestamps[source][1]) {
jdawkins 0:bd42ff91c25b 186 receiverTimestamps[source][1] += MMRANGING_2POWER40;
jdawkins 0:bd42ff91c25b 187 receiverTimestamps[source][2] += MMRANGING_2POWER40;
jdawkins 0:bd42ff91c25b 188 }
jdawkins 0:bd42ff91c25b 189
jdawkins 0:bd42ff91c25b 190 if(receiverTimestamps[source][1] > receiverTimestamps[source][2]) {
jdawkins 0:bd42ff91c25b 191 receiverTimestamps[source][2] += MMRANGING_2POWER40;
jdawkins 0:bd42ff91c25b 192 }
jdawkins 0:bd42ff91c25b 193
jdawkins 0:bd42ff91c25b 194 }
jdawkins 0:bd42ff91c25b 195
jdawkins 0:bd42ff91c25b 196 void DecaWaveNetwork::correctSenderTimestamps(uint8_t source)
jdawkins 0:bd42ff91c25b 197 {
jdawkins 0:bd42ff91c25b 198
jdawkins 0:bd42ff91c25b 199 if (senderTimestamps[source][0] > senderTimestamps[source][1]) {
jdawkins 0:bd42ff91c25b 200 senderTimestamps[source][1] += MMRANGING_2POWER40;
jdawkins 0:bd42ff91c25b 201 senderTimestamps[source][2] += MMRANGING_2POWER40;
jdawkins 0:bd42ff91c25b 202 overflow = true;
jdawkins 0:bd42ff91c25b 203 } else if (senderTimestamps[source][1] > senderTimestamps[source][2]) {
jdawkins 0:bd42ff91c25b 204 senderTimestamps[source][2] += MMRANGING_2POWER40;
jdawkins 0:bd42ff91c25b 205 overflow = true;
jdawkins 0:bd42ff91c25b 206 } else overflow = false;
jdawkins 0:bd42ff91c25b 207
jdawkins 0:bd42ff91c25b 208 }
jdawkins 0:bd42ff91c25b 209 void DecaWaveNetwork::sendMessage(uint8_t *msg,uint8_t length)
jdawkins 0:bd42ff91c25b 210 {
jdawkins 0:bd42ff91c25b 211 dw.sendFrame(msg,length);
jdawkins 0:bd42ff91c25b 212 }
jdawkins 0:bd42ff91c25b 213
jdawkins 0:bd42ff91c25b 214 void DecaWaveNetwork::parseMavlinkMsg(uint8_t msg_len)
jdawkins 0:bd42ff91c25b 215 {
jdawkins 0:bd42ff91c25b 216
jdawkins 0:bd42ff91c25b 217 //char buf[1024];
jdawkins 0:bd42ff91c25b 218 uint8_t byte;
jdawkins 0:bd42ff91c25b 219 mavlink_message_t msg;
jdawkins 0:bd42ff91c25b 220 mavlink_status_t status;
jdawkins 0:bd42ff91c25b 221
jdawkins 0:bd42ff91c25b 222
jdawkins 0:bd42ff91c25b 223 for(int i=0; i<msg_len; i++) {
jdawkins 0:bd42ff91c25b 224 memcpy(&byte,&mavlink_buffer[i],1);
jdawkins 0:bd42ff91c25b 225
jdawkins 0:bd42ff91c25b 226 if(mavlink_parse_char(MAVLINK_COMM_0,byte,&msg,&status)) {
jdawkins 0:bd42ff91c25b 227
jdawkins 1:93fcc351837a 228 //printf("Parse Successfully msg id %d\r\n",msg.msgid);
jdawkins 0:bd42ff91c25b 229 if(msg.msgid == MAVLINK_MSG_ID_HEARTBEAT) {
jdawkins 0:bd42ff91c25b 230 mavlink_heartbeat_t hb_msg;
jdawkins 0:bd42ff91c25b 231 mavlink_msg_heartbeat_decode(&msg,&hb_msg);
jdawkins 5:0a6df7e647f3 232 //printf("System ID %d Comp ID %d \r\n",msg.sysid,msg.compid);
jdawkins 0:bd42ff91c25b 233 nodes_in_range[msg.sysid]= msg.compid;
jdawkins 3:1caa9d659257 234 last_heartbeat[msg.sysid]=LocalTimer.read();
jdawkins 0:bd42ff91c25b 235 // dwm_LED = !dwm_LED;
jdawkins 0:bd42ff91c25b 236 }
jdawkins 0:bd42ff91c25b 237 /* if(msg.msgid==MAVLINK_MSG_ID_FUSED_IMU) {
jdawkins 0:bd42ff91c25b 238
jdawkins 0:bd42ff91c25b 239 mavlink_fused_imu_t imu_msg;
jdawkins 0:bd42ff91c25b 240 // sendMessage(&pc,buf,n);
jdawkins 0:bd42ff91c25b 241 mavlink_msg_fused_imu_decode(&msg,&imu_msg);
jdawkins 0:bd42ff91c25b 242 imu_time = imu_msg.time_boot_ms;
jdawkins 0:bd42ff91c25b 243 roll = imu_msg.roll;
jdawkins 0:bd42ff91c25b 244 pitch = imu_msg.pitch;
jdawkins 0:bd42ff91c25b 245 yaw = imu_msg.yaw;
jdawkins 0:bd42ff91c25b 246 gx = imu_msg.gyro_x;
jdawkins 0:bd42ff91c25b 247 gy = imu_msg.gyro_y;
jdawkins 0:bd42ff91c25b 248 gz = imu_msg.gyro_z;
jdawkins 0:bd42ff91c25b 249 ax = imu_msg.accel_x;
jdawkins 0:bd42ff91c25b 250 ay = imu_msg.accel_y;
jdawkins 0:bd42ff91c25b 251 az = imu_msg.accel_z;
jdawkins 0:bd42ff91c25b 252
jdawkins 0:bd42ff91c25b 253 imu_LED = !imu_LED;
jdawkins 0:bd42ff91c25b 254 }*/
jdawkins 0:bd42ff91c25b 255
jdawkins 0:bd42ff91c25b 256 //This is from the perspective of the anchor node which doesn't compute the range
jdawkins 0:bd42ff91c25b 257 if(msg.msgid == MAVLINK_MSG_ID_RANGE_TO_NODE) {
jdawkins 0:bd42ff91c25b 258 mavlink_range_to_node_t rng_msg;
jdawkins 0:bd42ff91c25b 259 mavlink_msg_range_to_node_decode(&msg,&rng_msg);
jdawkins 0:bd42ff91c25b 260
jdawkins 0:bd42ff91c25b 261 if(rng_msg.tgt_id==address) { // if the range message is inteded for me
jdawkins 0:bd42ff91c25b 262 // rng_time = rng_msg.time_boot_ms;
jdawkins 0:bd42ff91c25b 263 ranges[rng_msg.my_id] = rng_msg.range; // my_id is the id of the sender
jdawkins 0:bd42ff91c25b 264
jdawkins 0:bd42ff91c25b 265 }
jdawkins 2:fb2268373246 266
jdawkins 0:bd42ff91c25b 267 }
jdawkins 0:bd42ff91c25b 268
jdawkins 2:fb2268373246 269 if(msg.msgid == MAVLINK_MSG_ID_LOCAL_POSITION_NED) {
jdawkins 2:fb2268373246 270 mavlink_local_position_ned_t pose_msg;
jdawkins 2:fb2268373246 271 mavlink_msg_local_position_ned_decode(&msg,&pose_msg);
jdawkins 3:1caa9d659257 272 //printf("System ID %d Comp ID %d \r\n",msg.sysid,msg.compid);
jdawkins 2:fb2268373246 273 node_pose[msg.sysid] = pose_msg;
jdawkins 2:fb2268373246 274
jdawkins 2:fb2268373246 275 }
jdawkins 2:fb2268373246 276
jdawkins 2:fb2268373246 277
jdawkins 2:fb2268373246 278
jdawkins 0:bd42ff91c25b 279 }// if Mavlink Parse Returns true
jdawkins 0:bd42ff91c25b 280
jdawkins 0:bd42ff91c25b 281 }// End For number of bytes
jdawkins 0:bd42ff91c25b 282 }
jdawkins 0:bd42ff91c25b 283
jdawkins 0:bd42ff91c25b 284 void DecaWaveNetwork::checkConnectivity(){
jdawkins 0:bd42ff91c25b 285 printf("connectivity check\r\n");
jdawkins 0:bd42ff91c25b 286 for(int i=0;i<MAX_NODES;i++){
jdawkins 0:bd42ff91c25b 287 if(LocalTimer.read()-last_heartbeat[i] > 5.0){ //if its been more than 5 seconds since we recieved a heartbeat assume node is out of range
jdawkins 0:bd42ff91c25b 288 nodes_in_range[i] = 0;
jdawkins 0:bd42ff91c25b 289 }
jdawkins 0:bd42ff91c25b 290 }
jdawkins 1:93fcc351837a 291 }
jdawkins 1:93fcc351837a 292 float DecaWaveNetwork::getRange(int node){
jdawkins 1:93fcc351837a 293 return ranges[node];
jdawkins 0:bd42ff91c25b 294 }