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.
Dependencies: nRF51_Vdd TextLCD BME280
etx.c
00001 /* 00002 * Copyright (c) 2014-2018, 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/mac_neighbor_table/mac_neighbor_table.h" 00032 #include "Service_Libs/utils/isqrt.h" 00033 00034 //TODO: Refactor this away! 00035 #include "MAC/rf_driver_storage.h" 00036 00037 #define TRACE_GROUP "etx" 00038 00039 static uint16_t etx_current_calc(uint16_t etx, uint8_t accumulated_failures); 00040 static uint16_t etx_dbm_lqi_calc(uint8_t lqi, int8_t dbm); 00041 static void etx_value_change_callback_needed_check(uint16_t etx, uint16_t *stored_diff_etx, uint8_t accumulated_failures, uint8_t attribute_index); 00042 static void etx_accum_failures_callback_needed_check(etx_storage_t * entry, uint8_t attribute_index); 00043 00044 typedef struct { 00045 uint16_t hysteresis; // 12 bit fraction 00046 uint8_t accum_threshold; 00047 etx_value_change_handler_t *callback_ptr; 00048 etx_accum_failures_handler_t *accum_cb_ptr; 00049 etx_storage_t *etx_storage_list; 00050 uint8_t ext_storage_list_size; 00051 int8_t interface_id; 00052 } ext_info_t; 00053 00054 static ext_info_t etx_info = { 00055 .hysteresis = 0, 00056 .accum_threshold = 0, 00057 .callback_ptr = NULL, 00058 .accum_cb_ptr = NULL, 00059 .etx_storage_list = NULL, 00060 .ext_storage_list_size = 0, 00061 .interface_id = -1 00062 }; 00063 00064 /** 00065 * \brief A function to update ETX value based on transmission attempts 00066 * 00067 * Update is made based on failed and successful message sending 00068 * attempts for a message. 00069 * 00070 * \param attempts number of attempts to send message 00071 * \param success was message sending successful 00072 * \param addr_type address type, ADDR_802_15_4_SHORT or ADDR_802_15_4_LONG 00073 * \param addr_ptr PAN ID with 802.15.4 address 00074 */ 00075 void etx_transm_attempts_update(int8_t interface_id, uint8_t attempts, bool success, uint8_t attribute_index) 00076 { 00077 uint32_t etx; 00078 uint8_t accumulated_failures; 00079 // Gets table entry 00080 etx_storage_t * entry = etx_storage_entry_get(interface_id, attribute_index); 00081 if (!entry) { 00082 return; 00083 } 00084 00085 accumulated_failures = entry->accumulated_failures; 00086 00087 if (!success) { 00088 /* Stores failed attempts to estimate ETX and to calculate 00089 new ETX after successful sending */ 00090 if (accumulated_failures + attempts < 32) { 00091 entry->accumulated_failures += attempts; 00092 } else { 00093 success = true; 00094 } 00095 } 00096 00097 if (success) { 00098 entry->accumulated_failures = 0; 00099 } else { 00100 etx_accum_failures_callback_needed_check(entry, attribute_index); 00101 } 00102 00103 if (entry->etx) { 00104 // If hysteresis is set stores ETX value for comparison 00105 if (etx_info.hysteresis && !entry->stored_diff_etx) { 00106 entry->stored_diff_etx = entry->etx; 00107 } 00108 00109 if (success) { 00110 // ETX = 7/8 * current ETX + 1/8 * ((attempts + failed attempts) << 12) 00111 etx = entry->etx - (entry->etx >> ETX_MOVING_AVERAGE_FRACTION); 00112 etx += (attempts + accumulated_failures) << (12 - ETX_MOVING_AVERAGE_FRACTION); 00113 00114 if (etx > 0xffff) { 00115 entry->etx = 0xffff; 00116 } else { 00117 entry->etx = etx; 00118 } 00119 } 00120 00121 // If real ETX value has been received do not update based on LQI or dBm 00122 entry->tmp_etx = false; 00123 00124 // Checks if ETX value change callback is needed 00125 etx_value_change_callback_needed_check(entry->etx, &(entry->stored_diff_etx), entry->accumulated_failures, attribute_index); 00126 } 00127 } 00128 00129 /** 00130 * \brief A function to update ETX value based on remote incoming IDR 00131 * 00132 * Update is made based on remote incoming IDR received from 00133 * neighbor. 00134 * 00135 * \param remote_incoming_idr Remote incoming IDR 00136 * \param mac64_addr_ptr long MAC address 00137 */ 00138 void etx_remote_incoming_idr_update(int8_t interface_id, uint8_t remote_incoming_idr, uint8_t attribute_index) 00139 { 00140 etx_storage_t * entry = etx_storage_entry_get(interface_id, attribute_index); 00141 00142 if (entry) { 00143 // If ETX has been set 00144 if (entry->etx) { 00145 // If hysteresis is set stores ETX value to enable comparison 00146 if (etx_info.hysteresis && !entry->stored_diff_etx) { 00147 entry->stored_diff_etx = entry->etx; 00148 } 00149 // remote EXT = remote incoming IDR^2 (12 bit fraction) 00150 uint32_t remote_ext = ((uint32_t)remote_incoming_idr * remote_incoming_idr) << 2; 00151 00152 // ETX = 7/8 * current ETX + 1/8 * remote ETX */ 00153 uint32_t etx = entry->etx - (entry->etx >> ETX_MOVING_AVERAGE_FRACTION); 00154 etx += remote_ext >> ETX_MOVING_AVERAGE_FRACTION; 00155 00156 if (etx > 0xffff) { 00157 entry->etx = 0xffff; 00158 } else { 00159 entry->etx = etx; 00160 } 00161 00162 // Checks if ETX value change callback is needed 00163 etx_value_change_callback_needed_check(entry->etx, &(entry->stored_diff_etx), entry->accumulated_failures, attribute_index); 00164 } 00165 entry->remote_incoming_idr = remote_incoming_idr; 00166 } 00167 } 00168 00169 /** 00170 * \brief A function to read ETX value 00171 * 00172 * Returns ETX value for an address 00173 * 00174 * \param interface_id network interface id 00175 * \param addr_type address type, ADDR_802_15_4_SHORT or ADDR_802_15_4_LONG 00176 * \param addr_ptr PAN ID with 802.15.4 address 00177 * 00178 * \return 0x0100 to 0xFFFF ETX value (8 bit fraction) 00179 * \return 0xFFFF address not associated 00180 * \return 0x0000 address unknown or other error 00181 * \return 0x0001 no ETX statistics on this interface 00182 */ 00183 uint16_t etx_read(int8_t interface_id, addrtype_t addr_type, const uint8_t *addr_ptr) 00184 { 00185 protocol_interface_info_entry_t *interface = protocol_stack_interface_info_get_by_id(interface_id); 00186 00187 if (!addr_ptr || !interface) { 00188 return 0; 00189 } 00190 00191 if (interface->etx_read_override) { 00192 // Interface has modified ETX calculation 00193 return interface->etx_read_override(interface, addr_type, addr_ptr); 00194 } 00195 00196 uint8_t attribute_index; 00197 if (interface->nwk_id == IF_IPV6) { 00198 return 1; 00199 } 00200 00201 //Must Support old MLE table and new still same time 00202 mac_neighbor_table_entry_t *mac_neighbor = mac_neighbor_table_address_discover(mac_neighbor_info(interface), addr_ptr + PAN_ID_LEN, addr_type); 00203 if (!mac_neighbor) { 00204 return 0xffff; 00205 } 00206 attribute_index = mac_neighbor->index ; 00207 00208 00209 //tr_debug("Etx Read from atribute %u", attribute_index); 00210 00211 etx_storage_t * entry = etx_storage_entry_get(interface_id, attribute_index); 00212 00213 if (!entry) { 00214 return 0xffff; 00215 } 00216 00217 uint16_t etx = etx_current_calc(entry->etx, entry->accumulated_failures); 00218 etx >>= 4; 00219 00220 //tr_debug("Etx value %u", etx); 00221 00222 return etx; 00223 } 00224 00225 /** 00226 * \brief A function to read local incoming IDR value 00227 * 00228 * Returns local incoming IDR value for an address 00229 * 00230 * \param mac64_addr_ptr long MAC address 00231 * 00232 * \return 0x0100 to 0xFFFF incoming IDR value (8 bit fraction) 00233 * \return 0x0000 address unknown 00234 */ 00235 uint16_t etx_local_incoming_idr_read(int8_t interface_id, uint8_t attribute_index) 00236 { 00237 uint32_t local_incoming_idr = 0; 00238 etx_storage_t * entry = etx_storage_entry_get(interface_id, attribute_index); 00239 if (entry) { 00240 uint16_t local_etx = etx_current_calc(entry->etx, entry->accumulated_failures); 00241 00242 local_incoming_idr = isqrt32((uint32_t)local_etx << 16); 00243 // divide by sqrt(2^12) 00244 local_incoming_idr = local_incoming_idr >> 6; 00245 } 00246 00247 return local_incoming_idr; 00248 } 00249 00250 /** 00251 * \brief A function to read local incoming IDR value 00252 * 00253 * Returns local incoming IDR value for an address 00254 * 00255 * \param mac64_addr_ptr long MAC address 00256 * 00257 * \return 0x0100 to 0xFFFF incoming IDR value (8 bit fraction) 00258 * \return 0x0000 address unknown 00259 */ 00260 uint16_t etx_local_etx_read(int8_t interface_id, uint8_t attribute_index) 00261 { 00262 etx_storage_t * entry = etx_storage_entry_get(interface_id, attribute_index); 00263 if (!entry) { 00264 return 0; 00265 } 00266 return etx_current_calc(entry->etx, entry->accumulated_failures) >> 4; 00267 } 00268 00269 /** 00270 * \brief A function to calculate current ETX 00271 * 00272 * Returns current ETX value based on ETX and failed attempts. Return 00273 * value is scaled by scaling factor 00274 * 00275 * \param etx ETX (12 bit fraction) 00276 * \param accumulated_failures failed attempts 00277 * 00278 * \return ETX value (12 bit fraction) 00279 */ 00280 static uint16_t etx_current_calc(uint16_t etx, uint8_t accumulated_failures) 00281 { 00282 uint32_t current_etx; 00283 00284 // If there is no failed attempts 00285 if (accumulated_failures == 0) { 00286 current_etx = etx; 00287 } else { 00288 /* Calculates ETX estimate based on failed attempts 00289 ETX = current ETX + 1/8 * (failed attempts << 12) */ 00290 current_etx = etx + (accumulated_failures << (12 - ETX_MOVING_AVERAGE_FRACTION)); 00291 if (current_etx > 0xffff) { 00292 current_etx = 0xffff; 00293 } 00294 } 00295 00296 return current_etx; 00297 } 00298 00299 /** 00300 * \brief A function to update ETX value based on LQI and dBm 00301 * 00302 * Update is made based on dBM and LQI of received message. 00303 * 00304 * \param lqi link quality indicator 00305 * \param dbm measured dBm 00306 * \param mac64_addr_ptr long MAC address 00307 * 00308 * \return 0x0100 to 0xFFFF local incoming IDR value (8 bit fraction) 00309 */ 00310 uint16_t etx_lqi_dbm_update(int8_t interface_id, uint8_t lqi, int8_t dbm, uint8_t attribute_index) 00311 { 00312 uint32_t local_incoming_idr = 0; 00313 uint32_t etx = 0; 00314 00315 etx_storage_t *entry = etx_storage_entry_get(interface_id, attribute_index); 00316 00317 if (entry) { 00318 // If local ETX is not set calculate it based on LQI and dBm 00319 if (!entry->etx) { 00320 etx = etx_dbm_lqi_calc(lqi, dbm); 00321 entry->etx = etx; 00322 entry->tmp_etx = true; 00323 } 00324 // If local ETX has been calculated without remote incoming IDR and 00325 // remote incoming IDR is available update it by remote incoming IDR value 00326 if (entry->remote_incoming_idr && entry->tmp_etx) { 00327 entry->tmp_etx = false; 00328 00329 local_incoming_idr = isqrt32((uint32_t)entry->etx << 16); 00330 // divide by sqrt(2^12) and scale to 12 bit fraction 00331 local_incoming_idr = local_incoming_idr >> 2; 00332 00333 etx = local_incoming_idr * (((uint16_t)entry->remote_incoming_idr) << 7); 00334 entry->etx = etx >> 12; 00335 00336 local_incoming_idr >>= 4; 00337 } 00338 00339 // If local ETX has been calculated indicates new neighbor 00340 if (etx) { 00341 etx_neighbor_add(interface_id, attribute_index); 00342 } 00343 } 00344 00345 // If local ETX is not set return temporary ETX based on LQI and dB, 00346 if (!local_incoming_idr) { 00347 if (!etx) { 00348 etx = etx_dbm_lqi_calc(lqi, dbm); 00349 } 00350 00351 local_incoming_idr = isqrt32(etx << 16); 00352 // divide by sqrt(2^12) 00353 local_incoming_idr >>= 6; 00354 } 00355 00356 return local_incoming_idr; 00357 } 00358 00359 /** 00360 * \brief A function to calculate ETX value based on dBm and LQI 00361 * 00362 * Calculation is made using RF driver service. If service does not 00363 * exists then local function is used. 00364 * 00365 * \param lqi link quality indicator 00366 * \param dbm measured dBm 00367 * 00368 * \return ETX value (12 bit fraction) 00369 */ 00370 static uint16_t etx_dbm_lqi_calc(uint8_t lqi, int8_t dbm) 00371 { 00372 protocol_interface_info_entry_t *cur = 0; 00373 int8_t driver_ret_value = -1; 00374 uint16_t etx; 00375 00376 phy_signal_info_s signal_info; 00377 signal_info.type = PHY_SIGNAL_INFO_ETX; 00378 signal_info.lqi = lqi; 00379 signal_info.dbm = dbm; 00380 signal_info.result = 0xffff; 00381 00382 //TODO: This is needed, but RF driver cannot be accessed directly! Figure out MAC extension for this. 00383 cur = protocol_stack_interface_info_get(IF_6LoWPAN); 00384 if ((cur) && (cur->dev_driver) && (cur->dev_driver->phy_driver)) { 00385 phy_device_driver_s *dev_driver = cur->dev_driver->phy_driver; 00386 if (dev_driver->extension) { 00387 driver_ret_value = dev_driver->extension(PHY_EXTENSION_CONVERT_SIGNAL_INFO, (uint8_t *)&signal_info); 00388 } 00389 } 00390 00391 if ((driver_ret_value != -1) && (signal_info.result != 0xffff)) { 00392 etx = signal_info.result; 00393 etx <<= 4; 00394 } else { 00395 /* Atmel version 00396 dBm = RSSI base value [dBm] + 1.03 [dB] x ED level 00397 LQI = errors in received frame */ 00398 00399 // for dBm -90 and LQI 0 ETX will be 2.4 00400 etx = ((dbm * -1) * (256 - lqi)); 00401 etx >>= 1; // scale result to 12 bits 00402 etx += 1 << 12; // add one (perfect link) 00403 } 00404 00405 return etx; 00406 } 00407 00408 /** 00409 * \brief A function to register ETX value change callback 00410 * 00411 * Register ETX value change callback. When ETX value has changed more or equal 00412 * to hysteresis value ETX module calls ETX value change callback. 00413 * 00414 * \param nwk_interface_id network interface id 00415 * \param hysteresis hysteresis value (8 bit fraction) 00416 * \param callback_ptr callback function pointer 00417 * 00418 * \return 0 not 6LowPAN interface 00419 * \return 1 success 00420 */ 00421 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) 00422 { 00423 if ((nwk_id == IF_6LoWPAN) && hysteresis && callback_ptr) { 00424 etx_info.hysteresis = hysteresis << 4; 00425 etx_info.callback_ptr = callback_ptr; 00426 etx_info.interface_id = interface_id; 00427 return 1; 00428 } else { 00429 return 0; 00430 } 00431 } 00432 00433 bool etx_storage_list_allocate(int8_t interface_id, uint8_t etx_storage_size) 00434 { 00435 if (!etx_storage_size) { 00436 ns_dyn_mem_free(etx_info.etx_storage_list); 00437 etx_info.etx_storage_list = NULL; 00438 etx_info.ext_storage_list_size = 0; 00439 return true; 00440 } 00441 00442 if (etx_info.ext_storage_list_size == etx_storage_size) { 00443 return true; 00444 } 00445 00446 ns_dyn_mem_free(etx_info.etx_storage_list); 00447 etx_info.ext_storage_list_size = 0; 00448 etx_info.etx_storage_list = ns_dyn_mem_alloc(sizeof(etx_storage_t) * etx_storage_size); 00449 if (!etx_info.etx_storage_list) { 00450 return false; 00451 } 00452 00453 etx_info.ext_storage_list_size = etx_storage_size; 00454 etx_info.interface_id = interface_id; 00455 etx_storage_t * list_ptr = etx_info.etx_storage_list; 00456 for (uint8_t i = 0; i< etx_storage_size; i++) { 00457 memset(list_ptr, 0, sizeof(etx_storage_t)); 00458 00459 list_ptr++; 00460 } 00461 return true; 00462 00463 } 00464 00465 etx_storage_t *etx_storage_entry_get(int8_t interface_id, uint8_t attribute_index) 00466 { 00467 if (etx_info.interface_id != interface_id || !etx_info.etx_storage_list || attribute_index >= etx_info.ext_storage_list_size) { 00468 tr_debug("Unknow ID or un initilized ETX %u", attribute_index); 00469 return NULL; 00470 } 00471 00472 etx_storage_t *entry = etx_info.etx_storage_list + attribute_index; 00473 return entry; 00474 } 00475 00476 00477 /** 00478 * \brief A function to register accumulated failures callback 00479 * 00480 * When the number of accumulated failures has reached the threshold 00481 * value, the ETX module calls the accumulated failures callback on 00482 * every transmission failure. 00483 * 00484 * \param nwk_id network ID (6LoWPAN) 00485 * \param interface_id interface ID 00486 * \param threshold threshold value for accumulated failures 00487 * \param callback_ptr callback function pointer 00488 * 00489 * \return 0 not 6LowPAN interface 00490 * \return 1 success 00491 */ 00492 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) 00493 { 00494 if ((nwk_id == IF_6LoWPAN) && threshold && callback_ptr) { 00495 etx_info.interface_id = interface_id; 00496 etx_info.accum_threshold = threshold; 00497 etx_info.accum_cb_ptr = callback_ptr; 00498 return 1; 00499 } else { 00500 return 0; 00501 } 00502 } 00503 00504 /** 00505 * \brief A function to check if ETX value change callback is needed 00506 * 00507 * Calculates current ETX and compares it against stored ETX. If change 00508 * of the values is more than hysteresis calls ETX value change 00509 * callback. 00510 * 00511 * \param etx ETX (12 bit fraction) 00512 * \param stored_diff_etx stored ETX value 00513 * \param accumulated_failures failed attempts 00514 * \param mac64_addr_ptr long MAC address 00515 * \param mac16_addr short MAC address or 0xffff address is not set 00516 * 00517 * \return ETX value (12 bit fraction) 00518 */ 00519 static void etx_value_change_callback_needed_check(uint16_t etx, uint16_t *stored_diff_etx, uint8_t accumulated_failures, uint8_t attribute_index) 00520 { 00521 uint16_t current_etx; 00522 bool callback = false; 00523 if (!etx_info.hysteresis) { 00524 return; 00525 } 00526 00527 // Calculates current ETX 00528 current_etx = etx_current_calc(etx, accumulated_failures); 00529 00530 // If difference is more than hysteresis 00531 if (current_etx > *stored_diff_etx) { 00532 if (current_etx - *stored_diff_etx >= etx_info.hysteresis) { 00533 callback = true; 00534 } 00535 } else if (current_etx < *stored_diff_etx) { 00536 if (*stored_diff_etx - current_etx >= etx_info.hysteresis) { 00537 callback = true; 00538 } 00539 } 00540 00541 // Calls callback function 00542 if (callback) { 00543 etx_info.callback_ptr(etx_info.interface_id, (*stored_diff_etx) >> 4, current_etx >> 4, attribute_index); 00544 *stored_diff_etx = current_etx; 00545 } 00546 } 00547 00548 /** 00549 * \brief A function to check if accumulated failures callback is needed 00550 * 00551 * If the current number of accumulated failures is equal or greater than 00552 * the set threshold value, the function calls accumulated failures callback. 00553 * 00554 * \param neigh_table_ptr the neighbor node in question 00555 */ 00556 static void etx_accum_failures_callback_needed_check(etx_storage_t * entry, uint8_t attribute_index) 00557 { 00558 if (!etx_info.accum_threshold) { 00559 return; 00560 } 00561 00562 if (entry->accumulated_failures < etx_info.accum_threshold) { 00563 return; 00564 } 00565 00566 etx_info.accum_cb_ptr(etx_info.interface_id, entry->accumulated_failures, attribute_index); 00567 } 00568 00569 /** 00570 * \brief A function to remove ETX neighbor 00571 * 00572 * Notifies ETX module that neighbor has been removed. Calls ETX value change callback 00573 * if that is set. 00574 * 00575 * \param mac64_addr_ptr long MAC address 00576 * 00577 */ 00578 void etx_neighbor_remove(int8_t interface_id, uint8_t attribute_index) { 00579 00580 tr_debug("Remove attribute %u", attribute_index); 00581 uint16_t stored_diff_etx; 00582 etx_storage_t *entry = etx_storage_entry_get(interface_id, attribute_index); 00583 if (entry && etx_info.callback_ptr) { 00584 00585 if (entry->etx) { 00586 stored_diff_etx = entry->stored_diff_etx >> 4; 00587 if (!stored_diff_etx) { 00588 stored_diff_etx = 0xffff; 00589 } 00590 etx_info.callback_ptr(etx_info.interface_id, stored_diff_etx, 0xffff, attribute_index); 00591 } 00592 //Clear all data base back to zero for new user 00593 memset(entry, 0, sizeof(etx_storage_t)); 00594 } 00595 } 00596 00597 /** 00598 * \brief A function to add ETX neighbor 00599 * 00600 * Notifies ETX module that neighbor has been added. Calls ETX value change callback 00601 * if that is set. 00602 * 00603 * \param mac64_addr_ptr long MAC address 00604 * 00605 */ 00606 void etx_neighbor_add(int8_t interface_id, uint8_t attribute_index) { 00607 00608 tr_debug("Add attribute %u", attribute_index); 00609 uint16_t stored_diff_etx; 00610 etx_storage_t *entry = etx_storage_entry_get(interface_id, attribute_index); 00611 if (entry && etx_info.callback_ptr) { 00612 // Gets table entry 00613 00614 if (entry->etx) { 00615 stored_diff_etx = entry->stored_diff_etx; 00616 if (!stored_diff_etx) { 00617 stored_diff_etx = entry->etx; 00618 } 00619 etx_info.callback_ptr(etx_info.interface_id, stored_diff_etx >> 4, entry->etx >> 4, 00620 attribute_index); 00621 } 00622 } 00623 }
Generated on Tue Jul 12 2022 15:15:43 by
