Pythonscript för ballistikkort model Sniper101

Moderatorer: potatiskastare, Kari Lindström

thoste
Inlägg: 96
Blev medlem: 19 mar 2017, 22:20

Pythonscript för ballistikkort model Sniper101

Inlägg av thoste »

Jag gissar på att några av er har snubblat över Tiborasaurus Rex Sniper 101-serie. Han har ett ganska intressant sätt att ta fram ballistikkort och jag tänkte att istället för att göra detta manuellt som han gör i videon, varför inte göra ett pythonscript som sköter det åt en istället. Risken minskar avsevärt att man missar att ändra en parameter om man skulle göra detta manuellt. Har lagt ner rätt mycket jobb på denna så jag tänkte jag kan dela med mig av det och försöka bidra till allt bra som finns här på forumet :-)

Videon som beskriver hur Rex gör: https://www.youtube.com/watch?v=711L4ez ... n&index=64

Scriptet kommer generera excel-filer med tabeller som det bara är att klippa och klistra in i Rex excel-mallar. Det genereras en fil per barometrisk tryck som du anger i den vektorn.

Scriptet använder web-scraping för att hämta data. Det kommer att öppna ett särskillt fönster i Chrome och man kommer se hur det fyller i och ändrar variabler allteftersom det framskrider.

Så vad behöver du för att köra scriptet:
1. Ett textbehandlingsprogram för att editera pythonscriptet och ändra variabler.
2. Någon form av python-program som kan köra python script. Jag kör med Anaconda men det finns säkert fler.
3. Chromedriver drivrutin, senaste finns att ladda ner här : https://chromedriver.chromium.org/

Jag rekommenderar varmt att titta igenom scriptet först innan ni kör det så ni förstår vad det gör. Jag har försökt vara duktig med att lägga in kommentarer och förklara vad som sker. Tips är att samtidigt titta på JBM-ballistics sida så ser ni vad respektive variabel betyder.

Och sen givetvis en liten disclaimer om att eventuella fel tar jag inget ansvar för :-P Detta är för eget bruk och används på egen risk (vad nu det skulle vara)

Kod: Markera allt

# -*- coding: utf-8 -*-
"""
Created on Wed Feb  3 09:33:54 2021

@author: jthom

This script use Python SELENIUM and Chrome to connect to JBMBallistics and runs a series of 
different parameters to generate excel data to be inserted into the quick sheets
for different ballistic solutions

The resulting output will be one sheet per barometric pressure

Carefully read the terms for definitions at https://www.jbmballistics.com/ballistics/calculators/help/traj_drift/traj_drift_exp.shtml
before inserting data to script

"""

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains 
from selenium.webdriver.support.ui import Select
from datetime import datetime
import pandas as pd
import time


##############################################################################
#							                                                 #
#                               Input section		                         #
#							                                                 #
##############################################################################

# Path to where chromedriver for the current version of chrome is saved
#chromedriver_path = 'C:/Users/jthom/chromedriver'

# Weblink to JBM ballistics 
jbm_link = "https://www.jbmballistics.com/cgi-bin/jbmtraj_drift-5.1.cgi"

# Delay for filing in forms, fiddle with these if chromedriver says that 
# it cannot "find elements"
delay_time_0 = 0.1
delay_time_1 = 0.5
delay_time_2 = 2
delay_time_3 = 5 # Wait before retry

note_comment = "Lapua D46 in Tikka T3x" # Additional message in result file

#####################################
#	           						#
#    Bullet drop and environment   	#
#	           						#
#####################################

# Distance to chronograph
distance_to_chrono = 6
distance_to_chrono_unit = "m"

# Humidity
humidity = 70   # Relative [%], based on average value in Sweden according to SMHI

# Bullet drop temperature
bullet_drop_temp_v   = [ -30, -20, -10,   0,   5,  10,  15,  20,  25 ]
temp_unit            = "°C"

# Muzzle velocity vector - Must match number of elements in temperature vector
muzzle_velocity_v    = [ 762, 762, 765, 770, 773, 777, 782, 788, 795 ]  
muzzle_velocity_unit = "m/s"


# Ambient air pressure
pressure_v         = [950, 970, 990, 1010, 1030, 1050] # According to SMHI
pressure_unit      = "mb"
pressure_corrected = False  # False = should not be checked,  True = should be checked
                            # If a barometer is used at FFP for measureing the ambient air pressure, then use "False"
std_atm_altitude   = False  # False = unchecked,  True = checked


# Range
ranges_in_meters = True   # False = should not be checked,  True = should be checked
range_zero       = 100    # Range for zero
range_min        = 100    # Minimum range
range_max        = 1500   # Maximum range
range_inc        = 10     # Range increment
range_inc_spin   = 100    # Range increment for spind drift calculation


