Premchan369 commited on
Commit
dfb8eb7
Β·
verified Β·
1 Parent(s): fc6ad07

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +267 -0
app.py ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import gradio as gr
3
+ import cv2, math, json, requests, numpy as np
4
+ from PIL import Image
5
+ from ultralytics import YOLO
6
+
7
+ MODEL = YOLO("model/best.pt")
8
+
9
+ # ── API keys β€” set in HuggingFace Space Secrets ──
10
+ K2_KEY = "REPLACE_K2_KEY"
11
+ KT_KEY = "REPLACE_KEEPTRACK_KEY"
12
+ K2_URL = "https://api.k2think.ai/v1/chat/completions"
13
+ K2_MDL = "MBZUAI-IFM/K2-Think-v2"
14
+ KT_BASE = "https://api.keeptrack.space/v4"
15
+
16
+ # ── KeepTrack helpers (verified endpoints only) ───────────────────────
17
+ def kt_get(path, timeout=15):
18
+ try:
19
+ r = requests.get(f"{KT_BASE}{path}",
20
+ headers={"X-API-Key": KT_KEY}, timeout=timeout)
21
+ r.raise_for_status()
22
+ return r.json()
23
+ except:
24
+ return None
25
+
26
+ def get_sat_lla(norad_id=25544):
27
+ r = kt_get(f"/sat/{norad_id}/lla")
28
+ return r if isinstance(r, dict) else {}
29
+
30
+ def get_sat_eci(norad_id=25544):
31
+ r = kt_get(f"/sat/{norad_id}/eci")
32
+ return r if isinstance(r, dict) else {}
33
+
34
+ def get_sat_rae(norad_id=25544, lat=28.61, lon=77.20, alt=0.2):
35
+ r = kt_get(f"/sat/{norad_id}/rae/{lat}/{lon}/{alt}")
36
+ return r if isinstance(r, dict) else {}
37
+
38
+ def get_sat_metadata(norad_id=25544):
39
+ r = kt_get(f"/sat/{norad_id}")
40
+ return r if isinstance(r, dict) else {}
41
+
42
+ def get_debris_count():
43
+ r = kt_get("/metrics/debris/count")
44
+ if isinstance(r, dict): return str(r.get("count", "N/A"))
45
+ return str(r) if r else "N/A"
46
+
47
+ def get_active_count():
48
+ r = kt_get("/metrics/active/count")
49
+ if isinstance(r, dict): return str(r.get("count", "N/A"))
50
+ return str(r) if r else "N/A"
51
+
52
+ def get_socrates():
53
+ r = kt_get("/socrates/latest", timeout=25)
54
+ if isinstance(r, list): return r[:5]
55
+ if isinstance(r, dict):
56
+ return (r.get("conjunctions") or r.get("data") or [])[:5]
57
+ return []
58
+
59
+ def parse_socrates_entry(c):
60
+ if not isinstance(c, dict): return {}
61
+ return {
62
+ "sat1Name": c.get("sat1Name") or c.get("OBJECT1_NAME") or c.get("sat1") or "?",
63
+ "sat2Name": c.get("sat2Name") or c.get("OBJECT2_NAME") or c.get("sat2") or "?",
64
+ "minRng": c.get("minRng") or c.get("MISS_DISTANCE") or c.get("missDistance") or "?",
65
+ "tcaTime": c.get("tcaTime") or c.get("TCA") or c.get("tca") or "?",
66
+ }
67
+
68
+ def extract_meta(raw):
69
+ if not raw: return {}
70
+ return {
71
+ "name": raw.get("SATNAME") or raw.get("name") or raw.get("satname") or "N/A",
72
+ "country": raw.get("COUNTRY") or raw.get("country") or "N/A",
73
+ "type": raw.get("OBJECT_TYPE") or raw.get("objectType") or "N/A",
74
+ }
75
+
76
+ def fmt(v, dp=2):
77
+ try: return f"{float(v):.{dp}f}"
78
+ except: return str(v)
79
+
80
+ # ── Zones ─────────────────────────────────────────────────────────────
81
+ ZONE_MAP = {
82
+ (0,0):"TOP-LEFT",(1,0):"TOP-CENTER",(2,0):"TOP-RIGHT",
83
+ (0,1):"MID-LEFT",(1,1):"CENTER",(2,1):"MID-RIGHT",
84
+ (0,2):"BOT-LEFT",(1,2):"BOT-CENTER",(2,2):"BOT-RIGHT",
85
+ }
86
+ ZONE_CMDS = {
87
+ "TOP-LEFT": ["MOVE RIGHT","MOVE DOWN"],
88
+ "TOP-CENTER":["MOVE DOWN","ADJUST PITCH DOWN"],
89
+ "TOP-RIGHT": ["MOVE LEFT","MOVE DOWN"],
90
+ "MID-LEFT": ["MOVE RIGHT","ADJUST THRUST VECTOR RIGHT"],
91
+ "CENTER": ["EMERGENCY REVERSE THRUST","RAPID EVASIVE MANEUVER"],
92
+ "MID-RIGHT": ["MOVE LEFT","ADJUST THRUST VECTOR LEFT"],
93
+ "BOT-LEFT": ["MOVE RIGHT","MOVE UP"],
94
+ "BOT-CENTER":["MOVE UP","ADJUST PITCH UP"],
95
+ "BOT-RIGHT": ["MOVE LEFT","MOVE UP"],
96
+ }
97
+ SPEED = {"CRITICAL":"REDUCE SPEED IMMEDIATELY","HIGH":"REDUCE SPEED",
98
+ "MEDIUM":"MODERATE SPEED","LOW":"MAINTAIN SPEED"}
99
+ COLORS_BGR = {"CRITICAL":(0,0,255),"HIGH":(0,100,255),"MEDIUM":(0,200,255),"LOW":(0,255,120)}
100
+
101
+ def zone_fn(xc,yc,fw,fh):
102
+ c=0 if xc<fw/3 else(1 if xc<2*fw/3 else 2)
103
+ r=0 if yc<fh/3 else(1 if yc<2*fh/3 else 2)
104
+ return ZONE_MAP[(c,r)]
105
+
106
+ def analyse(boxes,fw,fh):
107
+ cx,cy=fw/2,fh/2; objs=[]
108
+ for box in boxes:
109
+ x1,y1,x2,y2=box[:4]; conf=float(box[4]) if len(box)>4 else 1.0
110
+ xc,yc2=(x1+x2)/2,(y1+y2)/2
111
+ dist=math.hypot(xc-cx,yc2-cy); size=(x2-x1)*(y2-y1)
112
+ risk=(size/(dist+1))*conf
113
+ lvl="CRITICAL" if risk>800 else "HIGH" if risk>300 else "MEDIUM" if risk>80 else "LOW"
114
+ objs.append({"box":(int(x1),int(y1),int(x2),int(y2)),
115
+ "zone":zone_fn(xc,yc2,fw,fh),"risk":round(risk,3),
116
+ "level":lvl,"conf":round(conf,3),"dist":round(dist,1)})
117
+ return sorted(objs,key=lambda x:x["risk"],reverse=True)
118
+
119
+ def decide(objs):
120
+ if not objs: return {"status":"STABLE","cmds":["MAINTAIN CURRENT ORBIT"],"level":"LOW"}
121
+ t=objs[0]; lvl=t["level"]
122
+ cmds=list(ZONE_CMDS.get(t["zone"],["HOLD POSITION"]))+[SPEED[lvl]]
123
+ if lvl=="CRITICAL": cmds.insert(0,"COLLISION ALERT")
124
+ return {"status":{"CRITICAL":"EMERGENCY","HIGH":"DANGER","MEDIUM":"CAUTION","LOW":"MONITOR"}[lvl],
125
+ "cmds":cmds,"level":lvl,"target":t,"all":objs}
126
+
127
+ def k2_brief(dec, lla, eci, rae, socrates, debris_count, active_count, meta, k2_key):
128
+ if k2_key in ("REPLACE_K2_KEY",""):
129
+ return ""
130
+ soc_str = "; ".join(
131
+ f"{c.get('sat1Name','?')} -- {c.get('sat2Name','?')} miss:{c.get('minRng','?')}"
132
+ for c in socrates[:3]) or "none"
133
+ prompt=(
134
+ f"Satellite: {meta.get('name','Unknown')} ({meta.get('type','?')})\n"
135
+ f"LLA: lat={fmt(lla.get('lat','?'))}deg lon={fmt(lla.get('lon','?'))}deg "
136
+ f"alt={fmt(lla.get('alt','?'))}km\n"
137
+ f"ECI: x={eci.get('x','?')} y={eci.get('y','?')} z={eci.get('z','?')} km\n"
138
+ f"Vision status:{dec['status']} | Debris detected:{len(dec.get('all',[]))} | "
139
+ f"Zone:{dec.get('target',{}).get('zone','N/A')}\n"
140
+ f"Active sats:{active_count} | Total orbital debris:{debris_count}\n"
141
+ f"SOCRATES conjunctions: {soc_str}\n"
142
+ f"Commands:{dec['cmds']}\n"
143
+ "Write MISSION BRIEF (3 sentences) + RISK ASSESSMENT + FINAL INSTRUCTIONS (-> format). "
144
+ "Navigation only, no robotic arms."
145
+ )
146
+ try:
147
+ r=requests.post(K2_URL,
148
+ headers={"Authorization":f"Bearer {k2_key}","Content-Type":"application/json"},
149
+ json={"model":K2_MDL,"messages":[{"role":"user","content":prompt}],"stream":False},
150
+ timeout=30)
151
+ r.raise_for_status()
152
+ return r.json()["choices"][0]["message"]["content"]
153
+ except Exception as e:
154
+ return f"[K2 unavailable: {e}]"
155
+
156
+ def render(frame,objs,dec,lla,socrates):
157
+ img=frame.copy(); h,w=img.shape[:2]
158
+ for o in objs:
159
+ x1,y1,x2,y2=o["box"]; col=COLORS_BGR[o["level"]]
160
+ cv2.rectangle(img,(x1,y1),(x2,y2),col,2)
161
+ cv2.putText(img,f"{o['level']}|{o['zone']}|{o['conf']:.2f}",
162
+ (x1,max(y1-5,10)),cv2.FONT_HERSHEY_SIMPLEX,0.35,col,1)
163
+ ov=img.copy(); cv2.rectangle(ov,(0,0),(w,130),(8,8,25),-1)
164
+ img=cv2.addWeighted(ov,0.75,img,0.25,0)
165
+ cv2.putText(img,f"STATUS: {dec['status']}",(10,22),cv2.FONT_HERSHEY_SIMPLEX,0.62,(255,255,100),2)
166
+ cv2.putText(img,f"DEBRIS: {len(objs)}",(10,42),cv2.FONT_HERSHEY_SIMPLEX,0.5,(180,220,255),1)
167
+ if socrates:
168
+ cv2.putText(img,f"SOCRATES: {len(socrates)} conjunction(s)",(10,60),
169
+ cv2.FONT_HERSHEY_SIMPLEX,0.42,(0,80,255),1)
170
+ if lla:
171
+ cv2.putText(img,
172
+ f"SAT: lat={fmt(lla.get('lat','?'))}deg lon={fmt(lla.get('lon','?'))}deg alt={fmt(lla.get('alt','?'))}km",
173
+ (10,78),cv2.FONT_HERSHEY_SIMPLEX,0.38,(150,200,255),1)
174
+ y=98
175
+ for c in dec["cmds"][:3]:
176
+ cv2.putText(img,f" -> {c}",(10,y),cv2.FONT_HERSHEY_SIMPLEX,0.4,(100,255,180),1); y+=16
177
+ return img
178
+
179
+ def process(input_img, norad_id, use_k2, use_kt):
180
+ norad_id = int(str(norad_id)) if str(norad_id).isdigit() else 25544
181
+ frame = cv2.cvtColor(np.array(input_img), cv2.COLOR_RGB2BGR)
182
+ res = MODEL(frame, verbose=False)
183
+ boxes = res[0].boxes
184
+ raw = []
185
+ if boxes and len(boxes):
186
+ xy=boxes.xyxy.cpu().numpy(); cf=boxes.conf.cpu().numpy()
187
+ raw=[(*xy[i],cf[i]) for i in range(len(xy))]
188
+ objs = analyse(raw, frame.shape[1], frame.shape[0])
189
+ dec = decide(objs)
190
+
191
+ lla={}; eci={}; rae={}; meta={}
192
+ socrates=[]; debris_count="N/A"; active_count="N/A"
193
+
194
+ if use_kt and KT_KEY not in ("REPLACE_KEEPTRACK_KEY",""):
195
+ lla = get_sat_lla(norad_id)
196
+ eci = get_sat_eci(norad_id)
197
+ rae = get_sat_rae(norad_id)
198
+ meta = extract_meta(get_sat_metadata(norad_id))
199
+ debris_count = get_debris_count()
200
+ active_count = get_active_count()
201
+ socrates = [parse_socrates_entry(c) for c in get_socrates()]
202
+ # Escalate if SOCRATES has close approaches
203
+ if socrates and dec["level"] != "CRITICAL":
204
+ dec["cmds"].insert(0,"SOCRATES CONJUNCTION WARNING")
205
+
206
+ brief = k2_brief(dec,lla,eci,rae,socrates,debris_count,active_count,meta,K2_KEY) if use_k2 else ""
207
+ ann = render(frame, objs, dec, lla, socrates)
208
+ pil = Image.fromarray(cv2.cvtColor(ann, cv2.COLOR_BGR2RGB))
209
+
210
+ lines = [
211
+ f"Debris Detected: {len(objs)}",
212
+ f"Status: {dec['status']}",
213
+ f"Risk Level: {dec['level']}",
214
+ ]
215
+ if dec.get("target"):
216
+ t=dec["target"]
217
+ lines.append(f"Highest Risk: {t['zone']} [{t['level']}] conf:{t['conf']}")
218
+ if meta.get("name") and meta["name"] != "N/A":
219
+ lines.append(f"Satellite: {meta['name']} | {meta.get('country','?')} | {meta.get('type','?')}")
220
+ if lla:
221
+ lines.append(f"LLA Position: lat:{fmt(lla.get('lat','?'))}deg "
222
+ f"lon:{fmt(lla.get('lon','?'))}deg alt:{fmt(lla.get('alt','?'))}km")
223
+ if eci:
224
+ lines.append(f"ECI Position: x:{eci.get('x','?')} y:{eci.get('y','?')} z:{eci.get('z','?')} km")
225
+ if rae:
226
+ lines.append(f"Ground RAE: az:{fmt(rae.get('az','?'))}deg el:{fmt(rae.get('el','?'))}deg")
227
+ if debris_count != "N/A":
228
+ lines.append(f"Total Orbital Debris: {debris_count}")
229
+ if active_count != "N/A":
230
+ lines.append(f"Active Satellites: {active_count}")
231
+ if socrates:
232
+ lines.append(f"SOCRATES Alerts: {len(socrates)} conjunction(s)")
233
+ for s in socrates[:2]:
234
+ lines.append(f" -> {s.get('sat1Name','?')} <-> {s.get('sat2Name','?')} miss:{s.get('minRng','?')}km")
235
+ lines += ["", "Navigation Instructions:"]
236
+ for c in dec["cmds"]:
237
+ lines.append(f" -> {c}")
238
+ if brief:
239
+ lines += ["", "=== K2 Think V2 Mission Brief ===", brief]
240
+ return pil, "\n".join(lines)
241
+
242
+ with gr.Blocks(title="Satellite Debris Avoidance", theme=gr.themes.Monochrome()) as demo:
243
+ gr.Markdown("# Satellite Autonomous Debris Avoidance System v2.1")
244
+ gr.Markdown(
245
+ "YOLOv8m vision + KeepTrack API (LLA, ECI, RAE, SOCRATES, metrics) + K2 Think V2 AI\n\n"
246
+ "**Output:** navigation commands only β€” MOVE LEFT/RIGHT/UP/DOWN, ADJUST THRUST, REDUCE SPEED"
247
+ )
248
+ with gr.Row():
249
+ with gr.Column(scale=1):
250
+ inp = gr.Image(type="pil", label="Input Frame")
251
+ norad = gr.Textbox(value="25544", label="NORAD ID")
252
+ use_k2 = gr.Checkbox(label="Use K2 Think V2 AI Brief", value=False)
253
+ use_kt = gr.Checkbox(label="Use KeepTrack API (live orbital data)", value=True)
254
+ btn = gr.Button("Analyse Frame", variant="primary")
255
+ with gr.Column(scale=1):
256
+ out_img = gr.Image(label="Annotated Output")
257
+ out_txt = gr.Textbox(label="Navigation Decision + Orbital Data", lines=22)
258
+ btn.click(process, inputs=[inp,norad,use_k2,use_kt], outputs=[out_img,out_txt])
259
+ gr.Markdown("""
260
+ ### KeepTrack Endpoints Used (verified working)
261
+ `/sat/{id}/lla` Β· `/sat/{id}/eci` Β· `/sat/{id}/rae/{lat}/{lon}/{alt}` Β· `/sat/{id}` Β·
262
+ `/socrates/latest` Β· `/metrics/debris/count` Β· `/metrics/active/count`
263
+ ### CelesTrak Debris Groups
264
+ Cosmos-1408 Β· Fengyun-1C Β· Iridium-33 Β· Cosmos-2251 Β· Active satellites
265
+ """)
266
+
267
+ demo.launch()