p2p/coe backend implemented

This commit is contained in:
2026-06-16 15:07:59 -04:00
parent 60fd4810ff
commit 89e6968dc4
29 changed files with 7906 additions and 88 deletions
+6
View File
@@ -0,0 +1,6 @@
/backend/.venv
/backend/__pycache__
/backend/_run_sched_test.py
IMPLEMENTATIONPLAN.md
scheduler.db
scheduler.db*
+144 -88
View File
@@ -1,68 +1,71 @@
# Scheduler
A test scheduler for the NJTH which compiles a schedule according to the rules and priority of the tests.
A test scheduler for the NJTH which compiles a best schedule according to the rules and priority of the tests. Moniters the test results and if there is failed or invalid tests, it calculates estimated time for rerun and reschedules the tests according to user's requests.
## Inputs
- Target test directory, test results directories
- Estimated runtime for each type of test for calculations
- Top priority tests
- Allow user to manually override and enter tests that needs to be run tonight
- Day time testing (Y/N)
- if yes, enter how many hours
- Target tests csv
- Keys: Priority, Index, Interferer, COE Pair , Rotation, TC ID, Victim Band, 6GHz Power Mode, 5G Test Point, 5G Channel, 5G Bandwidth, 5G RSSI, 5G Direction, 5G STA, 6G Test Point, 6G Channel, 6G Bandwidth, 6G RSSI ,6G Direction, 6G STA, 2G Test Point, 2G Channel, 2G Bandwidth, 2G RSSI, 2G Direction, 2G STA
| TestID | Type | ROT | COE_Pairs | RX_TX | Priority | Scheduled_on | Completed |
| ------ | ---- | --- | --------- | ----- |--------- | ------------ | --------- |
| P2PRXAX001 | P2P | ROT1 | | RX | 3 | | 0 |
| P2PRXAX004 | P2P | ROT1 | [COERXAX001, COERXAX002] | AX | 2 | | 0 |
| P2PTXAX001 | P2P | ROT1 | | TX | 3 | | 0 |
| COERXAX001 | COE | ROT1 | | RX | 3 | | 0 |
| COERXAX002 | COE | ROT1 | | RX | 3 | | 0 |
| P2PRXAX014 | P2P | ROT2 | | RX | 3 | | 0 |
| P2PTXAX014 | P2P | ROT2 | | TX | 3 | | 0 |
- DUT test result directory path, REF test result directory path
- Estimated runtime for each type of test for calculations
- Rotation definition
- Test exclusions
- Top priority tests
- Allow user to manually override and enter tests that needs to be run tonight
- Day time testing
## Output
### Backend
- Compile a schedule that would require the lowest amount of time in total according to the following rules and priority:
- **Rules**
- Two tests' overlapping stations are located at the same test points, they can be run in the same night
- i.e. when two tests have the same rotation
- P2PRXAX001, P2PRXAX004, P2PTXAX001, COERXAX001, COERXAX002 can be run on the same night;
- P2PRXAX014, P2PTXAX014 can be run on the same night
- Up link down link p2p should run b2b (RX and TX)
- Ex. P2PRXAX001 and P2PTXAX001 should run b2b
- COE P2P bundle should run b2b
- Ex. P2PRXAX001 and COERXAX001 and COERXAX002 should run b2b
- Can be run in the same night
- Same rotation and same test point P2P/COE tests
- COERXBE002: T1F -> STA63, T2A -> STA56, T3E -> STA4, R3
- COERXBE003: T1F -> STA63, T2A -> STA56, T3E -> STA4, R3
- Different rotations but all stations are at same testpoints
- R2 P2PRXAC003 5G T1C -> STA4
- R1 P2PRXAC012 2G T1C -> STA4
- Same rotation and same test point P3P tests
- Needs to be run back to back
- P2P up link (RX) and down link (TX)
- Ex. P2PRXAX001 and P2PTXAX001
- COE P2P bundle
- DUT and REF tests
- **Priority**
- 1: highest priority (run tonight)
- 5: lowest priority
- General priority: COE P2P pair -> P2P only -> P3P
- Set priority of each test according to the type when first read in the tests P2P with COE pairing -> 2, P2P only -> 3, and P3P -> 4
- Change priority to 1 for manually entered top priority tests, meaning it will be run tonight
- Default priority: COE P2P pair -> P2P only -> P3P
- 1: highest priority (must run tonight)
- 2: COE/P2P pairs
- 3: P2P only
- 4: P3P
- 5: lowest priority
- Priority 1 and 5 needs to be entered manually
- Same test points and same rotation will be run first
- Same test points but different will run if there is still remaining testing time for the day
- **Calculation**
- Use the estimate run time for each type of test to fit as many tests as possible in a shift of testing. One shift at night on weekdays is 16hrs, on weekends 24 hours with 1hr margin
- Use the estimate run time for each type of test to fit as many tests as possible in a shift of testing.
- Weekdays are 16hrs, weekends are 24 hours with 1hr margin
- If day time testing for today is on, today will be counted as 24 hours
- Holidays will be counted as 24 hours
- Estimate run time
- P2P: 85 mins
- COE: 115 mins
- P3P: 105 mins
- If day time testing for today is yes,
- **Failed test management**
- After there's a modification to the result directories (results are in), and there are tests that are scheduled for last night but were not added to the result directories, meaning the test failed/invalid
- Estimate how much time for rerun:
- If user choose to rerun for next day, move the whole schedule down by a day
- If user choose to rerun during the day, schedule unchanged
### Frontend
### Frontend Components
- **Calendar**
| | Sun | Mon | Tue | Wed | Thu | Fri | Sat |
| - | --- | --- | --- | --- | --- | --- | --- |
| S1 | TestId | TestId | TestId | TestId | TestId | TestId | TestId |
| S2 | TestId | TestId | TestId | TestId | TestId | TestId | TestId |
| S3 | TestId | TestId | TestId | TestId | TestId | TestId | TestId |
- List the order that the tests should be conducted after compiling
- Display the schedule that the tests should be conducted after compiling
- Split one day into 3 eight-hour shift
- Shift 1 is daytime, on weekdays don't include unless day time testing for today is on. On weekends it is included
- Shift 2 and 3 are night time
- Shift 1: 1AM - 9AM
- Shift 2: 9AM - 5PM
- On weekdays don't include unless day time testing is on. On weekends it is included
- Shifi 3: 5PM - 1AM
- Hover over each tests to see test details: type, rotation
- Display one week at a time, arrows to go to prev/next week
- Today will be hightlighted or boxed
@@ -75,68 +78,121 @@ A test scheduler for the NJTH which compiles a schedule according to the rules a
- Day time testing for today: Yes or No
- If yes, include shift 1 in todays testing
- Top priority tests
- Enter a list of comma separated TestIDs to make those test priority 1
- **Test Config for tonight**
- After the schedule is compiled, show where each station should be placed at what test point
- Enter a list of comma separated TestIDs to make those tests priority 1
- Lowest priority tests
- Enter a list of comma separated TestIDs to make those tests priority 5
- **Estimated Completion Date**
- **Settings**
- Estimated run time for each type of test
- Paths to target and result directories
- Rotation definition
- Let user define rotation rules, two different fields, one for P2P/COE tests, one for P3P tests, then display the rules with two tables Ex:
- P2P/COE
| Rotation | 5G | 6G | 2G |
|----------|----|----|----|
| ROT1 | STA56 | STA63 | STA4 |
| ROT2 | STA4 | STA56 | STA63 |
| ROT3 | STA63 | | STA56 |
- P3P
| Rotation | 5G | 6G | 2G |
|----------|----|----|----|
| ROT1 | STA56/58/59 | STA63/64/65 | STA4/5/6 |
| ROT2 | STA4/5/6 | STA56/58/59 | STA63/64/65 |
| ROT3 | STA63/64/65 | | STA56/58/59 |
- Test exclusion
- User can enter rules to exclude tests that will not be run
- Manual priority override
- Holidays
- User can enter holidays and that day will be counted as a weekend (24hrs)
- **Failed Tests**
- Display a banner with:
- Display a red banner with:
- A message saying what tests failed last night and requires a rerun
- Estimate rerun time
- Yes/No button for rerun during the day
- If yes, display today's shift 1 with the rerun tests
- If no, the rerun tests will be ran tonight, the whole schedule is shift down by one day, update the calendar
#### Layout
### Frontend Layout
```
┌──────────────────────────────────────────────────────────────┐
| CGW453 Scheduler
|────────────────────────────────────┐ COMPLETION DATE
| | |
| | CALENDAR |
| | |
| | |
|
|
|
|
|
| CGW453 Scheduler [⚙ Settings] |
|──────────────────────────────────────────────────────────────|
│ ⚠ Tests[____] failed last night — est time required to |
│ rerun: [_____] Rerun these tests during the day today? │
| [Yes/No] |
├──────────────────────────────────────────────────────────────┤
| Jun 7th - June 13th < > |
| ┌────────────────────────────────────┐ |
| | SUN MON TUE WED THU FRI SAT | COMPLETION DATE |
| |────────────────────────────────────| Day time [Y/N] |
| | | Top priority:[] |
| |────────────────────────────────────| Lowest Priority:[] |
| | | |
| |────────────────────────────────────| |
| | | [Remake Schedule] |
| └────────────────────────────────────┘ |
└──────────────────────────────────────────────────────────────┘
```
## Implementation
### Backend
- Python, FastAPI, SQLite for storage
- graph.py
- graph
- Node: test
- Edge: compatible
- Adjacency list
- **Graph will not be modified**
- active_tests (set)
- Keep track of tests that are not completed
- Remove test from active_tests when test completed or is excluded
- priorities (dict)
- Keep track of test's priority
- Methods
- build_graph()
- compatible(test1, test2) -> bool
- all overlapping stations are at the same test point
- Degree(t): ``len(graph[t] & active_tests)``
- Base node selection: priority -> degree
``
base = min(
active_tests, key=lambda t: (priority[t], -len(graph[t] & active_tests)))
``
Scheduler.py implementation
Graph:
- Only built once, don't exclude any tests, implement using adjacency list
dut_active_priority, ref_active_priority:
- 2 dicts, one for active DUT tests one for active reference tests, dict {testId: priority, testId: priority, ...}
- Default priorities
- P2P with COE pairs: 2
- P2P only, COE tests: 3
- P3P tests: 4
- Remove tests when they are completed
- When result directories are modified
- Rebuild full dict when exclusion rules are modified
- When user updates exclusion rules in settings(frontend)
### Frontend
- React
Methods:
build_test_bundles():
- Create bundles of tests that needs to be run back to back
- Types of test bundles and their order
1. Failed tests rerun if exist
2. The base is P2P and has COE pairs: P2P on DUT -> COE pairings on DUT -> P2P on REF -> COE pairings on REF
3. P2P tests without COE pairs: P2PRX on DUT -> P2PTX on DUT -> P2PRX on REF -> P2PTX on REF
4. P3P tests (ALL ROT's)
```
test_bundles: {
base: base test
bundled_tests: tests that are bundled to the base test
total_time: total time of all tests base + bundled_tests
priority: priority of base test
}
```
- sort test_bundles first with priority then with least total_time
create_schedule():
- General rules:
- Monday to Thursday, starts on shift 3 of that day (5pm-1am), fill in test bundles untill shift 3 and shift 1 of next day(1am-9am) is filled.
- use an array of shifts: [3,1]
- total 16hr +1 hr margin
- tests needs to fit into 17hrs
- All tests bundles needs to be compatible
- Check compatibility between current test bundle base and the first test bundle base
- Friday, starts on shift 3 of Friday, fill in test bundles untill shift 1 of Monday
- use an array of shifts: [3,1,2,3,1,2,3,1]
- total 64hr + 1hr margin
- tests need to fit into 65 hours
- All tests bundles needs to be compatible
- Check compatibility between current test bundle base and the first test bundle base
- For frontend to easily fetch schedule and display it, create a Schedule structured like:
```
Schedule: {
date: {
Shift1: list[ScheduleTest] or []
Shift2: list[ScheduleTest] or []
Shift3: list[ScheduleTest] or []
},
date: {
Shift1: list[ScheduleTest] or []
Shift2: list[ScheduleTest] or []
Shift3: list[ScheduleTest] or []
}, ...
}
```
+2
View File
@@ -0,0 +1,2 @@
DUT="CGW453"
REF="CGW452"
+182
View File
@@ -0,0 +1,182 @@
from pathlib import Path
from typing import Any
from datetime import date, datetime
import os
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
import db
import graph
from parser import CsvValidationError, parse_target_csv
from scheduler import SchedulerTest, compile_schedule, remove_from_active, reset_scheduler_state
APP_ROOT = Path(__file__).resolve().parent
DB_PATH = APP_ROOT / "scheduler.db"
DUT = os.getenv("DUT", "CGW453").strip()
REF = os.getenv("REF", "CGW452").strip()
app = FastAPI(title="Scheduler API", version="0.1.0")
class LoadTestsRequest(BaseModel):
csv_path: str = Field(..., description="Absolute or backend-relative path to target CSV")
class SaveSettingsRequest(BaseModel):
settings: dict[str, Any]
class CompileScheduleRequest(BaseModel):
start_date: str | None = Field(default=None, description="YYYY-MM-DD")
rule: str = ""
daytime_testing_today: bool = False
top_priority_tests: list[str] = Field(default_factory=list)
lowest_priority_tests: list[str] = Field(default_factory=list)
class RemoveActiveTestsRequest(BaseModel):
test_ids: list[str] = Field(default_factory=list)
@app.on_event("startup")
def on_startup() -> None:
db.init_db(DB_PATH)
graph.reset_graph_state()
@app.get("/api/health")
def health() -> dict[str, str]:
return {"status": "ok"}
@app.post("/api/settings")
def save_settings(request: SaveSettingsRequest) -> dict[str, str]:
db.save_settings(request.settings, DB_PATH)
return {"status": "saved"}
@app.get("/api/settings")
def get_settings() -> dict[str, Any]:
return db.read_settings(DB_PATH)
@app.post("/api/tests/load")
def load_tests(request: LoadTestsRequest) -> dict[str, Any]:
csv_path = Path(request.csv_path)
if not csv_path.is_absolute():
csv_path = APP_ROOT / csv_path
try:
parsed = parse_target_csv(csv_path)
except FileNotFoundError as exc:
raise HTTPException(status_code=404, detail=str(exc)) from exc
except CsvValidationError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
count = db.upsert_tests(parsed.tests, DB_PATH)
reset_scheduler_state()
graph.reset_graph_state()
all_dut_tests = db.list_tests_for_device(DUT, DB_PATH)
graph.build_graph_once(all_dut_tests)
return {
"loaded_tests": count,
"warnings": parsed.warnings,
"dut_graph_nodes": len(graph.get_graph()),
}
@app.post("/api/schedule/active/remove")
def remove_active_tests(request: RemoveActiveTestsRequest) -> dict[str, Any]:
remove_from_active({item.strip() for item in request.test_ids if item.strip()})
return {"status": "ok", "removed": len(request.test_ids)}
@app.post("/api/schedule/compile")
def compile_schedule_endpoint(request: CompileScheduleRequest) -> dict[str, Any]:
if request.start_date:
try:
datetime.strptime(request.start_date, "%Y-%m-%d")
except ValueError as exc:
raise HTTPException(status_code=400, detail="start_date must be YYYY-MM-DD") from exc
stored_tests = db.list_schedulable_tests(DB_PATH, rule=request.rule)
if not stored_tests:
version = db.create_schedule_version([], DB_PATH)
return {
"schedule_version": version,
"scheduled_tests": 0,
"completion_date": None,
}
scheduler_tests = [
SchedulerTest(
test_id=t.test_id,
device=t.device,
test_type=t.test_type,
rotation=t.rotation,
rx_tx=t.rx_tx,
has_coe_pair=t.has_coe_pair,
coe_pairing=t.coe_pairing or [],
config=t.config,
estimated_minutes=t.estimated_minutes,
priority=t.priority,
raw_payload=t.raw_payload or {},
)
for t in stored_tests
]
holiday_dates = db.list_holidays(DB_PATH)
try:
entries, completion_date = compile_schedule(
tests=scheduler_tests,
start_date=request.start_date,
holiday_dates=holiday_dates,
top_priority_tests={item.strip() for item in request.top_priority_tests if item.strip()},
lowest_priority_tests={item.strip() for item in request.lowest_priority_tests if item.strip()},
daytime_testing_today=request.daytime_testing_today,
)
except RuntimeError as exc:
raise HTTPException(status_code=409, detail=str(exc)) from exc
version = db.create_schedule_version(
[(e.test_id, e.device, e.scheduled_date, e.shift_index, e.sequence_in_shift) for e in entries],
DB_PATH,
)
return {
"schedule_version": version,
"scheduled_tests": len(entries),
"completion_date": completion_date,
}
@app.get("/api/schedule/week")
def get_schedule_week(start: str | None = None) -> dict[str, Any]:
week_start = start or date.today().isoformat()
try:
datetime.strptime(week_start, "%Y-%m-%d")
except ValueError as exc:
raise HTTPException(status_code=400, detail="start must be YYYY-MM-DD") from exc
rows = db.get_schedule_week(week_start, DB_PATH)
return {
"start_date": week_start,
"items": [
{
"test_id": row.test_id,
"device": row.device,
"scheduled_date": row.scheduled_date,
"shift_index": row.shift_index,
"sequence_in_shift": row.sequence_in_shift,
"test_type": row.test_type,
"rotation": row.rotation,
"status": row.status,
"priority": row.priority,
"estimated_minutes": row.estimated_minutes,
}
for row in rows
],
}
+251
View File
@@ -0,0 +1,251 @@
Priority,Index,Interferer,COE Pair ,Rotation,TC ID,Victim Band,6GHz Power Mode,5G Test Point,5G Channel,5G Bandwidth,5G RSSI,5G Direction,5G STA,6G Test Point,6G Channel,6G Bandwidth,6G RSSI,6G Direction,6G STA,2G Test Point,2G Channel,2G Bandwidth,2G RSSI,2G Direction,2G STA
P2,3,Yes,Yes,R2,COERXAC001,5G,LPI,T1F,100,80,-76,UL,STA4,T2A,5,160,OFF,DL,STA63,T3E,6,20,-70,DL,STA56
P2,6,Yes,Yes,R2,COERXAC002,5G,LPI,T1F,161,80,-76,UL,STA4,T2A,133,160,OFF,DL,STA63,T3E,6,20,-70,DL,STA56
P2,15,Yes,Yes,R1,COERXAC003,2G,LPI,T1F,36,80,-76,DL,STA56,T2A,5,160,OFF,DL,STA63,T3E,1,20,-70,UL,STA4
P2,17,Yes,Yes,R1,COERXAC004,2G,LPI,T1F,36,80,-76,DL,STA56,T2A,133,160,OFF,DL,STA63,T3E,6,20,-70,UL,STA4
P2,19,Yes,Yes,R1,COERXAC005,2G,LPI,T1F,36,80,-76,DL,STA56,T2A,197,160,OFF,DL,STA63,T3E,11,20,-70,UL,STA4
P2,3,Yes,Yes,R1,COERXAX001,5G,LPI,T1F,100,80,-76,UL,STA56,T2A,5,160,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,4,Yes,Yes,R1,COERXAX002,5G,LPI,T1F,100,80,-76,UL,STA56,T2A,133,160,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,6,Yes,Yes,R1,COERXAX003,5G,LPI,T1F,161,80,-76,UL,STA56,T2A,5,160,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,7,Yes,Yes,R1,COERXAX004,5G,LPI,T1F,161,80,-76,UL,STA56,T2A,133,160,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,8,Yes,Yes,R1,COERXAX005,5G,SP,T1F,161,80,-76,UL,STA56,T2A,5,320,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,9,Yes,Yes,R1,COERXAX006,5G,LPI,T1F,161,80,-76,UL,STA56,T2A,133,320,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,11,Yes,Yes,R1,COERXAX007,5G,SP,T1F,100,160,-76,UL,STA56,T2A,5,320,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,12,Yes,Yes,R1,COERXAX008,5G,LPI,T1F,100,160,-76,UL,STA56,T2A,133,320,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,14,Yes,Yes,R2,COERXAX009,6G,LPI,T1F,161,80,-76,DL,STA63,T2A,5,160,-79,UL,STA56,T3E,6,20,-70,DL,STA4
P2,16,Yes,Yes,R2,COERXAX010,6G,LPI,T1F,161,80,-76,DL,STA63,T2A,133,160,-79,UL,STA56,T3E,6,20,-70,DL,STA4
P2,18,Yes,Yes,R2,COERXAX011,6G,LPI,T1F,161,80,-76,DL,STA63,T2A,197,160,-79,UL,STA56,T3E,6,20,-70,DL,STA4
P2,20,Yes,Yes,R2,COERXAX012,6G,SP,T1F,161,80,-76,DL,STA63,T2A,5,320,-79,UL,STA56,T3E,6,20,-70,DL,STA4
P2,22,Yes,Yes,R2,COERXAX013,6G,LPI,T1F,161,80,-76,DL,STA63,T2A,133,320,-79,UL,STA56,T3E,6,20,-70,DL,STA4
P2,24,Yes,Yes,R3,COERXAX014,2G,LPI,T1F,36,80,-76,DL,STA4,T2A,5,160,-79,DL,STA63,T3E,1,20,-70,UL,STA56
P2,26,Yes,Yes,R3,COERXAX015,2G,LPI,T1F,36,80,-76,DL,STA4,T2A,133,160,-79,DL,STA63,T3E,6,20,-70,UL,STA56
P2,28,Yes,Yes,R3,COERXAX016,2G,LPI,T1F,36,80,-76,DL,STA4,T2A,197,160,-79,DL,STA63,T3E,11,20,-70,UL,STA56
P2,3,Yes,Yes,R3,COERXBE001,5G,LPI,T1F,100,80,-76,UL,STA63,T2A,5,160,-79,DL,STA56,T3E,6,20,-70,DL,STA4
P2,4,Yes,Yes,R3,COERXBE002,5G,LPI,T1F,100,80,-76,UL,STA63,T2A,133,160,-79,DL,STA56,T3E,6,20,-70,DL,STA4
P2,6,Yes,Yes,R3,COERXBE003,5G,LPI,T1F,161,80,-76,UL,STA63,T2A,5,160,-79,DL,STA56,T3E,6,20,-70,DL,STA4
P2,7,Yes,Yes,R3,COERXBE004,5G,LPI,T1F,161,80,-76,UL,STA63,T2A,133,160,-79,DL,STA56,T3E,6,20,-70,DL,STA4
P2,8,Yes,Yes,R3,COERXBE005,5G,SP,T1F,161,80,-76,UL,STA63,T2A,5,320,-79,DL,STA64,T3E,6,20,-70,DL,STA4
P2,9,Yes,Yes,R3,COERXBE006,5G,LPI,T1F,161,80,-76,UL,STA63,T2A,133,320,-79,DL,STA64,T3E,6,20,-70,DL,STA4
P2,11,Yes,Yes,R3,COERXBE007,5G,SP,T1F,100,160,-76,UL,STA63,T2A,5,320,-79,DL,STA64,T3E,6,20,-70,DL,STA4
P2,12,Yes,Yes,R3,COERXBE008,5G,LPI,T1F,100,160,-76,UL,STA63,T2A,133,320,-79,DL,STA64,T3E,6,20,-70,DL,STA4
P2,14,Yes,Yes,R1,COERXBE009,6G,LPI,T1F,161,80,-76,DL,STA56,T2A,5,160,-79,UL,STA63,T3E,6,20,-70,DL,STA4
P2,16,Yes,Yes,R1,COERXBE010,6G,LPI,T1F,161,80,-76,DL,STA56,T2A,133,160,-79,UL,STA63,T3E,6,20,-70,DL,STA4
P2,18,Yes,Yes,R1,COERXBE011,6G,LPI,T1F,161,80,-76,DL,STA56,T2A,197,160,-79,UL,STA63,T3E,6,20,-70,DL,STA4
P2,20,Yes,Yes,R1,COERXBE012,6G,SP,T1F,161,80,-76,DL,STA56,T2A,5,320,-79,UL,STA63,T3E,6,20,-70,DL,STA4
P2,22,Yes,Yes,R1,COERXBE013,6G,LPI,T1F,161,80,-76,DL,STA56,T2A,133,320,-79,UL,STA63,T3E,6,20,-70,DL,STA4
P2,24,Yes,Yes,R2,COERXBE014,2G,LPI,T1F,36,80,-76,DL,STA4,T2A,5,160,-79,DL,STA56,T3E,1,20,-70,UL,STA63
P2,26,Yes,Yes,R2,COERXBE015,2G,LPI,T1F,36,80,-76,DL,STA4,T2A,133,160,-79,DL,STA56,T3E,6,20,-70,UL,STA63
P2,28,Yes,Yes,R2,COERXBE016,2G,LPI,T1F,36,80,-76,DL,STA4,T2A,197,160,-79,DL,STA56,T3E,11,20,-70,UL,STA63
P2,3,Yes,Yes,R2,COETXAC001,5G,LPI,T1F,100,80,-76,DL,STA4,T1K2A,5,160,OFF,DL,STA63,T3E,6,20,-70,DL,STA56
P2,6,Yes,Yes,R2,COETXAC002,5G,LPI,T1F,161,80,-76,DL,STA4,T1K2A,133,160,OFF,DL,STA63,T3E,6,20,-70,DL,STA56
P2,15,Yes,Yes,R1,COETXAC003,2G,LPI,T1F,36,80,-76,DL,STA56,T1K2A,5,160,OFF,DL,STA63,T3E,1,20,-70,DL,STA4
P2,17,Yes,Yes,R1,COETXAC004,2G,LPI,T1F,36,80,-76,DL,STA56,T1K2A,133,160,OFF,DL,STA63,T3E,6,20,-70,DL,STA4
P2,19,Yes,Yes,R1,COETXAC005,2G,LPI,T1F,36,80,-76,DL,STA56,T1K2A,5,320,OFF,DL,STA63,T3E,11,20,-70,DL,STA4
P2,3,Yes,Yes,R1,COETXAX001,5G,LPI,T1F,100,80,-76,DL,STA56,T2A,5,160,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,4,Yes,Yes,R1,COETXAX002,5G,LPI,T1F,100,80,-76,DL,STA56,T2A,133,160,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,6,Yes,Yes,R1,COETXAX003,5G,LPI,T1F,161,80,-76,DL,STA56,T2A,5,160,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,7,Yes,Yes,R1,COETXAX004,5G,LPI,T1F,161,80,-76,DL,STA56,T2A,133,160,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,9,Yes,Yes,R1,COETXAX005,5G,SP,T1F,100,160,-76,DL,STA56,T2A,5,320,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,10,Yes,Yes,R1,COETXAX006,5G,LPI,T1F,100,160,-76,DL,STA56,T2A,133,320,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,12,Yes,Yes,R2,COETXAX007,6G,LPI,T1F,161,80,-76,DL,STA63,T2A,5,160,-79,DL,STA56,T3E,6,20,-70,DL,STA4
P2,14,Yes,Yes,R2,COETXAX008,6G,LPI,T1F,161,80,-76,DL,STA63,T2A,133,160,-79,DL,STA56,T3E,6,20,-70,DL,STA4
P2,16,Yes,Yes,R2,COETXAX009,6G,LPI,T1F,161,80,-76,DL,STA63,T2A,197,160,-79,DL,STA56,T3E,6,20,-70,DL,STA4
P2,18,Yes,Yes,R2,COETXAX010,6G,SP,T1F,161,80,-76,DL,STA63,T2A,5,320,-79,DL,STA56,T3E,6,20,-70,DL,STA4
P2,20,Yes,Yes,R2,COETXAX011,6G,LPI,T1F,161,80,-76,DL,STA63,T2A,133,320,-79,DL,STA56,T3E,6,20,-70,DL,STA4
P2,22,Yes,Yes,R3,COETXAX012,2G,LPI,T1F,36,80,-76,DL,STA56,T2A,5,160,-79,DL,STA63,T3E,1,20,-70,DL,STA56
P2,24,Yes,Yes,R3,COETXAX013,2G,LPI,T1F,36,80,-76,DL,STA56,T2A,133,160,-79,DL,STA63,T3E,6,20,-70,DL,STA56
P2,26,Yes,Yes,R3,COETXAX014,2G,LPI,T1F,36,80,-76,DL,STA56,T2A,197,160,-79,DL,STA63,T3E,11,20,-70,DL,STA56
P2,3,Yes,Yes,R3,COETXBE001,5G,LPI,T1F,100,80,-76,DL,STA63,T2A,5,160,-79,DL,STA56,T3E,6,20,-70,DL,STA4
P2,4,Yes,Yes,R3,COETXBE002,5G,LPI,T1F,100,80,-76,DL,STA63,T2A,133,160,-79,DL,STA56,T3E,6,20,-70,DL,STA4
P2,6,Yes,Yes,R3,COETXBE003,5G,LPI,T1F,161,80,-76,DL,STA63,T2A,5,160,-79,DL,STA56,T3E,6,20,-70,DL,STA4
P2,7,Yes,Yes,R3,COETXBE004,5G,LPI,T1F,161,80,-76,DL,STA63,T2A,133,160,-79,DL,STA56,T3E,6,20,-70,DL,STA4
P2,9,Yes,Yes,R3,COETXBE005,5G,SP,T1F,100,160,-76,DL,STA63,T2A,5,320,-79,DL,STA64,T3E,6,20,-70,DL,STA4
P2,10,Yes,Yes,R3,COETXBE006,5G,LPI,T1F,100,160,-76,DL,STA63,T2A,133,320,-79,DL,STA64,T3E,6,20,-70,DL,STA4
P2,12,Yes,Yes,R1,COETXBE007,6G,LPI,T1F,161,80,-76,DL,STA56,T2A,5,160,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,14,Yes,Yes,R1,COETXBE008,6G,LPI,T1F,161,80,-76,DL,STA56,T2A,133,160,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,16,Yes,Yes,R1,COETXBE009,6G,LPI,T1F,161,80,-76,DL,STA56,T2A,197,160,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,18,Yes,Yes,R1,COETXBE010,6G,SP,T1F,161,80,-76,DL,STA56,T2A,5,320,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,20,Yes,Yes,R1,COETXBE011,6G,LPI,T1F,161,80,-76,DL,STA56,T2A,133,320,-79,DL,STA63,T3E,6,20,-70,DL,STA4
P2,22,Yes,Yes,R2,COETXBE012,2G,LPI,T1F,36,80,-76,DL,STA4,T2A,5,160,-79,DL,STA56,T3E,1,20,-70,DL,STA63
P2,24,Yes,Yes,R2,COETXBE013,2G,LPI,T1F,36,80,-76,DL,STA4,T2A,133,160,-79,DL,STA56,T3E,6,20,-70,DL,STA63
P2,26,Yes,Yes,R2,COETXBE014,2G,LPI,T1F,36,80,-76,DL,STA4,T2A,197,160,-79,DL,STA56,T3E,11,20,-70,DL,STA63
P1,1,No,No,R2,P2PRXAC001,5G,LPI,T1F,36,80,-76,UL,STA4,,,,,,,,,,,,
P1,1,No,No,R2,P2PRXAC002,5G,LPI,T2B,36,80,-67,UL,STA4,,,,,,,,,,,,
P1,1,No,No,R2,P2PRXAC003,5G,LPI,T1C,36,80,-45,UL,STA4,,,,,,,,,,,,
P1,2,No,Yes,R2,P2PRXAC004,5G,LPI,T1F,100,80,-76,UL,STA4,,,,,,,,,,,,
P1,2,No,No,R2,P2PRXAC005,5G,LPI,T2B,100,80,-67,UL,STA4,,,,,,,,,,,,
P1,2,No,No,R2,P2PRXAC006,5G,LPI,T1C,100,80,-45,UL,STA4,,,,,,,,,,,,
P1,5,No,Yes,R2,P2PRXAC007,5G,LPI,T1F,161,80,-76,UL,STA4,,,,,,,,,,,,
P1,5,No,No,R2,P2PRXAC008,5G,LPI,T2B,161,80,-67,UL,STA4,,,,,,,,,,,,
P1,5,No,No,R2,P2PRXAC009,5G,LPI,T1C,161,80,-45,UL,STA4,,,,,,,,,,,,
P1,14,No,Yes,R1,P2PRXAC010,2G,LPI,,,,,,,,,,,,,T3E,1,20,-70,UL,STA4
P1,14,No,No,R1,P2PRXAC011,2G,LPI,,,,,,,,,,,,,T1D,1,20,-60,UL,STA4
P1,14,No,No,R1,P2PRXAC012,2G,LPI,,,,,,,,,,,,,T1C,1,20,-45,UL,STA4
P1,16,No,Yes,R1,P2PRXAC013,2G,LPI,,,,,,,,,,,,,T3E,6,20,-70,UL,STA4
P1,16,No,No,R1,P2PRXAC014,2G,LPI,,,,,,,,,,,,,T1D,6,20,-60,UL,STA4
P1,16,No,No,R1,P2PRXAC015,2G,LPI,,,,,,,,,,,,,T1C,6,20,-45,UL,STA4
P1,18,No,Yes,R1,P2PRXAC016,2G,LPI,,,,,,,,,,,,,T3E,11,20,-70,UL,STA4
P1,18,No,No,R1,P2PRXAC017,2G,LPI,,,,,,,,,,,,,T1D,11,20,-60,UL,STA4
P1,18,No,No,R1,P2PRXAC018,2G,LPI,,,,,,,,,,,,,T1C,11,20,-45,UL,STA4
P1,1,No,No,R1,P2PRXAX001,5G,LPI,T1F,36,80,-76,UL,STA56,,,,,,,,,,,,
P1,1,No,No,R1,P2PRXAX002,5G,LPI,T2B,36,80,-67,UL,STA56,,,,,,,,,,,,
P1,1,No,No,R1,P2PRXAX003,5G,LPI,T1C,36,80,-45,UL,STA56,,,,,,,,,,,,
P1,2,No,Yes,R1,P2PRXAX004,5G,LPI,T1F,100,80,-76,UL,STA56,,,,,,,,,,,,
P1,2,No,No,R1,P2PRXAX005,5G,LPI,T2B,100,80,-67,UL,STA56,,,,,,,,,,,,
P1,2,No,No,R1,P2PRXAX006,5G,LPI,T1C,100,80,-45,UL,STA56,,,,,,,,,,,,
P1,5,No,Yes,R1,P2PRXAX007,5G,LPI,T1F,161,80,-76,UL,STA56,,,,,,,,,,,,
P1,5,No,No,R1,P2PRXAX008,5G,LPI,T2B,161,80,-67,UL,STA56,,,,,,,,,,,,
P1,5,No,No,R1,P2PRXAX009,5G,LPI,T1C,161,80,-45,UL,STA56,,,,,,,,,,,,
P1,10,No,Yes,R1,P2PRXAX010,5G,LPI,T1F,100,160,-76,UL,STA56,,,,,,,,,,,,
P1,10,No,No,R1,P2PRXAX011,5G,LPI,T2B,100,160,-67,UL,STA56,,,,,,,,,,,,
P1,10,No,No,R1,P2PRXAX012,5G,LPI,T1C,100,160,-45,UL,STA56,,,,,,,,,,,,
P1,13,No,Yes,R2,P2PRXAX013,6G,LPI,,,,,,,T2A,5,160,-79,UL,STA56,,,,,,
P1,13,No,No,R2,P2PRXAX014,6G,LPI,,,,,,,T2B,5,160,-70,UL,STA56,,,,,,
P1,13,No,No,R2,P2PRXAX015,6G,LPI,,,,,,,T1C,5,160,-45,UL,STA56,,,,,,
P1,15,No,Yes,R2,P2PRXAX016,6G,LPI,,,,,,,T2A,133,160,-79,UL,STA56,,,,,,
P1,15,No,No,R2,P2PRXAX017,6G,LPI,,,,,,,T2B,133,160,-70,UL,STA56,,,,,,
P1,15,No,No,R2,P2PRXAX018,6G,LPI,,,,,,,T1C,133,160,-45,UL,STA63,,,,,,
P1,17,No,Yes,R2,P2PRXAX019,6G,LPI,,,,,,,T2A,197,160,-79,UL,STA56,,,,,,
P1,17,No,No,R2,P2PRXAX020,6G,LPI,,,,,,,T2B,197,160,-70,UL,STA56,,,,,,
P1,17,No,No,R2,P2PRXAX021,6G,LPI,,,,,,,T1C,197,160,-45,UL,STA56,,,,,,
P1,19,No,Yes,R2,P2PRXAX022,6G,SP,,,,,,,T2A,5,320,-79,UL,STA56,,,,,,
P1,19,No,No,R2,P2PRXAX023,6G,SP,,,,,,,T2B,5,320,-70,UL,STA56,,,,,,
P1,19,No,No,R2,P2PRXAX024,6G,SP,,,,,,,T1C,5,320,-45,UL,STA56,,,,,,
P1,21,No,Yes,R2,P2PRXAX025,6G,LPI,,,,,,,T2A,133,320,-79,UL,STA56,,,,,,
P1,21,No,No,R2,P2PRXAX026,6G,LPI,,,,,,,T2B,133,320,-70,UL,STA56,,,,,,
P1,21,No,No,R2,P2PRXAX027,6G,LPI,,,,,,,T1C,133,320,-45,UL,STA56,,,,,,
P1,23,No,Yes,R3,P2PRXAX028,2G,LPI,,,,,,,,,,,,,T3E,1,20,-70,UL,STA56
P1,23,No,No,R3,P2PRXAX029,2G,LPI,,,,,,,,,,,,,T1D,1,20,-60,UL,STA56
P1,23,No,No,R3,P2PRXAX030,2G,LPI,,,,,,,,,,,,,T1C,1,20,-45,UL,STA56
P1,25,No,Yes,R3,P2PRXAX031,2G,LPI,,,,,,,,,,,,,T3E,6,20,-70,UL,STA56
P1,25,No,No,R3,P2PRXAX032,2G,LPI,,,,,,,,,,,,,T1D,6,20,-60,UL,STA56
P1,25,No,No,R3,P2PRXAX033,2G,LPI,,,,,,,,,,,,,T1C,6,20,-45,UL,STA56
P1,27,No,Yes,R3,P2PRXAX034,2G,LPI,,,,,,,,,,,,,T3E,11,20,-70,UL,STA56
P1,27,No,No,R3,P2PRXAX035,2G,LPI,,,,,,,,,,,,,T1D,11,20,-60,UL,STA56
P1,27,No,No,R3,P2PRXAX036,2G,LPI,,,,,,,,,,,,,T1C,11,20,-45,UL,STA56
P1,1,No,No,R3,P2PRXBE001,5G,LPI,T1F,36,80,-76,UL,STA63,,,,,,,,,,,,
P1,1,No,No,R3,P2PRXBE002,5G,LPI,T2B,36,80,-67,UL,STA63,,,,,,,,,,,,
P1,1,No,No,R3,P2PRXBE003,5G,LPI,T1C,36,80,-45,UL,STA63,,,,,,,,,,,,
P1,2,No,Yes,R3,P2PRXBE004,5G,LPI,T1F,100,80,-76,UL,STA63,,,,,,,,,,,,
P1,2,No,No,R3,P2PRXBE005,5G,LPI,T2B,100,80,-67,UL,STA63,,,,,,,,,,,,
P1,2,No,No,R3,P2PRXBE006,5G,LPI,T1C,100,80,-45,UL,STA63,,,,,,,,,,,,
P1,5,No,Yes,R3,P2PRXBE007,5G,LPI,T1F,161,80,-76,UL,STA63,,,,,,,,,,,,
P1,5,No,No,R3,P2PRXBE008,5G,LPI,T2B,161,80,-67,UL,STA63,,,,,,,,,,,,
P1,5,No,No,R3,P2PRXBE009,5G,LPI,T1C,161,80,-45,UL,STA63,,,,,,,,,,,,
P1,10,No,Yes,R3,P2PRXBE010,5G,LPI,T1F,100,160,-76,UL,STA63,,,,,,,,,,,,
P1,10,No,No,R3,P2PRXBE011,5G,LPI,T2B,100,160,-67,UL,STA63,,,,,,,,,,,,
P1,10,No,No,R3,P2PRXBE012,5G,LPI,T1C,100,160,-45,UL,STA63,,,,,,,,,,,,
P1,13,No,Yes,R1,P2PRXBE013,6G,LPI,,,,,,,T2A,5,160,-79,UL,STA63,,,,,,
P1,13,No,No,R1,P2PRXBE014,6G,LPI,,,,,,,T2B,5,160,-70,UL,STA63,,,,,,
P1,13,No,No,R1,P2PRXBE015,6G,LPI,,,,,,,T1C,5,160,-45,UL,STA63,,,,,,
P1,15,No,Yes,R1,P2PRXBE016,6G,LPI,,,,,,,T2A,133,160,-79,UL,STA63,,,,,,
P1,15,No,No,R1,P2PRXBE017,6G,LPI,,,,,,,T2B,133,160,-70,UL,STA63,,,,,,
P1,15,No,No,R1,P2PRXBE018,6G,LPI,,,,,,,T1C,133,160,-45,UL,STA63,,,,,,
P1,17,No,Yes,R1,P2PRXBE019,6G,LPI,,,,,,,T2A,197,160,-79,UL,STA63,,,,,,
P1,17,No,No,R1,P2PRXBE020,6G,LPI,,,,,,,T2B,197,160,-70,UL,STA63,,,,,,
P1,17,No,No,R1,P2PRXBE021,6G,LPI,,,,,,,T1C,197,160,-45,UL,STA63,,,,,,
P1,19,No,Yes,R1,P2PRXBE022,6G,SP,,,,,,,T2A,5,320,-79,UL,STA63,,,,,,
P1,19,No,No,R1,P2PRXBE023,6G,SP,,,,,,,T2B,5,320,-70,UL,STA63,,,,,,
P1,19,No,No,R1,P2PRXBE024,6G,SP,,,,,,,T1C,5,320,-45,UL,STA63,,,,,,
P1,21,No,Yes,R1,P2PRXBE025,6G,LPI,,,,,,,T2A,133,320,-79,UL,STA63,,,,,,
P1,21,No,No,R1,P2PRXBE026,6G,LPI,,,,,,,T2B,133,320,-70,UL,STA63,,,,,,
P1,21,No,No,R1,P2PRXBE027,6G,LPI,,,,,,,T1C,133,320,-45,UL,STA63,,,,,,
P1,23,No,Yes,R2,P2PRXBE028,2G,LPI,,,,,,,,,,,,,T3E,1,20,-70,UL,STA63
P1,23,No,No,R2,P2PRXBE029,2G,LPI,,,,,,,,,,,,,T1D,1,20,-60,UL,STA63
P1,23,No,No,R2,P2PRXBE030,2G,LPI,,,,,,,,,,,,,T1C,1,20,-45,UL,STA63
P1,25,No,Yes,R2,P2PRXBE031,2G,LPI,,,,,,,,,,,,,T3E,6,20,-70,UL,STA63
P1,25,No,No,R2,P2PRXBE032,2G,LPI,,,,,,,,,,,,,T1D,6,20,-60,UL,STA63
P1,25,No,No,R2,P2PRXBE033,2G,LPI,,,,,,,,,,,,,T1C,6,20,-45,UL,STA63
P1,27,No,Yes,R2,P2PRXBE034,2G,LPI,,,,,,,,,,,,,T3E,11,20,-70,UL,STA63
P1,27,No,No,R2,P2PRXBE035,2G,LPI,,,,,,,,,,,,,T1D,11,20,-60,UL,STA63
P1,27,No,No,R2,P2PRXBE036,2G,LPI,,,,,,,,,,,,,T1C,11,20,-45,UL,STA63
P1,1,No,No,R2,P2PTXAC001,5G,LPI,T1F,36,80,-76,DL,STA4,,,,,,,,,,,,
P1,1,No,No,R2,P2PTXAC002,5G,LPI,T2B,36,80,-67,DL,STA4,,,,,,,,,,,,
P1,1,No,No,R2,P2PTXAC003,5G,LPI,T1C,36,80,-45,DL,STA4,,,,,,,,,,,,
P1,2,No,Yes,R2,P2PTXAC004,5G,LPI,T1F,100,80,-76,DL,STA4,,,,,,,,,,,,
P1,2,No,No,R2,P2PTXAC005,5G,LPI,T2B,100,80,-67,DL,STA4,,,,,,,,,,,,
P1,2,No,No,R2,P2PTXAC006,5G,LPI,T1C,100,80,-45,DL,STA4,,,,,,,,,,,,
P1,5,No,Yes,R2,P2PTXAC007,5G,LPI,T1F,161,80,-76,DL,STA4,,,,,,,,,,,,
P1,5,No,No,R2,P2PTXAC008,5G,LPI,T2B,161,80,-67,DL,STA4,,,,,,,,,,,,
P1,5,No,No,R2,P2PTXAC009,5G,LPI,T1C,161,80,-45,DL,STA4,,,,,,,,,,,,
P1,14,No,Yes,R1,P2PTXAC010,2G,LPI,,,,,,,,,,,,,T3E,1,20,-70,DL,STA4
P1,14,No,No,R1,P2PTXAC011,2G,LPI,,,,,,,,,,,,,T1D,1,20,-60,DL,STA4
P1,14,No,No,R1,P2PTXAC012,2G,LPI,,,,,,,,,,,,,T1C,1,20,-45,DL,STA4
P1,16,No,Yes,R1,P2PTXAC013,2G,LPI,,,,,,,,,,,,,T3E,6,20,-70,DL,STA4
P1,16,No,No,R1,P2PTXAC014,2G,LPI,,,,,,,,,,,,,T1D,6,20,-60,DL,STA4
P1,16,No,No,R1,P2PTXAC015,2G,LPI,,,,,,,,,,,,,T1C,6,20,-45,DL,STA4
P1,18,No,Yes,R1,P2PTXAC016,2G,LPI,,,,,,,,,,,,,T3E,11,20,-70,DL,STA4
P1,18,No,No,R1,P2PTXAC017,2G,LPI,,,,,,,,,,,,,T1D,11,20,-60,DL,STA4
P1,18,No,No,R1,P2PTXAC018,2G,LPI,,,,,,,,,,,,,T1C,11,20,-45,DL,STA4
P1,1,No,No,R1,P2PTXAX001,5G,LPI,T1F,36,80,-76,DL,STA56,,,,,,,,,,,,
P1,1,No,No,R1,P2PTXAX002,5G,LPI,T2B,36,80,-67,DL,STA56,,,,,,,,,,,,
P1,1,No,No,R1,P2PTXAX003,5G,LPI,T1C,36,80,-45,DL,STA56,,,,,,,,,,,,
P1,2,No,Yes,R1,P2PTXAX004,5G,LPI,T1F,100,80,-76,DL,STA56,,,,,,,,,,,,
P1,2,No,No,R1,P2PTXAX005,5G,LPI,T2B,100,80,-67,DL,STA56,,,,,,,,,,,,
P1,2,No,No,R1,P2PTXAX006,5G,LPI,T1C,100,80,-45,DL,STA56,,,,,,,,,,,,
P1,5,No,Yes,R1,P2PTXAX007,5G,LPI,T1F,161,80,-76,DL,STA56,,,,,,,,,,,,
P1,5,No,No,R1,P2PTXAX008,5G,LPI,T2B,161,80,-67,DL,STA56,,,,,,,,,,,,
P1,5,No,No,R1,P2PTXAX009,5G,LPI,T1C,161,80,-45,DL,STA56,,,,,,,,,,,,
P1,8,No,Yes,R1,P2PTXAX010,5G,LPI,T1F,100,160,-76,DL,STA56,,,,,,,,,,,,
P1,8,No,No,R1,P2PTXAX011,5G,LPI,T2B,100,160,-67,DL,STA56,,,,,,,,,,,,
P1,8,No,No,R1,P2PTXAX012,5G,LPI,T1C,100,160,-45,DL,STA56,,,,,,,,,,,,
P1,11,No,Yes,R2,P2PTXAX013,6G,LPI,,,,,,,T2A,5,160,-79,DL,STA56,,,,,,
P1,11,No,No,R2,P2PTXAX014,6G,LPI,,,,,,,T2B,5,160,-70,DL,STA56,,,,,,
P1,11,No,No,R2,P2PTXAX015,6G,LPI,,,,,,,T1C,5,160,-45,DL,STA56,,,,,,
P1,13,No,Yes,R2,P2PTXAX016,6G,LPI,,,,,,,T2A,133,160,-79,DL,STA56,,,,,,
P1,13,No,No,R2,P2PTXAX017,6G,LPI,,,,,,,T2B,133,160,-70,DL,STA56,,,,,,
P1,13,No,No,R2,P2PTXAX018,6G,LPI,,,,,,,T1C,133,160,-45,DL,STA56,,,,,,
P1,15,No,Yes,R2,P2PTXAX019,6G,LPI,,,,,,,T2A,197,160,-79,DL,STA56,,,,,,
P1,15,No,No,R2,P2PTXAX020,6G,LPI,,,,,,,T2B,197,160,-70,DL,STA56,,,,,,
P1,15,No,No,R2,P2PTXAX021,6G,LPI,,,,,,,T1C,197,160,-45,DL,STA56,,,,,,
P1,17,No,Yes,R2,P2PTXAX022,6G,SP,,,,,,,T2A,5,320,-79,DL,STA56,,,,,,
P1,17,No,No,R2,P2PTXAX023,6G,SP,,,,,,,T2B,5,320,-70,DL,STA56,,,,,,
P1,17,No,No,R2,P2PTXAX024,6G,SP,,,,,,,T1C,5,320,-45,DL,STA56,,,,,,
P1,19,No,Yes,R2,P2PTXAX025,6G,LPI,,,,,,,T2A,133,320,-79,DL,STA56,,,,,,
P1,19,No,No,R2,P2PTXAX026,6G,LPI,,,,,,,T2B,133,320,-70,DL,STA56,,,,,,
P1,19,No,No,R2,P2PTXAX027,6G,LPI,,,,,,,T1C,133,320,-45,DL,STA56,,,,,,
P1,21,No,Yes,R3,P2PTXAX028,2G,LPI,,,,,,,,,,,,,T3E,1,20,-70,UL,STA56
P1,21,No,No,R3,P2PTXAX029,2G,LPI,,,,,,,,,,,,,T1D,1,20,-60,UL,STA56
P1,21,No,No,R3,P2PTXAX030,2G,LPI,,,,,,,,,,,,,T1C,1,20,-45,UL,STA56
P1,23,No,Yes,R3,P2PTXAX031,2G,LPI,,,,,,,,,,,,,T3E,6,20,-70,UL,STA56
P1,23,No,No,R3,P2PTXAX032,2G,LPI,,,,,,,,,,,,,T1D,6,20,-60,UL,STA56
P1,23,No,No,R3,P2PTXAX033,2G,LPI,,,,,,,,,,,,,T1C,6,20,-45,UL,STA56
P1,25,No,Yes,R3,P2PTXAX034,2G,LPI,,,,,,,,,,,,,T3E,11,20,-70,UL,STA56
P1,25,No,No,R3,P2PTXAX035,2G,LPI,,,,,,,,,,,,,T1D,11,20,-60,UL,STA56
P1,25,No,No,R3,P2PTXAX036,2G,LPI,,,,,,,,,,,,,T1C,11,20,-45,UL,STA56
P1,1,No,No,R3,P2PTXBE001,5G,LPI,T1F,36,80,-76,DL,STA63,,,,,,,,,,,,
P1,1,No,No,R3,P2PTXBE002,5G,LPI,T2B,36,80,-67,DL,STA63,,,,,,,,,,,,
P1,1,No,No,R3,P2PTXBE003,5G,LPI,T1C,36,80,-45,DL,STA63,,,,,,,,,,,,
P1,2,No,Yes,R3,P2PTXBE004,5G,LPI,T1F,100,80,-76,DL,STA63,,,,,,,,,,,,
P1,2,No,No,R3,P2PTXBE005,5G,LPI,T2B,100,80,-67,DL,STA63,,,,,,,,,,,,
P1,2,No,No,R3,P2PTXBE006,5G,LPI,T1C,100,80,-45,DL,STA63,,,,,,,,,,,,
P1,5,No,Yes,R3,P2PTXBE007,5G,LPI,T1F,161,80,-76,DL,STA63,,,,,,,,,,,,
P1,5,No,No,R3,P2PTXBE008,5G,LPI,T2B,161,80,-67,DL,STA63,,,,,,,,,,,,
P1,5,No,No,R3,P2PTXBE009,5G,LPI,T1C,161,80,-45,DL,STA63,,,,,,,,,,,,
P1,8,No,Yes,R3,P2PTXBE010,5G,LPI,T1F,100,160,-76,DL,STA63,,,,,,,,,,,,
P1,8,No,No,R3,P2PTXBE011,5G,LPI,T2B,100,160,-67,DL,STA63,,,,,,,,,,,,
P1,8,No,No,R3,P2PTXBE012,5G,LPI,T1C,100,160,-45,DL,STA63,,,,,,,,,,,,
P1,11,No,Yes,R3,P2PTXBE013,6G,LPI,T1F,5,160,-79,DL,STA63,,,,,,,,,,,,
P1,11,No,No,R1,P2PTXBE014,6G,LPI,T2B,5,160,-70,DL,STA63,,,,,,,,,,,,
P1,11,No,No,R1,P2PTXBE015,6G,LPI,T1C,5,160,-45,DL,STA63,,,,,,,,,,,,
P1,13,No,Yes,R1,P2PTXBE016,6G,LPI,T1F,133,160,-79,DL,STA63,,,,,,,,,,,,
P1,13,No,No,R1,P2PTXBE017,6G,LPI,T2B,133,160,-70,DL,STA63,,,,,,,,,,,,
P1,13,No,No,R1,P2PTXBE018,6G,LPI,T1C,133,160,-45,DL,STA63,,,,,,,,,,,,
P1,15,No,Yes,R1,P2PTXBE019,6G,LPI,T1F,197,160,-79,DL,STA63,,,,,,,,,,,,
P1,15,No,No,R1,P2PTXBE020,6G,LPI,T2B,197,160,-70,DL,STA63,,,,,,,,,,,,
P1,15,No,No,R1,P2PTXBE021,6G,LPI,T1C,197,160,-45,DL,STA63,,,,,,,,,,,,
P1,17,No,Yes,R1,P2PTXBE022,6G,SP,T1F,5,320,-79,DL,STA63,,,,,,,,,,,,
P1,17,No,No,R1,P2PTXBE023,6G,SP,T2B,5,320,-70,DL,STA63,,,,,,,,,,,,
P1,17,No,No,R1,P2PTXBE024,6G,SP,T1C,5,320,-45,DL,STA63,,,,,,,,,,,,
P1,19,No,Yes,R1,P2PTXBE025,6G,LPI,T1F,133,320,-79,DL,STA63,,,,,,,,,,,,
P1,19,No,No,R1,P2PTXBE026,6G,LPI,T2B,133,320,-70,DL,STA63,,,,,,,,,,,,
P1,19,No,No,R1,P2PTXBE027,6G,LPI,T1C,133,320,-45,DL,STA63,,,,,,,,,,,,
P1,21,No,Yes,R2,P2PTXBE028,2G,LPI,T3E,1,20,-70,UL,STA63,,,,,,,,,,,,
P1,21,No,No,R2,P2PTXBE029,2G,LPI,T1D,1,20,-60,UL,STA63,,,,,,,,,,,,
P1,21,No,No,R2,P2PTXBE030,2G,LPI,T1C,1,20,-45,UL,STA63,,,,,,,,,,,,
P1,23,No,Yes,R2,P2PTXBE031,2G,LPI,T3E,6,20,-70,UL,STA63,,,,,,,,,,,,
P1,23,No,No,R2,P2PTXBE032,2G,LPI,T1D,6,20,-60,UL,STA63,,,,,,,,,,,,
P1,23,No,No,R2,P2PTXBE033,2G,LPI,T1C,6,20,-45,UL,STA63,,,,,,,,,,,,
P1,25,No,Yes,R2,P2PTXBE034,2G,LPI,T3E,11,20,-70,UL,STA63,,,,,,,,,,,,
P1,25,No,No,R2,P2PTXBE035,2G,LPI,T1D,11,20,-60,UL,STA63,,,,,,,,,,,,
P1,25,No,No,R2,P2PTXBE036,2G,LPI,T1C,11,20,-45,UL,STA63,,,,,,,,,,,,
1 Priority Index Interferer COE Pair Rotation TC ID Victim Band 6GHz Power Mode 5G Test Point 5G Channel 5G Bandwidth 5G RSSI 5G Direction 5G STA 6G Test Point 6G Channel 6G Bandwidth 6G RSSI 6G Direction 6G STA 2G Test Point 2G Channel 2G Bandwidth 2G RSSI 2G Direction 2G STA
2 P2 3 Yes Yes R2 COERXAC001 5G LPI T1F 100 80 -76 UL STA4 T2A 5 160 OFF DL STA63 T3E 6 20 -70 DL STA56
3 P2 6 Yes Yes R2 COERXAC002 5G LPI T1F 161 80 -76 UL STA4 T2A 133 160 OFF DL STA63 T3E 6 20 -70 DL STA56
4 P2 15 Yes Yes R1 COERXAC003 2G LPI T1F 36 80 -76 DL STA56 T2A 5 160 OFF DL STA63 T3E 1 20 -70 UL STA4
5 P2 17 Yes Yes R1 COERXAC004 2G LPI T1F 36 80 -76 DL STA56 T2A 133 160 OFF DL STA63 T3E 6 20 -70 UL STA4
6 P2 19 Yes Yes R1 COERXAC005 2G LPI T1F 36 80 -76 DL STA56 T2A 197 160 OFF DL STA63 T3E 11 20 -70 UL STA4
7 P2 3 Yes Yes R1 COERXAX001 5G LPI T1F 100 80 -76 UL STA56 T2A 5 160 -79 DL STA63 T3E 6 20 -70 DL STA4
8 P2 4 Yes Yes R1 COERXAX002 5G LPI T1F 100 80 -76 UL STA56 T2A 133 160 -79 DL STA63 T3E 6 20 -70 DL STA4
9 P2 6 Yes Yes R1 COERXAX003 5G LPI T1F 161 80 -76 UL STA56 T2A 5 160 -79 DL STA63 T3E 6 20 -70 DL STA4
10 P2 7 Yes Yes R1 COERXAX004 5G LPI T1F 161 80 -76 UL STA56 T2A 133 160 -79 DL STA63 T3E 6 20 -70 DL STA4
11 P2 8 Yes Yes R1 COERXAX005 5G SP T1F 161 80 -76 UL STA56 T2A 5 320 -79 DL STA63 T3E 6 20 -70 DL STA4
12 P2 9 Yes Yes R1 COERXAX006 5G LPI T1F 161 80 -76 UL STA56 T2A 133 320 -79 DL STA63 T3E 6 20 -70 DL STA4
13 P2 11 Yes Yes R1 COERXAX007 5G SP T1F 100 160 -76 UL STA56 T2A 5 320 -79 DL STA63 T3E 6 20 -70 DL STA4
14 P2 12 Yes Yes R1 COERXAX008 5G LPI T1F 100 160 -76 UL STA56 T2A 133 320 -79 DL STA63 T3E 6 20 -70 DL STA4
15 P2 14 Yes Yes R2 COERXAX009 6G LPI T1F 161 80 -76 DL STA63 T2A 5 160 -79 UL STA56 T3E 6 20 -70 DL STA4
16 P2 16 Yes Yes R2 COERXAX010 6G LPI T1F 161 80 -76 DL STA63 T2A 133 160 -79 UL STA56 T3E 6 20 -70 DL STA4
17 P2 18 Yes Yes R2 COERXAX011 6G LPI T1F 161 80 -76 DL STA63 T2A 197 160 -79 UL STA56 T3E 6 20 -70 DL STA4
18 P2 20 Yes Yes R2 COERXAX012 6G SP T1F 161 80 -76 DL STA63 T2A 5 320 -79 UL STA56 T3E 6 20 -70 DL STA4
19 P2 22 Yes Yes R2 COERXAX013 6G LPI T1F 161 80 -76 DL STA63 T2A 133 320 -79 UL STA56 T3E 6 20 -70 DL STA4
20 P2 24 Yes Yes R3 COERXAX014 2G LPI T1F 36 80 -76 DL STA4 T2A 5 160 -79 DL STA63 T3E 1 20 -70 UL STA56
21 P2 26 Yes Yes R3 COERXAX015 2G LPI T1F 36 80 -76 DL STA4 T2A 133 160 -79 DL STA63 T3E 6 20 -70 UL STA56
22 P2 28 Yes Yes R3 COERXAX016 2G LPI T1F 36 80 -76 DL STA4 T2A 197 160 -79 DL STA63 T3E 11 20 -70 UL STA56
23 P2 3 Yes Yes R3 COERXBE001 5G LPI T1F 100 80 -76 UL STA63 T2A 5 160 -79 DL STA56 T3E 6 20 -70 DL STA4
24 P2 4 Yes Yes R3 COERXBE002 5G LPI T1F 100 80 -76 UL STA63 T2A 133 160 -79 DL STA56 T3E 6 20 -70 DL STA4
25 P2 6 Yes Yes R3 COERXBE003 5G LPI T1F 161 80 -76 UL STA63 T2A 5 160 -79 DL STA56 T3E 6 20 -70 DL STA4
26 P2 7 Yes Yes R3 COERXBE004 5G LPI T1F 161 80 -76 UL STA63 T2A 133 160 -79 DL STA56 T3E 6 20 -70 DL STA4
27 P2 8 Yes Yes R3 COERXBE005 5G SP T1F 161 80 -76 UL STA63 T2A 5 320 -79 DL STA64 T3E 6 20 -70 DL STA4
28 P2 9 Yes Yes R3 COERXBE006 5G LPI T1F 161 80 -76 UL STA63 T2A 133 320 -79 DL STA64 T3E 6 20 -70 DL STA4
29 P2 11 Yes Yes R3 COERXBE007 5G SP T1F 100 160 -76 UL STA63 T2A 5 320 -79 DL STA64 T3E 6 20 -70 DL STA4
30 P2 12 Yes Yes R3 COERXBE008 5G LPI T1F 100 160 -76 UL STA63 T2A 133 320 -79 DL STA64 T3E 6 20 -70 DL STA4
31 P2 14 Yes Yes R1 COERXBE009 6G LPI T1F 161 80 -76 DL STA56 T2A 5 160 -79 UL STA63 T3E 6 20 -70 DL STA4
32 P2 16 Yes Yes R1 COERXBE010 6G LPI T1F 161 80 -76 DL STA56 T2A 133 160 -79 UL STA63 T3E 6 20 -70 DL STA4
33 P2 18 Yes Yes R1 COERXBE011 6G LPI T1F 161 80 -76 DL STA56 T2A 197 160 -79 UL STA63 T3E 6 20 -70 DL STA4
34 P2 20 Yes Yes R1 COERXBE012 6G SP T1F 161 80 -76 DL STA56 T2A 5 320 -79 UL STA63 T3E 6 20 -70 DL STA4
35 P2 22 Yes Yes R1 COERXBE013 6G LPI T1F 161 80 -76 DL STA56 T2A 133 320 -79 UL STA63 T3E 6 20 -70 DL STA4
36 P2 24 Yes Yes R2 COERXBE014 2G LPI T1F 36 80 -76 DL STA4 T2A 5 160 -79 DL STA56 T3E 1 20 -70 UL STA63
37 P2 26 Yes Yes R2 COERXBE015 2G LPI T1F 36 80 -76 DL STA4 T2A 133 160 -79 DL STA56 T3E 6 20 -70 UL STA63
38 P2 28 Yes Yes R2 COERXBE016 2G LPI T1F 36 80 -76 DL STA4 T2A 197 160 -79 DL STA56 T3E 11 20 -70 UL STA63
39 P2 3 Yes Yes R2 COETXAC001 5G LPI T1F 100 80 -76 DL STA4 T1K2A 5 160 OFF DL STA63 T3E 6 20 -70 DL STA56
40 P2 6 Yes Yes R2 COETXAC002 5G LPI T1F 161 80 -76 DL STA4 T1K2A 133 160 OFF DL STA63 T3E 6 20 -70 DL STA56
41 P2 15 Yes Yes R1 COETXAC003 2G LPI T1F 36 80 -76 DL STA56 T1K2A 5 160 OFF DL STA63 T3E 1 20 -70 DL STA4
42 P2 17 Yes Yes R1 COETXAC004 2G LPI T1F 36 80 -76 DL STA56 T1K2A 133 160 OFF DL STA63 T3E 6 20 -70 DL STA4
43 P2 19 Yes Yes R1 COETXAC005 2G LPI T1F 36 80 -76 DL STA56 T1K2A 5 320 OFF DL STA63 T3E 11 20 -70 DL STA4
44 P2 3 Yes Yes R1 COETXAX001 5G LPI T1F 100 80 -76 DL STA56 T2A 5 160 -79 DL STA63 T3E 6 20 -70 DL STA4
45 P2 4 Yes Yes R1 COETXAX002 5G LPI T1F 100 80 -76 DL STA56 T2A 133 160 -79 DL STA63 T3E 6 20 -70 DL STA4
46 P2 6 Yes Yes R1 COETXAX003 5G LPI T1F 161 80 -76 DL STA56 T2A 5 160 -79 DL STA63 T3E 6 20 -70 DL STA4
47 P2 7 Yes Yes R1 COETXAX004 5G LPI T1F 161 80 -76 DL STA56 T2A 133 160 -79 DL STA63 T3E 6 20 -70 DL STA4
48 P2 9 Yes Yes R1 COETXAX005 5G SP T1F 100 160 -76 DL STA56 T2A 5 320 -79 DL STA63 T3E 6 20 -70 DL STA4
49 P2 10 Yes Yes R1 COETXAX006 5G LPI T1F 100 160 -76 DL STA56 T2A 133 320 -79 DL STA63 T3E 6 20 -70 DL STA4
50 P2 12 Yes Yes R2 COETXAX007 6G LPI T1F 161 80 -76 DL STA63 T2A 5 160 -79 DL STA56 T3E 6 20 -70 DL STA4
51 P2 14 Yes Yes R2 COETXAX008 6G LPI T1F 161 80 -76 DL STA63 T2A 133 160 -79 DL STA56 T3E 6 20 -70 DL STA4
52 P2 16 Yes Yes R2 COETXAX009 6G LPI T1F 161 80 -76 DL STA63 T2A 197 160 -79 DL STA56 T3E 6 20 -70 DL STA4
53 P2 18 Yes Yes R2 COETXAX010 6G SP T1F 161 80 -76 DL STA63 T2A 5 320 -79 DL STA56 T3E 6 20 -70 DL STA4
54 P2 20 Yes Yes R2 COETXAX011 6G LPI T1F 161 80 -76 DL STA63 T2A 133 320 -79 DL STA56 T3E 6 20 -70 DL STA4
55 P2 22 Yes Yes R3 COETXAX012 2G LPI T1F 36 80 -76 DL STA56 T2A 5 160 -79 DL STA63 T3E 1 20 -70 DL STA56
56 P2 24 Yes Yes R3 COETXAX013 2G LPI T1F 36 80 -76 DL STA56 T2A 133 160 -79 DL STA63 T3E 6 20 -70 DL STA56
57 P2 26 Yes Yes R3 COETXAX014 2G LPI T1F 36 80 -76 DL STA56 T2A 197 160 -79 DL STA63 T3E 11 20 -70 DL STA56
58 P2 3 Yes Yes R3 COETXBE001 5G LPI T1F 100 80 -76 DL STA63 T2A 5 160 -79 DL STA56 T3E 6 20 -70 DL STA4
59 P2 4 Yes Yes R3 COETXBE002 5G LPI T1F 100 80 -76 DL STA63 T2A 133 160 -79 DL STA56 T3E 6 20 -70 DL STA4
60 P2 6 Yes Yes R3 COETXBE003 5G LPI T1F 161 80 -76 DL STA63 T2A 5 160 -79 DL STA56 T3E 6 20 -70 DL STA4
61 P2 7 Yes Yes R3 COETXBE004 5G LPI T1F 161 80 -76 DL STA63 T2A 133 160 -79 DL STA56 T3E 6 20 -70 DL STA4
62 P2 9 Yes Yes R3 COETXBE005 5G SP T1F 100 160 -76 DL STA63 T2A 5 320 -79 DL STA64 T3E 6 20 -70 DL STA4
63 P2 10 Yes Yes R3 COETXBE006 5G LPI T1F 100 160 -76 DL STA63 T2A 133 320 -79 DL STA64 T3E 6 20 -70 DL STA4
64 P2 12 Yes Yes R1 COETXBE007 6G LPI T1F 161 80 -76 DL STA56 T2A 5 160 -79 DL STA63 T3E 6 20 -70 DL STA4
65 P2 14 Yes Yes R1 COETXBE008 6G LPI T1F 161 80 -76 DL STA56 T2A 133 160 -79 DL STA63 T3E 6 20 -70 DL STA4
66 P2 16 Yes Yes R1 COETXBE009 6G LPI T1F 161 80 -76 DL STA56 T2A 197 160 -79 DL STA63 T3E 6 20 -70 DL STA4
67 P2 18 Yes Yes R1 COETXBE010 6G SP T1F 161 80 -76 DL STA56 T2A 5 320 -79 DL STA63 T3E 6 20 -70 DL STA4
68 P2 20 Yes Yes R1 COETXBE011 6G LPI T1F 161 80 -76 DL STA56 T2A 133 320 -79 DL STA63 T3E 6 20 -70 DL STA4
69 P2 22 Yes Yes R2 COETXBE012 2G LPI T1F 36 80 -76 DL STA4 T2A 5 160 -79 DL STA56 T3E 1 20 -70 DL STA63
70 P2 24 Yes Yes R2 COETXBE013 2G LPI T1F 36 80 -76 DL STA4 T2A 133 160 -79 DL STA56 T3E 6 20 -70 DL STA63
71 P2 26 Yes Yes R2 COETXBE014 2G LPI T1F 36 80 -76 DL STA4 T2A 197 160 -79 DL STA56 T3E 11 20 -70 DL STA63
72 P1 1 No No R2 P2PRXAC001 5G LPI T1F 36 80 -76 UL STA4
73 P1 1 No No R2 P2PRXAC002 5G LPI T2B 36 80 -67 UL STA4
74 P1 1 No No R2 P2PRXAC003 5G LPI T1C 36 80 -45 UL STA4
75 P1 2 No Yes R2 P2PRXAC004 5G LPI T1F 100 80 -76 UL STA4
76 P1 2 No No R2 P2PRXAC005 5G LPI T2B 100 80 -67 UL STA4
77 P1 2 No No R2 P2PRXAC006 5G LPI T1C 100 80 -45 UL STA4
78 P1 5 No Yes R2 P2PRXAC007 5G LPI T1F 161 80 -76 UL STA4
79 P1 5 No No R2 P2PRXAC008 5G LPI T2B 161 80 -67 UL STA4
80 P1 5 No No R2 P2PRXAC009 5G LPI T1C 161 80 -45 UL STA4
81 P1 14 No Yes R1 P2PRXAC010 2G LPI T3E 1 20 -70 UL STA4
82 P1 14 No No R1 P2PRXAC011 2G LPI T1D 1 20 -60 UL STA4
83 P1 14 No No R1 P2PRXAC012 2G LPI T1C 1 20 -45 UL STA4
84 P1 16 No Yes R1 P2PRXAC013 2G LPI T3E 6 20 -70 UL STA4
85 P1 16 No No R1 P2PRXAC014 2G LPI T1D 6 20 -60 UL STA4
86 P1 16 No No R1 P2PRXAC015 2G LPI T1C 6 20 -45 UL STA4
87 P1 18 No Yes R1 P2PRXAC016 2G LPI T3E 11 20 -70 UL STA4
88 P1 18 No No R1 P2PRXAC017 2G LPI T1D 11 20 -60 UL STA4
89 P1 18 No No R1 P2PRXAC018 2G LPI T1C 11 20 -45 UL STA4
90 P1 1 No No R1 P2PRXAX001 5G LPI T1F 36 80 -76 UL STA56
91 P1 1 No No R1 P2PRXAX002 5G LPI T2B 36 80 -67 UL STA56
92 P1 1 No No R1 P2PRXAX003 5G LPI T1C 36 80 -45 UL STA56
93 P1 2 No Yes R1 P2PRXAX004 5G LPI T1F 100 80 -76 UL STA56
94 P1 2 No No R1 P2PRXAX005 5G LPI T2B 100 80 -67 UL STA56
95 P1 2 No No R1 P2PRXAX006 5G LPI T1C 100 80 -45 UL STA56
96 P1 5 No Yes R1 P2PRXAX007 5G LPI T1F 161 80 -76 UL STA56
97 P1 5 No No R1 P2PRXAX008 5G LPI T2B 161 80 -67 UL STA56
98 P1 5 No No R1 P2PRXAX009 5G LPI T1C 161 80 -45 UL STA56
99 P1 10 No Yes R1 P2PRXAX010 5G LPI T1F 100 160 -76 UL STA56
100 P1 10 No No R1 P2PRXAX011 5G LPI T2B 100 160 -67 UL STA56
101 P1 10 No No R1 P2PRXAX012 5G LPI T1C 100 160 -45 UL STA56
102 P1 13 No Yes R2 P2PRXAX013 6G LPI T2A 5 160 -79 UL STA56
103 P1 13 No No R2 P2PRXAX014 6G LPI T2B 5 160 -70 UL STA56
104 P1 13 No No R2 P2PRXAX015 6G LPI T1C 5 160 -45 UL STA56
105 P1 15 No Yes R2 P2PRXAX016 6G LPI T2A 133 160 -79 UL STA56
106 P1 15 No No R2 P2PRXAX017 6G LPI T2B 133 160 -70 UL STA56
107 P1 15 No No R2 P2PRXAX018 6G LPI T1C 133 160 -45 UL STA63
108 P1 17 No Yes R2 P2PRXAX019 6G LPI T2A 197 160 -79 UL STA56
109 P1 17 No No R2 P2PRXAX020 6G LPI T2B 197 160 -70 UL STA56
110 P1 17 No No R2 P2PRXAX021 6G LPI T1C 197 160 -45 UL STA56
111 P1 19 No Yes R2 P2PRXAX022 6G SP T2A 5 320 -79 UL STA56
112 P1 19 No No R2 P2PRXAX023 6G SP T2B 5 320 -70 UL STA56
113 P1 19 No No R2 P2PRXAX024 6G SP T1C 5 320 -45 UL STA56
114 P1 21 No Yes R2 P2PRXAX025 6G LPI T2A 133 320 -79 UL STA56
115 P1 21 No No R2 P2PRXAX026 6G LPI T2B 133 320 -70 UL STA56
116 P1 21 No No R2 P2PRXAX027 6G LPI T1C 133 320 -45 UL STA56
117 P1 23 No Yes R3 P2PRXAX028 2G LPI T3E 1 20 -70 UL STA56
118 P1 23 No No R3 P2PRXAX029 2G LPI T1D 1 20 -60 UL STA56
119 P1 23 No No R3 P2PRXAX030 2G LPI T1C 1 20 -45 UL STA56
120 P1 25 No Yes R3 P2PRXAX031 2G LPI T3E 6 20 -70 UL STA56
121 P1 25 No No R3 P2PRXAX032 2G LPI T1D 6 20 -60 UL STA56
122 P1 25 No No R3 P2PRXAX033 2G LPI T1C 6 20 -45 UL STA56
123 P1 27 No Yes R3 P2PRXAX034 2G LPI T3E 11 20 -70 UL STA56
124 P1 27 No No R3 P2PRXAX035 2G LPI T1D 11 20 -60 UL STA56
125 P1 27 No No R3 P2PRXAX036 2G LPI T1C 11 20 -45 UL STA56
126 P1 1 No No R3 P2PRXBE001 5G LPI T1F 36 80 -76 UL STA63
127 P1 1 No No R3 P2PRXBE002 5G LPI T2B 36 80 -67 UL STA63
128 P1 1 No No R3 P2PRXBE003 5G LPI T1C 36 80 -45 UL STA63
129 P1 2 No Yes R3 P2PRXBE004 5G LPI T1F 100 80 -76 UL STA63
130 P1 2 No No R3 P2PRXBE005 5G LPI T2B 100 80 -67 UL STA63
131 P1 2 No No R3 P2PRXBE006 5G LPI T1C 100 80 -45 UL STA63
132 P1 5 No Yes R3 P2PRXBE007 5G LPI T1F 161 80 -76 UL STA63
133 P1 5 No No R3 P2PRXBE008 5G LPI T2B 161 80 -67 UL STA63
134 P1 5 No No R3 P2PRXBE009 5G LPI T1C 161 80 -45 UL STA63
135 P1 10 No Yes R3 P2PRXBE010 5G LPI T1F 100 160 -76 UL STA63
136 P1 10 No No R3 P2PRXBE011 5G LPI T2B 100 160 -67 UL STA63
137 P1 10 No No R3 P2PRXBE012 5G LPI T1C 100 160 -45 UL STA63
138 P1 13 No Yes R1 P2PRXBE013 6G LPI T2A 5 160 -79 UL STA63
139 P1 13 No No R1 P2PRXBE014 6G LPI T2B 5 160 -70 UL STA63
140 P1 13 No No R1 P2PRXBE015 6G LPI T1C 5 160 -45 UL STA63
141 P1 15 No Yes R1 P2PRXBE016 6G LPI T2A 133 160 -79 UL STA63
142 P1 15 No No R1 P2PRXBE017 6G LPI T2B 133 160 -70 UL STA63
143 P1 15 No No R1 P2PRXBE018 6G LPI T1C 133 160 -45 UL STA63
144 P1 17 No Yes R1 P2PRXBE019 6G LPI T2A 197 160 -79 UL STA63
145 P1 17 No No R1 P2PRXBE020 6G LPI T2B 197 160 -70 UL STA63
146 P1 17 No No R1 P2PRXBE021 6G LPI T1C 197 160 -45 UL STA63
147 P1 19 No Yes R1 P2PRXBE022 6G SP T2A 5 320 -79 UL STA63
148 P1 19 No No R1 P2PRXBE023 6G SP T2B 5 320 -70 UL STA63
149 P1 19 No No R1 P2PRXBE024 6G SP T1C 5 320 -45 UL STA63
150 P1 21 No Yes R1 P2PRXBE025 6G LPI T2A 133 320 -79 UL STA63
151 P1 21 No No R1 P2PRXBE026 6G LPI T2B 133 320 -70 UL STA63
152 P1 21 No No R1 P2PRXBE027 6G LPI T1C 133 320 -45 UL STA63
153 P1 23 No Yes R2 P2PRXBE028 2G LPI T3E 1 20 -70 UL STA63
154 P1 23 No No R2 P2PRXBE029 2G LPI T1D 1 20 -60 UL STA63
155 P1 23 No No R2 P2PRXBE030 2G LPI T1C 1 20 -45 UL STA63
156 P1 25 No Yes R2 P2PRXBE031 2G LPI T3E 6 20 -70 UL STA63
157 P1 25 No No R2 P2PRXBE032 2G LPI T1D 6 20 -60 UL STA63
158 P1 25 No No R2 P2PRXBE033 2G LPI T1C 6 20 -45 UL STA63
159 P1 27 No Yes R2 P2PRXBE034 2G LPI T3E 11 20 -70 UL STA63
160 P1 27 No No R2 P2PRXBE035 2G LPI T1D 11 20 -60 UL STA63
161 P1 27 No No R2 P2PRXBE036 2G LPI T1C 11 20 -45 UL STA63
162 P1 1 No No R2 P2PTXAC001 5G LPI T1F 36 80 -76 DL STA4
163 P1 1 No No R2 P2PTXAC002 5G LPI T2B 36 80 -67 DL STA4
164 P1 1 No No R2 P2PTXAC003 5G LPI T1C 36 80 -45 DL STA4
165 P1 2 No Yes R2 P2PTXAC004 5G LPI T1F 100 80 -76 DL STA4
166 P1 2 No No R2 P2PTXAC005 5G LPI T2B 100 80 -67 DL STA4
167 P1 2 No No R2 P2PTXAC006 5G LPI T1C 100 80 -45 DL STA4
168 P1 5 No Yes R2 P2PTXAC007 5G LPI T1F 161 80 -76 DL STA4
169 P1 5 No No R2 P2PTXAC008 5G LPI T2B 161 80 -67 DL STA4
170 P1 5 No No R2 P2PTXAC009 5G LPI T1C 161 80 -45 DL STA4
171 P1 14 No Yes R1 P2PTXAC010 2G LPI T3E 1 20 -70 DL STA4
172 P1 14 No No R1 P2PTXAC011 2G LPI T1D 1 20 -60 DL STA4
173 P1 14 No No R1 P2PTXAC012 2G LPI T1C 1 20 -45 DL STA4
174 P1 16 No Yes R1 P2PTXAC013 2G LPI T3E 6 20 -70 DL STA4
175 P1 16 No No R1 P2PTXAC014 2G LPI T1D 6 20 -60 DL STA4
176 P1 16 No No R1 P2PTXAC015 2G LPI T1C 6 20 -45 DL STA4
177 P1 18 No Yes R1 P2PTXAC016 2G LPI T3E 11 20 -70 DL STA4
178 P1 18 No No R1 P2PTXAC017 2G LPI T1D 11 20 -60 DL STA4
179 P1 18 No No R1 P2PTXAC018 2G LPI T1C 11 20 -45 DL STA4
180 P1 1 No No R1 P2PTXAX001 5G LPI T1F 36 80 -76 DL STA56
181 P1 1 No No R1 P2PTXAX002 5G LPI T2B 36 80 -67 DL STA56
182 P1 1 No No R1 P2PTXAX003 5G LPI T1C 36 80 -45 DL STA56
183 P1 2 No Yes R1 P2PTXAX004 5G LPI T1F 100 80 -76 DL STA56
184 P1 2 No No R1 P2PTXAX005 5G LPI T2B 100 80 -67 DL STA56
185 P1 2 No No R1 P2PTXAX006 5G LPI T1C 100 80 -45 DL STA56
186 P1 5 No Yes R1 P2PTXAX007 5G LPI T1F 161 80 -76 DL STA56
187 P1 5 No No R1 P2PTXAX008 5G LPI T2B 161 80 -67 DL STA56
188 P1 5 No No R1 P2PTXAX009 5G LPI T1C 161 80 -45 DL STA56
189 P1 8 No Yes R1 P2PTXAX010 5G LPI T1F 100 160 -76 DL STA56
190 P1 8 No No R1 P2PTXAX011 5G LPI T2B 100 160 -67 DL STA56
191 P1 8 No No R1 P2PTXAX012 5G LPI T1C 100 160 -45 DL STA56
192 P1 11 No Yes R2 P2PTXAX013 6G LPI T2A 5 160 -79 DL STA56
193 P1 11 No No R2 P2PTXAX014 6G LPI T2B 5 160 -70 DL STA56
194 P1 11 No No R2 P2PTXAX015 6G LPI T1C 5 160 -45 DL STA56
195 P1 13 No Yes R2 P2PTXAX016 6G LPI T2A 133 160 -79 DL STA56
196 P1 13 No No R2 P2PTXAX017 6G LPI T2B 133 160 -70 DL STA56
197 P1 13 No No R2 P2PTXAX018 6G LPI T1C 133 160 -45 DL STA56
198 P1 15 No Yes R2 P2PTXAX019 6G LPI T2A 197 160 -79 DL STA56
199 P1 15 No No R2 P2PTXAX020 6G LPI T2B 197 160 -70 DL STA56
200 P1 15 No No R2 P2PTXAX021 6G LPI T1C 197 160 -45 DL STA56
201 P1 17 No Yes R2 P2PTXAX022 6G SP T2A 5 320 -79 DL STA56
202 P1 17 No No R2 P2PTXAX023 6G SP T2B 5 320 -70 DL STA56
203 P1 17 No No R2 P2PTXAX024 6G SP T1C 5 320 -45 DL STA56
204 P1 19 No Yes R2 P2PTXAX025 6G LPI T2A 133 320 -79 DL STA56
205 P1 19 No No R2 P2PTXAX026 6G LPI T2B 133 320 -70 DL STA56
206 P1 19 No No R2 P2PTXAX027 6G LPI T1C 133 320 -45 DL STA56
207 P1 21 No Yes R3 P2PTXAX028 2G LPI T3E 1 20 -70 UL STA56
208 P1 21 No No R3 P2PTXAX029 2G LPI T1D 1 20 -60 UL STA56
209 P1 21 No No R3 P2PTXAX030 2G LPI T1C 1 20 -45 UL STA56
210 P1 23 No Yes R3 P2PTXAX031 2G LPI T3E 6 20 -70 UL STA56
211 P1 23 No No R3 P2PTXAX032 2G LPI T1D 6 20 -60 UL STA56
212 P1 23 No No R3 P2PTXAX033 2G LPI T1C 6 20 -45 UL STA56
213 P1 25 No Yes R3 P2PTXAX034 2G LPI T3E 11 20 -70 UL STA56
214 P1 25 No No R3 P2PTXAX035 2G LPI T1D 11 20 -60 UL STA56
215 P1 25 No No R3 P2PTXAX036 2G LPI T1C 11 20 -45 UL STA56
216 P1 1 No No R3 P2PTXBE001 5G LPI T1F 36 80 -76 DL STA63
217 P1 1 No No R3 P2PTXBE002 5G LPI T2B 36 80 -67 DL STA63
218 P1 1 No No R3 P2PTXBE003 5G LPI T1C 36 80 -45 DL STA63
219 P1 2 No Yes R3 P2PTXBE004 5G LPI T1F 100 80 -76 DL STA63
220 P1 2 No No R3 P2PTXBE005 5G LPI T2B 100 80 -67 DL STA63
221 P1 2 No No R3 P2PTXBE006 5G LPI T1C 100 80 -45 DL STA63
222 P1 5 No Yes R3 P2PTXBE007 5G LPI T1F 161 80 -76 DL STA63
223 P1 5 No No R3 P2PTXBE008 5G LPI T2B 161 80 -67 DL STA63
224 P1 5 No No R3 P2PTXBE009 5G LPI T1C 161 80 -45 DL STA63
225 P1 8 No Yes R3 P2PTXBE010 5G LPI T1F 100 160 -76 DL STA63
226 P1 8 No No R3 P2PTXBE011 5G LPI T2B 100 160 -67 DL STA63
227 P1 8 No No R3 P2PTXBE012 5G LPI T1C 100 160 -45 DL STA63
228 P1 11 No Yes R3 P2PTXBE013 6G LPI T1F 5 160 -79 DL STA63
229 P1 11 No No R1 P2PTXBE014 6G LPI T2B 5 160 -70 DL STA63
230 P1 11 No No R1 P2PTXBE015 6G LPI T1C 5 160 -45 DL STA63
231 P1 13 No Yes R1 P2PTXBE016 6G LPI T1F 133 160 -79 DL STA63
232 P1 13 No No R1 P2PTXBE017 6G LPI T2B 133 160 -70 DL STA63
233 P1 13 No No R1 P2PTXBE018 6G LPI T1C 133 160 -45 DL STA63
234 P1 15 No Yes R1 P2PTXBE019 6G LPI T1F 197 160 -79 DL STA63
235 P1 15 No No R1 P2PTXBE020 6G LPI T2B 197 160 -70 DL STA63
236 P1 15 No No R1 P2PTXBE021 6G LPI T1C 197 160 -45 DL STA63
237 P1 17 No Yes R1 P2PTXBE022 6G SP T1F 5 320 -79 DL STA63
238 P1 17 No No R1 P2PTXBE023 6G SP T2B 5 320 -70 DL STA63
239 P1 17 No No R1 P2PTXBE024 6G SP T1C 5 320 -45 DL STA63
240 P1 19 No Yes R1 P2PTXBE025 6G LPI T1F 133 320 -79 DL STA63
241 P1 19 No No R1 P2PTXBE026 6G LPI T2B 133 320 -70 DL STA63
242 P1 19 No No R1 P2PTXBE027 6G LPI T1C 133 320 -45 DL STA63
243 P1 21 No Yes R2 P2PTXBE028 2G LPI T3E 1 20 -70 UL STA63
244 P1 21 No No R2 P2PTXBE029 2G LPI T1D 1 20 -60 UL STA63
245 P1 21 No No R2 P2PTXBE030 2G LPI T1C 1 20 -45 UL STA63
246 P1 23 No Yes R2 P2PTXBE031 2G LPI T3E 6 20 -70 UL STA63
247 P1 23 No No R2 P2PTXBE032 2G LPI T1D 6 20 -60 UL STA63
248 P1 23 No No R2 P2PTXBE033 2G LPI T1C 6 20 -45 UL STA63
249 P1 25 No Yes R2 P2PTXBE034 2G LPI T3E 11 20 -70 UL STA63
250 P1 25 No No R2 P2PTXBE035 2G LPI T1D 11 20 -60 UL STA63
251 P1 25 No No R2 P2PTXBE036 2G LPI T1C 11 20 -45 UL STA63
+71
View File
@@ -0,0 +1,71 @@
Priority,Index,Throttled,Rotation,TC ID,Band,6GHz Power Mode,Station 1 Test Point,Station 1 Channel,Station 1 Bandwidth,Station 1 RSSI,Station 1 Direction,Station 1 Rate,Station 1 STA,Station 2 Test Point,Station 2 Channel,Station 2 Bandwidth,Station 2 RSSI,Station 2 Direction,Station 2 Rate,Station 2 STA,Station 3 Test Point,Station 3 Channel,Station 3 Bandwidth,Station 3 RSSI,Station 3 Direction,Station 3 Rate,Station 3 STA
P2,1,Yes,R3,P3PRXBETH001,5G,LPI,T1F,36,80,-76,UL,22Mbps,STA63,T1I,36,80,-76,UL,22Mbps,STA64,T2J,36,80,-76,UL,11Mbps,STA65
P2,2,Yes,R3,P3PRXBETH002,5G,LPI,T1F,100,80,-76,UL,22Mbps,STA63,T1I,100,80,-76,UL,22Mbps,STA64,T2J,100,80,-76,UL,11Mbps,STA65
P2,3,Yes,R3,P3PRXBETH003,5G,LPI,T1F,161,80,-76,UL,22Mbps,STA63,T1I,161,80,-76,UL,22Mbps,STA64,T2J,161,80,-76,UL,11Mbps,STA65
P2,4,Yes,R1,P3PRXBETH004,6G,LPI,T2A,5,160,-79,UL,22Mbps,STA63,T1L,5,160,-79,UL,22Mbps,STA64,T2O,5,160,-79,UL,11Mbps,STA65
P2,5,Yes,R1,P3PRXBETH005,6G,LPI,T2A,133,160,-79,UL,22Mbps,STA63,T1L,133,160,-79,UL,22Mbps,STA64,T2O,133,160,-79,UL,11Mbps,STA65
P2,6,Yes,R1,P3PRXBETH006,6G,LPI,T2A,197,160,-79,UL,22Mbps,STA63,T1L,197,160,-79,UL,22Mbps,STA64,T2O,197,160,-79,UL,11Mbps,STA65
P2,7,Yes,R2,P3PRXBETH007,2G,LPI,T3E,6,20,-70,UL,22Mbps,STA63,T1P,6,20,-70,UL,22Mbps,STA64,T2Q,6,20,-70,UL,11Mbps,STA65
P2,8,Yes,R1,P3PRXBETH008,6G,SP,T2A,5,320,-79,UL,22Mbps,STA63,T1L,5,320,-79,UL,22Mbps,STA64,T2O,5,320,-79,UL,11Mbps,STA65
P2,9,Yes,R1,P3PRXBETH009,6G,LPI,T2A,133,320,-79,UL,22Mbps,STA63,T1L,133,320,-79,UL,22Mbps,STA64,T2O,133,320,-79,UL,11Mbps,STA65
P2,1,No,R3,P3PRXBEUT001,5G,LPI,T1F,36,80,-76,UL,Unlimited,STA63,T1I,36,80,-76,UL,Unlimited,STA64,T2J,36,80,-76,UL,Unlimited,STA65
P2,2,No,R3,P3PRXBEUT002,5G,LPI,T1F,100,80,-76,UL,Unlimited,STA63,T1I,100,80,-76,UL,Unlimited,STA64,T2J,100,80,-76,UL,Unlimited,STA65
P2,3,No,R3,P3PRXBEUT003,5G,LPI,T1F,161,80,-76,UL,Unlimited,STA63,T1I,161,80,-76,UL,Unlimited,STA64,T2J,161,80,-76,UL,Unlimited,STA65
P2,4,No,R1,P3PRXBEUT004,6G,LPI,T2A,5,160,-79,UL,Unlimited,STA63,T1L,5,160,-79,UL,Unlimited,STA64,T2O,5,160,-79,UL,Unlimited,STA65
P2,5,No,R1,P3PRXBEUT005,6G,LPI,T2A,133,160,-79,UL,Unlimited,STA63,T1L,133,160,-79,UL,Unlimited,STA64,T2O,133,160,-79,UL,Unlimited,STA65
P2,6,No,R1,P3PRXBEUT006,6G,LPI,T2A,197,160,-79,UL,Unlimited,STA63,T1L,197,160,-79,UL,Unlimited,STA64,T2O,197,160,-79,UL,Unlimited,STA65
P2,7,No,R2,P3PRXBEUT007,2G,LPI,T3E,6,20,-70,UL,Unlimited,STA63,T1P,6,20,-70,UL,Unlimited,STA64,T2Q,6,20,-70,UL,Unlimited,STA65
P2,8,No,R1,P3PRXBEUT008,6G,SP,T2A,5,320,-79,UL,Unlimited,STA63,T1L,5,320,-79,UL,Unlimited,STA64,T2O,5,320,-79,UL,Unlimited,STA65
P2,9,No,R1,P3PRXBEUT009,6G,LPI,T2A,133,320,-79,UL,Unlimited,STA63,T1L,133,320,-79,UL,Unlimited,STA64,T2O,133,320,-79,UL,Unlimited,STA65
P2,1,Yes,R1,P3PRXAXTH001,5G,LPI,T1F,36,80,-76,UL,22Mbps,STA56,T1I,36,80,-76,UL,22Mbps,STA58,T2J,36,80,-76,UL,11Mbps,STA59
P2,2,Yes,R1,P3PRXAXTH002,5G,LPI,T1F,100,80,-76,UL,22Mbps,STA56,T1I,100,80,-76,UL,22Mbps,STA58,T2J,100,80,-76,UL,11Mbps,STA59
P2,3,Yes,R1,P3PRXAXTH003,5G,LPI,T1F,161,80,-76,UL,22Mbps,STA56,T1I,161,80,-76,UL,22Mbps,STA58,T2J,161,80,-76,UL,11Mbps,STA59
P2,4,Yes,R2,P3PRXAXTH004,6G,LPI,T2A,5,160,-79,UL,22Mbps,STA56,T1L,5,160,-79,UL,22Mbps,STA58,T2O,5,160,-79,UL,11Mbps,STA59
P2,5,Yes,R2,P3PRXAXTH005,6G,LPI,T2A,133,160,-79,UL,22Mbps,STA56,T1L,133,160,-79,UL,22Mbps,STA58,T2O,133,160,-79,UL,11Mbps,STA59
P2,6,Yes,R2,P3PRXAXTH006,6G,LPI,T2A,197,160,-79,UL,22Mbps,STA56,T1L,197,160,-79,UL,22Mbps,STA58,T2O,197,160,-79,UL,11Mbps,STA59
P2,7,Yes,R3,P3PRXAXTH007,2G,LPI,T3E,6,20,-70,UL,22Mbps,STA56,T1P,6,20,-70,UL,22Mbps,STA58,T2Q,6,20,-70,UL,11Mbps,STA59
P2,8,Yes,R2,P3PRXAXTH008,6G,SP,T2A,5,320,-79,UL,22Mbps,STA56,T1L,5,320,-79,UL,22Mbps,STA58,T2O,5,320,-79,UL,11Mbps,STA59
P2,9,Yes,R2,P3PRXAXTH009,6G,LPI,T2A,133,320,-79,UL,22Mbps,STA56,T1L,133,320,-79,UL,22Mbps,STA58,T2O,133,320,-79,UL,11Mbps,STA59
P2,1,No,R1,P3PRXAXUT001,5G,LPI,T1F,36,80,-76,UL,Unlimited,STA56,T1I,36,80,-76,UL,Unlimited,STA58,T2J,36,80,-76,UL,Unlimited,STA59
P2,2,No,R1,P3PRXAXUT002,5G,LPI,T1F,100,80,-76,UL,Unlimited,STA56,T1I,100,80,-76,UL,Unlimited,STA58,T2J,100,80,-76,UL,Unlimited,STA59
P2,3,No,R1,P3PRXAXUT003,5G,LPI,T1F,161,80,-76,UL,Unlimited,STA56,T1I,161,80,-76,UL,Unlimited,STA58,T2J,161,80,-76,UL,Unlimited,STA59
P2,4,No,R2,P3PRXAXUT004,6G,LPI,T2A,5,160,-79,UL,Unlimited,STA56,T1L,5,160,-79,UL,Unlimited,STA58,T2O,5,160,-79,UL,Unlimited,STA59
P2,5,No,R2,P3PRXAXUT005,6G,LPI,T2A,133,160,-79,UL,Unlimited,STA56,T1L,133,160,-79,UL,Unlimited,STA58,T2O,133,160,-79,UL,Unlimited,STA59
P2,6,No,R2,P3PRXAXUT006,6G,LPI,T2A,197,160,-79,UL,Unlimited,STA56,T1L,197,160,-79,UL,Unlimited,STA58,T2O,197,160,-79,UL,Unlimited,STA59
P2,7,No,R3,P3PRXAXUT007,2G,LPI,T3E,6,20,-70,UL,Unlimited,STA56,T1P,6,20,-70,UL,Unlimited,STA58,T2Q,6,20,-70,UL,Unlimited,STA59
P2,8,No,R2,P3PRXAXUT008,6G,SP,T2A,5,320,-79,UL,Unlimited,STA56,T1L,5,320,-79,UL,Unlimited,STA58,T2O,5,320,-79,UL,Unlimited,STA59
P2,9,No,R2,P3PRXAXUT009,6G,LPI,T2A,133,320,-79,UL,Unlimited,STA56,T1L,133,320,-79,UL,Unlimited,STA58,T2O,133,320,-79,UL,Unlimited,STA59
P2,1,Yes,R1,P3PTXAXTH001,5G,LPI,T1F,36,80,-76,DL,22Mbps,STA56,T1I,36,80,-76,DL,22Mbps,STA58,T2J,36,80,-76,DL,11Mbps,STA59
P2,2,Yes,R1,P3PTXAXTH002,5G,LPI,T1F,100,80,-76,DL,22Mbps,STA56,T1I,100,80,-76,DL,22Mbps,STA58,T2J,100,80,-76,DL,11Mbps,STA59
P2,3,Yes,R1,P3PTXAXTH003,5G,LPI,T1F,161,80,-76,DL,22Mbps,STA56,T1I,161,80,-76,DL,22Mbps,STA58,T2J,161,80,-76,DL,11Mbps,STA59
P2,4,Yes,R2,P3PTXAXTH004,6G,LPI,T2A,5,160,-79,DL,22Mbps,STA56,T1L,5,160,-79,DL,22Mbps,STA58,T2O,5,160,-79,DL,11Mbps,STA59
P2,5,Yes,R2,P3PTXAXTH005,6G,LPI,T2A,133,160,-79,DL,22Mbps,STA56,T1L,133,160,-79,DL,22Mbps,STA58,T2O,133,160,-79,DL,11Mbps,STA59
P2,6,Yes,R2,P3PTXAXTH006,6G,LPI,T2A,197,160,-79,DL,22Mbps,STA56,T1L,197,160,-79,DL,22Mbps,STA58,T2O,197,160,-79,DL,11Mbps,STA59
P2,7,Yes,R3,P3PTXAXTH007,2G,LPI,T3E,6,20,-70,DL,22Mbps,STA56,T1P,6,20,-70,DL,22Mbps,STA58,T2Q,6,20,-70,DL,11Mbps,STA59
P2,8,Yes,R2,P3PTXAXTH008,6G,SP,T2A,5,320,-79,DL,22Mbps,STA56,T1L,5,320,-79,DL,22Mbps,STA58,T2O,5,320,-79,DL,11Mbps,STA59
P2,9,Yes,R2,P3PTXAXTH009,6G,LPI,T2A,133,320,-79,DL,22Mbps,STA56,T1L,133,320,-79,DL,22Mbps,STA58,T2O,133,320,-79,DL,11Mbps,STA59
P2,1,No,R1,P3PTXAXUT001,5G,LPI,T1F,36,80,-76,DL,Unlimited,STA56,T1I,36,80,-76,DL,Unlimited,STA58,T2J,36,80,-76,DL,Unlimited,STA59
P2,2,No,R1,P3PTXAXUT002,5G,LPI,T1F,100,80,-76,DL,Unlimited,STA56,T1I,100,80,-76,DL,Unlimited,STA58,T2J,100,80,-76,DL,Unlimited,STA59
P2,3,No,R1,P3PTXAXUT003,5G,LPI,T1F,161,80,-76,DL,Unlimited,STA56,T1I,161,80,-76,DL,Unlimited,STA58,T2J,161,80,-76,DL,Unlimited,STA59
P2,4,No,R2,P3PTXAXUT004,6G,LPI,T2A,5,160,-79,DL,Unlimited,STA56,T1L,5,160,-79,DL,Unlimited,STA58,T2O,5,160,-79,DL,Unlimited,STA59
P2,5,No,R2,P3PTXAXUT005,6G,LPI,T2A,133,160,-79,DL,Unlimited,STA56,T1L,133,160,-79,DL,Unlimited,STA58,T2O,133,160,-79,DL,Unlimited,STA59
P2,6,No,R2,P3PTXAXUT006,6G,LPI,T2A,197,160,-79,DL,Unlimited,STA56,T1L,197,160,-79,DL,Unlimited,STA58,T2O,197,160,-79,DL,Unlimited,STA59
P2,7,No,R3,P3PTXAXUT007,2G,LPI,T3E,6,20,-70,DL,Unlimited,STA56,T1P,6,20,-70,DL,Unlimited,STA58,T2Q,6,20,-70,DL,Unlimited,STA59
P2,8,No,R2,P3PTXAXUT008,6G,SP,T2A,5,320,-79,DL,Unlimited,STA56,T1L,5,320,-79,DL,Unlimited,STA58,T2O,5,320,-79,DL,Unlimited,STA59
P2,9,No,R2,P3PTXAXUT009,6G,LPI,T2A,133,320,-79,DL,Unlimited,STA56,T1L,133,320,-79,DL,Unlimited,STA58,T2O,133,320,-79,DL,Unlimited,STA59
P2,1,Yes,R2,P3PRXACTH001,5G,LPI,T1F,36,80,-76,UL,22Mbps,STA4,T1I,36,80,-76,UL,22Mbps,STA5,T2J,36,80,-76,UL,11Mbps,STA6
P2,2,Yes,R2,P3PRXACTH002,5G,LPI,T1F,100,80,-76,UL,22Mbps,STA4,T1I,100,80,-76,UL,22Mbps,STA5,T2J,100,80,-76,UL,11Mbps,STA6
P2,3,Yes,R2,P3PRXACTH003,5G,LPI,T1F,161,80,-76,UL,22Mbps,STA4,T1I,161,80,-76,UL,22Mbps,STA5,T2J,161,80,-76,UL,11Mbps,STA6
P2,4,Yes,R1,P3PRXACTH004,2G,LPI,T3E,6,20,-70,UL,22Mbps,STA4,T1P,6,20,-70,UL,22Mbps,STA5,T2Q,6,20,-70,UL,11Mbps,STA6
P2,1,No,R2,P3PRXACUT001,5G,LPI,T1F,36,80,-76,UL,Unlimited,STA4,T1I,36,80,-76,UL,Unlimited,STA5,T2J,36,80,-76,UL,Unlimited,STA6
P2,2,No,R2,P3PRXACUT002,5G,LPI,T1F,100,80,-76,UL,Unlimited,STA4,T1I,100,80,-76,UL,Unlimited,STA5,T2J,100,80,-76,UL,Unlimited,STA6
P2,3,No,R2,P3PRXACUT003,5G,LPI,T1F,161,80,-76,UL,Unlimited,STA4,T1I,161,80,-76,UL,Unlimited,STA5,T2J,161,80,-76,UL,Unlimited,STA6
P2,4,No,R1,P3PRXACUT004,2G,LPI,T3E,6,20,-70,UL,Unlimited,STA4,T1P,6,20,-70,UL,Unlimited,STA5,T2Q,6,20,-70,UL,Unlimited,STA6
P2,1,Yes,R2,P3PTXACTH001,5G,LPI,T1F,36,80,-76,DL,22Mbps,STA4,T1I,36,80,-76,DL,22Mbps,STA5,T2J,36,80,-76,DL,11Mbps,STA6
P2,2,Yes,R2,P3PTXACTH002,5G,LPI,T1F,100,80,-76,DL,22Mbps,STA4,T1I,100,80,-76,DL,22Mbps,STA5,T2J,100,80,-76,DL,11Mbps,STA6
P2,3,Yes,R2,P3PTXACTH003,5G,LPI,T1F,161,80,-76,DL,22Mbps,STA4,T1I,161,80,-76,DL,22Mbps,STA5,T2J,161,80,-76,DL,11Mbps,STA6
P2,4,Yes,R1,P3PTXACTH004,2G,LPI,T3E,6,20,-70,DL,22Mbps,STA4,T1P,6,20,-70,DL,22Mbps,STA5,T2Q,6,20,-70,DL,11Mbps,STA6
P2,1,No,R2,P3PTXACUT001,5G,LPI,T1F,36,80,-76,DL,Unlimited,STA4,T1I,36,80,-76,DL,Unlimited,STA5,T2J,36,80,-76,DL,Unlimited,STA6
P2,2,No,R2,P3PTXACUT002,5G,LPI,T1F,100,80,-76,DL,Unlimited,STA4,T1I,100,80,-76,DL,Unlimited,STA5,T2J,100,80,-76,DL,Unlimited,STA6
P2,3,No,R2,P3PTXACUT003,5G,LPI,T1F,161,80,-76,DL,Unlimited,STA4,T1I,161,80,-76,DL,Unlimited,STA5,T2J,161,80,-76,DL,Unlimited,STA6
P2,4,No,R1,P3PTXACUT004,2G,LPI,T3E,6,20,-70,DL,Unlimited,STA4,T1P,6,20,-70,DL,Unlimited,STA5,T2Q,6,20,-70,DL,Unlimited,STA6
1 Priority Index Throttled Rotation TC ID Band 6GHz Power Mode Station 1 Test Point Station 1 Channel Station 1 Bandwidth Station 1 RSSI Station 1 Direction Station 1 Rate Station 1 STA Station 2 Test Point Station 2 Channel Station 2 Bandwidth Station 2 RSSI Station 2 Direction Station 2 Rate Station 2 STA Station 3 Test Point Station 3 Channel Station 3 Bandwidth Station 3 RSSI Station 3 Direction Station 3 Rate Station 3 STA
2 P2 1 Yes R3 P3PRXBETH001 5G LPI T1F 36 80 -76 UL 22Mbps STA63 T1I 36 80 -76 UL 22Mbps STA64 T2J 36 80 -76 UL 11Mbps STA65
3 P2 2 Yes R3 P3PRXBETH002 5G LPI T1F 100 80 -76 UL 22Mbps STA63 T1I 100 80 -76 UL 22Mbps STA64 T2J 100 80 -76 UL 11Mbps STA65
4 P2 3 Yes R3 P3PRXBETH003 5G LPI T1F 161 80 -76 UL 22Mbps STA63 T1I 161 80 -76 UL 22Mbps STA64 T2J 161 80 -76 UL 11Mbps STA65
5 P2 4 Yes R1 P3PRXBETH004 6G LPI T2A 5 160 -79 UL 22Mbps STA63 T1L 5 160 -79 UL 22Mbps STA64 T2O 5 160 -79 UL 11Mbps STA65
6 P2 5 Yes R1 P3PRXBETH005 6G LPI T2A 133 160 -79 UL 22Mbps STA63 T1L 133 160 -79 UL 22Mbps STA64 T2O 133 160 -79 UL 11Mbps STA65
7 P2 6 Yes R1 P3PRXBETH006 6G LPI T2A 197 160 -79 UL 22Mbps STA63 T1L 197 160 -79 UL 22Mbps STA64 T2O 197 160 -79 UL 11Mbps STA65
8 P2 7 Yes R2 P3PRXBETH007 2G LPI T3E 6 20 -70 UL 22Mbps STA63 T1P 6 20 -70 UL 22Mbps STA64 T2Q 6 20 -70 UL 11Mbps STA65
9 P2 8 Yes R1 P3PRXBETH008 6G SP T2A 5 320 -79 UL 22Mbps STA63 T1L 5 320 -79 UL 22Mbps STA64 T2O 5 320 -79 UL 11Mbps STA65
10 P2 9 Yes R1 P3PRXBETH009 6G LPI T2A 133 320 -79 UL 22Mbps STA63 T1L 133 320 -79 UL 22Mbps STA64 T2O 133 320 -79 UL 11Mbps STA65
11 P2 1 No R3 P3PRXBEUT001 5G LPI T1F 36 80 -76 UL Unlimited STA63 T1I 36 80 -76 UL Unlimited STA64 T2J 36 80 -76 UL Unlimited STA65
12 P2 2 No R3 P3PRXBEUT002 5G LPI T1F 100 80 -76 UL Unlimited STA63 T1I 100 80 -76 UL Unlimited STA64 T2J 100 80 -76 UL Unlimited STA65
13 P2 3 No R3 P3PRXBEUT003 5G LPI T1F 161 80 -76 UL Unlimited STA63 T1I 161 80 -76 UL Unlimited STA64 T2J 161 80 -76 UL Unlimited STA65
14 P2 4 No R1 P3PRXBEUT004 6G LPI T2A 5 160 -79 UL Unlimited STA63 T1L 5 160 -79 UL Unlimited STA64 T2O 5 160 -79 UL Unlimited STA65
15 P2 5 No R1 P3PRXBEUT005 6G LPI T2A 133 160 -79 UL Unlimited STA63 T1L 133 160 -79 UL Unlimited STA64 T2O 133 160 -79 UL Unlimited STA65
16 P2 6 No R1 P3PRXBEUT006 6G LPI T2A 197 160 -79 UL Unlimited STA63 T1L 197 160 -79 UL Unlimited STA64 T2O 197 160 -79 UL Unlimited STA65
17 P2 7 No R2 P3PRXBEUT007 2G LPI T3E 6 20 -70 UL Unlimited STA63 T1P 6 20 -70 UL Unlimited STA64 T2Q 6 20 -70 UL Unlimited STA65
18 P2 8 No R1 P3PRXBEUT008 6G SP T2A 5 320 -79 UL Unlimited STA63 T1L 5 320 -79 UL Unlimited STA64 T2O 5 320 -79 UL Unlimited STA65
19 P2 9 No R1 P3PRXBEUT009 6G LPI T2A 133 320 -79 UL Unlimited STA63 T1L 133 320 -79 UL Unlimited STA64 T2O 133 320 -79 UL Unlimited STA65
20 P2 1 Yes R1 P3PRXAXTH001 5G LPI T1F 36 80 -76 UL 22Mbps STA56 T1I 36 80 -76 UL 22Mbps STA58 T2J 36 80 -76 UL 11Mbps STA59
21 P2 2 Yes R1 P3PRXAXTH002 5G LPI T1F 100 80 -76 UL 22Mbps STA56 T1I 100 80 -76 UL 22Mbps STA58 T2J 100 80 -76 UL 11Mbps STA59
22 P2 3 Yes R1 P3PRXAXTH003 5G LPI T1F 161 80 -76 UL 22Mbps STA56 T1I 161 80 -76 UL 22Mbps STA58 T2J 161 80 -76 UL 11Mbps STA59
23 P2 4 Yes R2 P3PRXAXTH004 6G LPI T2A 5 160 -79 UL 22Mbps STA56 T1L 5 160 -79 UL 22Mbps STA58 T2O 5 160 -79 UL 11Mbps STA59
24 P2 5 Yes R2 P3PRXAXTH005 6G LPI T2A 133 160 -79 UL 22Mbps STA56 T1L 133 160 -79 UL 22Mbps STA58 T2O 133 160 -79 UL 11Mbps STA59
25 P2 6 Yes R2 P3PRXAXTH006 6G LPI T2A 197 160 -79 UL 22Mbps STA56 T1L 197 160 -79 UL 22Mbps STA58 T2O 197 160 -79 UL 11Mbps STA59
26 P2 7 Yes R3 P3PRXAXTH007 2G LPI T3E 6 20 -70 UL 22Mbps STA56 T1P 6 20 -70 UL 22Mbps STA58 T2Q 6 20 -70 UL 11Mbps STA59
27 P2 8 Yes R2 P3PRXAXTH008 6G SP T2A 5 320 -79 UL 22Mbps STA56 T1L 5 320 -79 UL 22Mbps STA58 T2O 5 320 -79 UL 11Mbps STA59
28 P2 9 Yes R2 P3PRXAXTH009 6G LPI T2A 133 320 -79 UL 22Mbps STA56 T1L 133 320 -79 UL 22Mbps STA58 T2O 133 320 -79 UL 11Mbps STA59
29 P2 1 No R1 P3PRXAXUT001 5G LPI T1F 36 80 -76 UL Unlimited STA56 T1I 36 80 -76 UL Unlimited STA58 T2J 36 80 -76 UL Unlimited STA59
30 P2 2 No R1 P3PRXAXUT002 5G LPI T1F 100 80 -76 UL Unlimited STA56 T1I 100 80 -76 UL Unlimited STA58 T2J 100 80 -76 UL Unlimited STA59
31 P2 3 No R1 P3PRXAXUT003 5G LPI T1F 161 80 -76 UL Unlimited STA56 T1I 161 80 -76 UL Unlimited STA58 T2J 161 80 -76 UL Unlimited STA59
32 P2 4 No R2 P3PRXAXUT004 6G LPI T2A 5 160 -79 UL Unlimited STA56 T1L 5 160 -79 UL Unlimited STA58 T2O 5 160 -79 UL Unlimited STA59
33 P2 5 No R2 P3PRXAXUT005 6G LPI T2A 133 160 -79 UL Unlimited STA56 T1L 133 160 -79 UL Unlimited STA58 T2O 133 160 -79 UL Unlimited STA59
34 P2 6 No R2 P3PRXAXUT006 6G LPI T2A 197 160 -79 UL Unlimited STA56 T1L 197 160 -79 UL Unlimited STA58 T2O 197 160 -79 UL Unlimited STA59
35 P2 7 No R3 P3PRXAXUT007 2G LPI T3E 6 20 -70 UL Unlimited STA56 T1P 6 20 -70 UL Unlimited STA58 T2Q 6 20 -70 UL Unlimited STA59
36 P2 8 No R2 P3PRXAXUT008 6G SP T2A 5 320 -79 UL Unlimited STA56 T1L 5 320 -79 UL Unlimited STA58 T2O 5 320 -79 UL Unlimited STA59
37 P2 9 No R2 P3PRXAXUT009 6G LPI T2A 133 320 -79 UL Unlimited STA56 T1L 133 320 -79 UL Unlimited STA58 T2O 133 320 -79 UL Unlimited STA59
38 P2 1 Yes R1 P3PTXAXTH001 5G LPI T1F 36 80 -76 DL 22Mbps STA56 T1I 36 80 -76 DL 22Mbps STA58 T2J 36 80 -76 DL 11Mbps STA59
39 P2 2 Yes R1 P3PTXAXTH002 5G LPI T1F 100 80 -76 DL 22Mbps STA56 T1I 100 80 -76 DL 22Mbps STA58 T2J 100 80 -76 DL 11Mbps STA59
40 P2 3 Yes R1 P3PTXAXTH003 5G LPI T1F 161 80 -76 DL 22Mbps STA56 T1I 161 80 -76 DL 22Mbps STA58 T2J 161 80 -76 DL 11Mbps STA59
41 P2 4 Yes R2 P3PTXAXTH004 6G LPI T2A 5 160 -79 DL 22Mbps STA56 T1L 5 160 -79 DL 22Mbps STA58 T2O 5 160 -79 DL 11Mbps STA59
42 P2 5 Yes R2 P3PTXAXTH005 6G LPI T2A 133 160 -79 DL 22Mbps STA56 T1L 133 160 -79 DL 22Mbps STA58 T2O 133 160 -79 DL 11Mbps STA59
43 P2 6 Yes R2 P3PTXAXTH006 6G LPI T2A 197 160 -79 DL 22Mbps STA56 T1L 197 160 -79 DL 22Mbps STA58 T2O 197 160 -79 DL 11Mbps STA59
44 P2 7 Yes R3 P3PTXAXTH007 2G LPI T3E 6 20 -70 DL 22Mbps STA56 T1P 6 20 -70 DL 22Mbps STA58 T2Q 6 20 -70 DL 11Mbps STA59
45 P2 8 Yes R2 P3PTXAXTH008 6G SP T2A 5 320 -79 DL 22Mbps STA56 T1L 5 320 -79 DL 22Mbps STA58 T2O 5 320 -79 DL 11Mbps STA59
46 P2 9 Yes R2 P3PTXAXTH009 6G LPI T2A 133 320 -79 DL 22Mbps STA56 T1L 133 320 -79 DL 22Mbps STA58 T2O 133 320 -79 DL 11Mbps STA59
47 P2 1 No R1 P3PTXAXUT001 5G LPI T1F 36 80 -76 DL Unlimited STA56 T1I 36 80 -76 DL Unlimited STA58 T2J 36 80 -76 DL Unlimited STA59
48 P2 2 No R1 P3PTXAXUT002 5G LPI T1F 100 80 -76 DL Unlimited STA56 T1I 100 80 -76 DL Unlimited STA58 T2J 100 80 -76 DL Unlimited STA59
49 P2 3 No R1 P3PTXAXUT003 5G LPI T1F 161 80 -76 DL Unlimited STA56 T1I 161 80 -76 DL Unlimited STA58 T2J 161 80 -76 DL Unlimited STA59
50 P2 4 No R2 P3PTXAXUT004 6G LPI T2A 5 160 -79 DL Unlimited STA56 T1L 5 160 -79 DL Unlimited STA58 T2O 5 160 -79 DL Unlimited STA59
51 P2 5 No R2 P3PTXAXUT005 6G LPI T2A 133 160 -79 DL Unlimited STA56 T1L 133 160 -79 DL Unlimited STA58 T2O 133 160 -79 DL Unlimited STA59
52 P2 6 No R2 P3PTXAXUT006 6G LPI T2A 197 160 -79 DL Unlimited STA56 T1L 197 160 -79 DL Unlimited STA58 T2O 197 160 -79 DL Unlimited STA59
53 P2 7 No R3 P3PTXAXUT007 2G LPI T3E 6 20 -70 DL Unlimited STA56 T1P 6 20 -70 DL Unlimited STA58 T2Q 6 20 -70 DL Unlimited STA59
54 P2 8 No R2 P3PTXAXUT008 6G SP T2A 5 320 -79 DL Unlimited STA56 T1L 5 320 -79 DL Unlimited STA58 T2O 5 320 -79 DL Unlimited STA59
55 P2 9 No R2 P3PTXAXUT009 6G LPI T2A 133 320 -79 DL Unlimited STA56 T1L 133 320 -79 DL Unlimited STA58 T2O 133 320 -79 DL Unlimited STA59
56 P2 1 Yes R2 P3PRXACTH001 5G LPI T1F 36 80 -76 UL 22Mbps STA4 T1I 36 80 -76 UL 22Mbps STA5 T2J 36 80 -76 UL 11Mbps STA6
57 P2 2 Yes R2 P3PRXACTH002 5G LPI T1F 100 80 -76 UL 22Mbps STA4 T1I 100 80 -76 UL 22Mbps STA5 T2J 100 80 -76 UL 11Mbps STA6
58 P2 3 Yes R2 P3PRXACTH003 5G LPI T1F 161 80 -76 UL 22Mbps STA4 T1I 161 80 -76 UL 22Mbps STA5 T2J 161 80 -76 UL 11Mbps STA6
59 P2 4 Yes R1 P3PRXACTH004 2G LPI T3E 6 20 -70 UL 22Mbps STA4 T1P 6 20 -70 UL 22Mbps STA5 T2Q 6 20 -70 UL 11Mbps STA6
60 P2 1 No R2 P3PRXACUT001 5G LPI T1F 36 80 -76 UL Unlimited STA4 T1I 36 80 -76 UL Unlimited STA5 T2J 36 80 -76 UL Unlimited STA6
61 P2 2 No R2 P3PRXACUT002 5G LPI T1F 100 80 -76 UL Unlimited STA4 T1I 100 80 -76 UL Unlimited STA5 T2J 100 80 -76 UL Unlimited STA6
62 P2 3 No R2 P3PRXACUT003 5G LPI T1F 161 80 -76 UL Unlimited STA4 T1I 161 80 -76 UL Unlimited STA5 T2J 161 80 -76 UL Unlimited STA6
63 P2 4 No R1 P3PRXACUT004 2G LPI T3E 6 20 -70 UL Unlimited STA4 T1P 6 20 -70 UL Unlimited STA5 T2Q 6 20 -70 UL Unlimited STA6
64 P2 1 Yes R2 P3PTXACTH001 5G LPI T1F 36 80 -76 DL 22Mbps STA4 T1I 36 80 -76 DL 22Mbps STA5 T2J 36 80 -76 DL 11Mbps STA6
65 P2 2 Yes R2 P3PTXACTH002 5G LPI T1F 100 80 -76 DL 22Mbps STA4 T1I 100 80 -76 DL 22Mbps STA5 T2J 100 80 -76 DL 11Mbps STA6
66 P2 3 Yes R2 P3PTXACTH003 5G LPI T1F 161 80 -76 DL 22Mbps STA4 T1I 161 80 -76 DL 22Mbps STA5 T2J 161 80 -76 DL 11Mbps STA6
67 P2 4 Yes R1 P3PTXACTH004 2G LPI T3E 6 20 -70 DL 22Mbps STA4 T1P 6 20 -70 DL 22Mbps STA5 T2Q 6 20 -70 DL 11Mbps STA6
68 P2 1 No R2 P3PTXACUT001 5G LPI T1F 36 80 -76 DL Unlimited STA4 T1I 36 80 -76 DL Unlimited STA5 T2J 36 80 -76 DL Unlimited STA6
69 P2 2 No R2 P3PTXACUT002 5G LPI T1F 100 80 -76 DL Unlimited STA4 T1I 100 80 -76 DL Unlimited STA5 T2J 100 80 -76 DL Unlimited STA6
70 P2 3 No R2 P3PTXACUT003 5G LPI T1F 161 80 -76 DL Unlimited STA4 T1I 161 80 -76 DL Unlimited STA5 T2J 161 80 -76 DL Unlimited STA6
71 P2 4 No R1 P3PTXACUT004 2G LPI T3E 6 20 -70 DL Unlimited STA4 T1P 6 20 -70 DL Unlimited STA5 T2Q 6 20 -70 DL Unlimited STA6
+511
View File
@@ -0,0 +1,511 @@
import json
import sqlite3
import os
import re
from contextlib import contextmanager
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Iterator
APP_ROOT = Path(__file__).resolve().parent
DB_PATH = APP_ROOT / "scheduler.db"
# Hardware device names (from environment or defaults)
DUT = os.getenv("DUT", "CGW453").strip()
REF = os.getenv("REF", "CGW452").strip()
# Device names for the test database (use hardware device names)
DEVICE_DUT = DUT
DEVICE_REF = REF
@dataclass(frozen=True)
class TestRecord:
test_id: str
device: str
test_type: str
rotation: str | None
rx_tx: str | None
has_coe_pair: bool
coe_pairing: list[str]
priority: int
victim_band: str | None
config: dict[str, dict[str, str | None]]
estimated_minutes: int
status: str = "pending"
excluded: bool = False
raw_payload: dict[str, Any] | None = None
@dataclass(frozen=True)
class ScheduleRow:
test_id: str
device: str
scheduled_date: str
shift_index: int
sequence_in_shift: int
test_type: str
rotation: str | None
status: str
priority: int
estimated_minutes: int
@contextmanager
def get_connection(db_path: str | Path = DB_PATH) -> Iterator[sqlite3.Connection]:
conn = sqlite3.connect(str(db_path))
conn.row_factory = sqlite3.Row
try:
yield conn
conn.commit()
finally:
conn.close()
def init_db(db_path: str | Path = DB_PATH) -> None:
# Validate device names are set
if not DUT or not REF:
raise ValueError(f"Invalid device names: DUT={DUT!r}, REF={REF!r}")
with get_connection(db_path) as conn:
conn.executescript(
"""
CREATE TABLE IF NOT EXISTS tests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
test_id TEXT NOT NULL,
device TEXT NOT NULL,
test_type TEXT NOT NULL CHECK (test_type IN ('P2P', 'COE', 'P3P')),
rotation TEXT,
rx_tx TEXT CHECK (rx_tx IN ('RX', 'TX') OR rx_tx IS NULL),
has_coe_pair INTEGER NOT NULL DEFAULT 0,
coe_pairing_json TEXT,
priority INTEGER NOT NULL CHECK (priority BETWEEN 1 AND 5),
victim_band TEXT,
config_json TEXT,
estimated_minutes INTEGER NOT NULL,
status TEXT NOT NULL CHECK (status IN ('pending', 'completed', 'failed', 'invalid')),
excluded INTEGER NOT NULL DEFAULT 0,
raw_payload TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE(test_id, device)
);
CREATE TABLE IF NOT EXISTS schedules (
id INTEGER PRIMARY KEY AUTOINCREMENT,
schedule_version INTEGER NOT NULL,
test_id TEXT NOT NULL,
device TEXT NOT NULL,
scheduled_date TEXT NOT NULL,
shift_index INTEGER NOT NULL CHECK (shift_index IN (1, 2, 3)),
sequence_in_shift INTEGER NOT NULL,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(test_id, device) REFERENCES tests(test_id, device)
);
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value_json TEXT NOT NULL,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS holidays (
date TEXT PRIMARY KEY,
note TEXT
);
CREATE TABLE IF NOT EXISTS runtime_defaults (
test_type TEXT PRIMARY KEY,
minutes INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS rerun_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
detected_date TEXT NOT NULL,
failed_test_ids_json TEXT NOT NULL,
estimated_rerun_minutes INTEGER NOT NULL,
rerun_during_day INTEGER
);
CREATE INDEX IF NOT EXISTS idx_tests_status ON tests(status);
CREATE INDEX IF NOT EXISTS idx_tests_priority ON tests(priority);
CREATE INDEX IF NOT EXISTS idx_schedules_date_shift ON schedules(scheduled_date, shift_index);
"""
)
_ensure_column(conn, "tests", "has_coe_pair", "INTEGER NOT NULL DEFAULT 0")
_ensure_column(conn, "tests", "coe_pairing_json", "TEXT")
_ensure_column(conn, "tests", "config_json", "TEXT")
_ensure_column(conn, "tests", "victim_band", "TEXT")
_ensure_column(conn, "tests", "excluded", "INTEGER NOT NULL DEFAULT 0")
# Seed runtime defaults for schedule estimation.
conn.execute(
"""
INSERT INTO runtime_defaults(test_type, minutes) VALUES
('P2P', 80),
('COE', 115),
('P3P', 105)
ON CONFLICT(test_type) DO NOTHING
"""
)
def upsert_tests(records: list[TestRecord], db_path: str | Path = DB_PATH) -> int:
if not records:
return 0
with get_connection(db_path) as conn:
conn.executemany(
"""
INSERT INTO tests(
test_id, device, test_type, rotation, rx_tx, has_coe_pair,
coe_pairing_json, config_json, priority, victim_band, estimated_minutes, status, excluded, raw_payload
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(test_id, device) DO UPDATE SET
test_type = excluded.test_type,
device = excluded.device,
rotation = excluded.rotation,
rx_tx = excluded.rx_tx,
has_coe_pair = excluded.has_coe_pair,
coe_pairing_json = excluded.coe_pairing_json,
config_json = excluded.config_json,
priority = excluded.priority,
victim_band = excluded.victim_band,
estimated_minutes = excluded.estimated_minutes,
status = excluded.status,
excluded = excluded.excluded,
raw_payload = excluded.raw_payload,
updated_at = CURRENT_TIMESTAMP
""",
[
(
r.test_id,
r.device,
r.test_type,
r.rotation,
r.rx_tx,
int(r.has_coe_pair),
json.dumps(r.coe_pairing or []),
json.dumps(r.config),
r.priority,
r.victim_band,
r.estimated_minutes,
r.status,
int(r.excluded),
json.dumps(r.raw_payload or {}),
)
for r in records
],
)
return len(records)
def _ensure_column(conn: sqlite3.Connection, table: str, column: str, column_type: str) -> None:
existing = conn.execute(f"PRAGMA table_info({table})").fetchall()
column_names = {row[1] for row in existing}
if column not in column_names:
conn.execute(f"ALTER TABLE {table} ADD COLUMN {column} {column_type}")
def save_settings(payload: dict[str, Any], db_path: str | Path = DB_PATH) -> None:
with get_connection(db_path) as conn:
for key, value in payload.items():
conn.execute(
"""
INSERT INTO settings(key, value_json, updated_at)
VALUES (?, ?, CURRENT_TIMESTAMP)
ON CONFLICT(key) DO UPDATE SET
value_json = excluded.value_json,
updated_at = CURRENT_TIMESTAMP
""",
(key, json.dumps(value)),
)
def read_settings(db_path: str | Path = DB_PATH) -> dict[str, Any]:
with get_connection(db_path) as conn:
rows = conn.execute("SELECT key, value_json FROM settings").fetchall()
return {row["key"]: json.loads(row["value_json"]) for row in rows}
def _parse_rule_tokens(rule: str | None) -> list[str]:
if not rule:
return []
return [token.strip().upper() for token in rule.split(",") if token.strip()]
def _normalize_text(value: Any) -> str:
if value is None:
return ""
return " ".join(str(value).strip().upper().split())
def _normalize_compact(value: Any) -> str:
return re.sub(r"[^A-Z0-9+\-]", "", _normalize_text(value))
def _extract_signed_int(value: Any) -> str:
match = re.search(r"[-+]?\d+", _normalize_text(value))
return match.group(0) if match else ""
def _band_entry(config: dict[str, dict[str, str | None]], band: str) -> dict[str, str | None]:
return config.get(band) or config.get(band.lower()) or {}
def _entry_value(entry: dict[str, str | None], key: str) -> str:
if not entry:
return ""
return _normalize_text(entry.get(key) or entry.get(key.capitalize()) or entry.get(key.upper()))
def _match_any_band_value(record: TestRecord, key: str, token_suffix: str) -> bool:
if not token_suffix:
return False
target_num = _extract_signed_int(token_suffix)
target_compact = _normalize_compact(token_suffix)
for band in ("5G", "6G", "2G"):
entry = _band_entry(record.config, band)
value = _entry_value(entry, key)
if not value:
continue
if target_num:
if _extract_signed_int(value) == target_num:
return True
elif target_compact and _normalize_compact(value) == target_compact:
return True
return False
def _matches_atomic_exclusion_token(record: TestRecord, token: str) -> bool:
if not token:
return False
device = _normalize_text(record.device)
test_type = _normalize_text(record.test_type)
rotation = _normalize_text(record.rotation)
victim_band = _normalize_text(record.victim_band)
power_mode = _entry_value(_band_entry(record.config, "6G"), "power_mode")
known_devices = {_normalize_text(DUT), _normalize_text(REF)}
if token in {"LPI", "SP"}:
return power_mode == token
if token in known_devices:
return device == token
if token in {"P2P", "P3P", "COE"}:
return test_type == token
if token in {"2G", "5G", "6G"}:
return victim_band == token
if token.startswith("BW"):
return _match_any_band_value(record, "bandwidth", token[2:])
if token.startswith("CH"):
return _match_any_band_value(record, "channel", token[2:])
if token.startswith("RSSI"):
return _match_any_band_value(record, "rssi", token[4:])
if token.startswith("R") and len(token) > 1:
return rotation == token
return False
def _matches_exclusion_token(record: TestRecord, token: str) -> bool:
if not token:
return False
parts = [part for part in token.split("_") if part]
if len(parts) > 1:
# Treat underscore as logical AND across sub-tokens.
return all(_matches_atomic_exclusion_token(record, part) for part in parts)
return _matches_atomic_exclusion_token(record, token)
def _should_exclude_by_rule(record: TestRecord, tokens: list[str]) -> bool:
return any(_matches_exclusion_token(record, token) for token in tokens)
def list_schedulable_tests(db_path: str | Path = DB_PATH, rule: str = "") -> list[TestRecord]:
tokens = _parse_rule_tokens(rule)
with get_connection(db_path) as conn:
rows = conn.execute(
"""
SELECT
test_id, device, test_type, rotation, rx_tx, has_coe_pair,
config_json, victim_band,
coe_pairing_json, priority, estimated_minutes,
excluded, status, raw_payload
FROM tests
WHERE excluded = 0
AND status != 'completed'
"""
).fetchall()
results: list[TestRecord] = []
for row in rows:
results.append(
TestRecord(
test_id=row["test_id"],
device=row["device"],
test_type=row["test_type"],
rotation=row["rotation"],
rx_tx=row["rx_tx"],
has_coe_pair=bool(row["has_coe_pair"]),
coe_pairing=json.loads(row["coe_pairing_json"] or "[]"),
priority=int(row["priority"]),
victim_band=row["victim_band"],
config=json.loads(row["config_json"] or "{}"),
estimated_minutes=int(row["estimated_minutes"]),
status=row["status"],
excluded=bool(row["excluded"]),
raw_payload=json.loads(row["raw_payload"] or "{}"),
)
)
if not tokens:
return results
return [record for record in results if not _should_exclude_by_rule(record, tokens)]
# Get all tests for a specific device, regardless of exclusion or completion status. Used for schedule display and management.
def list_tests_for_device(device: str, db_path: str | Path = DB_PATH) -> list[TestRecord]:
with get_connection(db_path) as conn:
rows = conn.execute(
"""
SELECT
test_id, device, test_type, rotation, rx_tx, has_coe_pair,
config_json, victim_band,
coe_pairing_json, priority, estimated_minutes,
excluded, status, raw_payload
FROM tests
WHERE device = ?
""",
(device,),
).fetchall()
results: list[TestRecord] = []
for row in rows:
results.append(
TestRecord(
test_id=row["test_id"],
device=row["device"],
test_type=row["test_type"],
rotation=row["rotation"],
rx_tx=row["rx_tx"],
has_coe_pair=bool(row["has_coe_pair"]),
coe_pairing=json.loads(row["coe_pairing_json"] or "[]"),
priority=int(row["priority"]),
victim_band=row["victim_band"],
config=json.loads(row["config_json"] or "{}"),
estimated_minutes=int(row["estimated_minutes"]),
status=row["status"],
excluded=bool(row["excluded"]),
raw_payload=json.loads(row["raw_payload"] or "{}"),
)
)
return results
def list_holidays(db_path: str | Path = DB_PATH) -> set[str]:
with get_connection(db_path) as conn:
rows = conn.execute("SELECT date FROM holidays").fetchall()
return {row["date"] for row in rows}
def create_schedule_version(
entries: list[tuple[str, str, str, int, int]],
db_path: str | Path = DB_PATH,
) -> int:
with get_connection(db_path) as conn:
row = conn.execute("SELECT COALESCE(MAX(schedule_version), 0) AS current FROM schedules").fetchone()
next_version = int(row["current"]) + 1
if entries:
conn.executemany(
"""
INSERT INTO schedules(schedule_version, test_id, device, scheduled_date, shift_index, sequence_in_shift)
VALUES (?, ?, ?, ?, ?, ?)
""",
[
(next_version, test_id, device, scheduled_date, shift_index, sequence)
for test_id, device, scheduled_date, shift_index, sequence in entries
],
)
return next_version
def get_schedule_week(start_date: str, db_path: str | Path = DB_PATH) -> list[ScheduleRow]:
with get_connection(db_path) as conn:
version_row = conn.execute("SELECT MAX(schedule_version) AS latest FROM schedules").fetchone()
latest = version_row["latest"]
if latest is None:
return []
rows = conn.execute(
"""
SELECT
s.test_id,
s.device,
s.scheduled_date,
s.shift_index,
s.sequence_in_shift,
t.test_type,
t.rotation,
t.status,
t.priority,
t.estimated_minutes
FROM schedules s
JOIN tests t ON t.test_id = s.test_id
WHERE s.schedule_version = ?
AND s.scheduled_date >= ?
AND s.scheduled_date < date(?, '+7 day')
ORDER BY s.scheduled_date, s.shift_index, s.sequence_in_shift
""",
(latest, start_date, start_date),
).fetchall()
result: list[ScheduleRow] = []
for row in rows:
result.append(
ScheduleRow(
test_id=row["test_id"],
device=row["device"],
scheduled_date=row["scheduled_date"],
shift_index=int(row["shift_index"]),
sequence_in_shift=int(row["sequence_in_shift"]),
test_type=row["test_type"],
rotation=row["rotation"],
status=row["status"],
priority=int(row["priority"]),
estimated_minutes=int(row["estimated_minutes"]),
)
)
return result
def mark_tests_completed(test_ids_with_device: list[tuple[str, str]], db_path: str | Path = DB_PATH) -> None:
if not test_ids_with_device:
return
with get_connection(db_path) as conn:
conn.executemany(
"""
UPDATE tests
SET status = 'completed', updated_at = CURRENT_TIMESTAMP
WHERE test_id = ?
AND device = ?
AND status != 'completed'
""",
test_ids_with_device,
)
+92
View File
@@ -0,0 +1,92 @@
from __future__ import annotations
from typing import Any
# Immutable-in-practice graph state for the loaded DUT test set.
_TESTS_BY_ID: dict[str, Any] = {}
_GRAPH: dict[str, set[str]] = {}
def reset_graph_state() -> None:
global _TESTS_BY_ID, _GRAPH
_TESTS_BY_ID = {}
_GRAPH = {}
def is_graph_built() -> bool:
return bool(_GRAPH)
def build_graph_once(tests: list[Any]) -> dict[str, set[str]]:
"""Build DUT-only compatibility adjacency graph once per load lifecycle."""
global _TESTS_BY_ID, _GRAPH
if _GRAPH:
return _GRAPH
# Keep only one representative per DUT test_id.
_TESTS_BY_ID = {str(t.test_id): t for t in tests if getattr(t, "test_id", None)}
_GRAPH = _build_graph(_TESTS_BY_ID)
return _GRAPH
def get_graph() -> dict[str, set[str]]:
return _GRAPH
def _build_graph(tests: dict[str, Any]) -> dict[str, set[str]]:
test_ids = list(tests.keys())
graph = {test_id: set() for test_id in test_ids}
for i in range(len(test_ids)):
for j in range(i + 1, len(test_ids)):
a_id = test_ids[i]
b_id = test_ids[j]
a = tests[a_id]
b = tests[b_id]
if _compatible(a, b):
graph[a_id].add(b_id)
graph[b_id].add(a_id)
return graph
def _compatible(a: Any, b: Any) -> bool:
# Compatible when same rotation, or both non-P3P and overlap has identical testpoints.
if a.rotation == b.rotation:
return True
if a.test_type != "P3P" and b.test_type != "P3P" and _same_test_points_for_overlap(a.config, b.config):
return True
return False
def _same_test_points_for_overlap(
config_a: dict[str, dict[str, str | None]],
config_b: dict[str, dict[str, str | None]],
) -> bool:
station_map_a = _build_station_testpoint_map(config_a)
station_map_b = _build_station_testpoint_map(config_b)
overlap = set(station_map_a.keys()) & set(station_map_b.keys())
for station in overlap:
if station_map_a[station] != station_map_b[station]:
return False
return True
def _build_station_testpoint_map(config: dict[str, dict[str, str | None]]) -> dict[str, str]:
station_to_testpoint: dict[str, str] = {}
for band in ("5G", "6G", "2G"):
entry = config.get(band) or {}
testpoint = _norm(entry.get("test_point") or entry.get("Testpoint"))
sta_raw = _norm(entry.get("sta") or entry.get("STA"))
if not testpoint or not sta_raw:
continue
for sta in sta_raw.split(","):
sta_clean = _norm(sta)
if sta_clean:
station_to_testpoint[sta_clean] = testpoint
return station_to_testpoint
def _norm(value: Any) -> str:
if value is None:
return ""
return " ".join(str(value).strip().upper().split())
+2515
View File
File diff suppressed because it is too large Load Diff
+301
View File
@@ -0,0 +1,301 @@
import csv
import os
from dataclasses import dataclass
from pathlib import Path
from db import DEVICE_DUT, DEVICE_REF, TestRecord
REQUIRED_COLUMNS = [
"Priority",
"Index",
"Interferer",
"COE Pair",
"Rotation",
"TC ID",
"Victim Band",
"6GHz Power Mode",
"5G Test Point",
"5G Channel",
"5G Bandwidth",
"5G RSSI",
"5G Direction",
"5G STA",
"6G Test Point",
"6G Channel",
"6G Bandwidth",
"6G RSSI",
"6G Direction",
"6G STA",
"2G Test Point",
"2G Channel",
"2G Bandwidth",
"2G RSSI",
"2G Direction",
"2G STA",
]
RUNTIME_DEFAULTS = {
"P2P": 80,
"COE": 115,
"P3P": 105,
}
@dataclass(frozen=True)
class ParseResult:
tests: list[TestRecord]
warnings: list[str]
class CsvValidationError(ValueError):
pass
def parse_target_csv(csv_path: str | Path) -> ParseResult:
path = Path(csv_path)
if not path.exists():
raise FileNotFoundError(f"CSV file not found: {path}")
with path.open("r", encoding="utf-8-sig", newline="") as handle:
reader = csv.DictReader(handle)
if not reader.fieldnames:
raise CsvValidationError("CSV is missing a header row.")
normalized_fieldnames = {name.strip().upper() for name in reader.fieldnames if name}
# Check for required columns
missing_columns = [
c
for c in REQUIRED_COLUMNS
if c.strip().upper() not in normalized_fieldnames
]
if missing_columns:
raise CsvValidationError(
f"CSV is missing required columns: {', '.join(missing_columns)}"
)
tests: list[TestRecord] = []
warnings: list[str] = []
seen_test_ids: set[str] = set()
records_with_signature: list[tuple[TestRecord, tuple[str, ...] | None]] = []
for row_num, row in enumerate(reader, start=2):
test_id = (row.get("TC ID") or "").strip()
if not test_id:
warnings.append(f"Row {row_num}: missing TC ID, row skipped.")
continue
if test_id in seen_test_ids:
warnings.append(f"Row {row_num}: duplicate TC ID '{test_id}', row skipped.")
continue
seen_test_ids.add(test_id)
test_type = _infer_test_type(test_id)
rx_tx = _infer_rx_tx(test_id)
rotation = _empty_to_none(_row_get(row, "Rotation"))
has_coe_pair = _normalize_yes_no(_row_get(row, "COE Pair"))
config = _build_config(row)
signature = _victim_band_signature(row)
estimated_minutes = RUNTIME_DEFAULTS.get(test_type)
victim_band = _normalize_victim_band(_row_get(row, "Victim Band"))
# Default priority based on test type and COE pairing
if test_type == "P2P":
priority = 2 if has_coe_pair else 3
elif test_type == "COE":
priority = 3
else:
priority = 4
record = TestRecord(
test_id=test_id,
device=DEVICE_DUT,
test_type=test_type,
rotation=rotation,
rx_tx=rx_tx,
has_coe_pair=has_coe_pair,
coe_pairing=[],
priority=priority,
victim_band=victim_band,
config=config,
estimated_minutes=estimated_minutes,
status="pending",
excluded=False,
raw_payload=row,
)
records_with_signature.append((record, signature))
# Build COE pairing based on victim band signature and RX/TX+band suffix.
# Example key suffixes: RXAX, TXAX, RXBE, TXBE.
coe_by_signature_and_suffix: dict[tuple, list[str]] = {}
for record, signature in records_with_signature:
if record.test_type == "COE" and signature is not None:
suffix = _extract_pairing_suffix(record.test_id)
if suffix:
key = (signature, suffix)
coe_by_signature_and_suffix.setdefault(key, []).append(record.test_id)
for record, signature in records_with_signature:
pairs = record.coe_pairing
if record.test_type == "P2P" and signature is not None:
suffix = _extract_pairing_suffix(record.test_id)
key = (signature, suffix) if suffix else None
pairs = sorted(coe_by_signature_and_suffix.get(key, [])) if key else []
for device in (DEVICE_DUT, DEVICE_REF):
tests.append(
TestRecord(
test_id=record.test_id,
device=device,
test_type=record.test_type,
rotation=record.rotation,
rx_tx=record.rx_tx,
has_coe_pair=bool(pairs),
coe_pairing=pairs,
priority=record.priority,
victim_band=record.victim_band,
config=record.config,
estimated_minutes=record.estimated_minutes,
status=record.status,
excluded=record.excluded,
raw_payload=record.raw_payload,
)
)
return ParseResult(tests=tests, warnings=warnings)
def _infer_test_type(test_id: str) -> str:
token = test_id.upper()
if token.startswith("COE"):
return "COE"
if token.startswith("P3P"):
return "P3P"
return "P2P"
def _infer_rx_tx(test_id: str) -> str | None:
token = test_id.upper()
if "RX" in token:
return "RX"
if "TX" in token:
return "TX"
return None
def _extract_pairing_suffix(test_id: str) -> str | None:
"""Extract the RX/TX+band suffix used for pairing.
Format: [COE|P2P][RX|TX][Band][Number]
Example: P2PRXAC004 -> 'RXAC', COETXAX012 -> 'TXAX'
"""
token = test_id.upper()
# Remove COE/P2P prefix
if token.startswith("COE"):
token = token[3:]
elif token.startswith("P2P") or token.startswith("P3P"):
token = token[3:]
else:
return None
# Extract RX/TX + band (for example RXAX, TXBE).
if len(token) >= 4 and (token.startswith("RX") or token.startswith("TX")):
return token[:4]
return None
def _empty_to_none(value: str | None) -> str | None:
if value is None:
return None
cleaned = value.strip()
return cleaned if cleaned else None
def _victim_band_signature(row: dict[str, str]) -> tuple[str, ...] | None:
band = _normalize_victim_band(_row_get(row, "Victim Band"))
if band is None:
return None
prefix = f"{band} "
test_point = _normalize_value(_row_get(row, f"{prefix}Test Point"))
channel = _normalize_value(_row_get(row, f"{prefix}Channel"))
rssi = _normalize_value(_row_get(row, f"{prefix}RSSI"))
bandwidth = _normalize_value(_row_get(row, f"{prefix}Bandwidth"))
# Direction is intentionally ignored for COE pairing matching.
if not all([test_point, channel, rssi, bandwidth]):
return None
return (band, test_point, channel, rssi, bandwidth)
def _normalize_victim_band(value: str | None) -> str | None:
if value is None:
return None
normalized = value.strip().upper().replace("GHZ", "G")
if normalized.startswith("5"):
return "5G"
if normalized.startswith("6"):
return "6G"
if normalized.startswith("2"):
return "2G"
return None
def _normalize_value(value: str | None) -> str:
if value is None:
return ""
return " ".join(value.strip().upper().split())
def _normalize_yes_no(value: str | None) -> bool:
return _normalize_value(value) == "YES"
def _build_config(row: dict[str, str]) -> dict[str, dict[str, str | None]]:
return {
"5G": {
"test_point": _empty_to_none(_row_get(row, "5G Test Point")),
"channel": _empty_to_none(_row_get(row, "5G Channel")),
"bandwidth": _empty_to_none(_row_get(row, "5G Bandwidth")),
"rssi": _empty_to_none(_row_get(row, "5G RSSI")),
"direction": _empty_to_none(_row_get(row, "5G Direction")),
"sta": _empty_to_none(_row_get(row, "5G STA")),
},
"6G": {
"power_mode": _empty_to_none(_row_get(row, "6GHz Power Mode")),
"test_point": _empty_to_none(_row_get(row, "6G Test Point")),
"channel": _empty_to_none(_row_get(row, "6G Channel")),
"bandwidth": _empty_to_none(_row_get(row, "6G Bandwidth")),
"rssi": _empty_to_none(_row_get(row, "6G RSSI")),
"direction": _empty_to_none(_row_get(row, "6G Direction")),
"sta": _empty_to_none(_row_get(row, "6G STA")),
},
"2G": {
"test_point": _empty_to_none(_row_get(row, "2G Test Point")),
"channel": _empty_to_none(_row_get(row, "2G Channel")),
"bandwidth": _empty_to_none(_row_get(row, "2G Bandwidth")),
"rssi": _empty_to_none(_row_get(row, "2G RSSI")),
"direction": _empty_to_none(_row_get(row, "2G Direction")),
"sta": _empty_to_none(_row_get(row, "2G STA")),
},
}
def _row_get(row: dict[str, str], key: str) -> str | None:
if key in row:
return row.get(key)
normalized_key = key.strip().upper()
for existing_key, value in row.items():
if existing_key and existing_key.strip().upper() == normalized_key:
return value
if normalized_key == "COE PAIR":
for alias in ("COE PAIRING", "COE_PAIRING"):
for existing_key, value in row.items():
if existing_key and existing_key.strip().upper() == alias:
return value
return None
+5
View File
@@ -0,0 +1,5 @@
fastapi==0.116.0
uvicorn==0.35.0
pydantic==2.11.7
watchdog==6.0.0
httpx==0.28.1
+262
View File
@@ -0,0 +1,262 @@
import os
import re
import threading
try:
import smbclient
except ModuleNotFoundError:
smbclient = None
from db import DEVICE_DUT, DEVICE_REF, mark_tests_completed
_SMB_SESSIONS = set()
_SCAN_STATE_LOCK = threading.Lock()
_ACTIVE_SCAN_COUNT = 0
_RESULT_TEST_ID_PATTERN = re.compile(r"(?:COE|P2P|P3P)(?:RX|TX)?[A-Z]{2}\d{3}", re.IGNORECASE)
def _extract_test_id_from_result_dir_name(dir_name):
if not dir_name:
return None
match = _RESULT_TEST_ID_PATTERN.search(str(dir_name).upper())
if match:
return match.group(0)
return None
def _build_match_tokens(parsed):
tokens = set()
def _add(value):
if value in (None, ""):
return
tokens.add(str(value).upper())
_add(parsed.get("interference"))
_add(parsed.get("device"))
_add(parsed.get("rotation"))
_add(parsed.get("test_point"))
_add(parsed.get("rssi"))
_add(parsed.get("station"))
_add(parsed.get("band"))
_add(parsed.get("channel"))
_add(parsed.get("bandwidth"))
_add(parsed.get("direction"))
_add(parsed.get("throttled"))
_add(parsed.get("test_id"))
for bw in parsed.get("extra_bandwidths") or []:
_add(bw)
# Add numeric alias for devices, e.g. CGW453 -> 453.
device = (parsed.get("device") or "").upper()
device_digits = re.sub(r"\D", "", device)
if device_digits:
tokens.add(device_digits)
# SP can appear as flag or specific token variant (e.g., SP40).
if parsed.get("sp"):
tokens.add("SP")
tokens.add(str(parsed.get("sp")).upper())
return tokens
def _rule_matches_target(rule, parsed_tokens):
# Rule parts are AND-ed: CGW453_P3P_ROT2 => device AND interference AND rotation.
# Support _, -, or spaces as condition separators.
parts = [p for p in re.split(r"[_\-\s]+", rule) if p]
if not parts:
return False
def _part_matches(part):
if part in parsed_tokens:
return True
# Support tag variants (e.g., BW80 should match BW80M/BW80+80 in source names).
# Keep this conservative for very short parts to avoid overmatching.
if len(part) >= 3:
for token in parsed_tokens:
if token.startswith(part) or part in token:
return True
return False
return all(_part_matches(part) for part in parts)
def _should_exclude_target(parsed, exclusions):
parsed_tokens = _build_match_tokens(parsed)
# Any matching rule excludes the testcase.
return any(_rule_matches_target(rule, parsed_tokens) for rule in exclusions)
def _scan_started():
global _ACTIVE_SCAN_COUNT
with _SCAN_STATE_LOCK:
_ACTIVE_SCAN_COUNT += 1
def _scan_finished():
global _ACTIVE_SCAN_COUNT
with _SCAN_STATE_LOCK:
_ACTIVE_SCAN_COUNT = max(0, _ACTIVE_SCAN_COUNT - 1)
def is_scan_in_progress():
with _SCAN_STATE_LOCK:
return _ACTIVE_SCAN_COUNT > 0
def resolve_runtime_path(path_value):
if not path_value:
return path_value
raw_path = str(path_value).strip()
if not raw_path:
return raw_path
# If the path is already valid in the current runtime, keep it.
if os.path.exists(raw_path):
return raw_path
# UNC/network paths are handled separately via smbclient.
if raw_path.startswith("\\\\") or raw_path.startswith("//"):
return raw_path
# Map host paths (Windows or Linux) to the container mount point when running in a container.
if os.name != "nt":
mount_root = os.getenv("HOST_MOUNT_ROOT", "/host").strip() or "/host"
host_root = os.getenv("HOST_BROWSE_ROOT", "").strip()
raw_norm = raw_path.replace("\\", "/")
if host_root:
host_norm = host_root.replace("\\", "/").rstrip("/")
if raw_norm.lower() == host_norm.lower() or raw_norm.lower().startswith(host_norm.lower() + "/"):
relative = raw_norm[len(host_norm):].lstrip("/")
if relative:
return os.path.join(mount_root, *relative.split("/"))
return mount_root
return raw_path
def _normalize_input_path(path_value):
if not path_value:
return path_value
path = resolve_runtime_path(path_value)
path = str(path).strip()
# Accept //server/share style and normalize to UNC for smbclient.
if path.startswith("//"):
return "\\\\" + path.lstrip("/").replace("/", "\\")
# Accept /<ipv4>/<share>/... and normalize to UNC for Linux-hosted inputs.
if re.match(r"^/\d{1,3}(?:\.\d{1,3}){3}/[^/]+", path):
return "\\\\" + path.lstrip("/").replace("/", "\\")
return path
def _is_unc_path(path):
return isinstance(path, str) and path.startswith("\\\\")
def _extract_unc_server(path):
if not _is_unc_path(path):
return None
rest = path[2:]
return rest.split("\\", 1)[0] if rest else None
def _register_smb_session_if_needed(path):
if not _is_unc_path(path):
return
if smbclient is None:
raise ModuleNotFoundError("smbclient is required to scan UNC result paths")
server = _extract_unc_server(path)
if not server or server in _SMB_SESSIONS:
return
username = os.getenv("SMB_USERNAME", "").strip()
password = os.getenv("SMB_PASSWORD", "")
domain = os.getenv("SMB_DOMAIN", "").strip()
if username and domain and "\\" not in username and "@" not in username:
username = f"{domain}\\{username}"
if username:
smbclient.register_session(server, username=username, password=password)
else:
smbclient.register_session(server)
_SMB_SESSIONS.add(server)
def _iter_dir_entries(path):
path = _normalize_input_path(path)
if _is_unc_path(path):
if smbclient is None:
raise ModuleNotFoundError("smbclient is required to scan UNC result paths")
_register_smb_session_if_needed(path)
return list(smbclient.scandir(path))
return list(os.scandir(path))
def scan_results(results_dir_dut, results_dir_ref):
results_dir_dut = _normalize_input_path(results_dir_dut)
results_dir_ref = _normalize_input_path(results_dir_ref)
if not results_dir_dut or not results_dir_ref:
return
try:
dut_entries = [
entry.name
for entry in _iter_dir_entries(results_dir_dut)
if entry.is_dir()
]
except OSError as exc:
print(f"[scanner] Cannot read results dir: {exc}")
return
print(f"[scanner] results: {len(dut_entries)} result dir(s) found in DUT results dir")
try:
ref_entries = [
entry.name
for entry in _iter_dir_entries(results_dir_ref)
if entry.is_dir()
]
except OSError as exc:
print(f"[scanner] Cannot read results dir: {exc}")
return
print(f"[scanner] results: {len(ref_entries)} result dir(s) found in reference results dir")
completed_batch = []
unmatched_entries = []
for entry_name in dut_entries:
test_id = _extract_test_id_from_result_dir_name(entry_name)
if test_id:
completed_batch.append((test_id, DEVICE_DUT))
else:
unmatched_entries.append(entry_name)
for entry_name in ref_entries:
test_id = _extract_test_id_from_result_dir_name(entry_name)
if test_id:
completed_batch.append((test_id, DEVICE_REF))
else:
unmatched_entries.append(entry_name)
if unmatched_entries:
print(f"[scanner] skipped {len(unmatched_entries)} result dir(s) with no recognizable test id")
mark_tests_completed(completed_batch)
+548
View File
@@ -0,0 +1,548 @@
from __future__ import annotations
import os
from dataclasses import dataclass
from datetime import date, datetime, timedelta
from typing import Any
import graph
# Bundle priority tiers for scheduling order (lower number = higher priority)
BUNDLE_PRIORITY_FAILED = 0 # Failed tests requiring rerun
BUNDLE_PRIORITY_P2P_WITH_COE = 1 # P2P tests with COE pairs
BUNDLE_PRIORITY_P2P_ONLY = 2 # P2P tests without COE pairs (RX/TX bundled)
BUNDLE_PRIORITY_COE_ONLY = 3 # COE tests without P2P pairing (should be rare/unschedulable)
BUNDLE_PRIORITY_P3P = 4 # P3P tests
DUT = os.getenv("DUT", "CGW453").strip()
REF = os.getenv("REF", "CGW452").strip()
TestKey = tuple[str, str]
@dataclass(frozen=True)
class SchedulerTest:
test_id: str
device: str
test_type: str
rotation: str | None
rx_tx: str | None
has_coe_pair: bool
coe_pairing: list[str]
config: dict[str, dict[str, str | None]]
estimated_minutes: int
priority: int
raw_payload: dict[str, Any]
@dataclass(frozen=True)
class ScheduleEntry:
test_id: str
device: str
scheduled_date: str
shift_index: int
sequence_in_shift: int
@dataclass(frozen=True)
class TestBundle:
"""A bundle of tests that must be run together in sequence."""
test_ids: list[TestKey]
priority_tier: int
total_minutes: int
# Global scheduler state. Graph is built once per loaded test set.
_TESTS_BY_ID: dict[TestKey, SchedulerTest] = {}
_ACTIVE_DUT: dict[TestKey, int] = {}
_ACTIVE_REF: dict[TestKey, int] = {}
def reset_scheduler_state() -> None:
global _TESTS_BY_ID, _ACTIVE_DUT, _ACTIVE_REF
_TESTS_BY_ID = {}
_ACTIVE_DUT = {}
_ACTIVE_REF = {}
def initialize_scheduler_state(tests: list[SchedulerTest]) -> None:
"""Initialize active scheduler state once for the current loaded schedulable dataset."""
global _TESTS_BY_ID, _ACTIVE_DUT, _ACTIVE_REF
if _TESTS_BY_ID:
return
_TESTS_BY_ID = {(t.test_id, t.device): t for t in tests}
_ACTIVE_DUT = {
test_id: _derive_priority(test, set(), set(), tests)
for test_id, test in _TESTS_BY_ID.items() if test.device == DUT
}
_ACTIVE_REF = {
test_id: _derive_priority(test, set(), set(), tests)
for test_id, test in _TESTS_BY_ID.items() if test.device == REF
}
def set_user_priorities(top_priority_tests: set[str], lowest_priority_tests: set[str]) -> None:
"""Update active priority map in-place using user overrides."""
if not _TESTS_BY_ID:
return
all_tests = list(_TESTS_BY_ID.values())
for key in list(_ACTIVE_DUT.keys()):
test = _TESTS_BY_ID.get(key)
if test is None:
continue
_ACTIVE_DUT[key] = _derive_priority(test, top_priority_tests, lowest_priority_tests, all_tests)
for key in list(_ACTIVE_REF.keys()):
test = _TESTS_BY_ID.get(key)
if test is None:
continue
_ACTIVE_REF[key] = _derive_priority(test, top_priority_tests, lowest_priority_tests, all_tests)
def remove_from_active(completed_test_ids: set[str]) -> None:
"""Remove completed/invalid tests from active list without rebuilding graph."""
for test_id in completed_test_ids:
_ACTIVE_DUT.pop((test_id, DUT), None)
_ACTIVE_REF.pop((test_id, REF), None)
def compile_schedule(
tests: list[SchedulerTest],
start_date: str | None,
holiday_dates: set[str],
top_priority_tests: set[str],
lowest_priority_tests: set[str],
daytime_testing_today: bool,
) -> tuple[list[ScheduleEntry], str | None]:
"""Compile an optimized schedule for the given tests.
Uses the greedy algorithm respecting shift sequences per design:
- Mon-Thu: shift 3 (5pm-1am) then shift 1 next day (1am-10am)
- Friday: shift 3 through Monday shift 1 (4-day window)
Returns a list of ScheduleEntry objects and the completion date.
"""
if not tests:
return [], None
if not graph.is_graph_built():
raise RuntimeError("Graph not initialized. Load tests first to build DUT compatibility graph.")
initialize_scheduler_state(tests)
set_user_priorities(top_priority_tests, lowest_priority_tests)
# Use a local working copy for this compile run. Global active remains until result processing removes IDs.
dut_active_priority: dict[TestKey, int] = dict(_ACTIVE_DUT)
ref_active_priority: dict[TestKey, int] = dict(_ACTIVE_REF)
entries: list[ScheduleEntry] = []
current_date = _parse_date(start_date)
last_date: str | None = None
while dut_active_priority or ref_active_priority:
# Get the shift sequence for current date (respects Mon/Fri/weekend rules)
shift_sequence = _get_shift_sequence(current_date, holiday_dates)
if not shift_sequence:
break # No valid shift sequence
dut_active_test_ids: set[TestKey] = set(dut_active_priority.keys())
ref_active_test_ids: set[TestKey] = set(ref_active_priority.keys())
bundles = _create_bundles(dut_active_test_ids, ref_active_test_ids, _TESTS_BY_ID)
shift_capacities = {
(date_obj, shift_idx): _shift_capacity_for_date(
current_date=date_obj,
holiday_dates=holiday_dates,
start_date_override=start_date,
daytime_testing_today=daytime_testing_today,
).get(shift_idx, 0)
for date_obj, shift_idx in shift_sequence
}
placed_entries, placed_test_ids, placed_last_date = _fit_bundles_to_shifts(
bundles=bundles,
shift_sequence=shift_sequence,
tests=_TESTS_BY_ID,
graph_by_test_id=graph.get_graph(),
shift_capacities=shift_capacities,
)
entries.extend(placed_entries)
for key in placed_test_ids:
dut_active_priority.pop(key, None)
ref_active_priority.pop(key, None)
if placed_last_date is not None:
last_date = placed_last_date
# Move to next scheduling window start date
# After shift 1 (1am-9am), there's shift 2 if daytime testing, then shift 3 (5pm)
# After shift 3 (5pm), shift 1 is next day (1am)
if shift_sequence:
last_sequence_date = shift_sequence[-1][0]
last_shift_index = shift_sequence[-1][1]
if last_shift_index == 1:
# Last shift was 1 (1am-9am); next shift 3 is same day (5pm)
current_date = last_sequence_date
elif last_shift_index == 2:
# Last shift was 2 (9am-5pm); next shift 3 is same day (5pm)
current_date = last_sequence_date
else: # last_shift_index == 3
# Last shift was 3 (5pm-1am); next shift 1 is next day (1am)
current_date = last_sequence_date + timedelta(days=1)
else:
current_date = current_date + timedelta(days=1)
return entries, last_date
def format_schedule_for_frontend(
entries: list[ScheduleEntry],
) -> dict[str, dict[int, list[SchedulerTest]]]:
"""Convert flat ScheduleEntry list to nested format for frontend.
Returns: {date_string: {shift_index: [SchedulerTest, ...]}, ...}
"""
schedule: dict[str, dict[int, list[SchedulerTest]]] = {}
for entry in entries:
if entry.scheduled_date not in schedule:
schedule[entry.scheduled_date] = {1: [], 2: [], 3: []}
test = _TESTS_BY_ID.get((entry.test_id, entry.device))
if test:
schedule[entry.scheduled_date][entry.shift_index].append(test)
return schedule
def _derive_priority(
test: SchedulerTest,
top_priority_tests: set[str],
lowest_priority_tests: set[str],
all_tests: list[SchedulerTest],
) -> int:
if test.test_id in top_priority_tests:
return 1
if test.test_id in lowest_priority_tests:
return 5
return test.priority
def _fit_bundles_to_shifts(
bundles: list[TestBundle],
shift_sequence: list[tuple[date, int]],
tests: dict[TestKey, SchedulerTest],
graph_by_test_id: dict[str, set[str]],
shift_capacities: dict[tuple[date, int], int],
) -> tuple[list[ScheduleEntry], set[TestKey], str | None]:
"""Fit bundles into a shift sequence window (e.g., one day or one weekend).
Returns:
- List of ScheduleEntry for placed tests
- Set of test_ids that were placed
- Last scheduled date
Bundles are placed in sequence order and may span multiple shifts inside the same window.
"""
entries: list[ScheduleEntry] = []
placed_test_ids: set[TestKey] = set()
last_date: str | None = None
# Track state per shift
shift_state: dict[tuple[date, int], dict] = {}
for date_shift in shift_sequence:
shift_state[date_shift] = {
'remaining_minutes': shift_capacities.get(date_shift, 0),
'sequence_counter': 1,
}
shift_positions = {date_shift: idx for idx, date_shift in enumerate(shift_sequence)}
current_shift_pos = 0
window_placed_test_ids: set[str] = set()
for bundle in bundles:
# Skip bundles that are already fully placed.
if any(test_id in placed_test_ids for test_id in bundle.test_ids):
continue
if not bundle.test_ids:
continue
# Keep all tests in the same window pairwise-compatible.
if window_placed_test_ids:
is_compatible_with_window = True
bundle_test_ids_only = {test_key[0] for test_key in bundle.test_ids}
for placed_test_id in window_placed_test_ids:
placed_neighbors = graph_by_test_id.get(placed_test_id, set())
if not all(current_test_id in placed_neighbors for current_test_id in bundle_test_ids_only):
is_compatible_with_window = False
break
if not is_compatible_with_window:
continue
# Ensure the whole bundle can still fit somewhere in the remaining window.
remaining_window = sum(
shift_state[date_shift]['remaining_minutes'] for date_shift in shift_sequence[current_shift_pos:]
)
if remaining_window < bundle.total_minutes:
continue
bundle_start_shift_pos = current_shift_pos
placed_bundle_tests: list[tuple[TestKey, tuple[date, int]]] = []
failed = False
for key in bundle.test_ids:
test = tests[key]
prev_remaining = 0
while current_shift_pos < len(shift_sequence):
date_shift = shift_sequence[current_shift_pos]
state = shift_state[date_shift]
state["remaining_minutes"] += prev_remaining # Add back any leftover from previous shift
if state['remaining_minutes'] >= test.estimated_minutes:
print(f"remaining minutes for {date_shift}: {state['remaining_minutes']} - placing {key} ({test.estimated_minutes}m)")
entries.append(
ScheduleEntry(
test_id=test.test_id,
device=test.device,
scheduled_date=date_shift[0].isoformat(),
shift_index=date_shift[1],
sequence_in_shift=state['sequence_counter'],
)
)
state['sequence_counter'] += 1
state['remaining_minutes'] -= test.estimated_minutes
placed_test_ids.add(key)
placed_bundle_tests.append((key, date_shift))
last_date = date_shift[0].isoformat()
break
# Move to the next shift in the sequence and keep the bundle contiguous.
current_shift_pos += 1
prev_remaining = state['remaining_minutes']
if current_shift_pos >= len(shift_sequence):
failed = True
break
if failed:
break
if failed:
# Roll back any partially placed tests from this bundle.
for key, date_shift in reversed(placed_bundle_tests):
state = shift_state[date_shift]
state['remaining_minutes'] += tests[key].estimated_minutes
state['sequence_counter'] -= 1
entries.pop()
placed_test_ids.discard(key)
last_date = None
current_shift_pos = bundle_start_shift_pos
continue
if placed_bundle_tests:
window_placed_test_ids.update(test_key[0] for test_key in bundle.test_ids)
return entries, placed_test_ids, last_date
def _create_bundles(
dut_active_test_ids: set[TestKey],
ref_active_test_ids: set[TestKey],
tests: dict[TestKey, SchedulerTest],
) -> list[TestBundle]:
"""Create bundles of tests that must run together, sorted by priority tier and efficiency.
Bundling rules:
- P2P with COE pairs: [P2P, COE1, COE2, ...]
- P2P without COE: [P2P_RX, P2P_TX] if both active
- COE without a linked active P2P: [COE] (standalone bundle)
- P3P: individual test (not bundled)
Returned list is sorted by (priority_tier, total_minutes) to place small/high-priority bundles first.
"""
bundles: list[TestBundle] = []
processed: set[TestKey] = set()
dut_by_id: dict[str, TestKey] = {test_id: key for test_id, _device in dut_active_test_ids for key in [(test_id, DUT)] if key in dut_active_test_ids}
ref_by_id: dict[str, TestKey] = {test_id: key for test_id, _device in ref_active_test_ids for key in [(test_id, REF)] if key in ref_active_test_ids}
all_ids = sorted(set(dut_by_id.keys()) | set(ref_by_id.keys()))
def _active_key(test_id: str, device: str) -> TestKey | None:
key = (test_id, device)
if key in dut_active_test_ids or key in ref_active_test_ids:
return key
return None
for test_id in all_ids:
seed_keys = [key for key in (_active_key(test_id, DUT), _active_key(test_id, REF)) if key is not None and key not in processed]
if not seed_keys:
continue
representative_key = seed_keys[0]
test = tests[representative_key]
if test.test_type == "P2P":
if test.has_coe_pair:
bundle_test_ids: list[TestKey] = []
for device in (DUT, REF):
base_key = _active_key(test_id, device)
if base_key is not None and base_key not in processed:
bundle_test_ids.append(base_key)
for coe_id in sorted(test.coe_pairing):
coe_key = _active_key(coe_id, device)
if coe_key is not None and coe_key not in processed:
bundle_test_ids.append(coe_key)
if not bundle_test_ids:
continue
processed.update(bundle_test_ids)
priority_tier = BUNDLE_PRIORITY_P2P_WITH_COE
bundles.append(TestBundle(
test_ids=bundle_test_ids,
priority_tier=priority_tier,
total_minutes=sum(tests[key].estimated_minutes for key in bundle_test_ids)
))
else:
bundle_test_ids: list[TestKey] = []
for device in (DUT, REF):
base_key = _active_key(test_id, device)
if base_key is None or base_key in processed:
continue
bundle_test_ids.append(base_key)
pair_id = _rx_tx_pair_id(test_id, set(k[0] for k in (dut_active_test_ids if device == DUT else ref_active_test_ids)))
if pair_id:
pair_key = _active_key(pair_id, device)
if pair_key is not None and pair_key not in processed and pair_key not in bundle_test_ids:
bundle_test_ids.append(pair_key)
if not bundle_test_ids:
continue
processed.update(bundle_test_ids)
priority_tier = BUNDLE_PRIORITY_P2P_ONLY
bundles.append(TestBundle(
test_ids=bundle_test_ids,
priority_tier=priority_tier,
total_minutes=sum(tests[key].estimated_minutes for key in bundle_test_ids)
))
elif test.test_type == "P3P":
bundle_test_ids = [key for key in (_active_key(test_id, DUT), _active_key(test_id, REF)) if key is not None and key not in processed]
if not bundle_test_ids:
continue
priority_tier = BUNDLE_PRIORITY_P3P
bundles.append(TestBundle(
test_ids=bundle_test_ids,
priority_tier=priority_tier,
total_minutes=sum(tests[key].estimated_minutes for key in bundle_test_ids)
))
processed.update(bundle_test_ids)
# Second pass to catch any active tests left uncovered by first-pass grouping.
all_active_keys = sorted(dut_active_test_ids | ref_active_test_ids)
for key in all_active_keys:
if key in processed:
continue
test = tests[key]
priority_tier = BUNDLE_PRIORITY_COE_ONLY
bundle_test_ids = [key]
bundles.append(TestBundle(
test_ids=bundle_test_ids,
priority_tier=priority_tier,
total_minutes=sum(tests[item].estimated_minutes for item in bundle_test_ids)
))
processed.update(bundle_test_ids)
# Sort by priority tier (lower first), then by total minutes (smaller first for efficiency)
bundles.sort(key=lambda b: (b.priority_tier, b.total_minutes))
return bundles
def _rx_tx_pair_id(test_id: str, active: set[str]) -> str | None:
if "RX" in test_id:
candidate = test_id.replace("RX", "TX", 1)
return candidate if candidate in active else None
if "TX" in test_id:
candidate = test_id.replace("TX", "RX", 1)
return candidate if candidate in active else None
return None
def _shift_capacity_for_date(
current_date: date,
holiday_dates: set[str],
start_date_override: str | None,
daytime_testing_today: bool,
) -> dict[int, int]:
iso = current_date.isoformat()
if iso in holiday_dates:
return {1: 480, 2: 480, 3: 480}
is_weekend = current_date.weekday() >= 5
if is_weekend:
return {1: 480, 2: 480, 3: 480}
if start_date_override and iso == _parse_date(start_date_override).isoformat() and daytime_testing_today:
return {1: 480, 2: 480, 3: 480}
return {1: 600, 2: 0, 3: 420}
def _parse_date(value: str | None) -> date:
if not value:
return date.today()
return datetime.strptime(value, "%Y-%m-%d").date()
def _get_shift_sequence(start_date: date, holiday_dates: set[str]) -> list[tuple[date, int]]:
"""Generate the sequence of (date, shift_index) tuples for a scheduling window.
Rules per design:
- Monday-Thursday: [3, 1] (shift 3 today, shift 1 next day) = 16 hours
- Friday: [3, 1, 2, 3, 1, 2, 3, 1] (Fri-Mon) = 24 hours continuous
- Saturday/Sunday/Holiday weekday: all 3 shifts
Always starts on shift 3 for weekdays.
Returns ordered list of (date, shift_index) pairs.
"""
iso = start_date.isoformat()
is_holiday = iso in holiday_dates
weekday = start_date.weekday() # 0=Mon, 4=Fri, 5=Sat, 6=Sun
shifts: list[tuple[date, int]] = []
# If holiday on a weekday, treat as weekend (all 3 shifts)
if is_holiday and weekday < 5:
shifts.append((start_date, 1))
shifts.append((start_date, 2))
shifts.append((start_date, 3))
return shifts
# Standard weekend day (Sat/Sun)
if weekday >= 5:
shifts.append((start_date, 1))
shifts.append((start_date, 2))
shifts.append((start_date, 3))
return shifts
# Friday: 4-day window (Fri-Mon)
if weekday == 4:
shifts.append((start_date, 3)) # Fri shift 3
sat = start_date + timedelta(days=1)
shifts.append((sat, 1)) # Sat shift 1
shifts.append((sat, 2)) # Sat shift 2
shifts.append((sat, 3)) # Sat shift 3
sun = sat + timedelta(days=1)
shifts.append((sun, 1)) # Sun shift 1
shifts.append((sun, 2)) # Sun shift 2
shifts.append((sun, 3)) # Sun shift 3
mon = sun + timedelta(days=1)
shifts.append((mon, 1)) # Mon shift 1
return shifts
# Monday-Thursday: 2-shift window
shifts.append((start_date, 3)) # Today shift 3
next_day = start_date + timedelta(days=1)
shifts.append((next_day, 1)) # Tomorrow shift 1
return shifts
+24
View File
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+16
View File
@@ -0,0 +1,16 @@
# React + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.
+21
View File
@@ -0,0 +1,21 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{js,jsx}'],
extends: [
js.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
globals: globals.browser,
parserOptions: { ecmaFeatures: { jsx: true } },
},
},
])
+13
View File
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>my-react-app</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
+2454
View File
File diff suppressed because it is too large Load Diff
+27
View File
@@ -0,0 +1,27 @@
{
"name": "my-react-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.2.6",
"react-dom": "^19.2.6"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"eslint": "^10.3.0",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.6.0",
"vite": "^8.0.12"
}
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.3 KiB

