Simulate a DWDM Optical Network

Model fiber optic signal propagation and OSNR using GNPy.

Introduction

30 Second Summary

Every video call, streaming session, and cloud backup travels as pulses of light through fiber optic cables spanning thousands of kilometers. Telecom engineers plan these networks by simulating how signals degrade over distance and how amplifiers restore them.

In this project, you will use GNPy, an open-source optical network planning tool, to simulate real-world DWDM fiber networks. You will progress from running pre-built simulations on a real US backbone network to designing your own 3-node network from scratch.

What You'll Build

A working optical network simulation environment using GNPy, the same tool telecom operators use for production network planning.

By the end of this project, you'll have:

  • GNPy installed via both pip and git clone methods with verified CLI tools running on Windows 11.
  • Transmission simulation results for the CORONET Global backbone network showing per-channel OSNR and GSNR metrics with visual plots of signal propagation.
  • A custom 3-node DWDM network topology JSON file that you designed and simulated from scratch, complete with fiber spans and EDFA amplifiers.
  • Secret Mission: Compute multiple service routes through a mesh network and evaluate which paths can support 400G traffic.

Are there any prerequisites?

You need basic command-line familiarity (navigating directories and running commands). No prior knowledge of optical networking or DWDM is required. You should have Python 3.12 installed on Windows 11.

Install GNPy via Pip

GNPy is the open-source optical network planning tool used by telecom operators to simulate how light signals travel through fiber optic cables. Installing it gives you the same simulation engine that real network engineers use to plan DWDM networks.

In this step, you will create a clean Python virtual environment, install GNPy from PyPI, and run a quick smoke test to confirm the CLI tools work.

In this step, get ready to:
  • Create a project folder with an isolated Python virtual environment.
  • Install GNPy and verify its CLI tools are accessible.
  • Run the default transmission simulation as a smoke test.
Create a virtual environment and install GNPy via pip

Python virtual environments isolate project dependencies so they do not conflict with other projects on your machine. You will create one specifically for this GNPy project.

  • Press the Windows key to open your search bar.
  • Type PowerShell and press Enter to open it.

What is PowerShell?

PowerShell is the command-line shell built into Windows. You will use it to run all terminal commands in this project.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir gnpy-project
cd gnpy-project
  • Confirm the folder exists by running:
ls

You should see an empty gnpy-project directory listed on your Desktop.

  • Create a Python virtual environment by running this command:
python -m venv .venv

What does this command do?

python -m venv .venv tells Python to create a virtual environment in a folder called .venv inside your project directory. All packages you install will live in this folder, separate from your system Python.

  • Allow PowerShell to run activation scripts by running this command:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

Why do I need to change the execution policy?

Windows 11 blocks PowerShell scripts by default as a security measure. This one-time change allows locally-created scripts (like the virtual environment activation script) to run. It only affects your user account and persists across sessions.

  • If PowerShell asks you to confirm the policy change, type Y and press Enter.
  • Activate the virtual environment by running this command:
.venv\Scripts\Activate.ps1

Your terminal prompt should now start with (.venv) indicating the virtual environment is active.

  • Install GNPy by running this command:
pip install gnpy

What does this install?

This downloads GNPy v2.14.1 (the latest version) from PyPI along with all its dependencies, including matplotlib for plotting. It also installs command-line tools like gnpy-transmission-example and gnpy-example-data into your virtual environment.

The installation may take a minute as it downloads several packages. Wait until you see the Successfully installed message before continuing.

Seeing a permissions error or download failure?

Make sure your prompt shows (.venv) before the path. If it does not, re-run .venv\Scripts\Activate.ps1 to activate the virtual environment first.

If you see a network timeout, check your internet connection and try again.

Verify CLI tools are available

GNPy ships with command-line tools that you will use throughout this project. Verifying them now confirms the installation succeeded.

  • Check that the transmission example tool is installed by running this command:
gnpy-transmission-example -h

What should I see?

You should see help text listing all available options for the transmission simulation tool, including flags like --show-channels, --plot, and --power. This confirms gnpy-transmission-example is on your PATH and ready to use.

  • Find the example data directory by running this command:
