# Improving a triangulation test script

I am a relative beginner to Python and as such I've been working on little things here and there in the office that strike me as something interesting that might be fun to try and code a solution.

Recently, I was working with some cell phone data and decided to write a little script that would triangulate a cell phone location from ping strengths/XY locations from 3 different towers. As we only get one cell tower data returns in a exigence request anyway, this was pretty much a practice problem for me, which I would love to check the accuracy on anyway.

As such, I was just looking for some critical feedback on any parts of this that could be written cleaner or functionally more efficient than what i have. It works fine if all the correct values are entered for all user inputs but I am not sure how to keep things moving along if an invalid value is entered. Right now, I just exit out of the script if a invalid number is entered and the user would have to start over. I would like to be able to loop(?) back through with the correct value after a prompt of some sort.

Part 1

User inputs (this was going to be a tool/script to be run from someones desktop in the shell)for tower signal strengths and then standardizing those inputs. I pulled all the math off another thread here.

try:
s1=float (raw_input ("please enter signal strength number 1" + "-->" + " "))
s2=float (raw_input ("please enter signal strength number 2" + "-->" + " "))
s3=float (raw_input (" please enter signal strength number 3" + "-->" + " "))

except (EOFError,ValueError,TypeError):
oops2 = raw_input("An unusuable value was entered for the signal strength, Press ENTER      and try again")
print oops2
exit()

if s1  < 0.31 or s1 > 99:
badinput = raw_input("Signal strength #1 is invalid,\
please enter a value between 0.31 - 99. Press ENTER and try again")
exit()

if s2  < 0.31 or s2 > 99:
badinput = raw_input("Signal strength #2 is invalid, please enter a value between 0.31 -    99. Press ENTER and try again")
exit()

if s3  < 0.31 or s3 > 99:
badinput = raw_input("Signal strength #3 is invalid, please enter a value between 0.31 - 99. Press ENTER and try again")
exit()

print "_______________"
print ""

else:
def signal_strength(s1,s2,s3):
w1=float(s1)/(s1+s2+s3)
return w1
tower1_strength=signal_strength(s1,s2,s3)
print ("standardized signal strength for tower #1-->") + (" ") + str(tower1_strength)

def signal_strength2(s1,s2,s3):
w2=float(s2)/(s1+s2+s3)
return w2
tower2_strength=signal_strength2(s1,s2,s3)
print ("standardized signal strength for tower #2-->") + (" ") + str(tower2_strength)

def signal_strength3(s1,s2,s3):
w3=float(s3)/(s1+s2+s3)
return w3
tower3_strength=signal_strength3(s1,s2,s3)
print ("standardized signal strength for tower #3-->") + (" ") + str(tower3_strength)

print "_______________"
print ""


Part II

User inputs to select which three towers to choose the XY data for, which is held in a dictionary. Right now I just have three sample values in the dictionary rather than all the cell tower xy's located in my AOI.

# sample dictionary for cell carrier tower ID:X,Y Coordinates (Decimal Degrees), will      use carrier ID's as key

longitude = {1:-89.928573,2:-89.813409,3:-89.825694}
latitude = {1:43.899751,2:43.913357,3:43.82814}

try:
ID1 = int(raw_input ("Carrier ID for tower #1" + "-->" + " "))
ID1x= longitude[ID1]
ID1y = latitude[ID1]

ID2 = int(raw_input ("Carrier ID for tower #2" + "-->" + " "))
ID2x = longitude[ID2]
ID2y = latitude[ID2]

ID3 = int(raw_input ("Carrier ID for tower #3" + "-->" + " "))
ID3x = longitude[ID3]
ID3y = latitude[ID3]

except Exception,e:
oops = raw_input ("An invalid Tower ID# was used,Please use the Carrier/Provider   supplied identification number. Press ENTER and try again")
print oops
exit()

#Need to figure out how to loop back through the Tower ID# (and signal strength for that    matter) data entry chunk after exception, if possible

print "_______________"
print ""

def user_x(tower1_strength,tower2_strength,tower3_strength,tx1,tx2,tx3):
x_location=float(tower1_strength*tx1)+(tower2_strength*tx2)+(tower3_strength*tx3)
return x_location
X_Coord=user_x(tower1_strength,tower2_strength,tower3_strength,ID1x,ID2x,ID3x)
print ("the X coordinate for the position is-->") + (" ") + str(X_Coord)

def user_y(tower1_strength,tower2_strength,tower3_strength,ty1,ty2,ty3):
y_location=float(tower1_strength*ty1)+(tower2_strength*ty2)+(tower3_strength*ty3)
return y_location
Y_Coord=user_y(tower1_strength,tower2_strength,tower3_strength,ID1y,ID2y,ID3y)
print ("the Y coordinate for the position is-->") + (" ") + str(Y_Coord)

print "_______________"
print ""

ty = raw_input ("thank you")
print ty