+24
View File
@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="bluesky-icon" viewBox="0 0 16 17">
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
</symbol>
<symbol id="discord-icon" viewBox="0 0 20 19">
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
</symbol>
<symbol id="documentation-icon" viewBox="0 0 21 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
</symbol>
<symbol id="github-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
</symbol>
<symbol id="social-icon" viewBox="0 0 20 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
</symbol>
<symbol id="x-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

+184
View File
@@ -0,0 +1,184 @@
.counter {
font-size: 16px;
padding: 5px 10px;
border-radius: 5px;
color: var(--accent);
background: var(--accent-bg);
border: 2px solid transparent;
transition: border-color 0.3s;
margin-bottom: 24px;
&:hover {
border-color: var(--accent-border);
}
&:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
}
.hero {
position: relative;
.base,
.framework,
.vite {
inset-inline: 0;
margin: 0 auto;
}
.base {
width: 170px;
position: relative;
z-index: 0;
}
.framework,
.vite {
position: absolute;
}
.framework {
z-index: 1;
top: 34px;
height: 28px;
transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
scale(1.4);
}
.vite {
z-index: 0;
top: 107px;
height: 26px;
width: auto;
transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
scale(0.8);
}
}
#center {
display: flex;
flex-direction: column;
gap: 25px;
place-content: center;
place-items: center;
flex-grow: 1;
@media (max-width: 1024px) {
padding: 32px 20px 24px;
gap: 18px;
}
}
#next-steps {
display: flex;
border-top: 1px solid var(--border);
text-align: left;
& > div {
flex: 1 1 0;
padding: 32px;
@media (max-width: 1024px) {
padding: 24px 20px;
}
}
.icon {
margin-bottom: 16px;
width: 22px;
height: 22px;
}
@media (max-width: 1024px) {
flex-direction: column;
text-align: center;
}
}
#docs {
border-right: 1px solid var(--border);
@media (max-width: 1024px) {
border-right: none;
border-bottom: 1px solid var(--border);
}
}
#next-steps ul {
list-style: none;
padding: 0;
display: flex;
gap: 8px;
margin: 32px 0 0;
.logo {
height: 18px;
}
a {
color: var(--text-h);
font-size: 16px;
border-radius: 6px;
background: var(--social-bg);
display: flex;
padding: 6px 12px;
align-items: center;
gap: 8px;
text-decoration: none;
transition: box-shadow 0.3s;
&:hover {
box-shadow: var(--shadow);
}
.button-icon {
height: 18px;
width: 18px;
}
}
@media (max-width: 1024px) {
margin-top: 20px;
flex-wrap: wrap;
justify-content: center;
li {
flex: 1 1 calc(50% - 8px);
}
a {
width: 100%;
justify-content: center;
box-sizing: border-box;
}
}
}
#spacer {
height: 88px;
border-top: 1px solid var(--border);
@media (max-width: 1024px) {
height: 48px;
}
}
.ticks {
position: relative;
width: 100%;
&::before,
&::after {
content: '';
position: absolute;
top: -4.5px;
border: 5px solid transparent;
}
&::before {
left: 0;
border-left-color: var(--border);
}
&::after {
right: 0;
border-right-color: var(--border);
}
}
+122
View File
@@ -0,0 +1,122 @@
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from './assets/vite.svg'
import heroImg from './assets/hero.png'
import './App.css'
function App() {
const [count, setCount] = useState(0)
return (
<>
<section id="center">
<div className="hero">
<img src={heroImg} className="base" width="170" height="179" alt="" />
<img src={reactLogo} className="framework" alt="React logo" />
<img src={viteLogo} className="vite" alt="Vite logo" />
</div>
<div>
<h1>Get started</h1>
<p>
Edit <code>src/App.jsx</code> and save to test <code>HMR</code>
</p>
</div>
<button
type="button"
className="counter"
onClick={() => setCount((count) => count + 1)}
>
Count is {count}
</button>
</section>
<div className="ticks"></div>
<section id="next-steps">
<div id="docs">
<svg className="icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#documentation-icon"></use>
</svg>
<h2>Documentation</h2>
<p>Your questions, answered</p>
<ul>
<li>
<a href="https://vite.dev/" target="_blank">
<img className="logo" src={viteLogo} alt="" />
Explore Vite
</a>
</li>
<li>
<a href="https://react.dev/" target="_blank">
<img className="button-icon" src={reactLogo} alt="" />
Learn more
</a>
</li>
</ul>
</div>
<div id="social">
<svg className="icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#social-icon"></use>
</svg>
<h2>Connect with us</h2>
<p>Join the Vite community</p>
<ul>
<li>
<a href="https://github.com/vitejs/vite" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#github-icon"></use>
</svg>
GitHub
</a>
</li>
<li>
<a href="https://chat.vite.dev/" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#discord-icon"></use>
</svg>
Discord
</a>
</li>
<li>
<a href="https://x.com/vite_js" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#x-icon"></use>
</svg>
X.com
</a>
</li>
<li>
<a href="https://bsky.app/profile/vite.dev" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#bluesky-icon"></use>
</svg>
Bluesky
</a>
</li>
</ul>
</div>
</section>
<div className="ticks"></div>
<section id="spacer"></section>
</>
)
}
export default App
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.5 KiB