gnpy-example-data

What does gnpy-example-data do?

This command prints the full path to GNPy's bundled example data directory. That folder contains pre-built network topology files you will use for simulations, including edfa_example_network.json and CORONET_Global_Topology.json.

  • Explore the example data folder by running this command:
ls $(gnpy-example-data)

You should see a list of JSON and Excel files including edfa_example_network.json, CORONET_Global_Topology.json, eqpt_config.json, and meshTopologyExampleV2.xls.

Command not found?

If you see an error saying the command is not recognized, confirm your virtual environment is still active. Your prompt should show (.venv) at the beginning. If it does not, re-activate by running .venv\Scripts\Activate.ps1 again.

Run a quick smoke test

A smoke test is a quick run to confirm the tool produces valid output. You will run GNPy's default simulation on a single-span fiber network.

  • Run the default transmission simulation by running this command:
gnpy-transmission-example

What does this simulation do?

Without any arguments, gnpy-transmission-example runs a transmission simulation on the default single-span network defined in edfa_example_network.json.

It propagates a spectrum of channels at 32 Gbaud, 50 GHz spacing, and 0 dBm per channel. The output shows the average OSNR and SNR across network elements (transceivers, fibers, and amplifiers).

You should see a table of results in your terminal with columns showing signal metrics including OSNR and SNR values for each network element.

What are OSNR and SNR?

OSNR (Optical Signal-to-Noise Ratio) measures signal power divided by amplifier noise (Pch/Pase). It tells you how much the amplifiers are degrading your signal.

SNR (Signal-to-Noise Ratio) adds fiber nonlinear noise into the equation (Pch/(Pnli+Pase)). It gives a more complete picture of signal quality at any point in the network.

Seeing an error instead of results?

Make sure you are running the command from inside the gnpy-project folder with (.venv) showing in your prompt.

If you see an import error, try reinstalling with pip install gnpy and check for any error messages during installation.

Your GNPy environment is ready. Next up, you will clone the GNPy repository to get direct access to all the example network files and explore the equipment configuration.

Install GNPy via Git Clone

In Step 1, you installed GNPy from PyPI and confirmed the CLI tools work. That approach is fast, but the example data files end up buried inside your Python packages folder.

Cloning the repository from GitHub gives you direct access to GNPy's example data files right inside your project folder. You will be able to open, read, and modify topology files without hunting through system directories.

In this step, get ready to:
  • Clone the GNPy repository and install it in editable mode.
  • Explore the example-data directory and identify key network files.
  • Understand the three inputs every GNPy simulation requires.
Clone the GNPy repository

The official GNPy source lives on GitHub under the Telecom Infra Project organisation. Cloning it creates a local copy of the entire codebase, including all example network topology files.

  • Clone the repository into your project folder by running this command:
git clone https://github.com/Telecominfraproject/oopt-gnpy

What does this command do?

This downloads the full GNPy source code into a new folder called oopt-gnpy inside your current directory (C:\gnpy-project).

  • Confirm the folder was created by running:
ls

You should see an oopt-gnpy folder listed alongside your .venv folder.

Don't see the oopt-gnpy folder?

  • Make sure you are inside C:\gnpy-project before running the clone command. Run pwd to confirm your current directory.
  • If you see a "git is not recognized" error, Git is not installed or not in your PATH. Download it from git-scm.com and restart your terminal after installation.

Help me fix my git clone issue

  • Navigate into the cloned repository by running:
cd oopt-gnpy

Now you need to install GNPy from this local source in editable mode. Editable mode means any changes to the source code take effect immediately without reinstalling.

  • Install GNPy in editable mode by running:
pip install --editable .

What does editable mode do?

The --editable flag (often shortened to -e) links your Python environment to the local source code instead of copying files into site-packages. This is the recommended developer installation for GNPy.

The . at the end tells pip to install from the current directory.

  • Verify the installation succeeded by running:
python -c "import gnpy"

If no error appears, GNPy is installed correctly from the cloned source.