# Stop calculating the bullet drop if the bullet speed < MACH threshold value (i.e. enters transsonic)
mach_threshold_cutoff = True # True  = Abort data extraction when bullet speed <= mach_threshold_value
                             # False = Calculate bullet drop until range_max
mach_threshold_value = 1.0   # MACH, bullet speed w.r.t. speed of sound

# Replace the calculation above with a mach table as an output, the table wil be used as input in excel for formating the 


#####   Auxilary tables   #######
# Temperatures for auxilary tables as wind, moving target and spin drift
aux_tables_temp_v            = [-30,   5,  25]
#aux_tables_muzzle_velocity_v = [762, 773, 795]
aux_tables_muzzle_velocity_v = [722, 733, 755]

# Wind
wind_speed         = 10
wind_speed_unit    = "m/s"
wind_angle         = 90
wind_speed_default = 0

# Moving target
target_speed         = 1
target_speed_unit    = "m/s"
target_speed_default = 0
target_angle         = 90

# Energy
energy_unit = "Energy (Joules)"

# Vital zone of target
vital_zone_radius      = 30
vital_zone_radius_unit = "cm"

# Additional
target_relative_drops      = False
round_output_whole_numbers = False

#####################################
#	           						#
#            Bullet input       	#
#	           						#
#####################################

bullet_bc  = 0.254 # Source: https://www.lapua.com/bullets/d46/
drag_model = "G7"

bullet_caliber      = 0.308
bullet_caliber_unit = "in"

bullet_weight      = 185
bullet_weight_unit = "gr"

bullet_length      = 1.323
bullet_length_unit = "in"

plastic_tip_length      = 0
plastic_tip_length_unit = "in"


#####################################
#	           						#
#            Rifle input        	#
#	           						#
#####################################

sight_height      = 4.9
sight_height_unit = "cm"

sight_offset      = 0
sight_offset_unit = "cm"

zero_height      = 0
zero_height_unit = "cm"

zero_offset      = 0
zero_offset_unit = "cm"

windage      = 0
windage_unit ="mil"

elevation      = 0
elevation_unit = "mil"

line_sight_angle = 0
cant_angle       = 0

barrel_twist           = 11
barrel_twist_unit      = "in"
barrel_twist_direction = "Right"

column_1_val  = 0.1    # scope turret value per click
column_1_unit = "mil"  # scope turret unit
column_2_val  = 1
column_2_unit = "cm"


##############################################################################
#							                                                 #
#                              Start of program		                         #
#							                                                 #
##############################################################################


#####################################
#	           						#
#    Connect to JBM ballistics &  	#
#        set default values        	#
#	           						#
#####################################


# Open Chrome
print('Opening Chrome')
driver  = webdriver.Chrome(chromedriver_path)    
actions = ActionChains(driver)

# Go to website
driver.get(jbm_link)
html = driver.find_element_by_tag_name('html') # Define the page, used for scrolling



print("Setting default values")

# Ballistic coefficient
driver.find_element_by_xpath('//input[@name="bc.v"]').clear()                 
driver.find_element_by_xpath('//input[@name="bc.v"]').send_keys(str(bullet_bc))
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath("//select[@name='d_f.v']")).select_by_visible_text(drag_model)
time.sleep(delay_time_0)

# Bullet weight
driver.find_element_by_xpath('//input[@name="bt_wgt.v"]').clear()                 
driver.find_element_by_xpath('//input[@name="bt_wgt.v"]').send_keys(str(bullet_weight))
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath('//select[@name="bt_wgt.u"]')).select_by_visible_text(bullet_weight_unit)
time.sleep(delay_time_0)

# Bullet caliber
driver.find_element_by_xpath('//input[@name="cal.v"]').clear()                 
driver.find_element_by_xpath('//input[@name="cal.v"]').send_keys(str(bullet_caliber))
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath('//select[@name="cal.u"]')).select_by_visible_text(bullet_caliber_unit)
time.sleep(delay_time_0)

# Bullet length
driver.find_element_by_xpath('//input[@name="blt_len.v"]').clear()                 
driver.find_element_by_xpath('//input[@name="blt_len.v"]').send_keys(str(bullet_length))
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath('//select[@name="blt_len.u"]')).select_by_visible_text(bullet_length_unit)
time.sleep(delay_time_0)

# Plastic tip length
driver.find_element_by_xpath('//input[@name="tip_len.v"]').clear()                 
driver.find_element_by_xpath('//input[@name="tip_len.v"]').send_keys(str(plastic_tip_length))
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath('//select[@name="tip_len.u"]')).select_by_visible_text(plastic_tip_length_unit)
time.sleep(delay_time_0)

#Muzzle velocity unit
Select(driver.find_element_by_xpath('//select[@name="m_vel.u"]')).select_by_visible_text(muzzle_velocity_unit)
time.sleep(delay_time_0)

