writing proc avg in tcl script in NS2 - tcl

I want to calculate avg of { energy_level ,number of nodes & traffic's data on nodes } by one mobile sink in the network with 5 static nodes.
I have to calculate this avg with proc in tcl script code not with awk code.
Please help me

If you have collected a list of values that you want to compute the average of, use this procedure to do the computation:
proc ArithmeticMean {listOfValues} {
set length [llength $listOfValues]
if {$length == 0} {
return 0.0
}
set sum [::tcl::mathop::+ {*}$listOfValues]
return [expr {double($sum) / $length}]
}
The summing of the values uses the + “operator command” with expansion syntax, and is the cheapest way of adding them all together.

Related

Assign random number to node in TCL script for ns-2: ERROR variable is array

I am trying to run the following tcl script but getting an error
can't set "val": variable is array
while executing
"set val [random_int $upper_limit]"
Here is my code,Please any help
proc random_int { upper_limit } {
global myrand
set myrand [expr int(rand() * $upper_limit + 1)]
return $myrand
}
set upper_limit 21
set val [random_int $upper_limit]
$ns at 0.6 "[$node($val) set ragent_] malicious"
Your current main problem is that there's an existing use of the val as an array; Tcl's variables can't simultaneously be scalars and arrays. The most expedient fix is to change the name of the variable, perhaps to value.
set value [random_int $upper_limit]
$ns at 0.6 "[$node($value) set ragent_] malicious"
Apart from that, your random number generator could be a bit sharper code. It probably doesn't need to access any global variables, and it really should have the expression put in braces (for a bunch of reasons including both speed and safety). Here's the trimmed/tuned version:
proc random_int { upper_limit } {
expr { int(rand() * $upper_limit + 1) }
}
Occasionally, I write such procedures slightly differently, like this:
proc random_int { upper_limit } {expr {
int(rand() * $upper_limit + 1)
}}
It's semantically identical, but it makes it clearer what the author is really thinking about.

multidimensional array search in tcl

I am using multidimensional associative array in tcl ,from fourth column to N column I am storing values and i will compare each of those with second column and store the result in third column for each of row's values.Sometimes it searches the perfectly sometimes it skips the values and gives out error.Is there any better way to search in multidimensional array in tcl ? What is the problem with this code?
for {set index 1} {$index < $count} {incr index} {
for {set val 3} {$val < $d1 } {incr val} {
if {$flag1 != 1} {
if {$asarr($index,2)== $asarr($index,$val)} {
set asarr($index,1) 50
} else {
set asarr($index,1) 100
set flag1 1
break
}
}
}
set flag1 0
}
There is a discrepancy between your code and the problem explanation in your question.
From your question I summarise these requirements:
data is stored in 2 dimensional array
each row stores one set of data
within each row, data is stored in columns where:
1st column is not mention in the original question
2nd column contains a value against which all 'data columns' will be compared
3rd column is where the result of the comparison will be stored (looking at the code, result is either a 50 or a 100)
4th to Nth column hold the data that needs to be compared against value of 2nd column
And this is what your code actually does:
# start iterating array by rows
# first row has index 1
for {set index 1} {$index < $count} {incr index} {
# start iterating over columns
# NOTE: either column indexes are assumed to start at 0
# or we, in fact, start with the 3rd column instead of 4th
for {set val 3} {$val < $d1 } {incr val} {
# check the special flag named 'flag1'
# NOTE: it would be better to give it a meaningful name
# such as 'found' since it signals that we found
# an element equal to the one in 2nd column
if {$flag1 != 1} {
if {$asarr($index,2)== $asarr($index,$val)} {
# if the element at current position ($index,$val)
# is equal to the element at ($index,2)
# write '50' at ($index,1)
# NOTE: result is stored in 1st column,
# not in 3rd as the question would assume
set asarr($index,1) 50
} else {
# if the current element is not equal to the one
# in 2nd column, write '100' at ($index, 1).
# Again, this is not the 3rd column as requested originally
set asarr($index,1) 100
# set the special flag to 1, meaning that the above
# IF clause will not be run again
set flag1 1
# execute a break, meaning that we immediately interrupt
# the inner FOR loop, meaning that the above IF clause
# will not be run again in this iteration.
# As Donal pointed out, you could completely
# remove the $flag1 and achieve the same result with
# this break clause
break
}
}
}
# reset the flag so that IF clause is executed at least once
# for the next iteration of the outer loop
set flag1 0
}
If we assume that the 'flag1' is set to 0 (or anything != 1) before the FOR loops start, then the end result is that the code iterates through array rows, compares each column (starting from 3rd!) against column number 2, it repeats this for all other columns BUT ONLY if their values are equal to column 2 and EACH TIME OVERWRITES THE SAME value '50' over column 1. As soon as a different value is encountered, the result '100' is written to column 1 and the rest of the values are immediately skipped. Execution then starts from the beginning but with the next row.
How does that compare against your original intentions?
The code could (and should) be greatly simplified at least so it doesn't overwrite the same value many times, however, I am not exactly sure what you are trying to achieve. Please try to clarify your requirements.
I don't know if this will be useful (you said that the code does not behave as you would like it to), but here is an alternative version that behaves according to the requirements I guessed from your question (and listed above):
for {set index 1} {$index < $count} {incr index} {
set asarr($index,3) 50
for {set val 4} {$val < $d1 } {incr val} {
if {$asarr($index,2)!= $asarr($index,$val)} {
set asarr($index,3) 100
break
}
}
}

conversion of and operation having a function from c# to tcl

how to do the and operation given as one line statement in tcl in tcl where pcieDeviceControlRegister is a function given as in the code:
code:
pcieDeviceControlRegister = cfgSpace.pcieDeviceControlRegister & (~((uint)0xF));
Reference for pcieDeviceControlRegister function is :
public uint pcieDeviceControlRegister
{
get
{
if (pcieCapabilityOffset != 0)
return (ReadDW((int)(pcieCapabilityOffset + 8) / 4, 0xF)) & 0xFFFF;
else
return 0;
}
set
{
if (pcieCapabilityOffset != 0)
{
uint val = ReadDW((int)(pcieCapabilityOffset + 8) / 4, 0xF)& 0xFFFF0000;
val |= value;
// write should be done with byte enables !!!
WriteDW((int)(pcieCapabilityOffset + 8) / 4, val, 0xF);
}
}
}
You'll have to arrange for the mapping of ReadDW and WriteDW into Tcl, probably by writing a little C or C++ code that makes commands (with the same names) that do those operations. I'm assuming that you've already done that. (SWIG can generate the glue code if you need it.)
Then, we define a command like this:
proc pcieDeviceControlRegister {{newValue ""}} {
global pcieCapabilityOffset
# Filter the bogus setup case early; if this is really an error case though,
# it is better to actually throw an error instead of struggling on badly.
if {$pcieCapabilityOffset == 0} {
return 0
# error "PCIE capability offset is zero"
}
set offset [expr {($pcieCapabilityOffset + 8) / 4}]
if {$newValue eq ""} {
# This is a read operation
return [expr {[ReadDW $offset 0xF] & 0xFFFF}]
} else {
# This is a write operation
set val [expr {[ReadDW $offset 0xF] & 0xFFFF0000}]
# Note that we do the bit filtering HERE
set val [expr {$val | ($newValue & 0xFFFF)}]
WriteDW $offset $val 0xF
return
}
}
With that, which you should be able to see is a pretty simple translation of the C# property code (with a bit of minor refactoring), you can then write your calling code like this:
pcieDeviceControlRegister [expr {[pcieDeviceControlRegister] & ~0xF}]
With Tcl, you don't write casts to different types of integers: Tcl just has numbers (which are theoretically of infinite width) so instead you need to do a few more bit masks in key places.
The conversion of the above code to a method on an object is left as an exercise. It doesn't change very much…

tcl formatting floating point with fixed precision

# the unit of period is picosecond
set period 625000.0
set period_sec [format %3.6g [expr $period * 1e-12]]
puts $period_sec
result: 6.25e-07
Is there a way to force tcl to get results like 625e-09
Assuming that you want to format it to the nearest exponent, you could use a proc which formats it like this:
proc fix_sci {n} {
# Not a sci-fmt number with negative exponent
if {![string match "*e-*" $n]} {return $n}
# The set of exponents
set a 9
set b 12
# Grab the number (I called it 'front') and the exponent (called 'exp')
regexp -- {(-?[0-9.]+)e-0*([0-9]+)} $n - front exp
# Check which set of exponent is closer to the exponent of the number
if {[expr {abs($exp-$a)}] < [expr {abs($exp-$b)}]} {
# If it's the first, get the difference and adjust 'front'
set dif [expr {$exp-$a}]
set front [expr {$front/(10.0**$dif)}]
set exp $a
} else {
# If it's the first, get the difference and adjust 'front'
set dif [expr {$exp-$b}]
set front [expr {$front/(10.0**$dif)}]
set exp $b
}
# Return the formatted numbers, front in max 3 digits and exponent in 2 digits
return [format %3ge-%.2d $front $exp]
}
Note that your original code returns 6.25e-007 (3 digits in the exponent).
If you need to change the rule or rounding the exponent, you will have to change the if part (i.e. [expr {abs($exp-$a)}] < [expr {abs($exp-$b)}]). For example $exp >= $a could be used to format if the exponent is 9 or below.
ideone demo of above code for 'closest' exponent.
For Tcl versions before 8.5, use pow(10.0,$dif) instead of 10.0**$dif
I do not think there is anything in the format command that will help you directly. However, if you consider a slight variation on the format code, then it may be a lot easier to get what you want (with a bit of string manipulation):
format %#3.6g $number
gives a number like: 6.25000e-007
This can be parsed more easily:
Extract the exponent
Determine the number of positions to shift the decimal point
Shift it and replace the exponent
It is not entirely straightforward, I am afraid, but it should be doable. Wiki page http://wiki.tcl.tk/5000 may give you some inspiration.

how to use Tcl language to control simulation

I want to use tcl to control simulation iteration.
In each iteration, I get some random value, and save the results
set set_of_double_values{0}
foreach y_val $set_of_double_values {
set_parameter Snr $y_val ;
set numsim 2;
set_value/$env(sim_name)/numsim $numsim;
for {set i 0} {$i < $numsim} {incr i 1} {
some random value
}
run_iteration;
}
run_iteration
using this code, I always run the inner part many times, and I can not save the right results...