In statistics, where I know about enough to be dangerous, there are 2 SD's. One is an SD for large populations, and one is for a sample of a population, sometimes (IIRC) called student's SD. I think the one using (n-1) is the sample population SD. I think Excel has some help on it, for a commonly available reference.
For our purposes, the sample SD, or student's sd (IIRC) is the better one to use since all of our shot groups are relatively small. We're not dealing with 1000's of events...
(This is one thing I learned that was not in kindergarten, to mock an old book/saying - lol!)
"Down the floor, out the door, Go Brandon Go!!!!!"
Fixed date bug - Needed to add 20 years and 4 days (in seconds) to the unix time value (which is in UTC by the way) The script puts it in your local time zone ;-)
thanks a lot garmin.. you saving 1 digit for what? better be good.
Code:
#
# Notes: Requires Java
#
# Copy entire folder from google drive
# https://drive.google.com/drive/folders/1fb_MCyteiMZrcL-gj7DJXlCn-Y_LsGkO?usp=drive_link
# Connect Garmin Xero C1 Pro to Computer
# Run the script (right click + run with powershell)
# Navigate to the .FIT files on the device in the pop up and select them, press "Open" button
# Name the sessions when prompted in terminal window. Spaces are OK, dont include .txt
#
#
#
# Created by ZenEffect
cls
Add-Type -AssemblyName System.Windows.Forms
#ripped this part off of a google search. It creates function for the pop up window to select files
function Select-File {
[CmdletBinding()]
param(
[Parameter(ParameterSetName="Single")]
[Parameter(ParameterSetName="Multi")]
[Parameter(ParameterSetName="Save")]
[string]$StartingFolder = [environment]::getfolderpath("mydocuments"),
[Parameter(ParameterSetName="Single")]
[Parameter(ParameterSetName="Multi")]
[Parameter(ParameterSetName="Save")]
[string]$NameFilter = "All Files (*.*)|*.*",
[Parameter(ParameterSetName="Single")]
[Parameter(ParameterSetName="Multi")]
[Parameter(ParameterSetName="Save")]
[switch]$AllowAnyExtension,
[Parameter(Mandatory=$true,ParameterSetName="Save")]
[switch]$Save,
[Parameter(Mandatory=$true,ParameterSetName="Multi")]
[Alias("Multi")]
[switch]$AllowMulti
)
if ($Save) {
$Dialog = New-Object -TypeName System.Windows.Forms.SaveFileDialog
} else {
$Dialog = New-Object -TypeName System.Windows.Forms.OpenFileDialog
if ($AllowMulti) {
$Dialog.Multiselect = $true
}
}
if ($AllowAnyExtension) {
$NameFilter = $NameFilter + "|All Files (*.*)|*.*"
}
$Dialog.Filter = $NameFilter
$Dialog.InitialDirectory = $StartingFolder
[void]($Dialog.ShowDialog())
$Dialog.FileNames
}
# .FIT file to .CSV - Copies to a "fit_temp" folder first for processing. Does not interact with device except to copy the files to computer first.
$fit = (Select-File -StartingFolder "C:\" -NameFilter "Garmin FIT Files (*.fit)|*.fit" -AllowMulti)
copy $fit .\fit_Temp
$fit_temp = get-childitem -Path .\fit_temp | select-object fullname
foreach ($fitfile in $fit_temp)
{
$fit3 = $fitfile.fullname
$fit2 = "`"$fit3`""
# sends the selected files to the bat file that came with the Garmin SDK. Only modification to the .BAT was to remove the "Pause" at the end. FIT files
# are processed into CSV by some Garmin SDK Java magic.
start-process -filepath '.\java\fittocsv.bat' -argumentlist $fit2 -wait
}
# gets list of newly created .csv from fit_temp directory for processing
$items = get-childitem -Path .\fit_temp\*.csv | select fullname
foreach ($item in $items)
{
# Gets shot data and calculates standard deviation
$csv1 = $item.fullname | import-csv
$csv_data = $csv1 | where 'type' -eq 'data' | where 'local number' -eq 4
$measured = $csv_data.'value 2' | measure-object -average
$newNumbers = 0
ForEach ($number in $csv_data) {
$newNumbers += [Math]::Pow(($number.'value 2' - $measured.Average), 2)
}
$stdDev = [math]::Sqrt($($newNumbers / ($measured.Count - 1)))
$stdDevp = [math]::Sqrt($($newNumbers / ($measured.Count)))
$stddev_fps=$stddev / 304.79999025
$stddevp_fps=$stddevp / 304.79999025
# Begin CSV to TXT file processing
foreach ($csv in $csv1)
{
$time = (([System.DateTimeOffset]::FromUnixTimeSeconds($time_hack)).DateTime.ToLocalTime()).ToString("s")
$type = $csv.type
$localnumber = $csv."local number"
if ($type -eq "Data" -and $localnumber -eq "3")
{
$min = ($csv."Value 2" / 304.79999025)
$max = ($csv."Value 3" / 304.79999025)
$average = ($csv."Value 4" / 304.79999025)
$shots = $csv."Value 6"
$time_hack=([int]$csv."Value 1" + [int]631065600)
$time = (([System.DateTimeOffset]::FromUnixTimeSeconds($time_hack)).DateTime.ToLocalTime()).ToString("s")
$extreme_spread= ($max - $min)
Write-host "Session Time/Date:" $time
Write-host "# of Shots:", $shots
Write-host " "
Write-host "Min:", $min
Write-host "Max:", $max
Write-host "Average:", $average
Write-host " "
write-host "Standard Deviation:", $stddev_fps
write-host "Population Standard Deviation:", $stddevp_fps
Write-host "Extreme Spread:", $extreme_spread
write-host " "
write-host " "
$Session_name = Read-Host -Prompt "Enter Session Name"
Write-output "Session Time/Date: $time" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "Session Name: $session_name" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "# of Shots: $shots" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output " " | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "Min: $min" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "Max: $max" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "Average: $average" | out-file -filepath .\Results\$Session_name.txt -Append
Write-Output "Standard Deviation: $stddev_fps" | out-file -filepath .\Results\$Session_name.txt -Append
Write-Output "Population Standard Deviation: $stddevp_fps" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "Extreme Spread: $extreme_spread" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output " " | out-file -filepath .\Results\$Session_name.txt -Append
$newNumbers = 0
cls
}
if ($type -eq "Data" -and $localnumber -eq "4")
{
$shot_num = $csv.'Value 3'
$speed = ($csv.'Value 2' / 304.79999025)
$time_hack1=([int]$csv."value 1" + [int]631065600)
$time1 = (([System.DateTimeOffset]::FromUnixTimeSeconds($time_hack1)).DateTime.ToLocalTime()).ToString("s")
Write-output " " | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "Shot #: $shot_num" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "Speed: $speed" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "Time of Shot: $time1" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output " " | out-file -filepath .\Results\$Session_name.txt -Append
}
}
}
# cleanup actions
move-item -path .\fit_temp\*.csv -destination .\results\
move-item .\fit_temp\*.fit -destination .\fit_backup
Will work on 2nd csv output that is converted to feet per second, time corrected, and cleaned up. I got to playing with putting all the info into an array but then exporting the array as csv is giving me issues. I think I'm going to work around rather than solve the issue. Will manually make a csv, import it, export it so it has appropriate headers and see how that goes. Expect an update to the script later today.
Hey if you want someone to write a user's guide for all this, I can help out. I'm a technical writer by trade. 20+ years in the software development world.
01-18-2024, 06:02 PM (This post was last modified: 01-18-2024, 09:58 PM by Zeneffect.)
Code:
?#
# Notes: Requires Java
#
# Copy entire folder from google drive
# https://drive.google.com/drive/folders/1fb_MCyteiMZrcL-gj7DJXlCn-Y_LsGkO?usp=drive_link
# Connect Garmin Xero C1 Pro to Computer
# Run the script (right click + run with powershell)
# Navigate to the .FIT files on the device in the pop up and select them, press "Open" button
# Name the sessions when prompted in terminal window. Spaces are OK, dont include .txt
#
#
#
# Created by ZenEffect
cls
Add-Type -AssemblyName System.Windows.Forms
#ripped this part off of a google search. It creates function for the pop up window to select files
function Select-File {
[CmdletBinding()]
param(
[Parameter(ParameterSetName="Single")]
[Parameter(ParameterSetName="Multi")]
[Parameter(ParameterSetName="Save")]
[string]$StartingFolder = [environment]::getfolderpath("mydocuments"),
[Parameter(ParameterSetName="Single")]
[Parameter(ParameterSetName="Multi")]
[Parameter(ParameterSetName="Save")]
[string]$NameFilter = "All Files (*.*)|*.*",
[Parameter(ParameterSetName="Single")]
[Parameter(ParameterSetName="Multi")]
[Parameter(ParameterSetName="Save")]
[switch]$AllowAnyExtension,
[Parameter(Mandatory=$true,ParameterSetName="Save")]
[switch]$Save,
[Parameter(Mandatory=$true,ParameterSetName="Multi")]
[Alias("Multi")]
[switch]$AllowMulti
)
if ($Save) {
$Dialog = New-Object -TypeName System.Windows.Forms.SaveFileDialog
} else {
$Dialog = New-Object -TypeName System.Windows.Forms.OpenFileDialog
if ($AllowMulti) {
$Dialog.Multiselect = $true
}
}
if ($AllowAnyExtension) {
$NameFilter = $NameFilter + "|All Files (*.*)|*.*"
}
$Dialog.Filter = $NameFilter
$Dialog.InitialDirectory = $StartingFolder
[void]($Dialog.ShowDialog())
$Dialog.FileNames
}
# .FIT file to .CSV - Copies to a "fit_temp" folder first for processing. Does not interact with device except to copy the files to computer first.
$fit = (Select-File -StartingFolder "C:\" -NameFilter "Garmin FIT Files (*.fit)|*.fit" -AllowMulti)
copy $fit .\fit_Temp
$fit_temp = get-childitem -Path .\fit_temp | select-object fullname
foreach ($fitfile in $fit_temp)
{
$fit3 = $fitfile.fullname
$fit2 = "`"$fit3`""
# sends the selected files to the bat file that came with the Garmin SDK. Only modification to the .BAT was to remove the "Pause" at the end. FIT files
# are processed into CSV by some Garmin SDK Java magic.
start-process -filepath '.\java\fittocsv.bat' -argumentlist $fit2 -wait
}
# gets list of newly created .csv from fit_temp directory for processing
$items = get-childitem -Path .\fit_temp\*.csv | select fullname
foreach ($item in $items)
{
# Gets shot data and calculates standard deviation
$csv1 = $item.fullname | import-csv
$csv_data = $csv1 | where 'type' -eq 'data' | where 'local number' -eq 4
$measured = $csv_data.'value 2' | measure-object -average
$newNumbers = 0
ForEach ($number in $csv_data) {
$newNumbers += [Math]::Pow(($number.'value 2' - $measured.Average), 2)
}
$stdDev = [math]::Sqrt($($newNumbers / ($measured.Count - 1)))
$stdDevp = [math]::Sqrt($($newNumbers / ($measured.Count)))
$stddev_fps=$stddev / 304.79999025
$stddevp_fps=$stddevp / 304.79999025
# Begin CSV to TXT file processing
foreach ($csv in $csv1)
{
$type = $csv.type
$localnumber = $csv."local number"
if ($type -eq "Data" -and $localnumber -eq "3")
{
$min = ($csv."Value 2" / 304.79999025)
$max = ($csv."Value 3" / 304.79999025)
$average = ($csv."Value 4" / 304.79999025)
$shots = $csv."Value 6"
$time_hack=([int]$csv."Value 1" + [int]631065600)
$time = (([System.DateTimeOffset]::FromUnixTimeSeconds($time_hack)).DateTime.ToLocalTime()).ToString("s")
$extreme_spread= ($max - $min)
Write-host "Session Time/Date:" $time
Write-host "# of Shots:", $shots
Write-host " "
Write-host "Min:", $min
Write-host "Max:", $max
Write-host "Average:", $average
Write-host " "
write-host "Standard Deviation:", $stddev_fps
write-host "Population Standard Deviation:", $stddevp_fps
Write-host "Extreme Spread:", $extreme_spread
write-host " "
write-host " "
$Session_name = Read-Host -Prompt "Enter Session Name"
Write-output "Session Time/Date: $time" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "Session Name: $session_name" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "# of Shots: $shots" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output " " | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "Min: $min" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "Max: $max" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "Average: $average" | out-file -filepath .\Results\$Session_name.txt -Append
Write-Output "Standard Deviation: $stddev_fps" | out-file -filepath .\Results\$Session_name.txt -Append
Write-Output "Population Standard Deviation: $stddevp_fps" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "Extreme Spread: $extreme_spread" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output " " | out-file -filepath .\Results\$Session_name.txt -Append
Write-Output "Session_Time,Session_Name,Count,Min,Max,Average,SD,PSD,ES,Shot_Num,Shot_Time,Velocity" | out-file -filepath .\Results\$Session_name.csx -Append
Write-Output "$time , $session_name , $shots , $min , $max , $average , $stddev_fps , $stddevp_fps , $extreme_spread" | out-file -filepath .\Results\$Session_name.csx -Append
$newNumbers = 0
cls
}
if ($type -eq "Data" -and $localnumber -eq "4")
{
$shot_num = $csv.'Value 3'
$speed = ($csv.'Value 2' / 304.79999025)
$time_hack1=([int]$csv."value 1" + [int]631065600)
$time1 = (([System.DateTimeOffset]::FromUnixTimeSeconds($time_hack1)).DateTime.ToLocalTime()).ToString("s")
Write-output " " | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "Shot #: $shot_num" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "Speed: $speed" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output "Time of Shot: $time1" | out-file -filepath .\Results\$Session_name.txt -Append
Write-output " " | out-file -filepath .\Results\$Session_name.txt -Append
Write-Output ",,,,,,,,,$shot_num,$time1,$speed" | out-file -filepath .\Results\$Session_name.csx -Append
}
}
$csvprocessing = import-csv .\results\$session_name.csx
$csvprocessing | export-csv .\results\$session_name.csv -NoTypeInformation
remove-item .\results\$session_name.csx
}
# cleanup actions
move-item -path .\fit_temp\*.csv -destination .\results\
move-item .\fit_temp\*.fit -destination .\fit_backup
cleaned up CSV output is added, with actual CSV headers! It will be the same as your session name. what a cheap ass hack i thought of before goign to sleep. google drive repository is updated, updating github now.
I need to add a couple of safety features incase of bad or empty input but this is pretty much complete now.
01-18-2024, 06:18 PM (This post was last modified: 01-19-2024, 01:34 AM by Zeneffect.)
SDW Wrote:Hey if you want someone to write a user's guide for all this, I can help out. I'm a technical writer by trade. 20+ years in the software development world.
I do, and once written maybe we should make a separate post and a mod can sticky it since the discussion, development, etc are exclusive to this forum.
Extract the zip file anywhere on the PC
Go to the stuff you just unzipped, there is a "Garmin_CSV_Converter_v2.51.ps1" file there. Double click it, it should open in notepad
After the first # on first line, type something, anything you want. dog, cat, cheese then save. This makes YOU the script owner and you wont get prompted for execution level blah blah in powershell
Usage:
Right Click the "Garmin_CSV_Converter_v2.51.ps1" file then select "Run with Powershell" in the context menu
Select the .FIT files on the Garmin Xero C1 that you wish to export/convert (you can select multiple files and it works) then press the Open button
When prompted, enter a session name for the currently displayed session. spaces are ok, do not add a .txt or .csv to the end, it does that automatically.
Thats it, you are done
Results are found in the "Results" folder
I made a quick video. it should be easy enough to use for anyone as its minimal user interaction. its also worth mentioning you dont need to do notepad every time, just once is enough.
huh... just noticed in the output.. if you delete a shot on the device, you can detect it in the txt and csv as the shot number is the shot number, not the order of which it appears in the file. If you do 5 shots and delete shot #3 you will get shot #s listed as 1,2,4,5. the count is correct for the total shots displayed/calculated and will show 4. everything is fine, this is a feature not a bug.
01-18-2024, 06:49 PM (This post was last modified: 01-18-2024, 08:11 PM by SDW.)
Nice!
Edit...
What should the title of the instructions be? Should capture in a nutshell what the user hopes to accomplish at the end. "How to convert Garmin data to CSV data on your PC" or similar?
Maybe an intro ought to be something like "The following procedures show you how to copy ballistic data from your Xero to your PC and convert it to a useable format..." Or similar. I'm still trying to grasp the big picture, actually.
The rest of my questions, I'll PM you, @Zeneffect. No sense in cluttering up this thread with back and forth discussion on how to document the process.
thats it in a nutshell. its a script/utility that allows one to batch process the session data into 3x forms of usable data, directly from the garmin device when plugged into a PC.
I could write an installer (not really installer, self extracting exe that places things where in static locations so i can hard-code a shortcut) and make a shortcut. Instructions would be "double click this", then its installed. double click the shortcut and its running... select your files off the garmin and name the sessions when prompted. view results.
01-18-2024, 10:45 PM (This post was last modified: 01-18-2024, 10:54 PM by SDW.)
I ran into this just now while trying to run the script on my Lenovo LT, using PowerShell ISE. It's Windows 10 if that makes a difference (probably not). Apparently, running of scripts is disabled on my machine. I have not googled it yet. Do you think it will be a common issue with Windows users?
Apparently PowerShell won't run scripts from untrusted sources. (This script says Publisher: UNKNOWN.) I'm still reading the explanation of how to get around it.
01-18-2024, 10:58 PM (This post was last modified: 01-18-2024, 11:05 PM by Zeneffect.)
Set-ExecutionPolicy -ExecutionPolicy Unrestricted
Then run script again. End user issues running a powershell script is sort of expected. Can't predict everyone's setup however I used only the most basic of powershell functions so there shouldn't be compatibility issues.
Thanks. That's the next one to try. I just tried setting it to RemoteSigned, but that didn't work either. Different error message. I though it would because the script is local. But no.
For the sake of the procedures I should add this info under preliminary steps/setup.
Should we advise users to set the execution policy to back to restricted when finished with a conversion session?
BTW, did you mention this earlier in the thread? If so, I'm sorry, I missed it.
01-18-2024, 11:10 PM (This post was last modified: 01-18-2024, 11:18 PM by Zeneffect.)
I didn't mention earlier, it's par for the course with powershell scripts.
I suppose it should be in notes somewhere but the way I see it is, it sets a bar of competency and lowers the amount of questions and repetitive troubleshooting on my end.
I can also sign the script bit we would still need to fiddle with the executionpolicy anyways so I don't see the point. It's all plain text, no secrets or voodoo.
If you want to setup a zoom call or something, I'm available in about 30 min. Just pm me and I can send a link + step through the code with you.
This is a little new to me. I've run scripts and batch files, and Bash shell scripts back in the day. But not PowerShell on my own machine, as opposed to a work machine. This is more my wife's wheelhouse. She's a senior SW quality engineer at her company.
Problem with setting the bar for competency is that most people don't know how incompetent they are. LOL They will follow your basic instructions, hit a wall like I did, and then bug you about it. This is where I'd come in. I've written lots of reqs, test procedures, API documentation, and user guides. With writing guides, one of the cardinal rules is, do it well enough that the user doesn't need to reach out to customer service for help in how to use the SW. AKA keep the phones from ringing, so to speak.
I think the instructions might warrant some kind of brief heads up about execution policies. Maybe reference a short appendix of some sort. But it's your call. I'm sure you don't want to make this into a huge deal. With legal disclaimers and yadda yadda LOL Actually, "Use at your own risk" might not be a terrible idea. CMA
We could do a zoom call at some point. It'd be a good idea. I did start a Word doc to cover this stuff. Mostly for fun, just because I like doing it. Probably the same reason you developed this script package.
If it has an .exe extension, or other executable extension, you should be able to right click on the file in file explorer, and get the properties window to come up. Down toward the bottom is a place where you can "unblock" the file. Hit apply, then close, and try to run it then. This is a common thing for most Windows nowadays to set any executable to block from running. I don't know if power shell falls into this category but if nothing else works, it is worth a try.
"Down the floor, out the door, Go Brandon Go!!!!!"
01-19-2024, 12:57 AM (This post was last modified: 01-19-2024, 01:05 AM by Zeneffect.)
A rubber band is more reliable than a nerf gun with this chrono.
It was raining, I had a new toy, what else was I gonna do?
PM sent with link.
I dont like the idea of an exe for homebrew stuff as the code isnt exposed and easily modifiable, and you arent exactly sure what its doing. With a powershell script you can change it with just notepad, nothing is packaged up or encrypted, its wide open for all to see exactly what its doing and how. by being 100% transparent it should be more trustworthy as it can be peer reviewed at any time.
Want to change the measurement value? Notepad, find + replace 304.79999025 with whatever new value, save, and you are done.
BUT since an EXE IS much easier to use... I just converted it to an exe. uploading to google drive now.
01-19-2024, 01:08 AM (This post was last modified: 01-19-2024, 01:35 AM by Zeneffect.)
ok NOW its just an application that still needs the directory structure behind it.
download folder, run the exe. I realized i created folders, but when it downloads since they are empty they werent included. I added stub.txt files to ensure the folders stick.
01-19-2024, 02:51 AM (This post was last modified: 01-19-2024, 03:17 AM by Zeneffect.)
and i updated again. included delta from average and delta from last shot information. I need to add a prompt for bullet weight in grains to do a calculation for energy... then i will have all the features i want.
*edit*
reviewing the data.. i got my delta math bakkards. crap... fixing.
and fixed.
*edit edit*
bullet weight is entered with a decimal point yea? I think i see the field so i can extract and do an energy calculation, and list bullet weight from the session data. going to have every feature from the app.
once im satisfied with the output and features, ill move onto building in failsafe to prevent user induced failure (you didnt name your session, or you failed to select files!)