You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							260 lines
						
					
					
						
							17 KiB
						
					
					
				
			
		
		
	
	
							260 lines
						
					
					
						
							17 KiB
						
					
					
				| {
 | |
|  "cells": [
 | |
|   {
 | |
|    "cell_type": "code",
 | |
|    "execution_count": 6,
 | |
|    "metadata": {},
 | |
|    "outputs": [],
 | |
|    "source": [
 | |
|     "from opendbc.car import structs\n",
 | |
|     "from opendbc.car.subaru.values import CAR, SubaruFlags\n",
 | |
|     "from opendbc.car.subaru.fingerprints import FW_VERSIONS\n",
 | |
|     "\n",
 | |
|     "TEST_PLATFORMS = set(CAR) - CAR.with_flags(SubaruFlags.PREGLOBAL)\n",
 | |
|     "\n",
 | |
|     "Ecu = structs.CarParams.Ecu\n",
 | |
|     "\n",
 | |
|     "FW_BY_ECU = {platform: {ecu: versions for (ecu, addr, sub_addr), versions in fw_versions.items()} for platform, fw_versions in FW_VERSIONS.items()}"
 | |
|    ]
 | |
|   },
 | |
|   {
 | |
|    "cell_type": "code",
 | |
|    "execution_count": 7,
 | |
|    "metadata": {},
 | |
|    "outputs": [],
 | |
|    "source": [
 | |
|     "PLATFORM_CODES = {\n",
 | |
|     "    Ecu.abs: {\n",
 | |
|     "        0: {\n",
 | |
|     "            b'\\xa5': [CAR.SUBARU_ASCENT, CAR.SUBARU_ASCENT_2023],\n",
 | |
|     "            b'\\xa2': [CAR.SUBARU_IMPREZA, CAR.SUBARU_IMPREZA_2020, CAR.SUBARU_CROSSTREK_HYBRID],\n",
 | |
|     "            b'\\xa1': [CAR.SUBARU_OUTBACK, CAR.SUBARU_LEGACY, CAR.SUBARU_OUTBACK_2023],\n",
 | |
|     "            b'\\xa3': [CAR.SUBARU_FORESTER, CAR.SUBARU_FORESTER_HYBRID, CAR.SUBARU_FORESTER_2022],\n",
 | |
|     "            b'z': [CAR.SUBARU_IMPREZA],\n",
 | |
|     "        }\n",
 | |
|     "    }\n",
 | |
|     "}\n",
 | |
|     "\n",
 | |
|     "YEAR_CODES = {\n",
 | |
|     "    Ecu.abs: {\n",
 | |
|     "        2: {\n",
 | |
|     "            b'\\x18': 2018,\n",
 | |
|     "            b'\\x19': 2019,\n",
 | |
|     "            b'\\x20': 2020,\n",
 | |
|     "            b'\\x21': 2021,\n",
 | |
|     "            b'\\x22': 2022,\n",
 | |
|     "            b'\\x23': 2023,\n",
 | |
|     "        }\n",
 | |
|     "    }\n",
 | |
|     "}"
 | |
|    ]
 | |
|   },
 | |
|   {
 | |
|    "cell_type": "code",
 | |
|    "execution_count": 8,
 | |
|    "metadata": {},
 | |
|    "outputs": [],
 | |
|    "source": [
 | |
|     "def get_codes(platforms, codes):\n",
 | |
|     "  results = []\n",
 | |
|     "  for platform in platforms:\n",
 | |
|     "    for ecu in codes:\n",
 | |
|     "      for i in codes[ecu]:\n",
 | |
|     "        if isinstance(i, tuple):\n",
 | |
|     "          j = slice(i[0], i[1])\n",
 | |
|     "        else:\n",
 | |
|     "          j = slice(i, i+1)\n",
 | |
|     "        for version in FW_BY_ECU[platform][ecu]:\n",
 | |
|     "          code = version[j]\n",
 | |
|     "          if code not in codes[ecu][i]:\n",
 | |
|     "            print(f\"{platform} {code.hex()} not in {codes[ecu][i].keys()}\")\n",
 | |
|     "          else:\n",
 | |
|     "            results.append((platform, codes[ecu][i][code]))\n",
 | |
|     "  return results"
 | |
|    ]
 | |
|   },
 | |
|   {
 | |
|    "cell_type": "code",
 | |
|    "execution_count": 9,
 | |
|    "metadata": {},
 | |
|    "outputs": [
 | |
|     {
 | |
|      "name": "stdout",
 | |
|      "output_type": "stream",
 | |
|      "text": [
 | |
|       "SUBARU_IMPREZA 08 not in dict_keys([b'\\x18', b'\\x19', b' ', b'!', b'\"', b'#'])\n",
 | |
|       "SUBARU_IMPREZA 08 not in dict_keys([b'\\x18', b'\\x19', b' ', b'!', b'\"', b'#'])\n",
 | |
|       "SUBARU_IMPREZA 0c not in dict_keys([b'\\x18', b'\\x19', b' ', b'!', b'\"', b'#'])\n",
 | |
|       "SUBARU_IMPREZA 0c not in dict_keys([b'\\x18', b'\\x19', b' ', b'!', b'\"', b'#'])\n",
 | |
|       "SUBARU_IMPREZA 2e not in dict_keys([b'\\x18', b'\\x19', b' ', b'!', b'\"', b'#'])\n",
 | |
|       "SUBARU_IMPREZA 3f not in dict_keys([b'\\x18', b'\\x19', b' ', b'!', b'\"', b'#'])\n",
 | |
|       "correct_year=False  platform=SUBARU_FORESTER                  year=2018  years=[2019, 2020, 2021]\n",
 | |
|       "correct_year=False  platform=SUBARU_FORESTER                  year=2018  years=[2019, 2020, 2021]\n",
 | |
|       "correct_year=True   platform=SUBARU_FORESTER                  year=2019  years=[2019, 2020, 2021]\n",
 | |
|       "correct_year=True   platform=SUBARU_FORESTER                  year=2019  years=[2019, 2020, 2021]\n",
 | |
|       "correct_year=True   platform=SUBARU_FORESTER                  year=2019  years=[2019, 2020, 2021]\n",
 | |
|       "correct_year=True   platform=SUBARU_FORESTER                  year=2020  years=[2019, 2020, 2021]\n",
 | |
|       "correct_year=True   platform=SUBARU_FORESTER                  year=2020  years=[2019, 2020, 2021]\n",
 | |
|       "correct_year=True   platform=SUBARU_OUTBACK                   year=2020  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_OUTBACK                   year=2020  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_OUTBACK                   year=2020  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_OUTBACK                   year=2020  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_OUTBACK                   year=2020  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_OUTBACK                   year=2020  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_OUTBACK                   year=2020  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_OUTBACK                   year=2020  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_OUTBACK                   year=2020  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_OUTBACK                   year=2022  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_OUTBACK                   year=2022  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=False  platform=SUBARU_FORESTER_HYBRID           year=2019  years=[2020]\n",
 | |
|       "correct_year=False  platform=SUBARU_CROSSTREK_HYBRID          year=2019  years=[2020]\n",
 | |
|       "correct_year=False  platform=SUBARU_CROSSTREK_HYBRID          year=2021  years=[2020]\n",
 | |
|       "correct_year=True   platform=SUBARU_ASCENT_2023               year=2023  years=[2023]\n",
 | |
|       "correct_year=True   platform=SUBARU_IMPREZA                   year=2019  years=[2017, 2018, 2019]\n",
 | |
|       "correct_year=True   platform=SUBARU_IMPREZA                   year=2019  years=[2017, 2018, 2019]\n",
 | |
|       "correct_year=True   platform=SUBARU_IMPREZA                   year=2018  years=[2017, 2018, 2019]\n",
 | |
|       "correct_year=True   platform=SUBARU_IMPREZA                   year=2019  years=[2017, 2018, 2019]\n",
 | |
|       "correct_year=True   platform=SUBARU_IMPREZA                   year=2019  years=[2017, 2018, 2019]\n",
 | |
|       "correct_year=True   platform=SUBARU_IMPREZA                   year=2019  years=[2017, 2018, 2019]\n",
 | |
|       "correct_year=False  platform=SUBARU_FORESTER_2022             year=2021  years=[2022, 2023, 2024]\n",
 | |
|       "correct_year=False  platform=SUBARU_FORESTER_2022             year=2021  years=[2022, 2023, 2024]\n",
 | |
|       "correct_year=True   platform=SUBARU_FORESTER_2022             year=2022  years=[2022, 2023, 2024]\n",
 | |
|       "correct_year=True   platform=SUBARU_FORESTER_2022             year=2022  years=[2022, 2023, 2024]\n",
 | |
|       "correct_year=True   platform=SUBARU_ASCENT                    year=2019  years=[2019, 2020, 2021]\n",
 | |
|       "correct_year=True   platform=SUBARU_ASCENT                    year=2021  years=[2019, 2020, 2021]\n",
 | |
|       "correct_year=True   platform=SUBARU_OUTBACK_2023              year=2023  years=[2023]\n",
 | |
|       "correct_year=True   platform=SUBARU_OUTBACK_2023              year=2023  years=[2023]\n",
 | |
|       "correct_year=False  platform=SUBARU_IMPREZA_2020              year=2019  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=False  platform=SUBARU_IMPREZA_2020              year=2019  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_IMPREZA_2020              year=2020  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_IMPREZA_2020              year=2021  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_IMPREZA_2020              year=2021  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_IMPREZA_2020              year=2021  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_IMPREZA_2020              year=2021  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_LEGACY                    year=2020  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_LEGACY                    year=2020  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_LEGACY                    year=2020  years=[2020, 2021, 2022]\n",
 | |
|       "correct_year=True   platform=SUBARU_LEGACY                    year=2020  years=[2020, 2021, 2022]\n"
 | |
|      ]
 | |
|     }
 | |
|    ],
 | |
|    "source": [
 | |
|     "def test_year_code(platform, year):\n",
 | |
|     "  car_docs = CAR(platform).config.car_docs\n",
 | |
|     "  if isinstance(car_docs, list):\n",
 | |
|     "    car_docs = car_docs[0]\n",
 | |
|     "  years = [int(y) for y in car_docs.year_list]\n",
 | |
|     "  correct_year = year in years\n",
 | |
|     "  print(f\"{correct_year=!s: <6} {platform=: <32} {year=: <5} {years=}\")\n",
 | |
|     "\n",
 | |
|     "codes = get_codes(TEST_PLATFORMS, YEAR_CODES)\n",
 | |
|     "for platform, year in codes:\n",
 | |
|     "  test_year_code(platform, year)"
 | |
|    ]
 | |
|   },
 | |
|   {
 | |
|    "cell_type": "code",
 | |
|    "execution_count": 10,
 | |
|    "metadata": {},
 | |
|    "outputs": [
 | |
|     {
 | |
|      "name": "stdout",
 | |
|      "output_type": "stream",
 | |
|      "text": [
 | |
|       "in_possible_platforms=True   platform=SUBARU_FORESTER                  platforms=['SUBARU_FORESTER', 'SUBARU_FORESTER_HYBRID', 'SUBARU_FORESTER_2022']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_FORESTER                  platforms=['SUBARU_FORESTER', 'SUBARU_FORESTER_HYBRID', 'SUBARU_FORESTER_2022']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_FORESTER                  platforms=['SUBARU_FORESTER', 'SUBARU_FORESTER_HYBRID', 'SUBARU_FORESTER_2022']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_FORESTER                  platforms=['SUBARU_FORESTER', 'SUBARU_FORESTER_HYBRID', 'SUBARU_FORESTER_2022']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_FORESTER                  platforms=['SUBARU_FORESTER', 'SUBARU_FORESTER_HYBRID', 'SUBARU_FORESTER_2022']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_FORESTER                  platforms=['SUBARU_FORESTER', 'SUBARU_FORESTER_HYBRID', 'SUBARU_FORESTER_2022']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_FORESTER                  platforms=['SUBARU_FORESTER', 'SUBARU_FORESTER_HYBRID', 'SUBARU_FORESTER_2022']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_OUTBACK                   platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_OUTBACK                   platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_OUTBACK                   platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_OUTBACK                   platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_OUTBACK                   platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_OUTBACK                   platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_OUTBACK                   platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_OUTBACK                   platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_OUTBACK                   platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_OUTBACK                   platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_OUTBACK                   platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_FORESTER_HYBRID           platforms=['SUBARU_FORESTER', 'SUBARU_FORESTER_HYBRID', 'SUBARU_FORESTER_2022']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_CROSSTREK_HYBRID          platforms=['SUBARU_IMPREZA', 'SUBARU_IMPREZA_2020', 'SUBARU_CROSSTREK_HYBRID']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_CROSSTREK_HYBRID          platforms=['SUBARU_IMPREZA', 'SUBARU_IMPREZA_2020', 'SUBARU_CROSSTREK_HYBRID']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_ASCENT_2023               platforms=['SUBARU_ASCENT', 'SUBARU_ASCENT_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA                   platforms=['SUBARU_IMPREZA']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA                   platforms=['SUBARU_IMPREZA']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA                   platforms=['SUBARU_IMPREZA']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA                   platforms=['SUBARU_IMPREZA']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA                   platforms=['SUBARU_IMPREZA']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA                   platforms=['SUBARU_IMPREZA']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA                   platforms=['SUBARU_IMPREZA']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA                   platforms=['SUBARU_IMPREZA']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA                   platforms=['SUBARU_IMPREZA', 'SUBARU_IMPREZA_2020', 'SUBARU_CROSSTREK_HYBRID']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA                   platforms=['SUBARU_IMPREZA', 'SUBARU_IMPREZA_2020', 'SUBARU_CROSSTREK_HYBRID']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA                   platforms=['SUBARU_IMPREZA', 'SUBARU_IMPREZA_2020', 'SUBARU_CROSSTREK_HYBRID']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA                   platforms=['SUBARU_IMPREZA', 'SUBARU_IMPREZA_2020', 'SUBARU_CROSSTREK_HYBRID']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_FORESTER_2022             platforms=['SUBARU_FORESTER', 'SUBARU_FORESTER_HYBRID', 'SUBARU_FORESTER_2022']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_FORESTER_2022             platforms=['SUBARU_FORESTER', 'SUBARU_FORESTER_HYBRID', 'SUBARU_FORESTER_2022']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_FORESTER_2022             platforms=['SUBARU_FORESTER', 'SUBARU_FORESTER_HYBRID', 'SUBARU_FORESTER_2022']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_FORESTER_2022             platforms=['SUBARU_FORESTER', 'SUBARU_FORESTER_HYBRID', 'SUBARU_FORESTER_2022']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_ASCENT                    platforms=['SUBARU_ASCENT', 'SUBARU_ASCENT_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_ASCENT                    platforms=['SUBARU_ASCENT', 'SUBARU_ASCENT_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_OUTBACK_2023              platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_OUTBACK_2023              platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA_2020              platforms=['SUBARU_IMPREZA', 'SUBARU_IMPREZA_2020', 'SUBARU_CROSSTREK_HYBRID']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA_2020              platforms=['SUBARU_IMPREZA', 'SUBARU_IMPREZA_2020', 'SUBARU_CROSSTREK_HYBRID']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA_2020              platforms=['SUBARU_IMPREZA', 'SUBARU_IMPREZA_2020', 'SUBARU_CROSSTREK_HYBRID']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA_2020              platforms=['SUBARU_IMPREZA', 'SUBARU_IMPREZA_2020', 'SUBARU_CROSSTREK_HYBRID']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA_2020              platforms=['SUBARU_IMPREZA', 'SUBARU_IMPREZA_2020', 'SUBARU_CROSSTREK_HYBRID']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA_2020              platforms=['SUBARU_IMPREZA', 'SUBARU_IMPREZA_2020', 'SUBARU_CROSSTREK_HYBRID']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_IMPREZA_2020              platforms=['SUBARU_IMPREZA', 'SUBARU_IMPREZA_2020', 'SUBARU_CROSSTREK_HYBRID']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_LEGACY                    platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_LEGACY                    platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_LEGACY                    platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n",
 | |
|       "in_possible_platforms=True   platform=SUBARU_LEGACY                    platforms=['SUBARU_OUTBACK', 'SUBARU_LEGACY', 'SUBARU_OUTBACK_2023']\n"
 | |
|      ]
 | |
|     }
 | |
|    ],
 | |
|    "source": [
 | |
|     "def test_platform_code(platform, platforms):\n",
 | |
|     "  platforms = [str(p) for p in platforms]\n",
 | |
|     "  in_possible_platforms = platform in platforms\n",
 | |
|     "  print(f\"{in_possible_platforms=!s: <6} {platform=: <32} {platforms=}\")\n",
 | |
|     "\n",
 | |
|     "codes = get_codes(TEST_PLATFORMS, PLATFORM_CODES)\n",
 | |
|     "for platform, possible_platforms in codes:\n",
 | |
|     "  test_platform_code(platform, possible_platforms)"
 | |
|    ]
 | |
|   },
 | |
|   {
 | |
|    "cell_type": "code",
 | |
|    "execution_count": null,
 | |
|    "metadata": {},
 | |
|    "outputs": [],
 | |
|    "source": []
 | |
|   }
 | |
|  ],
 | |
|  "metadata": {
 | |
|   "kernelspec": {
 | |
|    "display_name": "Python 3 (ipykernel)",
 | |
|    "language": "python",
 | |
|    "name": "python3"
 | |
|   },
 | |
|   "language_info": {
 | |
|    "codemirror_mode": {
 | |
|     "name": "ipython",
 | |
|     "version": 3
 | |
|    },
 | |
|    "file_extension": ".py",
 | |
|    "mimetype": "text/x-python",
 | |
|    "name": "python",
 | |
|    "nbconvert_exporter": "python",
 | |
|    "pygments_lexer": "ipython3",
 | |
|    "version": "3.12.3"
 | |
|   }
 | |
|  },
 | |
|  "nbformat": 4,
 | |
|  "nbformat_minor": 4
 | |
| }
 | |
| 
 |