Seeing an installation error?

  • Make sure your virtual environment is still activated. You should see (.venv) at the start of your terminal prompt. If not, reactivate it with ..\.venv\Scripts\Activate.ps1 and try again.
  • Confirm you are inside the oopt-gnpy directory when running the pip command. Run pwd to check.

Help me troubleshoot my GNPy editable install

Explore the example-data directory

The cloned repository contains an example-data folder with real network topologies and equipment configurations. This is the main advantage of cloning: direct filesystem access to all example files.

  • List the contents of the example-data directory by running:
ls gnpy\example-data

You should see a collection of .json and .xls files. The key files you will use throughout this project are:

  • CORONET_Global_Topology.json - a real US backbone fiber network topology.
  • edfa_example_network.json - the simple single-span network you already tested in Step 1.
  • eqpt_config.json - the default equipment library defining amplifier models, fiber types, and transceiver specs.
  • meshTopologyExampleV2.xls - a mesh network topology used for path computation.

Why so many files?

GNPy ships with multiple example topologies so you can experiment with different network scales. The CORONET files represent real-world US backbone networks. The edfa_example files are simplified networks for learning.

  • Open eqpt_config.json in Notepad to see how equipment is defined:
notepad gnpy\example-data\eqpt_config.json

Scroll through the file. You will see JSON sections defining different equipment types: amplifiers (Edfa), fiber types (Fiber), transceivers (Transceiver), and ROADMs.

What is eqpt_config.json?

This file is the equipment library. It acts like a catalog of datasheets for every piece of hardware in the simulated network.

Each entry specifies real physical properties. For amplifiers, you will see noise figure values and gain ranges. For fiber, you will see attenuation coefficients and dispersion parameters. GNPy uses these properties to calculate how signals degrade across the network.

Notepad showing garbled text?

  • The file should open as readable JSON text. If you see strange characters, close Notepad, right-click the file in File Explorer, and choose **Open with** then select a text editor that supports UTF-8.

Help me open eqpt_config.json properly

Understand the three inputs GNPy needs

Every GNPy simulation combines three pieces of information to produce results. Now that you have seen the example files, here is how they map to the three required inputs.

  • Network topology (JSON or XLS) describes how the network is physically connected. It defines nodes (cities, amplifier sites), fiber spans between them, and the connections linking everything together. Files like CORONET_Global_Topology.json and edfa_example_network.json are topology files.
  • Equipment library (JSON) holds machine-readable datasheets for every piece of hardware the simulation references. Amplifier noise figures, fiber attenuation values, and transceiver capabilities all live here. The file eqpt_config.json is the equipment library.
  • Simulation options tell GNPy what to simulate and under which conditions. This includes the number of channels, channel spacing, and launch power per channel. The defaults are 32 Gbaud channels at 50 GHz spacing with 0 dBm per channel.

How do these three inputs work together?

The topology tells GNPy the physical route a signal takes. The equipment library tells it how each component along that route affects the signal. The simulation options define the traffic (channels and power levels) being pushed through.

GNPy combines all three to calculate how much noise accumulates at each point, producing OSNR and GSNR values that tell you whether the link can carry traffic reliably.

You now have the full GNPy source code on your machine with direct access to all example topologies. Next up, you will run a transmission simulation across a real US backbone network and see how signals degrade across hundreds of kilometers of fiber.

Simulate the CORONET Global Backbone

You now have GNPy installed and verified with both the pip and git clone methods. The real power of this tool shows when you point it at a production-scale network topology.

In this step, you will simulate light signal transmission across the CORONET Global backbone. This is a real US-wide fiber optic network included in GNPy's example data. You will observe how signal quality (measured as OSNR and GSNR) degrades through fiber spans and recovers at each amplifier along the path.

In this step, get ready to:
  • Run a transmission simulation on the CORONET Global network between Abilene and Albany.
  • Explore per-channel metrics and list all available nodes.
  • Generate graphical plots showing signal propagation along the path.
Run transmission on the CORONET Global network

