diff --git a/vna_system/api/endpoints/acquisition.py b/vna_system/api/endpoints/acquisition.py new file mode 100644 index 0000000..e2225d4 --- /dev/null +++ b/vna_system/api/endpoints/acquisition.py @@ -0,0 +1,88 @@ +from fastapi import APIRouter, HTTPException +import vna_system.core.singletons as singletons + +router = APIRouter(prefix="/api/v1", tags=["acquisition"]) + + +@router.get("/acquisition/status") +async def get_acquisition_status(): + """Get current acquisition status.""" + acquisition = singletons.vna_data_acquisition_instance + + return { + "running": acquisition.is_running, + "paused": acquisition.is_paused, + "continuous_mode": acquisition.is_continuous_mode, + "sweep_count": acquisition._sweep_buffer._sweep_counter if hasattr(acquisition._sweep_buffer, '_sweep_counter') else 0 + } + + +@router.post("/acquisition/start") +async def start_acquisition(): + """Start data acquisition.""" + try: + acquisition = singletons.vna_data_acquisition_instance + + if not acquisition.is_running: + # Start thread if not running + acquisition.start() + + # Set to continuous mode (also resumes if paused) + acquisition.set_continuous_mode(True) + return {"success": True, "message": "Acquisition started"} + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +@router.post("/acquisition/stop") +async def stop_acquisition(): + """Stop/pause data acquisition.""" + try: + acquisition = singletons.vna_data_acquisition_instance + if not acquisition.is_running: + return {"success": True, "message": "Acquisition already stopped"} + + # Just pause instead of full stop - keeps thread alive for restart + acquisition.pause() + return {"success": True, "message": "Acquisition stopped"} + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +@router.post("/acquisition/single-sweep") +async def trigger_single_sweep(): + """Trigger a single sweep. Automatically starts acquisition if needed.""" + try: + acquisition = singletons.vna_data_acquisition_instance + + if not acquisition.is_running: + # Start acquisition if not running + acquisition.start() + + acquisition.trigger_single_sweep() + return {"success": True, "message": "Single sweep triggered"} + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +@router.get("/acquisition/latest-sweep") +async def get_latest_sweep(): + """Get the latest sweep data.""" + try: + acquisition = singletons.vna_data_acquisition_instance + latest_sweep = acquisition._sweep_buffer.get_latest_sweep() + + if not latest_sweep: + return {"sweep": None, "message": "No sweep data available"} + + return { + "sweep": { + "sweep_number": latest_sweep.sweep_number, + "timestamp": latest_sweep.timestamp, + "total_points": latest_sweep.total_points, + "points": latest_sweep.points[:10] if len(latest_sweep.points) > 10 else latest_sweep.points # Limit for API response + }, + "message": f"Latest sweep #{latest_sweep.sweep_number} with {latest_sweep.total_points} points" + } + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) \ No newline at end of file diff --git a/vna_system/api/endpoints/health.py b/vna_system/api/endpoints/health.py index 12c11f6..49a94d9 100644 --- a/vna_system/api/endpoints/health.py +++ b/vna_system/api/endpoints/health.py @@ -15,24 +15,3 @@ router = APIRouter(prefix="/api/v1", tags=["health"]) async def root(): """Root endpoint.""" return {"message": "VNA System API", "version": "1.0.0"} - - -@router.get("/health") -async def health_check(): - """Health check endpoint.""" - return { - "status": "healthy", - "acquisition_running": singletons.vna_data_acquisition_instance.is_running, - "processing_running": singletons.processing_manager.is_running, - "processing_stats": singletons.processing_manager.get_processing_stats() - } - - -@router.get("/device-status") -async def device_status(): - """Get device connection status and details.""" - acquisition = singletons.vna_data_acquisition_instance - - return { - "acquisition_running": acquisition.is_running, - } \ No newline at end of file diff --git a/vna_system/api/endpoints/processing.py b/vna_system/api/endpoints/processing.py deleted file mode 100644 index 7dc08ee..0000000 --- a/vna_system/api/endpoints/processing.py +++ /dev/null @@ -1,27 +0,0 @@ -from fastapi import APIRouter, HTTPException -import vna_system.core.singletons as singletons - -router = APIRouter(prefix="/api/v1", tags=["processing"]) - - -@router.get("/stats") -async def get_stats(): - """Get processing statistics.""" - return singletons.processing_manager.get_processing_stats() - - -@router.get("/history") -async def get_history(processor_name: str | None = None, limit: int = 10): - """Get processing results history.""" - results = singletons.processing_manager.get_latest_results(processor_name)[-limit:] - return [result.to_dict() for result in results] - - -@router.get("/sweep/{sweep_number}") -async def get_sweep(sweep_number: int, processor_name: str | None = None): - """Get results for specific sweep.""" - results = singletons.processing_manager.get_result_by_sweep(sweep_number, processor_name) - if not results: - raise HTTPException(status_code=404, detail=f"No results found for sweep {sweep_number}") - - return [result.to_dict() for result in results] \ No newline at end of file diff --git a/vna_system/api/main.py b/vna_system/api/main.py index 251744d..c045b6f 100644 --- a/vna_system/api/main.py +++ b/vna_system/api/main.py @@ -12,9 +12,7 @@ from fastapi.staticfiles import StaticFiles from pathlib import Path import vna_system.core.singletons as singletons -from vna_system.core.processing.sweep_processor import SweepProcessingManager -from vna_system.core.processing.websocket_handler import WebSocketManager -from vna_system.api.endpoints import health, processing, settings, web_ui +from vna_system.api.endpoints import health, settings, web_ui, acquisition from vna_system.api.websockets import processing as ws_processing @@ -64,14 +62,14 @@ async def lifespan(app: FastAPI): log_level = config.get("logging", {}).get("level", "INFO") logging.getLogger().setLevel(getattr(logging, log_level)) - # Start acquisition + # Start acquisition logger.info("Starting data acquisition...") - # Connect processing to acquisition - singletons.processing_manager.set_sweep_buffer(singletons.vna_data_acquisition_instance.sweep_buffer) singletons.vna_data_acquisition_instance.start() - singletons.processing_manager.start() - logger.info("Sweep processing started") + # Initialize processor system + logger.info("Starting processor system...") + singletons.processor_manager.start_processing() + logger.info(f"Processor system started with processors: {singletons.processor_manager.list_processors()}") logger.info("VNA API Server started successfully") @@ -84,11 +82,11 @@ async def lifespan(app: FastAPI): # Shutdown logger.info("Shutting down VNA API Server...") - if singletons.processing_manager: - singletons.processing_manager.stop() - logger.info("Processing stopped") + if singletons.processor_manager: + singletons.processor_manager.stop_processing() + logger.info("Processor system stopped") - if singletons.vna_data_acquisition_instance and singletons.vna_data_acquisition_instance.is_running: + if singletons.vna_data_acquisition_instance and singletons.vna_data_acquisition_instance._running: singletons.vna_data_acquisition_instance.stop() logger.info("Acquisition stopped") @@ -116,7 +114,8 @@ else: # Include routers app.include_router(web_ui.router) # Web UI should be first for root path app.include_router(health.router) -app.include_router(processing.router) +# app.include_router(processing.router) +app.include_router(acquisition.router) app.include_router(settings.router) app.include_router(ws_processing.router) diff --git a/vna_system/api/websockets/processing.py b/vna_system/api/websockets/processing.py index b7614c2..3696a79 100644 --- a/vna_system/api/websockets/processing.py +++ b/vna_system/api/websockets/processing.py @@ -12,5 +12,4 @@ router = APIRouter() @router.websocket("/ws/processing") async def processing_websocket_endpoint(websocket: WebSocket): """WebSocket endpoint for real-time processing data streaming.""" - - await singletons.websocket_manager.handle_websocket(websocket) \ No newline at end of file + await singletons.processor_websocket_handler.handle_websocket_connection(websocket) \ No newline at end of file diff --git a/vna_system/binary_input/current_input.bin b/vna_system/binary_input/current_input.bin index 7f4ad93..e701777 120000 --- a/vna_system/binary_input/current_input.bin +++ b/vna_system/binary_input/current_input.bin @@ -1 +1 @@ -config_inputs/s11_start100_stop8800_points1000_bw1khz.bin \ No newline at end of file +config_inputs/s21_start100_stop8800_points1000_bw1khz.bin \ No newline at end of file diff --git a/vna_system/calibration/current_calibration b/vna_system/calibration/current_calibration deleted file mode 120000 index 6c64061..0000000 --- a/vna_system/calibration/current_calibration +++ /dev/null @@ -1 +0,0 @@ -s11_start100_stop8800_points1000_bw1khz/tuncTuncTuncSahur \ No newline at end of file diff --git a/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/calibration_info.json b/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/calibration_info.json new file mode 100644 index 0000000..3b0a3c0 --- /dev/null +++ b/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/calibration_info.json @@ -0,0 +1,18 @@ +{ + "preset": { + "filename": "s11_start100_stop8800_points1000_bw1khz.bin", + "mode": "s11", + "start_freq": 100000000.0, + "stop_freq": 8800000000.0, + "points": 1000, + "bandwidth": 1000.0 + }, + "calibration_name": "SRGDFDFG", + "standards": [ + "open", + "short", + "load" + ], + "created_timestamp": "2025-09-25T16:15:09.918600", + "is_complete": true +} \ No newline at end of file diff --git a/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/load.json b/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/load.json new file mode 100644 index 0000000..707804f --- /dev/null +++ b/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/load.json @@ -0,0 +1,4007 @@ +{ + "sweep_number": 224, + "timestamp": 1758806106.5021908, + "points": [ + [ + -2.6736810207366943, + 0.45182546973228455 + ], + [ + -2.6649160385131836, + 0.5124151110649109 + ], + [ + -2.6564583778381348, + 0.5761726498603821 + ], + [ + -2.6471076011657715, + 0.6357683539390564 + ], + [ + -2.63456392288208, + 0.6980700492858887 + ], + [ + -3.406205415725708, + 0.9276503920555115 + ], + [ + -3.3926522731781006, + 0.9973128437995911 + ], + [ + -3.3718581199645996, + 1.0803601741790771 + ], + [ + -3.354764461517334, + 1.1557843685150146 + ], + [ + -3.3333864212036133, + 1.2311294078826904 + ], + [ + -3.306791067123413, + 1.3099294900894165 + ], + [ + -3.2835750579833984, + 1.3874938488006592 + ], + [ + -3.260197162628174, + 1.4654496908187866 + ], + [ + -3.235013484954834, + 1.5420405864715576 + ], + [ + -3.2073099613189697, + 1.615953803062439 + ], + [ + -3.17728853225708, + 1.6916990280151367 + ], + [ + -3.1466078758239746, + 1.7638152837753296 + ], + [ + -3.111706495285034, + 1.8342149257659912 + ], + [ + -3.0758213996887207, + 1.9107712507247925 + ], + [ + -3.04197359085083, + 1.9872909784317017 + ], + [ + -3.007207155227661, + 2.0558650493621826 + ], + [ + -2.9629905223846436, + 2.1273269653320312 + ], + [ + -2.9264559745788574, + 2.204591989517212 + ], + [ + -2.8800888061523438, + 2.27834415435791 + ], + [ + -2.842005491256714, + 2.3492860794067383 + ], + [ + -2.798678398132324, + 2.4209070205688477 + ], + [ + -2.7528514862060547, + 2.4890832901000977 + ], + [ + -2.702406406402588, + 2.5655250549316406 + ], + [ + -2.6573588848114014, + 2.6316630840301514 + ], + [ + -2.606713056564331, + 2.7075676918029785 + ], + [ + -2.5558927059173584, + 2.7759933471679688 + ], + [ + -2.511176109313965, + 2.8374271392822266 + ], + [ + -2.453795909881592, + 2.907130718231201 + ], + [ + -2.395359754562378, + 2.969266891479492 + ], + [ + -2.335897922515869, + 3.0380139350891113 + ], + [ + -2.270254135131836, + 3.102494955062866 + ], + [ + -2.1994450092315674, + 3.1652512550354004 + ], + [ + -2.1340577602386475, + 3.2305760383605957 + ], + [ + -2.0573885440826416, + 3.2901291847229004 + ], + [ + -1.9910917282104492, + 3.3597488403320312 + ], + [ + -1.914387583732605, + 3.421565532684326 + ], + [ + -1.8417962789535522, + 3.4749624729156494 + ], + [ + -1.7740607261657715, + 3.538019895553589 + ], + [ + -1.6910836696624756, + 3.593351364135742 + ], + [ + -1.6129546165466309, + 3.650934934616089 + ], + [ + -1.5314576625823975, + 3.7079336643218994 + ], + [ + -1.4456006288528442, + 3.7681336402893066 + ], + [ + -1.3613699674606323, + 3.816110610961914 + ], + [ + -1.2707738876342773, + 3.867534637451172 + ], + [ + -1.1808480024337769, + 3.917786121368408 + ], + [ + -1.090746521949768, + 3.965712308883667 + ], + [ + -0.9997296929359436, + 4.015567302703857 + ], + [ + -0.8969882726669312, + 4.064459323883057 + ], + [ + -0.8074730038642883, + 4.103725433349609 + ], + [ + -0.705039918422699, + 4.142040252685547 + ], + [ + -0.6112637519836426, + 4.195705890655518 + ], + [ + -0.5010696649551392, + 4.22493839263916 + ], + [ + -0.4009867012500763, + 4.263381481170654 + ], + [ + -0.2912496328353882, + 4.291356086730957 + ], + [ + -0.17719513177871704, + 4.322331428527832 + ], + [ + -0.0559571348130703, + 4.347788333892822 + ], + [ + 0.053220413625240326, + 4.367687225341797 + ], + [ + 0.1828378587961197, + 4.393011093139648 + ], + [ + 0.3024580180644989, + 4.400704860687256 + ], + [ + 0.4196595847606659, + 4.400711536407471 + ], + [ + 0.5461052656173706, + 4.420855522155762 + ], + [ + 0.6670081615447998, + 4.422549247741699 + ], + [ + 0.7900673151016235, + 4.433554172515869 + ], + [ + 0.9182475209236145, + 4.435775279998779 + ], + [ + 1.0549386739730835, + 4.4369611740112305 + ], + [ + 1.1896811723709106, + 4.438133239746094 + ], + [ + 1.3255913257598877, + 4.427420616149902 + ], + [ + 1.4510456323623657, + 4.396478176116943 + ], + [ + 1.5899698734283447, + 4.381324768066406 + ], + [ + 1.7368065118789673, + 4.359009265899658 + ], + [ + 1.867936134338379, + 4.326304912567139 + ], + [ + 1.9993454217910767, + 4.286786079406738 + ], + [ + 2.1317646503448486, + 4.249267101287842 + ], + [ + 2.26725697517395, + 4.204619884490967 + ], + [ + 2.4096617698669434, + 4.160815715789795 + ], + [ + 2.5364108085632324, + 4.112579822540283 + ], + [ + 2.657930612564087, + 4.05103063583374 + ], + [ + 2.787731409072876, + 3.9864959716796875 + ], + [ + 2.9073808193206787, + 3.9175057411193848 + ], + [ + 3.0339295864105225, + 3.849170684814453 + ], + [ + 3.1596813201904297, + 3.782402992248535 + ], + [ + 3.266500949859619, + 3.694551467895508 + ], + [ + 3.377516746520996, + 3.6156153678894043 + ], + [ + 3.5012471675872803, + 3.522921085357666 + ], + [ + 3.607501268386841, + 3.4285483360290527 + ], + [ + 3.721850872039795, + 3.327152967453003 + ], + [ + 3.827000141143799, + 3.223395824432373 + ], + [ + 3.9379539489746094, + 3.1131348609924316 + ], + [ + 4.042356014251709, + 2.996729612350464 + ], + [ + 4.159390449523926, + 2.877631187438965 + ], + [ + 4.2581048011779785, + 2.7344279289245605 + ], + [ + 4.342874526977539, + 2.610862970352173 + ], + [ + 4.443049430847168, + 2.4738378524780273 + ], + [ + 4.523603439331055, + 2.3404018878936768 + ], + [ + 4.5946245193481445, + 2.2019197940826416 + ], + [ + 4.675113677978516, + 2.0515711307525635 + ], + [ + 4.738132476806641, + 1.8976340293884277 + ], + [ + 4.78986930847168, + 1.7495304346084595 + ], + [ + 4.853011608123779, + 1.5902783870697021 + ], + [ + 4.901889324188232, + 1.4279320240020752 + ], + [ + 4.941633701324463, + 1.2628992795944214 + ], + [ + 4.986309051513672, + 1.1021469831466675 + ], + [ + 5.011632442474365, + 0.935245931148529 + ], + [ + 5.037826061248779, + 0.7647016048431396 + ], + [ + 5.053710460662842, + 0.6024985313415527 + ], + [ + 5.0733466148376465, + 0.43650737404823303 + ], + [ + 5.089456558227539, + 0.27373409271240234 + ], + [ + 5.078577041625977, + 0.10420450568199158 + ], + [ + 5.072869300842285, + -0.05629599466919899 + ], + [ + 5.057137489318848, + -0.21737471222877502 + ], + [ + 5.033437728881836, + -0.34463685750961304 + ], + [ + 4.998108863830566, + -0.5058478116989136 + ], + [ + 4.966320514678955, + -0.6498308777809143 + ], + [ + 4.915668964385986, + -0.7943239212036133 + ], + [ + 4.865395545959473, + -0.9435988664627075 + ], + [ + 4.807960510253906, + -1.081966519355774 + ], + [ + 4.7376532554626465, + -1.2200692892074585 + ], + [ + 4.667508602142334, + -1.355132818222046 + ], + [ + 4.595383644104004, + -1.4871387481689453 + ], + [ + 4.515742301940918, + -1.622791051864624 + ], + [ + 4.422509670257568, + -1.7464717626571655 + ], + [ + 4.3369340896606445, + -1.8784594535827637 + ], + [ + 4.242724418640137, + -2.005474090576172 + ], + [ + 4.14840030670166, + -2.147378444671631 + ], + [ + 4.063354969024658, + -2.2759499549865723 + ], + [ + 3.951305389404297, + -2.3975613117218018 + ], + [ + 3.8580098152160645, + -2.5223536491394043 + ], + [ + 3.765162467956543, + -2.6296277046203613 + ], + [ + 3.6515040397644043, + -2.745894432067871 + ], + [ + 3.5635275840759277, + -2.845367908477783 + ], + [ + 3.4549825191497803, + -2.9300620555877686 + ], + [ + 3.3704142570495605, + -3.033066511154175 + ], + [ + 3.2684061527252197, + -3.090548515319824 + ], + [ + 3.160047769546509, + -3.149569272994995 + ], + [ + 3.0653798580169678, + -3.193134069442749 + ], + [ + 2.957157611846924, + -3.2273616790771484 + ], + [ + 2.8522789478302, + -3.2683937549591064 + ], + [ + 2.7458417415618896, + -3.283806562423706 + ], + [ + 2.632183313369751, + -3.307053565979004 + ], + [ + 2.528501510620117, + -3.3292500972747803 + ], + [ + 2.4107823371887207, + -3.344839096069336 + ], + [ + 2.298522472381592, + -3.348083257675171 + ], + [ + 2.1977953910827637, + -3.372875452041626 + ], + [ + 2.083407163619995, + -3.366670846939087 + ], + [ + 1.9608125686645508, + -3.3750596046447754 + ], + [ + 1.8605008125305176, + -3.384669780731201 + ], + [ + 1.7375348806381226, + -3.395942211151123 + ], + [ + 1.6165754795074463, + -3.4232215881347656 + ], + [ + 1.473440170288086, + -3.625798225402832 + ], + [ + 1.3664487600326538, + -3.628476858139038 + ], + [ + 1.2635457515716553, + -3.6369354724884033 + ], + [ + 1.1742236614227295, + -3.616102933883667 + ], + [ + 1.0705455541610718, + -3.617340087890625 + ], + [ + 0.9761837124824524, + -3.6077592372894287 + ], + [ + 0.8788896203041077, + -3.604526996612549 + ], + [ + 0.7796502709388733, + -3.5850894451141357 + ], + [ + 0.6826730370521545, + -3.5633981227874756 + ], + [ + 0.5934434533119202, + -3.5596537590026855 + ], + [ + 0.5035812854766846, + -3.5311779975891113 + ], + [ + 0.4178984463214874, + -3.5169882774353027 + ], + [ + 0.32849785685539246, + -3.5019373893737793 + ], + [ + 0.24413107335567474, + -3.4760217666625977 + ], + [ + 0.16599296033382416, + -3.457561492919922 + ], + [ + 0.0886717289686203, + -3.432316541671753 + ], + [ + 0.0069116936065256596, + -3.410564422607422 + ], + [ + -0.06821972876787186, + -3.385152816772461 + ], + [ + -0.1381102055311203, + -3.3575682640075684 + ], + [ + -0.2036321461200714, + -3.32989239692688 + ], + [ + -0.2721547484397888, + -3.2988481521606445 + ], + [ + -0.3456687927246094, + -3.2797422409057617 + ], + [ + -0.40491026639938354, + -3.2436702251434326 + ], + [ + -0.46992459893226624, + -3.217508316040039 + ], + [ + -0.5275281667709351, + -3.18990421295166 + ], + [ + -0.5910951495170593, + -3.159196615219116 + ], + [ + -0.6481737494468689, + -3.1255087852478027 + ], + [ + -0.6999361515045166, + -3.0945961475372314 + ], + [ + -0.7655573487281799, + -3.066328287124634 + ], + [ + -0.8193655014038086, + -3.029822826385498 + ], + [ + -0.8740184307098389, + -2.9954843521118164 + ], + [ + -0.9328928589820862, + -2.9635941982269287 + ], + [ + -0.9758638739585876, + -2.9242727756500244 + ], + [ + -1.0377837419509888, + -2.890282154083252 + ], + [ + -1.086008906364441, + -2.853980302810669 + ], + [ + -1.1375977993011475, + -2.827772855758667 + ], + [ + -1.1902581453323364, + -2.789005756378174 + ], + [ + -1.2380311489105225, + -2.7569501399993896 + ], + [ + -1.2801151275634766, + -2.723897933959961 + ], + [ + -1.331512212753296, + -2.6854257583618164 + ], + [ + -1.3717644214630127, + -2.6477699279785156 + ], + [ + -1.4093807935714722, + -2.6117165088653564 + ], + [ + -1.4476438760757446, + -2.56960129737854 + ], + [ + -1.4962366819381714, + -2.5276761054992676 + ], + [ + -1.5316818952560425, + -2.483553647994995 + ], + [ + -1.5692683458328247, + -2.439359188079834 + ], + [ + -1.607676386833191, + -2.3932254314422607 + ], + [ + -1.615010142326355, + -2.3210482597351074 + ], + [ + -1.6576086282730103, + -2.2704386711120605 + ], + [ + -1.7165112495422363, + -2.220757484436035 + ], + [ + -1.7511240243911743, + -2.1732826232910156 + ], + [ + -1.804411768913269, + -2.117375373840332 + ], + [ + -1.842379093170166, + -2.0728418827056885 + ], + [ + -1.8945618867874146, + -2.0168027877807617 + ], + [ + -1.935418963432312, + -1.9641530513763428 + ], + [ + -1.9654779434204102, + -1.9175623655319214 + ], + [ + -2.014981985092163, + -1.8624297380447388 + ], + [ + -2.055156946182251, + -1.8145853281021118 + ], + [ + -2.089393377304077, + -1.7664117813110352 + ], + [ + -2.121464252471924, + -1.7126656770706177 + ], + [ + -2.154043197631836, + -1.664276361465454 + ], + [ + -2.1856307983398438, + -1.615336537361145 + ], + [ + -2.215449333190918, + -1.5591015815734863 + ], + [ + -2.242549419403076, + -1.5095828771591187 + ], + [ + -2.265376567840576, + -1.4667716026306152 + ], + [ + -2.2891957759857178, + -1.4067754745483398 + ], + [ + -2.3102810382843018, + -1.3600338697433472 + ], + [ + -2.32448410987854, + -1.3162206411361694 + ], + [ + -2.344461441040039, + -1.2646205425262451 + ], + [ + -2.358267068862915, + -1.2159160375595093 + ], + [ + -2.3702125549316406, + -1.1691083908081055 + ], + [ + -2.377868890762329, + -1.121673822402954 + ], + [ + -2.394197463989258, + -1.0700950622558594 + ], + [ + -2.405461072921753, + -1.0208792686462402 + ], + [ + -2.407872200012207, + -0.968040406703949 + ], + [ + -2.4239590167999268, + -0.9168859124183655 + ], + [ + -2.4302685260772705, + -0.8633595705032349 + ], + [ + -2.438366413116455, + -0.8092073798179626 + ], + [ + -2.4480350017547607, + -0.7520635724067688 + ], + [ + -2.45904803276062, + -0.6941565871238708 + ], + [ + -2.4681553840637207, + -0.6360258460044861 + ], + [ + -2.483577251434326, + -0.5720770359039307 + ], + [ + -2.4936487674713135, + -0.5103459358215332 + ], + [ + -2.503358840942383, + -0.4530770182609558 + ], + [ + -2.516498565673828, + -0.39213451743125916 + ], + [ + -2.524306058883667, + -0.3296535611152649 + ], + [ + -2.5337798595428467, + -0.2714705765247345 + ], + [ + -2.5415852069854736, + -0.21370919048786163 + ], + [ + -2.5448076725006104, + -0.15511257946491241 + ], + [ + -2.5307958126068115, + -0.10204117000102997 + ], + [ + -2.527467727661133, + -0.04747380316257477 + ], + [ + -2.52011775970459, + 0.007723364047706127 + ], + [ + -2.5181925296783447, + 0.06148742884397507 + ], + [ + -2.5121519565582275, + 0.1203572079539299 + ], + [ + -2.5046167373657227, + 0.17504900693893433 + ], + [ + -2.4977223873138428, + 0.23374029994010925 + ], + [ + -2.4909656047821045, + 0.2944478988647461 + ], + [ + -2.4815306663513184, + 0.34626659750938416 + ], + [ + -2.474107265472412, + 0.4034506678581238 + ], + [ + -2.461988925933838, + 0.46261775493621826 + ], + [ + -2.4554049968719482, + 0.5274679660797119 + ], + [ + -2.448575973510742, + 0.5868113040924072 + ], + [ + -2.4302854537963867, + 0.6411546468734741 + ], + [ + -2.4183151721954346, + 0.6985329389572144 + ], + [ + -2.413236379623413, + 0.7610287666320801 + ], + [ + -2.4032866954803467, + 0.820647656917572 + ], + [ + -2.3982157707214355, + 0.8830277323722839 + ], + [ + -2.3764331340789795, + 0.9381933808326721 + ], + [ + -2.3649919033050537, + 0.9967764616012573 + ], + [ + -2.3597707748413086, + 1.0586053133010864 + ], + [ + -2.3494198322296143, + 1.111100435256958 + ], + [ + -2.341068983078003, + 1.161250114440918 + ], + [ + -2.3189733028411865, + 1.2125217914581299 + ], + [ + -2.3094048500061035, + 1.2694249153137207 + ], + [ + -2.2991182804107666, + 1.3180114030838013 + ], + [ + -2.2867796421051025, + 1.3622760772705078 + ], + [ + -2.2749311923980713, + 1.3993581533432007 + ], + [ + -2.258960008621216, + 1.4342812299728394 + ], + [ + -2.2377524375915527, + 1.4952926635742188 + ], + [ + -2.215458393096924, + 1.5359910726547241 + ], + [ + -2.1919753551483154, + 1.561612606048584 + ], + [ + -2.1656274795532227, + 1.5948957204818726 + ], + [ + -2.1345701217651367, + 1.6266098022460938 + ], + [ + -2.097571611404419, + 1.6797171831130981 + ], + [ + -2.064439058303833, + 1.713385820388794 + ], + [ + -2.0182299613952637, + 1.7452658414840698 + ], + [ + -1.9683722257614136, + 1.7842978239059448 + ], + [ + -1.9140623807907104, + 1.8159682750701904 + ], + [ + -1.8470770120620728, + 1.8618063926696777 + ], + [ + -1.816527247428894, + 1.9144901037216187 + ], + [ + -1.7516878843307495, + 1.954081416130066 + ], + [ + -1.6953322887420654, + 1.9995709657669067 + ], + [ + -1.6254856586456299, + 2.058678388595581 + ], + [ + -1.5642616748809814, + 2.1180386543273926 + ], + [ + -1.5240942239761353, + 2.167421817779541 + ], + [ + -1.4526803493499756, + 2.2239482402801514 + ], + [ + -1.396411418914795, + 2.2834792137145996 + ], + [ + -1.3327165842056274, + 2.3447656631469727 + ], + [ + -1.2825573682785034, + 2.4180798530578613 + ], + [ + -1.2271827459335327, + 2.4892048835754395 + ], + [ + -1.1655495166778564, + 2.5345427989959717 + ], + [ + -1.1101999282836914, + 2.590912342071533 + ], + [ + -1.0590189695358276, + 2.6529579162597656 + ], + [ + -1.0067218542099, + 2.7113568782806396 + ], + [ + -0.9553897976875305, + 2.775033712387085 + ], + [ + -0.9018446207046509, + 2.826817512512207 + ], + [ + -0.8346455097198486, + 2.8655762672424316 + ], + [ + -0.7675134539604187, + 2.90928053855896 + ], + [ + -0.7167988419532776, + 2.963583469390869 + ], + [ + -0.6518580317497253, + 3.0032525062561035 + ], + [ + -0.5896396636962891, + 3.052675724029541 + ], + [ + -0.5292495489120483, + 3.087273359298706 + ], + [ + -0.4397505223751068, + 3.114755392074585 + ], + [ + -0.37930282950401306, + 3.149266242980957 + ], + [ + -0.3047275245189667, + 3.1856613159179688 + ], + [ + -0.22711072862148285, + 3.2127630710601807 + ], + [ + -0.15210235118865967, + 3.2369232177734375 + ], + [ + -0.08250471949577332, + 3.2628254890441895 + ], + [ + 0.010138730518519878, + 3.290278434753418 + ], + [ + 0.08341272175312042, + 3.2997281551361084 + ], + [ + 0.16364938020706177, + 3.3170077800750732 + ], + [ + 0.24537791311740875, + 3.33978009223938 + ], + [ + 0.3225707709789276, + 3.346046209335327 + ], + [ + 0.4019952118396759, + 3.3418052196502686 + ], + [ + 0.604122519493103, + 3.221787691116333 + ], + [ + 0.6914963126182556, + 3.223146677017212 + ], + [ + 0.8036956191062927, + 3.212400436401367 + ], + [ + 0.9067635536193848, + 3.2051706314086914 + ], + [ + 0.997984766960144, + 3.2036614418029785 + ], + [ + 1.1015901565551758, + 3.1929075717926025 + ], + [ + 1.1932510137557983, + 3.187041997909546 + ], + [ + 1.2955536842346191, + 3.1623470783233643 + ], + [ + 1.382931113243103, + 3.141171455383301 + ], + [ + 1.483323574066162, + 3.1154446601867676 + ], + [ + 1.5709964036941528, + 3.095097303390503 + ], + [ + 1.6650030612945557, + 3.066406726837158 + ], + [ + 1.7640049457550049, + 3.029162645339966 + ], + [ + 1.852841854095459, + 2.990962505340576 + ], + [ + 1.933456301689148, + 2.943995475769043 + ], + [ + 2.0165939331054688, + 2.8938910961151123 + ], + [ + 2.093055009841919, + 2.8433969020843506 + ], + [ + 2.183103084564209, + 2.781916379928589 + ], + [ + 2.2641451358795166, + 2.725630044937134 + ], + [ + 2.3360953330993652, + 2.6565022468566895 + ], + [ + 2.4069952964782715, + 2.580667018890381 + ], + [ + 2.481017827987671, + 2.4989025592803955 + ], + [ + 2.559150457382202, + 2.435673713684082 + ], + [ + 2.6248691082000732, + 2.3538222312927246 + ], + [ + 2.687619686126709, + 2.264403820037842 + ], + [ + 2.7574827671051025, + 2.159935235977173 + ], + [ + 2.8187215328216553, + 2.0657289028167725 + ], + [ + 2.8884854316711426, + 1.962977409362793 + ], + [ + 2.943880319595337, + 1.8824036121368408 + ], + [ + 3.0009965896606445, + 1.7796638011932373 + ], + [ + 3.059818983078003, + 1.6701451539993286 + ], + [ + 3.119795560836792, + 1.5660635232925415 + ], + [ + 3.179224967956543, + 1.4552077054977417 + ], + [ + 3.215134859085083, + 1.3652101755142212 + ], + [ + 3.2675390243530273, + 1.2542645931243896 + ], + [ + 3.3136749267578125, + 1.1495474576950073 + ], + [ + 3.354755163192749, + 1.0480055809020996 + ], + [ + 3.3946781158447266, + 0.9453073740005493 + ], + [ + 3.4283385276794434, + 0.847625732421875 + ], + [ + 3.4463376998901367, + 0.7492050528526306 + ], + [ + 3.4690709114074707, + 0.6568482518196106 + ], + [ + 3.4896035194396973, + 0.5599454045295715 + ], + [ + 3.5035505294799805, + 0.4682500660419464 + ], + [ + 3.5108110904693604, + 0.38048332929611206 + ], + [ + 3.5131337642669678, + 0.2925216257572174 + ], + [ + 3.5079283714294434, + 0.20350931584835052 + ], + [ + 3.5004985332489014, + 0.11184374243021011 + ], + [ + 3.4859938621520996, + 0.0272199809551239 + ], + [ + 3.467696189880371, + -0.06074925884604454 + ], + [ + 3.4491379261016846, + -0.15370480716228485 + ], + [ + 3.4266412258148193, + -0.25017639994621277 + ], + [ + 3.400458812713623, + -0.3469836711883545 + ], + [ + 3.3704798221588135, + -0.43758195638656616 + ], + [ + 3.341146945953369, + -0.5373067855834961 + ], + [ + 3.3167412281036377, + -0.6403863430023193 + ], + [ + 3.2941503524780273, + -0.7457326054573059 + ], + [ + 3.2800180912017822, + -0.8522046208381653 + ], + [ + 3.273306131362915, + -0.9474582672119141 + ], + [ + 3.244905471801758, + -1.0374606847763062 + ], + [ + 3.240784168243408, + -1.1273307800292969 + ], + [ + 3.238358736038208, + -1.2069965600967407 + ], + [ + 3.2290518283843994, + -1.273685097694397 + ], + [ + 3.2154459953308105, + -1.3342549800872803 + ], + [ + 3.2046396732330322, + -1.3789488077163696 + ], + [ + 3.183739185333252, + -1.4194018840789795 + ], + [ + 3.1693122386932373, + -1.4729559421539307 + ], + [ + 3.1344897747039795, + -1.503928542137146 + ], + [ + 3.087867259979248, + -1.5323659181594849 + ], + [ + 3.050173044204712, + -1.5592883825302124 + ], + [ + 3.000412940979004, + -1.587936520576477 + ], + [ + 2.9390182495117188, + -1.6146379709243774 + ], + [ + 2.881744623184204, + -1.6487141847610474 + ], + [ + 2.811448812484741, + -1.6720460653305054 + ], + [ + 2.752296209335327, + -1.7009607553482056 + ], + [ + 2.679608106613159, + -1.7358677387237549 + ], + [ + 2.625382900238037, + -1.776227355003357 + ], + [ + 2.555453062057495, + -1.8114641904830933 + ], + [ + 2.4777891635894775, + -1.8502765893936157 + ], + [ + 2.4119601249694824, + -1.8715084791183472 + ], + [ + 2.333287000656128, + -1.9057646989822388 + ], + [ + 2.257178544998169, + -1.9445021152496338 + ], + [ + 2.1759588718414307, + -1.9900689125061035 + ], + [ + 2.106809377670288, + -2.0283520221710205 + ], + [ + 2.0257275104522705, + -2.0787975788116455 + ], + [ + 1.9454381465911865, + -2.1256701946258545 + ], + [ + 1.8816059827804565, + -2.196702480316162 + ], + [ + 1.8152947425842285, + -2.218325138092041 + ], + [ + 1.7501616477966309, + -2.2719597816467285 + ], + [ + 1.6950534582138062, + -2.321479082107544 + ], + [ + 1.6311957836151123, + -2.370927572250366 + ], + [ + 1.5608556270599365, + -2.4165143966674805 + ], + [ + 1.518396258354187, + -2.460632085800171 + ], + [ + 1.4535807371139526, + -2.5074775218963623 + ], + [ + 1.4655482769012451, + -2.4239885807037354 + ], + [ + 1.4297897815704346, + -2.4633631706237793 + ], + [ + 1.3624173402786255, + -2.488583564758301 + ], + [ + 1.281326413154602, + -2.5086562633514404 + ], + [ + 1.2148277759552002, + -2.5413739681243896 + ], + [ + 1.1541898250579834, + -2.576077938079834 + ], + [ + 1.120560646057129, + -2.588573694229126 + ], + [ + 1.0596061944961548, + -2.5915963649749756 + ], + [ + 0.9839295744895935, + -2.6220412254333496 + ], + [ + 0.9174445271492004, + -2.6488966941833496 + ], + [ + 0.8565689325332642, + -2.669872522354126 + ], + [ + 0.7940139770507812, + -2.6969826221466064 + ], + [ + 0.7390028834342957, + -2.6805472373962402 + ], + [ + 0.6767779588699341, + -2.6966135501861572 + ], + [ + 0.6264951229095459, + -2.7159435749053955 + ], + [ + 0.5704982876777649, + -2.7378273010253906 + ], + [ + 0.5115839838981628, + -2.74786639213562 + ], + [ + 0.4556136429309845, + -2.7649221420288086 + ], + [ + 0.39606210589408875, + -2.739521026611328 + ], + [ + 0.3375975489616394, + -2.753755569458008 + ], + [ + 0.285901814699173, + -2.7672626972198486 + ], + [ + 0.24291600286960602, + -2.7797658443450928 + ], + [ + 0.19613294303417206, + -2.781292200088501 + ], + [ + 0.14553982019424438, + -2.7767372131347656 + ], + [ + 0.08217504620552063, + -2.7646071910858154 + ], + [ + 0.03410906344652176, + -2.781472682952881 + ], + [ + -0.011148075573146343, + -2.7739884853363037 + ], + [ + -0.0541127547621727, + -2.771231174468994 + ], + [ + -0.09749379009008408, + -2.7685039043426514 + ], + [ + -0.13890770077705383, + -2.7591419219970703 + ], + [ + -0.19543200731277466, + -2.746670722961426 + ], + [ + -0.2452128529548645, + -2.7403695583343506 + ], + [ + -0.2969488203525543, + -2.741960048675537 + ], + [ + -0.32703039050102234, + -2.7307851314544678 + ], + [ + -0.3661307692527771, + -2.7157373428344727 + ], + [ + -0.4008972644805908, + -2.7027904987335205 + ], + [ + -0.4249809682369232, + -2.6813294887542725 + ], + [ + -0.5050779581069946, + -2.6763815879821777 + ], + [ + -0.5491519570350647, + -2.663710355758667 + ], + [ + -0.5824120044708252, + -2.6504125595092773 + ], + [ + -0.6192184090614319, + -2.6219286918640137 + ], + [ + -0.6586366891860962, + -2.600358247756958 + ], + [ + -0.6942715644836426, + -2.5729053020477295 + ], + [ + -0.7309486865997314, + -2.5588650703430176 + ], + [ + -0.7765742540359497, + -2.5507702827453613 + ], + [ + -0.8136449456214905, + -2.5285873413085938 + ], + [ + -0.8497253060340881, + -2.496798276901245 + ], + [ + -0.8963299989700317, + -2.465245485305786 + ], + [ + -0.93206787109375, + -2.429049491882324 + ], + [ + -0.9799259901046753, + -2.3877339363098145 + ], + [ + -1.0078473091125488, + -2.398555278778076 + ], + [ + -1.0372157096862793, + -2.361074686050415 + ], + [ + -1.0823018550872803, + -2.3224871158599854 + ], + [ + -1.1329315900802612, + -2.2890939712524414 + ], + [ + -1.1833562850952148, + -2.2415292263031006 + ], + [ + -1.2269871234893799, + -2.2159857749938965 + ], + [ + -1.2922029495239258, + -2.162691116333008 + ], + [ + -1.3056666851043701, + -2.161792755126953 + ], + [ + -1.354131817817688, + -2.121523141860962 + ], + [ + -1.4096392393112183, + -2.0868189334869385 + ], + [ + -1.461287260055542, + -2.045318365097046 + ], + [ + -1.5167489051818848, + -2.0227153301239014 + ], + [ + -1.571979284286499, + -1.9972461462020874 + ], + [ + -1.6312527656555176, + -1.9690998792648315 + ], + [ + -1.6827654838562012, + -1.952263355255127 + ], + [ + -1.708079218864441, + -1.9103525876998901 + ], + [ + -1.7569104433059692, + -1.884315848350525 + ], + [ + -1.8081934452056885, + -1.8666108846664429 + ], + [ + -1.8563565015792847, + -1.8517392873764038 + ], + [ + -1.8965286016464233, + -1.8361438512802124 + ], + [ + -1.9335756301879883, + -1.8242131471633911 + ], + [ + -1.964591383934021, + -1.8026390075683594 + ], + [ + -2.0047719478607178, + -1.762892723083496 + ], + [ + -2.041518211364746, + -1.7451794147491455 + ], + [ + -2.071052074432373, + -1.7293972969055176 + ], + [ + -2.0981240272521973, + -1.706528902053833 + ], + [ + -2.11956524848938, + -1.6901549100875854 + ], + [ + -2.1400058269500732, + -1.6698988676071167 + ], + [ + -2.149365186691284, + -1.6461429595947266 + ], + [ + -2.158566474914551, + -1.6161984205245972 + ], + [ + -2.2120606899261475, + -1.5779215097427368 + ], + [ + -2.2249817848205566, + -1.5428466796875 + ], + [ + -2.2397396564483643, + -1.513846755027771 + ], + [ + -2.255702495574951, + -1.4739855527877808 + ], + [ + -2.789912700653076, + -1.9098845720291138 + ], + [ + -2.82194185256958, + -1.8552011251449585 + ], + [ + -2.8512136936187744, + -1.808366298675537 + ], + [ + -2.894787549972534, + -1.7530639171600342 + ], + [ + -2.9243111610412598, + -1.6943556070327759 + ], + [ + -2.9650661945343018, + -1.638841152191162 + ], + [ + -3.001330614089966, + -1.5780820846557617 + ], + [ + -3.0343077182769775, + -1.5203001499176025 + ], + [ + -3.0667078495025635, + -1.4537278413772583 + ], + [ + -3.105184316635132, + -1.391394019126892 + ], + [ + -3.1322951316833496, + -1.3280572891235352 + ], + [ + -3.1623547077178955, + -1.259221076965332 + ], + [ + -1.939096212387085, + -0.6807480454444885 + ], + [ + -1.952365517616272, + -0.6386276483535767 + ], + [ + -1.9693323373794556, + -0.5959262251853943 + ], + [ + -1.9833849668502808, + -0.5515741109848022 + ], + [ + -1.9971498250961304, + -0.5061970353126526 + ], + [ + -2.009053945541382, + -0.4613075256347656 + ], + [ + -2.021026372909546, + -0.41577762365341187 + ], + [ + -2.031355381011963, + -0.3683095872402191 + ], + [ + -2.0408895015716553, + -0.32223084568977356 + ], + [ + -2.049889326095581, + -0.27467721700668335 + ], + [ + -2.058365821838379, + -0.22663073241710663 + ], + [ + -2.0657122135162354, + -0.1788879632949829 + ], + [ + -2.0707199573516846, + -0.12972895801067352 + ], + [ + -2.07615065574646, + -0.08084411174058914 + ], + [ + -2.0804061889648438, + -0.031422313302755356 + ], + [ + -2.0833592414855957, + 0.018214151263237 + ], + [ + -2.085083246231079, + 0.0708853080868721 + ], + [ + -2.0858254432678223, + 0.12087997794151306 + ], + [ + -2.085240364074707, + 0.17192336916923523 + ], + [ + -2.084052085876465, + 0.223068505525589 + ], + [ + -2.081662178039551, + 0.27628636360168457 + ], + [ + -2.0768208503723145, + 0.3311088979244232 + ], + [ + -2.067357063293457, + 0.3807849884033203 + ], + [ + -2.062736988067627, + 0.43460559844970703 + ], + [ + -2.0520730018615723, + 0.4866396188735962 + ], + [ + -2.0412445068359375, + 0.5380857586860657 + ], + [ + -2.0276601314544678, + 0.5891246795654297 + ], + [ + -2.011918783187866, + 0.6406463384628296 + ], + [ + -1.9971976280212402, + 0.694553792476654 + ], + [ + -1.983178734779358, + 0.7432039976119995 + ], + [ + -1.9616353511810303, + 0.7950911521911621 + ], + [ + -1.9458224773406982, + 0.8461785316467285 + ], + [ + -1.9264793395996094, + 0.8947480916976929 + ], + [ + -1.9000548124313354, + 0.9455226063728333 + ], + [ + -1.8813544511795044, + 0.9935013651847839 + ], + [ + -1.8542447090148926, + 1.0390750169754028 + ], + [ + -1.8293994665145874, + 1.0922362804412842 + ], + [ + -1.7988624572753906, + 1.132934331893921 + ], + [ + -1.7675772905349731, + 1.1780340671539307 + ], + [ + -1.7385889291763306, + 1.2278425693511963 + ], + [ + -1.7053543329238892, + 1.273457407951355 + ], + [ + -1.6693856716156006, + 1.3176956176757812 + ], + [ + -1.633844256401062, + 1.3631333112716675 + ], + [ + -1.5976521968841553, + 1.4036017656326294 + ], + [ + -1.5626500844955444, + 1.4425859451293945 + ], + [ + -1.52292799949646, + 1.4863156080245972 + ], + [ + -1.4851118326187134, + 1.5260387659072876 + ], + [ + -1.4443962574005127, + 1.5574613809585571 + ], + [ + -1.3997883796691895, + 1.5909099578857422 + ], + [ + -1.3596993684768677, + 1.636057734489441 + ], + [ + -1.3204857110977173, + 1.6726205348968506 + ], + [ + -1.2703770399093628, + 1.697584629058838 + ], + [ + -1.2326018810272217, + 1.7287253141403198 + ], + [ + -1.1931045055389404, + 1.7673418521881104 + ], + [ + -1.153369426727295, + 1.7960216999053955 + ], + [ + -1.0999146699905396, + 1.8214483261108398 + ], + [ + -1.0547243356704712, + 1.8478145599365234 + ], + [ + -1.0112537145614624, + 1.8764448165893555 + ], + [ + -0.9666260480880737, + 1.8865482807159424 + ], + [ + -0.9129154682159424, + 1.914810299873352 + ], + [ + -0.8649275898933411, + 1.9297493696212769 + ], + [ + -0.8224252462387085, + 1.9530653953552246 + ], + [ + -0.7731329798698425, + 1.963896632194519 + ], + [ + -0.7225728631019592, + 1.9904707670211792 + ], + [ + -0.6760976910591125, + 1.9916374683380127 + ], + [ + -0.6234354376792908, + 2.014925718307495 + ], + [ + -0.5778567790985107, + 2.026262044906616 + ], + [ + -0.524203360080719, + 2.034918785095215 + ], + [ + -0.47348344326019287, + 2.040405035018921 + ], + [ + -0.42911845445632935, + 2.051117181777954 + ], + [ + -0.3717295825481415, + 2.057615280151367 + ], + [ + -0.32046380639076233, + 2.0602846145629883 + ], + [ + -0.2691158354282379, + 2.059157133102417 + ], + [ + -0.21888059377670288, + 2.0386364459991455 + ], + [ + -0.1683487445116043, + 2.0663208961486816 + ], + [ + -0.10686074197292328, + 2.078583002090454 + ], + [ + -0.05108408257365227, + 2.067359685897827 + ], + [ + -0.00803030002862215, + 2.074599027633667 + ], + [ + 0.048057761043310165, + 2.0656816959381104 + ], + [ + 0.09408649057149887, + 2.066115617752075 + ], + [ + 0.1477361023426056, + 2.054093599319458 + ], + [ + 0.19644670188426971, + 2.0475094318389893 + ], + [ + 0.23930613696575165, + 2.0432071685791016 + ], + [ + 0.2943762242794037, + 2.0298473834991455 + ], + [ + 0.3447219133377075, + 2.01904559135437 + ], + [ + 0.39233526587486267, + 2.003962278366089 + ], + [ + 0.44757068157196045, + 1.9899786710739136 + ], + [ + 0.4949842691421509, + 1.9744194746017456 + ], + [ + 0.5531154870986938, + 1.9555765390396118 + ], + [ + 0.5992284417152405, + 1.9338862895965576 + ], + [ + 0.646668553352356, + 1.9173840284347534 + ], + [ + 0.6888584494590759, + 1.8864469528198242 + ], + [ + 0.7450215220451355, + 1.870263695716858 + ], + [ + 0.794282078742981, + 1.8498250246047974 + ], + [ + 0.8379050493240356, + 1.8245142698287964 + ], + [ + 0.8887669444084167, + 1.8075196743011475 + ], + [ + 0.937105119228363, + 1.7697194814682007 + ], + [ + 0.9815776944160461, + 1.7397254705429077 + ], + [ + 1.0308599472045898, + 1.7040032148361206 + ], + [ + 1.0745210647583008, + 1.6717205047607422 + ], + [ + 1.1184508800506592, + 1.6470627784729004 + ], + [ + 1.1646403074264526, + 1.6061427593231201 + ], + [ + 1.205773115158081, + 1.5797123908996582 + ], + [ + 1.2512071132659912, + 1.5444042682647705 + ], + [ + 1.2964011430740356, + 1.4943119287490845 + ], + [ + 1.3324358463287354, + 1.4676051139831543 + ], + [ + 1.3684406280517578, + 1.420765995979309 + ], + [ + 1.4130305051803589, + 1.3833521604537964 + ], + [ + 1.4555394649505615, + 1.3444534540176392 + ], + [ + 1.4818600416183472, + 1.3038278818130493 + ], + [ + 1.5246280431747437, + 1.2591265439987183 + ], + [ + 1.559377908706665, + 1.2145498991012573 + ], + [ + 1.5942211151123047, + 1.1636466979980469 + ], + [ + 1.6295610666275024, + 1.1227933168411255 + ], + [ + 1.6616950035095215, + 1.0683050155639648 + ], + [ + 1.6954121589660645, + 1.0288985967636108 + ], + [ + 1.7216964960098267, + 0.9798287153244019 + ], + [ + 1.7503950595855713, + 0.9276873469352722 + ], + [ + 1.778201699256897, + 0.8792389035224915 + ], + [ + 1.8050205707550049, + 0.8297778964042664 + ], + [ + 1.836473822593689, + 0.7740538716316223 + ], + [ + 1.8550477027893066, + 0.7254865169525146 + ], + [ + 1.8791964054107666, + 0.6720587611198425 + ], + [ + 1.89922034740448, + 0.6195781230926514 + ], + [ + 1.9190661907196045, + 0.5651131868362427 + ], + [ + 1.9393385648727417, + 0.5110737085342407 + ], + [ + 1.9546383619308472, + 0.4575277864933014 + ], + [ + 1.9703558683395386, + 0.4025319814682007 + ], + [ + 1.9852973222732544, + 0.34665659070014954 + ], + [ + 1.9997735023498535, + 0.2932497262954712 + ], + [ + 2.0096211433410645, + 0.23785769939422607 + ], + [ + 2.02043080329895, + 0.1810353845357895 + ], + [ + 2.028529167175293, + 0.12593293190002441 + ], + [ + 2.0358526706695557, + 0.06951350718736649 + ], + [ + 2.041053056716919, + 0.014001079834997654 + ], + [ + 2.0463147163391113, + -0.04065854847431183 + ], + [ + 2.0535101890563965, + -0.09649515151977539 + ], + [ + 2.054388999938965, + -0.1520168036222458 + ], + [ + 2.0538721084594727, + -0.20868702232837677 + ], + [ + 2.053163766860962, + -0.2584943175315857 + ], + [ + 2.0484564304351807, + -0.3143188953399658 + ], + [ + 2.043355941772461, + -0.36864492297172546 + ], + [ + 2.042011022567749, + -0.4278182089328766 + ], + [ + 2.034322738647461, + -0.48044708371162415 + ], + [ + 2.025951623916626, + -0.5363051891326904 + ], + [ + 2.016893148422241, + -0.5878207087516785 + ], + [ + 2.0083096027374268, + -0.6492957472801208 + ], + [ + 2.0000500679016113, + -0.6932437419891357 + ], + [ + 1.987940788269043, + -0.7574043869972229 + ], + [ + 1.9705811738967896, + -0.8061993718147278 + ], + [ + 1.9567289352416992, + -0.8605440258979797 + ], + [ + 1.9346753358840942, + -0.9095450639724731 + ], + [ + 1.9202497005462646, + -0.9648932814598083 + ], + [ + 1.9040396213531494, + -1.0079864263534546 + ], + [ + 1.8808674812316895, + -1.060410976409912 + ], + [ + 1.8595004081726074, + -1.107595443725586 + ], + [ + 1.8395739793777466, + -1.16050124168396 + ], + [ + 1.8140474557876587, + -1.2115402221679688 + ], + [ + 1.7881207466125488, + -1.2526763677597046 + ], + [ + 1.7519309520721436, + -1.2990410327911377 + ], + [ + 1.7247456312179565, + -1.3432480096817017 + ], + [ + 1.700600028038025, + -1.3812659978866577 + ], + [ + 1.6677035093307495, + -1.432289958000183 + ], + [ + 1.6372331380844116, + -1.470292091369629 + ], + [ + 1.602839708328247, + -1.51361882686615 + ], + [ + 1.576010823249817, + -1.5580676794052124 + ], + [ + 1.540656328201294, + -1.5951287746429443 + ], + [ + 1.503659725189209, + -1.642486810684204 + ], + [ + 1.4607102870941162, + -1.671753168106079 + ], + [ + 1.4102507829666138, + -1.6961443424224854 + ], + [ + 1.3745391368865967, + -1.7225377559661865 + ], + [ + 1.3331958055496216, + -1.7523412704467773 + ], + [ + 1.2921346426010132, + -1.7896794080734253 + ], + [ + 1.2396968603134155, + -1.809958815574646 + ], + [ + 1.2020959854125977, + -1.8427790403366089 + ], + [ + 1.1576862335205078, + -1.874192714691162 + ], + [ + 1.118741750717163, + -1.9031258821487427 + ], + [ + 1.077420711517334, + -1.9349583387374878 + ], + [ + 1.0102405548095703, + -1.9094842672348022 + ], + [ + 0.9447270631790161, + -1.9233429431915283 + ], + [ + 0.9168527126312256, + -1.9392727613449097 + ], + [ + 0.8702920079231262, + -1.9707372188568115 + ], + [ + 0.8179040551185608, + -2.013446807861328 + ], + [ + 0.7824435234069824, + -2.04972767829895 + ], + [ + 0.7528225183486938, + -2.0892317295074463 + ], + [ + 0.711268961429596, + -2.1270482540130615 + ], + [ + 0.6382469534873962, + -2.1659374237060547 + ], + [ + 0.583331823348999, + -2.205756187438965 + ], + [ + 0.5129031538963318, + -2.2281875610351562 + ], + [ + 0.41605865955352783, + -2.168739080429077 + ], + [ + 0.3060882091522217, + -2.146209478378296 + ], + [ + 0.15365441143512726, + -2.105172872543335 + ], + [ + 0.0020785960368812084, + -2.040670394897461 + ], + [ + -0.15418343245983124, + -1.9243831634521484 + ], + [ + -0.3202892243862152, + -1.798927664756775 + ], + [ + -0.4346787929534912, + -1.6801048517227173 + ], + [ + -0.5110172629356384, + -1.613135576248169 + ], + [ + -0.5405659675598145, + -1.5904440879821777 + ], + [ + -0.5455673933029175, + -1.607232928276062 + ], + [ + -0.6063836216926575, + -1.9449594020843506 + ], + [ + -0.624832808971405, + -1.9866611957550049 + ], + [ + -0.6481931209564209, + -2.0024936199188232 + ], + [ + -0.6634911298751831, + -1.9572324752807617 + ], + [ + -0.6663447618484497, + -1.8878417015075684 + ], + [ + -0.6563385128974915, + -1.7929601669311523 + ], + [ + -0.6403387784957886, + -1.6972758769989014 + ], + [ + -0.6024368405342102, + -1.6063774824142456 + ], + [ + -0.5762656927108765, + -1.5338255167007446 + ], + [ + -0.5187647938728333, + -1.4662768840789795 + ], + [ + -0.48415064811706543, + -1.435086965560913 + ], + [ + -0.5525069236755371, + -1.5038563013076782 + ], + [ + -0.5161784887313843, + -1.4564497470855713 + ], + [ + -0.49484115839004517, + -1.4358186721801758 + ], + [ + -0.47963976860046387, + -1.437211513519287 + ], + [ + -0.49533164501190186, + -1.4836785793304443 + ], + [ + -0.5715327262878418, + -1.6046595573425293 + ], + [ + -0.7012724876403809, + -1.7719199657440186 + ], + [ + -0.8673467636108398, + -1.9547784328460693 + ], + [ + -1.015973448753357, + -2.101969003677368 + ], + [ + -1.1314986944198608, + -2.170522689819336 + ], + [ + -1.2274649143218994, + -2.160341262817383 + ], + [ + -1.3087838888168335, + -2.136418581008911 + ], + [ + -1.3310165405273438, + -2.1153032779693604 + ], + [ + -1.407902717590332, + -2.0705678462982178 + ], + [ + -1.475193738937378, + -2.0379385948181152 + ], + [ + -1.5394911766052246, + -1.9791806936264038 + ], + [ + -1.6067612171173096, + -1.91769540309906 + ], + [ + -1.650140643119812, + -1.8407727479934692 + ], + [ + -1.6875827312469482, + -1.7631479501724243 + ], + [ + -1.7184261083602905, + -1.671218752861023 + ], + [ + -1.7424614429473877, + -1.5910464525222778 + ], + [ + -1.7474254369735718, + -1.5262022018432617 + ], + [ + -1.7653908729553223, + -1.4905372858047485 + ], + [ + -1.8015632629394531, + -1.4718059301376343 + ], + [ + -1.8849856853485107, + -1.4614430665969849 + ], + [ + -1.9283195734024048, + -1.4638653993606567 + ], + [ + -1.9919277429580688, + -1.4629545211791992 + ], + [ + -2.0574071407318115, + -1.4738165140151978 + ], + [ + -2.1340599060058594, + -1.4894769191741943 + ], + [ + -2.2231273651123047, + -1.4883272647857666 + ], + [ + -2.291485548019409, + -1.470659852027893 + ], + [ + -2.3644659519195557, + -1.440994143486023 + ], + [ + -2.4340333938598633, + -1.4051308631896973 + ], + [ + -2.491485118865967, + -1.3487628698349 + ], + [ + -2.5485143661499023, + -1.2934077978134155 + ], + [ + -2.592172861099243, + -1.2255971431732178 + ], + [ + -2.6381776332855225, + -1.1586625576019287 + ], + [ + -2.695106029510498, + -1.0983964204788208 + ], + [ + -2.7385199069976807, + -1.0343594551086426 + ], + [ + -2.776291608810425, + -0.9640145301818848 + ], + [ + -2.81166934967041, + -0.8906009793281555 + ], + [ + -2.836707830429077, + -0.8137257099151611 + ], + [ + -2.858428955078125, + -0.7408859729766846 + ], + [ + -2.8790154457092285, + -0.6690824627876282 + ], + [ + -2.8940415382385254, + -0.5941474437713623 + ], + [ + -2.9010426998138428, + -0.5238000750541687 + ], + [ + -2.909085988998413, + -0.45078906416893005 + ], + [ + -2.9083189964294434, + -0.37507739663124084 + ], + [ + -2.9072840213775635, + -0.2997860908508301 + ], + [ + -3.005812883377075, + -0.21556322276592255 + ], + [ + -3.017341136932373, + -0.13587506115436554 + ], + [ + -3.023409843444824, + -0.05870097503066063 + ], + [ + -3.025590181350708, + 0.01880655437707901 + ], + [ + -3.025848865509033, + 0.09365110844373703 + ], + [ + -3.0220961570739746, + 0.17214366793632507 + ], + [ + -3.0202388763427734, + 0.24609537422657013 + ], + [ + -3.0081255435943604, + 0.3179972767829895 + ], + [ + -2.995783567428589, + 0.39712363481521606 + ], + [ + -2.984403371810913, + 0.46626558899879456 + ], + [ + -2.970194101333618, + 0.5454097986221313 + ], + [ + -2.9559531211853027, + 0.615135133266449 + ], + [ + -2.9344537258148193, + 0.6851721405982971 + ], + [ + -2.9574944972991943, + 0.7915886044502258 + ], + [ + -2.9441006183624268, + 0.8669255375862122 + ], + [ + -2.916849374771118, + 0.9476485848426819 + ], + [ + -2.897669792175293, + 1.0129587650299072 + ], + [ + -2.876121759414673, + 1.086795687675476 + ], + [ + -2.84883451461792, + 1.1496262550354004 + ], + [ + -2.8302626609802246, + 1.2204465866088867 + ], + [ + -2.7993199825286865, + 1.2962018251419067 + ], + [ + -2.7594807147979736, + 1.3708014488220215 + ], + [ + -2.7271833419799805, + 1.4501303434371948 + ], + [ + -2.6744866371154785, + 1.5389537811279297 + ], + [ + -2.61313796043396, + 1.5949156284332275 + ], + [ + -2.5551230907440186, + 1.6584594249725342 + ], + [ + -2.4908816814422607, + 1.7083662748336792 + ], + [ + -2.4266505241394043, + 1.7613481283187866 + ], + [ + -2.38466477394104, + 1.7962907552719116 + ], + [ + -2.3341479301452637, + 1.8218728303909302 + ], + [ + -2.2738988399505615, + 1.8703877925872803 + ], + [ + -2.2376792430877686, + 1.8903529644012451 + ], + [ + -2.1968719959259033, + 1.9464670419692993 + ], + [ + -2.153024911880493, + 1.9752123355865479 + ], + [ + -2.1001553535461426, + 2.00573992729187 + ], + [ + -2.054490089416504, + 2.0517256259918213 + ], + [ + -1.992921233177185, + 2.088070869445801 + ], + [ + -1.9374616146087646, + 2.111299991607666 + ], + [ + -1.880867838859558, + 2.13771915435791 + ], + [ + -1.8321253061294556, + 2.172494649887085 + ], + [ + -1.7726197242736816, + 2.2032346725463867 + ], + [ + -1.727648377418518, + 2.228423833847046 + ], + [ + -1.6646350622177124, + 2.245445966720581 + ], + [ + -1.6086881160736084, + 2.256934404373169 + ], + [ + -1.5574150085449219, + 2.2773215770721436 + ], + [ + -1.5145063400268555, + 2.3043603897094727 + ], + [ + -1.4563183784484863, + 2.2967689037323 + ], + [ + -1.3895223140716553, + 2.304969310760498 + ], + [ + -1.3440701961517334, + 2.321601629257202 + ], + [ + -1.3160995244979858, + 2.337130069732666 + ], + [ + -1.2590749263763428, + 2.335090398788452 + ], + [ + -1.2157617807388306, + 2.353987693786621 + ], + [ + -1.1620811223983765, + 2.3656516075134277 + ], + [ + -1.1153879165649414, + 2.363826274871826 + ], + [ + -1.0671113729476929, + 2.378605842590332 + ], + [ + -1.0245308876037598, + 2.4003958702087402 + ], + [ + -0.9690240621566772, + 2.404007911682129 + ], + [ + -0.9172849655151367, + 2.416661024093628 + ], + [ + -0.888172447681427, + 2.4239163398742676 + ], + [ + -0.8456668257713318, + 2.434183359146118 + ], + [ + -0.8011134266853333, + 2.4406189918518066 + ], + [ + -0.7593628764152527, + 2.447582483291626 + ], + [ + -0.7207033634185791, + 2.459336280822754 + ], + [ + -0.6904757022857666, + 2.488373041152954 + ], + [ + -0.6264764666557312, + 2.486638069152832 + ], + [ + -0.596717894077301, + 2.4937942028045654 + ], + [ + -0.546213686466217, + 2.4793148040771484 + ], + [ + -0.48355063796043396, + 2.5083060264587402 + ], + [ + -0.4249427020549774, + 2.500951051712036 + ], + [ + -0.37415939569473267, + 2.534940481185913 + ], + [ + -0.3312223553657532, + 2.5396077632904053 + ], + [ + -0.2889305353164673, + 2.5419344902038574 + ], + [ + -0.21558496356010437, + 2.560695171356201 + ], + [ + -0.16291315853595734, + 2.576202392578125 + ], + [ + -0.10912346839904785, + 2.5665762424468994 + ], + [ + -0.051017604768276215, + 2.5686533451080322 + ], + [ + 0.018875345587730408, + 2.567444086074829 + ], + [ + 0.06465987861156464, + 2.5701773166656494 + ], + [ + 0.1324782520532608, + 2.561803102493286 + ], + [ + 0.2068946808576584, + 2.555746078491211 + ], + [ + 0.2597694993019104, + 2.5605828762054443 + ], + [ + 0.3346307873725891, + 2.54529070854187 + ], + [ + 0.4071377217769623, + 2.5414583683013916 + ], + [ + 0.4494141936302185, + 2.5375921726226807 + ], + [ + 0.5025323629379272, + 2.508594274520874 + ], + [ + 0.5790141820907593, + 2.5116915702819824 + ], + [ + 0.6427742838859558, + 2.501234292984009 + ], + [ + 0.6994442343711853, + 2.4768364429473877 + ], + [ + 0.7833623290061951, + 2.457221031188965 + ], + [ + 0.8016390204429626, + 2.4768195152282715 + ], + [ + 0.9044339060783386, + 2.4217684268951416 + ], + [ + 0.9513251185417175, + 2.4143903255462646 + ], + [ + 1.0317407846450806, + 2.372506856918335 + ], + [ + 1.0866315364837646, + 2.3450143337249756 + ], + [ + 1.1452995538711548, + 2.3264284133911133 + ], + [ + 1.222049355506897, + 2.2988715171813965 + ], + [ + 1.2785722017288208, + 2.264704942703247 + ], + [ + 1.3450956344604492, + 2.2289061546325684 + ], + [ + 1.4226233959197998, + 2.2009341716766357 + ], + [ + 1.4660910367965698, + 2.160318374633789 + ], + [ + 1.5323148965835571, + 2.1187257766723633 + ], + [ + 1.5581592321395874, + 2.095895528793335 + ], + [ + 1.6538889408111572, + 2.0462610721588135 + ], + [ + 1.7080063819885254, + 1.995171070098877 + ], + [ + 1.7654551267623901, + 1.9639097452163696 + ], + [ + 1.83365797996521, + 1.9093478918075562 + ], + [ + 1.886694312095642, + 1.8681954145431519 + ], + [ + 1.940342903137207, + 1.8244189023971558 + ], + [ + 1.99257493019104, + 1.7620192766189575 + ], + [ + 2.0535972118377686, + 1.7182610034942627 + ], + [ + 2.104647397994995, + 1.670270323753357 + ], + [ + 2.160341739654541, + 1.6226904392242432 + ], + [ + 2.204922914505005, + 1.5611199140548706 + ], + [ + 2.2633750438690186, + 1.4981132745742798 + ], + [ + 2.2971978187561035, + 1.4515384435653687 + ], + [ + 2.355032205581665, + 1.397208571434021 + ], + [ + 2.395582675933838, + 1.3320810794830322 + ], + [ + 2.437032699584961, + 1.2740654945373535 + ], + [ + 2.4867217540740967, + 1.205893874168396 + ], + [ + 2.530080556869507, + 1.138196349143982 + ], + [ + 2.575047254562378, + 1.0777791738510132 + ], + [ + 2.618283271789551, + 1.0098692178726196 + ], + [ + 2.6614513397216797, + 0.9418083429336548 + ], + [ + 2.6999340057373047, + 0.8697630763053894 + ], + [ + 2.7298192977905273, + 0.8084556460380554 + ], + [ + 2.7694876194000244, + 0.7403592467308044 + ], + [ + 2.7972564697265625, + 0.6631399393081665 + ], + [ + 2.8277485370635986, + 0.5916881561279297 + ], + [ + 2.8645966053009033, + 0.5160143971443176 + ], + [ + 2.8924617767333984, + 0.43553632497787476 + ], + [ + 2.9207751750946045, + 0.3516179919242859 + ], + [ + 2.9441661834716797, + 0.2656373977661133 + ], + [ + 2.9708361625671387, + 0.18012608587741852 + ], + [ + 2.995119094848633, + 0.09640496969223022 + ], + [ + 3.017622232437134, + 0.002266376744955778 + ], + [ + 3.043760061264038, + -0.08903345465660095 + ], + [ + 3.0452544689178467, + -0.16451512277126312 + ], + [ + 3.0585885047912598, + -0.2522426247596741 + ], + [ + 3.0773463249206543, + -0.3484383523464203 + ], + [ + 3.086075782775879, + -0.447540819644928 + ], + [ + 3.080408811569214, + -0.5331095457077026 + ], + [ + 3.0869836807250977, + -0.6308740377426147 + ], + [ + 3.083768129348755, + -0.7343985438346863 + ], + [ + 3.0898289680480957, + -0.8323004245758057 + ], + [ + 3.0833899974823, + -0.9319173097610474 + ], + [ + 3.0904698371887207, + -1.0364118814468384 + ], + [ + 3.080061674118042, + -1.132294774055481 + ], + [ + 3.0699727535247803, + -1.2475190162658691 + ], + [ + 3.0850470066070557, + -1.3411211967468262 + ], + [ + 3.0926270484924316, + -1.4598098993301392 + ], + [ + 2.9818525314331055, + -1.5555393695831299 + ], + [ + 2.9591095447540283, + -1.6487717628479004 + ], + [ + 2.9116525650024414, + -1.7719738483428955 + ], + [ + 2.8920342922210693, + -1.8652126789093018 + ], + [ + 2.835754632949829, + -1.9713828563690186 + ], + [ + 2.804757833480835, + -2.1031105518341064 + ], + [ + 2.753603458404541, + -2.2083773612976074 + ], + [ + 2.689694881439209, + -2.318589925765991 + ], + [ + 2.638504981994629, + -2.4216058254241943 + ], + [ + 2.5509848594665527, + -2.5275609493255615 + ], + [ + 2.489881753921509, + -2.6566803455352783 + ], + [ + 2.380188465118408, + -2.7509565353393555 + ], + [ + 2.2542405128479004, + -2.8752071857452393 + ], + [ + 2.150723934173584, + -2.9281368255615234 + ], + [ + 2.0715248584747314, + -3.0241780281066895 + ], + [ + 1.9682269096374512, + -3.1185126304626465 + ], + [ + 1.8939309120178223, + -3.1630985736846924 + ], + [ + 1.7857937812805176, + -3.261897325515747 + ], + [ + 1.6356526613235474, + -3.3611268997192383 + ], + [ + 1.5163850784301758, + -3.4567573070526123 + ], + [ + 1.3732144832611084, + -3.5010128021240234 + ], + [ + 1.2104030847549438, + -3.5518276691436768 + ], + [ + 1.0430190563201904, + -3.5933995246887207 + ], + [ + 0.8822158575057983, + -3.623825788497925 + ], + [ + 0.6627976298332214, + -3.6547963619232178 + ], + [ + 0.44273343682289124, + -3.6631481647491455 + ], + [ + 0.42717331647872925, + -3.7039060592651367 + ], + [ + 0.32416683435440063, + -3.795259714126587 + ], + [ + 0.17221441864967346, + -3.8220949172973633 + ], + [ + 0.03688724339008331, + -3.916059732437134 + ], + [ + -0.11089131981134415, + -3.936948299407959 + ], + [ + -0.252693235874176, + -3.9844772815704346 + ], + [ + -0.43921273946762085, + -4.0095295906066895 + ], + [ + -0.6141329407691956, + -4.009274482727051 + ], + [ + -0.7696290612220764, + -3.970614194869995 + ], + [ + -0.931189775466919, + -3.9552981853485107 + ], + [ + -1.1234012842178345, + -3.8894717693328857 + ], + [ + -1.3107601404190063, + -3.815281391143799 + ], + [ + -1.4806294441223145, + -3.7320592403411865 + ], + [ + -3.1617636680603027, + -7.200752258300781 + ], + [ + -3.5687215328216553, + -7.0088019371032715 + ], + [ + -3.4965732097625732, + -7.553720474243164 + ], + [ + -3.828077554702759, + -7.578862190246582 + ], + [ + -4.215964317321777, + -7.5158257484436035 + ], + [ + -4.464430332183838, + -7.543081760406494 + ], + [ + -4.748383522033691, + -7.505837917327881 + ], + [ + -5.119718074798584, + -7.260599136352539 + ], + [ + -5.392154693603516, + -7.093082427978516 + ], + [ + -5.704444408416748, + -6.892176151275635 + ], + [ + -5.940671920776367, + -6.73836088180542 + ], + [ + -6.2074875831604, + -6.455677509307861 + ], + [ + -6.4956464767456055, + -6.174989700317383 + ], + [ + -6.733001232147217, + -5.923089027404785 + ], + [ + -6.919020175933838, + -5.556769371032715 + ], + [ + -7.1211748123168945, + -5.126523971557617 + ], + [ + -7.485099792480469, + -5.612582206726074 + ], + [ + -7.772981643676758, + -5.378384590148926 + ], + [ + -7.964595794677734, + -5.172052383422852 + ], + [ + -8.221421241760254, + -5.0016374588012695 + ], + [ + -8.366085052490234, + -4.667886257171631 + ], + [ + -8.53973388671875, + -4.368419647216797 + ], + [ + -8.660114288330078, + -4.072993755340576 + ], + [ + -8.829169273376465, + -3.807664632797241 + ], + [ + -8.966348648071289, + -3.554037570953369 + ], + [ + -9.068779945373535, + -3.2637033462524414 + ], + [ + -9.138891220092773, + -2.962503671646118 + ], + [ + -9.22436237335205, + -2.6824145317077637 + ], + [ + -9.287652015686035, + -2.415142774581909 + ], + [ + -9.32740592956543, + -2.1294572353363037 + ], + [ + -9.349847793579102, + -1.8417268991470337 + ], + [ + -9.568178176879883, + -1.583898901939392 + ], + [ + -9.549560546875, + -1.31840181350708 + ], + [ + -9.608781814575195, + -1.0010920763015747 + ], + [ + -9.653103828430176, + -0.7563896179199219 + ], + [ + -9.655699729919434, + -0.4968414604663849 + ], + [ + -9.631248474121094, + -0.22788356244564056 + ], + [ + -9.6608304977417, + 0.03178777918219566 + ], + [ + -9.656210899353027, + 0.2685079574584961 + ], + [ + -9.6510009765625, + 0.5217161178588867 + ], + [ + -9.626096725463867, + 0.7814257144927979 + ], + [ + -9.619415283203125, + 0.9793218374252319 + ], + [ + -9.602843284606934, + 1.2214114665985107 + ], + [ + -9.577664375305176, + 1.438049554824829 + ], + [ + -9.52717399597168, + 1.7186967134475708 + ], + [ + -9.467283248901367, + 1.9591264724731445 + ] + ], + "total_points": 1000 +} \ No newline at end of file diff --git a/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/load_metadata.json b/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/load_metadata.json new file mode 100644 index 0000000..91d9c89 --- /dev/null +++ b/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/load_metadata.json @@ -0,0 +1,16 @@ +{ + "preset": { + "filename": "s11_start100_stop8800_points1000_bw1khz.bin", + "mode": "s11", + "start_freq": 100000000.0, + "stop_freq": 8800000000.0, + "points": 1000, + "bandwidth": 1000.0 + }, + "calibration_name": "SRGDFDFG", + "standard": "load", + "sweep_number": 224, + "sweep_timestamp": 1758806106.5021908, + "created_timestamp": "2025-09-25T16:15:09.918522", + "total_points": 1000 +} \ No newline at end of file diff --git a/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/open.json b/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/open.json new file mode 100644 index 0000000..707804f --- /dev/null +++ b/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/open.json @@ -0,0 +1,4007 @@ +{ + "sweep_number": 224, + "timestamp": 1758806106.5021908, + "points": [ + [ + -2.6736810207366943, + 0.45182546973228455 + ], + [ + -2.6649160385131836, + 0.5124151110649109 + ], + [ + -2.6564583778381348, + 0.5761726498603821 + ], + [ + -2.6471076011657715, + 0.6357683539390564 + ], + [ + -2.63456392288208, + 0.6980700492858887 + ], + [ + -3.406205415725708, + 0.9276503920555115 + ], + [ + -3.3926522731781006, + 0.9973128437995911 + ], + [ + -3.3718581199645996, + 1.0803601741790771 + ], + [ + -3.354764461517334, + 1.1557843685150146 + ], + [ + -3.3333864212036133, + 1.2311294078826904 + ], + [ + -3.306791067123413, + 1.3099294900894165 + ], + [ + -3.2835750579833984, + 1.3874938488006592 + ], + [ + -3.260197162628174, + 1.4654496908187866 + ], + [ + -3.235013484954834, + 1.5420405864715576 + ], + [ + -3.2073099613189697, + 1.615953803062439 + ], + [ + -3.17728853225708, + 1.6916990280151367 + ], + [ + -3.1466078758239746, + 1.7638152837753296 + ], + [ + -3.111706495285034, + 1.8342149257659912 + ], + [ + -3.0758213996887207, + 1.9107712507247925 + ], + [ + -3.04197359085083, + 1.9872909784317017 + ], + [ + -3.007207155227661, + 2.0558650493621826 + ], + [ + -2.9629905223846436, + 2.1273269653320312 + ], + [ + -2.9264559745788574, + 2.204591989517212 + ], + [ + -2.8800888061523438, + 2.27834415435791 + ], + [ + -2.842005491256714, + 2.3492860794067383 + ], + [ + -2.798678398132324, + 2.4209070205688477 + ], + [ + -2.7528514862060547, + 2.4890832901000977 + ], + [ + -2.702406406402588, + 2.5655250549316406 + ], + [ + -2.6573588848114014, + 2.6316630840301514 + ], + [ + -2.606713056564331, + 2.7075676918029785 + ], + [ + -2.5558927059173584, + 2.7759933471679688 + ], + [ + -2.511176109313965, + 2.8374271392822266 + ], + [ + -2.453795909881592, + 2.907130718231201 + ], + [ + -2.395359754562378, + 2.969266891479492 + ], + [ + -2.335897922515869, + 3.0380139350891113 + ], + [ + -2.270254135131836, + 3.102494955062866 + ], + [ + -2.1994450092315674, + 3.1652512550354004 + ], + [ + -2.1340577602386475, + 3.2305760383605957 + ], + [ + -2.0573885440826416, + 3.2901291847229004 + ], + [ + -1.9910917282104492, + 3.3597488403320312 + ], + [ + -1.914387583732605, + 3.421565532684326 + ], + [ + -1.8417962789535522, + 3.4749624729156494 + ], + [ + -1.7740607261657715, + 3.538019895553589 + ], + [ + -1.6910836696624756, + 3.593351364135742 + ], + [ + -1.6129546165466309, + 3.650934934616089 + ], + [ + -1.5314576625823975, + 3.7079336643218994 + ], + [ + -1.4456006288528442, + 3.7681336402893066 + ], + [ + -1.3613699674606323, + 3.816110610961914 + ], + [ + -1.2707738876342773, + 3.867534637451172 + ], + [ + -1.1808480024337769, + 3.917786121368408 + ], + [ + -1.090746521949768, + 3.965712308883667 + ], + [ + -0.9997296929359436, + 4.015567302703857 + ], + [ + -0.8969882726669312, + 4.064459323883057 + ], + [ + -0.8074730038642883, + 4.103725433349609 + ], + [ + -0.705039918422699, + 4.142040252685547 + ], + [ + -0.6112637519836426, + 4.195705890655518 + ], + [ + -0.5010696649551392, + 4.22493839263916 + ], + [ + -0.4009867012500763, + 4.263381481170654 + ], + [ + -0.2912496328353882, + 4.291356086730957 + ], + [ + -0.17719513177871704, + 4.322331428527832 + ], + [ + -0.0559571348130703, + 4.347788333892822 + ], + [ + 0.053220413625240326, + 4.367687225341797 + ], + [ + 0.1828378587961197, + 4.393011093139648 + ], + [ + 0.3024580180644989, + 4.400704860687256 + ], + [ + 0.4196595847606659, + 4.400711536407471 + ], + [ + 0.5461052656173706, + 4.420855522155762 + ], + [ + 0.6670081615447998, + 4.422549247741699 + ], + [ + 0.7900673151016235, + 4.433554172515869 + ], + [ + 0.9182475209236145, + 4.435775279998779 + ], + [ + 1.0549386739730835, + 4.4369611740112305 + ], + [ + 1.1896811723709106, + 4.438133239746094 + ], + [ + 1.3255913257598877, + 4.427420616149902 + ], + [ + 1.4510456323623657, + 4.396478176116943 + ], + [ + 1.5899698734283447, + 4.381324768066406 + ], + [ + 1.7368065118789673, + 4.359009265899658 + ], + [ + 1.867936134338379, + 4.326304912567139 + ], + [ + 1.9993454217910767, + 4.286786079406738 + ], + [ + 2.1317646503448486, + 4.249267101287842 + ], + [ + 2.26725697517395, + 4.204619884490967 + ], + [ + 2.4096617698669434, + 4.160815715789795 + ], + [ + 2.5364108085632324, + 4.112579822540283 + ], + [ + 2.657930612564087, + 4.05103063583374 + ], + [ + 2.787731409072876, + 3.9864959716796875 + ], + [ + 2.9073808193206787, + 3.9175057411193848 + ], + [ + 3.0339295864105225, + 3.849170684814453 + ], + [ + 3.1596813201904297, + 3.782402992248535 + ], + [ + 3.266500949859619, + 3.694551467895508 + ], + [ + 3.377516746520996, + 3.6156153678894043 + ], + [ + 3.5012471675872803, + 3.522921085357666 + ], + [ + 3.607501268386841, + 3.4285483360290527 + ], + [ + 3.721850872039795, + 3.327152967453003 + ], + [ + 3.827000141143799, + 3.223395824432373 + ], + [ + 3.9379539489746094, + 3.1131348609924316 + ], + [ + 4.042356014251709, + 2.996729612350464 + ], + [ + 4.159390449523926, + 2.877631187438965 + ], + [ + 4.2581048011779785, + 2.7344279289245605 + ], + [ + 4.342874526977539, + 2.610862970352173 + ], + [ + 4.443049430847168, + 2.4738378524780273 + ], + [ + 4.523603439331055, + 2.3404018878936768 + ], + [ + 4.5946245193481445, + 2.2019197940826416 + ], + [ + 4.675113677978516, + 2.0515711307525635 + ], + [ + 4.738132476806641, + 1.8976340293884277 + ], + [ + 4.78986930847168, + 1.7495304346084595 + ], + [ + 4.853011608123779, + 1.5902783870697021 + ], + [ + 4.901889324188232, + 1.4279320240020752 + ], + [ + 4.941633701324463, + 1.2628992795944214 + ], + [ + 4.986309051513672, + 1.1021469831466675 + ], + [ + 5.011632442474365, + 0.935245931148529 + ], + [ + 5.037826061248779, + 0.7647016048431396 + ], + [ + 5.053710460662842, + 0.6024985313415527 + ], + [ + 5.0733466148376465, + 0.43650737404823303 + ], + [ + 5.089456558227539, + 0.27373409271240234 + ], + [ + 5.078577041625977, + 0.10420450568199158 + ], + [ + 5.072869300842285, + -0.05629599466919899 + ], + [ + 5.057137489318848, + -0.21737471222877502 + ], + [ + 5.033437728881836, + -0.34463685750961304 + ], + [ + 4.998108863830566, + -0.5058478116989136 + ], + [ + 4.966320514678955, + -0.6498308777809143 + ], + [ + 4.915668964385986, + -0.7943239212036133 + ], + [ + 4.865395545959473, + -0.9435988664627075 + ], + [ + 4.807960510253906, + -1.081966519355774 + ], + [ + 4.7376532554626465, + -1.2200692892074585 + ], + [ + 4.667508602142334, + -1.355132818222046 + ], + [ + 4.595383644104004, + -1.4871387481689453 + ], + [ + 4.515742301940918, + -1.622791051864624 + ], + [ + 4.422509670257568, + -1.7464717626571655 + ], + [ + 4.3369340896606445, + -1.8784594535827637 + ], + [ + 4.242724418640137, + -2.005474090576172 + ], + [ + 4.14840030670166, + -2.147378444671631 + ], + [ + 4.063354969024658, + -2.2759499549865723 + ], + [ + 3.951305389404297, + -2.3975613117218018 + ], + [ + 3.8580098152160645, + -2.5223536491394043 + ], + [ + 3.765162467956543, + -2.6296277046203613 + ], + [ + 3.6515040397644043, + -2.745894432067871 + ], + [ + 3.5635275840759277, + -2.845367908477783 + ], + [ + 3.4549825191497803, + -2.9300620555877686 + ], + [ + 3.3704142570495605, + -3.033066511154175 + ], + [ + 3.2684061527252197, + -3.090548515319824 + ], + [ + 3.160047769546509, + -3.149569272994995 + ], + [ + 3.0653798580169678, + -3.193134069442749 + ], + [ + 2.957157611846924, + -3.2273616790771484 + ], + [ + 2.8522789478302, + -3.2683937549591064 + ], + [ + 2.7458417415618896, + -3.283806562423706 + ], + [ + 2.632183313369751, + -3.307053565979004 + ], + [ + 2.528501510620117, + -3.3292500972747803 + ], + [ + 2.4107823371887207, + -3.344839096069336 + ], + [ + 2.298522472381592, + -3.348083257675171 + ], + [ + 2.1977953910827637, + -3.372875452041626 + ], + [ + 2.083407163619995, + -3.366670846939087 + ], + [ + 1.9608125686645508, + -3.3750596046447754 + ], + [ + 1.8605008125305176, + -3.384669780731201 + ], + [ + 1.7375348806381226, + -3.395942211151123 + ], + [ + 1.6165754795074463, + -3.4232215881347656 + ], + [ + 1.473440170288086, + -3.625798225402832 + ], + [ + 1.3664487600326538, + -3.628476858139038 + ], + [ + 1.2635457515716553, + -3.6369354724884033 + ], + [ + 1.1742236614227295, + -3.616102933883667 + ], + [ + 1.0705455541610718, + -3.617340087890625 + ], + [ + 0.9761837124824524, + -3.6077592372894287 + ], + [ + 0.8788896203041077, + -3.604526996612549 + ], + [ + 0.7796502709388733, + -3.5850894451141357 + ], + [ + 0.6826730370521545, + -3.5633981227874756 + ], + [ + 0.5934434533119202, + -3.5596537590026855 + ], + [ + 0.5035812854766846, + -3.5311779975891113 + ], + [ + 0.4178984463214874, + -3.5169882774353027 + ], + [ + 0.32849785685539246, + -3.5019373893737793 + ], + [ + 0.24413107335567474, + -3.4760217666625977 + ], + [ + 0.16599296033382416, + -3.457561492919922 + ], + [ + 0.0886717289686203, + -3.432316541671753 + ], + [ + 0.0069116936065256596, + -3.410564422607422 + ], + [ + -0.06821972876787186, + -3.385152816772461 + ], + [ + -0.1381102055311203, + -3.3575682640075684 + ], + [ + -0.2036321461200714, + -3.32989239692688 + ], + [ + -0.2721547484397888, + -3.2988481521606445 + ], + [ + -0.3456687927246094, + -3.2797422409057617 + ], + [ + -0.40491026639938354, + -3.2436702251434326 + ], + [ + -0.46992459893226624, + -3.217508316040039 + ], + [ + -0.5275281667709351, + -3.18990421295166 + ], + [ + -0.5910951495170593, + -3.159196615219116 + ], + [ + -0.6481737494468689, + -3.1255087852478027 + ], + [ + -0.6999361515045166, + -3.0945961475372314 + ], + [ + -0.7655573487281799, + -3.066328287124634 + ], + [ + -0.8193655014038086, + -3.029822826385498 + ], + [ + -0.8740184307098389, + -2.9954843521118164 + ], + [ + -0.9328928589820862, + -2.9635941982269287 + ], + [ + -0.9758638739585876, + -2.9242727756500244 + ], + [ + -1.0377837419509888, + -2.890282154083252 + ], + [ + -1.086008906364441, + -2.853980302810669 + ], + [ + -1.1375977993011475, + -2.827772855758667 + ], + [ + -1.1902581453323364, + -2.789005756378174 + ], + [ + -1.2380311489105225, + -2.7569501399993896 + ], + [ + -1.2801151275634766, + -2.723897933959961 + ], + [ + -1.331512212753296, + -2.6854257583618164 + ], + [ + -1.3717644214630127, + -2.6477699279785156 + ], + [ + -1.4093807935714722, + -2.6117165088653564 + ], + [ + -1.4476438760757446, + -2.56960129737854 + ], + [ + -1.4962366819381714, + -2.5276761054992676 + ], + [ + -1.5316818952560425, + -2.483553647994995 + ], + [ + -1.5692683458328247, + -2.439359188079834 + ], + [ + -1.607676386833191, + -2.3932254314422607 + ], + [ + -1.615010142326355, + -2.3210482597351074 + ], + [ + -1.6576086282730103, + -2.2704386711120605 + ], + [ + -1.7165112495422363, + -2.220757484436035 + ], + [ + -1.7511240243911743, + -2.1732826232910156 + ], + [ + -1.804411768913269, + -2.117375373840332 + ], + [ + -1.842379093170166, + -2.0728418827056885 + ], + [ + -1.8945618867874146, + -2.0168027877807617 + ], + [ + -1.935418963432312, + -1.9641530513763428 + ], + [ + -1.9654779434204102, + -1.9175623655319214 + ], + [ + -2.014981985092163, + -1.8624297380447388 + ], + [ + -2.055156946182251, + -1.8145853281021118 + ], + [ + -2.089393377304077, + -1.7664117813110352 + ], + [ + -2.121464252471924, + -1.7126656770706177 + ], + [ + -2.154043197631836, + -1.664276361465454 + ], + [ + -2.1856307983398438, + -1.615336537361145 + ], + [ + -2.215449333190918, + -1.5591015815734863 + ], + [ + -2.242549419403076, + -1.5095828771591187 + ], + [ + -2.265376567840576, + -1.4667716026306152 + ], + [ + -2.2891957759857178, + -1.4067754745483398 + ], + [ + -2.3102810382843018, + -1.3600338697433472 + ], + [ + -2.32448410987854, + -1.3162206411361694 + ], + [ + -2.344461441040039, + -1.2646205425262451 + ], + [ + -2.358267068862915, + -1.2159160375595093 + ], + [ + -2.3702125549316406, + -1.1691083908081055 + ], + [ + -2.377868890762329, + -1.121673822402954 + ], + [ + -2.394197463989258, + -1.0700950622558594 + ], + [ + -2.405461072921753, + -1.0208792686462402 + ], + [ + -2.407872200012207, + -0.968040406703949 + ], + [ + -2.4239590167999268, + -0.9168859124183655 + ], + [ + -2.4302685260772705, + -0.8633595705032349 + ], + [ + -2.438366413116455, + -0.8092073798179626 + ], + [ + -2.4480350017547607, + -0.7520635724067688 + ], + [ + -2.45904803276062, + -0.6941565871238708 + ], + [ + -2.4681553840637207, + -0.6360258460044861 + ], + [ + -2.483577251434326, + -0.5720770359039307 + ], + [ + -2.4936487674713135, + -0.5103459358215332 + ], + [ + -2.503358840942383, + -0.4530770182609558 + ], + [ + -2.516498565673828, + -0.39213451743125916 + ], + [ + -2.524306058883667, + -0.3296535611152649 + ], + [ + -2.5337798595428467, + -0.2714705765247345 + ], + [ + -2.5415852069854736, + -0.21370919048786163 + ], + [ + -2.5448076725006104, + -0.15511257946491241 + ], + [ + -2.5307958126068115, + -0.10204117000102997 + ], + [ + -2.527467727661133, + -0.04747380316257477 + ], + [ + -2.52011775970459, + 0.007723364047706127 + ], + [ + -2.5181925296783447, + 0.06148742884397507 + ], + [ + -2.5121519565582275, + 0.1203572079539299 + ], + [ + -2.5046167373657227, + 0.17504900693893433 + ], + [ + -2.4977223873138428, + 0.23374029994010925 + ], + [ + -2.4909656047821045, + 0.2944478988647461 + ], + [ + -2.4815306663513184, + 0.34626659750938416 + ], + [ + -2.474107265472412, + 0.4034506678581238 + ], + [ + -2.461988925933838, + 0.46261775493621826 + ], + [ + -2.4554049968719482, + 0.5274679660797119 + ], + [ + -2.448575973510742, + 0.5868113040924072 + ], + [ + -2.4302854537963867, + 0.6411546468734741 + ], + [ + -2.4183151721954346, + 0.6985329389572144 + ], + [ + -2.413236379623413, + 0.7610287666320801 + ], + [ + -2.4032866954803467, + 0.820647656917572 + ], + [ + -2.3982157707214355, + 0.8830277323722839 + ], + [ + -2.3764331340789795, + 0.9381933808326721 + ], + [ + -2.3649919033050537, + 0.9967764616012573 + ], + [ + -2.3597707748413086, + 1.0586053133010864 + ], + [ + -2.3494198322296143, + 1.111100435256958 + ], + [ + -2.341068983078003, + 1.161250114440918 + ], + [ + -2.3189733028411865, + 1.2125217914581299 + ], + [ + -2.3094048500061035, + 1.2694249153137207 + ], + [ + -2.2991182804107666, + 1.3180114030838013 + ], + [ + -2.2867796421051025, + 1.3622760772705078 + ], + [ + -2.2749311923980713, + 1.3993581533432007 + ], + [ + -2.258960008621216, + 1.4342812299728394 + ], + [ + -2.2377524375915527, + 1.4952926635742188 + ], + [ + -2.215458393096924, + 1.5359910726547241 + ], + [ + -2.1919753551483154, + 1.561612606048584 + ], + [ + -2.1656274795532227, + 1.5948957204818726 + ], + [ + -2.1345701217651367, + 1.6266098022460938 + ], + [ + -2.097571611404419, + 1.6797171831130981 + ], + [ + -2.064439058303833, + 1.713385820388794 + ], + [ + -2.0182299613952637, + 1.7452658414840698 + ], + [ + -1.9683722257614136, + 1.7842978239059448 + ], + [ + -1.9140623807907104, + 1.8159682750701904 + ], + [ + -1.8470770120620728, + 1.8618063926696777 + ], + [ + -1.816527247428894, + 1.9144901037216187 + ], + [ + -1.7516878843307495, + 1.954081416130066 + ], + [ + -1.6953322887420654, + 1.9995709657669067 + ], + [ + -1.6254856586456299, + 2.058678388595581 + ], + [ + -1.5642616748809814, + 2.1180386543273926 + ], + [ + -1.5240942239761353, + 2.167421817779541 + ], + [ + -1.4526803493499756, + 2.2239482402801514 + ], + [ + -1.396411418914795, + 2.2834792137145996 + ], + [ + -1.3327165842056274, + 2.3447656631469727 + ], + [ + -1.2825573682785034, + 2.4180798530578613 + ], + [ + -1.2271827459335327, + 2.4892048835754395 + ], + [ + -1.1655495166778564, + 2.5345427989959717 + ], + [ + -1.1101999282836914, + 2.590912342071533 + ], + [ + -1.0590189695358276, + 2.6529579162597656 + ], + [ + -1.0067218542099, + 2.7113568782806396 + ], + [ + -0.9553897976875305, + 2.775033712387085 + ], + [ + -0.9018446207046509, + 2.826817512512207 + ], + [ + -0.8346455097198486, + 2.8655762672424316 + ], + [ + -0.7675134539604187, + 2.90928053855896 + ], + [ + -0.7167988419532776, + 2.963583469390869 + ], + [ + -0.6518580317497253, + 3.0032525062561035 + ], + [ + -0.5896396636962891, + 3.052675724029541 + ], + [ + -0.5292495489120483, + 3.087273359298706 + ], + [ + -0.4397505223751068, + 3.114755392074585 + ], + [ + -0.37930282950401306, + 3.149266242980957 + ], + [ + -0.3047275245189667, + 3.1856613159179688 + ], + [ + -0.22711072862148285, + 3.2127630710601807 + ], + [ + -0.15210235118865967, + 3.2369232177734375 + ], + [ + -0.08250471949577332, + 3.2628254890441895 + ], + [ + 0.010138730518519878, + 3.290278434753418 + ], + [ + 0.08341272175312042, + 3.2997281551361084 + ], + [ + 0.16364938020706177, + 3.3170077800750732 + ], + [ + 0.24537791311740875, + 3.33978009223938 + ], + [ + 0.3225707709789276, + 3.346046209335327 + ], + [ + 0.4019952118396759, + 3.3418052196502686 + ], + [ + 0.604122519493103, + 3.221787691116333 + ], + [ + 0.6914963126182556, + 3.223146677017212 + ], + [ + 0.8036956191062927, + 3.212400436401367 + ], + [ + 0.9067635536193848, + 3.2051706314086914 + ], + [ + 0.997984766960144, + 3.2036614418029785 + ], + [ + 1.1015901565551758, + 3.1929075717926025 + ], + [ + 1.1932510137557983, + 3.187041997909546 + ], + [ + 1.2955536842346191, + 3.1623470783233643 + ], + [ + 1.382931113243103, + 3.141171455383301 + ], + [ + 1.483323574066162, + 3.1154446601867676 + ], + [ + 1.5709964036941528, + 3.095097303390503 + ], + [ + 1.6650030612945557, + 3.066406726837158 + ], + [ + 1.7640049457550049, + 3.029162645339966 + ], + [ + 1.852841854095459, + 2.990962505340576 + ], + [ + 1.933456301689148, + 2.943995475769043 + ], + [ + 2.0165939331054688, + 2.8938910961151123 + ], + [ + 2.093055009841919, + 2.8433969020843506 + ], + [ + 2.183103084564209, + 2.781916379928589 + ], + [ + 2.2641451358795166, + 2.725630044937134 + ], + [ + 2.3360953330993652, + 2.6565022468566895 + ], + [ + 2.4069952964782715, + 2.580667018890381 + ], + [ + 2.481017827987671, + 2.4989025592803955 + ], + [ + 2.559150457382202, + 2.435673713684082 + ], + [ + 2.6248691082000732, + 2.3538222312927246 + ], + [ + 2.687619686126709, + 2.264403820037842 + ], + [ + 2.7574827671051025, + 2.159935235977173 + ], + [ + 2.8187215328216553, + 2.0657289028167725 + ], + [ + 2.8884854316711426, + 1.962977409362793 + ], + [ + 2.943880319595337, + 1.8824036121368408 + ], + [ + 3.0009965896606445, + 1.7796638011932373 + ], + [ + 3.059818983078003, + 1.6701451539993286 + ], + [ + 3.119795560836792, + 1.5660635232925415 + ], + [ + 3.179224967956543, + 1.4552077054977417 + ], + [ + 3.215134859085083, + 1.3652101755142212 + ], + [ + 3.2675390243530273, + 1.2542645931243896 + ], + [ + 3.3136749267578125, + 1.1495474576950073 + ], + [ + 3.354755163192749, + 1.0480055809020996 + ], + [ + 3.3946781158447266, + 0.9453073740005493 + ], + [ + 3.4283385276794434, + 0.847625732421875 + ], + [ + 3.4463376998901367, + 0.7492050528526306 + ], + [ + 3.4690709114074707, + 0.6568482518196106 + ], + [ + 3.4896035194396973, + 0.5599454045295715 + ], + [ + 3.5035505294799805, + 0.4682500660419464 + ], + [ + 3.5108110904693604, + 0.38048332929611206 + ], + [ + 3.5131337642669678, + 0.2925216257572174 + ], + [ + 3.5079283714294434, + 0.20350931584835052 + ], + [ + 3.5004985332489014, + 0.11184374243021011 + ], + [ + 3.4859938621520996, + 0.0272199809551239 + ], + [ + 3.467696189880371, + -0.06074925884604454 + ], + [ + 3.4491379261016846, + -0.15370480716228485 + ], + [ + 3.4266412258148193, + -0.25017639994621277 + ], + [ + 3.400458812713623, + -0.3469836711883545 + ], + [ + 3.3704798221588135, + -0.43758195638656616 + ], + [ + 3.341146945953369, + -0.5373067855834961 + ], + [ + 3.3167412281036377, + -0.6403863430023193 + ], + [ + 3.2941503524780273, + -0.7457326054573059 + ], + [ + 3.2800180912017822, + -0.8522046208381653 + ], + [ + 3.273306131362915, + -0.9474582672119141 + ], + [ + 3.244905471801758, + -1.0374606847763062 + ], + [ + 3.240784168243408, + -1.1273307800292969 + ], + [ + 3.238358736038208, + -1.2069965600967407 + ], + [ + 3.2290518283843994, + -1.273685097694397 + ], + [ + 3.2154459953308105, + -1.3342549800872803 + ], + [ + 3.2046396732330322, + -1.3789488077163696 + ], + [ + 3.183739185333252, + -1.4194018840789795 + ], + [ + 3.1693122386932373, + -1.4729559421539307 + ], + [ + 3.1344897747039795, + -1.503928542137146 + ], + [ + 3.087867259979248, + -1.5323659181594849 + ], + [ + 3.050173044204712, + -1.5592883825302124 + ], + [ + 3.000412940979004, + -1.587936520576477 + ], + [ + 2.9390182495117188, + -1.6146379709243774 + ], + [ + 2.881744623184204, + -1.6487141847610474 + ], + [ + 2.811448812484741, + -1.6720460653305054 + ], + [ + 2.752296209335327, + -1.7009607553482056 + ], + [ + 2.679608106613159, + -1.7358677387237549 + ], + [ + 2.625382900238037, + -1.776227355003357 + ], + [ + 2.555453062057495, + -1.8114641904830933 + ], + [ + 2.4777891635894775, + -1.8502765893936157 + ], + [ + 2.4119601249694824, + -1.8715084791183472 + ], + [ + 2.333287000656128, + -1.9057646989822388 + ], + [ + 2.257178544998169, + -1.9445021152496338 + ], + [ + 2.1759588718414307, + -1.9900689125061035 + ], + [ + 2.106809377670288, + -2.0283520221710205 + ], + [ + 2.0257275104522705, + -2.0787975788116455 + ], + [ + 1.9454381465911865, + -2.1256701946258545 + ], + [ + 1.8816059827804565, + -2.196702480316162 + ], + [ + 1.8152947425842285, + -2.218325138092041 + ], + [ + 1.7501616477966309, + -2.2719597816467285 + ], + [ + 1.6950534582138062, + -2.321479082107544 + ], + [ + 1.6311957836151123, + -2.370927572250366 + ], + [ + 1.5608556270599365, + -2.4165143966674805 + ], + [ + 1.518396258354187, + -2.460632085800171 + ], + [ + 1.4535807371139526, + -2.5074775218963623 + ], + [ + 1.4655482769012451, + -2.4239885807037354 + ], + [ + 1.4297897815704346, + -2.4633631706237793 + ], + [ + 1.3624173402786255, + -2.488583564758301 + ], + [ + 1.281326413154602, + -2.5086562633514404 + ], + [ + 1.2148277759552002, + -2.5413739681243896 + ], + [ + 1.1541898250579834, + -2.576077938079834 + ], + [ + 1.120560646057129, + -2.588573694229126 + ], + [ + 1.0596061944961548, + -2.5915963649749756 + ], + [ + 0.9839295744895935, + -2.6220412254333496 + ], + [ + 0.9174445271492004, + -2.6488966941833496 + ], + [ + 0.8565689325332642, + -2.669872522354126 + ], + [ + 0.7940139770507812, + -2.6969826221466064 + ], + [ + 0.7390028834342957, + -2.6805472373962402 + ], + [ + 0.6767779588699341, + -2.6966135501861572 + ], + [ + 0.6264951229095459, + -2.7159435749053955 + ], + [ + 0.5704982876777649, + -2.7378273010253906 + ], + [ + 0.5115839838981628, + -2.74786639213562 + ], + [ + 0.4556136429309845, + -2.7649221420288086 + ], + [ + 0.39606210589408875, + -2.739521026611328 + ], + [ + 0.3375975489616394, + -2.753755569458008 + ], + [ + 0.285901814699173, + -2.7672626972198486 + ], + [ + 0.24291600286960602, + -2.7797658443450928 + ], + [ + 0.19613294303417206, + -2.781292200088501 + ], + [ + 0.14553982019424438, + -2.7767372131347656 + ], + [ + 0.08217504620552063, + -2.7646071910858154 + ], + [ + 0.03410906344652176, + -2.781472682952881 + ], + [ + -0.011148075573146343, + -2.7739884853363037 + ], + [ + -0.0541127547621727, + -2.771231174468994 + ], + [ + -0.09749379009008408, + -2.7685039043426514 + ], + [ + -0.13890770077705383, + -2.7591419219970703 + ], + [ + -0.19543200731277466, + -2.746670722961426 + ], + [ + -0.2452128529548645, + -2.7403695583343506 + ], + [ + -0.2969488203525543, + -2.741960048675537 + ], + [ + -0.32703039050102234, + -2.7307851314544678 + ], + [ + -0.3661307692527771, + -2.7157373428344727 + ], + [ + -0.4008972644805908, + -2.7027904987335205 + ], + [ + -0.4249809682369232, + -2.6813294887542725 + ], + [ + -0.5050779581069946, + -2.6763815879821777 + ], + [ + -0.5491519570350647, + -2.663710355758667 + ], + [ + -0.5824120044708252, + -2.6504125595092773 + ], + [ + -0.6192184090614319, + -2.6219286918640137 + ], + [ + -0.6586366891860962, + -2.600358247756958 + ], + [ + -0.6942715644836426, + -2.5729053020477295 + ], + [ + -0.7309486865997314, + -2.5588650703430176 + ], + [ + -0.7765742540359497, + -2.5507702827453613 + ], + [ + -0.8136449456214905, + -2.5285873413085938 + ], + [ + -0.8497253060340881, + -2.496798276901245 + ], + [ + -0.8963299989700317, + -2.465245485305786 + ], + [ + -0.93206787109375, + -2.429049491882324 + ], + [ + -0.9799259901046753, + -2.3877339363098145 + ], + [ + -1.0078473091125488, + -2.398555278778076 + ], + [ + -1.0372157096862793, + -2.361074686050415 + ], + [ + -1.0823018550872803, + -2.3224871158599854 + ], + [ + -1.1329315900802612, + -2.2890939712524414 + ], + [ + -1.1833562850952148, + -2.2415292263031006 + ], + [ + -1.2269871234893799, + -2.2159857749938965 + ], + [ + -1.2922029495239258, + -2.162691116333008 + ], + [ + -1.3056666851043701, + -2.161792755126953 + ], + [ + -1.354131817817688, + -2.121523141860962 + ], + [ + -1.4096392393112183, + -2.0868189334869385 + ], + [ + -1.461287260055542, + -2.045318365097046 + ], + [ + -1.5167489051818848, + -2.0227153301239014 + ], + [ + -1.571979284286499, + -1.9972461462020874 + ], + [ + -1.6312527656555176, + -1.9690998792648315 + ], + [ + -1.6827654838562012, + -1.952263355255127 + ], + [ + -1.708079218864441, + -1.9103525876998901 + ], + [ + -1.7569104433059692, + -1.884315848350525 + ], + [ + -1.8081934452056885, + -1.8666108846664429 + ], + [ + -1.8563565015792847, + -1.8517392873764038 + ], + [ + -1.8965286016464233, + -1.8361438512802124 + ], + [ + -1.9335756301879883, + -1.8242131471633911 + ], + [ + -1.964591383934021, + -1.8026390075683594 + ], + [ + -2.0047719478607178, + -1.762892723083496 + ], + [ + -2.041518211364746, + -1.7451794147491455 + ], + [ + -2.071052074432373, + -1.7293972969055176 + ], + [ + -2.0981240272521973, + -1.706528902053833 + ], + [ + -2.11956524848938, + -1.6901549100875854 + ], + [ + -2.1400058269500732, + -1.6698988676071167 + ], + [ + -2.149365186691284, + -1.6461429595947266 + ], + [ + -2.158566474914551, + -1.6161984205245972 + ], + [ + -2.2120606899261475, + -1.5779215097427368 + ], + [ + -2.2249817848205566, + -1.5428466796875 + ], + [ + -2.2397396564483643, + -1.513846755027771 + ], + [ + -2.255702495574951, + -1.4739855527877808 + ], + [ + -2.789912700653076, + -1.9098845720291138 + ], + [ + -2.82194185256958, + -1.8552011251449585 + ], + [ + -2.8512136936187744, + -1.808366298675537 + ], + [ + -2.894787549972534, + -1.7530639171600342 + ], + [ + -2.9243111610412598, + -1.6943556070327759 + ], + [ + -2.9650661945343018, + -1.638841152191162 + ], + [ + -3.001330614089966, + -1.5780820846557617 + ], + [ + -3.0343077182769775, + -1.5203001499176025 + ], + [ + -3.0667078495025635, + -1.4537278413772583 + ], + [ + -3.105184316635132, + -1.391394019126892 + ], + [ + -3.1322951316833496, + -1.3280572891235352 + ], + [ + -3.1623547077178955, + -1.259221076965332 + ], + [ + -1.939096212387085, + -0.6807480454444885 + ], + [ + -1.952365517616272, + -0.6386276483535767 + ], + [ + -1.9693323373794556, + -0.5959262251853943 + ], + [ + -1.9833849668502808, + -0.5515741109848022 + ], + [ + -1.9971498250961304, + -0.5061970353126526 + ], + [ + -2.009053945541382, + -0.4613075256347656 + ], + [ + -2.021026372909546, + -0.41577762365341187 + ], + [ + -2.031355381011963, + -0.3683095872402191 + ], + [ + -2.0408895015716553, + -0.32223084568977356 + ], + [ + -2.049889326095581, + -0.27467721700668335 + ], + [ + -2.058365821838379, + -0.22663073241710663 + ], + [ + -2.0657122135162354, + -0.1788879632949829 + ], + [ + -2.0707199573516846, + -0.12972895801067352 + ], + [ + -2.07615065574646, + -0.08084411174058914 + ], + [ + -2.0804061889648438, + -0.031422313302755356 + ], + [ + -2.0833592414855957, + 0.018214151263237 + ], + [ + -2.085083246231079, + 0.0708853080868721 + ], + [ + -2.0858254432678223, + 0.12087997794151306 + ], + [ + -2.085240364074707, + 0.17192336916923523 + ], + [ + -2.084052085876465, + 0.223068505525589 + ], + [ + -2.081662178039551, + 0.27628636360168457 + ], + [ + -2.0768208503723145, + 0.3311088979244232 + ], + [ + -2.067357063293457, + 0.3807849884033203 + ], + [ + -2.062736988067627, + 0.43460559844970703 + ], + [ + -2.0520730018615723, + 0.4866396188735962 + ], + [ + -2.0412445068359375, + 0.5380857586860657 + ], + [ + -2.0276601314544678, + 0.5891246795654297 + ], + [ + -2.011918783187866, + 0.6406463384628296 + ], + [ + -1.9971976280212402, + 0.694553792476654 + ], + [ + -1.983178734779358, + 0.7432039976119995 + ], + [ + -1.9616353511810303, + 0.7950911521911621 + ], + [ + -1.9458224773406982, + 0.8461785316467285 + ], + [ + -1.9264793395996094, + 0.8947480916976929 + ], + [ + -1.9000548124313354, + 0.9455226063728333 + ], + [ + -1.8813544511795044, + 0.9935013651847839 + ], + [ + -1.8542447090148926, + 1.0390750169754028 + ], + [ + -1.8293994665145874, + 1.0922362804412842 + ], + [ + -1.7988624572753906, + 1.132934331893921 + ], + [ + -1.7675772905349731, + 1.1780340671539307 + ], + [ + -1.7385889291763306, + 1.2278425693511963 + ], + [ + -1.7053543329238892, + 1.273457407951355 + ], + [ + -1.6693856716156006, + 1.3176956176757812 + ], + [ + -1.633844256401062, + 1.3631333112716675 + ], + [ + -1.5976521968841553, + 1.4036017656326294 + ], + [ + -1.5626500844955444, + 1.4425859451293945 + ], + [ + -1.52292799949646, + 1.4863156080245972 + ], + [ + -1.4851118326187134, + 1.5260387659072876 + ], + [ + -1.4443962574005127, + 1.5574613809585571 + ], + [ + -1.3997883796691895, + 1.5909099578857422 + ], + [ + -1.3596993684768677, + 1.636057734489441 + ], + [ + -1.3204857110977173, + 1.6726205348968506 + ], + [ + -1.2703770399093628, + 1.697584629058838 + ], + [ + -1.2326018810272217, + 1.7287253141403198 + ], + [ + -1.1931045055389404, + 1.7673418521881104 + ], + [ + -1.153369426727295, + 1.7960216999053955 + ], + [ + -1.0999146699905396, + 1.8214483261108398 + ], + [ + -1.0547243356704712, + 1.8478145599365234 + ], + [ + -1.0112537145614624, + 1.8764448165893555 + ], + [ + -0.9666260480880737, + 1.8865482807159424 + ], + [ + -0.9129154682159424, + 1.914810299873352 + ], + [ + -0.8649275898933411, + 1.9297493696212769 + ], + [ + -0.8224252462387085, + 1.9530653953552246 + ], + [ + -0.7731329798698425, + 1.963896632194519 + ], + [ + -0.7225728631019592, + 1.9904707670211792 + ], + [ + -0.6760976910591125, + 1.9916374683380127 + ], + [ + -0.6234354376792908, + 2.014925718307495 + ], + [ + -0.5778567790985107, + 2.026262044906616 + ], + [ + -0.524203360080719, + 2.034918785095215 + ], + [ + -0.47348344326019287, + 2.040405035018921 + ], + [ + -0.42911845445632935, + 2.051117181777954 + ], + [ + -0.3717295825481415, + 2.057615280151367 + ], + [ + -0.32046380639076233, + 2.0602846145629883 + ], + [ + -0.2691158354282379, + 2.059157133102417 + ], + [ + -0.21888059377670288, + 2.0386364459991455 + ], + [ + -0.1683487445116043, + 2.0663208961486816 + ], + [ + -0.10686074197292328, + 2.078583002090454 + ], + [ + -0.05108408257365227, + 2.067359685897827 + ], + [ + -0.00803030002862215, + 2.074599027633667 + ], + [ + 0.048057761043310165, + 2.0656816959381104 + ], + [ + 0.09408649057149887, + 2.066115617752075 + ], + [ + 0.1477361023426056, + 2.054093599319458 + ], + [ + 0.19644670188426971, + 2.0475094318389893 + ], + [ + 0.23930613696575165, + 2.0432071685791016 + ], + [ + 0.2943762242794037, + 2.0298473834991455 + ], + [ + 0.3447219133377075, + 2.01904559135437 + ], + [ + 0.39233526587486267, + 2.003962278366089 + ], + [ + 0.44757068157196045, + 1.9899786710739136 + ], + [ + 0.4949842691421509, + 1.9744194746017456 + ], + [ + 0.5531154870986938, + 1.9555765390396118 + ], + [ + 0.5992284417152405, + 1.9338862895965576 + ], + [ + 0.646668553352356, + 1.9173840284347534 + ], + [ + 0.6888584494590759, + 1.8864469528198242 + ], + [ + 0.7450215220451355, + 1.870263695716858 + ], + [ + 0.794282078742981, + 1.8498250246047974 + ], + [ + 0.8379050493240356, + 1.8245142698287964 + ], + [ + 0.8887669444084167, + 1.8075196743011475 + ], + [ + 0.937105119228363, + 1.7697194814682007 + ], + [ + 0.9815776944160461, + 1.7397254705429077 + ], + [ + 1.0308599472045898, + 1.7040032148361206 + ], + [ + 1.0745210647583008, + 1.6717205047607422 + ], + [ + 1.1184508800506592, + 1.6470627784729004 + ], + [ + 1.1646403074264526, + 1.6061427593231201 + ], + [ + 1.205773115158081, + 1.5797123908996582 + ], + [ + 1.2512071132659912, + 1.5444042682647705 + ], + [ + 1.2964011430740356, + 1.4943119287490845 + ], + [ + 1.3324358463287354, + 1.4676051139831543 + ], + [ + 1.3684406280517578, + 1.420765995979309 + ], + [ + 1.4130305051803589, + 1.3833521604537964 + ], + [ + 1.4555394649505615, + 1.3444534540176392 + ], + [ + 1.4818600416183472, + 1.3038278818130493 + ], + [ + 1.5246280431747437, + 1.2591265439987183 + ], + [ + 1.559377908706665, + 1.2145498991012573 + ], + [ + 1.5942211151123047, + 1.1636466979980469 + ], + [ + 1.6295610666275024, + 1.1227933168411255 + ], + [ + 1.6616950035095215, + 1.0683050155639648 + ], + [ + 1.6954121589660645, + 1.0288985967636108 + ], + [ + 1.7216964960098267, + 0.9798287153244019 + ], + [ + 1.7503950595855713, + 0.9276873469352722 + ], + [ + 1.778201699256897, + 0.8792389035224915 + ], + [ + 1.8050205707550049, + 0.8297778964042664 + ], + [ + 1.836473822593689, + 0.7740538716316223 + ], + [ + 1.8550477027893066, + 0.7254865169525146 + ], + [ + 1.8791964054107666, + 0.6720587611198425 + ], + [ + 1.89922034740448, + 0.6195781230926514 + ], + [ + 1.9190661907196045, + 0.5651131868362427 + ], + [ + 1.9393385648727417, + 0.5110737085342407 + ], + [ + 1.9546383619308472, + 0.4575277864933014 + ], + [ + 1.9703558683395386, + 0.4025319814682007 + ], + [ + 1.9852973222732544, + 0.34665659070014954 + ], + [ + 1.9997735023498535, + 0.2932497262954712 + ], + [ + 2.0096211433410645, + 0.23785769939422607 + ], + [ + 2.02043080329895, + 0.1810353845357895 + ], + [ + 2.028529167175293, + 0.12593293190002441 + ], + [ + 2.0358526706695557, + 0.06951350718736649 + ], + [ + 2.041053056716919, + 0.014001079834997654 + ], + [ + 2.0463147163391113, + -0.04065854847431183 + ], + [ + 2.0535101890563965, + -0.09649515151977539 + ], + [ + 2.054388999938965, + -0.1520168036222458 + ], + [ + 2.0538721084594727, + -0.20868702232837677 + ], + [ + 2.053163766860962, + -0.2584943175315857 + ], + [ + 2.0484564304351807, + -0.3143188953399658 + ], + [ + 2.043355941772461, + -0.36864492297172546 + ], + [ + 2.042011022567749, + -0.4278182089328766 + ], + [ + 2.034322738647461, + -0.48044708371162415 + ], + [ + 2.025951623916626, + -0.5363051891326904 + ], + [ + 2.016893148422241, + -0.5878207087516785 + ], + [ + 2.0083096027374268, + -0.6492957472801208 + ], + [ + 2.0000500679016113, + -0.6932437419891357 + ], + [ + 1.987940788269043, + -0.7574043869972229 + ], + [ + 1.9705811738967896, + -0.8061993718147278 + ], + [ + 1.9567289352416992, + -0.8605440258979797 + ], + [ + 1.9346753358840942, + -0.9095450639724731 + ], + [ + 1.9202497005462646, + -0.9648932814598083 + ], + [ + 1.9040396213531494, + -1.0079864263534546 + ], + [ + 1.8808674812316895, + -1.060410976409912 + ], + [ + 1.8595004081726074, + -1.107595443725586 + ], + [ + 1.8395739793777466, + -1.16050124168396 + ], + [ + 1.8140474557876587, + -1.2115402221679688 + ], + [ + 1.7881207466125488, + -1.2526763677597046 + ], + [ + 1.7519309520721436, + -1.2990410327911377 + ], + [ + 1.7247456312179565, + -1.3432480096817017 + ], + [ + 1.700600028038025, + -1.3812659978866577 + ], + [ + 1.6677035093307495, + -1.432289958000183 + ], + [ + 1.6372331380844116, + -1.470292091369629 + ], + [ + 1.602839708328247, + -1.51361882686615 + ], + [ + 1.576010823249817, + -1.5580676794052124 + ], + [ + 1.540656328201294, + -1.5951287746429443 + ], + [ + 1.503659725189209, + -1.642486810684204 + ], + [ + 1.4607102870941162, + -1.671753168106079 + ], + [ + 1.4102507829666138, + -1.6961443424224854 + ], + [ + 1.3745391368865967, + -1.7225377559661865 + ], + [ + 1.3331958055496216, + -1.7523412704467773 + ], + [ + 1.2921346426010132, + -1.7896794080734253 + ], + [ + 1.2396968603134155, + -1.809958815574646 + ], + [ + 1.2020959854125977, + -1.8427790403366089 + ], + [ + 1.1576862335205078, + -1.874192714691162 + ], + [ + 1.118741750717163, + -1.9031258821487427 + ], + [ + 1.077420711517334, + -1.9349583387374878 + ], + [ + 1.0102405548095703, + -1.9094842672348022 + ], + [ + 0.9447270631790161, + -1.9233429431915283 + ], + [ + 0.9168527126312256, + -1.9392727613449097 + ], + [ + 0.8702920079231262, + -1.9707372188568115 + ], + [ + 0.8179040551185608, + -2.013446807861328 + ], + [ + 0.7824435234069824, + -2.04972767829895 + ], + [ + 0.7528225183486938, + -2.0892317295074463 + ], + [ + 0.711268961429596, + -2.1270482540130615 + ], + [ + 0.6382469534873962, + -2.1659374237060547 + ], + [ + 0.583331823348999, + -2.205756187438965 + ], + [ + 0.5129031538963318, + -2.2281875610351562 + ], + [ + 0.41605865955352783, + -2.168739080429077 + ], + [ + 0.3060882091522217, + -2.146209478378296 + ], + [ + 0.15365441143512726, + -2.105172872543335 + ], + [ + 0.0020785960368812084, + -2.040670394897461 + ], + [ + -0.15418343245983124, + -1.9243831634521484 + ], + [ + -0.3202892243862152, + -1.798927664756775 + ], + [ + -0.4346787929534912, + -1.6801048517227173 + ], + [ + -0.5110172629356384, + -1.613135576248169 + ], + [ + -0.5405659675598145, + -1.5904440879821777 + ], + [ + -0.5455673933029175, + -1.607232928276062 + ], + [ + -0.6063836216926575, + -1.9449594020843506 + ], + [ + -0.624832808971405, + -1.9866611957550049 + ], + [ + -0.6481931209564209, + -2.0024936199188232 + ], + [ + -0.6634911298751831, + -1.9572324752807617 + ], + [ + -0.6663447618484497, + -1.8878417015075684 + ], + [ + -0.6563385128974915, + -1.7929601669311523 + ], + [ + -0.6403387784957886, + -1.6972758769989014 + ], + [ + -0.6024368405342102, + -1.6063774824142456 + ], + [ + -0.5762656927108765, + -1.5338255167007446 + ], + [ + -0.5187647938728333, + -1.4662768840789795 + ], + [ + -0.48415064811706543, + -1.435086965560913 + ], + [ + -0.5525069236755371, + -1.5038563013076782 + ], + [ + -0.5161784887313843, + -1.4564497470855713 + ], + [ + -0.49484115839004517, + -1.4358186721801758 + ], + [ + -0.47963976860046387, + -1.437211513519287 + ], + [ + -0.49533164501190186, + -1.4836785793304443 + ], + [ + -0.5715327262878418, + -1.6046595573425293 + ], + [ + -0.7012724876403809, + -1.7719199657440186 + ], + [ + -0.8673467636108398, + -1.9547784328460693 + ], + [ + -1.015973448753357, + -2.101969003677368 + ], + [ + -1.1314986944198608, + -2.170522689819336 + ], + [ + -1.2274649143218994, + -2.160341262817383 + ], + [ + -1.3087838888168335, + -2.136418581008911 + ], + [ + -1.3310165405273438, + -2.1153032779693604 + ], + [ + -1.407902717590332, + -2.0705678462982178 + ], + [ + -1.475193738937378, + -2.0379385948181152 + ], + [ + -1.5394911766052246, + -1.9791806936264038 + ], + [ + -1.6067612171173096, + -1.91769540309906 + ], + [ + -1.650140643119812, + -1.8407727479934692 + ], + [ + -1.6875827312469482, + -1.7631479501724243 + ], + [ + -1.7184261083602905, + -1.671218752861023 + ], + [ + -1.7424614429473877, + -1.5910464525222778 + ], + [ + -1.7474254369735718, + -1.5262022018432617 + ], + [ + -1.7653908729553223, + -1.4905372858047485 + ], + [ + -1.8015632629394531, + -1.4718059301376343 + ], + [ + -1.8849856853485107, + -1.4614430665969849 + ], + [ + -1.9283195734024048, + -1.4638653993606567 + ], + [ + -1.9919277429580688, + -1.4629545211791992 + ], + [ + -2.0574071407318115, + -1.4738165140151978 + ], + [ + -2.1340599060058594, + -1.4894769191741943 + ], + [ + -2.2231273651123047, + -1.4883272647857666 + ], + [ + -2.291485548019409, + -1.470659852027893 + ], + [ + -2.3644659519195557, + -1.440994143486023 + ], + [ + -2.4340333938598633, + -1.4051308631896973 + ], + [ + -2.491485118865967, + -1.3487628698349 + ], + [ + -2.5485143661499023, + -1.2934077978134155 + ], + [ + -2.592172861099243, + -1.2255971431732178 + ], + [ + -2.6381776332855225, + -1.1586625576019287 + ], + [ + -2.695106029510498, + -1.0983964204788208 + ], + [ + -2.7385199069976807, + -1.0343594551086426 + ], + [ + -2.776291608810425, + -0.9640145301818848 + ], + [ + -2.81166934967041, + -0.8906009793281555 + ], + [ + -2.836707830429077, + -0.8137257099151611 + ], + [ + -2.858428955078125, + -0.7408859729766846 + ], + [ + -2.8790154457092285, + -0.6690824627876282 + ], + [ + -2.8940415382385254, + -0.5941474437713623 + ], + [ + -2.9010426998138428, + -0.5238000750541687 + ], + [ + -2.909085988998413, + -0.45078906416893005 + ], + [ + -2.9083189964294434, + -0.37507739663124084 + ], + [ + -2.9072840213775635, + -0.2997860908508301 + ], + [ + -3.005812883377075, + -0.21556322276592255 + ], + [ + -3.017341136932373, + -0.13587506115436554 + ], + [ + -3.023409843444824, + -0.05870097503066063 + ], + [ + -3.025590181350708, + 0.01880655437707901 + ], + [ + -3.025848865509033, + 0.09365110844373703 + ], + [ + -3.0220961570739746, + 0.17214366793632507 + ], + [ + -3.0202388763427734, + 0.24609537422657013 + ], + [ + -3.0081255435943604, + 0.3179972767829895 + ], + [ + -2.995783567428589, + 0.39712363481521606 + ], + [ + -2.984403371810913, + 0.46626558899879456 + ], + [ + -2.970194101333618, + 0.5454097986221313 + ], + [ + -2.9559531211853027, + 0.615135133266449 + ], + [ + -2.9344537258148193, + 0.6851721405982971 + ], + [ + -2.9574944972991943, + 0.7915886044502258 + ], + [ + -2.9441006183624268, + 0.8669255375862122 + ], + [ + -2.916849374771118, + 0.9476485848426819 + ], + [ + -2.897669792175293, + 1.0129587650299072 + ], + [ + -2.876121759414673, + 1.086795687675476 + ], + [ + -2.84883451461792, + 1.1496262550354004 + ], + [ + -2.8302626609802246, + 1.2204465866088867 + ], + [ + -2.7993199825286865, + 1.2962018251419067 + ], + [ + -2.7594807147979736, + 1.3708014488220215 + ], + [ + -2.7271833419799805, + 1.4501303434371948 + ], + [ + -2.6744866371154785, + 1.5389537811279297 + ], + [ + -2.61313796043396, + 1.5949156284332275 + ], + [ + -2.5551230907440186, + 1.6584594249725342 + ], + [ + -2.4908816814422607, + 1.7083662748336792 + ], + [ + -2.4266505241394043, + 1.7613481283187866 + ], + [ + -2.38466477394104, + 1.7962907552719116 + ], + [ + -2.3341479301452637, + 1.8218728303909302 + ], + [ + -2.2738988399505615, + 1.8703877925872803 + ], + [ + -2.2376792430877686, + 1.8903529644012451 + ], + [ + -2.1968719959259033, + 1.9464670419692993 + ], + [ + -2.153024911880493, + 1.9752123355865479 + ], + [ + -2.1001553535461426, + 2.00573992729187 + ], + [ + -2.054490089416504, + 2.0517256259918213 + ], + [ + -1.992921233177185, + 2.088070869445801 + ], + [ + -1.9374616146087646, + 2.111299991607666 + ], + [ + -1.880867838859558, + 2.13771915435791 + ], + [ + -1.8321253061294556, + 2.172494649887085 + ], + [ + -1.7726197242736816, + 2.2032346725463867 + ], + [ + -1.727648377418518, + 2.228423833847046 + ], + [ + -1.6646350622177124, + 2.245445966720581 + ], + [ + -1.6086881160736084, + 2.256934404373169 + ], + [ + -1.5574150085449219, + 2.2773215770721436 + ], + [ + -1.5145063400268555, + 2.3043603897094727 + ], + [ + -1.4563183784484863, + 2.2967689037323 + ], + [ + -1.3895223140716553, + 2.304969310760498 + ], + [ + -1.3440701961517334, + 2.321601629257202 + ], + [ + -1.3160995244979858, + 2.337130069732666 + ], + [ + -1.2590749263763428, + 2.335090398788452 + ], + [ + -1.2157617807388306, + 2.353987693786621 + ], + [ + -1.1620811223983765, + 2.3656516075134277 + ], + [ + -1.1153879165649414, + 2.363826274871826 + ], + [ + -1.0671113729476929, + 2.378605842590332 + ], + [ + -1.0245308876037598, + 2.4003958702087402 + ], + [ + -0.9690240621566772, + 2.404007911682129 + ], + [ + -0.9172849655151367, + 2.416661024093628 + ], + [ + -0.888172447681427, + 2.4239163398742676 + ], + [ + -0.8456668257713318, + 2.434183359146118 + ], + [ + -0.8011134266853333, + 2.4406189918518066 + ], + [ + -0.7593628764152527, + 2.447582483291626 + ], + [ + -0.7207033634185791, + 2.459336280822754 + ], + [ + -0.6904757022857666, + 2.488373041152954 + ], + [ + -0.6264764666557312, + 2.486638069152832 + ], + [ + -0.596717894077301, + 2.4937942028045654 + ], + [ + -0.546213686466217, + 2.4793148040771484 + ], + [ + -0.48355063796043396, + 2.5083060264587402 + ], + [ + -0.4249427020549774, + 2.500951051712036 + ], + [ + -0.37415939569473267, + 2.534940481185913 + ], + [ + -0.3312223553657532, + 2.5396077632904053 + ], + [ + -0.2889305353164673, + 2.5419344902038574 + ], + [ + -0.21558496356010437, + 2.560695171356201 + ], + [ + -0.16291315853595734, + 2.576202392578125 + ], + [ + -0.10912346839904785, + 2.5665762424468994 + ], + [ + -0.051017604768276215, + 2.5686533451080322 + ], + [ + 0.018875345587730408, + 2.567444086074829 + ], + [ + 0.06465987861156464, + 2.5701773166656494 + ], + [ + 0.1324782520532608, + 2.561803102493286 + ], + [ + 0.2068946808576584, + 2.555746078491211 + ], + [ + 0.2597694993019104, + 2.5605828762054443 + ], + [ + 0.3346307873725891, + 2.54529070854187 + ], + [ + 0.4071377217769623, + 2.5414583683013916 + ], + [ + 0.4494141936302185, + 2.5375921726226807 + ], + [ + 0.5025323629379272, + 2.508594274520874 + ], + [ + 0.5790141820907593, + 2.5116915702819824 + ], + [ + 0.6427742838859558, + 2.501234292984009 + ], + [ + 0.6994442343711853, + 2.4768364429473877 + ], + [ + 0.7833623290061951, + 2.457221031188965 + ], + [ + 0.8016390204429626, + 2.4768195152282715 + ], + [ + 0.9044339060783386, + 2.4217684268951416 + ], + [ + 0.9513251185417175, + 2.4143903255462646 + ], + [ + 1.0317407846450806, + 2.372506856918335 + ], + [ + 1.0866315364837646, + 2.3450143337249756 + ], + [ + 1.1452995538711548, + 2.3264284133911133 + ], + [ + 1.222049355506897, + 2.2988715171813965 + ], + [ + 1.2785722017288208, + 2.264704942703247 + ], + [ + 1.3450956344604492, + 2.2289061546325684 + ], + [ + 1.4226233959197998, + 2.2009341716766357 + ], + [ + 1.4660910367965698, + 2.160318374633789 + ], + [ + 1.5323148965835571, + 2.1187257766723633 + ], + [ + 1.5581592321395874, + 2.095895528793335 + ], + [ + 1.6538889408111572, + 2.0462610721588135 + ], + [ + 1.7080063819885254, + 1.995171070098877 + ], + [ + 1.7654551267623901, + 1.9639097452163696 + ], + [ + 1.83365797996521, + 1.9093478918075562 + ], + [ + 1.886694312095642, + 1.8681954145431519 + ], + [ + 1.940342903137207, + 1.8244189023971558 + ], + [ + 1.99257493019104, + 1.7620192766189575 + ], + [ + 2.0535972118377686, + 1.7182610034942627 + ], + [ + 2.104647397994995, + 1.670270323753357 + ], + [ + 2.160341739654541, + 1.6226904392242432 + ], + [ + 2.204922914505005, + 1.5611199140548706 + ], + [ + 2.2633750438690186, + 1.4981132745742798 + ], + [ + 2.2971978187561035, + 1.4515384435653687 + ], + [ + 2.355032205581665, + 1.397208571434021 + ], + [ + 2.395582675933838, + 1.3320810794830322 + ], + [ + 2.437032699584961, + 1.2740654945373535 + ], + [ + 2.4867217540740967, + 1.205893874168396 + ], + [ + 2.530080556869507, + 1.138196349143982 + ], + [ + 2.575047254562378, + 1.0777791738510132 + ], + [ + 2.618283271789551, + 1.0098692178726196 + ], + [ + 2.6614513397216797, + 0.9418083429336548 + ], + [ + 2.6999340057373047, + 0.8697630763053894 + ], + [ + 2.7298192977905273, + 0.8084556460380554 + ], + [ + 2.7694876194000244, + 0.7403592467308044 + ], + [ + 2.7972564697265625, + 0.6631399393081665 + ], + [ + 2.8277485370635986, + 0.5916881561279297 + ], + [ + 2.8645966053009033, + 0.5160143971443176 + ], + [ + 2.8924617767333984, + 0.43553632497787476 + ], + [ + 2.9207751750946045, + 0.3516179919242859 + ], + [ + 2.9441661834716797, + 0.2656373977661133 + ], + [ + 2.9708361625671387, + 0.18012608587741852 + ], + [ + 2.995119094848633, + 0.09640496969223022 + ], + [ + 3.017622232437134, + 0.002266376744955778 + ], + [ + 3.043760061264038, + -0.08903345465660095 + ], + [ + 3.0452544689178467, + -0.16451512277126312 + ], + [ + 3.0585885047912598, + -0.2522426247596741 + ], + [ + 3.0773463249206543, + -0.3484383523464203 + ], + [ + 3.086075782775879, + -0.447540819644928 + ], + [ + 3.080408811569214, + -0.5331095457077026 + ], + [ + 3.0869836807250977, + -0.6308740377426147 + ], + [ + 3.083768129348755, + -0.7343985438346863 + ], + [ + 3.0898289680480957, + -0.8323004245758057 + ], + [ + 3.0833899974823, + -0.9319173097610474 + ], + [ + 3.0904698371887207, + -1.0364118814468384 + ], + [ + 3.080061674118042, + -1.132294774055481 + ], + [ + 3.0699727535247803, + -1.2475190162658691 + ], + [ + 3.0850470066070557, + -1.3411211967468262 + ], + [ + 3.0926270484924316, + -1.4598098993301392 + ], + [ + 2.9818525314331055, + -1.5555393695831299 + ], + [ + 2.9591095447540283, + -1.6487717628479004 + ], + [ + 2.9116525650024414, + -1.7719738483428955 + ], + [ + 2.8920342922210693, + -1.8652126789093018 + ], + [ + 2.835754632949829, + -1.9713828563690186 + ], + [ + 2.804757833480835, + -2.1031105518341064 + ], + [ + 2.753603458404541, + -2.2083773612976074 + ], + [ + 2.689694881439209, + -2.318589925765991 + ], + [ + 2.638504981994629, + -2.4216058254241943 + ], + [ + 2.5509848594665527, + -2.5275609493255615 + ], + [ + 2.489881753921509, + -2.6566803455352783 + ], + [ + 2.380188465118408, + -2.7509565353393555 + ], + [ + 2.2542405128479004, + -2.8752071857452393 + ], + [ + 2.150723934173584, + -2.9281368255615234 + ], + [ + 2.0715248584747314, + -3.0241780281066895 + ], + [ + 1.9682269096374512, + -3.1185126304626465 + ], + [ + 1.8939309120178223, + -3.1630985736846924 + ], + [ + 1.7857937812805176, + -3.261897325515747 + ], + [ + 1.6356526613235474, + -3.3611268997192383 + ], + [ + 1.5163850784301758, + -3.4567573070526123 + ], + [ + 1.3732144832611084, + -3.5010128021240234 + ], + [ + 1.2104030847549438, + -3.5518276691436768 + ], + [ + 1.0430190563201904, + -3.5933995246887207 + ], + [ + 0.8822158575057983, + -3.623825788497925 + ], + [ + 0.6627976298332214, + -3.6547963619232178 + ], + [ + 0.44273343682289124, + -3.6631481647491455 + ], + [ + 0.42717331647872925, + -3.7039060592651367 + ], + [ + 0.32416683435440063, + -3.795259714126587 + ], + [ + 0.17221441864967346, + -3.8220949172973633 + ], + [ + 0.03688724339008331, + -3.916059732437134 + ], + [ + -0.11089131981134415, + -3.936948299407959 + ], + [ + -0.252693235874176, + -3.9844772815704346 + ], + [ + -0.43921273946762085, + -4.0095295906066895 + ], + [ + -0.6141329407691956, + -4.009274482727051 + ], + [ + -0.7696290612220764, + -3.970614194869995 + ], + [ + -0.931189775466919, + -3.9552981853485107 + ], + [ + -1.1234012842178345, + -3.8894717693328857 + ], + [ + -1.3107601404190063, + -3.815281391143799 + ], + [ + -1.4806294441223145, + -3.7320592403411865 + ], + [ + -3.1617636680603027, + -7.200752258300781 + ], + [ + -3.5687215328216553, + -7.0088019371032715 + ], + [ + -3.4965732097625732, + -7.553720474243164 + ], + [ + -3.828077554702759, + -7.578862190246582 + ], + [ + -4.215964317321777, + -7.5158257484436035 + ], + [ + -4.464430332183838, + -7.543081760406494 + ], + [ + -4.748383522033691, + -7.505837917327881 + ], + [ + -5.119718074798584, + -7.260599136352539 + ], + [ + -5.392154693603516, + -7.093082427978516 + ], + [ + -5.704444408416748, + -6.892176151275635 + ], + [ + -5.940671920776367, + -6.73836088180542 + ], + [ + -6.2074875831604, + -6.455677509307861 + ], + [ + -6.4956464767456055, + -6.174989700317383 + ], + [ + -6.733001232147217, + -5.923089027404785 + ], + [ + -6.919020175933838, + -5.556769371032715 + ], + [ + -7.1211748123168945, + -5.126523971557617 + ], + [ + -7.485099792480469, + -5.612582206726074 + ], + [ + -7.772981643676758, + -5.378384590148926 + ], + [ + -7.964595794677734, + -5.172052383422852 + ], + [ + -8.221421241760254, + -5.0016374588012695 + ], + [ + -8.366085052490234, + -4.667886257171631 + ], + [ + -8.53973388671875, + -4.368419647216797 + ], + [ + -8.660114288330078, + -4.072993755340576 + ], + [ + -8.829169273376465, + -3.807664632797241 + ], + [ + -8.966348648071289, + -3.554037570953369 + ], + [ + -9.068779945373535, + -3.2637033462524414 + ], + [ + -9.138891220092773, + -2.962503671646118 + ], + [ + -9.22436237335205, + -2.6824145317077637 + ], + [ + -9.287652015686035, + -2.415142774581909 + ], + [ + -9.32740592956543, + -2.1294572353363037 + ], + [ + -9.349847793579102, + -1.8417268991470337 + ], + [ + -9.568178176879883, + -1.583898901939392 + ], + [ + -9.549560546875, + -1.31840181350708 + ], + [ + -9.608781814575195, + -1.0010920763015747 + ], + [ + -9.653103828430176, + -0.7563896179199219 + ], + [ + -9.655699729919434, + -0.4968414604663849 + ], + [ + -9.631248474121094, + -0.22788356244564056 + ], + [ + -9.6608304977417, + 0.03178777918219566 + ], + [ + -9.656210899353027, + 0.2685079574584961 + ], + [ + -9.6510009765625, + 0.5217161178588867 + ], + [ + -9.626096725463867, + 0.7814257144927979 + ], + [ + -9.619415283203125, + 0.9793218374252319 + ], + [ + -9.602843284606934, + 1.2214114665985107 + ], + [ + -9.577664375305176, + 1.438049554824829 + ], + [ + -9.52717399597168, + 1.7186967134475708 + ], + [ + -9.467283248901367, + 1.9591264724731445 + ] + ], + "total_points": 1000 +} \ No newline at end of file diff --git a/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/open_metadata.json b/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/open_metadata.json new file mode 100644 index 0000000..20430a1 --- /dev/null +++ b/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/open_metadata.json @@ -0,0 +1,16 @@ +{ + "preset": { + "filename": "s11_start100_stop8800_points1000_bw1khz.bin", + "mode": "s11", + "start_freq": 100000000.0, + "stop_freq": 8800000000.0, + "points": 1000, + "bandwidth": 1000.0 + }, + "calibration_name": "SRGDFDFG", + "standard": "open", + "sweep_number": 224, + "sweep_timestamp": 1758806106.5021908, + "created_timestamp": "2025-09-25T16:15:09.913979", + "total_points": 1000 +} \ No newline at end of file diff --git a/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/short.json b/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/short.json new file mode 100644 index 0000000..707804f --- /dev/null +++ b/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/short.json @@ -0,0 +1,4007 @@ +{ + "sweep_number": 224, + "timestamp": 1758806106.5021908, + "points": [ + [ + -2.6736810207366943, + 0.45182546973228455 + ], + [ + -2.6649160385131836, + 0.5124151110649109 + ], + [ + -2.6564583778381348, + 0.5761726498603821 + ], + [ + -2.6471076011657715, + 0.6357683539390564 + ], + [ + -2.63456392288208, + 0.6980700492858887 + ], + [ + -3.406205415725708, + 0.9276503920555115 + ], + [ + -3.3926522731781006, + 0.9973128437995911 + ], + [ + -3.3718581199645996, + 1.0803601741790771 + ], + [ + -3.354764461517334, + 1.1557843685150146 + ], + [ + -3.3333864212036133, + 1.2311294078826904 + ], + [ + -3.306791067123413, + 1.3099294900894165 + ], + [ + -3.2835750579833984, + 1.3874938488006592 + ], + [ + -3.260197162628174, + 1.4654496908187866 + ], + [ + -3.235013484954834, + 1.5420405864715576 + ], + [ + -3.2073099613189697, + 1.615953803062439 + ], + [ + -3.17728853225708, + 1.6916990280151367 + ], + [ + -3.1466078758239746, + 1.7638152837753296 + ], + [ + -3.111706495285034, + 1.8342149257659912 + ], + [ + -3.0758213996887207, + 1.9107712507247925 + ], + [ + -3.04197359085083, + 1.9872909784317017 + ], + [ + -3.007207155227661, + 2.0558650493621826 + ], + [ + -2.9629905223846436, + 2.1273269653320312 + ], + [ + -2.9264559745788574, + 2.204591989517212 + ], + [ + -2.8800888061523438, + 2.27834415435791 + ], + [ + -2.842005491256714, + 2.3492860794067383 + ], + [ + -2.798678398132324, + 2.4209070205688477 + ], + [ + -2.7528514862060547, + 2.4890832901000977 + ], + [ + -2.702406406402588, + 2.5655250549316406 + ], + [ + -2.6573588848114014, + 2.6316630840301514 + ], + [ + -2.606713056564331, + 2.7075676918029785 + ], + [ + -2.5558927059173584, + 2.7759933471679688 + ], + [ + -2.511176109313965, + 2.8374271392822266 + ], + [ + -2.453795909881592, + 2.907130718231201 + ], + [ + -2.395359754562378, + 2.969266891479492 + ], + [ + -2.335897922515869, + 3.0380139350891113 + ], + [ + -2.270254135131836, + 3.102494955062866 + ], + [ + -2.1994450092315674, + 3.1652512550354004 + ], + [ + -2.1340577602386475, + 3.2305760383605957 + ], + [ + -2.0573885440826416, + 3.2901291847229004 + ], + [ + -1.9910917282104492, + 3.3597488403320312 + ], + [ + -1.914387583732605, + 3.421565532684326 + ], + [ + -1.8417962789535522, + 3.4749624729156494 + ], + [ + -1.7740607261657715, + 3.538019895553589 + ], + [ + -1.6910836696624756, + 3.593351364135742 + ], + [ + -1.6129546165466309, + 3.650934934616089 + ], + [ + -1.5314576625823975, + 3.7079336643218994 + ], + [ + -1.4456006288528442, + 3.7681336402893066 + ], + [ + -1.3613699674606323, + 3.816110610961914 + ], + [ + -1.2707738876342773, + 3.867534637451172 + ], + [ + -1.1808480024337769, + 3.917786121368408 + ], + [ + -1.090746521949768, + 3.965712308883667 + ], + [ + -0.9997296929359436, + 4.015567302703857 + ], + [ + -0.8969882726669312, + 4.064459323883057 + ], + [ + -0.8074730038642883, + 4.103725433349609 + ], + [ + -0.705039918422699, + 4.142040252685547 + ], + [ + -0.6112637519836426, + 4.195705890655518 + ], + [ + -0.5010696649551392, + 4.22493839263916 + ], + [ + -0.4009867012500763, + 4.263381481170654 + ], + [ + -0.2912496328353882, + 4.291356086730957 + ], + [ + -0.17719513177871704, + 4.322331428527832 + ], + [ + -0.0559571348130703, + 4.347788333892822 + ], + [ + 0.053220413625240326, + 4.367687225341797 + ], + [ + 0.1828378587961197, + 4.393011093139648 + ], + [ + 0.3024580180644989, + 4.400704860687256 + ], + [ + 0.4196595847606659, + 4.400711536407471 + ], + [ + 0.5461052656173706, + 4.420855522155762 + ], + [ + 0.6670081615447998, + 4.422549247741699 + ], + [ + 0.7900673151016235, + 4.433554172515869 + ], + [ + 0.9182475209236145, + 4.435775279998779 + ], + [ + 1.0549386739730835, + 4.4369611740112305 + ], + [ + 1.1896811723709106, + 4.438133239746094 + ], + [ + 1.3255913257598877, + 4.427420616149902 + ], + [ + 1.4510456323623657, + 4.396478176116943 + ], + [ + 1.5899698734283447, + 4.381324768066406 + ], + [ + 1.7368065118789673, + 4.359009265899658 + ], + [ + 1.867936134338379, + 4.326304912567139 + ], + [ + 1.9993454217910767, + 4.286786079406738 + ], + [ + 2.1317646503448486, + 4.249267101287842 + ], + [ + 2.26725697517395, + 4.204619884490967 + ], + [ + 2.4096617698669434, + 4.160815715789795 + ], + [ + 2.5364108085632324, + 4.112579822540283 + ], + [ + 2.657930612564087, + 4.05103063583374 + ], + [ + 2.787731409072876, + 3.9864959716796875 + ], + [ + 2.9073808193206787, + 3.9175057411193848 + ], + [ + 3.0339295864105225, + 3.849170684814453 + ], + [ + 3.1596813201904297, + 3.782402992248535 + ], + [ + 3.266500949859619, + 3.694551467895508 + ], + [ + 3.377516746520996, + 3.6156153678894043 + ], + [ + 3.5012471675872803, + 3.522921085357666 + ], + [ + 3.607501268386841, + 3.4285483360290527 + ], + [ + 3.721850872039795, + 3.327152967453003 + ], + [ + 3.827000141143799, + 3.223395824432373 + ], + [ + 3.9379539489746094, + 3.1131348609924316 + ], + [ + 4.042356014251709, + 2.996729612350464 + ], + [ + 4.159390449523926, + 2.877631187438965 + ], + [ + 4.2581048011779785, + 2.7344279289245605 + ], + [ + 4.342874526977539, + 2.610862970352173 + ], + [ + 4.443049430847168, + 2.4738378524780273 + ], + [ + 4.523603439331055, + 2.3404018878936768 + ], + [ + 4.5946245193481445, + 2.2019197940826416 + ], + [ + 4.675113677978516, + 2.0515711307525635 + ], + [ + 4.738132476806641, + 1.8976340293884277 + ], + [ + 4.78986930847168, + 1.7495304346084595 + ], + [ + 4.853011608123779, + 1.5902783870697021 + ], + [ + 4.901889324188232, + 1.4279320240020752 + ], + [ + 4.941633701324463, + 1.2628992795944214 + ], + [ + 4.986309051513672, + 1.1021469831466675 + ], + [ + 5.011632442474365, + 0.935245931148529 + ], + [ + 5.037826061248779, + 0.7647016048431396 + ], + [ + 5.053710460662842, + 0.6024985313415527 + ], + [ + 5.0733466148376465, + 0.43650737404823303 + ], + [ + 5.089456558227539, + 0.27373409271240234 + ], + [ + 5.078577041625977, + 0.10420450568199158 + ], + [ + 5.072869300842285, + -0.05629599466919899 + ], + [ + 5.057137489318848, + -0.21737471222877502 + ], + [ + 5.033437728881836, + -0.34463685750961304 + ], + [ + 4.998108863830566, + -0.5058478116989136 + ], + [ + 4.966320514678955, + -0.6498308777809143 + ], + [ + 4.915668964385986, + -0.7943239212036133 + ], + [ + 4.865395545959473, + -0.9435988664627075 + ], + [ + 4.807960510253906, + -1.081966519355774 + ], + [ + 4.7376532554626465, + -1.2200692892074585 + ], + [ + 4.667508602142334, + -1.355132818222046 + ], + [ + 4.595383644104004, + -1.4871387481689453 + ], + [ + 4.515742301940918, + -1.622791051864624 + ], + [ + 4.422509670257568, + -1.7464717626571655 + ], + [ + 4.3369340896606445, + -1.8784594535827637 + ], + [ + 4.242724418640137, + -2.005474090576172 + ], + [ + 4.14840030670166, + -2.147378444671631 + ], + [ + 4.063354969024658, + -2.2759499549865723 + ], + [ + 3.951305389404297, + -2.3975613117218018 + ], + [ + 3.8580098152160645, + -2.5223536491394043 + ], + [ + 3.765162467956543, + -2.6296277046203613 + ], + [ + 3.6515040397644043, + -2.745894432067871 + ], + [ + 3.5635275840759277, + -2.845367908477783 + ], + [ + 3.4549825191497803, + -2.9300620555877686 + ], + [ + 3.3704142570495605, + -3.033066511154175 + ], + [ + 3.2684061527252197, + -3.090548515319824 + ], + [ + 3.160047769546509, + -3.149569272994995 + ], + [ + 3.0653798580169678, + -3.193134069442749 + ], + [ + 2.957157611846924, + -3.2273616790771484 + ], + [ + 2.8522789478302, + -3.2683937549591064 + ], + [ + 2.7458417415618896, + -3.283806562423706 + ], + [ + 2.632183313369751, + -3.307053565979004 + ], + [ + 2.528501510620117, + -3.3292500972747803 + ], + [ + 2.4107823371887207, + -3.344839096069336 + ], + [ + 2.298522472381592, + -3.348083257675171 + ], + [ + 2.1977953910827637, + -3.372875452041626 + ], + [ + 2.083407163619995, + -3.366670846939087 + ], + [ + 1.9608125686645508, + -3.3750596046447754 + ], + [ + 1.8605008125305176, + -3.384669780731201 + ], + [ + 1.7375348806381226, + -3.395942211151123 + ], + [ + 1.6165754795074463, + -3.4232215881347656 + ], + [ + 1.473440170288086, + -3.625798225402832 + ], + [ + 1.3664487600326538, + -3.628476858139038 + ], + [ + 1.2635457515716553, + -3.6369354724884033 + ], + [ + 1.1742236614227295, + -3.616102933883667 + ], + [ + 1.0705455541610718, + -3.617340087890625 + ], + [ + 0.9761837124824524, + -3.6077592372894287 + ], + [ + 0.8788896203041077, + -3.604526996612549 + ], + [ + 0.7796502709388733, + -3.5850894451141357 + ], + [ + 0.6826730370521545, + -3.5633981227874756 + ], + [ + 0.5934434533119202, + -3.5596537590026855 + ], + [ + 0.5035812854766846, + -3.5311779975891113 + ], + [ + 0.4178984463214874, + -3.5169882774353027 + ], + [ + 0.32849785685539246, + -3.5019373893737793 + ], + [ + 0.24413107335567474, + -3.4760217666625977 + ], + [ + 0.16599296033382416, + -3.457561492919922 + ], + [ + 0.0886717289686203, + -3.432316541671753 + ], + [ + 0.0069116936065256596, + -3.410564422607422 + ], + [ + -0.06821972876787186, + -3.385152816772461 + ], + [ + -0.1381102055311203, + -3.3575682640075684 + ], + [ + -0.2036321461200714, + -3.32989239692688 + ], + [ + -0.2721547484397888, + -3.2988481521606445 + ], + [ + -0.3456687927246094, + -3.2797422409057617 + ], + [ + -0.40491026639938354, + -3.2436702251434326 + ], + [ + -0.46992459893226624, + -3.217508316040039 + ], + [ + -0.5275281667709351, + -3.18990421295166 + ], + [ + -0.5910951495170593, + -3.159196615219116 + ], + [ + -0.6481737494468689, + -3.1255087852478027 + ], + [ + -0.6999361515045166, + -3.0945961475372314 + ], + [ + -0.7655573487281799, + -3.066328287124634 + ], + [ + -0.8193655014038086, + -3.029822826385498 + ], + [ + -0.8740184307098389, + -2.9954843521118164 + ], + [ + -0.9328928589820862, + -2.9635941982269287 + ], + [ + -0.9758638739585876, + -2.9242727756500244 + ], + [ + -1.0377837419509888, + -2.890282154083252 + ], + [ + -1.086008906364441, + -2.853980302810669 + ], + [ + -1.1375977993011475, + -2.827772855758667 + ], + [ + -1.1902581453323364, + -2.789005756378174 + ], + [ + -1.2380311489105225, + -2.7569501399993896 + ], + [ + -1.2801151275634766, + -2.723897933959961 + ], + [ + -1.331512212753296, + -2.6854257583618164 + ], + [ + -1.3717644214630127, + -2.6477699279785156 + ], + [ + -1.4093807935714722, + -2.6117165088653564 + ], + [ + -1.4476438760757446, + -2.56960129737854 + ], + [ + -1.4962366819381714, + -2.5276761054992676 + ], + [ + -1.5316818952560425, + -2.483553647994995 + ], + [ + -1.5692683458328247, + -2.439359188079834 + ], + [ + -1.607676386833191, + -2.3932254314422607 + ], + [ + -1.615010142326355, + -2.3210482597351074 + ], + [ + -1.6576086282730103, + -2.2704386711120605 + ], + [ + -1.7165112495422363, + -2.220757484436035 + ], + [ + -1.7511240243911743, + -2.1732826232910156 + ], + [ + -1.804411768913269, + -2.117375373840332 + ], + [ + -1.842379093170166, + -2.0728418827056885 + ], + [ + -1.8945618867874146, + -2.0168027877807617 + ], + [ + -1.935418963432312, + -1.9641530513763428 + ], + [ + -1.9654779434204102, + -1.9175623655319214 + ], + [ + -2.014981985092163, + -1.8624297380447388 + ], + [ + -2.055156946182251, + -1.8145853281021118 + ], + [ + -2.089393377304077, + -1.7664117813110352 + ], + [ + -2.121464252471924, + -1.7126656770706177 + ], + [ + -2.154043197631836, + -1.664276361465454 + ], + [ + -2.1856307983398438, + -1.615336537361145 + ], + [ + -2.215449333190918, + -1.5591015815734863 + ], + [ + -2.242549419403076, + -1.5095828771591187 + ], + [ + -2.265376567840576, + -1.4667716026306152 + ], + [ + -2.2891957759857178, + -1.4067754745483398 + ], + [ + -2.3102810382843018, + -1.3600338697433472 + ], + [ + -2.32448410987854, + -1.3162206411361694 + ], + [ + -2.344461441040039, + -1.2646205425262451 + ], + [ + -2.358267068862915, + -1.2159160375595093 + ], + [ + -2.3702125549316406, + -1.1691083908081055 + ], + [ + -2.377868890762329, + -1.121673822402954 + ], + [ + -2.394197463989258, + -1.0700950622558594 + ], + [ + -2.405461072921753, + -1.0208792686462402 + ], + [ + -2.407872200012207, + -0.968040406703949 + ], + [ + -2.4239590167999268, + -0.9168859124183655 + ], + [ + -2.4302685260772705, + -0.8633595705032349 + ], + [ + -2.438366413116455, + -0.8092073798179626 + ], + [ + -2.4480350017547607, + -0.7520635724067688 + ], + [ + -2.45904803276062, + -0.6941565871238708 + ], + [ + -2.4681553840637207, + -0.6360258460044861 + ], + [ + -2.483577251434326, + -0.5720770359039307 + ], + [ + -2.4936487674713135, + -0.5103459358215332 + ], + [ + -2.503358840942383, + -0.4530770182609558 + ], + [ + -2.516498565673828, + -0.39213451743125916 + ], + [ + -2.524306058883667, + -0.3296535611152649 + ], + [ + -2.5337798595428467, + -0.2714705765247345 + ], + [ + -2.5415852069854736, + -0.21370919048786163 + ], + [ + -2.5448076725006104, + -0.15511257946491241 + ], + [ + -2.5307958126068115, + -0.10204117000102997 + ], + [ + -2.527467727661133, + -0.04747380316257477 + ], + [ + -2.52011775970459, + 0.007723364047706127 + ], + [ + -2.5181925296783447, + 0.06148742884397507 + ], + [ + -2.5121519565582275, + 0.1203572079539299 + ], + [ + -2.5046167373657227, + 0.17504900693893433 + ], + [ + -2.4977223873138428, + 0.23374029994010925 + ], + [ + -2.4909656047821045, + 0.2944478988647461 + ], + [ + -2.4815306663513184, + 0.34626659750938416 + ], + [ + -2.474107265472412, + 0.4034506678581238 + ], + [ + -2.461988925933838, + 0.46261775493621826 + ], + [ + -2.4554049968719482, + 0.5274679660797119 + ], + [ + -2.448575973510742, + 0.5868113040924072 + ], + [ + -2.4302854537963867, + 0.6411546468734741 + ], + [ + -2.4183151721954346, + 0.6985329389572144 + ], + [ + -2.413236379623413, + 0.7610287666320801 + ], + [ + -2.4032866954803467, + 0.820647656917572 + ], + [ + -2.3982157707214355, + 0.8830277323722839 + ], + [ + -2.3764331340789795, + 0.9381933808326721 + ], + [ + -2.3649919033050537, + 0.9967764616012573 + ], + [ + -2.3597707748413086, + 1.0586053133010864 + ], + [ + -2.3494198322296143, + 1.111100435256958 + ], + [ + -2.341068983078003, + 1.161250114440918 + ], + [ + -2.3189733028411865, + 1.2125217914581299 + ], + [ + -2.3094048500061035, + 1.2694249153137207 + ], + [ + -2.2991182804107666, + 1.3180114030838013 + ], + [ + -2.2867796421051025, + 1.3622760772705078 + ], + [ + -2.2749311923980713, + 1.3993581533432007 + ], + [ + -2.258960008621216, + 1.4342812299728394 + ], + [ + -2.2377524375915527, + 1.4952926635742188 + ], + [ + -2.215458393096924, + 1.5359910726547241 + ], + [ + -2.1919753551483154, + 1.561612606048584 + ], + [ + -2.1656274795532227, + 1.5948957204818726 + ], + [ + -2.1345701217651367, + 1.6266098022460938 + ], + [ + -2.097571611404419, + 1.6797171831130981 + ], + [ + -2.064439058303833, + 1.713385820388794 + ], + [ + -2.0182299613952637, + 1.7452658414840698 + ], + [ + -1.9683722257614136, + 1.7842978239059448 + ], + [ + -1.9140623807907104, + 1.8159682750701904 + ], + [ + -1.8470770120620728, + 1.8618063926696777 + ], + [ + -1.816527247428894, + 1.9144901037216187 + ], + [ + -1.7516878843307495, + 1.954081416130066 + ], + [ + -1.6953322887420654, + 1.9995709657669067 + ], + [ + -1.6254856586456299, + 2.058678388595581 + ], + [ + -1.5642616748809814, + 2.1180386543273926 + ], + [ + -1.5240942239761353, + 2.167421817779541 + ], + [ + -1.4526803493499756, + 2.2239482402801514 + ], + [ + -1.396411418914795, + 2.2834792137145996 + ], + [ + -1.3327165842056274, + 2.3447656631469727 + ], + [ + -1.2825573682785034, + 2.4180798530578613 + ], + [ + -1.2271827459335327, + 2.4892048835754395 + ], + [ + -1.1655495166778564, + 2.5345427989959717 + ], + [ + -1.1101999282836914, + 2.590912342071533 + ], + [ + -1.0590189695358276, + 2.6529579162597656 + ], + [ + -1.0067218542099, + 2.7113568782806396 + ], + [ + -0.9553897976875305, + 2.775033712387085 + ], + [ + -0.9018446207046509, + 2.826817512512207 + ], + [ + -0.8346455097198486, + 2.8655762672424316 + ], + [ + -0.7675134539604187, + 2.90928053855896 + ], + [ + -0.7167988419532776, + 2.963583469390869 + ], + [ + -0.6518580317497253, + 3.0032525062561035 + ], + [ + -0.5896396636962891, + 3.052675724029541 + ], + [ + -0.5292495489120483, + 3.087273359298706 + ], + [ + -0.4397505223751068, + 3.114755392074585 + ], + [ + -0.37930282950401306, + 3.149266242980957 + ], + [ + -0.3047275245189667, + 3.1856613159179688 + ], + [ + -0.22711072862148285, + 3.2127630710601807 + ], + [ + -0.15210235118865967, + 3.2369232177734375 + ], + [ + -0.08250471949577332, + 3.2628254890441895 + ], + [ + 0.010138730518519878, + 3.290278434753418 + ], + [ + 0.08341272175312042, + 3.2997281551361084 + ], + [ + 0.16364938020706177, + 3.3170077800750732 + ], + [ + 0.24537791311740875, + 3.33978009223938 + ], + [ + 0.3225707709789276, + 3.346046209335327 + ], + [ + 0.4019952118396759, + 3.3418052196502686 + ], + [ + 0.604122519493103, + 3.221787691116333 + ], + [ + 0.6914963126182556, + 3.223146677017212 + ], + [ + 0.8036956191062927, + 3.212400436401367 + ], + [ + 0.9067635536193848, + 3.2051706314086914 + ], + [ + 0.997984766960144, + 3.2036614418029785 + ], + [ + 1.1015901565551758, + 3.1929075717926025 + ], + [ + 1.1932510137557983, + 3.187041997909546 + ], + [ + 1.2955536842346191, + 3.1623470783233643 + ], + [ + 1.382931113243103, + 3.141171455383301 + ], + [ + 1.483323574066162, + 3.1154446601867676 + ], + [ + 1.5709964036941528, + 3.095097303390503 + ], + [ + 1.6650030612945557, + 3.066406726837158 + ], + [ + 1.7640049457550049, + 3.029162645339966 + ], + [ + 1.852841854095459, + 2.990962505340576 + ], + [ + 1.933456301689148, + 2.943995475769043 + ], + [ + 2.0165939331054688, + 2.8938910961151123 + ], + [ + 2.093055009841919, + 2.8433969020843506 + ], + [ + 2.183103084564209, + 2.781916379928589 + ], + [ + 2.2641451358795166, + 2.725630044937134 + ], + [ + 2.3360953330993652, + 2.6565022468566895 + ], + [ + 2.4069952964782715, + 2.580667018890381 + ], + [ + 2.481017827987671, + 2.4989025592803955 + ], + [ + 2.559150457382202, + 2.435673713684082 + ], + [ + 2.6248691082000732, + 2.3538222312927246 + ], + [ + 2.687619686126709, + 2.264403820037842 + ], + [ + 2.7574827671051025, + 2.159935235977173 + ], + [ + 2.8187215328216553, + 2.0657289028167725 + ], + [ + 2.8884854316711426, + 1.962977409362793 + ], + [ + 2.943880319595337, + 1.8824036121368408 + ], + [ + 3.0009965896606445, + 1.7796638011932373 + ], + [ + 3.059818983078003, + 1.6701451539993286 + ], + [ + 3.119795560836792, + 1.5660635232925415 + ], + [ + 3.179224967956543, + 1.4552077054977417 + ], + [ + 3.215134859085083, + 1.3652101755142212 + ], + [ + 3.2675390243530273, + 1.2542645931243896 + ], + [ + 3.3136749267578125, + 1.1495474576950073 + ], + [ + 3.354755163192749, + 1.0480055809020996 + ], + [ + 3.3946781158447266, + 0.9453073740005493 + ], + [ + 3.4283385276794434, + 0.847625732421875 + ], + [ + 3.4463376998901367, + 0.7492050528526306 + ], + [ + 3.4690709114074707, + 0.6568482518196106 + ], + [ + 3.4896035194396973, + 0.5599454045295715 + ], + [ + 3.5035505294799805, + 0.4682500660419464 + ], + [ + 3.5108110904693604, + 0.38048332929611206 + ], + [ + 3.5131337642669678, + 0.2925216257572174 + ], + [ + 3.5079283714294434, + 0.20350931584835052 + ], + [ + 3.5004985332489014, + 0.11184374243021011 + ], + [ + 3.4859938621520996, + 0.0272199809551239 + ], + [ + 3.467696189880371, + -0.06074925884604454 + ], + [ + 3.4491379261016846, + -0.15370480716228485 + ], + [ + 3.4266412258148193, + -0.25017639994621277 + ], + [ + 3.400458812713623, + -0.3469836711883545 + ], + [ + 3.3704798221588135, + -0.43758195638656616 + ], + [ + 3.341146945953369, + -0.5373067855834961 + ], + [ + 3.3167412281036377, + -0.6403863430023193 + ], + [ + 3.2941503524780273, + -0.7457326054573059 + ], + [ + 3.2800180912017822, + -0.8522046208381653 + ], + [ + 3.273306131362915, + -0.9474582672119141 + ], + [ + 3.244905471801758, + -1.0374606847763062 + ], + [ + 3.240784168243408, + -1.1273307800292969 + ], + [ + 3.238358736038208, + -1.2069965600967407 + ], + [ + 3.2290518283843994, + -1.273685097694397 + ], + [ + 3.2154459953308105, + -1.3342549800872803 + ], + [ + 3.2046396732330322, + -1.3789488077163696 + ], + [ + 3.183739185333252, + -1.4194018840789795 + ], + [ + 3.1693122386932373, + -1.4729559421539307 + ], + [ + 3.1344897747039795, + -1.503928542137146 + ], + [ + 3.087867259979248, + -1.5323659181594849 + ], + [ + 3.050173044204712, + -1.5592883825302124 + ], + [ + 3.000412940979004, + -1.587936520576477 + ], + [ + 2.9390182495117188, + -1.6146379709243774 + ], + [ + 2.881744623184204, + -1.6487141847610474 + ], + [ + 2.811448812484741, + -1.6720460653305054 + ], + [ + 2.752296209335327, + -1.7009607553482056 + ], + [ + 2.679608106613159, + -1.7358677387237549 + ], + [ + 2.625382900238037, + -1.776227355003357 + ], + [ + 2.555453062057495, + -1.8114641904830933 + ], + [ + 2.4777891635894775, + -1.8502765893936157 + ], + [ + 2.4119601249694824, + -1.8715084791183472 + ], + [ + 2.333287000656128, + -1.9057646989822388 + ], + [ + 2.257178544998169, + -1.9445021152496338 + ], + [ + 2.1759588718414307, + -1.9900689125061035 + ], + [ + 2.106809377670288, + -2.0283520221710205 + ], + [ + 2.0257275104522705, + -2.0787975788116455 + ], + [ + 1.9454381465911865, + -2.1256701946258545 + ], + [ + 1.8816059827804565, + -2.196702480316162 + ], + [ + 1.8152947425842285, + -2.218325138092041 + ], + [ + 1.7501616477966309, + -2.2719597816467285 + ], + [ + 1.6950534582138062, + -2.321479082107544 + ], + [ + 1.6311957836151123, + -2.370927572250366 + ], + [ + 1.5608556270599365, + -2.4165143966674805 + ], + [ + 1.518396258354187, + -2.460632085800171 + ], + [ + 1.4535807371139526, + -2.5074775218963623 + ], + [ + 1.4655482769012451, + -2.4239885807037354 + ], + [ + 1.4297897815704346, + -2.4633631706237793 + ], + [ + 1.3624173402786255, + -2.488583564758301 + ], + [ + 1.281326413154602, + -2.5086562633514404 + ], + [ + 1.2148277759552002, + -2.5413739681243896 + ], + [ + 1.1541898250579834, + -2.576077938079834 + ], + [ + 1.120560646057129, + -2.588573694229126 + ], + [ + 1.0596061944961548, + -2.5915963649749756 + ], + [ + 0.9839295744895935, + -2.6220412254333496 + ], + [ + 0.9174445271492004, + -2.6488966941833496 + ], + [ + 0.8565689325332642, + -2.669872522354126 + ], + [ + 0.7940139770507812, + -2.6969826221466064 + ], + [ + 0.7390028834342957, + -2.6805472373962402 + ], + [ + 0.6767779588699341, + -2.6966135501861572 + ], + [ + 0.6264951229095459, + -2.7159435749053955 + ], + [ + 0.5704982876777649, + -2.7378273010253906 + ], + [ + 0.5115839838981628, + -2.74786639213562 + ], + [ + 0.4556136429309845, + -2.7649221420288086 + ], + [ + 0.39606210589408875, + -2.739521026611328 + ], + [ + 0.3375975489616394, + -2.753755569458008 + ], + [ + 0.285901814699173, + -2.7672626972198486 + ], + [ + 0.24291600286960602, + -2.7797658443450928 + ], + [ + 0.19613294303417206, + -2.781292200088501 + ], + [ + 0.14553982019424438, + -2.7767372131347656 + ], + [ + 0.08217504620552063, + -2.7646071910858154 + ], + [ + 0.03410906344652176, + -2.781472682952881 + ], + [ + -0.011148075573146343, + -2.7739884853363037 + ], + [ + -0.0541127547621727, + -2.771231174468994 + ], + [ + -0.09749379009008408, + -2.7685039043426514 + ], + [ + -0.13890770077705383, + -2.7591419219970703 + ], + [ + -0.19543200731277466, + -2.746670722961426 + ], + [ + -0.2452128529548645, + -2.7403695583343506 + ], + [ + -0.2969488203525543, + -2.741960048675537 + ], + [ + -0.32703039050102234, + -2.7307851314544678 + ], + [ + -0.3661307692527771, + -2.7157373428344727 + ], + [ + -0.4008972644805908, + -2.7027904987335205 + ], + [ + -0.4249809682369232, + -2.6813294887542725 + ], + [ + -0.5050779581069946, + -2.6763815879821777 + ], + [ + -0.5491519570350647, + -2.663710355758667 + ], + [ + -0.5824120044708252, + -2.6504125595092773 + ], + [ + -0.6192184090614319, + -2.6219286918640137 + ], + [ + -0.6586366891860962, + -2.600358247756958 + ], + [ + -0.6942715644836426, + -2.5729053020477295 + ], + [ + -0.7309486865997314, + -2.5588650703430176 + ], + [ + -0.7765742540359497, + -2.5507702827453613 + ], + [ + -0.8136449456214905, + -2.5285873413085938 + ], + [ + -0.8497253060340881, + -2.496798276901245 + ], + [ + -0.8963299989700317, + -2.465245485305786 + ], + [ + -0.93206787109375, + -2.429049491882324 + ], + [ + -0.9799259901046753, + -2.3877339363098145 + ], + [ + -1.0078473091125488, + -2.398555278778076 + ], + [ + -1.0372157096862793, + -2.361074686050415 + ], + [ + -1.0823018550872803, + -2.3224871158599854 + ], + [ + -1.1329315900802612, + -2.2890939712524414 + ], + [ + -1.1833562850952148, + -2.2415292263031006 + ], + [ + -1.2269871234893799, + -2.2159857749938965 + ], + [ + -1.2922029495239258, + -2.162691116333008 + ], + [ + -1.3056666851043701, + -2.161792755126953 + ], + [ + -1.354131817817688, + -2.121523141860962 + ], + [ + -1.4096392393112183, + -2.0868189334869385 + ], + [ + -1.461287260055542, + -2.045318365097046 + ], + [ + -1.5167489051818848, + -2.0227153301239014 + ], + [ + -1.571979284286499, + -1.9972461462020874 + ], + [ + -1.6312527656555176, + -1.9690998792648315 + ], + [ + -1.6827654838562012, + -1.952263355255127 + ], + [ + -1.708079218864441, + -1.9103525876998901 + ], + [ + -1.7569104433059692, + -1.884315848350525 + ], + [ + -1.8081934452056885, + -1.8666108846664429 + ], + [ + -1.8563565015792847, + -1.8517392873764038 + ], + [ + -1.8965286016464233, + -1.8361438512802124 + ], + [ + -1.9335756301879883, + -1.8242131471633911 + ], + [ + -1.964591383934021, + -1.8026390075683594 + ], + [ + -2.0047719478607178, + -1.762892723083496 + ], + [ + -2.041518211364746, + -1.7451794147491455 + ], + [ + -2.071052074432373, + -1.7293972969055176 + ], + [ + -2.0981240272521973, + -1.706528902053833 + ], + [ + -2.11956524848938, + -1.6901549100875854 + ], + [ + -2.1400058269500732, + -1.6698988676071167 + ], + [ + -2.149365186691284, + -1.6461429595947266 + ], + [ + -2.158566474914551, + -1.6161984205245972 + ], + [ + -2.2120606899261475, + -1.5779215097427368 + ], + [ + -2.2249817848205566, + -1.5428466796875 + ], + [ + -2.2397396564483643, + -1.513846755027771 + ], + [ + -2.255702495574951, + -1.4739855527877808 + ], + [ + -2.789912700653076, + -1.9098845720291138 + ], + [ + -2.82194185256958, + -1.8552011251449585 + ], + [ + -2.8512136936187744, + -1.808366298675537 + ], + [ + -2.894787549972534, + -1.7530639171600342 + ], + [ + -2.9243111610412598, + -1.6943556070327759 + ], + [ + -2.9650661945343018, + -1.638841152191162 + ], + [ + -3.001330614089966, + -1.5780820846557617 + ], + [ + -3.0343077182769775, + -1.5203001499176025 + ], + [ + -3.0667078495025635, + -1.4537278413772583 + ], + [ + -3.105184316635132, + -1.391394019126892 + ], + [ + -3.1322951316833496, + -1.3280572891235352 + ], + [ + -3.1623547077178955, + -1.259221076965332 + ], + [ + -1.939096212387085, + -0.6807480454444885 + ], + [ + -1.952365517616272, + -0.6386276483535767 + ], + [ + -1.9693323373794556, + -0.5959262251853943 + ], + [ + -1.9833849668502808, + -0.5515741109848022 + ], + [ + -1.9971498250961304, + -0.5061970353126526 + ], + [ + -2.009053945541382, + -0.4613075256347656 + ], + [ + -2.021026372909546, + -0.41577762365341187 + ], + [ + -2.031355381011963, + -0.3683095872402191 + ], + [ + -2.0408895015716553, + -0.32223084568977356 + ], + [ + -2.049889326095581, + -0.27467721700668335 + ], + [ + -2.058365821838379, + -0.22663073241710663 + ], + [ + -2.0657122135162354, + -0.1788879632949829 + ], + [ + -2.0707199573516846, + -0.12972895801067352 + ], + [ + -2.07615065574646, + -0.08084411174058914 + ], + [ + -2.0804061889648438, + -0.031422313302755356 + ], + [ + -2.0833592414855957, + 0.018214151263237 + ], + [ + -2.085083246231079, + 0.0708853080868721 + ], + [ + -2.0858254432678223, + 0.12087997794151306 + ], + [ + -2.085240364074707, + 0.17192336916923523 + ], + [ + -2.084052085876465, + 0.223068505525589 + ], + [ + -2.081662178039551, + 0.27628636360168457 + ], + [ + -2.0768208503723145, + 0.3311088979244232 + ], + [ + -2.067357063293457, + 0.3807849884033203 + ], + [ + -2.062736988067627, + 0.43460559844970703 + ], + [ + -2.0520730018615723, + 0.4866396188735962 + ], + [ + -2.0412445068359375, + 0.5380857586860657 + ], + [ + -2.0276601314544678, + 0.5891246795654297 + ], + [ + -2.011918783187866, + 0.6406463384628296 + ], + [ + -1.9971976280212402, + 0.694553792476654 + ], + [ + -1.983178734779358, + 0.7432039976119995 + ], + [ + -1.9616353511810303, + 0.7950911521911621 + ], + [ + -1.9458224773406982, + 0.8461785316467285 + ], + [ + -1.9264793395996094, + 0.8947480916976929 + ], + [ + -1.9000548124313354, + 0.9455226063728333 + ], + [ + -1.8813544511795044, + 0.9935013651847839 + ], + [ + -1.8542447090148926, + 1.0390750169754028 + ], + [ + -1.8293994665145874, + 1.0922362804412842 + ], + [ + -1.7988624572753906, + 1.132934331893921 + ], + [ + -1.7675772905349731, + 1.1780340671539307 + ], + [ + -1.7385889291763306, + 1.2278425693511963 + ], + [ + -1.7053543329238892, + 1.273457407951355 + ], + [ + -1.6693856716156006, + 1.3176956176757812 + ], + [ + -1.633844256401062, + 1.3631333112716675 + ], + [ + -1.5976521968841553, + 1.4036017656326294 + ], + [ + -1.5626500844955444, + 1.4425859451293945 + ], + [ + -1.52292799949646, + 1.4863156080245972 + ], + [ + -1.4851118326187134, + 1.5260387659072876 + ], + [ + -1.4443962574005127, + 1.5574613809585571 + ], + [ + -1.3997883796691895, + 1.5909099578857422 + ], + [ + -1.3596993684768677, + 1.636057734489441 + ], + [ + -1.3204857110977173, + 1.6726205348968506 + ], + [ + -1.2703770399093628, + 1.697584629058838 + ], + [ + -1.2326018810272217, + 1.7287253141403198 + ], + [ + -1.1931045055389404, + 1.7673418521881104 + ], + [ + -1.153369426727295, + 1.7960216999053955 + ], + [ + -1.0999146699905396, + 1.8214483261108398 + ], + [ + -1.0547243356704712, + 1.8478145599365234 + ], + [ + -1.0112537145614624, + 1.8764448165893555 + ], + [ + -0.9666260480880737, + 1.8865482807159424 + ], + [ + -0.9129154682159424, + 1.914810299873352 + ], + [ + -0.8649275898933411, + 1.9297493696212769 + ], + [ + -0.8224252462387085, + 1.9530653953552246 + ], + [ + -0.7731329798698425, + 1.963896632194519 + ], + [ + -0.7225728631019592, + 1.9904707670211792 + ], + [ + -0.6760976910591125, + 1.9916374683380127 + ], + [ + -0.6234354376792908, + 2.014925718307495 + ], + [ + -0.5778567790985107, + 2.026262044906616 + ], + [ + -0.524203360080719, + 2.034918785095215 + ], + [ + -0.47348344326019287, + 2.040405035018921 + ], + [ + -0.42911845445632935, + 2.051117181777954 + ], + [ + -0.3717295825481415, + 2.057615280151367 + ], + [ + -0.32046380639076233, + 2.0602846145629883 + ], + [ + -0.2691158354282379, + 2.059157133102417 + ], + [ + -0.21888059377670288, + 2.0386364459991455 + ], + [ + -0.1683487445116043, + 2.0663208961486816 + ], + [ + -0.10686074197292328, + 2.078583002090454 + ], + [ + -0.05108408257365227, + 2.067359685897827 + ], + [ + -0.00803030002862215, + 2.074599027633667 + ], + [ + 0.048057761043310165, + 2.0656816959381104 + ], + [ + 0.09408649057149887, + 2.066115617752075 + ], + [ + 0.1477361023426056, + 2.054093599319458 + ], + [ + 0.19644670188426971, + 2.0475094318389893 + ], + [ + 0.23930613696575165, + 2.0432071685791016 + ], + [ + 0.2943762242794037, + 2.0298473834991455 + ], + [ + 0.3447219133377075, + 2.01904559135437 + ], + [ + 0.39233526587486267, + 2.003962278366089 + ], + [ + 0.44757068157196045, + 1.9899786710739136 + ], + [ + 0.4949842691421509, + 1.9744194746017456 + ], + [ + 0.5531154870986938, + 1.9555765390396118 + ], + [ + 0.5992284417152405, + 1.9338862895965576 + ], + [ + 0.646668553352356, + 1.9173840284347534 + ], + [ + 0.6888584494590759, + 1.8864469528198242 + ], + [ + 0.7450215220451355, + 1.870263695716858 + ], + [ + 0.794282078742981, + 1.8498250246047974 + ], + [ + 0.8379050493240356, + 1.8245142698287964 + ], + [ + 0.8887669444084167, + 1.8075196743011475 + ], + [ + 0.937105119228363, + 1.7697194814682007 + ], + [ + 0.9815776944160461, + 1.7397254705429077 + ], + [ + 1.0308599472045898, + 1.7040032148361206 + ], + [ + 1.0745210647583008, + 1.6717205047607422 + ], + [ + 1.1184508800506592, + 1.6470627784729004 + ], + [ + 1.1646403074264526, + 1.6061427593231201 + ], + [ + 1.205773115158081, + 1.5797123908996582 + ], + [ + 1.2512071132659912, + 1.5444042682647705 + ], + [ + 1.2964011430740356, + 1.4943119287490845 + ], + [ + 1.3324358463287354, + 1.4676051139831543 + ], + [ + 1.3684406280517578, + 1.420765995979309 + ], + [ + 1.4130305051803589, + 1.3833521604537964 + ], + [ + 1.4555394649505615, + 1.3444534540176392 + ], + [ + 1.4818600416183472, + 1.3038278818130493 + ], + [ + 1.5246280431747437, + 1.2591265439987183 + ], + [ + 1.559377908706665, + 1.2145498991012573 + ], + [ + 1.5942211151123047, + 1.1636466979980469 + ], + [ + 1.6295610666275024, + 1.1227933168411255 + ], + [ + 1.6616950035095215, + 1.0683050155639648 + ], + [ + 1.6954121589660645, + 1.0288985967636108 + ], + [ + 1.7216964960098267, + 0.9798287153244019 + ], + [ + 1.7503950595855713, + 0.9276873469352722 + ], + [ + 1.778201699256897, + 0.8792389035224915 + ], + [ + 1.8050205707550049, + 0.8297778964042664 + ], + [ + 1.836473822593689, + 0.7740538716316223 + ], + [ + 1.8550477027893066, + 0.7254865169525146 + ], + [ + 1.8791964054107666, + 0.6720587611198425 + ], + [ + 1.89922034740448, + 0.6195781230926514 + ], + [ + 1.9190661907196045, + 0.5651131868362427 + ], + [ + 1.9393385648727417, + 0.5110737085342407 + ], + [ + 1.9546383619308472, + 0.4575277864933014 + ], + [ + 1.9703558683395386, + 0.4025319814682007 + ], + [ + 1.9852973222732544, + 0.34665659070014954 + ], + [ + 1.9997735023498535, + 0.2932497262954712 + ], + [ + 2.0096211433410645, + 0.23785769939422607 + ], + [ + 2.02043080329895, + 0.1810353845357895 + ], + [ + 2.028529167175293, + 0.12593293190002441 + ], + [ + 2.0358526706695557, + 0.06951350718736649 + ], + [ + 2.041053056716919, + 0.014001079834997654 + ], + [ + 2.0463147163391113, + -0.04065854847431183 + ], + [ + 2.0535101890563965, + -0.09649515151977539 + ], + [ + 2.054388999938965, + -0.1520168036222458 + ], + [ + 2.0538721084594727, + -0.20868702232837677 + ], + [ + 2.053163766860962, + -0.2584943175315857 + ], + [ + 2.0484564304351807, + -0.3143188953399658 + ], + [ + 2.043355941772461, + -0.36864492297172546 + ], + [ + 2.042011022567749, + -0.4278182089328766 + ], + [ + 2.034322738647461, + -0.48044708371162415 + ], + [ + 2.025951623916626, + -0.5363051891326904 + ], + [ + 2.016893148422241, + -0.5878207087516785 + ], + [ + 2.0083096027374268, + -0.6492957472801208 + ], + [ + 2.0000500679016113, + -0.6932437419891357 + ], + [ + 1.987940788269043, + -0.7574043869972229 + ], + [ + 1.9705811738967896, + -0.8061993718147278 + ], + [ + 1.9567289352416992, + -0.8605440258979797 + ], + [ + 1.9346753358840942, + -0.9095450639724731 + ], + [ + 1.9202497005462646, + -0.9648932814598083 + ], + [ + 1.9040396213531494, + -1.0079864263534546 + ], + [ + 1.8808674812316895, + -1.060410976409912 + ], + [ + 1.8595004081726074, + -1.107595443725586 + ], + [ + 1.8395739793777466, + -1.16050124168396 + ], + [ + 1.8140474557876587, + -1.2115402221679688 + ], + [ + 1.7881207466125488, + -1.2526763677597046 + ], + [ + 1.7519309520721436, + -1.2990410327911377 + ], + [ + 1.7247456312179565, + -1.3432480096817017 + ], + [ + 1.700600028038025, + -1.3812659978866577 + ], + [ + 1.6677035093307495, + -1.432289958000183 + ], + [ + 1.6372331380844116, + -1.470292091369629 + ], + [ + 1.602839708328247, + -1.51361882686615 + ], + [ + 1.576010823249817, + -1.5580676794052124 + ], + [ + 1.540656328201294, + -1.5951287746429443 + ], + [ + 1.503659725189209, + -1.642486810684204 + ], + [ + 1.4607102870941162, + -1.671753168106079 + ], + [ + 1.4102507829666138, + -1.6961443424224854 + ], + [ + 1.3745391368865967, + -1.7225377559661865 + ], + [ + 1.3331958055496216, + -1.7523412704467773 + ], + [ + 1.2921346426010132, + -1.7896794080734253 + ], + [ + 1.2396968603134155, + -1.809958815574646 + ], + [ + 1.2020959854125977, + -1.8427790403366089 + ], + [ + 1.1576862335205078, + -1.874192714691162 + ], + [ + 1.118741750717163, + -1.9031258821487427 + ], + [ + 1.077420711517334, + -1.9349583387374878 + ], + [ + 1.0102405548095703, + -1.9094842672348022 + ], + [ + 0.9447270631790161, + -1.9233429431915283 + ], + [ + 0.9168527126312256, + -1.9392727613449097 + ], + [ + 0.8702920079231262, + -1.9707372188568115 + ], + [ + 0.8179040551185608, + -2.013446807861328 + ], + [ + 0.7824435234069824, + -2.04972767829895 + ], + [ + 0.7528225183486938, + -2.0892317295074463 + ], + [ + 0.711268961429596, + -2.1270482540130615 + ], + [ + 0.6382469534873962, + -2.1659374237060547 + ], + [ + 0.583331823348999, + -2.205756187438965 + ], + [ + 0.5129031538963318, + -2.2281875610351562 + ], + [ + 0.41605865955352783, + -2.168739080429077 + ], + [ + 0.3060882091522217, + -2.146209478378296 + ], + [ + 0.15365441143512726, + -2.105172872543335 + ], + [ + 0.0020785960368812084, + -2.040670394897461 + ], + [ + -0.15418343245983124, + -1.9243831634521484 + ], + [ + -0.3202892243862152, + -1.798927664756775 + ], + [ + -0.4346787929534912, + -1.6801048517227173 + ], + [ + -0.5110172629356384, + -1.613135576248169 + ], + [ + -0.5405659675598145, + -1.5904440879821777 + ], + [ + -0.5455673933029175, + -1.607232928276062 + ], + [ + -0.6063836216926575, + -1.9449594020843506 + ], + [ + -0.624832808971405, + -1.9866611957550049 + ], + [ + -0.6481931209564209, + -2.0024936199188232 + ], + [ + -0.6634911298751831, + -1.9572324752807617 + ], + [ + -0.6663447618484497, + -1.8878417015075684 + ], + [ + -0.6563385128974915, + -1.7929601669311523 + ], + [ + -0.6403387784957886, + -1.6972758769989014 + ], + [ + -0.6024368405342102, + -1.6063774824142456 + ], + [ + -0.5762656927108765, + -1.5338255167007446 + ], + [ + -0.5187647938728333, + -1.4662768840789795 + ], + [ + -0.48415064811706543, + -1.435086965560913 + ], + [ + -0.5525069236755371, + -1.5038563013076782 + ], + [ + -0.5161784887313843, + -1.4564497470855713 + ], + [ + -0.49484115839004517, + -1.4358186721801758 + ], + [ + -0.47963976860046387, + -1.437211513519287 + ], + [ + -0.49533164501190186, + -1.4836785793304443 + ], + [ + -0.5715327262878418, + -1.6046595573425293 + ], + [ + -0.7012724876403809, + -1.7719199657440186 + ], + [ + -0.8673467636108398, + -1.9547784328460693 + ], + [ + -1.015973448753357, + -2.101969003677368 + ], + [ + -1.1314986944198608, + -2.170522689819336 + ], + [ + -1.2274649143218994, + -2.160341262817383 + ], + [ + -1.3087838888168335, + -2.136418581008911 + ], + [ + -1.3310165405273438, + -2.1153032779693604 + ], + [ + -1.407902717590332, + -2.0705678462982178 + ], + [ + -1.475193738937378, + -2.0379385948181152 + ], + [ + -1.5394911766052246, + -1.9791806936264038 + ], + [ + -1.6067612171173096, + -1.91769540309906 + ], + [ + -1.650140643119812, + -1.8407727479934692 + ], + [ + -1.6875827312469482, + -1.7631479501724243 + ], + [ + -1.7184261083602905, + -1.671218752861023 + ], + [ + -1.7424614429473877, + -1.5910464525222778 + ], + [ + -1.7474254369735718, + -1.5262022018432617 + ], + [ + -1.7653908729553223, + -1.4905372858047485 + ], + [ + -1.8015632629394531, + -1.4718059301376343 + ], + [ + -1.8849856853485107, + -1.4614430665969849 + ], + [ + -1.9283195734024048, + -1.4638653993606567 + ], + [ + -1.9919277429580688, + -1.4629545211791992 + ], + [ + -2.0574071407318115, + -1.4738165140151978 + ], + [ + -2.1340599060058594, + -1.4894769191741943 + ], + [ + -2.2231273651123047, + -1.4883272647857666 + ], + [ + -2.291485548019409, + -1.470659852027893 + ], + [ + -2.3644659519195557, + -1.440994143486023 + ], + [ + -2.4340333938598633, + -1.4051308631896973 + ], + [ + -2.491485118865967, + -1.3487628698349 + ], + [ + -2.5485143661499023, + -1.2934077978134155 + ], + [ + -2.592172861099243, + -1.2255971431732178 + ], + [ + -2.6381776332855225, + -1.1586625576019287 + ], + [ + -2.695106029510498, + -1.0983964204788208 + ], + [ + -2.7385199069976807, + -1.0343594551086426 + ], + [ + -2.776291608810425, + -0.9640145301818848 + ], + [ + -2.81166934967041, + -0.8906009793281555 + ], + [ + -2.836707830429077, + -0.8137257099151611 + ], + [ + -2.858428955078125, + -0.7408859729766846 + ], + [ + -2.8790154457092285, + -0.6690824627876282 + ], + [ + -2.8940415382385254, + -0.5941474437713623 + ], + [ + -2.9010426998138428, + -0.5238000750541687 + ], + [ + -2.909085988998413, + -0.45078906416893005 + ], + [ + -2.9083189964294434, + -0.37507739663124084 + ], + [ + -2.9072840213775635, + -0.2997860908508301 + ], + [ + -3.005812883377075, + -0.21556322276592255 + ], + [ + -3.017341136932373, + -0.13587506115436554 + ], + [ + -3.023409843444824, + -0.05870097503066063 + ], + [ + -3.025590181350708, + 0.01880655437707901 + ], + [ + -3.025848865509033, + 0.09365110844373703 + ], + [ + -3.0220961570739746, + 0.17214366793632507 + ], + [ + -3.0202388763427734, + 0.24609537422657013 + ], + [ + -3.0081255435943604, + 0.3179972767829895 + ], + [ + -2.995783567428589, + 0.39712363481521606 + ], + [ + -2.984403371810913, + 0.46626558899879456 + ], + [ + -2.970194101333618, + 0.5454097986221313 + ], + [ + -2.9559531211853027, + 0.615135133266449 + ], + [ + -2.9344537258148193, + 0.6851721405982971 + ], + [ + -2.9574944972991943, + 0.7915886044502258 + ], + [ + -2.9441006183624268, + 0.8669255375862122 + ], + [ + -2.916849374771118, + 0.9476485848426819 + ], + [ + -2.897669792175293, + 1.0129587650299072 + ], + [ + -2.876121759414673, + 1.086795687675476 + ], + [ + -2.84883451461792, + 1.1496262550354004 + ], + [ + -2.8302626609802246, + 1.2204465866088867 + ], + [ + -2.7993199825286865, + 1.2962018251419067 + ], + [ + -2.7594807147979736, + 1.3708014488220215 + ], + [ + -2.7271833419799805, + 1.4501303434371948 + ], + [ + -2.6744866371154785, + 1.5389537811279297 + ], + [ + -2.61313796043396, + 1.5949156284332275 + ], + [ + -2.5551230907440186, + 1.6584594249725342 + ], + [ + -2.4908816814422607, + 1.7083662748336792 + ], + [ + -2.4266505241394043, + 1.7613481283187866 + ], + [ + -2.38466477394104, + 1.7962907552719116 + ], + [ + -2.3341479301452637, + 1.8218728303909302 + ], + [ + -2.2738988399505615, + 1.8703877925872803 + ], + [ + -2.2376792430877686, + 1.8903529644012451 + ], + [ + -2.1968719959259033, + 1.9464670419692993 + ], + [ + -2.153024911880493, + 1.9752123355865479 + ], + [ + -2.1001553535461426, + 2.00573992729187 + ], + [ + -2.054490089416504, + 2.0517256259918213 + ], + [ + -1.992921233177185, + 2.088070869445801 + ], + [ + -1.9374616146087646, + 2.111299991607666 + ], + [ + -1.880867838859558, + 2.13771915435791 + ], + [ + -1.8321253061294556, + 2.172494649887085 + ], + [ + -1.7726197242736816, + 2.2032346725463867 + ], + [ + -1.727648377418518, + 2.228423833847046 + ], + [ + -1.6646350622177124, + 2.245445966720581 + ], + [ + -1.6086881160736084, + 2.256934404373169 + ], + [ + -1.5574150085449219, + 2.2773215770721436 + ], + [ + -1.5145063400268555, + 2.3043603897094727 + ], + [ + -1.4563183784484863, + 2.2967689037323 + ], + [ + -1.3895223140716553, + 2.304969310760498 + ], + [ + -1.3440701961517334, + 2.321601629257202 + ], + [ + -1.3160995244979858, + 2.337130069732666 + ], + [ + -1.2590749263763428, + 2.335090398788452 + ], + [ + -1.2157617807388306, + 2.353987693786621 + ], + [ + -1.1620811223983765, + 2.3656516075134277 + ], + [ + -1.1153879165649414, + 2.363826274871826 + ], + [ + -1.0671113729476929, + 2.378605842590332 + ], + [ + -1.0245308876037598, + 2.4003958702087402 + ], + [ + -0.9690240621566772, + 2.404007911682129 + ], + [ + -0.9172849655151367, + 2.416661024093628 + ], + [ + -0.888172447681427, + 2.4239163398742676 + ], + [ + -0.8456668257713318, + 2.434183359146118 + ], + [ + -0.8011134266853333, + 2.4406189918518066 + ], + [ + -0.7593628764152527, + 2.447582483291626 + ], + [ + -0.7207033634185791, + 2.459336280822754 + ], + [ + -0.6904757022857666, + 2.488373041152954 + ], + [ + -0.6264764666557312, + 2.486638069152832 + ], + [ + -0.596717894077301, + 2.4937942028045654 + ], + [ + -0.546213686466217, + 2.4793148040771484 + ], + [ + -0.48355063796043396, + 2.5083060264587402 + ], + [ + -0.4249427020549774, + 2.500951051712036 + ], + [ + -0.37415939569473267, + 2.534940481185913 + ], + [ + -0.3312223553657532, + 2.5396077632904053 + ], + [ + -0.2889305353164673, + 2.5419344902038574 + ], + [ + -0.21558496356010437, + 2.560695171356201 + ], + [ + -0.16291315853595734, + 2.576202392578125 + ], + [ + -0.10912346839904785, + 2.5665762424468994 + ], + [ + -0.051017604768276215, + 2.5686533451080322 + ], + [ + 0.018875345587730408, + 2.567444086074829 + ], + [ + 0.06465987861156464, + 2.5701773166656494 + ], + [ + 0.1324782520532608, + 2.561803102493286 + ], + [ + 0.2068946808576584, + 2.555746078491211 + ], + [ + 0.2597694993019104, + 2.5605828762054443 + ], + [ + 0.3346307873725891, + 2.54529070854187 + ], + [ + 0.4071377217769623, + 2.5414583683013916 + ], + [ + 0.4494141936302185, + 2.5375921726226807 + ], + [ + 0.5025323629379272, + 2.508594274520874 + ], + [ + 0.5790141820907593, + 2.5116915702819824 + ], + [ + 0.6427742838859558, + 2.501234292984009 + ], + [ + 0.6994442343711853, + 2.4768364429473877 + ], + [ + 0.7833623290061951, + 2.457221031188965 + ], + [ + 0.8016390204429626, + 2.4768195152282715 + ], + [ + 0.9044339060783386, + 2.4217684268951416 + ], + [ + 0.9513251185417175, + 2.4143903255462646 + ], + [ + 1.0317407846450806, + 2.372506856918335 + ], + [ + 1.0866315364837646, + 2.3450143337249756 + ], + [ + 1.1452995538711548, + 2.3264284133911133 + ], + [ + 1.222049355506897, + 2.2988715171813965 + ], + [ + 1.2785722017288208, + 2.264704942703247 + ], + [ + 1.3450956344604492, + 2.2289061546325684 + ], + [ + 1.4226233959197998, + 2.2009341716766357 + ], + [ + 1.4660910367965698, + 2.160318374633789 + ], + [ + 1.5323148965835571, + 2.1187257766723633 + ], + [ + 1.5581592321395874, + 2.095895528793335 + ], + [ + 1.6538889408111572, + 2.0462610721588135 + ], + [ + 1.7080063819885254, + 1.995171070098877 + ], + [ + 1.7654551267623901, + 1.9639097452163696 + ], + [ + 1.83365797996521, + 1.9093478918075562 + ], + [ + 1.886694312095642, + 1.8681954145431519 + ], + [ + 1.940342903137207, + 1.8244189023971558 + ], + [ + 1.99257493019104, + 1.7620192766189575 + ], + [ + 2.0535972118377686, + 1.7182610034942627 + ], + [ + 2.104647397994995, + 1.670270323753357 + ], + [ + 2.160341739654541, + 1.6226904392242432 + ], + [ + 2.204922914505005, + 1.5611199140548706 + ], + [ + 2.2633750438690186, + 1.4981132745742798 + ], + [ + 2.2971978187561035, + 1.4515384435653687 + ], + [ + 2.355032205581665, + 1.397208571434021 + ], + [ + 2.395582675933838, + 1.3320810794830322 + ], + [ + 2.437032699584961, + 1.2740654945373535 + ], + [ + 2.4867217540740967, + 1.205893874168396 + ], + [ + 2.530080556869507, + 1.138196349143982 + ], + [ + 2.575047254562378, + 1.0777791738510132 + ], + [ + 2.618283271789551, + 1.0098692178726196 + ], + [ + 2.6614513397216797, + 0.9418083429336548 + ], + [ + 2.6999340057373047, + 0.8697630763053894 + ], + [ + 2.7298192977905273, + 0.8084556460380554 + ], + [ + 2.7694876194000244, + 0.7403592467308044 + ], + [ + 2.7972564697265625, + 0.6631399393081665 + ], + [ + 2.8277485370635986, + 0.5916881561279297 + ], + [ + 2.8645966053009033, + 0.5160143971443176 + ], + [ + 2.8924617767333984, + 0.43553632497787476 + ], + [ + 2.9207751750946045, + 0.3516179919242859 + ], + [ + 2.9441661834716797, + 0.2656373977661133 + ], + [ + 2.9708361625671387, + 0.18012608587741852 + ], + [ + 2.995119094848633, + 0.09640496969223022 + ], + [ + 3.017622232437134, + 0.002266376744955778 + ], + [ + 3.043760061264038, + -0.08903345465660095 + ], + [ + 3.0452544689178467, + -0.16451512277126312 + ], + [ + 3.0585885047912598, + -0.2522426247596741 + ], + [ + 3.0773463249206543, + -0.3484383523464203 + ], + [ + 3.086075782775879, + -0.447540819644928 + ], + [ + 3.080408811569214, + -0.5331095457077026 + ], + [ + 3.0869836807250977, + -0.6308740377426147 + ], + [ + 3.083768129348755, + -0.7343985438346863 + ], + [ + 3.0898289680480957, + -0.8323004245758057 + ], + [ + 3.0833899974823, + -0.9319173097610474 + ], + [ + 3.0904698371887207, + -1.0364118814468384 + ], + [ + 3.080061674118042, + -1.132294774055481 + ], + [ + 3.0699727535247803, + -1.2475190162658691 + ], + [ + 3.0850470066070557, + -1.3411211967468262 + ], + [ + 3.0926270484924316, + -1.4598098993301392 + ], + [ + 2.9818525314331055, + -1.5555393695831299 + ], + [ + 2.9591095447540283, + -1.6487717628479004 + ], + [ + 2.9116525650024414, + -1.7719738483428955 + ], + [ + 2.8920342922210693, + -1.8652126789093018 + ], + [ + 2.835754632949829, + -1.9713828563690186 + ], + [ + 2.804757833480835, + -2.1031105518341064 + ], + [ + 2.753603458404541, + -2.2083773612976074 + ], + [ + 2.689694881439209, + -2.318589925765991 + ], + [ + 2.638504981994629, + -2.4216058254241943 + ], + [ + 2.5509848594665527, + -2.5275609493255615 + ], + [ + 2.489881753921509, + -2.6566803455352783 + ], + [ + 2.380188465118408, + -2.7509565353393555 + ], + [ + 2.2542405128479004, + -2.8752071857452393 + ], + [ + 2.150723934173584, + -2.9281368255615234 + ], + [ + 2.0715248584747314, + -3.0241780281066895 + ], + [ + 1.9682269096374512, + -3.1185126304626465 + ], + [ + 1.8939309120178223, + -3.1630985736846924 + ], + [ + 1.7857937812805176, + -3.261897325515747 + ], + [ + 1.6356526613235474, + -3.3611268997192383 + ], + [ + 1.5163850784301758, + -3.4567573070526123 + ], + [ + 1.3732144832611084, + -3.5010128021240234 + ], + [ + 1.2104030847549438, + -3.5518276691436768 + ], + [ + 1.0430190563201904, + -3.5933995246887207 + ], + [ + 0.8822158575057983, + -3.623825788497925 + ], + [ + 0.6627976298332214, + -3.6547963619232178 + ], + [ + 0.44273343682289124, + -3.6631481647491455 + ], + [ + 0.42717331647872925, + -3.7039060592651367 + ], + [ + 0.32416683435440063, + -3.795259714126587 + ], + [ + 0.17221441864967346, + -3.8220949172973633 + ], + [ + 0.03688724339008331, + -3.916059732437134 + ], + [ + -0.11089131981134415, + -3.936948299407959 + ], + [ + -0.252693235874176, + -3.9844772815704346 + ], + [ + -0.43921273946762085, + -4.0095295906066895 + ], + [ + -0.6141329407691956, + -4.009274482727051 + ], + [ + -0.7696290612220764, + -3.970614194869995 + ], + [ + -0.931189775466919, + -3.9552981853485107 + ], + [ + -1.1234012842178345, + -3.8894717693328857 + ], + [ + -1.3107601404190063, + -3.815281391143799 + ], + [ + -1.4806294441223145, + -3.7320592403411865 + ], + [ + -3.1617636680603027, + -7.200752258300781 + ], + [ + -3.5687215328216553, + -7.0088019371032715 + ], + [ + -3.4965732097625732, + -7.553720474243164 + ], + [ + -3.828077554702759, + -7.578862190246582 + ], + [ + -4.215964317321777, + -7.5158257484436035 + ], + [ + -4.464430332183838, + -7.543081760406494 + ], + [ + -4.748383522033691, + -7.505837917327881 + ], + [ + -5.119718074798584, + -7.260599136352539 + ], + [ + -5.392154693603516, + -7.093082427978516 + ], + [ + -5.704444408416748, + -6.892176151275635 + ], + [ + -5.940671920776367, + -6.73836088180542 + ], + [ + -6.2074875831604, + -6.455677509307861 + ], + [ + -6.4956464767456055, + -6.174989700317383 + ], + [ + -6.733001232147217, + -5.923089027404785 + ], + [ + -6.919020175933838, + -5.556769371032715 + ], + [ + -7.1211748123168945, + -5.126523971557617 + ], + [ + -7.485099792480469, + -5.612582206726074 + ], + [ + -7.772981643676758, + -5.378384590148926 + ], + [ + -7.964595794677734, + -5.172052383422852 + ], + [ + -8.221421241760254, + -5.0016374588012695 + ], + [ + -8.366085052490234, + -4.667886257171631 + ], + [ + -8.53973388671875, + -4.368419647216797 + ], + [ + -8.660114288330078, + -4.072993755340576 + ], + [ + -8.829169273376465, + -3.807664632797241 + ], + [ + -8.966348648071289, + -3.554037570953369 + ], + [ + -9.068779945373535, + -3.2637033462524414 + ], + [ + -9.138891220092773, + -2.962503671646118 + ], + [ + -9.22436237335205, + -2.6824145317077637 + ], + [ + -9.287652015686035, + -2.415142774581909 + ], + [ + -9.32740592956543, + -2.1294572353363037 + ], + [ + -9.349847793579102, + -1.8417268991470337 + ], + [ + -9.568178176879883, + -1.583898901939392 + ], + [ + -9.549560546875, + -1.31840181350708 + ], + [ + -9.608781814575195, + -1.0010920763015747 + ], + [ + -9.653103828430176, + -0.7563896179199219 + ], + [ + -9.655699729919434, + -0.4968414604663849 + ], + [ + -9.631248474121094, + -0.22788356244564056 + ], + [ + -9.6608304977417, + 0.03178777918219566 + ], + [ + -9.656210899353027, + 0.2685079574584961 + ], + [ + -9.6510009765625, + 0.5217161178588867 + ], + [ + -9.626096725463867, + 0.7814257144927979 + ], + [ + -9.619415283203125, + 0.9793218374252319 + ], + [ + -9.602843284606934, + 1.2214114665985107 + ], + [ + -9.577664375305176, + 1.438049554824829 + ], + [ + -9.52717399597168, + 1.7186967134475708 + ], + [ + -9.467283248901367, + 1.9591264724731445 + ] + ], + "total_points": 1000 +} \ No newline at end of file diff --git a/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/short_metadata.json b/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/short_metadata.json new file mode 100644 index 0000000..50a607e --- /dev/null +++ b/vna_system/calibration/s11_start100_stop8800_points1000_bw1khz/SRGDFDFG/short_metadata.json @@ -0,0 +1,16 @@ +{ + "preset": { + "filename": "s11_start100_stop8800_points1000_bw1khz.bin", + "mode": "s11", + "start_freq": 100000000.0, + "stop_freq": 8800000000.0, + "points": 1000, + "bandwidth": 1000.0 + }, + "calibration_name": "SRGDFDFG", + "standard": "short", + "sweep_number": 224, + "sweep_timestamp": 1758806106.5021908, + "created_timestamp": "2025-09-25T16:15:09.916088", + "total_points": 1000 +} \ No newline at end of file diff --git a/vna_system/calibration/s21_start100_stop8800_points1000_bw1khz/bambambum/calibration_info.json b/vna_system/calibration/s21_start100_stop8800_points1000_bw1khz/bambambum/calibration_info.json new file mode 100644 index 0000000..da09a58 --- /dev/null +++ b/vna_system/calibration/s21_start100_stop8800_points1000_bw1khz/bambambum/calibration_info.json @@ -0,0 +1,16 @@ +{ + "preset": { + "filename": "s21_start100_stop8800_points1000_bw1khz.bin", + "mode": "s21", + "start_freq": 100000000.0, + "stop_freq": 8800000000.0, + "points": 1000, + "bandwidth": 1000.0 + }, + "calibration_name": "bambambum", + "standards": [ + "through" + ], + "created_timestamp": "2025-09-25T14:41:46.014320", + "is_complete": true +} \ No newline at end of file diff --git a/vna_system/calibration/s21_start100_stop8800_points1000_bw1khz/bambambum/through.json b/vna_system/calibration/s21_start100_stop8800_points1000_bw1khz/bambambum/through.json new file mode 100644 index 0000000..fe6dcf5 --- /dev/null +++ b/vna_system/calibration/s21_start100_stop8800_points1000_bw1khz/bambambum/through.json @@ -0,0 +1,4007 @@ +{ + "sweep_number": 50, + "timestamp": 1758800474.7658994, + "points": [ + [ + 0.00011530049960128963, + -3.646380719146691e-05 + ], + [ + 0.0001547646097606048, + 1.3271062925923616e-05 + ], + [ + 9.263205720344558e-05, + -2.477719317539595e-05 + ], + [ + 0.00011107375758001581, + 2.327580477867741e-05 + ], + [ + 8.545240416424349e-05, + 6.452835350501118e-06 + ], + [ + 0.00015776556392665952, + 0.00010731354996096343 + ], + [ + 0.0001602150296093896, + 9.17403885978274e-05 + ], + [ + 0.0002642860636115074, + 4.546686977846548e-05 + ], + [ + 7.91085694800131e-05, + -7.171036122599617e-05 + ], + [ + 4.505639662966132e-05, + 6.207797559909523e-05 + ], + [ + 6.701951497234404e-05, + -0.00015854122466407716 + ], + [ + 8.323958900291473e-05, + -3.169844421790913e-05 + ], + [ + 0.00020263764599803835, + 1.664467890805099e-05 + ], + [ + 2.953433431684971e-05, + -4.510977305471897e-06 + ], + [ + 3.887499769916758e-05, + -1.339894879492931e-05 + ], + [ + 0.00015488159260712564, + -5.412819155026227e-05 + ], + [ + -6.463602039730176e-05, + -5.9493289882084355e-05 + ], + [ + 0.000113986614451278, + -6.742681307514431e-06 + ], + [ + 0.00017253785335924476, + 1.4293553249444813e-05 + ], + [ + 5.319682531990111e-05, + -1.1635373084573075e-05 + ], + [ + 3.739305611816235e-05, + 0.00011188806820427999 + ], + [ + 5.39939574082382e-05, + 9.991689148591831e-05 + ], + [ + 0.00012238188355695456, + 0.00012132679694332182 + ], + [ + -8.157116099027917e-05, + -2.8022082915413193e-05 + ], + [ + 5.6301378208445385e-05, + 8.282306953333318e-05 + ], + [ + -5.605917249340564e-05, + 2.798883360810578e-05 + ], + [ + 2.8578091587405652e-05, + -5.657008659909479e-05 + ], + [ + 0.00010246937745250762, + 1.0899844710365869e-05 + ], + [ + 8.073562639765441e-05, + -5.41983827133663e-05 + ], + [ + 1.0439671314088628e-05, + 3.641795046860352e-05 + ], + [ + 7.047240796964616e-05, + 0.00012107361544622108 + ], + [ + 3.858578929794021e-05, + 7.677506073378026e-05 + ], + [ + 0.00023057725047692657, + 4.4042888475814834e-05 + ], + [ + 8.056355000007898e-05, + 1.0444839972478803e-05 + ], + [ + 0.00010464256774866953, + -3.210046634194441e-05 + ], + [ + 0.00012366415467113256, + 0.00011096092202933505 + ], + [ + 0.00030877735116519034, + -5.120248533785343e-05 + ], + [ + 5.2395964303286746e-05, + -0.00013780749577563256 + ], + [ + 7.942825322970748e-05, + 0.000163398843142204 + ], + [ + -5.425828476290917e-06, + -0.00013761773880105466 + ], + [ + 6.601081986445934e-05, + 1.7684556951280683e-05 + ], + [ + 0.0002076589153148234, + -7.526911940658465e-05 + ], + [ + 0.00011178779823239893, + -0.00019154345500282943 + ], + [ + 0.00014221442688722163, + 2.6556049306236673e-06 + ], + [ + 0.00011995705426670611, + 3.331583502586e-05 + ], + [ + 6.02799846092239e-05, + 0.0001587715814821422 + ], + [ + 0.00014022371033206582, + -4.52821514045354e-05 + ], + [ + -6.707726424792781e-05, + 0.0001234556402778253 + ], + [ + -2.0539739125524648e-05, + -1.003048600978218e-05 + ], + [ + 8.99730293895118e-05, + -3.872895467793569e-05 + ], + [ + 6.872956873849034e-05, + -9.829103510128334e-05 + ], + [ + 0.00020777733880095184, + 0.00012431851064320654 + ], + [ + 7.640158582944423e-05, + -0.00012750321184284985 + ], + [ + 0.0001242409780388698, + -8.134636300383136e-05 + ], + [ + 0.00021889095660299063, + -8.853993494994938e-05 + ], + [ + 0.0002522641443647444, + -5.046409114584094e-06 + ], + [ + 0.00010959948849631473, + -3.6941724829375744e-05 + ], + [ + 0.00011368638661224395, + -0.00011237056605750695 + ], + [ + 5.118840272189118e-05, + -0.00011314774747006595 + ], + [ + -3.116794323432259e-05, + -0.00023659651924390346 + ], + [ + 3.9078135159797966e-05, + -8.521152631146833e-05 + ], + [ + 9.341181430500001e-05, + -1.4175070646160748e-05 + ], + [ + 7.655985973542556e-05, + -6.260118243517354e-05 + ], + [ + -8.355620229849592e-05, + -0.0001345423806924373 + ], + [ + -1.699167478363961e-05, + -8.18747648736462e-05 + ], + [ + -7.097581601556158e-06, + -9.349693573312834e-05 + ], + [ + -0.00011157570406794548, + 7.546567940153182e-05 + ], + [ + 0.00011757006723200902, + 8.815102046355605e-05 + ], + [ + 4.288386116968468e-05, + -6.211375148268417e-05 + ], + [ + 0.00015164280193857849, + 0.00011065861326642334 + ], + [ + 0.00017845831462182105, + -6.242457311600447e-05 + ], + [ + -4.0081871702568606e-05, + 7.9968391219154e-05 + ], + [ + 0.00020363536896184087, + 0.00010864450450753793 + ], + [ + 9.412787767359987e-05, + 2.4890321583370678e-05 + ], + [ + 0.0001409673859598115, + -0.000168627651873976 + ], + [ + 0.0002470679464749992, + 4.5157226850278676e-05 + ], + [ + 0.00023180317657534033, + 3.497389116091654e-05 + ], + [ + 1.4714189092046581e-05, + -6.726715218974277e-05 + ], + [ + -4.945691398461349e-05, + -5.4924763389863074e-05 + ], + [ + 0.00018736059428192675, + 6.285002746153623e-05 + ], + [ + 0.0003565224469639361, + 0.00010498626943444833 + ], + [ + 0.0001742645399644971, + 0.0002002005639951676 + ], + [ + 0.00017189359641633928, + 7.729716890025884e-05 + ], + [ + 0.00023148072068579495, + -0.00011803050438174978 + ], + [ + 0.00011229751544306055, + 3.956279397243634e-05 + ], + [ + 0.00027033762307837605, + 8.722456550458446e-05 + ], + [ + 0.00012663287634495646, + -8.020726090762764e-05 + ], + [ + 0.00023095674987416714, + -7.550472219008952e-05 + ], + [ + 0.00012767792213708162, + 0.00015305615670513362 + ], + [ + 0.0002965192834381014, + 6.193531589815393e-05 + ], + [ + 0.00021775501954834908, + 3.548544555087574e-05 + ], + [ + 0.00012114409764762968, + -1.0841761650226545e-05 + ], + [ + 0.00024530437076464295, + 1.4345293493533973e-05 + ], + [ + 0.000253668607911095, + -7.272363291122019e-05 + ], + [ + 0.0002602151071187109, + -3.1481315090786666e-05 + ], + [ + 0.00017305508663412184, + -0.00023558063548989594 + ], + [ + 1.509689445811091e-05, + -3.079148882534355e-05 + ], + [ + 0.00021803467825520784, + -6.636665784753859e-05 + ], + [ + 0.0001543516991659999, + -9.043898899108171e-05 + ], + [ + 0.00015458140114787966, + 4.951149458065629e-05 + ], + [ + 8.351831638719887e-05, + -3.882740929839201e-05 + ], + [ + 0.00012741083628498018, + -0.00015264008834492415 + ], + [ + 0.0002785071555990726, + -0.00013357537682168186 + ], + [ + 0.00021723359532188624, + -3.847144398605451e-05 + ], + [ + 3.5343268791621085e-06, + -9.88701794994995e-05 + ], + [ + 0.0001632142229937017, + 4.524778705672361e-05 + ], + [ + 3.229347385058645e-06, + 2.4844708605087362e-05 + ], + [ + 9.396403038408607e-05, + -0.00014346373791340739 + ], + [ + 0.000175290580955334, + -6.981052592891501e-06 + ], + [ + 0.0001457369071431458, + -0.00017897720681503415 + ], + [ + 8.667835936648771e-05, + 8.403304673265666e-05 + ], + [ + 0.00012577864981722087, + -7.381828618235886e-05 + ], + [ + 0.00011330959387123585, + -0.0001598932285560295 + ], + [ + 2.639456397446338e-05, + -0.00011582735169213265 + ], + [ + 0.00028204743284732103, + -0.00010128250141860917 + ], + [ + 8.45097383717075e-06, + -0.00010852538980543613 + ], + [ + -0.0001335698034381494, + -0.00014033292245585471 + ], + [ + -4.3498068407643586e-05, + -0.00023364339722320437 + ], + [ + -2.0919278540532105e-05, + -0.00011110940249636769 + ], + [ + 0.0001884332305053249, + -0.0001067068733391352 + ], + [ + 8.374391472898424e-05, + -0.00031674097408540547 + ], + [ + -8.874786726664752e-05, + -8.052795601543039e-05 + ], + [ + -0.00017662654863670468, + 2.2809538222645642e-06 + ], + [ + 2.3111848349799402e-05, + -0.00018632065621204674 + ], + [ + 3.546266452758573e-05, + -0.00014216815179679543 + ], + [ + -2.508251327526523e-06, + -5.4075575462775305e-05 + ], + [ + -8.307464304380119e-05, + -3.118084714515135e-05 + ], + [ + -2.7725989639293402e-05, + -8.442046237178147e-05 + ], + [ + 1.1974833796557505e-05, + -5.85914749535732e-05 + ], + [ + 3.461955930106342e-05, + 8.591904770582914e-05 + ], + [ + 3.0898398108547553e-05, + -0.00018044738681055605 + ], + [ + -0.00011867046123370528, + -0.0001230950583703816 + ], + [ + -2.6044470359920524e-05, + 6.846272299299017e-05 + ], + [ + -0.00012131969560869038, + 2.707455678319093e-05 + ], + [ + -0.00015011972573120147, + -3.8763198972446844e-05 + ], + [ + -5.923795833950862e-05, + 1.570347376400605e-05 + ], + [ + -0.00012484152102842927, + 8.253673149738461e-05 + ], + [ + 7.351665408350527e-05, + -1.6273543224087916e-06 + ], + [ + -0.00018924102187156677, + 1.6522883015568368e-05 + ], + [ + -0.00012528114893939346, + 0.00011369791172910482 + ], + [ + -0.00013341690646484494, + 0.00010240166011499241 + ], + [ + -8.947827154770494e-05, + 0.00024297159689012915 + ], + [ + 9.24638588912785e-05, + 0.00013230196782387793 + ], + [ + -1.5816580344107933e-05, + 5.821174636366777e-05 + ], + [ + -9.470982331549749e-05, + 8.05352465249598e-05 + ], + [ + -5.24154347658623e-05, + 0.00020531495101749897 + ], + [ + -5.4335261665983126e-05, + 6.532522093039006e-05 + ], + [ + -0.00016835449787322432, + 2.5252777049900033e-05 + ], + [ + -1.930291546159424e-05, + 0.0001391541154589504 + ], + [ + 5.5970729590626433e-05, + 0.0002556243271101266 + ], + [ + 5.433190381154418e-05, + 0.0003195074968971312 + ], + [ + 2.2606749553233385e-05, + 0.0001360370370093733 + ], + [ + 1.2206664905534126e-05, + 0.00023173201770987362 + ], + [ + 0.0001417613384546712, + 0.0003469166695140302 + ], + [ + 0.00017462001414969563, + 0.0003287328581791371 + ], + [ + 5.316428723745048e-05, + 0.00043166466639377177 + ], + [ + 0.00027434530784375966, + 0.00025074571021832526 + ], + [ + 0.0001225324667757377, + 0.00021164611098356545 + ], + [ + 0.0001507347187725827, + 0.0003471960953902453 + ], + [ + 0.00017334529547952116, + 0.0003274360788054764 + ], + [ + 5.844273982802406e-05, + 0.00028924093930982053 + ], + [ + 8.072974742390215e-05, + 0.00044660980347543955 + ], + [ + 0.0001780718012014404, + 0.0004640214319806546 + ], + [ + 9.189418778987601e-05, + 0.0004889767151325941 + ], + [ + 0.00033719840575940907, + 0.0005435643834061921 + ], + [ + 0.0003147540846839547, + 0.0005612726672552526 + ], + [ + 0.0002694925351534039, + 0.0006687808781862259 + ], + [ + 0.00048392321332357824, + 0.0006170335109345615 + ], + [ + 0.0005186388734728098, + 0.0006197305046953261 + ], + [ + 0.0008242904441431165, + 0.0007194650243036449 + ], + [ + 0.0008652667747810483, + 0.0008114285301417112 + ], + [ + 0.0010292009683325887, + 0.0006062138127163053 + ], + [ + 0.0014150794595479965, + 0.0004557458159979433 + ], + [ + 0.0015624177176505327, + 0.00021975468553137034 + ], + [ + 0.0017702683107927442, + -0.0002415591588942334 + ], + [ + 0.0016638068482279778, + -0.0006321680848486722 + ], + [ + 0.0014114441582933068, + -0.0010435397271066904 + ], + [ + 0.0010857089655473828, + -0.0011651086388155818 + ], + [ + 0.0007056377362459898, + -0.0011386532569304109 + ], + [ + 0.0004612570919562131, + -0.0011735272128134966 + ], + [ + 0.0003479450533632189, + -0.0010133126052096486 + ], + [ + 0.00012721327948383987, + -0.0010257114190608263 + ], + [ + 0.00019056674500461668, + -0.0008295546867884696 + ], + [ + -4.8703917855164036e-05, + -0.0008259104215539992 + ], + [ + -9.180213965009898e-05, + -0.000753801956307143 + ], + [ + -1.9090150090050884e-05, + -0.0005654814885929227 + ], + [ + -0.0002226837386842817, + -0.0005469375173561275 + ], + [ + -0.00018615536100696772, + -0.0005543736042454839 + ], + [ + -0.00010507548722671345, + -0.0005314997397363186 + ], + [ + -0.00024025572929531336, + -0.0005440686363726854 + ], + [ + -0.00011270276445429772, + -0.0003774442011490464 + ], + [ + -0.00019747664919123054, + -0.0005275770672596991 + ], + [ + -9.04357511899434e-05, + -0.0002752280270215124 + ], + [ + -0.00024884569575078785, + -0.0001886969112092629 + ], + [ + -0.00023473045439459383, + -0.0002085089508909732 + ], + [ + -0.00026358856121078134, + -0.00021613620629068464 + ], + [ + -0.00011553957301657647, + -0.00013244515866972506 + ], + [ + -0.00011890615860465914, + -0.00017041021783370525 + ], + [ + -7.632752385688946e-05, + -0.00021375773940235376 + ], + [ + -0.00016474809672217816, + -9.794427751330659e-05 + ], + [ + 6.731947451044107e-06, + -9.324418351752684e-05 + ], + [ + 0.00017705689242575318, + -0.00020848153508268297 + ], + [ + 0.0002813788305502385, + -8.620354492450133e-05 + ], + [ + 0.0002434320776956156, + -0.0004743700847029686 + ], + [ + 0.00018576954607851803, + -0.0005814784090034664 + ], + [ + -6.707974534947425e-05, + -0.0006593560683541 + ], + [ + -0.00034505678922869265, + -0.0005284716025926173 + ], + [ + -0.0004481654323171824, + -0.0004911813302896917 + ], + [ + -0.0005259643658064306, + -0.00040889394585974514 + ], + [ + -0.00045516638783738017, + -0.0002692798734642565 + ], + [ + -0.0005364666576497257, + -0.000213407794944942 + ], + [ + -0.00043520060717128217, + 1.3447409401123878e-05 + ], + [ + -0.0004069422429893166, + 1.1993941370747052e-05 + ], + [ + -0.0005096862441860139, + 1.3433144886221271e-05 + ], + [ + -0.0006096059223636985, + 3.163456858601421e-05 + ], + [ + -0.0005536325625143945, + 0.00015425989113282412 + ], + [ + -0.0005291858105920255, + 7.437691238010302e-05 + ], + [ + -0.0005320590571500361, + 0.00012662712833844125 + ], + [ + -0.0005198814906179905, + 0.0003774277865886688 + ], + [ + -0.00033146710484288633, + 0.00021767219004686922 + ], + [ + -0.0003887157654389739, + 0.00022123446979094297 + ], + [ + -0.0004434663860592991, + 0.00026239335420541465 + ], + [ + -0.0002620837767608464, + 0.0003385671298019588 + ], + [ + -0.00028471433324739337, + 0.00023729715030640364 + ], + [ + -0.0002640082675497979, + 0.0003296942450106144 + ], + [ + -0.0002489913604222238, + 0.0003692628815770149 + ], + [ + -0.00027029585908167064, + 0.00047393934801220894 + ], + [ + -0.00013080686039756984, + 0.0004813353007193655 + ], + [ + -0.0001147630246123299, + 0.0005195637932047248 + ], + [ + -0.00023347564274445176, + 0.0005091914790682495 + ], + [ + -0.00015952478861436248, + 0.00048122668522410095 + ], + [ + -2.6359699404565617e-05, + 0.000529611308593303 + ], + [ + -2.6715793865150772e-05, + 0.0006889069336466491 + ], + [ + -3.979156099376269e-05, + 0.0005183117464184761 + ], + [ + 5.150394281372428e-05, + 0.0004850835248362273 + ], + [ + 5.575527757173404e-05, + 0.0006006287876516581 + ], + [ + 7.161161192925647e-05, + 0.0006172577268444002 + ], + [ + -2.9470627850969322e-05, + 0.0005255999276414514 + ], + [ + 4.5857857912778854e-05, + 0.0006040598964318633 + ], + [ + 8.568806515540928e-05, + 0.0006040566368028522 + ], + [ + 0.00010236026719212532, + 0.000523533730302006 + ], + [ + 0.00015470624202862382, + 0.0006434533861465752 + ], + [ + 0.0002547996991779655, + 0.0005899668904021382 + ], + [ + 0.00039091231883503497, + 0.0006604752270504832 + ], + [ + 0.00045621179742738605, + 0.0005950038903392851 + ], + [ + 0.00048538303235545754, + 0.0005672930274158716 + ], + [ + 0.0005508169415406883, + 0.0005541019490920007 + ], + [ + 0.0005139088607393205, + 0.0006252257153391838 + ], + [ + 0.0006021962617523968, + 0.0006011438090354204 + ], + [ + 0.0007869645487517118, + 0.0007335554691962898 + ], + [ + 0.000854598474688828, + 0.000613181502558291 + ], + [ + 0.0009294537012465298, + 0.0005846100393682718 + ], + [ + 0.0009855751413851976, + 0.0005680512986145914 + ], + [ + 0.0010812716791406274, + 0.0004892848082818091 + ], + [ + 0.00112615164835006, + 0.00040666363202035427 + ], + [ + 0.0011689496459439397, + 0.00026917277136817575 + ], + [ + 0.0011927677551284432, + 0.00017122161807492375 + ], + [ + 0.0012545579811558127, + 6.294490594882518e-05 + ], + [ + 0.0012788308085873723, + -5.606841659755446e-05 + ], + [ + 0.0012714636977761984, + -0.0002810666337609291 + ], + [ + 0.0013228199677541852, + -0.00047933170571923256 + ], + [ + 0.0012388979084789753, + -0.0005234646378085017 + ], + [ + 0.0012709565926343203, + -0.0006478445720858872 + ], + [ + 0.0012236725306138396, + -0.0008336812607012689 + ], + [ + 0.0011329107219353318, + -0.000988235231488943 + ], + [ + 0.0010321795707568526, + -0.0011385605903342366 + ], + [ + 0.0009362398996017873, + -0.001143771456554532 + ], + [ + 0.0006930490490049124, + -0.0013770060613751411 + ], + [ + 0.0005694470601156354, + -0.0013606708962470293 + ], + [ + 0.00035566824954003096, + -0.0012862615985795856 + ], + [ + 0.0002597278216853738, + -0.0013205643044784665 + ], + [ + 0.00021357530204113573, + -0.001293489709496498 + ], + [ + 0.000167580132256262, + -0.0013317925622686744 + ], + [ + 0.00011830482253571972, + -0.0013580986997112632 + ], + [ + -4.910390998702496e-05, + -0.001473579672165215 + ], + [ + -0.000241258749156259, + -0.0015073202084749937 + ], + [ + -0.000505536503624171, + -0.0014224014012143016 + ], + [ + -0.0006457361741922796, + -0.0013467384269461036 + ], + [ + -0.0008647317299619317, + -0.0011376242619007826 + ], + [ + -0.0009368619648739696, + -0.0010120858205482364 + ], + [ + -0.0010955878533422947, + -0.0008175665279850364 + ], + [ + -0.0010768539505079389, + -0.0005740101332776248 + ], + [ + -0.0011166938347741961, + -0.0005009974702261388 + ], + [ + -0.0010660281404852867, + -0.00042198027949780226 + ], + [ + -0.0011178463464602828, + -0.00018937040294986218 + ], + [ + -0.0010796671267598867, + -0.00010412775736767799 + ], + [ + -0.0010543247917667031, + 6.496027344837785e-05 + ], + [ + -0.0010352343088015914, + 0.0001512266753707081 + ], + [ + -0.0009446665062569082, + 0.0002555176615715027 + ], + [ + -0.0008784193196333945, + 0.0003427061310503632 + ], + [ + -0.0008644345216453075, + 0.0003610769344959408 + ], + [ + -0.0006938559818081558, + 0.0005825193948112428 + ], + [ + -0.0006280444795265794, + 0.0007403810741379857 + ], + [ + -0.0003684788243845105, + 0.0007465055678039789 + ], + [ + -0.00021401970298029482, + 0.0006840425776317716 + ], + [ + -0.00026152731152251363, + 0.0005489250761456788 + ], + [ + -0.00016053576837293804, + 0.0004627954913303256 + ], + [ + -0.00012718922516796738, + 0.000512791215442121 + ], + [ + -0.00018369051394984126, + 0.000553369231056422 + ], + [ + -2.939874138974119e-05, + 0.0004817867302335799 + ], + [ + -0.00014777261822018772, + 0.0005565339815802872 + ], + [ + -0.00015527669165749103, + 0.0005997820408083498 + ], + [ + -7.399629248538986e-05, + 0.000609048584010452 + ], + [ + -9.393566870130599e-05, + 0.0006632916047237813 + ], + [ + 1.6671474440954626e-05, + 0.0006827664328739047 + ], + [ + 5.93424956605304e-05, + 0.0006577587337233126 + ], + [ + 0.00010897206084337085, + 0.0007173236808739603 + ], + [ + 4.737056951853447e-05, + 0.0006196901667863131 + ], + [ + 0.00020818770281039178, + 0.0006930588860996068 + ], + [ + 0.0002999946882482618, + 0.00065512431319803 + ], + [ + 0.0003920901217497885, + 0.0005999291897751391 + ], + [ + 0.0003916056884918362, + 0.0005298159667290747 + ], + [ + 0.0005037125083617866, + 0.0005075567169114947 + ], + [ + 0.0005411443416960537, + 0.000501019589137286 + ], + [ + 0.0005362433730624616, + 0.00038792824489064515 + ], + [ + 0.0006095512071624398, + 0.0003390763304196298 + ], + [ + 0.0005978186964057386, + 0.00030593815608881414 + ], + [ + 0.0006689679576084018, + 0.00022965020616538823 + ], + [ + 0.0007021501660346985, + 0.00016942883667070419 + ], + [ + 0.0005487410817295313, + 9.621965728001669e-05 + ], + [ + 0.0006762350094504654, + -2.7420681362855248e-05 + ], + [ + 0.0006436765543185174, + -1.514297673566034e-05 + ], + [ + 0.0006248305435292423, + -0.00012997855083085597 + ], + [ + 0.0006683830870315433, + -0.00010545827535679564 + ], + [ + 0.0005566630279645324, + -0.00012731169408652931 + ], + [ + 0.0005099255358800292, + -0.00018129848467651755 + ], + [ + 0.0004934549797326326, + -0.00026379432529211044 + ], + [ + 0.0005960666458122432, + -0.00023521562980022281 + ], + [ + 0.0004687027249019593, + -0.00032199593260884285 + ], + [ + 0.00040995024028234184, + -0.00035161981941200793 + ], + [ + 0.0003221253282390535, + -0.0002561977016739547 + ], + [ + 0.00036716030444949865, + -0.0003957172157242894 + ], + [ + 0.00018920519505627453, + -0.00029108242597430944 + ], + [ + 0.00021363529958762228, + -0.00032903687679208815 + ], + [ + 0.00015267373237293214, + -0.00032445674878545105 + ], + [ + 0.00019766016339417547, + -0.00018410875054541975 + ], + [ + 8.23366644908674e-05, + -0.0002720456977840513 + ], + [ + 0.00011298662138869986, + -0.00033362829708494246 + ], + [ + 0.00011044393613701686, + -0.0002794598403852433 + ], + [ + 6.811692583141848e-05, + -0.00032806425588205457 + ], + [ + 5.59862055524718e-05, + -0.00014902447583153844 + ], + [ + -1.531274028820917e-05, + -0.00027057528495788574 + ], + [ + 9.746065916260704e-05, + -0.00018742651445791125 + ], + [ + 3.7948473618598655e-05, + -0.0002246617223136127 + ], + [ + 8.698776218807325e-05, + -0.0001522709644632414 + ], + [ + -3.941175600630231e-05, + -0.00011063311831094325 + ], + [ + -3.281071985838935e-05, + -0.00013639465032611042 + ], + [ + -6.47137057967484e-05, + -0.0001403467613272369 + ], + [ + 6.03104563197121e-05, + -3.135324368486181e-05 + ], + [ + -1.3939235941506922e-05, + 2.386859523539897e-05 + ], + [ + 1.7270805983571336e-05, + -6.953181582503021e-05 + ], + [ + 2.5251300030504353e-05, + -0.00013694260269403458 + ], + [ + 3.267208012402989e-05, + -3.829713386949152e-05 + ], + [ + -3.251232101320056e-07, + -0.00010014746658271179 + ], + [ + -1.62760479724966e-05, + 4.066645851708017e-05 + ], + [ + -5.179992149351165e-05, + -4.936126424581744e-05 + ], + [ + -1.660434099903796e-05, + 2.2316608010441996e-05 + ], + [ + 6.82564641465433e-05, + 6.861495785415173e-05 + ], + [ + 9.38836546993116e-06, + -4.432005880516954e-05 + ], + [ + -3.9863378333393484e-05, + 3.16164041578304e-05 + ], + [ + -1.057444023899734e-05, + -2.0253209243037418e-07 + ], + [ + 8.195433474611491e-05, + -1.1520035513967741e-05 + ], + [ + 0.00011095001536887139, + -1.059957139659673e-05 + ], + [ + 8.337744657183066e-05, + 1.5067332242324483e-05 + ], + [ + 0.00017606880282983184, + -5.604907073575305e-06 + ], + [ + 8.989534399006516e-05, + -7.171631750679808e-07 + ], + [ + 4.765128323924728e-05, + 4.413989154272713e-05 + ], + [ + 5.755214715463808e-06, + 4.9294758355244994e-05 + ], + [ + 0.00010074985766550526, + 7.105463737389073e-05 + ], + [ + 0.00014992499200161546, + 2.9478112992364913e-05 + ], + [ + 9.346340812044218e-05, + 5.0182647100882605e-05 + ], + [ + 7.420429028570652e-05, + 4.2477946408325806e-05 + ], + [ + 0.00012406971654854715, + 7.608429586980492e-05 + ], + [ + 0.0001226847234647721, + 9.487816714681685e-05 + ], + [ + 5.767882612417452e-05, + -1.7013957403833047e-05 + ], + [ + -2.355688229727093e-05, + -7.824073691153899e-05 + ], + [ + 6.133164424682036e-05, + 3.526428554323502e-05 + ], + [ + 6.259474321268499e-05, + -1.0072585610032547e-05 + ], + [ + 0.00011449033627286553, + -2.9021266527706757e-05 + ], + [ + 0.0001347274665022269, + -8.885367606126238e-06 + ], + [ + 0.00012137123121647164, + -8.569598139729351e-05 + ], + [ + 1.66738209372852e-05, + -8.697310840943828e-05 + ], + [ + 0.00010836692672455683, + -7.558638753835112e-05 + ], + [ + 0.00018751085735857487, + -3.050160921702627e-05 + ], + [ + 6.086621215217747e-05, + -7.810431270627305e-05 + ], + [ + 3.7773210351588205e-05, + -2.261154259031173e-05 + ], + [ + 0.00014447861758526415, + -7.990384619915858e-05 + ], + [ + 0.00015396956587210298, + 1.7868058421299793e-05 + ], + [ + 3.334482607897371e-05, + 6.223251693882048e-05 + ], + [ + 8.136503311106935e-05, + -2.966910324175842e-05 + ], + [ + 9.750761091709137e-05, + 9.648648301663343e-06 + ], + [ + 5.242007773631485e-06, + 1.6848732684593415e-06 + ], + [ + 3.022654345841147e-05, + 3.965796349802986e-05 + ], + [ + 0.00014042522525414824, + -5.529642294277437e-05 + ], + [ + 0.00015334939234890044, + 3.02395383187104e-05 + ], + [ + 8.827181591186672e-05, + 1.4103292414802127e-05 + ], + [ + 0.00012786526349373162, + 1.575983151269611e-05 + ], + [ + 3.085629577981308e-05, + 2.2254103896557353e-05 + ], + [ + 0.00010250949708279222, + 7.037012983346358e-05 + ], + [ + 0.00012112142576370388, + 3.888261198881082e-05 + ], + [ + 0.00010147179273189977, + 1.838143475652032e-06 + ], + [ + 0.00011561848077690229, + -7.861761332605965e-06 + ], + [ + 0.00013902390492148697, + -7.845462096156552e-05 + ], + [ + 0.0001607080630492419, + -7.142432878026739e-05 + ], + [ + 4.747317507280968e-05, + -3.56140335497912e-05 + ], + [ + 0.00013006258814129978, + -0.00010222307901130989 + ], + [ + 0.00015986671496648341, + -5.3726213081972674e-05 + ], + [ + 0.00010183953418163583, + -2.755099194473587e-05 + ], + [ + 0.00016701863205526024, + -5.6202730775112286e-05 + ], + [ + 0.00018564399215392768, + -0.0001029378836392425 + ], + [ + 0.0001399498723912984, + -6.696507625747472e-05 + ], + [ + 0.00017419214418623596, + 1.1945911865041126e-05 + ], + [ + 0.0001854930305853486, + -4.362500112620182e-05 + ], + [ + 9.467615018365905e-05, + 5.5310183597612195e-06 + ], + [ + 3.800391641561873e-05, + 6.231994484551251e-05 + ], + [ + 8.804297976894304e-05, + 1.0908977856161073e-05 + ], + [ + 8.508133032592013e-05, + 3.6624031054088846e-05 + ], + [ + 0.00011309607361909002, + 3.613677472458221e-05 + ], + [ + 0.0001394839637214318, + 1.5198705114016775e-05 + ], + [ + 0.00011522071872605011, + -1.5142897609621286e-05 + ], + [ + 0.00014747038949280977, + 2.922484782175161e-05 + ], + [ + 0.000141178781632334, + 4.9312682676827535e-05 + ], + [ + 0.00019254282233305275, + -4.1524759581079707e-05 + ], + [ + 0.00015310279559344053, + -1.639160473132506e-05 + ], + [ + 0.00014042538532521576, + -4.675748277804814e-05 + ], + [ + 7.724640454398468e-05, + -6.929063965799287e-05 + ], + [ + 8.24694216134958e-05, + 2.2166905182530172e-06 + ], + [ + 0.0001054295789799653, + -1.648426587053109e-05 + ], + [ + 7.587159052491188e-05, + -5.4988595366012305e-06 + ], + [ + 0.0001388622767990455, + 3.624122109613381e-05 + ], + [ + 9.167673124466091e-05, + -5.995632818667218e-05 + ], + [ + 0.0001151424803538248, + -6.630576535826549e-05 + ], + [ + 5.248317393125035e-05, + -7.435764564434066e-05 + ], + [ + 6.177969044074416e-05, + -7.963784446474165e-05 + ], + [ + 3.722184192156419e-05, + 2.3684016923652962e-05 + ], + [ + 5.7931942137656733e-05, + -8.239352609962225e-05 + ], + [ + 3.598071270971559e-05, + 3.6926336179021746e-05 + ], + [ + 5.790386785520241e-05, + -5.1690156396944076e-05 + ], + [ + 8.468055602861568e-05, + -7.448687028954737e-06 + ], + [ + 0.00013229917385615408, + 9.79340711637633e-06 + ], + [ + 6.043354733265005e-05, + -6.8558533712348435e-06 + ], + [ + 6.701536767650396e-05, + -4.440799602889456e-05 + ], + [ + 6.09125527262222e-05, + 6.017462965246523e-06 + ], + [ + -8.703228786544059e-07, + -5.038428207626566e-05 + ], + [ + 4.558325235848315e-05, + -1.2676126971200574e-05 + ], + [ + 6.635545287281275e-05, + 1.2571221304824576e-05 + ], + [ + 3.807308530667797e-05, + 1.281204185943352e-05 + ], + [ + 8.742984209675342e-05, + -5.3701380238635466e-05 + ], + [ + -1.9512828657752834e-05, + -1.5547553630312905e-05 + ], + [ + 0.00013862371270079166, + -1.5722072930657305e-05 + ], + [ + 7.309925422305241e-05, + -2.069264701276552e-05 + ], + [ + 7.788795483065769e-05, + 4.8664140194887295e-05 + ], + [ + -2.7421778213465586e-05, + 3.1682247936259955e-05 + ], + [ + 5.445412898552604e-05, + 6.806022429373115e-05 + ], + [ + -7.287721473403508e-06, + 2.4947401470853947e-05 + ], + [ + 5.570230769080808e-06, + 5.8510620874585584e-05 + ], + [ + 5.7361718063475564e-05, + 1.4183396160660777e-05 + ], + [ + 5.613458779407665e-05, + 4.184594945400022e-05 + ], + [ + -8.573761078878306e-06, + 2.1715517505072057e-05 + ], + [ + 6.721395038766786e-05, + 5.089958358439617e-05 + ], + [ + 3.053363980143331e-05, + 2.011881224461831e-05 + ], + [ + 5.651637547998689e-05, + 5.5682583479210734e-05 + ], + [ + 4.117904609302059e-05, + 1.9339211576152593e-05 + ], + [ + 0.00010340898734284565, + 4.985295527148992e-05 + ], + [ + 6.813739310018718e-05, + 4.478945265873335e-05 + ], + [ + 4.968335997546092e-05, + 0.00010435593139845878 + ], + [ + 7.24894052837044e-05, + 4.1106421122094616e-05 + ], + [ + 5.409263030742295e-05, + 5.213939948589541e-05 + ], + [ + 3.167701288475655e-05, + 7.572573667857796e-05 + ], + [ + 0.00010718471457948908, + 0.00013546708214562386 + ], + [ + 6.604434747714549e-05, + 0.00012977964070159942 + ], + [ + 6.150898843770847e-05, + 7.060065399855375e-05 + ], + [ + 0.00014067110896576196, + 0.00014074399950914085 + ], + [ + 0.00012152236740803346, + 0.00014919847308192402 + ], + [ + 0.0001699929853202775, + 0.00018860406999010593 + ], + [ + 0.00018172212003264576, + 0.00014113742508925498 + ], + [ + 0.0001811923284549266, + 0.00013552149175666273 + ], + [ + 0.00022097289911471307, + 0.00013165174459572881 + ], + [ + 0.00020332248823251575, + 0.00015280533989425749 + ], + [ + 0.000319731974741444, + 0.00013100914657115936 + ], + [ + 0.0003134380385745317, + 0.00019236774824094027 + ], + [ + 0.00029284224729053676, + 6.63962127873674e-05 + ], + [ + 0.00036063636071048677, + 7.179773092502728e-05 + ], + [ + 0.0003499164595268667, + 6.390141061274335e-05 + ], + [ + 0.00039556092815473676, + 4.310891745262779e-05 + ], + [ + 0.0003719819651450962, + 3.8387926906580105e-05 + ], + [ + 0.0004723378224298358, + -0.00011365430691512302 + ], + [ + 0.0004860356275457889, + -0.00010590978490654379 + ], + [ + 0.00040268912562169135, + -0.00014374047168530524 + ], + [ + 0.0005009335000067949, + -0.00026816598256118596 + ], + [ + 0.00046177496551536024, + -0.00027108279755339026 + ], + [ + 0.0004575960338115692, + -0.0004840891342610121 + ], + [ + 0.0005042399279773235, + -0.0006457368726842105 + ], + [ + 0.0004627674934454262, + -0.0006337413797155023 + ], + [ + 0.00028662433032877743, + -1.90680225387041e-06 + ], + [ + 0.00030454524676315486, + -1.9111676010652445e-05 + ], + [ + 0.0003421177971176803, + -5.2830378990620375e-05 + ], + [ + 0.0003017013950739056, + -0.00012347834126558155 + ], + [ + 0.0002539001579862088, + -0.00014035908679943532 + ], + [ + 0.00021081608429085463, + -0.00018194100994151086 + ], + [ + 0.0001777371799107641, + -0.00019166705897077918 + ], + [ + 0.0001242455473402515, + -0.0001959251967491582 + ], + [ + 0.00012219586642459035, + -0.0001647164608584717 + ], + [ + 4.676024036598392e-05, + -0.00018546651699580252 + ], + [ + 2.233386112493463e-05, + -0.00018155999714508653 + ], + [ + -1.1564942724362481e-05, + -0.0001359039597446099 + ], + [ + 2.4983326511573978e-05, + -3.528924207785167e-05 + ], + [ + -1.0752582966233604e-06, + -2.999546268256381e-05 + ], + [ + 3.576920062187128e-05, + -1.825657454901375e-05 + ], + [ + 7.761972483422142e-06, + 7.05993841165764e-07 + ], + [ + -6.719344582961639e-06, + 2.5007613658090122e-05 + ], + [ + -2.4045742975431494e-05, + 8.35513710626401e-05 + ], + [ + 7.096929766703397e-05, + 9.931833483278751e-05 + ], + [ + 8.403114770771936e-05, + 0.00017586724425200373 + ], + [ + 0.00012270234583411366, + 0.00015530220116488636 + ], + [ + 0.00014409951108973473, + 0.00012754516501445323 + ], + [ + 0.00016537582268938422, + 0.00012710242299363017 + ], + [ + 0.00022311275824904442, + 0.00014245050260797143 + ], + [ + 0.0002087163447868079, + 0.0001131083263317123 + ], + [ + 0.00021309246949385852, + 0.00012498752039391547 + ], + [ + 0.00025084023945964873, + 5.299424447002821e-05 + ], + [ + 0.0002608357463032007, + 2.7135462005389854e-05 + ], + [ + 0.0002763443626463413, + -2.8653032131842338e-05 + ], + [ + 0.00029611808713525534, + 3.55801239493303e-05 + ], + [ + 0.00030846084700897336, + 1.3655275324708782e-05 + ], + [ + 0.0003166685637552291, + 1.60012914420804e-05 + ], + [ + 0.0002153668610844761, + 2.3139011318562552e-05 + ], + [ + 0.0003025938640348613, + -3.0828559829387814e-06 + ], + [ + 0.0003358719404786825, + -2.496509114280343e-05 + ], + [ + 0.0003637029731180519, + 1.405021976097487e-06 + ], + [ + 0.0004018870531581342, + 1.5212600374070462e-05 + ], + [ + 0.0004636181110981852, + -5.0011098210234195e-05 + ], + [ + 0.0004385429492685944, + -0.00013543908426072448 + ], + [ + 0.0004323543980717659, + -0.00016160232189577073 + ], + [ + 0.0004019680491182953, + -0.0002750568091869354 + ], + [ + 0.0004719514399766922, + -0.00027932028751820326 + ], + [ + 0.0004686187021434307, + -0.0003434975806158036 + ], + [ + 0.00036839526728726923, + -0.00039121517329476774 + ], + [ + 0.00038924504769966006, + -0.00041312092798762023 + ], + [ + 0.0003309195162728429, + -0.00047630094923079014 + ], + [ + 0.0002666111395228654, + -0.00048062633140943944 + ], + [ + 0.0002154722751583904, + -0.0005181439919397235 + ], + [ + 0.00018532403919380158, + -0.0005790647119283676 + ], + [ + 0.00010379481682321057, + -0.0005707485834136605 + ], + [ + 8.058117236942053e-05, + -0.0006319577805697918 + ], + [ + 2.8907790692755952e-05, + -0.0005854147020727396 + ], + [ + -6.427567132050171e-05, + -0.0005376053159125149 + ], + [ + -6.421432044589892e-05, + -0.0005096140666864812 + ], + [ + -0.00010459296026965603, + -0.0005729905678890646 + ], + [ + -0.0001612132036825642, + -0.0005682211485691369 + ], + [ + -0.00013797471183352172, + -0.00048077767132781446 + ], + [ + -0.000209187637665309, + -0.0004137754440307617 + ], + [ + -0.0002073528157779947, + -0.0005211715470068157 + ], + [ + -0.00020200060680508614, + -0.0005895804497413337 + ], + [ + -0.00031371155637316406, + -0.0005497562815435231 + ], + [ + -0.00030186629737727344, + -0.0005346519174054265 + ], + [ + -0.0002727466926444322, + -0.0005638363072648644 + ], + [ + -0.00035572124761529267, + -0.0005300894845277071 + ], + [ + -0.0003876102564390749, + -0.0005431145546026528 + ], + [ + -0.00044469957356341183, + -0.0006252997554838657 + ], + [ + -0.0005636935238726437, + -0.0006069358787499368 + ], + [ + -0.0007311307126656175, + -0.0005002734251320362 + ], + [ + -0.0008275651489384472, + -0.0006015338003635406 + ], + [ + -0.0008904770365916193, + -0.0005547030013985932 + ], + [ + -0.0009559242171235383, + -0.00041976821376010776 + ], + [ + -0.0010875267907977104, + -0.00038394128205254674 + ], + [ + -0.0011636452982202172, + -0.0002814203326124698 + ], + [ + -0.001283421297557652, + -0.00018143179477192461 + ], + [ + -0.0013424284989014268, + -0.00011655214620986953 + ], + [ + -0.001432287972420454, + 0.00013305118773132563 + ], + [ + -0.0014072058256715536, + 0.00012943697220180184 + ], + [ + -0.001567431609146297, + 0.0003342333366163075 + ], + [ + -0.0016721317078918219, + 0.0005040077958256006 + ], + [ + -0.001720612752251327, + 0.0006403350853361189 + ], + [ + -0.0016977599589154124, + 0.0008992531802505255 + ], + [ + -0.001731579890474677, + 0.0010714279487729073 + ], + [ + -0.001754898577928543, + 0.0013992756139487028 + ], + [ + -0.0015821709530428052, + 0.001742251100949943 + ], + [ + -0.0014265980571508408, + 0.001982392743229866 + ], + [ + -0.0012310458114370704, + 0.0022947785910218954 + ], + [ + -0.001056540641002357, + 0.0024502906017005444 + ], + [ + -0.0007174683851189911, + 0.0026621785946190357 + ], + [ + -0.0004795708227902651, + 0.002672571921721101 + ], + [ + -0.0002717252355068922, + 0.0027686329558491707 + ], + [ + -1.3133144705079758e-07, + 0.002929314272478223 + ], + [ + 0.00025021348847076297, + 0.002974224742501974 + ], + [ + 0.0005334011511877179, + 0.0031565744429826736 + ], + [ + 0.0007464135414920747, + 0.003299398347735405 + ], + [ + 0.0012000263668596745, + 0.0033474816009402275 + ], + [ + 0.0016840293537825346, + 0.0033533149398863316 + ], + [ + 0.0020424623508006334, + 0.0032274513505399227 + ], + [ + 0.0024406544398516417, + 0.0030743652023375034 + ], + [ + 0.0028979210183024406, + 0.0030444359872490168 + ], + [ + 0.003363208845257759, + 0.002844679169356823 + ], + [ + 0.0038125473074615, + 0.0024983214680105448 + ], + [ + 0.004250954370945692, + 0.0020760942716151476 + ], + [ + 0.0046110600233078, + 0.0016937474720180035 + ], + [ + 0.004875716287642717, + 0.0011554440716281533 + ], + [ + 0.005152030847966671, + 0.0005268891691230237 + ], + [ + 0.005369127262383699, + -4.924636959913187e-05 + ], + [ + 0.005527344532310963, + -0.0007594573544338346 + ], + [ + 0.0056267790496349335, + -0.0013599272351711988 + ], + [ + 0.005568897817283869, + -0.002194231143221259 + ], + [ + 0.00535617396235466, + -0.002960532670840621 + ], + [ + 0.0049422914162278175, + -0.0037353846710175276 + ], + [ + 0.004450547508895397, + -0.004355332348495722 + ], + [ + 0.003921043127775192, + -0.005007309839129448 + ], + [ + 0.0033193002454936504, + -0.005481666419655085 + ], + [ + 0.0024664851371198893, + -0.0059072417207062244 + ], + [ + 0.0018313314067199826, + -0.006342379376292229 + ], + [ + 0.0009014217648655176, + -0.006351500283926725 + ], + [ + 0.00016503028746228665, + -0.006455926690250635 + ], + [ + -0.0006953343981876969, + -0.006371926516294479 + ], + [ + -0.0015099381562322378, + -0.006221710704267025 + ], + [ + -0.002199425594881177, + -0.006033947691321373 + ], + [ + -0.0028915656730532646, + -0.005694058258086443 + ], + [ + -0.0034954920411109924, + -0.005141569767147303 + ], + [ + -0.004043262917548418, + -0.004632364492863417 + ], + [ + -0.004531036131083965, + -0.004052793141454458 + ], + [ + -0.004871174693107605, + -0.003405197523534298 + ], + [ + -0.005153139587491751, + -0.002770850667729974 + ], + [ + -0.005412174388766289, + -0.0021658947225660086 + ], + [ + -0.005502344109117985, + -0.0015664884122088552 + ], + [ + -0.00566920917481184, + -0.0009738137596286833 + ], + [ + -0.005614024121314287, + -0.00019035511650145054 + ], + [ + -0.005526029504835606, + 0.00036499174893833697 + ], + [ + -0.005362239666283131, + 0.0009286891436204314 + ], + [ + -0.005185924004763365, + 0.001564373029395938 + ], + [ + -0.00493655214086175, + 0.0020667575299739838 + ], + [ + -0.004500051494687796, + 0.0025835251435637474 + ], + [ + -0.00413368409499526, + 0.002958666766062379 + ], + [ + -0.0036826469004154205, + 0.0034298808313906193 + ], + [ + -0.0031737934332340956, + 0.0035719876177608967 + ], + [ + -0.0027505187317728996, + 0.0037968019023537636 + ], + [ + -0.0022788397036492825, + 0.003968153148889542 + ], + [ + -0.0019234416540712118, + 0.004053422249853611 + ], + [ + -0.0014926772564649582, + 0.0042281667701900005 + ], + [ + -0.0010921360226348042, + 0.004221515264362097 + ], + [ + -0.0006919415318407118, + 0.004230254329741001 + ], + [ + -0.00030746226548217237, + 0.004196871537715197 + ], + [ + 4.742267992696725e-05, + 0.004305101931095123 + ], + [ + 0.00044075155165046453, + 0.004339936189353466 + ], + [ + 0.0008886155555956066, + 0.004220751114189625 + ], + [ + 0.0012931018136441708, + 0.004008661024272442 + ], + [ + 0.0017801228677853942, + 0.003859599120914936 + ], + [ + 0.002204119460657239, + 0.0036667846143245697 + ], + [ + 0.002534181112423539, + 0.0034529876429587603 + ], + [ + 0.002982158213853836, + 0.0031030760146677494 + ], + [ + 0.003204381326213479, + 0.0026998782996088266 + ], + [ + 0.0034985782112926245, + 0.0023150681518018246 + ], + [ + 0.0036087397020310163, + 0.0018406417220830917 + ], + [ + 0.003736075945198536, + 0.001370020559988916 + ], + [ + 0.003881625598296523, + 0.0009743761620484293 + ], + [ + 0.003879844443872571, + 0.000514528714120388 + ], + [ + 0.0038233951199799776, + 6.181152275530621e-05 + ], + [ + 0.003661785740405321, + -0.00035873008891940117 + ], + [ + 0.0035923654213547707, + -0.0008448782609775662 + ], + [ + 0.0033961099106818438, + -0.00103192706592381 + ], + [ + 0.0031824856996536255, + -0.0012866719625890255 + ], + [ + 0.0029754466377198696, + -0.001683895941823721 + ], + [ + 0.0027190311811864376, + -0.002010615775361657 + ], + [ + 0.0024862331338226795, + -0.0022396519780158997 + ], + [ + 0.0020728756207972765, + -0.002418370684608817 + ], + [ + 0.0016947572585195303, + -0.0025763947051018476 + ], + [ + 0.0012900506844744086, + -0.0027082546148449183 + ], + [ + 0.0009370669140480459, + -0.002632505027577281 + ], + [ + 0.0005042011034674942, + -0.0026034200564026833 + ], + [ + 0.00021046653273515403, + -0.002487980527803302 + ], + [ + -0.00014165358152240515, + -0.0023027483839541674 + ], + [ + -0.00035576222580857575, + -0.002035568468272686 + ], + [ + -0.0006159085896797478, + -0.0018630496924743056 + ], + [ + -0.0007071301806718111, + -0.0015120262978598475 + ], + [ + -0.0007641097763553262, + -0.0012090231757611036 + ], + [ + -0.0007059669587761164, + -0.0009808744071051478 + ], + [ + -0.0006589337135665119, + -0.0007310816436074674 + ], + [ + -0.0004351520037744194, + -0.0005833892500959337 + ], + [ + -0.0002488021564204246, + -0.00043030965025536716 + ], + [ + -9.863742161542177e-05, + -0.000516578322276473 + ], + [ + 8.18302360130474e-05, + -0.00045096955727785826 + ], + [ + 0.0002443165867589414, + -0.0005034085479564965 + ], + [ + 0.000359629571903497, + -0.0006325927679426968 + ], + [ + 0.0004884273512288928, + -0.0007248519686982036 + ], + [ + 0.0004150996101088822, + -0.0008944777655415237 + ], + [ + 0.00041216303361579776, + -0.001006535952910781 + ], + [ + 0.0004912072326987982, + -0.0013879712205380201 + ], + [ + 0.0003993469581473619, + -0.0014757810859009624 + ], + [ + 0.00025947988615371287, + -0.0016049359692260623 + ], + [ + 6.736376963090152e-05, + -0.0016925119562074542 + ], + [ + -0.00016880346811376512, + -0.0017351574497297406 + ], + [ + -0.00028569131973199546, + -0.0017058715457096696 + ], + [ + -0.0005201714811846614, + -0.0017065800493583083 + ], + [ + -0.0006977381417527795, + -0.001517267432063818 + ], + [ + -0.0009965798817574978, + -0.0013612149050459266 + ], + [ + -0.0010607762960717082, + -0.001161788241006434 + ], + [ + -0.0011547842295840383, + -0.0008807272533886135 + ], + [ + -0.001466301386244595, + -0.0008188107749447227 + ], + [ + -0.0015436348039656878, + -0.0005799944628961384 + ], + [ + -0.0015133272390812635, + -0.00018731862655840814 + ], + [ + -0.0015263463137671351, + 0.00015786562289576977 + ], + [ + -0.0012021625880151987, + 0.0004399884201120585 + ], + [ + -0.0010685777524486184, + 0.0008206398342736065 + ], + [ + -0.0008704621577635407, + 0.0011772210709750652 + ], + [ + -0.0005692276172339916, + 0.0015355543000623584 + ], + [ + -0.0003069634549319744, + 0.0018457019468769431 + ], + [ + 0.00015590197290293872, + 0.001824130187742412 + ], + [ + -0.0001228966866619885, + -0.00019190204329788685 + ], + [ + -0.0006531634135171771, + -0.0018301213858649135 + ], + [ + -0.0012375119840726256, + -0.0029094384517520666 + ], + [ + -0.0017487980658188462, + -0.0033185905776917934 + ], + [ + -0.002246473915874958, + -0.0032699902076274157 + ], + [ + -0.002831117482855916, + -0.003065943019464612 + ], + [ + -0.0033100878354161978, + -0.002698566997423768 + ], + [ + -0.003407267155125737, + -0.002154268091544509 + ], + [ + -0.0033814129419624805, + -0.0016892596613615751 + ], + [ + -0.0032977573573589325, + -0.001157331746071577 + ], + [ + -0.0031159198842942715, + -0.0008537080138921738 + ], + [ + -0.003191532101482153, + -0.00044633884681388736 + ], + [ + -0.0032275437843054533, + -0.0001462185027776286 + ], + [ + -0.003012145636603236, + 0.0002604536712169647 + ], + [ + -0.0028381317388266325, + 0.0003776380908675492 + ], + [ + -0.0022613995242863894, + 0.0005238885059952736 + ], + [ + -0.0014560137642547488, + 0.00047036170144565403 + ], + [ + -0.00086206168634817, + 0.0002817233616951853 + ], + [ + -0.0006032038363628089, + 0.00021159778407309204 + ], + [ + -0.00011340891796862707, + -5.3295600082492456e-05 + ], + [ + 0.00016124517424032092, + -0.0002651084796525538 + ], + [ + -4.5957862312207e-05, + -0.0003323914424981922 + ], + [ + -0.00022391343372873962, + -0.0003602452343329787 + ], + [ + -0.00013345830666366965, + -0.0002926751913037151 + ], + [ + -0.0002729710831772536, + -0.00012529313971754164 + ], + [ + -0.00043890750384889543, + -4.015295780845918e-05 + ], + [ + -0.0005086606834083796, + 0.0002020557876676321 + ], + [ + -0.0007041674689389765, + 0.0005602953024208546 + ], + [ + -0.0006086999201215804, + 0.0008286058437079191 + ], + [ + -0.0007030234555713832, + 0.001030842657200992 + ], + [ + -0.0006760492105968297, + 0.0014989720657467842 + ], + [ + -0.00036269836709834635, + 0.0018198189791291952 + ], + [ + -0.00032333645503968, + 0.0021750524174422026 + ], + [ + -9.333108027931303e-05, + 0.0023572880309075117 + ], + [ + 0.0003160211490467191, + 0.0024389068130403757 + ], + [ + 0.00018318541697226465, + 0.002301475265994668 + ], + [ + 0.00041946209967136383, + 0.002359693171456456 + ], + [ + 0.0009064091136679053, + 0.0022039671894162893 + ], + [ + 0.0011411780724301934, + 0.00206999434158206 + ], + [ + 0.0013993309112265706, + 0.0018565738573670387 + ], + [ + 0.0015599073376506567, + 0.0016837783623486757 + ], + [ + 0.001594425062648952, + 0.0012232037261128426 + ], + [ + 0.0016965734539553523, + 0.0011039305245503783 + ], + [ + 0.0018039975548163056, + 0.0005264365463517606 + ], + [ + 0.0015977645525708795, + 0.00028628698782995343 + ], + [ + 0.0014952659839764237, + -0.0001961258822120726 + ], + [ + 0.0011417244095355272, + -0.0005251590046100318 + ], + [ + 0.0007416370790451765, + -0.0006010454380884767 + ], + [ + 0.0008490909240208566, + -0.0005089117330498993 + ], + [ + 0.0006121544283814728, + -0.0003955411375500262 + ], + [ + 0.0003290566382929683, + -0.0005107056931592524 + ], + [ + -7.652054591744673e-06, + -0.0003248360881116241 + ], + [ + -0.00029056394123472273, + -0.00031828609644435346 + ], + [ + -0.0006744918064214289, + -0.00024974107509478927 + ], + [ + -0.0009550356771796942, + 9.840988786891103e-05 + ], + [ + -0.0010883377399295568, + 0.00031872879480943084 + ], + [ + -0.0012434649979695678, + 0.000525184441357851 + ], + [ + -0.001327025005593896, + 0.0008552265935577452 + ], + [ + -0.0012681226944550872, + 0.00100162613671273 + ], + [ + -0.0010116997873410583, + 0.0014383252710103989 + ], + [ + -0.0002676947333384305, + 0.000906943459995091 + ], + [ + -0.000262223940808326, + 0.0009606724488548934 + ], + [ + -0.00020758266327902675, + 0.0009924713522195816 + ], + [ + 3.639547503553331e-05, + 0.0009198096813634038 + ], + [ + 0.00014087957970332354, + 0.0010359972948208451 + ], + [ + 0.00025531480787321925, + 0.0011834887554869056 + ], + [ + 0.0004969460424035788, + 0.0010802416363731027 + ], + [ + 0.0005574465030804276, + 0.0011028971057385206 + ], + [ + 0.0007809874368831515, + 0.0012242062948644161 + ], + [ + 0.0009507303475402296, + 0.001025813864544034 + ], + [ + 0.0011338663753122091, + 0.0008358638151548803 + ], + [ + 0.0011173631064593792, + 0.0008234403212554753 + ], + [ + 0.001420484040863812, + 0.0005145404720678926 + ], + [ + 0.0011715302243828773, + -0.0003230587753932923 + ], + [ + 0.0011529120383784175, + -0.00030502749723382294 + ], + [ + 0.0011868983274325728, + -0.0005829521105624735 + ], + [ + 0.0011103765573352575, + -0.0009321459219790995 + ], + [ + 0.0011787881376221776, + -0.0010479441843926907 + ], + [ + 0.0011224375339224935, + -0.001426337636075914 + ], + [ + 0.0009603378712199628, + -0.001681984169408679 + ], + [ + 0.0008374579483643174, + -0.0021777243819087744 + ], + [ + 0.000288566283416003, + -0.0028178521897643805 + ], + [ + -0.00019930850248783827, + -0.0029444177635014057 + ], + [ + -0.0011246999492868781, + -0.003018423682078719 + ], + [ + -0.0019383453764021397, + -0.0025894027203321457 + ], + [ + -0.0024563537444919348, + -0.0019084711093455553 + ], + [ + -0.0027788644656538963, + -0.0013454993022605777 + ], + [ + -0.0014947355957701802, + -0.0001219289333675988 + ], + [ + -0.0011841484811156988, + 0.0001096077321562916 + ], + [ + -0.0009568059467710555, + 8.207877544919029e-05 + ], + [ + -0.0008069266914390028, + -0.0001539430086268112 + ], + [ + -0.0008208288927562535, + -0.00015092593093868345 + ], + [ + -0.0008348810370080173, + -0.00024482139269821346 + ], + [ + -0.0010183034464716911, + -0.00027860834961757064 + ], + [ + -0.0009855502285063267, + -0.00026855256874114275 + ], + [ + -0.0012584890937432647, + -0.00014391027798410505 + ], + [ + -0.0013039478799328208, + 6.126395601313561e-05 + ], + [ + -0.0013860112521797419, + 0.0001767454668879509 + ], + [ + -0.0015753115294501185, + 0.00028656015638262033 + ], + [ + -0.0017558954423293471, + 0.00023889947624411434 + ], + [ + -0.0017644587205722928, + 0.0005148178897798061 + ], + [ + -0.0016515195602551103, + 0.00029904942493885756 + ], + [ + -0.0016849254025146365, + 0.0003558616735972464 + ], + [ + -0.0016616820357739925, + 0.0005842125974595547 + ], + [ + -0.0018813112983480096, + 0.0008099281694740057 + ], + [ + -0.001989802112802863, + 0.000976811395958066 + ], + [ + -0.002122934442013502, + 0.0014173361705616117 + ], + [ + -0.0021649328991770744, + 0.0016961190849542618 + ], + [ + -0.0021361438557505608, + 0.0021034919191151857 + ], + [ + -0.0021578026935458183, + 0.0025610916782170534 + ], + [ + -0.0020549865439534187, + 0.0033173596020787954 + ], + [ + -0.0017680260352790356, + 0.0038842125795781612 + ], + [ + -0.0013716709800064564, + 0.0046292077749967575 + ], + [ + -0.0005029086023569107, + 0.005155065096914768 + ], + [ + 0.00045500046689994633, + 0.005736964289098978 + ], + [ + 0.0013454307336360216, + 0.005701947957277298 + ], + [ + 0.0025502091739326715, + 0.005694045685231686 + ], + [ + 0.003646074328571558, + 0.005261705722659826 + ], + [ + 0.004745731130242348, + 0.0044685541652143 + ], + [ + 0.005372700747102499, + 0.003537070704624057 + ], + [ + 0.005995138548314571, + 0.002577391220256686 + ], + [ + 0.006121416110545397, + 0.0014807075494900346 + ], + [ + 0.005986968986690044, + 0.0004454606678336859 + ], + [ + 0.005732550285756588, + -0.000511878402903676 + ], + [ + 0.00543525256216526, + -0.001323349424637854 + ], + [ + 0.004944028332829475, + -0.0018570158863440156 + ], + [ + 0.004517220426350832, + -0.0024148309603333473 + ], + [ + 0.004024025052785873, + -0.0029137202072888613 + ], + [ + 0.0034751819912344217, + -0.0031573951710015535 + ], + [ + 0.0030113463290035725, + -0.0034185685217380524 + ], + [ + 0.0022511265706270933, + -0.0033668463584035635 + ], + [ + 0.0016678101383149624, + -0.003506302135065198 + ], + [ + 0.001307767117395997, + -0.003552040783688426 + ], + [ + 0.0007208664319477975, + -0.003659888170659542 + ], + [ + 0.0003613158187363297, + -0.0035573544446378946 + ], + [ + -0.00011986550089204684, + -0.003345959587022662 + ], + [ + -0.0005137895932421088, + -0.0030765333212912083 + ], + [ + -0.0008103484869934618, + -0.0030175538267940283 + ], + [ + -0.001011849963106215, + -0.002712815534323454 + ], + [ + -0.0014460805105045438, + -0.002305984031409025 + ], + [ + -0.0015727415448054671, + -0.0020340806804597378 + ], + [ + -0.0016723884036764503, + -0.0016466756351292133 + ], + [ + -0.0017286984948441386, + -0.00136860110796988 + ], + [ + -0.0017989694606512785, + -0.0011287853121757507 + ], + [ + -0.001734469668008387, + -0.000835832383017987 + ], + [ + -0.0016105783870443702, + -0.0004979585064575076 + ], + [ + -0.001498743542470038, + -0.0002969675697386265 + ], + [ + -0.0013381985481828451, + -9.721787500893697e-05 + ], + [ + -0.0013639607932418585, + 9.035745461005718e-05 + ], + [ + -0.001249913708306849, + 0.00020961173868272454 + ], + [ + -0.0009967738296836615, + 0.00038923919782973826 + ], + [ + -0.0008952004136517644, + 0.00035610253689810634 + ], + [ + -0.0008429278386756778, + 0.0004332589451223612 + ], + [ + -0.0006760049727745354, + 0.00042631730320863426 + ], + [ + -0.00047092619934119284, + 0.000550663738977164 + ], + [ + -0.0004366882494650781, + 0.00046092376578599215 + ], + [ + -0.0004004412912763655, + 0.0004943646490573883 + ], + [ + -0.00024359925009775907, + 0.0005222581094130874 + ], + [ + -0.0002129172789864242, + 0.00046470618690364063 + ], + [ + -9.998010500567034e-05, + 0.0004051532887388021 + ], + [ + -0.00021373177878558636, + 0.0003795860684476793 + ], + [ + -0.00015298821381293237, + 0.0004434835573192686 + ], + [ + -0.00020355767628643662, + 0.00035099787055514753 + ], + [ + -6.787067832192406e-05, + 0.00038139152457006276 + ], + [ + -4.368196096038446e-05, + 0.0002784410899039358 + ], + [ + -5.0729278882499784e-05, + 0.00033073322265408933 + ], + [ + -1.8950631783809513e-05, + 0.00036135080154053867 + ], + [ + -4.60226547147613e-05, + 0.0002550317149143666 + ], + [ + -4.9753443818190135e-06, + 0.0002943126601167023 + ], + [ + 3.667740020318888e-05, + 0.00033814861671999097 + ], + [ + 3.0372415494639426e-05, + 0.00019494850130286068 + ], + [ + 5.7489360187901184e-05, + 0.0002018155064433813 + ], + [ + 6.616293831029907e-05, + 0.00024862951249815524 + ], + [ + 3.569247928680852e-05, + 0.0002290619449922815 + ], + [ + 4.6027067583054304e-05, + 0.00018797694065142423 + ], + [ + 0.00011523268767632544, + 0.0002520679263398051 + ], + [ + 0.00019119241915177554, + 0.0001887301041278988 + ], + [ + 1.963181421160698e-05, + 0.00023372970463242382 + ], + [ + 4.907522816210985e-05, + 0.0002623464388307184 + ], + [ + 0.0001160443207481876, + 0.00013599739759229124 + ], + [ + 5.1320315833436325e-05, + 0.0003299501840956509 + ], + [ + 9.119144669966772e-05, + 0.0002581499284133315 + ], + [ + 0.00017873440810944885, + 0.0001950178120750934 + ], + [ + 0.0002246685471618548, + 0.00019706103194039315 + ], + [ + 0.0001559338707011193, + 0.00017631787341088057 + ], + [ + 7.933386950753629e-05, + 0.0002380117803113535 + ], + [ + 0.00011564185115275905, + 0.00020408336422406137 + ], + [ + 0.00019758340204134583, + 7.177110819611698e-05 + ], + [ + 0.00019391992827877402, + 0.00010407812806079164 + ], + [ + 0.00022520490165334195, + 8.579453424317762e-05 + ], + [ + 0.00016923068324103951, + 0.00010937576735159382 + ], + [ + 0.00013766785559710115, + 0.0001538667274871841 + ], + [ + 9.134950960287824e-05, + 0.00015858327969908714 + ], + [ + 0.00011745499796234071, + 0.00013500810018740594 + ], + [ + -3.824380110017955e-05, + 0.00014808817650191486 + ], + [ + 3.8634265365544707e-05, + 0.00018870820349548012 + ], + [ + 0.0002255616564070806, + 5.902284465264529e-05 + ], + [ + 0.00010114470205735415, + 9.682199015514925e-05 + ], + [ + 0.0002862330002244562, + 0.00013255098019726574 + ], + [ + 0.00024776009377092123, + 3.481244857539423e-05 + ], + [ + 2.3148753825807944e-05, + 9.506038622930646e-05 + ], + [ + 0.0002670097746886313, + 8.401592640439048e-05 + ], + [ + 0.0001987678842851892, + 0.00010504567035241053 + ], + [ + 6.743526319041848e-05, + 0.00024751058663241565 + ], + [ + 0.00029025328694842756, + 0.0001857235620263964 + ], + [ + 0.0001983236870728433, + 0.00019350553338881582 + ], + [ + 0.00013808326912112534, + 0.0002099911798723042 + ], + [ + 0.00020198861602693796, + 0.00021240056958049536 + ], + [ + 0.00011322669888613746, + 0.00010049615957541391 + ], + [ + 0.00014189658395480365, + 0.0001872979773906991 + ], + [ + 0.0003604701778385788, + 9.940413292497396e-05 + ], + [ + 0.0002825631818268448, + -0.00013672317436430603 + ], + [ + 0.00033368790172971785, + -0.00010625823051668704 + ], + [ + 0.0003222878440283239, + 0.00015314373013097793 + ], + [ + 0.0003864019236061722, + 9.940662130247802e-05 + ], + [ + 0.0005687772645615041, + -0.00016956732724793255 + ], + [ + 0.0004775998822879046, + -9.155445877695456e-05 + ], + [ + 0.000298536557238549, + -0.00011266092769801617 + ], + [ + 0.00048634945414960384, + -0.0002687525993678719 + ], + [ + 0.0004725925100501627, + -0.00013922897051088512 + ], + [ + 0.0004984108381904662, + -0.0004769994702655822 + ], + [ + 0.0007588288281112909, + -0.00016838876763358712 + ], + [ + 0.001029319828376174, + -0.0003297104558441788 + ], + [ + 0.0006125936633907259, + -0.0007844512001611292 + ], + [ + 0.00046529644168913364, + -0.0006260887021198869 + ], + [ + 0.0004708615888375789, + -0.0006605982198379934 + ], + [ + 0.00043017190182581544, + -0.0009181486675515771 + ], + [ + 0.0004611682961694896, + -0.000866751535795629 + ], + [ + 0.0004915079334750772, + -0.0006079974700696766 + ], + [ + 0.00037738081300631166, + -0.0009400053531862795 + ], + [ + 0.00019310510833747685, + -0.0007922975346446037 + ], + [ + 0.0003301291726529598, + -0.0010884335497394204 + ], + [ + 0.0005347576225176454, + -0.0012515961425378919 + ], + [ + 0.00044525248813442886, + -0.001237173331901431 + ], + [ + 0.0002049403265118599, + -0.0014737347373738885 + ], + [ + 0.0006259393994696438, + -0.0018042773008346558 + ], + [ + 0.00044931023148819804, + -0.0025834429543465376 + ], + [ + -0.0002388909342698753, + -0.0015313772018998861 + ], + [ + -0.0005217522848397493, + -0.0011369726853445172 + ], + [ + -0.00045011803740635514, + -0.0014445470878854394 + ], + [ + -0.0007649653707630932, + -0.0010153109906241298 + ], + [ + -0.0008399365469813347, + -0.0012224232777953148 + ], + [ + -0.0005345265963114798, + -0.0013694459339603782 + ], + [ + -0.0006330999312922359, + -0.0012520735617727041 + ], + [ + -0.0007280901772901416, + -0.0010761554585769773 + ], + [ + -0.0009217592305503786, + -0.0009558111196383834 + ], + [ + -0.001081267255358398, + -0.00031484721694141626 + ], + [ + -0.0012302096001803875, + -0.0007458669133484364 + ], + [ + -0.0016944983508437872, + -0.0008735618903301656 + ], + [ + -0.003650585189461708, + -0.001607439829967916 + ], + [ + -0.004094173666089773, + -0.00180875847581774 + ], + [ + -0.002533932449296117, + 0.00016556821356061846 + ], + [ + -0.002488949103280902, + 5.241473263595253e-05 + ], + [ + -0.002333409385755658, + 0.00077130610588938 + ], + [ + -0.0020680162124335766, + 0.0007700799033045769 + ], + [ + -0.0015857625985518098, + 0.0009944558842107654 + ], + [ + -0.0014850037405267358, + 0.001286727492697537 + ], + [ + -0.0011459465604275465, + 0.0011765117524191737 + ], + [ + -0.0007777121500112116, + 0.0014583771117031574 + ], + [ + -0.0008000337984412909, + 0.0018266979604959488 + ], + [ + -0.0009740700479596853, + 0.0022710177581757307 + ], + [ + -0.0002672994160093367, + 0.002068726811558008 + ], + [ + -0.0005208143847994506, + 0.0020830719731748104 + ], + [ + -0.0018717662896960974, + 0.003007944906130433 + ], + [ + -0.0010693584335967898, + 0.0030956293921917677 + ], + [ + 0.0004783102194778621, + 0.0020338341128081083 + ], + [ + 3.914970511686988e-05, + 0.002390749752521515 + ], + [ + 0.0013152945321053267, + 0.0020755871664732695 + ], + [ + 0.0010854083811864257, + 0.0014486514264717698 + ], + [ + 0.0016178804216906428, + 0.002506934106349945 + ], + [ + 0.0014162956504151225, + 0.0007748946081846952 + ], + [ + 0.0016683050198480487, + 0.0013800835004076362 + ], + [ + 0.0009737486834637821, + 0.0005410646554082632 + ], + [ + 0.001907138037495315, + 0.0005282413912937045 + ], + [ + 0.0021120659075677395, + 0.0010347977513447404 + ], + [ + 0.001385138719342649, + -4.6002824092283845e-05 + ], + [ + 0.002209632657468319, + -0.0004110128211323172 + ], + [ + 0.0021208934485912323, + 3.876421760651283e-05 + ], + [ + 0.001899477792903781, + 0.0004085125110577792 + ], + [ + 0.0017430849839001894, + -6.226101686479524e-05 + ], + [ + 0.0015900093130767345, + -0.0014757534954696894 + ], + [ + 0.001302627963013947, + -0.0010400869650766253 + ], + [ + 0.0009553904528729618, + -0.0016644648276269436 + ], + [ + 0.0007697956170886755, + -0.001371526625007391 + ], + [ + 0.0010051612043753266, + -0.0017974695656448603 + ], + [ + 0.00013481295900419354, + -0.0007941773510538042 + ], + [ + 0.0001293059322051704, + -0.0011632631067186594 + ], + [ + 0.0001342183240922168, + -0.0017161972355097532 + ], + [ + 0.0007037981995381415, + -0.001687468378804624 + ], + [ + 0.00031602519447915256, + -0.0019189275335520506 + ], + [ + 0.00019035414152313024, + -0.00016808028158266097 + ], + [ + -0.0006904809270054102, + -0.0015655288007110357 + ], + [ + -0.0001150473763118498, + -0.0032739785965532064 + ], + [ + -0.0005176039412617683, + -0.003064983757212758 + ], + [ + -0.0027701123617589474, + -0.0013210291508585215 + ] + ], + "total_points": 1000 +} \ No newline at end of file diff --git a/vna_system/calibration/s21_start100_stop8800_points1000_bw1khz/bambambum/through_metadata.json b/vna_system/calibration/s21_start100_stop8800_points1000_bw1khz/bambambum/through_metadata.json new file mode 100644 index 0000000..74dc78f --- /dev/null +++ b/vna_system/calibration/s21_start100_stop8800_points1000_bw1khz/bambambum/through_metadata.json @@ -0,0 +1,16 @@ +{ + "preset": { + "filename": "s21_start100_stop8800_points1000_bw1khz.bin", + "mode": "s21", + "start_freq": 100000000.0, + "stop_freq": 8800000000.0, + "points": 1000, + "bandwidth": 1000.0 + }, + "calibration_name": "bambambum", + "standard": "through", + "sweep_number": 50, + "sweep_timestamp": 1758800474.7658994, + "created_timestamp": "2025-09-25T14:41:46.014170", + "total_points": 1000 +} \ No newline at end of file diff --git a/vna_system/core/acquisition/data_acquisition.py b/vna_system/core/acquisition/data_acquisition.py index 49daa36..6105fcd 100644 --- a/vna_system/core/acquisition/data_acquisition.py +++ b/vna_system/core/acquisition/data_acquisition.py @@ -10,7 +10,7 @@ from typing import BinaryIO, List, Tuple import serial -from vna_system.config import config as cfg +from vna_system.core import config as cfg from vna_system.core.acquisition.sweep_buffer import SweepBuffer @@ -31,6 +31,11 @@ class VNADataAcquisition: self._running: bool = False self._thread: threading.Thread | None = None self._stop_event: threading.Event = threading.Event() + self._paused: bool = False + + # Acquisition modes + self._continuous_mode: bool = True + self._single_sweep_requested: bool = False # Sweep collection state self._collecting: bool = False @@ -77,6 +82,54 @@ class VNADataAcquisition: """Return a reference to the sweep buffer.""" return self._sweep_buffer + @property + def is_paused(self) -> bool: + """Return True if acquisition is paused.""" + return self._paused + + @property + def is_continuous_mode(self) -> bool: + """Return True if in continuous sweep mode.""" + return self._continuous_mode + + def pause(self) -> None: + """Pause the data acquisition.""" + if not self._running: + logger.warning("Cannot pause: acquisition not running") + return + + self._paused = True + logger.info("Data acquisition paused") + + def set_continuous_mode(self, continuous: bool = True) -> None: + """Set continuous or single sweep mode. Also resumes if paused.""" + self._continuous_mode = continuous + + # Resume acquisition if setting to continuous mode and currently paused + if continuous and self._paused: + self._paused = False + logger.info("Data acquisition resumed (continuous mode)") + + mode_str = "continuous" if continuous else "single sweep" + logger.info(f"Acquisition mode set to: {mode_str}") + + def trigger_single_sweep(self) -> None: + """Trigger a single sweep. Automatically switches to single sweep mode if needed.""" + if not self._running: + logger.warning("Cannot trigger single sweep: acquisition not running") + return + + # Switch to single sweep mode if currently in continuous mode + if self._continuous_mode: + self.set_continuous_mode(False) + logger.info("Switched from continuous to single sweep mode") + + self._single_sweep_requested = True + if self._paused: + self._paused = False + logger.info("Data acquisition resumed for single sweep") + logger.info("Single sweep triggered") + # --------------------------------------------------------------------- # # Serial management # --------------------------------------------------------------------- # @@ -104,6 +157,11 @@ class VNADataAcquisition: """Main acquisition loop executed by the background thread.""" while self._running and not self._stop_event.is_set(): try: + # Check if paused + if self._paused: + time.sleep(0.1) + continue + # Auto-detect port self.port: str = cfg.get_vna_port() logger.info(f"Using auto-detected port: {self.port}") @@ -114,6 +172,16 @@ class VNADataAcquisition: buffered = io.BufferedReader(raw, buffer_size=cfg.SERIAL_BUFFER_SIZE) self._process_sweep_data(buffered, ser) + # Handle single sweep mode + if not self._continuous_mode: + if self._single_sweep_requested: + self._single_sweep_requested = False + logger.info("Single sweep completed, pausing acquisition") + self.pause() + else: + # In single sweep mode but no sweep requested, pause + self.pause() + except Exception as exc: # noqa: BLE001 logger.error("Acquisition error: %s", exc) time.sleep(1.0) diff --git a/vna_system/core/acquisition/sweep_buffer.py b/vna_system/core/acquisition/sweep_buffer.py index c37519f..0007650 100644 --- a/vna_system/core/acquisition/sweep_buffer.py +++ b/vna_system/core/acquisition/sweep_buffer.py @@ -5,7 +5,7 @@ from collections import deque from dataclasses import dataclass from typing import List, Tuple -from vna_system.config.config import SWEEP_BUFFER_MAX_SIZE +from vna_system.core.config import SWEEP_BUFFER_MAX_SIZE @dataclass @@ -52,9 +52,3 @@ class SweepBuffer: """Get the most recent sweep""" with self._lock: return self._buffer[-1] if self._buffer else None - - - def set_sweep_counter(self, sweep_number: int) -> None: - """Set the sweep counter to continue from a specific number.""" - with self._lock: - self._sweep_counter = sweep_number \ No newline at end of file diff --git a/vna_system/config/config.py b/vna_system/core/config.py similarity index 100% rename from vna_system/config/config.py rename to vna_system/core/config.py diff --git a/vna_system/core/processing/__init__.py b/vna_system/core/processing/__init__.py deleted file mode 100644 index d678fc3..0000000 --- a/vna_system/core/processing/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Sweep data processing module.""" \ No newline at end of file diff --git a/vna_system/core/processing/base_processor.py b/vna_system/core/processing/base_processor.py deleted file mode 100644 index d5f27ec..0000000 --- a/vna_system/core/processing/base_processor.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python3 -""" -Base sweep processor interface and utilities. -""" - -from __future__ import annotations - -import abc -from typing import Any, Dict - -from vna_system.core.acquisition.sweep_buffer import SweepData - - -class BaseSweepProcessor(abc.ABC): - """Abstract base class for sweep data processors.""" - - def __init__(self, config: Dict[str, Any]) -> None: - """Initialize processor with configuration.""" - self.config = config - self.enabled = config.get("enabled", True) - - @property - @abc.abstractmethod - def name(self) -> str: - """Return processor name.""" - pass - - @abc.abstractmethod - def process(self, sweep: SweepData) -> ProcessingResult | None: - """Process a sweep and return results.""" - pass - - def should_process(self, sweep: SweepData) -> bool: - """Check if this processor should process the given sweep.""" - return self.enabled - - -class ProcessingResult: - """Result of sweep processing.""" - - def __init__( - self, - processor_name: str, - sweep_number: int, - data: Dict[str, Any], - plotly_figure: Dict[str, Any] | None = None, - file_path: str | None = None, - ) -> None: - self.processor_name = processor_name - self.sweep_number = sweep_number - self.data = data - self.plotly_figure = plotly_figure - self.file_path = file_path - - def to_dict(self) -> Dict[str, Any]: - """Convert result to dictionary for serialization.""" - return { - "processor_name": self.processor_name, - "sweep_number": self.sweep_number, - "data": self.data, - "plotly_figure": self.plotly_figure, - "file_path": self.file_path, - } \ No newline at end of file diff --git a/vna_system/core/processing/config.json b/vna_system/core/processing/config.json deleted file mode 100644 index 3310563..0000000 --- a/vna_system/core/processing/config.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "processors": { - "magnitude_plot": { - "enabled": true, - "output_dir": "./plots/magnitude", - "save_image": true, - "image_format": "png", - "width": 800, - "height": 600 - } - }, - "processing": { - "queue_max_size": 1000 - }, - "storage": { - "dir": "./processing_results", - "max_sweeps": 10000, - "sweeps_per_subdir": 1000 - } -} \ No newline at end of file diff --git a/vna_system/core/processing/processors/__init__.py b/vna_system/core/processing/processors/__init__.py deleted file mode 100644 index 101db2b..0000000 --- a/vna_system/core/processing/processors/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Sweep data processors.""" \ No newline at end of file diff --git a/vna_system/core/processing/processors/magnitude_plot.py b/vna_system/core/processing/processors/magnitude_plot.py deleted file mode 100644 index c83029d..0000000 --- a/vna_system/core/processing/processors/magnitude_plot.py +++ /dev/null @@ -1,209 +0,0 @@ -#!/usr/bin/env python3 -""" -Magnitude plot processor for sweep data. -""" - -from __future__ import annotations - -from pathlib import Path -from typing import Any, Dict, List, Tuple - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib -matplotlib.use('Agg') # Use non-interactive backend -import plotly.graph_objects as go - -from vna_system.core.acquisition.sweep_buffer import SweepData -from vna_system.core.processing.base_processor import BaseSweepProcessor, ProcessingResult -from vna_system.core.processing.calibration_processor import CalibrationProcessor -import vna_system.core.singletons as singletons - - -class MagnitudePlotProcessor(BaseSweepProcessor): - """Processor that creates magnitude plots from sweep data.""" - - def __init__(self, config: Dict[str, Any]) -> None: - super().__init__(config) - self.output_dir = Path(config.get("output_dir", "./plots")) - self.save_image = config.get("save_image", True) - self.image_format = config.get("image_format", "png") - self.width = config.get("width", 800) - self.height = config.get("height", 600) - - # Calibration support - self.apply_calibration = config.get("apply_calibration", True) - self.calibration_processor = CalibrationProcessor() - - # Create output directory if it doesn't exist - if self.save_image: - self.output_dir.mkdir(parents=True, exist_ok=True) - - @property - def name(self) -> str: - return "magnitude_plot" - - def process(self, sweep: SweepData) -> ProcessingResult | None: - """Process sweep data and create magnitude plot.""" - if not self.should_process(sweep): - return None - - # Get current calibration from settings manager - current_calibration = singletons.settings_manager.get_current_calibration() - - # Apply calibration if available and enabled - processed_sweep = self._apply_calibration_if_available(sweep, current_calibration) - - # Extract magnitude data - magnitude_data = self._extract_magnitude_data(processed_sweep) - if not magnitude_data: - return None - - # Create plotly figure for websocket/API consumption - plotly_fig = self._create_plotly_figure(sweep, magnitude_data, current_calibration is not None) - - # Save image if requested (using matplotlib) - file_path = None - if self.save_image: - file_path = self._save_matplotlib_image(sweep, magnitude_data, current_calibration is not None) - - # Prepare result data - result_data = { - "sweep_number": sweep.sweep_number, - "timestamp": sweep.timestamp, - "total_points": sweep.total_points, - "magnitude_stats": self._calculate_magnitude_stats(magnitude_data), - "calibration_applied": current_calibration is not None and self.apply_calibration, - "calibration_name": current_calibration.name if current_calibration else None, - } - - return ProcessingResult( - processor_name=self.name, - sweep_number=sweep.sweep_number, - data=result_data, - plotly_figure=plotly_fig.to_dict(), - file_path=str(file_path) if file_path else None, - ) - - def _apply_calibration_if_available(self, sweep: SweepData, calibration_set) -> SweepData: - """Apply calibration to sweep data if calibration is available and enabled.""" - if not self.apply_calibration or not calibration_set: - return sweep - - try: - # Apply calibration and get corrected complex array - calibrated_complex = self.calibration_processor.apply_calibration(sweep, calibration_set) - - # Convert back to (real, imag) tuples for SweepData - calibrated_points = [(complex_val.real, complex_val.imag) for complex_val in calibrated_complex] - - # Create new SweepData with calibrated points - return SweepData( - sweep_number=sweep.sweep_number, - timestamp=sweep.timestamp, - points=calibrated_points, - total_points=len(calibrated_points) - ) - except Exception as e: - print(f"Failed to apply calibration: {e}") - return sweep - - def _extract_magnitude_data(self, sweep: SweepData) -> List[Tuple[float, float]]: - """Extract magnitude data from sweep points.""" - magnitude_data = [] - for i, (real, imag) in enumerate(sweep.points): - magnitude = np.sqrt(real**2 + imag**2) - magnitude_data.append((i, magnitude)) - return magnitude_data - - def _create_plotly_figure(self, sweep: SweepData, magnitude_data: List[Tuple[float, float]], calibrated: bool = False) -> go.Figure: - """Create plotly figure for magnitude plot.""" - indices = [point[0] for point in magnitude_data] - magnitudes = [point[1] for point in magnitude_data] - - fig = go.Figure() - - # Choose color based on calibration status - line_color = 'green' if calibrated else 'blue' - trace_name = 'Magnitude (Calibrated)' if calibrated else 'Magnitude (Raw)' - - fig.add_trace(go.Scatter( - x=indices, - y=magnitudes, - mode='lines', - name=trace_name, - line=dict(color=line_color, width=2) - )) - - # Add calibration indicator to title - title_suffix = ' (Calibrated)' if calibrated else ' (Raw)' - fig.update_layout( - title=f'Magnitude Plot - Sweep #{sweep.sweep_number}{title_suffix}', - xaxis_title='Point Index', - yaxis_title='Magnitude', - width=self.width, - height=self.height, - template='plotly_white', - showlegend=False - ) - - return fig - - def _save_matplotlib_image(self, sweep: SweepData, magnitude_data: List[Tuple[float, float]], calibrated: bool = False) -> Path | None: - """Save plot as image file using matplotlib.""" - try: - # Add calibration indicator to filename - cal_suffix = "_cal" if calibrated else "" - filename = f"magnitude_sweep_{sweep.sweep_number:06d}{cal_suffix}.{self.image_format}" - file_path = self.output_dir / filename - - # Extract data for plotting - indices = [point[0] for point in magnitude_data] - magnitudes = [point[1] for point in magnitude_data] - - # Create matplotlib figure - fig, ax = plt.subplots(figsize=(self.width/100, self.height/100), dpi=100) - - # Choose color and label based on calibration status - line_color = 'green' if calibrated else 'blue' - label = 'Magnitude (Calibrated)' if calibrated else 'Magnitude (Raw)' - - ax.plot(indices, magnitudes, color=line_color, linewidth=2, label=label) - - # Add calibration indicator to title - title_suffix = ' (Calibrated)' if calibrated else ' (Raw)' - ax.set_title(f'Magnitude Plot - Sweep #{sweep.sweep_number}{title_suffix}') - ax.set_xlabel('Point Index') - ax.set_ylabel('Magnitude') - ax.legend() - ax.grid(True, alpha=0.3) - - # Add info text - info_text = f'Timestamp: {sweep.timestamp:.3f}s\nTotal Points: {sweep.total_points}' - ax.text(0.02, 0.98, info_text, transform=ax.transAxes, - verticalalignment='top', bbox=dict(boxstyle='round', facecolor='white', alpha=0.8)) - - # Save figure - plt.savefig(str(file_path), bbox_inches='tight', dpi=100) - plt.close(fig) - - return file_path - except Exception as e: - print(f"Failed to save matplotlib image: {e}") - return None - - def _calculate_magnitude_stats(self, magnitude_data: List[Tuple[float, float]]) -> Dict[str, float]: - """Calculate basic statistics for magnitude data.""" - if not magnitude_data: - return {} - - magnitudes = [point[1] for point in magnitude_data] - magnitudes_array = np.array(magnitudes) - - return { - "min": float(np.min(magnitudes_array)), - "max": float(np.max(magnitudes_array)), - "mean": float(np.mean(magnitudes_array)), - "std": float(np.std(magnitudes_array)), - "median": float(np.median(magnitudes_array)), - } \ No newline at end of file diff --git a/vna_system/core/processing/results_storage.py b/vna_system/core/processing/results_storage.py deleted file mode 100644 index a5636ff..0000000 --- a/vna_system/core/processing/results_storage.py +++ /dev/null @@ -1,295 +0,0 @@ -from __future__ import annotations - -import json -import logging -import threading -from pathlib import Path -from typing import Any, Dict, List - -from vna_system.core.processing.base_processor import ProcessingResult - - -logger = logging.getLogger(__name__) - - -class ResultsStorage: - """Sweep-based file storage for processing results.""" - - def __init__( - self, - storage_dir: str = "processing_results", - max_sweeps: int = 10000, - sweeps_per_subdir: int = 1000 - ) -> None: - self.storage_dir = Path(storage_dir) - self.max_sweeps = max_sweeps - self.sweeps_per_subdir = sweeps_per_subdir - - # Thread safety - self._lock = threading.RLock() - - # Create storage directory - self.storage_dir.mkdir(parents=True, exist_ok=True) - - # In-memory cache for quick lookup: {sweep_number: {processor_name: file_path, ...}} - self._cache: Dict[int, Dict[str, str]] = {} - self._build_cache() - - def _build_cache(self) -> None: - """Build in-memory cache by scanning storage directory.""" - logger.info("Building results cache...") - self._cache = {} - - # Scan all sweep subdirectories - for subdir in self.storage_dir.iterdir(): - if subdir.is_dir() and subdir.name.startswith("sweeps_"): - # Scan sweep directories within subdirectory - for sweep_dir in subdir.iterdir(): - if sweep_dir.is_dir() and sweep_dir.name.startswith("sweep_"): - try: - sweep_number = int(sweep_dir.name.split("_")[1]) - self._cache[sweep_number] = {} - - # Find all processor files in this sweep - for result_file in sweep_dir.glob("*.json"): - if result_file.name != "metadata.json": - processor_name = result_file.stem - self._cache[sweep_number][processor_name] = str(result_file) - - except (ValueError, IndexError): - logger.warning(f"Invalid sweep directory name: {sweep_dir.name}") - - logger.info(f"Built cache with {len(self._cache)} sweeps") - - def _get_sweep_dir(self, sweep_number: int) -> Path: - """Get directory path for specific sweep.""" - # Group sweeps in subdirectories to avoid too many directories - subdir_index = sweep_number // self.sweeps_per_subdir - subdir_name = f"sweeps_{subdir_index:03d}_{(subdir_index + 1) * self.sweeps_per_subdir - 1:06d}" - - sweep_name = f"sweep_{sweep_number:06d}" - sweep_dir = self.storage_dir / subdir_name / sweep_name - sweep_dir.mkdir(parents=True, exist_ok=True) - return sweep_dir - - def _get_result_file_path(self, sweep_number: int, processor_name: str) -> Path: - """Get file path for specific sweep and processor result.""" - sweep_dir = self._get_sweep_dir(sweep_number) - return sweep_dir / f"{processor_name}.json" - - def _get_metadata_file_path(self, sweep_number: int) -> Path: - """Get metadata file path for specific sweep.""" - sweep_dir = self._get_sweep_dir(sweep_number) - return sweep_dir / "metadata.json" - - def store_result(self, result: ProcessingResult, overwrite: bool = False) -> None: - """Store a processing result.""" - with self._lock: - sweep_number = result.sweep_number - processor_name = result.processor_name - - # Get file paths - result_file = self._get_result_file_path(sweep_number, processor_name) - metadata_file = self._get_metadata_file_path(sweep_number) - - # Skip if result file already exists (don't overwrite unless forced) - if result_file.exists() and not overwrite: # SHOULD NOT HAPPEND - logger.debug(f"Result file already exists, skipping: {result_file}") - # Still update cache for existing file - if sweep_number not in self._cache: - self._cache[sweep_number] = {} - self._cache[sweep_number][processor_name] = str(result_file) - return - - try: - # Store processor result - with open(result_file, 'w') as f: - json.dump(result.to_dict(), f, indent=2) - - # Update/create metadata - metadata = {} - if metadata_file.exists(): - try: - with open(metadata_file, 'r') as f: - metadata = json.load(f) - except Exception as e: - logger.warning(f"Failed to read metadata: {e}") - - # Update metadata - metadata.update({ - "sweep_number": sweep_number, - "timestamp": result.data.get("timestamp", 0), - "total_points": result.data.get("total_points", 0), - "processors": metadata.get("processors", []) - }) - - # Add processor to list if not already there - if processor_name not in metadata["processors"]: - metadata["processors"].append(processor_name) - - # Save metadata - with open(metadata_file, 'w') as f: - json.dump(metadata, f, indent=2) - - # Update cache - if sweep_number not in self._cache: - self._cache[sweep_number] = {} - - self._cache[sweep_number][processor_name] = str(result_file) - - # Cleanup old sweeps if needed - self._cleanup_old_sweeps() - - logger.debug(f"Stored result for sweep {sweep_number}, processor {processor_name}") - - except Exception as e: - logger.error(f"Failed to store result: {e}") - - def _cleanup_old_sweeps(self) -> None: - """Remove old sweeps if we exceed the limit.""" - if len(self._cache) > self.max_sweeps: - # Sort by sweep number and remove oldest - sorted_sweeps = sorted(self._cache.keys()) - sweeps_to_remove = sorted_sweeps[:len(sorted_sweeps) - self.max_sweeps] - - for sweep_number in sweeps_to_remove: - try: - sweep_dir = self._get_sweep_dir(sweep_number) - - # Remove all files in sweep directory - if sweep_dir.exists(): - for file in sweep_dir.iterdir(): - file.unlink() - sweep_dir.rmdir() - logger.debug(f"Removed sweep directory: {sweep_dir}") - - # Remove from cache - del self._cache[sweep_number] - - except Exception as e: - logger.error(f"Failed to remove sweep {sweep_number}: {e}") - - def get_latest_results(self, processor_name: str | None = None, limit: int = 10) -> List[ProcessingResult]: - """Get latest processing results.""" - with self._lock: - results = [] - - # Get latest sweep numbers - sorted_sweeps = sorted(self._cache.keys(), reverse=True) - - for sweep_number in sorted_sweeps[:limit * 2]: # Get more sweeps to find enough results - if len(results) >= limit: - break - - sweep_results = self.get_result_by_sweep(sweep_number, processor_name) - results.extend(sweep_results) - - return results[:limit] - - def get_result_by_sweep(self, sweep_number: int, processor_name: str | None = None) -> List[ProcessingResult]: - """Get processing results for specific sweep number.""" - with self._lock: - results = [] - - if sweep_number not in self._cache: - return results - - sweep_processors = self._cache[sweep_number] - - # Filter by processor name if specified - if processor_name: - if processor_name in sweep_processors: - processors_to_load = [processor_name] - else: - processors_to_load = [] - else: - processors_to_load = list(sweep_processors.keys()) - - # Load results for each processor - for proc_name in processors_to_load: - file_path = Path(sweep_processors[proc_name]) - try: - if file_path.exists(): - with open(file_path, 'r') as f: - result_data = json.load(f) - - result = ProcessingResult( - processor_name=result_data["processor_name"], - sweep_number=result_data["sweep_number"], - data=result_data["data"], - plotly_figure=result_data.get("plotly_figure"), - file_path=result_data.get("file_path") - ) - results.append(result) - - except Exception as e: - logger.error(f"Failed to load result from {file_path}: {e}") - - return results - - def get_sweep_metadata(self, sweep_number: int) -> Dict[str, Any] | None: - """Get metadata for specific sweep.""" - with self._lock: - if sweep_number not in self._cache: - return None - - metadata_file = self._get_metadata_file_path(sweep_number) - try: - if metadata_file.exists(): - with open(metadata_file, 'r') as f: - return json.load(f) - except Exception as e: - logger.error(f"Failed to read metadata for sweep {sweep_number}: {e}") - - return None - - def get_available_sweeps(self, limit: int | None = None) -> List[int]: - """Get list of available sweep numbers.""" - with self._lock: - sorted_sweeps = sorted(self._cache.keys(), reverse=True) - if limit: - return sorted_sweeps[:limit] - return sorted_sweeps - - def get_processors_for_sweep(self, sweep_number: int) -> List[str]: - """Get list of processors that have results for specific sweep.""" - with self._lock: - if sweep_number in self._cache: - return list(self._cache[sweep_number].keys()) - return [] - - def get_storage_stats(self) -> Dict[str, Any]: - """Get storage statistics.""" - with self._lock: - if not self._cache: - return { - "storage_dir": str(self.storage_dir), - "total_sweeps": 0, - "processors": {}, - "oldest_sweep": None, - "newest_sweep": None - } - - # Calculate processor statistics - processor_counts = {} - for sweep_processors in self._cache.values(): - for processor_name in sweep_processors.keys(): - processor_counts[processor_name] = processor_counts.get(processor_name, 0) + 1 - - sorted_sweeps = sorted(self._cache.keys()) - - return { - "storage_dir": str(self.storage_dir), - "total_sweeps": len(self._cache), - "processors": processor_counts, - "oldest_sweep": sorted_sweeps[0] if sorted_sweeps else None, - "newest_sweep": sorted_sweeps[-1] if sorted_sweeps else None, - "storage_structure": "sweep-based" - } - - def get_max_sweep_number(self) -> int: - """Get the maximum sweep number currently stored.""" - with self._lock: - if not self._cache: - return 0 - return max(self._cache.keys()) \ No newline at end of file diff --git a/vna_system/core/processing/sweep_processor.py b/vna_system/core/processing/sweep_processor.py deleted file mode 100644 index 03be78a..0000000 --- a/vna_system/core/processing/sweep_processor.py +++ /dev/null @@ -1,231 +0,0 @@ -from __future__ import annotations - -import json -import logging -import queue -import threading -import time -from typing import Any, Dict, List - -from vna_system.core.acquisition.sweep_buffer import SweepBuffer, SweepData -from vna_system.core.processing.base_processor import BaseSweepProcessor, ProcessingResult -from vna_system.core.processing.processors.magnitude_plot import MagnitudePlotProcessor -from vna_system.core.processing.results_storage import ResultsStorage - - -logger = logging.getLogger(__name__) - - -class SweepProcessingManager: - """Manager for sweep data processing with configurable processors and queue-based results.""" - - def __init__(self, config_path: str = "vna_system/core/processing/config.json") -> None: - self.processors: List[BaseSweepProcessor] = [] - self.config: Dict[str, Any] = {} - self.sweep_buffer: SweepBuffer | None = None - - # Processing control - self._running = False - self._thread: threading.Thread | None = None - self._stop_event = threading.Event() - self._last_processed_sweep = 0 - - # Results queue for websocket consumption - self.results_queue: queue.Queue[ProcessingResult] = queue.Queue(maxsize=10) - - # Load configuration first - self.load_config(config_path) - - # File-based results storage (after config is loaded) - storage_config = self.config.get("storage", {}) - self.results_storage = ResultsStorage( - storage_dir=storage_config.get("dir", "processing_results"), - max_sweeps=storage_config.get("max_sweeps", 10000), - sweeps_per_subdir=storage_config.get("sweeps_per_subdir", 1000) - ) - - - def load_config(self, config_path: str) -> None: - """Load processing configuration from JSON file.""" - try: - with open(config_path, 'r') as f: - self.config = json.load(f) - self._initialize_processors() - logger.info(f"Loaded processing config from {config_path}") - except Exception as e: - logger.error(f"Failed to load config from {config_path}: {e}") - - def _initialize_processors(self) -> None: - """Initialize processors based on configuration.""" - self.processors.clear() - - processor_configs = self.config.get("processors", {}) - - # Initialize magnitude plot processor - if "magnitude_plot" in processor_configs: - magnitude_config = processor_configs["magnitude_plot"] - self.processors.append(MagnitudePlotProcessor(magnitude_config)) - - logger.info(f"Initialized {len(self.processors)} processors") - - - def set_sweep_buffer(self, sweep_buffer: SweepBuffer) -> None: - """Set the sweep buffer to process data from.""" - self.sweep_buffer = sweep_buffer - - # Initialize sweep counter to continue from existing data - try: - max_sweep = self.results_storage.get_max_sweep_number() - if max_sweep > 0: - self.sweep_buffer.set_sweep_counter(max_sweep) - self._last_processed_sweep = max_sweep # Sync our tracking - logger.info(f"Set sweep buffer to continue from sweep #{max_sweep + 1}") - else: - logger.info("Sweep buffer starts from #1") - except Exception as e: - logger.warning(f"Failed to set sweep counter: {e}") - - def start(self) -> None: - """Start the processing thread.""" - if self._running: - logger.debug("Processing already running; start() call ignored.") - return - - if not self.sweep_buffer: - logger.error("Cannot start processing without sweep buffer") - return - - self._running = True - self._stop_event.clear() - self._thread = threading.Thread(target=self._processing_loop, daemon=True) - self._thread.start() - logger.info("Sweep processing started") - - def stop(self) -> None: - """Stop the processing thread.""" - if not self._running: - logger.debug("Processing not running; stop() call ignored.") - return - - self._running = False - self._stop_event.set() - - if self._thread and self._thread.is_alive(): - self._thread.join(timeout=5.0) - logger.info("Processing thread joined") - - @property - def is_running(self) -> bool: - """Check if processing is currently running.""" - return self._running and (self._thread is not None and self._thread.is_alive()) - - def _processing_loop(self) -> None: - """Main processing loop executed by the background thread.""" - while self._running and not self._stop_event.is_set(): - try: - if not self.sweep_buffer: - time.sleep(0.1) - continue - - # Get latest sweep - latest_sweep = self.sweep_buffer.get_latest_sweep() - if not latest_sweep: - time.sleep(0.1) - continue - - # Process new sweeps - if latest_sweep.sweep_number > self._last_processed_sweep: - self._process_sweep(latest_sweep) - self._last_processed_sweep = latest_sweep.sweep_number - - time.sleep(0.05) # Small delay to avoid busy waiting - - except Exception as e: - logger.error(f"Processing loop error: {e}") - time.sleep(1.0) - - def _process_sweep(self, sweep: SweepData) -> None: - """Process a single sweep with all configured processors.""" - logger.debug(f"Processing sweep #{sweep.sweep_number}") - - for processor in self.processors: - try: - result = processor.process(sweep) - if result: - self._store_result(result) - self._queue_result(result) - logger.debug(f"Processed sweep #{sweep.sweep_number} with {processor.name}") - except Exception as e: - logger.error(f"Error in processor {processor.name}: {e}") - - def _store_result(self, result: ProcessingResult) -> None: - """Store processing result in file storage.""" - self.results_storage.store_result(result) - - def _queue_result(self, result: ProcessingResult) -> None: - """Queue processing result for websocket consumption.""" - try: - self.results_queue.put_nowait(result) - except queue.Full: - logger.warning("Results queue is full, dropping oldest result") - try: - self.results_queue.get_nowait() # Remove oldest - self.results_queue.put_nowait(result) # Add new - except queue.Empty: - pass - - def get_next_result(self, timeout: float | None = None) -> ProcessingResult | None: - """Get next processing result from queue. Used by websocket handler.""" - try: - return self.results_queue.get(timeout=timeout) - except queue.Empty: - return None - - def get_pending_results_count(self) -> int: - """Get number of pending results in queue.""" - return self.results_queue.qsize() - - def drain_results_queue(self) -> List[ProcessingResult]: - """Drain all pending results from queue. Used for batch processing.""" - results = [] - while True: - try: - result = self.results_queue.get_nowait() - results.append(result) - except queue.Empty: - break - return results - - def get_latest_results(self, processor_name: str | None = None, limit: int = 10) -> List[ProcessingResult]: - """Get latest processing results from storage, optionally filtered by processor name.""" - return self.results_storage.get_latest_results(processor_name, limit) - - def get_result_by_sweep(self, sweep_number: int, processor_name: str | None = None) -> List[ProcessingResult]: - """Get processing results for a specific sweep number from storage.""" - return self.results_storage.get_result_by_sweep(sweep_number, processor_name) - - def add_processor(self, processor: BaseSweepProcessor) -> None: - """Add a processor manually.""" - self.processors.append(processor) - logger.info(f"Added processor: {processor.name}") - - def remove_processor(self, processor_name: str) -> bool: - """Remove a processor by name.""" - for i, processor in enumerate(self.processors): - if processor.name == processor_name: - del self.processors[i] - logger.info(f"Removed processor: {processor_name}") - return True - return False - - def get_processing_stats(self) -> Dict[str, Any]: - """Get processing statistics.""" - storage_stats = self.results_storage.get_storage_stats() - return { - "is_running": self.is_running, - "processors_count": len(self.processors), - "processor_names": [p.name for p in self.processors], - "last_processed_sweep": self._last_processed_sweep, - "results_queue_size": self.results_queue.qsize(), - "storage_stats": storage_stats, - } \ No newline at end of file diff --git a/vna_system/core/processing/websocket_handler.py b/vna_system/core/processing/websocket_handler.py deleted file mode 100644 index 5af18a5..0000000 --- a/vna_system/core/processing/websocket_handler.py +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env python3 -""" -WebSocket handler for real-time sweep data processing results using FastAPI. -""" - -from __future__ import annotations - -import asyncio -import json -import logging -from typing import Any, Dict, List - -from fastapi import WebSocket, WebSocketDisconnect - -from vna_system.core.processing.sweep_processor import SweepProcessingManager - - -logger = logging.getLogger(__name__) - - -class WebSocketManager: - """WebSocket manager for streaming processing results to clients.""" - - def __init__(self, processing_manager: SweepProcessingManager) -> None: - self.processing_manager = processing_manager - self.active_connections: List[WebSocket] = [] - - async def connect(self, websocket: WebSocket) -> None: - """Accept a new WebSocket connection.""" - await websocket.accept() - self.active_connections.append(websocket) - logger.info(f"Client connected. Total clients: {len(self.active_connections)}") - - def disconnect(self, websocket: WebSocket) -> None: - """Remove WebSocket connection.""" - if websocket in self.active_connections: - self.active_connections.remove(websocket) - logger.info(f"Client disconnected. Total clients: {len(self.active_connections)}") - - async def send_personal_message(self, message: Dict[str, Any], websocket: WebSocket) -> None: - """Send message to specific websocket.""" - try: - await websocket.send_text(json.dumps(message)) - except Exception as e: - logger.error(f"Error sending message to client: {e}") - - async def broadcast(self, message: Dict[str, Any]) -> None: - """Send message to all connected clients.""" - if not self.active_connections: - return - - disconnected = [] - for connection in self.active_connections: - try: - await connection.send_text(json.dumps(message)) - except Exception as e: - logger.error(f"Error broadcasting to client: {e}") - disconnected.append(connection) - - # Remove failed connections - for connection in disconnected: - self.disconnect(connection) - - async def handle_websocket(self, websocket: WebSocket) -> None: - """Handle WebSocket connection.""" - await self.connect(websocket) - - try: - # Send initial status - await self.send_personal_message({ - "type": "status", - "data": { - "connected": True, - "processing_stats": self.processing_manager.get_processing_stats() - } - }, websocket) - - # Start processing results stream - stream_task = asyncio.create_task(self._stream_results(websocket)) - - try: - # Handle incoming messages - while True: - data = await websocket.receive_text() - try: - message = json.loads(data) - await self._handle_message(websocket, message) - except json.JSONDecodeError: - await self.send_personal_message({ - "type": "error", - "message": "Invalid JSON format" - }, websocket) - except WebSocketDisconnect: - logger.info("WebSocket disconnected") - except Exception as e: - logger.error(f"WebSocket message handling error: {e}") - - except WebSocketDisconnect: - logger.info("WebSocket disconnected") - except Exception as e: - logger.error(f"WebSocket error: {e}") - finally: - stream_task.cancel() - self.disconnect(websocket) - - async def _handle_message(self, websocket: WebSocket, message: Dict[str, Any]) -> None: - """Handle incoming client message.""" - message_type = message.get("type") - - if message_type == "get_stats": - await self.send_personal_message({ - "type": "stats", - "data": self.processing_manager.get_processing_stats() - }, websocket) - - elif message_type == "get_history": - processor_name = message.get("processor_name") - limit = message.get("limit", 10) - - results = self.processing_manager.get_latest_results(processor_name)[-limit:] - await self.send_personal_message({ - "type": "history", - "data": [result.to_dict() for result in results] - }, websocket) - - elif message_type == "get_sweep": - sweep_number = message.get("sweep_number") - processor_name = message.get("processor_name") - - if sweep_number is not None: - results = self.processing_manager.get_result_by_sweep(sweep_number, processor_name) - await self.send_personal_message({ - "type": "sweep_data", - "data": [result.to_dict() for result in results] - }, websocket) - else: - await self.send_personal_message({ - "type": "error", - "message": "sweep_number is required" - }, websocket) - - else: - await self.send_personal_message({ - "type": "error", - "message": f"Unknown message type: {message_type}" - }, websocket) - - async def _stream_results(self, websocket: WebSocket) -> None: - """Stream processing results to websocket.""" - while websocket in self.active_connections: - try: - result = self.processing_manager.get_next_result(timeout=0.1) - if result: - await self.send_personal_message({ - "type": "processing_result", - "data": result.to_dict() - }, websocket) - - await asyncio.sleep(0.05) - - except asyncio.CancelledError: - break - except Exception as e: - logger.error(f"Error streaming results: {e}") - await asyncio.sleep(1.0) diff --git a/vna_system/core/processors/__init__.py b/vna_system/core/processors/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vna_system/core/processors/base_processor.py b/vna_system/core/processors/base_processor.py new file mode 100644 index 0000000..2179802 --- /dev/null +++ b/vna_system/core/processors/base_processor.py @@ -0,0 +1,190 @@ +from abc import ABC, abstractmethod +from typing import Dict, Any, List, Optional +from dataclasses import dataclass +from pathlib import Path +import json +import threading +from datetime import datetime + +from vna_system.core.settings.preset_manager import ConfigPreset + + +@dataclass +class UIParameter: + name: str + label: str + type: str # 'slider', 'toggle', 'select', 'input' + value: Any + options: Optional[Dict[str, Any]] = None # min/max for slider, choices for select, etc. + + +@dataclass +class ProcessedResult: + processor_id: str + timestamp: float + data: Dict[str, Any] + plotly_config: Dict[str, Any] + ui_parameters: List[UIParameter] + metadata: Dict[str, Any] + + +class BaseProcessor(ABC): + def __init__(self, processor_id: str, config_dir: Path): + self.processor_id = processor_id + self.config_dir = config_dir + self.config_file = config_dir / f"{processor_id}_config.json" + self._lock = threading.RLock() + self._sweep_history: List[Any] = [] + self._max_history = 1 + self._config = {} + self._load_config() + + @property + def max_history(self) -> int: + return self._max_history + + @max_history.setter + def max_history(self, value: int): + with self._lock: + self._max_history = max(1, value) + self._trim_history() + + def _trim_history(self): + if len(self._sweep_history) > self._max_history: + self._sweep_history = self._sweep_history[-self._max_history:] + + def _load_config(self): + if self.config_file.exists(): + try: + with open(self.config_file, 'r') as f: + self._config = json.load(f) + self._validate_config() + except (json.JSONDecodeError, FileNotFoundError): + self._config = self._get_default_config() + self.save_config() + else: + self._config = self._get_default_config() + self.save_config() + + def save_config(self): + self.config_dir.mkdir(parents=True, exist_ok=True) + with open(self.config_file, 'w') as f: + json.dump(self._config, f, indent=2) + + def update_config(self, updates: Dict[str, Any]): + with self._lock: + old_config = self._config.copy() + # Convert types based on existing config values + converted_updates = self._convert_config_types(updates) + self._config.update(converted_updates) + try: + self._validate_config() + self.save_config() + except Exception as e: + self._config = old_config + raise ValueError(f"Invalid configuration: {e}") + + def _convert_config_types(self, updates: Dict[str, Any]) -> Dict[str, Any]: + """Convert string values to appropriate types based on existing config""" + converted = {} + + for key, value in updates.items(): + # If the key is not in the current config, keep the value as-is + if key not in self._config: + converted[key] = value + continue + + existing_value = self._config[key] + + # Convert booleans from string + if isinstance(existing_value, bool) and isinstance(value, str): + converted[key] = value.lower() in ('true', '1', 'on', 'yes') + continue + + # Convert numbers from string + if isinstance(existing_value, (int, float)) and isinstance(value, str): + try: + if isinstance(existing_value, int): + # Handle cases like "50.0" → 50 + converted[key] = int(float(value)) + else: + converted[key] = float(value) + except ValueError: + # Keep the original string if conversion fails + converted[key] = value + continue + + # For all other cases, keep the value as-is + converted[key] = value + + return converted + + + def get_config(self) -> Dict[str, Any]: + return self._config.copy() + + def add_sweep_data(self, sweep_data: Any, calibrated_data: Any, vna_config: ConfigPreset | None): + with self._lock: + self._sweep_history.append({ + 'sweep_data': sweep_data, + 'calibrated_data': calibrated_data, + 'vna_config': vna_config.__dict__ if vna_config is not None else {}, + 'timestamp': datetime.now().timestamp() + }) + self._trim_history() + + return self.recalculate() + + def recalculate(self) -> Optional[ProcessedResult]: + with self._lock: + if not self._sweep_history: + return None + + latest = self._sweep_history[-1] + return self._process_data( + latest['sweep_data'], + latest['calibrated_data'], + latest['vna_config'] + ) + + def _process_data(self, sweep_data: Any, calibrated_data: Any, vna_config: Dict[str, Any]) -> ProcessedResult: + processed_data = self.process_sweep(sweep_data, calibrated_data, vna_config) + plotly_config = self.generate_plotly_config(processed_data, vna_config) + ui_parameters = self.get_ui_parameters() + + return ProcessedResult( + processor_id=self.processor_id, + timestamp=datetime.now().timestamp(), + data=processed_data, + plotly_config=plotly_config, + ui_parameters=ui_parameters, + metadata=self._get_metadata() + ) + + @abstractmethod + def process_sweep(self, sweep_data: Any, calibrated_data: Any, vna_config: Dict[str, Any]) -> Dict[str, Any]: + pass + + @abstractmethod + def generate_plotly_config(self, processed_data: Dict[str, Any], vna_config: Dict[str, Any]) -> Dict[str, Any]: + pass + + @abstractmethod + def get_ui_parameters(self) -> List[UIParameter]: + pass + + @abstractmethod + def _get_default_config(self) -> Dict[str, Any]: + pass + + @abstractmethod + def _validate_config(self): + pass + + def _get_metadata(self) -> Dict[str, Any]: + return { + 'processor_id': self.processor_id, + 'config': self._config, + 'history_count': len(self._sweep_history), + 'max_history': self._max_history + } \ No newline at end of file diff --git a/vna_system/core/processing/calibration_processor.py b/vna_system/core/processors/calibration_processor.py similarity index 86% rename from vna_system/core/processing/calibration_processor.py rename to vna_system/core/processors/calibration_processor.py index c946917..92844a2 100644 --- a/vna_system/core/processing/calibration_processor.py +++ b/vna_system/core/processors/calibration_processor.py @@ -6,13 +6,11 @@ Supports both S11 and S21 measurement modes with appropriate correction algorith """ import numpy as np -from pathlib import Path -from typing import Optional +from typing import List, Tuple -from vna_system.core.acquisition.sweep_buffer import SweepData -from vna_system.core.settings.preset_manager import VNAMode -from vna_system.core.settings.calibration_manager import CalibrationSet -from vna_system.core.settings.calibration_manager import CalibrationStandard +from ..acquisition.sweep_buffer import SweepData +from ..settings.preset_manager import VNAMode +from ..settings.calibration_manager import CalibrationSet, CalibrationStandard class CalibrationProcessor: @@ -26,16 +24,16 @@ class CalibrationProcessor: def __init__(self): pass - def apply_calibration(self, sweep_data: SweepData, calibration_set: CalibrationSet) -> np.ndarray: + def apply_calibration(self, sweep_data: SweepData, calibration_set: CalibrationSet) -> List[Tuple[float, float]]: """ - Apply calibration to sweep data and return corrected complex array. + Apply calibration to sweep data and return corrected complex data as list of (real, imag) tuples. Args: sweep_data: Raw sweep data from VNA calibration_set: Calibration standards data Returns: - Complex array with calibration applied + List of (real, imag) tuples with calibration applied Raises: ValueError: If calibration is incomplete or mode not supported @@ -48,12 +46,15 @@ class CalibrationProcessor: # Apply calibration based on measurement mode if calibration_set.preset.mode == VNAMode.S21: - return self._apply_s21_calibration(raw_signal, calibration_set) + calibrated_array = self._apply_s21_calibration(raw_signal, calibration_set) elif calibration_set.preset.mode == VNAMode.S11: - return self._apply_s11_calibration(raw_signal, calibration_set) + calibrated_array = self._apply_s11_calibration(raw_signal, calibration_set) else: raise ValueError(f"Unsupported measurement mode: {calibration_set.preset.mode}") + # Convert back to list of (real, imag) tuples + return [(complex_val.real, complex_val.imag) for complex_val in calibrated_array] + def _sweep_to_complex_array(self, sweep_data: SweepData) -> np.ndarray: """Convert SweepData to complex numpy array.""" complex_data = [] @@ -95,7 +96,6 @@ class CalibrationProcessor: Final correction: S11 = (Raw - Ed) / (Er + Es * (Raw - Ed)) """ - from vna_system.core.settings.calibration_manager import CalibrationStandard # Get calibration standards open_sweep = calibration_set.standards[CalibrationStandard.OPEN] @@ -131,4 +131,4 @@ class CalibrationProcessor: calibrated_signal = corrected_numerator / corrected_denominator - return calibrated_signal + return calibrated_signal \ No newline at end of file diff --git a/vna_system/core/processors/configs/magnitude_config.json b/vna_system/core/processors/configs/magnitude_config.json new file mode 100644 index 0000000..184fbd8 --- /dev/null +++ b/vna_system/core/processors/configs/magnitude_config.json @@ -0,0 +1,9 @@ +{ + "y_min": -110, + "y_max": 10, + "smoothing_enabled": false, + "smoothing_window": 17, + "marker_enabled": true, + "marker_frequency": 1, + "grid_enabled": false +} \ No newline at end of file diff --git a/vna_system/core/processors/configs/phase_config.json b/vna_system/core/processors/configs/phase_config.json new file mode 100644 index 0000000..912d1ec --- /dev/null +++ b/vna_system/core/processors/configs/phase_config.json @@ -0,0 +1,13 @@ +{ + "y_min": -210, + "y_max": 360, + "unwrap_phase": false, + "phase_offset": -60, + "smoothing_enabled": true, + "smoothing_window": 5, + "marker_enabled": false, + "marker_frequency": 2000000000, + "reference_line_enabled": false, + "reference_phase": 0, + "grid_enabled": true +} \ No newline at end of file diff --git a/vna_system/core/processors/configs/smith_chart_config.json b/vna_system/core/processors/configs/smith_chart_config.json new file mode 100644 index 0000000..556e162 --- /dev/null +++ b/vna_system/core/processors/configs/smith_chart_config.json @@ -0,0 +1,9 @@ +{ + "impedance_mode": true, + "reference_impedance": 100, + "marker_enabled": true, + "marker_frequency": 1000000000, + "grid_circles": true, + "grid_radials": true, + "trace_color_mode": "solid" +} \ No newline at end of file diff --git a/vna_system/core/processors/implementations/__init__.py b/vna_system/core/processors/implementations/__init__.py new file mode 100644 index 0000000..f7907d8 --- /dev/null +++ b/vna_system/core/processors/implementations/__init__.py @@ -0,0 +1,5 @@ +from .magnitude_processor import MagnitudeProcessor +from .phase_processor import PhaseProcessor +from .smith_chart_processor import SmithChartProcessor + +__all__ = ['MagnitudeProcessor', 'PhaseProcessor', 'SmithChartProcessor'] \ No newline at end of file diff --git a/vna_system/core/processors/implementations/magnitude_processor.py b/vna_system/core/processors/implementations/magnitude_processor.py new file mode 100644 index 0000000..0c911bb --- /dev/null +++ b/vna_system/core/processors/implementations/magnitude_processor.py @@ -0,0 +1,184 @@ +import numpy as np +from typing import Dict, Any, List +from pathlib import Path + +from ..base_processor import BaseProcessor, UIParameter + + +class MagnitudeProcessor(BaseProcessor): + def __init__(self, config_dir: Path): + super().__init__("magnitude", config_dir) + + def process_sweep(self, sweep_data: Any, calibrated_data: Any, vna_config: Dict[str, Any]) -> Dict[str, Any]: + if not calibrated_data or not hasattr(calibrated_data, 'points'): + return {'error': 'No calibrated data available'} + + frequencies = [] + magnitudes_db = [] + + for i, (real, imag) in enumerate(calibrated_data.points): + complex_val = complex(real, imag) + magnitude_db = 20 * np.log10(abs(complex_val)) if abs(complex_val) > 0 else -120 + + # Calculate frequency based on VNA config + start_freq = vna_config.get('start_frequency', 100e6) + stop_freq = vna_config.get('stop_frequency', 8.8e9) + total_points = len(calibrated_data.points) + frequency = start_freq + (stop_freq - start_freq) * i / (total_points - 1) + + frequencies.append(frequency) + magnitudes_db.append(magnitude_db) + + # Apply smoothing if enabled + if self._config.get('smoothing_enabled', False): + window_size = self._config.get('smoothing_window', 5) + magnitudes_db = self._apply_moving_average(magnitudes_db, window_size) + + return { + 'frequencies': frequencies, + 'magnitudes_db': magnitudes_db, + 'y_min': self._config.get('y_min', -80), + 'y_max': self._config.get('y_max', 10), + 'marker_enabled': self._config.get('marker_enabled', True), + 'marker_frequency': self._config.get('marker_frequency', frequencies[len(frequencies)//2] if frequencies else 1e9), + 'grid_enabled': self._config.get('grid_enabled', True) + } + + def generate_plotly_config(self, processed_data: Dict[str, Any], vna_config: Dict[str, Any]) -> Dict[str, Any]: + if 'error' in processed_data: + return {'error': processed_data['error']} + + frequencies = processed_data['frequencies'] + magnitudes_db = processed_data['magnitudes_db'] + + # Find marker point + marker_freq = processed_data['marker_frequency'] + marker_idx = min(range(len(frequencies)), key=lambda i: abs(frequencies[i] - marker_freq)) + marker_mag = magnitudes_db[marker_idx] + + traces = [{ + 'x': [f / 1e9 for f in frequencies], # Convert to GHz + 'y': magnitudes_db, + 'type': 'scatter', + 'mode': 'lines', + 'name': 'S11 Magnitude', + 'line': {'color': 'blue', 'width': 2} + }] + + # Add marker if enabled + if processed_data['marker_enabled']: + traces.append({ + 'x': [frequencies[marker_idx] / 1e9], + 'y': [marker_mag], + 'type': 'scatter', + 'mode': 'markers', + 'name': f'Marker: {frequencies[marker_idx]/1e9:.3f} GHz, {marker_mag:.2f} dB', + 'marker': {'color': 'red', 'size': 8, 'symbol': 'circle'} + }) + + return { + 'data': traces, + 'layout': { + 'title': 'S11 Magnitude Response', + 'xaxis': { + 'title': 'Frequency (GHz)', + 'showgrid': processed_data['grid_enabled'] + }, + 'yaxis': { + 'title': 'Magnitude (dB)', + 'range': [processed_data['y_min'], processed_data['y_max']], + 'showgrid': processed_data['grid_enabled'] + }, + 'hovermode': 'x unified', + 'showlegend': True + } + } + + def get_ui_parameters(self) -> List[UIParameter]: + return [ + UIParameter( + name='y_min', + label='Y Axis Min (dB)', + type='slider', + value=self._config.get('y_min', -80), + options={'min': -120, 'max': 0, 'step': 5} + ), + UIParameter( + name='y_max', + label='Y Axis Max (dB)', + type='slider', + value=self._config.get('y_max', 10), + options={'min': -20, 'max': 20, 'step': 5} + ), + UIParameter( + name='smoothing_enabled', + label='Enable Smoothing', + type='toggle', + value=self._config.get('smoothing_enabled', False) + ), + UIParameter( + name='smoothing_window', + label='Smoothing Window Size', + type='slider', + value=self._config.get('smoothing_window', 5), + options={'min': 3, 'max': 21, 'step': 2} + ), + UIParameter( + name='marker_enabled', + label='Show Marker', + type='toggle', + value=self._config.get('marker_enabled', True) + ), + UIParameter( + name='marker_frequency', + label='Marker Frequency (Hz)', + type='input', + value=self._config.get('marker_frequency', 1e9), + options={'type': 'number', 'min': 100e6, 'max': 8.8e9} + ), + UIParameter( + name='grid_enabled', + label='Show Grid', + type='toggle', + value=self._config.get('grid_enabled', True) + ) + ] + + def _get_default_config(self) -> Dict[str, Any]: + return { + 'y_min': -80, + 'y_max': 10, + 'smoothing_enabled': False, + 'smoothing_window': 5, + 'marker_enabled': True, + 'marker_frequency': 1e9, + 'grid_enabled': True + } + + def _validate_config(self): + required_keys = ['y_min', 'y_max', 'smoothing_enabled', 'smoothing_window', + 'marker_enabled', 'marker_frequency', 'grid_enabled'] + + for key in required_keys: + if key not in self._config: + raise ValueError(f"Missing required config key: {key}") + + if self._config['y_min'] >= self._config['y_max']: + raise ValueError("y_min must be less than y_max") + + if self._config['smoothing_window'] < 3 or self._config['smoothing_window'] % 2 == 0: + raise ValueError("smoothing_window must be odd and >= 3") + + def _apply_moving_average(self, data: List[float], window_size: int) -> List[float]: + if window_size >= len(data): + return data + + smoothed = [] + half_window = window_size // 2 + + for i in range(len(data)): + start_idx = max(0, i - half_window) + end_idx = min(len(data), i + half_window + 1) + smoothed.append(sum(data[start_idx:end_idx]) / (end_idx - start_idx)) + + return smoothed \ No newline at end of file diff --git a/vna_system/core/processors/implementations/phase_processor.py b/vna_system/core/processors/implementations/phase_processor.py new file mode 100644 index 0000000..b80e1a8 --- /dev/null +++ b/vna_system/core/processors/implementations/phase_processor.py @@ -0,0 +1,242 @@ +import numpy as np +from typing import Dict, Any, List +from pathlib import Path + +from ..base_processor import BaseProcessor, UIParameter + + +class PhaseProcessor(BaseProcessor): + def __init__(self, config_dir: Path): + super().__init__("phase", config_dir) + + def process_sweep(self, sweep_data: Any, calibrated_data: Any, vna_config: Dict[str, Any]) -> Dict[str, Any]: + if not calibrated_data or not hasattr(calibrated_data, 'points'): + return {'error': 'No calibrated data available'} + + frequencies = [] + phases_deg = [] + + for i, (real, imag) in enumerate(calibrated_data.points): + complex_val = complex(real, imag) + phase_rad = np.angle(complex_val) + phase_deg = np.degrees(phase_rad) + + # Phase unwrapping if enabled + if self._config.get('unwrap_phase', True) and i > 0: + phase_diff = phase_deg - phases_deg[-1] + if phase_diff > 180: + phase_deg -= 360 + elif phase_diff < -180: + phase_deg += 360 + + # Calculate frequency + start_freq = vna_config.get('start_frequency', 100e6) + stop_freq = vna_config.get('stop_frequency', 8.8e9) + total_points = len(calibrated_data.points) + frequency = start_freq + (stop_freq - start_freq) * i / (total_points - 1) + + frequencies.append(frequency) + phases_deg.append(phase_deg) + + # Apply offset if configured + phase_offset = self._config.get('phase_offset', 0) + if phase_offset != 0: + phases_deg = [phase + phase_offset for phase in phases_deg] + + # Apply smoothing if enabled + if self._config.get('smoothing_enabled', False): + window_size = self._config.get('smoothing_window', 5) + phases_deg = self._apply_moving_average(phases_deg, window_size) + + return { + 'frequencies': frequencies, + 'phases_deg': phases_deg, + 'y_min': self._config.get('y_min', -180), + 'y_max': self._config.get('y_max', 180), + 'marker_enabled': self._config.get('marker_enabled', True), + 'marker_frequency': self._config.get('marker_frequency', frequencies[len(frequencies)//2] if frequencies else 1e9), + 'grid_enabled': self._config.get('grid_enabled', True), + 'reference_line_enabled': self._config.get('reference_line_enabled', False), + 'reference_phase': self._config.get('reference_phase', 0) + } + + def generate_plotly_config(self, processed_data: Dict[str, Any], vna_config: Dict[str, Any]) -> Dict[str, Any]: + if 'error' in processed_data: + return {'error': processed_data['error']} + + frequencies = processed_data['frequencies'] + phases_deg = processed_data['phases_deg'] + + # Find marker point + marker_freq = processed_data['marker_frequency'] + marker_idx = min(range(len(frequencies)), key=lambda i: abs(frequencies[i] - marker_freq)) + marker_phase = phases_deg[marker_idx] + + traces = [{ + 'x': [f / 1e9 for f in frequencies], # Convert to GHz + 'y': phases_deg, + 'type': 'scatter', + 'mode': 'lines', + 'name': 'S11 Phase', + 'line': {'color': 'green', 'width': 2} + }] + + # Add marker if enabled + if processed_data['marker_enabled']: + traces.append({ + 'x': [frequencies[marker_idx] / 1e9], + 'y': [marker_phase], + 'type': 'scatter', + 'mode': 'markers', + 'name': f'Marker: {frequencies[marker_idx]/1e9:.3f} GHz, {marker_phase:.1f}°', + 'marker': {'color': 'red', 'size': 8, 'symbol': 'circle'} + }) + + # Add reference line if enabled + if processed_data['reference_line_enabled']: + traces.append({ + 'x': [frequencies[0] / 1e9, frequencies[-1] / 1e9], + 'y': [processed_data['reference_phase'], processed_data['reference_phase']], + 'type': 'scatter', + 'mode': 'lines', + 'name': f'Reference: {processed_data["reference_phase"]:.1f}°', + 'line': {'color': 'gray', 'width': 1, 'dash': 'dash'} + }) + + return { + 'data': traces, + 'layout': { + 'title': 'S11 Phase Response', + 'xaxis': { + 'title': 'Frequency (GHz)', + 'showgrid': processed_data['grid_enabled'] + }, + 'yaxis': { + 'title': 'Phase (degrees)', + 'range': [processed_data['y_min'], processed_data['y_max']], + 'showgrid': processed_data['grid_enabled'] + }, + 'hovermode': 'x unified', + 'showlegend': True + } + } + + def get_ui_parameters(self) -> List[UIParameter]: + return [ + UIParameter( + name='y_min', + label='Y Axis Min (degrees)', + type='slider', + value=self._config.get('y_min', -180), + options={'min': -360, 'max': 0, 'step': 15} + ), + UIParameter( + name='y_max', + label='Y Axis Max (degrees)', + type='slider', + value=self._config.get('y_max', 180), + options={'min': 0, 'max': 360, 'step': 15} + ), + UIParameter( + name='unwrap_phase', + label='Unwrap Phase', + type='toggle', + value=self._config.get('unwrap_phase', True) + ), + UIParameter( + name='phase_offset', + label='Phase Offset (degrees)', + type='slider', + value=self._config.get('phase_offset', 0), + options={'min': -180, 'max': 180, 'step': 5} + ), + UIParameter( + name='smoothing_enabled', + label='Enable Smoothing', + type='toggle', + value=self._config.get('smoothing_enabled', False) + ), + UIParameter( + name='smoothing_window', + label='Smoothing Window Size', + type='slider', + value=self._config.get('smoothing_window', 5), + options={'min': 3, 'max': 21, 'step': 2} + ), + UIParameter( + name='marker_enabled', + label='Show Marker', + type='toggle', + value=self._config.get('marker_enabled', True) + ), + UIParameter( + name='marker_frequency', + label='Marker Frequency (Hz)', + type='input', + value=self._config.get('marker_frequency', 1e9), + options={'type': 'number', 'min': 100e6, 'max': 8.8e9} + ), + UIParameter( + name='reference_line_enabled', + label='Show Reference Line', + type='toggle', + value=self._config.get('reference_line_enabled', False) + ), + UIParameter( + name='reference_phase', + label='Reference Phase (degrees)', + type='slider', + value=self._config.get('reference_phase', 0), + options={'min': -180, 'max': 180, 'step': 15} + ), + UIParameter( + name='grid_enabled', + label='Show Grid', + type='toggle', + value=self._config.get('grid_enabled', True) + ) + ] + + def _get_default_config(self) -> Dict[str, Any]: + return { + 'y_min': -180, + 'y_max': 180, + 'unwrap_phase': True, + 'phase_offset': 0, + 'smoothing_enabled': False, + 'smoothing_window': 5, + 'marker_enabled': True, + 'marker_frequency': 1e9, + 'reference_line_enabled': False, + 'reference_phase': 0, + 'grid_enabled': True + } + + def _validate_config(self): + required_keys = ['y_min', 'y_max', 'unwrap_phase', 'phase_offset', + 'smoothing_enabled', 'smoothing_window', 'marker_enabled', + 'marker_frequency', 'reference_line_enabled', 'reference_phase', 'grid_enabled'] + + for key in required_keys: + if key not in self._config: + raise ValueError(f"Missing required config key: {key}") + + if self._config['y_min'] >= self._config['y_max']: + raise ValueError("y_min must be less than y_max") + + if self._config['smoothing_window'] < 3 or self._config['smoothing_window'] % 2 == 0: + raise ValueError("smoothing_window must be odd and >= 3") + + def _apply_moving_average(self, data: List[float], window_size: int) -> List[float]: + if window_size >= len(data): + return data + + smoothed = [] + half_window = window_size // 2 + + for i in range(len(data)): + start_idx = max(0, i - half_window) + end_idx = min(len(data), i + half_window + 1) + smoothed.append(sum(data[start_idx:end_idx]) / (end_idx - start_idx)) + + return smoothed \ No newline at end of file diff --git a/vna_system/core/processors/implementations/smith_chart_processor.py b/vna_system/core/processors/implementations/smith_chart_processor.py new file mode 100644 index 0000000..f6b01d9 --- /dev/null +++ b/vna_system/core/processors/implementations/smith_chart_processor.py @@ -0,0 +1,303 @@ +import numpy as np +from typing import Dict, Any, List +from pathlib import Path + +from ..base_processor import BaseProcessor, UIParameter + + +class SmithChartProcessor(BaseProcessor): + def __init__(self, config_dir: Path): + super().__init__("smith_chart", config_dir) + + def process_sweep(self, sweep_data: Any, calibrated_data: Any, vna_config: Dict[str, Any]) -> Dict[str, Any]: + if not calibrated_data or not hasattr(calibrated_data, 'points'): + return {'error': 'No calibrated data available'} + + frequencies = [] + real_parts = [] + imag_parts = [] + reflection_coeffs = [] + + for i, (real, imag) in enumerate(calibrated_data.points): + complex_val = complex(real, imag) + + # Calculate frequency + start_freq = vna_config.get('start_frequency', 100e6) + stop_freq = vna_config.get('stop_frequency', 8.8e9) + total_points = len(calibrated_data.points) + frequency = start_freq + (stop_freq - start_freq) * i / (total_points - 1) + + frequencies.append(frequency) + real_parts.append(real) + imag_parts.append(imag) + reflection_coeffs.append(complex_val) + + # Convert to impedance if requested + impedance_mode = self._config.get('impedance_mode', False) + z0 = self._config.get('reference_impedance', 50) + + if impedance_mode: + impedances = [(z0 * (1 + gamma) / (1 - gamma)) for gamma in reflection_coeffs] + plot_real = [z.real for z in impedances] + plot_imag = [z.imag for z in impedances] + else: + plot_real = real_parts + plot_imag = imag_parts + + return { + 'frequencies': frequencies, + 'real_parts': plot_real, + 'imag_parts': plot_imag, + 'impedance_mode': impedance_mode, + 'reference_impedance': z0, + 'marker_enabled': self._config.get('marker_enabled', True), + 'marker_frequency': self._config.get('marker_frequency', frequencies[len(frequencies)//2] if frequencies else 1e9), + 'grid_circles': self._config.get('grid_circles', True), + 'grid_radials': self._config.get('grid_radials', True), + 'trace_color_mode': self._config.get('trace_color_mode', 'frequency') + } + + def generate_plotly_config(self, processed_data: Dict[str, Any], vna_config: Dict[str, Any]) -> Dict[str, Any]: + if 'error' in processed_data: + return {'error': processed_data['error']} + + real_parts = processed_data['real_parts'] + imag_parts = processed_data['imag_parts'] + frequencies = processed_data['frequencies'] + + # Find marker point + marker_freq = processed_data['marker_frequency'] + marker_idx = min(range(len(frequencies)), key=lambda i: abs(frequencies[i] - marker_freq)) + + # Create main trace + trace_config = { + 'x': real_parts, + 'y': imag_parts, + 'type': 'scatter', + 'mode': 'lines+markers', + 'marker': {'size': 3}, + 'line': {'width': 2} + } + + if processed_data['trace_color_mode'] == 'frequency': + # Color by frequency + trace_config['marker']['color'] = [f / 1e9 for f in frequencies] + trace_config['marker']['colorscale'] = 'Viridis' + trace_config['marker']['showscale'] = True + trace_config['marker']['colorbar'] = {'title': 'Frequency (GHz)'} + trace_config['name'] = 'S11 (colored by frequency)' + else: + trace_config['marker']['color'] = 'blue' + trace_config['name'] = 'S11' + + traces = [trace_config] + + # Add marker if enabled + if processed_data['marker_enabled']: + marker_real = real_parts[marker_idx] + marker_imag = imag_parts[marker_idx] + + if processed_data['impedance_mode']: + marker_label = f'Z = {marker_real:.1f} + j{marker_imag:.1f} Ω' + else: + marker_label = f'Γ = {marker_real:.3f} + j{marker_imag:.3f}' + + traces.append({ + 'x': [marker_real], + 'y': [marker_imag], + 'type': 'scatter', + 'mode': 'markers', + 'name': f'Marker: {frequencies[marker_idx]/1e9:.3f} GHz
{marker_label}', + 'marker': {'color': 'red', 'size': 10, 'symbol': 'diamond'} + }) + + # Add Smith chart grid if not in impedance mode + if not processed_data['impedance_mode']: + if processed_data['grid_circles']: + traces.extend(self._generate_smith_circles()) + if processed_data['grid_radials']: + traces.extend(self._generate_smith_radials()) + + # Layout configuration + if processed_data['impedance_mode']: + layout = { + 'title': f'Impedance Plot (Z₀ = {processed_data["reference_impedance"]} Ω)', + 'xaxis': {'title': 'Resistance (Ω)', 'showgrid': True, 'zeroline': True}, + 'yaxis': {'title': 'Reactance (Ω)', 'showgrid': True, 'zeroline': True, 'scaleanchor': 'x'}, + 'hovermode': 'closest' + } + else: + layout = { + 'title': 'Smith Chart', + 'xaxis': { + 'title': 'Real Part', + 'range': [-1.1, 1.1], + 'showgrid': False, + 'zeroline': False, + 'showticklabels': True + }, + 'yaxis': { + 'title': 'Imaginary Part', + 'range': [-1.1, 1.1], + 'showgrid': False, + 'zeroline': False, + 'scaleanchor': 'x', + 'scaleratio': 1 + }, + 'hovermode': 'closest' + } + + layout['showlegend'] = True + + return { + 'data': traces, + 'layout': layout + } + + def _generate_smith_circles(self) -> List[Dict[str, Any]]: + circles = [] + # Constant resistance circles + for r in [0, 0.2, 0.5, 1, 2, 5]: + if r == 0: + # Unit circle + theta = np.linspace(0, 2*np.pi, 100) + x = np.cos(theta) + y = np.sin(theta) + else: + center_x = r / (r + 1) + radius = 1 / (r + 1) + theta = np.linspace(0, 2*np.pi, 100) + x = center_x + radius * np.cos(theta) + y = radius * np.sin(theta) + + circles.append({ + 'x': x.tolist(), + 'y': y.tolist(), + 'type': 'scatter', + 'mode': 'lines', + 'line': {'color': 'lightgray', 'width': 1}, + 'showlegend': False, + 'hoverinfo': 'skip' + }) + + # Constant reactance circles + for x in [-5, -2, -1, -0.5, -0.2, 0.2, 0.5, 1, 2, 5]: + if abs(x) < 1e-10: + continue + center_y = 1/x + radius = abs(1/x) + theta = np.linspace(-np.pi, np.pi, 100) + circle_x = 1 + radius * np.cos(theta) + circle_y = center_y + radius * np.sin(theta) + + # Clip to unit circle + valid_points = circle_x**2 + circle_y**2 <= 1.01 + circle_x = circle_x[valid_points] + circle_y = circle_y[valid_points] + + if len(circle_x) > 0: + circles.append({ + 'x': circle_x.tolist(), + 'y': circle_y.tolist(), + 'type': 'scatter', + 'mode': 'lines', + 'line': {'color': 'lightblue', 'width': 1}, + 'showlegend': False, + 'hoverinfo': 'skip' + }) + + return circles + + def _generate_smith_radials(self) -> List[Dict[str, Any]]: + radials = [] + # Radial lines for constant phase + for angle_deg in range(0, 360, 30): + angle_rad = np.radians(angle_deg) + x = [0, np.cos(angle_rad)] + y = [0, np.sin(angle_rad)] + + radials.append({ + 'x': x, + 'y': y, + 'type': 'scatter', + 'mode': 'lines', + 'line': {'color': 'lightgray', 'width': 1, 'dash': 'dot'}, + 'showlegend': False, + 'hoverinfo': 'skip' + }) + + return radials + + def get_ui_parameters(self) -> List[UIParameter]: + return [ + UIParameter( + name='impedance_mode', + label='Impedance Plot Mode', + type='toggle', + value=self._config.get('impedance_mode', False) + ), + UIParameter( + name='reference_impedance', + label='Reference Impedance (Ω)', + type='select', + value=self._config.get('reference_impedance', 50), + options={'choices': [25, 50, 75, 100, 600]} + ), + UIParameter( + name='marker_enabled', + label='Show Marker', + type='toggle', + value=self._config.get('marker_enabled', True) + ), + UIParameter( + name='marker_frequency', + label='Marker Frequency (Hz)', + type='input', + value=self._config.get('marker_frequency', 1e9), + options={'type': 'number', 'min': 100e6, 'max': 8.8e9} + ), + UIParameter( + name='grid_circles', + label='Show Impedance Circles', + type='toggle', + value=self._config.get('grid_circles', True) + ), + UIParameter( + name='grid_radials', + label='Show Phase Radials', + type='toggle', + value=self._config.get('grid_radials', True) + ), + UIParameter( + name='trace_color_mode', + label='Trace Color Mode', + type='select', + value=self._config.get('trace_color_mode', 'frequency'), + options={'choices': ['frequency', 'solid']} + ) + ] + + def _get_default_config(self) -> Dict[str, Any]: + return { + 'impedance_mode': False, + 'reference_impedance': 50, + 'marker_enabled': True, + 'marker_frequency': 1e9, + 'grid_circles': True, + 'grid_radials': True, + 'trace_color_mode': 'frequency' + } + + def _validate_config(self): + required_keys = ['impedance_mode', 'reference_impedance', 'marker_enabled', + 'marker_frequency', 'grid_circles', 'grid_radials', 'trace_color_mode'] + + for key in required_keys: + if key not in self._config: + raise ValueError(f"Missing required config key: {key}") + + if self._config['reference_impedance'] <= 0: + raise ValueError("reference_impedance must be positive") + + if self._config['trace_color_mode'] not in ['frequency', 'solid']: + raise ValueError("trace_color_mode must be 'frequency' or 'solid'") \ No newline at end of file diff --git a/vna_system/core/processors/manager.py b/vna_system/core/processors/manager.py new file mode 100644 index 0000000..3bf8b1b --- /dev/null +++ b/vna_system/core/processors/manager.py @@ -0,0 +1,189 @@ +from typing import Dict, List, Optional, Any, Callable +import threading +import logging +from pathlib import Path + +from vna_system.core.settings.preset_manager import ConfigPreset +from vna_system.core.processors.base_processor import BaseProcessor, ProcessedResult +from vna_system.core.processors.calibration_processor import CalibrationProcessor +from vna_system.core.acquisition.sweep_buffer import SweepBuffer, SweepData +from vna_system.core.settings.settings_manager import VNASettingsManager + + + +class ProcessorManager: + def __init__(self, sweep_buffer: SweepBuffer, settings_manager: VNASettingsManager, config_dir: Path): + self.config_dir = config_dir + self._processors: Dict[str, BaseProcessor] = {} + self._lock = threading.RLock() + self._result_callbacks: List[Callable[[str, ProcessedResult], None]] = [] + self.logger = logging.getLogger(__name__) + + # Data acquisition integration + self.sweep_buffer: SweepBuffer = sweep_buffer + self._running = False + self._thread: Optional[threading.Thread] = None + self._stop_event = threading.Event() + self._last_processed_sweep = 0 + + # Calibration processor for applying calibrations + self.calibration_processor = CalibrationProcessor() + + self.settings_manager = settings_manager + # Register default processors + self._register_default_processors() + + def register_processor(self, processor: BaseProcessor): + with self._lock: + self._processors[processor.processor_id] = processor + self.logger.info(f"Registered processor: {processor.processor_id}") + + def get_processor(self, processor_id: str) -> Optional[BaseProcessor]: + return self._processors.get(processor_id) + + def list_processors(self) -> List[str]: + return list(self._processors.keys()) + + def add_result_callback(self, callback: Callable[[str, ProcessedResult], None]): + print("adding callback") + self._result_callbacks.append(callback) + + def process_sweep(self, sweep_data: SweepData, calibrated_data: Any, vna_config: ConfigPreset | None): + results = {} + print(f"Processing sweep {sweep_data.sweep_number=}") + with self._lock: + for processor_id, processor in self._processors.items(): + try: + result = processor.add_sweep_data(sweep_data, calibrated_data, vna_config) + if result: + results[processor_id] = result + for callback in self._result_callbacks: + try: + callback(processor_id, result) + except Exception as e: + self.logger.error(f"Callback error for {processor_id}: {e}") + + except Exception as e: + self.logger.error(f"Processing error in {processor_id}: {e}") + + return results + + def recalculate_processor(self, processor_id: str, config_updates: Optional[Dict[str, Any]] = None) -> Optional[ProcessedResult]: + processor = self.get_processor(processor_id) + if not processor: + raise ValueError(f"Processor {processor_id} not found") + + try: + if config_updates: + processor.update_config(config_updates) + + result = processor.recalculate() + if result: + for callback in self._result_callbacks: + try: + callback(processor_id, result) + except Exception as e: + self.logger.error(f"Callback error for {processor_id}: {e}") + + return result + + except Exception as e: + self.logger.error(f"Recalculation error in {processor_id}: {e}") + raise + + + def get_processor_ui_parameters(self, processor_id: str): + processor = self.get_processor(processor_id) + if not processor: + raise ValueError(f"Processor {processor_id} not found") + return [param.__dict__ for param in processor.get_ui_parameters()] + + + def _register_default_processors(self): + """Register default processors""" + try: + from .implementations import MagnitudeProcessor, PhaseProcessor, SmithChartProcessor + + magnitude_processor = MagnitudeProcessor(self.config_dir) + self.register_processor(magnitude_processor) + + phase_processor = PhaseProcessor(self.config_dir) + self.register_processor(phase_processor) + + smith_processor = SmithChartProcessor(self.config_dir) + self.register_processor(smith_processor) + + self.logger.info("Default processors registered successfully") + except Exception as e: + self.logger.error(f"Failed to register default processors: {e}") + + def set_sweep_buffer(self, sweep_buffer: SweepBuffer): + """Set the sweep buffer for data acquisition integration""" + self.sweep_buffer = sweep_buffer + + def start_processing(self): + """Start background processing of sweep data""" + if self._running or not self.sweep_buffer: + return + + self._running = True + self._stop_event.clear() + self._thread = threading.Thread(target=self._processing_loop, daemon=True) + self._thread.start() + self.logger.info("Processor manager started") + + def stop_processing(self): + """Stop background processing""" + if not self._running: + return + + self._running = False + self._stop_event.set() + if self._thread: + self._thread.join(timeout=1.0) + self.logger.info("Processor manager stopped") + + def _processing_loop(self): + """Main processing loop""" + while self._running and not self._stop_event.is_set(): + try: + latest_sweep = self.sweep_buffer.get_latest_sweep() + + if latest_sweep and latest_sweep.sweep_number > self._last_processed_sweep: + # Apply calibration + calibrated_data = self._apply_calibration(latest_sweep) + + # Get VNA configuration + vna_config = self.settings_manager.get_current_preset() + + # Process through all processors (results handled by callbacks) + self.process_sweep(latest_sweep, calibrated_data, vna_config) + + self._last_processed_sweep = latest_sweep.sweep_number + + # Check every 50ms + self._stop_event.wait(0.05) + + except Exception as e: + self.logger.error(f"Error in processing loop: {e}") + self._stop_event.wait(0.1) + + def _apply_calibration(self, sweep_data: SweepData) -> SweepData: + """Apply calibration to sweep data""" + try: + # Get current calibration set through settings manager + calibration_set = self.settings_manager.get_current_calibration() + if calibration_set and calibration_set.is_complete(): + # Apply calibration using our calibration processor + calibrated_points = self.calibration_processor.apply_calibration(sweep_data, calibration_set) + return SweepData( + sweep_number=sweep_data.sweep_number, + timestamp=sweep_data.timestamp, + points=calibrated_points, + total_points=len(calibrated_points) + ) + except Exception as e: + self.logger.error(f"Calibration failed: {e}") + + # Return original data if calibration fails or not available + return sweep_data diff --git a/vna_system/core/processors/storage/__init__.py b/vna_system/core/processors/storage/__init__.py new file mode 100644 index 0000000..7ce8915 --- /dev/null +++ b/vna_system/core/processors/storage/__init__.py @@ -0,0 +1,3 @@ +from .data_storage import DataStorage, StorageEntry + +__all__ = ['DataStorage', 'StorageEntry'] \ No newline at end of file diff --git a/vna_system/core/processors/storage/data_storage.py b/vna_system/core/processors/storage/data_storage.py new file mode 100644 index 0000000..a8ab921 --- /dev/null +++ b/vna_system/core/processors/storage/data_storage.py @@ -0,0 +1,162 @@ +from typing import Dict, Optional, List, Any +from dataclasses import dataclass, asdict +import threading +from datetime import datetime +from collections import defaultdict, deque +import json +from pathlib import Path + +from ..base_processor import ProcessedResult + + +@dataclass +class StorageEntry: + result: ProcessedResult + stored_at: float + + +class DataStorage: + def __init__(self, max_entries_per_processor: int = 100, enable_persistence: bool = False, storage_dir: Optional[Path] = None): + self.max_entries = max_entries_per_processor + self.enable_persistence = enable_persistence + self.storage_dir = storage_dir + self._data: Dict[str, deque] = defaultdict(lambda: deque(maxlen=self.max_entries)) + self._lock = threading.RLock() + + if self.enable_persistence and self.storage_dir: + self.storage_dir.mkdir(parents=True, exist_ok=True) + + def store_result(self, processor_id: str, result: ProcessedResult): + with self._lock: + entry = StorageEntry( + result=result, + stored_at=datetime.now().timestamp() + ) + + self._data[processor_id].append(entry) + + if self.enable_persistence: + self._persist_entry(processor_id, entry) + + def get_latest_result(self, processor_id: str) -> Optional[ProcessedResult]: + with self._lock: + processor_data = self._data.get(processor_id) + if processor_data: + return processor_data[-1].result + return None + + def get_results_history(self, processor_id: str, limit: Optional[int] = None) -> List[ProcessedResult]: + with self._lock: + processor_data = self._data.get(processor_id, deque()) + entries = list(processor_data) + + if limit: + entries = entries[-limit:] + + return [entry.result for entry in entries] + + def get_all_latest_results(self) -> Dict[str, ProcessedResult]: + results = {} + with self._lock: + for processor_id in self._data: + latest = self.get_latest_result(processor_id) + if latest: + results[processor_id] = latest + return results + + def clear_processor_data(self, processor_id: str): + with self._lock: + if processor_id in self._data: + self._data[processor_id].clear() + + def get_processor_count(self, processor_id: str) -> int: + with self._lock: + return len(self._data.get(processor_id, deque())) + + def get_storage_stats(self) -> Dict[str, Any]: + with self._lock: + stats = { + 'total_processors': len(self._data), + 'max_entries_per_processor': self.max_entries, + 'enable_persistence': self.enable_persistence, + 'processors': {} + } + + for processor_id, entries in self._data.items(): + stats['processors'][processor_id] = { + 'count': len(entries), + 'oldest_entry': entries[0].stored_at if entries else None, + 'latest_entry': entries[-1].stored_at if entries else None + } + + return stats + + def _persist_entry(self, processor_id: str, entry: StorageEntry): + if not self.storage_dir: + return + + processor_dir = self.storage_dir / processor_id + processor_dir.mkdir(exist_ok=True) + + timestamp = datetime.fromtimestamp(entry.stored_at).strftime('%Y%m%d_%H%M%S_%f') + filename = f"{timestamp}.json" + filepath = processor_dir / filename + + try: + data = { + 'stored_at': entry.stored_at, + 'processor_id': entry.result.processor_id, + 'timestamp': entry.result.timestamp, + 'data': entry.result.data, + 'plotly_config': entry.result.plotly_config, + 'ui_parameters': [asdict(param) for param in entry.result.ui_parameters], + 'metadata': entry.result.metadata + } + + with open(filepath, 'w') as f: + json.dump(data, f, indent=2, default=str) + + except Exception as e: + # Log error but don't fail the storage operation + pass + + def load_persisted_data(self, processor_id: str, limit: Optional[int] = None) -> List[ProcessedResult]: + if not self.enable_persistence or not self.storage_dir: + return [] + + processor_dir = self.storage_dir / processor_id + if not processor_dir.exists(): + return [] + + results = [] + json_files = sorted(processor_dir.glob('*.json')) + + if limit: + json_files = json_files[-limit:] + + for filepath in json_files: + try: + with open(filepath, 'r') as f: + data = json.load(f) + + # Reconstruct UIParameter objects + ui_parameters = [] + for param_data in data.get('ui_parameters', []): + from ..base_processor import UIParameter + ui_parameters.append(UIParameter(**param_data)) + + result = ProcessedResult( + processor_id=data['processor_id'], + timestamp=data['timestamp'], + data=data['data'], + plotly_config=data['plotly_config'], + ui_parameters=ui_parameters, + metadata=data['metadata'] + ) + results.append(result) + + except Exception as e: + # Skip corrupted files + continue + + return results \ No newline at end of file diff --git a/vna_system/core/processors/websocket_handler.py b/vna_system/core/processors/websocket_handler.py new file mode 100644 index 0000000..12195d6 --- /dev/null +++ b/vna_system/core/processors/websocket_handler.py @@ -0,0 +1,224 @@ +from typing import Dict, Any, Set, Optional +import json +import asyncio +import logging +from datetime import datetime +from fastapi import WebSocket, WebSocketDisconnect + +from vna_system.core.processors.base_processor import ProcessedResult +from vna_system.core.processors.manager import ProcessorManager +from vna_system.core.processors.storage import DataStorage + + +class ProcessorWebSocketHandler: + """ + Handles incoming websocket messages and broadcasts processor results + to all connected clients. Safe to call from non-async threads via + _broadcast_result_sync(). + """ + + def __init__(self, processor_manager: ProcessorManager, data_storage: DataStorage): + self.processor_manager = processor_manager + self.data_storage = data_storage + self.active_connections: Set[WebSocket] = set() + self.logger = logging.getLogger(__name__) + + # Главный (running) event loop FastAPI/uvicorn. + # Устанавливается при принятии первого соединения. + self._loop: Optional[asyncio.AbstractEventLoop] = None + + # Регистрируемся как колбэк на готовые результаты процессоров + self.processor_manager.add_result_callback(self._on_processor_result) + + # --------------- Публичные async-обработчики входящих сообщений --------------- + + async def handle_websocket_connection(self, websocket: WebSocket): + """ + Accepts a websocket and serves messages until disconnect. + Сохраняет ссылку на главный running loop, чтобы из других потоков + можно было безопасно шедулить корутины. + """ + # Сохраним ссылку на активный loop (гарантированно внутри async-контекста) + if self._loop is None: + try: + self._loop = asyncio.get_running_loop() + self.logger.info("Stored main event loop reference for broadcasting") + except RuntimeError: + # Теоретически маловероятно здесь + self.logger.warning("Could not obtain running loop; broadcasts may be skipped") + + await websocket.accept() + self.active_connections.add(websocket) + self.logger.info(f"WebSocket connected. Total connections: {len(self.active_connections)}") + + try: + while True: + data = await websocket.receive_text() + await self.handle_message(websocket, data) + except WebSocketDisconnect: + await self.disconnect(websocket) + except Exception as e: + self.logger.error(f"WebSocket error: {e}") + await self.disconnect(websocket) + + async def handle_message(self, websocket: WebSocket, data: str): + try: + message = json.loads(data) + message_type = message.get('type') + + if message_type == 'recalculate': + await self._handle_recalculate(websocket, message) + elif message_type == 'get_history': + await self._handle_get_history(websocket, message) + else: + await self._send_error(websocket, f"Unknown message type: {message_type}") + + except json.JSONDecodeError: + await self._send_error(websocket, "Invalid JSON format") + except Exception as e: + self.logger.exception("Error handling websocket message") + await self._send_error(websocket, f"Internal error: {str(e)}") + + async def disconnect(self, websocket: WebSocket): + if websocket in self.active_connections: + self.active_connections.remove(websocket) + self.logger.info(f"WebSocket disconnected. Total connections: {len(self.active_connections)}") + + # --------------- Команды клиента --------------- + + async def _handle_recalculate(self, websocket: WebSocket, message: Dict[str, Any]): + processor_id = message.get('processor_id') + config_updates = message.get('config_updates') + + if not processor_id: + await self._send_error(websocket, "processor_id is required") + return + + try: + result = self.processor_manager.recalculate_processor(processor_id, config_updates) + if result: + response = self._result_to_message(processor_id, result) + await websocket.send_text(json.dumps(response)) + else: + await self._send_error(websocket, f"No result from processor {processor_id}") + + except Exception as e: + self.logger.exception("Recalculation failed") + await self._send_error(websocket, f"Recalculation failed: {str(e)}") + + async def _handle_get_history(self, websocket: WebSocket, message: Dict[str, Any]): + processor_id = message.get('processor_id') + limit = message.get('limit', 10) + + if not processor_id: + await self._send_error(websocket, "processor_id is required") + return + + try: + history = self.data_storage.get_results_history(processor_id, limit) + response = { + 'type': 'processor_history', + 'processor_id': processor_id, + 'history': [ + { + 'timestamp': r.timestamp, + 'data': r.data, + 'plotly_config': r.plotly_config, + 'metadata': r.metadata + } + for r in history + ] + } + await websocket.send_text(json.dumps(response)) + + except Exception as e: + self.logger.exception("Error getting history") + await self._send_error(websocket, f"Error getting history: {str(e)}") + + # --------------- Служебные методы --------------- + + def _result_to_message(self, processor_id: str, result: ProcessedResult) -> Dict[str, Any]: + return { + 'type': 'processor_result', + 'processor_id': processor_id, + 'timestamp': result.timestamp, + 'data': result.data, + 'plotly_config': result.plotly_config, + 'ui_parameters': [param.__dict__ for param in result.ui_parameters], + 'metadata': result.metadata + } + + async def _send_error(self, websocket: WebSocket, message: str): + try: + response = { + 'type': 'error', + 'message': message, + 'timestamp': datetime.now().timestamp() + } + await websocket.send_text(json.dumps(response)) + except Exception as e: + self.logger.error(f"Error sending error message: {e}") + + # --------------- Получение результатов из процессоров (из другого потока) --------------- + + def _on_processor_result(self, processor_id: str, result: ProcessedResult): + """ + Колбэк вызывается из потока обработки свипов (не из asyncio loop). + Здесь нельзя напрямую await'ить — нужно перепоручить рассылку в главный loop. + """ + # Сохраняем результат в хранилище (синхронно) + try: + self.data_storage.store_result(processor_id, result) + except Exception: + self.logger.exception("Failed to store processor result") + + # Рассылаем клиентам + self._broadcast_result_sync(processor_id, result) + + def _broadcast_result_sync(self, processor_id: str, result: ProcessedResult): + """ + Потокобезопасная рассылка в активный event loop. + Вызывается из НЕ-async потока. + """ + if not self.active_connections: + return + + # Подготовим строку JSON один раз + message_str = json.dumps(self._result_to_message(processor_id, result)) + + loop = self._loop + if loop is None or not loop.is_running(): + # Луп ещё не был сохранён (нет подключений) или уже остановлен + self.logger.debug("No running event loop available for broadcast; skipping") + return + + try: + # Перекидываем корутину в главный loop из стороннего потока + fut = asyncio.run_coroutine_threadsafe(self._send_to_connections(message_str), loop) + # Опционально: можно добавить обработку результата/исключений: + # fut.add_done_callback(lambda f: f.exception() and self.logger.error(f"Broadcast error: {f.exception()}")) + except Exception as e: + self.logger.error(f"Failed to schedule broadcast: {e}") + + async def _send_to_connections(self, message_str: str): + """ + Реальная рассылка по всем активным соединениям (внутри event loop). + """ + if not self.active_connections: + return + + disconnected = [] + # Снимок, чтобы итерация была стабильной + for websocket in list(self.active_connections): + try: + await websocket.send_text(message_str) + except Exception as e: + self.logger.error(f"Error broadcasting to a websocket: {e}") + disconnected.append(websocket) + + # Очистим отключившиеся + for websocket in disconnected: + try: + await self.disconnect(websocket) + except Exception as e: + self.logger.error(f"Error during disconnect cleanup: {e}") diff --git a/vna_system/core/settings/calibration_manager.py b/vna_system/core/settings/calibration_manager.py index 9914dce..b75cd3f 100644 --- a/vna_system/core/settings/calibration_manager.py +++ b/vna_system/core/settings/calibration_manager.py @@ -7,9 +7,9 @@ from enum import Enum from pathlib import Path from typing import Dict, List -from vna_system.config import config as cfg +from vna_system.core import config as cfg from vna_system.core.acquisition.sweep_buffer import SweepData -from .preset_manager import ConfigPreset, VNAMode, PresetManager +from .preset_manager import ConfigPreset, VNAMode class CalibrationStandard(Enum): @@ -64,7 +64,7 @@ class CalibrationSet: class CalibrationManager: - def __init__(self, preset_manager: PresetManager, base_dir: Path | None = None): + def __init__(self, base_dir: Path | None = None): self.base_dir = Path(base_dir or cfg.BASE_DIR) self.calibration_dir = self.base_dir / "calibration" self.current_calibration_symlink = self.calibration_dir / "current_calibration" @@ -74,9 +74,6 @@ class CalibrationManager: # Current working calibration set self._current_working_set: CalibrationSet | None = None - # Preset manager for parsing filenames - self.preset_manager = preset_manager - def start_new_calibration(self, preset: ConfigPreset) -> CalibrationSet: """Start new calibration set for preset""" self._current_working_set = CalibrationSet(preset) @@ -266,7 +263,7 @@ class CalibrationManager: self.current_calibration_symlink.symlink_to(relative_path) - def get_current_calibration(self) -> CalibrationSet | None: + def get_current_calibration(self, current_preset: ConfigPreset) -> CalibrationSet | None: """Get currently selected calibration as CalibrationSet""" if not self.current_calibration_symlink.exists(): return None @@ -276,14 +273,12 @@ class CalibrationManager: calibration_name = target.name preset_name = target.parent.name - # Parse preset from filename - preset = self.preset_manager._parse_filename(f"{preset_name}.bin") + # If current_preset matches, use it + if current_preset.filename == f"{preset_name}.bin": + return self.load_calibration_set(current_preset, calibration_name) + else: + raise RuntimeError("Current calibration is set and is meant for different preset.") - if preset is None: - return None - - # Load and return the calibration set - return self.load_calibration_set(preset, calibration_name) except Exception: return None diff --git a/vna_system/core/settings/preset_manager.py b/vna_system/core/settings/preset_manager.py index 54f5f64..56a95ef 100644 --- a/vna_system/core/settings/preset_manager.py +++ b/vna_system/core/settings/preset_manager.py @@ -6,7 +6,7 @@ from enum import Enum from pathlib import Path from typing import List -from vna_system.config import config as cfg +from vna_system.core import config as cfg class VNAMode(Enum): diff --git a/vna_system/core/settings/settings_manager.py b/vna_system/core/settings/settings_manager.py index 8225882..e53ba15 100644 --- a/vna_system/core/settings/settings_manager.py +++ b/vna_system/core/settings/settings_manager.py @@ -4,7 +4,7 @@ import logging from pathlib import Path from typing import Dict, List -from vna_system.config import config as cfg +from vna_system.core import config as cfg from vna_system.core.acquisition.sweep_buffer import SweepData from .preset_manager import PresetManager, ConfigPreset, VNAMode from .calibration_manager import CalibrationManager, CalibrationSet, CalibrationStandard @@ -27,7 +27,7 @@ class VNASettingsManager: # Initialize sub-managers self.preset_manager = PresetManager(self.base_dir / "binary_input") - self.calibration_manager = CalibrationManager(self.preset_manager, self.base_dir) + self.calibration_manager = CalibrationManager(self.base_dir) # ---------- Preset Management ---------- @@ -96,7 +96,11 @@ class VNASettingsManager: def get_current_calibration(self) -> CalibrationSet | None: """Get currently selected calibration set (saved and active via symlink)""" - return self.calibration_manager.get_current_calibration() + current_preset = self.get_current_preset() + if current_preset is not None: + return self.calibration_manager.get_current_calibration(current_preset) + else: + return None def get_calibration_info(self, calibration_name: str, preset: ConfigPreset | None = None) -> Dict: """Get calibration information""" diff --git a/vna_system/core/singletons.py b/vna_system/core/singletons.py index b0957f6..80be95e 100644 --- a/vna_system/core/singletons.py +++ b/vna_system/core/singletons.py @@ -5,13 +5,21 @@ Global singleton instances for the VNA system. This module centralizes all singleton instances to avoid global variables scattered throughout the codebase. """ +from pathlib import Path from vna_system.core.acquisition.data_acquisition import VNADataAcquisition -from vna_system.core.processing.sweep_processor import SweepProcessingManager -from vna_system.core.processing.websocket_handler import WebSocketManager +from vna_system.core.processors.storage.data_storage import DataStorage from vna_system.core.settings.settings_manager import VNASettingsManager +from vna_system.core.processors.manager import ProcessorManager +from vna_system.core.processors.websocket_handler import ProcessorWebSocketHandler # Global singleton instances vna_data_acquisition_instance: VNADataAcquisition = VNADataAcquisition() -processing_manager: SweepProcessingManager = SweepProcessingManager() -websocket_manager: WebSocketManager = WebSocketManager(processing_manager) -settings_manager: VNASettingsManager = VNASettingsManager() \ No newline at end of file +settings_manager: VNASettingsManager = VNASettingsManager() + +# Processor system +processor_config_dir = Path("vna_system/core/processors/configs") +processor_manager: ProcessorManager = ProcessorManager(vna_data_acquisition_instance.sweep_buffer, settings_manager, processor_config_dir) +data_storage = DataStorage() +processor_websocket_handler: ProcessorWebSocketHandler = ProcessorWebSocketHandler( + processor_manager, data_storage +) \ No newline at end of file diff --git a/vna_system/scripts/start.sh b/vna_system/scripts/start.sh index 097af56..5705385 100755 --- a/vna_system/scripts/start.sh +++ b/vna_system/scripts/start.sh @@ -89,4 +89,6 @@ log_info "Press Ctrl+C to stop the server" echo # Run the main application -exec python3 -m vna_system.api.main \ No newline at end of file +exec python3 -m vna_system.api.main + + diff --git a/vna_system/web_ui/static/css/acquisition.css b/vna_system/web_ui/static/css/acquisition.css new file mode 100644 index 0000000..6f20a8b --- /dev/null +++ b/vna_system/web_ui/static/css/acquisition.css @@ -0,0 +1,81 @@ +/* Acquisition Controls */ +.acquisition-controls { + display: flex; + gap: var(--space-2); + margin-bottom: var(--space-3); +} + +.acquisition-status { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-4); + padding: var(--space-2-5); + background-color: var(--bg-tertiary); + border: 1px solid var(--border-primary); + border-radius: var(--radius-md); + font-size: var(--font-size-sm); +} + +.acquisition-mode { + display: flex; + align-items: center; + gap: var(--space-1-5); +} + +.mode-label { + color: var(--text-secondary); + font-weight: var(--font-weight-medium); +} + +.mode-value { + color: var(--text-primary); + font-weight: var(--font-weight-semibold); +} + +/* Status indicator variations */ +.status-indicator__dot--idle { + background-color: var(--color-gray-400); +} + +.status-indicator__dot--running { + background-color: var(--color-success-500); + animation: pulse 2s infinite; +} + +.status-indicator__dot--paused { + background-color: var(--color-warning-500); +} + +.status-indicator__dot--error { + background-color: var(--color-error-500); +} + +@keyframes pulse { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.6; + } +} + +/* Button accent variant */ +.btn--accent { + background-color: var(--color-accent-600, #7c3aed); + color: white; +} + +.btn--accent:hover { + background-color: var(--color-accent-700, #6d28d9); +} + +.btn--accent.btn--bordered { + border-color: var(--color-accent-500, #8b5cf6); + box-shadow: 0 0 0 1px var(--color-accent-600, #7c3aed), var(--shadow-sm); +} + +.btn--accent.btn--bordered:hover { + box-shadow: 0 0 0 1px var(--color-accent-500, #8b5cf6), var(--shadow-md); + transform: translateY(-1px); +} \ No newline at end of file diff --git a/vna_system/web_ui/static/css/components.css b/vna_system/web_ui/static/css/components.css index c6fe495..95093fc 100644 --- a/vna_system/web_ui/static/css/components.css +++ b/vna_system/web_ui/static/css/components.css @@ -223,6 +223,7 @@ display: flex; align-items: center; justify-content: space-between; + border-radius: var(--radius-xl); padding: var(--space-4) var(--space-5); background-color: var(--bg-tertiary); border-bottom: 1px solid var(--border-primary); @@ -272,16 +273,30 @@ } .chart-card__content { + display: flex; position: relative; padding: var(--space-4); min-height: 450px; + gap: var(--space-4); } .chart-card__plot { - width: 100% !important; + flex: 1; height: 420px !important; min-height: 400px; position: relative; + min-width: 0; /* Allows flex item to shrink */ +} + +.chart-card__settings { + width: 250px; + flex-shrink: 0; + background-color: var(--bg-secondary); + border: 1px solid var(--border-primary); + border-radius: var(--radius-lg); + padding: var(--space-3); + overflow-y: auto; + max-height: 420px; } .chart-card__plot .js-plotly-plot { @@ -294,12 +309,85 @@ height: 100% !important; } +/* Fix Plotly toolbar positioning with project design system */ +.chart-card__plot .modebar { + position: absolute !important; + top: var(--space-3) !important; + right: var(--space-3) !important; + z-index: 1000 !important; + background: var(--bg-surface) !important; + border: 1px solid var(--border-primary) !important; + border-radius: var(--radius-lg) !important; + padding: var(--space-1) !important; + box-shadow: var(--shadow-md) !important; + backdrop-filter: blur(8px) !important; + display: flex !important; + flex-direction: row !important; + align-items: center !important; + gap: var(--space-1) !important; +} + +.chart-card__plot .modebar-group { + display: flex !important; + flex-direction: row !important; + margin: 0 !important; + padding: 0 !important; +} + +.chart-card__plot .modebar-group:not(:last-child)::after { + content: '' !important; + width: 1px !important; + height: 16px !important; + background: var(--border-primary) !important; + margin: 0 var(--space-1) !important; +} + +.chart-card__plot .modebar-btn { + width: 28px !important; + height: 28px !important; + margin: 0 !important; + padding: var(--space-1-5) !important; + border: none !important; + background: transparent !important; + border-radius: var(--radius-default) !important; + cursor: pointer !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + transition: all var(--transition-fast) !important; + color: var(--text-secondary) !important; +} + +.chart-card__plot .modebar-btn:hover { + background: var(--bg-surface-hover) !important; + color: var(--text-primary) !important; + transform: translateY(-1px) !important; +} + +.chart-card__plot .modebar-btn svg { + width: 14px !important; + height: 14px !important; + fill: currentColor !important; + transition: fill var(--transition-fast) !important; +} + +.chart-card__plot .modebar-btn--active { + background: var(--color-primary-600) !important; + color: white !important; +} + +.chart-card__plot .modebar-btn--active:hover { + background: var(--color-primary-700) !important; + transform: translateY(-1px) !important; +} + .chart-card__meta { display: flex; justify-content: space-between; align-items: center; padding: var(--space-3) var(--space-5); background-color: var(--bg-primary); + border-radius: var(--radius-xl); border-top: 1px solid var(--border-primary); font-size: var(--font-size-xs); color: var(--text-tertiary); @@ -458,4 +546,357 @@ .form-input::placeholder { color: var(--text-tertiary); +} + +/* Processor Settings */ +.processor-settings { + display: flex; + flex-direction: column; + gap: var(--space-3); +} + +.processor-settings--empty { + padding: var(--space-4); + text-align: center; + color: var(--text-tertiary); + font-size: var(--font-size-sm); +} + +.processor-config { + background-color: var(--bg-secondary); + border: 1px solid var(--border-primary); + border-radius: var(--radius-lg); + padding: var(--space-3); +} + +.processor-config__header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: var(--space-3); + cursor: pointer; + user-select: none; +} + +.processor-config__title { + display: flex; + align-items: center; + gap: var(--space-2); + font-weight: var(--font-weight-medium); + color: var(--text-primary); + font-size: var(--font-size-sm); +} + +.processor-config__icon { + width: 16px; + height: 16px; + color: var(--color-primary-500); +} + +.processor-config__toggle { + width: 16px; + height: 16px; + color: var(--text-tertiary); + transition: all var(--transition-fast); + transform: rotate(0deg); +} + +.processor-config--expanded .processor-config__toggle { + transform: rotate(180deg); +} + +.processor-config__content { + display: none; + grid-template-columns: 1fr; + gap: var(--space-3); +} + +.processor-config--expanded .processor-config__content { + display: grid; +} + +.processor-param { + display: flex; + flex-direction: column; + gap: var(--space-2); +} + +.processor-param__label { + font-size: var(--font-size-xs); + font-weight: var(--font-weight-medium); + color: var(--text-secondary); +} + +.processor-param__slider { + -webkit-appearance: none; + appearance: none; + width: 100%; + height: 6px; + background-color: var(--bg-primary); + border-radius: var(--radius-full); + outline: none; + cursor: pointer; +} + +.processor-param__slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 18px; + height: 18px; + background-color: var(--color-primary-500); + border-radius: var(--radius-full); + cursor: pointer; + border: 2px solid var(--bg-primary); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + +.processor-param__slider::-moz-range-thumb { + width: 18px; + height: 18px; + background-color: var(--color-primary-500); + border-radius: var(--radius-full); + cursor: pointer; + border: 2px solid var(--bg-primary); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + +.processor-param__toggle { + position: relative; + display: inline-block; + width: 44px; + height: 24px; +} + +.processor-param__toggle input { + opacity: 0; + width: 0; + height: 0; +} + +.processor-param__toggle-slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: var(--bg-primary); + border: 1px solid var(--border-primary); + transition: var(--transition-fast); + border-radius: var(--radius-full); +} + +.processor-param__toggle-slider:before { + position: absolute; + content: ""; + height: 16px; + width: 16px; + left: 3px; + bottom: 3px; + background-color: var(--text-tertiary); + transition: var(--transition-fast); + border-radius: var(--radius-full); +} + +input:checked + .processor-param__toggle-slider { + background-color: var(--color-primary-500); + border-color: var(--color-primary-500); +} + +input:checked + .processor-param__toggle-slider:before { + transform: translateX(20px); + background-color: white; +} + +.processor-param__select { + padding: var(--space-2) var(--space-3); + background-color: var(--bg-primary); + border: 1px solid var(--border-primary); + border-radius: var(--radius-default); + font-size: var(--font-size-sm); + color: var(--text-primary); + cursor: pointer; + transition: all var(--transition-fast); +} + +.processor-param__select:focus { + outline: none; + border-color: var(--color-primary-500); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +.processor-param__value { + font-size: var(--font-size-xs); + color: var(--text-tertiary); + text-align: right; +} + +/* Chart Settings Styles */ +.chart-settings { + height: 100%; + display: flex; + flex-direction: column; +} + +.chart-settings__header { + font-size: var(--font-size-sm); + font-weight: var(--font-weight-medium); + color: var(--text-primary); + margin-bottom: var(--space-3); + padding-bottom: var(--space-2); + border-bottom: 1px solid var(--border-primary); +} + +.chart-settings__controls { + display: flex; + flex-direction: column; + gap: var(--space-3); + flex: 1; +} + +.settings-empty { + color: var(--text-tertiary); + font-size: var(--font-size-sm); + text-align: center; + padding: var(--space-4); +} + +.chart-setting { + display: flex; + flex-direction: column; + gap: var(--space-2); +} + +.chart-setting__label { + font-size: var(--font-size-xs); + font-weight: var(--font-weight-medium); + color: var(--text-secondary); + display: flex; + justify-content: space-between; + align-items: center; +} + +.chart-setting__value { + font-size: var(--font-size-xs); + color: var(--text-tertiary); + font-weight: var(--font-weight-normal); +} + +.chart-setting__slider { + -webkit-appearance: none; + appearance: none; + width: 100%; + height: 6px; + background-color: var(--bg-primary); + border-radius: var(--radius-full); + outline: none; + cursor: pointer; +} + +.chart-setting__slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 16px; + height: 16px; + background-color: var(--color-primary-500); + border-radius: var(--radius-full); + cursor: pointer; + border: 2px solid var(--bg-secondary); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + +.chart-setting__toggle { + display: flex; + align-items: center; + gap: var(--space-2); + cursor: pointer; +} + +.chart-setting__toggle input { + position: relative; + width: 36px; + height: 20px; + -webkit-appearance: none; + appearance: none; + background-color: var(--bg-primary); + border: 1px solid var(--border-primary); + border-radius: var(--radius-full); + cursor: pointer; + transition: all var(--transition-fast); +} + +.chart-setting__toggle input:before { + content: ''; + position: absolute; + width: 16px; + height: 16px; + border-radius: var(--radius-full); + background-color: var(--text-tertiary); + transition: all var(--transition-fast); + top: 1px; + left: 1px; +} + +.chart-setting__toggle input:checked { + background-color: var(--color-primary-500); + border-color: var(--color-primary-500); +} + +.chart-setting__toggle input:checked:before { + transform: translateX(16px); + background-color: white; +} + +.chart-setting__select, +.chart-setting__input { + padding: var(--space-2); + background-color: var(--bg-primary); + border: 1px solid var(--border-primary); + border-radius: var(--radius-default); + font-size: var(--font-size-sm); + color: var(--text-primary); + cursor: pointer; + transition: all var(--transition-fast); +} + +.chart-setting__select:focus, +.chart-setting__input:focus { + outline: none; + border-color: var(--color-primary-500); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +/* Mobile Responsive Styles for Chart Settings */ +@media (max-width: 1024px) { + .chart-card__content { + flex-direction: column; + gap: var(--space-3); + } + + .chart-card__settings { + width: 100%; + max-height: 200px; + order: 1; + } + + .chart-card__plot { + order: 2; + height: 350px !important; + min-height: 300px; + } +} + +@media (max-width: 768px) { + .chart-card__content { + padding: var(--space-3); + } + + .chart-card__plot { + height: 300px !important; + min-height: 250px; + } + + .chart-card__settings { + max-height: 150px; + } } \ No newline at end of file diff --git a/vna_system/web_ui/static/css/layout.css b/vna_system/web_ui/static/css/layout.css index 1c25a0d..8ede981 100644 --- a/vna_system/web_ui/static/css/layout.css +++ b/vna_system/web_ui/static/css/layout.css @@ -240,8 +240,8 @@ body { } .charts-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(550px, 1fr)); + display: flex; + flex-direction: column; gap: var(--space-6); margin-bottom: var(--space-6); } diff --git a/vna_system/web_ui/static/js/main.js b/vna_system/web_ui/static/js/main.js index 4902daf..e3af3ee 100644 --- a/vna_system/web_ui/static/js/main.js +++ b/vna_system/web_ui/static/js/main.js @@ -9,6 +9,7 @@ import { UIManager } from './modules/ui.js'; import { NotificationManager } from './modules/notifications.js'; import { StorageManager } from './modules/storage.js'; import { SettingsManager } from './modules/settings.js'; +import { AcquisitionManager } from './modules/acquisition.js'; /** * Main Application Class @@ -35,13 +36,21 @@ class VNADashboard { } }; - // Initialize managers + // Core managers (order matters now) this.storage = new StorageManager(); this.notifications = new NotificationManager(); - this.ui = new UIManager(this.notifications); + + // Charts first (used by UI, but UI init не зависит от готовности charts) this.charts = new ChartManager(this.config.charts, this.notifications); + + // WebSocket before UI (UI подписывается на события ws) this.websocket = new WebSocketManager(this.config.websocket, this.notifications); - this.settings = new SettingsManager(this.notifications); + + // UI получает зависимости извне + this.ui = new UIManager(this.notifications, this.websocket, this.charts); + + this.acquisition = new AcquisitionManager(this.notifications); + this.settings = new SettingsManager(this.notifications, this.websocket, this.acquisition); // Bind methods this.handleWebSocketData = this.handleWebSocketData.bind(this); @@ -94,11 +103,14 @@ class VNADashboard { * Initialize all modules */ async initializeModules() { - // Initialize UI manager first + // Initialize chart manager (DOM containers) + await this.charts.init(); + + // Initialize UI manager await this.ui.init(); - // Initialize chart manager - await this.charts.init(); + // Initialize acquisition manager first (required by settings) + this.acquisition.initialize(); // Initialize settings manager await this.settings.init(); @@ -109,11 +121,8 @@ class VNADashboard { // Load and apply user preferences after UI is initialized const preferences = await this.storage.getPreferences(); if (preferences) { - // Clean up old invalid processors from preferences this.cleanupPreferences(preferences); this.applyPreferences(preferences); - - // Save cleaned preferences back to storage this.savePreferences(); } } @@ -122,17 +131,8 @@ class VNADashboard { * Set up event listeners */ setupEventListeners() { - // WebSocket events + // WebSocket events - UIManager handles most of this this.websocket.on('data', this.handleWebSocketData); - this.websocket.on('connected', () => { - this.ui.setConnectionStatus('connected'); - }); - this.websocket.on('disconnected', () => { - this.ui.setConnectionStatus('disconnected'); - }); - this.websocket.on('connecting', () => { - this.ui.setConnectionStatus('connecting'); - }); // Browser events document.addEventListener('visibilitychange', this.handleVisibilityChange); @@ -149,22 +149,19 @@ class VNADashboard { // Navigation this.ui.onViewChange((view) => { console.log(`📱 Switched to view: ${view}`); - - // Refresh settings when switching to settings view if (view === 'settings') { this.settings.refresh(); } }); - // Processor toggles + // Processor toggles (UI-only visibility) this.ui.onProcessorToggle((processor, enabled) => { console.log(`🔧 Processor ${processor}: ${enabled ? 'enabled' : 'disabled'}`); this.charts.toggleProcessor(processor, enabled); this.savePreferences(); }); - // Chart clearing removed - users can disable processors individually - + // Export this.ui.onExportData(() => { console.log('📊 Exporting chart data...'); this.exportData(); @@ -172,33 +169,16 @@ class VNADashboard { } /** - * Handle WebSocket data + * Handle non-processor WebSocket data */ handleWebSocketData(data) { - try { - if (data.type === 'processing_result') { - // Add chart data - this.charts.addData(data.data); - - // Update processor in UI - this.ui.updateProcessorFromData(data.data.processor_name); - - // Update general stats - this.ui.updateStats({ - sweepCount: data.data.sweep_number, - lastUpdate: new Date() - }); - } else if (data.type === 'system_status') { - this.ui.updateSystemStatus(data.data); - } - } catch (error) { - console.error('❌ Error handling WebSocket data:', error); - this.notifications.show({ - type: 'error', - title: 'Data Processing Error', - message: 'Failed to process incoming data' - }); + // UIManager handles processor_result directly, we only handle other types + if (data.type === 'system_status') { + this.ui.updateSystemStatus(data.data); + } else if (data.type === 'processor_history') { + console.log('📜 Received processor history:', data); } + // Note: 'error' is handled by WebSocket manager directly } /** @@ -206,10 +186,8 @@ class VNADashboard { */ handleVisibilityChange() { if (document.hidden) { - // Pause expensive operations when hidden this.charts.pause(); } else { - // Resume when visible this.charts.resume(); } } @@ -245,9 +223,7 @@ class VNADashboard { exportData() { try { const data = this.charts.exportData(); - const blob = new Blob([JSON.stringify(data, null, 2)], { - type: 'application/json' - }); + const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); @@ -277,15 +253,11 @@ class VNADashboard { * Clean up old/invalid preferences */ cleanupPreferences(preferences) { - // List of invalid/old processor names that should be removed const invalidProcessors = ['sweep_counter']; - if (preferences.disabledProcessors && Array.isArray(preferences.disabledProcessors)) { - // Remove invalid processors preferences.disabledProcessors = preferences.disabledProcessors.filter( processor => !invalidProcessors.includes(processor) ); - if (preferences.disabledProcessors.length === 0) { delete preferences.disabledProcessors; } @@ -298,9 +270,9 @@ class VNADashboard { applyPreferences(preferences) { try { if (preferences.disabledProcessors && Array.isArray(preferences.disabledProcessors)) { - preferences.disabledProcessors.forEach(processor => { + preferences.disabledProcessors.forEach(proc => { if (this.ui && typeof this.ui.setProcessorEnabled === 'function') { - this.ui.setProcessorEnabled(processor, false); + this.ui.setProcessorEnabled(proc, false); } }); } @@ -348,14 +320,11 @@ class VNADashboard { console.log('🧹 Cleaning up VNA Dashboard...'); - // Remove event listeners document.removeEventListener('visibilitychange', this.handleVisibilityChange); window.removeEventListener('beforeunload', this.handleBeforeUnload); - // Disconnect WebSocket this.websocket.disconnect(); - // Cleanup managers this.charts.destroy(); this.settings.destroy(); this.ui.destroy(); @@ -395,7 +364,6 @@ if (typeof process !== 'undefined' && process?.env?.NODE_ENV === 'development') ui: () => window.vnaDashboard?.ui }; } else { - // Browser environment debug helpers window.debug = { dashboard: () => window.vnaDashboard, websocket: () => window.vnaDashboard?.websocket, @@ -403,4 +371,4 @@ if (typeof process !== 'undefined' && process?.env?.NODE_ENV === 'development') ui: () => window.vnaDashboard?.ui, settings: () => window.vnaDashboard?.settings }; -} \ No newline at end of file +} diff --git a/vna_system/web_ui/static/js/modules/acquisition.js b/vna_system/web_ui/static/js/modules/acquisition.js new file mode 100644 index 0000000..9109d9d --- /dev/null +++ b/vna_system/web_ui/static/js/modules/acquisition.js @@ -0,0 +1,255 @@ +/** + * Acquisition Control Module + * Handles VNA data acquisition control via REST API + */ + +export class AcquisitionManager { + constructor(notifications) { + this.notifications = notifications; + this.isInitialized = false; + this.currentStatus = { + running: false, + paused: false, + continuous_mode: true, + sweep_count: 0 + }; + + // Bind methods + this.handleStartClick = this.handleStartClick.bind(this); + this.handleStopClick = this.handleStopClick.bind(this); + this.handleSingleSweepClick = this.handleSingleSweepClick.bind(this); + this.updateStatus = this.updateStatus.bind(this); + } + + initialize() { + if (this.isInitialized) return; + + // Get DOM elements + this.elements = { + startBtn: document.getElementById('startBtn'), + stopBtn: document.getElementById('stopBtn'), + singleSweepBtn: document.getElementById('singleSweepBtn'), + statusText: document.getElementById('acquisitionStatusText'), + statusDot: document.querySelector('.status-indicator__dot'), + modeText: document.getElementById('acquisitionModeText') + }; + + // Add event listeners + this.elements.startBtn?.addEventListener('click', this.handleStartClick); + this.elements.stopBtn?.addEventListener('click', this.handleStopClick); + this.elements.singleSweepBtn?.addEventListener('click', this.handleSingleSweepClick); + + // Initial status update + this.updateStatus(); + + // Poll status every 2 seconds + this.statusInterval = setInterval(this.updateStatus, 2000); + + this.isInitialized = true; + console.log('AcquisitionManager initialized'); + } + + async handleStartClick() { + try { + this.setButtonLoading(this.elements.startBtn, true); + + const response = await fetch('/api/v1/acquisition/start', { + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }); + + const result = await response.json(); + + if (result.success) { + this.notifications.show(result.message, 'success'); + await this.updateStatus(); + } else { + this.notifications.show(result.error || 'Failed to start acquisition', 'error'); + } + } catch (error) { + console.error('Error starting acquisition:', error); + this.notifications.show('Failed to start acquisition', 'error'); + } finally { + this.setButtonLoading(this.elements.startBtn, false); + } + } + + async handleStopClick() { + try { + this.setButtonLoading(this.elements.stopBtn, true); + + const response = await fetch('/api/v1/acquisition/stop', { + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }); + + const result = await response.json(); + + if (result.success) { + this.notifications.show(result.message, 'success'); + await this.updateStatus(); + } else { + this.notifications.show(result.error || 'Failed to stop acquisition', 'error'); + } + } catch (error) { + console.error('Error stopping acquisition:', error); + this.notifications.show('Failed to stop acquisition', 'error'); + } finally { + this.setButtonLoading(this.elements.stopBtn, false); + } + } + + async handleSingleSweepClick() { + try { + this.setButtonLoading(this.elements.singleSweepBtn, true); + + const response = await fetch('/api/v1/acquisition/single-sweep', { + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }); + + const result = await response.json(); + + if (result.success) { + this.notifications.show(result.message, 'success'); + await this.updateStatus(); + } else { + this.notifications.show(result.error || 'Failed to trigger single sweep', 'error'); + } + } catch (error) { + console.error('Error triggering single sweep:', error); + this.notifications.show('Failed to trigger single sweep', 'error'); + } finally { + this.setButtonLoading(this.elements.singleSweepBtn, false); + } + } + + async updateStatus() { + try { + const response = await fetch('/api/v1/acquisition/status'); + const status = await response.json(); + + this.currentStatus = status; + this.updateUI(status); + } catch (error) { + console.error('Error getting acquisition status:', error); + this.updateUI({ + running: false, + paused: false, + continuous_mode: true, + sweep_count: 0 + }); + } + } + + updateUI(status) { + // Update status text and indicator + let statusText = 'Idle'; + let statusClass = 'status-indicator__dot--idle'; + + if (status.running) { + if (status.paused) { + statusText = 'Stopped'; + statusClass = 'status-indicator__dot--paused'; + } else { + statusText = 'Running'; + statusClass = 'status-indicator__dot--running'; + } + } + + if (this.elements.statusText) { + this.elements.statusText.textContent = statusText; + } + + if (this.elements.statusDot) { + this.elements.statusDot.className = `status-indicator__dot ${statusClass}`; + } + + // Update mode text + if (this.elements.modeText) { + this.elements.modeText.textContent = status.continuous_mode ? 'Continuous' : 'Single'; + } + + // Update button states + if (this.elements.startBtn) { + this.elements.startBtn.disabled = status.running && !status.paused; + } + + if (this.elements.stopBtn) { + this.elements.stopBtn.disabled = !status.running || status.paused; + } + + if (this.elements.singleSweepBtn) { + this.elements.singleSweepBtn.disabled = !status.running; + } + + // Update sweep count in header if available + const sweepCountEl = document.getElementById('sweepCount'); + if (sweepCountEl) { + sweepCountEl.textContent = status.sweep_count || 0; + } + } + + setButtonLoading(button, loading) { + if (!button) return; + + if (loading) { + button.disabled = true; + button.classList.add('loading'); + const icon = button.querySelector('i'); + if (icon) { + icon.setAttribute('data-lucide', 'loader-2'); + icon.style.animation = 'spin 1s linear infinite'; + // Re-initialize lucide for the changed icon + if (window.lucide) { + window.lucide.createIcons(); + } + } + } else { + button.disabled = false; + button.classList.remove('loading'); + const icon = button.querySelector('i'); + if (icon) { + icon.style.animation = ''; + // Restore original icon + const buttonId = button.id; + const originalIcons = { + 'startBtn': 'play', + 'stopBtn': 'square', + 'singleSweepBtn': 'zap' + }; + const originalIcon = originalIcons[buttonId]; + if (originalIcon) { + icon.setAttribute('data-lucide', originalIcon); + if (window.lucide) { + window.lucide.createIcons(); + } + } + } + } + } + + // Public method to trigger single sweep programmatically + async triggerSingleSweep() { + return await this.handleSingleSweepClick(); + } + + // Public method to get current acquisition status + isRunning() { + return this.currentStatus.running && !this.currentStatus.paused; + } + + destroy() { + if (this.statusInterval) { + clearInterval(this.statusInterval); + } + + // Remove event listeners + this.elements?.startBtn?.removeEventListener('click', this.handleStartClick); + this.elements?.stopBtn?.removeEventListener('click', this.handleStopClick); + this.elements?.singleSweepBtn?.removeEventListener('click', this.handleSingleSweepClick); + + this.isInitialized = false; + console.log('AcquisitionManager destroyed'); + } +} \ No newline at end of file diff --git a/vna_system/web_ui/static/js/modules/charts.js b/vna_system/web_ui/static/js/modules/charts.js index bb874a4..cf07fcc 100644 --- a/vna_system/web_ui/static/js/modules/charts.js +++ b/vna_system/web_ui/static/js/modules/charts.js @@ -8,21 +8,17 @@ export class ChartManager { this.config = config; this.notifications = notifications; - // Chart storage - this.charts = new Map(); // processor_name -> chart instance - this.chartData = new Map(); // processor_name -> data array + this.charts = new Map(); // id -> { element, plotContainer, isVisible } + this.chartData = new Map(); // id -> [{ timestamp, metadata, data, plotly_config }] this.disabledProcessors = new Set(); - // UI elements this.chartsGrid = null; this.emptyState = null; - // Update management this.updateQueue = new Map(); this.isUpdating = false; this.isPaused = false; - // Performance tracking this.performanceStats = { chartsCreated: 0, updatesProcessed: 0, @@ -30,290 +26,181 @@ export class ChartManager { lastUpdateTime: null }; - // Plotly theme configuration + // Debounce timers for settings updates + this.settingDebounceTimers = {}; + + // Track last setting values to prevent feedback loops + this.lastSettingValues = {}; + this.plotlyConfig = { displayModeBar: true, - modeBarButtonsToRemove: [ - 'select2d', 'lasso2d', 'hoverClosestCartesian', - 'hoverCompareCartesian', 'toggleSpikelines' - ], + modeBarButtonsToRemove: ['select2d','lasso2d','hoverClosestCartesian','hoverCompareCartesian','toggleSpikelines'], displaylogo: false, - responsive: false, // Disable responsive to avoid resize issues + responsive: false, doubleClick: 'reset', - toImageButtonOptions: { - format: 'png', - filename: 'vna_chart', - height: 600, - width: 800, - scale: 1 - } + toImageButtonOptions: { format: 'png', filename: 'vna_chart', height: 600, width: 800, scale: 1 } }; this.plotlyLayout = { plot_bgcolor: 'transparent', paper_bgcolor: 'transparent', - font: { - family: 'Inter, -apple-system, BlinkMacSystemFont, sans-serif', - size: 12, - color: '#f1f5f9' // --text-secondary + font: { family: 'Inter, -apple-system, BlinkMacSystemFont, sans-serif', size: 12, color: '#f1f5f9' }, + colorway: ['#3b82f6','#22c55e','#eab308','#ef4444','#8b5cf6','#06b6d4','#f59e0b','#10b981'], + margin: { l: 60, r: 50, t: 50, b: 60 }, // Increased right and top margins + showlegend: true, + legend: { + orientation: 'v', + x: 1.02, + y: 1, + xanchor: 'left', + yanchor: 'top', + bgcolor: 'rgba(30, 41, 59, 0.9)', + bordercolor: '#475569', + borderwidth: 1, + font: { size: 10, color: '#f1f5f9' } }, - colorway: [ - '#3b82f6', '#22c55e', '#eab308', '#ef4444', - '#8b5cf6', '#06b6d4', '#f59e0b', '#10b981' - ], - margin: { l: 60, r: 40, t: 40, b: 60 }, - showlegend: false, - xaxis: { - gridcolor: '#334155', - zerolinecolor: '#475569', - color: '#cbd5e1', - fixedrange: false // Allow zoom/pan - }, - yaxis: { - gridcolor: '#334155', - zerolinecolor: '#475569', - color: '#cbd5e1', - fixedrange: false // Allow zoom/pan - }, - autosize: true, - width: null, // Let CSS control width - height: null // Let CSS control height + xaxis: { gridcolor: '#334155', zerolinecolor: '#475569', color: '#cbd5e1', fixedrange: false }, + yaxis: { gridcolor: '#334155', zerolinecolor: '#475569', color: '#cbd5e1', fixedrange: false }, + autosize: true, width: null, height: null }; } - /** - * Initialize chart manager - */ async init() { console.log('📊 Initializing Chart Manager...'); - - // Get DOM elements this.chartsGrid = document.getElementById('chartsGrid'); this.emptyState = document.getElementById('emptyState'); - - if (!this.chartsGrid || !this.emptyState) { - throw new Error('Required DOM elements not found'); - } - - // Ensure Plotly is available - if (typeof Plotly === 'undefined') { - throw new Error('Plotly.js not loaded'); - } - + if (!this.chartsGrid || !this.emptyState) throw new Error('Required DOM elements not found'); + if (typeof Plotly === 'undefined') throw new Error('Plotly.js not loaded'); console.log('✅ Chart Manager initialized'); } - /** - * Add new data from WebSocket - */ - addData(processingResult) { + /** Новый входной формат — прямо payload от WS: processor_result */ + addResult(payload) { try { - const { processor_name, sweep_number, plotly_figure, data } = processingResult; - - if (!processor_name || !plotly_figure) { - console.warn('⚠️ Invalid processing result:', processingResult); + const { processor_id, timestamp, plotly_config, metadata, data } = payload; + if (!processor_id) { + console.warn('⚠️ Invalid result - missing processor_id:', payload); return; } - // Check if processor is disabled - if (this.disabledProcessors.has(processor_name)) { - console.log(`⏸️ Skipping disabled processor: ${processor_name}`); + if (this.disabledProcessors.has(processor_id)) { + console.log(`⏸️ Skipping disabled processor: ${processor_id}`); return; } - // Store data - if (!this.chartData.has(processor_name)) { - this.chartData.set(processor_name, []); - } - - const dataArray = this.chartData.get(processor_name); - dataArray.push({ - sweep_number, - plotly_figure, - data, - timestamp: new Date() + // Store only the latest data (no history needed) + this.chartData.set(processor_id, { + timestamp: new Date((timestamp ?? Date.now()) * 1000), // если приходит epoch seconds + metadata: metadata || {}, + data: data || {}, + plotly_config: plotly_config || { data: [], layout: {} } }); - // Limit data points for performance - if (dataArray.length > this.config.maxDataPoints) { - dataArray.splice(0, dataArray.length - this.config.maxDataPoints); - } + if (!this.charts.has(processor_id)) this.createChart(processor_id); - // Create or update chart - if (!this.charts.has(processor_name)) { - this.createChart(processor_name); - } - - this.updateChart(processor_name, plotly_figure); + this.updateChart(processor_id, plotly_config || { data: [], layout: {} }); this.hideEmptyState(); - - } catch (error) { - console.error('❌ Error adding chart data:', error); - this.notifications.show({ - type: 'error', - title: 'Chart Error', - message: `Failed to update ${processingResult?.processor_name} chart` + } catch (e) { + console.error('❌ Error adding chart result:', e); + this.notifications?.show?.({ + type: 'error', title: 'Chart Error', message: `Failed to update chart` }); } } - /** - * Create new chart - */ - createChart(processorName) { - console.log(`📊 Creating chart for processor: ${processorName}`); + createChart(processorId) { + console.log(`📊 Creating chart for processor: ${processorId}`); + const card = this.createChartCard(processorId); + this.chartsGrid.appendChild(card); - // Create chart card HTML - const chartCard = this.createChartCard(processorName); - this.chartsGrid.appendChild(chartCard); - - // Get plot container - const plotContainer = chartCard.querySelector('.chart-card__plot'); - - // Create empty chart with proper sizing + const plotContainer = card.querySelector('.chart-card__plot'); const layout = { ...this.plotlyLayout, - title: { - text: this.formatProcessorName(processorName), - font: { size: 16, color: '#f1f5f9' } - }, + title: { text: this.formatProcessorName(processorId), font: { size: 16, color: '#f1f5f9' } }, width: plotContainer.clientWidth || 500, height: plotContainer.clientHeight || 420 }; - Plotly.newPlot(plotContainer, [], layout, this.plotlyConfig); - // Set up resize observer for proper sizing if (window.ResizeObserver) { - const resizeObserver = new ResizeObserver(() => { - if (plotContainer && plotContainer.clientWidth > 0) { - Plotly.Plots.resize(plotContainer); - } + const ro = new ResizeObserver(() => { + if (plotContainer && plotContainer.clientWidth > 0) Plotly.Plots.resize(plotContainer); }); - resizeObserver.observe(plotContainer); - - // Store observer for cleanup - plotContainer._resizeObserver = resizeObserver; + ro.observe(plotContainer); + plotContainer._resizeObserver = ro; } - // Store chart reference - this.charts.set(processorName, { - element: chartCard, - plotContainer: plotContainer, - isVisible: true - }); - + this.charts.set(processorId, { element: card, plotContainer, isVisible: true }); this.performanceStats.chartsCreated++; - // Add animation class if (this.config.animation) { - setTimeout(() => { - chartCard.classList.add('chart-card--animated'); - }, 50); + setTimeout(() => card.classList.add('chart-card--animated'), 50); } } - /** - * Update existing chart - */ - updateChart(processorName, plotlyFigure) { + updateChart(processorId, plotlyConfig) { if (this.isPaused) return; - const chart = this.charts.get(processorName); - if (!chart || !chart.plotContainer) { - console.warn(`⚠️ Chart not found for processor: ${processorName}`); - return; - } + const chart = this.charts.get(processorId); + if (!chart?.plotContainer) { console.warn(`⚠️ Chart not found for processor: ${processorId}`); return; } try { - const startTime = performance.now(); - - // Queue update to avoid blocking UI - this.queueUpdate(processorName, () => { - // Prepare layout without overriding size + const start = performance.now(); + this.queueUpdate(processorId, async () => { const updateLayout = { ...this.plotlyLayout, - ...plotlyFigure.layout, - title: { - text: this.formatProcessorName(processorName), - font: { size: 16, color: '#f1f5f9' } - } + ...(plotlyConfig.layout || {}), + title: { text: this.formatProcessorName(processorId), font: { size: 16, color: '#f1f5f9' } } }; - - // Don't override existing width/height if they're working delete updateLayout.width; delete updateLayout.height; - // Update plot with new data - Plotly.react( - chart.plotContainer, - plotlyFigure.data, - updateLayout, - this.plotlyConfig - ); + await Plotly.react(chart.plotContainer, plotlyConfig.data || [], updateLayout, this.plotlyConfig); - // Update metadata - this.updateChartMetadata(processorName); - - // Update performance stats - const updateTime = performance.now() - startTime; - this.updatePerformanceStats(updateTime); + this.updateChartMetadata(processorId); + // Only update settings if chart is newly created or if parameters actually changed + if (!chart.settingsInitialized) { + this.updateChartSettings(processorId); + chart.settingsInitialized = true; + } else { + // Check if settings need updating without forcing it + this.updateChartSettings(processorId); + } + const dt = performance.now() - start; + this.updatePerformanceStats(dt); }); - - } catch (error) { - console.error(`❌ Error updating chart ${processorName}:`, error); + } catch (e) { + console.error(`❌ Error updating chart ${processorId}:`, e); } } - /** - * Queue chart update to avoid blocking UI - */ - queueUpdate(processorName, updateFn) { - this.updateQueue.set(processorName, updateFn); - - if (!this.isUpdating) { - this.processUpdateQueue(); - } + queueUpdate(id, fn) { + this.updateQueue.set(id, fn); + if (!this.isUpdating) this.processUpdateQueue(); } - /** - * Process queued updates - */ async processUpdateQueue() { if (this.isPaused) return; - this.isUpdating = true; - while (this.updateQueue.size > 0 && !this.isPaused) { - const [processorName, updateFn] = this.updateQueue.entries().next().value; - this.updateQueue.delete(processorName); - - try { - await updateFn(); - } catch (error) { - console.error(`❌ Error in queued update for ${processorName}:`, error); - } - - // Yield to browser between updates - await new Promise(resolve => setTimeout(resolve, 0)); + const [id, fn] = this.updateQueue.entries().next().value; + this.updateQueue.delete(id); + try { await fn(); } catch (e) { console.error(`❌ Error in queued update for ${id}:`, e); } + await new Promise(r => setTimeout(r, 0)); } - this.isUpdating = false; } - /** - * Create chart card HTML - */ - createChartCard(processorName) { + createChartCard(processorId) { const card = document.createElement('div'); card.className = 'chart-card'; - card.dataset.processor = processorName; + card.dataset.processor = processorId; card.innerHTML = `
- ${this.formatProcessorName(processorName)} + ${this.formatProcessorName(processorId)}
-
+
+
+
+
Settings
+
+ +
+
+
-
- Last update: -- -
-
- Sweep: -- -
+
Last update: --
+
Info: --
`; - // Add event listeners - this.setupChartCardEvents(card, processorName); + this.setupChartCardEvents(card, processorId); - // Initialize Lucide icons - if (typeof lucide !== 'undefined') { - lucide.createIcons({ attrs: { 'stroke-width': 1.5 } }); - } + // Initialize settings immediately + this.updateChartSettings(processorId); + if (typeof lucide !== 'undefined') lucide.createIcons({ attrs: { 'stroke-width': 1.5 } }); return card; } - /** - * Set up chart card event listeners - */ - setupChartCardEvents(card, processorName) { - card.addEventListener('click', (event) => { - const action = event.target.closest('[data-action]')?.dataset.action; + setupChartCardEvents(card, processorId) { + card.addEventListener('click', (e) => { + const action = e.target.closest('[data-action]')?.dataset.action; if (!action) return; - - event.stopPropagation(); - + e.stopPropagation(); switch (action) { - case 'fullscreen': - this.toggleFullscreen(processorName); - break; - case 'download': - this.downloadChart(processorName); - break; + case 'fullscreen': this.toggleFullscreen(processorId); break; + case 'download': this.downloadChart(processorId); break; case 'hide': - this.hideChart(processorName); - // Also disable the processor toggle - if (window.vnaDashboard && window.vnaDashboard.ui) { - window.vnaDashboard.ui.setProcessorEnabled(processorName, false); - } + this.hideChart(processorId); + if (window.vnaDashboard?.ui) window.vnaDashboard.ui.setProcessorEnabled(processorId, false); break; } }); } - /** - * Update chart metadata - */ - updateChartMetadata(processorName) { - const chart = this.charts.get(processorName); - const data = this.chartData.get(processorName); + updateChartMetadata(processorId) { + const chart = this.charts.get(processorId); + const latestData = this.chartData.get(processorId); + if (!chart || !latestData) return; + const tsEl = chart.element.querySelector('[data-timestamp]'); + const infoEl = chart.element.querySelector('[data-sweep]'); - if (!chart || !data || data.length === 0) return; - - const latestData = data[data.length - 1]; - const timestampEl = chart.element.querySelector('[data-timestamp]'); - const sweepEl = chart.element.querySelector('[data-sweep]'); - - if (timestampEl) { - timestampEl.textContent = `Last update: ${latestData.timestamp.toLocaleTimeString()}`; - timestampEl.dataset.timestamp = latestData.timestamp.toISOString(); + if (tsEl) { + const dt = latestData.timestamp instanceof Date ? latestData.timestamp : new Date(); + tsEl.textContent = `Last update: ${dt.toLocaleTimeString()}`; + tsEl.dataset.timestamp = dt.toISOString(); } - if (sweepEl) { - sweepEl.textContent = `Sweep: #${latestData.sweep_number}`; - sweepEl.dataset.sweep = latestData.sweep_number; + if (infoEl) { + // Hide history count since we no longer track it + infoEl.textContent = ''; + } + } + + formatProcessorName(n) { + return n.split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' '); + } + + /** + * Update chart settings with current UI parameters + */ + updateChartSettings(processorId) { + const chart = this.charts.get(processorId); + const settingsContainer = chart?.element?.querySelector('.chart-settings__controls'); + if (!settingsContainer) return; + + // Get UI parameters from the latest chart data metadata + const latestData = this.chartData.get(processorId); + const uiParameters = latestData?.metadata?.ui_parameters; + + // Store current parameters to avoid unnecessary updates + if (!chart.lastUiParameters) chart.lastUiParameters = null; + + // Check if parameters have actually changed + const parametersChanged = !chart.lastUiParameters || + JSON.stringify(uiParameters) !== JSON.stringify(chart.lastUiParameters); + + if (!parametersChanged) { + console.log(`⚪ No parameter changes for ${processorId}, skipping settings update`); + return; // No need to update if parameters haven't changed + } + + console.log(`🔄 Updating settings for ${processorId}:`, { + old: chart.lastUiParameters, + new: uiParameters + }); + + chart.lastUiParameters = uiParameters ? JSON.parse(JSON.stringify(uiParameters)) : null; + + // Fallback to UI manager if no data available + if (!uiParameters || !Array.isArray(uiParameters) || uiParameters.length === 0) { + const uiManager = window.vnaDashboard?.ui; + const processor = uiManager?.processors?.get(processorId); + + if (processor?.uiParameters && Array.isArray(processor.uiParameters) && processor.uiParameters.length > 0) { + const settingsHtml = processor.uiParameters.map(param => + this.createSettingControl(param, processorId) + ).join(''); + settingsContainer.innerHTML = settingsHtml; + this.setupSettingsEvents(settingsContainer, processorId); + return; + } + + settingsContainer.innerHTML = '
No settings available
'; + return; + } + + // Generate settings HTML from chart data + const settingsHtml = uiParameters.map(param => + this.createSettingControl(param, processorId) + ).join(''); + + settingsContainer.innerHTML = settingsHtml; + + // Add event listeners + this.setupSettingsEvents(settingsContainer, processorId); + + // Initialize last values from current UI state to prevent immediate updates + if (uiParameters) { + uiParameters.forEach(param => { + const settingKey = `${processorId}_${param.name}`; + this.lastSettingValues[settingKey] = param.value; + console.log(`💾 Initialized setting value: ${settingKey} = ${param.value}`); + }); + } + + if (typeof lucide !== 'undefined') { + lucide.createIcons({ attrs: { 'stroke-width': 1.5 } }); } } /** - * Format processor name for display + * Create setting control HTML */ - formatProcessorName(processorName) { - return processorName - .split('_') - .map(word => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); + createSettingControl(param, processorId) { + const paramId = `chart_${processorId}_${param.name}`; + const value = param.value; + const opts = param.options || {}; + + switch (param.type) { + case 'slider': + case 'range': + return ` +
+ + +
+ `; + case 'toggle': + case 'boolean': + return ` +
+ +
+ `; + case 'select': + case 'dropdown': { + let choices = []; + if (Array.isArray(opts)) choices = opts; + else if (Array.isArray(opts.choices)) choices = opts.choices; + const optionsHtml = choices.map(option => + `` + ).join(''); + return ` +
+ + +
+ `; + } + default: + return ` +
+ + +
+ `; + } } /** - * Toggle processor visibility + * Setup event listeners for settings */ - toggleProcessor(processorName, enabled) { - if (enabled) { - this.disabledProcessors.delete(processorName); - this.showChart(processorName); + setupSettingsEvents(settingsContainer, processorId) { + const onParamChange = (e) => { + if (!e.target.closest('.chart-setting')) return; + this.handleSettingChange(e, processorId); + }; + + settingsContainer.addEventListener('input', onParamChange); + settingsContainer.addEventListener('change', onParamChange); + } + + /** + * Handle setting parameter change + */ + handleSettingChange(event, processorId) { + const settingElement = event.target.closest('.chart-setting'); + if (!settingElement) return; + + const paramName = settingElement.dataset.param; + const input = event.target; + + let value; + if (input.type === 'checkbox') { + value = input.checked; + } else if (input.type === 'range') { + value = parseFloat(input.value); + // Update display value + const valueDisplay = settingElement.querySelector('.chart-setting__value'); + if (valueDisplay) valueDisplay.textContent = value; } else { - this.disabledProcessors.add(processorName); - this.hideChart(processorName); - } - } - - /** - * Show chart - */ - showChart(processorName) { - const chart = this.charts.get(processorName); - if (chart) { - chart.element.classList.remove('chart-card--hidden'); - chart.isVisible = true; - - // Trigger resize to ensure proper rendering - setTimeout(() => { - if (chart.plotContainer) { - Plotly.Plots.resize(chart.plotContainer); - } - }, 100); + value = input.value; } - this.updateEmptyStateVisibility(); - } - - /** - * Hide chart - */ - hideChart(processorName) { - const chart = this.charts.get(processorName); - if (chart) { - chart.element.classList.add('chart-card--hidden'); - chart.isVisible = false; + // Normalize boolean values to prevent comparison issues + if (typeof value === 'string' && (value === 'true' || value === 'false')) { + value = value === 'true'; } - this.updateEmptyStateVisibility(); - } + console.log(`🔧 Chart setting changed: ${processorId}.${paramName} = ${value}`); - /** - * Remove chart completely - */ - removeChart(processorName) { - const chart = this.charts.get(processorName); - if (chart) { - // Clean up ResizeObserver - if (chart.plotContainer && chart.plotContainer._resizeObserver) { - chart.plotContainer._resizeObserver.disconnect(); - chart.plotContainer._resizeObserver = null; + // Store the current setting value to prevent loops + if (!this.lastSettingValues) this.lastSettingValues = {}; + const settingKey = `${processorId}_${paramName}`; + + // Normalize saved value for comparison + let lastValue = this.lastSettingValues[settingKey]; + if (typeof lastValue === 'string' && (lastValue === 'true' || lastValue === 'false')) { + lastValue = lastValue === 'true'; + } + + // Check if this is the same value we just set to prevent feedback loop + if (lastValue === value) { + console.log(`🔄 Skipping duplicate setting: ${settingKey} = ${value} (was ${this.lastSettingValues[settingKey]})`); + return; + } + + this.lastSettingValues[settingKey] = value; + + // Debounce setting updates to prevent rapid firing + const debounceKey = `${processorId}_${paramName}`; + if (this.settingDebounceTimers) { + clearTimeout(this.settingDebounceTimers[debounceKey]); + } else { + this.settingDebounceTimers = {}; + } + + this.settingDebounceTimers[debounceKey] = setTimeout(() => { + console.log(`📤 Sending setting update: ${processorId}.${paramName} = ${value}`); + // Send update via WebSocket + const websocket = window.vnaDashboard?.websocket; + if (websocket && websocket.recalculate) { + websocket.recalculate(processorId, { [paramName]: value }); + } else { + console.warn('⚠️ WebSocket not available for settings update'); } + delete this.settingDebounceTimers[debounceKey]; + }, 300); // 300ms delay to prevent rapid updates + } - // Clean up Plotly - if (chart.plotContainer) { - Plotly.purge(chart.plotContainer); - } - - // Remove from DOM - chart.element.remove(); - - // Clean up data - this.charts.delete(processorName); - this.chartData.delete(processorName); - this.disabledProcessors.delete(processorName); + toggleProcessor(id, enabled) { enabled ? this.showChart(id) : this.hideChart(id); } + showChart(id) { + const c = this.charts.get(id); + if (c) { + c.element.classList.remove('chart-card--hidden'); + c.isVisible = true; + setTimeout(() => c.plotContainer && Plotly.Plots.resize(c.plotContainer), 100); + } + this.updateEmptyStateVisibility(); + } + hideChart(id) { + const c = this.charts.get(id); + if (c) { c.element.classList.add('chart-card--hidden'); c.isVisible = false; } + this.updateEmptyStateVisibility(); + } + + removeChart(id) { + const c = this.charts.get(id); + if (c) { + if (c.plotContainer?._resizeObserver) { c.plotContainer._resizeObserver.disconnect(); c.plotContainer._resizeObserver = null; } + if (c.plotContainer) Plotly.purge(c.plotContainer); + c.element.remove(); + this.charts.delete(id); + this.chartData.delete(id); + this.disabledProcessors.delete(id); } - this.updateEmptyStateVisibility(); } - /** - * Clear all charts - */ clearAll() { - console.log('🗑️ Clearing all charts...'); - - for (const [processorName] of this.charts) { - this.removeChart(processorName); - } - + for (const [id] of this.charts) this.removeChart(id); this.charts.clear(); this.chartData.clear(); this.updateQueue.clear(); this.updateEmptyStateVisibility(); } - /** - * Download chart as image - */ - downloadChart(processorName) { - const chart = this.charts.get(processorName); - if (!chart || !chart.plotContainer) return; - + downloadChart(id) { + const c = this.charts.get(id); + if (!c?.plotContainer) return; try { - Plotly.downloadImage(chart.plotContainer, { - format: 'png', - width: 1200, - height: 800, - filename: `${processorName}-${Date.now()}` - }); - - this.notifications.show({ - type: 'success', - title: 'Download Started', - message: `Chart image download started` - }); - - } catch (error) { - console.error('❌ Chart download failed:', error); - this.notifications.show({ - type: 'error', - title: 'Download Failed', - message: 'Failed to download chart image' - }); + Plotly.downloadImage(c.plotContainer, { format: 'png', width: 1200, height: 800, filename: `${id}-${Date.now()}` }); + this.notifications?.show?.({ type: 'success', title: 'Download Started', message: 'Chart image download started' }); + } catch (e) { + console.error('❌ Chart download failed:', e); + this.notifications?.show?.({ type: 'error', title: 'Download Failed', message: 'Failed to download chart image' }); } } - /** - * Toggle fullscreen mode - */ - toggleFullscreen(processorName) { - const chart = this.charts.get(processorName); - if (!chart || !chart.element) return; - + toggleFullscreen(id) { + const c = this.charts.get(id); + if (!c?.element) return; if (!document.fullscreenElement) { - chart.element.requestFullscreen().then(() => { - // Resize chart for fullscreen after CSS transitions complete + c.element.requestFullscreen()?.then(() => { setTimeout(() => { - if (chart.plotContainer) { - // Get fullscreen dimensions - const rect = chart.plotContainer.getBoundingClientRect(); - - // Update layout for fullscreen - Plotly.relayout(chart.plotContainer, { - width: rect.width, - height: rect.height - }); - - // Force resize - Plotly.Plots.resize(chart.plotContainer); + if (c.plotContainer) { + const r = c.plotContainer.getBoundingClientRect(); + Plotly.relayout(c.plotContainer, { width: r.width, height: r.height }); + Plotly.Plots.resize(c.plotContainer); } }, 200); }).catch(console.error); } else { - document.exitFullscreen().then(() => { - // Resize chart back to normal view - setTimeout(() => { - if (chart.plotContainer) { - Plotly.Plots.resize(chart.plotContainer); - } - }, 100); + document.exitFullscreen()?.then(() => { + setTimeout(() => c.plotContainer && Plotly.Plots.resize(c.plotContainer), 100); }); } } - /** - * Hide empty state - */ hideEmptyState() { - if (this.emptyState) { - this.emptyState.classList.add('empty-state--hidden'); - } + if (this.emptyState) this.emptyState.classList.add('empty-state--hidden'); } - - /** - * Update empty state visibility - */ updateEmptyStateVisibility() { if (!this.emptyState) return; - - const hasVisibleCharts = Array.from(this.charts.values()) - .some(chart => chart.isVisible); - - if (hasVisibleCharts) { - this.emptyState.classList.add('empty-state--hidden'); - } else { - this.emptyState.classList.remove('empty-state--hidden'); - } + const hasVisible = Array.from(this.charts.values()).some(c => c.isVisible); + this.emptyState.classList.toggle('empty-state--hidden', hasVisible); } - /** - * Update performance statistics - */ - updatePerformanceStats(updateTime) { + updatePerformanceStats(dt) { this.performanceStats.updatesProcessed++; this.performanceStats.lastUpdateTime = new Date(); - - // Calculate rolling average - const totalTime = this.performanceStats.avgUpdateTime * - (this.performanceStats.updatesProcessed - 1) + updateTime; - this.performanceStats.avgUpdateTime = totalTime / this.performanceStats.updatesProcessed; + const total = this.performanceStats.avgUpdateTime * (this.performanceStats.updatesProcessed - 1) + dt; + this.performanceStats.avgUpdateTime = total / this.performanceStats.updatesProcessed; } - /** - * Pause updates (for performance when tab is hidden) - */ - pause() { - this.isPaused = true; - console.log('⏸️ Chart updates paused'); - } + pause() { this.isPaused = true; console.log('⏸️ Chart updates paused'); } + resume() { this.isPaused = false; console.log('▶️ Chart updates resumed'); if (this.updateQueue.size) this.processUpdateQueue(); } - /** - * Resume updates - */ - resume() { - this.isPaused = false; - console.log('▶️ Chart updates resumed'); - - // Process any queued updates - if (this.updateQueue.size > 0) { - this.processUpdateQueue(); - } - } - - /** - * Export all chart data - */ exportData() { const exportData = { timestamp: new Date().toISOString(), charts: {}, - stats: this.performanceStats + stats: this.performanceStats, + description: "Current chart data snapshot - latest data from each visible processor" }; - for (const [processorName, dataArray] of this.chartData) { - exportData.charts[processorName] = dataArray.map(item => ({ - sweep_number: item.sweep_number, - data: item.data, - timestamp: item.timestamp.toISOString() - })); + // Export only the latest data from each visible chart (not all historical data) + for (const [processorId, chart] of this.charts) { + // Skip hidden/disabled processors + if (!chart.isVisible || this.disabledProcessors.has(processorId)) { + continue; + } + + const latestData = this.chartData.get(processorId); + if (!latestData) { + continue; + } + + exportData.charts[processorId] = { + processor_name: this.formatProcessorName(processorId), + current_data: { + data: latestData.data, + metadata: latestData.metadata, + timestamp: latestData.timestamp.toISOString() + }, + plotly_config: this.getCurrentPlotlyData(processorId), + settings: this.getProcessorSettings(processorId) + }; } + console.log(`📊 Exporting current data for ${Object.keys(exportData.charts).length} visible charts`); return exportData; } /** - * Get disabled processors list + * Get current Plotly data/layout for a processor */ - getDisabledProcessors() { - return Array.from(this.disabledProcessors); + getCurrentPlotlyData(processorId) { + const chart = this.charts.get(processorId); + if (!chart?.plotContainer?._fullData || !chart?.plotContainer?._fullLayout) { + return null; + } + + try { + return { + data: chart.plotContainer._fullData, + layout: { + title: chart.plotContainer._fullLayout.title, + xaxis: chart.plotContainer._fullLayout.xaxis, + yaxis: chart.plotContainer._fullLayout.yaxis, + showlegend: chart.plotContainer._fullLayout.showlegend, + legend: chart.plotContainer._fullLayout.legend + } + }; + } catch (error) { + console.warn(`⚠️ Could not extract Plotly data for ${processorId}:`, error); + return null; + } } /** - * Get chart statistics + * Get current processor settings (UI parameters) */ + getProcessorSettings(processorId) { + const latestData = this.chartData.get(processorId); + if (!latestData) { + return null; + } + + return latestData.metadata?.ui_parameters || null; + } + + getDisabledProcessors() { return Array.from(this.disabledProcessors); } getStats() { return { ...this.performanceStats, @@ -674,20 +678,12 @@ export class ChartManager { }; } - /** - * Cleanup - */ destroy() { console.log('🧹 Cleaning up Chart Manager...'); - - // Clear all charts (this also cleans up Plotly instances) this.clearAll(); - - // Clear update queue this.updateQueue.clear(); this.isUpdating = false; this.isPaused = true; - console.log('✅ Chart Manager cleanup complete'); } -} \ No newline at end of file +} diff --git a/vna_system/web_ui/static/js/modules/notifications.js b/vna_system/web_ui/static/js/modules/notifications.js index baba19c..1f91e11 100644 --- a/vna_system/web_ui/static/js/modules/notifications.js +++ b/vna_system/web_ui/static/js/modules/notifications.js @@ -8,6 +8,7 @@ export class NotificationManager { this.container = null; this.notifications = new Map(); // id -> notification element this.nextId = 1; + this.recentNotifications = new Map(); // title+message -> timestamp for deduplication // Configuration this.config = { @@ -65,6 +66,26 @@ export class NotificationManager { * Show a notification */ show(options) { + // Check for duplicate notifications within the last 2 seconds + const { type = 'info', title, message } = options; + const notificationKey = `${type}:${title}:${message}`; + const now = Date.now(); + const lastShown = this.recentNotifications.get(notificationKey); + + if (lastShown && (now - lastShown) < 2000) { + console.log('🔄 Skipping duplicate notification:', notificationKey); + return null; // Skip duplicate notification + } + + this.recentNotifications.set(notificationKey, now); + + // Clean up old entries (older than 5 seconds) + for (const [key, timestamp] of this.recentNotifications) { + if (now - timestamp > 5000) { + this.recentNotifications.delete(key); + } + } + const notification = this.createNotification(options); this.addNotification(notification); return notification.id; diff --git a/vna_system/web_ui/static/js/modules/settings.js b/vna_system/web_ui/static/js/modules/settings.js index d6563b5..e689d19 100644 --- a/vna_system/web_ui/static/js/modules/settings.js +++ b/vna_system/web_ui/static/js/modules/settings.js @@ -4,13 +4,20 @@ */ export class SettingsManager { - constructor(notifications) { + constructor(notifications, websocket, acquisition) { this.notifications = notifications; + this.websocket = websocket; + this.acquisition = acquisition; this.isInitialized = false; this.currentPreset = null; this.currentCalibration = null; this.workingCalibration = null; + // State for calibration capture + this.waitingForSweep = false; + this.pendingStandard = null; + this.disabledStandards = new Set(); // Track which standards are being captured + // DOM elements will be populated during init this.elements = {}; @@ -280,6 +287,32 @@ export class SettingsManager { this.elements.calibrationStandards.innerHTML = ''; } + resetCalibrationState() { + // Clear working calibration + this.workingCalibration = null; + + // Hide calibration steps UI + this.hideCalibrationSteps(); + + // Clear calibration name input + if (this.elements.calibrationNameInput) { + this.elements.calibrationNameInput.value = ''; + this.elements.calibrationNameInput.disabled = true; + } + + // Disable save button + if (this.elements.saveCalibrationBtn) { + this.elements.saveCalibrationBtn.disabled = true; + } + + // Clear progress display + if (this.elements.progressText) { + this.elements.progressText.textContent = '0/0'; + } + + console.log('🔄 Calibration state reset for preset change'); + } + generateStandardButtons(workingCalibration) { const container = this.elements.calibrationStandards; container.innerHTML = ''; @@ -295,8 +328,15 @@ export class SettingsManager { const isCompleted = completedStandards.includes(standard); const isMissing = missingStandards.includes(standard); + const isBeingCaptured = this.disabledStandards.has(standard); - if (isCompleted) { + if (isBeingCaptured) { + // Standard is currently being captured + button.classList.add('btn--warning'); + button.innerHTML = ` Capturing ${standard.toUpperCase()}...`; + button.disabled = true; + button.title = 'Standard is currently being captured'; + } else if (isCompleted) { button.classList.add('btn--success'); button.innerHTML = ` ${standard.toUpperCase()} ✓`; button.disabled = false; @@ -364,6 +404,9 @@ export class SettingsManager { message: result.message }); + // Reset calibration state when preset changes + this.resetCalibrationState(); + // Reload status await this.loadStatus(); @@ -422,46 +465,78 @@ export class SettingsManager { } async handleCalibrateStandard(standard) { + // Prevent multiple simultaneous captures + if (this.disabledStandards.has(standard)) { + console.log(`Standard ${standard} is already being captured`); + return; + } + try { + // Mark this standard as disabled + this.disabledStandards.add(standard); + const button = document.querySelector(`[data-standard="${standard}"]`); if (button) { button.disabled = true; - button.textContent = 'Capturing...'; + button.innerHTML = ' Waiting for next sweep...'; + if (typeof lucide !== 'undefined') lucide.createIcons(); } - const response = await fetch('/api/v1/settings/calibration/add-standard', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ standard }) - }); + // Check if acquisition is running + const isAcquisitionRunning = this.acquisition && this.acquisition.isRunning(); - if (!response.ok) throw new Error(`HTTP ${response.status}`); + if (!isAcquisitionRunning) { + // If not running, trigger a single sweep first + this.notifications.show({ + type: 'info', + title: 'Triggering Sweep', + message: `Requesting single sweep for ${standard.toUpperCase()} standard` + }); - const result = await response.json(); + if (this.acquisition) { + try { + await this.acquisition.triggerSingleSweep(); + } catch (error) { + console.error('Failed to trigger single sweep:', error); + this.notifications.show({ + type: 'error', + title: 'Sweep Error', + message: 'Failed to trigger single sweep for calibration' + }); + this.resetStandardCaptureState(standard); + return; + } + } + } + + // Set up state to wait for next sweep + this.waitingForSweep = true; + this.pendingStandard = standard; + + // Subscribe to processor results to catch next sweep + if (this.websocket) { + this.websocket.on('processor_result', this.handleSweepForCalibration.bind(this)); + } + + const message = isAcquisitionRunning + ? `Please trigger a new sweep to capture ${standard.toUpperCase()} standard` + : `Single sweep requested, waiting for data to capture ${standard.toUpperCase()} standard`; this.notifications.show({ - type: 'success', - title: 'Standard Captured', - message: result.message + type: 'info', + title: 'Waiting for Sweep', + message: message }); - // Reload working calibration - await this.loadWorkingCalibration(); - } catch (error) { - console.error('Failed to capture standard:', error); + console.error('Failed to start standard capture:', error); this.notifications.show({ type: 'error', title: 'Calibration Error', - message: 'Failed to capture calibration standard' + message: 'Failed to start calibration standard capture' }); - // Re-enable button - const button = document.querySelector(`[data-standard="${standard}"]`); - if (button) { - button.disabled = false; - this.generateStandardButtons(this.workingCalibration); - } + this.resetStandardCaptureState(standard); } } @@ -570,7 +645,87 @@ export class SettingsManager { await this.loadInitialData(); } + async handleSweepForCalibration(payload) { + // Only process if we're waiting for a sweep and this is sweep data + if (!this.waitingForSweep || !this.pendingStandard) return; + + try { + console.log(`📡 New sweep received, capturing ${this.pendingStandard} standard...`); + + // Remove listener to avoid duplicate calls + if (this.websocket) { + this.websocket.off('processor_result', this.handleSweepForCalibration.bind(this)); + } + + const button = document.querySelector(`[data-standard="${this.pendingStandard}"]`); + if (button) { + button.innerHTML = ' Capturing...'; + if (typeof lucide !== 'undefined') lucide.createIcons(); + } + + const response = await fetch('/api/v1/settings/calibration/add-standard', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ standard: this.pendingStandard }) + }); + + if (!response.ok) throw new Error(`HTTP ${response.status}`); + + const result = await response.json(); + + this.notifications.show({ + type: 'success', + title: 'Standard Captured', + message: result.message + }); + + // Reset state + this.resetStandardCaptureState(); + + // Reload working calibration + await this.loadWorkingCalibration(); + + } catch (error) { + console.error('Failed to capture standard:', error); + this.notifications.show({ + type: 'error', + title: 'Calibration Error', + message: 'Failed to capture calibration standard' + }); + + this.resetStandardCaptureState(); + } + } + + resetStandardCaptureState(standard = null) { + // Reset specific standard or all + if (standard) { + this.disabledStandards.delete(standard); + } else { + this.disabledStandards.clear(); + } + + // If this was the pending standard, reset waiting state + if (!standard || standard === this.pendingStandard) { + this.waitingForSweep = false; + this.pendingStandard = null; + + // Remove WebSocket listener if still attached + if (this.websocket) { + this.websocket.off('processor_result', this.handleSweepForCalibration.bind(this)); + } + } + + // Restore button states + if (this.workingCalibration) { + this.generateStandardButtons(this.workingCalibration); + } + } + destroy() { + // Clean up calibration capture state + this.resetStandardCaptureState(); + // Remove event listeners this.elements.presetDropdown?.removeEventListener('change', this.handlePresetChange); this.elements.setPresetBtn?.removeEventListener('click', this.handleSetPreset); diff --git a/vna_system/web_ui/static/js/modules/ui.js b/vna_system/web_ui/static/js/modules/ui.js index 212ea33..ebe388a 100644 --- a/vna_system/web_ui/static/js/modules/ui.js +++ b/vna_system/web_ui/static/js/modules/ui.js @@ -4,8 +4,10 @@ */ export class UIManager { - constructor(notifications) { + constructor(notifications, websocket, charts) { this.notifications = notifications; + this.websocket = websocket; // injected WebSocketManager + this.charts = charts; // injected ChartManager // UI Elements this.elements = { @@ -13,7 +15,6 @@ export class UIManager { processorToggles: null, sweepCount: null, dataRate: null, - clearChartsBtn: null, exportBtn: null, navButtons: null, views: null @@ -22,26 +23,16 @@ export class UIManager { // State this.currentView = 'dashboard'; this.connectionStatus = 'disconnected'; - this.processors = new Map(); // processor_name -> { enabled, count } + // processorId -> { enabled, count, uiParameters, config } + this.processors = new Map(); // Event handlers this.eventHandlers = { viewChange: [], processorToggle: [], - clearCharts: [], exportData: [] }; - // Statistics tracking - this.stats = { - sweepCount: 0, - dataRate: 0, - lastUpdate: null, - rateCalculation: { - samples: [], - windowMs: 10000 // 10 second window - } - }; } /** @@ -59,8 +50,16 @@ export class UIManager { // Initialize UI state this.initializeUIState(); - // Start update loop - this.startUpdateLoop(); + // Wire WebSocket events + if (this.websocket) { + this.websocket.on('connecting', () => this.setConnectionStatus('connecting')); + this.websocket.on('connected', () => this.setConnectionStatus('connected')); + this.websocket.on('disconnected', () => this.setConnectionStatus('disconnected')); + + // main data stream from backend + this.websocket.on('processor_result', (payload) => this.onProcessorResult(payload)); + } + console.log('✅ UI Manager initialized'); } @@ -71,21 +70,15 @@ export class UIManager { findElements() { this.elements.connectionStatus = document.getElementById('connectionStatus'); this.elements.processorToggles = document.getElementById('processorToggles'); - this.elements.sweepCount = document.getElementById('sweepCount'); - this.elements.dataRate = document.getElementById('dataRate'); - // clearChartsBtn removed this.elements.exportBtn = document.getElementById('exportBtn'); this.elements.navButtons = document.querySelectorAll('.nav-btn[data-view]'); this.elements.views = document.querySelectorAll('.view'); // Validate required elements - const required = [ - 'connectionStatus', 'processorToggles', 'exportBtn' - ]; - - for (const element of required) { - if (!this.elements[element]) { - throw new Error(`Required UI element not found: ${element}`); + const required = ['connectionStatus', 'processorToggles', 'exportBtn']; + for (const key of required) { + if (!this.elements[key]) { + throw new Error(`Required UI element not found: ${key}`); } } } @@ -102,8 +95,6 @@ export class UIManager { }); }); - // Note: Clear charts functionality removed - use processor toggles instead - // Export button this.elements.exportBtn.addEventListener('click', () => { this.triggerExportData(); @@ -112,74 +103,37 @@ export class UIManager { // Processor toggles container (event delegation) this.elements.processorToggles.addEventListener('click', (e) => { const toggle = e.target.closest('.processor-toggle'); - if (toggle) { - const processor = toggle.dataset.processor; - const isEnabled = toggle.classList.contains('processor-toggle--active'); - this.toggleProcessor(processor, !isEnabled); - } + if (!toggle) return; + const processor = toggle.dataset.processor; + const isEnabled = toggle.classList.contains('processor-toggle--active'); + this.toggleProcessor(processor, !isEnabled); }); + // Window resize - window.addEventListener('resize', this.debounce(() => { - this.handleResize(); - }, 300)); + window.addEventListener('resize', this.debounce(() => this.handleResize(), 300)); } /** * Initialize UI state */ initializeUIState() { - // Set initial connection status this.setConnectionStatus('disconnected'); - - // Initialize stats display - this.updateStatsDisplay(); - - // Set initial view this.switchView(this.currentView); - - // Clean up any invalid processors - this.cleanupInvalidProcessors(); } - /** - * Clean up invalid/old processors - */ - cleanupInvalidProcessors() { - const invalidProcessors = ['sweep_counter']; - - invalidProcessors.forEach(processor => { - if (this.processors.has(processor)) { - console.log(`🧹 Removing invalid processor: ${processor}`); - this.processors.delete(processor); - } - }); - } /** - * Start update loop for real-time UI updates - */ - startUpdateLoop() { - setInterval(() => { - this.updateDataRateDisplay(); - }, 1000); - } - - /** - * Switch between views (Dashboard, Settings, Logs) + * Switch between views */ switchView(viewName) { if (this.currentView === viewName) return; - console.log(`🔄 Switching to view: ${viewName}`); - - // Update navigation buttons this.elements.navButtons.forEach(button => { const isActive = button.dataset.view === viewName; button.classList.toggle('nav-btn--active', isActive); }); - // Update views this.elements.views.forEach(view => { const isActive = view.id === `${viewName}View`; view.classList.toggle('view--active', isActive); @@ -195,7 +149,6 @@ export class UIManager { setConnectionStatus(status) { if (this.connectionStatus === status) return; - console.log(`🔌 Connection status: ${status}`); this.connectionStatus = status; const statusElement = this.elements.connectionStatus; @@ -227,180 +180,221 @@ export class UIManager { } /** - * Update processor toggles + * Handle incoming processor result from backend */ - updateProcessorToggles(processors) { - if (!this.elements.processorToggles) return; + onProcessorResult(payload) { + const { processor_id, timestamp, data, plotly_config, ui_parameters, metadata } = payload; + if (!processor_id) return; - // Clear existing toggles - this.elements.processorToggles.innerHTML = ''; + // Register/update processor in UI map + const proc = this.processors.get(processor_id) || { enabled: true }; + proc.uiParameters = ui_parameters || []; + proc.config = metadata?.config || {}; + this.processors.set(processor_id, proc); - // Create toggles for each processor - processors.forEach(processor => { - const toggle = this.createProcessorToggle(processor); - this.elements.processorToggles.appendChild(toggle); - }); + // Refresh toggles and settings + this.refreshProcessorToggles(); + this.updateProcessorSettings(); + + // No more statistics tracking needed + + // Pass to charts + if (this.charts) { + this.charts.addResult({ + processor_id, + timestamp, + data, + plotly_config, + metadata + }); + } + } + + /** + * Rebuild processor toggles + */ + refreshProcessorToggles() { + const container = this.elements.processorToggles; + if (!container) return; + + container.innerHTML = ''; + for (const [name, data] of this.processors.entries()) { + const toggle = document.createElement('div'); + toggle.className = `processor-toggle ${data.enabled ? 'processor-toggle--active' : ''}`; + toggle.dataset.processor = name; + toggle.innerHTML = ` +
+
${this.formatProcessorName(name)}
+ `; + container.appendChild(toggle); + } - // Update Lucide icons if (typeof lucide !== 'undefined') { lucide.createIcons({ attrs: { 'stroke-width': 1.5 } }); } } /** - * Create processor toggle element + * Toggle processor visibility (UI-only) */ - createProcessorToggle(processor) { - const toggle = document.createElement('div'); - toggle.className = `processor-toggle ${processor.enabled ? 'processor-toggle--active' : ''}`; - toggle.dataset.processor = processor.name; + toggleProcessor(processorId, enabled) { + const proc = this.processors.get(processorId) || { enabled: true }; + proc.enabled = enabled; + this.processors.set(processorId, proc); - toggle.innerHTML = ` -
-
${this.formatProcessorName(processor.name)}
-
${processor.count || 0}
- `; - - return toggle; - } - - /** - * Toggle processor state - */ - toggleProcessor(processorName, enabled) { - console.log(`🔧 Toggle processor ${processorName}: ${enabled}`); - - // Update processor state - const processor = this.processors.get(processorName) || { count: 0 }; - processor.enabled = enabled; - this.processors.set(processorName, processor); - - // Update UI only if elements exist - if (this.elements.processorToggles) { - const toggle = this.elements.processorToggles.querySelector(`[data-processor="${processorName}"]`); - if (toggle) { - toggle.classList.toggle('processor-toggle--active', enabled); - } else { - // Toggle element doesn't exist yet, refresh toggles - this.refreshProcessorToggles(); - } - } - - // Emit event - this.emitEvent('processorToggle', processorName, enabled); - } - - /** - * Update processor from incoming data - */ - updateProcessorFromData(processorName) { - let processor = this.processors.get(processorName); - - if (!processor) { - // New processor discovered - processor = { enabled: true, count: 0 }; - this.processors.set(processorName, processor); + // Update toggle UI + const toggle = this.elements.processorToggles.querySelector(`[data-processor="${processorId}"]`); + if (toggle) { + toggle.classList.toggle('processor-toggle--active', enabled); + } else { this.refreshProcessorToggles(); } - // Increment count - processor.count++; + // Update charts + if (this.charts) { + this.charts.toggleProcessor(processorId, enabled); + } - // Update count display - const toggle = this.elements.processorToggles.querySelector(`[data-processor="${processorName}"]`); - if (toggle) { - const countElement = toggle.querySelector('.processor-toggle__count'); - if (countElement) { - countElement.textContent = processor.count; + this.emitEvent('processorToggle', processorId, enabled); + } + + /** + * Update processor settings panel (deprecated - now handled by ChartManager) + */ + updateProcessorSettings() { + // Settings are now integrated with charts - no separate panel needed + return; + } + + /** + * Create processor parameter element + */ + createProcessorParam(param, processorId) { + const paramId = `${processorId}_${param.name}`; + const value = param.value; + const opts = param.options || {}; + + switch (param.type) { + case 'slider': + case 'range': + return ` +
+
+ ${param.label} + ${value} +
+ +
+ `; + case 'toggle': + case 'boolean': + return ` +
+
${param.label}
+ +
+ `; + case 'select': + case 'dropdown': { + let choices = []; + if (Array.isArray(opts)) choices = opts; + else if (Array.isArray(opts.choices)) choices = opts.choices; + const optionsHtml = choices.map(option => + `` + ).join(''); + return ` +
+
${param.label}
+ +
+ `; } + default: + return ` +
+
${param.label}
+ +
+ `; } } /** - * Refresh processor toggles display + * Toggle processor config panel */ - refreshProcessorToggles() { - const processors = Array.from(this.processors.entries()).map(([name, data]) => ({ - name, - enabled: data.enabled, - count: data.count - })); - - this.updateProcessorToggles(processors); + toggleProcessorConfig(configElement) { + if (!configElement) return; + const isExpanded = configElement.classList.contains('processor-config--expanded'); + configElement.classList.toggle('processor-config--expanded', !isExpanded); } /** - * Update statistics display + * Handle processor parameter change + * Sends 'recalculate' with config_updates */ - updateStats(newStats) { - if (newStats.sweepCount !== undefined) { - this.stats.sweepCount = newStats.sweepCount; + handleProcessorParamChange(event) { + const paramElement = event.target.closest('.processor-param'); + const configElement = event.target.closest('.processor-config'); + if (!paramElement || !configElement) return; + + const processorId = configElement.dataset.processor; + const paramName = paramElement.dataset.param; + const input = event.target; + + let value; + if (input.type === 'checkbox') { + value = input.checked; + } else if (input.type === 'range') { + value = parseFloat(input.value); + // Update display value + const valueDisplay = paramElement.querySelector('.processor-param__value'); + if (valueDisplay) valueDisplay.textContent = value; + } else { + value = input.value; } - if (newStats.lastUpdate) { - this.updateDataRate(newStats.lastUpdate); - } + console.log(`🔧 Parameter changed: ${processorId}.${paramName} = ${value}`); - this.updateStatsDisplay(); - } - - /** - * Update data rate calculation - */ - updateDataRate(timestamp) { - const now = timestamp.getTime(); - this.stats.rateCalculation.samples.push(now); - - // Remove samples outside the window - const cutoff = now - this.stats.rateCalculation.windowMs; - this.stats.rateCalculation.samples = this.stats.rateCalculation.samples - .filter(time => time > cutoff); - - // Calculate rate (samples per second) - const samplesInWindow = this.stats.rateCalculation.samples.length; - const windowSeconds = this.stats.rateCalculation.windowMs / 1000; - this.stats.dataRate = Math.round(samplesInWindow / windowSeconds * 10) / 10; - } - - /** - * Update statistics display elements - */ - updateStatsDisplay() { - if (this.elements.sweepCount) { - this.elements.sweepCount.textContent = this.stats.sweepCount.toLocaleString(); - } - - this.updateDataRateDisplay(); - } - - /** - * Update data rate display - */ - updateDataRateDisplay() { - if (this.elements.dataRate) { - this.elements.dataRate.textContent = `${this.stats.dataRate}/s`; + // Send update via WebSocket using existing command + if (this.websocket) { + this.websocket.recalculate(processorId, { [paramName]: value }); } } - /** - * Set processor enabled state (external API) - */ - setProcessorEnabled(processorName, enabled) { - if (!processorName) return; - // If processor doesn't exist yet, store the preference for later - if (!this.processors.has(processorName)) { - const processor = { enabled, count: 0 }; - this.processors.set(processorName, processor); + /** + * Public API + */ + setProcessorEnabled(processorId, enabled) { + if (!processorId) return; + + if (!this.processors.has(processorId)) { + const processor = { enabled }; + this.processors.set(processorId, processor); return; } - this.toggleProcessor(processorName, enabled); + this.toggleProcessor(processorId, enabled); } - /** - * Format processor name for display - */ formatProcessorName(processorName) { return processorName .split('_') @@ -408,84 +402,34 @@ export class UIManager { .join(' '); } - // Clear charts functionality removed - use processor toggles - - /** - * Trigger export data action - */ triggerExportData() { this.emitEvent('exportData'); } - /** - * Handle window resize - */ handleResize() { console.log('📱 Window resized'); - // Trigger chart resize if needed - // This is handled by the chart manager + // Charts handle their own resize } - /** - * Update system status (for future use) - */ - updateSystemStatus(statusData) { - console.log('📊 System status update:', statusData); - // Future implementation for system health monitoring - } + // System status and theme methods simplified + updateSystemStatus(statusData) { console.log('📊 System status:', statusData); } + setTheme(theme) { document.documentElement.setAttribute('data-theme', theme); } + getCurrentTheme() { return document.documentElement.getAttribute('data-theme') || 'dark'; } - /** - * Set theme (for future use) - */ - setTheme(theme) { - console.log(`🎨 Setting theme: ${theme}`); - document.documentElement.setAttribute('data-theme', theme); - } + // Events + onViewChange(callback) { this.eventHandlers.viewChange.push(callback); } + onProcessorToggle(callback) { this.eventHandlers.processorToggle.push(callback); } + onExportData(callback) { this.eventHandlers.exportData.push(callback); } - /** - * Get current theme - */ - getCurrentTheme() { - return document.documentElement.getAttribute('data-theme') || 'dark'; - } - - /** - * Event system - */ - onViewChange(callback) { - this.eventHandlers.viewChange.push(callback); - } - - onProcessorToggle(callback) { - this.eventHandlers.processorToggle.push(callback); - } - - onClearCharts(callback) { - this.eventHandlers.clearCharts.push(callback); - } - - onExportData(callback) { - this.eventHandlers.exportData.push(callback); - } - - /** - * Emit event to registered handlers - */ emitEvent(eventType, ...args) { if (this.eventHandlers[eventType]) { this.eventHandlers[eventType].forEach(handler => { - try { - handler(...args); - } catch (error) { - console.error(`❌ Error in ${eventType} handler:`, error); - } + try { handler(...args); } + catch (error) { console.error(`❌ Error in ${eventType} handler:`, error); } }); } } - /** - * Utility: Debounce function - */ debounce(func, wait) { let timeout; return function executedFunction(...args) { @@ -498,21 +442,14 @@ export class UIManager { }; } - /** - * Get UI statistics - */ getStats() { return { currentView: this.currentView, connectionStatus: this.connectionStatus, - processorsCount: this.processors.size, - ...this.stats + processorsCount: this.processors.size }; } - /** - * Cleanup - */ destroy() { console.log('🧹 Cleaning up UI Manager...'); @@ -526,4 +463,4 @@ export class UIManager { console.log('✅ UI Manager cleanup complete'); } -} \ No newline at end of file +} diff --git a/vna_system/web_ui/static/js/modules/websocket.js b/vna_system/web_ui/static/js/modules/websocket.js index 4920341..c659770 100644 --- a/vna_system/web_ui/static/js/modules/websocket.js +++ b/vna_system/web_ui/static/js/modules/websocket.js @@ -16,11 +16,9 @@ export class WebSocketManager { this.lastPing = null; this.eventListeners = new Map(); - // Message queue for when disconnected this.messageQueue = []; this.maxQueueSize = 100; - // Statistics this.stats = { messagesReceived: 0, messagesPerSecond: 0, @@ -29,14 +27,10 @@ export class WebSocketManager { bytesReceived: 0 }; - // Rate limiting for message processing this.messageRateCounter = []; this.rateWindowMs = 1000; } - /** - * Connect to WebSocket server - */ async connect() { if (this.isConnected || this.isConnecting) { console.log('🔌 WebSocket already connected/connecting'); @@ -52,9 +46,7 @@ export class WebSocketManager { this.ws = new WebSocket(this.config.url); this.setupWebSocketEvents(); - // Wait for connection or timeout await this.waitForConnection(5000); - } catch (error) { this.isConnecting = false; console.error('❌ WebSocket connection failed:', error); @@ -63,52 +55,32 @@ export class WebSocketManager { } } - /** - * Wait for WebSocket connection with timeout - */ waitForConnection(timeoutMs) { return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - reject(new Error('WebSocket connection timeout')); - }, timeoutMs); + const timeout = setTimeout(() => reject(new Error('WebSocket connection timeout')), timeoutMs); - const checkConnection = () => { + const check = () => { if (this.isConnected) { clearTimeout(timeout); resolve(); - } else if (this.ws?.readyState === WebSocket.CLOSED || - this.ws?.readyState === WebSocket.CLOSING) { + } else if (this.ws?.readyState === WebSocket.CLOSED || this.ws?.readyState === WebSocket.CLOSING) { clearTimeout(timeout); reject(new Error('WebSocket connection failed')); } }; - // Check immediately and then every 100ms - checkConnection(); + check(); const interval = setInterval(() => { - checkConnection(); - if (this.isConnected) { - clearInterval(interval); - } + check(); + if (this.isConnected) clearInterval(interval); }, 100); - // Clean up interval on resolve/reject - const originalResolve = resolve; - const originalReject = reject; - resolve = (...args) => { - clearInterval(interval); - originalResolve(...args); - }; - reject = (...args) => { - clearInterval(interval); - originalReject(...args); - }; + const wrap = fn => (...args) => { clearInterval(interval); fn(...args); }; + resolve = wrap(resolve); + reject = wrap(reject); }); } - /** - * Set up WebSocket event handlers - */ setupWebSocketEvents() { if (!this.ws) return; @@ -123,7 +95,7 @@ export class WebSocketManager { this.processPendingMessages(); this.emit('connected', event); - this.notifications.show({ + this.notifications?.show?.({ type: 'success', title: 'Connected', message: 'Real-time connection established' @@ -135,7 +107,7 @@ export class WebSocketManager { this.handleMessage(event.data); } catch (error) { console.error('❌ Error processing WebSocket message:', error); - this.notifications.show({ + this.notifications?.show?.({ type: 'error', title: 'Message Error', message: 'Failed to process received data' @@ -154,102 +126,88 @@ export class WebSocketManager { }; } - /** - * Handle incoming messages - */ handleMessage(data) { - // Update statistics this.stats.messagesReceived++; this.stats.lastMessageTime = new Date(); - this.stats.bytesReceived += data.length; - - // Rate limiting check + this.stats.bytesReceived += (typeof data === 'string' ? data.length : 0); this.updateMessageRate(); try { - let parsedData; + if (typeof data !== 'string') { + console.warn('⚠️ Received non-text message, ignoring'); + return; + } + if (data === 'ping') { this.handlePing(); return; } + if (data === 'pong') { this.handlePong(); return; } - // Handle different message types - if (typeof data === 'string') { - if (data === 'ping') { - this.handlePing(); - return; - } else if (data === 'pong') { - this.handlePong(); - return; - } else { - parsedData = JSON.parse(data); - } - } else { - // Handle binary data if needed - console.warn('⚠️ Received binary data, not implemented'); + let payload; + try { + payload = JSON.parse(data); + } catch (jsonError) { + console.error('❌ Invalid JSON format:', data); + console.error('JSON parse error:', jsonError); + this.notifications?.show?.({ + type: 'error', + title: 'WebSocket Error', + message: 'Received invalid JSON from server' + }); return; } - // Emit data event - this.emit('data', parsedData); + // Публичное событие «data» — для всего, плюс более точечные: + this.emit('data', payload); - } catch (error) { - console.error('❌ Failed to parse WebSocket message:', error); + switch (payload.type) { + case 'processor_result': + this.emit('processor_result', payload); + break; + case 'processor_history': + this.emit('processor_history', payload); + break; + case 'error': + console.error('🔴 Server error:', payload.message); + this.notifications?.show?.({ + type: 'error', title: 'Server Error', message: payload.message + }); + break; + default: + console.warn('⚠️ Unknown payload type:', payload.type); + } + } catch (e) { + console.error('❌ Failed to parse WebSocket JSON:', e); console.log('📝 Raw message:', data); } } - /** - * Update message rate statistics - */ updateMessageRate() { const now = Date.now(); this.messageRateCounter.push(now); - - // Remove messages outside the rate window - this.messageRateCounter = this.messageRateCounter.filter( - time => now - time < this.rateWindowMs - ); - + this.messageRateCounter = this.messageRateCounter.filter(t => now - t < this.rateWindowMs); this.stats.messagesPerSecond = this.messageRateCounter.length; } - /** - * Handle ping message - */ handlePing() { - if (this.ws && this.ws.readyState === WebSocket.OPEN) { - this.ws.send('pong'); - } + if (this.ws?.readyState === WebSocket.OPEN) this.ws.send('pong'); } - - /** - * Handle pong message - */ handlePong() { if (this.lastPing) { - const latency = Date.now() - this.lastPing; - console.log(`🏓 WebSocket latency: ${latency}ms`); + console.log(`🏓 WebSocket latency: ${Date.now() - this.lastPing}ms`); this.lastPing = null; } } - - /** - * Start ping-pong mechanism - */ startPingPong() { this.pingTimer = setInterval(() => { - if (this.ws && this.ws.readyState === WebSocket.OPEN) { + if (this.ws?.readyState === WebSocket.OPEN) { this.lastPing = Date.now(); this.ws.send('ping'); } - }, 30000); // Ping every 30 seconds + }, 30000); } - /** - * Handle connection errors - */ - handleConnectionError(error) { - console.error('❌ WebSocket connection error:', error); - - if (!this.isConnected) { - this.notifications.show({ + handleConnectionError() { + if (!this.isConnected && this.isConnecting) { + // Only show error notification during connection attempts, not after disconnection + this.notifications?.show?.({ type: 'error', title: 'Connection Failed', message: 'Unable to establish real-time connection' @@ -257,89 +215,53 @@ export class WebSocketManager { } } - /** - * Handle disconnection - */ handleDisconnection(event) { const wasConnected = this.isConnected; this.isConnected = false; this.isConnecting = false; - // Clean up timers - if (this.pingTimer) { - clearInterval(this.pingTimer); - this.pingTimer = null; - } + if (this.pingTimer) { clearInterval(this.pingTimer); this.pingTimer = null; } this.emit('disconnected', event); if (wasConnected) { - this.notifications.show({ + this.notifications?.show?.({ type: 'warning', title: 'Disconnected', message: 'Real-time connection lost. Attempting to reconnect...' }); - - // Auto-reconnect this.scheduleReconnect(); } } - /** - * Schedule automatic reconnection - */ scheduleReconnect() { if (this.reconnectTimer) return; - - const delay = Math.min( - this.config.reconnectInterval * Math.pow(2, this.reconnectAttempts), - 30000 // Max 30 seconds - ); - + const delay = Math.min(this.config.reconnectInterval * Math.pow(2, this.reconnectAttempts), 30000); console.log(`🔄 Scheduling reconnect in ${delay}ms (attempt ${this.reconnectAttempts + 1})`); - this.reconnectTimer = setTimeout(() => { this.reconnectTimer = null; this.reconnect(); }, delay); } - /** - * Manually trigger reconnection - */ async reconnect() { if (this.reconnectAttempts >= this.config.maxReconnectAttempts) { console.error('❌ Max reconnection attempts reached'); - this.notifications.show({ + this.notifications?.show?.({ type: 'error', title: 'Connection Failed', message: 'Unable to reconnect after multiple attempts' }); return; } - this.reconnectAttempts++; - - // Close existing connection - if (this.ws) { - this.ws.close(); - this.ws = null; - } - - try { - await this.connect(); - } catch (error) { - console.error(`❌ Reconnection attempt ${this.reconnectAttempts} failed:`, error); - this.scheduleReconnect(); - } + if (this.ws) { this.ws.close(); this.ws = null; } + try { await this.connect(); } + catch (error) { console.error(`❌ Reconnection attempt ${this.reconnectAttempts} failed:`, error); this.scheduleReconnect(); } } - /** - * Send message - */ send(data) { if (!this.isConnected || !this.ws) { - // Queue message for later if (this.messageQueue.length < this.maxQueueSize) { this.messageQueue.push(data); console.log('📤 Message queued (not connected)'); @@ -348,65 +270,72 @@ export class WebSocketManager { } return false; } - try { - const message = typeof data === 'string' ? data : JSON.stringify(data); - this.ws.send(message); + const msg = (typeof data === 'string') ? data : JSON.stringify(data); + this.ws.send(msg); return true; - } catch (error) { - console.error('❌ Failed to send message:', error); + } catch (e) { + console.error('❌ Failed to send message:', e); return false; } } - /** - * Process pending messages after reconnection - */ processPendingMessages() { - if (this.messageQueue.length > 0) { - console.log(`📤 Processing ${this.messageQueue.length} queued messages`); - - const messages = [...this.messageQueue]; - this.messageQueue = []; - - messages.forEach(message => { - this.send(message); - }); - } + if (this.messageQueue.length === 0) return; + console.log(`📤 Processing ${this.messageQueue.length} queued messages`); + const messages = [...this.messageQueue]; + this.messageQueue = []; + messages.forEach(m => this.send(m)); + } + + // === ПУБЛИЧНОЕ API ДЛЯ СОВМЕСТИМОСТИ С БЭКЕНДОМ === + + /** Запросить пересчёт с обновлением конфигурации (СУЩЕСТВУЕТ НА БЭКЕНДЕ) */ + recalculate(processorId, configUpdates = undefined) { + return this.send({ + type: 'recalculate', + processor_id: processorId, + ...(configUpdates ? { config_updates: configUpdates } : {}) + }); + } + + /** Получить историю результатов процессора (СУЩЕСТВУЕТ НА БЭКЕНДЕ) */ + getHistory(processorId, limit = 10) { + return this.send({ + type: 'get_history', + processor_id: processorId, + limit + }); + } + + // === Events === + on(event, callback) { + if (!this.eventListeners.has(event)) this.eventListeners.set(event, []); + this.eventListeners.get(event).push(callback); + } + off(event, callback) { + if (!this.eventListeners.has(event)) return; + const arr = this.eventListeners.get(event); + const i = arr.indexOf(callback); + if (i > -1) arr.splice(i, 1); + } + emit(event, data) { + if (!this.eventListeners.has(event)) return; + this.eventListeners.get(event).forEach(cb => { + try { cb(data); } catch (e) { console.error(`❌ Error in event listener for ${event}:`, e); } + }); } - /** - * Disconnect WebSocket - */ disconnect() { console.log('🔌 Disconnecting WebSocket'); - - // Clear reconnection timer - if (this.reconnectTimer) { - clearTimeout(this.reconnectTimer); - this.reconnectTimer = null; - } - - // Clear ping timer - if (this.pingTimer) { - clearInterval(this.pingTimer); - this.pingTimer = null; - } - - // Close WebSocket - if (this.ws) { - this.ws.close(1000, 'Manual disconnect'); - this.ws = null; - } - + if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer = null; } + if (this.pingTimer) { clearInterval(this.pingTimer); this.pingTimer = null; } + if (this.ws) { this.ws.close(1000, 'Manual disconnect'); this.ws = null; } this.isConnected = false; this.isConnecting = false; this.reconnectAttempts = 0; } - /** - * Get connection statistics - */ getStats() { return { ...this.stats, @@ -417,44 +346,9 @@ export class WebSocketManager { }; } - /** - * Event listener management - */ - on(event, callback) { - if (!this.eventListeners.has(event)) { - this.eventListeners.set(event, []); - } - this.eventListeners.get(event).push(callback); - } - - off(event, callback) { - if (this.eventListeners.has(event)) { - const listeners = this.eventListeners.get(event); - const index = listeners.indexOf(callback); - if (index > -1) { - listeners.splice(index, 1); - } - } - } - - emit(event, data) { - if (this.eventListeners.has(event)) { - this.eventListeners.get(event).forEach(callback => { - try { - callback(data); - } catch (error) { - console.error(`❌ Error in event listener for ${event}:`, error); - } - }); - } - } - - /** - * Cleanup - */ destroy() { this.disconnect(); this.eventListeners.clear(); this.messageQueue = []; } -} \ No newline at end of file +} diff --git a/vna_system/web_ui/templates/index.html b/vna_system/web_ui/templates/index.html index bd361bd..6877f6d 100644 --- a/vna_system/web_ui/templates/index.html +++ b/vna_system/web_ui/templates/index.html @@ -32,6 +32,7 @@ + @@ -49,16 +50,6 @@ Connecting... -
-
- Sweeps: - 0 -
-
- Rate: - 0/s -
-