Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: cobaLCDJoyMotor_Thread odometry_omni_3roda_v3 odometry_omni_3roda_v1 odometry_omni_3roda_v2 ... more
etx.c
00001 /* 00002 * Copyright (c) 2014-2017, Arm Limited and affiliates. 00003 * SPDX-License-Identifier: Apache-2.0 00004 * 00005 * Licensed under the Apache License, Version 2.0 (the "License"); 00006 * you may not use this file except in compliance with the License. 00007 * You may obtain a copy of the License at 00008 * 00009 * http://www.apache.org/licenses/LICENSE-2.0 00010 * 00011 * Unless required by applicable law or agreed to in writing, software 00012 * distributed under the License is distributed on an "AS IS" BASIS, 00013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00014 * See the License for the specific language governing permissions and 00015 * limitations under the License. 00016 */ 00017 #include "nsconfig.h" 00018 #include "ns_types.h" 00019 #include "common_functions.h" 00020 #include "ns_trace.h" 00021 #include "string.h" 00022 #include "nsdynmemLIB.h" 00023 #include "platform/arm_hal_phy.h" 00024 #include "net_interface.h" 00025 00026 #include "Core/include/address.h" 00027 #include "MLE/mle.h" 00028 #include "NWK_INTERFACE/Include/protocol.h" 00029 #include "NWK_INTERFACE/Include/protocol_stats.h" 00030 #include "Service_Libs/etx/etx.h" 00031 #include "Service_Libs/utils/isqrt.h" 00032 00033 //TODO: Refactor this away! 00034 #include "MAC/rf_driver_storage.h" 00035 00036 #define TRACE_GROUP "etx" 00037 00038 static uint16_t etx_current_calc(uint16_t etx, uint8_t accumulated_failures); 00039 static uint16_t etx_dbm_lqi_calc(uint8_t lqi, int8_t dbm); 00040 static void etx_value_change_callback_needed_check(uint16_t etx, uint16_t *stored_diff_etx, uint8_t accumulated_failures, const uint8_t *mac64_addr_ptr, uint16_t mac16_addr); 00041 static void etx_accum_failures_callback_needed_check(struct mle_neigh_table_entry_t *neigh_table_ptr); 00042 00043 typedef struct { 00044 uint16_t hysteresis; // 12 bit fraction 00045 uint8_t accum_threshold; 00046 etx_value_change_handler_t *callback_ptr; 00047 etx_accum_failures_handler_t *accum_cb_ptr; 00048 int8_t interface_id; 00049 } ext_info_t; 00050 00051 static ext_info_t etx_info = { 00052 .hysteresis = 0, 00053 .accum_threshold = 0, 00054 .callback_ptr = NULL, 00055 .accum_cb_ptr = NULL, 00056 .interface_id = -1 00057 }; 00058 00059 /** 00060 * \brief A function to update ETX value based on transmission attempts 00061 * 00062 * Update is made based on failed and successful message sending 00063 * attempts for a message. 00064 * 00065 * \param attempts number of attempts to send message 00066 * \param success was message sending successful 00067 * \param addr_type address type, ADDR_802_15_4_SHORT or ADDR_802_15_4_LONG 00068 * \param addr_ptr PAN ID with 802.15.4 address 00069 */ 00070 void etx_transm_attempts_update(int8_t interface_id, uint8_t attempts, bool success, addrtype_t addr_type, const uint8_t *addr_ptr) 00071 { 00072 mle_neigh_table_entry_t *neigh_table_ptr = NULL; 00073 uint32_t etx; 00074 uint8_t accumulated_failures; 00075 00076 if (!addr_ptr) { 00077 return; 00078 } 00079 00080 // Gets table entry 00081 neigh_table_ptr = mle_class_get_by_link_address(interface_id, addr_ptr + PAN_ID_LEN, addr_type); 00082 00083 if (neigh_table_ptr == NULL) { 00084 return; 00085 } 00086 00087 accumulated_failures = neigh_table_ptr->accumulated_failures; 00088 00089 if (!success) { 00090 /* Stores failed attempts to estimate ETX and to calculate 00091 new ETX after successful sending */ 00092 if (accumulated_failures + attempts < 32) { 00093 neigh_table_ptr->accumulated_failures += attempts; 00094 } else { 00095 success = true; 00096 } 00097 } 00098 00099 if (success) { 00100 neigh_table_ptr->accumulated_failures = 0; 00101 } else { 00102 etx_accum_failures_callback_needed_check(neigh_table_ptr); 00103 } 00104 00105 if (neigh_table_ptr->etx) { 00106 // If hysteresis is set stores ETX value for comparison 00107 if (etx_info.hysteresis && !neigh_table_ptr->stored_diff_etx) { 00108 neigh_table_ptr->stored_diff_etx = neigh_table_ptr->etx; 00109 } 00110 00111 if (success) { 00112 // ETX = 7/8 * current ETX + 1/8 * ((attempts + failed attempts) << 12) 00113 etx = neigh_table_ptr->etx - (neigh_table_ptr->etx >> ETX_MOVING_AVERAGE_FRACTION); 00114 etx += (attempts + accumulated_failures) << (12 - ETX_MOVING_AVERAGE_FRACTION); 00115 00116 if (etx > 0xffff) { 00117 neigh_table_ptr->etx = 0xffff; 00118 } else { 00119 neigh_table_ptr->etx = etx; 00120 } 00121 } 00122 00123 // If real ETX value has been received do not update based on LQI or dBm 00124 neigh_table_ptr->tmp_etx = false; 00125 00126 // Checks if ETX value change callback is needed 00127 etx_value_change_callback_needed_check(neigh_table_ptr->etx, &(neigh_table_ptr->stored_diff_etx), neigh_table_ptr->accumulated_failures, &(neigh_table_ptr->mac64[0]), neigh_table_ptr->short_adr); 00128 00129 // Updates ETX statistics 00130 if (neigh_table_ptr->priorityFlag) { 00131 protocol_stats_update(STATS_ETX_1ST_PARENT, neigh_table_ptr->etx >> 4); 00132 } else if (neigh_table_ptr->second_priority_flag) { 00133 protocol_stats_update(STATS_ETX_2ND_PARENT, neigh_table_ptr->etx >> 4); 00134 } 00135 } 00136 } 00137 00138 /** 00139 * \brief A function to update ETX value based on remote incoming IDR 00140 * 00141 * Update is made based on remote incoming IDR received from 00142 * neighbor. 00143 * 00144 * \param remote_incoming_idr Remote incoming IDR 00145 * \param mac64_addr_ptr long MAC address 00146 */ 00147 void etx_remote_incoming_idr_update(int8_t interface_id, uint8_t remote_incoming_idr, mle_neigh_table_entry_t *neigh_table_ptr) 00148 { 00149 (void) interface_id; 00150 00151 if (neigh_table_ptr) { 00152 // If ETX has been set 00153 if (neigh_table_ptr->etx) { 00154 // If hysteresis is set stores ETX value to enable comparison 00155 if (etx_info.hysteresis && !neigh_table_ptr->stored_diff_etx) { 00156 neigh_table_ptr->stored_diff_etx = neigh_table_ptr->etx; 00157 } 00158 // remote EXT = remote incoming IDR^2 (12 bit fraction) 00159 uint32_t remote_ext = ((uint32_t)remote_incoming_idr * remote_incoming_idr) << 2; 00160 00161 // ETX = 7/8 * current ETX + 1/8 * remote ETX */ 00162 uint32_t etx = neigh_table_ptr->etx - (neigh_table_ptr->etx >> ETX_MOVING_AVERAGE_FRACTION); 00163 etx += remote_ext >> ETX_MOVING_AVERAGE_FRACTION; 00164 00165 if (etx > 0xffff) { 00166 neigh_table_ptr->etx = 0xffff; 00167 } else { 00168 neigh_table_ptr->etx = etx; 00169 } 00170 00171 // Checks if ETX value change callback is needed 00172 etx_value_change_callback_needed_check(neigh_table_ptr->etx, &(neigh_table_ptr->stored_diff_etx), neigh_table_ptr->accumulated_failures, &(neigh_table_ptr->mac64[0]), neigh_table_ptr->short_adr); 00173 } 00174 neigh_table_ptr->remote_incoming_idr = remote_incoming_idr; 00175 } 00176 } 00177 00178 /** 00179 * \brief A function to read ETX value 00180 * 00181 * Returns ETX value for an address 00182 * 00183 * \param interface_id network interface id 00184 * \param addr_type address type, ADDR_802_15_4_SHORT or ADDR_802_15_4_LONG 00185 * \param addr_ptr PAN ID with 802.15.4 address 00186 * 00187 * \return 0x0100 to 0xFFFF ETX value (8 bit fraction) 00188 * \return 0xFFFF address not associated 00189 * \return 0x0000 address unknown or other error 00190 * \return 0x0001 no ETX statistics on this interface 00191 */ 00192 uint16_t etx_read(int8_t interface_id, addrtype_t addr_type, const uint8_t *addr_ptr) 00193 { 00194 mle_neigh_table_entry_t *neigh_table_ptr; 00195 uint16_t etx = 0; 00196 00197 if (!addr_ptr) { 00198 return 0; 00199 } 00200 00201 if (!mle_class_exists_for_interface(interface_id)) { 00202 return 1; 00203 } 00204 00205 neigh_table_ptr = mle_class_get_by_link_address(interface_id, addr_ptr + PAN_ID_LEN, addr_type); 00206 00207 if (neigh_table_ptr) { 00208 etx = etx_current_calc(neigh_table_ptr->etx, neigh_table_ptr->accumulated_failures); 00209 etx >>= 4; 00210 } else { 00211 etx = 0xffff; 00212 } 00213 00214 return etx; 00215 } 00216 00217 /** 00218 * \brief A function to read local incoming IDR value 00219 * 00220 * Returns local incoming IDR value for an address 00221 * 00222 * \param mac64_addr_ptr long MAC address 00223 * 00224 * \return 0x0100 to 0xFFFF incoming IDR value (8 bit fraction) 00225 * \return 0x0000 address unknown 00226 */ 00227 uint16_t etx_local_incoming_idr_read(int8_t interface_id, mle_neigh_table_entry_t *neigh_table_ptr) 00228 { 00229 uint32_t local_incoming_idr = 0; 00230 (void) interface_id; 00231 if (neigh_table_ptr) { 00232 uint16_t local_etx = etx_current_calc(neigh_table_ptr->etx, neigh_table_ptr->accumulated_failures); 00233 00234 local_incoming_idr = isqrt32((uint32_t)local_etx << 16); 00235 // divide by sqrt(2^12) 00236 local_incoming_idr = local_incoming_idr >> 6; 00237 } 00238 00239 return local_incoming_idr; 00240 } 00241 00242 /** 00243 * \brief A function to calculate current ETX 00244 * 00245 * Returns current ETX value based on ETX and failed attempts. Return 00246 * value is scaled by scaling factor 00247 * 00248 * \param etx ETX (12 bit fraction) 00249 * \param accumulated_failures failed attempts 00250 * 00251 * \return ETX value (12 bit fraction) 00252 */ 00253 static uint16_t etx_current_calc(uint16_t etx, uint8_t accumulated_failures) 00254 { 00255 uint32_t current_etx; 00256 00257 // If there is no failed attempts 00258 if (accumulated_failures == 0) { 00259 current_etx = etx; 00260 } else { 00261 /* Calculates ETX estimate based on failed attempts 00262 ETX = current ETX + 1/8 * (failed attempts << 12) */ 00263 current_etx = etx + (accumulated_failures << (12 - ETX_MOVING_AVERAGE_FRACTION)); 00264 if (current_etx > 0xffff) { 00265 current_etx = 0xffff; 00266 } 00267 } 00268 00269 return current_etx; 00270 } 00271 00272 /** 00273 * \brief A function to update ETX value based on LQI and dBm 00274 * 00275 * Update is made based on dBM and LQI of received message. 00276 * 00277 * \param lqi link quality indicator 00278 * \param dbm measured dBm 00279 * \param mac64_addr_ptr long MAC address 00280 * 00281 * \return 0x0100 to 0xFFFF local incoming IDR value (8 bit fraction) 00282 */ 00283 uint16_t etx_lqi_dbm_update(int8_t interface_id, uint8_t lqi, int8_t dbm, mle_neigh_table_entry_t *neigh_table_ptr) 00284 { 00285 uint32_t local_incoming_idr = 0; 00286 uint32_t etx = 0; 00287 (void) interface_id; 00288 00289 if (neigh_table_ptr) { 00290 // If local ETX is not set calculate it based on LQI and dBm 00291 if (!neigh_table_ptr->etx) { 00292 etx = etx_dbm_lqi_calc(lqi, dbm); 00293 neigh_table_ptr->etx = etx; 00294 neigh_table_ptr->tmp_etx = true; 00295 } 00296 // If local ETX has been calculated without remote incoming IDR and 00297 // remote incoming IDR is available update it by remote incoming IDR value 00298 if (neigh_table_ptr->remote_incoming_idr && neigh_table_ptr->tmp_etx) { 00299 neigh_table_ptr->tmp_etx = false; 00300 00301 local_incoming_idr = isqrt32((uint32_t)neigh_table_ptr->etx << 16); 00302 // divide by sqrt(2^12) and scale to 12 bit fraction 00303 local_incoming_idr = local_incoming_idr >> 2; 00304 00305 etx = local_incoming_idr * (((uint16_t)neigh_table_ptr->remote_incoming_idr) << 7); 00306 neigh_table_ptr->etx = etx >> 12; 00307 00308 local_incoming_idr >>= 4; 00309 } 00310 00311 // If local ETX has been calculated indicates new neighbor 00312 if (etx) { 00313 etx_neighbor_add(interface_id, neigh_table_ptr); 00314 } 00315 } 00316 00317 // If local ETX is not set return temporary ETX based on LQI and dB, 00318 if (!local_incoming_idr) { 00319 if (!etx) { 00320 etx = etx_dbm_lqi_calc(lqi, dbm); 00321 } 00322 00323 local_incoming_idr = isqrt32(etx << 16); 00324 // divide by sqrt(2^12) 00325 local_incoming_idr >>= 6; 00326 } 00327 00328 return local_incoming_idr; 00329 } 00330 00331 /** 00332 * \brief A function to calculate ETX value based on dBm and LQI 00333 * 00334 * Calculation is made using RF driver service. If service does not 00335 * exists then local function is used. 00336 * 00337 * \param lqi link quality indicator 00338 * \param dbm measured dBm 00339 * 00340 * \return ETX value (12 bit fraction) 00341 */ 00342 static uint16_t etx_dbm_lqi_calc(uint8_t lqi, int8_t dbm) 00343 { 00344 protocol_interface_info_entry_t *cur = 0; 00345 int8_t driver_ret_value = -1; 00346 uint16_t etx; 00347 00348 phy_signal_info_s signal_info; 00349 signal_info.type = PHY_SIGNAL_INFO_ETX; 00350 signal_info.lqi = lqi; 00351 signal_info.dbm = dbm; 00352 signal_info.result = 0xffff; 00353 00354 //TODO: This is needed, but RF driver cannot be accessed directly! Figure out MAC extension for this. 00355 cur = protocol_stack_interface_info_get(IF_6LoWPAN); 00356 if ((cur) && (cur->dev_driver) && (cur->dev_driver->phy_driver)) { 00357 phy_device_driver_s *dev_driver = cur->dev_driver->phy_driver; 00358 if (dev_driver->extension) { 00359 driver_ret_value = dev_driver->extension(PHY_EXTENSION_CONVERT_SIGNAL_INFO, (uint8_t *)&signal_info); 00360 } 00361 } 00362 00363 if ((driver_ret_value != -1) && (signal_info.result != 0xffff)) { 00364 etx = signal_info.result; 00365 etx <<= 4; 00366 } else { 00367 /* Atmel version 00368 dBm = RSSI base value [dBm] + 1.03 [dB] x ED level 00369 LQI = errors in received frame */ 00370 00371 // for dBm -90 and LQI 0 ETX will be 2.4 00372 etx = ((dbm * -1) * (256 - lqi)); 00373 etx >>= 1; // scale result to 12 bits 00374 etx += 1 << 12; // add one (perfect link) 00375 } 00376 00377 return etx; 00378 } 00379 00380 /** 00381 * \brief A function to register ETX value change callback 00382 * 00383 * Register ETX value change callback. When ETX value has changed more or equal 00384 * to hysteresis value ETX module calls ETX value change callback. 00385 * 00386 * \param nwk_interface_id network interface id 00387 * \param hysteresis hysteresis value (8 bit fraction) 00388 * \param callback_ptr callback function pointer 00389 * 00390 * \return 0 not 6LowPAN interface 00391 * \return 1 success 00392 */ 00393 uint8_t etx_value_change_callback_register(nwk_interface_id nwk_id, int8_t interface_id, uint16_t hysteresis, etx_value_change_handler_t *callback_ptr) 00394 { 00395 if ((nwk_id == IF_6LoWPAN) && hysteresis && callback_ptr) { 00396 etx_info.hysteresis = hysteresis << 4; 00397 etx_info.callback_ptr = callback_ptr; 00398 etx_info.interface_id = interface_id; 00399 return 1; 00400 } else { 00401 return 0; 00402 } 00403 } 00404 00405 /** 00406 * \brief A function to register accumulated failures callback 00407 * 00408 * When the number of accumulated failures has reached the threshold 00409 * value, the ETX module calls the accumulated failures callback on 00410 * every transmission failure. 00411 * 00412 * \param nwk_id network ID (6LoWPAN) 00413 * \param interface_id interface ID 00414 * \param threshold threshold value for accumulated failures 00415 * \param callback_ptr callback function pointer 00416 * 00417 * \return 0 not 6LowPAN interface 00418 * \return 1 success 00419 */ 00420 uint8_t etx_accum_failures_callback_register(nwk_interface_id nwk_id, int8_t interface_id, uint8_t threshold, etx_accum_failures_handler_t *callback_ptr) 00421 { 00422 if ((nwk_id == IF_6LoWPAN) && threshold && callback_ptr) { 00423 etx_info.interface_id = interface_id; 00424 etx_info.accum_threshold = threshold; 00425 etx_info.accum_cb_ptr = callback_ptr; 00426 return 1; 00427 } else { 00428 return 0; 00429 } 00430 } 00431 00432 /** 00433 * \brief A function to check if ETX value change callback is needed 00434 * 00435 * Calculates current ETX and compares it against stored ETX. If change 00436 * of the values is more than hysteresis calls ETX value change 00437 * callback. 00438 * 00439 * \param etx ETX (12 bit fraction) 00440 * \param stored_diff_etx stored ETX value 00441 * \param accumulated_failures failed attempts 00442 * \param mac64_addr_ptr long MAC address 00443 * \param mac16_addr short MAC address or 0xffff address is not set 00444 * 00445 * \return ETX value (12 bit fraction) 00446 */ 00447 static void etx_value_change_callback_needed_check(uint16_t etx, uint16_t *stored_diff_etx, uint8_t accumulated_failures, const uint8_t *mac64_addr_ptr, uint16_t mac16_addr) 00448 { 00449 uint16_t current_etx; 00450 bool callback = false; 00451 00452 if (!etx_info.hysteresis) { 00453 return; 00454 } 00455 00456 // Calculates current ETX 00457 current_etx = etx_current_calc(etx, accumulated_failures); 00458 00459 // If difference is more than hysteresis 00460 if (current_etx > *stored_diff_etx) { 00461 if (current_etx - *stored_diff_etx >= etx_info.hysteresis) { 00462 callback = true; 00463 } 00464 } else if (current_etx < *stored_diff_etx) { 00465 if (*stored_diff_etx - current_etx >= etx_info.hysteresis) { 00466 callback = true; 00467 } 00468 } 00469 00470 // Calls callback function 00471 if (callback) { 00472 etx_info.callback_ptr(etx_info.interface_id, (*stored_diff_etx) >> 4, current_etx >> 4, mac64_addr_ptr, mac16_addr); 00473 *stored_diff_etx = current_etx; 00474 } 00475 } 00476 00477 /** 00478 * \brief A function to check if accumulated failures callback is needed 00479 * 00480 * If the current number of accumulated failures is equal or greater than 00481 * the set threshold value, the function calls accumulated failures callback. 00482 * 00483 * \param neigh_table_ptr the neighbor node in question 00484 */ 00485 static void etx_accum_failures_callback_needed_check(struct mle_neigh_table_entry_t *neigh_table_ptr) 00486 { 00487 if (!etx_info.accum_threshold) { 00488 return; 00489 } 00490 00491 if (neigh_table_ptr->accumulated_failures < etx_info.accum_threshold) { 00492 return; 00493 } 00494 00495 etx_info.accum_cb_ptr(etx_info.interface_id, neigh_table_ptr->accumulated_failures, neigh_table_ptr); 00496 } 00497 00498 /** 00499 * \brief A function to remove ETX neighbor 00500 * 00501 * Notifies ETX module that neighbor has been removed. Calls ETX value change callback 00502 * if that is set. 00503 * 00504 * \param mac64_addr_ptr long MAC address 00505 * 00506 */ 00507 void etx_neighbor_remove(int8_t interface_id, mle_neigh_table_entry_t *neigh_table_ptr) { 00508 00509 uint16_t stored_diff_etx; 00510 (void) interface_id; 00511 if (neigh_table_ptr && etx_info.callback_ptr) { 00512 if (neigh_table_ptr->etx) { 00513 stored_diff_etx = neigh_table_ptr->stored_diff_etx >> 4; 00514 if (!stored_diff_etx) { 00515 stored_diff_etx = 0xffff; 00516 } 00517 etx_info.callback_ptr(etx_info.interface_id, stored_diff_etx, 0xffff, neigh_table_ptr->mac64, 00518 neigh_table_ptr->short_adr); 00519 } 00520 } 00521 } 00522 00523 /** 00524 * \brief A function to add ETX neighbor 00525 * 00526 * Notifies ETX module that neighbor has been added. Calls ETX value change callback 00527 * if that is set. 00528 * 00529 * \param mac64_addr_ptr long MAC address 00530 * 00531 */ 00532 void etx_neighbor_add(int8_t interface_id, mle_neigh_table_entry_t *neigh_table_ptr) { 00533 00534 uint16_t stored_diff_etx; 00535 (void) interface_id; 00536 if (neigh_table_ptr && etx_info.callback_ptr) { 00537 // Gets table entry 00538 00539 if (neigh_table_ptr->etx) { 00540 stored_diff_etx = neigh_table_ptr->stored_diff_etx; 00541 if (!stored_diff_etx) { 00542 stored_diff_etx = neigh_table_ptr->etx; 00543 } 00544 etx_info.callback_ptr(etx_info.interface_id, stored_diff_etx >> 4, neigh_table_ptr->etx >> 4, 00545 neigh_table_ptr->mac64, neigh_table_ptr->short_adr); 00546 } 00547 } 00548 }
Generated on Tue Jul 12 2022 13:02:51 by