#Distance to chronograph
driver.find_element_by_xpath('//input[@name="ch_dst.v"]').clear()                 
driver.find_element_by_xpath('//input[@name="ch_dst.v"]').send_keys(str(distance_to_chrono))
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath('//select[@name="ch_dst.u"]')).select_by_visible_text(distance_to_chrono_unit)
time.sleep(delay_time_0)

# Sight parameters
driver.find_element_by_xpath('//input[@name="hgt_sgt.v"]').clear()                 
driver.find_element_by_xpath('//input[@name="hgt_sgt.v"]').send_keys(str(sight_height))
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath('//select[@name="hgt_sgt.u"]')).select_by_visible_text(sight_height_unit)
time.sleep(delay_time_0)
driver.find_element_by_xpath('//input[@name="ofs_sgt.v"]').clear()                 
driver.find_element_by_xpath('//input[@name="ofs_sgt.v"]').send_keys(str(sight_offset))
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath('//select[@name="ofs_sgt.u"]')).select_by_visible_text(sight_offset_unit)
time.sleep(delay_time_0)
driver.find_element_by_xpath('//input[@name="hgt_zer.v"]').clear()                 
driver.find_element_by_xpath('//input[@name="hgt_zer.v"]').send_keys(str(zero_height))
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath('//select[@name="hgt_zer.u"]')).select_by_visible_text(zero_height_unit)
time.sleep(delay_time_0)
driver.find_element_by_xpath('//input[@name="ofs_zer.v"]').clear()                 
driver.find_element_by_xpath('//input[@name="ofs_zer.v"]').send_keys(str(zero_offset))
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath('//select[@name="ofs_zer.u"]')).select_by_visible_text(zero_offset_unit)
time.sleep(delay_time_0)
driver.find_element_by_xpath('//input[@name="azm.v"]').clear()                 
driver.find_element_by_xpath('//input[@name="azm.v"]').send_keys(str(windage))
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath('//select[@name="azm.u"]')).select_by_visible_text(windage_unit)
time.sleep(delay_time_0)
driver.find_element_by_xpath('//input[@name="ele.v"]').clear()                 
driver.find_element_by_xpath('//input[@name="ele.v"]').send_keys(str(elevation))
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath('//select[@name="ele.u"]')).select_by_visible_text(elevation_unit)
time.sleep(delay_time_0)
driver.find_element_by_xpath('//input[@name="los.v"]').clear()                 
driver.find_element_by_xpath('//input[@name="los.v"]').send_keys(str(line_sight_angle))
time.sleep(delay_time_0)
driver.find_element_by_xpath('//input[@name="cnt.v"]').clear()                 
driver.find_element_by_xpath('//input[@name="cnt.v"]').send_keys(str(cant_angle))
time.sleep(delay_time_0)

# Barrel twist
driver.find_element_by_xpath('//input[@name="b_twt.v"]').clear()            
driver.find_element_by_xpath('//input[@name="b_twt.v"]').send_keys(str(barrel_twist))
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath('//select[@name="b_twt.u"]')).select_by_visible_text(barrel_twist_unit)
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath('//select[@name="b_twt_dir.v"]')).select_by_visible_text(barrel_twist_direction)
time.sleep(delay_time_0)

# Wind speed unit
Select(driver.find_element_by_xpath('//select[@name="spd_wnd.u"]')).select_by_visible_text(wind_speed_unit)
time.sleep(delay_time_0)

# Wind angle
driver.find_element_by_xpath('//input[@name="ang_wnd.v"]').clear()            
driver.find_element_by_xpath('//input[@name="ang_wnd.v"]').send_keys(str(wind_angle))
time.sleep(delay_time_0)

# Target speed unit
Select(driver.find_element_by_xpath('//select[@name="spd_tgt.u"]')).select_by_visible_text(target_speed_unit)
time.sleep(delay_time_0)

# Target speed angle
driver.find_element_by_xpath('//input[@name="ang_tgt.v"]').clear()            
driver.find_element_by_xpath('//input[@name="ang_tgt.v"]').send_keys(str(target_angle))

# Range
driver.find_element_by_xpath('//input[@name="rng_min.v"]').clear()            
driver.find_element_by_xpath('//input[@name="rng_min.v"]').send_keys(str(range_min))
time.sleep(delay_time_0)
driver.find_element_by_xpath('//input[@name="rng_max.v"]').clear()            
driver.find_element_by_xpath('//input[@name="rng_max.v"]').send_keys(str(range_max))
time.sleep(delay_time_0)
driver.find_element_by_xpath('//input[@name="rng_zer.v"]').clear()            
driver.find_element_by_xpath('//input[@name="rng_zer.v"]').send_keys(str(range_zero))
time.sleep(delay_time_0)

# Temperature unit
Select(driver.find_element_by_xpath('//select[@name="tmp.u"]')).select_by_visible_text(temp_unit)
time.sleep(delay_time_0)