The CORONET Global Topology represents a real US backbone fiber network with cities like Abilene, Albany, Atlanta, and dozens more connected by fiber spans and amplifiers. Running a simulation on it shows you exactly how telecom engineers evaluate whether a path can carry traffic.

  • Navigate to the example-data directory by running this command:
cd C:\gnpy-project\oopt-gnpy\gnpy\example-data
  • Run the transmission simulation on the CORONET Global network by running this command:
gnpy-transmission-example CORONET_Global_Topology.json

What does this command do?

The gnpy-transmission-example command takes a network topology JSON file and simulates the propagation of light signals through it. By default, it propagates a spectrum of channels at 32 Gbaud, 50 GHz spacing, and 0 dBm per channel.

For the CORONET Global network, the default simulation computes transmission between Abilene and Albany. The output shows OSNR and SNR values at each network element along the path (transceivers, ROADMs, fibers, and amplifiers).

  • Look at the terminal output. You should see a list of network elements (Transceiver, Roadm, Fiber, Edfa) with signal metrics for each one.

What is OSNR?

OSNR stands for Optical Signal-to-Noise Ratio. In GNPy, OSNR = Pch/Pase. This measures the signal power (Pch) relative to the noise added by amplifiers (Pase, or Amplified Spontaneous Emission).

SNR (Signal-to-Noise Ratio) goes further: SNR = Pch/(Pnli+Pase). It adds fiber nonlinear interference noise (Pnli) to give a more complete picture of signal quality. A higher value means a cleaner signal.

Not seeing any output?

Make sure your virtual environment is still activated. You should see (.venv) at the start of your prompt. If not, re-activate it with .venv\Scripts\Activate from C:\gnpy-project.

Also confirm you are inside the example-data folder so the command can find CORONET_Global_Topology.json.

help me troubleshoot why gnpy-transmission-example is not producing output

Explore simulation output and channel metrics

The default output shows average metrics across all channels. To see the performance of each individual channel, you can add the --show-channels flag. You can also list every node in the network to pick a different route.

  • Re-run the simulation with per-channel metrics by running this command:
gnpy-transmission-example CORONET_Global_Topology.json --show-channels

What does --show-channels add?

The --show-channels flag displays the final per-channel OSNR and GSNR summary. Each DWDM channel operates at a slightly different frequency, and their performance can vary due to frequency-dependent effects. This flag lets you see whether any channels are close to the noise floor.

  • List all available transceiver nodes in the CORONET network by running this command:
gnpy-transmission-example CORONET_Global_Topology.json -l

What does the -l flag show?

The -l (or --list-nodes) flag lists all transceiver nodes in the network topology. Each node name corresponds to a city in the CORONET backbone (Abilene, Albany, Atlanta, Austin, and many more). You can use any two of these as source and destination for a simulation.

  • Look at the list of nodes in the output. Pick a different pair of cities to simulate.
  • Run the simulation for a different source-destination pair using the --path flag. For example, to simulate between Atlanta and Austin:
gnpy-transmission-example CORONET_Global_Topology.json --path "Atlanta|Austin" --show-channels

How does --path work?

The --path flag lets you specify ROADM nodes as a list separated by |. GNPy infers the source and destination from the first and last nodes you provide. You can include intermediate nodes to force the route through specific cities.

  • You should see OSNR and SNR output for the new route. Notice how the values differ from the Abilene-to-Albany route because the fiber distances and number of amplifiers are different.

Seeing a node name error?

The node names in --path must exactly match the names from the -l output. They are case-sensitive. Run the -l command again and copy the exact city names you want to use.

help me fix a node name error in gnpy-transmission-example

Visualize the results

Numbers in a terminal tell part of the story. A plot makes the signal degradation pattern immediately visible. You can see exactly where the signal drops through each fiber span and jumps back up at each EDFA amplifier.

  • Re-run the default Abilene-to-Albany simulation with the plot flag by running this command:
gnpy-transmission-example CORONET_Global_Topology.json -pl

What does the -pl flag generate?

