Interactive specification
The SeeYou CUP format, made legible.
A comma-separated file carrying two things at once: a list of waypoints, and a list of tasks built from them. Naviter maintains a real, versioned spec for it — but it stops exactly where observation zones get interesting. This reference covers both halves, and fills the gap from the implementation everyone actually interoperates with.
One file, two halves
A .cup file holds a list of waypoints — one CSV row each, with a header row naming the columns — and, optionally, a list of tasks that reference those waypoints by name. A single literal line separates them:
name,code,country,lat,lon,elev,style,rwdir,rwlen,rwwidth,freq,desc "Lesce","LJBL",SI,4621.379N,01410.467E,504.0m,5,144,1130.0m,,123.500,"Home Airfield" …more waypoints… -----Related Tasks----- "300 km Triangle","Lesce","Sv Peter","Maier","Sv Peter","Lesce" Options,TaskTime=03:00:00,WpDis=True ObsZone=0,Style=2,R1=2500m,A1=180,Line=1
Most .cup files in circulation have no task section at all — they are pure waypoint databases, which is how site and turnpoint collections are distributed. A file that simply ends after the separator is valid and means "no tasks".
Encoding is UTF-8, or Windows-1252 for legacy files. Datum is WGS-84 throughout. Line endings are CRLF in practice; accept LF. There is no version marker inside the file — the format has only ever grown by adding optional trailing columns and optional key/value parameters, so a current reader parses a 2004-era file unchanged.
ObsZone=0 addresses. For both, this page follows XCSoar's TaskFileSeeYou.cpp — the behaviour instruments were written to interoperate with.CUPX (.cupx) is two ZIP archives concatenated — pictures under pics/, then a POINTS.CUP whose pics column names them. The CUP grammar inside is identical; CUPX is purely packaging.
The header row is load-bearing
CUP is CSV, in a permissive dialect. Fields are comma-separated; a field containing a comma must be double-quoted; embedded quotes are escaped by doubling them (""), per RFC 4180. Quoting is optional otherwise, and real files quote name and desc while leaving codes, coordinates and numerics bare.
Two rules turn the header from decoration into the thing you must actually parse:
| Rule | Consequence |
|---|---|
| Column order is arbitrary | lat,lon,elev,name,code,country,style,… is a conforming header and must be honoured. |
| Trailing columns may be dropped | Any column after style is optional and can be removed from both header and data. |
rwwidth. A very large body of files uses name,code,country,lat,lon,elev,style,rwdir,rwlen,freq,desc — eleven columns, no rwwidth. A positional reader written against the 14-column layout silently reads the frequency as a runway width on every one of them. The header is the only thing that tells the two apart.Whitespace around fields is common and insignificant. 488.0m, 5 and 5103.117N all appear in files shipped as reference test data. Trim every field before parsing it. Empty fields are legal and mean "not specified"; a row may end in a run of empty fields or a trailing comma.
Units
Numeric fields carry an optional unit suffix. When it is missing, metres are assumed. The decimal separator is always a point, and units may be mixed freely within one file.
| Field | Accepted units | Example |
|---|---|---|
elev | m (default), ft | 504.0m · 525ft |
rwlen, rwwidth | m (default), nm, ml | 1130.0m · 0.005NM · 0.01ml |
R1, R2 | m (default), nm, ml | 35000m · 2.5nm |
MaxAlt | m (default), ft | 1500m · 5000ft |
Degrees and decimal minutes, fixed width
Latitude is 9 characters, longitude 10, each ending in a hemisphere letter. This is not decimal degrees, and it is not DMS — the digits after the point are thousandths of a minute.
| Field | Layout | Example | Decodes to |
|---|---|---|---|
| Latitude | DDMM.mmm[N|S] | 5107.830N | 51° 07.830′ N = 51.1305° |
| Longitude | DDDMM.mmm[E|W] | 01410.467E | 014° 10.467′ E = 14.17445° |
decimal = degrees + minutes / 60 if hemisphere is S or W: decimal = -decimal
The degrees field is zero-padded to a fixed width — 2 for latitude, 3 for longitude — and that is what makes the split unambiguous. Slice by position from the right: the two characters before the decimal point are whole minutes, everything before them is degrees.
46 + 21.379, never 4621 + 379 — do not split on the decimal point. And .830 is 0.830 minutes (49.8 seconds), not 83 seconds; reading the decimals as seconds puts the waypoint about a kilometre out.Three decimal minutes is roughly 1.85 m of latitude. That is the format's resolution — a round trip through CUP will not preserve more.
Every column and every key
Filter by name or meaning, or narrow by group. The waypoint columns are the CSV header keys; the task keys are the Options and ObsZone parameters below the separator.
Only name, lat and lon are practically required. name is doubly important: it must be unique, because it is the only key a task has for referencing a waypoint.
What kind of place this is
The style column is an integer 0–21. A value outside the table must be read as 0 (Unknown) rather than rejected — that is an explicit requirement of the spec, not a tolerance.
rwdir, rwlen, rwwidth and freq carry meaning. Styles 20 and 21 — PG Take Off and PG Landing Zone — were added for free flight, and on those rwdir is re-used to declare the mid direction of the first take-off sector rather than a runway heading.Routes built from names
Everything after -----Related Tasks----- describes tasks. A task is one task line, optionally followed by decoration lines that apply to it. The next task begins at the next line that is not a decoration line.
description, takeoff, 1st turnpoint, 2nd turnpoint, …, last turnpoint, landing
Every field after the description is a waypoint name that must match the name column of a waypoint defined above the separator, matched as an exact string. The number of points, and of tasks, is unlimited.
"1000km FAI Triangle","0LESCE","Sv Peter","1K MAIER","1K ZELTW","Sv Peter","0LESCE" ,"0LESCE","0Start","750 Huje","750 Brenner","0Start","0LESCE",
The description is optional; when it is empty the line simply begins with a comma, and the reading application infers the task type. That leading " or , is exactly how a reader recognises a task line — everything else in the section is decoration belonging to the task above it.
Decoration lines
| Line | Meaning |
|---|---|
| Options,Key=Value,… | Task-wide settings — timing, distance basis, tolerances. One per task. |
| ObsZone=<i>,Key=Value,… | Observation zone for task point i. 0 = start. |
| Point=<i>,<waypoint record> | Defines a task point inline, for points with no waypoint above the separator. The payload is a full waypoint row. |
| STARTS=a,b,c | Alternative start points, all defined as waypoints. They share one observation zone — the ObsZone=0 line applies to whichever start is used. |
Point=1,"Point_3",PNT_3,,4627.136N,01412.856E,0.0m,1,,,,,,,
Point=1 is documented as "the first turnpoint (after takeoff)" — the index counts task-line columns with the takeoff at 0, so Point=1 is the third comma-separated field. Note this is offset by one from ObsZone= numbering; the two schemes are not interchangeable.
The Options line
Options,NoStart=12:34:56,TaskTime=01:45:12,WpDis=False,NearDis=0.7km,NearAlt=300.0m
WpDis is the most consequential key in the file: it decides whether this is a racing task (True — measure waypoint to waypoint) or an assigned-area task (False — measure through the actual fixes), and that in turn changes how the observation-zone parameters are interpreted. Every key is optional; an absent key means the application default. The full list is in the field explorer.
NoStart. A task imported from CUP is route + goal; anything richer has to come from another format.From parameters to shapes
An ObsZone= line gives a point's zone as key/value pairs. Style names the orientation of the zone, not its shape — the shape comes from the combination of R1, A1, R2, A2 and Line.
Style | The zone's bisector points… |
|---|---|
0 | Fixed — along the bearing given in A12 |
1 | Symmetrical — bisecting the inbound and outbound legs |
2 | To next point |
3 | To previous point |
4 | To start point |
An unknown or unparsable Style falls back to 1 (Symmetrical). Defaults for absent keys: R1 = 500 m, R2 = 200 m, Line = false.
A1 is a half-angle measured either side of the bisector — so a full cylinder is written A1=180, never A1=360. And on a line zone, R1 is a half-width — a 5 km start gate is R1=2500m,Line=1. Both mistakes fail silently and produce a plausible-looking wrong task.Try it
Move the controls and watch the zone. The ObsZone line below is what a file would carry for the shape on screen; the bisector points up.
The full mapping
Naviter defines the keys; the geometry below is XCSoar's, and is the de-facto standard.
| Condition | Resulting zone |
|---|---|
No ObsZone line for the point | FAI quadrant — 90°, 3 km |
Line=1 on the start or finish | Line of total width 2 × R1 |
A1 ≈ 180 | Cylinder of radius R1 |
Racing, Style=0 | Cylinder of radius R1 — fixed sectors are not representable |
Racing, Style=1, R1 ≥ 3000 and 2 × A1 ≈ 90° | FAI quadrant |
Racing, Style=1, otherwise | Symmetric circular sector, radius R1, total angle 2 × A1 |
Area task, intermediate, R2 > 0 and A2 ≈ 0 | Annular sector between R2 and R1 |
| Area task, intermediate, otherwise | Sector of radius R1 spanning bisector ± A1 |
| Anything else | Cylinder of radius R1 |
Three parameter combinations are additionally recognised by value, within ±2 units, as named zones:
A1 | R1 | A2 | R2 | Zone |
|---|---|---|---|---|
| 45° | 10 000 m | 180° | 500 m | Keyhole |
| 45° | 20 000 m | 180° | 500 m | BGA fixed course |
| 90° | 10 000 m | 180° | 500 m | BGA enhanced option |
MaxAlt= (start-height ceiling, meaningful only on ObsZone=0) and Reduce= (zone may be auto-shrunk) are written and read by real tools but appear nowhere in the Naviter document. Skip keys you do not know — never fail a file over one.The format's sharpest edge
ObsZone=0 is the start, and the start is the second waypoint on the task line — because the first is the takeoff.
task line: desc , takeoff , start , tp1 , tp2 , … , finish , landing column: 0 1 2 3 4 n-1 n ObsZone: 0 1 2 n-3 —
ObsZone=i addresses task-line column i + 2. Neither the takeoff nor the landing can carry a zone. This is what XCSoar implements, and it is consistent with the Naviter document's own "0 = Start".
It bites in both directions:
- A reader that maps
ObsZone=ionto the i-th waypoint after the description applies every radius one point too early — the start's gate width lands on the takeoff, and so on down the task. - Conversely, a great many free-flight files carry no takeoff column at all and begin the list at the start point. On those, index
ireally does address columni + 1, and the spec-conformant reader is the one that is wrong.
There is no flag distinguishing the two. The usable heuristic is to compare the highest ObsZone index against the number of waypoint columns C: a spec-conformant file tops out at C − 3, while a takeoff-less file reaches C − 2 or C − 1. When a file carries no zones at all, the question is moot.
ObsZone line lands on under the spec reading, and flag an index that cannot be spec-conformant.Paste CUP, read it back in English
Edit the file on the left. Every line is parsed live — the header row drives the column mapping, coordinates are converted to decimal, styles and units are spelled out, task columns are labelled with their role, and each observation zone is resolved to a shape. Nothing is uploaded; it all runs in the page.
Be permissive on input, strict on output
- Zone indexing. The takeoff offset is the biggest source of disagreement between implementations. Detect it; do not assume it.
- Halves.
A1is a half-angle andR1on a line is a half-width. Both fail silently when doubled or halved. - Header-driven, not position-driven. Column order is arbitrary and trailing columns are droppable. The 11-column no-
rwwidthlayout is widespread enough to count as a second canonical order. - Unescaped quotes. Files exist whose description contains a bare
"— "Rabbit holes, 20" ditch south of rwy". Strict RFC 4180 parsing throws; be lenient on the trailing free-text field. - Names are the only key. Matching is exact and case-sensitive in practice. Preserve names byte-for-byte through a round trip — do not trim, re-case or truncate a name a task might reference.
- Encoding. Legacy files are Windows-1252. A UTF-8 decode of one mangles accented site names rather than failing, so sniff for invalid sequences and fall back.
- Unknown keys and columns. Both
OptionsandObsZonecarry vendor extensions. Skip what you do not recognise; never fail the file. - Units default to metres when the suffix is absent, and may be mixed within one file. Never assume the file-wide unit from the first row.
- An empty task section is valid.
-----Related Tasks-----as the last line means "no tasks", not "malformed". - Style is clamped, not rejected. An out-of-range
stylemust be read as0(Unknown).