• @Conor Yes, that is fine...I did fail to mention that I would like to convert it to an Arcpy script or tool at some point in the future (which led me to post here) but I just wanted to get the Python part of it down first...I feel more comfortable in Arcpy than straight python. – Jaspar Katt Mar 3 '14 at 21:43

Don't repeat yourself

You can define small functions to avoid duplicating code for all signals.

Also, you can use the relevant data structure to store information. For instance, the strength of the signal go by 3, let's put them in the same object (let's say a list to keep things simple).

Finally, your attempt to write function for standardisation of signals was nice. However, you end up writing and computing the same things many times. You could compute the sum just once.

Make things simple for the user

Instead of asking for 3 inputs and then exit if something goes wrong, you can check each and every input as it is provided so that the user does not go through the hassle of providing many values only to get rejected because the first was invalid. Also, you do not need to exit if the input is not valid, you can just ask again.

Taking the following comments into account, Part I becomes :

def get_signal_strength_from_user(i):
""" Prompt the user to get a signal strength. Loops til a valid value (float in the right range) is provided and return it."""
while True:
try:
s = float(raw_input("Please enter signal strength number " + str(i) + " --> "))
except (EOFError,ValueError,TypeError):
raw_input("An unusuable value was entered for the signal strength, Press ENTER and try again")
continue
if 0.31 <= s <= 99:
return s # s has a valid value
else:
raw_input("Signal strength is invalid (value should be in [0.31 - 99]. Press ENTER and try again")

# Using variables :
s1,s2,s3 = (get_signal_strength_from_user(1), get_signal_strength_from_user(2), get_signal_strength_from_user(3))
print("Signals strength is ", s1, s2, s3)

sum_signal = s1+s2+s3
w1,w2,w3 = s1/sum_signal, s2/sum_signal, s3/sum_signal
print("Standardized signals strength is ", w1, w2, w3)

# Using lists :
nb_src = 3

signals = [get_signal_strength_from_user(i+1) for i in range(nb_src)]
print("Signals strenght is ", signals)

sum_signal = sum(signals)
standard_signals = [s/sum_signal for s in signals]
print("Standardized signals strength is ", standard_signals)


I've done it using lists and different variables so that you can pick whatever is easier for you.

Now for part II :

I don't really understand why you need to user to pick 3 towers out of 3 towers.

Correctness

Also, I'd like to point out that your algorithm will not work if the same tower (or if 2 towers at the same location - which is roughly the same) is used more than once. Indeed, localisation on a plan with relative strenght from 2 sources only gives you the direction as the mathematical property you are using is right for any point of a line.

My feeling is that even more than that, the towers should not be aligned because this could lead to (at least) 2 distinct points in most cases.

The fact that this does not seem obvious from the code lead me to think that maybe the maths behind are wrong too.

Anyway, let's go back to the code.

Don't repeat yourself

It might not seem obvious because the names of the parameters are changed but you have defined the same function twice.

def user_c(tower1_strength,tower2_strength,tower3_strength,c1,c2,c3):
c = float(tower1_strength*c1) + (tower2_strength*c2) + (tower3_strength*c3)
return c


(c stands for coordinate here).

Also, the conversion and the temporary variable are not useful here, the argument names do not need to be that long and the function could be renamed :

def mean(s1,s2,s3,c1,c2,c3):
return s1*c1 + s2*c2 + s3*c3

• @ josay Like i said, data from towers from an exigence request is returned from the carrier for only one tower so this was really just a practice exercise for myself in writing some code to solve a real world scenario. I will never get the chance to field test it or get the rsquared value but wanted the python practice. Thanks for the tips as i will incorporate them in a rewrite. I knew there was a way to simplify it...just wasnt experienced enough to get there. – meh Mar 4 '14 at 10:19
• @ josay....the tower xy's were to be held in a dictionary. The ones listed are just 3 of many, i just wasnt in the mood to put them all in on the chance that there was a better way to do that part of it. Like i said, i just pulled the math from another thread on triangulating cell towers, and the results when i ran the script seem ok but i will never know for sure. There would never be two cell towers at the same location owned by the same carrier so that wont be an issue, the idea would be to use the three closest towers, regardless of how distant they would be to one another or the target. – meh Mar 4 '14 at 11:53
• @josay...I may not be reading that last part right where you substitute the parameter "c" for coordinate for my two parameters for the x and y coordinate. I don't think this will work, will it? – meh Mar 10 '14 at 15:26
• Well, the fact is that from a logical/mathematical point of view, you've defined the same function twice. Sure it does have different names and the parameters have a different name too but the computation itself is using the same formula. From a mathematical point of view, f(x) = xx and g(y) = yy for instance are just different ways to write the square function. I am not sure if I made it clear so please let me know if you have issues. – SylvainD Mar 10 '14 at 15:32
• I guess i was confused by the substitution of one value, c, for the two lat/long x and y coordinate values in my function. I haven't had time to play around with the edits you suggested but I do intend on giving them a try at some point in the future. I will keep you posted on what I find here... – meh Mar 11 '14 at 18:43