The -pl (or --plot) flag generates graphical plots using matplotlib (included with GNPy). The plots show signal power and OSNR at every point along the transmission path.

Look at the plot. You will see a sawtooth pattern: the signal drops through each fiber span (loss) and recovers at each EDFA amplifier (gain). This visual makes it easy to spot where a network might be struggling.

  • A plot window opens. Examine the graph to identify the sawtooth pattern of signal loss and amplification.
  • Close the plot window when you are done examining it.

Why does the signal follow a sawtooth pattern?

Fiber optic cables attenuate (weaken) light signals over distance. A typical single-mode fiber loses about 0.2 dB per kilometer. After 80 km of fiber, the signal has lost around 16 dB of power.

EDFA amplifiers are placed after each fiber span to boost the signal back up. This creates the repeating drop-and-recover pattern you see in the plot. The trade-off is that each amplifier adds a small amount of noise (ASE), which accumulates over the full path.

Plot window not appearing?

The -pl flag requires a display environment. On Windows 11 with a normal desktop session, this should work automatically. If the plot does not appear, try running the command again and check that no error messages mention matplotlib.

help me get the gnpy plot to display on Windows

You have now simulated a real US backbone optical network and visualized how signals propagate across thousands of kilometers of fiber. Next up, you will design your own DWDM network topology from scratch.

Design Your Own DWDM Network

You have simulated a production US backbone network and seen how signal quality changes across fiber spans and amplifiers. Now it is time to design your own network from scratch.

In this step, you will create a custom 3-node DWDM topology in JSON, simulate it with GNPy, and experiment with physical parameters to see how they affect signal quality.

In this step, get ready to:
  • Author a 3-node linear network topology JSON file defining transceivers, fiber spans, and EDFA amplifiers.
  • Run a transmission simulation on your custom network and interpret per-channel results.
  • Experiment with fiber span length and launch power to observe SNR trade-offs.
Create a 3-node linear network topology JSON

GNPy reads network designs from JSON files that describe elements (transceivers, fiber spans, amplifiers) and how they connect. You will now author your own topology file that models a simple 3-city linear link.

  • Create a new file called my_network.json inside your C:\gnpy-project folder.
  • You can create it in any text editor (Notepad, VS Code, or any editor you prefer).
  • Paste the following topology into my_network.json:
{
  "network_name": "Custom 3-Node Linear Network",
  "elements": [
    {
      "uid": "City-A",
      "type": "Transceiver",
      "metadata": {
        "location": {
          "city": "City-A",
          "region": "",
          "latitude": 0,
          "longitude": 0
        }
      }
    },
    {
      "uid": "fiber (City-A → City-B)",
      "type": "Fiber",
      "type_variety": "SSMF",
      "params": {
        "length": 80,
        "loss_coef": 0.2,
        "length_units": "km",
        "att_in": 0,
        "con_in": 0.5,
        "con_out": 0.5
      },
      "metadata": {
        "location": {
          "city": "",
          "region": "",
          "latitude": 1,
          "longitude": 0
        }
      }
    },
    {
      "uid": "Edfa1",
      "type": "Edfa",
      "type_variety": "std_low_gain",
      "operational": {
        "gain_target": 17,
        "tilt_target": 0,
        "out_voa": 0
      },
      "metadata": {
        "location": {
          "city": "",
          "region": "",
          "latitude": 2,
          "longitude": 0
        }
      }
    },
    {
      "uid": "City-B",
      "type": "Transceiver",
      "metadata": {
        "location": {
          "city": "City-B",
          "region": "",
          "latitude": 2,
          "longitude": 0
        }
      }
    },
    {
      "uid": "fiber (City-B → City-C)",
      "type": "Fiber",
      "type_variety": "SSMF",
      "params": {
        "length": 80,
        "loss_coef": 0.2,
        "length_units": "km",
        "att_in": 0,
        "con_in": 0.5,
        "con_out": 0.5
      },
      "metadata": {
        "location": {
          "city": "",
          "region": "",
          "latitude": 3,
          "longitude": 0
        }
      }
    },
    {
      "uid": "Edfa2",
      "type": "Edfa",
      "type_variety": "std_low_gain",
      "operational": {
        "gain_target": 17,
        "tilt_target": 0,
        "out_voa": 0
      },
      "metadata": {
        "location": {
          "city": "",
          "region": "",
          "latitude": 4,
          "longitude": 0
        }
      }
    },
    {
      "uid": "City-C",
      "type": "Transceiver",
      "metadata": {
        "location": {
          "city": "City-C",
          "region": "",
          "latitude": 4,
          "longitude": 0
        }
      }
    }
  ],
  "connections": [
    {
      "from_node": "City-A",
      "to_node": "fiber (City-A → City-B)"
    },
    {
      "from_node": "fiber (City-A → City-B)",
      "to_node": "Edfa1"
    },
    {
      "from_node": "Edfa1",
      "to_node": "City-B"
    },
    {
      "from_node": "City-B",
      "to_node": "fiber (City-B → City-C)"
    },
    {
      "from_node": "fiber (City-B → City-C)",
      "to_node": "Edfa2"
    },
    {
      "from_node": "Edfa2",
      "to_node": "City-C"
    }
  ]
}

