feat(sat): the pass table is the whole list

Asked why the satellite dropdown exists when the passes are on the right, and
the honest answer was that the table was incomplete. Three kinds of satellite
never appeared in it: QO-100, which has no pass because it never sets; one whose
elements have not arrived, which cannot be predicted at all; and one whose next
pass falls beyond the prediction window. Following a satellite and not finding it
in the list reads as OpsLog having lost it.

They are listed at the end now, each saying which of the three it is, and
clicking one selects it exactly like a pass row. The table stops being "what is
coming" and becomes "what you follow, soonest first" — which is what an operator
was scanning it for anyway.

The dropdown stays, and now for one reason rather than by default: it is the
only selector left when the readout column is hidden, which is what an operator
does when they want the map full width during a pass.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
2026-09-09 12:01:08 +02:00
co-authored by Claude Opus 5
parent b15055ba4a
commit 660b1bfa1f
3 changed files with 44 additions and 4 deletions
+4 -2
View File
@@ -13,7 +13,8 @@
"The frequency plan goes from 25 satellites to 44, cut from Celestrak, PE0SAT and the SatNOGS transponder database instead of typed by hand — the nine Tevel-2 satellites, the Chinese space station, AO-27, AO-123, RS-44 and twenty more. Twelve that had re-entered are gone, first-generation Tevel among them. Your own file is merged rather than replaced: satellites you have never seen are added, and any frequency you corrected stands.",
"A satellite is now found by its catalog number rather than by its name. \"RADFXSAT (FOX-1B)\" and \"AO-91\" are the same bird, and so are \"TIANYAN 01\" and \"TO-108\" — the second pair never met before, so TO-108 tracked nothing.",
"On the satellite tab, the mode is a coloured badge instead of a grey footnote, and an FM bird shows its CTCSS tone with the same weight as a frequency — a repeater called without its tone does not answer, and the operator hears an empty channel and concludes the satellite is not up. When there is no tone it says so, rather than leaving a blank that could mean either. The mode also appears in the transponder list and in the header, so it survives hiding the readout column.",
"New option: follow the azimuth only. A station with an ordinary rotator and no elevation motor can now track a satellite — a pass at the edge of the footprint stays between the horizon and about 15° for its whole length, and a beam covers that with its beamwidth. With it on, any rotator in the list can be chosen. What you give up is the high passes, where a satellite overhead has a bearing that means nothing, which is why it is a switch and not something OpsLog decides for you."
"New option: follow the azimuth only. A station with an ordinary rotator and no elevation motor can now track a satellite — a pass at the edge of the footprint stays between the horizon and about 15° for its whole length, and a beam covers that with its beamwidth. With it on, any rotator in the list can be chosen. What you give up is the high passes, where a satellite overhead has a bearing that means nothing, which is why it is a switch and not something OpsLog decides for you.",
"The pass table now lists every satellite you follow, not only the ones with a pass coming. QO-100 never has one because it never sets, a bird whose elements have not arrived cannot be predicted, and one whose next pass falls beyond the window is simply past the horizon of the table — all three used to look like satellites OpsLog had lost. They sit at the end, each saying which of the three it is, and clicking one selects it like any other row."
],
"fr": [
"Toutes les interfaces de rotor sont désormais dans Réglages ▸ Rotator, et la page satellite ne fait quen choisir une. EasyComm et PstRotator se configuraient dans les réglages satellite pendant que les autres se configuraient dans la liste des rotors : un même pylône était décrit deux fois. Ce que vous aviez réglé est déplacé dans la liste et sélectionné automatiquement.",
@@ -26,7 +27,8 @@
"Le plan de fréquences passe de 25 à 44 satellites, généré depuis Celestrak, PE0SAT et la base de transpondeurs SatNOGS au lieu d’être saisi à la main — les neuf Tevel-2, la station spatiale chinoise, AO-27, AO-123, RS-44 et vingt autres. Douze rentrés dans latmosphère ont été retirés, dont les Tevel de première génération. Votre fichier est fusionné et non remplacé : les satellites inconnus sont ajoutés, et vos corrections de fréquence restent.",
"Un satellite est désormais trouvé par son numéro de catalogue plutôt que par son nom. « RADFXSAT (FOX-1B) » et « AO-91 » sont le même oiseau, tout comme « TIANYAN 01 » et « TO-108 » — ces deux-là ne se rencontraient jamais, donc TO-108 ne suivait rien.",
"Sur longlet satellite, le mode est une pastille colorée au lieu dune note grise, et un satellite FM affiche sa tonalité CTCSS avec le même poids quune fréquence — un relais appelé sans sa tonalité ne répond pas, et lOM entend un canal vide et en conclut que le satellite nest pas passé. Quand il ny a pas de tonalité, cest écrit, plutôt quun blanc qui pourrait vouloir dire lun ou lautre. Le mode apparaît aussi dans la liste des transpondeurs et dans len-tête, donc il survit au masquage de la colonne de droite.",
"Nouvelle option : suivre lazimut seulement. Une station avec un rotor ordinaire et sans moteur d’élévation peut désormais suivre un satellite — un passage en bord dempreinte reste entre lhorizon et 15° environ sur toute sa durée, et une beam couvre ça avec son ouverture. Avec loption activée, nimporte quel rotor de la liste peut être choisi. Ce quon perd, ce sont les passages hauts, où un satellite au zénith a un cap qui ne veut plus rien dire — doù un réglage plutôt quun choix fait à votre place."
"Nouvelle option : suivre lazimut seulement. Une station avec un rotor ordinaire et sans moteur d’élévation peut désormais suivre un satellite — un passage en bord dempreinte reste entre lhorizon et 15° environ sur toute sa durée, et une beam couvre ça avec son ouverture. Avec loption activée, nimporte quel rotor de la liste peut être choisi. Ce quon perd, ce sont les passages hauts, où un satellite au zénith a un cap qui ne veut plus rien dire — doù un réglage plutôt quun choix fait à votre place.",
"Le tableau des passages liste désormais tous les satellites suivis, et plus seulement ceux qui ont un passage à venir. QO-100 nen a jamais puisquil ne se couche pas, un satellite dont les éléments ne sont pas arrivés ne peut pas être prédit, et celui dont le prochain passage tombe au-delà de la fenêtre est simplement hors de portée du tableau — les trois avaient lair de satellites quOpsLog avait perdus. Ils sont en fin de liste, chacun disant lequel des trois cas il est, et un clic les sélectionne comme nimporte quelle ligne."
]
},
{
+38 -2
View File
@@ -246,6 +246,19 @@ export function SatellitePanel({ myGrid }: { myGrid: string }) {
return birds.filter((b) => b.has_elements && (b.transponders?.length ?? 0) > 0);
}, [birds]);
const bird = useMemo(() => birds.find((b) => b.name === sel) ?? null, [birds, sel]);
// The satellites you follow that have NO pass in the table.
//
// They are the reason the table was not the whole list: QO-100 never has a
// pass because it never sets, a bird whose elements have not arrived cannot
// be predicted at all, and one whose next pass falls outside the prediction
// window is simply beyond it. Left out, those three looked like satellites
// OpsLog had lost — so they are listed at the end, each saying which of the
// three it is, and clicking one selects it exactly like a pass row.
const idle = useMemo(() => {
const withPass = new Set(passes.map((p) => p.name));
return shown.filter((b) => !withPass.has(b.name));
}, [shown, passes]);
const tp = bird?.transponders?.[tpIdx] ?? null;
// The mode each satellite is worked in, for the pass table's dot. Its first
// transponder: on a bird that has two, the first is the one it is known for.
@@ -881,10 +894,10 @@ export function SatellitePanel({ myGrid }: { myGrid: string }) {
{t('sat.nextPasses')}
</div>
<div className="flex-1 min-h-0 overflow-auto">
{passes.length === 0 && (
{passes.length === 0 && idle.length === 0 && (
<div className="p-2 text-[11px] text-muted-foreground">{t('sat.noPasses')}</div>
)}
{passes.length > 0 && (
{(passes.length > 0 || idle.length > 0) && (
// A real table, so the name column takes the width the longest
// name needs — "ZHUHAI-1 OVS-1A" was cut to eight characters in
// a fixed one — and the rest keeps its columns lined up under
@@ -933,6 +946,29 @@ export function SatellitePanel({ myGrid }: { myGrid: string }) {
</tr>
);
})}
{/* The rest of what you follow, so the table IS the list:
nothing you can select is missing from it. */}
{idle.map((b) => {
const why = b.geostationary ? t('sat.alwaysUp')
: !b.has_elements ? t('sat.noElements')
: t('sat.noPassWindow');
return (
<tr
key={`idle-${b.name}`}
onClick={() => setSel(b.name)}
className={cn('cursor-pointer hover:bg-accent/50 border-t border-border/40',
b.name === sel && 'bg-accent/40')}
>
<td className="px-2 py-1 whitespace-nowrap">
<span className={cn('font-medium', !b.has_elements && 'text-muted-foreground')}>{b.name}</span>
<ModeDot mode={modeOf(b.name)} />
</td>
<td colSpan={4} className="px-2 py-1 text-right text-muted-foreground whitespace-nowrap">
{why}
</td>
</tr>
);
})}
</tbody>
</table>
)}
+2
View File
@@ -606,6 +606,7 @@ const en: Dict = {
'sat.tipEl': 'Elevation', 'sat.tipAz': 'Azimuth', 'sat.tipRange': 'Distance', 'sat.tipAlt': 'Altitude',
'sat.tone': 'Tone', 'sat.toneHint': 'CTCSS on the uplink', 'sat.toneNone': 'no tone needed',
'sat.rotAzOnly': 'azimuth only',
'sat.alwaysUp': 'always up', 'sat.noPassWindow': 'no pass in the window',
'sat.tipAos': 'Rises', 'sat.tipLos': 'Sets', 'sat.tipMaxEl': 'Peak',
'sat.tipBelow': 'below the horizon', 'sat.tipNoPass': 'no pass in the prediction window',
'sat.approaching': 'approaching', 'sat.receding': 'receding', 'sat.below': 'below the horizon',
@@ -1224,6 +1225,7 @@ const fr: Dict = {
'sat.tipEl': 'Élévation', 'sat.tipAz': 'Azimut', 'sat.tipRange': 'Distance', 'sat.tipAlt': 'Altitude',
'sat.tone': 'Tonalité', 'sat.toneHint': 'CTCSS sur la montée', 'sat.toneNone': 'aucune tonalité requise',
'sat.rotAzOnly': 'azimut seul',
'sat.alwaysUp': 'toujours visible', 'sat.noPassWindow': 'aucun passage dans la fenêtre',
'sat.tipAos': 'Lever', 'sat.tipLos': 'Coucher', 'sat.tipMaxEl': 'Culmination',
'sat.tipBelow': 'sous lhorizon', 'sat.tipNoPass': 'aucun passage dans la fenêtre de prévision',
'sat.approaching': 'se rapproche', 'sat.receding': 's’éloigne', 'sat.below': 'sous lhorizon',