+111
View File
@@ -0,0 +1,111 @@
:root {
--text: #6b6375;
--text-h: #08060d;
--bg: #fff;
--border: #e5e4e7;
--code-bg: #f4f3ec;
--accent: #aa3bff;
--accent-bg: rgba(170, 59, 255, 0.1);
--accent-border: rgba(170, 59, 255, 0.5);
--social-bg: rgba(244, 243, 236, 0.5);
--shadow:
rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
--sans: system-ui, 'Segoe UI', Roboto, sans-serif;
--heading: system-ui, 'Segoe UI', Roboto, sans-serif;
--mono: ui-monospace, Consolas, monospace;
font: 18px/145% var(--sans);
letter-spacing: 0.18px;
color-scheme: light dark;
color: var(--text);
background: var(--bg);
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@media (max-width: 1024px) {
font-size: 16px;
}
}
@media (prefers-color-scheme: dark) {
:root {
--text: #9ca3af;
--text-h: #f3f4f6;
--bg: #16171d;
--border: #2e303a;
--code-bg: #1f2028;
--accent: #c084fc;
--accent-bg: rgba(192, 132, 252, 0.15);
--accent-border: rgba(192, 132, 252, 0.5);
--social-bg: rgba(47, 48, 58, 0.5);
--shadow:
rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
}
#social .button-icon {
filter: invert(1) brightness(2);
}
}
body {
margin: 0;
}
#root {
width: 1126px;
max-width: 100%;
margin: 0 auto;
text-align: center;
border-inline: 1px solid var(--border);
min-height: 100svh;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
h1,
h2 {
font-family: var(--heading);
font-weight: 500;
color: var(--text-h);
}
h1 {
font-size: 56px;
letter-spacing: -1.68px;
margin: 32px 0;
@media (max-width: 1024px) {
font-size: 36px;
margin: 20px 0;
}
}
h2 {
font-size: 24px;
line-height: 118%;
letter-spacing: -0.24px;
margin: 0 0 8px;
@media (max-width: 1024px) {
font-size: 20px;
}
}
p {
margin: 0;
}
code,
.counter {
font-family: var(--mono);
display: inline-flex;
border-radius: 4px;
color: var(--text-h);
}
code {
font-size: 15px;
line-height: 135%;
padding: 4px 8px;
background: var(--code-bg);
}
+10
View File
@@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)
+7
View File
@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
})