What does this topology define?

The JSON has three sections working together:

  • network_name gives your network a human-readable label.
  • elements lists every piece of equipment. You have three Transceiver nodes (City-A, City-B, City-C), two 80 km Fiber spans using SSMF (Standard Single-Mode Fiber) with 0.2 dB/km loss, and two Edfa amplifiers that compensate fiber loss.
  • connections defines unidirectional links between elements, forming the path: City-A → Fiber → Edfa → City-B → Fiber → Edfa → City-C.

Why std_low_gain for the amplifiers?

The std_low_gain amplifier type in GNPy's default equipment library has a maximum flat gain of 16 dB and a gain range starting at 8 dB. An 80 km SSMF span at 0.2 dB/km produces about 16 dB of loss (plus 1 dB from connectors), which sits right in this amplifier's operating range.

The gain_target of 17 tells the amplifier to compensate the full span loss so the signal arrives at the next element at roughly the same power level it started with.

  • Save my_network.json.
  • Confirm the file exists by running:
ls C:\gnpy-project\my_network.json

You should see my_network.json listed in the output.

File not found?

  • Make sure you saved the file inside C:\gnpy-project\ (not inside the oopt-gnpy subfolder).
  • Confirm the filename is exactly my_network.json with no extra extensions like .json.txt.

Still stuck? Help me find my_network.json in my project folder

Run your custom network simulation

Your topology file is ready. Time to feed it into GNPy and see whether your 3-node network can carry traffic.

  • Run the transmission simulation on your custom network with per-channel output by running:
gnpy-transmission-example C:\gnpy-project\my_network.json --show-channels

What does this command do?

gnpy-transmission-example propagates a default spectrum (32 Gbaud channels at 50 GHz spacing, 0 dBm/channel) from City-A to City-C through both fiber spans and amplifiers.

The --show-channels flag displays the final per-channel OSNR and GSNR summary so you can see how each individual wavelength performs.

You should see a table of results showing OSNR and SNR values for each network element, followed by a per-channel summary.

Are all channels above the threshold for 100G?

For 100G transmission (100 Gbit/s per channel), a typical minimum GSNR threshold is around 10-12 dB. If all your channels show GSNR values above this range, your network can support 100G traffic on every wavelength.

With two 80 km spans and properly configured amplifiers, you should see comfortable margins above this threshold.

Seeing a JSON parsing error?

  • Open my_network.json and check for missing commas between objects, mismatched braces, or trailing commas after the last item in an array.
  • Make sure all uid values in connections match the uid values in elements exactly (including the arrow character).
  • Verify the file is valid JSON by pasting it into an online JSON validator.

Need help? Help me fix a JSON parsing error in my GNPy topology file

Experiment with parameters

Real network engineers adjust physical parameters to understand how their designs perform under different conditions. You will now modify span length and launch power to see how each affects signal quality.

  • Open my_network.json in your text editor.
  • Find both fiber elements and change their length value from 80 to 120.
  • Save the file.
  • Re-run the simulation with the longer spans by running:
