# # Call Gap Simulation using Asychronous Elastic sampled Catch Up Call Generator - AESCUCG # # mawk -f cg6a.awk p.txt > cg6op.csv # # p.txt is empty and is required to prevent the script waiting for input from stdio # # type cg6bop.txt | sort > cg6bopSorted.txt # # # In the telephony books, calls are said to be spaced by an interArrivalTime # # The interArrivalTime is often modelled based on a negative exponetial. # interArrivalTime = -log(1-rand( )) / CallsPerSecond # # The negative exponetial distribution has the properties that when a call starts does not depend on history. # # The CallsPerSecond is the mean call rate. # # A simple call generator can be built that makes a call and waits for the interArrivalTime, # # Most real time systems allow timer interupts. # # These can be either regular ticks or started for the wanted duration. # This AWK script models what happens if the interArrivalTime required is less that the # smallest timer tick. # # It is possible to model the simple call generator in non real time by looping the simple # call generator, and incrementing the simulatedTime by adding interArrivalTime to simulatedTime. # # This script combines the simulation and a constant time time method and uses a catch up while loop # to catch up the simulatedTime each timerTick. # # This script also models a Call Gapping loosely based on ITU INAP Call Gapping. # NOTE: However, instead of storing the gapInterval in each call gapper it is global # NOTE: CallGap Messages are not sent to the simulated , # call gapping is applied to all simulated ssp and numbers. # # Time is stored in a variable called simulatedTime and realTime # # simulatedTime is incremented by the interArrivalTimes once per simulated call # tickedTime is incremented by the timerticks once per timer tick or interupt # # # Every time the tickTimer ticks I would normally call a function called timerTickTimeSliceCode() # Every time a simulated call is required I call a function called nextCallOpportunityTimeSlice( simulatedTime ) # # # The Timer ticks are at a constant interval. This script uses a loop, and does not wait for real time. # BEGIN { CallsPerSecond = 5 simulatedTime = 0.0 tickedTime = 0.0 startGapSimulatedTime = 0.0 gapInterval = 1.0 # seconds gapDuration = 120 # seconds - if no call gap updates for this duration, Call gapper is deallocated. calls = 0 rejectedCalls = 0 sentCalls = 0 timerTick = 0.5 timerTicks = 1000 numSSP = 10 numCG = 5 # numbers of Full Dialled numbers seen. # cgA[] - An associative array to store all call gapper instances. # # Associative arrays have an index # AWK does not support structures but does support Associative arrays of values # the type of the value can be a string or number. # # Why not structure it using _ and . # # instead of: # ssp[instance].dialledNumber[leadingDigits].timeOfLastCall # cgA[ "ssp_1_leadingDigits.timeOfLastCall ] # cgA[ "ssp[1].dn[01234-56789].timeOfLastCall" ] print "Simulation uses: CallsPerSecond = " CallsPerSecond ", gapInterval = " gapInterval " s, timerTick = " timerTick ", numSSP= " numSSP ", numCG= " numCG # numbers of Full Dialled numbers seen. print "" print "===================== TOTALS: , calls , sentCalls , rejectedCalls , callsInTimeSlice " print "thisCall: newCallDetailsP , calls: , instancesA[ newCallDetailsP ] ,tickedTime:, tickedTime ,simulatedTime:, simulatedTime , simulatedTimeDelta , timeSinceLastCall:, timeSinceLastCall , call ,outgome:, cgA[ newCallDetailsP .callOutcome ]" } # # # function rejectThisCall( newCallDetailsP ){ rejectedCalls ++ # callOutcome = "rejectThisCall," cgA[ newCallDetailsP ".rejectCall" ] ++ } function sendThisCall( newCallDetailsP ){ sentCalls ++ # cgA[ newCallDetailsP ".callOutcome" ] is updated in callGapThisCall() cgA[ newCallDetailsP ".sendCall" ] ++ } function nextInterArrivalTime() { #-LN(1-A7)/$B$2 # -LN( interArrivalTime = -log(1-rand( )) / CallsPerSecond #print " nextInterArrivalTime()" interArrivalTime return interArrivalTime } function startCallGapInterval( newCallDetailsP , simulatedTime ){ # what needs to be done is already done. # print " startCallGapInterval(" # startGapSimulatedTime = simulatedTime # cgA[ newCallDetailsP ".startGapSimulatedTime" ] = simulatedTime } function callOpportunity( ){ # # randomly pick an exchange and dialled number # # It is assumed that a call gapper exists for each dialled number on all exchanges. # pickedExchange = int( rand( )* numSSP ) pickedNumber = "01234-56789" int( rand( ) * numCG ) # only supports full Dialled number gapping # gap each full number #instance = "ssp_" pickedExchange "_" pickedNumber "" instance = "ssp[" pickedExchange "].dn[" pickedNumber "]" # count the number of times this CG has been picked instancesA[ instance ] ++ #print "newExchange: " instance return instance } function callGapThisCall( newCallDetailsP, simulatedTime1 ){ # # look up call details for this Call Gapper instance. # # Run call gapping logic. # NOTE: at the end of the duration, the call generator is not removed, # so test if not used for longer than the duration and reallocate. # startGapSimulatedTime = cgA[ newCallDetailsP ".startGapSimulatedTime" ] if( startGapSimulatedTime == "" ) { cgA[ newCallDetailsP ".startGapSimulatedTime" ] = simulatedTime1 cgA[ newCallDetailsP ".callOutcome" ] = "new CallGapper Allocated " # print " function callGapThisCall(" newCallDetailsP "," simulatedTime1 "," cgA[ newCallDetailsP ".startGapSimulatedTime" ] "," cgA[ newCallDetailsP ".callOutcome" ] sentCalls ++ return ( 1 == 0 ) } # simulate call gappers being removed due to inactivity # If a call gap message has not been received for gapDuration seconds, deallocate call Gapper # This code does not receive a stream of call gap update messages. # if( ( simulatedTime1 - startGapSimulatedTime ) > gapDuration ){ # # reallocate a call gapper and restart timer. # cgA[ newCallDetailsP ".startGapSimulatedTime" ] = simulatedTime1 cgA[ newCallDetailsP ".callOutcome" ] = "reAllocate CallGapper and start it " sentCalls ++ return ( 1 == 0 ) } # # gapInterval = cgA[ newCallDetailsP ".gapInterval" ] # All call gappers have the same interval and duration # if ( ( simulatedTime - startGapSimulatedTime ) < gapInterval ) { # do not update cgA[ newCallDetailsP ".startGapSimulatedTime" ] # why not # cgA[ newCallDetailsP ".startGapSimulatedTime" ] = simulatedTime1 cgA[ newCallDetailsP ".callOutcome" ] = "rejectCall" return ( 1 == 1 ) } else { cgA[ newCallDetailsP ".startGapSimulatedTime" ] = simulatedTime1 cgA[ newCallDetailsP ".callOutcome" ] = "sendCall" return ( 1 == 0 ) } } #tickedTime = tickedTime+50ms #// function to be run each InterArrival Time function nextCallOpportunityTimeSlice( simulatedTime ) { calls ++ cgA[ newCallDetailsP ".lastSimulatedTime" ] = simulatedTime simulatedTimeDelta = nextInterArrivalTime() newSimulatedTime = simulatedTime + simulatedTimeDelta # has simulatedTimeTime newCallDetailsP = callOpportunity( ) #// randomly pick some call details. lastCall = cgA[ newCallDetailsP ".startGapSimulatedTime" ] if ( callGapThisCall( newCallDetailsP, newSimulatedTime ) ) { rejectThisCall( newCallDetailsP ) #// Gap this call, but still update call stats } else { sendThisCall( newCallDetailsP ) #// Admit this call, and update call stats startCallGapInterval( newCallDetailsP, newSimulatedTime ) } timeSinceLastCall = ( newSimulatedTime - cgA[ newCallDetailsP ".lastSimulatedTime" ] ) #print "thisCall: ," tickedTime "," simulatedTime "," simulatedTimeDelta "," ( simulatedTime - startGapSimulatedTime ) "," call "," callOutcome print "thisCall: " newCallDetailsP ", calls: ," instancesA[ newCallDetailsP ] ",tickedTime:," tickedTime ",simulatedTime:," simulatedTime "," simulatedTimeDelta ", timeSinceLastCall:," timeSinceLastCall "," call ",outgome:," cgA[ newCallDetailsP ".callOutcome" ] return simulatedTimeDelta } #On TimerTick: function timerTickTimeSliceCode(){ tickedTime = tickedTime + timerTick callsInTimeSlice = 0 while ( simulatedTime < tickedTime ){ callsInTimeSlice ++ simulatedTime = simulatedTime + nextCallOpportunityTimeSlice( simulatedTime ) #// oversample this to catch up # print "call: " call " nextCallOpportunityTimeSlice( " simulatedTime ")" } # # no more calls in this time slice # # NOTE: add 100 to callsInTimeSlice so that you get a 3 digit number for sorting # distributionA[ 100+callsInTimeSlice ]++ print "===timerTickTimeSlice=== TOTALS:,calls:," calls ",sentCalls:," sentCalls ",rejectedCalls:," rejectedCalls ",callsInTimeSlice:," callsInTimeSlice } END { # # # instancesA[ instance ] ++ print "===================== TOTALS: ," calls ", " sentCalls ", " rejectedCalls ", timeTickTimeSliceCode( ," simulatedTime "," tickedTime ", ) calls in timeSlice ", call for( cnt = 0 ; cnt < timerTicks ; cnt ++ ) { timerTickTimeSliceCode() } print "===================== Simulation =====================" print "Simulation uses: CallsPerSecond = " CallsPerSecond ", gapInterval = " gapInterval " s" print " timerTick = " timerTick ", timerTicks =" timerTicks ", totalTime=" ( timerTick * timerTicks ) " seconds " print " numSSP= " numSSP ", numCG= " numCG # numbers of Full Dialled numbers seen. print "===================== Instances =====================" totalCalls = 0 for( instance in instancesA ){ opStr = "Z_ instance: ," instance " used " instancesA[ instance ] " times @ " ( instancesA[ instance ] / timerTicks / timerTick ) " cps " opStr = opStr ( timerTicks * timerTick / instancesA[ instance ] ) " s iat, " opStr = opStr ", send:," cgA[ instance ".sendCall" ] ", reject:," cgA[ instance ".rejectCall" ] print opStr totalCalls = totalCalls + instancesA[ instance ] } print "totalCalls=" totalCalls print "===================== Call distribution === ( 100 +CallsInTimerTick )=================" for( cnt = 100 ; cnt < 150 ; cnt ++ ) { print "Z_ distribution ," cnt " , " distributionA[ cnt ] } }