Posted 23 March 2024
After a multi-year hiatus, I recently started flying contests again in the Condor Soaring Simulator. As sort of a side project, I have also been working with the XCSoar glider navigation program, to see if I could use XCSoar to help navigate AAT/TAT tasks in Condor (Condor doesn’t support AAT/TAT tasks natively with the in-sim PDA).
After using XCSoar for a while, I became frustrated with XCSoar’s inability to define ‘custom’ turnpoints based on LAT/LON coordinates, which are used quite frequently in Condor contest tasks. After a long-fought and ultimately unsuccessful battle with XCSoar’s source code to see if I could modify the program to facilitate this, I admitted defeat and decided to try another way to skin this cat. XCSoar will accept an ‘Additional Waypoints’ file, so I decided to see if I could create a program to convert the ‘new TP’ blocks in the Condor ‘Task Briefing’ description to XCSoar-compatile .CUP file waypoint lines, which could then be loaded into XCSoar for selection as task waypoints.
Here is the .CUP file format defintion page from the SeeYou (naviter) program website:
The above description is NOT very easy to read. It is full of errors, so some imagination is required to make sense of it. The ‘hardpoints’ in the description are as follows:
- Latitude strings are exactly 9 characters long. Longitude strings are exactly 10 characters long.
- In latitude strings, the decimal point is exactly the 5th character (Char4) . In longitude strings, the decimal point is exactly the 6th character (Char5).
- Both latitude and longitude strings apparently must be zero-padded as necessary to make the string character counts work out. For instance, in the longitude example the ‘degree’ value of ‘014’ must be exactly three characters.
Example Run:
Here’s a recent task briefing from Condor-Club:
As can be seen in the above screengrab, TP2-TP2 are all ‘custom’ turnpoints defined only by Lat/Lon coordinates. Manually adding these to a .CUP formatted file for use in XCSoar would be essentially impossible, given that the turnpoint coordinates only become visible 15 minutes before server start.
To start the process, the turnpoint blocks from the above briefing were copy/pasted one at a time into a text document (I use NotePad++), as follows (note that I manually changed the name of the last turnpoint from ‘Finish’ to TP 6, as my Python script currently only looks for ‘Start’ and ‘TP’ starting strings)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
TP 2 (955 m.) Heading: 93° for 65.1 km, Coords: 49.8.359N / 20.14.201E Classic turnpoint, min. height: 0 m., max.: 10,000 m. angle: 360°, radius: 1,000 m. TP 3 (781 m.) Heading: 158° for 32.7 km, Coords: 48.51.629N / 20.22.768E Classic turnpoint, min. height: 0 m., max.: 10,000 m. angle: 360°, radius: 1,000 m. TP 4 (617 m.) Heading: 268° for 99.2 km, Coords: 48.49.454N / 19.1.724E Classic turnpoint, min. height: 0 m., max.: 10,000 m. angle: 360°, radius: 1,000 m. TP 5 (394 m.) Heading: 138° for 24.1 km, Coords: 48.39.956N / 19.15.148E Classic turnpoint, min. height: 0 m., max.: 10,000 m. angle: 360°, radius: 1,000 m. TP 6 (204 m.) Heading: 106° for 84.8 km, Coords: 48.31.81N / 20.22.800E Classic turnpoint, min. height: 0 m., max.: 10,000 m. angle: 180°, radius: 1,500 m. |
My CondorTPX_to_CupWP.py Python script opens a ‘FileOpen’ dialog where the input file (in this case ‘NewTPs_IN.txt’) can be selected by the user, and a ‘FileSave’ dialog where the output file (in this case ‘NewTPS_OUT.CUP’) can be selected, and then parses through the blocks in the input file, converting them to equivalent .CUP-formatted lines compatible with XCSoar. Here is the console printout from the ‘verbose’ (-v) version of the script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
File containing custom Condor task turnpoints = C:/Users/Frank/Desktop/NewTPS_IN.txt File to which XCSoar-compatible turnpoint lines will be written = C:/Users/Frank/Desktop/NewTPS_OUT.txt list contains 34 items 0: TP 2 (955 m.) 1: Heading: 93° for 65.1 km, 2: Coords: 49.8.359N / 20.14.201E 3: Classic turnpoint, 4: min. height: 0 m., max.: 10,000 m. 5: angle: 360°, radius: 1,000 m. 6: 7: TP 3 (781 m.) 8: Heading: 158° for 32.7 km, 9: Coords: 48.51.629N / 20.22.768E 10: Classic turnpoint, 11: min. height: 0 m., max.: 10,000 m. 12: angle: 360°, radius: 1,000 m. 13: 14: TP 4 (617 m.) 15: Heading: 268° for 99.2 km, 16: Coords: 48.49.454N / 19.1.724E 17: Classic turnpoint, 18: min. height: 0 m., max.: 10,000 m. 19: angle: 360°, radius: 1,000 m. 20: 21: TP 5 (394 m.) 22: Heading: 138° for 24.1 km, 23: Coords: 48.39.956N / 19.15.148E 24: Classic turnpoint, 25: min. height: 0 m., max.: 10,000 m. 26: angle: 360°, radius: 1,000 m. 27: 28: TP 6 (204 m.) 29: Heading: 106° for 84.8 km, 30: Coords: 48.31.81N / 20.22.800E 31: Classic turnpoint, 32: min. height: 0 m., max.: 10,000 m. 33: angle: 180°, radius: 1,500 m. Start Processing Lines 0: TP 2 (955 m.) In ConstructCUPString(tpname = NewTP 2, inlist_idx = 0) ['Coords:', '49.8.359N', '/', '20.14.201E'] Constructing Latitude... latitude string = 49.8.359N latitude lat_deg str = 49 latitude lat_deg = 49 after adjustment, latitude deg str = 49 lat_deg_dot_idx = 2, min_dot_idx = 4 latitude minplusdec str = 8.359N latitude NorS str = N latitude min dot idx = 1 latitude min val str = 8 latitude min dec val str = 359 after 'len < 2' adjustment, latitude min str = 08 after 'len < 2' adjustment, latitude min dec str = 359 latitude min = 8 latitude min str = 08 latitude .CUP str = 4908.359N Constructing Longitude... longitude string = 20.14.201E longitude lon_deg str = 20 longitude lon_deg = 20 after 'len < 3' adjustment, longitude deg str = 020 lon_deg_dot_idx = 2, min_dot_idx = 5 longitude minplusdec str = 14.201E longitude EorW str = E longitude min dot idx = 2 longitude min val str = 14 longitude min dec val str = 201 after 'len < 2' adjustment, longitude min str = 14 after 'len < 3' adjustment, longitude min dec str = 201 longitude .CUP str = 02014.201E Extracting Turnpoint Style: stylestr = Classic numstr = 2 back in main pgm: cupstr = NewTP 2, NewTP 2, ,4908.359N, 02014.201E, ,Classic, , , ,, x = 4 5: angle: 360°, radius: 1,000 m. numstr = back in main pgm: cupstr = , x = 5 6: numstr = back in main pgm: cupstr = , x = 6 7: TP 3 (781 m.) In ConstructCUPString(tpname = NewTP 3, inlist_idx = 7) ['Coords:', '48.51.629N', '/', '20.22.768E'] Constructing Latitude... latitude string = 48.51.629N latitude lat_deg str = 48 latitude lat_deg = 48 after adjustment, latitude deg str = 48 lat_deg_dot_idx = 2, min_dot_idx = 5 latitude minplusdec str = 51.629N latitude NorS str = N latitude min dot idx = 2 latitude min val str = 51 latitude min dec val str = 629 after 'len < 2' adjustment, latitude min str = 51 after 'len < 2' adjustment, latitude min dec str = 629 latitude min = 51 latitude min str = 51 latitude .CUP str = 4851.629N Constructing Longitude... longitude string = 20.22.768E longitude lon_deg str = 20 longitude lon_deg = 20 after 'len < 3' adjustment, longitude deg str = 020 lon_deg_dot_idx = 2, min_dot_idx = 5 longitude minplusdec str = 22.768E longitude EorW str = E longitude min dot idx = 2 longitude min val str = 22 longitude min dec val str = 768 after 'len < 2' adjustment, longitude min str = 22 after 'len < 3' adjustment, longitude min dec str = 768 longitude .CUP str = 02022.768E Extracting Turnpoint Style: stylestr = Classic numstr = 3 back in main pgm: cupstr = NewTP 3, NewTP 3, ,4851.629N, 02022.768E, ,Classic, , , ,, x = 11 12: angle: 360°, radius: 1,000 m. numstr = back in main pgm: cupstr = , x = 12 13: numstr = back in main pgm: cupstr = , x = 13 14: TP 4 (617 m.) In ConstructCUPString(tpname = NewTP 4, inlist_idx = 14) ['Coords:', '48.49.454N', '/', '19.1.724E'] Constructing Latitude... latitude string = 48.49.454N latitude lat_deg str = 48 latitude lat_deg = 48 after adjustment, latitude deg str = 48 lat_deg_dot_idx = 2, min_dot_idx = 5 latitude minplusdec str = 49.454N latitude NorS str = N latitude min dot idx = 2 latitude min val str = 49 latitude min dec val str = 454 after 'len < 2' adjustment, latitude min str = 49 after 'len < 2' adjustment, latitude min dec str = 454 latitude min = 49 latitude min str = 49 latitude .CUP str = 4849.454N Constructing Longitude... longitude string = 19.1.724E longitude lon_deg str = 19 longitude lon_deg = 19 after 'len < 3' adjustment, longitude deg str = 019 lon_deg_dot_idx = 2, min_dot_idx = 4 longitude minplusdec str = 1.724E longitude EorW str = E longitude min dot idx = 1 longitude min val str = 1 longitude min dec val str = 724 after 'len < 2' adjustment, longitude min str = 01 after 'len < 3' adjustment, longitude min dec str = 724 longitude .CUP str = 01901.724E Extracting Turnpoint Style: stylestr = Classic numstr = 4 back in main pgm: cupstr = NewTP 4, NewTP 4, ,4849.454N, 01901.724E, ,Classic, , , ,, x = 18 19: angle: 360°, radius: 1,000 m. numstr = back in main pgm: cupstr = , x = 19 20: numstr = back in main pgm: cupstr = , x = 20 21: TP 5 (394 m.) In ConstructCUPString(tpname = NewTP 5, inlist_idx = 21) ['Coords:', '48.39.956N', '/', '19.15.148E'] Constructing Latitude... latitude string = 48.39.956N latitude lat_deg str = 48 latitude lat_deg = 48 after adjustment, latitude deg str = 48 lat_deg_dot_idx = 2, min_dot_idx = 5 latitude minplusdec str = 39.956N latitude NorS str = N latitude min dot idx = 2 latitude min val str = 39 latitude min dec val str = 956 after 'len < 2' adjustment, latitude min str = 39 after 'len < 2' adjustment, latitude min dec str = 956 latitude min = 39 latitude min str = 39 latitude .CUP str = 4839.956N Constructing Longitude... longitude string = 19.15.148E longitude lon_deg str = 19 longitude lon_deg = 19 after 'len < 3' adjustment, longitude deg str = 019 lon_deg_dot_idx = 2, min_dot_idx = 5 longitude minplusdec str = 15.148E longitude EorW str = E longitude min dot idx = 2 longitude min val str = 15 longitude min dec val str = 148 after 'len < 2' adjustment, longitude min str = 15 after 'len < 3' adjustment, longitude min dec str = 148 longitude .CUP str = 01915.148E Extracting Turnpoint Style: stylestr = Classic numstr = 5 back in main pgm: cupstr = NewTP 5, NewTP 5, ,4839.956N, 01915.148E, ,Classic, , , ,, x = 25 26: angle: 360°, radius: 1,000 m. numstr = back in main pgm: cupstr = , x = 26 27: numstr = back in main pgm: cupstr = , x = 27 28: TP 6 (204 m.) In ConstructCUPString(tpname = NewTP 6, inlist_idx = 28) ['Coords:', '48.31.81N', '/', '20.22.800E'] Constructing Latitude... latitude string = 48.31.81N latitude lat_deg str = 48 latitude lat_deg = 48 after adjustment, latitude deg str = 48 lat_deg_dot_idx = 2, min_dot_idx = 5 latitude minplusdec str = 31.81N latitude NorS str = N latitude min dot idx = 2 latitude min val str = 31 latitude min dec val str = 81 after 'len < 2' adjustment, latitude min str = 31 after 'len < 2' adjustment, latitude min dec str = 810 latitude min = 31 latitude min str = 31 latitude .CUP str = 4831.810N Constructing Longitude... longitude string = 20.22.800E longitude lon_deg str = 20 longitude lon_deg = 20 after 'len < 3' adjustment, longitude deg str = 020 lon_deg_dot_idx = 2, min_dot_idx = 5 longitude minplusdec str = 22.800E longitude EorW str = E longitude min dot idx = 2 longitude min val str = 22 longitude min dec val str = 800 after 'len < 2' adjustment, longitude min str = 22 after 'len < 3' adjustment, longitude min dec str = 800 longitude .CUP str = 02022.800E Extracting Turnpoint Style: stylestr = Classic numstr = 6 back in main pgm: cupstr = NewTP 6, NewTP 6, ,4831.810N, 02022.800E, ,Classic, , , ,, x = 32 33: angle: 180°, radius: 1,500 m. numstr = back in main pgm: cupstr = , x = 33 all 34 infile lines processed Contents of output file C:/Users/Frank/Desktop/NewTPS_OUT.txt name,code,country,lat,lon,elev,style,rwdir,rwlen,freq,desc NewTP 2, NewTP 2, ,4908.359N, 02014.201E, ,Classic, , , , NewTP 3, NewTP 3, ,4851.629N, 02022.768E, ,Classic, , , , NewTP 4, NewTP 4, ,4849.454N, 01901.724E, ,Classic, , , , NewTP 5, NewTP 5, ,4839.956N, 01915.148E, ,Classic, , , , NewTP 6, NewTP 6, ,4831.810N, 02022.800E, ,Classic, , , , Press any key to continue . . . |
At the very end of the above printout, the newly-written contents of the output file are read back out again as a verification that the conversion was successful. Here is the actual contents of the ‘NewTPS_OUT.CUP’ file:
1 2 3 4 5 6 |
name,code,country,lat,lon,elev,style,rwdir,rwlen,freq,desc NewTP 2, NewTP 2, ,4908.359N, 02014.201E, ,Classic, , , , NewTP 3, NewTP 3, ,4851.629N, 02022.768E, ,Classic, , , , NewTP 4, NewTP 4, ,4849.454N, 01901.724E, ,Classic, , , , NewTP 5, NewTP 5, ,4839.956N, 01915.148E, ,Classic, , , , NewTP 6, NewTP 6, ,4831.810N, 02022.800E, ,Classic, , , , |
This file now has to be transferred to the directory used by XCSoar for waypoints, and then selected in XCSoar (Config->System->Site Files) to load as the ‘More Waypoints’ selection. After this, all the above turnpoints will be available for task construction. Here’s a photo of my Android tablet with the above task turnpoints loaded:
It is clear from the above images that the Condor-Club ‘custom’ task turnpoints have been converted properly from text blocks to SeeYou .CUP format waypoint strings, so now I can use XCSoar to navigate Condor tasks with ‘custom’ turnpoints – Yay!
Here’s the Python script I created to do the conversion:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# This Python file uses the following encoding: utf-8 import argparse parser = argparse.ArgumentParser() parser.add_argument("-v", "--verbose", help="increase output verbosity", action="store_true") args = parser.parse_args() # Purpose: Convert Condor task 'New TP' turnpoints (copy/pasted from Condor task description # to a text file) to CUP-formatted strings and write them to a user-selected file to be used # in XCSoar # CUP file format: # name,code,country,lat,lon,elev,style,rwdir,rwlen,freq,desc #"Cruseilles",Cruseill,,4601.100N,00606.400E,716.0m,1,,,, #"Marignane",,,4325.998N,00512.846E,4m,5,317,2500,0, #Condor Task 'New Waypoint' format: # TP 2 (7,362 ft) # Heading: 144° for 46.8 NM, # Coords: 45.23.99N / 6.45.300E # Classic turnpoint, # min. height: 0 ft, max.: 32,808 ft # angle: 360°, radius: 3,281 ft #Plan: # Step1: Get text file containing Condor 'newTP' blocks copy/pasted from task description # Step2: Get 'outfile' filename to write .CUP lines to. Create if necessary. # If aleady existing, ask user if we can overwrite. 'No' causes program to exit # Step3: Read lines until finding a line starting with 'TP' or 'Start' # Step4: Parse 'Coords' line to get coordinate Lat/Lon strings # Step5: Construct .CUP formatted line & write to outfile # Step1: Get text file containing Condor 'newTP' blocks copy/pasted from task description import tkinter as tk from tkinter.filedialog import askopenfilename, asksaveasfilename root = tk.Tk() ; root.withdraw() # PURPOSE: constructs a .CUP-formatted string from the (six?) lines waypoint # block in the typical Condor-club task briefing. # For example: # ---------------------------------------------------------------------- # Start (2,434 ft) # Heading: 115° for 3.9 NM, # Coords: 46.31.377N / 6.42.372E # Classic turnpoint, # min. height: 0 ft, max.: 6,234 ft # angle: 180°, radius: 13,123 ft # ---------------------------------------------------------------------- # The CUP waypoint format is: # name,code,country,lat,lon,elev,style,rwdir,rwlen,freq,desc # but we are only going to fill the name, code, lat, lon and style fields #Inputs: # TPName = string containing the name to be used in the .CUP string # x = integer denoting index into the turnpoint block string list acquired from the input file #Outputs # returns a string and the INDEX TO THE NEXT LINE in the list of strings from the input file #Plan: # Step1: assign 'TPname' to 'name' and 'code' # Step2: skip the 'Heading' line # Step3: extract the lat/lon strings from the 'Coords' line and convert them to .CUP format # Step4: extract the style string from the next line # Step5: concatenate the above, with appropriate ',' entries for blank entries def ConstructCUPString(tpname, inlist, inlist_idx): if args.verbose: print(f"In ConstructCUPString(tpname = {tpname}, inlist_idx = {inlist_idx})") #Step1: assign 'TPname' to 'name' and 'code' namestr = tpname codestr = tpname #Step2: skip the 'Heading' line inlist_idx += 2 #Step3: extract the lat/lon strings from the 'Coords' line and convert them to .CUP format # From SeeYou site: Latitude is a field of length 9, where char0-1 are degrees, Char2-3 are minutes, Char4 decimal point, # char5-7 are decimal minutes and char8 is either N or S. The ellipsoid used is WGS-1984 # Example: SeeYou 5107.830N is equal to 51° 07.830' N print(f"inlist[{inlist_idx}] = {inlist[inlist_idx]}") coordstrlist = inlist[inlist_idx].split() if args.verbose: print(coordstrlist) print("Constructing Latitude...\n") #latitude deg latstr = coordstrlist[1] if args.verbose: print(f"latitude string = {latstr}") lat_deg_dot_idx = latstr.find('.') lat_deg_val_str = latstr[0:lat_deg_dot_idx] lat_deg_val = int(lat_deg_val_str) if args.verbose: print(f"latitude lat_deg str = {lat_deg_val_str}") print(f"latitude lat_deg = {lat_deg_val}") #if necessary, add leading zero(s) to lat_deg value if len(lat_deg_val_str) < 2: if len(lat_deg_val_str) < 1: lat_deg_val_str = '0'+ lat_deg_val_str lat_deg_val_str = '0'+ lat_deg_val_str if args.verbose: print(f"after adjustment, latitude deg str = {lat_deg_val_str}") #latitude min min_dot_idx = latstr.find('.',lat_deg_dot_idx +1 ) if args.verbose: print(f"lat_deg_dot_idx = {lat_deg_dot_idx}, min_dot_idx = {min_dot_idx}") minplusdec_str = latstr[lat_deg_dot_idx +1:] #still has 'N/S' appended lat_NorS_str = minplusdec_str[len(minplusdec_str)-1] minplusdec_str = minplusdec_str[0:len(minplusdec_str)] if args.verbose: print(f"latitude minplusdec str = {minplusdec_str}") print(f"latitude NorS str = {lat_NorS_str}") lat_min_dot_idx = minplusdec_str.find('.') if args.verbose: print(f"latitude min dot idx = {lat_min_dot_idx}") lat_min_val_str = minplusdec_str[0:lat_min_dot_idx] if args.verbose: print(f"latitude min val str = {lat_min_val_str}") lat_min_dec_val_str = minplusdec_str[lat_min_dot_idx + 1: len(minplusdec_str)-1] if args.verbose: print(f"latitude min dec val str = {lat_min_dec_val_str}") lat_min_dec_val = int(lat_min_dec_val_str) lat_min_val = int(lat_min_val_str) #if necessary, add leading zero(s) to minutes value if len(lat_min_val_str) < 2: if len(lat_min_val_str): lat_min_val_str = '0'+ lat_min_val_str if args.verbose: print(f"after 'len < 2' adjustment, latitude min str = {lat_min_val_str}") #if necessary, add trailing zero(s) to minutes decimal value if len(lat_min_dec_val_str) < 3: if len(lat_min_dec_val_str) < 2: lat_min_dec_val_str = lat_min_dec_val_str + '0' lat_min_dec_val_str = lat_min_dec_val_str + '0' if args.verbose: print(f"after 'len < 2' adjustment, latitude min dec str = {lat_min_dec_val_str}") print(f"latitude min = {lat_min_val}") print(f"latitude min str = {lat_min_val_str}") #now construct SeeYou equivalent cuplatstr = lat_deg_val_str + lat_min_val_str + '.' + lat_min_dec_val_str + lat_NorS_str if args.verbose: print(f"latitude .CUP str = {cuplatstr}") print("\nConstructing Longitude...\n") lonstr = coordstrlist[3] if args.verbose: print(f"longitude string = {lonstr}") lon_deg_dot_idx = lonstr.find('.') lon_deg_val_str = lonstr[0:lon_deg_dot_idx] lon_deg_val = int(lon_deg_val_str) if args.verbose: print(f"longitude lon_deg str = {lon_deg_val_str}") print(f"longitude lon_deg = {lon_deg_val}") #if necessary, add leading zero(s) to lon_deg value if len(lon_deg_val_str) < 3: if len(lon_deg_val_str) < 2: lon_deg_val_str = '0'+ lon_deg_val_str lon_deg_val_str = '0'+ lon_deg_val_str if args.verbose: print(f"after 'len < 3' adjustment, longitude deg str = {lon_deg_val_str}") #longitude min min_dot_idx = lonstr.find('.',lon_deg_dot_idx +1 ) if args.verbose: print(f"lon_deg_dot_idx = {lon_deg_dot_idx}, min_dot_idx = {min_dot_idx}") minplusdec_str = lonstr[lon_deg_dot_idx +1:] #still has 'E/W' appended lon_EorW_str = minplusdec_str[len(minplusdec_str)-1] minplusdec_str = minplusdec_str[0:len(minplusdec_str)] if args.verbose: print(f"longitude minplusdec str = {minplusdec_str}") print(f"longitude EorW str = {lon_EorW_str}") lon_min_dot_idx = minplusdec_str.find('.') lon_min_val_str = minplusdec_str[0:lon_min_dot_idx] lon_min_dec_val_str = minplusdec_str[lon_min_dot_idx + 1: len(minplusdec_str)-1] if args.verbose: print(f"longitude min dot idx = {lon_min_dot_idx}") print(f"longitude min val str = {lon_min_val_str}") print(f"longitude min dec val str = {lon_min_dec_val_str}") #if necessary, add leading zero(s) to minutes value if len(lon_min_val_str) < 2: if len(lon_min_val_str): lon_min_val_str = '0'+ lon_min_val_str if args.verbose: print(f"after 'len < 2' adjustment, longitude min str = {lon_min_val_str}") #if necessary, add trailing zero(s) to minutes decimal value if len(lon_min_dec_val_str) < 3: if len(lon_min_dec_val_str) < 2: lon_min_dec_val_str = lon_min_dec_val_str + '0' lon_min_dec_val_str = lon_min_dec_val_str + '0' if args.verbose: print(f"after 'len < 3' adjustment, longitude min dec str = {lon_min_dec_val_str}") #now construct SeeYou equivalent cuplonstr = lon_deg_val_str + lon_min_val_str + '.' + lon_min_dec_val_str + lon_EorW_str if args.verbose: print(f"longitude .CUP str = {cuplonstr}") #Step4: extract the style string from the next line if args.verbose: print(f"\nExtracting Turnpoint Style: ") inlist_idx += 1 stylestrlist = inlist[inlist_idx].split() stylestr = stylestrlist[0] if args.verbose: print(f"stylestr = {stylestr}\n") #Step5: concatenate the above, with appropriate ',' entries for blank entries # name,code,country,lat,lon,elev,style,rwdir,rwlen,freq,desc cupStr = namestr + ", " + codestr + ", ," + cuplatstr + ", " + cuplonstr + ", ," + stylestr + ", , , ," inlist_idx += 1 return cupStr, inlist_idx #------------------------------------------------------------------------------------------------------ #----------------------------------------Main Program------------------------------------------------ #------------------------------------------------------------------------------------------------------ infile = askopenfilename(parent=root) print(f"File containing custom Condor task turnpoints = {infile}") # Step2: Get 'outfile' filename to write .CUP lines to. Create if necessary. # If aleady existing, ask user if we can overwrite. 'No' causes program to exit outfilename = asksaveasfilename(parent = root) print(f"File to which XCSoar-compatible turnpoint lines will be written = {outfilename}") ofile = open(outfilename,'w') ofile.write(f"name,code,country,lat,lon,elev,style,rwdir,rwlen,freq,desc\n") # Step3: Read 'infile' lines until finding a line starting with 'TP' or 'Start' f=open(infile, encoding="utf-8")#"utf-8" encoding param needed so deg symbol displays OK inlist = f.readlines() f.close() TPname = '' #Print out entire list if args.verbose: print("list contains " + str(len(inlist)) + " items") for x in range(len(inlist)): instr = inlist[x] print(str(x) + ": " + instr) if args.verbose: print("\nStart Processing Lines\n") # for x in range(len(inlist)): x = 0 while x < len(inlist): instr = inlist[x] if args.verbose: print(str(x) + ": " + instr) outstr = '' #03/23/2024 needed to avoid error in verbose print block numstr = '' #03/23/2024 needed to avoid error in verbose print block if 'Start' in instr: TPname = 'NewStart'+ str(x) outstr, x = ConstructCUPString(TPname, inlist, x) ofile.write(outstr + "\n") #Write .CUP formatted line & write to outfile # if args.verbose: # print(f"back in main pgm: cupstr = {outstr}, x = {x}") elif 'TP' in instr: #TP number separated by a space numstr = instr[2:4] TPname = 'NewTP'+ numstr outstr, x = ConstructCUPString(TPname, inlist, x) ofile.write(outstr + "\n") #Write .CUP formatted line & write to outfile if args.verbose: print(f"numstr = {numstr}") print(f"back in main pgm: cupstr = {outstr}, x = {x}") x = x + 1 print(f"all {x} infile lines processed") ofile.close() #now print out ofile contents # if args.verbose: print(f"\nContents of output file {outfilename}") outfile = open(outfilename, 'r') for line in outfile: print(line) |
Enjoy!
Frank