gnpy-transmission-example C:\gnpy-project\my_network.json --show-channels

What changed with longer spans?

A 120 km span produces about 24 dB of loss (120 km x 0.2 dB/km), compared to 16 dB for the original 80 km spans. The amplifiers must work harder to compensate, and more accumulated noise (Pase) reduces your OSNR.

You should see noticeably lower GSNR values compared to your 80 km results. This is why real networks place amplifiers every 80-100 km.

Now test how launch power affects the balance between noise types.

  • Change the fiber length values back to 80 and save the file.
  • Run the simulation with a lower launch power of -2 dBm by running:
gnpy-transmission-example C:\gnpy-project\my_network.json --show-channels --power -2
  • Run the simulation again with a higher launch power of +2 dBm by running:
gnpy-transmission-example C:\gnpy-project\my_network.json --show-channels --power 2

What is the power vs noise trade-off?

OSNR = Pch/Pase. Higher launch power increases the signal (Pch) relative to amplifier noise (Pase), improving OSNR.

SNR = Pch/(Pnli+Pase). However, higher power also increases nonlinear noise (Pnli) from effects like four-wave mixing in the fiber. At some point, pushing more power actually decreases overall SNR because nonlinear noise grows faster than signal power.

Compare your -2 dBm and +2 dBm results. You should see OSNR improve with higher power, but GSNR (which accounts for nonlinear noise) may show a smaller improvement or even a slight decrease at very high power.

  • Confirm that my_network.json is saved with the original 80 km spans (revert your 120 km edit if you have not already).

Getting unexpected SNR values?

  • Make sure you reverted the fiber length back to 80 before running the power experiments.
  • Verify that --power is followed by a space and then the number (e.g. --power -2 not --power-2).
  • If you see a warning about gain targets, the amplifier may be saturating. This is expected behavior for extreme power values.

Need help interpreting your results? Help me understand my GNPy SNR output

✔️ Awesome, I've got everything!

Great. Double check you have saved my_network.json with the original 80 km fiber spans before moving on.

ⓧ I'd like to double check the full code

Here is the complete my_network.json file as it should look with 80 km spans:

{
  "network_name": "Custom 3-Node Linear Network",
  "elements": [
    {
      "uid": "City-A",
      "type": "Transceiver",
      "metadata": {
        "location": {
          "city": "City-A",
          "region": "",
          "latitude": 0,
          "longitude": 0
        }
      }
    },
    {
      "uid": "fiber (City-A → City-B)",
      "type": "Fiber",
      "type_variety": "SSMF",
      "params": {
        "length": 80,
        "loss_coef": 0.2,
        "length_units": "km",
        "att_in": 0,
        "con_in": 0.5,
        "con_out": 0.5
      },
      "metadata": {
        "location": {
          "city": "",
          "region": "",
          "latitude": 1,
          "longitude": 0
        }
      }
    },
    {
      "uid": "Edfa1",
      "type": "Edfa",
      "type_variety": "std_low_gain",
      "operational": {
        "gain_target": 17,
        "tilt_target": 0,
        "out_voa": 0
      },
      "metadata": {
        "location": {
          "city": "",
          "region": "",
          "latitude": 2,
          "longitude": 0
        }
      }
    },
    {
      "uid": "City-B",
      "type": "Transceiver",
      "metadata": {
        "location": {
          "city": "City-B",
          "region": "",
          "latitude": 2,
          "longitude": 0
        }
      }
    },
    {
      "uid": "fiber (City-B → City-C)",
      "type": "Fiber",
      "type_variety": "SSMF",
      "params": {
        "length": 80,
        "loss_coef": 0.2,
        "length_units": "km",
        "att_in": 0,
        "con_in": 0.5,
        "con_out": 0.5
      },
      "metadata": {
        "location": {
          "city": "",
          "region": "",
          "latitude": 3,
          "longitude": 0
        }
      }
    },
    {
      "uid": "Edfa2",
      "type": "Edfa",
      "type_variety": "std_low_gain",
      "operational": {
        "gain_target": 17,
        "tilt_target": 0,
        "out_voa": 0
      },
      "metadata": {
        "location": {
          "city": "",
          "region": "",
          "latitude": 4,
          "longitude": 0
        }
      }
    },
    {
      "uid": "City-C",
      "type": "Transceiver",
      "metadata": {
        "location": {
          "city": "City-C",
          "region": "",
          "latitude": 4,
          "longitude": 0
        }
      }
    }
  ],
  "connections": [
    {
      "from_node": "City-A",
      "to_node": "fiber (City-A → City-B)"
    },
    {
      "from_node": "fiber (City-A → City-B)",
      "to_node": "Edfa1"
    },
    {
      "from_node": "Edfa1",
      "to_node": "City-B"
    },
    {
      "from_node": "City-B",
      "to_node": "fiber (City-B → City-C)"
    },
    {
      "from_node": "fiber (City-B → City-C)",
      "to_node": "Edfa2"
    },
    {
      "from_node": "Edfa2",
      "to_node": "City-C"
    }
  ]
}

