Sourav Mukherjee
PhD Student
Room N2410
University of Bremen
Department of Communications Engineering (Arbeitsbereich Nachrichtentechnik)
Otto-Hahn-Allee NW1
D-28359 Bremen, Germany
📧 mukherjee@ant.uni-bremen.de
If you use the code in any way, please cite the original papers and the code.
The code is citable using the following DOI:

Title: Dynamic FDD for Spectrum Sharing in Non-Terrestrial Networks
Authors: Sourav Mukherjee, Bho Matthiesen, Armin Dekorsy, Petar Popovski
ArXiv:
Title: Dynamic Downlink-Uplink Spectrum Sharing in Non-Terrestrial Networks
Authors: Sourav Mukherjee, Bho Matthiesen, Armin Dekorsy, Petar Popovski
ArXiv: https://arxiv.org/abs/2511.08188
Published: in IEEE Proceedings of International Conference on Communications Workshops (ICC Workshops) 2026, Glasgow, UK
numpyskyfieldmatplotlibcvxpymosek
cvxpy is configured to solve with MOSEK, so a working MOSEK installation and license are required.
Example installation:
pip install numpy skyfield matplotlib cvxpy mosekUse main.py to run one (J, K) case directly.
Example:
python3 main.py \
--num-sat 2 \
--num-ue 10 \
--n-iter 5 \
--chunk-id 1 \
--output-root DataMain arguments:
--num-sat: number of satellitesJ--num-ue: number of UEsK--n-iter: number of simulation iterations for this run--chunk-id: chunk number used in output filenames--output-root: where results are written--seed: reproducible base seed--plot: also save a chunk-level CDF plot
Use scripts/generate_experiment_jobs.py.
This script now does three things automatically:
- Generates chunk job scripts
- Runs the chunk jobs in parallel locally
- Merges the chunk outputs at the end
Example:
python3 scripts/generate_experiment_jobs.py \
--j-values 1 2 3 4 \
--k-values 10 15 20 25 30 40 \
--total-iterations 200 \
--chunk-size 10 \
--jobs-root Data/jobs \
--output-root Data \
--max-parallel 4Meaning of the main batch arguments:
--j-values: list ofJvalues--k-values: list ofKvalues--total-iterations: total iterations per configuration--chunk-size: iterations per chunk job--jobs-root: where generated shell scripts and logs are stored--output-root: where simulation results are stored--max-parallel: maximum number of chunk jobs running at the same time
Example:
--total-iterations 200--chunk-size 10
means each (J, K) configuration is split into 20 chunk jobs, each running 10 iterations.
If you only want the shell scripts without executing them:
python3 scripts/generate_experiment_jobs.py \
--j-values 1 2 \
--k-values 10 15 \
--total-iterations 40 \
--chunk-size 10 \
--jobs-root Data/jobs \
--output-root Data \
--generate-onlyIf --output-root Data is used, results are written under:
Data/experiments/
For a configuration (J=2, K=15), the structure is:
Data/experiments/J_02_K_015/
├── chunks/
│ ├── chunk_001.json
│ ├── chunk_002.json
│ └── ...
├── merged/
│ ├── results.json
│ └── cdf_merged.eps
├── plots/
│ └── chunk_001.eps
└── runtime/
├── chunk_001_satellites.json
└── ...
Each chunk file is saved here:
Data/experiments/J_XX_K_YYY/chunks/chunk_ZZZ.json
Each chunk file is checkpointed after every completed iteration and overwritten in the same file.
If you open the file during execution, you can see:
statuscompleted_iterationstarget_iterationsiteration_resultsresult_case_orderresults_by_caseresults_with_spinresults_without_spin
So if a run fails midway, the file still shows how many iterations were already completed and their values.
The main f_0 series saved for each chunk are:
bestSolution_wspin:max(solutions)bestSolution_spin0:solutions[0]bestSolution_spin1:solutions[-1]bestSolution_randSpin: random choice betweensolutions[0]andsolutions[-1]
For backward compatibility, the chunk files also keep:
results_with_spinas an alias ofbestSolution_wspinresults_without_spinas an alias ofbestSolution_randSpin
For each chunk, the generated satellite geometry is stored in:
Data/experiments/J_XX_K_YYY/runtime/chunk_ZZZ_satellites.json
After all chunks finish, merged outputs are stored in:
Data/experiments/J_XX_K_YYY/merged/results.json
Data/experiments/J_XX_K_YYY/merged/cdf_merged.eps
The merged JSON stores the same four f_0 cases inside results_by_case, and the merged CDF plot shows all available cases.
The overall summary across configurations is stored in:
Data/experiments/summary.csv
summary.csv includes mean and max columns for each saved case.
Generated shell scripts and local execution logs are stored under:
Data/jobs/
Example:
Data/jobs/J_02_K_015/
├── chunk_001.sh
├── chunk_002.sh
├── run_all_chunks.sh
└── logs/
├── chunk_001.log
└── chunk_002.log
Useful generated files:
Data/jobs/run_all.sh: run all chunk scripts sequentiallyData/jobs/run_all_parallel.sh: run all chunk scripts in parallelData/jobs/manifest.json: manifest of all generated chunk jobs
- The default UE drop radius is controlled in
main.pybyDEFAULT_RADIUS_KM. - The default local parallelism is based on CPU count if
--max-parallelis not specified. - If you want a specific radius, pass
--radius-km ...explicitly.
root/
├── main.py
├── satTLEgenerator.py
├── scripts/
│ ├── generate_experiment_jobs.py
│ └── merge_experiment_results.py
├── Data/
├── Results/
└── utils/
├── __init__.py
├── components.py
├── helper.py
├── network.py
├── optimizer.py
├── simulator.py
└── synthetic_orbit.py