# Pressure unit
Select(driver.find_element_by_xpath('//select[@name="prs.u"]')).select_by_visible_text(pressure_unit)
time.sleep(delay_time_0)

# Humidity
driver.find_element_by_xpath('//input[@name="hum.v"]').clear()            
driver.find_element_by_xpath('//input[@name="hum.v"]').send_keys(str(humidity))
time.sleep(delay_time_0)

# Pressure correction
pressure_box = driver.find_element_by_xpath('//input[@name="cor_prs.v"]') 
if pressure_box.is_selected() != pressure_corrected: # Check if box is different from variable
    pressure_box.click()                               #If different, click box
    time.sleep(delay_time_0)

# Std atm at altitude
altitude_box = driver.find_element_by_xpath('//input[@name="std_alt.v"]') 
if altitude_box.is_selected() != std_atm_altitude: # Check if box is different from variable
    altitude_box.click()                               #If different, click box
    time.sleep(delay_time_0)

# Energy unit
Select(driver.find_element_by_xpath('//select[@name="col_eng.v"]')).select_by_visible_text(energy_unit)
time.sleep(delay_time_0)

# Vital zone
driver.find_element_by_xpath('//input[@name="rad_vz.v"]').clear()                 
driver.find_element_by_xpath('//input[@name="rad_vz.v"]').send_keys(str(vital_zone_radius))
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath('//select[@name="rad_vz.u"]')).select_by_visible_text(vital_zone_radius_unit)
time.sleep(delay_time_0)


# Column 1
driver.find_element_by_xpath('//input[@name="col1_un.v"]').clear()            
driver.find_element_by_xpath('//input[@name="col1_un.v"]').send_keys(str(column_1_val))
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath('//select[@name="col1_un.u"]')).select_by_visible_text(column_1_unit)
time.sleep(delay_time_0)

# Column 2
driver.find_element_by_xpath('//input[@name="col2_un.v"]').clear()            
driver.find_element_by_xpath('//input[@name="col2_un.v"]').send_keys(str(column_2_val))
time.sleep(delay_time_0)
Select(driver.find_element_by_xpath('//select[@name="col2_un.u"]')).select_by_visible_text(column_2_unit)
time.sleep(delay_time_0)

# Ranges in meters
ranges_in_meters_box = driver.find_element_by_xpath('//input[@name="rng_un.v"]')
if ranges_in_meters_box.is_selected() != ranges_in_meters:
    ranges_in_meters_box.click()
    time.sleep(delay_time_0)

# Target relative drops
target_relative_drops_box = driver.find_element_by_xpath('//input[@name="def_cnt.v"]')
if target_relative_drops_box.is_selected() != target_relative_drops:
    target_relative_drops_box.click()
    time.sleep(delay_time_0)

# Round output to whole numbers
round_output_whole_numbers = driver.find_element_by_xpath('//input[@name="rnd_clk.v"]')
if round_output_whole_numbers.is_selected() != target_relative_drops:
    round_output_whole_numbers.click()
    time.sleep(delay_time_0)


#############################################################
#	           				                         		#
#                        Loop sequence                  	#
#	           				                    	    	#
#############################################################