You have designed a DWDM network from scratch, simulated it, and observed how physical parameters affect signal quality. Next up, you will tackle the Secret Mission and compute multiple service routes through a mesh network.

Secret mission

Multi-Path Route Computation

Ready for a real network planning challenge? Use gnpy-path-request to compute and compare multiple service routes through a mesh topology. Determine which paths can carry 400G traffic and which links are the performance bottleneck.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs or cloud resources to worry about. Decide whether to keep your project files for future experimentation, or delete them to free up disk space.

Resources you used:

  • Project folder at C:\gnpy-project (contains your virtual environment, cloned repository, custom topology, and results).
  • Virtual environment at C:\gnpy-project\.venv with GNPy and all dependencies installed.
  • Cloned GNPy repository at C:\gnpy-project\oopt-gnpy.
  • Custom files: my_network.json and path_results.json.

Keep everything running

No action needed. Choose this if you want to keep experimenting with GNPy, try new topologies, or revisit the CORONET simulation later.

  • Your virtual environment stays at C:\gnpy-project\.venv. Reactivate it anytime with .venv\Scripts\Activate from the project folder.
  • All example data, your custom topology, and path results remain in place.
  • Git remains installed on your system for future use.

Pause - I'll come back to this later

Deactivate the virtual environment to free up your terminal session. All files stay on disk for when you return.

  • Deactivate your virtual environment by running:
deactivate
  • When you want to resume, open PowerShell, navigate to C:\gnpy-project, and reactivate with .venv\Scripts\Activate.

Delete - I don't want to use this again

Remove all project files, the virtual environment, and the cloned repository. This frees up disk space completely.

  • First, deactivate your virtual environment if it is still active:
deactivate
  • Navigate out of the project folder:
cd ~\Desktop
  • Delete the entire project folder and all its contents:
Remove-Item -Recurse -Force C:\gnpy-project
  • Confirm the folder is gone by running:
Test-Path C:\gnpy-project
  • You should see False in the output, confirming the folder has been removed.
  • Git remains installed on your system. It is a general-purpose tool you will likely use for other projects.

Nice Work!

Nice Work!

You just simulated real-world optical networks the same way telecom engineers plan production fiber infrastructure. From installing GNPy to designing your own DWDM network from scratch, you now understand how light signals travel through fiber, degrade over distance, and get restored by amplifiers.

You've learned how to:

  • Simulated signal transmission across the CORONET Global backbone (a real US optical network) and analyzed per-channel OSNR and GSNR metrics with visualization.
  • Designed a custom 3-node DWDM network topology from scratch in JSON, complete with transceivers, fiber spans, and EDFA amplifiers.
  • Experimented with fiber span length and launch power to understand how physical parameters affect signal quality and nonlinear noise.
  • Secret Mission: Computed multiple service routes through a mesh network using gnpy-path-request and evaluated which paths can support 400G traffic based on GSNR thresholds.

Ready to quiz yourself?