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.
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 12:21:50 by