# Loop the pressure value
for pressure in pressure_v:
    
    # Spin drift should not be included as a start
    spin_drift_box = driver.find_element_by_xpath('//input[@name="inc_drf.v"]')
    if spin_drift_box.is_selected():
        spin_drift_box.click()
        time.sleep(delay_time_1)
    
    
    # Set pressure
    print(" ")
    print("Barometric pressure "+str(pressure)+" "+pressure_unit)
    print(" ")
    driver.find_element_by_xpath('//input[@name="prs.v"]').clear() 
    time.sleep(delay_time_1)                      
    driver.find_element_by_xpath('//input[@name="prs.v"]').send_keys(str(pressure))
    time.sleep(delay_time_1)
    
    
    #####################################
    #	           						#
    #            Bullet drop        	#
    #	           						#
    #####################################
       
    # Loop the bullet drop for muzzle velocity and temperature vectors assuming no wind, target speed or spin drift    
    print("   Calculating bullet drop for variations in muzzle velocity and temperature")
    print(" ")
    
    # Set default values for wind speed and target speed
    driver.find_element_by_xpath('//input[@name="spd_wnd.v"]').clear()            
    time.sleep(delay_time_1)           
    driver.find_element_by_xpath('//input[@name="spd_wnd.v"]').send_keys(str(wind_speed_default))
    time.sleep(delay_time_1)
    driver.find_element_by_xpath('//input[@name="spd_tgt.v"]').clear()   
    time.sleep(delay_time_1)                    
    driver.find_element_by_xpath('//input[@name="spd_tgt.v"]').send_keys(str(target_speed_default))
    time.sleep(delay_time_1)
    
    # Set range increment for muzzle velocity and temperature vectors  
    driver.find_element_by_xpath('//input[@name="rng_inc.v"]').clear()   
    time.sleep(delay_time_1)                    
    driver.find_element_by_xpath('//input[@name="rng_inc.v"]').send_keys(str(range_inc))
    time.sleep(delay_time_1)
    
    # Result matrix
    bullet_drop_result_mat = pd.DataFrame() 
    
    for i1 in range(0 , len(muzzle_velocity_v)):
        
        try:
            # Get vector values
            muzzle_velocity = muzzle_velocity_v[i1]
            temperature = bullet_drop_temp_v[i1]
            
            # Set values
            print("      Muzzle velocity: "+str(muzzle_velocity)+" "+muzzle_velocity_unit)
            print("      Temperature:     "+str(temperature)+" "+temp_unit)
            time.sleep(delay_time_1)
            driver.find_element_by_xpath('//input[@name="m_vel.v"]').clear()            
            time.sleep(delay_time_1)
            driver.find_element_by_xpath('//input[@name="m_vel.v"]').send_keys(str(muzzle_velocity))
            time.sleep(delay_time_1)
            driver.find_element_by_xpath('//input[@name="tmp.v"]').clear()   
            time.sleep(delay_time_1)                    
            driver.find_element_by_xpath('//input[@name="tmp.v"]').send_keys(str(temperature))
            time.sleep(delay_time_1)
                    
            # Click "Calculate" button
            driver.find_element_by_xpath('//input[@class="submit_button"]').click() 
            time.sleep(delay_time_2)
                    
            ### Result page ###
            
            # Define the table as a variable
            output_table = driver.find_element_by_xpath('//table[@class="output_table"]/tbody') 
            
            column_df = pd.DataFrame()
            range_df  = pd.DataFrame()
            for row_n, i2 in enumerate( output_table.find_elements_by_xpath('.//tr') , 0): #Loop through table rows
                
                #Skip first 3 rows
                if row_n < 3: 
                    continue
                                        
                #Process row for data
                i4 = 0
                break_i2 = False
                for i3 in i2.find_elements_by_xpath('.//td'):
                    if i4 == 0:
                        range_i2 = float(i3.text)
                        #print(range_i2)
    
                    elif i4 == 1:
                        drop_clicks_i2 = abs(float(i3.text))
                        
                    elif i4 == 6:
                        mach_i2 = float( i3.text )
                    
                    
                        # Check if the bullet enters transsonic zone. If so, abort
                        if mach_threshold_cutoff:
                            #print("Range = "+str(range_i2)+", mach = "+str(mach_i2))
                            if mach_i2 <= mach_threshold_value:
                                #print("break")
                                break_i2 = True
                                break # for i3 loop
                        
                        
                    i4 += 1
    
                # Write row data to a df
                if i1 == 0:
                    row_df = pd.DataFrame({"range":[range_i2] , (str(temperature)+temp_unit):[drop_clicks_i2]})
                else:
                    row_df = pd.DataFrame({(str(temperature)+temp_unit):[drop_clicks_i2]})
                
                # Append row df to column df
                column_df = column_df.append((row_df), ignore_index = True)
                
                # Append range value to range_df
                range_df  = range_df.append( (pd.DataFrame({"range":[range_i2] })), ignore_index = True)
                
                
                # The last row will be appended when mach_treshold_value is reached
                if break_i2:
                    break
                
                                        
            # Append column to     
            if i1 == 0:
                bullet_drop_result_mat = column_df
                
            else:
                # Append the column_df to result_mat and inrease the number of rows on result_mat
                bullet_drop_result_mat = pd.concat([bullet_drop_result_mat, column_df], ignore_index=False, axis=1)
                
                # Update the range column in result_mat
                bullet_drop_result_mat["range"] = range_df
                
            #Go back to input page
            driver.find_element_by_xpath('//a[@href="/cgi-bin/jbmtraj_drift-5.1.cgi"]').click()
            time.sleep(delay_time_2)
            
        except:
            print("")
            print("Somethin prevents the program to continue, waiting "+str(delay_time_3)+"sec before retry")
            time.sleep(delay_time_3)

    #####################################
    #	           						#
    #             Wind speed            #
    #	           						#
    #####################################
    
    # Loop the bullet drop for muzzle velocity and temperature vectors for different wind speed
    print(" ")
    print("   Calculating horizontal drift due to wind")
    print(" ")
       
    # Set values for wind speed
    driver.find_element_by_xpath('//input[@name="spd_wnd.v"]').clear()   
    time.sleep(delay_time_1)                    
    driver.find_element_by_xpath('//input[@name="spd_wnd.v"]').send_keys(str(wind_speed))
    time.sleep(delay_time_1)
    
    # Result matrix
    wind_result_mat = pd.DataFrame() 
    
    for i1 in range(0 , len(aux_tables_temp_v)):
        
        try:
            # Get vector values
            muzzle_velocity = aux_tables_muzzle_velocity_v[i1]
            temperature     = aux_tables_temp_v[i1]
    
            # Set values
            print("      Muzzle velocity: "+str(muzzle_velocity)+" "+muzzle_velocity_unit)
            print("      Temperature:     "+str(temperature)+" "+temp_unit)
            time.sleep(delay_time_1)
            driver.find_element_by_xpath('//input[@name="m_vel.v"]').clear()
            time.sleep(delay_time_1)
            driver.find_element_by_xpath('//input[@name="m_vel.v"]').send_keys(str(muzzle_velocity))
            time.sleep(delay_time_1)
            driver.find_element_by_xpath('//input[@name="tmp.v"]').clear()  
            time.sleep(delay_time_1)                     
            driver.find_element_by_xpath('//input[@name="tmp.v"]').send_keys(str(temperature))
            time.sleep(delay_time_1)
                    
            # Click "Calculate" button
            driver.find_element_by_xpath('//input[@class="submit_button"]').click() 
            time.sleep(delay_time_2)
                    
            ### Result page ###
            
            # Define the table as a variable
            output_table = driver.find_element_by_xpath('//table[@class="output_table"]/tbody') 
            
            column_df = pd.DataFrame()
            for row_n, i2 in enumerate( output_table.find_elements_by_xpath('.//tr') , 0): #Loop through table rows
                
                #Skip first 3 rows
                if row_n < 3: 
                    continue
                                        
                #Process row for data
                i4 = 0
                for i3 in i2.find_elements_by_xpath('.//td'):
                    if i4 == 0:
                        range_i2 = float(i3.text)
                        
                    elif i4 == 3:
                        wind_clicks_i2 = abs(float( i3.text ))
                        
                    # elif i4 == 6:
                    #     mach_i2 = float( i3.text )
                        
                    # elif i4 == 7:
                    #     energy_i2 = float( i3.text )
                        
                    # elif i4 == 9:
                    #     lead_clicks_i2 = abs(float( i3.text ))
                        
                    i4 += 1
        
                # Write row data to a df
                if i1 == 0:
                    row_df = pd.DataFrame({"range":[range_i2] , (str(temperature)+temp_unit):[wind_clicks_i2]})
                else:
                    row_df = pd.DataFrame({(str(temperature)+temp_unit):[wind_clicks_i2]})
                
                # Append row df to column df
                column_df = column_df.append((row_df), ignore_index = True)
                
            # Append column to     
            if i1 == 0:
                wind_result_mat = column_df
                
            else:
                wind_result_mat[(str(temperature)+temp_unit)] = column_df
                        
            #Go back to input page
            driver.find_element_by_xpath('//a[@href="/cgi-bin/jbmtraj_drift-5.1.cgi"]').click()
            time.sleep(3)
            
        except:
            print("")
            print("Somethin prevents the program to continue, waiting "+str(delay_time_3)+"sec before retry")
            time.sleep(delay_time_3)
        
        
    #####################################
    #	           						#
    #           Target speed            #
    #	           						#
    #####################################
    
    # Loop the bullet drop for muzzle velocity and temperature vectors for different target speed
    print(" ")
    print("   Calculating horizontal lead for a moving target")
    print(" ")
       
    # Set default value for wind speed
    driver.find_element_by_xpath('//input[@name="spd_wnd.v"]').clear()    
    time.sleep(delay_time_1)                   
    driver.find_element_by_xpath('//input[@name="spd_wnd.v"]').send_keys(str(wind_speed_default))
    time.sleep(delay_time_1)
    
    # Set default value for target speed
    driver.find_element_by_xpath('//input[@name="spd_tgt.v"]').clear()
    time.sleep(delay_time_1)           
    driver.find_element_by_xpath('//input[@name="spd_tgt.v"]').send_keys(str(target_speed))
    time.sleep(delay_time_1)
    
    # Result matrix
    target_result_mat = pd.DataFrame() 
    
    for i1 in range(0 , len(aux_tables_temp_v)):
        
        try:
            
            # Get vector values
            muzzle_velocity = aux_tables_muzzle_velocity_v[i1]
            temperature     = aux_tables_temp_v[i1]
    
            # Set values
            print("      Muzzle velocity: "+str(muzzle_velocity)+" "+muzzle_velocity_unit)
            print("      Temperature:     "+str(temperature)+" "+temp_unit)
            time.sleep(delay_time_1)
            driver.find_element_by_xpath('//input[@name="m_vel.v"]').clear()            
            time.sleep(delay_time_1)
            driver.find_element_by_xpath('//input[@name="m_vel.v"]').send_keys(str(muzzle_velocity))
            time.sleep(delay_time_1)
            driver.find_element_by_xpath('//input[@name="tmp.v"]').clear()  
            time.sleep(delay_time_1)          
            driver.find_element_by_xpath('//input[@name="tmp.v"]').send_keys(str(temperature))
            time.sleep(delay_time_1)
                    
            # Click "Calculate" button
            driver.find_element_by_xpath('//input[@class="submit_button"]').click() 
            time.sleep(delay_time_2)
                    
            ### Result page ###
            
            # Define the table as a variable
            output_table = driver.find_element_by_xpath('//table[@class="output_table"]/tbody') 
            
            column_df = pd.DataFrame()
            for row_n, i2 in enumerate( output_table.find_elements_by_xpath('.//tr') , 0): #Loop through table rows
                
                #Skip first 3 rows
                if row_n < 3: 
                    continue
                                        
                #Process row for data
                i4 = 0
                for i3 in i2.find_elements_by_xpath('.//td'):
                    if i4 == 0:
                        range_i2 = float(i3.text)
                        
                    # elif i4 == 6:
                    #     mach_i2 = float( i3.text )
                        
                    elif i4 == 9:
                        lead_clicks_i2 = abs(float( i3.text ))
                        
                    i4 += 1
        
                # Write row data to a df
                if i1 == 0:
                    row_df = pd.DataFrame({"range":[range_i2] , (str(temperature)+temp_unit):[lead_clicks_i2]})
                else:
                    row_df = pd.DataFrame({(str(temperature)+temp_unit):[lead_clicks_i2]})
                
                # Append row df to column df
                column_df = column_df.append((row_df), ignore_index = True)
                
            # Append column to     
            if i1 == 0:
                target_result_mat = column_df
                
            else:
                target_result_mat[(str(temperature)+temp_unit)] = column_df
                        
            #Go back to input page
            driver.find_element_by_xpath('//a[@href="/cgi-bin/jbmtraj_drift-5.1.cgi"]').click()
            time.sleep(3)
            
        except:
            print("")
            print("Somethin prevents the program to continue, waiting "+str(delay_time_3)+"sec before retry")
            time.sleep(delay_time_3)
    
    
    #####################################
    #	           						#
    #            Spin drift             #
    #	           						#
    #####################################
    
    
    # Loop the bullet drop for muzzle velocity and temperature vectors for different spin drift
    print(" ")
    print("   Calculating horizontal drift due to spin drift")
    print(" ")
       
    # Set default value for wind speed and target speed
    driver.find_element_by_xpath('//input[@name="spd_wnd.v"]').clear()
    time.sleep(delay_time_1)           
    driver.find_element_by_xpath('//input[@name="spd_wnd.v"]').send_keys(str(wind_speed_default))
    time.sleep(delay_time_1)
    driver.find_element_by_xpath('//input[@name="spd_tgt.v"]').clear()       
    time.sleep(delay_time_1)                
    driver.find_element_by_xpath('//input[@name="spd_tgt.v"]').send_keys(str(target_speed_default))
    time.sleep(delay_time_1)
    
    # Set range increment for muzzle velocity and temperature vectors  
    driver.find_element_by_xpath('//input[@name="rng_inc.v"]').clear()            
    time.sleep(delay_time_1)           
    driver.find_element_by_xpath('//input[@name="rng_inc.v"]').send_keys(str(range_inc_spin))
    time.sleep(delay_time_1)
    
    
    # Activate spin drift
    driver.find_element_by_xpath('//input[@name="inc_drf.v"]').click()
    
    # Result matrix
    spin_result_mat = pd.DataFrame() 
    
    for i1 in range(0 , len(aux_tables_temp_v)):
        
        try:
                    
            # Get vector values
            muzzle_velocity = aux_tables_muzzle_velocity_v[i1]
            temperature     = aux_tables_temp_v[i1]
    
            # Set values
            print("      Muzzle velocity: "+str(muzzle_velocity)+" "+muzzle_velocity_unit)
            print("      Temperature:     "+str(temperature)+" "+temp_unit)
            time.sleep(delay_time_1)
            driver.find_element_by_xpath('//input[@name="m_vel.v"]').clear()            
            time.sleep(delay_time_1)
            driver.find_element_by_xpath('//input[@name="m_vel.v"]').send_keys(str(muzzle_velocity))
            time.sleep(delay_time_1)
            driver.find_element_by_xpath('//input[@name="tmp.v"]').clear()  
            time.sleep(delay_time_1)                     
            driver.find_element_by_xpath('//input[@name="tmp.v"]').send_keys(str(temperature))
            time.sleep(delay_time_1)
                    
            # Click "Calculate" button
            driver.find_element_by_xpath('//input[@class="submit_button"]').click() 
            time.sleep(delay_time_2)
                    
            ### Result page ###
            
            # Define the table as a variable
            output_table = driver.find_element_by_xpath('//table[@class="output_table"]/tbody') 
            
            column_df   = pd.DataFrame()
            column_df_1 = pd.DataFrame()
            column_df_2 = pd.DataFrame()
            for row_n, i2 in enumerate( output_table.find_elements_by_xpath('.//tr') , 0): #Loop through table rows
                
                #Skip first 3 rows
                if row_n < 3: 
                    continue
                                        
                #Process row for data
                i4 = 0
                for i3 in i2.find_elements_by_xpath('.//td'):
                    if i4 == 0:
                        range_i2 = float(i3.text)
                        
                    elif i4 == 3:
                        drift_clicks_i2 = abs(float( i3.text ))
                        
                    elif i4 == 4:
                        drift_dist_i2 = abs(float( i3.text ))
                        
                    i4 += 1
        
                # Write row data to a df and add df to column df
                if i1 == 0:
                    row_df = pd.DataFrame({"range":[range_i2] , (str(temperature)+temp_unit+" - clicks"):[drift_clicks_i2], (str(temperature)+temp_unit+" - "+column_2_unit):[drift_dist_i2]})
                    column_df = column_df.append((row_df), ignore_index = True)
                else:
                    row_df_1 = pd.DataFrame({(str(temperature)+temp_unit+" - clicks"):[drift_clicks_i2]})
                    row_df_2 = pd.DataFrame({(str(temperature)+temp_unit+" - "+column_2_unit):[drift_dist_i2]})
                    column_df_1 = column_df_1.append((row_df_1), ignore_index = True)
                    column_df_2 = column_df_2.append((row_df_2), ignore_index = True)
                
            # Append column to result matrix     
            if i1 == 0:
                spin_result_mat = column_df
                
            else:
                spin_result_mat[(str(temperature)+temp_unit+" clicks")] = column_df_1
                spin_result_mat[(str(temperature)+temp_unit+" - "+column_2_unit)] = column_df_2
                        
            #Go back to input page
            driver.find_element_by_xpath('//a[@href="/cgi-bin/jbmtraj_drift-5.1.cgi"]').click()
            time.sleep(3)
            
        except:
            print("")
            print("Somethin prevents the program to continue, waiting "+str(delay_time_3)+"sec before retry")
            time.sleep(delay_time_3)
        
    
    #####################################
    #	           						#
    #   Write results to .xlsx-files   	#
    #	           						#
    #####################################
    
    # Write results and input data to excel-file, one file per pressure
    
    # Variable table
    var_tab = pd.DataFrame({"Variable":["Date and time", "Comment", "Bullet", "Pressure", "Pressure corrected", "Sight height", "Muzzle velocity", "Muzzle velocity auxilary tables", "Bullet drop temperatures", "Temperature auxilary tables", "Distance to chronograph", "Barrel twist", "Humidity", "Wind", "Target speed"], "Value":[(datetime.today().strftime('%Y-%m-%d - %H:%M:%S')) , (note_comment), ( "Caliber: "+str(bullet_caliber)+bullet_caliber_unit+",  "+str(bullet_weight)+bullet_weight_unit+",  BC: "+str(bullet_bc)+" "+drag_model+",  Length: "+str(bullet_length)+bullet_length_unit), (str(pressure)+pressure_unit) , pressure_corrected, (str(sight_height)+sight_height_unit),muzzle_velocity_v, aux_tables_muzzle_velocity_v , bullet_drop_temp_v, aux_tables_temp_v, ( str(distance_to_chrono)+" "+distance_to_chrono_unit), ( str(barrel_twist)+barrel_twist_unit+",  "+barrel_twist_direction), (str(humidity)+"%"), ("Wind speed default: "+str(wind_speed_default)+wind_speed_unit+",  Wind speed: "+str(wind_speed)+wind_speed_unit+",  Wind angle: "+str(wind_angle)+"deg"), ("Target speed default: "+str(target_speed_default)+target_speed_unit+",  Target speed: "+str(target_speed)+target_speed_unit+",  Target angle: "+str(target_angle)+"deg") ]  })
    
    # File name string
    xls_name_str = "JBMbal_quick_sheet_"+str(pressure)+pressure_unit+"_"+datetime.today().strftime('%Y-%m-%d')+".xlsx"
    
    # Pandas excel writer
    writer = pd.ExcelWriter(xls_name_str, engine="xlsxwriter")
    
    # Write each dataframe to a different worksheet.
    var_tab.to_excel(writer, sheet_name="Input parameters")
    bullet_drop_result_mat.to_excel(writer, sheet_name="Bullet drop")
    wind_result_mat.to_excel(writer, sheet_name="Wind drift")
    target_result_mat.to_excel(writer, sheet_name="Moving target lead")
    spin_result_mat.to_excel(writer, sheet_name="Spin drift")
    
    # Close Pands Excel writer and output the excel file
    writer.save()
    
Assumptions is the mother of all f*ck-ups