diff --git a/.github/workflows/pythoncoverage.yml b/.github/workflows/pythoncoverage.yml
index 2055238d..92e64fa1 100644
--- a/.github/workflows/pythoncoverage.yml
+++ b/.github/workflows/pythoncoverage.yml
@@ -15,11 +15,11 @@ jobs:
# os: [ubuntu-latest, macos-latest, windows-latest]
os: [ubuntu-latest, ]
# python-version: [3.7, 3.8]
- python-version: [3.7, ]
+ python-version: [3.9, ]
steps:
- uses: actions/checkout@v2
- - name: Set up Python 3.8
+ - name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
diff --git a/.github/workflows/release_linux.yml b/.github/workflows/release_linux.yml
index 132f759b..b3665edb 100644
--- a/.github/workflows/release_linux.yml
+++ b/.github/workflows/release_linux.yml
@@ -4,6 +4,7 @@ on:
push:
tags:
- v*
+ workflow_dispatch:
jobs:
release:
@@ -14,12 +15,12 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v2
with:
- python-version: 3.8
+ python-version: 3.9
- name: Install dependencies and pyinstall
run: |
python -m pip install --upgrade pip setuptools
pip install -r requirements.txt
- pip install PyInstaller==4.0
+ pip install PyInstaller==4.5.1
- name: Build binary
run: |
pyinstaller --onefile -n nanovna-saver nanovna-saver.py
diff --git a/.github/workflows/release_macos.yml b/.github/workflows/release_macos.yml
index 6e8f6c68..83a59765 100644
--- a/.github/workflows/release_macos.yml
+++ b/.github/workflows/release_macos.yml
@@ -4,6 +4,7 @@ on:
push:
tags:
- v*
+ workflow_dispatch:
jobs:
release:
@@ -14,12 +15,12 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v2
with:
- python-version: 3.8
+ python-version: 3.9
- name: Install dependencies and pyinstall
run: |
python -m pip install --upgrade pip setuptools
pip install -r requirements.txt
- pip install PyInstaller==4.0
+ pip install PyInstaller==4.5.1
- name: Build binary
run: |
pyinstaller --onefile -n nanovna-saver nanovna-saver.py
diff --git a/.github/workflows/release_win.yml b/.github/workflows/release_win.yml
index a9e22d22..7dd7daaf 100644
--- a/.github/workflows/release_win.yml
+++ b/.github/workflows/release_win.yml
@@ -4,6 +4,7 @@ on:
push:
tags:
- v*
+ workflow_dispatch:
jobs:
release:
@@ -17,13 +18,13 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v2
with:
- python-version: 3.8
+ python-version: 3.9
architecture: ${{ matrix.arch }}
- name: Install dependencies and pyinstall
run: |
python -m pip install --upgrade pip setuptools
pip install -r requirements.txt
- pip install PyInstaller==4.3
+ pip install PyInstaller==4.7
- name: Build binary
run: |
pyinstaller --onefile -n nanovna-saver.exe nanovna-saver.py
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1ccc1761..8a974da2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,18 @@
Changelog
=========
+v0.3.10
+------
+
+- Default Band ranges for 5 and 9cm
+- Layout should fit on smaller screens
+- Fixed fixed axis settings
+- Show VNA type in port selector
+- Recognise tinySA (screenshot only)
+- Some more cables in TDR
+- Reference plane applied after calibration
+- Calibration fixes by DiSlord
+
v0.3.9
------
diff --git a/NanoVNASaver/About.py b/NanoVNASaver/About.py
index ec594301..39130f15 100644
--- a/NanoVNASaver/About.py
+++ b/NanoVNASaver/About.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-VERSION = "0.3.9"
+VERSION = "0.3.10"
VERSION_URL = (
"https://raw.githubusercontent.com/"
"NanoVNA-Saver/nanovna-saver/master/NanoVNASaver/About.py")
@@ -26,7 +26,7 @@
INFO = f"""NanoVNASaver {VERSION}
Copyright (C) 2019, 2020 Rune B. Broberg
-Copyright (C) 2020 NanoVNA-Saver Authors
+Copyright (C) 2020, 2021 NanoVNA-Saver Authors
This program comes with ABSOLUTELY NO WARRANTY
This program is licensed under the GNU General Public License version 3
diff --git a/NanoVNASaver/Analysis/Analysis.py b/NanoVNASaver/Analysis/Analysis.py
index 241f280f..30601562 100644
--- a/NanoVNASaver/Analysis/Analysis.py
+++ b/NanoVNASaver/Analysis/Analysis.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,7 +19,6 @@
import logging
import math
import numpy as np
-from scipy.signal import argrelextrema
from PyQt5 import QtWidgets
from scipy import signal
@@ -30,7 +29,7 @@ class Analysis:
_widget = None
@classmethod
- def find_crossing_zero(cls, data, threshold=0):
+ def find_crossing_zero(cls, data):
'''
Find values crossing zero
@@ -45,7 +44,6 @@ def find_crossing_zero(cls, data, threshold=0):
:param cls:
:param data: list of values
- :param threshold: unused, for future manage flipping around 0
'''
my_data = np.array(data)
zeroes = np.where(my_data == 0)[0]
@@ -118,8 +116,7 @@ def find_maximums(cls, data, threshold=None):
# maximums = argrelextrema(my_data, np.greater)[0]
if threshold is None:
return peaks
- else:
- return [k for k in peaks if data[k] > threshold]
+ return [k for k in peaks if data[k] > threshold]
def __init__(self, app: QtWidgets.QWidget):
self.app = app
@@ -136,10 +133,10 @@ def reset(self):
def calculateRolloff(self, location1, location2):
if location1 == location2:
return 0, 0
- frequency1 = self.app.data21[location1].freq
- frequency2 = self.app.data21[location2].freq
- gain1 = self.app.data21[location1].gain
- gain2 = self.app.data21[location2].gain
+ frequency1 = self.app.data.s21[location1].freq
+ frequency2 = self.app.data.s21[location2].freq
+ gain1 = self.app.data.s21[location1].gain
+ gain2 = self.app.data.s21[location2].gain
frequency_factor = frequency2 / frequency1
if frequency_factor < 1:
frequency_factor = 1 / frequency_factor
diff --git a/NanoVNASaver/Analysis/AntennaAnalysis.py b/NanoVNASaver/Analysis/AntennaAnalysis.py
index 84168fc2..c69078d3 100644
--- a/NanoVNASaver/Analysis/AntennaAnalysis.py
+++ b/NanoVNASaver/Analysis/AntennaAnalysis.py
@@ -1,7 +1,7 @@
# NanoVNASaver
#
# A python program to view and export Touchstone data from a NanoVNA
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -15,6 +15,9 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+
+from PyQt5.Qt import QTimer
+
'''
Created on May 30th 2020
@@ -37,28 +40,88 @@ class MagLoopAnalysis(VSWRAnalysis):
'''
max_dips_shown = 1
- vswr_limit_value = 2.56
- bandwith = 250000
+
+ vswr_bandwith_value = 2.56 # -3 dB ?!?
+ bandwith = 25000 # 25 kHz
+
+ def __init__(self, app):
+ # app.sweep_control.get_start() return -1 ?!?
+ # will populate first runAnalysis()
+ self.min_freq = None # app.sweep_control.get_start()
+ self.max_freq = None # app.sweep_control.get_end()
+ self.vswr_limit_value = self.vswr_bandwith_value
+
+ super().__init__(app)
def runAnalysis(self):
super().runAnalysis()
+ new_start = self.app.sweep_control.get_start()
+ new_end = self.app.sweep_control.get_end()
+ if self.min_freq is None:
+ self.min_freq = new_start
+ self.max_freq = new_end
+ logger.debug("setting hard limits to %s - %s",
+ self.min_freq, self.max_freq)
if len(self.minimums) > 1:
self.layout.addRow("", QtWidgets.QLabel(
- "Not magloop or try to lower VSWR limit"))
-
- for m in self.minimums[:1]:
- # only one time
+ "Multiple minimums, not magloop or try to lower VSWR limit"))
+ return
+ if len(self.minimums) == 1:
+ m = self.minimums[0]
start, lowest, end = m
if start != end:
- Q = self.app.data11[lowest].freq / \
- (self.app.data11[end].freq - self.app.data11[start].freq)
- self.layout.addRow(
- "Q", QtWidgets.QLabel("{}".format(int(Q))))
- self.app.sweep_control.set_start(self.app.data11[start].freq)
- self.app.sweep_control.set_end(self.app.data11[end].freq)
+ if self.vswr_limit_value == self.vswr_bandwith_value:
+ Q = self.app.data.s11[lowest].freq / \
+ (self.app.data.s11[end].freq -
+ self.app.data.s11[start].freq)
+ self.layout.addRow(
+ "Q", QtWidgets.QLabel("{}".format(int(Q))))
+ new_start = self.app.data.s11[start].freq - self.bandwith
+ new_end = self.app.data.s11[end].freq + self.bandwith
+ logger.debug("Single Spot, new scan on %s-%s",
+ new_start, new_end)
+
else:
- self.app.sweep_control.set_start(
- self.app.data11[start].freq - self.bandwith)
- self.app.sweep_control.set_end(
- self.app.data11[end].freq + self.bandwith)
+ new_start = self.app.data.s11[start].freq - 2 * self.bandwith
+ new_end = self.app.data.s11[end].freq + 2 * self.bandwith
+ logger.debug(" Zoom to %s-%s", new_start, new_end)
+
+ if self.vswr_limit_value > self.vswr_bandwith_value:
+ self.vswr_limit_value = max(
+ self.vswr_bandwith_value, self.vswr_limit_value - 1)
+ self.input_vswr_limit.setValue(self.vswr_limit_value)
+ logger.debug(
+ "found higher minimum, lowering vswr search to %s", self.vswr_limit_value)
+ else:
+ new_start = new_start - 5 * self.bandwith
+ new_end = new_end + 5 * self.bandwith
+ if all((new_start <= self.min_freq,
+ new_end >= self.max_freq)):
+ if self.vswr_limit_value < 10:
+ self.vswr_limit_value += 2
+ self.input_vswr_limit.setValue(self.vswr_limit_value)
+ logger.debug(
+ "no minimum found, looking for higher value %s", self.vswr_limit_value)
+ new_start = max(self.min_freq, new_start)
+ new_end = min(self.max_freq, new_end)
+ logger.debug("next search will be %s - %s for vswr %s",
+ new_start,
+ new_end,
+ self.vswr_limit_value)
+
+ self.app.sweep_control.set_start(new_start)
+ self.app.sweep_control.set_end(new_end)
+ # set timer to let finish all stuff before new sweep
+ QTimer.singleShot(2000, self._safe_sweep)
+
+ def _safe_sweep(self):
+ '''
+ sweep only if button enabled
+ to prevent multiple/concurrent sweep
+ '''
+
+ if self.app.sweep_control.btn_start.isEnabled():
+ self.app.sweep_start()
+ else:
+ logger.error("sweep alredy running")
diff --git a/NanoVNASaver/Analysis/BandPassAnalysis.py b/NanoVNASaver/Analysis/BandPassAnalysis.py
index d8254b25..159a559f 100644
--- a/NanoVNASaver/Analysis/BandPassAnalysis.py
+++ b/NanoVNASaver/Analysis/BandPassAnalysis.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -108,7 +108,7 @@ def runAnalysis(self):
pass_band_location = self.app.markers[0].location
logger.debug("Pass band location: %d", pass_band_location)
- if len(self.app.data21) == 0:
+ if len(self.app.data.s21) == 0:
logger.debug("No data to analyse")
self.result_label.setText("No data to analyse.")
return
@@ -119,13 +119,13 @@ def runAnalysis(self):
f"Please place {self.app.markers[0].name} in the passband.")
return
- pass_band_db = self.app.data21[pass_band_location].gain
+ pass_band_db = self.app.data.s21[pass_band_location].gain
logger.debug("Initial passband gain: %d", pass_band_db)
initial_lower_cutoff_location = -1
for i in range(pass_band_location, -1, -1):
- if (pass_band_db - self.app.data21[i].gain) > 3:
+ if (pass_band_db - self.app.data.s21[i].gain) > 3:
# We found a cutoff location
initial_lower_cutoff_location = i
break
@@ -134,13 +134,13 @@ def runAnalysis(self):
self.result_label.setText("Lower cutoff location not found.")
return
- initial_lower_cutoff_frequency = self.app.data21[initial_lower_cutoff_location].freq
+ initial_lower_cutoff_frequency = self.app.data.s21[initial_lower_cutoff_location].freq
logger.debug("Found initial lower cutoff frequency at %d", initial_lower_cutoff_frequency)
initial_upper_cutoff_location = -1
- for i in range(pass_band_location, len(self.app.data21), 1):
- if (pass_band_db - self.app.data21[i].gain) > 3:
+ for i in range(pass_band_location, len(self.app.data.s21), 1):
+ if (pass_band_db - self.app.data.s21[i].gain) > 3:
# We found a cutoff location
initial_upper_cutoff_location = i
break
@@ -149,30 +149,30 @@ def runAnalysis(self):
self.result_label.setText("Upper cutoff location not found.")
return
- initial_upper_cutoff_frequency = self.app.data21[initial_upper_cutoff_location].freq
+ initial_upper_cutoff_frequency = self.app.data.s21[initial_upper_cutoff_location].freq
logger.debug("Found initial upper cutoff frequency at %d", initial_upper_cutoff_frequency)
peak_location = -1
- peak_db = self.app.data21[initial_lower_cutoff_location].gain
+ peak_db = self.app.data.s21[initial_lower_cutoff_location].gain
for i in range(initial_lower_cutoff_location, initial_upper_cutoff_location, 1):
- db = self.app.data21[i].gain
+ db = self.app.data.s21[i].gain
if db > peak_db:
peak_db = db
peak_location = i
- logger.debug("Found peak of %f at %d", peak_db, self.app.data11[peak_location].freq)
+ logger.debug("Found peak of %f at %d", peak_db, self.app.data.s11[peak_location].freq)
lower_cutoff_location = -1
pass_band_db = peak_db
for i in range(peak_location, -1, -1):
- if (pass_band_db - self.app.data21[i].gain) > 3:
+ if (pass_band_db - self.app.data.s21[i].gain) > 3:
# We found the cutoff location
lower_cutoff_location = i
break
- lower_cutoff_frequency = self.app.data21[lower_cutoff_location].freq
- lower_cutoff_gain = self.app.data21[lower_cutoff_location].gain - pass_band_db
+ lower_cutoff_frequency = self.app.data.s21[lower_cutoff_location].freq
+ lower_cutoff_gain = self.app.data.s21[lower_cutoff_location].gain - pass_band_db
if lower_cutoff_gain < -4:
logger.debug("Lower cutoff frequency found at %f dB"
@@ -189,14 +189,14 @@ def runAnalysis(self):
upper_cutoff_location = -1
pass_band_db = peak_db
- for i in range(peak_location, len(self.app.data21), 1):
- if (pass_band_db - self.app.data21[i].gain) > 3:
+ for i in range(peak_location, len(self.app.data.s21), 1):
+ if (pass_band_db - self.app.data.s21[i].gain) > 3:
# We found the cutoff location
upper_cutoff_location = i
break
- upper_cutoff_frequency = self.app.data21[upper_cutoff_location].freq
- upper_cutoff_gain = self.app.data21[upper_cutoff_location].gain - pass_band_db
+ upper_cutoff_frequency = self.app.data.s21[upper_cutoff_location].freq
+ upper_cutoff_gain = self.app.data.s21[upper_cutoff_location].gain - pass_band_db
if upper_cutoff_gain < -4:
logger.debug("Upper cutoff frequency found at %f dB"
" - insufficient data points for true -3 dB point.",
@@ -227,7 +227,7 @@ def runAnalysis(self):
lower_six_db_location = -1
for i in range(lower_cutoff_location, -1, -1):
- if (pass_band_db - self.app.data21[i].gain) > 6:
+ if (pass_band_db - self.app.data.s21[i].gain) > 6:
# We found 6dB location
lower_six_db_location = i
break
@@ -235,39 +235,39 @@ def runAnalysis(self):
if lower_six_db_location < 0:
self.result_label.setText("Lower 6 dB location not found.")
return
- lower_six_db_cutoff_frequency = self.app.data21[lower_six_db_location].freq
+ lower_six_db_cutoff_frequency = self.app.data.s21[lower_six_db_location].freq
self.lower_six_db_label.setText(
format_frequency(lower_six_db_cutoff_frequency))
ten_db_location = -1
for i in range(lower_cutoff_location, -1, -1):
- if (pass_band_db - self.app.data21[i].gain) > 10:
+ if (pass_band_db - self.app.data.s21[i].gain) > 10:
# We found 6dB location
ten_db_location = i
break
twenty_db_location = -1
for i in range(lower_cutoff_location, -1, -1):
- if (pass_band_db - self.app.data21[i].gain) > 20:
+ if (pass_band_db - self.app.data.s21[i].gain) > 20:
# We found 6dB location
twenty_db_location = i
break
sixty_db_location = -1
for i in range(lower_six_db_location, -1, -1):
- if (pass_band_db - self.app.data21[i].gain) > 60:
+ if (pass_band_db - self.app.data.s21[i].gain) > 60:
# We found 60dB location! Wow.
sixty_db_location = i
break
if sixty_db_location > 0:
if sixty_db_location > 0:
- sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq
+ sixty_db_cutoff_frequency = self.app.data.s21[sixty_db_location].freq
self.lower_sixty_db_label.setText(
format_frequency(sixty_db_cutoff_frequency))
elif ten_db_location != -1 and twenty_db_location != -1:
- ten = self.app.data21[ten_db_location].freq
- twenty = self.app.data21[twenty_db_location].freq
+ ten = self.app.data.s21[ten_db_location].freq
+ twenty = self.app.data.s21[twenty_db_location].freq
sixty_db_frequency = ten * \
10 ** (5 * (math.log10(twenty) - math.log10(ten)))
self.lower_sixty_db_label.setText(
@@ -289,8 +289,8 @@ def runAnalysis(self):
# Upper roll-off
upper_six_db_location = -1
- for i in range(upper_cutoff_location, len(self.app.data21), 1):
- if (pass_band_db - self.app.data21[i].gain) > 6:
+ for i in range(upper_cutoff_location, len(self.app.data.s21), 1):
+ if (pass_band_db - self.app.data.s21[i].gain) > 6:
# We found 6dB location
upper_six_db_location = i
break
@@ -298,7 +298,7 @@ def runAnalysis(self):
if upper_six_db_location < 0:
self.result_label.setText("Upper 6 dB location not found.")
return
- upper_six_db_cutoff_frequency = self.app.data21[upper_six_db_location].freq
+ upper_six_db_cutoff_frequency = self.app.data.s21[upper_six_db_location].freq
self.upper_six_db_label.setText(
format_frequency(upper_six_db_cutoff_frequency))
@@ -308,33 +308,33 @@ def runAnalysis(self):
format_frequency(six_db_span))
ten_db_location = -1
- for i in range(upper_cutoff_location, len(self.app.data21), 1):
- if (pass_band_db - self.app.data21[i].gain) > 10:
+ for i in range(upper_cutoff_location, len(self.app.data.s21), 1):
+ if (pass_band_db - self.app.data.s21[i].gain) > 10:
# We found 6dB location
ten_db_location = i
break
twenty_db_location = -1
- for i in range(upper_cutoff_location, len(self.app.data21), 1):
- if (pass_band_db - self.app.data21[i].gain) > 20:
+ for i in range(upper_cutoff_location, len(self.app.data.s21), 1):
+ if (pass_band_db - self.app.data.s21[i].gain) > 20:
# We found 6dB location
twenty_db_location = i
break
sixty_db_location = -1
- for i in range(upper_six_db_location, len(self.app.data21), 1):
- if (pass_band_db - self.app.data21[i].gain) > 60:
+ for i in range(upper_six_db_location, len(self.app.data.s21), 1):
+ if (pass_band_db - self.app.data.s21[i].gain) > 60:
# We found 60dB location! Wow.
sixty_db_location = i
break
if sixty_db_location > 0:
- sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq
+ sixty_db_cutoff_frequency = self.app.data.s21[sixty_db_location].freq
self.upper_sixty_db_label.setText(
format_frequency(sixty_db_cutoff_frequency))
elif ten_db_location != -1 and twenty_db_location != -1:
- ten = self.app.data21[ten_db_location].freq
- twenty = self.app.data21[twenty_db_location].freq
+ ten = self.app.data.s21[ten_db_location].freq
+ twenty = self.app.data.s21[twenty_db_location].freq
sixty_db_frequency = ten * \
10 ** (5 * (math.log10(twenty) - math.log10(ten)))
self.upper_sixty_db_label.setText(
@@ -355,8 +355,8 @@ def runAnalysis(self):
if upper_cutoff_gain < -4 or lower_cutoff_gain < -4:
self.result_label.setText(
- f"Analysis complete ({len(self.app.data11)} points)\n"
+ f"Analysis complete ({len(self.app.data.s11)} points)\n"
f"Insufficient data for analysis. Increase segment count.")
else:
self.result_label.setText(
- f"Analysis complete ({len(self.app.data11)} points)")
+ f"Analysis complete ({len(self.app.data.s11)} points)")
diff --git a/NanoVNASaver/Analysis/BandStopAnalysis.py b/NanoVNASaver/Analysis/BandStopAnalysis.py
index 7a656097..8c6af782 100644
--- a/NanoVNASaver/Analysis/BandStopAnalysis.py
+++ b/NanoVNASaver/Analysis/BandStopAnalysis.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -101,31 +101,31 @@ def reset(self):
def runAnalysis(self):
self.reset()
- if len(self.app.data21) == 0:
+ if len(self.app.data.s21) == 0:
logger.debug("No data to analyse")
self.result_label.setText("No data to analyse.")
return
peak_location = -1
- peak_db = self.app.data21[0].gain
- for i in range(len(self.app.data21)):
- db = self.app.data21[i].gain
+ peak_db = self.app.data.s21[0].gain
+ for i in range(len(self.app.data.s21)):
+ db = self.app.data.s21[i].gain
if db > peak_db:
peak_db = db
peak_location = i
- logger.debug("Found peak of %f at %d", peak_db, self.app.data11[peak_location].freq)
+ logger.debug("Found peak of %f at %d", peak_db, self.app.data.s11[peak_location].freq)
lower_cutoff_location = -1
pass_band_db = peak_db
- for i in range(len(self.app.data21)):
- if (pass_band_db - self.app.data21[i].gain) > 3:
+ for i in range(len(self.app.data.s21)):
+ if (pass_band_db - self.app.data.s21[i].gain) > 3:
# We found the cutoff location
lower_cutoff_location = i
break
- lower_cutoff_frequency = self.app.data21[lower_cutoff_location].freq
- lower_cutoff_gain = self.app.data21[lower_cutoff_location].gain - pass_band_db
+ lower_cutoff_frequency = self.app.data.s21[lower_cutoff_location].freq
+ lower_cutoff_gain = self.app.data.s21[lower_cutoff_location].gain - pass_band_db
if lower_cutoff_gain < -4:
logger.debug("Lower cutoff frequency found at %f dB"
@@ -142,14 +142,14 @@ def runAnalysis(self):
self.app.markers[1].frequencyInput.setText(str(lower_cutoff_frequency))
upper_cutoff_location = -1
- for i in range(len(self.app.data21)-1, -1, -1):
- if (pass_band_db - self.app.data21[i].gain) > 3:
+ for i in range(len(self.app.data.s21)-1, -1, -1):
+ if (pass_band_db - self.app.data.s21[i].gain) > 3:
# We found the cutoff location
upper_cutoff_location = i
break
- upper_cutoff_frequency = self.app.data21[upper_cutoff_location].freq
- upper_cutoff_gain = self.app.data21[upper_cutoff_location].gain - pass_band_db
+ upper_cutoff_frequency = self.app.data.s21[upper_cutoff_location].freq
+ upper_cutoff_gain = self.app.data.s21[upper_cutoff_location].gain - pass_band_db
if upper_cutoff_gain < -4:
logger.debug("Upper cutoff frequency found at %f dB"
" - insufficient data points for true -3 dB point.",
@@ -178,8 +178,8 @@ def runAnalysis(self):
# Lower roll-off
lower_six_db_location = -1
- for i in range(lower_cutoff_location, len(self.app.data21)):
- if (pass_band_db - self.app.data21[i].gain) > 6:
+ for i in range(lower_cutoff_location, len(self.app.data.s21)):
+ if (pass_band_db - self.app.data.s21[i].gain) > 6:
# We found 6dB location
lower_six_db_location = i
break
@@ -187,38 +187,38 @@ def runAnalysis(self):
if lower_six_db_location < 0:
self.result_label.setText("Lower 6 dB location not found.")
return
- lower_six_db_cutoff_frequency = self.app.data21[lower_six_db_location].freq
+ lower_six_db_cutoff_frequency = self.app.data.s21[lower_six_db_location].freq
self.lower_six_db_label.setText(
format_frequency(lower_six_db_cutoff_frequency))
ten_db_location = -1
- for i in range(lower_cutoff_location, len(self.app.data21)):
- if (pass_band_db - self.app.data21[i].gain) > 10:
+ for i in range(lower_cutoff_location, len(self.app.data.s21)):
+ if (pass_band_db - self.app.data.s21[i].gain) > 10:
# We found 6dB location
ten_db_location = i
break
twenty_db_location = -1
- for i in range(lower_cutoff_location, len(self.app.data21)):
- if (pass_band_db - self.app.data21[i].gain) > 20:
+ for i in range(lower_cutoff_location, len(self.app.data.s21)):
+ if (pass_band_db - self.app.data.s21[i].gain) > 20:
# We found 6dB location
twenty_db_location = i
break
sixty_db_location = -1
- for i in range(lower_six_db_location, len(self.app.data21)):
- if (pass_band_db - self.app.data21[i].gain) > 60:
+ for i in range(lower_six_db_location, len(self.app.data.s21)):
+ if (pass_band_db - self.app.data.s21[i].gain) > 60:
# We found 60dB location! Wow.
sixty_db_location = i
break
if sixty_db_location > 0:
- sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq
+ sixty_db_cutoff_frequency = self.app.data.s21[sixty_db_location].freq
self.lower_sixty_db_label.setText(
format_frequency(sixty_db_cutoff_frequency))
elif ten_db_location != -1 and twenty_db_location != -1:
- ten = self.app.data21[ten_db_location].freq
- twenty = self.app.data21[twenty_db_location].freq
+ ten = self.app.data.s21[ten_db_location].freq
+ twenty = self.app.data.s21[twenty_db_location].freq
sixty_db_frequency = ten * 10 ** (5 * (math.log10(twenty) - math.log10(ten)))
self.lower_sixty_db_label.setText(
f"{format_frequency(sixty_db_frequency)} (derived)")
@@ -242,7 +242,7 @@ def runAnalysis(self):
upper_six_db_location = -1
for i in range(upper_cutoff_location, -1, -1):
- if (pass_band_db - self.app.data21[i].gain) > 6:
+ if (pass_band_db - self.app.data.s21[i].gain) > 6:
# We found 6dB location
upper_six_db_location = i
break
@@ -250,7 +250,7 @@ def runAnalysis(self):
if upper_six_db_location < 0:
self.result_label.setText("Upper 6 dB location not found.")
return
- upper_six_db_cutoff_frequency = self.app.data21[upper_six_db_location].freq
+ upper_six_db_cutoff_frequency = self.app.data.s21[upper_six_db_location].freq
self.upper_six_db_label.setText(
format_frequency(upper_six_db_cutoff_frequency))
@@ -261,32 +261,32 @@ def runAnalysis(self):
ten_db_location = -1
for i in range(upper_cutoff_location, -1, -1):
- if (pass_band_db - self.app.data21[i].gain) > 10:
+ if (pass_band_db - self.app.data.s21[i].gain) > 10:
# We found 6dB location
ten_db_location = i
break
twenty_db_location = -1
for i in range(upper_cutoff_location, -1, -1):
- if (pass_band_db - self.app.data21[i].gain) > 20:
+ if (pass_band_db - self.app.data.s21[i].gain) > 20:
# We found 6dB location
twenty_db_location = i
break
sixty_db_location = -1
for i in range(upper_six_db_location, -1, -1):
- if (pass_band_db - self.app.data21[i].gain) > 60:
+ if (pass_band_db - self.app.data.s21[i].gain) > 60:
# We found 60dB location! Wow.
sixty_db_location = i
break
if sixty_db_location > 0:
- sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq
+ sixty_db_cutoff_frequency = self.app.data.s21[sixty_db_location].freq
self.upper_sixty_db_label.setText(
format_frequency(sixty_db_cutoff_frequency))
elif ten_db_location != -1 and twenty_db_location != -1:
- ten = self.app.data21[ten_db_location].freq
- twenty = self.app.data21[twenty_db_location].freq
+ ten = self.app.data.s21[ten_db_location].freq
+ twenty = self.app.data.s21[twenty_db_location].freq
sixty_db_frequency = ten * 10 ** (
5 * (math.log10(twenty) - math.log10(ten)))
self.upper_sixty_db_label.setText(
@@ -309,8 +309,8 @@ def runAnalysis(self):
if upper_cutoff_gain < -4 or lower_cutoff_gain < -4:
self.result_label.setText(
- f"Analysis complete ({len(self.app.data11)} points)\n"
+ f"Analysis complete ({len(self.app.data.s11)} points)\n"
f"Insufficient data for analysis. Increase segment count.")
else:
self.result_label.setText(
- f"Analysis complete ({len(self.app.data11)} points)")
+ f"Analysis complete ({len(self.app.data.s11)} points)")
diff --git a/NanoVNASaver/Analysis/HighPassAnalysis.py b/NanoVNASaver/Analysis/HighPassAnalysis.py
index 26f7c5c0..ab585a1c 100644
--- a/NanoVNASaver/Analysis/HighPassAnalysis.py
+++ b/NanoVNASaver/Analysis/HighPassAnalysis.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -64,7 +64,7 @@ def runAnalysis(self):
pass_band_location = self.app.markers[0].location
logger.debug("Pass band location: %d", pass_band_location)
- if len(self.app.data21) == 0:
+ if len(self.app.data.s21) == 0:
logger.debug("No data to analyse")
self.result_label.setText("No data to analyse.")
return
@@ -75,13 +75,13 @@ def runAnalysis(self):
f"Please place {self.app.markers[0].name } in the passband.")
return
- pass_band_db = self.app.data21[pass_band_location].gain
+ pass_band_db = self.app.data.s21[pass_band_location].gain
logger.debug("Initial passband gain: %d", pass_band_db)
initial_cutoff_location = -1
for i in range(pass_band_location, -1, -1):
- db = self.app.data21[i].gain
+ db = self.app.data.s21[i].gain
if (pass_band_db - db) > 3:
# We found a cutoff location
initial_cutoff_location = i
@@ -91,32 +91,32 @@ def runAnalysis(self):
self.result_label.setText("Cutoff location not found.")
return
- initial_cutoff_frequency = self.app.data21[initial_cutoff_location].freq
+ initial_cutoff_frequency = self.app.data.s21[initial_cutoff_location].freq
logger.debug("Found initial cutoff frequency at %d", initial_cutoff_frequency)
peak_location = -1
- peak_db = self.app.data21[initial_cutoff_location].gain
- for i in range(len(self.app.data21) - 1, initial_cutoff_location - 1, -1):
- if self.app.data21[i].gain > peak_db:
+ peak_db = self.app.data.s21[initial_cutoff_location].gain
+ for i in range(len(self.app.data.s21) - 1, initial_cutoff_location - 1, -1):
+ if self.app.data.s21[i].gain > peak_db:
peak_db = db
peak_location = i
- logger.debug("Found peak of %f at %d", peak_db, self.app.data11[peak_location].freq)
+ logger.debug("Found peak of %f at %d", peak_db, self.app.data.s11[peak_location].freq)
- self.app.markers[0].setFrequency(str(self.app.data21[peak_location].freq))
- self.app.markers[0].frequencyInput.setText(str(self.app.data21[peak_location].freq))
+ self.app.markers[0].setFrequency(str(self.app.data.s21[peak_location].freq))
+ self.app.markers[0].frequencyInput.setText(str(self.app.data.s21[peak_location].freq))
cutoff_location = -1
pass_band_db = peak_db
for i in range(peak_location, -1, -1):
- if (pass_band_db - self.app.data21[i].gain) > 3:
+ if (pass_band_db - self.app.data.s21[i].gain) > 3:
# We found the cutoff location
cutoff_location = i
break
- cutoff_frequency = self.app.data21[cutoff_location].freq
- cutoff_gain = self.app.data21[cutoff_location].gain - pass_band_db
+ cutoff_frequency = self.app.data.s21[cutoff_location].freq
+ cutoff_gain = self.app.data.s21[cutoff_location].gain - pass_band_db
if cutoff_gain < -4:
logger.debug("Cutoff frequency found at %f dB"
" - insufficient data points for true -3 dB point.",
@@ -131,7 +131,7 @@ def runAnalysis(self):
six_db_location = -1
for i in range(cutoff_location, -1, -1):
- if (pass_band_db - self.app.data21[i].gain) > 6:
+ if (pass_band_db - self.app.data.s21[i].gain) > 6:
# We found 6dB location
six_db_location = i
break
@@ -139,39 +139,39 @@ def runAnalysis(self):
if six_db_location < 0:
self.result_label.setText("6 dB location not found.")
return
- six_db_cutoff_frequency = self.app.data21[six_db_location].freq
+ six_db_cutoff_frequency = self.app.data.s21[six_db_location].freq
self.six_db_label.setText(
format_frequency(six_db_cutoff_frequency))
ten_db_location = -1
for i in range(cutoff_location, -1, -1):
- if (pass_band_db - self.app.data21[i].gain) > 10:
+ if (pass_band_db - self.app.data.s21[i].gain) > 10:
# We found 6dB location
ten_db_location = i
break
twenty_db_location = -1
for i in range(cutoff_location, -1, -1):
- if (pass_band_db - self.app.data21[i].gain) > 20:
+ if (pass_band_db - self.app.data.s21[i].gain) > 20:
# We found 6dB location
twenty_db_location = i
break
sixty_db_location = -1
for i in range(six_db_location, -1, -1):
- if (pass_band_db - self.app.data21[i].gain) > 60:
+ if (pass_band_db - self.app.data.s21[i].gain) > 60:
# We found 60dB location! Wow.
sixty_db_location = i
break
if sixty_db_location > 0:
if sixty_db_location > 0:
- sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq
+ sixty_db_cutoff_frequency = self.app.data.s21[sixty_db_location].freq
self.sixty_db_label.setText(
format_frequency(sixty_db_cutoff_frequency))
elif ten_db_location != -1 and twenty_db_location != -1:
- ten = self.app.data21[ten_db_location].freq
- twenty = self.app.data21[twenty_db_location].freq
+ ten = self.app.data.s21[ten_db_location].freq
+ twenty = self.app.data.s21[twenty_db_location].freq
sixty_db_frequency = ten * 10 ** (5 * (math.log10(twenty) - math.log10(ten)))
self.sixty_db_label.setText(
f"{format_frequency(sixty_db_frequency)} (derived)")
@@ -187,4 +187,4 @@ def runAnalysis(self):
self.db_per_octave_label.setText("Not calculated")
self.db_per_decade_label.setText("Not calculated")
- self.result_label.setText(f"Analysis complete ({len(self.app.data11)}) points)")
+ self.result_label.setText(f"Analysis complete ({len(self.app.data.s11)}) points)")
diff --git a/NanoVNASaver/Analysis/LowPassAnalysis.py b/NanoVNASaver/Analysis/LowPassAnalysis.py
index 7d523927..78f3d3e9 100644
--- a/NanoVNASaver/Analysis/LowPassAnalysis.py
+++ b/NanoVNASaver/Analysis/LowPassAnalysis.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -66,7 +66,7 @@ def runAnalysis(self):
pass_band_location = self.app.markers[0].location
logger.debug("Pass band location: %d", pass_band_location)
- if len(self.app.data21) == 0:
+ if len(self.app.data.s21) == 0:
logger.debug("No data to analyse")
self.result_label.setText("No data to analyse.")
return
@@ -78,13 +78,13 @@ def runAnalysis(self):
f"Please place {self.app.markers[0].name} in the passband.")
return
- pass_band_db = self.app.data21[pass_band_location].gain
+ pass_band_db = self.app.data.s21[pass_band_location].gain
logger.debug("Initial passband gain: %d", pass_band_db)
initial_cutoff_location = -1
- for i in range(pass_band_location, len(self.app.data21)):
- db = self.app.data21[i].gain
+ for i in range(pass_band_location, len(self.app.data.s21)):
+ db = self.app.data.s21[i].gain
if (pass_band_db - db) > 3:
# We found a cutoff location
initial_cutoff_location = i
@@ -94,34 +94,34 @@ def runAnalysis(self):
self.result_label.setText("Cutoff location not found.")
return
- initial_cutoff_frequency = self.app.data21[initial_cutoff_location].freq
+ initial_cutoff_frequency = self.app.data.s21[initial_cutoff_location].freq
logger.debug("Found initial cutoff frequency at %d", initial_cutoff_frequency)
peak_location = -1
- peak_db = self.app.data21[initial_cutoff_location].gain
+ peak_db = self.app.data.s21[initial_cutoff_location].gain
for i in range(0, initial_cutoff_location):
- db = self.app.data21[i].gain
+ db = self.app.data.s21[i].gain
if db > peak_db:
peak_db = db
peak_location = i
- logger.debug("Found peak of %f at %d", peak_db, self.app.data11[peak_location].freq)
+ logger.debug("Found peak of %f at %d", peak_db, self.app.data.s11[peak_location].freq)
- self.app.markers[0].setFrequency(str(self.app.data21[peak_location].freq))
- self.app.markers[0].frequencyInput.setText(str(self.app.data21[peak_location].freq))
+ self.app.markers[0].setFrequency(str(self.app.data.s21[peak_location].freq))
+ self.app.markers[0].frequencyInput.setText(str(self.app.data.s21[peak_location].freq))
cutoff_location = -1
pass_band_db = peak_db
- for i in range(peak_location, len(self.app.data21)):
- db = self.app.data21[i].gain
+ for i in range(peak_location, len(self.app.data.s21)):
+ db = self.app.data.s21[i].gain
if (pass_band_db - db) > 3:
# We found the cutoff location
cutoff_location = i
break
- cutoff_frequency = self.app.data21[cutoff_location].freq
- cutoff_gain = self.app.data21[cutoff_location].gain - pass_band_db
+ cutoff_frequency = self.app.data.s21[cutoff_location].freq
+ cutoff_gain = self.app.data.s21[cutoff_location].gain - pass_band_db
if cutoff_gain < -4:
logger.debug(
"Cutoff frequency found at %f dB"
@@ -136,8 +136,8 @@ def runAnalysis(self):
self.app.markers[1].frequencyInput.setText(str(cutoff_frequency))
six_db_location = -1
- for i in range(cutoff_location, len(self.app.data21)):
- db = self.app.data21[i].gain
+ for i in range(cutoff_location, len(self.app.data.s21)):
+ db = self.app.data.s21[i].gain
if (pass_band_db - db) > 6:
# We found 6dB location
six_db_location = i
@@ -146,41 +146,41 @@ def runAnalysis(self):
if six_db_location < 0:
self.result_label.setText("6 dB location not found.")
return
- six_db_cutoff_frequency = self.app.data21[six_db_location].freq
+ six_db_cutoff_frequency = self.app.data.s21[six_db_location].freq
self.six_db_label.setText(
format_frequency(six_db_cutoff_frequency))
ten_db_location = -1
- for i in range(cutoff_location, len(self.app.data21)):
- db = self.app.data21[i].gain
+ for i in range(cutoff_location, len(self.app.data.s21)):
+ db = self.app.data.s21[i].gain
if (pass_band_db - db) > 10:
# We found 6dB location
ten_db_location = i
break
twenty_db_location = -1
- for i in range(cutoff_location, len(self.app.data21)):
- db = self.app.data21[i].gain
+ for i in range(cutoff_location, len(self.app.data.s21)):
+ db = self.app.data.s21[i].gain
if (pass_band_db - db) > 20:
# We found 6dB location
twenty_db_location = i
break
sixty_db_location = -1
- for i in range(six_db_location, len(self.app.data21)):
- db = self.app.data21[i].gain
+ for i in range(six_db_location, len(self.app.data.s21)):
+ db = self.app.data.s21[i].gain
if (pass_band_db - db) > 60:
# We found 60dB location! Wow.
sixty_db_location = i
break
if sixty_db_location > 0:
- sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq
+ sixty_db_cutoff_frequency = self.app.data.s21[sixty_db_location].freq
self.sixty_db_label.setText(
format_frequency(sixty_db_cutoff_frequency))
elif ten_db_location != -1 and twenty_db_location != -1:
- ten = self.app.data21[ten_db_location].freq
- twenty = self.app.data21[twenty_db_location].freq
+ ten = self.app.data.s21[ten_db_location].freq
+ twenty = self.app.data.s21[twenty_db_location].freq
sixty_db_frequency = ten * \
10 ** (5 * (math.log10(twenty) - math.log10(ten)))
self.sixty_db_label.setText(
@@ -202,4 +202,4 @@ def runAnalysis(self):
self.db_per_decade_label.setText("Not calculated")
self.result_label.setText(
- "Analysis complete (" + str(len(self.app.data11)) + " points)")
+ "Analysis complete (" + str(len(self.app.data.s11)) + " points)")
diff --git a/NanoVNASaver/Analysis/PeakSearchAnalysis.py b/NanoVNASaver/Analysis/PeakSearchAnalysis.py
index 0611f962..0a522df0 100644
--- a/NanoVNASaver/Analysis/PeakSearchAnalysis.py
+++ b/NanoVNASaver/Analysis/PeakSearchAnalysis.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -97,19 +97,19 @@ def runAnalysis(self):
count = self.input_number_of_peaks.value()
if self.rbtn_data_vswr.isChecked():
fn = format_vswr
- for d in self.app.data11:
+ for d in self.app.data.s11:
data.append(d.vswr)
elif self.rbtn_data_s21_gain.isChecked():
fn = format_gain
- for d in self.app.data21:
+ for d in self.app.data.s21:
data.append(d.gain)
elif self.rbtn_data_resistance.isChecked():
fn = format_resistance
- for d in self.app.data11:
+ for d in self.app.data.s11:
data.append(d.impedance().real)
elif self.rbtn_data_reactance.isChecked():
fn = str
- for d in self.app.data11:
+ for d in self.app.data.s11:
data.append(d.impedance().imag)
else:
@@ -150,10 +150,10 @@ def runAnalysis(self):
logger.debug("Index %d", i)
logger.debug("Prominence %f", prominences[i])
logger.debug("Index in sweep %d", peaks[i])
- logger.debug("Frequency %d", self.app.data11[peaks[i]].freq)
+ logger.debug("Frequency %d", self.app.data.s11[peaks[i]].freq)
logger.debug("Value %f", sign * data[peaks[i]])
self.layout.addRow(
- f"Freq {format_frequency_short(self.app.data11[peaks[i]].freq)}",
+ f"Freq {format_frequency_short(self.app.data.s11[peaks[i]].freq)}",
QtWidgets.QLabel(f" value {fn(sign * data[peaks[i]])}"
))
@@ -162,9 +162,9 @@ def runAnalysis(self):
logger.warning("More peaks found than there are markers")
for i in range(min(count, len(self.app.markers))):
self.app.markers[i].setFrequency(
- str(self.app.data11[peaks[indices[i]]].freq))
+ str(self.app.data.s11[peaks[indices[i]]].freq))
self.app.markers[i].frequencyInput.setText(
- str(self.app.data11[peaks[indices[i]]].freq))
+ str(self.app.data.s11[peaks[indices[i]]].freq))
max_val = -10**10
max_idx = -1
@@ -180,6 +180,6 @@ def reset(self):
logger.debug("Results start at %d, out of %d",
self.results_header, self.layout.rowCount())
- for i in range(self.results_header, self.layout.rowCount()):
+ for _ in range(self.results_header, self.layout.rowCount()):
logger.debug("deleting %s", self.layout.rowCount())
self.layout.removeRow(self.layout.rowCount() - 1)
diff --git a/NanoVNASaver/Analysis/SimplePeakSearchAnalysis.py b/NanoVNASaver/Analysis/SimplePeakSearchAnalysis.py
index a6fb449d..0629885c 100644
--- a/NanoVNASaver/Analysis/SimplePeakSearchAnalysis.py
+++ b/NanoVNASaver/Analysis/SimplePeakSearchAnalysis.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -81,22 +81,22 @@ def runAnalysis(self):
if self.rbtn_data_vswr.isChecked():
suffix = ""
data = []
- for d in self.app.data11:
+ for d in self.app.data.s11:
data.append(d.vswr)
elif self.rbtn_data_resistance.isChecked():
suffix = " \N{OHM SIGN}"
data = []
- for d in self.app.data11:
+ for d in self.app.data.s11:
data.append(d.impedance().real)
elif self.rbtn_data_reactance.isChecked():
suffix = " \N{OHM SIGN}"
data = []
- for d in self.app.data11:
+ for d in self.app.data.s11:
data.append(d.impedance().imag)
elif self.rbtn_data_s21_gain.isChecked():
suffix = " dB"
data = []
- for d in self.app.data21:
+ for d in self.app.data.s21:
data.append(d.gain)
else:
logger.warning("Searching for peaks on unknown data")
@@ -117,10 +117,10 @@ def runAnalysis(self):
return
self.peak_frequency.setText(
- format_frequency(self.app.data11[idx_peak].freq))
+ format_frequency(self.app.data.s11[idx_peak].freq))
self.peak_value.setText(str(round(data[idx_peak], 3)) + suffix)
if self.checkbox_move_marker.isChecked() and len(self.app.markers) >= 1:
- self.app.markers[0].setFrequency(str(self.app.data11[idx_peak].freq))
+ self.app.markers[0].setFrequency(str(self.app.data.s11[idx_peak].freq))
self.app.markers[0].frequencyInput.setText(
- format_frequency(self.app.data11[idx_peak].freq))
+ format_frequency(self.app.data.s11[idx_peak].freq))
diff --git a/NanoVNASaver/Analysis/VSWRAnalysis.py b/NanoVNASaver/Analysis/VSWRAnalysis.py
index 200dda46..24d2f1ea 100644
--- a/NanoVNASaver/Analysis/VSWRAnalysis.py
+++ b/NanoVNASaver/Analysis/VSWRAnalysis.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,23 +16,19 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+import os
+import csv
import logging
+from collections import OrderedDict
-from PyQt5 import QtWidgets
import numpy as np
+from PyQt5 import QtWidgets
from NanoVNASaver.Analysis import Analysis, PeakSearchAnalysis
-from NanoVNASaver.Formatting import format_frequency
-from NanoVNASaver.Formatting import format_complex_imp
+from NanoVNASaver.Formatting import (
+ format_frequency, format_complex_imp,
+ format_frequency_short, format_resistance)
from NanoVNASaver.RFTools import reflection_coefficient
-import os
-import csv
-from NanoVNASaver.Marker.Values import Label
-from NanoVNASaver.Marker.Widget import MarkerLabel
-from NanoVNASaver.Marker.Widget import Marker
-from collections import OrderedDict
-from NanoVNASaver.Formatting import format_frequency_short
-from NanoVNASaver.Formatting import format_resistance
logger = logging.getLogger(__name__)
@@ -78,19 +74,18 @@ def __init__(self, app):
def runAnalysis(self):
max_dips_shown = self.max_dips_shown
- data = []
- for d in self.app.data11:
- data.append(d.vswr)
+ data = [d.vswr for d in self.app.data.s11]
+
# min_idx = np.argmin(data)
#
# logger.debug("Minimum at %d", min_idx)
# logger.debug("Value at minimum: %f", data[min_idx])
- # logger.debug("Frequency: %d", self.app.data11[min_idx].freq)
+ # logger.debug("Frequency: %d", self.app.data.s11[min_idx].freq)
#
# if self.checkbox_move_marker.isChecked():
- # self.app.markers[0].setFrequency(str(self.app.data11[min_idx].freq))
- # self.app.markers[0].frequencyInput.setText(str(self.app.data11[min_idx].freq))
+ # self.app.markers[0].setFrequency(str(self.app.data.s11[min_idx].freq))
+ # self.app.markers[0].frequencyInput.setText(str(self.app.data.s11[min_idx].freq))
threshold = self.input_vswr_limit.value()
minimums = self.find_minimums(data, threshold)
@@ -101,7 +96,7 @@ def runAnalysis(self):
results_header = self.layout.indexOf(self.results_label)
logger.debug("Results start at %d, out of %d",
results_header, self.layout.rowCount())
- for i in range(results_header, self.layout.rowCount()):
+ for _ in range(results_header, self.layout.rowCount()):
self.layout.removeRow(self.layout.rowCount() - 1)
if len(minimums) > max_dips_shown:
@@ -113,7 +108,7 @@ def runAnalysis(self):
dips.append(data[lowest])
best_dips = []
- for i in range(max_dips_shown):
+ for _ in range(max_dips_shown):
min_idx = np.argmin(dips)
best_dips.append(minimums[min_idx])
dips.remove(dips[min_idx])
@@ -127,24 +122,23 @@ def runAnalysis(self):
logger.debug(
"Section from %d to %d, lowest at %d", start, end, lowest)
self.layout.addRow("Start", QtWidgets.QLabel(
- format_frequency(self.app.data11[start].freq)))
+ format_frequency(self.app.data.s11[start].freq)))
self.layout.addRow(
"Minimum",
QtWidgets.QLabel(
- f"{format_frequency(self.app.data11[lowest].freq)}"
+ f"{format_frequency(self.app.data.s11[lowest].freq)}"
f" ({round(data[lowest], 2)})"))
self.layout.addRow("End", QtWidgets.QLabel(
- format_frequency(self.app.data11[end].freq)))
+ format_frequency(self.app.data.s11[end].freq)))
self.layout.addRow(
"Span",
QtWidgets.QLabel(
- format_frequency(self.app.data11[end].freq -
- self.app.data11[start].freq)))
- self.layout.addWidget(PeakSearchAnalysis.QHLine())
+ format_frequency(self.app.data.s11[end].freq -
+ self.app.data.s11[start].freq)))
else:
self.layout.addRow("Low spot", QtWidgets.QLabel(
- format_frequency(self.app.data11[lowest].freq)))
- self.layout.addWidget(PeakSearchAnalysis.QHLine())
+ format_frequency(self.app.data.s11[lowest].freq)))
+ self.layout.addWidget(PeakSearchAnalysis.QHLine())
# Remove the final separator line
self.layout.removeRow(self.layout.rowCount() - 1)
else:
@@ -186,11 +180,11 @@ def __init__(self, app):
self.layout.addRow(self.results_label)
def _get_data(self, index):
- my_data = {"freq": self.app.data11[index].freq,
- "s11": self.app.data11[index].z,
- "lambda": self.app.data11[index].wavelength,
- "impedance": self.app.data11[index].impedance(),
- "vswr": self.app.data11[index].vswr,
+ my_data = {"freq": self.app.data.s11[index].freq,
+ "s11": self.app.data.s11[index].z,
+ "lambda": self.app.data.s11[index].wavelength,
+ "impedance": self.app.data.s11[index].impedance(),
+ "vswr": self.app.data.s11[index].vswr,
}
my_data["vswr_49"] = self.vswr_transformed(
my_data["impedance"], 49)
@@ -202,13 +196,8 @@ def _get_data(self, index):
return my_data
def _get_crossing(self):
-
- data = []
- for d in self.app.data11:
- data.append(d.phase)
-
- crossing = sorted(self.find_crossing_zero(data))
- return crossing
+ data = [d.phase for d in self.app.data.s11]
+ return sorted(self.find_crossing_zero(data))
def runAnalysis(self):
self.reset()
@@ -228,17 +217,15 @@ def runAnalysis(self):
results_header = self.layout.indexOf(self.results_label)
logger.debug("Results start at %d, out of %d",
results_header, self.layout.rowCount())
- for i in range(results_header, self.layout.rowCount()):
+ for _ in range(results_header, self.layout.rowCount()):
self.layout.removeRow(self.layout.rowCount() - 1)
# if len(crossing) > max_dips_shown:
# self.layout.addRow(QtWidgets.QLabel("More than " + str(max_dips_shown) +
# " dips found. Lowest shown."))
-
# self.crossing = crossing[:max_dips_shown]
- extended_data = []
if len(crossing) > 0:
-
+ extended_data = []
for m in crossing:
start, lowest, end = m
my_data = self._get_data(lowest)
@@ -251,11 +238,11 @@ def runAnalysis(self):
self.layout.addRow(
"Resonance",
QtWidgets.QLabel(
- f"{format_frequency(self.app.data11[lowest].freq)}"
- f" ({format_complex_imp(self.app.data11[lowest].impedance())})"))
+ f"{format_frequency(self.app.data.s11[lowest].freq)}"
+ f" ({format_complex_imp(self.app.data.s11[lowest].impedance())})"))
else:
self.layout.addRow("Resonance", QtWidgets.QLabel(
- format_frequency(self.app.data11[lowest].freq)))
+ format_frequency(self.app.data.s11[lowest].freq)))
self.layout.addWidget(PeakSearchAnalysis.QHLine())
# Remove the final separator line
self.layout.removeRow(self.layout.rowCount() - 1)
@@ -296,7 +283,7 @@ def runAnalysis(self):
crossing = self._get_crossing()
data = []
- for d in self.app.data11:
+ for d in self.app.data.s11:
data.append(d.impedance().real)
maximums = sorted(self.find_maximums(data, threshold=500))
@@ -342,13 +329,12 @@ def runAnalysis(self):
# self.app.markers[i].label['returnloss'].setMinimumWidth(80)
self.app.markers[i].setFrequency(
- str(self.app.data11[both[i]].freq))
+ str(self.app.data.s11[both[i]].freq))
self.app.markers[i].frequencyInput.setText(
- str(self.app.data11[both[i]].freq))
+ str(self.app.data.s11[both[i]].freq))
else:
logger.info("TO DO: find near data")
- for m in crossing:
- start, lowest, end = m
+ for _, lowest, _ in crossing:
my_data = self._get_data(lowest)
if lowest in extended_data:
@@ -382,9 +368,9 @@ def runAnalysis(self):
for i, index in enumerate(sorted(extended_data.keys())):
self.layout.addRow(
- f"{format_frequency_short(self.app.data11[index].freq)}",
+ f"{format_frequency_short(self.app.data.s11[index].freq)}",
QtWidgets.QLabel(f" ({diff[i]['freq']})"
- f" {format_complex_imp(self.app.data11[index].impedance())}"
+ f" {format_complex_imp(self.app.data.s11[index].impedance())}"
f" ({diff[i]['r']})"
f" {diff[i]['lambda']} m"))
@@ -402,16 +388,17 @@ def runAnalysis(self):
row = extended_data[index]
writer.writerow(row)
- def compare(self, old, new, fields=[("freq", str), ]):
+ def compare(self, old, new, fields=None):
'''
Compare data to help changes
NB
- must be same sweep
+ must be same sweep
( same index must be same frequence )
:param old:
:param new:
'''
+ fields = fields or [("freq", str), ]
def no_compare():
@@ -455,7 +442,7 @@ def no_compare():
if delta_f > 0:
logger.debug("possible missing band, ")
- if (len(old_idx) > (i + split + 1)):
+ if len(old_idx) > (i + split + 1):
if abs(new[k]["freq"] - old[old_idx[i + split + 1]]["freq"]) < max_delta_f:
logger.debug("new is missing band, compare next ")
split += 1
diff --git a/NanoVNASaver/Calibration.py b/NanoVNASaver/Calibration.py
index b5146982..c9e88ad8 100644
--- a/NanoVNASaver/Calibration.py
+++ b/NanoVNASaver/Calibration.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -97,8 +97,7 @@ def get(self, freq: int) -> CalData:
return self.data[freq]
def items(self):
- for item in self.data.items():
- yield item
+ yield from self.data.items()
def values(self):
for freq in self.frequencies():
@@ -212,14 +211,14 @@ def calc_corrections(self):
caldata["delta_e"] = - ((g1 * (gm2 - gm3) - g2 * gm2 + g3 *
gm3) * gm1 + (g2 * gm3 - g3 * gm3) *
gm2) / denominator
- except ZeroDivisionError:
+ except ZeroDivisionError as exc:
self.isCalculated = False
logger.error(
"Division error - did you use the same measurement"
" for two of short, open and load?")
raise ValueError(
f"Two of short, open and load returned the same"
- f" values at frequency {freq}Hz.")
+ f" values at frequency {freq}Hz.") from exc
if self.isValid2Port():
caldata["e30"] = caldata["isolation"].z
@@ -236,38 +235,36 @@ def gamma_short(self, freq: int) -> complex:
g = Calibration.IDEAL_SHORT
if not self.useIdealShort:
logger.debug("Using short calibration set values.")
- Zsp = complex(0, 1) * 2 * math.pi * freq * (
+ Zsp = complex(0, 2 * math.pi * freq * (
self.shortL0 + self.shortL1 * freq +
- self.shortL2 * freq**2 + self.shortL3 * freq**3)
+ self.shortL2 * freq**2 + self.shortL3 * freq**3))
# Referencing https://arxiv.org/pdf/1606.02446.pdf (18) - (21)
g = (Zsp / 50 - 1) / (Zsp / 50 + 1) * cmath.exp(
- complex(0, 1) * 2 * math.pi * 2 * freq *
- self.shortLength * -1)
+ complex(0, 2 * math.pi * 2 * freq * self.shortLength * -1))
return g
def gamma_open(self, freq: int) -> complex:
g = Calibration.IDEAL_OPEN
if not self.useIdealOpen:
logger.debug("Using open calibration set values.")
- divisor = (2 * math.pi * freq * (
+ Zop = complex(0, 2 * math.pi * freq * (
self.openC0 + self.openC1 * freq +
self.openC2 * freq**2 + self.openC3 * freq**3))
- if divisor != 0:
- Zop = complex(0, -1) / divisor
- g = ((Zop / 50 - 1) / (Zop / 50 + 1)) * cmath.exp(
- complex(0, 1) * 2 * math.pi *
- 2 * freq * self.openLength * -1)
+ g = ((1 - 50 * Zop) / (1 + 50 * Zop)) * cmath.exp(
+ complex(0, 2 * math.pi * 2 * freq * self.openLength * -1))
return g
def gamma_load(self, freq: int) -> complex:
g = Calibration.IDEAL_LOAD
if not self.useIdealLoad:
logger.debug("Using load calibration set values.")
- Zl = self.loadR + (complex(0, 1) * 2 *
- math.pi * freq * self.loadL)
+ Zl = complex(self.loadR, 0)
+ if self.loadC > 0:
+ Zl = self.loadR / complex(1, 2 * self.loadR * math.pi * freq * self.loadC)
+ if self.loadL > 0:
+ Zl = Zl + complex(0, 2 * math.pi * freq * self.loadL)
g = (Zl / 50 - 1) / (Zl / 50 + 1) * cmath.exp(
- complex(0, 1) * 2 * math.pi *
- 2 * freq * self.loadLength * -1)
+ complex(0, 2 * math.pi * 2 * freq * self.loadLength * -1))
return g
def gamma_through(self, freq: int) -> complex:
@@ -354,12 +351,10 @@ def load(self, filename):
self.notes.append(note)
continue
if line.startswith("#"):
- if not parsed_header:
- # Check that this is a valid header
- if line == (
- "# Hz ShortR ShortI OpenR OpenI LoadR LoadI"
- " ThroughR ThroughI IsolationR IsolationI"):
- parsed_header = True
+ if not parsed_header and line == (
+ "# Hz ShortR ShortI OpenR OpenI LoadR LoadI"
+ " ThroughR ThroughI IsolationR IsolationI"):
+ parsed_header = True
continue
if not parsed_header:
logger.warning(
@@ -372,11 +367,7 @@ def load(self, filename):
logger.warning("Illegal data in cal file. Line %i", i)
cal = m.groupdict()
- if cal["throughr"]:
- nr_cals = 5
- else:
- nr_cals = 3
-
+ nr_cals = 5 if cal["throughr"] else 3
for name in Calibration.CAL_NAMES[:nr_cals]:
self.dataset.insert(
name,
diff --git a/NanoVNASaver/Charts/CLogMag.py b/NanoVNASaver/Charts/CLogMag.py
index a4776d0c..fa2f8fe4 100644
--- a/NanoVNASaver/Charts/CLogMag.py
+++ b/NanoVNASaver/Charts/CLogMag.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,11 +20,12 @@
import logging
from typing import List
-from PyQt5 import QtWidgets, QtGui
+from PyQt5 import QtGui
from NanoVNASaver.RFTools import Datapoint
-from .Frequency import FrequencyChart
-from .LogMag import LogMagChart
+from NanoVNASaver.Charts.Chart import Chart
+from NanoVNASaver.Charts.Frequency import FrequencyChart
+from NanoVNASaver.Charts.LogMag import LogMagChart
logger = logging.getLogger(__name__)
@@ -32,9 +33,7 @@
class CombinedLogMagChart(FrequencyChart):
def __init__(self, name=""):
super().__init__(name)
- self.leftMargin = 30
- self.chartWidth = 250
- self.chartHeight = 250
+
self.minDisplayValue = -80
self.maxDisplayValue = 10
@@ -50,15 +49,6 @@ def __init__(self, name=""):
self.isInverted = False
- self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin,
- self.chartHeight + self.topMargin + self.bottomMargin)
- self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
- QtWidgets.QSizePolicy.MinimumExpanding))
- pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
- self.setPalette(pal)
- self.setAutoFillBackground(True)
-
def setCombinedData(self, data11, data21):
self.data11 = data11
self.data21 = data21
@@ -80,23 +70,23 @@ def resetDisplayLimits(self):
self.update()
def drawChart(self, qp: QtGui.QPainter):
- qp.setPen(QtGui.QPen(self.textColor))
- qp.drawText(int(round(self.chartWidth / 2)) - 20, 15, self.name + " (dB)")
+ qp.setPen(QtGui.QPen(Chart.color.text))
+ qp.drawText(int(round(self.dim.width / 2)) - 20, 15, self.name + " (dB)")
qp.drawText(10, 15, "S11")
- qp.drawText(self.leftMargin + self.chartWidth - 8, 15, "S21")
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ qp.drawText(self.leftMargin + self.dim.width - 8, 15, "S21")
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin, self.topMargin - 5,
- self.leftMargin, self.topMargin+self.chartHeight+5)
- qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight,
- self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight)
+ self.leftMargin, self.topMargin+self.dim.height+5)
+ qp.drawLine(self.leftMargin-5, self.topMargin+self.dim.height,
+ self.leftMargin+self.dim.width, self.topMargin + self.dim.height)
def drawValues(self, qp: QtGui.QPainter):
if len(self.data11) == 0 and len(self.reference11) == 0:
return
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
- line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(self.lineThickness)
+ pen = QtGui.QPen(Chart.color.sweep)
+ pen.setWidth(self.dim.point)
+ line_pen = QtGui.QPen(Chart.color.sweep)
+ line_pen.setWidth(self.dim.line)
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
highlighter.setWidth(1)
if not self.fixedSpan:
@@ -218,26 +208,26 @@ def drawValues(self, qp: QtGui.QPainter):
for i in range(tick_count):
db = first_tick + i * tick_step
- y = self.topMargin + round((maxValue - db)/span*self.chartHeight)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin-5, y, self.leftMargin+self.chartWidth, y)
+ y = self.topMargin + round((maxValue - db)/span*self.dim.height)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin-5, y, self.leftMargin+self.dim.width, y)
if db > minValue and db != maxValue:
- qp.setPen(QtGui.QPen(self.textColor))
+ qp.setPen(QtGui.QPen(Chart.color.text))
if tick_step < 1:
dbstr = str(round(db, 1))
else:
dbstr = str(db)
qp.drawText(3, y + 4, dbstr)
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5, self.topMargin,
- self.leftMargin + self.chartWidth, self.topMargin)
- qp.setPen(self.textColor)
+ self.leftMargin + self.dim.width, self.topMargin)
+ qp.setPen(Chart.color.text)
qp.drawText(3, self.topMargin + 4, str(maxValue))
- qp.drawText(3, self.chartHeight+self.topMargin, str(minValue))
+ qp.drawText(3, self.dim.height+self.topMargin, str(minValue))
self.drawFrequencyTicks(qp)
- qp.setPen(self.swrColor)
+ qp.setPen(Chart.color.swr)
for vswr in self.swrMarkers:
if vswr <= 1:
continue
@@ -245,45 +235,45 @@ def drawValues(self, qp: QtGui.QPainter):
if self.isInverted:
logMag = logMag * -1
y = self.topMargin + round((self.maxValue - logMag) /
- self.span * self.chartHeight)
+ self.span * self.dim.height)
qp.drawLine(self.leftMargin, y,
- self.leftMargin + self.chartWidth, y)
+ self.leftMargin + self.dim.width, y)
qp.drawText(self.leftMargin + 3, y - 1, "VSWR: " + str(vswr))
if len(self.data11) > 0:
- c = QtGui.QColor(self.sweepColor)
+ c = QtGui.QColor(Chart.color.sweep)
c.setAlpha(255)
pen = QtGui.QPen(c)
pen.setWidth(2)
qp.setPen(pen)
qp.drawLine(33, 9, 38, 9)
- c = QtGui.QColor(self.secondarySweepColor)
+ c = QtGui.QColor(Chart.color.sweep_secondary)
c.setAlpha(255)
pen = QtGui.QPen(c)
pen.setWidth(2)
qp.setPen(pen)
- qp.drawLine(self.leftMargin + self.chartWidth - 20, 9,
- self.leftMargin + self.chartWidth - 15, 9)
+ qp.drawLine(self.leftMargin + self.dim.width - 20, 9,
+ self.leftMargin + self.dim.width - 15, 9)
if len(self.reference11) > 0:
- c = QtGui.QColor(self.referenceColor)
+ c = QtGui.QColor(Chart.color.reference)
c.setAlpha(255)
pen = QtGui.QPen(c)
pen.setWidth(2)
qp.setPen(pen)
qp.drawLine(33, 14, 38, 14)
- c = QtGui.QColor(self.secondaryReferenceColor)
+ c = QtGui.QColor(Chart.color.reference_secondary)
c.setAlpha(255)
pen = QtGui.QPen(c)
pen.setWidth(2)
qp.setPen(pen)
- qp.drawLine(self.leftMargin + self.chartWidth - 20, 14,
- self.leftMargin + self.chartWidth - 15, 14)
+ qp.drawLine(self.leftMargin + self.dim.width - 20, 14,
+ self.leftMargin + self.dim.width - 15, 14)
- self.drawData(qp, self.data11, self.sweepColor)
- self.drawData(qp, self.data21, self.secondarySweepColor)
- self.drawData(qp, self.reference11, self.referenceColor)
- self.drawData(qp, self.reference21, self.secondaryReferenceColor)
+ self.drawData(qp, self.data11, Chart.color.sweep)
+ self.drawData(qp, self.data21, Chart.color.sweep_secondary)
+ self.drawData(qp, self.reference11, Chart.color.reference)
+ self.drawData(qp, self.reference21, Chart.color.reference_secondary)
self.drawMarkers(qp, data=self.data11)
self.drawMarkers(qp, data=self.data21)
@@ -291,11 +281,11 @@ def getYPosition(self, d: Datapoint) -> int:
logMag = self.logMag(d)
if math.isinf(logMag):
return None
- return self.topMargin + round((self.maxValue - logMag) / self.span * self.chartHeight)
+ return self.topMargin + round((self.maxValue - logMag) / self.span * self.dim.height)
def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
- val = -1 * ((absy / self.chartHeight * self.span) - self.maxValue)
+ val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
return [val]
def logMag(self, p: Datapoint) -> float:
diff --git a/NanoVNASaver/Charts/Capacitance.py b/NanoVNASaver/Charts/Capacitance.py
index 3db11418..b0647b05 100644
--- a/NanoVNASaver/Charts/Capacitance.py
+++ b/NanoVNASaver/Charts/Capacitance.py
@@ -18,13 +18,15 @@
# along with this program. If not, see .
import math
import logging
+from decimal import InvalidOperation
from typing import List
from PyQt5 import QtWidgets, QtGui
from NanoVNASaver.RFTools import Datapoint
from NanoVNASaver.SITools import Format, Value
-from .Frequency import FrequencyChart
+from NanoVNASaver.Charts.Chart import Chart
+from NanoVNASaver.Charts.Frequency import FrequencyChart
logger = logging.getLogger(__name__)
@@ -32,9 +34,9 @@
class CapacitanceChart(FrequencyChart):
def __init__(self, name=""):
super().__init__(name)
- self.leftMargin = 30
- self.chartWidth = 250
- self.chartHeight = 250
+ self.leftMargin = 45
+ self.dim.width = 250
+ self.dim.height = 250
self.minDisplayValue = 0
self.maxDisplayValue = 100
@@ -42,31 +44,31 @@ def __init__(self, name=""):
self.maxValue = 1
self.span = 1
- self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin,
- self.chartHeight + self.topMargin + self.bottomMargin)
+ self.setMinimumSize(self.dim.width + self.rightMargin + self.leftMargin,
+ self.dim.height + self.topMargin + self.bottomMargin)
self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
QtWidgets.QSizePolicy.MinimumExpanding))
pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
+ pal.setColor(QtGui.QPalette.Background, Chart.color.background)
self.setPalette(pal)
self.setAutoFillBackground(True)
def drawChart(self, qp: QtGui.QPainter):
- qp.setPen(QtGui.QPen(self.textColor))
+ qp.setPen(QtGui.QPen(Chart.color.text))
qp.drawText(3, 15, self.name + " (F)")
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.chartHeight+5)
- qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight,
- self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.dim.height+5)
+ qp.drawLine(self.leftMargin-5, self.topMargin+self.dim.height,
+ self.leftMargin+self.dim.width, self.topMargin + self.dim.height)
self.drawTitle(qp)
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
- line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(self.lineThickness)
+ pen = QtGui.QPen(Chart.color.sweep)
+ pen.setWidth(self.dim.point)
+ line_pen = QtGui.QPen(Chart.color.sweep)
+ line_pen.setWidth(self.dim.line)
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
highlighter.setWidth(1)
if not self.fixedSpan:
@@ -118,39 +120,46 @@ def drawValues(self, qp: QtGui.QPainter):
span = 1e-15
self.span = span
- target_ticks = math.floor(self.chartHeight / 60)
- fmt = Format(max_nr_digits=1)
+ target_ticks = math.floor(self.dim.height / 60)
+ fmt = Format(max_nr_digits=3)
for i in range(target_ticks):
val = minValue + (i / target_ticks) * span
- y = self.topMargin + round((self.maxValue - val) / self.span * self.chartHeight)
- qp.setPen(self.textColor)
- if val != minValue:
- valstr = str(Value(val, fmt=fmt))
- qp.drawText(3, y + 3, valstr)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.chartWidth, y)
-
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ try:
+ y = self.topMargin + round((self.maxValue - val) / self.span * self.dim.height)
+ qp.setPen(Chart.color.text)
+ if val != minValue:
+ valstr = str(Value(val, fmt=fmt))
+ qp.drawText(3, y + 3, valstr)
+ except (ValueError, InvalidOperation) as exc:
+ logger.debug("Drawing wrent wrong: %s", exc)
+ y = self.topMargin
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.dim.width, y)
+
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5, self.topMargin,
- self.leftMargin + self.chartWidth, self.topMargin)
- qp.setPen(self.textColor)
+ self.leftMargin + self.dim.width, self.topMargin)
+ qp.setPen(Chart.color.text)
qp.drawText(3, self.topMargin + 4, str(Value(maxValue, fmt=fmt)))
- qp.drawText(3, self.chartHeight+self.topMargin, str(Value(minValue, fmt=fmt)))
+ qp.drawText(3, self.dim.height+self.topMargin, str(Value(minValue, fmt=fmt)))
self.drawFrequencyTicks(qp)
- self.drawData(qp, self.data, self.sweepColor)
- self.drawData(qp, self.reference, self.referenceColor)
+ self.drawData(qp, self.data, Chart.color.sweep)
+ self.drawData(qp, self.reference, Chart.color.reference)
self.drawMarkers(qp)
def getYPosition(self, d: Datapoint) -> int:
- return (
- self.topMargin +
- round((self.maxValue - d.capacitiveEquivalent()) /
- self.span * self.chartHeight))
+ try:
+ return (
+ self.topMargin +
+ round((self.maxValue - d.capacitiveEquivalent()) /
+ self.span * self.dim.height))
+ except ValueError:
+ return self.topMargin
def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
- val = -1 * ((absy / self.chartHeight * self.span) - self.maxValue)
+ val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
return [val * 10e11]
def copy(self):
@@ -163,8 +172,8 @@ class InductanceChart(FrequencyChart):
def __init__(self, name=""):
super().__init__(name)
self.leftMargin = 30
- self.chartWidth = 250
- self.chartHeight = 250
+ self.dim.width = 250
+ self.dim.height = 250
self.minDisplayValue = 0
self.maxDisplayValue = 100
@@ -172,31 +181,31 @@ def __init__(self, name=""):
self.maxValue = 1
self.span = 1
- self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin,
- self.chartHeight + self.topMargin + self.bottomMargin)
+ self.setMinimumSize(self.dim.width + self.rightMargin + self.leftMargin,
+ self.dim.height + self.topMargin + self.bottomMargin)
self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
QtWidgets.QSizePolicy.MinimumExpanding))
pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
+ pal.setColor(QtGui.QPalette.Background, Chart.color.background)
self.setPalette(pal)
self.setAutoFillBackground(True)
def drawChart(self, qp: QtGui.QPainter):
- qp.setPen(QtGui.QPen(self.textColor))
+ qp.setPen(QtGui.QPen(Chart.color.text))
qp.drawText(3, 15, self.name + " (H)")
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.chartHeight+5)
- qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight,
- self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.dim.height+5)
+ qp.drawLine(self.leftMargin-5, self.topMargin+self.dim.height,
+ self.leftMargin+self.dim.width, self.topMargin + self.dim.height)
self.drawTitle(qp)
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
- line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(self.lineThickness)
+ pen = QtGui.QPen(Chart.color.sweep)
+ pen.setWidth(self.dim.point)
+ line_pen = QtGui.QPen(Chart.color.sweep)
+ line_pen.setWidth(self.dim.line)
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
highlighter.setWidth(1)
if not self.fixedSpan:
@@ -248,38 +257,38 @@ def drawValues(self, qp: QtGui.QPainter):
span = 1e-15
self.span = span
- target_ticks = math.floor(self.chartHeight / 60)
- fmt = Format(max_nr_digits=1)
+ target_ticks = math.floor(self.dim.height / 60)
+ fmt = Format(max_nr_digits=3)
for i in range(target_ticks):
val = minValue + (i / target_ticks) * span
- y = self.topMargin + round((self.maxValue - val) / self.span * self.chartHeight)
- qp.setPen(self.textColor)
+ y = self.topMargin + round((self.maxValue - val) / self.span * self.dim.height)
+ qp.setPen(Chart.color.text)
if val != minValue:
valstr = str(Value(val, fmt=fmt))
qp.drawText(3, y + 3, valstr)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.chartWidth, y)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.dim.width, y)
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5, self.topMargin,
- self.leftMargin + self.chartWidth, self.topMargin)
- qp.setPen(self.textColor)
+ self.leftMargin + self.dim.width, self.topMargin)
+ qp.setPen(Chart.color.text)
qp.drawText(3, self.topMargin + 4, str(Value(maxValue, fmt=fmt)))
- qp.drawText(3, self.chartHeight+self.topMargin, str(Value(minValue, fmt=fmt)))
+ qp.drawText(3, self.dim.height+self.topMargin, str(Value(minValue, fmt=fmt)))
self.drawFrequencyTicks(qp)
- self.drawData(qp, self.data, self.sweepColor)
- self.drawData(qp, self.reference, self.referenceColor)
+ self.drawData(qp, self.data, Chart.color.sweep)
+ self.drawData(qp, self.reference, Chart.color.reference)
self.drawMarkers(qp)
def getYPosition(self, d: Datapoint) -> int:
return (self.topMargin +
round((self.maxValue - d.inductiveEquivalent()) /
- self.span * self.chartHeight))
+ self.span * self.dim.height))
def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
- val = -1 * ((absy / self.chartHeight * self.span) - self.maxValue)
+ val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
return [val * 10e11]
def copy(self):
diff --git a/NanoVNASaver/Charts/Chart.py b/NanoVNASaver/Charts/Chart.py
index a53321c2..abb57582 100644
--- a/NanoVNASaver/Charts/Chart.py
+++ b/NanoVNASaver/Charts/Chart.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,102 +16,122 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-import math
-from typing import List, Set
import logging
+from dataclasses import dataclass, replace
+from typing import List, Set, Tuple, ClassVar, Any
+
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtCore import pyqtSignal
from NanoVNASaver.RFTools import Datapoint
from NanoVNASaver.Marker import Marker
+
logger = logging.getLogger(__name__)
+@dataclass
+class ChartColors: # pylint: disable=too-many-instance-attributes
+ background: QtGui.QColor = QtGui.QColor(QtCore.Qt.white)
+ foreground: QtGui.QColor = QtGui.QColor(QtCore.Qt.lightGray)
+ reference: QtGui.QColor = QtGui.QColor(0, 0, 255, 64)
+ reference_secondary: QtGui.QColor = QtGui.QColor(0, 0, 192, 48)
+ sweep: QtGui.QColor = QtGui.QColor(QtCore.Qt.darkYellow)
+ sweep_secondary: QtGui.QColor = QtGui.QColor(QtCore.Qt.darkMagenta)
+ swr: QtGui.QColor = QtGui.QColor(255, 0, 0, 128)
+ text: QtGui.QColor = QtGui.QColor(QtCore.Qt.black)
+ bands: QtGui.QColor = QtGui.QColor(128, 128, 128, 48)
+
+@dataclass
+class ChartDimensions:
+ height: int = 200
+ height_min: int = 200
+ width: int = 200
+ width_min: int = 200
+ line: int = 1
+ point: int = 2
+
+@dataclass
+class ChartDragBox:
+ pos: Tuple[int] = (-1, -1)
+ pos_start: Tuple[int] = (0, 0)
+ state: bool = False
+ move_x: int = -1
+ move_y: int = -1
+
+@dataclass
+class ChartFlags:
+ draw_lines: bool = False
+ is_popout: bool = False
+
+@dataclass
+class ChartMarkerConfig:
+ draw_label: bool = False
+ fill: bool = False
+ at_tip: bool = False
+ size: int = 3
+
+class ChartMarker(QtWidgets.QWidget):
+ cfg: ClassVar[ChartMarkerConfig] = ChartMarkerConfig()
+
+ def __init__(self, qp: QtGui.QPaintDevice):
+ super().__init__()
+ self.qp = qp
+
+ def draw(self, x: int, y: int, color: QtGui.QColor, text: str = ""):
+ offset = self.cfg.size // 2
+ if self.cfg.at_tip:
+ y -= offset
+ pen = QtGui.QPen(color)
+ self.qp.setPen(pen)
+ qpp = QtGui.QPainterPath()
+ qpp.moveTo(x, y + offset)
+ qpp.lineTo(x - offset, y - offset)
+ qpp.lineTo(x + offset, y - offset)
+ qpp.lineTo(x, y + offset)
+
+ if self.cfg.fill:
+ self.qp.fillPath(qpp, color)
+ else:
+ self.qp.drawPath(qpp)
+
+ if text and self.cfg.draw_label:
+ text_width = self.qp.fontMetrics().horizontalAdvance(text)
+ self.qp.drawText(x - text_width // 2, y - 3 - offset, text)
+
+
class Chart(QtWidgets.QWidget):
- sweepColor = QtCore.Qt.darkYellow
- secondarySweepColor = QtCore.Qt.darkMagenta
- referenceColor: QtGui.QColor = QtGui.QColor(QtCore.Qt.blue)
- referenceColor.setAlpha(64)
- secondaryReferenceColor: QtGui.QColor = QtGui.QColor(QtCore.Qt.blue)
- secondaryReferenceColor.setAlpha(64)
- backgroundColor: QtGui.QColor = QtGui.QColor(QtCore.Qt.white)
- foregroundColor: QtGui.QColor = QtGui.QColor(QtCore.Qt.lightGray)
- textColor: QtGui.QColor = QtGui.QColor(QtCore.Qt.black)
- swrColor: QtGui.QColor = QtGui.QColor(QtCore.Qt.red)
- swrColor.setAlpha(128)
- data: List[Datapoint] = []
- reference: List[Datapoint] = []
- markers: List[Marker] = []
- swrMarkers: Set[float] = set()
- bands = None
- draggedMarker: Marker = None
- name = ""
- sweepTitle = ""
- drawLines = False
- minChartHeight = 200
- minChartWidth = 200
- chartWidth = minChartWidth
- chartHeight = minChartHeight
- lineThickness = 1
- pointSize = 2
- markerSize = 3
- drawMarkerNumbers = False
- markerAtTip = False
- filledMarkers = False
- draggedBox = False
- draggedBoxStart = (0, 0)
- draggedBoxCurrent = (-1, -1)
- moveStartX = -1
- moveStartY = -1
-
- isPopout = False
- popoutRequested = pyqtSignal(object)
+ bands: ClassVar[Any] = None
+ popoutRequested: ClassVar[Any] = pyqtSignal(object)
+ color: ClassVar[ChartColors] = ChartColors()
+ marker_cfg: ClassVar[ChartMarkerConfig] = ChartMarkerConfig()
def __init__(self, name):
super().__init__()
self.name = name
+ self.sweepTitle = ""
- self.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
- self.action_save_screenshot = QtWidgets.QAction("Save image")
- self.action_save_screenshot.triggered.connect(self.saveScreenshot)
- self.addAction(self.action_save_screenshot)
- self.action_popout = QtWidgets.QAction("Popout chart")
- self.action_popout.triggered.connect(lambda: self.popoutRequested.emit(self))
- self.addAction(self.action_popout)
+ self.dim = ChartDimensions()
+ self.dragbox = ChartDragBox()
+ self.flag = ChartFlags()
- self.swrMarkers = set()
-
- def setSweepColor(self, color: QtGui.QColor):
- self.sweepColor = color
- self.update()
+ self.draggedMarker = None
- def setSecondarySweepColor(self, color: QtGui.QColor):
- self.secondarySweepColor = color
- self.update()
+ self.data: List[Datapoint] = []
+ self.reference: List[Datapoint] = []
- def setReferenceColor(self, color: QtGui.QColor):
- self.referenceColor = color
- self.update()
+ self.markers: List[Marker] = []
+ self.swrMarkers: Set[float] = set()
- def setSecondaryReferenceColor(self, color: QtGui.QColor):
- self.secondaryReferenceColor = color
- self.update()
-
- def setBackgroundColor(self, color: QtGui.QColor):
- self.backgroundColor = color
- pal = self.palette()
- pal.setColor(QtGui.QPalette.Background, color)
- self.setPalette(pal)
- self.update()
+ self.action_popout = QtWidgets.QAction("Popout chart")
+ self.action_popout.triggered.connect(lambda: self.popoutRequested.emit(self))
+ self.addAction(self.action_popout)
- def setForegroundColor(self, color: QtGui.QColor):
- self.foregroundColor = color
- self.update()
+ self.action_save_screenshot = QtWidgets.QAction("Save image")
+ self.action_save_screenshot.triggered.connect(self.saveScreenshot)
+ self.addAction(self.action_save_screenshot)
- def setTextColor(self, color: QtGui.QColor):
- self.textColor = color
- self.update()
+ self.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
def setReference(self, data):
self.reference = data
@@ -132,15 +152,15 @@ def setBands(self, bands):
self.bands = bands
def setLineThickness(self, thickness):
- self.lineThickness = thickness
+ self.dim.line = thickness
self.update()
def setPointSize(self, size):
- self.pointSize = size
+ self.dim.point = size
self.update()
def setMarkerSize(self, size):
- self.markerSize = size
+ ChartMarker.cfg.size = size
self.update()
def setSweepTitle(self, title):
@@ -162,49 +182,19 @@ def getNearestMarker(self, x, y) -> Marker:
nearest = None
for m in self.markers:
mx, my = self.getPosition(self.data[m.location])
- dx = abs(x - mx)
- dy = abs(y - my)
- distance = math.sqrt(dx**2 + dy**2)
+ distance = abs(complex(x - mx, y - my))
if distance < shortest:
shortest = distance
nearest = m
return nearest
- def getYPosition(self, d: Datapoint) -> int:
- return 0
-
- def getXPosition(self, d: Datapoint) -> int:
- return 0
-
- def getPosition(self, d: Datapoint) -> (int, int):
+ def getPosition(self, d: Datapoint) -> Tuple[int, int]:
return self.getXPosition(d), self.getYPosition(d)
def setDrawLines(self, draw_lines):
- self.drawLines = draw_lines
+ self.flag.draw_lines = draw_lines
self.update()
- def setDrawMarkerNumbers(self, draw_marker_numbers):
- self.drawMarkerNumbers = draw_marker_numbers
- self.update()
-
- def setMarkerAtTip(self, marker_at_tip):
- self.markerAtTip = marker_at_tip
- self.update()
-
- def setFilledMarkers(self, filled_markers):
- self.filledMarkers = filled_markers
- self.update()
-
- @staticmethod
- def shortenFrequency(frequency: int) -> str:
- if frequency < 50000:
- return str(frequency)
- if frequency < 5000000:
- return str(round(frequency / 1000)) + "k"
- if frequency < 50000000:
- return str(round(frequency / 1000000, 2)) + "M"
- return str(round(frequency / 1000000, 1)) + "M"
-
def mousePressEvent(self, event: QtGui.QMouseEvent) -> None:
if event.buttons() == QtCore.Qt.RightButton:
event.ignore()
@@ -212,64 +202,57 @@ def mousePressEvent(self, event: QtGui.QMouseEvent) -> None:
if event.buttons() == QtCore.Qt.MiddleButton:
# Drag event
event.accept()
- self.moveStartX = event.x()
- self.moveStartY = event.y()
+ self.dragbox.move_x = event.x()
+ self.dragbox.move_y = event.y()
return
- if event.modifiers() == QtCore.Qt.ShiftModifier:
- self.draggedMarker = self.getNearestMarker(event.x(), event.y())
- elif event.modifiers() == QtCore.Qt.ControlModifier:
+ if event.modifiers() == QtCore.Qt.ControlModifier:
event.accept()
- self.draggedBox = True
- self.draggedBoxStart = (event.x(), event.y())
+ self.dragbox.state = True
+ self.dragbox.pos_start = (event.x(), event.y())
return
+ if event.modifiers() == QtCore.Qt.ShiftModifier:
+ self.draggedMarker = self.getNearestMarker(event.x(), event.y())
self.mouseMoveEvent(event)
- def mouseReleaseEvent(self, a0: QtGui.QMouseEvent) -> None:
+ def mouseReleaseEvent(self, a0: QtGui.QMouseEvent):
self.draggedMarker = None
- if self.draggedBox:
- self.zoomTo(self.draggedBoxStart[0], self.draggedBoxStart[1], a0.x(), a0.y())
- self.draggedBox = False
- self.draggedBoxCurrent = (-1, -1)
- self.draggedBoxStart = (0, 0)
+ if self.dragbox.state:
+ self.zoomTo(self.dragbox.pos_start[0], self.dragbox.pos_start[1], a0.x(), a0.y())
+ self.dragbox.state = False
+ self.dragbox.pos = (-1, -1)
+ self.dragbox.pos_start = (0, 0)
self.update()
def zoomTo(self, x1, y1, x2, y2):
- pass
+ raise NotImplementedError()
def saveScreenshot(self):
logger.info("Saving %s to file...", self.name)
- filename, _ = QtWidgets.QFileDialog.getSaveFileName(parent=self, caption="Save image",
- filter="PNG (*.png);;All files (*.*)")
+ filename, _ = QtWidgets.QFileDialog.getSaveFileName(
+ parent=self, caption="Save image",
+ filter="PNG (*.png);;All files (*.*)")
logger.debug("Filename: %s", filename)
- if filename != "":
- if not QtCore.QFileInfo(filename).suffix():
- filename += ".png"
- self.grab().save(filename)
+ if not filename:
+ return
+ if not QtCore.QFileInfo(filename).suffix():
+ filename += ".png"
+ self.grab().save(filename)
def copy(self):
new_chart = self.__class__(self.name)
new_chart.data = self.data
new_chart.reference = self.reference
- new_chart.sweepColor = self.sweepColor
- new_chart.secondarySweepColor = self.secondarySweepColor
- new_chart.referenceColor = self.referenceColor
- new_chart.secondaryReferenceColor = self.secondaryReferenceColor
- new_chart.setBackgroundColor(self.backgroundColor)
- new_chart.textColor = self.textColor
- new_chart.foregroundColor = self.foregroundColor
- new_chart.swrColor = self.swrColor
+ new_chart.dim = replace(self.dim)
+ new_chart.flag = replace(self.flag)
+ new_chart.marker_cfg = replace(self.marker_cfg)
new_chart.markers = self.markers
new_chart.swrMarkers = self.swrMarkers
new_chart.bands = self.bands
- new_chart.drawLines = self.drawLines
- new_chart.markerSize = self.markerSize
- new_chart.drawMarkerNumbers = self.drawMarkerNumbers
- new_chart.filledMarkers = self.filledMarkers
- new_chart.markerAtTip = self.markerAtTip
+
new_chart.resize(self.width(), self.height())
- new_chart.setPointSize(self.pointSize)
- new_chart.setLineThickness(self.lineThickness)
+ new_chart.setPointSize(self.dim.point)
+ new_chart.setLineThickness(self.dim.line)
return new_chart
def addSWRMarker(self, swr: float):
@@ -289,36 +272,22 @@ def clearSWRMarkers(self):
self.swrMarkers.clear()
self.update()
- def setSWRColor(self, color: QtGui.QColor):
- self.swrColor = color
- self.update()
-
def drawMarker(self, x, y, qp: QtGui.QPainter, color: QtGui.QColor, number=0):
- if self.markerAtTip:
- y -= self.markerSize
- pen = QtGui.QPen(color)
- qp.setPen(pen)
- qpp = QtGui.QPainterPath()
- qpp.moveTo(x, y + self.markerSize)
- qpp.lineTo(x - self.markerSize, y - self.markerSize)
- qpp.lineTo(x + self.markerSize, y - self.markerSize)
- qpp.lineTo(x, y + self.markerSize)
-
- if self.filledMarkers:
- qp.fillPath(qpp, color)
- else:
- qp.drawPath(qpp)
-
- if self.drawMarkerNumbers:
- number_x = x - 3
- number_y = y - self.markerSize - 3
- qp.drawText(number_x, number_y, str(number))
+ cmarker = ChartMarker(qp)
+ cmarker.draw(x, y, color, str(number))
def drawTitle(self, qp: QtGui.QPainter, position: QtCore.QPoint = None):
- if self.sweepTitle != "":
- qp.setPen(self.textColor)
- if position is None:
- qf = QtGui.QFontMetricsF(self.font())
- width = qf.boundingRect(self.sweepTitle).width()
- position = QtCore.QPointF(self.width()/2 - width/2, 15)
- qp.drawText(position, self.sweepTitle)
+ if not self.sweepTitle:
+ return
+ qp.setPen(Chart.color.text)
+ if position is None:
+ qf = QtGui.QFontMetricsF(self.font())
+ width = qf.boundingRect(self.sweepTitle).width()
+ position = QtCore.QPointF(self.width()/2 - width/2, 15)
+ qp.drawText(position, self.sweepTitle)
+
+ def update(self):
+ pal = self.palette()
+ pal.setColor(QtGui.QPalette.Background, Chart.color.background)
+ self.setPalette(pal)
+ super().update()
diff --git a/NanoVNASaver/Charts/Frequency.py b/NanoVNASaver/Charts/Frequency.py
index 66e9fd68..87674d5e 100644
--- a/NanoVNASaver/Charts/Frequency.py
+++ b/NanoVNASaver/Charts/Frequency.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,14 +18,17 @@
# along with this program. If not, see .
import math
import logging
-from typing import List
+from typing import List, Tuple
import numpy as np
from PyQt5 import QtWidgets, QtGui, QtCore
-from NanoVNASaver.Formatting import parse_frequency
+from NanoVNASaver.Charts.Chart import Chart
+from NanoVNASaver.Formatting import (
+ parse_frequency, parse_value,
+ format_frequency_chart, format_y_axis)
from NanoVNASaver.RFTools import Datapoint
-from .Chart import Chart
+from NanoVNASaver.SITools import Format, Value
logger = logging.getLogger(__name__)
@@ -37,6 +40,7 @@ class FrequencyChart(Chart):
maxFrequency = 100000000
minFrequency = 1000000
+ # TODO: use unscaled values instead of unit dependend ones
minDisplayValue = -1
maxDisplayValue = 1
@@ -53,6 +57,18 @@ class FrequencyChart(Chart):
def __init__(self, name):
super().__init__(name)
+ self.leftMargin = 30
+ self.dim.width = 250
+ self.dim.height = 250
+ self.fstart = 0
+ self.fstop = 0
+
+ self.name_unit = ""
+ self.value_function = lambda x: 0.0
+
+ self.minValue = -1
+ self.maxValue = 1
+ self.span = 1
self.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
mode_group = QtWidgets.QActionGroup(self)
@@ -79,11 +95,11 @@ def __init__(self, name):
self.x_menu.addSeparator()
self.action_set_fixed_start = QtWidgets.QAction(
- "Start (" + Chart.shortenFrequency(self.minFrequency) + ")")
+ "Start (" + format_frequency_chart(self.minFrequency) + ")")
self.action_set_fixed_start.triggered.connect(self.setMinimumFrequency)
self.action_set_fixed_stop = QtWidgets.QAction(
- "Stop (" + Chart.shortenFrequency(self.maxFrequency) + ")")
+ "Stop (" + format_frequency_chart(self.maxFrequency) + ")")
self.action_set_fixed_stop.triggered.connect(self.setMaximumFrequency)
self.x_menu.addAction(self.action_set_fixed_start)
@@ -122,14 +138,14 @@ def __init__(self, name):
self.y_menu.addAction(self.y_action_fixed_span)
self.y_menu.addSeparator()
- self.action_set_fixed_maximum = QtWidgets.QAction(
- f"Maximum ({self.maxDisplayValue})")
- self.action_set_fixed_maximum.triggered.connect(self.setMaximumValue)
-
self.action_set_fixed_minimum = QtWidgets.QAction(
f"Minimum ({self.minDisplayValue})")
self.action_set_fixed_minimum.triggered.connect(self.setMinimumValue)
+ self.action_set_fixed_maximum = QtWidgets.QAction(
+ f"Maximum ({self.maxDisplayValue})")
+ self.action_set_fixed_maximum.triggered.connect(self.setMaximumValue)
+
self.y_menu.addAction(self.action_set_fixed_maximum)
self.y_menu.addAction(self.action_set_fixed_minimum)
@@ -160,11 +176,35 @@ def __init__(self, name):
self.menu.addAction(self.action_popout)
self.setFocusPolicy(QtCore.Qt.ClickFocus)
+ self.setMinimumSize(self.dim.width + self.rightMargin + self.leftMargin,
+ self.dim.height + self.topMargin + self.bottomMargin)
+ self.setSizePolicy(
+ QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
+ QtWidgets.QSizePolicy.MinimumExpanding))
+ pal = QtGui.QPalette()
+ pal.setColor(QtGui.QPalette.Background, Chart.color.background)
+ self.setPalette(pal)
+ self.setAutoFillBackground(True)
+
+ def _set_start_stop(self):
+ if self.fixedSpan:
+ fstart = self.minFrequency
+ fstop = self.maxFrequency
+ else:
+ if len(self.data) > 0:
+ fstart = self.data[0].freq
+ fstop = self.data[len(self.data)-1].freq
+ else:
+ fstart = self.reference[0].freq
+ fstop = self.reference[len(self.reference) - 1].freq
+ self.fstart = fstart
+ self.fstop = fstop
+
def contextMenuEvent(self, event):
self.action_set_fixed_start.setText(
- f"Start ({Chart.shortenFrequency(self.minFrequency)})")
+ f"Start ({format_frequency_chart(self.minFrequency)})")
self.action_set_fixed_stop.setText(
- f"Stop ({Chart.shortenFrequency(self.maxFrequency)})")
+ f"Stop ({format_frequency_chart(self.maxFrequency)})")
self.action_set_fixed_minimum.setText(
f"Minimum ({self.minDisplayValue})")
self.action_set_fixed_maximum.setText(
@@ -192,12 +232,6 @@ def setFixedSpan(self, fixed_span: bool):
def setFixedValues(self, fixed_values: bool):
self.fixedValues = fixed_values
- if fixed_values and self.minDisplayValue >= self.maxDisplayValue:
- self.fixedValues = False
- self.y_action_automatic.setChecked(True)
- self.y_action_fixed_span.setChecked(False)
- if fixed_values and self.minDisplayValue <= 0:
- self.minDisplayValue = 0.01
self.update()
def setLogarithmicX(self, logarithmic: bool):
@@ -217,11 +251,15 @@ def setMinimumFrequency(self):
"Set start frequency", text=str(self.minFrequency))
if not selected:
return
+ span = abs(self.maxFrequency - self.minFrequency)
min_freq = parse_frequency(min_freq_str)
- if min_freq > 0 and not (self.fixedSpan and min_freq >= self.maxFrequency):
- self.minFrequency = min_freq
- if self.fixedSpan:
- self.update()
+ if min_freq < 0:
+ return
+ self.minFrequency = min_freq
+ if self.minFrequency >= self.maxFrequency:
+ self.maxFrequency = self.minFrequency + span
+ self.fixedSpan = True
+ self.update()
def setMaximumFrequency(self):
max_freq_str, selected = QtWidgets.QInputDialog.getText(
@@ -229,37 +267,48 @@ def setMaximumFrequency(self):
"Set stop frequency", text=str(self.maxFrequency))
if not selected:
return
+ span = abs(self.maxFrequency - self.minFrequency)
max_freq = parse_frequency(max_freq_str)
- if max_freq > 0 and not (self.fixedSpan and max_freq <= self.minFrequency):
- self.maxFrequency = max_freq
- if self.fixedSpan:
- self.update()
+ if max_freq < 0:
+ return
+ self.maxFrequency = max_freq
+ if self.maxFrequency <= self.minFrequency:
+ self.minFrequency = max(self.maxFrequency - span, 0)
+ self.fixedSpan = True
+ self.update()
def setMinimumValue(self):
- min_val, selected = QtWidgets.QInputDialog.getDouble(
+ text, selected = QtWidgets.QInputDialog.getText(
self, "Minimum value",
- "Set minimum value", value=self.minDisplayValue,
- decimals=3)
+ "Set minimum value",
+ text=format_y_axis(self.minDisplayValue, self.name_unit))
if not selected:
return
- if not (self.fixedValues and min_val >= self.maxDisplayValue):
- self.minDisplayValue = min_val
+ min_val = parse_value(text)
+ yspan = abs(self.maxDisplayValue - self.minDisplayValue)
+ self.minDisplayValue = min_val
+ if self.minDisplayValue >= self.maxDisplayValue:
+ self.maxDisplayValue = self.minDisplayValue + yspan
+ # TODO: negativ logarythmical scale
if self.logarithmicY and min_val <= 0:
self.minDisplayValue = 0.01
- if self.fixedValues:
- self.update()
+ self.fixedValues = True
+ self.update()
def setMaximumValue(self):
- max_val, selected = QtWidgets.QInputDialog.getDouble(
+ text, selected = QtWidgets.QInputDialog.getText(
self, "Maximum value",
- "Set maximum value", value=self.maxDisplayValue,
- decimals=3)
+ "Set maximum value",
+ text=format_y_axis(self.maxDisplayValue, self.name_unit))
if not selected:
return
- if not (self.fixedValues and max_val <= self.minDisplayValue):
- self.maxDisplayValue = max_val
- if self.fixedValues:
- self.update()
+ max_val = parse_value(text)
+ yspan = abs(self.maxDisplayValue - self.minDisplayValue)
+ self.maxDisplayValue = max_val
+ if self.maxDisplayValue <= self.minDisplayValue:
+ self.minDisplayValue = self.maxDisplayValue - yspan
+ self.fixedValues = True
+ self.update()
def resetDisplayLimits(self):
self.fixedValues = False
@@ -279,12 +328,18 @@ def getXPosition(self, d: Datapoint) -> int:
if self.logarithmicX:
span = math.log(self.fstop) - math.log(self.fstart)
return self.leftMargin + round(
- self.chartWidth * (math.log(d.freq) -
+ self.dim.width * (math.log(d.freq) -
math.log(self.fstart)) / span)
return self.leftMargin + round(
- self.chartWidth * (d.freq - self.fstart) / span)
+ self.dim.width * (d.freq - self.fstart) / span)
return math.floor(self.width()/2)
+ def getYPosition(self, d: Datapoint) -> int:
+ return (
+ self.topMargin +
+ round((self.maxValue - d.capacitiveEquivalent()) /
+ self.span * self.dim.height))
+
def frequencyAtPosition(self, x, limit=True) -> int:
"""
Calculates the frequency at a given X-position
@@ -300,14 +355,14 @@ def frequencyAtPosition(self, x, limit=True) -> int:
absx = x - self.leftMargin
if limit and absx < 0:
return self.fstart
- if limit and absx > self.chartWidth:
+ if limit and absx > self.dim.width:
return self.fstop
if self.logarithmicX:
span = math.log(self.fstop) - math.log(self.fstart)
- step = span/self.chartWidth
+ step = span/self.dim.width
return round(math.exp(math.log(self.fstart) + absx * step))
span = self.fstop - self.fstart
- step = span/self.chartWidth
+ step = span/self.dim.width
return round(self.fstart + absx * step)
return -1
@@ -322,10 +377,13 @@ def valueAtPosition(self, _) -> List[float]:
is above or below the chart, returns maximum
or minimum values.
"""
- return []
+ absy = y - self.topMargin
+ val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
+ return [val * 10e11]
def wheelEvent(self, a0: QtGui.QWheelEvent) -> None:
- if len(self.data) == 0 and len(self.reference) == 0:
+ if ((len(self.data) == 0 and len(self.reference) == 0) or
+ a0.angleDelta().y() == 0):
a0.ignore()
return
do_zoom_x = do_zoom_y = True
@@ -333,54 +391,28 @@ def wheelEvent(self, a0: QtGui.QWheelEvent) -> None:
do_zoom_x = False
if a0.modifiers() == QtCore.Qt.ControlModifier:
do_zoom_y = False
- if a0.angleDelta().y() > 0:
- # Zoom in
- a0.accept()
- # Center of zoom = a0.x(), a0.y()
- # We zoom in by 1/10 of the width/height.
- rate = a0.angleDelta().y() / 120
- if do_zoom_x:
- zoomx = rate * self.chartWidth / 10
- else:
- zoomx = 0
- if do_zoom_y:
- zoomy = rate * self.chartHeight / 10
- else:
- zoomy = 0
- absx = max(0, a0.x() - self.leftMargin)
- absy = max(0, a0.y() - self.topMargin)
- ratiox = absx/self.chartWidth
- ratioy = absy/self.chartHeight
- p1x = int(self.leftMargin + ratiox * zoomx)
- p1y = int(self.topMargin + ratioy * zoomy)
- p2x = int(self.leftMargin + self.chartWidth - (1 - ratiox) * zoomx)
- p2y = int(self.topMargin + self.chartHeight - (1 - ratioy) * zoomy)
- self.zoomTo(p1x, p1y, p2x, p2y)
- elif a0.angleDelta().y() < 0:
- # Zoom out
- a0.accept()
- # Center of zoom = a0.x(), a0.y()
- # We zoom out by 1/9 of the width/height, to match zoom in.
- rate = -a0.angleDelta().y() / 120
- if do_zoom_x:
- zoomx = rate * self.chartWidth / 9
- else:
- zoomx = 0
- if do_zoom_y:
- zoomy = rate * self.chartHeight / 9
- else:
- zoomy = 0
- absx = max(0, a0.x() - self.leftMargin)
- absy = max(0, a0.y() - self.topMargin)
- ratiox = absx/self.chartWidth
- ratioy = absy/self.chartHeight
- p1x = int(self.leftMargin - ratiox * zoomx)
- p1y = int(self.topMargin - ratioy * zoomy)
- p2x = int(self.leftMargin + self.chartWidth + (1 - ratiox) * zoomx)
- p2y = int(self.topMargin + self.chartHeight + (1 - ratioy) * zoomy)
- self.zoomTo(p1x, p1y, p2x, p2y)
- else:
- a0.ignore()
+ self._wheel_zomm(
+ a0, do_zoom_x, do_zoom_y,
+ math.copysign(1, a0.angleDelta().y()))
+
+
+ def _wheel_zomm(self, a0, do_zoom_x, do_zoom_y, sign: int=1):
+ # Zoom in
+ a0.accept()
+ # Center of zoom = a0.x(), a0.y()
+ # We zoom in by 1/10 of the width/height.
+ rate = sign * a0.angleDelta().y() / 120
+ zoomx = rate * self.dim.width / 10 if do_zoom_x else 0
+ zoomy = rate * self.dim.height / 10 if do_zoom_y else 0
+ absx = max(0, a0.x() - self.leftMargin)
+ absy = max(0, a0.y() - self.topMargin)
+ ratiox = absx/self.dim.width
+ ratioy = absy/self.dim.height
+ p1x = int(self.leftMargin + ratiox * zoomx)
+ p1y = int(self.topMargin + ratioy * zoomy)
+ p2x = int(self.leftMargin + self.dim.width - (1 - ratiox) * zoomx)
+ p2y = int(self.topMargin + self.dim.height - (1 - ratioy) * zoomy)
+ self.zoomTo(p1x, p1y, p2x, p2y)
def zoomTo(self, x1, y1, x2, y2):
val1 = self.valueAtPosition(y1)
@@ -408,21 +440,21 @@ def mouseMoveEvent(self, a0: QtGui.QMouseEvent):
if a0.buttons() == QtCore.Qt.MiddleButton:
# Drag the display
a0.accept()
- if self.moveStartX != -1 and self.moveStartY != -1:
- dx = self.moveStartX - a0.x()
- dy = self.moveStartY - a0.y()
+ if self.dragbox.move_x != -1 and self.dragbox.move_y != -1:
+ dx = self.dragbox.move_x - a0.x()
+ dy = self.dragbox.move_y - a0.y()
self.zoomTo(self.leftMargin + dx, self.topMargin + dy,
- self.leftMargin + self.chartWidth + dx,
- self.topMargin + self.chartHeight + dy)
+ self.leftMargin + self.dim.width + dx,
+ self.topMargin + self.dim.height + dy)
- self.moveStartX = a0.x()
- self.moveStartY = a0.y()
+ self.dragbox.move_x = a0.x()
+ self.dragbox.move_y = a0.y()
return
if a0.modifiers() == QtCore.Qt.ControlModifier:
# Dragging a box
- if not self.draggedBox:
- self.draggedBoxStart = (a0.x(), a0.y())
- self.draggedBoxCurrent = (a0.x(), a0.y())
+ if not self.dragbox.state:
+ self.dragbox.pos_start = (a0.x(), a0.y())
+ self.dragbox.pos = (a0.x(), a0.y())
self.update()
a0.accept()
return
@@ -438,14 +470,20 @@ def mouseMoveEvent(self, a0: QtGui.QMouseEvent):
m.frequencyInput.setText(str(f))
def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
- self.chartWidth = a0.size().width()-self.rightMargin-self.leftMargin
- self.chartHeight = a0.size().height() - self.bottomMargin - self.topMargin
+ self.dim.width = a0.size().width()-self.rightMargin-self.leftMargin
+ self.dim.height = a0.size().height() - self.bottomMargin - self.topMargin
self.update()
def paintEvent(self, _: QtGui.QPaintEvent) -> None:
qp = QtGui.QPainter(self)
self.drawChart(qp)
self.drawValues(qp)
+ self._check_frequency_boundaries(qp)
+ if self.dragbox.state and self.dragbox.pos[0] != -1:
+ self.drawDragbog(qp)
+ qp.end()
+
+ def _check_frequency_boundaries(self, qp: QtGui.QPainter):
if (len(self.data) > 0 and
(self.data[0].freq > self.fstop or
self.data[len(self.data)-1].freq < self.fstart)
@@ -455,50 +493,124 @@ def paintEvent(self, _: QtGui.QPaintEvent) -> None:
self.reference[len(self.reference)-1].freq < self.fstart)):
# Data outside frequency range
qp.setBackgroundMode(QtCore.Qt.OpaqueMode)
- qp.setBackground(self.backgroundColor)
- qp.setPen(self.textColor)
- qp.drawText(self.leftMargin + self.chartWidth/2 - 70,
- self.topMargin + self.chartHeight/2 - 20,
+ qp.setBackground(Chart.color.background)
+ qp.setPen(Chart.color.text)
+ qp.drawText(self.leftMargin + self.dim.width/2 - 70,
+ self.topMargin + self.dim.height/2 - 20,
"Data outside frequency span")
- if self.draggedBox and self.draggedBoxCurrent[0] != -1:
- dashed_pen = QtGui.QPen(self.foregroundColor, 1, QtCore.Qt.DashLine)
- qp.setPen(dashed_pen)
- top_left = QtCore.QPoint(self.draggedBoxStart[0], self.draggedBoxStart[1])
- bottom_right = QtCore.QPoint(self.draggedBoxCurrent[0], self.draggedBoxCurrent[1])
- rect = QtCore.QRect(top_left, bottom_right)
- qp.drawRect(rect)
- qp.end()
+
+ def drawDragbog(self, qp: QtGui.QPainter):
+ dashed_pen = QtGui.QPen(Chart.color.foreground, 1, QtCore.Qt.DashLine)
+ qp.setPen(dashed_pen)
+ top_left = QtCore.QPoint(self.dragbox.pos_start[0], self.dragbox.pos_start[1])
+ bottom_right = QtCore.QPoint(self.dragbox.pos[0], self.dragbox.pos[1])
+ rect = QtCore.QRect(top_left, bottom_right)
+ qp.drawRect(rect)
def drawChart(self, qp: QtGui.QPainter):
- qp.setPen(QtGui.QPen(self.textColor))
- qp.drawText(3, 15, self.name)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin, self.topMargin - 5,
- self.leftMargin, self.topMargin + self.chartHeight + 5)
- qp.drawLine(self.leftMargin-5, self.topMargin + self.chartHeight,
- self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight)
+ qp.setPen(QtGui.QPen(Chart.color.text))
+ headline = self.name
+ if self.name_unit:
+ headline += f" ({self.name_unit})"
+ qp.drawText(3, 15, headline)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin, 20,
+ self.leftMargin, self.topMargin+self.dim.height+5)
+ qp.drawLine(self.leftMargin-5, self.topMargin+self.dim.height,
+ self.leftMargin+self.dim.width, self.topMargin + self.dim.height)
self.drawTitle(qp)
+ def drawValues(self, qp: QtGui.QPainter):
+ if len(self.data) == 0 and len(self.reference) == 0:
+ return
+ pen = QtGui.QPen(Chart.color.sweep)
+ pen.setWidth(self.dim.point)
+ line_pen = QtGui.QPen(Chart.color.sweep)
+ line_pen.setWidth(self.dim.line)
+ highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
+ highlighter.setWidth(1)
+
+ self._set_start_stop()
+
+ # Draw bands if required
+ if self.bands.enabled:
+ self.drawBands(qp, self.fstart, self.fstop)
+
+ min_value, max_value = self._find_scaling()
+ self.maxValue = max_value
+ self.minValue = min_value
+ span = max_value - min_value
+ if span == 0:
+ logger.info("Span is zero for %s-Chart, setting to a small value.", self.name)
+ span = 1e-15
+ self.span = span
+
+ target_ticks = math.floor(self.dim.height / 60)
+ fmt = Format(max_nr_digits=1)
+ for i in range(target_ticks):
+ val = min_value + (i / target_ticks) * span
+ y = self.topMargin + round((self.maxValue - val) / self.span * self.dim.height)
+ qp.setPen(Chart.color.text)
+ if val != min_value:
+ valstr = str(Value(val, fmt=fmt))
+ qp.drawText(3, y + 3, valstr)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.dim.width, y)
+
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin - 5, self.topMargin,
+ self.leftMargin + self.dim.width, self.topMargin)
+ qp.setPen(Chart.color.text)
+ qp.drawText(3, self.topMargin + 4, str(Value(max_value, fmt=fmt)))
+ qp.drawText(3, self.dim.height+self.topMargin, str(Value(min_value, fmt=fmt)))
+ self.drawFrequencyTicks(qp)
+
+ self.drawData(qp, self.data, Chart.color.sweep)
+ self.drawData(qp, self.reference, Chart.color.reference)
+ self.drawMarkers(qp)
+
+ def _find_scaling(self) -> Tuple[int, int]:
+ if self.fixedValues:
+ return (self.minDisplayValue / 10e11,
+ self.maxDisplayValue / 10e11)
+ min_value = 1
+ max_value = -1
+ for d in self.data:
+ val = self.value_function(d)
+ if val > max_value:
+ max_value = val
+ if val < min_value:
+ min_value = val
+ for d in self.reference: # Also check min/max for the reference sweep
+ if d.freq < self.fstart or d.freq > self.fstop:
+ continue
+ val = self.value_function(d)
+ if val > max_value:
+ max_value = val
+ if val < min_value:
+ min_value = val
+ return (min_value, max_value)
+
def drawFrequencyTicks(self, qp):
fspan = self.fstop - self.fstart
- qp.setPen(self.textColor)
+ qp.setPen(Chart.color.text)
qp.drawText(self.leftMargin - 20,
- self.topMargin + self.chartHeight + 15,
- Chart.shortenFrequency(self.fstart))
- ticks = math.floor(self.chartWidth / 100) # Number of ticks does not include the origin
+ self.topMargin + self.dim.height + 15,
+ format_frequency_chart(self.fstart))
+ ticks = math.floor(self.dim.width / 100) # Number of ticks does not include the origin
for i in range(ticks):
- x = self.leftMargin + round((i + 1) * self.chartWidth / ticks)
+ x = self.leftMargin + round((i + 1) * self.dim.width / ticks)
if self.logarithmicX:
fspan = math.log(self.fstop) - math.log(self.fstart)
freq = round(math.exp(((i + 1) * fspan / ticks) + math.log(self.fstart)))
else:
freq = round(fspan / ticks * (i + 1) + self.fstart)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(x, self.topMargin, x, self.topMargin + self.chartHeight + 5)
- qp.setPen(self.textColor)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(x, self.topMargin, x, self.topMargin + self.dim.height + 5)
+ qp.setPen(Chart.color.text)
qp.drawText(x - 20,
- self.topMargin + self.chartHeight + 15,
- Chart.shortenFrequency(freq))
+ self.topMargin + self.dim.height + 15,
+ format_frequency_chart(freq))
def drawBands(self, qp, fstart, fstop):
qp.setBrush(self.bands.color)
@@ -514,21 +626,21 @@ def drawBands(self, qp, fstart, fstop):
continue
x_start = max(self.leftMargin + 1,
self.getXPosition(Datapoint(start, 0, 0)))
- x_stop = min(self.leftMargin + self.chartWidth,
+ x_stop = min(self.leftMargin + self.dim.width,
self.getXPosition(Datapoint(end, 0, 0)))
qp.drawRect(x_start,
self.topMargin,
x_stop - x_start,
- self.chartHeight)
+ self.dim.height)
def drawData(self, qp: QtGui.QPainter, data: List[Datapoint],
color: QtGui.QColor, y_function=None):
if y_function is None:
y_function = self.getYPosition
pen = QtGui.QPen(color)
- pen.setWidth(self.pointSize)
+ pen.setWidth(self.dim.point)
line_pen = QtGui.QPen(color)
- line_pen.setWidth(self.lineThickness)
+ line_pen.setWidth(self.dim.line)
qp.setPen(pen)
for i, d in enumerate(data):
x = self.getXPosition(d)
@@ -537,7 +649,7 @@ def drawData(self, qp: QtGui.QPainter, data: List[Datapoint],
continue
if self.isPlotable(x, y):
qp.drawPoint(int(x), int(y))
- if self.drawLines and i > 0:
+ if self.flag.draw_lines and i > 0:
prevx = self.getXPosition(data[i - 1])
prevy = y_function(data[i - 1])
if prevy is None:
@@ -569,8 +681,8 @@ def drawMarkers(self, qp, data=None, y_function=None):
def isPlotable(self, x, y):
return y is not None and x is not None and \
- self.leftMargin <= x <= self.leftMargin + self.chartWidth and \
- self.topMargin <= y <= self.topMargin + self.chartHeight
+ self.leftMargin <= x <= self.leftMargin + self.dim.width and \
+ self.topMargin <= y <= self.topMargin + self.dim.height
def getPlotable(self, x, y, distantx, distanty):
p1 = np.array([x, y])
@@ -578,10 +690,10 @@ def getPlotable(self, x, y, distantx, distanty):
# First check the top line
if distanty < self.topMargin:
p3 = np.array([self.leftMargin, self.topMargin])
- p4 = np.array([self.leftMargin + self.chartWidth, self.topMargin])
- elif distanty > self.topMargin + self.chartHeight:
- p3 = np.array([self.leftMargin, self.topMargin + self.chartHeight])
- p4 = np.array([self.leftMargin + self.chartWidth, self.topMargin + self.chartHeight])
+ p4 = np.array([self.leftMargin + self.dim.width, self.topMargin])
+ elif distanty > self.topMargin + self.dim.height:
+ p3 = np.array([self.leftMargin, self.topMargin + self.dim.height])
+ p4 = np.array([self.leftMargin + self.dim.width, self.topMargin + self.dim.height])
else:
return x, y
da = p2 - p1
@@ -596,15 +708,16 @@ def getPlotable(self, x, y, distantx, distanty):
return x, y
def copy(self):
- new_chart: FrequencyChart = super().copy()
+ new_chart = super().copy()
new_chart.fstart = self.fstart
new_chart.fstop = self.fstop
new_chart.maxFrequency = self.maxFrequency
new_chart.minFrequency = self.minFrequency
+ new_chart.span = self.span
new_chart.minDisplayValue = self.minDisplayValue
new_chart.maxDisplayValue = self.maxDisplayValue
- new_chart.pointSize = self.pointSize
- new_chart.lineThickness = self.lineThickness
+ new_chart.pointSize = self.dim.point
+ new_chart.lineThickness = self.dim.line
new_chart.setFixedSpan(self.fixedSpan)
new_chart.action_automatic.setChecked(not self.fixedSpan)
@@ -627,10 +740,10 @@ def copy(self):
def keyPressEvent(self, a0: QtGui.QKeyEvent) -> None:
m = self.getActiveMarker()
if m is not None and a0.modifiers() == QtCore.Qt.NoModifier:
- if a0.key() == QtCore.Qt.Key_Down or a0.key() == QtCore.Qt.Key_Left:
+ if a0.key() in [QtCore.Qt.Key_Down, QtCore.Qt.Key_Left]:
m.frequencyInput.keyPressEvent(QtGui.QKeyEvent(
a0.type(), QtCore.Qt.Key_Down, a0.modifiers()))
- elif a0.key() == QtCore.Qt.Key_Up or a0.key() == QtCore.Qt.Key_Right:
+ elif a0.key() in [QtCore.Qt.Key_Up, QtCore.Qt.Key_Right]:
m.frequencyInput.keyPressEvent(QtGui.QKeyEvent(
a0.type(), QtCore.Qt.Key_Up, a0.modifiers()))
else:
diff --git a/NanoVNASaver/Charts/GroupDelay.py b/NanoVNASaver/Charts/GroupDelay.py
index f0ca4aaa..ac44a794 100644
--- a/NanoVNASaver/Charts/GroupDelay.py
+++ b/NanoVNASaver/Charts/GroupDelay.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,8 +22,9 @@
import numpy as np
-from PyQt5 import QtWidgets, QtGui
+from PyQt5 import QtGui
+from NanoVNASaver.Charts.Chart import Chart
from NanoVNASaver.RFTools import Datapoint
from .Frequency import FrequencyChart
logger = logging.getLogger(__name__)
@@ -32,9 +33,12 @@
class GroupDelayChart(FrequencyChart):
def __init__(self, name="", reflective=True):
super().__init__(name)
+
+ self.name_unit = "ns"
+
self.leftMargin = 40
- self.chartWidth = 250
- self.chartHeight = 250
+ self.dim.width = 250
+ self.dim.height = 250
self.fstart = 0
self.fstop = 0
self.minDelay = 0
@@ -49,15 +53,6 @@ def __init__(self, name="", reflective=True):
self.minDisplayValue = -180
self.maxDisplayValue = 180
- self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin,
- self.chartHeight + self.topMargin + self.bottomMargin)
- self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
- QtWidgets.QSizePolicy.MinimumExpanding))
- pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
- self.setPalette(pal)
- self.setAutoFillBackground(True)
-
def copy(self):
new_chart: GroupDelayChart = super().copy()
new_chart.reflective = self.reflective
@@ -125,22 +120,13 @@ def calculateGroupDelay(self):
self.update()
- def drawChart(self, qp: QtGui.QPainter):
- qp.setPen(QtGui.QPen(self.textColor))
- qp.drawText(3, 15, self.name + " (ns)")
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.chartHeight+5)
- qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight,
- self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight)
- self.drawTitle(qp)
-
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
- line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(self.lineThickness)
+ pen = QtGui.QPen(Chart.color.sweep)
+ pen.setWidth(self.dim.point)
+ line_pen = QtGui.QPen(Chart.color.sweep)
+ line_pen.setWidth(self.dim.line)
if self.fixedValues:
min_delay = self.minDisplayValue
@@ -159,13 +145,13 @@ def drawValues(self, qp: QtGui.QPainter):
self.maxDelay = max_delay
self.span = span
- tickcount = math.floor(self.chartHeight / 60)
+ tickcount = math.floor(self.dim.height / 60)
for i in range(tickcount):
delay = min_delay + span * i / tickcount
- y = self.topMargin + round((self.maxDelay - delay) / self.span * self.chartHeight)
+ y = self.topMargin + round((self.maxDelay - delay) / self.span * self.dim.height)
if delay != min_delay and delay != max_delay:
- qp.setPen(QtGui.QPen(self.textColor))
+ qp.setPen(QtGui.QPen(Chart.color.text))
if delay != 0:
digits = max(0, min(2, math.floor(3 - math.log10(abs(delay)))))
if digits == 0:
@@ -175,47 +161,36 @@ def drawValues(self, qp: QtGui.QPainter):
else:
delaystr = "0"
qp.drawText(3, y + 3, delaystr)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.chartWidth, y)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.dim.width, y)
qp.drawLine(self.leftMargin - 5,
self.topMargin,
- self.leftMargin + self.chartWidth,
+ self.leftMargin + self.dim.width,
self.topMargin)
- qp.setPen(self.textColor)
+ qp.setPen(Chart.color.text)
qp.drawText(3, self.topMargin + 5, str(max_delay))
- qp.drawText(3, self.chartHeight + self.topMargin, str(min_delay))
+ qp.drawText(3, self.dim.height + self.topMargin, str(min_delay))
- if self.fixedSpan:
- fstart = self.minFrequency
- fstop = self.maxFrequency
- else:
- if len(self.data) > 0:
- fstart = self.data[0].freq
- fstop = self.data[len(self.data)-1].freq
- else:
- fstart = self.reference[0].freq
- fstop = self.reference[len(self.reference) - 1].freq
- self.fstart = fstart
- self.fstop = fstop
+ self._set_start_stop()
# Draw bands if required
if self.bands.enabled:
- self.drawBands(qp, fstart, fstop)
+ self.drawBands(qp, self.fstart, self.fstop)
self.drawFrequencyTicks(qp)
- color = self.sweepColor
+ color = Chart.color.sweep
pen = QtGui.QPen(color)
- pen.setWidth(self.pointSize)
+ pen.setWidth(self.dim.point)
line_pen = QtGui.QPen(color)
- line_pen.setWidth(self.lineThickness)
+ line_pen.setWidth(self.dim.line)
qp.setPen(pen)
for i in range(len(self.data)):
x = self.getXPosition(self.data[i])
y = self.getYPositionFromDelay(self.groupDelay[i])
if self.isPlotable(x, y):
qp.drawPoint(int(x), int(y))
- if self.drawLines and i > 0:
+ if self.flag.draw_lines and i > 0:
prevx = self.getXPosition(self.data[i - 1])
prevy = self.getYPositionFromDelay(self.groupDelay[i - 1])
qp.setPen(line_pen)
@@ -229,18 +204,18 @@ def drawValues(self, qp: QtGui.QPainter):
qp.drawLine(prevx, prevy, new_x, new_y)
qp.setPen(pen)
- color = self.referenceColor
+ color = Chart.color.reference
pen = QtGui.QPen(color)
- pen.setWidth(self.pointSize)
+ pen.setWidth(self.dim.point)
line_pen = QtGui.QPen(color)
- line_pen.setWidth(self.lineThickness)
+ line_pen.setWidth(self.dim.line)
qp.setPen(pen)
for i in range(len(self.reference)):
x = self.getXPosition(self.reference[i])
y = self.getYPositionFromDelay(self.groupDelayReference[i])
if self.isPlotable(x, y):
qp.drawPoint(int(x), int(y))
- if self.drawLines and i > 0:
+ if self.flag.draw_lines and i > 0:
prevx = self.getXPosition(self.reference[i - 1])
prevy = self.getYPositionFromDelay(self.groupDelayReference[i - 1])
qp.setPen(line_pen)
@@ -267,9 +242,9 @@ def getYPosition(self, d: Datapoint) -> int:
return self.getYPositionFromDelay(delay)
def getYPositionFromDelay(self, delay: float):
- return self.topMargin + round((self.maxDelay - delay) / self.span * self.chartHeight)
+ return self.topMargin + round((self.maxDelay - delay) / self.span * self.dim.height)
def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
- val = -1 * ((absy / self.chartHeight * self.span) - self.maxDelay)
+ val = -1 * ((absy / self.dim.height * self.span) - self.maxDelay)
return [val]
diff --git a/NanoVNASaver/Charts/Inductance.py b/NanoVNASaver/Charts/Inductance.py
index 7e912514..4b56c450 100644
--- a/NanoVNASaver/Charts/Inductance.py
+++ b/NanoVNASaver/Charts/Inductance.py
@@ -24,7 +24,8 @@
from NanoVNASaver.RFTools import Datapoint
from NanoVNASaver.SITools import Format, Value
-from .Frequency import FrequencyChart
+from NanoVNASaver.Charts.Chart import Chart
+from NanoVNASaver.Charts.Frequency import FrequencyChart
logger = logging.getLogger(__name__)
@@ -32,9 +33,9 @@
class InductanceChart(FrequencyChart):
def __init__(self, name=""):
super().__init__(name)
- self.leftMargin = 30
- self.chartWidth = 250
- self.chartHeight = 250
+ self.leftMargin = 45
+ self.dim.width = 250
+ self.dim.height = 250
self.minDisplayValue = 0
self.maxDisplayValue = 100
@@ -42,31 +43,31 @@ def __init__(self, name=""):
self.maxValue = 1
self.span = 1
- self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin,
- self.chartHeight + self.topMargin + self.bottomMargin)
+ self.setMinimumSize(self.dim.width + self.rightMargin + self.leftMargin,
+ self.dim.height + self.topMargin + self.bottomMargin)
self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
QtWidgets.QSizePolicy.MinimumExpanding))
pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
+ pal.setColor(QtGui.QPalette.Background, Chart.color.background)
self.setPalette(pal)
self.setAutoFillBackground(True)
def drawChart(self, qp: QtGui.QPainter):
- qp.setPen(QtGui.QPen(self.textColor))
+ qp.setPen(QtGui.QPen(Chart.color.text))
qp.drawText(3, 15, self.name + " (H)")
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.chartHeight+5)
- qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight,
- self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.dim.height+5)
+ qp.drawLine(self.leftMargin-5, self.topMargin+self.dim.height,
+ self.leftMargin+self.dim.width, self.topMargin + self.dim.height)
self.drawTitle(qp)
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
- line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(self.lineThickness)
+ pen = QtGui.QPen(Chart.color.sweep)
+ pen.setWidth(self.dim.point)
+ line_pen = QtGui.QPen(Chart.color.sweep)
+ line_pen.setWidth(self.dim.line)
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
highlighter.setWidth(1)
if not self.fixedSpan:
@@ -118,38 +119,38 @@ def drawValues(self, qp: QtGui.QPainter):
span = 1e-15
self.span = span
- target_ticks = math.floor(self.chartHeight / 60)
- fmt = Format(max_nr_digits=1)
+ target_ticks = math.floor(self.dim.height / 60)
+ fmt = Format(max_nr_digits=3)
for i in range(target_ticks):
val = minValue + (i / target_ticks) * span
- y = self.topMargin + round((self.maxValue - val) / self.span * self.chartHeight)
- qp.setPen(self.textColor)
+ y = self.topMargin + round((self.maxValue - val) / self.span * self.dim.height)
+ qp.setPen(Chart.color.text)
if val != minValue:
valstr = str(Value(val, fmt=fmt))
qp.drawText(3, y + 3, valstr)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.chartWidth, y)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.dim.width, y)
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5, self.topMargin,
- self.leftMargin + self.chartWidth, self.topMargin)
- qp.setPen(self.textColor)
+ self.leftMargin + self.dim.width, self.topMargin)
+ qp.setPen(Chart.color.text)
qp.drawText(3, self.topMargin + 4, str(Value(maxValue, fmt=fmt)))
- qp.drawText(3, self.chartHeight+self.topMargin, str(Value(minValue, fmt=fmt)))
+ qp.drawText(3, self.dim.height+self.topMargin, str(Value(minValue, fmt=fmt)))
self.drawFrequencyTicks(qp)
- self.drawData(qp, self.data, self.sweepColor)
- self.drawData(qp, self.reference, self.referenceColor)
+ self.drawData(qp, self.data, Chart.color.sweep)
+ self.drawData(qp, self.reference, Chart.color.reference)
self.drawMarkers(qp)
def getYPosition(self, d: Datapoint) -> int:
return (self.topMargin +
round((self.maxValue - d.inductiveEquivalent()) /
- self.span * self.chartHeight))
+ self.span * self.dim.height))
def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
- val = -1 * ((absy / self.chartHeight * self.span) - self.maxValue)
+ val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
return [val * 10e11]
def copy(self):
diff --git a/NanoVNASaver/Charts/LogMag.py b/NanoVNASaver/Charts/LogMag.py
index 32fce9d6..51f27e0c 100644
--- a/NanoVNASaver/Charts/LogMag.py
+++ b/NanoVNASaver/Charts/LogMag.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,10 +20,11 @@
import logging
from typing import List
-from PyQt5 import QtWidgets, QtGui
+from PyQt5 import QtGui
from NanoVNASaver.RFTools import Datapoint
-from .Frequency import FrequencyChart
+from NanoVNASaver.Charts.Chart import Chart
+from NanoVNASaver.Charts.Frequency import FrequencyChart
logger = logging.getLogger(__name__)
@@ -31,9 +32,9 @@
class LogMagChart(FrequencyChart):
def __init__(self, name=""):
super().__init__(name)
- self.leftMargin = 30
- self.chartWidth = 250
- self.chartHeight = 250
+
+ self.name_unit = "dB"
+
self.minDisplayValue = -80
self.maxDisplayValue = 10
@@ -43,50 +44,15 @@ def __init__(self, name=""):
self.isInverted = False
- self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin,
- self.chartHeight + self.topMargin + self.bottomMargin)
- self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
- QtWidgets.QSizePolicy.MinimumExpanding))
- pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
- self.setPalette(pal)
- self.setAutoFillBackground(True)
-
- def drawChart(self, qp: QtGui.QPainter):
- qp.setPen(QtGui.QPen(self.textColor))
- qp.drawText(3, 15, self.name + " (dB)")
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin, self.topMargin - 5,
- self.leftMargin, self.topMargin+self.chartHeight+5)
- qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight,
- self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight)
- self.drawTitle(qp)
-
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
- line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(self.lineThickness)
- highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
- highlighter.setWidth(1)
- if not self.fixedSpan:
- if len(self.data) > 0:
- fstart = self.data[0].freq
- fstop = self.data[len(self.data)-1].freq
- else:
- fstart = self.reference[0].freq
- fstop = self.reference[len(self.reference) - 1].freq
- self.fstart = fstart
- self.fstop = fstop
- else:
- fstart = self.fstart = self.minFrequency
- fstop = self.fstop = self.maxFrequency
+
+ self._set_start_stop()
# Draw bands if required
if self.bands.enabled:
- self.drawBands(qp, fstart, fstop)
+ self.drawBands(qp, self.fstart, self.fstop)
if self.fixedValues:
maxValue = self.maxDisplayValue
@@ -171,49 +137,49 @@ def drawValues(self, qp: QtGui.QPainter):
for i in range(tick_count):
db = first_tick + i * tick_step
- y = self.topMargin + round((maxValue - db)/span*self.chartHeight)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin-5, y, self.leftMargin+self.chartWidth, y)
+ y = self.topMargin + round((maxValue - db)/span*self.dim.height)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin-5, y, self.leftMargin+self.dim.width, y)
if db > minValue and db != maxValue:
- qp.setPen(QtGui.QPen(self.textColor))
+ qp.setPen(QtGui.QPen(Chart.color.text))
if tick_step < 1:
dbstr = str(round(db, 1))
else:
dbstr = str(db)
qp.drawText(3, y + 4, dbstr)
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5, self.topMargin,
- self.leftMargin + self.chartWidth, self.topMargin)
- qp.setPen(self.textColor)
+ self.leftMargin + self.dim.width, self.topMargin)
+ qp.setPen(Chart.color.text)
qp.drawText(3, self.topMargin + 4, str(maxValue))
- qp.drawText(3, self.chartHeight+self.topMargin, str(minValue))
+ qp.drawText(3, self.dim.height+self.topMargin, str(minValue))
self.drawFrequencyTicks(qp)
- qp.setPen(self.swrColor)
+ qp.setPen(Chart.color.swr)
for vswr in self.swrMarkers:
if vswr <= 1:
continue
logMag = 20 * math.log10((vswr-1)/(vswr+1))
if self.isInverted:
logMag = logMag * -1
- y = self.topMargin + round((self.maxValue - logMag) / self.span * self.chartHeight)
- qp.drawLine(self.leftMargin, y, self.leftMargin + self.chartWidth, y)
+ y = self.topMargin + round((self.maxValue - logMag) / self.span * self.dim.height)
+ qp.drawLine(self.leftMargin, y, self.leftMargin + self.dim.width, y)
qp.drawText(self.leftMargin + 3, y - 1, "VSWR: " + str(vswr))
- self.drawData(qp, self.data, self.sweepColor)
- self.drawData(qp, self.reference, self.referenceColor)
+ self.drawData(qp, self.data, Chart.color.sweep)
+ self.drawData(qp, self.reference, Chart.color.reference)
self.drawMarkers(qp)
def getYPosition(self, d: Datapoint) -> int:
logMag = self.logMag(d)
if math.isinf(logMag):
return None
- return self.topMargin + round((self.maxValue - logMag) / self.span * self.chartHeight)
+ return self.topMargin + round((self.maxValue - logMag) / self.span * self.dim.height)
def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
- val = -1 * ((absy / self.chartHeight * self.span) - self.maxValue)
+ val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
return [val]
def logMag(self, p: Datapoint) -> float:
diff --git a/NanoVNASaver/Charts/Magnitude.py b/NanoVNASaver/Charts/Magnitude.py
index 489fd8da..18be21c6 100644
--- a/NanoVNASaver/Charts/Magnitude.py
+++ b/NanoVNASaver/Charts/Magnitude.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,19 +20,18 @@
import logging
from typing import List
-from PyQt5 import QtWidgets, QtGui
+from PyQt5 import QtGui
from NanoVNASaver.RFTools import Datapoint
-from .Frequency import FrequencyChart
+from NanoVNASaver.Charts.Chart import Chart
+from NanoVNASaver.Charts.Frequency import FrequencyChart
logger = logging.getLogger(__name__)
class MagnitudeChart(FrequencyChart):
def __init__(self, name=""):
super().__init__(name)
- self.leftMargin = 30
- self.chartWidth = 250
- self.chartHeight = 250
+
self.minDisplayValue = 0
self.maxDisplayValue = 1
@@ -44,40 +43,15 @@ def __init__(self, name=""):
self.maxValue = 1
self.span = 1
- self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin,
- self.chartHeight + self.topMargin + self.bottomMargin)
- self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
- QtWidgets.QSizePolicy.MinimumExpanding))
- pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
- self.setPalette(pal)
- self.setAutoFillBackground(True)
-
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
- line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(self.lineThickness)
- highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
- highlighter.setWidth(1)
- if not self.fixedSpan:
- if len(self.data) > 0:
- fstart = self.data[0].freq
- fstop = self.data[len(self.data)-1].freq
- else:
- fstart = self.reference[0].freq
- fstop = self.reference[len(self.reference) - 1].freq
- self.fstart = fstart
- self.fstop = fstop
- else:
- fstart = self.fstart = self.minFrequency
- fstop = self.fstop = self.maxFrequency
+
+ self._set_start_stop()
# Draw bands if required
if self.bands.enabled:
- self.drawBands(qp, fstart, fstop)
+ self.drawBands(qp, self.fstart, self.fstop)
if self.fixedValues:
maxValue = self.maxDisplayValue
@@ -113,12 +87,12 @@ def drawValues(self, qp: QtGui.QPainter):
span = 0.01
self.span = span
- target_ticks = math.floor(self.chartHeight / 60)
+ target_ticks = math.floor(self.dim.height / 60)
for i in range(target_ticks):
val = minValue + i / target_ticks * span
- y = self.topMargin + round((self.maxValue - val) / self.span * self.chartHeight)
- qp.setPen(self.textColor)
+ y = self.topMargin + round((self.maxValue - val) / self.span * self.dim.height)
+ qp.setPen(Chart.color.text)
if val != minValue:
digits = max(0, min(2, math.floor(3 - math.log10(abs(val)))))
if digits == 0:
@@ -126,37 +100,37 @@ def drawValues(self, qp: QtGui.QPainter):
else:
vswrstr = str(round(val, digits))
qp.drawText(3, y + 3, vswrstr)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.chartWidth, y)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.dim.width, y)
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5, self.topMargin,
- self.leftMargin + self.chartWidth, self.topMargin)
- qp.setPen(self.textColor)
+ self.leftMargin + self.dim.width, self.topMargin)
+ qp.setPen(Chart.color.text)
qp.drawText(3, self.topMargin + 4, str(maxValue))
- qp.drawText(3, self.chartHeight+self.topMargin, str(minValue))
+ qp.drawText(3, self.dim.height+self.topMargin, str(minValue))
self.drawFrequencyTicks(qp)
- qp.setPen(self.swrColor)
+ qp.setPen(Chart.color.swr)
for vswr in self.swrMarkers:
if vswr <= 1:
continue
mag = (vswr-1)/(vswr+1)
- y = self.topMargin + round((self.maxValue - mag) / self.span * self.chartHeight)
- qp.drawLine(self.leftMargin, y, self.leftMargin + self.chartWidth, y)
+ y = self.topMargin + round((self.maxValue - mag) / self.span * self.dim.height)
+ qp.drawLine(self.leftMargin, y, self.leftMargin + self.dim.width, y)
qp.drawText(self.leftMargin + 3, y - 1, "VSWR: " + str(vswr))
- self.drawData(qp, self.data, self.sweepColor)
- self.drawData(qp, self.reference, self.referenceColor)
+ self.drawData(qp, self.data, Chart.color.sweep)
+ self.drawData(qp, self.reference, Chart.color.reference)
self.drawMarkers(qp)
def getYPosition(self, d: Datapoint) -> int:
mag = self.magnitude(d)
- return self.topMargin + round((self.maxValue - mag) / self.span * self.chartHeight)
+ return self.topMargin + round((self.maxValue - mag) / self.span * self.dim.height)
def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
- val = -1 * ((absy / self.chartHeight * self.span) - self.maxValue)
+ val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
return [val]
@staticmethod
diff --git a/NanoVNASaver/Charts/MagnitudeZ.py b/NanoVNASaver/Charts/MagnitudeZ.py
index 40390bb5..90a073a6 100644
--- a/NanoVNASaver/Charts/MagnitudeZ.py
+++ b/NanoVNASaver/Charts/MagnitudeZ.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,12 +20,13 @@
import logging
from typing import List
-from PyQt5 import QtWidgets, QtGui
+from PyQt5 import QtGui
from NanoVNASaver.RFTools import Datapoint
from NanoVNASaver.SITools import Format, Value
-from .Frequency import FrequencyChart
-from .LogMag import LogMagChart
+from NanoVNASaver.Charts.Chart import Chart
+from NanoVNASaver.Charts.Frequency import FrequencyChart
+from NanoVNASaver.Charts.LogMag import LogMagChart
logger = logging.getLogger(__name__)
@@ -34,9 +35,7 @@
class MagnitudeZChart(FrequencyChart):
def __init__(self, name=""):
super().__init__(name)
- self.leftMargin = 30
- self.chartWidth = 250
- self.chartHeight = 250
+
self.minDisplayValue = 0
self.maxDisplayValue = 100
@@ -44,40 +43,15 @@ def __init__(self, name=""):
self.maxValue = 1
self.span = 1
- self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin,
- self.chartHeight + self.topMargin + self.bottomMargin)
- self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
- QtWidgets.QSizePolicy.MinimumExpanding))
- pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
- self.setPalette(pal)
- self.setAutoFillBackground(True)
-
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
- line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(self.lineThickness)
- highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
- highlighter.setWidth(1)
- if not self.fixedSpan:
- if len(self.data) > 0:
- fstart = self.data[0].freq
- fstop = self.data[len(self.data)-1].freq
- else:
- fstart = self.reference[0].freq
- fstop = self.reference[len(self.reference) - 1].freq
- self.fstart = fstart
- self.fstop = fstop
- else:
- fstart = self.fstart = self.minFrequency
- fstop = self.fstop = self.maxFrequency
+
+ self._set_start_stop()
# Draw bands if required
if self.bands.enabled:
- self.drawBands(qp, fstart, fstop)
+ self.drawBands(qp, self.fstart, self.fstop)
if self.fixedValues:
maxValue = self.maxDisplayValue
@@ -124,46 +98,46 @@ def drawValues(self, qp: QtGui.QPainter):
self.span = span
# We want one horizontal tick per 50 pixels, at most
- horizontal_ticks = math.floor(self.chartHeight/50)
+ horizontal_ticks = math.floor(self.dim.height/50)
fmt = Format(max_nr_digits=4)
for i in range(horizontal_ticks):
- y = self.topMargin + round(i * self.chartHeight / horizontal_ticks)
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ y = self.topMargin + round(i * self.dim.height / horizontal_ticks)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5, y,
- self.leftMargin + self.chartWidth + 5, y)
- qp.setPen(QtGui.QPen(self.textColor))
+ self.leftMargin + self.dim.width + 5, y)
+ qp.setPen(QtGui.QPen(Chart.color.text))
val = Value(self.valueAtPosition(y)[0], fmt=fmt)
qp.drawText(3, y + 4, str(val))
qp.drawText(3,
- self.chartHeight + self.topMargin,
+ self.dim.height + self.topMargin,
str(Value(self.minValue, fmt=fmt)))
self.drawFrequencyTicks(qp)
- self.drawData(qp, self.data, self.sweepColor)
- self.drawData(qp, self.reference, self.referenceColor)
+ self.drawData(qp, self.data, Chart.color.sweep)
+ self.drawData(qp, self.reference, Chart.color.reference)
self.drawMarkers(qp)
def getYPosition(self, d: Datapoint) -> int:
mag = self.magnitude(d)
if self.logarithmicY and mag == 0:
- return self.topMargin - self.chartHeight
+ return self.topMargin - self.dim.height
if math.isfinite(mag):
if self.logarithmicY:
span = math.log(self.maxValue) - math.log(self.minValue)
- return self.topMargin + round((math.log(self.maxValue) - math.log(mag)) / span * self.chartHeight)
- return self.topMargin + round((self.maxValue - mag) / self.span * self.chartHeight)
- else:
- return self.topMargin
+ return self.topMargin + round(
+ (math.log(self.maxValue) - math.log(mag)) / span * self.dim.height)
+ return self.topMargin + round((self.maxValue - mag) / self.span * self.dim.height)
+ return self.topMargin
def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
if self.logarithmicY:
span = math.log(self.maxValue) - math.log(self.minValue)
- val = math.exp(math.log(self.maxValue) - absy * span / self.chartHeight)
+ val = math.exp(math.log(self.maxValue) - absy * span / self.dim.height)
else:
- val = self.maxValue - (absy / self.chartHeight * self.span)
+ val = self.maxValue - (absy / self.dim.height * self.span)
return [val]
@staticmethod
@@ -171,7 +145,7 @@ def magnitude(p: Datapoint) -> float:
return abs(p.impedance())
def logarithmicYAllowed(self) -> bool:
- return True;
+ return True
def copy(self):
new_chart: LogMagChart = super().copy()
diff --git a/NanoVNASaver/Charts/MagnitudeZSeries.py b/NanoVNASaver/Charts/MagnitudeZSeries.py
index 1d276caf..816ca839 100644
--- a/NanoVNASaver/Charts/MagnitudeZSeries.py
+++ b/NanoVNASaver/Charts/MagnitudeZSeries.py
@@ -3,7 +3,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -17,24 +17,17 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-import math
import logging
-from typing import List
-
-from PyQt5 import QtWidgets, QtGui
from NanoVNASaver.RFTools import Datapoint
-from .MagnitudeZ import MagnitudeZChart
+from NanoVNASaver.Charts.MagnitudeZ import MagnitudeZChart
logger = logging.getLogger(__name__)
class MagnitudeZSeriesChart(MagnitudeZChart):
- def __init__(self, name=""):
- super().__init__(name)
@staticmethod
def magnitude(p: Datapoint) -> float:
return abs(p.seriesImpedance())
-
diff --git a/NanoVNASaver/Charts/MagnitudeZShunt.py b/NanoVNASaver/Charts/MagnitudeZShunt.py
index 0bd4d057..3736e3db 100644
--- a/NanoVNASaver/Charts/MagnitudeZShunt.py
+++ b/NanoVNASaver/Charts/MagnitudeZShunt.py
@@ -3,7 +3,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -17,24 +17,16 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-import math
import logging
-from typing import List
-
-from PyQt5 import QtWidgets, QtGui
from NanoVNASaver.RFTools import Datapoint
from .MagnitudeZ import MagnitudeZChart
-
logger = logging.getLogger(__name__)
class MagnitudeZShuntChart(MagnitudeZChart):
- def __init__(self, name=""):
- super().__init__(name)
@staticmethod
def magnitude(p: Datapoint) -> float:
return abs(p.shuntImpedance())
-
diff --git a/NanoVNASaver/Charts/Permeability.py b/NanoVNASaver/Charts/Permeability.py
index 76607f28..d0b9d39c 100644
--- a/NanoVNASaver/Charts/Permeability.py
+++ b/NanoVNASaver/Charts/Permeability.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,12 +20,13 @@
import logging
from typing import List
-from PyQt5 import QtWidgets, QtGui
+from PyQt5 import QtGui
from NanoVNASaver.Marker import Marker
from NanoVNASaver.RFTools import Datapoint
from NanoVNASaver.SITools import Format, Value
-from .Frequency import FrequencyChart
+from NanoVNASaver.Charts.Chart import Chart
+from NanoVNASaver.Charts.Frequency import FrequencyChart
logger = logging.getLogger(__name__)
@@ -34,8 +35,8 @@ def __init__(self, name=""):
super().__init__(name)
self.leftMargin = 40
self.rightMargin = 30
- self.chartWidth = 230
- self.chartHeight = 250
+ self.dim.width = 230
+ self.dim.height = 250
self.fstart = 0
self.fstop = 0
self.span = 0.01
@@ -44,20 +45,6 @@ def __init__(self, name=""):
self.maxDisplayValue = 100
self.minDisplayValue = -100
- #
- # Set up size policy and palette
- #
-
- self.setMinimumSize(self.chartWidth + self.leftMargin +
- self.rightMargin, self.chartHeight + 40)
- self.setSizePolicy(QtWidgets.QSizePolicy(
- QtWidgets.QSizePolicy.MinimumExpanding,
- QtWidgets.QSizePolicy.MinimumExpanding))
- pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
- self.setPalette(pal)
- self.setAutoFillBackground(True)
-
def logarithmicYAllowed(self) -> bool:
return True;
@@ -66,42 +53,30 @@ def copy(self):
return new_chart
def drawChart(self, qp: QtGui.QPainter):
- qp.setPen(QtGui.QPen(self.textColor))
+ qp.setPen(QtGui.QPen(Chart.color.text))
qp.drawText(self.leftMargin + 5, 15, self.name + " (\N{MICRO SIGN}\N{OHM SIGN} / Hz)")
qp.drawText(10, 15, "R")
- qp.drawText(self.leftMargin + self.chartWidth + 10, 15, "X")
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ qp.drawText(self.leftMargin + self.dim.width + 10, 15, "X")
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin, self.topMargin - 5,
- self.leftMargin, self.topMargin + self.chartHeight + 5)
- qp.drawLine(self.leftMargin-5, self.topMargin + self.chartHeight,
- self.leftMargin + self.chartWidth + 5, self.topMargin + self.chartHeight)
+ self.leftMargin, self.topMargin + self.dim.height + 5)
+ qp.drawLine(self.leftMargin-5, self.topMargin + self.dim.height,
+ self.leftMargin + self.dim.width + 5, self.topMargin + self.dim.height)
self.drawTitle(qp)
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
- line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(self.lineThickness)
- highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
- highlighter.setWidth(1)
- if self.fixedSpan:
- fstart = self.minFrequency
- fstop = self.maxFrequency
- else:
- if len(self.data) > 0:
- fstart = self.data[0].freq
- fstop = self.data[len(self.data)-1].freq
- else:
- fstart = self.reference[0].freq
- fstop = self.reference[len(self.reference) - 1].freq
- self.fstart = fstart
- self.fstop = fstop
+ pen = QtGui.QPen(Chart.color.sweep)
+ pen.setWidth(self.dim.point)
+ line_pen = QtGui.QPen(Chart.color.sweep)
+ line_pen.setWidth(self.dim.line)
+
+ self._set_start_stop()
# Draw bands if required
if self.bands.enabled:
- self.drawBands(qp, fstart, fstop)
+ self.drawBands(qp, self.fstart, self.fstop)
# Find scaling
if self.fixedValues:
@@ -124,7 +99,7 @@ def drawValues(self, qp: QtGui.QPainter):
if im < min_val:
min_val = im
for d in self.reference: # Also check min/max for the reference sweep
- if d.freq < fstart or d.freq > fstop:
+ if d.freq < self.fstart or d.freq > self.fstop:
continue
imp = d.impedance()
re, im = imp.real, imp.imag
@@ -150,43 +125,43 @@ def drawValues(self, qp: QtGui.QPainter):
self.span = span
# We want one horizontal tick per 50 pixels, at most
- horizontal_ticks = math.floor(self.chartHeight/50)
+ horizontal_ticks = math.floor(self.dim.height/50)
fmt = Format(max_nr_digits=4)
for i in range(horizontal_ticks):
- y = self.topMargin + round(i * self.chartHeight / horizontal_ticks)
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ y = self.topMargin + round(i * self.dim.height / horizontal_ticks)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5, y,
- self.leftMargin + self.chartWidth + 5, y)
- qp.setPen(QtGui.QPen(self.textColor))
+ self.leftMargin + self.dim.width + 5, y)
+ qp.setPen(QtGui.QPen(Chart.color.text))
val = Value(self.valueAtPosition(y)[0], fmt=fmt)
qp.drawText(3, y + 4, str(val))
qp.drawText(3,
- self.chartHeight + self.topMargin,
+ self.dim.height + self.topMargin,
str(Value(min_val, fmt=fmt)))
self.drawFrequencyTicks(qp)
primary_pen = pen
- secondary_pen = QtGui.QPen(self.secondarySweepColor)
+ secondary_pen = QtGui.QPen(Chart.color.sweep_secondary)
if len(self.data) > 0:
- c = QtGui.QColor(self.sweepColor)
+ c = QtGui.QColor(Chart.color.sweep)
c.setAlpha(255)
pen = QtGui.QPen(c)
pen.setWidth(2)
qp.setPen(pen)
qp.drawLine(20, 9, 25, 9)
- c = QtGui.QColor(self.secondarySweepColor)
+ c = QtGui.QColor(Chart.color.sweep_secondary)
c.setAlpha(255)
pen.setColor(c)
qp.setPen(pen)
qp.drawLine(
- self.leftMargin + self.chartWidth, 9,
- self.leftMargin + self.chartWidth + 5, 9)
+ self.leftMargin + self.dim.width, 9,
+ self.leftMargin + self.dim.width + 5, 9)
- primary_pen.setWidth(self.pointSize)
- secondary_pen.setWidth(self.pointSize)
- line_pen.setWidth(self.lineThickness)
+ primary_pen.setWidth(self.dim.point)
+ secondary_pen.setWidth(self.dim.point)
+ line_pen.setWidth(self.dim.line)
for i in range(len(self.data)):
x = self.getXPosition(self.data[i])
@@ -198,13 +173,13 @@ def drawValues(self, qp: QtGui.QPainter):
qp.setPen(secondary_pen)
if self.isPlotable(x, y_im):
qp.drawPoint(x, y_im)
- if self.drawLines and i > 0:
+ if self.flag.draw_lines and i > 0:
prev_x = self.getXPosition(self.data[i - 1])
prev_y_re = self.getReYPosition(self.data[i-1])
prev_y_im = self.getImYPosition(self.data[i-1])
# Real part first
- line_pen.setColor(self.sweepColor)
+ line_pen.setColor(Chart.color.sweep)
qp.setPen(line_pen)
if self.isPlotable(x, y_re) and self.isPlotable(prev_x, prev_y_re):
qp.drawLine(x, y_re, prev_x, prev_y_re)
@@ -216,7 +191,7 @@ def drawValues(self, qp: QtGui.QPainter):
qp.drawLine(prev_x, prev_y_re, new_x, new_y)
# Imag part second
- line_pen.setColor(self.secondarySweepColor)
+ line_pen.setColor(Chart.color.sweep_secondary)
qp.setPen(line_pen)
if self.isPlotable(x, y_im) and self.isPlotable(prev_x, prev_y_im):
qp.drawLine(x, y_im, prev_x, prev_y_im)
@@ -227,27 +202,27 @@ def drawValues(self, qp: QtGui.QPainter):
new_x, new_y = self.getPlotable(prev_x, prev_y_im, x, y_im)
qp.drawLine(prev_x, prev_y_im, new_x, new_y)
- primary_pen.setColor(self.referenceColor)
- line_pen.setColor(self.referenceColor)
- secondary_pen.setColor(self.secondaryReferenceColor)
+ primary_pen.setColor(Chart.color.reference)
+ line_pen.setColor(Chart.color.reference)
+ secondary_pen.setColor(Chart.color.reference_secondary)
qp.setPen(primary_pen)
if len(self.reference) > 0:
- c = QtGui.QColor(self.referenceColor)
+ c = QtGui.QColor(Chart.color.reference)
c.setAlpha(255)
pen = QtGui.QPen(c)
pen.setWidth(2)
qp.setPen(pen)
qp.drawLine(20, 14, 25, 14)
- c = QtGui.QColor(self.secondaryReferenceColor)
+ c = QtGui.QColor(Chart.color.reference_secondary)
c.setAlpha(255)
pen = QtGui.QPen(c)
pen.setWidth(2)
qp.setPen(pen)
- qp.drawLine(self.leftMargin + self.chartWidth, 14,
- self.leftMargin + self.chartWidth + 5, 14)
+ qp.drawLine(self.leftMargin + self.dim.width, 14,
+ self.leftMargin + self.dim.width + 5, 14)
for i in range(len(self.reference)):
- if self.reference[i].freq < fstart or self.reference[i].freq > fstop:
+ if self.reference[i].freq < self.fstart or self.reference[i].freq > self.fstop:
continue
x = self.getXPosition(self.reference[i])
y_re = self.getReYPosition(self.reference[i])
@@ -258,12 +233,12 @@ def drawValues(self, qp: QtGui.QPainter):
qp.setPen(secondary_pen)
if self.isPlotable(x, y_im):
qp.drawPoint(x, y_im)
- if self.drawLines and i > 0:
+ if self.flag.draw_lines and i > 0:
prev_x = self.getXPosition(self.reference[i - 1])
prev_y_re = self.getReYPosition(self.reference[i-1])
prev_y_im = self.getImYPosition(self.reference[i-1])
- line_pen.setColor(self.referenceColor)
+ line_pen.setColor(Chart.color.reference)
qp.setPen(line_pen)
# Real part first
if self.isPlotable(x, y_re) and self.isPlotable(prev_x, prev_y_re):
@@ -275,7 +250,7 @@ def drawValues(self, qp: QtGui.QPainter):
new_x, new_y = self.getPlotable(prev_x, prev_y_re, x, y_re)
qp.drawLine(prev_x, prev_y_re, new_x, new_y)
- line_pen.setColor(self.secondaryReferenceColor)
+ line_pen.setColor(Chart.color.reference_secondary)
qp.setPen(line_pen)
# Imag part second
if self.isPlotable(x, y_im) and self.isPlotable(prev_x, prev_y_im):
@@ -308,9 +283,9 @@ def getImYPosition(self, d: Datapoint) -> int:
return -1
return self.topMargin + round(
(math.log(self.max) - math.log(im)) /
- span * self.chartHeight)
+ span * self.dim.height)
return self.topMargin + round(
- (self.max - im) / self.span * self.chartHeight)
+ (self.max - im) / self.span * self.dim.height)
def getReYPosition(self, d: Datapoint) -> int:
re = d.impedance().real
@@ -323,9 +298,9 @@ def getReYPosition(self, d: Datapoint) -> int:
return -1
return self.topMargin + round(
(math.log(self.max) - math.log(re)) /
- span * self.chartHeight)
+ span * self.dim.height)
return self.topMargin + round(
- (self.max - re) / self.span * self.chartHeight)
+ (self.max - re) / self.span * self.dim.height)
def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
@@ -333,12 +308,12 @@ def valueAtPosition(self, y) -> List[float]:
min_val = self.max - self.span
if self.max > 0 and min_val > 0:
span = math.log(self.max) - math.log(min_val)
- step = span / self.chartHeight
+ step = span / self.dim.height
val = math.exp(math.log(self.max) - absy * step)
else:
val = -1
else:
- val = -1 * ((absy / self.chartHeight * self.span) - self.max)
+ val = -1 * ((absy / self.dim.height * self.span) - self.max)
return [val]
def getNearestMarker(self, x, y) -> Marker:
diff --git a/NanoVNASaver/Charts/Phase.py b/NanoVNASaver/Charts/Phase.py
index 6fc39dfd..2f740b6e 100644
--- a/NanoVNASaver/Charts/Phase.py
+++ b/NanoVNASaver/Charts/Phase.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -25,7 +25,8 @@
from PyQt5 import QtWidgets, QtGui
from NanoVNASaver.RFTools import Datapoint
-from .Frequency import FrequencyChart
+from NanoVNASaver.Charts.Chart import Chart
+from NanoVNASaver.Charts.Frequency import FrequencyChart
logger = logging.getLogger(__name__)
@@ -33,11 +34,7 @@
class PhaseChart(FrequencyChart):
def __init__(self, name=""):
super().__init__(name)
- self.leftMargin = 40
- self.chartWidth = 250
- self.chartHeight = 250
- self.fstart = 0
- self.fstop = 0
+
self.minAngle = 0
self.maxAngle = 0
self.span = 0
@@ -49,15 +46,6 @@ def __init__(self, name=""):
self.minDisplayValue = -180
self.maxDisplayValue = 180
- self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin,
- self.chartHeight + self.topMargin + self.bottomMargin)
- self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
- QtWidgets.QSizePolicy.MinimumExpanding))
- pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
- self.setPalette(pal)
- self.setAutoFillBackground(True)
-
self.y_menu.addSeparator()
self.action_unwrap = QtWidgets.QAction("Unwrap")
self.action_unwrap.setCheckable(True)
@@ -65,7 +53,7 @@ def __init__(self, name=""):
self.y_menu.addAction(self.action_unwrap)
def copy(self):
- new_chart: PhaseChart = super().copy()
+ new_chart = super().copy()
new_chart.setUnwrap(self.unwrap)
new_chart.action_unwrap.setChecked(self.unwrap)
return new_chart
@@ -77,10 +65,6 @@ def setUnwrap(self, unwrap: bool):
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
- line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(self.lineThickness)
if self.unwrap:
rawData = []
@@ -114,13 +98,13 @@ def drawValues(self, qp: QtGui.QPainter):
self.maxAngle = maxAngle
self.span = span
- tickcount = math.floor(self.chartHeight / 60)
+ tickcount = math.floor(self.dim.height / 60)
for i in range(tickcount):
angle = minAngle + span * i / tickcount
- y = self.topMargin + round((self.maxAngle - angle) / self.span * self.chartHeight)
+ y = self.topMargin + round((self.maxAngle - angle) / self.span * self.dim.height)
if angle != minAngle and angle != maxAngle:
- qp.setPen(QtGui.QPen(self.textColor))
+ qp.setPen(QtGui.QPen(Chart.color.text))
if angle != 0:
digits = max(0, min(2, math.floor(3 - math.log10(abs(angle)))))
if digits == 0:
@@ -130,37 +114,25 @@ def drawValues(self, qp: QtGui.QPainter):
else:
anglestr = "0"
qp.drawText(3, y + 3, anglestr + "°")
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.chartWidth, y)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.dim.width, y)
qp.drawLine(self.leftMargin - 5,
self.topMargin,
- self.leftMargin + self.chartWidth,
+ self.leftMargin + self.dim.width,
self.topMargin)
- qp.setPen(self.textColor)
+ qp.setPen(Chart.color.text)
qp.drawText(3, self.topMargin + 5, str(maxAngle) + "°")
- qp.drawText(3, self.chartHeight + self.topMargin, str(minAngle) + "°")
+ qp.drawText(3, self.dim.height + self.topMargin, str(minAngle) + "°")
- if self.fixedSpan:
- fstart = self.minFrequency
- fstop = self.maxFrequency
- else:
- if len(self.data) > 0:
- fstart = self.data[0].freq
- fstop = self.data[len(self.data)-1].freq
- else:
- fstart = self.reference[0].freq
- fstop = self.reference[len(self.reference) - 1].freq
- self.fstart = fstart
- self.fstop = fstop
+ self._set_start_stop()
# Draw bands if required
if self.bands.enabled:
- self.drawBands(qp, fstart, fstop)
+ self.drawBands(qp, self.fstart, self.fstop)
self.drawFrequencyTicks(qp)
-
- self.drawData(qp, self.data, self.sweepColor)
- self.drawData(qp, self.reference, self.referenceColor)
+ self.drawData(qp, self.data, Chart.color.sweep)
+ self.drawData(qp, self.reference, Chart.color.reference)
self.drawMarkers(qp)
def getYPosition(self, d: Datapoint) -> int:
@@ -173,9 +145,9 @@ def getYPosition(self, d: Datapoint) -> int:
angle = math.degrees(d.phase)
else:
angle = math.degrees(d.phase)
- return self.topMargin + round((self.maxAngle - angle) / self.span * self.chartHeight)
+ return self.topMargin + round((self.maxAngle - angle) / self.span * self.dim.height)
def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
- val = -1 * ((absy / self.chartHeight * self.span) - self.maxAngle)
+ val = -1 * ((absy / self.dim.height * self.span) - self.maxAngle)
return [val]
diff --git a/NanoVNASaver/Charts/Polar.py b/NanoVNASaver/Charts/Polar.py
index 06fa33df..8db1319c 100644
--- a/NanoVNASaver/Charts/Polar.py
+++ b/NanoVNASaver/Charts/Polar.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,7 +22,8 @@
from PyQt5 import QtGui, QtCore
from NanoVNASaver.RFTools import Datapoint
-from .Square import SquareChart
+from NanoVNASaver.Charts.Chart import Chart
+from NanoVNASaver.Charts.Square import SquareChart
logger = logging.getLogger(__name__)
@@ -30,16 +31,16 @@
class PolarChart(SquareChart):
def __init__(self, name=""):
super().__init__(name)
- self.chartWidth = 250
- self.chartHeight = 250
+ self.dim.width = 250
+ self.dim.height = 250
- self.setMinimumSize(self.chartWidth + 40, self.chartHeight + 40)
+ self.setMinimumSize(self.dim.width + 40, self.dim.height + 40)
pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
+ pal.setColor(QtGui.QPalette.Background, Chart.color.background)
self.setPalette(pal)
self.setAutoFillBackground(True)
- def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
+ def paintEvent(self, _: QtGui.QPaintEvent) -> None:
qp = QtGui.QPainter(self)
self.drawChart(qp)
self.drawValues(qp)
@@ -48,51 +49,49 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
def drawChart(self, qp: QtGui.QPainter):
centerX = int(self.width()/2)
centerY = int(self.height()/2)
- qp.setPen(QtGui.QPen(self.textColor))
+ qp.setPen(QtGui.QPen(Chart.color.text))
qp.drawText(3, 15, self.name)
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawEllipse(QtCore.QPoint(centerX, centerY),
- int(self.chartWidth / 2),
- int(self.chartHeight / 2))
+ int(self.dim.width / 2),
+ int(self.dim.height / 2))
qp.drawEllipse(QtCore.QPoint(centerX, centerY),
- int(self.chartWidth / 4),
- int(self.chartHeight / 4))
- qp.drawLine(centerX - int(self.chartWidth / 2), centerY,
- centerX + int(self.chartWidth / 2), centerY)
- qp.drawLine(centerX, centerY - int(self.chartHeight / 2),
- centerX, centerY + int(self.chartHeight / 2))
- qp.drawLine(centerX + int(self.chartHeight / 2 * math.sin(math.pi / 4)),
- centerY + int(self.chartHeight / 2 * math.sin(math.pi / 4)),
- centerX - int(self.chartHeight / 2 * math.sin(math.pi / 4)),
- centerY - int(self.chartHeight / 2 * math.sin(math.pi / 4)))
- qp.drawLine(centerX + int(self.chartHeight / 2 * math.sin(math.pi / 4)),
- centerY - int(self.chartHeight / 2 * math.sin(math.pi / 4)),
- centerX - int(self.chartHeight / 2 * math.sin(math.pi / 4)),
- centerY + int(self.chartHeight / 2 * math.sin(math.pi / 4)))
+ int(self.dim.width / 4),
+ int(self.dim.height / 4))
+ qp.drawLine(centerX - int(self.dim.width / 2), centerY,
+ centerX + int(self.dim.width / 2), centerY)
+ qp.drawLine(centerX, centerY - int(self.dim.height / 2),
+ centerX, centerY + int(self.dim.height / 2))
+ qp.drawLine(centerX + int(self.dim.height / 2 * math.sin(math.pi / 4)),
+ centerY + int(self.dim.height / 2 * math.sin(math.pi / 4)),
+ centerX - int(self.dim.height / 2 * math.sin(math.pi / 4)),
+ centerY - int(self.dim.height / 2 * math.sin(math.pi / 4)))
+ qp.drawLine(centerX + int(self.dim.height / 2 * math.sin(math.pi / 4)),
+ centerY - int(self.dim.height / 2 * math.sin(math.pi / 4)),
+ centerX - int(self.dim.height / 2 * math.sin(math.pi / 4)),
+ centerY + int(self.dim.height / 2 * math.sin(math.pi / 4)))
self.drawTitle(qp)
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
- line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(self.lineThickness)
- highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
- highlighter.setWidth(1)
+ pen = QtGui.QPen(Chart.color.sweep)
+ pen.setWidth(self.dim.point)
+ line_pen = QtGui.QPen(Chart.color.sweep)
+ line_pen.setWidth(self.dim.line)
qp.setPen(pen)
for i in range(len(self.data)):
x = self.getXPosition(self.data[i])
- y = self.height()/2 + self.data[i].im * -1 * self.chartHeight/2
+ y = self.height()/2 + self.data[i].im * -1 * self.dim.height/2
qp.drawPoint(int(x), int(y))
- if self.drawLines and i > 0:
+ if self.flag.draw_lines and i > 0:
prevx = self.getXPosition(self.data[i-1])
- prevy = self.height() / 2 + self.data[i-1].im * -1 * self.chartHeight / 2
+ prevy = self.height() / 2 + self.data[i-1].im * -1 * self.dim.height / 2
qp.setPen(line_pen)
qp.drawLine(x, y, prevx, prevy)
qp.setPen(pen)
- pen.setColor(self.referenceColor)
- line_pen.setColor(self.referenceColor)
+ pen.setColor(Chart.color.reference)
+ line_pen.setColor(Chart.color.reference)
qp.setPen(pen)
if len(self.data) > 0:
fstart = self.data[0].freq
@@ -105,11 +104,11 @@ def drawValues(self, qp: QtGui.QPainter):
if data.freq < fstart or data.freq > fstop:
continue
x = self.getXPosition(self.reference[i])
- y = self.height()/2 + data.im * -1 * self.chartHeight/2
+ y = self.height()/2 + data.im * -1 * self.dim.height/2
qp.drawPoint(int(x), int(y))
- if self.drawLines and i > 0:
+ if self.flag.draw_lines and i > 0:
prevx = self.getXPosition(self.reference[i-1])
- prevy = self.height() / 2 + self.reference[i-1].im * -1 * self.chartHeight / 2
+ prevy = self.height() / 2 + self.reference[i-1].im * -1 * self.dim.height / 2
qp.setPen(line_pen)
qp.drawLine(x, y, prevx, prevy)
qp.setPen(pen)
@@ -117,14 +116,14 @@ def drawValues(self, qp: QtGui.QPainter):
for m in self.markers:
if m.location != -1 and m.location < len(self.data):
x = self.getXPosition(self.data[m.location])
- y = self.height() / 2 + self.data[m.location].im * -1 * self.chartHeight / 2
+ y = self.height() / 2 + self.data[m.location].im * -1 * self.dim.height / 2
self.drawMarker(x, y, qp, m.color, self.markers.index(m)+1)
def getXPosition(self, d: Datapoint) -> int:
- return self.width()/2 + d.re * self.chartWidth/2
+ return self.width()/2 + d.re * self.dim.width/2
def getYPosition(self, d: Datapoint) -> int:
- return self.height()/2 + d.im * -1 * self.chartHeight/2
+ return self.height()/2 + d.im * -1 * self.dim.height/2
def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None:
if a0.buttons() == QtCore.Qt.RightButton:
@@ -132,9 +131,9 @@ def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None:
return
x = a0.x()
y = a0.y()
- absx = x - (self.width() - self.chartWidth) / 2
- absy = y - (self.height() - self.chartHeight) / 2
- if absx < 0 or absx > self.chartWidth or absy < 0 or absy > self.chartHeight \
+ absx = x - (self.width() - self.dim.width) / 2
+ absy = y - (self.height() - self.dim.height) / 2
+ if absx < 0 or absx > self.dim.width or absy < 0 or absy > self.dim.height \
or len(self.data) == len(self.reference) == 0:
a0.ignore()
return
@@ -146,8 +145,8 @@ def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None:
target = self.reference
positions = []
for d in target:
- thisx = self.width() / 2 + d.re * self.chartWidth / 2
- thisy = self.height() / 2 + d.im * -1 * self.chartHeight / 2
+ thisx = self.width() / 2 + d.re * self.dim.width / 2
+ thisy = self.height() / 2 + d.im * -1 * self.dim.height / 2
positions.append(math.sqrt((x - thisx)**2 + (y - thisy)**2))
minimum_position = positions.index(min(positions))
diff --git a/NanoVNASaver/Charts/QFactor.py b/NanoVNASaver/Charts/QFactor.py
index 2a1fd436..ed955464 100644
--- a/NanoVNASaver/Charts/QFactor.py
+++ b/NanoVNASaver/Charts/QFactor.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,10 +20,11 @@
import logging
from typing import List
-from PyQt5 import QtWidgets, QtGui
+from PyQt5 import QtGui
from NanoVNASaver.RFTools import Datapoint
-from .Frequency import FrequencyChart
+from NanoVNASaver.Charts.Chart import Chart
+from NanoVNASaver.Charts.Frequency import FrequencyChart
logger = logging.getLogger(__name__)
@@ -32,8 +33,8 @@ class QualityFactorChart(FrequencyChart):
def __init__(self, name=""):
super().__init__(name)
self.leftMargin = 35
- self.chartWidth = 250
- self.chartHeight = 250
+ self.dim.width = 250
+ self.dim.height = 250
self.fstart = 0
self.fstop = 0
self.minQ = 0
@@ -42,15 +43,6 @@ def __init__(self, name=""):
self.minDisplayValue = 0
self.maxDisplayValue = 100
- self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin,
- self.chartHeight + self.topMargin + self.bottomMargin)
- self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
- QtWidgets.QSizePolicy.MinimumExpanding))
- pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
- self.setPalette(pal)
- self.setAutoFillBackground(True)
-
def drawChart(self, qp: QtGui.QPainter):
super().drawChart(qp)
@@ -75,25 +67,25 @@ def drawChart(self, qp: QtGui.QPainter):
if self.span == 0:
return # No data to draw the graph from
- tickcount = math.floor(self.chartHeight / 60)
+ tickcount = math.floor(self.dim.height / 60)
for i in range(tickcount):
q = self.minQ + i * self.span / tickcount
- y = self.topMargin + round((self.maxQ - q) / self.span * self.chartHeight)
+ y = self.topMargin + round((self.maxQ - q) / self.span * self.dim.height)
if q < 10:
q = round(q, 2)
elif q < 20:
q = round(q, 1)
else:
q = round(q)
- qp.setPen(QtGui.QPen(self.textColor))
+ qp.setPen(QtGui.QPen(Chart.color.text))
qp.drawText(3, y+3, str(q))
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin-5, y, self.leftMargin + self.chartWidth, y)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin-5, y, self.leftMargin + self.dim.width, y)
qp.drawLine(self.leftMargin - 5,
self.topMargin,
- self.leftMargin + self.chartWidth, self.topMargin)
- qp.setPen(self.textColor)
+ self.leftMargin + self.dim.width, self.topMargin)
+ qp.setPen(Chart.color.text)
if maxQ < 10:
qstr = str(round(maxQ, 2))
elif maxQ < 20:
@@ -107,39 +99,29 @@ def drawValues(self, qp: QtGui.QPainter):
return
if self.span == 0:
return
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
- line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(self.lineThickness)
+ pen = QtGui.QPen(Chart.color.sweep)
+ pen.setWidth(self.dim.point)
+ line_pen = QtGui.QPen(Chart.color.sweep)
+ line_pen.setWidth(self.dim.line)
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
highlighter.setWidth(1)
- if self.fixedSpan:
- fstart = self.minFrequency
- fstop = self.maxFrequency
- else:
- if len(self.data) > 0:
- fstart = self.data[0].freq
- fstop = self.data[len(self.data)-1].freq
- else:
- fstart = self.reference[0].freq
- fstop = self.reference[len(self.reference) - 1].freq
- self.fstart = fstart
- self.fstop = fstop
+
+ self._set_start_stop()
# Draw bands if required
if self.bands.enabled:
- self.drawBands(qp, fstart, fstop)
+ self.drawBands(qp, self.fstart, self.fstop)
self.drawFrequencyTicks(qp)
- self.drawData(qp, self.data, self.sweepColor)
- self.drawData(qp, self.reference, self.referenceColor)
+ self.drawData(qp, self.data, Chart.color.sweep)
+ self.drawData(qp, self.reference, Chart.color.reference)
self.drawMarkers(qp)
def getYPosition(self, d: Datapoint) -> int:
Q = d.qFactor()
- return self.topMargin + round((self.maxQ - Q) / self.span * self.chartHeight)
+ return self.topMargin + round((self.maxQ - Q) / self.span * self.dim.height)
def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
- val = -1 * ((absy / self.chartHeight * self.span) - self.maxQ)
+ val = -1 * ((absy / self.dim.height * self.span) - self.maxQ)
return [val]
diff --git a/NanoVNASaver/Charts/RI.py b/NanoVNASaver/Charts/RI.py
index 9d5d2d4b..ebe479b2 100644
--- a/NanoVNASaver/Charts/RI.py
+++ b/NanoVNASaver/Charts/RI.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,12 +22,13 @@
from PyQt5 import QtWidgets, QtGui
+from NanoVNASaver.Formatting import format_frequency_chart
from NanoVNASaver.Marker import Marker
from NanoVNASaver.RFTools import Datapoint
from NanoVNASaver.SITools import Format, Value
-from .Chart import Chart
-from .Frequency import FrequencyChart
+from NanoVNASaver.Charts.Chart import Chart
+from NanoVNASaver.Charts.Frequency import FrequencyChart
logger = logging.getLogger(__name__)
@@ -37,8 +38,8 @@ def __init__(self, name=""):
super().__init__(name)
self.leftMargin = 45
self.rightMargin = 45
- self.chartWidth = 230
- self.chartHeight = 250
+ self.dim.width = 230
+ self.dim.height = 250
self.fstart = 0
self.fstop = 0
self.span_real = 0.01
@@ -99,22 +100,6 @@ def __init__(self, name=""):
self.y_menu.addAction(self.action_set_fixed_maximum_imag)
self.y_menu.addAction(self.action_set_fixed_minimum_imag)
- #
- # Set up size policy and palette
- #
-
- self.setMinimumSize(
- self.chartWidth + self.leftMargin + self.rightMargin,
- self.chartHeight + 40)
- self.setSizePolicy(
- QtWidgets.QSizePolicy(
- QtWidgets.QSizePolicy.MinimumExpanding,
- QtWidgets.QSizePolicy.MinimumExpanding))
- pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
- self.setPalette(pal)
- self.setAutoFillBackground(True)
-
def copy(self):
new_chart: RealImaginaryChart = super().copy()
@@ -125,47 +110,37 @@ def copy(self):
return new_chart
def drawChart(self, qp: QtGui.QPainter):
- qp.setPen(QtGui.QPen(self.textColor))
+ qp.setPen(QtGui.QPen(Chart.color.text))
qp.drawText(self.leftMargin + 5, 15,
f"{self.name} (\N{OHM SIGN})")
qp.drawText(10, 15, "R")
- qp.drawText(self.leftMargin + self.chartWidth + 10, 15, "X")
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ qp.drawText(self.leftMargin + self.dim.width + 10, 15, "X")
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin,
self.topMargin - 5,
self.leftMargin,
- self.topMargin + self.chartHeight + 5)
+ self.topMargin + self.dim.height + 5)
qp.drawLine(self.leftMargin-5,
- self.topMargin + self.chartHeight,
- self.leftMargin + self.chartWidth + 5,
- self.topMargin + self.chartHeight)
+ self.topMargin + self.dim.height,
+ self.leftMargin + self.dim.width + 5,
+ self.topMargin + self.dim.height)
self.drawTitle(qp)
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
- line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(self.lineThickness)
+ pen = QtGui.QPen(Chart.color.sweep)
+ pen.setWidth(self.dim.point)
+ line_pen = QtGui.QPen(Chart.color.sweep)
+ line_pen.setWidth(self.dim.line)
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
highlighter.setWidth(1)
- if self.fixedSpan:
- fstart = self.minFrequency
- fstop = self.maxFrequency
- else:
- if len(self.data) > 0:
- fstart = self.data[0].freq
- fstop = self.data[len(self.data)-1].freq
- else:
- fstart = self.reference[0].freq
- fstop = self.reference[len(self.reference) - 1].freq
- self.fstart = fstart
- self.fstop = fstop
+
+ self._set_start_stop()
# Draw bands if required
if self.bands.enabled:
- self.drawBands(qp, fstart, fstop)
+ self.drawBands(qp, self.fstart, self.fstop)
# Find scaling
if self.fixedValues:
@@ -192,7 +167,7 @@ def drawValues(self, qp: QtGui.QPainter):
if im < min_imag:
min_imag = im
for d in self.reference: # Also check min/max for the reference sweep
- if d.freq < fstart or d.freq > fstop:
+ if d.freq < self.fstart or d.freq > self.fstop:
continue
imp = self.impedance(d)
re, im = imp.real, imp.imag
@@ -253,45 +228,45 @@ def drawValues(self, qp: QtGui.QPainter):
self.span_imag = span_imag
# We want one horizontal tick per 50 pixels, at most
- horizontal_ticks = math.floor(self.chartHeight/50)
+ horizontal_ticks = math.floor(self.dim.height/50)
- fmt = Format(max_nr_digits=4)
+ fmt = Format(max_nr_digits=3)
for i in range(horizontal_ticks):
- y = self.topMargin + round(i * self.chartHeight / horizontal_ticks)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.chartWidth + 5, y)
- qp.setPen(QtGui.QPen(self.textColor))
+ y = self.topMargin + round(i * self.dim.height / horizontal_ticks)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.dim.width + 5, y)
+ qp.setPen(QtGui.QPen(Chart.color.text))
re = max_real - i * span_real / horizontal_ticks
im = max_imag - i * span_imag / horizontal_ticks
qp.drawText(3, y + 4, str(Value(re, fmt=fmt)))
- qp.drawText(self.leftMargin + self.chartWidth + 8, y + 4, str(Value(im, fmt=fmt)))
+ qp.drawText(self.leftMargin + self.dim.width + 8, y + 4, str(Value(im, fmt=fmt)))
- qp.drawText(3, self.chartHeight + self.topMargin, str(Value(min_real, fmt=fmt)))
- qp.drawText(self.leftMargin + self.chartWidth + 8,
- self.chartHeight + self.topMargin,
+ qp.drawText(3, self.dim.height + self.topMargin, str(Value(min_real, fmt=fmt)))
+ qp.drawText(self.leftMargin + self.dim.width + 8,
+ self.dim.height + self.topMargin,
str(Value(min_imag, fmt=fmt)))
self.drawFrequencyTicks(qp)
primary_pen = pen
- secondary_pen = QtGui.QPen(self.secondarySweepColor)
+ secondary_pen = QtGui.QPen(Chart.color.sweep_secondary)
if len(self.data) > 0:
- c = QtGui.QColor(self.sweepColor)
+ c = QtGui.QColor(Chart.color.sweep)
c.setAlpha(255)
pen = QtGui.QPen(c)
pen.setWidth(2)
qp.setPen(pen)
qp.drawLine(20, 9, 25, 9)
- c = QtGui.QColor(self.secondarySweepColor)
+ c = QtGui.QColor(Chart.color.sweep_secondary)
c.setAlpha(255)
pen.setColor(c)
qp.setPen(pen)
- qp.drawLine(self.leftMargin + self.chartWidth, 9,
- self.leftMargin + self.chartWidth + 5, 9)
+ qp.drawLine(self.leftMargin + self.dim.width, 9,
+ self.leftMargin + self.dim.width + 5, 9)
- primary_pen.setWidth(self.pointSize)
- secondary_pen.setWidth(self.pointSize)
- line_pen.setWidth(self.lineThickness)
+ primary_pen.setWidth(self.dim.point)
+ secondary_pen.setWidth(self.dim.point)
+ line_pen.setWidth(self.dim.line)
for i in range(len(self.data)):
x = self.getXPosition(self.data[i])
@@ -303,13 +278,13 @@ def drawValues(self, qp: QtGui.QPainter):
qp.setPen(secondary_pen)
if self.isPlotable(x, y_im):
qp.drawPoint(x, y_im)
- if self.drawLines and i > 0:
+ if self.flag.draw_lines and i > 0:
prev_x = self.getXPosition(self.data[i - 1])
prev_y_re = self.getReYPosition(self.data[i-1])
prev_y_im = self.getImYPosition(self.data[i-1])
# Real part first
- line_pen.setColor(self.sweepColor)
+ line_pen.setColor(Chart.color.sweep)
qp.setPen(line_pen)
if self.isPlotable(x, y_re) and self.isPlotable(prev_x, prev_y_re):
qp.drawLine(x, y_re, prev_x, prev_y_re)
@@ -321,7 +296,7 @@ def drawValues(self, qp: QtGui.QPainter):
qp.drawLine(prev_x, prev_y_re, new_x, new_y)
# Imag part second
- line_pen.setColor(self.secondarySweepColor)
+ line_pen.setColor(Chart.color.sweep_secondary)
qp.setPen(line_pen)
if self.isPlotable(x, y_im) and self.isPlotable(prev_x, prev_y_im):
qp.drawLine(x, y_im, prev_x, prev_y_im)
@@ -332,27 +307,27 @@ def drawValues(self, qp: QtGui.QPainter):
new_x, new_y = self.getPlotable(prev_x, prev_y_im, x, y_im)
qp.drawLine(prev_x, prev_y_im, new_x, new_y)
- primary_pen.setColor(self.referenceColor)
- line_pen.setColor(self.referenceColor)
- secondary_pen.setColor(self.secondaryReferenceColor)
+ primary_pen.setColor(Chart.color.reference)
+ line_pen.setColor(Chart.color.reference)
+ secondary_pen.setColor(Chart.color.reference_secondary)
qp.setPen(primary_pen)
if len(self.reference) > 0:
- c = QtGui.QColor(self.referenceColor)
+ c = QtGui.QColor(Chart.color.reference)
c.setAlpha(255)
pen = QtGui.QPen(c)
pen.setWidth(2)
qp.setPen(pen)
qp.drawLine(20, 14, 25, 14)
- c = QtGui.QColor(self.secondaryReferenceColor)
+ c = QtGui.QColor(Chart.color.reference_secondary)
c.setAlpha(255)
pen = QtGui.QPen(c)
pen.setWidth(2)
qp.setPen(pen)
- qp.drawLine(self.leftMargin + self.chartWidth, 14,
- self.leftMargin + self.chartWidth + 5, 14)
+ qp.drawLine(self.leftMargin + self.dim.width, 14,
+ self.leftMargin + self.dim.width + 5, 14)
for i in range(len(self.reference)):
- if self.reference[i].freq < fstart or self.reference[i].freq > fstop:
+ if self.reference[i].freq < self.fstart or self.reference[i].freq > self.fstop:
continue
x = self.getXPosition(self.reference[i])
y_re = self.getReYPosition(self.reference[i])
@@ -363,12 +338,12 @@ def drawValues(self, qp: QtGui.QPainter):
qp.setPen(secondary_pen)
if self.isPlotable(x, y_im):
qp.drawPoint(x, y_im)
- if self.drawLines and i > 0:
+ if self.flag.draw_lines and i > 0:
prev_x = self.getXPosition(self.reference[i - 1])
prev_y_re = self.getReYPosition(self.reference[i-1])
prev_y_im = self.getImYPosition(self.reference[i-1])
- line_pen.setColor(self.referenceColor)
+ line_pen.setColor(Chart.color.reference)
qp.setPen(line_pen)
# Real part first
if self.isPlotable(x, y_re) and self.isPlotable(prev_x, prev_y_re):
@@ -380,7 +355,7 @@ def drawValues(self, qp: QtGui.QPainter):
new_x, new_y = self.getPlotable(prev_x, prev_y_re, x, y_re)
qp.drawLine(prev_x, prev_y_re, new_x, new_y)
- line_pen.setColor(self.secondaryReferenceColor)
+ line_pen.setColor(Chart.color.reference_secondary)
qp.setPen(line_pen)
# Imag part second
if self.isPlotable(x, y_im) and self.isPlotable(prev_x, prev_y_im):
@@ -404,19 +379,18 @@ def drawValues(self, qp: QtGui.QPainter):
def getImYPosition(self, d: Datapoint) -> int:
im = self.impedance(d).imag
- return self.topMargin + round((self.max_imag - im) / self.span_imag * self.chartHeight)
+ return self.topMargin + round((self.max_imag - im) / self.span_imag * self.dim.height)
def getReYPosition(self, d: Datapoint) -> int:
re = self.impedance(d).real
if math.isfinite(re):
- return self.topMargin + round((self.max_real - re) / self.span_real * self.chartHeight)
- else:
- return self.topMargin
+ return self.topMargin + round((self.max_real - re) / self.span_real * self.dim.height)
+ return self.topMargin
def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
- valRe = -1 * ((absy / self.chartHeight * self.span_real) - self.max_real)
- valIm = -1 * ((absy / self.chartHeight * self.span_imag) - self.max_imag)
+ valRe = -1 * ((absy / self.dim.height * self.span_real) - self.max_real)
+ valIm = -1 * ((absy / self.dim.height * self.span_imag) - self.max_imag)
return [valRe, valIm]
def zoomTo(self, x1, y1, x2, y2):
@@ -517,9 +491,9 @@ def setFixedValues(self, fixed_values: bool):
def contextMenuEvent(self, event):
self.action_set_fixed_start.setText(
- f"Start ({Chart.shortenFrequency(self.minFrequency)})")
+ f"Start ({format_frequency_chart(self.minFrequency)})")
self.action_set_fixed_stop.setText(
- f"Stop ({Chart.shortenFrequency(self.maxFrequency)})")
+ f"Stop ({format_frequency_chart(self.maxFrequency)})")
self.action_set_fixed_minimum_real.setText(
f"Minimum R ({self.minDisplayReal})")
self.action_set_fixed_maximum_real.setText(
diff --git a/NanoVNASaver/Charts/RISeries.py b/NanoVNASaver/Charts/RISeries.py
index b2ca4eff..245b9372 100644
--- a/NanoVNASaver/Charts/RISeries.py
+++ b/NanoVNASaver/Charts/RISeries.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,20 +16,15 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-import math
import logging
-from typing import List
from NanoVNASaver.RFTools import Datapoint
-
from .RI import RealImaginaryChart
logger = logging.getLogger(__name__)
class RealImaginarySeriesChart(RealImaginaryChart):
- def __init__(self, name=""):
- super().__init__(name)
def impedance(self, p: Datapoint) -> complex:
return p.seriesImpedance()
diff --git a/NanoVNASaver/Charts/RIShunt.py b/NanoVNASaver/Charts/RIShunt.py
index 82e602f8..29874253 100644
--- a/NanoVNASaver/Charts/RIShunt.py
+++ b/NanoVNASaver/Charts/RIShunt.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,20 +16,15 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-import math
import logging
-from typing import List
from NanoVNASaver.RFTools import Datapoint
-
from .RI import RealImaginaryChart
logger = logging.getLogger(__name__)
class RealImaginaryShuntChart(RealImaginaryChart):
- def __init__(self, name=""):
- super().__init__(name)
def impedance(self, p: Datapoint) -> complex:
return p.shuntImpedance()
diff --git a/NanoVNASaver/Charts/SParam.py b/NanoVNASaver/Charts/SParam.py
index 5874d29f..6699bc3d 100644
--- a/NanoVNASaver/Charts/SParam.py
+++ b/NanoVNASaver/Charts/SParam.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,11 +20,12 @@
import logging
from typing import List
-from PyQt5 import QtWidgets, QtGui
+from PyQt5 import QtGui
from NanoVNASaver.RFTools import Datapoint
-from .Frequency import FrequencyChart
-from .LogMag import LogMagChart
+from NanoVNASaver.Charts.Chart import Chart
+from NanoVNASaver.Charts.Frequency import FrequencyChart
+from NanoVNASaver.Charts.LogMag import LogMagChart
logger = logging.getLogger(__name__)
@@ -32,9 +33,7 @@
class SParameterChart(FrequencyChart):
def __init__(self, name=""):
super().__init__(name)
- self.leftMargin = 30
- self.chartWidth = 250
- self.chartHeight = 250
+
self.minDisplayValue = -1
self.maxDisplayValue = 1
self.fixedValues = True
@@ -48,51 +47,26 @@ def __init__(self, name=""):
self.isInverted = False
- self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin,
- self.chartHeight + self.topMargin + self.bottomMargin)
- self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
- QtWidgets.QSizePolicy.MinimumExpanding))
- pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
- self.setPalette(pal)
- self.setAutoFillBackground(True)
-
def drawChart(self, qp: QtGui.QPainter):
- qp.setPen(QtGui.QPen(self.textColor))
- qp.drawText(int(round(self.chartWidth / 2)) - 20, 15, self.name + "")
+ qp.setPen(QtGui.QPen(Chart.color.text))
+ qp.drawText(int(round(self.dim.width / 2)) - 20, 15, self.name + "")
qp.drawText(10, 15, "Real")
- qp.drawText(self.leftMargin + self.chartWidth - 15, 15, "Imag")
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ qp.drawText(self.leftMargin + self.dim.width - 15, 15, "Imag")
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin, self.topMargin - 5,
- self.leftMargin, self.topMargin+self.chartHeight+5)
- qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight,
- self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight)
+ self.leftMargin, self.topMargin+self.dim.height+5)
+ qp.drawLine(self.leftMargin-5, self.topMargin+self.dim.height,
+ self.leftMargin+self.dim.width, self.topMargin + self.dim.height)
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
- line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(self.lineThickness)
- highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
- highlighter.setWidth(1)
- if not self.fixedSpan:
- if len(self.data) > 0:
- fstart = self.data[0].freq
- fstop = self.data[len(self.data)-1].freq
- else:
- fstart = self.reference[0].freq
- fstop = self.reference[len(self.reference) - 1].freq
- self.fstart = fstart
- self.fstop = fstop
- else:
- fstart = self.fstart = self.minFrequency
- fstop = self.fstop = self.maxFrequency
+
+ self._set_start_stop()
# Draw bands if required
if self.bands.enabled:
- self.drawBands(qp, fstart, fstop)
+ self.drawBands(qp, self.fstart, self.fstop)
if self.fixedValues:
maxValue = self.maxDisplayValue
@@ -130,45 +104,45 @@ def drawValues(self, qp: QtGui.QPainter):
span = 0.01
self.span = span
- tick_count = math.floor(self.chartHeight / 60)
+ tick_count = math.floor(self.dim.height / 60)
tick_step = self.span / tick_count
for i in range(tick_count):
val = minValue + i * tick_step
- y = self.topMargin + round((maxValue - val)/span*self.chartHeight)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin-5, y, self.leftMargin+self.chartWidth, y)
+ y = self.topMargin + round((maxValue - val)/span*self.dim.height)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin-5, y, self.leftMargin+self.dim.width, y)
if val > minValue and val != maxValue:
- qp.setPen(QtGui.QPen(self.textColor))
+ qp.setPen(QtGui.QPen(Chart.color.text))
qp.drawText(3, y + 4, str(round(val, 2)))
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5, self.topMargin,
- self.leftMargin + self.chartWidth, self.topMargin)
- qp.setPen(self.textColor)
+ self.leftMargin + self.dim.width, self.topMargin)
+ qp.setPen(Chart.color.text)
qp.drawText(3, self.topMargin + 4, str(maxValue))
- qp.drawText(3, self.chartHeight+self.topMargin, str(minValue))
+ qp.drawText(3, self.dim.height+self.topMargin, str(minValue))
self.drawFrequencyTicks(qp)
- self.drawData(qp, self.data, self.sweepColor, self.getReYPosition)
- self.drawData(qp, self.reference, self.referenceColor, self.getReYPosition)
- self.drawData(qp, self.data, self.secondarySweepColor, self.getImYPosition)
- self.drawData(qp, self.reference, self.secondaryReferenceColor, self.getImYPosition)
+ self.drawData(qp, self.data, Chart.color.sweep, self.getReYPosition)
+ self.drawData(qp, self.reference, Chart.color.reference, self.getReYPosition)
+ self.drawData(qp, self.data, Chart.color.sweep_secondary, self.getImYPosition)
+ self.drawData(qp, self.reference, Chart.color.reference_secondary, self.getImYPosition)
self.drawMarkers(qp, y_function=self.getReYPosition)
self.drawMarkers(qp, y_function=self.getImYPosition)
def getYPosition(self, d: Datapoint) -> int:
- return self.topMargin + round((self.maxValue - d.re) / self.span * self.chartHeight)
+ return self.topMargin + round((self.maxValue - d.re) / self.span * self.dim.height)
def getReYPosition(self, d: Datapoint) -> int:
- return self.topMargin + round((self.maxValue - d.re) / self.span * self.chartHeight)
+ return self.topMargin + round((self.maxValue - d.re) / self.span * self.dim.height)
def getImYPosition(self, d: Datapoint) -> int:
- return self.topMargin + round((self.maxValue - d.im) / self.span * self.chartHeight)
+ return self.topMargin + round((self.maxValue - d.im) / self.span * self.dim.height)
def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
- val = -1 * ((absy / self.chartHeight * self.span) - self.maxValue)
+ val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
return [val]
def logMag(self, p: Datapoint) -> float:
diff --git a/NanoVNASaver/Charts/Smith.py b/NanoVNASaver/Charts/Smith.py
index 2668e8fb..23099633 100644
--- a/NanoVNASaver/Charts/Smith.py
+++ b/NanoVNASaver/Charts/Smith.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,7 +22,8 @@
from PyQt5 import QtGui, QtCore
from NanoVNASaver.RFTools import Datapoint
-from .Square import SquareChart
+from NanoVNASaver.Charts.Chart import Chart
+from NanoVNASaver.Charts.Square import SquareChart
logger = logging.getLogger(__name__)
@@ -30,16 +31,16 @@
class SmithChart(SquareChart):
def __init__(self, name=""):
super().__init__(name)
- self.chartWidth = 250
- self.chartHeight = 250
+ self.dim.width = 250
+ self.dim.height = 250
- self.setMinimumSize(self.chartWidth + 40, self.chartHeight + 40)
+ self.setMinimumSize(self.dim.width + 40, self.dim.height + 40)
pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
+ pal.setColor(QtGui.QPalette.Background, Chart.color.background)
self.setPalette(pal)
self.setAutoFillBackground(True)
- def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
+ def paintEvent(self, _: QtGui.QPaintEvent) -> None:
qp = QtGui.QPainter(self)
# qp.begin(self) # Apparently not needed?
self.drawSmithChart(qp)
@@ -49,67 +50,67 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
def drawSmithChart(self, qp: QtGui.QPainter):
centerX = int(self.width()/2)
centerY = int(self.height()/2)
- qp.setPen(QtGui.QPen(self.textColor))
+ qp.setPen(QtGui.QPen(Chart.color.text))
qp.drawText(3, 15, self.name)
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawEllipse(QtCore.QPoint(centerX, centerY),
- int(self.chartWidth / 2),
- int(self.chartHeight / 2))
+ int(self.dim.width / 2),
+ int(self.dim.height / 2))
qp.drawLine(
- centerX - int(self.chartWidth / 2),
+ centerX - int(self.dim.width / 2),
centerY,
- centerX + int(self.chartWidth / 2),
+ centerX + int(self.dim.width / 2),
centerY)
- qp.drawEllipse(QtCore.QPoint(centerX + int(self.chartWidth/4), centerY),
- int(self.chartWidth/4), int(self.chartHeight/4)) # Re(Z) = 1
- qp.drawEllipse(QtCore.QPoint(centerX + int(2/3*self.chartWidth/2), centerY),
- int(self.chartWidth/6), int(self.chartHeight/6)) # Re(Z) = 2
- qp.drawEllipse(QtCore.QPoint(centerX + int(3 / 4 * self.chartWidth / 2), centerY),
- int(self.chartWidth / 8), int(self.chartHeight / 8)) # Re(Z) = 3
- qp.drawEllipse(QtCore.QPoint(centerX + int(5 / 6 * self.chartWidth / 2), centerY),
- int(self.chartWidth / 12), int(self.chartHeight / 12)) # Re(Z) = 5
-
- qp.drawEllipse(QtCore.QPoint(centerX + int(1 / 3 * self.chartWidth / 2), centerY),
- int(self.chartWidth / 3), int(self.chartHeight / 3)) # Re(Z) = 0.5
- qp.drawEllipse(QtCore.QPoint(centerX + int(1 / 6 * self.chartWidth / 2), centerY),
- int(self.chartWidth / 2.4), int(self.chartHeight / 2.4)) # Re(Z) = 0.2
-
- qp.drawArc(centerX + int(3/8*self.chartWidth), centerY, int(self.chartWidth/4),
- int(self.chartWidth/4), 90*16, 152*16) # Im(Z) = -5
- qp.drawArc(centerX + int(3/8*self.chartWidth), centerY, int(self.chartWidth/4),
- -int(self.chartWidth/4), -90 * 16, -152 * 16) # Im(Z) = 5
- qp.drawArc(centerX + int(self.chartWidth/4), centerY, int(self.chartWidth/2),
- int(self.chartHeight/2), 90*16, 127*16) # Im(Z) = -2
- qp.drawArc(centerX + int(self.chartWidth/4), centerY, int(self.chartWidth/2),
- -int(self.chartHeight/2), -90*16, -127*16) # Im(Z) = 2
+ qp.drawEllipse(QtCore.QPoint(centerX + int(self.dim.width/4), centerY),
+ int(self.dim.width/4), int(self.dim.height/4)) # Re(Z) = 1
+ qp.drawEllipse(QtCore.QPoint(centerX + int(2/3*self.dim.width/2), centerY),
+ int(self.dim.width/6), int(self.dim.height/6)) # Re(Z) = 2
+ qp.drawEllipse(QtCore.QPoint(centerX + int(3 / 4 * self.dim.width / 2), centerY),
+ int(self.dim.width / 8), int(self.dim.height / 8)) # Re(Z) = 3
+ qp.drawEllipse(QtCore.QPoint(centerX + int(5 / 6 * self.dim.width / 2), centerY),
+ int(self.dim.width / 12), int(self.dim.height / 12)) # Re(Z) = 5
+
+ qp.drawEllipse(QtCore.QPoint(centerX + int(1 / 3 * self.dim.width / 2), centerY),
+ int(self.dim.width / 3), int(self.dim.height / 3)) # Re(Z) = 0.5
+ qp.drawEllipse(QtCore.QPoint(centerX + int(1 / 6 * self.dim.width / 2), centerY),
+ int(self.dim.width / 2.4), int(self.dim.height / 2.4)) # Re(Z) = 0.2
+
+ qp.drawArc(centerX + int(3/8*self.dim.width), centerY, int(self.dim.width/4),
+ int(self.dim.width/4), 90*16, 152*16) # Im(Z) = -5
+ qp.drawArc(centerX + int(3/8*self.dim.width), centerY, int(self.dim.width/4),
+ -int(self.dim.width/4), -90 * 16, -152 * 16) # Im(Z) = 5
+ qp.drawArc(centerX + int(self.dim.width/4), centerY, int(self.dim.width/2),
+ int(self.dim.height/2), 90*16, 127*16) # Im(Z) = -2
+ qp.drawArc(centerX + int(self.dim.width/4), centerY, int(self.dim.width/2),
+ -int(self.dim.height/2), -90*16, -127*16) # Im(Z) = 2
qp.drawArc(centerX, centerY,
- self.chartWidth, self.chartHeight,
+ self.dim.width, self.dim.height,
90*16, 90*16) # Im(Z) = -1
qp.drawArc(centerX, centerY,
- self.chartWidth, -self.chartHeight,
+ self.dim.width, -self.dim.height,
-90 * 16, -90 * 16) # Im(Z) = 1
- qp.drawArc(centerX - int(self.chartWidth / 2), centerY,
- self.chartWidth * 2, self.chartHeight * 2,
+ qp.drawArc(centerX - int(self.dim.width / 2), centerY,
+ self.dim.width * 2, self.dim.height * 2,
int(99.5*16), int(43.5*16)) # Im(Z) = -0.5
- qp.drawArc(centerX - int(self.chartWidth / 2), centerY,
- self.chartWidth * 2, -self.chartHeight * 2,
+ qp.drawArc(centerX - int(self.dim.width / 2), centerY,
+ self.dim.width * 2, -self.dim.height * 2,
int(-99.5 * 16), int(-43.5 * 16)) # Im(Z) = 0.5
- qp.drawArc(centerX - self.chartWidth * 2, centerY,
- self.chartWidth * 5, self.chartHeight * 5,
+ qp.drawArc(centerX - self.dim.width * 2, centerY,
+ self.dim.width * 5, self.dim.height * 5,
int(93.85 * 16), int(18.85 * 16)) # Im(Z) = -0.2
- qp.drawArc(centerX - self.chartWidth*2, centerY,
- self.chartWidth*5, -self.chartHeight*5,
+ qp.drawArc(centerX - self.dim.width*2, centerY,
+ self.dim.width*5, -self.dim.height*5,
int(-93.85 * 16), int(-18.85 * 16)) # Im(Z) = 0.2
self.drawTitle(qp)
- qp.setPen(self.swrColor)
+ qp.setPen(Chart.color.swr)
for swr in self.swrMarkers:
if swr <= 1:
continue
gamma = (swr - 1)/(swr + 1)
- r = round(gamma * self.chartWidth/2)
+ r = round(gamma * self.dim.width/2)
qp.drawEllipse(QtCore.QPoint(centerX, centerY), r, r)
qp.drawText(
QtCore.QRect(centerX - 50, centerY - 4 + r, 100, 20),
@@ -118,25 +119,25 @@ def drawSmithChart(self, qp: QtGui.QPainter):
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
- line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(self.lineThickness)
+ pen = QtGui.QPen(Chart.color.sweep)
+ pen.setWidth(self.dim.point)
+ line_pen = QtGui.QPen(Chart.color.sweep)
+ line_pen.setWidth(self.dim.line)
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
highlighter.setWidth(1)
qp.setPen(pen)
for i in range(len(self.data)):
x = self.getXPosition(self.data[i])
- y = int(self.height()/2 + self.data[i].im * -1 * self.chartHeight/2)
+ y = int(self.height()/2 + self.data[i].im * -1 * self.dim.height/2)
qp.drawPoint(x, y)
- if self.drawLines and i > 0:
+ if self.flag.draw_lines and i > 0:
prevx = self.getXPosition(self.data[i-1])
- prevy = int(self.height() / 2 + self.data[i-1].im * -1 * self.chartHeight / 2)
+ prevy = int(self.height() / 2 + self.data[i-1].im * -1 * self.dim.height / 2)
qp.setPen(line_pen)
qp.drawLine(x, y, prevx, prevy)
qp.setPen(pen)
- pen.setColor(self.referenceColor)
- line_pen.setColor(self.referenceColor)
+ pen.setColor(Chart.color.reference)
+ line_pen.setColor(Chart.color.reference)
qp.setPen(pen)
if len(self.data) > 0:
fstart = self.data[0].freq
@@ -149,11 +150,11 @@ def drawValues(self, qp: QtGui.QPainter):
if data.freq < fstart or data.freq > fstop:
continue
x = self.getXPosition(data)
- y = int(self.height()/2 + data.im * -1 * self.chartHeight/2)
+ y = int(self.height()/2 + data.im * -1 * self.dim.height/2)
qp.drawPoint(x, y)
- if self.drawLines and i > 0:
+ if self.flag.draw_lines and i > 0:
prevx = self.getXPosition(self.reference[i-1])
- prevy = int(self.height() / 2 + self.reference[i-1].im * -1 * self.chartHeight / 2)
+ prevy = int(self.height() / 2 + self.reference[i-1].im * -1 * self.dim.height / 2)
qp.setPen(line_pen)
qp.drawLine(x, y, prevx, prevy)
qp.setPen(pen)
@@ -161,14 +162,14 @@ def drawValues(self, qp: QtGui.QPainter):
for m in self.markers:
if m.location != -1:
x = self.getXPosition(self.data[m.location])
- y = self.height() / 2 + self.data[m.location].im * -1 * self.chartHeight / 2
+ y = self.height() / 2 + self.data[m.location].im * -1 * self.dim.height / 2
self.drawMarker(x, y, qp, m.color, self.markers.index(m)+1)
def getXPosition(self, d: Datapoint) -> int:
- return int(self.width()/2 + d.re * self.chartWidth/2)
+ return int(self.width()/2 + d.re * self.dim.width/2)
def getYPosition(self, d: Datapoint) -> int:
- return int(self.height()/2 + d.im * -1 * self.chartHeight/2)
+ return int(self.height()/2 + d.im * -1 * self.dim.height/2)
def heightForWidth(self, a0: int) -> int:
return a0
@@ -179,9 +180,9 @@ def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None:
return
x = a0.x()
y = a0.y()
- absx = x - (self.width() - self.chartWidth) / 2
- absy = y - (self.height() - self.chartHeight) / 2
- if absx < 0 or absx > self.chartWidth or absy < 0 or absy > self.chartHeight \
+ absx = x - (self.width() - self.dim.width) / 2
+ absy = y - (self.height() - self.dim.height) / 2
+ if absx < 0 or absx > self.dim.width or absy < 0 or absy > self.dim.height \
or len(self.data) == len(self.reference) == 0:
a0.ignore()
return
@@ -193,8 +194,8 @@ def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None:
target = self.reference
positions = []
for d in target:
- thisx = self.width() / 2 + d.re * self.chartWidth / 2
- thisy = self.height() / 2 + d.im * -1 * self.chartHeight / 2
+ thisx = self.width() / 2 + d.re * self.dim.width / 2
+ thisy = self.height() / 2 + d.im * -1 * self.dim.height / 2
positions.append(math.sqrt((x - thisx)**2 + (y - thisy)**2))
minimum_position = positions.index(min(positions))
diff --git a/NanoVNASaver/Charts/Square.py b/NanoVNASaver/Charts/Square.py
index 9cd8fd61..83b96d18 100644
--- a/NanoVNASaver/Charts/Square.py
+++ b/NanoVNASaver/Charts/Square.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -32,15 +32,15 @@ def __init__(self, name):
QtWidgets.QSizePolicy.Fixed,
QtWidgets.QSizePolicy.MinimumExpanding)
self.setSizePolicy(sizepolicy)
- self.chartWidth = self.width()-40
- self.chartHeight = self.height()-40
+ self.dim.width = self.width()-40
+ self.dim.height = self.height()-40
def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
- if not self.isPopout:
+ if not self.flag.is_popout:
self.setFixedWidth(a0.size().height())
- self.chartWidth = a0.size().height()-40
- self.chartHeight = a0.size().height()-40
+ self.dim.width = a0.size().height()-40
+ self.dim.height = a0.size().height()-40
else:
min_dimension = min(a0.size().height(), a0.size().width())
- self.chartWidth = self.chartHeight = min_dimension - 40
+ self.dim.width = self.dim.height = min_dimension - 40
self.update()
diff --git a/NanoVNASaver/Charts/TDR.py b/NanoVNASaver/Charts/TDR.py
index 2d8b829c..bf2a90cd 100644
--- a/NanoVNASaver/Charts/TDR.py
+++ b/NanoVNASaver/Charts/TDR.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@
import numpy as np
from PyQt5 import QtWidgets, QtGui, QtCore
-from .Chart import Chart
+from NanoVNASaver.Charts.Chart import Chart
logger = logging.getLogger(__name__)
@@ -51,7 +51,7 @@ def __init__(self, name):
QtWidgets.QSizePolicy.MinimumExpanding,
QtWidgets.QSizePolicy.MinimumExpanding))
pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
+ pal.setColor(QtGui.QPalette.Background, Chart.color.background)
self.setPalette(pal)
self.setAutoFillBackground(True)
@@ -127,8 +127,8 @@ def __init__(self, name):
lambda: self.popoutRequested.emit(self))
self.menu.addAction(self.action_popout)
- self.chartWidth = self.width() - self.leftMargin - self.rightMargin
- self.chartHeight = self.height() - self.bottomMargin - self.topMargin
+ self.dim.width = self.width() - self.leftMargin - self.rightMargin
+ self.dim.height = self.height() - self.bottomMargin - self.topMargin
def contextMenuEvent(self, event):
self.action_set_fixed_start.setText(
@@ -231,20 +231,20 @@ def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None:
if a0.buttons() == QtCore.Qt.MiddleButton:
# Drag the display
a0.accept()
- if self.moveStartX != -1 and self.moveStartY != -1:
- dx = self.moveStartX - a0.x()
- dy = self.moveStartY - a0.y()
+ if self.dragbox.move_x != -1 and self.dragbox.move_y != -1:
+ dx = self.dragbox.move_x - a0.x()
+ dy = self.dragbox.move_y - a0.y()
self.zoomTo(self.leftMargin + dx, self.topMargin + dy,
- self.leftMargin + self.chartWidth + dx,
- self.topMargin + self.chartHeight + dy)
- self.moveStartX = a0.x()
- self.moveStartY = a0.y()
+ self.leftMargin + self.dim.width + dx,
+ self.topMargin + self.dim.height + dy)
+ self.dragbox.move_x = a0.x()
+ self.dragbox.move_y = a0.y()
return
if a0.modifiers() == QtCore.Qt.ControlModifier:
# Dragging a box
- if not self.draggedBox:
- self.draggedBoxStart = (a0.x(), a0.y())
- self.draggedBoxCurrent = (a0.x(), a0.y())
+ if not self.dragbox.state:
+ self.dragbox.pos_start = (a0.x(), a0.y())
+ self.dragbox.pos = (a0.x(), a0.y())
self.update()
a0.accept()
return
@@ -269,15 +269,15 @@ def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None:
self.update()
return
- def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
+ def paintEvent(self, _: QtGui.QPaintEvent) -> None:
qp = QtGui.QPainter(self)
- qp.setPen(QtGui.QPen(self.textColor))
+ qp.setPen(QtGui.QPen(Chart.color.text))
qp.drawText(3, 15, self.name)
width = self.width() - self.leftMargin - self.rightMargin
height = self.height() - self.bottomMargin - self.topMargin
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5,
self.height() - self.bottomMargin,
self.width() - self.rightMargin,
@@ -323,9 +323,9 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
for i in range(ticks):
x = self.leftMargin + round((i + 1) * width / ticks)
- qp.setPen(QtGui.QPen(self.foregroundColor))
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(x, self.topMargin, x, self.topMargin + height)
- qp.setPen(QtGui.QPen(self.textColor))
+ qp.setPen(QtGui.QPen(Chart.color.text))
qp.drawText(
x - 15,
self.topMargin + height + 15,
@@ -335,7 +335,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
int((x - self.leftMargin) * x_step) - 1] / 2,
1)) + "m")
- qp.setPen(QtGui.QPen(self.textColor))
+ qp.setPen(QtGui.QPen(Chart.color.text))
qp.drawText(
self.leftMargin - 10,
self.topMargin + height + 15,
@@ -347,16 +347,16 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
for i in range(y_ticks):
y = self.bottomMargin + int(i * y_tick_step)
- qp.setPen(self.foregroundColor)
+ qp.setPen(Chart.color.foreground)
qp.drawLine(self.leftMargin, y, self.leftMargin + width, y)
y_val = max_impedance - y_impedance_step * i * y_tick_step
- qp.setPen(self.textColor)
+ qp.setPen(Chart.color.text)
qp.drawText(3, y + 3, str(round(y_val, 1)))
qp.drawText(3, self.topMargin + height + 3, str(round(min_impedance, 1)))
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
+ pen = QtGui.QPen(Chart.color.sweep)
+ pen.setWidth(self.dim.point)
qp.setPen(pen)
for i in range(min_index, max_index):
if i < min_index or i > max_index:
@@ -365,7 +365,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
x = self.leftMargin + int((i - min_index) / x_step)
y = (self.topMargin + height) - int(self.tdrWindow.td[i] / y_step)
if self.isPlotable(x, y):
- pen.setColor(self.sweepColor)
+ pen.setColor(Chart.color.sweep)
qp.setPen(pen)
qp.drawPoint(x, y)
@@ -373,7 +373,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
y = (self.topMargin + height) -\
int((self.tdrWindow.step_response_Z[i]-min_impedance) / y_impedance_step)
if self.isPlotable(x, y):
- pen.setColor(self.secondarySweepColor)
+ pen.setColor(Chart.color.sweep_secondary)
qp.setPen(pen)
qp.drawPoint(x, y)
@@ -383,7 +383,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
(self.topMargin + height) - int(self.tdrWindow.td[id_max] / y_step))
qp.setPen(self.markers[0].color)
qp.drawEllipse(max_point, 2, 2)
- qp.setPen(self.textColor)
+ qp.setPen(Chart.color.text)
qp.drawText(max_point.x() - 10, max_point.y() - 5,
str(round(self.tdrWindow.distance_axis[id_max] / 2,
2)) + "m")
@@ -394,7 +394,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
int((self.markerLocation - min_index) / x_step),
(self.topMargin + height) -
int(self.tdrWindow.td[self.markerLocation] / y_step))
- qp.setPen(self.textColor)
+ qp.setPen(Chart.color.text)
qp.drawEllipse(marker_point, 2, 2)
qp.drawText(
marker_point.x() - 10,
@@ -402,11 +402,11 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
str(round(self.tdrWindow.distance_axis[self.markerLocation] / 2,
2)) + "m")
- if self.draggedBox and self.draggedBoxCurrent[0] != -1:
- dashed_pen = QtGui.QPen(self.foregroundColor, 1, QtCore.Qt.DashLine)
+ if self.dragbox.state and self.dragbox.pos[0] != -1:
+ dashed_pen = QtGui.QPen(Chart.color.foreground, 1, QtCore.Qt.DashLine)
qp.setPen(dashed_pen)
- top_left = QtCore.QPoint(self.draggedBoxStart[0], self.draggedBoxStart[1])
- bottom_right = QtCore.QPoint(self.draggedBoxCurrent[0], self.draggedBoxCurrent[1])
+ top_left = QtCore.QPoint(self.dragbox.pos_start[0], self.dragbox.stateStart[1])
+ bottom_right = QtCore.QPoint(self.dragbox.pos[0], self.dragbox.stateCurrent[1])
rect = QtCore.QRect(top_left, bottom_right)
qp.drawRect(rect)
@@ -474,8 +474,8 @@ def wheelEvent(self, a0: QtGui.QWheelEvent) -> None:
if len(self.tdrWindow.td) == 0:
a0.ignore()
return
- chart_height = self.chartHeight
- chart_width = self.chartWidth
+ chart_height = self.dim.height
+ chart_width = self.dim.width
do_zoom_x = do_zoom_y = True
if a0.modifiers() == QtCore.Qt.ShiftModifier:
do_zoom_x = False
@@ -534,5 +534,5 @@ def wheelEvent(self, a0: QtGui.QWheelEvent) -> None:
def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
super().resizeEvent(a0)
- self.chartWidth = self.width() - self.leftMargin - self.rightMargin
- self.chartHeight = self.height() - self.bottomMargin - self.topMargin
+ self.dim.width = self.width() - self.leftMargin - self.rightMargin
+ self.dim.height = self.height() - self.bottomMargin - self.topMargin
diff --git a/NanoVNASaver/Charts/VSWR.py b/NanoVNASaver/Charts/VSWR.py
index 7ce8dd47..de394630 100644
--- a/NanoVNASaver/Charts/VSWR.py
+++ b/NanoVNASaver/Charts/VSWR.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,53 +20,32 @@
import logging
from typing import List
-from PyQt5 import QtWidgets, QtGui
+from PyQt5 import QtGui
from NanoVNASaver.RFTools import Datapoint
-from .Frequency import FrequencyChart
+from NanoVNASaver.Charts.Chart import Chart
+from NanoVNASaver.Charts.Frequency import FrequencyChart
logger = logging.getLogger(__name__)
class VSWRChart(FrequencyChart):
- maxVSWR = 3
- span = 2
def __init__(self, name=""):
super().__init__(name)
- self.leftMargin = 30
- self.chartWidth = 250
- self.chartHeight = 250
- self.fstart = 0
- self.fstop = 0
+
self.maxDisplayValue = 25
self.minDisplayValue = 1
- self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin,
- self.chartHeight + self.topMargin + self.bottomMargin)
- self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
- QtWidgets.QSizePolicy.MinimumExpanding))
- pal = QtGui.QPalette()
- pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
- self.setPalette(pal)
- self.setAutoFillBackground(True)
+ self.maxVSWR = 3
+ self.span = 2
def logarithmicYAllowed(self) -> bool:
return True
- def copy(self):
- new_chart: VSWRChart = super().copy()
- return new_chart
-
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
- pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(self.pointSize)
- line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(self.lineThickness)
- highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
- highlighter.setWidth(1)
if self.fixedSpan:
fstart = self.minFrequency
fstop = self.maxFrequency
@@ -101,13 +80,13 @@ def drawValues(self, qp: QtGui.QPainter):
span = 0.01
self.span = span
- target_ticks = math.floor(self.chartHeight / 60)
+ target_ticks = math.floor(self.dim.height / 60)
if self.logarithmicY:
for i in range(target_ticks):
- y = int(self.topMargin + (i / target_ticks) * self.chartHeight)
+ y = int(self.topMargin + (i / target_ticks) * self.dim.height)
vswr = self.valueAtPosition(y)[0]
- qp.setPen(self.textColor)
+ qp.setPen(Chart.color.text)
if vswr != 0:
digits = max(0, min(2, math.floor(3 - math.log10(abs(vswr)))))
if digits == 0:
@@ -115,22 +94,22 @@ def drawValues(self, qp: QtGui.QPainter):
else:
vswrstr = str(round(vswr, digits))
qp.drawText(3, y+3, vswrstr)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin-5, y, self.leftMargin+self.chartWidth, y)
- qp.drawLine(self.leftMargin - 5, self.topMargin + self.chartHeight,
- self.leftMargin + self.chartWidth, self.topMargin + self.chartHeight)
- qp.setPen(self.textColor)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin-5, y, self.leftMargin+self.dim.width, y)
+ qp.drawLine(self.leftMargin - 5, self.topMargin + self.dim.height,
+ self.leftMargin + self.dim.width, self.topMargin + self.dim.height)
+ qp.setPen(Chart.color.text)
digits = max(0, min(2, math.floor(3 - math.log10(abs(minVSWR)))))
if digits == 0:
vswrstr = str(round(minVSWR))
else:
vswrstr = str(round(minVSWR, digits))
- qp.drawText(3, self.topMargin + self.chartHeight, vswrstr)
+ qp.drawText(3, self.topMargin + self.dim.height, vswrstr)
else:
for i in range(target_ticks):
vswr = minVSWR + i * self.span/target_ticks
y = self.getYPositionFromValue(vswr)
- qp.setPen(self.textColor)
+ qp.setPen(Chart.color.text)
if vswr != 0:
digits = max(0, min(2, math.floor(3 - math.log10(abs(vswr)))))
if digits == 0:
@@ -138,13 +117,13 @@ def drawValues(self, qp: QtGui.QPainter):
else:
vswrstr = str(round(vswr, digits))
qp.drawText(3, y+3, vswrstr)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(self.leftMargin-5, y, self.leftMargin+self.chartWidth, y)
+ qp.setPen(QtGui.QPen(Chart.color.foreground))
+ qp.drawLine(self.leftMargin-5, y, self.leftMargin+self.dim.width, y)
qp.drawLine(self.leftMargin - 5,
self.topMargin,
- self.leftMargin + self.chartWidth,
+ self.leftMargin + self.dim.width,
self.topMargin)
- qp.setPen(self.textColor)
+ qp.setPen(Chart.color.text)
digits = max(0, min(2, math.floor(3 - math.log10(abs(maxVSWR)))))
if digits == 0:
vswrstr = str(round(maxVSWR))
@@ -152,16 +131,15 @@ def drawValues(self, qp: QtGui.QPainter):
vswrstr = str(round(maxVSWR, digits))
qp.drawText(3, 35, vswrstr)
- self.drawFrequencyTicks(qp)
-
- qp.setPen(self.swrColor)
+ qp.setPen(Chart.color.swr)
for vswr in self.swrMarkers:
y = self.getYPositionFromValue(vswr)
- qp.drawLine(self.leftMargin, y, self.leftMargin + self.chartWidth, y)
+ qp.drawLine(self.leftMargin, y, self.leftMargin + self.dim.width, y)
qp.drawText(self.leftMargin + 3, y - 1, str(vswr))
- self.drawData(qp, self.data, self.sweepColor)
- self.drawData(qp, self.reference, self.referenceColor)
+ self.drawFrequencyTicks(qp)
+ self.drawData(qp, self.data, Chart.color.sweep)
+ self.drawData(qp, self.reference, Chart.color.reference)
self.drawMarkers(qp)
def getYPositionFromValue(self, vswr) -> int:
@@ -173,8 +151,8 @@ def getYPositionFromValue(self, vswr) -> int:
return -1
return (
self.topMargin +
- round((math.log(self.maxVSWR) - math.log(vswr)) / span * self.chartHeight))
- return self.topMargin + round((self.maxVSWR - vswr) / self.span * self.chartHeight)
+ round((math.log(self.maxVSWR) - math.log(vswr)) / span * self.dim.height))
+ return self.topMargin + round((self.maxVSWR - vswr) / self.span * self.dim.height)
def getYPosition(self, d: Datapoint) -> int:
return self.getYPositionFromValue(d.vswr)
@@ -185,12 +163,12 @@ def valueAtPosition(self, y) -> List[float]:
min_val = self.maxVSWR - self.span
if self.maxVSWR > 0 and min_val > 0:
span = math.log(self.maxVSWR) - math.log(min_val)
- step = span / self.chartHeight
+ step = span / self.dim.height
val = math.exp(math.log(self.maxVSWR) - absy * step)
else:
val = -1
else:
- val = -1 * ((absy / self.chartHeight * self.span) - self.maxVSWR)
+ val = -1 * ((absy / self.dim.height * self.span) - self.maxVSWR)
return [val]
def resetDisplayLimits(self):
diff --git a/NanoVNASaver/Charts/__init__.py b/NanoVNASaver/Charts/__init__.py
index fb0022be..d8ebd651 100644
--- a/NanoVNASaver/Charts/__init__.py
+++ b/NanoVNASaver/Charts/__init__.py
@@ -1,4 +1,4 @@
-from .Chart import Chart
+from .Chart import Chart
from .Frequency import FrequencyChart
from .Polar import PolarChart
from .Square import SquareChart
diff --git a/NanoVNASaver/Controls/Control.py b/NanoVNASaver/Controls/Control.py
new file mode 100644
index 00000000..ad882950
--- /dev/null
+++ b/NanoVNASaver/Controls/Control.py
@@ -0,0 +1,33 @@
+# NanoVNASaver
+#
+# A python program to view and export Touchstone data from a NanoVNA
+# Copyright (C) 2019, 2020 Rune B. Broberg
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+import logging
+
+from PyQt5 import QtWidgets, QtCore
+
+logger = logging.getLogger(__name__)
+
+class Control(QtWidgets.QGroupBox):
+ updated = QtCore.pyqtSignal(object)
+
+ def __init__(self, app: QtWidgets.QWidget, title: str = ""):
+ super().__init__()
+ self.app = app
+ self.setMaximumWidth(240)
+ self.setTitle(title)
+ self.layout = QtWidgets.QFormLayout(self)
diff --git a/NanoVNASaver/Controls/MarkerControl.py b/NanoVNASaver/Controls/MarkerControl.py
index f255a91e..72b8cc67 100644
--- a/NanoVNASaver/Controls/MarkerControl.py
+++ b/NanoVNASaver/Controls/MarkerControl.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,26 +19,23 @@
import logging
from PyQt5 import QtWidgets, QtCore
-from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QCheckBox
from NanoVNASaver.Marker import Marker
+from NanoVNASaver.Controls.Control import Control
+
logger = logging.getLogger(__name__)
-class MarkerControl(QtWidgets.QGroupBox):
- updated = pyqtSignal(object)
+class MarkerControl(Control):
- def __init__(self, app: QtWidgets.QWidget, title: str = "Markers"):
- super().__init__()
- self.app = app
- self.setMaximumWidth(250)
- self.setTitle(title)
- self.layout = QtWidgets.QFormLayout(self)
+ def __init__(self, app: QtWidgets.QWidget):
+ super().__init__(app, "Markers")
marker_count = max(self.app.settings.value("MarkerCount", 3, int), 1)
for i in range(marker_count):
marker = Marker("", self.app.settings)
+ #marker.setFixedHeight(20)
marker.updated.connect(self.app.markerUpdated)
label, layout = marker.getRow()
self.layout.addRow(label, layout)
@@ -51,6 +48,7 @@ def __init__(self, app: QtWidgets.QWidget, title: str = "Markers"):
self.layout.addRow(self.check_delta)
self.showMarkerButton = QtWidgets.QPushButton()
+ self.showMarkerButton.setFixedHeight(20)
if self.app.marker_frame.isHidden():
self.showMarkerButton.setText("Show data")
else:
@@ -68,16 +66,13 @@ def __init__(self, app: QtWidgets.QWidget, title: str = "Markers"):
self.layout.addRow(hbox)
def toggle_frame(self):
- if self.app.marker_frame.isHidden():
- self.app.marker_frame.setHidden(False)
- self.app.settings.setValue("MarkersVisible", True)
- self.showMarkerButton.setText("Hide data")
- self.showMarkerButton.repaint()
- else:
- self.app.marker_frame.setHidden(True)
- self.app.settings.setValue("MarkersVisible", False)
- self.showMarkerButton.setText("Show data")
+ def settings(hidden: bool):
+ self.app.marker_frame.setHidden(not hidden)
+ self.app.settings.setValue("MarkersVisible", hidden)
+ self.showMarkerButton.setText(
+ "Hide data" if hidden else "Show data")
self.showMarkerButton.repaint()
+ settings(self.app.marker_frame.isHidden())
def toggle_delta(self):
self.app.delta_marker_layout.setVisible(self.check_delta.isChecked())
diff --git a/NanoVNASaver/Controls/SerialControl.py b/NanoVNASaver/Controls/SerialControl.py
new file mode 100644
index 00000000..cf0096ff
--- /dev/null
+++ b/NanoVNASaver/Controls/SerialControl.py
@@ -0,0 +1,132 @@
+# NanoVNASaver
+#
+# A python program to view and export Touchstone data from a NanoVNA
+# Copyright (C) 2019, 2020 Rune B. Broberg
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+import logging
+from time import sleep
+
+from PyQt5 import QtWidgets
+
+from NanoVNASaver.Hardware.Hardware import Interface, get_interfaces, get_VNA
+from NanoVNASaver.Controls.Control import Control
+
+logger = logging.getLogger(__name__)
+
+class SerialControl(Control):
+
+ def __init__(self, app: QtWidgets.QWidget):
+ super().__init__(app, "Serial port control")
+
+ self.interface = Interface("serial", "none")
+ self.inp_port = QtWidgets.QComboBox()
+ self.inp_port.setMinimumHeight(20)
+ self.rescanSerialPort()
+ self.inp_port.setEditable(True)
+ self.btn_rescan = QtWidgets.QPushButton("Rescan")
+ self.btn_rescan.setMinimumHeight(20)
+ self.btn_rescan.setFixedWidth(60)
+ self.btn_rescan.clicked.connect(self.rescanSerialPort)
+ intput_layout = QtWidgets.QHBoxLayout()
+ intput_layout.addWidget(QtWidgets.QLabel("Port"), stretch=0)
+ intput_layout.addWidget(self.inp_port, stretch=1)
+ intput_layout.addWidget(self.btn_rescan, stretch=0)
+ self.layout.addRow(intput_layout)
+
+ button_layout = QtWidgets.QHBoxLayout()
+
+ self.btn_toggle = QtWidgets.QPushButton("Connect to device")
+ self.btn_toggle.setMinimumHeight(20)
+ self.btn_toggle.clicked.connect(self.serialButtonClick)
+ button_layout.addWidget(self.btn_toggle, stretch=1)
+
+ self.btn_settings = QtWidgets.QPushButton("Manage")
+ self.btn_settings.setMinimumHeight(20)
+ self.btn_settings.setFixedWidth(60)
+ self.btn_settings.clicked.connect(
+ lambda: self.app.display_window("device_settings"))
+
+ button_layout.addWidget(self.btn_settings, stretch=0)
+ self.layout.addRow(button_layout)
+
+ def rescanSerialPort(self):
+ self.inp_port.clear()
+ for iface in get_interfaces():
+ self.inp_port.insertItem(1, f"{iface}", iface)
+ self.inp_port.repaint()
+
+ def serialButtonClick(self):
+ if not self.app.vna.connected():
+ self.connect_device()
+ else:
+ self.disconnect_device()
+
+ def connect_device(self):
+ with self.interface.lock:
+ self.interface = self.inp_port.currentData()
+ logger.info("Connection %s", self.interface)
+ try:
+ self.interface.open()
+ except (IOError, AttributeError) as exc:
+ logger.error("Tried to open %s and failed: %s",
+ self.interface, exc)
+ return
+ if not self.interface.isOpen():
+ logger.error("Unable to open port %s", self.interface)
+ return
+ self.interface.timeout = 0.05
+ sleep(0.1)
+ try:
+ self.app.vna = get_VNA(self.interface)
+ except IOError as exc:
+ logger.error("Unable to connect to VNA: %s", exc)
+
+ self.app.vna.validateInput = self.app.settings.value(
+ "SerialInputValidation", True, bool)
+
+ # connected
+ self.btn_toggle.setText("Disconnect")
+ self.btn_toggle.repaint()
+
+ frequencies = self.app.vna.readFrequencies()
+ if not frequencies:
+ logger.warning("No frequencies read")
+ return
+ logger.info("Read starting frequency %s and end frequency %s",
+ frequencies[0], frequencies[-1])
+ self.app.sweep_control.set_start(frequencies[0])
+ if frequencies[0] < frequencies[-1]:
+ self.app.sweep_control.set_end(frequencies[-1])
+ else:
+ self.app.sweep_control.set_end(
+ frequencies[0] +
+ self.app.vna.datapoints * self.app.sweep_control.get_segments())
+
+ self.app.sweep_control.set_segments(1) # speed up things
+ self.app.sweep_control.update_center_span()
+ self.app.sweep_control.update_step_size()
+
+ self.app.windows["sweep_settings"].vna_connected()
+
+ logger.debug("Starting initial sweep")
+ self.app.sweep_start()
+
+ def disconnect_device(self):
+ with self.interface.lock:
+ logger.info("Closing connection to %s", self.interface)
+ self.interface.close()
+ self.btn_toggle.setText("Connect to device")
+ self.btn_toggle.repaint()
diff --git a/NanoVNASaver/Controls/SweepControl.py b/NanoVNASaver/Controls/SweepControl.py
index daa82ebc..d2b32421 100644
--- a/NanoVNASaver/Controls/SweepControl.py
+++ b/NanoVNASaver/Controls/SweepControl.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,26 +19,20 @@
import logging
from PyQt5 import QtWidgets, QtCore
-from PyQt5.QtCore import pyqtSignal
-
from NanoVNASaver.Formatting import (
format_frequency_sweep, format_frequency_short,
parse_frequency)
from NanoVNASaver.Inputs import FrequencyInputWidget
+from NanoVNASaver.Controls.Control import Control
logger = logging.getLogger(__name__)
-class SweepControl(QtWidgets.QGroupBox):
- updated = pyqtSignal(object)
+class SweepControl(Control):
- def __init__(self, app: QtWidgets.QWidget, title: str = "Sweep control"):
- super().__init__()
- self.app = app
- self.setMaximumWidth(250)
- self.setTitle(title)
- control_layout = QtWidgets.QFormLayout(self)
+ def __init__(self, app: QtWidgets.QWidget):
+ super().__init__(app, "Sweep control")
line = QtWidgets.QFrame()
line.setFrameShape(QtWidgets.QFrame.VLine)
@@ -49,9 +43,10 @@ def __init__(self, app: QtWidgets.QWidget, title: str = "Sweep control"):
input_layout.addLayout(input_left_layout)
input_layout.addWidget(line)
input_layout.addLayout(input_right_layout)
- control_layout.addRow(input_layout)
+ self.layout.addRow(input_layout)
self.input_start = FrequencyInputWidget()
+ self.input_start.setFixedHeight(20)
self.input_start.setMinimumWidth(60)
self.input_start.setAlignment(QtCore.Qt.AlignRight)
self.input_start.textEdited.connect(self.update_center_span)
@@ -59,12 +54,14 @@ def __init__(self, app: QtWidgets.QWidget, title: str = "Sweep control"):
input_left_layout.addRow(QtWidgets.QLabel("Start"), self.input_start)
self.input_end = FrequencyInputWidget()
+ self.input_end.setFixedHeight(20)
self.input_end.setAlignment(QtCore.Qt.AlignRight)
self.input_end.textEdited.connect(self.update_center_span)
self.input_end.textChanged.connect(self.update_step_size)
input_left_layout.addRow(QtWidgets.QLabel("Stop"), self.input_end)
self.input_center = FrequencyInputWidget()
+ self.input_center.setFixedHeight(20)
self.input_center.setMinimumWidth(60)
self.input_center.setAlignment(QtCore.Qt.AlignRight)
self.input_center.textEdited.connect(self.update_start_end)
@@ -72,6 +69,7 @@ def __init__(self, app: QtWidgets.QWidget, title: str = "Sweep control"):
input_right_layout.addRow(QtWidgets.QLabel("Center"), self.input_center)
self.input_span = FrequencyInputWidget()
+ self.input_span.setFixedHeight(20)
self.input_span.setAlignment(QtCore.Qt.AlignRight)
self.input_span.textEdited.connect(self.update_start_end)
@@ -79,6 +77,7 @@ def __init__(self, app: QtWidgets.QWidget, title: str = "Sweep control"):
self.input_segments = QtWidgets.QLineEdit(self.app.settings.value("Segments", "1"))
self.input_segments.setAlignment(QtCore.Qt.AlignRight)
+ self.input_segments.setFixedHeight(20)
self.input_segments.setFixedWidth(60)
self.input_segments.textEdited.connect(self.update_step_size)
@@ -88,23 +87,26 @@ def __init__(self, app: QtWidgets.QWidget, title: str = "Sweep control"):
segment_layout = QtWidgets.QHBoxLayout()
segment_layout.addWidget(self.input_segments)
segment_layout.addWidget(self.label_step)
- control_layout.addRow(QtWidgets.QLabel("Segments"), segment_layout)
+ self.layout.addRow(QtWidgets.QLabel("Segments"), segment_layout)
btn_settings_window = QtWidgets.QPushButton("Sweep settings ...")
+ btn_settings_window.setFixedHeight(20)
btn_settings_window.clicked.connect(
lambda: self.app.display_window("sweep_settings"))
- control_layout.addRow(btn_settings_window)
+ self.layout.addRow(btn_settings_window)
self.progress_bar = QtWidgets.QProgressBar()
self.progress_bar.setMaximum(100)
self.progress_bar.setValue(0)
- control_layout.addRow(self.progress_bar)
+ self.layout.addRow(self.progress_bar)
self.btn_start = QtWidgets.QPushButton("Sweep")
+ self.btn_start.setFixedHeight(20)
self.btn_start.clicked.connect(self.app.sweep_start)
self.btn_start.setShortcut(QtCore.Qt.Key_W | QtCore.Qt.CTRL)
self.btn_stop = QtWidgets.QPushButton("Stop")
+ self.btn_stop.setFixedHeight(20)
self.btn_stop.clicked.connect(self.app.sweep_stop)
self.btn_stop.setShortcut(QtCore.Qt.Key_Escape)
self.btn_stop.setDisabled(True)
@@ -114,7 +116,7 @@ def __init__(self, app: QtWidgets.QWidget, title: str = "Sweep control"):
btn_layout.setContentsMargins(0, 0, 0, 0)
btn_layout_widget = QtWidgets.QWidget()
btn_layout_widget.setLayout(btn_layout)
- control_layout.addRow(btn_layout_widget)
+ self.layout.addRow(btn_layout_widget)
self.input_start.textEdited.emit(self.input_start.text())
self.input_start.textChanged.emit(self.input_start.text())
diff --git a/NanoVNASaver/Controls/__init__.py b/NanoVNASaver/Controls/__init__.py
index ab0c77d2..c67faef2 100644
--- a/NanoVNASaver/Controls/__init__.py
+++ b/NanoVNASaver/Controls/__init__.py
@@ -1,2 +1,3 @@
from .MarkerControl import MarkerControl
from .SweepControl import SweepControl
+from .SerialControl import SerialControl
diff --git a/NanoVNASaver/Formatting.py b/NanoVNASaver/Formatting.py
index 7c406f88..17b4a9cc 100644
--- a/NanoVNASaver/Formatting.py
+++ b/NanoVNASaver/Formatting.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -36,10 +36,11 @@
FMT_COMPLEX = SITools.Format(max_nr_digits=3, allow_strip=True,
printable_min=0, unprintable_under="- ")
FMT_COMPLEX_NEG = SITools.Format(max_nr_digits=3, allow_strip=True)
+FMT_SHORT = SITools.Format(max_nr_digits=4)
FMT_WAVELENGTH = SITools.Format(max_nr_digits=4, space_str=" ")
FMT_PARSE = SITools.Format(parse_sloppy_unit=True, parse_sloppy_kilo=True,
parse_clamp_min=0)
-
+FMT_PARSE_VALUE = SITools.Format(parse_sloppy_unit=True, parse_sloppy_kilo=True)
def format_frequency(freq: Number) -> str:
return str(SITools.Value(freq, "Hz", FMT_FREQ))
@@ -52,6 +53,8 @@ def format_frequency_inputs(freq: float) -> str:
def format_frequency_short(freq: Number) -> str:
return str(SITools.Value(freq, "Hz", FMT_FREQ_SHORT))
+def format_frequency_chart(freq: Number) -> str:
+ return str(SITools.Value(freq, "", FMT_FREQ_SHORT))
def format_frequency_space(freq: float, fmt=FMT_FREQ_SPACE) -> str:
return str(SITools.Value(freq, "Hz", fmt))
@@ -107,6 +110,18 @@ def format_phase(val: float) -> str:
return f"{math.degrees(val):.2f}""\N{DEGREE SIGN}"
+def format_complex_adm(z: complex, allow_negative: bool = False) -> str:
+ if z == 0:
+ return "- S"
+ adm = 1/z
+
+ fmt_re = FMT_COMPLEX
+ if allow_negative:
+ fmt_re = FMT_COMPLEX_NEG
+ re = SITools.Value(adm.real, fmt=fmt_re)
+ im = SITools.Value(abs(adm.imag), fmt=FMT_COMPLEX)
+ return f"{re}{'-' if adm.imag < 0 else '+'}j{im} S"
+
def format_complex_imp(z: complex, allow_negative: bool = False) -> str:
fmt_re = FMT_COMPLEX
if allow_negative:
@@ -118,9 +133,19 @@ def format_complex_imp(z: complex, allow_negative: bool = False) -> str:
def format_wavelength(length: Number) -> str:
return str(SITools.Value(length, "m", FMT_WAVELENGTH))
+def format_y_axis(val: float, unit: str="") -> str:
+ return str(SITools.Value(val, unit, FMT_SHORT))
def parse_frequency(freq: str) -> int:
try:
return int(SITools.Value(freq, "Hz", FMT_PARSE))
except (ValueError, IndexError):
return -1
+
+def parse_value(val: str, unit: str = "",
+ fmt: SITools.Format = FMT_PARSE_VALUE) -> int:
+ try:
+ val.replace(',', '.')
+ return float(SITools.Value(val, unit, fmt))
+ except (ValueError, IndexError):
+ return 0.0
diff --git a/NanoVNASaver/Hardware/AVNA.py b/NanoVNASaver/Hardware/AVNA.py
index a26ed9a1..267532f9 100644
--- a/NanoVNASaver/Hardware/AVNA.py
+++ b/NanoVNASaver/Hardware/AVNA.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/NanoVNASaver/Hardware/Hardware.py b/NanoVNASaver/Hardware/Hardware.py
index 0d3a3869..68a5d7b0 100644
--- a/NanoVNASaver/Hardware/Hardware.py
+++ b/NanoVNASaver/Hardware/Hardware.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -32,6 +32,7 @@
from NanoVNASaver.Hardware.NanoVNA_H import NanoVNA_H
from NanoVNASaver.Hardware.NanoVNA_H4 import NanoVNA_H4
from NanoVNASaver.Hardware.NanoVNA_V2 import NanoVNA_V2
+from NanoVNASaver.Hardware.TinySA import TinySA
from NanoVNASaver.Hardware.Serial import drain_serial, Interface
@@ -48,10 +49,24 @@
TIMEOUT = 0.2
WAIT = 0.05
+NAME2DEVICE = {
+ "S-A-A-2" : NanoVNA_V2,
+ "AVNA": AVNA,
+ "H4": NanoVNA_H4,
+ "H": NanoVNA_H,
+ "F_V2": NanoVNA_F_V2,
+ "F": NanoVNA_F,
+ "NanoVNA": NanoVNA,
+ "tinySA": TinySA,
+ "Unknown": NanoVNA,
+}
+
# The USB Driver for NanoVNA V2 seems to deliver an
# incompatible hardware info like:
# 'PORTS\\VID_04B4&PID_0008\\DEMO'
# This function will fix it.
+
+
def _fix_v2_hwinfo(dev):
if dev.hwid == r'PORTS\VID_04B4&PID_0008\DEMO':
dev.vid, dev.pid = 0x04b4, 0x0008
@@ -72,64 +87,68 @@ def get_interfaces() -> List[Interface]:
t.name, d.vid, d.pid, d.device)
iface = Interface('serial', t.name)
iface.port = d.device
+ iface.open()
+ iface.comment = get_comment(iface)
+ iface.close()
interfaces.append(iface)
+
+ logger.debug("Interfaces: %s", interfaces)
return interfaces
def get_VNA(iface: Interface) -> 'VNA':
# serial_port.timeout = TIMEOUT
+ return NAME2DEVICE[iface.comment](iface)
+def get_comment(iface: Interface) -> str:
logger.info("Finding correct VNA type...")
with iface.lock:
vna_version = detect_version(iface)
if vna_version == 'v2':
- logger.info("Type: NanoVNA-V2")
- return NanoVNA_V2(iface)
+ return "S-A-A-2"
logger.info("Finding firmware variant...")
info = get_info(iface)
- if info.find("AVNA + Teensy") >= 0:
- logger.info("Type: AVNA")
- return AVNA(iface)
- if info.find("NanoVNA-H 4") >= 0:
- logger.info("Type: NanoVNA-H4")
- vna = NanoVNA_H4(iface)
- return vna
- if info.find("NanoVNA-H") >= 0:
- logger.info("Type: NanoVNA-H")
- vna = NanoVNA_H(iface)
- return vna
- if info.find("NanoVNA-F_V2") >= 0:
- logger.info("Type: NanoVNA-F_V2")
- return NanoVNA_F_V2(iface)
- if info.find("NanoVNA-F") >= 0:
- logger.info("Type: NanoVNA-F")
- return NanoVNA_F(iface)
- if info.find("NanoVNA") >= 0:
- logger.info("Type: Generic NanoVNA")
- return NanoVNA(iface)
+ for search, name in (
+ ("AVNA + Teensy", "AVNA"),
+ ("NanoVNA-H 4", "H4"),
+ ("NanoVNA-H", "H"),
+ ("NanoVNA-F_V2", "F_V2"),
+ ("NanoVNA-F", "F"),
+ ("NanoVNA", "NanoVNA"),
+ ("tinySA", "tinySA"),
+ ):
+ if info.find(search) >= 0:
+ return name
logger.warning("Did not recognize NanoVNA type from firmware.")
- return NanoVNA(iface)
+ return "Unknown"
def detect_version(serial_port: serial.Serial) -> str:
data = ""
for i in range(RETRIES):
+ drain_serial(serial_port)
+ serial_port.write("\r".encode("ascii"))
+ # workaround for some UnicodeDecodeError ... repeat ;-)
drain_serial(serial_port)
serial_port.write("\r".encode("ascii"))
sleep(0.05)
+
data = serial_port.read(128).decode("ascii")
if data.startswith("ch> "):
return "v1"
# -H versions
if data.startswith("\r\nch> "):
return "vh"
+ if data.startswith("\r\n?\r\nch> "):
+ return "vh"
if data.startswith("2"):
return "v2"
logger.debug("Retry detection: %s", i + 1)
logger.error('No VNA detected. Hardware responded to CR with: %s', data)
return ""
+
def get_info(serial_port: serial.Serial) -> str:
for _ in range(RETRIES):
drain_serial(serial_port)
@@ -151,4 +170,5 @@ def get_info(serial_port: serial.Serial) -> str:
logger.debug("Needed retries: %s", retries)
break
lines.append(line)
+ logger.debug("Info output: %s", lines)
return "\n".join(lines)
diff --git a/NanoVNASaver/Hardware/NanoVNA.py b/NanoVNASaver/Hardware/NanoVNA.py
index d0cea41c..4c360093 100644
--- a/NanoVNASaver/Hardware/NanoVNA.py
+++ b/NanoVNASaver/Hardware/NanoVNA.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -47,16 +47,13 @@ def __init__(self, iface: Interface):
def _get_running_frequencies(self):
- if self.name == "NanoVNA":
- logger.debug("Reading values: frequencies")
- try:
- frequencies = super().readValues("frequencies")
- return frequencies[0], frequencies[-1]
- except Exception as e:
- logger.warning("%s reading frequencies", e)
- logger.info("falling back to generic")
- else:
- logger.debug("Name %s, fallback to generic", self.name)
+ logger.debug("Reading values: frequencies")
+ try:
+ frequencies = super().readValues("frequencies")
+ return frequencies[0], frequencies[-1]
+ except Exception as e:
+ logger.warning("%s reading frequencies", e)
+ logger.info("falling back to generic")
return VNA._get_running_frequencies(self)
diff --git a/NanoVNASaver/Hardware/NanoVNA_F.py b/NanoVNASaver/Hardware/NanoVNA_F.py
index 4852fbda..c5efdffc 100644
--- a/NanoVNASaver/Hardware/NanoVNA_F.py
+++ b/NanoVNASaver/Hardware/NanoVNA_F.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,10 +18,6 @@
# along with this program. If not, see .
import logging
-import serial
-import numpy as np
-from PyQt5 import QtGui
-
from NanoVNASaver.Hardware.NanoVNA import NanoVNA
from NanoVNASaver.Hardware.Serial import Interface
diff --git a/NanoVNASaver/Hardware/NanoVNA_F_V2.py b/NanoVNASaver/Hardware/NanoVNA_F_V2.py
index c3fcd92a..b23d44fe 100644
--- a/NanoVNASaver/Hardware/NanoVNA_F_V2.py
+++ b/NanoVNASaver/Hardware/NanoVNA_F_V2.py
@@ -1,8 +1,24 @@
+# NanoVNASaver
+#
+# A python program to view and export Touchstone data from a NanoVNA
+# Copyright (C) 2019, 2020 Rune B. Broberg
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
import logging
-from NanoVNASaver.Hardware.Serial import drain_serial, Interface
+
import serial
-import struct
-import numpy as np
from PyQt5 import QtGui
from NanoVNASaver.Hardware.NanoVNA import NanoVNA
diff --git a/NanoVNASaver/Hardware/NanoVNA_H.py b/NanoVNASaver/Hardware/NanoVNA_H.py
index a8ae05ae..681b0a52 100644
--- a/NanoVNASaver/Hardware/NanoVNA_H.py
+++ b/NanoVNASaver/Hardware/NanoVNA_H.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/NanoVNASaver/Hardware/NanoVNA_H4.py b/NanoVNASaver/Hardware/NanoVNA_H4.py
index be0c14c8..47427083 100644
--- a/NanoVNASaver/Hardware/NanoVNA_H4.py
+++ b/NanoVNASaver/Hardware/NanoVNA_H4.py
@@ -1,7 +1,7 @@
# NanoVNASaver
#
# A python program to view and export Touchstone data from a NanoVNA
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/NanoVNASaver/Hardware/NanoVNA_V2.py b/NanoVNASaver/Hardware/NanoVNA_V2.py
index d45a04cc..e87c7560 100644
--- a/NanoVNASaver/Hardware/NanoVNA_V2.py
+++ b/NanoVNASaver/Hardware/NanoVNA_V2.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -202,6 +202,8 @@ def readValues(self, value) -> List[str]:
ret = [str(x.real) + ' ' + str(x.imag) for x in ret]
return ret
+ return []
+
def resetSweep(self, start: int, stop: int):
self.setSweep(start, stop)
@@ -214,8 +216,8 @@ def readVersion(self) -> 'Version':
sleep(WRITE_SLEEP)
resp = self.serial.read(2)
if len(resp) != 2:
- logger.error("Timeout reading version registers")
- return None
+ logger.error("Timeout reading version registers. Got: %s", resp)
+ raise IOError("Timeout reading version registers")
result = Version(f"{resp[0]}.0.{resp[1]}")
logger.debug("readVersion: %s", result)
return result
diff --git a/NanoVNASaver/Hardware/Serial.py b/NanoVNASaver/Hardware/Serial.py
index b7abdfda..16006374 100644
--- a/NanoVNASaver/Hardware/Serial.py
+++ b/NanoVNASaver/Hardware/Serial.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/NanoVNASaver/Hardware/TinySA.py b/NanoVNASaver/Hardware/TinySA.py
new file mode 100644
index 00000000..cff4e6a5
--- /dev/null
+++ b/NanoVNASaver/Hardware/TinySA.py
@@ -0,0 +1,124 @@
+# NanoVNASaver
+#
+# A python program to view and export Touchstone data from a NanoVNA
+# Copyright (C) 2019, 2020 Rune B. Broberg
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+import logging
+import struct
+from typing import List
+
+import serial
+import numpy as np
+from PyQt5 import QtGui
+
+from NanoVNASaver.Hardware.Serial import drain_serial, Interface
+from NanoVNASaver.Hardware.VNA import VNA
+
+logger = logging.getLogger(__name__)
+
+
+class TinySA(VNA):
+ name = "tinySA"
+ screenwidth = 320
+ screenheight = 240
+ valid_datapoints = (290, )
+
+ def __init__(self, iface: Interface):
+ super().__init__(iface)
+ self.features = set(('Screenshots',))
+ logger.debug("Setting initial start,stop")
+ self.start, self.stop = self._get_running_frequencies()
+ self.sweep_max_freq_Hz = 950e6
+ self._sweepdata = []
+
+ def _get_running_frequencies(self):
+
+ logger.debug("Reading values: frequencies")
+ try:
+ frequencies = super().readValues("frequencies")
+ return frequencies[0], frequencies[-1]
+ except Exception as e:
+ logger.warning("%s reading frequencies", e)
+ logger.info("falling back to generic")
+
+ return VNA._get_running_frequencies(self)
+
+ def _capture_data(self) -> bytes:
+ timeout = self.serial.timeout
+ with self.serial.lock:
+ drain_serial(self.serial)
+ timeout = self.serial.timeout
+ self.serial.write("capture\r".encode('ascii'))
+ self.serial.readline()
+ self.serial.timeout = 4
+ image_data = self.serial.read(
+ self.screenwidth * self.screenheight * 2)
+ self.serial.timeout = timeout
+ self.serial.timeout = timeout
+ return image_data
+
+ def _convert_data(self, image_data: bytes) -> bytes:
+ rgb_data = struct.unpack(
+ f">{self.screenwidth * self.screenheight}H",
+ image_data)
+ rgb_array = np.array(rgb_data, dtype=np.uint32)
+ return (0xFF000000 +
+ ((rgb_array & 0xF800) << 8) +
+ ((rgb_array & 0x07E0) << 5) +
+ ((rgb_array & 0x001F) << 3))
+
+ def getScreenshot(self) -> QtGui.QPixmap:
+ logger.debug("Capturing screenshot...")
+ if not self.connected():
+ return QtGui.QPixmap()
+ try:
+ rgba_array = self._convert_data(self._capture_data())
+ image = QtGui.QImage(
+ rgba_array,
+ self.screenwidth,
+ self.screenheight,
+ QtGui.QImage.Format_ARGB32)
+ logger.debug("Captured screenshot")
+ return QtGui.QPixmap(image)
+ except serial.SerialException as exc:
+ logger.exception(
+ "Exception while capturing screenshot: %s", exc)
+ return QtGui.QPixmap()
+
+ def resetSweep(self, start: int, stop: int):
+ return
+
+ def setSweep(self, start, stop):
+ self.start = start
+ self.stop = stop
+ list(self.exec_command(f"sweep {start} {stop} {self.datapoints}"))
+ list(self.exec_command("trigger auto"))
+
+ def readFrequencies(self) -> List[int]:
+ logger.debug("readFrequencies")
+ return [int(line) for line in self.exec_command("frequencies")]
+
+ def readValues(self, value) -> List[str]:
+ logger.debug("Read: %s", value)
+ if value == "data 0":
+ self._sweepdata = []
+ for line in self.exec_command("data"):
+ self._sweepdata.append(f"0 {line.strip()}")
+ return self._sweepdata
+ if value == "data 0":
+ return [x[0] for x in self._sweepdata]
+ if value == "data 1":
+ return [x[0] for x in self._sweepdata]
diff --git a/NanoVNASaver/Hardware/VNA.py b/NanoVNASaver/Hardware/VNA.py
index fd4fe1ec..ae749b46 100644
--- a/NanoVNASaver/Hardware/VNA.py
+++ b/NanoVNASaver/Hardware/VNA.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/NanoVNASaver/Inputs.py b/NanoVNASaver/Inputs.py
index 494dfbfb..ca73605c 100644
--- a/NanoVNASaver/Inputs.py
+++ b/NanoVNASaver/Inputs.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/NanoVNASaver/Marker/Delta.py b/NanoVNASaver/Marker/Delta.py
index 7488ea9e..1f068768 100644
--- a/NanoVNASaver/Marker/Delta.py
+++ b/NanoVNASaver/Marker/Delta.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
from NanoVNASaver import RFTools
from NanoVNASaver.Formatting import (
format_capacitance,
+ format_complex_adm,
format_complex_imp,
format_frequency_space,
format_gain,
@@ -51,8 +52,8 @@ def set_markers(self, marker_a: Marker, marker_b: Marker):
def updateLabels(self): # pylint: disable=arguments-differ
a = self.marker_a
b = self.marker_b
- s11_a = a.s11data[1]
- s11_b = b.s11data[1]
+ s11_a = a.s11[1]
+ s11_b = b.s11[1]
imp_a = s11_a.impedance()
imp_b = s11_b.impedance()
@@ -90,7 +91,7 @@ def updateLabels(self): # pylint: disable=arguments-differ
format_frequency_space(s11_b.freq - s11_a.freq))
self.label['lambda'].setText(
format_wavelength(s11_b.wavelength - s11_a.wavelength))
- self.label['admittance'].setText(format_complex_imp(imp_p, True))
+ self.label['admittance'].setText(format_complex_adm(imp_p, True))
self.label['impedance'].setText(format_complex_imp(imp, True))
self.label['parc'].setText(cap_p_str)
@@ -101,8 +102,8 @@ def updateLabels(self): # pylint: disable=arguments-differ
self.label['returnloss'].setText(
format_gain(s11_b.gain - s11_a.gain, self.returnloss_is_positive))
self.label['s11groupdelay'].setText(format_group_delay(
- RFTools.groupDelay(b.s11data, 1) -
- RFTools.groupDelay(a.s11data, 1)))
+ RFTools.groupDelay(b.s11, 1) -
+ RFTools.groupDelay(a.s11, 1)))
self.label['s11mag'].setText(
format_magnitude(abs(s11_b.z) - abs(s11_a.z)))
@@ -119,14 +120,14 @@ def updateLabels(self): # pylint: disable=arguments-differ
self.label['serr'].setText(format_resistance(imp.real, True))
self.label['vswr'].setText(format_vswr(s11_b.vswr - s11_a.vswr))
- if len(a.s21data) == len(a.s11data):
- s21_a = a.s21data[1]
- s21_b = b.s21data[1]
+ if len(a.s21) == len(a.s11):
+ s21_a = a.s21[1]
+ s21_b = b.s21[1]
self.label['s21gain'].setText(format_gain(
s21_b.gain - s21_a.gain))
self.label['s21groupdelay'].setText(format_group_delay(
- (RFTools.groupDelay(b.s21data, 1) -
- RFTools.groupDelay(a.s21data, 1)) / 2))
+ (RFTools.groupDelay(b.s21, 1) -
+ RFTools.groupDelay(a.s21, 1)) / 2))
self.label['s21mag'].setText(format_magnitude(
abs(s21_b.z) - abs(s21_a.z)))
self.label['s21phase'].setText(format_phase(
diff --git a/NanoVNASaver/Marker/Values.py b/NanoVNASaver/Marker/Values.py
index e1b4193b..ee58d071 100644
--- a/NanoVNASaver/Marker/Values.py
+++ b/NanoVNASaver/Marker/Values.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -68,26 +68,26 @@ def default_label_ids() -> str:
class Value():
"""Contains the data area to calculate marker values from"""
def __init__(self, freq: int = 0,
- s11data: List[Datapoint] = None,
- s21data: List[Datapoint] = None):
+ s11: List[Datapoint] = None,
+ s21: List[Datapoint] = None):
self.freq = freq
- self.s11data = [] if s11data is None else s11data[:]
- self.s21data = [] if s21data is None else s21data[:]
+ self.s11 = [] if s11 is None else s11[:]
+ self.s21 = [] if s21 is None else s21[:]
def store(self, index: int,
- s11data: List[Datapoint],
- s21data: List[Datapoint]):
+ s11: List[Datapoint],
+ s21: List[Datapoint]):
# handle boundaries
if index == 0:
index = 1
- s11data = [s11data[0], ] + s11data
- if s21data:
- s21data = [s21data[0], ] + s21data
- if index == len(s11data):
- s11data = s11data + [s11data[-1], ]
- if s21data:
- s21data = s21data + [s21data[-1], ]
- self.freq = s11data[1].freq
- self.s11data = s11data[index-1:index+2]
- if s21data:
- self.s21data = s21data[index-1:index+2]
+ s11 = [s11[0], ] + s11
+ if s21:
+ s21 = [s21[0], ] + s21
+ if index == len(s11):
+ s11 = s11 + [s11[-1], ]
+ if s21:
+ s21 = s21 + [s21[-1], ]
+ self.freq = s11[1].freq
+ self.s11 = s11[index-1:index+2]
+ if s21:
+ self.s21 = s21[index-1:index+2]
diff --git a/NanoVNASaver/Marker/Widget.py b/NanoVNASaver/Marker/Widget.py
index 5708f426..cf19aa19 100644
--- a/NanoVNASaver/Marker/Widget.py
+++ b/NanoVNASaver/Marker/Widget.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -25,6 +25,7 @@
from NanoVNASaver import RFTools
from NanoVNASaver.Formatting import (
format_capacitance,
+ format_complex_adm,
format_complex_imp,
format_frequency_space,
format_gain,
@@ -87,6 +88,7 @@ def __init__(self, name: str = "", qsettings: QtCore.QSettings = None):
self.name = f"Marker {Marker._instances}"
self.frequencyInput = FrequencyInput()
+ self.frequencyInput.setMinimumHeight(20)
self.frequencyInput.setAlignment(QtCore.Qt.AlignRight)
self.frequencyInput.editingFinished.connect(
lambda: self.setFrequency(
@@ -107,6 +109,7 @@ def __init__(self, name: str = "", qsettings: QtCore.QSettings = None):
###############################################################
self.btnColorPicker = QtWidgets.QPushButton("â–ˆ")
+ self.btnColorPicker.setMinimumHeight(20)
self.btnColorPicker.setFixedWidth(20)
self.btnColorPicker.clicked.connect(
lambda: self.setColor(QtWidgets.QColorDialog.getColor(
@@ -141,7 +144,9 @@ def __init__(self, name: str = "", qsettings: QtCore.QSettings = None):
# line only if more then 3 selected
self.left_form = QtWidgets.QFormLayout()
+ self.left_form.setVerticalSpacing(0)
self.right_form = QtWidgets.QFormLayout()
+ self.right_form.setVerticalSpacing(0)
box_layout.addLayout(self.left_form)
box_layout.addWidget(line)
box_layout.addLayout(self.right_form)
@@ -281,32 +286,36 @@ def resetLabels(self):
v.setText("")
def updateLabels(self,
- s11data: List[RFTools.Datapoint],
- s21data: List[RFTools.Datapoint]):
- if not s11data:
+ s11: List[RFTools.Datapoint],
+ s21: List[RFTools.Datapoint]):
+ if not s11:
return
if self.location == -1: # initial position
try:
- location = (self.index -1) / (self._instances - 1) * (len(s11data) - 1)
+ location = (self.index -1) / (self._instances - 1) * (len(s11) - 1)
self.location = int(location)
except ZeroDivisionError:
self.location = 0
try:
- s11 = s11data[self.location]
+ _s11 = s11[self.location]
except IndexError:
self.location = 0
return
- self.frequencyInput.setText(s11.freq)
- self.store(self.location, s11data, s21data)
+ self.frequencyInput.setText(_s11.freq)
+ self.store(self.location, s11, s21)
- imp = s11.impedance()
- cap_str = format_capacitance(RFTools.impedance_to_capacitance(imp, s11.freq))
- ind_str = format_inductance(RFTools.impedance_to_inductance(imp, s11.freq))
+ imp = _s11.impedance()
+ cap_str = format_capacitance(
+ RFTools.impedance_to_capacitance(imp, _s11.freq))
+ ind_str = format_inductance(
+ RFTools.impedance_to_inductance(imp, _s11.freq))
imp_p = RFTools.serial_to_parallel(imp)
- cap_p_str = format_capacitance(RFTools.impedance_to_capacitance(imp_p, s11.freq))
- ind_p_str = format_inductance(RFTools.impedance_to_inductance(imp_p, s11.freq))
+ cap_p_str = format_capacitance(
+ RFTools.impedance_to_capacitance(imp_p, _s11.freq))
+ ind_p_str = format_inductance(
+ RFTools.impedance_to_inductance(imp_p, _s11.freq))
if imp.imag < 0:
x_str = cap_str
@@ -318,40 +327,44 @@ def updateLabels(self,
else:
x_p_str = ind_p_str
- self.label['actualfreq'].setText(format_frequency_space(s11.freq))
- self.label['lambda'].setText(format_wavelength(s11.wavelength))
- self.label['admittance'].setText(format_complex_imp(imp_p))
+ self.label['actualfreq'].setText(format_frequency_space(_s11.freq))
+ self.label['lambda'].setText(format_wavelength(_s11.wavelength))
+ self.label['admittance'].setText(format_complex_adm(imp))
self.label['impedance'].setText(format_complex_imp(imp))
self.label['parc'].setText(cap_p_str)
self.label['parl'].setText(ind_p_str)
self.label['parlc'].setText(x_p_str)
self.label['parr'].setText(format_resistance(imp_p.real))
self.label['returnloss'].setText(
- format_gain(s11.gain, self.returnloss_is_positive))
+ format_gain(_s11.gain, self.returnloss_is_positive))
self.label['s11groupdelay'].setText(
- format_group_delay(RFTools.groupDelay(s11data, self.location)))
- self.label['s11mag'].setText(format_magnitude(abs(s11.z)))
- self.label['s11phase'].setText(format_phase(s11.phase))
+ format_group_delay(RFTools.groupDelay(s11, self.location)))
+ self.label['s11mag'].setText(format_magnitude(abs(_s11.z)))
+ self.label['s11phase'].setText(format_phase(_s11.phase))
self.label['s11polar'].setText(
- str(round(abs(s11.z), 2)) + "∠" + format_phase(s11.phase))
- self.label['s11q'].setText(format_q_factor(s11.qFactor()))
+ str(round(abs(_s11.z), 2)) + "∠" + format_phase(_s11.phase))
+ self.label['s11q'].setText(format_q_factor(_s11.qFactor()))
self.label['s11z'].setText(format_resistance(abs(imp)))
self.label['serc'].setText(cap_str)
self.label['serl'].setText(ind_str)
self.label['serlc'].setText(x_str)
self.label['serr'].setText(format_resistance(imp.real))
- self.label['vswr'].setText(format_vswr(s11.vswr))
+ self.label['vswr'].setText(format_vswr(_s11.vswr))
- if len(s21data) == len(s11data):
- s21 = s21data[self.location]
- self.label['s21gain'].setText(format_gain(s21.gain))
+ if len(s21) == len(s11):
+ _s21 = s21[self.location]
+ self.label['s21gain'].setText(format_gain(_s21.gain))
self.label['s21groupdelay'].setText(
- format_group_delay(RFTools.groupDelay(s21data, self.location) / 2))
- self.label['s21mag'].setText(format_magnitude(abs(s21.z)))
- self.label['s21phase'].setText(format_phase(s21.phase))
+ format_group_delay(RFTools.groupDelay(s21, self.location) / 2))
+ self.label['s21mag'].setText(format_magnitude(abs(_s21.z)))
+ self.label['s21phase'].setText(format_phase(_s21.phase))
self.label['s21polar'].setText(
- str(round(abs(s21.z), 2)) + "∠" + format_phase(s21.phase))
- self.label['s21magshunt'].setText(format_magnitude(abs(s21.shuntImpedance())))
- self.label['s21magseries'].setText(format_magnitude(abs(s21.seriesImpedance())))
- self.label['s21realimagshunt'].setText(format_complex_imp(s21.shuntImpedance(), allow_negative=True))
- self.label['s21realimagseries'].setText(format_complex_imp(s21.seriesImpedance(), allow_negative=True))
+ str(round(abs(_s21.z), 2)) + "∠" + format_phase(_s21.phase))
+ self.label['s21magshunt'].setText(
+ format_magnitude(abs(_s21.shuntImpedance())))
+ self.label['s21magseries'].setText(
+ format_magnitude(abs(_s21.seriesImpedance())))
+ self.label['s21realimagshunt'].setText(
+ format_complex_imp(_s21.shuntImpedance(), allow_negative=True))
+ self.label['s21realimagseries'].setText(
+ format_complex_imp(_s21.seriesImpedance(), allow_negative=True))
diff --git a/NanoVNASaver/NanoVNASaver.py b/NanoVNASaver/NanoVNASaver.py
index 6a94c1b1..6818f5b0 100644
--- a/NanoVNASaver/NanoVNASaver.py
+++ b/NanoVNASaver/NanoVNASaver.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,21 +20,20 @@
import sys
import threading
from collections import OrderedDict
-from time import sleep, strftime, localtime
-from typing import List
+from time import strftime, localtime
from PyQt5 import QtWidgets, QtCore, QtGui
from .Windows import (
AboutWindow, AnalysisWindow, CalibrationWindow,
DeviceSettingsWindow, DisplaySettingsWindow, SweepSettingsWindow,
- TDRWindow
+ TDRWindow, FilesWindow
)
-from .Controls import MarkerControl, SweepControl
+from .Controls import MarkerControl, SweepControl, SerialControl
from .Formatting import format_frequency, format_vswr, format_gain
-from .Hardware.Hardware import Interface, get_interfaces, get_VNA
+from .Hardware.Hardware import Interface
from .Hardware.VNA import VNA
-from .RFTools import Datapoint, corr_att_data
+from .RFTools import corr_att_data
from .Charts.Chart import Chart
from .Charts import (
CapacitanceChart,
@@ -72,7 +71,7 @@ def __init__(self):
self.settings = QtCore.QSettings(QtCore.QSettings.IniFormat,
QtCore.QSettings.UserScope,
"NanoVNASaver", "NanoVNASaver")
- print(f"Settings: {self.settings.fileName()}")
+ logger.info("Settings from: %s", self.settings.fileName())
self.threadpool = QtCore.QThreadPool()
self.sweep = Sweep()
self.worker = SweepWorker(self)
@@ -80,7 +79,6 @@ def __init__(self):
self.worker.signals.updated.connect(self.dataUpdated)
self.worker.signals.finished.connect(self.sweepFinished)
self.worker.signals.sweepError.connect(self.showSweepError)
- self.worker.signals.fatalSweepError.connect(self.showFatalSweepError)
self.markers = []
@@ -91,18 +89,19 @@ def __init__(self):
self.sweep_control = SweepControl(self)
self.marker_control = MarkerControl(self)
+ self.serial_control = SerialControl(self)
self.bands = BandsModel()
self.interface = Interface("serial", "None")
- self.vna = VNA(self.interface)
+ try:
+ self.vna = VNA(self.interface)
+ except IOError as exc:
+ self.showError(f"{exc}\n\nPlease try reconnect")
self.dataLock = threading.Lock()
- # TODO: use Touchstone class as data container
- self.data11: List[Datapoint] = []
- self.data21: List[Datapoint] = []
- self.referenceS11data: List[Datapoint] = []
- self.referenceS21data: List[Datapoint] = []
+ self.data = Touchstone()
+ self.ref_data = Touchstone()
self.sweepSource = ""
self.referenceSource = ""
@@ -179,8 +178,9 @@ def __init__(self):
self.combinedCharts = list(self.charts["combined"].values())
# List of all charts that can be selected for display
- self.selectable_charts = self.s11charts + self.s21charts + self.combinedCharts
- self.selectable_charts.append(self.tdr_mainwindow_chart)
+ self.selectable_charts = (
+ self.s11charts + self.s21charts +
+ self.combinedCharts + [self.tdr_mainwindow_chart, ])
# List of all charts that subscribe to updates (including duplicates!)
self.subscribing_charts = []
@@ -192,6 +192,8 @@ def __init__(self):
self.charts_layout = QtWidgets.QGridLayout()
+ QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+Q"), self, self.close)
+
###############################################################
# Create main layout
###############################################################
@@ -224,7 +226,7 @@ def __init__(self):
# "analysis": AnalysisWindow(self),
"calibration": CalibrationWindow(self),
"device_settings": DeviceSettingsWindow(self),
- "file": QtWidgets.QWidget(),
+ "file": FilesWindow(self),
"sweep_settings": SweepSettingsWindow(self),
"setup": DisplaySettingsWindow(self),
"tdr": TDRWindow(self),
@@ -253,7 +255,7 @@ def __init__(self):
self.marker_data_layout.addWidget(m.get_data_layout())
scroll2 = QtWidgets.QScrollArea()
- scroll2.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
+ #scroll2.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
scroll2.setWidgetResizable(True)
scroll2.setVisible(True)
@@ -275,6 +277,7 @@ def __init__(self):
s11_control_box = QtWidgets.QGroupBox()
s11_control_box.setTitle("S11")
s11_control_layout = QtWidgets.QFormLayout()
+ s11_control_layout.setVerticalSpacing(0)
s11_control_box.setLayout(s11_control_layout)
self.s11_min_swr_label = QtWidgets.QLabel()
@@ -287,6 +290,7 @@ def __init__(self):
s21_control_box = QtWidgets.QGroupBox()
s21_control_box.setTitle("S21")
s21_control_layout = QtWidgets.QFormLayout()
+ s21_control_layout.setVerticalSpacing(0)
s21_control_box.setLayout(s21_control_layout)
self.s21_min_gain_label = QtWidgets.QLabel()
@@ -301,6 +305,7 @@ def __init__(self):
self.windows["analysis"] = AnalysisWindow(self)
btn_show_analysis = QtWidgets.QPushButton("Analysis ...")
+ btn_show_analysis.setMinimumHeight(20)
btn_show_analysis.clicked.connect(
lambda: self.display_window("analysis"))
self.marker_column.addWidget(btn_show_analysis)
@@ -318,14 +323,16 @@ def __init__(self):
tdr_control_box.setTitle("TDR")
tdr_control_layout = QtWidgets.QFormLayout()
tdr_control_box.setLayout(tdr_control_layout)
- tdr_control_box.setMaximumWidth(250)
+ tdr_control_box.setMaximumWidth(240)
self.tdr_result_label = QtWidgets.QLabel()
+ self.tdr_result_label.setMinimumHeight(20)
tdr_control_layout.addRow(
"Estimated cable length:", self.tdr_result_label)
self.tdr_button = QtWidgets.QPushButton(
"Time Domain Reflectometry ...")
+ self.tdr_button.setMinimumHeight(20)
self.tdr_button.clicked.connect(lambda: self.display_window("tdr"))
tdr_control_layout.addRow(self.tdr_button)
@@ -345,13 +352,15 @@ def __init__(self):
###############################################################
reference_control_box = QtWidgets.QGroupBox()
- reference_control_box.setMaximumWidth(250)
+ reference_control_box.setMaximumWidth(240)
reference_control_box.setTitle("Reference sweep")
reference_control_layout = QtWidgets.QFormLayout(reference_control_box)
btn_set_reference = QtWidgets.QPushButton("Set current as reference")
+ btn_set_reference.setMinimumHeight(20)
btn_set_reference.clicked.connect(self.setReference)
self.btnResetReference = QtWidgets.QPushButton("Reset reference")
+ self.btnResetReference.setMinimumHeight(20)
self.btnResetReference.clicked.connect(self.resetReference)
self.btnResetReference.setDisabled(True)
@@ -364,84 +373,14 @@ def __init__(self):
# Serial control
###############################################################
- serial_control_box = QtWidgets.QGroupBox()
- serial_control_box.setMaximumWidth(250)
- serial_control_box.setTitle("Serial port control")
- serial_control_layout = QtWidgets.QFormLayout(serial_control_box)
- self.serialPortInput = QtWidgets.QComboBox()
- self.rescanSerialPort()
- self.serialPortInput.setEditable(True)
- btn_rescan_serial_port = QtWidgets.QPushButton("Rescan")
- btn_rescan_serial_port.setFixedWidth(65)
- btn_rescan_serial_port.clicked.connect(self.rescanSerialPort)
- serial_port_input_layout = QtWidgets.QHBoxLayout()
- serial_port_input_layout.addWidget(self.serialPortInput)
- serial_port_input_layout.addWidget(btn_rescan_serial_port)
- serial_control_layout.addRow(
- QtWidgets.QLabel("Serial port"), serial_port_input_layout)
-
- serial_button_layout = QtWidgets.QHBoxLayout()
-
- self.btnSerialToggle = QtWidgets.QPushButton("Connect to device")
- self.btnSerialToggle.clicked.connect(self.serialButtonClick)
- serial_button_layout.addWidget(self.btnSerialToggle, stretch=1)
-
- self.btnDeviceSettings = QtWidgets.QPushButton("Manage")
- self.btnDeviceSettings.setFixedWidth(65)
- self.btnDeviceSettings.clicked.connect(
- lambda: self.display_window("device_settings"))
- serial_button_layout.addWidget(self.btnDeviceSettings, stretch=0)
- serial_control_layout.addRow(serial_button_layout)
- left_column.addWidget(serial_control_box)
-
- ###############################################################
- # File control
- ###############################################################
-
- self.windows["file"].setWindowTitle("Files")
- self.windows["file"].setWindowIcon(self.icon)
- self.windows["file"].setMinimumWidth(200)
- QtWidgets.QShortcut(QtCore.Qt.Key_Escape, self.windows["file"],
- self.windows["file"].hide)
- file_window_layout = QtWidgets.QVBoxLayout()
- self.windows["file"].setLayout(file_window_layout)
-
- load_file_control_box = QtWidgets.QGroupBox("Import file")
- load_file_control_box.setMaximumWidth(300)
- load_file_control_layout = QtWidgets.QFormLayout(load_file_control_box)
-
- btn_load_sweep = QtWidgets.QPushButton("Load as sweep")
- btn_load_sweep.clicked.connect(self.loadSweepFile)
- btn_load_reference = QtWidgets.QPushButton("Load reference")
- btn_load_reference.clicked.connect(self.loadReferenceFile)
- load_file_control_layout.addRow(btn_load_sweep)
- load_file_control_layout.addRow(btn_load_reference)
-
- file_window_layout.addWidget(load_file_control_box)
-
- save_file_control_box = QtWidgets.QGroupBox("Export file")
- save_file_control_box.setMaximumWidth(300)
- save_file_control_layout = QtWidgets.QFormLayout(save_file_control_box)
-
- btn_export_file = QtWidgets.QPushButton("Save 1-Port file (S1P)")
- btn_export_file.clicked.connect(lambda: self.exportFile(1))
- save_file_control_layout.addRow(btn_export_file)
-
- btn_export_file = QtWidgets.QPushButton("Save 2-Port file (S2P)")
- btn_export_file.clicked.connect(lambda: self.exportFile(4))
- save_file_control_layout.addRow(btn_export_file)
-
- file_window_layout.addWidget(save_file_control_box)
-
- btn_open_file_window = QtWidgets.QPushButton("Files ...")
- btn_open_file_window.clicked.connect(
- lambda: self.display_window("file"))
+ left_column.addWidget(self.serial_control)
###############################################################
# Calibration
###############################################################
btnOpenCalibrationWindow = QtWidgets.QPushButton("Calibration ...")
+ btnOpenCalibrationWindow.setMinimumHeight(20)
self.calibrationWindow = CalibrationWindow(self)
btnOpenCalibrationWindow.clicked.connect(
lambda: self.display_window("calibration"))
@@ -451,16 +390,26 @@ def __init__(self):
###############################################################
btn_display_setup = QtWidgets.QPushButton("Display setup ...")
- btn_display_setup.setMaximumWidth(250)
+ btn_display_setup.setMinimumHeight(20)
+ btn_display_setup.setMaximumWidth(240)
btn_display_setup.clicked.connect(
lambda: self.display_window("setup"))
btn_about = QtWidgets.QPushButton("About ...")
- btn_about.setMaximumWidth(250)
+ btn_about.setMinimumHeight(20)
+ btn_about.setMaximumWidth(240)
btn_about.clicked.connect(
lambda: self.display_window("about"))
+
+ btn_open_file_window = QtWidgets.QPushButton("Files")
+ btn_open_file_window.setMinimumHeight(20)
+ btn_open_file_window.setMaximumWidth(240)
+
+ btn_open_file_window.clicked.connect(
+ lambda: self.display_window("file"))
+
button_grid = QtWidgets.QGridLayout()
button_grid.addWidget(btn_open_file_window, 0, 0)
button_grid.addWidget(btnOpenCalibrationWindow, 0, 1)
@@ -470,119 +419,6 @@ def __init__(self):
logger.debug("Finished building interface")
- def rescanSerialPort(self):
- self.serialPortInput.clear()
- for iface in get_interfaces():
- self.serialPortInput.insertItem(1, f"{iface}", iface)
- self.serialPortInput.repaint()
-
- def exportFile(self, nr_params: int = 1):
- if len(self.data11) == 0:
- QtWidgets.QMessageBox.warning(
- self, "No data to save", "There is no data to save.")
- return
- if nr_params > 2 and len(self.data21) == 0:
- QtWidgets.QMessageBox.warning(
- self, "No S21 data to save", "There is no S21 data to save.")
- return
-
- filedialog = QtWidgets.QFileDialog(self)
- if nr_params == 1:
- filedialog.setDefaultSuffix("s1p")
- filedialog.setNameFilter(
- "Touchstone 1-Port Files (*.s1p);;All files (*.*)")
- else:
- filedialog.setDefaultSuffix("s2p")
- filedialog.setNameFilter(
- "Touchstone 2-Port Files (*.s2p);;All files (*.*)")
- filedialog.setAcceptMode(QtWidgets.QFileDialog.AcceptSave)
- selected = filedialog.exec()
- if not selected:
- return
- filename = filedialog.selectedFiles()[0]
- if filename == "":
- logger.debug("No file name selected.")
- return
-
- ts = Touchstone(filename)
- ts.sdata[0] = self.data11
- if nr_params > 1:
- ts.sdata[1] = self.data21
- for dp in self.data11:
- ts.sdata[2].append(Datapoint(dp.freq, 0, 0))
- ts.sdata[3].append(Datapoint(dp.freq, 0, 0))
- try:
- ts.save(nr_params)
- except IOError as e:
- logger.exception("Error during file export: %s", e)
- return
-
- def serialButtonClick(self):
- if not self.vna.connected():
- self.connect_device()
- else:
- self.disconnect_device()
-
- def connect_device(self):
- if not self.interface:
- return
- with self.interface.lock:
- self.interface = self.serialPortInput.currentData()
- logger.info("Connection %s", self.interface)
- try:
- self.interface.open()
-
- except (IOError, AttributeError) as exc:
- logger.error("Tried to open %s and failed: %s",
- self.interface, exc)
- return
- if not self.interface.isOpen():
- logger.error("Unable to open port %s", self.interface)
- return
- self.interface.timeout = 0.05
- sleep(0.1)
- try:
- self.vna = get_VNA(self.interface)
- except IOError as exc:
- logger.error("Unable to connect to VNA: %s", exc)
-
- self.vna.validateInput = self.settings.value(
- "SerialInputValidation", True, bool)
-
- # connected
- self.btnSerialToggle.setText("Disconnect")
- self.btnSerialToggle.repaint()
-
- frequencies = self.vna.readFrequencies()
- if not frequencies:
- logger.warning("No frequencies read")
- return
- logger.info("Read starting frequency %s and end frequency %s",
- frequencies[0], frequencies[-1])
- self.sweep_control.set_start(frequencies[0])
- if frequencies[0] < frequencies[-1]:
- self.sweep_control.set_end(frequencies[-1])
- else:
- self.sweep_control.set_end(
- frequencies[0] +
- self.vna.datapoints * self.sweep_control.get_segments())
-
- self.sweep_control.set_segments(1) # speed up things
- self.sweep_control.update_center_span()
- self.sweep_control.update_step_size()
-
- self.windows["sweep_settings"].vna_connected()
-
- logger.debug("Starting initial sweep")
- self.sweep_start()
-
- def disconnect_device(self):
- with self.interface.lock:
- logger.info("Closing connection to %s", self.interface)
- self.interface.close()
- self.btnSerialToggle.setText("Connect to device")
- self.btnSerialToggle.repaint()
-
def sweep_start(self):
# Run the device data update
if not self.vna.connected():
@@ -612,10 +448,10 @@ def sweep_stop(self):
def saveData(self, data, data21, source=None):
with self.dataLock:
- self.data11 = data
- self.data21 = data21
+ self.data.s11 = data
+ self.data.s21 = data21
if self.s21att > 0:
- self.data21 = corr_att_data(self.data21, self.s21att)
+ self.data.s21 = corr_att_data(self.data.s21, self.s21att)
if source is not None:
self.sweepSource = source
else:
@@ -626,9 +462,9 @@ def saveData(self, data, data21, source=None):
def markerUpdated(self, marker: Marker):
with self.dataLock:
- marker.findLocation(self.data11)
+ marker.findLocation(self.data.s11)
marker.resetLabels()
- marker.updateLabels(self.data11, self.data21)
+ marker.updateLabels(self.data.s11, self.data.s21)
for c in self.subscribing_charts:
c.update()
if Marker.count() >= 2 and not self.delta_marker_layout.isHidden():
@@ -641,27 +477,27 @@ def markerUpdated(self, marker: Marker):
def dataUpdated(self):
with self.dataLock:
- s11data = self.data11[:]
- s21data = self.data21[:]
+ s11 = self.data.s11[:]
+ s21 = self.data.s21[:]
for m in self.markers:
m.resetLabels()
- m.updateLabels(s11data, s21data)
+ m.updateLabels(s11, s21)
for c in self.s11charts:
- c.setData(s11data)
+ c.setData(s11)
for c in self.s21charts:
- c.setData(s21data)
+ c.setData(s21)
for c in self.combinedCharts:
- c.setCombinedData(s11data, s21data)
+ c.setCombinedData(s11, s21)
self.sweep_control.progress_bar.setValue(self.worker.percentage)
self.windows["tdr"].updateTDR()
- if s11data:
- min_vswr = min(s11data, key=lambda data: data.vswr)
+ if s11:
+ min_vswr = min(s11, key=lambda data: data.vswr)
self.s11_min_swr_label.setText(
f"{format_vswr(min_vswr.vswr)} @ {format_frequency(min_vswr.freq)}")
self.s11_min_rl_label.setText(format_gain(min_vswr.gain))
@@ -669,9 +505,9 @@ def dataUpdated(self):
self.s11_min_swr_label.setText("")
self.s11_min_rl_label.setText("")
- if s21data:
- min_gain = min(s21data, key=lambda data: data.gain)
- max_gain = max(s21data, key=lambda data: data.gain)
+ if s21:
+ min_gain = min(s21, key=lambda data: data.gain)
+ max_gain = max(s21, key=lambda data: data.gain)
self.s21_min_gain_label.setText(
f"{format_gain(min_gain.gain)}"
f" @ {format_frequency(min_gain.freq)}")
@@ -695,22 +531,22 @@ def sweepFinished(self):
marker.frequencyInput.textEdited.emit(
marker.frequencyInput.text())
- def setReference(self, s11data=None, s21data=None, source=None):
- if not s11data:
+ def setReference(self, s11=None, s21=None, source=None):
+ if not s11:
with self.dataLock:
- s11data = self.data11[:]
- s21data = self.data21[:]
+ s11 = self.data.s11[:]
+ s21 = self.data.s21[:]
- self.referenceS11data = s11data
+ self.ref_data.s11 = s11
for c in self.s11charts:
- c.setReference(s11data)
+ c.setReference(s11)
- self.referenceS21data = s21data
+ self.ref_data.s21 = s21
for c in self.s21charts:
- c.setReference(s21data)
+ c.setReference(s21)
for c in self.combinedCharts:
- c.setCombinedReference(s11data, s21data)
+ c.setCombinedReference(s11, s21)
self.btnResetReference.setDisabled(False)
@@ -725,45 +561,24 @@ def updateTitle(self):
insert = "("
if self.sweepSource != "":
insert += (
- f"Sweep: {self.sweepSource} @ {len(self.data11)} points"
+ f"Sweep: {self.sweepSource} @ {len(self.data.s11)} points"
f"{', ' if self.referenceSource else ''}")
if self.referenceSource != "":
insert += (
f"Reference: {self.referenceSource} @"
- f" {len(self.referenceS11data)} points")
+ f" {len(self.ref_data.s11)} points")
insert += ")"
title = f"{self.baseTitle} {insert if insert else ''}"
self.setWindowTitle(title)
def resetReference(self):
- self.referenceS11data = []
- self.referenceS21data = []
+ self.ref_data = Touchstone()
self.referenceSource = ""
self.updateTitle()
for c in self.subscribing_charts:
c.resetReference()
self.btnResetReference.setDisabled(True)
- def loadReferenceFile(self):
- filename, _ = QtWidgets.QFileDialog.getOpenFileName(
- filter="Touchstone Files (*.s1p *.s2p);;All files (*.*)")
- if filename != "":
- self.resetReference()
- t = Touchstone(filename)
- t.load()
- self.setReference(t.s11data, t.s21data, filename)
-
- def loadSweepFile(self):
- filename, _ = QtWidgets.QFileDialog.getOpenFileName(
- filter="Touchstone Files (*.s1p *.s2p);;All files (*.*)")
- if filename != "":
- self.data11 = []
- self.data21 = []
- t = Touchstone(filename)
- t.load()
- self.saveData(t.s11data, t.s21data, filename)
- self.dataUpdated()
-
def sizeHint(self) -> QtCore.QSize:
return QtCore.QSize(1100, 950)
@@ -774,10 +589,6 @@ def display_window(self, name):
def showError(self, text):
QtWidgets.QMessageBox.warning(self, "Error", text)
- def showFatalSweepError(self):
- self.showError(self.worker.error_message)
- self.stopSerial()
-
def showSweepError(self):
self.showError(self.worker.error_message)
try:
@@ -829,8 +640,8 @@ def changeFont(self, font: QtGui.QFont) -> None:
qf_normal = QtGui.QFontMetricsF(normal_font)
# Characters we would normally display
standard_string = "0.123456789 0.123456789 MHz \N{OHM SIGN}"
- new_width = qf_new.boundingRect(standard_string).width()
- old_width = qf_normal.boundingRect(standard_string).width()
+ new_width = qf_new.horizontalAdvance(standard_string)
+ old_width = qf_normal.horizontalAdvance(standard_string)
self.scaleFactor = new_width / old_width
logger.debug("New font width: %f, normal font: %f, factor: %f",
new_width, old_width, self.scaleFactor)
@@ -842,6 +653,3 @@ def changeFont(self, font: QtGui.QFont) -> None:
def update_sweep_title(self):
for c in self.subscribing_charts:
c.setSweepTitle(self.sweep.properties.name)
-
- def set_tx_power(self, freq_range, power_desc):
- self.vna.setTXPower(freq_range, power_desc)
diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py
index d52429eb..f52ac57e 100644
--- a/NanoVNASaver/RFTools.py
+++ b/NanoVNASaver/RFTools.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,11 +18,7 @@
# along with this program. If not, see .
import math
import cmath
-from threading import Lock
-from typing import Iterator, List, NamedTuple, Tuple
-
-import numpy as np
-from scipy.interpolate import interp1d
+from typing import List, NamedTuple
from NanoVNASaver.SITools import Format, clamp_value
@@ -38,8 +34,7 @@ class Datapoint(NamedTuple):
@property
def z(self) -> complex:
- """ return the datapoint impedance as complex number """
- # FIXME: not impedance, but s11 ?
+ """ return the s value complex number """
return complex(self.re, self.im)
@property
@@ -108,8 +103,7 @@ def groupDelay(data: List[Datapoint], index: int) -> float:
delta_freq = data[idx1].freq - data[idx0].freq
if delta_freq == 0:
return 0
- val = -delta_angle / math.tau / delta_freq
- return val
+ return -delta_angle / math.tau / delta_freq
def impedance_to_capacitance(z: complex, freq: float) -> float:
@@ -170,8 +164,7 @@ def corr_att_data(data: List[Datapoint], att: float) -> List[Datapoint]:
"""Correct the ratio for a given attenuation on s21 input"""
if att <= 0:
return data
- else:
- att = 10**(att / 20)
+ att = 10**(att / 20)
ndata = []
for dp in data:
corrected = dp.z * att
diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py
index ec730677..b952e05b 100644
--- a/NanoVNASaver/SITools.py
+++ b/NanoVNASaver/SITools.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -158,8 +158,8 @@ def parse(self, value: str) -> "Value":
try:
self._value = (decimal.Decimal(value, context=Value.CTX)
* decimal.Decimal(factor, context=Value.CTX))
- except decimal.InvalidOperation:
- raise ValueError
+ except decimal.InvalidOperation as exc:
+ raise ValueError() from exc
self._value = clamp_value(
self._value, self.fmt.parse_clamp_min, self.fmt.parse_clamp_max)
return self
diff --git a/NanoVNASaver/Settings/Bands.py b/NanoVNASaver/Settings/Bands.py
index 620e6e2c..ac897056 100644
--- a/NanoVNASaver/Settings/Bands.py
+++ b/NanoVNASaver/Settings/Bands.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -41,6 +41,8 @@
"70 cm;432000000;438000000",
"23 cm;1240000000;1300000000",
"13 cm;2320000000;2450000000",
+ "9 cm;3300000000;3500000000",
+ "5 cm;5650000000;5925000000",
)
_HEADER_DATA = ("Band", "Start (Hz)", "End (Hz)")
diff --git a/NanoVNASaver/Settings/Sweep.py b/NanoVNASaver/Settings/Sweep.py
index 2ce88096..ec0d7f64 100644
--- a/NanoVNASaver/Settings/Sweep.py
+++ b/NanoVNASaver/Settings/Sweep.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/NanoVNASaver/SweepWorker.py b/NanoVNASaver/SweepWorker.py
index 40fc8158..602d7580 100644
--- a/NanoVNASaver/SweepWorker.py
+++ b/NanoVNASaver/SweepWorker.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -52,7 +52,6 @@ class WorkerSignals(QtCore.QObject):
updated = pyqtSignal()
finished = pyqtSignal()
sweepError = pyqtSignal()
- fatalSweepError = pyqtSignal()
class SweepWorker(QtCore.QRunnable):
@@ -107,13 +106,11 @@ def _run(self):
self.sweep = sweep
self.init_data()
- finished = False
- while not finished:
+ while True:
for i in range(sweep.segments):
logger.debug("Sweep segment no %d", i)
if self.stopped:
logger.debug("Stopping sweeping as signalled")
- finished = True
break
start, stop = sweep.get_index_range(i)
@@ -123,13 +120,11 @@ def _run(self):
self.percentage = (i + 1) * 100 / sweep.segments
self.updateData(freq, values11, values21, i)
except ValueError as e:
- self.error_message = str(e)
- self.stopped = True
- self.running = False
- self.signals.sweepError.emit()
-
- if not sweep.properties.mode == SweepMode.CONTINOUS:
- finished = True
+ self.gui_error(str(e))
+ else:
+ if sweep.properties.mode == SweepMode.CONTINOUS:
+ continue
+ break
if sweep.segments > 1:
start = sweep.start
@@ -190,33 +185,30 @@ def applyCalibration(self,
raw_data11: List[Datapoint],
raw_data21: List[Datapoint]
) -> Tuple[List[Datapoint], List[Datapoint]]:
- if self.offsetDelay != 0:
- tmp = []
- for dp in raw_data11:
- tmp.append(correct_delay(dp, self.offsetDelay, reflect=True))
- raw_data11 = tmp
- tmp = []
- for dp in raw_data21:
- tmp.append(correct_delay(dp, self.offsetDelay))
- raw_data21 = tmp
-
- if not self.app.calibration.isCalculated:
- return raw_data11, raw_data21
data11: List[Datapoint] = []
data21: List[Datapoint] = []
- if self.app.calibration.isValid1Port():
- for dp in raw_data11:
- data11.append(self.app.calibration.correct11(dp))
+ if not self.app.calibration.isCalculated:
+ data11 = raw_data11.copy()
+ data21 = raw_data21.copy()
else:
- data11 = raw_data11
+ if self.app.calibration.isValid1Port():
+ for dp in raw_data11:
+ data11.append(self.app.calibration.correct11(dp))
+ else:
+ data11 = raw_data11.copy()
+
+ if self.app.calibration.isValid2Port():
+ for dp in raw_data21:
+ data21.append(self.app.calibration.correct21(dp))
+ else:
+ data21 = raw_data21.copy()
+
+ if self.offsetDelay != 0:
+ data11 = [correct_delay(dp, self.offsetDelay, reflect=True) for dp in data11]
+ data21 = [correct_delay(dp, self.offsetDelay) for dp in data21]
- if self.app.calibration.isValid2Port():
- for dp in raw_data21:
- data21.append(self.app.calibration.correct21(dp))
- else:
- data21 = raw_data21
return data11, data21
def readAveragedSegment(self, start, stop, averages=1):
@@ -232,8 +224,17 @@ def readAveragedSegment(self, start, stop, averages=1):
break
logger.warning("Stop during average. Discarding sweep result.")
return [], [], []
- logger.debug("Reading average no %d / %d", i+1, averages)
- freq, tmp11, tmp21 = self.readSegment(start, stop)
+ logger.debug("Reading average no %d / %d", i + 1, averages)
+ retry = 0
+ tmp11 = []
+ while not tmp11 and retry < 5:
+ sleep(0.5 * retry)
+ retry += 1
+ freq, tmp11, tmp21 = self.readSegment(start, stop)
+ if retry > 1:
+ logger.error("retry %s readSegment(%s,%s)",
+ retry, start, stop)
+ sleep(0.5)
values11.append(tmp11)
values21.append(tmp21)
self.percentage += 100 / (self.sweep.segments * averages)
@@ -314,7 +315,6 @@ def readData(self, data):
f"device settings screen.")
return returndata
-
def gui_error(self, message: str):
self.error_message = message
self.stopped = True
diff --git a/NanoVNASaver/Touchstone.py b/NanoVNASaver/Touchstone.py
index 1813ebb1..df855b36 100644
--- a/NanoVNASaver/Touchstone.py
+++ b/NanoVNASaver/Touchstone.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -98,7 +98,7 @@ def parse(self, line: str):
class Touchstone:
FIELD_ORDER = ("11", "21", "12", "22")
- def __init__(self, filename: str):
+ def __init__(self, filename: str=""):
self.filename = filename
self.sdata = [[], [], [], []] # at max 4 data pairs
self.comments = []
@@ -106,35 +106,35 @@ def __init__(self, filename: str):
self._interp = {}
@property
- def s11data(self) -> List[Datapoint]:
+ def s11(self) -> List[Datapoint]:
return self.s("11")
- @s11data.setter
- def s11data(self, value: List[Datapoint]):
+ @s11.setter
+ def s11(self, value: List[Datapoint]):
self.sdata[0] = value
@property
- def s12data(self) -> List[Datapoint]:
+ def s12(self) -> List[Datapoint]:
return self.s("12")
- @s12data.setter
- def s12data(self, value: List[Datapoint]):
+ @s12.setter
+ def s12(self, value: List[Datapoint]):
self.sdata[2] = value
@property
- def s21data(self) -> List[Datapoint]:
+ def s21(self) -> List[Datapoint]:
return self.s("21")
- @s21data.setter
- def s21data(self, value: List[Datapoint]):
+ @s21.setter
+ def s21(self, value: List[Datapoint]):
self.sdata[1] = value
@property
- def s22data(self) -> List[Datapoint]:
+ def s22(self) -> List[Datapoint]:
return self.s("22")
- @s22data.setter
- def s22data(self, value: List[Datapoint]):
+ @s22.setter
+ def s22(self, value: List[Datapoint]):
self.sdata[3] = value
@property
@@ -149,6 +149,9 @@ def s_freq(self, name: str, freq: int) -> Datapoint:
float(self._interp[name]["real"](freq)),
float(self._interp[name]["imag"](freq)))
+ def swap(self):
+ self.sdata = [self.s22, self.s12, self.s21, self.s11]
+
def min_freq(self) -> int:
return self.s("11")[0].freq
@@ -279,7 +282,7 @@ def saves(self, nr_params: int = 1) -> str:
assert nr_params in (1, 4)
ts_str = "# HZ S RI R 50\n"
- for i, dp_s11 in enumerate(self.s11data):
+ for i, dp_s11 in enumerate(self.s11):
ts_str += f"{dp_s11.freq} {dp_s11.re} {dp_s11.im}"
for j in range(1, nr_params):
dp = self.sdata[j][i]
diff --git a/NanoVNASaver/Version.py b/NanoVNASaver/Version.py
index 6fff5300..1df6233f 100644
--- a/NanoVNASaver/Version.py
+++ b/NanoVNASaver/Version.py
@@ -1,7 +1,7 @@
# NanoVNASaver
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/NanoVNASaver/Windows/About.py b/NanoVNASaver/Windows/About.py
index 40cb45f4..6b3b5d5d 100644
--- a/NanoVNASaver/Windows/About.py
+++ b/NanoVNASaver/Windows/About.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/NanoVNASaver/Windows/AnalysisWindow.py b/NanoVNASaver/Windows/AnalysisWindow.py
index 04b69950..a453727f 100644
--- a/NanoVNASaver/Windows/AnalysisWindow.py
+++ b/NanoVNASaver/Windows/AnalysisWindow.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/NanoVNASaver/Windows/Bands.py b/NanoVNASaver/Windows/Bands.py
index 89db8097..c878842b 100644
--- a/NanoVNASaver/Windows/Bands.py
+++ b/NanoVNASaver/Windows/Bands.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/NanoVNASaver/Windows/CalibrationSettings.py b/NanoVNASaver/Windows/CalibrationSettings.py
index 7db5ded4..f990cb73 100644
--- a/NanoVNASaver/Windows/CalibrationSettings.py
+++ b/NanoVNASaver/Windows/CalibrationSettings.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -71,11 +71,13 @@ def __init__(self, app: QtWidgets.QWidget):
self.cal_label[label_name] = QtWidgets.QLabel("Uncalibrated")
cal_btn[label_name] = QtWidgets.QPushButton(
label_name.capitalize())
+ cal_btn[label_name].setMinimumHeight(20)
cal_btn[label_name].clicked.connect(partial(self.manual_save, label_name))
calibration_control_layout.addRow(
cal_btn[label_name], self.cal_label[label_name])
self.input_offset_delay = QtWidgets.QDoubleSpinBox()
+ self.input_offset_delay.setMinimumHeight(20)
self.input_offset_delay.setValue(0)
self.input_offset_delay.setSuffix(" ps")
self.input_offset_delay.setAlignment(QtCore.Qt.AlignRight)
@@ -86,15 +88,18 @@ def __init__(self, app: QtWidgets.QWidget):
calibration_control_layout.addRow("Offset delay", self.input_offset_delay)
self.btn_automatic = QtWidgets.QPushButton("Calibration assistant")
+ self.btn_automatic.setMinimumHeight(20)
calibration_control_layout.addRow(self.btn_automatic)
self.btn_automatic.clicked.connect(self.automaticCalibration)
apply_reset_layout = QtWidgets.QHBoxLayout()
btn_apply = QtWidgets.QPushButton("Apply")
+ btn_apply.setMinimumHeight(20)
btn_apply.clicked.connect(self.calculate)
btn_reset = QtWidgets.QPushButton("Reset")
+ btn_reset.setMinimumHeight(20)
btn_reset.clicked.connect(self.reset)
apply_reset_layout.addWidget(btn_apply)
@@ -114,8 +119,10 @@ def __init__(self, app: QtWidgets.QWidget):
file_box = QtWidgets.QGroupBox("Files")
file_layout = QtWidgets.QFormLayout(file_box)
btn_save_file = QtWidgets.QPushButton("Save calibration")
+ btn_save_file.setMinimumHeight(20)
btn_save_file.clicked.connect(lambda: self.saveCalibration())
btn_load_file = QtWidgets.QPushButton("Load calibration")
+ btn_load_file.setMinimumHeight(20)
btn_load_file.clicked.connect(lambda: self.loadCalibration())
save_load_layout = QtWidgets.QHBoxLayout()
@@ -137,10 +144,15 @@ def __init__(self, app: QtWidgets.QWidget):
cal_short_form = QtWidgets.QFormLayout(self.cal_short_box)
self.cal_short_box.setDisabled(True)
self.short_l0_input = QtWidgets.QLineEdit("0")
+ self.short_l0_input.setMinimumHeight(20)
self.short_l1_input = QtWidgets.QLineEdit("0")
+ self.short_l1_input.setMinimumHeight(20)
self.short_l2_input = QtWidgets.QLineEdit("0")
+ self.short_l2_input.setMinimumHeight(20)
self.short_l3_input = QtWidgets.QLineEdit("0")
+ self.short_l3_input.setMinimumHeight(20)
self.short_length = QtWidgets.QLineEdit("0")
+ self.short_length.setMinimumHeight(20)
cal_short_form.addRow("L0 (H(e-12))", self.short_l0_input)
cal_short_form.addRow("L1 (H(e-24))", self.short_l1_input)
cal_short_form.addRow("L2 (H(e-33))", self.short_l2_input)
@@ -151,10 +163,15 @@ def __init__(self, app: QtWidgets.QWidget):
cal_open_form = QtWidgets.QFormLayout(self.cal_open_box)
self.cal_open_box.setDisabled(True)
self.open_c0_input = QtWidgets.QLineEdit("50")
+ self.open_c0_input.setMinimumHeight(20)
self.open_c1_input = QtWidgets.QLineEdit("0")
+ self.open_c1_input.setMinimumHeight(20)
self.open_c2_input = QtWidgets.QLineEdit("0")
+ self.open_c2_input.setMinimumHeight(20)
self.open_c3_input = QtWidgets.QLineEdit("0")
+ self.open_c3_input.setMinimumHeight(20)
self.open_length = QtWidgets.QLineEdit("0")
+ self.open_length.setMinimumHeight(20)
cal_open_form.addRow("C0 (F(e-15))", self.open_c0_input)
cal_open_form.addRow("C1 (F(e-27))", self.open_c1_input)
cal_open_form.addRow("C2 (F(e-36))", self.open_c2_input)
@@ -165,19 +182,24 @@ def __init__(self, app: QtWidgets.QWidget):
cal_load_form = QtWidgets.QFormLayout(self.cal_load_box)
self.cal_load_box.setDisabled(True)
self.load_resistance = QtWidgets.QLineEdit("50")
+ self.load_resistance.setMinimumHeight(20)
self.load_inductance = QtWidgets.QLineEdit("0")
- # self.load_capacitance = QtWidgets.QLineEdit("0")
- # self.load_capacitance.setDisabled(True) # Not yet implemented
+ self.load_inductance.setMinimumHeight(20)
+ self.load_capacitance = QtWidgets.QLineEdit("0")
+ self.load_capacitance.setMinimumHeight(20)
+ #self.load_capacitance.setDisabled(True) # Not yet implemented
self.load_length = QtWidgets.QLineEdit("0")
+ self.load_length.setMinimumHeight(20)
cal_load_form.addRow("Resistance (\N{OHM SIGN})", self.load_resistance)
cal_load_form.addRow("Inductance (H(e-12))", self.load_inductance)
- # cal_load_form.addRow("Capacitance (F(e-12))", self.load_capacitance)
+ cal_load_form.addRow("Capacitance (F(e-15))", self.load_capacitance)
cal_load_form.addRow("Offset Delay (ps)", self.load_length)
self.cal_through_box = QtWidgets.QGroupBox("Through")
cal_through_form = QtWidgets.QFormLayout(self.cal_through_box)
self.cal_through_box.setDisabled(True)
self.through_length = QtWidgets.QLineEdit("0")
+ self.through_length.setMinimumHeight(20)
cal_through_form.addRow("Offset Delay (ps)", self.through_length)
cal_standard_layout.addWidget(self.cal_short_box)
@@ -190,14 +212,18 @@ def __init__(self, app: QtWidgets.QWidget):
self.cal_standard_save_box.setDisabled(True)
self.cal_standard_save_selector = QtWidgets.QComboBox()
+ self.cal_standard_save_selector.setMinimumHeight(20)
self.listCalibrationStandards()
cal_standard_save_layout.addWidget(self.cal_standard_save_selector)
cal_standard_save_button_layout = QtWidgets.QHBoxLayout()
btn_save_standard = QtWidgets.QPushButton("Save")
+ btn_save_standard.setMinimumHeight(20)
btn_save_standard.clicked.connect(self.saveCalibrationStandard)
btn_load_standard = QtWidgets.QPushButton("Load")
+ btn_load_standard.setMinimumHeight(20)
btn_load_standard.clicked.connect(self.loadCalibrationStandard)
btn_delete_standard = QtWidgets.QPushButton("Delete")
+ btn_delete_standard.setMinimumHeight(20)
btn_delete_standard.clicked.connect(self.deleteCalibrationStandard)
cal_standard_save_button_layout.addWidget(btn_load_standard)
cal_standard_save_button_layout.addWidget(btn_save_standard)
@@ -231,11 +257,11 @@ def checkExpertUser(self):
def cal_save(self, name: str):
if name in ("through", "isolation"):
- self.app.calibration.insert(name, self.app.data21)
+ self.app.calibration.insert(name, self.app.data.s21)
else:
- self.app.calibration.insert(name, self.app.data11)
+ self.app.calibration.insert(name, self.app.data.s11)
self.cal_label[name].setText(
- _format_cal_label(len(self.app.data11)))
+ _format_cal_label(len(self.app.data.s11)))
def manual_save(self, name: str):
if self.checkExpertUser():
@@ -287,7 +313,7 @@ def saveCalibrationStandard(self):
self.app.settings.setValue("LoadR", self.load_resistance.text())
self.app.settings.setValue("LoadL", self.load_inductance.text())
- # self.app.settings.setValue("LoadC", self.load_capacitance.text())
+ self.app.settings.setValue("LoadC", self.load_capacitance.text())
self.app.settings.setValue("LoadDelay", self.load_length.text())
self.app.settings.setValue("ThroughDelay", self.through_length.text())
@@ -322,7 +348,7 @@ def loadCalibrationStandard(self):
self.load_resistance.setText(str(self.app.settings.value("LoadR", 50)))
self.load_inductance.setText(str(self.app.settings.value("LoadL", 0)))
- # self.load_capacitance.setText(str(self.app.settings.value("LoadC", 0)))
+ self.load_capacitance.setText(str(self.app.settings.value("LoadC", 0)))
self.load_length.setText(str(self.app.settings.value("LoadDelay", 0)))
self.through_length.setText(str(self.app.settings.value("ThroughDelay", 0)))
@@ -485,8 +511,6 @@ def calculate(self):
try:
self.app.calibration.openC0 = self.getFloatValue(
self.open_c0_input.text())/10**15
- if self.app.calibration.openC0 == 0:
- raise ValueError("C0 cannot be 0.")
self.app.calibration.openC1 = self.getFloatValue(
self.open_c1_input.text())/10**27
self.app.calibration.openC2 = self.getFloatValue(
@@ -505,9 +529,9 @@ def calculate(self):
self.app.calibration.loadR = self.getFloatValue(
self.load_resistance.text())
self.app.calibration.loadL = self.getFloatValue(
- self.load_inductance.text())/10**12
- # self.app.calibration.loadC = self.getFloatValue(
- # self.load_capacitance.text()) / 10 ** 12
+ self.load_inductance.text()) / 10**12
+ self.app.calibration.loadC = self.getFloatValue(
+ self.load_capacitance.text()) / 10 ** 15
self.app.calibration.loadLength = self.getFloatValue(
self.load_length.text())/10**12
self.app.calibration.useIdealLoad = False
diff --git a/NanoVNASaver/Windows/DeviceSettings.py b/NanoVNASaver/Windows/DeviceSettings.py
index 8fc7c709..6a0ffb78 100644
--- a/NanoVNASaver/Windows/DeviceSettings.py
+++ b/NanoVNASaver/Windows/DeviceSettings.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/NanoVNASaver/Windows/DisplaySettings.py b/NanoVNASaver/Windows/DisplaySettings.py
index 33a7285d..e6819066 100644
--- a/NanoVNASaver/Windows/DisplaySettings.py
+++ b/NanoVNASaver/Windows/DisplaySettings.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,9 +21,12 @@
from PyQt5 import QtWidgets, QtCore, QtGui
+from NanoVNASaver.Charts.Chart import (
+ Chart, ChartColors, ChartMarker, ChartMarkerConfig)
from NanoVNASaver.Windows.Bands import BandsWindow
from NanoVNASaver.Windows.MarkerSettings import MarkerSettingsWindow
from NanoVNASaver.Marker import Marker
+
logger = logging.getLogger(__name__)
@@ -34,8 +37,9 @@ def __init__(self, app: QtWidgets.QWidget):
self.app = app
self.setWindowTitle("Display settings")
self.setWindowIcon(self.app.icon)
-
+ self.marker_cfg = ChartMarkerConfig()
self.marker_window = MarkerSettingsWindow(self.app)
+ self.callback_params = {}
QtWidgets.QShortcut(QtCore.Qt.Key_Escape, self, self.hide)
@@ -75,61 +79,10 @@ def __init__(self, app: QtWidgets.QWidget):
self.dark_mode_option.stateChanged.connect(self.changeDarkMode)
display_options_layout.addRow(self.dark_mode_option, dark_mode_label)
- self.btnColorPicker = QtWidgets.QPushButton("â–ˆ")
- self.btnColorPicker.setFixedWidth(20)
- self.sweepColor = self.app.settings.value(
- "SweepColor", defaultValue=QtGui.QColor(160, 140, 20, 128),
- type=QtGui.QColor)
- self.setSweepColor(self.sweepColor)
- self.btnColorPicker.clicked.connect(lambda: self.setSweepColor(
- QtWidgets.QColorDialog.getColor(
- self.sweepColor, options=QtWidgets.QColorDialog.ShowAlphaChannel)))
-
- display_options_layout.addRow("Sweep color", self.btnColorPicker)
-
- self.btnSecondaryColorPicker = QtWidgets.QPushButton("â–ˆ")
- self.btnSecondaryColorPicker.setFixedWidth(20)
- self.secondarySweepColor = self.app.settings.value("SecondarySweepColor",
- defaultValue=QtGui.QColor(
- 20, 160, 140, 128),
- type=QtGui.QColor)
- self.setSecondarySweepColor(self.secondarySweepColor)
- self.btnSecondaryColorPicker.clicked.connect(lambda: self.setSecondarySweepColor(
- QtWidgets.QColorDialog.getColor(self.secondarySweepColor,
- options=QtWidgets.QColorDialog.ShowAlphaChannel)))
-
- display_options_layout.addRow("Second sweep color", self.btnSecondaryColorPicker)
-
- self.btnReferenceColorPicker = QtWidgets.QPushButton("â–ˆ")
- self.btnReferenceColorPicker.setFixedWidth(20)
- self.referenceColor = self.app.settings.value(
- "ReferenceColor", defaultValue=QtGui.QColor(0, 0, 255, 48),
- type=QtGui.QColor)
- self.setReferenceColor(self.referenceColor)
- self.btnReferenceColorPicker.clicked.connect(lambda: self.setReferenceColor(
- QtWidgets.QColorDialog.getColor(
- self.referenceColor, options=QtWidgets.QColorDialog.ShowAlphaChannel)))
-
- display_options_layout.addRow("Reference color", self.btnReferenceColorPicker)
-
- self.btnSecondaryReferenceColorPicker = QtWidgets.QPushButton("â–ˆ")
- self.btnSecondaryReferenceColorPicker.setFixedWidth(20)
- self.secondaryReferenceColor = self.app.settings.value(
- "SecondaryReferenceColor",
- defaultValue=QtGui.QColor(0, 0, 255, 48),
- type=QtGui.QColor)
- self.setSecondaryReferenceColor(self.secondaryReferenceColor)
- self.btnSecondaryReferenceColorPicker.clicked.connect(
- lambda: self.setSecondaryReferenceColor(
- QtWidgets.QColorDialog.getColor(
- self.secondaryReferenceColor,
- options=QtWidgets.QColorDialog.ShowAlphaChannel)))
-
- display_options_layout.addRow(
- "Second reference color",
- self.btnSecondaryReferenceColorPicker)
+ self.trace_colors(display_options_layout)
self.pointSizeInput = QtWidgets.QSpinBox()
+ self.pointSizeInput.setMinimumHeight(20)
pointsize = self.app.settings.value("PointSize", 2, int)
self.pointSizeInput.setValue(pointsize)
self.changePointSize(pointsize)
@@ -141,6 +94,7 @@ def __init__(self, app: QtWidgets.QWidget):
display_options_layout.addRow("Point size", self.pointSizeInput)
self.lineThicknessInput = QtWidgets.QSpinBox()
+ self.lineThicknessInput.setMinimumHeight(20)
linethickness = self.app.settings.value("LineThickness", 1, int)
self.lineThicknessInput.setValue(linethickness)
self.changeLineThickness(linethickness)
@@ -152,16 +106,16 @@ def __init__(self, app: QtWidgets.QWidget):
display_options_layout.addRow("Line thickness", self.lineThicknessInput)
self.markerSizeInput = QtWidgets.QSpinBox()
+ self.markerSizeInput.setMinimumHeight(20)
markersize = self.app.settings.value("MarkerSize", 6, int)
self.markerSizeInput.setValue(markersize)
- self.changeMarkerSize(markersize)
+ self.markerSizeInput.setMinimum(4)
self.markerSizeInput.setMinimum(4)
self.markerSizeInput.setMaximum(20)
self.markerSizeInput.setSingleStep(2)
self.markerSizeInput.setSuffix(" px")
self.markerSizeInput.setAlignment(QtCore.Qt.AlignRight)
self.markerSizeInput.valueChanged.connect(self.changeMarkerSize)
- self.markerSizeInput.editingFinished.connect(self.validateMarkerSize)
display_options_layout.addRow("Marker size", self.markerSizeInput)
self.show_marker_number_option = QtWidgets.QCheckBox("Show marker numbers")
@@ -195,42 +149,10 @@ def __init__(self, app: QtWidgets.QWidget):
color_options_layout = QtWidgets.QFormLayout(color_options_box)
self.use_custom_colors = QtWidgets.QCheckBox("Use custom chart colors")
- self.use_custom_colors.stateChanged.connect(self.changeCustomColors)
+ self.use_custom_colors.stateChanged.connect(self.updateCharts)
color_options_layout.addRow(self.use_custom_colors)
- self.btn_background_picker = QtWidgets.QPushButton("â–ˆ")
- self.btn_background_picker.setFixedWidth(20)
- self.btn_background_picker.clicked.connect(
- lambda: self.setColor(
- "background",
- QtWidgets.QColorDialog.getColor(
- self.backgroundColor,
- options=QtWidgets.QColorDialog.ShowAlphaChannel)))
-
- color_options_layout.addRow(
- "Chart background", self.btn_background_picker)
-
- self.btn_foreground_picker = QtWidgets.QPushButton("â–ˆ")
- self.btn_foreground_picker.setFixedWidth(20)
- self.btn_foreground_picker.clicked.connect(
- lambda: self.setColor(
- "foreground",
- QtWidgets.QColorDialog.getColor(
- self.foregroundColor,
- options=QtWidgets.QColorDialog.ShowAlphaChannel)))
-
- color_options_layout.addRow("Chart foreground", self.btn_foreground_picker)
-
- self.btn_text_picker = QtWidgets.QPushButton("â–ˆ")
- self.btn_text_picker.setFixedWidth(20)
- self.btn_text_picker.clicked.connect(
- lambda: self.setColor(
- "text",
- QtWidgets.QColorDialog.getColor(
- self.textColor,
- options=QtWidgets.QColorDialog.ShowAlphaChannel)))
-
- color_options_layout.addRow("Chart text", self.btn_text_picker)
+ self.custom_colors(color_options_layout)
right_layout = QtWidgets.QVBoxLayout()
layout.addLayout(right_layout)
@@ -238,6 +160,7 @@ def __init__(self, app: QtWidgets.QWidget):
font_options_box = QtWidgets.QGroupBox("Font")
font_options_layout = QtWidgets.QFormLayout(font_options_box)
self.font_dropdown = QtWidgets.QComboBox()
+ self.font_dropdown.setMinimumHeight(20)
self.font_dropdown.addItems(["7", "8", "9", "10", "11", "12"])
font_size = self.app.settings.value("FontSize",
defaultValue="8",
@@ -255,19 +178,12 @@ def __init__(self, app: QtWidgets.QWidget):
self.show_bands.setChecked(self.app.bands.enabled)
self.show_bands.stateChanged.connect(lambda: self.setShowBands(self.show_bands.isChecked()))
bands_layout.addRow(self.show_bands)
-
- self.btn_bands_picker = QtWidgets.QPushButton("â–ˆ")
- self.btn_bands_picker.setFixedWidth(20)
- self.btn_bands_picker.clicked.connect(
- lambda: self.setColor(
- "bands",
- QtWidgets.QColorDialog.getColor(
- self.bandsColor,
- options=QtWidgets.QColorDialog.ShowAlphaChannel)))
-
- bands_layout.addRow("Chart bands", self.btn_bands_picker)
+ bands_layout.addRow(
+ "Chart bands",
+ self.color_picker("BandsColor", "bands"))
self.btn_manage_bands = QtWidgets.QPushButton("Manage bands")
+ self.btn_manage_bands.setMinimumHeight(20)
self.bandsWindow = BandsWindow(self.app)
self.btn_manage_bands.clicked.connect(self.displayBandsWindow)
@@ -286,18 +202,11 @@ def __init__(self, app: QtWidgets.QWidget):
# Single values from the .ini become floats rather than lists. Convert them.
self.vswrMarkers = [self.vswrMarkers]
- self.btn_vswr_picker = QtWidgets.QPushButton("â–ˆ")
- self.btn_vswr_picker.setFixedWidth(20)
- self.btn_vswr_picker.clicked.connect(
- lambda: self.setColor(
- "vswr",
- QtWidgets.QColorDialog.getColor(
- self.vswrColor,
- options=QtWidgets.QColorDialog.ShowAlphaChannel)))
-
- vswr_marker_layout.addRow("VSWR Markers", self.btn_vswr_picker)
+ vswr_marker_layout.addRow(
+ "VSWR Markers",self.color_picker("VSWRColor", "swr"))
self.vswr_marker_dropdown = QtWidgets.QComboBox()
+ self.vswr_marker_dropdown.setMinimumHeight(20)
vswr_marker_layout.addRow(self.vswr_marker_dropdown)
if len(self.vswrMarkers) == 0:
@@ -310,7 +219,9 @@ def __init__(self, app: QtWidgets.QWidget):
self.vswr_marker_dropdown.setCurrentIndex(0)
btn_add_vswr_marker = QtWidgets.QPushButton("Add ...")
+ btn_add_vswr_marker.setMinimumHeight(20)
btn_remove_vswr_marker = QtWidgets.QPushButton("Remove")
+ btn_remove_vswr_marker.setMinimumHeight(20)
vswr_marker_btn_layout = QtWidgets.QHBoxLayout()
vswr_marker_btn_layout.addWidget(btn_add_vswr_marker)
vswr_marker_btn_layout.addWidget(btn_remove_vswr_marker)
@@ -323,10 +234,13 @@ def __init__(self, app: QtWidgets.QWidget):
markers_layout = QtWidgets.QFormLayout(markers_box)
btn_add_marker = QtWidgets.QPushButton("Add")
+ btn_add_marker.setMinimumHeight(30)
btn_add_marker.clicked.connect(self.addMarker)
self.btn_remove_marker = QtWidgets.QPushButton("Remove")
+ self.btn_remove_marker.setMinimumHeight(30)
self.btn_remove_marker.clicked.connect(self.removeMarker)
btn_marker_settings = QtWidgets.QPushButton("Settings ...")
+ btn_marker_settings.setMinimumHeight(30)
btn_marker_settings.clicked.connect(self.displayMarkerWindow)
marker_btn_layout = QtWidgets.QHBoxLayout()
@@ -355,6 +269,7 @@ def __init__(self, app: QtWidgets.QWidget):
selections.append("None")
chart00_selection = QtWidgets.QComboBox()
+ chart00_selection.setMinimumHeight(30)
chart00_selection.addItems(selections)
chart00 = self.app.settings.value("Chart00", "S11 Smith Chart")
if chart00_selection.findText(chart00) > -1:
@@ -366,6 +281,7 @@ def __init__(self, app: QtWidgets.QWidget):
charts_layout.addWidget(chart00_selection, 0, 0)
chart01_selection = QtWidgets.QComboBox()
+ chart01_selection.setMinimumHeight(30)
chart01_selection.addItems(selections)
chart01 = self.app.settings.value("Chart01", "S11 Return Loss")
if chart01_selection.findText(chart01) > -1:
@@ -377,6 +293,7 @@ def __init__(self, app: QtWidgets.QWidget):
charts_layout.addWidget(chart01_selection, 0, 1)
chart02_selection = QtWidgets.QComboBox()
+ chart02_selection.setMinimumHeight(30)
chart02_selection.addItems(selections)
chart02 = self.app.settings.value("Chart02", "None")
if chart02_selection.findText(chart02) > -1:
@@ -388,6 +305,7 @@ def __init__(self, app: QtWidgets.QWidget):
charts_layout.addWidget(chart02_selection, 0, 2)
chart10_selection = QtWidgets.QComboBox()
+ chart10_selection.setMinimumHeight(30)
chart10_selection.addItems(selections)
chart10 = self.app.settings.value("Chart10", "S21 Polar Plot")
if chart10_selection.findText(chart10) > -1:
@@ -399,6 +317,7 @@ def __init__(self, app: QtWidgets.QWidget):
charts_layout.addWidget(chart10_selection, 1, 0)
chart11_selection = QtWidgets.QComboBox()
+ chart11_selection.setMinimumHeight(30)
chart11_selection.addItems(selections)
chart11 = self.app.settings.value("Chart11", "S21 Gain")
if chart11_selection.findText(chart11) > -1:
@@ -410,6 +329,7 @@ def __init__(self, app: QtWidgets.QWidget):
charts_layout.addWidget(chart11_selection, 1, 1)
chart12_selection = QtWidgets.QComboBox()
+ chart12_selection.setMinimumHeight(30)
chart12_selection.addItems(selections)
chart12 = self.app.settings.value("Chart12", "None")
if chart12_selection.findText(chart12) > -1:
@@ -427,21 +347,21 @@ def __init__(self, app: QtWidgets.QWidget):
self.changeChart(1, 1, chart11_selection.currentText())
self.changeChart(1, 2, chart12_selection.currentText())
- self.backgroundColor = self.app.settings.value(
- "BackgroundColor", defaultValue=QtGui.QColor("white"),
+ Chart.color.background = self.app.settings.value(
+ "BackgroundColor", defaultValue=ChartColors.background,
type=QtGui.QColor)
- self.foregroundColor = self.app.settings.value(
- "ForegroundColor", defaultValue=QtGui.QColor("lightgray"),
+ Chart.color.foreground = self.app.settings.value(
+ "ForegroundColor", defaultValue=ChartColors.foreground,
type=QtGui.QColor)
- self.textColor = self.app.settings.value(
- "TextColor", defaultValue=QtGui.QColor("black"),
+ Chart.color.text = self.app.settings.value(
+ "TextColor", defaultValue=ChartColors.text,
type=QtGui.QColor)
self.bandsColor = self.app.settings.value(
- "BandsColor", defaultValue=QtGui.QColor(128, 128, 128, 48),
+ "BandsColor", defaultValue=ChartColors.bands,
type=QtGui.QColor)
- self.app.bands.color = self.bandsColor
- self.vswrColor = self.app.settings.value(
- "VSWRColor", defaultValue=QtGui.QColor(192, 0, 0, 128),
+ self.app.bands.color = Chart.color.bands
+ Chart.color.swr = self.app.settings.value(
+ "VSWRColor", defaultValue=ChartColors.swr,
type=QtGui.QColor)
self.dark_mode_option.setChecked(
@@ -449,41 +369,15 @@ def __init__(self, app: QtWidgets.QWidget):
self.show_lines_option.setChecked(
self.app.settings.value("ShowLines", False, bool))
self.show_marker_number_option.setChecked(
- self.app.settings.value("ShowMarkerNumbers", False, bool))
+ self.app.settings.value("ShowMarkerNumbers", ChartMarkerConfig.draw_label, bool))
self.filled_marker_option.setChecked(
- self.app.settings.value("FilledMarkers", False, bool))
+ self.app.settings.value("FilledMarkers", ChartMarkerConfig.fill, bool))
if self.app.settings.value("UseCustomColors",
defaultValue=False, type=bool):
self.dark_mode_option.setDisabled(True)
self.dark_mode_option.setChecked(False)
self.use_custom_colors.setChecked(True)
- else:
- self.btn_background_picker.setDisabled(True)
- self.btn_foreground_picker.setDisabled(True)
- self.btn_text_picker.setDisabled(True)
-
- self.changeCustomColors() # Update all the colours of all the charts
-
- p = self.btn_background_picker.palette()
- p.setColor(QtGui.QPalette.ButtonText, self.backgroundColor)
- self.btn_background_picker.setPalette(p)
-
- p = self.btn_foreground_picker.palette()
- p.setColor(QtGui.QPalette.ButtonText, self.foregroundColor)
- self.btn_foreground_picker.setPalette(p)
-
- p = self.btn_text_picker.palette()
- p.setColor(QtGui.QPalette.ButtonText, self.textColor)
- self.btn_text_picker.setPalette(p)
-
- p = self.btn_bands_picker.palette()
- p.setColor(QtGui.QPalette.ButtonText, self.bandsColor)
- self.btn_bands_picker.setPalette(p)
-
- p = self.btn_vswr_picker.palette()
- p.setColor(QtGui.QPalette.ButtonText, self.vswrColor)
- self.btn_vswr_picker.setPalette(p)
left_layout.addWidget(display_options_box)
left_layout.addWidget(charts_box)
@@ -495,6 +389,42 @@ def __init__(self, app: QtWidgets.QWidget):
right_layout.addWidget(bands_box)
right_layout.addWidget(vswr_marker_box)
right_layout.addStretch(1)
+ self.update()
+
+ def trace_colors(self, layout: QtWidgets.QLayout):
+ for setting, name, attr in (
+ ('SweepColor', 'Sweep color', 'sweep'),
+ ('SecondarySweepColor', 'Second sweep color', 'sweep_secondary'),
+ ('ReferenceColor', 'Reference color', 'reference'),
+ ('SecondaryReferenceColor',
+ 'Second reference color', 'reference_secondary'),
+ ):
+ cp = self.color_picker(setting, attr)
+ layout.addRow(name, cp)
+
+ def custom_colors(self, layout: QtWidgets.QLayout):
+ for setting, name, attr in (
+ ('BackgroundColor', 'Chart background', 'background'),
+ ('ForegroundColor', 'Chart foreground', 'foreground'),
+ ('TextColor', 'Chart text', 'text'),
+ ):
+ cp = self.color_picker(setting, attr)
+ layout.addRow(name, cp)
+
+ def color_picker(self, setting: str, attr: str) -> QtWidgets.QPushButton:
+ cp = QtWidgets.QPushButton("â–ˆ")
+ cp.setFixedWidth(20)
+ cp.setMinimumHeight(20)
+ default = getattr(Chart.color, attr)
+ color = self.app.settings.value(
+ setting, defaultValue=default, type=QtGui.QColor)
+ setattr(Chart.color, attr, color)
+ self.callback_params[cp] = (setting, attr)
+ cp.clicked.connect(self.setColor)
+ p = cp.palette()
+ p.setColor(QtGui.QPalette.ButtonText, getattr(Chart.color, attr))
+ cp.setPalette(p)
+ return cp
def changeChart(self, x, y, chart):
found = None
@@ -524,7 +454,7 @@ def changeReturnLoss(self):
for m in self.app.markers:
m.returnloss_is_positive = state
- m.updateLabels(self.app.data11, self.app.data21)
+ m.updateLabels(self.app.data.s11, self.app.data.s21)
self.marker_window.exampleMarker.returnloss_is_positive = state
self.marker_window.updateMarker()
self.app.charts["s11"]["log_mag"].isInverted = state
@@ -539,20 +469,20 @@ def changeShowLines(self):
def changeShowMarkerNumber(self):
state = self.show_marker_number_option.isChecked()
self.app.settings.setValue("ShowMarkerNumbers", state)
- for c in self.app.subscribing_charts:
- c.setDrawMarkerNumbers(state)
+ ChartMarker.cfg.draw_label = state
+ self.updateCharts()
def changeFilledMarkers(self):
state = self.filled_marker_option.isChecked()
self.app.settings.setValue("FilledMarkers", state)
- for c in self.app.subscribing_charts:
- c.setFilledMarkers(state)
+ ChartMarker.cfg.fill = state
+ self.updateCharts()
def changeMarkerAtTip(self):
state = self.marker_at_tip.isChecked()
self.app.settings.setValue("MarkerAtTip", state)
- for c in self.app.subscribing_charts:
- c.setMarkerAtTip(state)
+ ChartMarker.cfg.at_tip = state
+ self.updateCharts()
def changePointSize(self, size: int):
self.app.settings.setValue("PointSize", size)
@@ -565,131 +495,49 @@ def changeLineThickness(self, size: int):
c.setLineThickness(size)
def changeMarkerSize(self, size: int):
- if size % 2 == 0:
- self.app.settings.setValue("MarkerSize", size)
- for c in self.app.subscribing_charts:
- c.setMarkerSize(int(size / 2))
-
- def validateMarkerSize(self):
- size = self.markerSizeInput.value()
- if size % 2 != 0:
- self.markerSizeInput.setValue(size + 1)
+ self.app.settings.setValue("MarkerSize", size)
+ ChartMarker.cfg.size = size
+ self.markerSizeInput.setValue(size)
+ self.updateCharts()
def changeDarkMode(self):
state = self.dark_mode_option.isChecked()
self.app.settings.setValue("DarkMode", state)
+ Chart.color.foreground = QtGui.QColor(QtCore.Qt.lightGray)
if state:
- for c in self.app.subscribing_charts:
- c.setBackgroundColor(QtGui.QColor(QtCore.Qt.black))
- c.setForegroundColor(QtGui.QColor(QtCore.Qt.lightGray))
- c.setTextColor(QtGui.QColor(QtCore.Qt.white))
- c.setSWRColor(self.vswrColor)
- else:
- for c in self.app.subscribing_charts:
- c.setBackgroundColor(QtGui.QColor(QtCore.Qt.white))
- c.setForegroundColor(QtGui.QColor(QtCore.Qt.lightGray))
- c.setTextColor(QtGui.QColor(QtCore.Qt.black))
- c.setSWRColor(self.vswrColor)
-
- def changeCustomColors(self):
- self.app.settings.setValue("UseCustomColors", self.use_custom_colors.isChecked())
- if self.use_custom_colors.isChecked():
- self.dark_mode_option.setDisabled(True)
- self.dark_mode_option.setChecked(False)
- self.btn_background_picker.setDisabled(False)
- self.btn_foreground_picker.setDisabled(False)
- self.btn_text_picker.setDisabled(False)
- for c in self.app.subscribing_charts:
- c.setBackgroundColor(self.backgroundColor)
- c.setForegroundColor(self.foregroundColor)
- c.setTextColor(self.textColor)
- c.setSWRColor(self.vswrColor)
+ Chart.color.background = QtGui.QColor(QtCore.Qt.black)
+ Chart.color.text = QtGui.QColor(QtCore.Qt.white)
+ Chart.color.swr = Chart.color.swr
else:
- self.dark_mode_option.setDisabled(False)
- self.btn_background_picker.setDisabled(True)
- self.btn_foreground_picker.setDisabled(True)
- self.btn_text_picker.setDisabled(True)
- self.changeDarkMode() # Reset to the default colors depending on Dark Mode setting
-
- def setColor(self, name: str, color: QtGui.QColor):
- if name == "background":
- p = self.btn_background_picker.palette()
- p.setColor(QtGui.QPalette.ButtonText, color)
- self.btn_background_picker.setPalette(p)
- self.backgroundColor = color
- self.app.settings.setValue("BackgroundColor", color)
- elif name == "foreground":
- p = self.btn_foreground_picker.palette()
- p.setColor(QtGui.QPalette.ButtonText, color)
- self.btn_foreground_picker.setPalette(p)
- self.foregroundColor = color
- self.app.settings.setValue("ForegroundColor", color)
- elif name == "text":
- p = self.btn_text_picker.palette()
- p.setColor(QtGui.QPalette.ButtonText, color)
- self.btn_text_picker.setPalette(p)
- self.textColor = color
- self.app.settings.setValue("TextColor", color)
- elif name == "bands":
- p = self.btn_bands_picker.palette()
- p.setColor(QtGui.QPalette.ButtonText, color)
- self.btn_bands_picker.setPalette(p)
- self.bandsColor = color
- self.app.settings.setValue("BandsColor", color)
- self.app.bands.setColor(color)
- elif name == "vswr":
- p = self.btn_vswr_picker.palette()
- p.setColor(QtGui.QPalette.ButtonText, color)
- self.btn_vswr_picker.setPalette(p)
- self.vswrColor = color
- self.app.settings.setValue("VSWRColor", color)
- self.changeCustomColors()
-
- def setSweepColor(self, color: QtGui.QColor):
- if color.isValid():
- self.sweepColor = color
- p = self.btnColorPicker.palette()
- p.setColor(QtGui.QPalette.ButtonText, color)
- self.btnColorPicker.setPalette(p)
- self.app.settings.setValue("SweepColor", color)
- self.app.settings.sync()
- for c in self.app.subscribing_charts:
- c.setSweepColor(color)
-
- def setSecondarySweepColor(self, color: QtGui.QColor):
- if color.isValid():
- self.secondarySweepColor = color
- p = self.btnSecondaryColorPicker.palette()
- p.setColor(QtGui.QPalette.ButtonText, color)
- self.btnSecondaryColorPicker.setPalette(p)
- self.app.settings.setValue("SecondarySweepColor", color)
- self.app.settings.sync()
- for c in self.app.subscribing_charts:
- c.setSecondarySweepColor(color)
-
- def setReferenceColor(self, color):
- if color.isValid():
- self.referenceColor = color
- p = self.btnReferenceColorPicker.palette()
- p.setColor(QtGui.QPalette.ButtonText, color)
- self.btnReferenceColorPicker.setPalette(p)
- self.app.settings.setValue("ReferenceColor", color)
- self.app.settings.sync()
-
- for c in self.app.subscribing_charts:
- c.setReferenceColor(color)
-
- def setSecondaryReferenceColor(self, color):
- if color.isValid():
- self.secondaryReferenceColor = color
- p = self.btnSecondaryReferenceColorPicker.palette()
- p.setColor(QtGui.QPalette.ButtonText, color)
- self.btnSecondaryReferenceColorPicker.setPalette(p)
- self.app.settings.setValue("SecondaryReferenceColor", color)
- self.app.settings.sync()
-
- for c in self.app.subscribing_charts:
- c.setSecondaryReferenceColor(color)
+ Chart.color.background = QtGui.QColor(QtCore.Qt.white)
+ Chart.color.text = QtGui.QColor(QtCore.Qt.black)
+ Chart.color.swr = Chart.color.swr
+ self.updateCharts()
+
+ def changeSetting(self, setting: str, value: str):
+ logger.debug("Setting %s: %s", setting, value)
+ self.app.settings.setValue(setting, value)
+ self.app.settings.sync()
+ self.updateCharts()
+
+ def setColor(self):
+ sender = self.sender()
+ logger.debug("Sender %s", sender)
+ setting, attr = self.callback_params[sender]
+ logger.debug("Setting: %s Attribute: %s", setting, attr)
+
+ color = getattr(Chart.color, attr)
+ color = QtWidgets.QColorDialog.getColor(
+ color, options=QtWidgets.QColorDialog.ShowAlphaChannel)
+
+ if not color.isValid():
+ logger.info("Invalid color")
+ return
+
+ palette = sender.palette()
+ palette.setColor(QtGui.QPalette.ButtonText, color)
+ sender.setPalette(palette)
+ self.changeSetting(setting, color)
def setShowBands(self, show_bands):
self.app.bands.enabled = show_bands
@@ -776,3 +624,7 @@ def removeVSWRMarker(self):
self.app.settings.setValue("VSWRMarkers", self.vswrMarkers)
for c in self.app.s11charts:
c.removeSWRMarker(value)
+
+ def updateCharts(self):
+ for c in self.app.subscribing_charts:
+ c.update()
diff --git a/NanoVNASaver/Windows/Files.py b/NanoVNASaver/Windows/Files.py
new file mode 100644
index 00000000..28c37e44
--- /dev/null
+++ b/NanoVNASaver/Windows/Files.py
@@ -0,0 +1,129 @@
+# NanoVNASaver
+#
+# A python program to view and export Touchstone data from a NanoVNA
+# Copyright (C) 2019, 2020 Rune B. Broberg
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+import logging
+
+from PyQt5 import QtWidgets, QtCore
+from NanoVNASaver.Touchstone import Touchstone
+from NanoVNASaver.RFTools import Datapoint
+
+logger = logging.getLogger(__name__)
+
+class FilesWindow(QtWidgets.QWidget):
+ def __init__(self, app: QtWidgets.QWidget):
+ super().__init__()
+ self.app = app
+
+ self.setWindowTitle("Files")
+ self.setWindowIcon(self.app.icon)
+ self.setMinimumWidth(200)
+ QtWidgets.QShortcut(QtCore.Qt.Key_Escape, self, self.hide)
+ file_window_layout = QtWidgets.QVBoxLayout()
+ self.setLayout(file_window_layout)
+
+ load_file_control_box = QtWidgets.QGroupBox("Import file")
+ load_file_control_box.setMaximumWidth(300)
+ load_file_control_layout = QtWidgets.QFormLayout(load_file_control_box)
+
+ btn_load_sweep = QtWidgets.QPushButton("Load as sweep")
+ btn_load_sweep.clicked.connect(self.loadSweepFile)
+ btn_load_reference = QtWidgets.QPushButton("Load reference")
+ btn_load_reference.clicked.connect(self.loadReferenceFile)
+ load_file_control_layout.addRow(btn_load_sweep)
+ load_file_control_layout.addRow(btn_load_reference)
+
+ file_window_layout.addWidget(load_file_control_box)
+
+ save_file_control_box = QtWidgets.QGroupBox("Export file")
+ save_file_control_box.setMaximumWidth(300)
+ save_file_control_layout = QtWidgets.QFormLayout(save_file_control_box)
+
+ btn_export_file = QtWidgets.QPushButton("Save 1-Port file (S1P)")
+ btn_export_file.clicked.connect(lambda: self.exportFile(1))
+ save_file_control_layout.addRow(btn_export_file)
+
+ btn_export_file = QtWidgets.QPushButton("Save 2-Port file (S2P)")
+ btn_export_file.clicked.connect(lambda: self.exportFile(4))
+ save_file_control_layout.addRow(btn_export_file)
+
+ file_window_layout.addWidget(save_file_control_box)
+
+ btn_open_file_window = QtWidgets.QPushButton("Files ...")
+ btn_open_file_window.clicked.connect(
+ lambda: self.app.display_window("file"))
+
+ def exportFile(self, nr_params: int = 1):
+ if len(self.app.data.s11) == 0:
+ QtWidgets.QMessageBox.warning(
+ self, "No data to save", "There is no data to save.")
+ return
+ if nr_params > 2 and len(self.app.data.s21) == 0:
+ QtWidgets.QMessageBox.warning(
+ self, "No S21 data to save", "There is no S21 data to save.")
+ return
+
+ filedialog = QtWidgets.QFileDialog(self)
+ if nr_params == 1:
+ filedialog.setDefaultSuffix("s1p")
+ filedialog.setNameFilter(
+ "Touchstone 1-Port Files (*.s1p);;All files (*.*)")
+ else:
+ filedialog.setDefaultSuffix("s2p")
+ filedialog.setNameFilter(
+ "Touchstone 2-Port Files (*.s2p);;All files (*.*)")
+ filedialog.setAcceptMode(QtWidgets.QFileDialog.AcceptSave)
+ selected = filedialog.exec()
+ if not selected:
+ return
+ filename = filedialog.selectedFiles()[0]
+ if filename == "":
+ logger.debug("No file name selected.")
+ return
+
+ ts = Touchstone(filename)
+ ts.sdata[0] = self.app.data.s11
+ if nr_params > 1:
+ ts.sdata[1] = self.app.data.s21
+ for dp in self.app.data.s11:
+ ts.sdata[2].append(Datapoint(dp.freq, 0, 0))
+ ts.sdata[3].append(Datapoint(dp.freq, 0, 0))
+ try:
+ ts.save(nr_params)
+ except IOError as e:
+ logger.exception("Error during file export: %s", e)
+ return
+
+ def loadReferenceFile(self):
+ filename, _ = QtWidgets.QFileDialog.getOpenFileName(
+ filter="Touchstone Files (*.s1p *.s2p);;All files (*.*)")
+ if filename != "":
+ self.app.resetReference()
+ t = Touchstone(filename)
+ t.load()
+ self.app.setReference(t.s11, t.s21, filename)
+
+ def loadSweepFile(self):
+ filename, _ = QtWidgets.QFileDialog.getOpenFileName(
+ filter="Touchstone Files (*.s1p *.s2p);;All files (*.*)")
+ if filename != "":
+ self.app.data.s11 = []
+ self.app.data.s21 = []
+ t = Touchstone(filename)
+ t.load()
+ self.app.saveData(t.s11, t.s21, filename)
+ self.app.dataUpdated()
diff --git a/NanoVNASaver/Windows/MarkerSettings.py b/NanoVNASaver/Windows/MarkerSettings.py
index 82c762d0..a00b3063 100644
--- a/NanoVNASaver/Windows/MarkerSettings.py
+++ b/NanoVNASaver/Windows/MarkerSettings.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/NanoVNASaver/Windows/Screenshot.py b/NanoVNASaver/Windows/Screenshot.py
index 218eb983..bc8e09e3 100644
--- a/NanoVNASaver/Windows/Screenshot.py
+++ b/NanoVNASaver/Windows/Screenshot.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/NanoVNASaver/Windows/SweepSettings.py b/NanoVNASaver/Windows/SweepSettings.py
index 710d519c..9e6e9011 100644
--- a/NanoVNASaver/Windows/SweepSettings.py
+++ b/NanoVNASaver/Windows/SweepSettings.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -56,6 +56,7 @@ def title_box(self):
layout = QtWidgets.QFormLayout(box)
input_title = QtWidgets.QLineEdit(self.app.sweep.properties.name)
+ input_title.setMinimumHeight(20)
input_title.editingFinished.connect(
lambda: self.update_title(input_title.text()))
layout.addRow(input_title)
@@ -66,26 +67,33 @@ def settings_box(self) -> 'QtWidgets.QWidget':
layout = QtWidgets.QFormLayout(box)
# Sweep Mode
+ sweep_btn_layout = QtWidgets.QHBoxLayout()
+
radio_button = QtWidgets.QRadioButton("Single sweep")
+ radio_button.setMinimumHeight(20)
radio_button.setChecked(
self.app.sweep.properties.mode == SweepMode.SINGLE)
radio_button.clicked.connect(
lambda: self.update_mode(SweepMode.SINGLE))
- layout.addWidget(radio_button)
+ sweep_btn_layout.addWidget(radio_button)
radio_button = QtWidgets.QRadioButton("Continous sweep")
+ radio_button.setMinimumHeight(20)
radio_button.setChecked(
self.app.sweep.properties.mode == SweepMode.CONTINOUS)
radio_button.clicked.connect(
lambda: self.update_mode(SweepMode.CONTINOUS))
- layout.addWidget(radio_button)
+ sweep_btn_layout.addWidget(radio_button)
radio_button = QtWidgets.QRadioButton("Averaged sweep")
+ radio_button.setMinimumHeight(20)
radio_button.setChecked(
self.app.sweep.properties.mode == SweepMode.AVERAGE)
radio_button.clicked.connect(
lambda: self.update_mode(SweepMode.AVERAGE))
- layout.addWidget(radio_button)
+ sweep_btn_layout.addWidget(radio_button)
+
+ layout.addRow(sweep_btn_layout)
# Log sweep
label = QtWidgets.QLabel(
@@ -94,23 +102,28 @@ def settings_box(self) -> 'QtWidgets.QWidget':
" amount of datapoints and many segments. Step display in"
" SweepControl cannot reflect this currently.")
label.setWordWrap(True)
+ label.setMinimumSize(600,70)
layout.addRow(label)
checkbox = QtWidgets.QCheckBox("Logarithmic sweep")
+ checkbox.setMinimumHeight(20)
checkbox.setCheckState(self.app.sweep.properties.logarithmic)
checkbox.toggled.connect(
lambda: self.update_logarithmic(checkbox.isChecked()))
- layout.addWidget(checkbox)
+ layout.addRow(checkbox)
# Averaging
label = QtWidgets.QLabel(
"Averaging allows discarding outlying samples to get better"
" averages. Common values are 3/0, 5/2, 9/4 and 25/6.")
label.setWordWrap(True)
+ label.setMinimumHeight(50)
layout.addRow(label)
averages = QtWidgets.QLineEdit(
str(self.app.sweep.properties.averages[0]))
+ averages.setMinimumHeight(20)
truncates = QtWidgets.QLineEdit(
str(self.app.sweep.properties.averages[1]))
+ truncates.setMinimumHeight(20)
averages.editingFinished.connect(
lambda: self.update_averaging(averages, truncates))
truncates.editingFinished.connect(
@@ -124,9 +137,11 @@ def settings_box(self) -> 'QtWidgets.QWidget':
" attenuator in line with the S21 input (CH1) here you can"
" specify it.")
label.setWordWrap(True)
+ label.setMinimumHeight(50)
layout.addRow(label)
input_att = QtWidgets.QLineEdit(str(self.app.s21att))
+ input_att.setMinimumHeight(20)
input_att.editingFinished.connect(
lambda: self.update_attenuator(input_att))
layout.addRow("Attenuator in port CH1 (s21) in dB", input_att)
@@ -135,26 +150,29 @@ def settings_box(self) -> 'QtWidgets.QWidget':
def sweep_box(self) -> 'QtWidgets.QWidget':
box = QtWidgets.QGroupBox("Sweep band")
layout = QtWidgets.QFormLayout(box)
+ sweep_pad_layout = QtWidgets.QHBoxLayout()
self.band_list = QtWidgets.QComboBox()
+ self.band_list.setMinimumHeight(20)
self.band_list.setModel(self.app.bands)
# pylint: disable=unnecessary-lambda
self.band_list.currentIndexChanged.connect(lambda: self.update_band())
layout.addRow("Select band", self.band_list)
- for raw_label, btn_label, value in (("Pad band limits", "None", 0),
- ("", "10%", 10),
- ("", "25%", 25),
- ("", "100%", 100),):
+ sweep_pad_layout.addWidget(QtWidgets.QLabel("Pad band limits:"))
+ for btn_label, value in (("None", 0), ("10%", 10), ("25%", 25), ("100%", 100),):
radio_button = QtWidgets.QRadioButton(btn_label)
+ radio_button.setMinimumHeight(20)
radio_button.setChecked(self.padding == value)
radio_button.clicked.connect(partial(self.update_padding, value))
- layout.addRow(raw_label, radio_button)
+ sweep_pad_layout.addWidget(radio_button)
+ layout.addRow(sweep_pad_layout)
self.band_label = QtWidgets.QLabel()
layout.addRow(self.band_label)
btn_set_band_sweep = QtWidgets.QPushButton("Set band sweep")
+ btn_set_band_sweep.setMinimumHeight(20)
btn_set_band_sweep.clicked.connect(lambda: self.update_band(True))
layout.addRow(btn_set_band_sweep)
return box
@@ -252,4 +270,4 @@ def update_title(self, title: str = ""):
def update_tx_power(self, freq_range, power_desc):
logger.debug("update_tx_power(%r)", power_desc)
with self.app.sweep.lock:
- self.app.set_tx_power(freq_range, power_desc)
+ self.app.vna.setTXPower(freq_range, power_desc)
diff --git a/NanoVNASaver/Windows/TDR.py b/NanoVNASaver/Windows/TDR.py
index 1102c348..7006dbe8 100644
--- a/NanoVNASaver/Windows/TDR.py
+++ b/NanoVNASaver/Windows/TDR.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020, 2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -62,7 +62,14 @@ def __init__(self, app: QtWidgets.QWidget):
self.tdr_velocity_dropdown.addItem("RG-8/U PE 50\N{OHM SIGN} (Belden 8237) (0.66)", 0.66)
self.tdr_velocity_dropdown.addItem("RG-8/U Foam (Belden 8214) (0.78)", 0.78)
self.tdr_velocity_dropdown.addItem("RG-8/U (Belden 9913) (0.84)", 0.84)
+ # Next one added by EKZ, KC3KZ, from measurement of actual cable
+ self.tdr_velocity_dropdown.addItem("RG-8/U (Shireen RFC®400 Low Loss) (0.86)", 0.86)
self.tdr_velocity_dropdown.addItem("RG-8X (Belden 9258) (0.82)", 0.82)
+ # Next three added by EKZ, KC3KZ, from measurement of actual cable
+ self.tdr_velocity_dropdown.addItem("RG-8X (Wireman \"Super 8\" CQ106) (0.81)", 0.81)
+ self.tdr_velocity_dropdown.addItem("RG-8X (Wireman \"MINI-8 Lo-Loss\" CQ118) (0.82)", 0.82)
+ self.tdr_velocity_dropdown.addItem(
+ "RG-58 (Wireman \"CQ 58 Lo-Loss Flex\" CQ129FF) (0.79)", 0.79)
self.tdr_velocity_dropdown.addItem(
"RG-11/U 75\N{OHM SIGN} Foam HDPE (Belden 9292) (0.84)", 0.84)
self.tdr_velocity_dropdown.addItem("RG-58/U 52\N{OHM SIGN} PE (Belden 9201) (0.66)", 0.66)
@@ -107,7 +114,7 @@ def updateTDR(self):
# TODO: Let the user select whether to use high or low resolution TDR?
FFT_POINTS = 2**14
- if len(self.app.data11) < 2:
+ if len(self.app.data.s11) < 2:
return
if self.tdr_velocity_dropdown.currentData() == -1:
@@ -121,17 +128,17 @@ def updateTDR(self):
except ValueError:
return
- step_size = self.app.data11[1].freq - self.app.data11[0].freq
+ step_size = self.app.data.s11[1].freq - self.app.data.s11[0].freq
if step_size == 0:
self.tdr_result_label.setText("")
logger.info("Cannot compute cable length at 0 span")
return
s11 = []
- for d in self.app.data11:
+ for d in self.app.data.s11:
s11.append(np.complex(d.re, d.im))
- window = np.blackman(len(self.app.data11))
+ window = np.blackman(len(self.app.data.s11))
windowed_s11 = window * s11
self.td = np.abs(np.fft.ifft(windowed_s11, FFT_POINTS))
diff --git a/NanoVNASaver/Windows/__init__.py b/NanoVNASaver/Windows/__init__.py
index 73d5ab2f..73e51386 100644
--- a/NanoVNASaver/Windows/__init__.py
+++ b/NanoVNASaver/Windows/__init__.py
@@ -4,6 +4,7 @@
from .CalibrationSettings import CalibrationWindow
from .DeviceSettings import DeviceSettingsWindow
from .DisplaySettings import DisplaySettingsWindow
+from .Files import FilesWindow
from .MarkerSettings import MarkerSettingsWindow
from .Screenshot import ScreenshotWindow
from .SweepSettings import SweepSettingsWindow
diff --git a/NanoVNASaver/__main__.py b/NanoVNASaver/__main__.py
index 19e9a90f..817e1ba4 100644
--- a/NanoVNASaver/__main__.py
+++ b/NanoVNASaver/__main__.py
@@ -4,7 +4,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -34,6 +34,7 @@
from NanoVNASaver.About import VERSION, INFO
from NanoVNASaver.NanoVNASaver import NanoVNASaver
+from NanoVNASaver.Touchstone import Touchstone
def main():
@@ -44,6 +45,10 @@ def main():
help="Set loglevel to debug")
parser.add_argument("-D", "--debug-file",
help="File to write debug logging output to")
+ parser.add_argument("-f", "--file",
+ help="Touchstone file to load as sweep for off device usage")
+ parser.add_argument("-r", "--ref-file",
+ help="Touchstone file to load as reference for off device usage")
parser.add_argument("--version", action="version",
version=f"NanoVNASaver {VERSION}")
args = parser.parse_args()
@@ -79,10 +84,22 @@ def main():
app = QtWidgets.QApplication(sys.argv)
window = NanoVNASaver()
window.show()
+
+ if args.file:
+ t = Touchstone(args.file)
+ t.load()
+ window.saveData(t.s11, t.s21, args.file)
+ window.dataUpdated()
+ if args.ref_file:
+ t = Touchstone(args.ref_file)
+ t.load()
+ window.setReference(t.s11, t.s21, args.ref_file)
+ window.dataUpdated()
try:
app.exec_()
except BaseException as exc:
logger.exception("%s", exc)
+ raise exc
if __name__ == '__main__':
main()
diff --git a/README.md b/README.md
index 6824534a..7e902f2f 100644
--- a/README.md
+++ b/README.md
@@ -12,11 +12,22 @@ sweep frequency spans in segments to gain more than 101 data
points, and generally display and analyze the resulting data.
- Copyright 2019, 2020 Rune B. Broberg
-- Copyright 2020 NanoVNA-Saver Authors
+- Copyright 2020, 2021 NanoVNA-Saver Authors
Latest Changes
--------------
+### Changes in v0.3.10
+
+- Default Band ranges for 5 and 9cm
+- Layout should fit on smaller screens
+- Fixed fixed axis settings
+- Show VNA type in port selector
+- Recognise tinySA (screenshot only)
+- Some more cables in TDR
+- Reference plane applied after calibration
+- Calibration fixes by DiSlord
+
### Changes in v0.3.9
- TX Power on V2
@@ -24,13 +35,6 @@ Latest Changes
- Magnitude Z Chart
- VSWR Chart improvements
-### Changes in v0.3.8
-
-- Allow editing of bands above 2.4GHz
-- Restore column layout on start
-- Support for Nanovna-F V2
-- Fixes a crash with S21 hack
-
Introduction
------------
@@ -69,13 +73,14 @@ Running the application
The software was written in Python on Windows, using Pycharm, and the modules
PyQT5, numpy, scipy and pyserial.
+Main development is currently done on Linux (Ubuntu 21.04)
### Binary releases
You can find 64bit binary releases for Windows, Linux and MacOS under
-Versions older than Windows 7 are not known to work.
+Versions older than Windows 7 are not known to work.
#### Windows 7
@@ -102,11 +107,14 @@ The downloadable executable runs directly, and requires no installation.
NanoVNASaver
-#### Ubuntu 18.04 & 19.04
+#### Ubuntu 20.04
-1. Install python3.7 and pip
+1. Install python3.8 and pip
- sudo apt install python3.7 python3-pip
+ sudo apt install python3.8 python3-pip
+ python3 -m venv ~/.venv_nano
+ . ~/.venv_nano/bin/activate
+ pip install -U pip
2. Clone repo and cd into the directory
@@ -115,8 +123,7 @@ The downloadable executable runs directly, and requires no installation.
3. Update pip and run the pip installation
- python3.7 -m pip install -U pip
- python3.7 -m pip install .
+ python3 -m pip install .
(You may need to install the additional packages python3-distutils,
python3-setuptools and python3-wheel for this command to work on some
@@ -124,7 +131,9 @@ The downloadable executable runs directly, and requires no installation.
4. Once completed run with the following command
- python3.7 nanovna-saver.py
+ . ~/.venv_nano/bin/activate
+ python3 nanovna-saver.py
+
#### MacPorts
@@ -144,19 +153,19 @@ Via a MacPorts distribution maintained by @ra1nb0w.
1. Install Homebrew from (This will ask for your password)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
-
+
2. Python :
brew install python
-3. Pip :
+3. Pip :
Download the get-pip.py file and run it to install pip
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python3 get-pip.py
-4. NanoVNASaver Installation :
+4. NanoVNASaver Installation :
clone the source code to the nanovna-saver folder
git clone https://github.com/NanoVNA-Saver/nanovna-saver
diff --git a/requirements.txt b/requirements.txt
index 1cf3ea0f..1c49ba0c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,5 @@
-scipy
-cython
-pyqt5
-pyserial
-numpy
+pyserial==3.5
+PyQt5==5.15.4
+numpy==1.21.1
+scipy==1.7.1
+cython==0.29.24
diff --git a/setup.cfg b/setup.cfg
index f2a8d320..646e60b8 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -3,10 +3,25 @@ name = NanoVNASaver
author = Rune B. Broberg
license = GNU GPL V3
license_file = LICENSE
-description = A multiplatform tool to save Touchstone files from the
- NanoVNA, sweep frequency spans in segments to gain more
- data points, and generally display and analyze the
- resulting data.
+description =
+ A multiplatform tool to save Touchstone files from the
+ NanoVNA, sweep frequency spans in segments to gain more
+ data points, and generally display and analyze the
+ resulting data.
long_description = file: README.md
url = https://github.com/NanoVNA-Saver/nanovna-saver
version = attr: NanoVNASaver.About.VERSION
+
+[options]
+packages = find_namespace:
+install_requires=
+ pyserial==3.5
+ PyQt5==5.15.4
+ numpy==1.21.1
+ scipy==1.7.1
+ cython==0.29.24
+python_requires = >=3.8, <4
+
+[options.entry_points]
+console_scripts =
+ NanoVNASaver = NanoVNASaver.__main__:main
diff --git a/setup.py b/setup.py
index 86056c77..75f50e2c 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,33 +16,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-import sys
+from setuptools import setup
-if sys.version_info < (3, 7):
- print("You need at least Python 3.7 for this application!")
- if sys.version_info[0] < 3:
- print("try running with python3 {}".format(" ".join(sys.argv)))
- sys.exit(1)
-
-try:
- from setuptools import setup, find_packages
-except ImportError:
- print("Could not find setuptools")
- print("Try installing them with pip install setuptools")
- sys.exit(1)
-
-setup(
- packages=find_packages(exclude=["test", ]),
- entry_points={
- 'console_scripts': [
- 'NanoVNASaver = NanoVNASaver.__main__:main'
- ],
- },
- install_requires=[
- 'pyserial',
- 'PyQt5',
- 'numpy',
- 'scipy',
- 'cython',
- ],
-)
+setup()
diff --git a/test/__init__.py b/test/__init__.py
index f9173b28..e9dffab6 100644
--- a/test/__init__.py
+++ b/test/__init__.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/test/data/ferrit_1.s1p b/test/data/ferrit_1.s1p
new file mode 100644
index 00000000..a1e82b4d
--- /dev/null
+++ b/test/data/ferrit_1.s1p
@@ -0,0 +1,1011 @@
+# HZ S RI R 50
+50000 -1.0067842098182076 0.0011153017447806102
+248166 -1.006174928728769 0.00442840579792926
+446332 -1.0054439612523787 0.0077487273768852725
+644498 -1.0051549662655994 0.011149517613500231
+842664 -1.0050452360708364 0.014481014389026327
+1040830 -1.0043882428768678 0.017227297350967206
+1238996 -1.0037918310691745 0.020354932787176084
+1437162 -1.0034141170468516 0.02325987454782127
+1635328 -1.002876109656067 0.025990732983152822
+1833494 -1.0020559524877648 0.029194786788658375
+2031660 -1.00176838980764 0.03213195914966064
+2229826 -1.0013439220260976 0.03467199034934392
+2427992 -1.0007786288604381 0.03777020385543433
+2626158 -1.0004468217453402 0.04017374368900063
+2824324 -0.9997982833527883 0.043162483565451874
+3022490 -0.9992418600237188 0.04568584083685219
+3220656 -0.9984741818329218 0.04859816904997604
+3418822 -0.9977206561394698 0.05125952742965386
+3616988 -0.9971904467236362 0.05403877920402576
+3815154 -0.9966180177851902 0.056630485917703394
+4013320 -0.995834219065029 0.0593084159556062
+4211486 -0.9950376645435575 0.061983646195783615
+4409652 -0.9940868145151401 0.06441032507087249
+4607818 -0.9933964223238081 0.06714042051651717
+4805984 -0.9929049501080238 0.06960290959433812
+5004150 -0.9919489151940186 0.07222780069291514
+5202316 -0.9909244067864482 0.07472432558356851
+5400482 -0.9903416584194137 0.0772191342059417
+5598648 -0.9894742888969045 0.07986170715805861
+5796814 -0.9886201270399215 0.08222425433067682
+5994980 -0.987659421461512 0.08445997530147394
+6193146 -0.9869386417633438 0.08720052144993128
+6391312 -0.9859102716906557 0.08970662974597833
+6589478 -0.9848381345608361 0.09210987395236654
+6787644 -0.984550026274278 0.09359798838738188
+6985810 -0.9833702086908046 0.0961213631945155
+7183976 -0.9827490470288655 0.09896777364573935
+7382142 -0.981700224166055 0.10116965223861381
+7580308 -0.9809946599431056 0.10343621396570513
+7778474 -0.9798102963405603 0.1058522271955124
+7976640 -0.979117828235555 0.1082789037353792
+8174806 -0.9781903851534418 0.11009982705129343
+8372972 -0.9770134859468044 0.11251438669723268
+8571138 -0.9761715134567278 0.11466580976696307
+8769304 -0.9751041793122773 0.11692821093872065
+8967470 -0.9740652566402871 0.11876885388940353
+9165636 -0.9731686938270698 0.12114745447289928
+9363802 -0.9723853549799725 0.12334488050532212
+9561968 -0.9714493091685139 0.12534871005675216
+9760134 -0.9704436777588432 0.1273418078672132
+9958300 -0.9698079814231773 0.12954084870142762
+10156466 -0.967976971798632 0.13164867317648446
+10354632 -0.9670545323802334 0.13356791075056315
+10552798 -0.9660461691241133 0.13577543278935073
+10750964 -0.9651076466159975 0.1379756063282972
+10949130 -0.9640930099431616 0.14017972448644114
+11147296 -0.9630667960340629 0.14207472612317798
+11345462 -0.9621520693950755 0.14394388120022455
+11543628 -0.961148303683412 0.14589672886932037
+11741794 -0.9600295222945815 0.14788564654788577
+11939960 -0.9590942934634199 0.1498017109375031
+12138126 -0.9581103455640871 0.1519097956659754
+12336292 -0.9570039922793734 0.15356668874059393
+12534458 -0.955924248031724 0.15568790154092102
+12732624 -0.9551055939835781 0.15739706129127182
+12930790 -0.9541470120958915 0.15931123620164145
+13128956 -0.9532388298114454 0.16132665832661935
+13327122 -0.9521117632684364 0.16317279963585063
+13525288 -0.9511213977487271 0.1650208859216832
+13723454 -0.9500729168297845 0.1669760144141851
+13921620 -0.9488018735868131 0.16884185968989465
+14119786 -0.9480608771693675 0.17045066481798085
+14317952 -0.9468319529246911 0.17230661432279473
+14516118 -0.9457750514684694 0.17426703538006946
+14714284 -0.944843107639742 0.17581728384101158
+14912450 -0.9438868549662003 0.17780360827119143
+15110616 -0.9428002474851552 0.17944202901537606
+15308782 -0.9413602948497594 0.18129937926727888
+15506948 -0.9406043598007816 0.1831110835241129
+15705114 -0.939531744001078 0.18464612861912305
+15903280 -0.9385099661963262 0.18636077191358758
+16101446 -0.9374401696013832 0.1881195121673736
+16299612 -0.9366588377036488 0.18981717689813293
+16497778 -0.9355822351106935 0.19123319659663018
+16695944 -0.9345018205636292 0.19332504949025395
+16894110 -0.9330866897864665 0.19461956935224467
+17092276 -0.9322753797183345 0.19641077756599704
+17290442 -0.9314426314102576 0.1979891038290788
+17488608 -0.930379039924932 0.19986480357564965
+17686774 -0.9296394143488722 0.20162864621358062
+17884940 -0.9283287052955264 0.2031231020370391
+18083106 -0.927091383377227 0.20441080188077884
+18281272 -0.926177608471449 0.2064746946202179
+18479438 -0.9251670407673479 0.20800552117500065
+18677604 -0.9239941661653075 0.20944648620705394
+18875770 -0.923145070005502 0.21104656045784723
+19073936 -0.9217865151937135 0.21256243639497213
+19272102 -0.9208561294258383 0.2139120959354267
+19470268 -0.9198835793392668 0.21551580951518073
+19668434 -0.9187472147569371 0.21701172294894344
+19866600 -0.9176074718083468 0.21880840926719022
+20064766 -0.9166004026011741 0.2204187078578255
+20262932 -0.9155462474470061 0.2219481371999832
+20461098 -0.9142534236961943 0.22316654321623372
+20659264 -0.9132949169926747 0.22464679953692732
+20857430 -0.9124799620221374 0.22648404052717702
+21055596 -0.9115660814760211 0.22774841296495946
+21253762 -0.9103499118889637 0.22924099525612676
+21451928 -0.9089356058479902 0.23092041108623035
+21650094 -0.9080848529655329 0.2321565462173064
+21848260 -0.9070984332393975 0.2337383111644792
+22046426 -0.9058740915675512 0.23517068617187112
+22244592 -0.9049919581664149 0.2364685962219484
+22442758 -0.9039568244006437 0.23771552482791045
+22640924 -0.9029617427621202 0.23934749122085083
+22839090 -0.9017370336355649 0.24070315025090508
+23037256 -0.9008043489891565 0.24200105743039937
+23235422 -0.8999735168822897 0.24349372226486066
+23433588 -0.8983984226311618 0.2447833497394113
+23631754 -0.8980395586687081 0.24618258511208024
+23829920 -0.8967618441093705 0.2474401275952972
+24028086 -0.8956164185097459 0.24878819900652316
+24226252 -0.8946196186085239 0.2504257615086617
+24424418 -0.8932607663878046 0.2514850758981898
+24622584 -0.8926278199363855 0.25301783603568406
+24820750 -0.8917556326270796 0.2542404777806109
+25018916 -0.8907418743693375 0.2555941930268214
+25217082 -0.889459991841326 0.2570338110089938
+25415248 -0.8886629381696207 0.258200962457762
+25613414 -0.887638424895949 0.2593713827495892
+25811580 -0.886429822760192 0.26096673443003837
+26009746 -0.8853046534764225 0.26209056700363625
+26207912 -0.8843700991216942 0.2631346055509418
+26406078 -0.8835240701674437 0.2648469720496766
+26604244 -0.8822121987782577 0.2655134462314514
+26802410 -0.8813754938310732 0.26723021454605106
+27000576 -0.8804429366358041 0.2688982923155748
+27198742 -0.8793842206541188 0.2693534248026331
+27396908 -0.8786026218898448 0.2705911911783534
+27595074 -0.8775714336336032 0.272263593126237
+27793240 -0.876472292015038 0.27313412548182303
+27991406 -0.8754684101353883 0.27462609812031596
+28189572 -0.8741117837697518 0.27551181320506163
+28387738 -0.8733767878306009 0.2767798185189747
+28585904 -0.8725463836954229 0.2780635212198391
+28784070 -0.871321324819546 0.27924943773169075
+28982236 -0.8706526706011414 0.2798414309789514
+29180402 -0.8698783834285405 0.28184374945467006
+29378568 -0.8686095786398663 0.28252944953335685
+29576734 -0.8677662773515005 0.283548264817558
+29774900 -0.8663575690445269 0.284885705569434
+29973066 -0.865193436713664 0.28567716959534284
+30171232 -0.8644839592234874 0.28703202697661034
+30369398 -0.863643663383751 0.28843853513469325
+30567564 -0.8625173651062521 0.2893546705239978
+30765730 -0.8614453003444347 0.2905675528250923
+30963896 -0.8606660543459355 0.2918606391766511
+31162062 -0.8597303485492499 0.29255927048210084
+31360228 -0.8587990025284072 0.29400981100178725
+31558394 -0.8576454117497913 0.2949848798864381
+31756560 -0.8565408260669212 0.2963546992780333
+31954726 -0.8556526152466318 0.2969139376919538
+32152892 -0.8546340182896094 0.2984172210449983
+32351058 -0.8537808923287259 0.2993298191230459
+32549224 -0.8526780661632113 0.3002192209665124
+32747390 -0.8518329382863373 0.30150059596031387
+32945556 -0.8507322510364121 0.3023742017499433
+33143722 -0.8498960937416468 0.3034235797309205
+33341888 -0.8492251103801843 0.3046409964123543
+33540054 -0.8477492757988856 0.30569793853910976
+33738220 -0.8468070237658533 0.30714294956857224
+33936386 -0.8460356489216412 0.3078928077256031
+34134552 -0.8448928176402594 0.30900401867238103
+34332718 -0.8443035423921404 0.30972217285151665
+34530884 -0.8431058728115677 0.31073751636016833
+34729050 -0.8420718299602861 0.3120003094517087
+34927216 -0.841432390032838 0.31281419428018536
+35125382 -0.840337624839326 0.31410741502451556
+35323548 -0.8394145734817079 0.31480587310344577
+35521714 -0.8383591082153516 0.3158635326072553
+35719880 -0.8372603524692601 0.31665754245859307
+35918046 -0.8364555231168818 0.3174399050827135
+36116212 -0.8357510049676001 0.3189237098381592
+36314378 -0.8346458666652832 0.3199522935365079
+36512544 -0.8336646028712648 0.32107678373297605
+36710710 -0.8327999828089462 0.321564371496974
+36908876 -0.8317112951675103 0.3221185784018417
+37107042 -0.8309079791394892 0.3239811959448411
+37305208 -0.8300046358436488 0.32464765499252257
+37503374 -0.8295545354892664 0.32557239265837595
+37701540 -0.8281332670080382 0.3266595158819125
+37899706 -0.8273896733161065 0.3273043475352658
+38097872 -0.8262906865884024 0.32841303964438384
+38296038 -0.8255379921604364 0.32900090860663067
+38494204 -0.8242353547724388 0.3300456443681177
+38692370 -0.823595207008422 0.33080533394441997
+38890536 -0.8227742939131598 0.3322714173243776
+39088702 -0.8219673551254358 0.3328855049479158
+39286868 -0.8211805705787962 0.33387070123472334
+39485034 -0.8200573898364841 0.33511542643005054
+39683200 -0.8189866621255957 0.3357515021603277
+39881366 -0.8182541092992759 0.33683639148964856
+40079532 -0.817430554891509 0.33744125591379037
+40277698 -0.8165702947551676 0.33828770687631293
+40475864 -0.8155886948112112 0.33948529206784345
+40674030 -0.8145273614218946 0.33999059082953836
+40872196 -0.8135816663416201 0.3408307416015232
+41070362 -0.8128276351709028 0.3420128529352481
+41268528 -0.8119968006060437 0.34300530100711635
+41466694 -0.8113449705660916 0.343736848019489
+41664860 -0.8100915085783266 0.34433033607569297
+41863026 -0.8094167651754376 0.34530749032817604
+42061192 -0.8085466976584907 0.3462602225863128
+42259358 -0.8076736055750685 0.3471584064137924
+42457524 -0.8065856563027525 0.34759393474464395
+42655690 -0.8057384806813437 0.34869018318538825
+42853856 -0.8052396936565194 0.3495183070884364
+43052022 -0.8044276424563785 0.3505451370871938
+43250188 -0.8032230759483038 0.3515508482612722
+43448354 -0.8026919693962659 0.3520621778475138
+43646520 -0.801904169056342 0.35244596904631775
+43844686 -0.8007482337077697 0.3533512577118196
+44042852 -0.7999237315013898 0.35461674576093244
+44241018 -0.7991370254538763 0.3551138305167819
+44439184 -0.7984136099053074 0.35580870829310224
+44637350 -0.7974390146414406 0.35700416584175326
+44835516 -0.7969868972713983 0.35783781644094076
+45033682 -0.7956216545086933 0.35843180884298637
+45231848 -0.7951907288434826 0.35914782503588333
+45430014 -0.7940815261765292 0.3599197721316027
+45628180 -0.7934814023180559 0.3610826285391673
+45826346 -0.7924431090683105 0.361916701792193
+46024512 -0.7910982146293022 0.3624931348310251
+46222678 -0.7907214350331777 0.3636195389239837
+46420844 -0.7899408712783115 0.36394880072654323
+46619010 -0.7891129510896255 0.3645639533025291
+46817176 -0.7883203634687393 0.36562997562280436
+47015342 -0.7873194306746885 0.36610733641487736
+47213508 -0.7864728972838668 0.36709624807598096
+47411674 -0.7855792423044223 0.36795442060241534
+47609840 -0.7853357874625253 0.3685689613029082
+47808006 -0.7843678546397739 0.36933277199963516
+48006172 -0.7832228583515005 0.37009000026442607
+48204338 -0.7825809548838522 0.3707621913002217
+48402504 -0.7818591107688656 0.3715264178439368
+48600670 -0.7811741590104013 0.37180826950726553
+48798836 -0.780080793409311 0.372755030461636
+48997002 -0.7795267431491626 0.37359722414215507
+49195168 -0.7785665575444365 0.374297125901011
+49393334 -0.7773246870306969 0.3752110123634278
+49591500 -0.7768484075641537 0.3756650028570994
+49789666 -0.7764139535258106 0.3764273792458343
+49987832 -0.7744502118795498 0.3764104305011052
+50185998 -0.7707908982264537 0.37429529247881604
+50384164 -0.7730922936078594 0.37724586154133305
+50582330 -0.7707562469183715 0.3767318220433405
+50780496 -0.7708885659654905 0.3778926574072053
+50978662 -0.7714891571044346 0.3797933999818348
+51176828 -0.770520290546701 0.38022780511637433
+51374994 -0.7691483836190265 0.38070266560258004
+51573160 -0.7683572152570004 0.38145165351584476
+51771326 -0.7672125937029892 0.38147498963288523
+51969492 -0.7656367964023196 0.3817477093903656
+52167658 -0.7658426535279186 0.38337584204722164
+52365824 -0.7640651713231891 0.38267619169559863
+52563990 -0.7637114912509817 0.3841071762932735
+52762156 -0.7640153068348827 0.38607231785177826
+52960322 -0.7596650927326614 0.38360297803619325
+53158488 -0.7595276303930529 0.3835399519327589
+53356654 -0.7574605184326988 0.3845386557524129
+53554820 -0.7592049332239773 0.38646964571582254
+53752986 -0.758438985410661 0.3874349156058
+53951152 -0.7579882408607237 0.38861137132820445
+54149318 -0.7589263820988276 0.3905335069914232
+54347484 -0.7572892407892087 0.3903992946551311
+54545650 -0.7549458747329352 0.38865520230041734
+54743816 -0.7545389348305513 0.38965421335395317
+54941982 -0.754169228669463 0.39057053521022145
+55140148 -0.7529738544068736 0.3906828450146439
+55338314 -0.7525216741156927 0.39127664000960544
+55536480 -0.7515347507778944 0.39235966004305156
+55734646 -0.7511687415901723 0.3931101629887206
+55932812 -0.7502018155438528 0.39374830323862536
+56130978 -0.749383233738564 0.3938909292986401
+56329144 -0.7485467564545134 0.3948984263574892
+56527310 -0.7474074772469045 0.3947119283917348
+56725476 -0.7470598086201965 0.3954204715163911
+56923642 -0.7463831764701275 0.39588176346405074
+57121808 -0.7459145821153726 0.39662612494644417
+57319974 -0.7447036853422573 0.3971686990945173
+57518140 -0.7439176401288284 0.3977886308440768
+57716306 -0.7434430807742319 0.3985275655121905
+57914472 -0.742645887024075 0.3990257718380538
+58112638 -0.7417306514193842 0.3991174825970683
+58310804 -0.7408410711945405 0.39999905062576147
+58508970 -0.7399470588290185 0.4003516895469387
+58707136 -0.7396767549317234 0.4009286050028892
+58905302 -0.7387382834672998 0.4018618771364599
+59103468 -0.7382613654956163 0.4021579486179507
+59301634 -0.7375654413865972 0.4032163873380167
+59499800 -0.7369259314748674 0.4034201684394169
+59697966 -0.7359763767481418 0.4039953630625601
+59896132 -0.7353274947971663 0.4042451032881088
+60094298 -0.7313435546576718 0.4046425556593557
+60292464 -0.7309682744445333 0.40498260397984664
+60490630 -0.7300154297892028 0.4056723060736549
+60688796 -0.7293376250857881 0.40611113801676063
+60886962 -0.728725849199044 0.4070440472749164
+61085128 -0.727920492865363 0.4076082042669511
+61283294 -0.7273397724027149 0.4082626995937004
+61481460 -0.7267025271346965 0.4087684484839439
+61679626 -0.7258277735386227 0.40909913862939185
+61877792 -0.7249186784676336 0.4097011072286138
+62075958 -0.724412281312979 0.41030959375505605
+62274124 -0.7236281870072936 0.41045358895310263
+62472290 -0.7228081666853691 0.41124965711527595
+62670456 -0.7224489127118217 0.41125086333711186
+62868622 -0.7213434806288317 0.4123224094695672
+63066788 -0.7207886071207602 0.4128992906814895
+63264954 -0.7200010769991486 0.4130437729697932
+63463120 -0.7194829935681456 0.4137474303208616
+63661286 -0.7186217605065742 0.4142297769264026
+63859452 -0.7181332164040228 0.41467483777981407
+64057618 -0.7174813364050859 0.41498123088142513
+64255784 -0.7165732925018745 0.41531160908540893
+64453950 -0.7161586074104362 0.4163020026691458
+64652116 -0.7152744900768129 0.4165678134912524
+64850282 -0.7146815158494956 0.4169558961499926
+65048448 -0.7138660472983613 0.41737371527394646
+65246614 -0.7133399810831555 0.4181500539038303
+65444780 -0.712621144210001 0.41841133790861307
+65642946 -0.7121258910532637 0.41893550746016966
+65841112 -0.7113638380343305 0.41958934610431975
+66039278 -0.7103092780767438 0.4198646959260608
+66237444 -0.7098983683219684 0.4202096812582987
+66435610 -0.7091650481142554 0.4206842809071137
+66633776 -0.7087774090113091 0.42151269584570344
+66831942 -0.708056544925192 0.42176819318712927
+67030108 -0.7075583848839757 0.4218376825656261
+67228274 -0.7068170569880725 0.42234288015956417
+67426440 -0.7062938597869833 0.4230236093751219
+67624606 -0.705565508209929 0.42338196468664774
+67822772 -0.7051313402485792 0.4240068828445301
+68020938 -0.7042383319316167 0.4243614566105464
+68219104 -0.7035058071614918 0.42469290685386857
+68417270 -0.702924728588514 0.42545544079499975
+68615436 -0.7023291486137295 0.42579892477386627
+68813602 -0.7014804926167261 0.4259489704918657
+69011768 -0.7009170178054169 0.42663056317811043
+69209934 -0.7003827183779021 0.42727077507770045
+69408100 -0.6995511791609504 0.427484470317891
+69606266 -0.6989060669176294 0.42822116142772076
+69804432 -0.6984884630843597 0.4285104010430358
+70002598 -0.6975406933313404 0.4290286977004459
+70200764 -0.6971239422256154 0.42972404636322137
+70398930 -0.6964254960634217 0.429925627746694
+70597096 -0.6960604463573461 0.43045669229394284
+70795262 -0.6955629467555031 0.43039266915017405
+70993428 -0.694485076988798 0.4311111579699656
+71191594 -0.6940804645072466 0.4318256031277793
+71389760 -0.6932089452286561 0.4320264605417869
+71587926 -0.6924652582053614 0.4323437982337504
+71786092 -0.6921905523269134 0.4325754592736454
+71984258 -0.6912170515356005 0.43331208298773793
+72182424 -0.6907158302865427 0.43371755681343743
+72380590 -0.6902478074038026 0.43387940844424105
+72578756 -0.6889167677763421 0.43462184959368055
+72776922 -0.6887004302819859 0.4349081332661876
+72975088 -0.6879842398113776 0.4356741501388379
+73173254 -0.6873629858231866 0.4356798225191158
+73371420 -0.6872001429733703 0.4363699870324563
+73569586 -0.6863208947010055 0.43664546789275066
+73767752 -0.6856416067451563 0.4369298758397648
+73965918 -0.6850133463297691 0.43748362604759966
+74164084 -0.684464311531097 0.4379881851740689
+74362250 -0.6839055729789474 0.4382852865769898
+74560416 -0.6831770657666768 0.43846150968387576
+74758582 -0.6824597662663247 0.43922400018089874
+74956748 -0.6818534266691687 0.43958053941373015
+75154914 -0.6811616277496443 0.4399769266885436
+75353080 -0.6805651982640576 0.44021655524511016
+75551246 -0.6800322879054607 0.44058690854094235
+75749412 -0.679325319952532 0.4412190050319499
+75947578 -0.678901051913593 0.4415273775260319
+76145744 -0.6779117992106786 0.4417858491171413
+76343910 -0.6774210158385806 0.44210619751273067
+76542076 -0.6768796387835402 0.4426459091092083
+76740242 -0.6764617414525529 0.4433152571145129
+76938408 -0.675857656133877 0.4436593843327578
+77136574 -0.6748409799241236 0.4437312289276521
+77334740 -0.6741721443472006 0.44442787228279035
+77532906 -0.6734840817996968 0.4446054667031962
+77731072 -0.673028097260254 0.44468768013051785
+77929238 -0.6722972806542328 0.4456433885646921
+78127404 -0.672044162748587 0.44560969275508555
+78325570 -0.6713710746623028 0.4458990149116179
+78523736 -0.6706251604364583 0.4458987252470313
+78721902 -0.6699539315767794 0.44636842880174976
+78920068 -0.6694284039472631 0.4466700159050375
+79118234 -0.6691715701125515 0.44679814950548796
+79316400 -0.6685228410964374 0.4470646722357106
+79514566 -0.6684890183220961 0.4474087062102144
+79712732 -0.6680761906949918 0.44777358104678705
+79910898 -0.6677935452267622 0.4476727106185119
+80109064 -0.6670841586233227 0.44817206893514067
+80307230 -0.6666283954941178 0.44874047972534026
+80505396 -0.6666700339090063 0.4490686018853184
+80703562 -0.6660568598451043 0.4494778700259624
+80901728 -0.6653933578931477 0.450290497156489
+81099894 -0.6652709839770297 0.4504864952328522
+81298060 -0.6649118735438916 0.45090649151775836
+81496226 -0.6639197809821653 0.4515852751233085
+81694392 -0.6634440292684904 0.4522257817006163
+81892558 -0.6630885390283017 0.45256476172231297
+82090724 -0.6623085946289428 0.4527303492403151
+82288890 -0.6618661344489934 0.4528356724359824
+82487056 -0.6607047122592 0.4537172892727244
+82685222 -0.6602569671767351 0.4533091081600843
+82883388 -0.6601634986601657 0.4536785307048919
+83081554 -0.6596887049591031 0.4535977190589028
+83279720 -0.6592464965511378 0.4540528569388933
+83477886 -0.6589829114683257 0.45476528848169595
+83676052 -0.6583454810664189 0.4546263241051149
+83874218 -0.6582497235820739 0.45508317291184786
+84072384 -0.6575086362127088 0.4555029339468835
+84270550 -0.6571117011796981 0.4556722528334468
+84468716 -0.6568636645891508 0.45640257224350755
+84666882 -0.6563600262128679 0.45657147427425426
+84865048 -0.6554926702908731 0.4565031850479298
+85063214 -0.6555914213419471 0.4571022977443253
+85261380 -0.6546786460524068 0.45723051164379236
+85459546 -0.6543366631871695 0.45777261194069496
+85657712 -0.65390016763469 0.45782260068959385
+85855878 -0.653469006233002 0.4582423960151682
+86054044 -0.6529322055950684 0.4592645952886424
+86252210 -0.6524408502068084 0.4590500665284407
+86450376 -0.6518446591308378 0.4592742422209962
+86648542 -0.6517330071988745 0.45962296966424315
+86846708 -0.6517847838656406 0.460158672819799
+87044874 -0.6509865548472552 0.4599396655060736
+87243040 -0.6500646746619909 0.4604808731242747
+87441206 -0.6499475857846898 0.4605852319718203
+87639372 -0.6491839474024073 0.46119290394438606
+87837538 -0.6487316644155428 0.4618101566442238
+88035704 -0.6484976701221442 0.4619577533238802
+88233870 -0.6480117594395981 0.462184101891592
+88432036 -0.6475279764484525 0.46232331053976433
+88630202 -0.6472458883083045 0.4625211533280293
+88828368 -0.6467485824978817 0.4630167591614821
+89026534 -0.6460518653581958 0.4630818328640295
+89224700 -0.6455133956958451 0.46390510623905973
+89422866 -0.6448644982885476 0.4638330066776778
+89621032 -0.6448840587738164 0.4638723207930118
+89819198 -0.6447172870304005 0.46457152485718395
+90017364 -0.6440716478855432 0.46481442849184057
+90215530 -0.6434898109542775 0.46479160229281735
+90413696 -0.6428673155604864 0.4653611382809977
+90611862 -0.6428611709288591 0.46553113819253683
+90810028 -0.642254100950694 0.46561519597192347
+91008194 -0.6417709039969375 0.4658091084568882
+91206360 -0.6417592603144574 0.4661908570626736
+91404526 -0.6410412721835878 0.466519673922666
+91602692 -0.6408028880155469 0.46662204400666546
+91800858 -0.6408186870187983 0.46683881114726117
+91999024 -0.6401916052991847 0.4671736153681838
+92197190 -0.6397731898722043 0.46719505051299715
+92395356 -0.6395384822264618 0.4679923328296805
+92593522 -0.6390683268980196 0.46792883736179747
+92791688 -0.6390547187350607 0.468509319481825
+92989854 -0.639094057628195 0.46835616637193916
+93188020 -0.6386035512586636 0.4695166104033484
+93386186 -0.637970408304923 0.46913535057797423
+93584352 -0.6380029973697632 0.46972003344726737
+93782518 -0.6375980739018302 0.4701303383374838
+93980684 -0.637776845094603 0.4706578590959565
+94178850 -0.6370788151562904 0.4713465227071158
+94377016 -0.6372220161403322 0.4720203806901549
+94575182 -0.6365694390704542 0.4726700539693395
+94773348 -0.6357552483982966 0.4734185928985557
+94971514 -0.6354769167250633 0.4742723725637641
+95169680 -0.6347075258414979 0.47472515282530064
+95367846 -0.6335991894586191 0.47542530156168183
+95566012 -0.6334610880274091 0.47598886697432796
+95764178 -0.6326037346346127 0.47659607469682236
+95962344 -0.6315446780060631 0.47714219505879224
+96160510 -0.6308730133133656 0.47749827083947977
+96358676 -0.6296781213946386 0.4776695413481489
+96556842 -0.6286209173700816 0.4779471046011256
+96755008 -0.6284493107947633 0.4782594942389335
+96953174 -0.6275584163872552 0.4784827419910441
+97151340 -0.6267272604814096 0.478437006501568
+97349506 -0.6266363811027783 0.4785590621748273
+97547672 -0.6251932847881864 0.4787776531767344
+97745838 -0.6243769616704046 0.4783420868188296
+97944004 -0.624008200773998 0.47893116867550284
+98142170 -0.6233315761163514 0.47863467480754895
+98340336 -0.622930183193089 0.478618742641787
+98538502 -0.6225243124383903 0.4785628529667713
+98736668 -0.6222075320433874 0.47850907707738277
+98934834 -0.621397201207573 0.4787159144187491
+99133000 -0.6208550303476552 0.47912049037732257
+99331166 -0.6208309049865064 0.4790544918546379
+99529332 -0.6203935920895504 0.47912129062757464
+99727498 -0.619722970743443 0.47936164583563384
+99925664 -0.6195757564193543 0.47907691717440376
+100123830 -0.6183407798420926 0.4796732535471175
+100321996 -0.6182808420573925 0.4795803965469518
+100520162 -0.6171570457801356 0.48015905466357484
+100718328 -0.6174058052480385 0.4798729056188302
+100916494 -0.6177997893813632 0.4801731423838648
+101114660 -0.61653174701606 0.48036798541265646
+101312826 -0.6162469373199918 0.4806642396873073
+101510992 -0.6156467331541975 0.4803223999576466
+101709158 -0.6155599952605131 0.4806500157321498
+101907324 -0.614986895360278 0.4808633867477019
+102105490 -0.6143987787544126 0.4809344899950372
+102303656 -0.6145034301389778 0.4809387358586457
+102501822 -0.6138780891451949 0.4810864176440074
+102699988 -0.6136139135229629 0.48144178061822884
+102898154 -0.6132622365560887 0.4815592663507585
+103096320 -0.6124830640614451 0.48231901111649866
+103294486 -0.6118152612693558 0.48193702468084976
+103492652 -0.611890416948505 0.48235210895211456
+103690818 -0.611537143540606 0.4821289989391377
+103888984 -0.611220002572808 0.4825928575648137
+104087150 -0.610155460640548 0.4824016452508259
+104285316 -0.6094541725268401 0.48257264060222566
+104483482 -0.6092032253390163 0.48303992732215656
+104681648 -0.6090298670113712 0.48269057899081597
+104879814 -0.6091860458296996 0.4828165440961778
+105077980 -0.608425787602816 0.4829238862032395
+105276146 -0.6079481400100649 0.4840008062501346
+105474312 -0.6077336502475178 0.4831852313480371
+105672478 -0.6071411528400177 0.4837157672833861
+105870644 -0.6065651428440506 0.48360599331025195
+106068810 -0.6061997845420667 0.48422675994259845
+106266976 -0.605979559630762 0.48449294522835085
+106465142 -0.6062200217963503 0.48476101146994544
+106663308 -0.6053868991142947 0.484551009008495
+106861474 -0.604959362218139 0.4844415589519883
+107059640 -0.6039922599178241 0.4847811384560404
+107257806 -0.6038824772843425 0.4850970104974923
+107455972 -0.6036482079778263 0.48546809500520816
+107654138 -0.6030227943980635 0.48513758703688986
+107852304 -0.603257520399321 0.48527321114674243
+108050470 -0.6024801026673421 0.4857345708138489
+108248636 -0.601778179527166 0.4853005321965566
+108446802 -0.6015866644148039 0.48573999878120766
+108644968 -0.6016809123616229 0.48550665046688
+108843134 -0.6012503622224767 0.48620341565335323
+109041300 -0.6004559846442283 0.48558215469420357
+109239466 -0.5998114329304941 0.48640418620904263
+109437632 -0.5995264955803536 0.4863713159650829
+109635798 -0.59953351298893 0.48642209428004
+109833964 -0.599139055485738 0.48692829007787114
+110032130 -0.5987355494744729 0.4870030711465913
+110230296 -0.5988294010796436 0.48762361364064793
+110428462 -0.5980654685667355 0.4871069279655053
+110626628 -0.5980244923887029 0.48757253491566405
+110824794 -0.5972748938543292 0.48711748182227504
+111022960 -0.5968370689208994 0.48709125459118224
+111221126 -0.5962702464066559 0.4877677106557131
+111419292 -0.5961249375335418 0.48805911584829015
+111617458 -0.5953965048818808 0.4875506932305826
+111815624 -0.5949667753730773 0.4874598409062234
+112013790 -0.594955101272929 0.4876693936525423
+112211956 -0.5944235992348059 0.488203107286507
+112410122 -0.593630393050059 0.4882251603120292
+112608288 -0.5942569662672487 0.48811814048456215
+112806454 -0.5933126759184295 0.4882013283478036
+113004620 -0.5933132721390955 0.4886215606660619
+113202786 -0.5930112797134635 0.48853355817425786
+113400952 -0.5920989270766649 0.487863145208888
+113599118 -0.5922863818140492 0.48858058912801916
+113797284 -0.591392672035369 0.48858431054999635
+113995450 -0.5914587889620107 0.4883134923548115
+114193616 -0.590927436236996 0.4889185036576553
+114391782 -0.5906840949956041 0.48868707294317293
+114589948 -0.5906184872251365 0.48904060569847624
+114788114 -0.5902743884667673 0.48914852298087974
+114986280 -0.5899789656060771 0.4886935774080529
+115184446 -0.5899019818545123 0.48929279762346906
+115382612 -0.5897198456170367 0.4892514610681745
+115580778 -0.5889549784971778 0.489527826485734
+115778944 -0.5890096816406007 0.48955962090829314
+115977110 -0.5881996345790527 0.4894465677779958
+116175276 -0.5882594938135409 0.48943177237030056
+116373442 -0.5882294262193875 0.4900710477799517
+116571608 -0.5872742515562575 0.4900393888292345
+116769774 -0.5872639326786874 0.4901311245232621
+116967940 -0.5873953804990886 0.48973454140679196
+117166106 -0.5870713790898984 0.4904216312742461
+117364272 -0.5866500604062839 0.4901325450840464
+117562438 -0.5864498967104699 0.4907744944885642
+117760604 -0.586155512602819 0.4907866129914429
+117958770 -0.585579399437763 0.4911399953631427
+118156936 -0.5855155058545631 0.4908458288034571
+118355102 -0.585031500118427 0.4905814504395309
+118553268 -0.5843896642654507 0.4905675925252828
+118751434 -0.5846427884855124 0.4910612796088099
+118949600 -0.5845010580278941 0.4911678175225484
+119147766 -0.5840102220531622 0.4913169444268409
+119345932 -0.5838924347785011 0.49120014849837346
+119544098 -0.5834794550536072 0.49135060460971075
+119742264 -0.582977598682201 0.4917604848889139
+119940430 -0.5831166140073434 0.49198301162403707
+120138596 -0.5827074778491068 0.49234829703123395
+120336762 -0.5824026001586855 0.49235328966560793
+120534928 -0.5816237512922889 0.4923717236896079
+120733094 -0.581414180711663 0.4917519151875198
+120931260 -0.5812669754598067 0.49226669535761836
+121129426 -0.5807986645786 0.4926179395721758
+121327592 -0.5803841682562655 0.4927484111022085
+121525758 -0.5801508792562343 0.4924651392507346
+121723924 -0.5799161759940815 0.49279589694329323
+121922090 -0.5795710074416636 0.4925115625569523
+122120256 -0.5791961014105903 0.4927907208324032
+122318422 -0.5790340669981712 0.4930425609160878
+122516588 -0.5789011521510115 0.4928118213876763
+122714754 -0.5782716966036034 0.4932006202198385
+122912920 -0.5780678700887893 0.4930134306039294
+123111086 -0.5780566791636084 0.4932833615481947
+123309252 -0.5772690245390698 0.49296884049057677
+123507418 -0.5773475219042186 0.4931226605592756
+123705584 -0.5766507846314294 0.49319350130057166
+123903750 -0.5765171075114587 0.4933412281454023
+124101916 -0.5760905385859648 0.4935634794339595
+124300082 -0.5752488039180911 0.493445212971143
+124498248 -0.5755537850199501 0.4938676470332876
+124696414 -0.57527778764158 0.4939098334275307
+124894580 -0.5752319904248395 0.4939098992513246
+125092746 -0.5745632233491486 0.49408956936352233
+125290912 -0.5747410396616348 0.4942504768272761
+125489078 -0.5747691058899445 0.49399374064980844
+125687244 -0.5744430349454287 0.49425742190146166
+125885410 -0.5737668935353437 0.4940785650302004
+126083576 -0.5733261804751885 0.49423303522927353
+126281742 -0.5737492062731365 0.49448703658061033
+126479908 -0.5729847568227814 0.49439179017801205
+126678074 -0.5723917750828152 0.4945284119279479
+126876240 -0.5723211384339555 0.4946057049839076
+127074406 -0.5726892172500444 0.49471541233076016
+127272572 -0.5720696124305398 0.4943597410904252
+127470738 -0.5714815110078629 0.49459827371463533
+127668904 -0.5717422628147759 0.49484741994744386
+127867070 -0.5715553748271487 0.4949509620021435
+128065236 -0.5709365231235051 0.4949664526539162
+128263402 -0.5707553307192557 0.49481405065982687
+128461568 -0.570522408049958 0.4948949153122092
+128659734 -0.5705122142890505 0.4952659196871134
+128857900 -0.5701276094172211 0.4949839088126708
+129056066 -0.5699938863369504 0.4950057305419786
+129254232 -0.5695980410749196 0.495253831109717
+129452398 -0.5693180226062361 0.4945103645758094
+129650564 -0.5692324600905139 0.49528890385538515
+129848730 -0.5687329558145349 0.49507918370152537
+130046896 -0.568673052022792 0.49470399798502324
+130245062 -0.5685609691304581 0.4954444221676455
+130443228 -0.5685706721773931 0.49531907886233617
+130641394 -0.5678277207166205 0.49558790773396566
+130839560 -0.5679114672566182 0.49595428552109094
+131037726 -0.5680113311460061 0.49582003521196627
+131235892 -0.5675687974879312 0.4954038082605077
+131434058 -0.5673464402093463 0.49560008106222236
+131632224 -0.5676486192895693 0.49603074429373517
+131830390 -0.5675614825735953 0.4960607786952986
+132028556 -0.5670879375164198 0.49626532960636327
+132226722 -0.5669049882689027 0.4962624773397435
+132424888 -0.5665524388585943 0.49634412582688264
+132623054 -0.5662100579421196 0.4969710360591503
+132821220 -0.5663341045261207 0.497044117308312
+133019386 -0.5658377332224578 0.49696562391917415
+133217552 -0.5653074023412655 0.49741527773490213
+133415718 -0.5647175854084606 0.49746765197517456
+133613884 -0.5649115891383082 0.49747656336483376
+133812050 -0.5641789003030057 0.4976061284223454
+134010216 -0.5637065073787833 0.49807212296063064
+134208382 -0.563532683212857 0.4981095966273183
+134406548 -0.5633552936760094 0.49809560432140265
+134604714 -0.5629896114862153 0.4978482450808561
+134802880 -0.5622568191872842 0.49789902796225405
+135001046 -0.5618275226452968 0.4981790559538528
+135199212 -0.5613929053699076 0.4980334046903302
+135397378 -0.5615815840196675 0.4979892441324557
+135595544 -0.561302489780293 0.49743902405840884
+135793710 -0.5606970605554985 0.4981186149374733
+135991876 -0.5602494003492585 0.49782271367889624
+136190042 -0.5605687690979926 0.4975531676072213
+136388208 -0.5600233001344687 0.49770114688883443
+136586374 -0.5594151059834367 0.49729734692540023
+136784540 -0.5592726083837931 0.49734345408956715
+136982706 -0.5592605363755934 0.49751289469315296
+137180872 -0.559009160850903 0.4974946571597357
+137379038 -0.5587262237325722 0.4971768298488325
+137577204 -0.5583304334290479 0.49802055119701966
+137775370 -0.5579754031984285 0.49754344197316264
+137973536 -0.5581506907385915 0.4976836011455215
+138171702 -0.557569807939111 0.497487449387719
+138369868 -0.5573758542036814 0.49708167407802667
+138568034 -0.557413895559886 0.49773345257582796
+138766200 -0.556613249798277 0.4973789529494263
+138964366 -0.5567333913572909 0.4973266380856803
+139162532 -0.5563591715039249 0.49762331388059716
+139360698 -0.5561805463135717 0.49713582648549604
+139558864 -0.5560081192295959 0.49740438302545154
+139757030 -0.5556536486064293 0.49730993493693587
+139955196 -0.5558130816560867 0.497452747479042
+140153362 -0.555674822158857 0.49738843779086506
+140351528 -0.5554169321418958 0.49714166991469577
+140549694 -0.5545919500474706 0.4972147616369226
+140747860 -0.5546666636789617 0.4969657905903836
+140946026 -0.5545906658039272 0.49683257620598437
+141144192 -0.5540609135771809 0.49739993708426716
+141342358 -0.5539989993789277 0.4971507667805723
+141540524 -0.5540277253511867 0.49713509033146497
+141738690 -0.553449054047963 0.49675141341841444
+141936856 -0.5533862487682308 0.49670170130826896
+142135022 -0.553353082871362 0.49656055551694356
+142333188 -0.5528568366094126 0.49672322923597095
+142531354 -0.5523765871321027 0.4966782881840795
+142729520 -0.552552366028916 0.49629271791353635
+142927686 -0.5520152580382367 0.49639348357531005
+143125852 -0.5520017299299252 0.4965062037815757
+143324018 -0.5518740690408437 0.49688724517705485
+143522184 -0.5513807040156683 0.49678249977201383
+143720350 -0.5512892477988934 0.49629844583972216
+143918516 -0.5513801493538806 0.49677879440217326
+144116682 -0.5510286154563795 0.4969483702147971
+144314848 -0.5510795683394184 0.49635031733702273
+144513014 -0.5506261677058878 0.49687900630137827
+144711180 -0.5501510667091125 0.4963137928803687
+144909346 -0.5502263428072923 0.4968443678478165
+145107512 -0.5497241919207809 0.49648287946128494
+145305678 -0.5497059290001767 0.49641170748792124
+145503844 -0.5501057343223146 0.4966624099465951
+145702010 -0.5492041273029967 0.49662139550081025
+145900176 -0.5490316020533877 0.49619646923682914
+146098342 -0.5487614538739329 0.49575983677054297
+146296508 -0.5488843814517332 0.4964582824127328
+146494674 -0.5488107473572137 0.49576528103141637
+146692840 -0.5482876639180191 0.49609348186288027
+146891006 -0.5485480933975097 0.49629822874146096
+147089172 -0.5483512089840422 0.4964217339284519
+147287338 -0.5478263163113849 0.49592315950512345
+147485504 -0.5472068776139443 0.4955802139983248
+147683670 -0.5477252681403598 0.4955828626489624
+147881836 -0.5469716929309877 0.49564857613490054
+148080002 -0.5473965431042292 0.4961345003451667
+148278168 -0.5468214024549286 0.4956630610134293
+148476334 -0.5462334902346915 0.49529886634136416
+148674500 -0.546859313287581 0.49593335853805026
+148872666 -0.5464167693459767 0.49610562316953677
+149070832 -0.5460169261125747 0.49550247033015216
+149268998 -0.5457438110705707 0.4957018371548848
+149467164 -0.5454546509929339 0.49503405118713995
+149665330 -0.545502368659083 0.49491826021913776
+149863496 -0.5453922448716509 0.4951189109442025
+150061662 -0.5442047941353705 0.49515085847617946
+150259828 -0.5453188101952507 0.4952244509414267
+150457994 -0.5452803070513891 0.4948414063018161
+150656160 -0.5443507430295702 0.4947990414691271
+150854326 -0.5445060090948104 0.4952772409323698
+151052492 -0.5443114186533141 0.4953906519448662
+151250658 -0.5442406152378183 0.49512636699736495
+151448824 -0.5443972149899919 0.494239311915992
+151646990 -0.5434403321271116 0.49463516038468275
+151845156 -0.5434968399604946 0.49442890199941314
+152043322 -0.5431823604272554 0.49498354452877064
+152241488 -0.5430515914078725 0.49397670067553756
+152439654 -0.5430360114538317 0.4945364875721975
+152637820 -0.5427049235333833 0.4946293965783067
+152835986 -0.5432432135180547 0.4942461532890556
+153034152 -0.5420933768588858 0.4942833651277058
+153232318 -0.5420786633464577 0.49426551170648714
+153430484 -0.5423865889926991 0.49428551054703834
+153628650 -0.5424681964116107 0.4945028088431839
+153826816 -0.5418875738744594 0.4937786678715946
+154024982 -0.5415349387658926 0.493579353590692
+154223148 -0.541519521048459 0.4936788272317445
+154421314 -0.5416983886304152 0.49431069342085976
+154619480 -0.5410698302139139 0.49392544842779207
+154817646 -0.5412028649009143 0.4940097255079638
+155015812 -0.5406182472569502 0.4932985342110125
+155213978 -0.5400628014645829 0.49368557986368317
+155412144 -0.5404804436251089 0.493911601795306
+155610310 -0.5400893696500718 0.4939844281539857
+155808476 -0.540106274142417 0.4930700330743947
+156006642 -0.5399231819125171 0.4929800746182148
+156204808 -0.5399613687031911 0.4932041425528471
+156402974 -0.5399040571084743 0.49257000060604755
+156601140 -0.5391151080291423 0.4936048780426403
+156799306 -0.5391104981073408 0.4927615239055699
+156997472 -0.5393823183379988 0.4928771004696679
+157195638 -0.538457303639529 0.4930402586900475
+157393804 -0.5391286714556148 0.49294109067662223
+157591970 -0.5387076823903757 0.4924079049004777
+157790136 -0.5388323856096546 0.4924089006135695
+157988302 -0.5383854814615648 0.49241847268088706
+158186468 -0.5387334210465654 0.4921510655744001
+158384634 -0.5376709753515191 0.4921263406911236
+158582800 -0.5383168277478237 0.4921644275791646
+158780966 -0.5379457909179439 0.49194171043890245
+158979132 -0.5373317669453495 0.4922483572422355
+159177298 -0.5373830874238802 0.4915129546406946
+159375464 -0.5374031959855252 0.4921060049604522
+159573630 -0.5372634836885778 0.49175385218850853
+159771796 -0.5369343536886118 0.4918601940882036
+159969962 -0.5365869935617983 0.4913258535277452
+160168128 -0.5362782175437121 0.4911193453375948
+160366294 -0.535950065172618 0.49168411886902835
+160564460 -0.5361624395741539 0.4910716849324317
+160762626 -0.5365314822152357 0.4906065302805193
+160960792 -0.5362161039573888 0.4914834169575682
+161158958 -0.5351942370756881 0.4915714549214734
+161357124 -0.5361063113297817 0.4911894805007131
+161555290 -0.535972116070882 0.49073667284516365
+161753456 -0.5349962713373506 0.49027138183945373
+161951622 -0.5350461959155709 0.49033696166799223
+162149788 -0.5351665215859669 0.4901341449760299
+162347954 -0.5341644906817714 0.4908064286561535
+162546120 -0.5345627832438523 0.4910353679692754
+162744286 -0.5348040000835488 0.49048925479901495
+162942452 -0.5348575173315361 0.4899353381130291
+163140618 -0.5344277135644719 0.4907838039321034
+163338784 -0.5338116069009972 0.490290017512335
+163536950 -0.5348786846845921 0.4899176580105449
+163735116 -0.5347448464591784 0.4902321123535098
+163933282 -0.5337141696430808 0.48975351650613613
+164131448 -0.5340360134273535 0.4889832644189771
+164329614 -0.5335964609451499 0.48953126939372743
+164527780 -0.5337588839904345 0.4894531631266699
+164725946 -0.5327451719252718 0.48925268010190254
+164924112 -0.5333063999183925 0.48901921267674564
+165122278 -0.5331794566598975 0.4886603796563316
+165320444 -0.5325052290342195 0.4893295325923095
+165518610 -0.5324513586016499 0.4886506306723141
+165716776 -0.532706374162579 0.488881649560147
+165914942 -0.5325540655755651 0.4882351407109197
+166113108 -0.5322959413840836 0.48799011783967017
+166311274 -0.5323699582491893 0.488540526747325
+166509440 -0.5320082377832507 0.4879036857569054
+166707606 -0.5322402573867899 0.48847508065309947
+166905772 -0.5320109315550026 0.48811726806500416
+167103938 -0.5317122464419108 0.4877904879709812
+167302104 -0.5313723404276658 0.4875433638491204
+167500270 -0.5314115275162267 0.48769809891307553
+167698436 -0.5312628082231982 0.48756376586073663
+167896602 -0.5318821209865163 0.4881125508746358
+168094768 -0.5310774746386581 0.48777268768739845
+168292934 -0.5313075551646131 0.4875050890708639
+168491100 -0.5312837896048419 0.4868194604812384
+168689266 -0.531153051709332 0.4866192539147562
+168887432 -0.5311984341991827 0.48696492403372804
+169085598 -0.530764802171508 0.4873132017444708
+169283764 -0.5302683044203927 0.48693241478100197
+169481930 -0.530399895963011 0.48671350627561805
+169680096 -0.530754006910931 0.4867397456854499
+169878262 -0.5303411363697679 0.485834045064425
+170076428 -0.5301431306060901 0.48664052717873735
+170274594 -0.5300902469313378 0.4861532109976854
+170472760 -0.5302950289523194 0.48592768533556285
+170670926 -0.5295171377170576 0.48498127024139764
+170869092 -0.5295238949251974 0.4853037255020677
+171067258 -0.5294940697781491 0.4850852551842365
+171265424 -0.5289266074175052 0.48507503501744603
+171463590 -0.5290339410772344 0.48544306322473535
+171661756 -0.5295235460996054 0.4840463692883466
+171859922 -0.5291194257209968 0.4851676626182237
+172058088 -0.5292068650306021 0.48437722564321356
+172256254 -0.5283207774642461 0.48469441222857074
+172454420 -0.5286728345934114 0.4847762082321427
+172652586 -0.528916365831792 0.4844627995082382
+172850752 -0.528728762058384 0.4843996247670509
+173048918 -0.5278934482634962 0.48369879929008813
+173247084 -0.5276944685742331 0.4832219192737278
+173445250 -0.5283746912040513 0.48342262239125544
+173643416 -0.5274029885687282 0.4832960080822912
+173841582 -0.5278545248180916 0.4830998152528022
+174039748 -0.5281062802135427 0.4835931854703849
+174237914 -0.527381668108355 0.4829113380019731
+174436080 -0.5277306640983617 0.48317034469585984
+174634246 -0.5276817038371838 0.48315298384004474
+174832412 -0.5268832376589174 0.48252070499165745
+175030578 -0.5266464097063832 0.48208522973486206
+175228744 -0.5268412675843072 0.48275205918104125
+175426910 -0.5262505029992296 0.48170140428108105
+175625076 -0.5264134267223473 0.48236689224050067
+175823242 -0.5274961222662492 0.48210400055004665
+176021408 -0.5267258162939422 0.4822765789589952
+176219574 -0.5264543973344729 0.48124023776722724
+176417740 -0.5265175123963629 0.48132040556816624
+176615906 -0.5265335143584463 0.48198552020914864
+176814072 -0.5268690585417543 0.4813363659075347
+177012238 -0.5261650718834183 0.48128523086176606
+177210404 -0.5262194104513425 0.48045719548378707
+177408570 -0.5258576930272204 0.4804262181472271
+177606736 -0.5257669191938337 0.48110916555133265
+177804902 -0.5261383718361548 0.48024045130683385
+178003068 -0.5259329311638604 0.48081966831625317
+178201234 -0.525991280600621 0.4805402302999279
+178399400 -0.5254233947369203 0.48011692235248427
+178597566 -0.5252140960686713 0.4803788301867396
+178795732 -0.5253914426509755 0.4794634242735235
+178993898 -0.5254619693634487 0.4797055280213719
+179192064 -0.5247691831835487 0.4797479728466725
+179390230 -0.5247101765038401 0.4797562716656141
+179588396 -0.5248932426256391 0.4793314843582359
+179786562 -0.5249936144038627 0.47869687974714953
+179984728 -0.5246396664988502 0.47838068992474264
+180182894 -0.525231186466234 0.47799744870868405
+180381060 -0.5246410770641243 0.47828860527989125
+180579226 -0.5246404113736808 0.478367377835382
+180777392 -0.5245056166276578 0.4787172402952823
+180975558 -0.5247375638246324 0.4780653064228829
+181173724 -0.5241360581954676 0.4777134747976045
+181371890 -0.5235170231182271 0.47800908144402093
+181570056 -0.5255547540609149 0.4773144972096866
+181768222 -0.524130102850782 0.4767243261751539
+181966388 -0.5236408542151954 0.47715144212065375
+182164554 -0.5236810720548398 0.47708123263560437
+182362720 -0.5237377835780881 0.4766628035245278
+182560886 -0.5240387778141492 0.47683346770008783
+182759052 -0.5238478847920683 0.4765019014220202
+182957218 -0.5241403860109134 0.47625216529626657
+183155384 -0.523481526344491 0.4759526643413183
+183353550 -0.5228646326385151 0.47564259826394983
+183551716 -0.5233625861581955 0.47579138312237607
+183749882 -0.523167654574928 0.4760384178660655
+183948048 -0.5223498022231744 0.4765662439470653
+184146214 -0.5222969867622275 0.4754799165658648
+184344380 -0.523035164769455 0.475053177955999
+184542546 -0.5227883497786678 0.47460934213792955
+184740712 -0.5230446483647708 0.47517169506234425
+184938878 -0.5225522785328018 0.4746366605852529
+185137044 -0.5227075891343023 0.474527895217515
+185335210 -0.5226942935939626 0.47472228266558186
+185533376 -0.5227638620665335 0.47392457167396246
+185731542 -0.522527979400426 0.47384868138855607
+185929708 -0.5221755641048343 0.4736319058633738
+186127874 -0.5225830103937726 0.4732968072465305
+186326040 -0.5221204840700419 0.47316295865683217
+186524206 -0.5215988122565453 0.47348889120855403
+186722372 -0.5219380468038364 0.4732372738053738
+186920538 -0.5217265938438538 0.47386071377227545
+187118704 -0.5217710755860814 0.4723983766022369
+187316870 -0.5212721317265826 0.47264787598066543
+187515036 -0.5213805298709981 0.4724239089449771
+187713202 -0.5215594816839564 0.472246378060787
+187911368 -0.5213711102272263 0.47197187348720565
+188109534 -0.521450160486768 0.4712801880051807
+188307700 -0.5209384544591996 0.471850736406406
+188505866 -0.5216791634403178 0.4719031202996459
+188704032 -0.5210637007930002 0.47177528150597287
+188902198 -0.5207758214095255 0.471343392207645
+189100364 -0.5213631400056529 0.4705725259994075
+189298530 -0.5209461867134247 0.47080640831447085
+189496696 -0.5208211145459307 0.47091619659558664
+189694862 -0.5203639097346344 0.470032073150943
+189893028 -0.5210470588005556 0.4701705951631817
+190091194 -0.5208415596979966 0.46997334714680783
+190289360 -0.5207253899863759 0.47022313197330634
+190487526 -0.5198667668514521 0.4699632836752004
+190685692 -0.520418652129516 0.4688520909310601
+190883858 -0.5204852574890583 0.4695430326391287
+191082024 -0.519922673992779 0.4688794420474408
+191280190 -0.5201860296607854 0.46959035682407024
+191478356 -0.520342309039519 0.4682202111139029
+191676522 -0.5201116096405651 0.4689213577720501
+191874688 -0.5198624626873155 0.4682608053168898
+192072854 -0.5197306149066134 0.4685183398214013
+192271020 -0.5200902118285993 0.4673054669913718
+192469186 -0.5199187261995978 0.467567427669073
+192667352 -0.5204491573037673 0.4669100024947316
+192865518 -0.5199576155373329 0.4666658143708899
+193063684 -0.5199667624755298 0.4668084750690734
+193261850 -0.5199752949754889 0.46663828766250437
+193460016 -0.5202664472463806 0.46659480105713474
+193658182 -0.5195557355179914 0.4669498264784356
+193856348 -0.5203706664830318 0.4666583454004931
+194054514 -0.5195962388558664 0.46598243205207746
+194252680 -0.520080265026571 0.4659073300722694
+194450846 -0.5196202588876042 0.4655026723341288
+194649012 -0.5197719989480277 0.4653275045051447
+194847178 -0.5195850007476891 0.4654535925133732
+195045344 -0.5188500152429645 0.46492920330034365
+195243510 -0.5186785592103448 0.4648492569739777
+195441676 -0.5195158181370438 0.46474149108449037
+195639842 -0.5193956947468711 0.46432455250402477
+195838008 -0.5192981014015724 0.46413729848029045
+196036174 -0.5196967756566829 0.4638913025859757
+196234340 -0.5195141393122953 0.46365145968735444
+196432506 -0.5192194752876804 0.46373839351032037
+196630672 -0.5188110390772915 0.46345582003565833
+196828838 -0.5193776589081361 0.4632101034162049
+197027004 -0.5195341545185744 0.4628832840888286
+197225170 -0.5185716105266099 0.4629025177572085
+197423336 -0.5192266937522773 0.4629069262026079
+197621502 -0.5192739514329376 0.46209160996695503
+197819668 -0.5194417035577057 0.46212468391422196
+198017834 -0.518701010183617 0.461497179488537
+198216000 -0.5187204148391155 0.4618009650362637
+198414166 -0.5185608866280346 0.46179262525785497
+198612332 -0.5182577707734736 0.46114919436584084
+198810498 -0.5186761845160872 0.4616452795980917
+199008664 -0.5186256106644875 0.4610280264078494
+199206830 -0.5193281182950799 0.4601890341065127
+199404996 -0.5188311915875279 0.4608180380251745
+199603162 -0.5192310395802778 0.4602200045870808
+199801328 -0.5186033720522678 0.46054644003281164
+199999494 -0.519072918034887 0.45974353722566413
diff --git a/test/data/ft240-43.s1p b/test/data/ft240-43.s1p
new file mode 100644
index 00000000..17a9a52a
--- /dev/null
+++ b/test/data/ft240-43.s1p
@@ -0,0 +1,2021 @@
+# HZ S RI R 50
+50000 -1.0000440487183417 0.012375249401504244
+149034 -1.000845604793446 0.036969764558453924
+248068 -0.999385670187039 0.06120170868073284
+347102 -0.9971259625471285 0.08590838524761664
+446136 -0.9941113428925963 0.11065054836715914
+545170 -0.9904250117957555 0.13532448432970737
+644204 -0.9860766628637774 0.16006690289910866
+743238 -0.9806069756692106 0.18545453574154472
+842272 -0.9745758215282093 0.21077748742362518
+941306 -0.9670342021673918 0.2365640974132041
+1040340 -0.958499622605099 0.26287813334766247
+1139374 -0.9481492795103106 0.28928249776854287
+1238408 -0.9359632351682226 0.3161466379842837
+1337442 -0.9211685385206595 0.3432669620722579
+1436476 -0.903741807707711 0.36938661710269793
+1535510 -0.8832464369249795 0.3941001666862777
+1634544 -0.8604130522488269 0.41684863157372776
+1733578 -0.8353666064520224 0.4365539062204734
+1832612 -0.809135858914292 0.4528870835910578
+1931646 -0.7828340999660549 0.46630575585700085
+2030680 -0.7566551003757538 0.4767175886896673
+2129714 -0.731279080159068 0.48464808810858345
+2228748 -0.7067325630866533 0.49030239489947286
+2327782 -0.6832641183744138 0.4941160579564003
+2426816 -0.6610739142724612 0.4964660210143591
+2525850 -0.6401829817390025 0.4975069990018587
+2624884 -0.6201531993288562 0.4971881817565435
+2723918 -0.6016373981054663 0.4963107254655197
+2822952 -0.5842597580340673 0.49469332024020857
+2921986 -0.5681708268078586 0.49263039296023947
+3021020 -0.5530382536567087 0.48984385652862666
+3120054 -0.5391376787326128 0.487017835311737
+3219088 -0.5260640358797839 0.4837646372090238
+3318122 -0.5138190876684805 0.48069975179013985
+3417156 -0.5023978873995086 0.477784108899533
+3516190 -0.49193345355010076 0.47470861249719315
+3615224 -0.48169150476612477 0.4716602815889278
+3714258 -0.47227770486328996 0.46884177410579936
+3813292 -0.4635254713503258 0.4658812597652106
+3912326 -0.45509893616151986 0.4632979857490776
+4011360 -0.44727608022726373 0.46083503292673916
+4110394 -0.4397019950263948 0.45864936637224785
+4209428 -0.43238410544509237 0.4562029385135756
+4308462 -0.4253547496677488 0.45403512376651206
+4407496 -0.4186061479715913 0.4521092057836463
+4506530 -0.4123056340946823 0.45025500327116985
+4605564 -0.40601603027830613 0.44852840416378675
+4704598 -0.4000789350656528 0.4471115201833866
+4803632 -0.39429911268711704 0.44557739857212886
+4902666 -0.38867176852870217 0.4440684385001954
+5001700 -0.38314418096154795 0.4428024363369352
+5100734 -0.37780568071454973 0.44158933845125115
+5199768 -0.3726814509751363 0.44046818951248734
+5298802 -0.3673887815751556 0.4392824024664967
+5397836 -0.36232392401296154 0.43836137324214086
+5496870 -0.35732023623857295 0.4376147508311724
+5595904 -0.3527170625956517 0.43655369830355983
+5694938 -0.347843832753303 0.43575220596078845
+5793972 -0.34325825170611673 0.43486777923773756
+5893006 -0.3385638799535938 0.434262903936108
+5992040 -0.33425674234688174 0.4334917658757122
+6091074 -0.3300186811627066 0.4329389791153027
+6190108 -0.3255759514883911 0.4321937924748982
+6289142 -0.32128002613024825 0.4314606989332759
+6388176 -0.3169374497966562 0.43117794039737256
+6487210 -0.3127747811517873 0.430540667549533
+6586244 -0.30873422412518947 0.429980986331365
+6685278 -0.30462680581833307 0.42931601366041916
+6784312 -0.3005663035754725 0.42873405564248485
+6883346 -0.29685056049604275 0.4282001731804133
+6982380 -0.2930879082492459 0.42785911868244536
+7081414 -0.2891859831204065 0.42741305896217974
+7180448 -0.28530606196504593 0.4269665110880303
+7279482 -0.2815236502029458 0.42652448337686927
+7378516 -0.2778448618419597 0.4262079894986772
+7477550 -0.2741075761634614 0.42578472968056885
+7576584 -0.27046535186173265 0.42540023342921857
+7675618 -0.2666797169742983 0.4249054949107095
+7774652 -0.2632343723832282 0.4246850349960751
+7873686 -0.25971710667262426 0.4239154032001261
+7972720 -0.2561367251187341 0.4237161586430076
+8071754 -0.2529978028472672 0.42322954209852565
+8170788 -0.24927111446785333 0.42279594651417984
+8269822 -0.24608535284345215 0.42254452660519326
+8368856 -0.2427692397461813 0.42189342161148735
+8467890 -0.23945617596531213 0.4216635109209783
+8566924 -0.23618485016284105 0.42115216922625476
+8665958 -0.23301832304888423 0.42069628088258154
+8764992 -0.22974367096594783 0.42031835909305076
+8864026 -0.2265229534424358 0.42010307750550135
+8963060 -0.2235983447986011 0.41953127432250936
+9062094 -0.2204558311381231 0.4193408187145298
+9161128 -0.21727271043491775 0.41881754588996534
+9260162 -0.21413910125442048 0.41843084776558664
+9359196 -0.21125891246324593 0.41795105739113037
+9458230 -0.20832694325946982 0.41761744032631826
+9557264 -0.20534919948958444 0.4173340407759466
+9656298 -0.20250904045626314 0.4166603238980209
+9755332 -0.19967749408018381 0.41619305000537277
+9854366 -0.19683578330703044 0.4159176418033673
+9953400 -0.1941170935320531 0.41548488788260096
+10052434 -0.19159661442522744 0.4152937413056145
+10151468 -0.18859201368605333 0.41479197854949046
+10250502 -0.1858462920745215 0.41429280960394105
+10349536 -0.18305611315115736 0.4138457638770978
+10448570 -0.18042132339027664 0.4133285036180959
+10547604 -0.1776986321632301 0.4129709059797729
+10646638 -0.17519333384574837 0.4126350590033875
+10745672 -0.17258119750592554 0.41221259138078153
+10844706 -0.1698629321726618 0.4116139718182843
+10943740 -0.1673773369799853 0.41122736860543346
+11042774 -0.1650183304425727 0.41072576166585634
+11141808 -0.16258654586377594 0.41038978827162764
+11240842 -0.1599758428784368 0.4098137052371423
+11339876 -0.1576845666643375 0.40965609343335807
+11438910 -0.15530762829026643 0.40892181031872266
+11537944 -0.1528254559700538 0.4085729683465986
+11636978 -0.15037966990738685 0.40808362212509036
+11736012 -0.14799505499103616 0.4077854711344866
+11835046 -0.1456974495771637 0.4073633850835315
+11934080 -0.14342915717325233 0.40681434372188247
+12033114 -0.14104360906930022 0.4064177603478956
+12132148 -0.1387266477065702 0.4058851758122036
+12231182 -0.13658580665121067 0.40541331009111875
+12330216 -0.13443803508675162 0.40486260897578696
+12429250 -0.13230157205748272 0.4045348907160653
+12528284 -0.1299537254930951 0.40416266727534705
+12627318 -0.12764764259023353 0.4035622568230409
+12726352 -0.12560170149383976 0.40335976520113775
+12825386 -0.1236207981683455 0.4028218414075756
+12924420 -0.1213970147295524 0.40233240647274215
+13023454 -0.11935159011977262 0.4018016908581305
+13122488 -0.1172342328612645 0.40131601799246946
+13221522 -0.11529501836232929 0.4008259209438857
+13320556 -0.11331923757767337 0.40033430287710897
+13419590 -0.11131328132162567 0.4000653768403586
+13518624 -0.10933137345255511 0.39952303393481853
+13617658 -0.10751319157107553 0.399124339466342
+13716692 -0.10527733525564519 0.3986889490816505
+13815726 -0.10351815814239211 0.39810739854335525
+13914760 -0.1014683219170833 0.3977315297475265
+14013794 -0.09967483270474763 0.3971867101260624
+14112828 -0.09781950608331745 0.39679915388647924
+14211862 -0.0958351604328481 0.3962552704766071
+14310896 -0.09389924127572022 0.3956870116840993
+14409930 -0.09225852211569856 0.39546303936533883
+14508964 -0.09047620296597696 0.3950899692537426
+14607998 -0.08856090925864106 0.3944242652904879
+14707032 -0.08673960188313122 0.3938915358008976
+14806066 -0.08504799091454185 0.3936085357908679
+14905100 -0.0832737259066695 0.39323909087236836
+15004134 -0.08169630679772236 0.39250738245006933
+15103168 -0.07988690321313502 0.3920963818634262
+15202202 -0.07813805319457508 0.3916871267638855
+15301236 -0.07637479145582703 0.39125828190601875
+15400270 -0.07481871043508284 0.3907984468890462
+15499304 -0.07304514383374992 0.3902411055817282
+15598338 -0.07136540221813067 0.38991325105296315
+15697372 -0.06982398135605054 0.3894226944764039
+15796406 -0.06812626673591 0.38904699765987566
+15895440 -0.06648990816446168 0.3885570404078556
+15994474 -0.06480600269468485 0.3881198379424092
+16093508 -0.06345473305294469 0.38757355875381355
+16192542 -0.06166386714629848 0.3870500358548991
+16291576 -0.060206180126728286 0.38693226078564574
+16390610 -0.05864992701323261 0.386393542609634
+16489644 -0.057030895784246735 0.38591543627569386
+16588678 -0.05566130547760734 0.38540863763241784
+16687712 -0.05410805684372143 0.38509494401182953
+16786746 -0.05272179680658622 0.3845630845610212
+16885780 -0.0512265414241611 0.384179764690434
+16984814 -0.04979810823691765 0.3836869917528111
+17083848 -0.04836914559444155 0.38326205283054243
+17182882 -0.04680686709956434 0.3828348328856347
+17281916 -0.045553062952683986 0.3825878174794791
+17380950 -0.04409746283422333 0.38209526006993905
+17479984 -0.04262405901186904 0.3813651853128447
+17579018 -0.041239733100135616 0.38104865081294076
+17678052 -0.03985545469349118 0.3807271024094046
+17777086 -0.03841827712178281 0.38022209770187937
+17876120 -0.037119993320735865 0.37997925668069227
+17975154 -0.035771153321643136 0.37937741324946983
+18074188 -0.03443696842927325 0.3790750665463068
+18173222 -0.033189997962804876 0.3786557449930398
+18272256 -0.031924157309091976 0.37818927859716067
+18371290 -0.030499470537547355 0.37760891016723164
+18470324 -0.029412211256504808 0.3772373795483398
+18569358 -0.027952985971153397 0.3768579368527577
+18668392 -0.026728608589207396 0.37643777004867246
+18767426 -0.025455233954375645 0.37593654061570797
+18866460 -0.024289385889030103 0.3757886964256715
+18965494 -0.022900537111343697 0.3750842726633074
+19064528 -0.021711216624276546 0.37474107535933593
+19163562 -0.02020478769978836 0.37446934620532896
+19262596 -0.019390906835535484 0.37390246535416527
+19361630 -0.01808031223364181 0.3735459454098052
+19460664 -0.017021775011879507 0.3730515483469473
+19559698 -0.015712120289242203 0.3726526815524478
+19658732 -0.014396872056040176 0.3725115361931483
+19757766 -0.013295469848449254 0.37210556547788776
+19856800 -0.012098308624520273 0.37145483107939514
+19955834 -0.010969049025210082 0.37130636694726654
+20054868 -0.010227716372844807 0.3708010231269848
+20153902 -0.008926418981638179 0.37051486627667835
+20252936 -0.0077666352181749105 0.3698855207237489
+20351970 -0.006816470916217028 0.36960225464473706
+20451004 -0.005741435969004015 0.3692498661072366
+20550038 -0.004390321806867153 0.3688474753197675
+20649072 -0.0034102133121003755 0.3685735046696806
+20748106 -0.0023691569694813154 0.3681321582664397
+20847140 -0.001257262193887764 0.36769529656597016
+20946174 4.051984693108225e-06 0.36738399520685894
+21045208 0.0007890653912741388 0.3671613602217866
+21144242 0.0019374797272924298 0.3666932283153979
+21243276 0.002819204607536686 0.3662518858038456
+21342310 0.003943167069343064 0.36584862265754015
+21441344 0.004918047255132635 0.3654330029913363
+21540378 0.005943694916988668 0.36510359736017944
+21639412 0.007078414613878926 0.36478129723469055
+21738446 0.008003576211855488 0.3644924980561215
+21837480 0.009114767881952385 0.36410830297047936
+21936514 0.009763499851275906 0.36367340349494093
+22035548 0.011101686392662987 0.3633571343368099
+22134582 0.012212910463799883 0.3629897278539641
+22233616 0.012990186417060866 0.36259136231437145
+22332650 0.014011773436192504 0.362214754691525
+22431684 0.014871161971244709 0.362013029496952
+22530718 0.015774127950887628 0.3615295280393804
+22629752 0.01685330376460739 0.36116792147962473
+22728786 0.017537868276112454 0.3607997096221492
+22827820 0.018692181563003594 0.36058987804238946
+22926854 0.019471410573186812 0.3602350651387701
+23025888 0.02044076063270116 0.3599429380710855
+23124922 0.021415767389431732 0.3594109044808562
+23223956 0.022306948218929416 0.3592975323831221
+23322990 0.023215508373695872 0.3589234178290431
+23422024 0.023979149611244876 0.3585442227922897
+23521058 0.024928808392326435 0.35822032218678684
+23620092 0.025926963998150827 0.35798523109468383
+23719126 0.02683987125948925 0.3577499861424331
+23818160 0.02759271161917185 0.35721540328781837
+23917194 0.028558740483493533 0.3569214491225801
+24016228 0.029287088647705864 0.35670281520726793
+24115262 0.030160335207617378 0.3563732137046263
+24214296 0.030935808461443018 0.3558956252434105
+24313330 0.031859793225929495 0.3556232869040077
+24412364 0.032683668175186996 0.35529263049629584
+24511398 0.03341172208951101 0.35517984697564847
+24610432 0.0343264064110862 0.35478666055812
+24709466 0.035187541945549176 0.35448209254077195
+24808500 0.03599508144275817 0.3540159546651729
+24907534 0.036690029276037865 0.3538208223622494
+25006568 0.03757585177902648 0.35331116892971454
+25105602 0.03819557234833949 0.35296657688385913
+25204636 0.03931450059406042 0.3528190718419485
+25303670 0.04002858863502328 0.3524692042711345
+25402704 0.040846307572763835 0.35236202627605784
+25501738 0.041611994353083076 0.35208810083700265
+25600772 0.04229634328477402 0.35178240862755766
+25699806 0.04309592957894165 0.3513727570907405
+25798840 0.04382706751232628 0.3512004916807285
+25897874 0.04464258428625451 0.35078839076671886
+25996908 0.04543355181542399 0.35050391276707615
+26095942 0.04620163571745694 0.35034795307747285
+26194976 0.04689126370002906 0.35008123392292795
+26294010 0.04777016525215434 0.3497682438601624
+26393044 0.048358704755932055 0.3495615419568055
+26492078 0.04913357111942969 0.3492273219995802
+26591112 0.05001149183712216 0.34902646513613955
+26690146 0.05072854116741899 0.3488284777973466
+26789180 0.051337389867188776 0.3484353141758505
+26888214 0.052150351553278365 0.3482493041504283
+26987248 0.052679048063130944 0.34779341357507026
+27086282 0.0533878211053751 0.34765960102392574
+27185316 0.054330258925734565 0.3471103894476515
+27284350 0.05461712556546158 0.3472018366774453
+27383384 0.05563653758219402 0.34678218250273773
+27482418 0.056153539607493516 0.3465724059983476
+27581452 0.05690081746070356 0.346226442147095
+27680486 0.057731231383181224 0.34616820484774685
+27779520 0.05835206614541294 0.34580796335161673
+27878554 0.05894269678237472 0.34556015073132273
+27977588 0.05974901878580039 0.3453671897231541
+28076622 0.060244920843025324 0.34506754377977844
+28175656 0.06101511703260513 0.34489591752344545
+28274690 0.06156402565152142 0.3445249166362521
+28373724 0.06228992924109188 0.34444572051093975
+28472758 0.06295474941688269 0.34408367177936383
+28571792 0.06370102566274524 0.3440198895246423
+28670826 0.06417027739860687 0.34353886096171293
+28769860 0.06482243834124306 0.3434580544613148
+28868894 0.06560128767083748 0.34317033931648405
+28967928 0.06615551745206272 0.34289811086581934
+29066962 0.06661527368981543 0.34262175093198555
+29165996 0.06732590392476417 0.34240726328596377
+29265030 0.06796343079919567 0.34217682635524554
+29364064 0.06867211492137291 0.3421010481438643
+29463098 0.06930308114834417 0.3418287613169085
+29562132 0.07003136274739698 0.34144807385964737
+29661166 0.07062933913429047 0.34104659241001933
+29760200 0.07092274088974887 0.3411885454472825
+29859234 0.07180948475197894 0.34077004122981236
+29958268 0.07240787056250363 0.34057512355251923
+30057302 0.07303857787490758 0.3404884104514696
+30156336 0.07352911151913351 0.34013676085370503
+30255370 0.07405239555291715 0.3397684446351473
+30354404 0.07464986622000332 0.3397298116066128
+30453438 0.07520609525753522 0.33949568783726997
+30552472 0.07575552414502419 0.33941197830264663
+30651506 0.07633999041468506 0.33908540631342593
+30750540 0.07711281759881351 0.33867783767920334
+30849574 0.07763960067992716 0.3384437131849167
+30948608 0.07822213715234534 0.3384183346474177
+31047642 0.07883928774725255 0.33829452217765965
+31146676 0.07929194687765137 0.3379904452417002
+31245710 0.07987387217024577 0.33794479550438994
+31344744 0.08042183555222915 0.33772933014782
+31443778 0.08096625982006693 0.33723939792654395
+31542812 0.08169455133435222 0.33717491301006314
+31641846 0.0821467139653082 0.3371264015249831
+31740880 0.08267482670046306 0.3367688206934853
+31839914 0.08313554207586873 0.33652436765425403
+31938948 0.08376474563847107 0.33633301158387846
+32037982 0.08443016927101231 0.3364043867807788
+32137016 0.08482138803270772 0.3360093973351501
+32236050 0.08538318437638232 0.33569910068421643
+32335084 0.08598793229403266 0.3356556886476026
+32434118 0.08660512532374359 0.3353394588101973
+32533152 0.08700387807369006 0.33532689398128157
+32632186 0.08749662683107094 0.3348756433673957
+32731220 0.08793981387502812 0.33486422640500374
+32830254 0.08860945179762363 0.33469803786816205
+32929288 0.08904032393157006 0.33446774122845
+33028322 0.08961106030261809 0.33417281072229515
+33127356 0.09010939185051416 0.33398069718326073
+33226390 0.0905678401809131 0.3339562909430932
+33325424 0.0910206193505532 0.33375321292530735
+33424458 0.09170198094419182 0.33356108698574266
+33523492 0.09212275052418196 0.3331704415423715
+33622526 0.09271205252044395 0.33308903701429704
+33721560 0.09312521916886898 0.3330384844362826
+33820594 0.09360508721894983 0.3328420282300695
+33919628 0.09414664951096524 0.33275728158773793
+34018662 0.0946015405550094 0.33249457552041534
+34117696 0.0950209526225676 0.3321806135465402
+34216730 0.09571895957352616 0.33221709517518216
+34315764 0.09618724088766464 0.3318744752216774
+34414798 0.09662317963914868 0.33182174425525224
+34513832 0.0970605318587363 0.3315220954522382
+34612866 0.0976237817375476 0.33145613011042935
+34711900 0.09808500008720196 0.3312424403007269
+34810934 0.09858595119784132 0.33093701574986023
+34909968 0.0989631639838757 0.3306996542776623
+35009002 0.09956022886049358 0.3307664864268501
+35108036 0.09990222427166726 0.3306184776490718
+35207070 0.10029004665718094 0.3304728164246802
+35306104 0.10068290888886226 0.3301874014247084
+35405138 0.10139589347245778 0.3301126110314642
+35504172 0.10185393678566207 0.32993264993095833
+35603206 0.10241132922519973 0.32967234308566273
+35702240 0.10274296391273467 0.3295321585706149
+35801274 0.10332642435654803 0.3293102303421031
+35900308 0.10346242772543994 0.32904301666381797
+35999342 0.1041477651339104 0.32915237728320107
+36098376 0.10433255870919637 0.32910214599225845
+36197410 0.10493772964580905 0.3286462175771481
+36296444 0.10535564173137275 0.32864362489292265
+36395478 0.10584311453422297 0.32833752076801176
+36494512 0.10639934223265478 0.3282083044863998
+36593546 0.10665572139324744 0.32808308115599916
+36692580 0.1070254190844114 0.3280035835881584
+36791614 0.10774431710647114 0.32793376764751797
+36890648 0.10817657853395007 0.3277087797593089
+36989682 0.10829907235594328 0.32752329656802115
+37088716 0.10875387008129112 0.3272612857474824
+37187750 0.10925067955193137 0.32735819084121565
+37286784 0.10968830481146981 0.3270996251221699
+37385818 0.11008671013290158 0.3269034108039648
+37484852 0.11066516689182894 0.326754357013417
+37583886 0.11110111368516395 0.3267677013798443
+37682920 0.11155348295089518 0.32642098891617666
+37781954 0.11191310757212096 0.32643729508674785
+37880988 0.11239862121537521 0.3261989538613668
+37980022 0.11287316934618336 0.3259839008255713
+38079056 0.11321222276456652 0.32598530833750533
+38178090 0.11363019812624135 0.3257645629755001
+38277124 0.1140239263376726 0.3256314683031785
+38376158 0.11431565408567287 0.32561116246736593
+38475192 0.11480721689844 0.32538234317700654
+38574226 0.11508943839707668 0.3253046833195092
+38673260 0.11548053951407554 0.32501469871175637
+38772294 0.1159607172558532 0.32493849565269434
+38871328 0.11631031918410936 0.3247562468573475
+38970362 0.11666699831356586 0.3245422091725447
+39069396 0.11700186605643365 0.3246085116094274
+39168430 0.11746852172048868 0.32425840649231275
+39267464 0.1178747548717266 0.3243276883706595
+39366498 0.11822431831210418 0.32406696371219074
+39465532 0.11875670091241873 0.3240212831420898
+39564566 0.11900559369363817 0.3237990986050767
+39663600 0.11943291459347748 0.32358095794633557
+39762634 0.11986042200764292 0.32370002494773925
+39861668 0.1202326487961674 0.3234672241826148
+39960702 0.12069827615017412 0.3234675275737465
+40059736 0.12134072488357137 0.3231756210212221
+40158770 0.1213723765581436 0.32312109622982604
+40257804 0.12203149704805753 0.32319925866874344
+40356838 0.1223321874995849 0.3228444955287431
+40455872 0.12239642319868076 0.3228639110703262
+40554906 0.12303843584389208 0.3226944992892721
+40653940 0.12336164345804143 0.322367553493246
+40752974 0.12376489199848566 0.3223725020135526
+40852008 0.12384987802386581 0.32242082989918486
+40951042 0.1244424900233537 0.3221391803557459
+41050076 0.12476416011555116 0.3222134941263583
+41149110 0.12529531896480586 0.3220882353455937
+41248144 0.12570013763341892 0.32185497528778684
+41347178 0.12586292566976218 0.32194687492608004
+41446212 0.12616637715993023 0.3217053641475544
+41545246 0.1264592901633366 0.32148332221030435
+41644280 0.12694950153548248 0.32132635953637106
+41743314 0.12724186709317942 0.3214742712237246
+41842348 0.12760171955007293 0.3211540740771081
+41941382 0.1280994576604904 0.3208442713475976
+42040416 0.12805703678096103 0.3210718300733569
+42139450 0.1286568211188197 0.32069610429611856
+42238484 0.12913110869101488 0.3206592586711514
+42337518 0.12914919407506079 0.32052857265194834
+42436552 0.129766613762605 0.32056820852500384
+42535586 0.13001336821594517 0.32043035913037354
+42634620 0.13032206042966746 0.3204565238697434
+42733654 0.13072399504117344 0.3203172514359991
+42832688 0.13105952192294693 0.32002735445501573
+42931722 0.13130516235811443 0.32010671815050307
+43030756 0.1318278024530878 0.3199733249886076
+43129790 0.132356771628508 0.3196969180701727
+43228824 0.1324283120549712 0.3196952996539896
+43327858 0.13266709536421759 0.3195617731894714
+43426892 0.1331060355655358 0.31952741611091134
+43525926 0.133418009187182 0.3193526505646155
+43624960 0.13354287847331395 0.3194122982712433
+43723994 0.13402996927221783 0.31897239159769
+43823028 0.13441266064084625 0.318936574711167
+43922062 0.13447025263389104 0.31906246237421226
+44021096 0.13502397984991493 0.31884263761314763
+44120130 0.13516204737542106 0.3186048562688516
+44219164 0.13560231187185362 0.3187437418931172
+44318198 0.13585551461816445 0.31864623820497345
+44417232 0.1362177097913103 0.3183846838659615
+44516266 0.13655641848416566 0.31829607173446417
+44615300 0.1366970428629347 0.31828010286770214
+44714334 0.13700915450502998 0.31820944033772214
+44813368 0.13738772467497484 0.31807727884139
+44912402 0.13781338461576698 0.31792840808326506
+45011436 0.13809360460939574 0.31772958285422986
+45110470 0.1382384350873972 0.31769535624585915
+45209504 0.13880180424381705 0.31765126870610355
+45308538 0.13891675515521568 0.31759078679737407
+45407572 0.13921062503399267 0.3174666075903047
+45506606 0.13949972004969835 0.3175824282838395
+45605640 0.1396660293206947 0.31740196815428007
+45704674 0.14015545953049727 0.3170725523834205
+45803708 0.14034015752045245 0.3170919812159424
+45902742 0.14076258607664163 0.3170597623796635
+46001776 0.14092863099292363 0.31704399700083874
+46100810 0.14137875056458088 0.31674865638443456
+46199844 0.14156131225748605 0.31689994320208714
+46298878 0.14171879817402594 0.31662277660324206
+46397912 0.1422965895162877 0.31675000586458635
+46496946 0.14260738091822417 0.31642990254321784
+46595980 0.14263975020828196 0.31616791895021046
+46695014 0.14281771875604402 0.31639718270568434
+46794048 0.14354649888252544 0.3161316131787417
+46893082 0.1434123596227361 0.31629476857064875
+46992116 0.14378712112551362 0.31619324283210787
+47091150 0.14394514955831367 0.31597990088450567
+47190184 0.144277869056639 0.31584362253909676
+47289218 0.1445113610548252 0.31572124199947793
+47388252 0.14488522445302923 0.31589684650622746
+47487286 0.14528802836512797 0.3156405573661851
+47586320 0.14523498534700416 0.315554830350116
+47685354 0.14554713623247265 0.31551252682980546
+47784388 0.14590852121574122 0.3153549934684646
+47883422 0.14625552984275297 0.31561789937285845
+47982456 0.1466082483309153 0.3153648099291271
+48081490 0.14653368519107848 0.31522261160651016
+48180524 0.14710360273258305 0.3151339333277947
+48279558 0.14707343840523765 0.3149508984887796
+48378592 0.14747737498211833 0.3150612479121866
+48477626 0.14769362490997728 0.31489811357422726
+48576660 0.14791796758897718 0.31487531531180724
+48675694 0.14809687138548058 0.3147564646908916
+48774728 0.14833566643770574 0.31475613742271336
+48873762 0.14864702036052366 0.31462348287669956
+48972796 0.14895265016864545 0.31445642455263706
+49071830 0.149314170619961 0.314523953226443
+49170864 0.14948615177450658 0.314389623551622
+49269898 0.14973428797319416 0.31415688267091296
+49368932 0.14990907445289706 0.31446765085484446
+49467966 0.15030811941300293 0.31414722711694515
+49567000 0.15055214649816046 0.3140696378113095
+49666034 0.15065341325888612 0.31405114754735364
+49765068 0.1509016640724813 0.31398324871735955
+49864102 0.15118207679979198 0.3140201383536916
+49963136 0.15151381796775237 0.3138957623359707
+50062170 0.1519697359968096 0.3136579497710532
+50161204 0.15208150583641036 0.31365471440342896
+50260238 0.1525116745547287 0.31366541334848447
+50359272 0.1526714408196618 0.31363134939015874
+50458306 0.15282653089679896 0.3135097281979029
+50557340 0.15323609762698429 0.3134540778111856
+50656374 0.15334749317011828 0.31330431373074824
+50755408 0.15359325009377411 0.31338634970343626
+50854442 0.15375341015384525 0.31311983712413294
+50953476 0.1541971234510377 0.31321561898391614
+51052510 0.15423809642047465 0.31312210991309164
+51151544 0.15440042857589492 0.3131579768320946
+51250578 0.1546159405785324 0.31290858511462083
+51349612 0.15505752790471178 0.31284446897130025
+51448646 0.15518874021217702 0.3129058686476869
+51547680 0.15525431387444463 0.3127076815594672
+51646714 0.1555938440890402 0.3129532782515391
+51745748 0.1556528185791505 0.31256610782543315
+51844782 0.15607887720842298 0.3125913021989305
+51943816 0.15637734190003455 0.31242187743379085
+52042850 0.15642162748458718 0.312560675917683
+52141884 0.15652860634514332 0.3122994203588769
+52240918 0.15666213954677527 0.3124668512881158
+52339952 0.1571780669514674 0.3122880512652398
+52438986 0.15731534711257875 0.3121271002363965
+52538020 0.15765134507894193 0.31215810243189107
+52637054 0.15788833746864459 0.31222075028238955
+52736088 0.1579419466919516 0.31221612857171904
+52835122 0.15828346691732426 0.3121025836598973
+52934156 0.1583703562889288 0.311923316628465
+53033190 0.15869868888019767 0.31201950277195656
+53132224 0.15888897510541988 0.3119125870748403
+53231258 0.1590583951009396 0.3118456329719456
+53330292 0.15930164409175368 0.3119223184553326
+53429326 0.1594227357432278 0.311761208406418
+53528360 0.1595785894636276 0.3116994910379742
+53627394 0.15973543227863352 0.31177746196098993
+53726428 0.15996323637370366 0.31173952652533604
+53825462 0.16034035360582244 0.311467887582987
+53924496 0.16047525212785058 0.311695318061979
+54023530 0.16071413420876554 0.31139012122434956
+54122564 0.16100801567484194 0.31145213145151057
+54221598 0.16112023561910496 0.3112657837574421
+54320632 0.16133454646845585 0.31115307463549086
+54419666 0.16143770437056829 0.311382233147246
+54518700 0.1615991318159734 0.31143414023164623
+54617734 0.16189290607584192 0.31116239665479084
+54716768 0.16206162351454861 0.3112341664354008
+54815802 0.16223213300617637 0.3111986577388447
+54914836 0.16238140692297678 0.3109150621878733
+55013870 0.1627705861888975 0.3111322909261663
+55112904 0.1627961267199415 0.3108302450363484
+55211938 0.16317023371903833 0.31071771849475127
+55310972 0.16306140719593956 0.31072838656528884
+55410006 0.16333311382382978 0.3108764655009701
+55509040 0.16358442915678784 0.3107981665329385
+55608074 0.163907959352644 0.3107807774596357
+55707108 0.1638824111724166 0.3106352920595226
+55806142 0.1642079129332683 0.3106701582288519
+55905176 0.1643216041442172 0.3106900086248918
+56004210 0.16441306126409788 0.31044733493822235
+56103244 0.1648442714314335 0.31042290677762635
+56202278 0.16505769659617134 0.31044271014320357
+56301312 0.16525205546453847 0.31065588532215355
+56400346 0.16515674305970696 0.31039599358460623
+56499380 0.16540768255609198 0.31030855126753004
+56598414 0.16573938111301686 0.3105030559751977
+56697448 0.16579217947040514 0.3104213037020045
+56796482 0.16584742029621893 0.3101103347043346
+56895516 0.16615792554677733 0.31027771041524876
+56994550 0.1664043573221258 0.31032088098166444
+57093584 0.16654425464884567 0.31022849167868966
+57192618 0.16662918863619117 0.3101027350139228
+57291652 0.16680924376694697 0.3102426014141091
+57390686 0.1671368704323146 0.31001332389672487
+57489720 0.16730654405561668 0.31020687432399374
+57588754 0.1673409634814929 0.30988780176673514
+57687788 0.16753502361397507 0.30981805809887747
+57786822 0.16763741360160478 0.3100324323496896
+57885856 0.16798446078174878 0.31009590961572675
+57984890 0.16799017691063325 0.3098113775346668
+58083924 0.1684778442852969 0.3097250638150387
+58182958 0.16856298841951592 0.3096931240994124
+58281992 0.16859344177430835 0.3097940452342682
+58381026 0.16857923824106266 0.3097381019408305
+58480060 0.16885604507750585 0.3097800414872854
+58579094 0.16911007976756795 0.30968260461511854
+58678128 0.16921090397925162 0.3095987970302391
+58777162 0.16947952871160246 0.3096108454553398
+58876196 0.16968696141324585 0.3095678810807177
+58975230 0.16978692620056038 0.3094814403649139
+59074264 0.16975282543158893 0.3096160275085054
+59173298 0.17008725895574547 0.3093933828796277
+59272332 0.1702723251605447 0.30945482898032645
+59371366 0.17031453047922668 0.3094508562746664
+59470400 0.17047291759349945 0.3093073482864508
+59569434 0.17060518011493989 0.30939624413680117
+59668468 0.170833459634358 0.3092907542899302
+59767502 0.17091945499575348 0.30947050179965313
+59866536 0.17104436757768135 0.3092084467660966
+59965570 0.17113672144390182 0.30932941186063506
+60064604 0.1715368001929616 0.30939658616519433
+60163638 0.17144625231327423 0.3092679985522117
+60262672 0.17135858587284125 0.3093417588436768
+60361706 0.17157318297544194 0.3091341580560436
+60460740 0.17181565860286435 0.3091364953125462
+60559774 0.17216530249306558 0.30905515938784
+60658808 0.17225447589082218 0.309072729918921
+60757842 0.17229340568585697 0.30914533406263645
+60856876 0.17246763809886895 0.3090530350969523
+60955910 0.17272656138227818 0.30896609435936784
+61054944 0.1727201898381766 0.3090705874317308
+61153978 0.1727678987368882 0.3090086951337181
+61253012 0.17308962828948327 0.3088804319867679
+61352046 0.17322919970592132 0.3089798126115834
+61451080 0.1732748684909967 0.30882070940173206
+61550114 0.17344873886094916 0.30900011164949565
+61649148 0.1733924446098022 0.30882184909765964
+61748182 0.17385776208522047 0.30889522712077
+61847216 0.1737823865874299 0.3087475869362238
+61946250 0.17401977153052275 0.30873136347430147
+62045284 0.17407400115373764 0.308695578253214
+62144318 0.17429293624330133 0.30877268348488307
+62243352 0.17465289639410878 0.30886929793863255
+62342386 0.17457232255528368 0.3086604107465373
+62441420 0.17461603197971548 0.3087876555372988
+62540454 0.17484603514055938 0.3087292864150969
+62639488 0.17496286672171857 0.3085924622862007
+62738522 0.1750573831869832 0.3085124811166236
+62837556 0.1751468755547488 0.30862566395616614
+62936590 0.17542570850793496 0.3087954288799767
+63035624 0.17533509283817997 0.30864986812589673
+63134658 0.1757006908280878 0.3087337425638085
+63233692 0.1758663225831687 0.3086072407612527
+63332726 0.17610418787950902 0.3086319042620737
+63431760 0.17612828192177088 0.3087130755151775
+63530794 0.1762962586351906 0.30860305437888913
+63629828 0.17629801953498633 0.3086012072021301
+63728862 0.17656535822030847 0.30832318337354314
+63827896 0.176642293128972 0.30861526627604496
+63926930 0.17654387033151947 0.30851217705251277
+64025964 0.17698870839022687 0.30859308957797454
+64124998 0.17701498155123355 0.30847007549777605
+64224032 0.17707619362707366 0.3086807394341982
+64323066 0.17715766555885715 0.3085357953909775
+64422100 0.17724447778748426 0.308585514081118
+64521134 0.17742187917112606 0.30842211750655135
+64620168 0.1774336430814479 0.30850709843278684
+64719202 0.17766055350233712 0.30854383547180697
+64818236 0.17770157886784543 0.30849612683867644
+64917270 0.17809826985635546 0.3084183028039231
+65016304 0.1780011948171231 0.30853409471202525
+65115338 0.17811705927622346 0.3083860065011049
+65214372 0.17848738030286773 0.308488552507301
+65313406 0.1784375561606622 0.3083836829236302
+65412440 0.17849763648556133 0.3084521961765762
+65511474 0.1787633253870975 0.30844264162998836
+65610508 0.17898624369409366 0.30833648230803995
+65709542 0.17889934110517658 0.30845225925890446
+65808576 0.17907522897674708 0.3085160548419716
+65907610 0.17894202815331786 0.3083898586433227
+66006644 0.17929257967931325 0.3085723061925179
+66105678 0.17932420422258713 0.30846942025370083
+66204712 0.17949793627199936 0.30859710849729693
+66303746 0.17959188814153967 0.30821336877166877
+66402780 0.17946892879091467 0.3083174059733634
+66501814 0.17968377558221926 0.3081694342642826
+66600848 0.179930930962725 0.3083635858497024
+66699882 0.18001368009751115 0.30840028213065707
+66798916 0.18015755288441704 0.3084842059500087
+66897950 0.18034925567035082 0.30810722447038386
+66996984 0.18029767504503594 0.3084074614590325
+67096018 0.18058949782674408 0.30829224439098774
+67195052 0.18071924380743243 0.308222407542712
+67294086 0.18076472770776603 0.30838837466183655
+67393120 0.18085963287963164 0.3084801258827175
+67492154 0.18090258697499642 0.308398750648429
+67591188 0.18094281288012465 0.3084117767988526
+67690222 0.1810854297986769 0.30845041820707997
+67789256 0.18134744006757988 0.30836720853174665
+67888290 0.18146025360196114 0.30861068699881017
+67987324 0.1815281268495168 0.30834537970791653
+68086358 0.1815619436076962 0.30848099172909704
+68185392 0.18150885256206073 0.3086343128745886
+68284426 0.18177786243148286 0.308292945079686
+68383460 0.18179492284299692 0.3083232833100656
+68482494 0.18190681577056486 0.3085141213739566
+68581528 0.18211301276170136 0.3083959377745101
+68680562 0.18224582744527387 0.3083776690011132
+68779596 0.1824395513921501 0.3084350151689915
+68878630 0.18234728606773823 0.3086498605451363
+68977664 0.18255925309228635 0.30837649699599184
+69076698 0.18249613555812952 0.30838844539390353
+69175732 0.18265423535297984 0.3087096479756226
+69274766 0.18283880810465392 0.30840274459496503
+69373800 0.18288666539559317 0.3084692478854444
+69472834 0.18295378875154497 0.308306186445019
+69571868 0.1830977446488129 0.30845950026356245
+69670902 0.18321680218204636 0.30848022214582116
+69769936 0.18334361734835375 0.3084712609469546
+69868970 0.18348957877348684 0.3086736637352356
+69968004 0.1835589862861476 0.30832232624629735
+70067038 0.18354052151211636 0.30807881440117363
+70166072 0.18367789245832095 0.30846792178363014
+70265106 0.18370637654926075 0.30848641101006957
+70364140 0.18381682043203823 0.3083805542701919
+70463174 0.18393647247247844 0.3083404176872244
+70562208 0.1837871392084064 0.3084522025877294
+70661242 0.18385990332751814 0.3082578867450349
+70760276 0.18410991945322433 0.3084688220796349
+70859310 0.18423904183324674 0.30853050972597124
+70958344 0.1843194009024995 0.30838878137945536
+71057378 0.18447771404985724 0.3085914666434999
+71156412 0.18439413366500523 0.30852864714812755
+71255446 0.1846809053638136 0.3082645442465961
+71354480 0.1847466782720936 0.30849846601639985
+71453514 0.18473600274212507 0.3085686961165236
+71552548 0.1847625060355195 0.3085373582399087
+71651582 0.18498937911423555 0.30870240921064596
+71750616 0.1850361034448153 0.30865501644791765
+71849650 0.18533352549814622 0.3085540394803812
+71948684 0.18524481462712503 0.3087210698059344
+72047718 0.18531730736471713 0.30844733388437895
+72146752 0.1855801004885814 0.30872062110947707
+72245786 0.18546431458058027 0.30835715595457164
+72344820 0.18566326230469546 0.30863051107808764
+72443854 0.18563493854889584 0.30870243888831944
+72542888 0.18589230065922144 0.30879349310518694
+72641922 0.18601857683007514 0.308934707994752
+72740956 0.1861202222289395 0.3085987510902608
+72839990 0.1859188885123403 0.30882233360189987
+72939024 0.18617865706083855 0.30882720632112565
+73038058 0.18633779909775497 0.3087771498730219
+73137092 0.18654315410869482 0.30893501241066956
+73236126 0.18657625692970092 0.3085358488596888
+73335160 0.18656844386588273 0.308713406956583
+73434194 0.18683704467378437 0.3089465037365615
+73533228 0.18672052126511166 0.3088942758807212
+73632262 0.1867686522933143 0.3088352648980784
+73731296 0.18681211684087046 0.3088343326565323
+73830330 0.1870491452775024 0.308958761438401
+73929364 0.1869732429706453 0.3089698584898504
+74028398 0.18727102240798926 0.3087137375421093
+74127432 0.18731758152013936 0.3089963516355256
+74226466 0.18743504707114544 0.30887040926896725
+74325500 0.18751675817379354 0.30897535958543537
+74424534 0.18773743999307235 0.30912437540578747
+74523568 0.18773128340125336 0.3092247331377983
+74622602 0.1878249784119037 0.3090632653612701
+74721636 0.18793444550302596 0.3092497355596222
+74820670 0.18794831045339852 0.309192976148384
+74919704 0.18795381565962627 0.3089517576418565
+75018738 0.1879418537042868 0.30938975872583147
+75117772 0.18822957423895165 0.30902307854603817
+75216806 0.18829300313144065 0.3092906884183398
+75315840 0.1884468544358687 0.30910991350430866
+75414874 0.18858777524732476 0.30928472403387086
+75513908 0.1883067616460702 0.30918413200189676
+75612942 0.18891285655369316 0.30933155516794014
+75711976 0.1888773807248431 0.3092060764509312
+75811010 0.18881252250737668 0.30945195443038737
+75910044 0.1888405798392741 0.3093214751018344
+76009078 0.18903746179063755 0.30925912435162883
+76108112 0.18891766503186144 0.30933080449662914
+76207146 0.188956877878554 0.3095201872728567
+76306180 0.18923743348260558 0.30951511039702495
+76405214 0.18934217399549633 0.3095478291757706
+76504248 0.18913315616143764 0.3095173125569668
+76603282 0.18953322983382556 0.3095805053475441
+76702316 0.1895488076933534 0.3097237924735209
+76801350 0.18975998358967336 0.30979647592146653
+76900384 0.18964168303376333 0.30956718285108054
+76999418 0.18973022558912706 0.3097794051542811
+77098452 0.19007981675561153 0.30968203458057375
+77197486 0.19008660784814244 0.3098269120332887
+77296520 0.18992924793717747 0.31003619533728993
+77395554 0.19019133408686748 0.3098960809258987
+77494588 0.19021578913560125 0.3098829055499614
+77593622 0.19026025172028052 0.3099313249957671
+77692656 0.1903013886591063 0.3099430607641531
+77791690 0.19049018069162793 0.309955198276333
+77890724 0.1906147091321361 0.3100127947618237
+77989758 0.19073745510701984 0.3099703356763612
+78088792 0.19066720628613734 0.3098673328261375
+78187826 0.19067090847786408 0.3101190364549947
+78286860 0.190935290623551 0.3101101060975372
+78385894 0.19092693357198146 0.3101258884807881
+78484928 0.19092346531839896 0.3100646687914152
+78583962 0.19104404132890146 0.31024487399260786
+78682996 0.19099315937265393 0.3099747916221135
+78782030 0.19113396264358642 0.31039186569957317
+78881064 0.19133129373427724 0.3101478852296193
+78980098 0.1913115827238283 0.3101222515526264
+79079132 0.19138512689848106 0.31002049335661197
+79178166 0.19136110737685838 0.31032558966780094
+79277200 0.19148013971745098 0.3101348934162002
+79376234 0.19164948609100135 0.31021459733100526
+79475268 0.19147024684345554 0.3100185166420357
+79574302 0.19150938179529128 0.3104692046430712
+79673336 0.19161457526968934 0.31029612350849756
+79772370 0.19164945057742502 0.3101474398839579
+79871404 0.19165841506261325 0.3101466035279082
+79970438 0.19181386427893987 0.3101349025063515
+80069472 0.19183893994348636 0.31048645228170313
+80168506 0.19186826222108946 0.3103217094410273
+80267540 0.19169890612722532 0.3105397545493947
+80366574 0.19178541700730525 0.3103832916658555
+80465608 0.19211323306676412 0.31063460866351367
+80564642 0.19174288566941294 0.3106816692152302
+80663676 0.19207614221683753 0.3104760987669507
+80762710 0.1917327766489125 0.3108288582815747
+80861744 0.19169446524686426 0.3108754077647546
+80960778 0.19191413860902487 0.31078185692399746
+81059812 0.19196137398248048 0.31091785132790356
+81158846 0.19187815377430373 0.3109879020854023
+81257880 0.19197510721790356 0.31107003154148577
+81356914 0.19198991343793637 0.3112253054721423
+81455948 0.19201396576638038 0.3114095632911917
+81554982 0.19210091036457574 0.3114587374882713
+81654016 0.1921321408213904 0.31147018633303336
+81753050 0.19242141032855367 0.31158217689195544
+81852084 0.19229049656053518 0.3115981210883427
+81951118 0.19236533780569215 0.3114828235822559
+82050152 0.19244991724813565 0.3119113883781243
+82149186 0.1923512234070595 0.3115207059485884
+82248220 0.1923412495901925 0.31183384803480213
+82347254 0.19249815605925139 0.31182329283794413
+82446288 0.19255852001916476 0.3120327438902253
+82545322 0.19252983195790802 0.3120317986968006
+82644356 0.19249764738268665 0.31199195976806265
+82743390 0.19261260095457008 0.3122382214271424
+82842424 0.1927655509214206 0.312234365159557
+82941458 0.19293452123103805 0.3121342021692716
+83040492 0.19299152818211546 0.31245468586914693
+83139526 0.1928325469094537 0.31204335560999336
+83238560 0.19305625049089903 0.3125670574380656
+83337594 0.1929304988781434 0.3123450364808102
+83436628 0.19323204487140425 0.31247962855117356
+83535662 0.19310149147233735 0.3127737794302946
+83634696 0.19329117402133186 0.31261298897727896
+83733730 0.19330274217277107 0.3127637761005858
+83832764 0.1931442730611737 0.3127374951814794
+83931798 0.19343611805606672 0.31271315417675283
+84030832 0.19355815865710696 0.31300434135371796
+84129866 0.19340543285085426 0.31288930606200394
+84228900 0.19331006663979788 0.3128950919877205
+84327934 0.19360512299866953 0.3130675569260312
+84426968 0.19361689037206048 0.31305458852410234
+84526002 0.19375163331147044 0.3131825928171815
+84625036 0.19368709176699392 0.312948052741616
+84724070 0.1938450576324477 0.31337408928898236
+84823104 0.19385779751893586 0.3133284048290789
+84922138 0.19387577443587464 0.3131941270199084
+85021172 0.1936071764699224 0.313452090690397
+85120206 0.19402687550164696 0.3134851717039748
+85219240 0.19412537052937545 0.31339344511010475
+85318274 0.19415549044683836 0.3134531038996519
+85417308 0.19404612211120198 0.3135965804175935
+85516342 0.19425470212795967 0.31350690137607806
+85615376 0.19415886688294762 0.3136323282613895
+85714410 0.19422909304013916 0.3133664590489681
+85813444 0.1942019910691302 0.31359535198827554
+85912478 0.19412471914850132 0.3135724434325961
+86011512 0.1943119444557395 0.3137909600205238
+86110546 0.1942187947642547 0.3137892487582337
+86209580 0.19429721007878284 0.3136719459155833
+86308614 0.1943868382430984 0.31390143939206766
+86407648 0.1946145744776743 0.3136381310692751
+86506682 0.1945702107560608 0.3139212180327577
+86605716 0.19436572428095308 0.3140240858499902
+86704750 0.19446677527580639 0.3140452576925941
+86803784 0.1944371352973632 0.31395543964939865
+86902818 0.19426449021737732 0.31410007455767897
+87001852 0.19465244073760657 0.3141948937004559
+87100886 0.19484663098235894 0.31418697987765754
+87199920 0.19445998440924672 0.3140726212966796
+87298954 0.19461980172348034 0.3143221922608356
+87397988 0.1947213070591164 0.3141793812044294
+87497022 0.1943183474131868 0.31431442815705585
+87596056 0.19483277255983017 0.3144389113052571
+87695090 0.19461247125210104 0.3142958963573502
+87794124 0.19480855515548734 0.31431593153781734
+87893158 0.19468095386096854 0.3145569267328845
+87992192 0.19477676051599185 0.31438437877160436
+88091226 0.19468003583666962 0.3146145054830128
+88190260 0.19455834499494642 0.31455716161656566
+88289294 0.1946652623277906 0.31466688378514834
+88388328 0.19466992945768744 0.31466897096121105
+88487362 0.19456641027337232 0.31462140434428637
+88586396 0.19467943665453094 0.31478838773656753
+88685430 0.19449213066454635 0.31472067259124814
+88784464 0.19461730751943682 0.3149527259545603
+88883498 0.19457022449997652 0.315076535957561
+88982532 0.19474005274028036 0.3150077205104766
+89081566 0.19467270716198593 0.3149781953312204
+89180600 0.19455631987452696 0.3152840147959444
+89279634 0.19465273385497844 0.31542206770989
+89378668 0.19455433145734827 0.3152432939706253
+89477702 0.19477735202790605 0.3153063188302533
+89576736 0.19466298976404559 0.3154068136970311
+89675770 0.19451487650601726 0.31536655396958796
+89774804 0.1946946865456373 0.31562356662220614
+89873838 0.19443631737911746 0.31551552665164234
+89972872 0.1947831772501528 0.3158630573636805
+90071906 0.19496878180760124 0.3159691500284188
+90170940 0.19460201145312112 0.3156522046957052
+90269974 0.19466029362882672 0.31584854987592503
+90369008 0.19473656279271195 0.31594313488211556
+90468042 0.19473471234762438 0.3160060396325479
+90567076 0.19475495156271053 0.3160297593481269
+90666110 0.19489639038281514 0.3161281936641405
+90765144 0.19469449296325214 0.3162878053214868
+90864178 0.19452735797266138 0.3162786440601982
+90963212 0.19475351711502764 0.3163244889352771
+91062246 0.19492147875671154 0.31656705257988044
+91161280 0.19471399376416113 0.31651982879057994
+91260314 0.19499054772720276 0.3165127781734675
+91359348 0.19488717597099497 0.31677151985398366
+91458382 0.19470957339940675 0.31667377357896687
+91557416 0.1948794927075204 0.316763211900904
+91656450 0.1947729629957799 0.3167879343081147
+91755484 0.19481833321977124 0.3169353142667953
+91854518 0.19503620703482694 0.31673685725722334
+91953552 0.19502238401180952 0.3170271575730314
+92052586 0.19491550207812627 0.31707259530108073
+92151620 0.19487258077704417 0.317249811985922
+92250654 0.19502982346455658 0.31714978146126316
+92349688 0.19492605560508128 0.31732312768937276
+92448722 0.19479494030940558 0.31753782940958536
+92547756 0.19516866410953623 0.3174405860273435
+92646790 0.1950853548221008 0.31757671650888625
+92745824 0.19516022453881438 0.31768735732528797
+92844858 0.1951271037253865 0.3176786068843224
+92943892 0.19505476243560504 0.3178682482026982
+93042926 0.19526316364007085 0.3177413565413325
+93141960 0.19506532355008221 0.3177927735682827
+93240994 0.19524816525477995 0.31799650893571774
+93340028 0.19523978373987372 0.31810976233863664
+93439062 0.19504359919070713 0.3179959994634464
+93538096 0.19537626974749125 0.31825354562505687
+93637130 0.19531014555672163 0.31816027028059757
+93736164 0.1952107469444191 0.318343035114328
+93835198 0.19520669685272968 0.3185295009902616
+93934232 0.1953236521649978 0.31843460329416007
+94033266 0.19531360443298232 0.31856539784375215
+94132300 0.1951326413351267 0.3186449341091535
+94231334 0.1953567424900229 0.3186924779340768
+94330368 0.19530657904734441 0.3187661968025626
+94429402 0.19532409765399505 0.3186971120416763
+94528436 0.1954617166673392 0.31893892815144126
+94627470 0.19539470015851287 0.3187606143264611
+94726504 0.19545471934638128 0.31903097473870645
+94825538 0.1955056342644955 0.31904205249724665
+94924572 0.19560446790263786 0.3190164100412226
+95023606 0.19563541089707517 0.31924996954675666
+95122640 0.19574260717077271 0.3194328188739793
+95221674 0.19539706930951364 0.3194637056012608
+95320708 0.19570087174964187 0.3193205165857807
+95419742 0.1954592037428612 0.3194224995169882
+95518776 0.19559854984307265 0.3193267438256559
+95617810 0.19571608316669248 0.3196050799629886
+95716844 0.19571471368434543 0.3198249341390752
+95815878 0.19575680361355136 0.31966976920453277
+95914912 0.19564059713832802 0.3197611015833956
+96013946 0.19575828730589423 0.3198470561097983
+96112980 0.19570159926061892 0.3200195008213598
+96212014 0.19578614072219014 0.32003471294996244
+96311048 0.1957450248261278 0.3201761634973837
+96410082 0.19566124340419325 0.3200752707812375
+96509116 0.195708226722023 0.3200073915933972
+96608150 0.19570283938232716 0.32033241234705084
+96707184 0.19566467082606404 0.3204637951917781
+96806218 0.1956837548555119 0.3205434448794061
+96905252 0.19573342195438848 0.32037145192987526
+97004286 0.19601948821797394 0.3205625393751432
+97103320 0.19585858913539028 0.3206827157042717
+97202354 0.1959684718238612 0.3208090422349592
+97301388 0.19579761396835546 0.32073717225830883
+97400422 0.1959414462266562 0.32095600298986937
+97499456 0.19608977721022885 0.32086200051421204
+97598490 0.19584235703435168 0.3209828515081976
+97697524 0.19580692517532206 0.3209990775613248
+97796558 0.1960664512853599 0.32091089037455195
+97895592 0.19599676536612584 0.321132973973165
+97994626 0.19588578643251348 0.3214425465525568
+98093660 0.19602602828350388 0.32122905790073447
+98192694 0.19600793602330785 0.3213762540495028
+98291728 0.1956715988120863 0.32156503318862617
+98390762 0.19609973668095865 0.3216307162994728
+98489796 0.19590354677055624 0.32151264283839737
+98588830 0.1959704816829138 0.3217074611725619
+98687864 0.1959023068730136 0.32158192828379983
+98786898 0.1960368242874243 0.3216468225697088
+98885932 0.1958837448914206 0.3218051166712123
+98984966 0.19591713314304085 0.32188861438641025
+99084000 0.196053532722229 0.32220085873857446
+99183034 0.19588589307013665 0.3220248800309541
+99282068 0.19588152418496088 0.3221274110868624
+99381102 0.19603199069374208 0.3221464162401373
+99480136 0.19612284562199941 0.32211207606211933
+99579170 0.1960239662958441 0.32212002978338056
+99678204 0.1961869443152644 0.32223138026463755
+99777238 0.19615265032719675 0.32253510382529227
+99876272 0.19601692625710157 0.32241863821906613
+99975306 0.19594593079358144 0.32235425248186067
+100074340 0.1961397860375494 0.32238374196078384
+100173374 0.1962810260036597 0.3225998093868947
+100272408 0.1962277832006454 0.3226415864456581
+100371442 0.19635149786011943 0.3227107889174928
+100470476 0.19609143684672226 0.32292363134606594
+100569510 0.19622793653554707 0.32297184868874285
+100668544 0.1963253201296045 0.3231554582076177
+100767578 0.19611083176896207 0.3231444697603188
+100866612 0.19611715306323022 0.3232730344936192
+100965646 0.19633067654944997 0.3232938182880848
+101064680 0.1963615680557811 0.3233464966150485
+101163714 0.19629335058014322 0.32330559884651683
+101262748 0.19634272487306734 0.3235326373031453
+101361782 0.1962509160425985 0.3235218702354365
+101460816 0.19623552972122776 0.3234296747726808
+101559850 0.196285659656151 0.323606025140146
+101658884 0.19610344962068668 0.3237983886752256
+101757918 0.19622731067199134 0.32382321838333983
+101856952 0.19616830544219588 0.3238186275354956
+101955986 0.19624207388092435 0.3240218741627557
+102055020 0.19633813520242643 0.32409339307592067
+102154054 0.19629133738604024 0.3242413137944315
+102253088 0.1962072227508355 0.32410784904359885
+102352122 0.1962641842859166 0.32437531185880897
+102451156 0.19637812531879179 0.32429272731907194
+102550190 0.19641935224352966 0.3243434607788519
+102649224 0.19624133134313881 0.3245125408854854
+102748258 0.19639131075157285 0.32460693875089075
+102847292 0.196315493095757 0.32455252664565015
+102946326 0.19619219287618256 0.32486198961653673
+103045360 0.1961672114677673 0.3248000057833631
+103144394 0.19638636909570178 0.32472139326359806
+103243428 0.19623120065855887 0.32503016192506257
+103342462 0.19633748874748078 0.325077509421586
+103441496 0.19615631483469403 0.3249946779939283
+103540530 0.19629205035977706 0.32520693937635753
+103639564 0.19625517982897775 0.3251137034699711
+103738598 0.1963893923102708 0.32522982107303633
+103837632 0.1964246346848858 0.3253358019326075
+103936666 0.1961126834208647 0.3257272618835337
+104035700 0.1962289550850744 0.32551634407004487
+104134734 0.19646641946939927 0.32549039092050946
+104233768 0.1963633010844418 0.32585709720629014
+104332802 0.19622633613187754 0.3257769257567453
+104431836 0.19623881764035475 0.3260307258644278
+104530870 0.196246880003976 0.3258477378029442
+104629904 0.19640130737201839 0.32601216198187083
+104728938 0.19646406577303527 0.3261609615262673
+104827972 0.19636555946853174 0.3260949562169019
+104927006 0.19643598703043497 0.32641529501963645
+105026040 0.1963482309733226 0.3262592257932336
+105125074 0.19624380830772817 0.32633036871510795
+105224108 0.19613368577398466 0.32642901429902815
+105323142 0.19636186145540602 0.32633408820005516
+105422176 0.19630534374003705 0.32671495108192294
+105521210 0.1962760958989672 0.3269473676453339
+105620244 0.1960693244949678 0.32684778330010794
+105719278 0.19623038623289937 0.3268399164755825
+105818312 0.19618036187414303 0.3267041243230543
+105917346 0.19648722453012302 0.3269153753088649
+106016380 0.19639358721088465 0.3272076233131733
+106115414 0.19633974746398958 0.32706356216649435
+106214448 0.19618876188485337 0.3271843845169827
+106313482 0.19624051303395482 0.32736455035169476
+106412516 0.1960312091034126 0.3273479730122958
+106511550 0.1959763752773775 0.3274487284871639
+106610584 0.19616319467047902 0.3273125368638869
+106709618 0.1961739468198969 0.32746355846956554
+106808652 0.19608588891992698 0.3276910761917371
+106907686 0.19621710660142025 0.328066387062912
+107006720 0.19627894360627296 0.3278150599175337
+107105754 0.19628014439323588 0.3280165334564211
+107204788 0.19647450192886792 0.3278219667279872
+107303822 0.19636560984317863 0.3279525483738302
+107402856 0.19629035899914893 0.32815808857832773
+107501890 0.19623694172704167 0.3281233546185718
+107600924 0.19629835329289877 0.3282947357258185
+107699958 0.19598601745950944 0.32829671176703257
+107798992 0.19619028839258074 0.3285304142522131
+107898026 0.19608596589613395 0.3285227022115943
+107997060 0.19619281201066477 0.32869317752694843
+108096094 0.19593117642902727 0.3285857010617342
+108195128 0.19613764301380926 0.3287135471558772
+108294162 0.19591989680069058 0.32876722175286494
+108393196 0.19611576820783846 0.32902741896601395
+108492230 0.1960363143924566 0.3289293505846242
+108591264 0.19605563358925052 0.32904331464132486
+108690298 0.19588453280410975 0.3289328475079913
+108789332 0.19585273438939943 0.3289871342826588
+108888366 0.1956983963615672 0.32922583788682097
+108987400 0.19584337384863013 0.3293553283648164
+109086434 0.19577338880206036 0.3293480564681509
+109185468 0.19586023798140767 0.3298987587073686
+109284502 0.19576464698165963 0.32955802504019843
+109383536 0.19588903298500143 0.329933221079442
+109482570 0.19563149588452836 0.3298895629480247
+109581604 0.19578566730289268 0.3300201462569077
+109680638 0.1957912462162749 0.3301047094142823
+109779672 0.19578422886283095 0.33036953877186553
+109878706 0.19563669996945038 0.3301937109709939
+109977740 0.1958770818539534 0.3305802131941199
+110076774 0.1958955299539563 0.33021093152267633
+110175808 0.19581951227406993 0.33054268249620217
+110274842 0.19581974392395834 0.3306475555887858
+110373876 0.19605291083834933 0.3307439010180209
+110472910 0.19569844517176224 0.3308139755011277
+110571944 0.19588967487284073 0.3308251532720633
+110670978 0.19583020199214343 0.330933914220878
+110770012 0.19568517017262083 0.3310811202805878
+110869046 0.19570236807088284 0.33118962510544425
+110968080 0.1957900261922454 0.3310923682382039
+111067114 0.19581950025385417 0.3312758379535011
+111166148 0.19565740461049752 0.33137951804394383
+111265182 0.19570726189537327 0.33124508889947246
+111364216 0.19563352436121598 0.331414420935351
+111463250 0.19569860346863838 0.3315842484089246
+111562284 0.19551135902501166 0.3316342484901899
+111661318 0.19563213095206294 0.33171881288785143
+111760352 0.1954436380796242 0.3319308774124123
+111859386 0.19561491766673997 0.33182049407006464
+111958420 0.19574715116894117 0.3319270027895419
+112057454 0.19562857496889313 0.33195458489391105
+112156488 0.19533241768458778 0.3320660783415074
+112255522 0.19567037557231684 0.3321769635726145
+112354556 0.19544284248387628 0.33220011234593394
+112453590 0.19544516411198873 0.332408719093099
+112552624 0.19533399271285484 0.33240593692608494
+112651658 0.19577657253138986 0.33268958420332784
+112750692 0.19568724643522917 0.33269247356420467
+112849726 0.1956758786906492 0.33290003393303297
+112948760 0.19580339371974304 0.33283437820744416
+113047794 0.19520630341566986 0.3329506330679126
+113146828 0.1954170800645215 0.3328563098440153
+113245862 0.1952782571400725 0.3331881987571346
+113344896 0.19542999051891402 0.333090662149759
+113443930 0.19523564424529205 0.33306691064708926
+113542964 0.19507975065569896 0.3333654424187699
+113641998 0.19536133591872568 0.3334713555250122
+113741032 0.19537400271705738 0.3335747960144948
+113840066 0.19526747298699595 0.33397743447474987
+113939100 0.1953352175792136 0.3337891510171362
+114038134 0.1954046990938177 0.3338621758463851
+114137168 0.19518055213958418 0.33386630286598384
+114236202 0.19526895428501656 0.33376868631463413
+114335236 0.19513825149740294 0.3341771622976617
+114434270 0.19524945345011602 0.3341267349101265
+114533304 0.19521855485896616 0.3341876068706394
+114632338 0.19510637337392536 0.3344121940474129
+114731372 0.19509356109226447 0.3342593589786948
+114830406 0.19483672732001062 0.33446993158817917
+114929440 0.194873621394793 0.3345602989033777
+115028474 0.19489262677310604 0.33495259741485933
+115127508 0.19525879688976872 0.33485038192728284
+115226542 0.19503214206378927 0.3349116260280665
+115325576 0.19481209821114082 0.33514084224537843
+115424610 0.19524288126094794 0.33529219993649184
+115523644 0.19482945838143836 0.33534930822267833
+115622678 0.19480800249608948 0.33527125050039785
+115721712 0.19516715155274947 0.3351469551947426
+115820746 0.1950472483555463 0.3356568808998777
+115919780 0.19473898474859586 0.3357596036473241
+116018814 0.19507253295697308 0.3358035703452942
+116117848 0.19483958355313483 0.33566104623759374
+116216882 0.19457071142471957 0.3357278492316211
+116315916 0.1945054386878162 0.3358635646750947
+116414950 0.19472744085135058 0.33594035220240726
+116513984 0.19485009025389366 0.3359024671439096
+116613018 0.19460371984292085 0.336037425391823
+116712052 0.19464919380432458 0.3360653425996501
+116811086 0.1946675405059259 0.33616447344150097
+116910120 0.1944895805829714 0.33624321492918285
+117009154 0.19446825987206326 0.3365958093890603
+117108188 0.1945107124779483 0.33647635441134255
+117207222 0.1943391019609544 0.3367776639731835
+117306256 0.19460994735969037 0.3369375547036471
+117405290 0.19418818254154102 0.33668896797866704
+117504324 0.19440848728691862 0.336750914199124
+117603358 0.19423621478307393 0.3370591500702564
+117702392 0.19425391558272798 0.33693920861286636
+117801426 0.19414784186919604 0.33710574630396745
+117900460 0.19390143781098265 0.3372493091642379
+117999494 0.19411192140475908 0.33724507428611955
+118098528 0.1942964126229824 0.3373564955888579
+118197562 0.19411170740946992 0.337617699133306
+118296596 0.1938816778886829 0.3377828719437272
+118395630 0.1940802731401175 0.3376664808273968
+118494664 0.19397795119691316 0.337664247810383
+118593698 0.19422915517728861 0.33809940977740643
+118692732 0.1936633628503147 0.33786067698410593
+118791766 0.19378804670781466 0.3382291068525192
+118890800 0.1939507917642807 0.33821552448934517
+118989834 0.19385380608139552 0.3383158662123652
+119088868 0.19386363286560784 0.3383657309913343
+119187902 0.19380534620804968 0.3382525301237217
+119286936 0.1937779832743579 0.33875686856193477
+119385970 0.1937691299596349 0.3387019304028824
+119485004 0.194017193256959 0.3389084098622411
+119584038 0.19364360094195948 0.33885516399314347
+119683072 0.19376148576018293 0.3389567411387815
+119782106 0.19374207089108028 0.3390577566365487
+119881140 0.19364722914473392 0.3389468181497423
+119980174 0.19339420823398293 0.33903356719521416
+120079208 0.1943523434317651 0.3397725609594743
+120178242 0.1942946426960076 0.33957123783499343
+120277276 0.19412499301457548 0.3397494454567614
+120376310 0.19414743436166815 0.3400111925102109
+120475344 0.1942592985598793 0.34002091464314
+120574378 0.19390726231975167 0.34041207728219347
+120673412 0.19387023766727712 0.34036673282102203
+120772446 0.19406453053120906 0.34012700632697807
+120871480 0.19402869532715256 0.34065628630621764
+120970514 0.19395782389725097 0.34067350681080616
+121069548 0.19375008015910022 0.3407909072677256
+121168582 0.19377087576882168 0.3408026942348611
+121267616 0.19396072565098949 0.34081330549425165
+121366650 0.19391957368002385 0.34113199529129656
+121465684 0.19375053700049952 0.3409852019207847
+121564718 0.19370160456625643 0.341064890774393
+121663752 0.19349178370893946 0.3412637125325645
+121762786 0.193758256498467 0.3414094016795598
+121861820 0.19342142181197522 0.34156166022245854
+121960854 0.19363862088891498 0.34152300337174957
+122059888 0.1934550203316364 0.34186465783901093
+122158922 0.1933419620267742 0.3415614998032925
+122257956 0.19348063719628916 0.3422551175955476
+122356990 0.19369374325268768 0.342022448169388
+122456024 0.19331789447054634 0.34224789743447015
+122555058 0.19334782412356394 0.34238909376586707
+122654092 0.19334747496906043 0.34216124331743236
+122753126 0.1932108659762469 0.34243278694247575
+122852160 0.19328046495517268 0.3425246163199633
+122951194 0.1931527140742474 0.3424155089381224
+123050228 0.19316598463142054 0.34275677138064253
+123149262 0.1930254154922616 0.3428005989469787
+123248296 0.19320276716355697 0.34277202295328896
+123347330 0.19321567823116534 0.3432225271929936
+123446364 0.19317225255192189 0.34303063077084445
+123545398 0.19284249466518494 0.34314505762068426
+123644432 0.19311533083425453 0.3430278557911865
+123743466 0.1930778644355555 0.34345677172811095
+123842500 0.19267490418683633 0.34339647468330997
+123941534 0.19286126139929088 0.34323899723674073
+124040568 0.19296483236538178 0.3440101937446899
+124139602 0.1928533998375601 0.3438725911402488
+124238636 0.19275653937490755 0.3435783630481198
+124337670 0.19256078082626255 0.34400017881783307
+124436704 0.19300657782705477 0.3439834005219614
+124535738 0.19250702508230477 0.34373983584950085
+124634772 0.19263525854182356 0.3442867944635947
+124733806 0.19261702262747146 0.344433474833476
+124832840 0.19251580668238988 0.344448340170529
+124931874 0.19254449973277443 0.3446904837944223
+125030908 0.19251040778876144 0.3444790743077356
+125129942 0.192411941645135 0.34492015629051226
+125228976 0.19257842171963765 0.3447095314402983
+125328010 0.1923431795624823 0.3449011664771885
+125427044 0.19233445009644565 0.34504837525713544
+125526078 0.19217637039626623 0.34497924801655877
+125625112 0.19219198865722897 0.3453381531530138
+125724146 0.19244791616879722 0.3454614466809204
+125823180 0.1923106643240182 0.3453533868620126
+125922214 0.19205654036053832 0.34549484118374685
+126021248 0.19221714265109816 0.34559301668953185
+126120282 0.19204129984775076 0.34562385020355796
+126219316 0.19201863159466048 0.3459135298536766
+126318350 0.19209255441768022 0.34593736367469
+126417384 0.19201876220568465 0.34591350834776385
+126516418 0.19192575361397773 0.3460149522373591
+126615452 0.191795822596152 0.346415793449146
+126714486 0.19183441244611438 0.3463090657615739
+126813520 0.19199887433585416 0.3461534810304289
+126912554 0.19164105652016675 0.34625912480883797
+127011588 0.1918449503587823 0.3465607464252008
+127110622 0.19169635114556716 0.34649855301608584
+127209656 0.1915277766166222 0.3466368824130133
+127308690 0.1917295175048316 0.34689254780599105
+127407724 0.19155716827777391 0.3470040154588144
+127506758 0.19166945497520513 0.347142640668813
+127605792 0.19153225082186082 0.3472269480166311
+127704826 0.19133825244536418 0.3469417412219457
+127803860 0.1915352890558858 0.34732692216238165
+127902894 0.19135065145263933 0.34749957467726905
+128001928 0.19148142708605753 0.34751908425200273
+128100962 0.19145255382498547 0.34800228415174594
+128199996 0.19111359673772665 0.34770112994331315
+128299030 0.19140362774104275 0.34779049596009554
+128398064 0.19121013201247786 0.34807856352602806
+128497098 0.1911503827777167 0.348246610745205
+128596132 0.19141062041375678 0.3481930091172836
+128695166 0.19106635491470061 0.3485512562688286
+128794200 0.1909239431950177 0.34834942604211966
+128893234 0.19111432665875144 0.34836686181284693
+128992268 0.19104310841329944 0.34860317748906827
+129091302 0.19100437592691955 0.34886534883637754
+129190336 0.19083478940409712 0.34888887091265214
+129289370 0.19102971035280653 0.34888371412535035
+129388404 0.19064990586504443 0.3489304249356903
+129487438 0.19089573155236986 0.3490011934522866
+129586472 0.1908813406033753 0.34909373625894824
+129685506 0.1908748467457187 0.3490654106923934
+129784540 0.1907988314036514 0.3493714199146784
+129883574 0.19048286824246813 0.3495822072671722
+129982608 0.19058191000434868 0.3496820824265072
+130081642 0.1905204476399259 0.3494693489456053
+130180676 0.1907063718627091 0.3500672212627
+130279710 0.1906215263839341 0.3500440377715457
+130378744 0.1903983344465392 0.35005331302869513
+130477778 0.19044291830782117 0.3499163502907786
+130576812 0.19054856737451809 0.350094788868816
+130675846 0.1903826838216799 0.3504578277316762
+130774880 0.19046626556704244 0.350377314185954
+130873914 0.19015577260956776 0.35051866089713135
+130972948 0.19016028899286594 0.3507331933969936
+131071982 0.19010646489022942 0.35078743998658557
+131171016 0.19017158321759967 0.3510372384782558
+131270050 0.1901332305231146 0.35080712684429377
+131369084 0.18988094221093546 0.3511080244792155
+131468118 0.19025581492483962 0.3510956960297686
+131567152 0.18994896839310615 0.35149569003229736
+131666186 0.18994329544570757 0.3515064702869233
+131765220 0.18985808563095222 0.3515982403094734
+131864254 0.1897739031431662 0.35154196943881966
+131963288 0.18982095702667798 0.35152406063576125
+132062322 0.18980637296283231 0.35165134813758414
+132161356 0.1897020507995876 0.35185517780995845
+132260390 0.18976205404333868 0.3519429288933312
+132359424 0.1898356580258734 0.35213330202221865
+132458458 0.18962242438134466 0.35231703972656303
+132557492 0.18954848523546183 0.3520940919847881
+132656526 0.18971918046926647 0.35232033659023143
+132755560 0.18939828899249334 0.3526529536160806
+132854594 0.1892958279044974 0.35251815509858575
+132953628 0.1891000422998207 0.3523463204463642
+133052662 0.18940375959331557 0.3526532291102463
+133151696 0.18918399880878556 0.3524781339151672
+133250730 0.1893493259974382 0.3528181314744563
+133349764 0.18923188709681932 0.35313012363119284
+133448798 0.18903071012262115 0.3532972586007677
+133547832 0.18917592728285915 0.3531036478409291
+133646866 0.1889311800627885 0.35333475549317866
+133745900 0.1887173361873705 0.3532482698809726
+133844934 0.1888374028307299 0.353227325227596
+133943968 0.1889420644939345 0.3531613527109894
+134043002 0.1884408295243903 0.35354736146970367
+134142036 0.18861636921358696 0.3534714310385409
+134241070 0.18868719804631157 0.3539472104544752
+134340104 0.18864018746154348 0.35401917872643224
+134439138 0.18837075299527126 0.3539061574152958
+134538172 0.18834319133920382 0.35385969026473557
+134637206 0.1883918761054749 0.35392667466746347
+134736240 0.18825643663475994 0.35436913299220363
+134835274 0.18818385413399705 0.3542051295480003
+134934308 0.18818237698535356 0.35433711727763717
+135033342 0.18790999924274113 0.3546187929059599
+135132376 0.1879762591843255 0.3546683516761667
+135231410 0.18793329047309856 0.35487816372205383
+135330444 0.18780360788918793 0.3548745384003039
+135429478 0.1882965341841264 0.3549405274089256
+135528512 0.1877978775806527 0.355054638834349
+135627546 0.18768911292056706 0.3552492048272304
+135726580 0.187344796666609 0.35541501182500124
+135825614 0.18759700085148515 0.35553321626555845
+135924648 0.18737982821522267 0.35568798070203084
+136023682 0.18754782923675103 0.35564981444393196
+136122716 0.18730871314148728 0.3560684671852514
+136221750 0.18744622876965547 0.35591073359519215
+136320784 0.18726597540959716 0.35629652136240747
+136419818 0.1871975591191668 0.3563846521967867
+136518852 0.18708717559552338 0.3563060075705835
+136617886 0.18722350884937877 0.3563577880268282
+136716920 0.18699466650982327 0.35669137000572704
+136815954 0.18700885525086186 0.3563741708004839
+136914988 0.18682219369791828 0.35696351643456314
+137014022 0.18669594582083243 0.356769779254764
+137113056 0.18690000135100315 0.35684618331949464
+137212090 0.18683359012531608 0.3569856499841107
+137311124 0.18664225928959016 0.35684564988470135
+137410158 0.18665612706377493 0.35701650019332826
+137509192 0.1866903932554165 0.3573602138655581
+137608226 0.18661071980305238 0.3573372945652124
+137707260 0.186375577186822 0.35763061005837243
+137806294 0.1863567716501876 0.3576891665527875
+137905328 0.1866808671705714 0.35773217264499435
+138004362 0.18616889305562706 0.35766280819468144
+138103396 0.18623246656989786 0.35787890142231255
+138202430 0.18608920111263227 0.3580146983586484
+138301464 0.1859085954285362 0.3580419689780472
+138400498 0.1858151719239465 0.35816066923065115
+138499532 0.1859863698837186 0.3583032106718517
+138598566 0.18592414183454808 0.3584298852425725
+138697600 0.18574575947728406 0.3584567569036109
+138796634 0.1856366824434665 0.35855628457599487
+138895668 0.18559703275921063 0.3586899053646492
+138994702 0.18580250721513622 0.35871693006327415
+139093736 0.1859007231394729 0.3584769043076056
+139192770 0.18592434922818638 0.35914439682259464
+139291804 0.1857552068594009 0.35894192549038284
+139390838 0.18562486468099085 0.3590947665114622
+139489872 0.18536434991040807 0.3590821668787864
+139588906 0.18538516582567946 0.3593082916280647
+139687940 0.1853108030791543 0.35934910826400596
+139786974 0.1853392745010806 0.3595485681565667
+139886008 0.18504952182970616 0.3597568723115563
+139985042 0.1852205280284322 0.3598593282201312
+140084076 0.1850986036198456 0.36018150907461555
+140183110 0.1848845596325288 0.35994851502609654
+140282144 0.18481079749865761 0.36032031277319154
+140381178 0.18466407958824455 0.360291987759212
+140480212 0.18458375688309014 0.3602511874231245
+140579246 0.1846701252013723 0.3601579347911772
+140678280 0.18472931188673633 0.3604006887406019
+140777314 0.184446263554401 0.36076647656869937
+140876348 0.1846230450860029 0.360871702765926
+140975382 0.18446070880177656 0.3611417724102899
+141074416 0.18461004087744137 0.36092091956865335
+141173450 0.18430567911628853 0.3612817017715909
+141272484 0.18430724897360107 0.36118024321826736
+141371518 0.18400605672318235 0.3613132965425719
+141470552 0.18428510349459937 0.36172957260086674
+141569586 0.1842589409802393 0.36147736351616105
+141668620 0.18422171385387362 0.36129326157750535
+141767654 0.1841587933142608 0.361488728603078
+141866688 0.18394522179371223 0.3621462222399877
+141965722 0.18395766942656935 0.36219045492514396
+142064756 0.18380820151137464 0.36216241137972127
+142163790 0.18401329637288472 0.36227999989316556
+142262824 0.18395487297699495 0.362448950846271
+142361858 0.18386602477833844 0.36258134405295367
+142460892 0.18364019030262524 0.3626324536845237
+142559926 0.18338763705278568 0.36268173982706126
+142658960 0.18363052132522523 0.36271449008451256
+142757994 0.18334255959713935 0.3629005154329452
+142857028 0.18313827316243897 0.3631793816780938
+142956062 0.18361060732048065 0.36332781769129063
+143055096 0.18331113730739637 0.3631027418716778
+143154130 0.1833736334104371 0.3632507462994208
+143253164 0.18325932040217682 0.36334180107690994
+143352198 0.18325068502710587 0.3634665714272804
+143451232 0.18291488998945793 0.3635788173671004
+143550266 0.1827990628926817 0.36377274517832425
+143649300 0.18280635644123425 0.3638828482108854
+143748334 0.18285640216243335 0.3638819946061461
+143847368 0.18274724890044558 0.3641326478359545
+143946402 0.18246959503122637 0.3640092710176077
+144045436 0.18240161852873946 0.3642500573505044
+144144470 0.18276099353728728 0.36440004832309514
+144243504 0.18241954522737713 0.3644659700374117
+144342538 0.18222316550553633 0.3648777178934252
+144441572 0.1823851830592884 0.364634924368721
+144540606 0.18212847877130442 0.36445986001779973
+144639640 0.18210115985201933 0.3645008674505741
+144738674 0.1821634125955238 0.3648359914480903
+144837708 0.18163936107505105 0.3646611952386646
+144936742 0.18185777524630492 0.3648289662175112
+145035776 0.1816977618754028 0.3650094103078079
+145134810 0.18156228165446167 0.36509127851116924
+145233844 0.18160325413536324 0.365408767020322
+145332878 0.18151729087219273 0.3651992401623852
+145431912 0.18132797979820744 0.3654566103819036
+145530946 0.18134861666781504 0.36550640963658876
+145629980 0.18141239153542024 0.3659715729368555
+145729014 0.1811809536575473 0.36581692829325807
+145828048 0.1812410149666919 0.366074713921361
+145927082 0.18134010194804262 0.36588330896401394
+146026116 0.1810775919612535 0.3659886206250947
+146125150 0.18087672884123077 0.3662845697795748
+146224184 0.18092408928592726 0.36655877737541187
+146323218 0.18116299151846385 0.3666289641289418
+146422252 0.18107880066355037 0.36680860129314563
+146521286 0.18090937097484833 0.367003508694348
+146620320 0.18070993987466072 0.36673742543147025
+146719354 0.18060497829599523 0.36670729872993285
+146818388 0.18074087806002204 0.3675064026095991
+146917422 0.18083797832462511 0.3674155634664587
+147016456 0.18084249205987962 0.36772983830398104
+147115490 0.18105759879747993 0.36777367166239283
+147214524 0.18070721399490677 0.367939617197719
+147313558 0.18042977457097215 0.3677602062119324
+147412592 0.18041504441160217 0.3680318315582515
+147511626 0.1804365322058839 0.36802779927633017
+147610660 0.18033228949624555 0.36834368595455863
+147709694 0.1802134121315829 0.36858478068157663
+147808728 0.1802283024806447 0.3685470767477355
+147907762 0.18024841454117665 0.3683499215978357
+148006796 0.1798901279753764 0.3687017066453789
+148105830 0.1802031381939678 0.36857992179952254
+148204864 0.17969746052611488 0.3688536780051392
+148303898 0.17996441199940405 0.3689369705980702
+148402932 0.1799170875556747 0.3690502912887293
+148501966 0.17997864784175685 0.3691974090209986
+148601000 0.17950478390427796 0.3692928562567896
+148700034 0.17955286671614418 0.3691315045224233
+148799068 0.17954768784302577 0.3694454331042871
+148898102 0.179478305374868 0.36949808641295234
+148997136 0.1792934397342979 0.3696519545799275
+149096170 0.17900791583041442 0.36985266134418415
+149195204 0.17888158105448781 0.3699310005577959
+149294238 0.17947387789980906 0.36995550395810417
+149393272 0.17917475971222258 0.37017843248101184
+149492306 0.17924081599319308 0.3701072690250871
+149591340 0.17908709193495176 0.37043881110688615
+149690374 0.17892365806069443 0.37055107621704236
+149789408 0.17877902497838943 0.37054474855338465
+149888442 0.17876597945477027 0.37052648212346295
+149987476 0.1787636762363938 0.37102110527066207
+150086510 0.17871238145910162 0.3708630280205588
+150185544 0.17869242154016232 0.3710066833776006
+150284578 0.17872305087361076 0.37125308654664524
+150383612 0.17869827425213894 0.37135860641791957
+150482646 0.17837569686719826 0.37178457933438175
+150581680 0.17837006875429773 0.371333713993087
+150680714 0.17841906780834835 0.3719644260343
+150779748 0.17816233260539394 0.37194675266198607
+150878782 0.17844203862531766 0.3720275144573134
+150977816 0.17832409707767705 0.3719945152534835
+151076850 0.17801505456267933 0.3723282461367621
+151175884 0.17778842573676093 0.37228045175341046
+151274918 0.17804914580013234 0.37249469191648965
+151373952 0.1778296071762815 0.37258719793805994
+151472986 0.17782494933448492 0.37289658618788013
+151572020 0.1776117804399633 0.3728681335923318
+151671054 0.17769343077965072 0.372860162142081
+151770088 0.1774873437405453 0.37292912854836235
+151869122 0.17724360845466913 0.3729245946463238
+151968156 0.17733455809958795 0.3729114523936698
+152067190 0.17761761805505202 0.37318181244011045
+152166224 0.17731751274939112 0.37306261273677765
+152265258 0.17729812795526215 0.3733251247121701
+152364292 0.17733506267037474 0.37345200046186566
+152463326 0.17701105615671162 0.37375090748161766
+152562360 0.17680756379663992 0.37359313543279354
+152661394 0.17671969345937177 0.3738102288228032
+152760428 0.17643754516905635 0.3736539596431989
+152859462 0.17660962382517895 0.3738198975792025
+152958496 0.1766337123478937 0.3740199843525206
+153057530 0.17637399130274667 0.37414168724764424
+153156564 0.17636904430874173 0.37426452830882156
+153255598 0.17616875048266362 0.37426355173520665
+153354632 0.17650338153154294 0.374493188721493
+153453666 0.1765291270710905 0.3743378345813941
+153552700 0.17654206020854682 0.37478405196491293
+153651734 0.1761868028830018 0.3748120559691135
+153750768 0.17583038229689654 0.37493967450195426
+153849802 0.1762422525383649 0.37506735546844305
+153948836 0.17598505657288094 0.3749601565772778
+154047870 0.1761852072839449 0.3752221980955541
+154146904 0.17598004020981423 0.3754339784369582
+154245938 0.17571647892987488 0.37565182500268646
+154344972 0.17589548063160504 0.3756125390606146
+154444006 0.17577003199575136 0.37556443190053723
+154543040 0.1756103448987806 0.37582341770396727
+154642074 0.17562377473223595 0.37588195604897473
+154741108 0.17547007258066816 0.37619742691238
+154840142 0.17525983965647174 0.37627452725645155
+154939176 0.1751022538872034 0.37605202506035557
+155038210 0.17522284408666525 0.37656685948850116
+155137244 0.17506704399013046 0.3765402214579569
+155236278 0.1748747888129539 0.37633922818576754
+155335312 0.17477367406517236 0.3767300932742041
+155434346 0.17505344243053636 0.37663746828131706
+155533380 0.1747608221798137 0.37694923906125594
+155632414 0.17469106548810376 0.3770872210501848
+155731448 0.17482951908921543 0.3773244817581974
+155830482 0.17480586500687598 0.3771203916770377
+155929516 0.17438172228717255 0.37753165690119805
+156028550 0.17426018424492187 0.3773842435612683
+156127584 0.1742916382151757 0.3774992767116389
+156226618 0.17432140797680076 0.3777991675981728
+156325652 0.17425443994408632 0.3779009468892263
+156424686 0.17379431807052334 0.3781031346489717
+156523720 0.1739457310215303 0.37806996860981323
+156622754 0.17407513193205842 0.3781101887171157
+156721788 0.17411198325932528 0.378181448734229
+156820822 0.1738465420099399 0.37821041398204946
+156919856 0.17363479684949218 0.37837844899639395
+157018890 0.17374879519848238 0.37871118506124757
+157117924 0.17382527375620013 0.3783780659413451
+157216958 0.1735531875334246 0.37888582731357473
+157315992 0.17318774498817224 0.3787035475490745
+157415026 0.17318285430350622 0.37901128467254414
+157514060 0.17342680362366805 0.3790827634175839
+157613094 0.17332917817025603 0.37913510526253635
+157712128 0.17326898665685583 0.37920421911420693
+157811162 0.17303021706562627 0.37935234440303556
+157910196 0.17334801437487263 0.37920378262720883
+158009230 0.1727830385737355 0.37968683583077756
+158108264 0.1730791065416823 0.37981122349796864
+158207298 0.17276070919690273 0.37970200052664344
+158306332 0.17284045880322432 0.3802994277341476
+158405366 0.17281308871427478 0.3802625348334041
+158504400 0.17264517805831847 0.3802284632107423
+158603434 0.17256216114662382 0.38035706833910304
+158702468 0.17252782573343395 0.3806442012995065
+158801502 0.1723732567182902 0.3803777909288966
+158900536 0.17221194128320014 0.3806405580267598
+158999570 0.1718765152033813 0.38040074390700906
+159098604 0.1724091486582776 0.3811750560181154
+159197638 0.1717370256708825 0.3808796210554756
+159296672 0.17219831950473521 0.38104155876396517
+159395706 0.17213044623605425 0.381313770994719
+159494740 0.17178734087507716 0.3813501818227208
+159593774 0.1717215473054759 0.38151319401915534
+159692808 0.17199984003825786 0.38174523351697365
+159791842 0.17170718653254477 0.3816687575756857
+159890876 0.1714648673584595 0.3816968361267209
+159989910 0.17111830139741216 0.38190352757937546
+160088944 0.17142928366648372 0.38239588908677546
+160187978 0.1715406322686448 0.381930197249036
+160287012 0.17116241639169158 0.3823530021340302
+160386046 0.17100156085900597 0.38239459365826106
+160485080 0.17103623287204392 0.38235037981585457
+160584114 0.17085725313805472 0.3821465622305535
+160683148 0.1714597576281517 0.3826180517354958
+160782182 0.17079197509291444 0.3828320046747262
+160881216 0.1709187538946648 0.38250953286135253
+160980250 0.17066648664795342 0.3832675545354801
+161079284 0.17062362876655943 0.38307337386632734
+161178318 0.17047311159109085 0.383143734154192
+161277352 0.17034178656929336 0.3832909887425476
+161376386 0.1700291531764863 0.3832452396402778
+161475420 0.17029126541380382 0.3835564224814944
+161574454 0.17000088147780618 0.38350925123551477
+161673488 0.17006081434004647 0.3835594698377219
+161772522 0.16996427239251685 0.38384661230864375
+161871556 0.16991706671915666 0.3841884193861548
+161970590 0.1699496686634684 0.383896461200884
+162069624 0.1697756979097601 0.3842967559290549
+162168658 0.16957937314230984 0.38415702497801113
+162267692 0.16912660696186052 0.3844939811849896
+162366726 0.16983342801798668 0.3845955103694774
+162465760 0.16963631710573338 0.3847247908763084
+162564794 0.1693933104569157 0.3848810745855368
+162663828 0.1694989573172116 0.3844616905037692
+162762862 0.16903363269398924 0.38514572877675485
+162861896 0.16909014255116742 0.3849790293980411
+162960930 0.16911456179965872 0.3851111492981061
+163059964 0.16885481117804532 0.38524845819581405
+163158998 0.1687427891903465 0.38522064764598307
+163258032 0.16898776129526974 0.38531000372620555
+163357066 0.169013911192389 0.3856927839503679
+163456100 0.16855674081845282 0.385869191051671
+163555134 0.16820649299717022 0.3860620512256951
+163654168 0.16833668408507171 0.3858716904867785
+163753202 0.1684915943654423 0.386090559850515
+163852236 0.16828354823050626 0.38625354262236855
+163951270 0.16801860006742764 0.38638529403434946
+164050304 0.168225330240768 0.3864096279724459
+164149338 0.16832413675709681 0.3864826752001386
+164248372 0.1683060045555922 0.38658707410759224
+164347406 0.16803588741191744 0.3868607342941686
+164446440 0.16756627493210127 0.3870361993212117
+164545474 0.16777087047120032 0.38700530725272375
+164644508 0.16720053744397292 0.38697263514438157
+164743542 0.16777550966456184 0.3869886863212394
+164842576 0.1673127821674917 0.387402125781478
+164941610 0.1673777606745769 0.3871515365984378
+165040644 0.16731369738020455 0.38749946383307865
+165139678 0.16727158528133637 0.3879138920837325
+165238712 0.16727638241657872 0.38742673076437933
+165337746 0.16710728401843405 0.3878896229189195
+165436780 0.1668594942906335 0.3881459254137634
+165535814 0.1668193881253491 0.38792965477668184
+165634848 0.16686710899370008 0.3880997518175746
+165733882 0.16687643161943957 0.3883279183090525
+165832916 0.16685837069446552 0.3882832653192966
+165931950 0.16647021554381455 0.3885000339120622
+166030984 0.16637298816596438 0.3884856731121906
+166130018 0.16644913494577454 0.3887145428225564
+166229052 0.16609559250692438 0.388912894878205
+166328086 0.16616269305919273 0.388886151208575
+166427120 0.16584425419479357 0.3886288172221885
+166526154 0.16593266594311498 0.3891316058632574
+166625188 0.16547213867954408 0.3891133536329249
+166724222 0.16592115956631343 0.3894089307376809
+166823256 0.16593349473622435 0.38940457884216667
+166922290 0.16551564816167946 0.3895845632454928
+167021324 0.1653388026556029 0.3893729287090471
+167120358 0.16542866618398278 0.3896797810998754
+167219392 0.16576795476038345 0.3899596822848986
+167318426 0.16534163972784857 0.3900531065478724
+167417460 0.16516392199514562 0.390134337302807
+167516494 0.16495509866870975 0.3902772057666276
+167615528 0.16538557124635953 0.39026181371788765
+167714562 0.16494194658324104 0.3903439705620865
+167813596 0.1648743948038161 0.39055891988061114
+167912630 0.16515595011380305 0.3906895631268386
+168011664 0.16520434488905536 0.3907284585578044
+168110698 0.16478966676638848 0.3908871430751239
+168209732 0.16489721368291055 0.3911459341429673
+168308766 0.16461341251907963 0.3909860332441357
+168407800 0.16409960162160517 0.39146411969606326
+168506834 0.16455937159342962 0.3913131552015285
+168605868 0.16415125435057915 0.3911779313680168
+168704902 0.1641173147114141 0.3917490661019452
+168803936 0.16423497638098183 0.39169963673499375
+168902970 0.16427372307589364 0.39179409390563524
+169002004 0.1635879006577855 0.3920660293327261
+169101038 0.16361220001967192 0.39217273597179564
+169200072 0.16357447313259899 0.39224335816267597
+169299106 0.16364336385945988 0.39206277358866903
+169398140 0.16370941548970946 0.39231509430599104
+169497174 0.1630429212484171 0.392424727712726
+169596208 0.16338223624780115 0.39244517895947284
+169695242 0.16350450330866712 0.3926506511760147
+169794276 0.16329204384734036 0.3926625282774725
+169893310 0.1630586941785168 0.3929921720113429
+169992344 0.1627774967010655 0.392970193972787
+170091378 0.1624080895552603 0.39305210395480533
+170190412 0.16280953482688013 0.39298393288484634
+170289446 0.16260774303311407 0.3932520216485826
+170388480 0.1624867451693106 0.39348453496569796
+170487514 0.16252798276924987 0.3933945069265263
+170586548 0.16233643588774374 0.3938956449349375
+170685582 0.1621256688074348 0.39405219928648577
+170784616 0.16227742749800764 0.39390841395003895
+170883650 0.16214938018522607 0.394018681106788
+170982684 0.16173601147832445 0.39432593521620685
+171081718 0.16206959163684373 0.3942638170413298
+171180752 0.16200135286564152 0.3942509957211414
+171279786 0.16180237418244536 0.39440534667405436
+171378820 0.16169263106073603 0.3948157532059177
+171477854 0.161614062174767 0.39469545372948767
+171576888 0.16123235837380556 0.3945060420488429
+171675922 0.1610653627645669 0.3947041428162339
+171774956 0.16134934904116588 0.39471791206863843
+171873990 0.1612674134593445 0.39515928572559567
+171973024 0.16090733416998454 0.3955428571456396
+172072058 0.160929278036938 0.39521641016398573
+172171092 0.1608425820524274 0.39575658068013475
+172270126 0.16097818345903622 0.3958520811092199
+172369160 0.16044125080480529 0.39576537101000786
+172468194 0.16049775654435883 0.39610651230413085
+172567228 0.1606106536852997 0.3960806944437716
+172666262 0.16022393870198065 0.39571761939040906
+172765296 0.16041908125876161 0.39622039445620555
+172864330 0.16021690599954522 0.39626746506093014
+172963364 0.16023723840689597 0.3964075213077735
+173062398 0.16003336447429659 0.3965921659097529
+173161432 0.1600354788586057 0.39670108230791157
+173260466 0.15996302133726328 0.3967795822885067
+173359500 0.15952472224557349 0.3965196680315082
+173458534 0.15986799491064055 0.3969050411518787
+173557568 0.15961060772964786 0.39708566785339144
+173656602 0.15958335425902972 0.39681340723534986
+173755636 0.159220060526337 0.39710911827281287
+173854670 0.15950646962729823 0.39775942760289923
+173953704 0.15951457484127615 0.39764756781788085
+174052738 0.15951907480726266 0.39747241514887416
+174151772 0.15917077762369908 0.39770436234101925
+174250806 0.15907599304118494 0.39802734671944934
+174349840 0.1587953056945016 0.3980028498383468
+174448874 0.15864597948271258 0.3980343113651487
+174547908 0.1587564885049299 0.39788598681968346
+174646942 0.15866431669565467 0.3984815521500238
+174745976 0.15874852022386512 0.3984506837578455
+174845010 0.15844689317032426 0.3984172528809258
+174944044 0.1581582974411407 0.39849514679030695
+175043078 0.15820254239597423 0.3988603165977249
+175142112 0.15811969368940568 0.3990892949622818
+175241146 0.15819060834948548 0.39914276568377843
+175340180 0.15787841382582848 0.3992027377270644
+175439214 0.15792492534568595 0.39940433821605215
+175538248 0.15774252007996728 0.39921318878018064
+175637282 0.15748892682486854 0.39925652581469107
+175736316 0.15755410611541537 0.3995120906537315
+175835350 0.15743934014175173 0.3997410201699015
+175934384 0.15727759872027433 0.3996971751853763
+176033418 0.15738923438252383 0.3998599661983001
+176132452 0.1571603684789859 0.4002813809136781
+176231486 0.15729814097227507 0.40013584457215545
+176330520 0.15718706022890597 0.4003114099504555
+176429554 0.1569024802689282 0.40009849309317974
+176528588 0.15660963319492496 0.40037616191235526
+176627622 0.15664361404063568 0.40060993746763585
+176726656 0.1567643325872404 0.40064997810858805
+176825690 0.1564115574984341 0.4005446713203013
+176924724 0.15624624503459444 0.4011475696481196
+177023758 0.1561477664698685 0.40083918558676374
+177122792 0.15597398887834604 0.4012374241728038
+177221826 0.15614663288318342 0.40115598592273216
+177320860 0.15584360264442668 0.4011990407098863
+177419894 0.15593280833756726 0.4013450555610015
+177518928 0.15588324328908182 0.4014949240096797
+177617962 0.15560484063764385 0.4020532950494137
+177716996 0.15561466431029336 0.40163333553074276
+177816030 0.15540986060913586 0.40157438261515477
+177915064 0.15554827635262916 0.4021341310205585
+178014098 0.15504950506310736 0.4020606044740019
+178113132 0.1550633459315851 0.4022632085034396
+178212166 0.15498071987608866 0.4024082930232483
+178311200 0.15503914106830102 0.40235047744514524
+178410234 0.1550070103772656 0.40263365266364937
+178509268 0.154885003776947 0.40264188263508927
+178608302 0.15485122619219113 0.40307122110793936
+178707336 0.15478556422841405 0.40293238364654815
+178806370 0.15453098289676945 0.40318535924521604
+178905404 0.15475472569314255 0.4032566070663842
+179004438 0.15437851111432585 0.40315079665153375
+179103472 0.1541978411396742 0.40335751161342415
+179202506 0.15401047223870892 0.4036328340728662
+179301540 0.15413931873974027 0.40320810289708264
+179400574 0.1540141712393669 0.4037669964743399
+179499608 0.15385032367470808 0.4039356557215839
+179598642 0.1537963903582955 0.4043578087690025
+179697676 0.15382005469874743 0.40395508364130533
+179796710 0.15336987950222297 0.4041445844161215
+179895744 0.15335740218848073 0.40444247393525734
+179994778 0.1532346110279667 0.4044440367437256
+180093812 0.1532575828838954 0.40465325631911614
+180192846 0.1533259590312063 0.40447177355769115
+180291880 0.15331184824159608 0.4050182844010998
+180390914 0.15315374746862903 0.4050071680950564
+180489948 0.15302009445324813 0.4051411028825221
+180588982 0.15264605244390306 0.405769604470247
+180688016 0.15249212508276688 0.4052485638943144
+180787050 0.15285389129494817 0.4056590732173589
+180886084 0.1526020512434956 0.4055609693081067
+180985118 0.15216961010306088 0.40561691619524787
+181084152 0.15261459131722122 0.4055592161921029
+181183186 0.15247870490792334 0.4056583554942544
+181282220 0.15225144644391692 0.40615632933085893
+181381254 0.15215335868893295 0.4060291552149155
+181480288 0.1522124769033838 0.4062042423475208
+181579322 0.15189557785084803 0.4068605046919135
+181678356 0.15198981333835798 0.4066886881864404
+181777390 0.15176717777901616 0.4066741065560728
+181876424 0.15151482692764978 0.4070454083055051
+181975458 0.15155883730260175 0.4069942402694285
+182074492 0.15139799952988445 0.4072193781911274
+182173526 0.15118829334631959 0.40711889832689807
+182272560 0.15126405667091944 0.4074035887725615
+182371594 0.15118396626096964 0.4073821337678123
+182470628 0.15100707148290946 0.4073271100190484
+182569662 0.15094730712572862 0.40764636308906554
+182668696 0.15088077857021365 0.40762656052752116
+182767730 0.1508974720324109 0.4076171947455751
+182866764 0.15066143105225863 0.4078371687168602
+182965798 0.15053069989906273 0.4081203749425412
+183064832 0.15036202810783308 0.4083515152174479
+183163866 0.15015416594361822 0.40829332884003283
+183262900 0.1502620019182581 0.40827715933720615
+183361934 0.15021777511757528 0.40829998011903035
+183460968 0.15010269216242875 0.40870905425998755
+183560002 0.14972673166121647 0.40862339370446005
+183659036 0.1498724780734997 0.40919274114445114
+183758070 0.14950258486646223 0.40857440456655986
+183857104 0.14953253111852957 0.4091539669214118
+183956138 0.14946566644465348 0.40906230548562733
+184055172 0.1494369769882999 0.40940475429702344
+184154206 0.14912067284876424 0.4096407990382345
+184253240 0.14891037067783505 0.40953918705898784
+184352274 0.1491922745426519 0.40977510218680435
+184451308 0.14911194438620057 0.409563972187374
+184550342 0.14889307740500826 0.40994278032747083
+184649376 0.14898639253377097 0.4099480636233435
+184748410 0.14862088996255313 0.4099543410844779
+184847444 0.14860780984002464 0.4101730813057124
+184946478 0.14863239487042285 0.41040236909617195
+185045512 0.14842733721688062 0.4101988058999021
+185144546 0.14820032920960524 0.41073186425715663
+185243580 0.14824057779476524 0.41055425614311436
+185342614 0.14823048758468294 0.41067890562416925
+185441648 0.14803802886770046 0.41086785501213546
+185540682 0.1478751389086092 0.4110627835725259
+185639716 0.14776639340474348 0.41061208000641325
+185738750 0.14773454834939623 0.41122093492530565
+185837784 0.14725947900278716 0.4111044026879816
+185936818 0.14734686789397175 0.41154131017581086
+186035852 0.1474509701056568 0.4114372418272398
+186134886 0.14739757405703774 0.41174345644096944
+186233920 0.1471786708823876 0.41209321026015744
+186332954 0.14687578181411184 0.41171624412484537
+186431988 0.1469161880894998 0.41198363328910814
+186531022 0.14701848819573138 0.4119704670720097
+186630056 0.14649994461461135 0.4118841638391588
+186729090 0.14650253694719556 0.4124311528669256
+186828124 0.14686981213656064 0.41245505292631485
+186927158 0.14647633957329806 0.4123347355196372
+187026192 0.14621274209970178 0.4130604882172275
+187125226 0.14651288929000975 0.4125245481681104
+187224260 0.14602538257292194 0.4126928743844943
+187323294 0.14590475258532676 0.4129013167592477
+187422328 0.14614959786819776 0.41289660484229707
+187521362 0.14579142636057058 0.4132991468639423
+187620396 0.14565613620271844 0.413301612958762
+187719430 0.1454138999863333 0.4133023099205736
+187818464 0.14527367045797918 0.4134536290706285
+187917498 0.1452196700498205 0.4134451878422925
+188016532 0.1453192734165691 0.41361670259521355
+188115566 0.14513159545378165 0.4138899515127764
+188214600 0.14491665781355206 0.41405193043318556
+188313634 0.1447423151386024 0.41396701529047647
+188412668 0.14488205184540265 0.41419558081665664
+188511702 0.14473750372564806 0.4142743319634537
+188610736 0.1445690678926074 0.4144731110049421
+188709770 0.14424227707430712 0.41464448980556434
+188808804 0.14437036270786302 0.41444669846482846
+188907838 0.14426682899516335 0.41479280906220184
+189006872 0.14392145846009202 0.414877831544981
+189105906 0.14416295704784446 0.41536573731582205
+189204940 0.14406073399616198 0.41503808544854054
+189303974 0.14407135230050935 0.4150201473398903
+189403008 0.14374934011568882 0.41530593104918
+189502042 0.14378057859446772 0.41554059615761363
+189601076 0.1434232675249787 0.41578800807739735
+189700110 0.14322046125145538 0.4161097297786191
+189799144 0.14317034724400923 0.4157392820206712
+189898178 0.14329778806901175 0.41579499639073103
+189997212 0.14314807095633814 0.4159014079236744
+190096246 0.14332906824777092 0.41654901555068463
+190195280 0.1433878617857742 0.416267884742786
+190294314 0.14329583882915808 0.4164730417911839
+190393348 0.14266709697089652 0.4166148372180548
+190492382 0.1429109276757009 0.41671481976147595
+190591416 0.1428526680176257 0.4170246486337383
+190690450 0.14289421421138965 0.417088815154661
+190789484 0.14257799512238345 0.41689284378833064
+190888518 0.14238209769056534 0.41710921195244627
+190987552 0.14197491079768831 0.4173467603524629
+191086586 0.14234511506511535 0.4174517955594586
+191185620 0.14217653071104966 0.4174820697883232
+191284654 0.14188955332273315 0.4175536076962317
+191383688 0.1419555376898774 0.4179708934388642
+191482722 0.1414106108906664 0.4181758668545236
+191581756 0.1416972644512109 0.41819102603171204
+191680790 0.1416797872234208 0.41822500994454204
+191779824 0.14165717305146877 0.4181104091067723
+191878858 0.14114740932883044 0.41842041525545914
+191977892 0.1413512601918031 0.4183889771032038
+192076926 0.14103647266283215 0.4186500596736239
+192175960 0.14134078591525245 0.41878416454865397
+192274994 0.14099234668840047 0.4188423916966435
+192374028 0.14080226929422227 0.41904803830117615
+192473062 0.1403500815148266 0.41893914551056566
+192572096 0.14046622966397732 0.4191877126685954
+192671130 0.14034479901682154 0.41930085703572284
+192770164 0.14035198229357002 0.41940506077106043
+192869198 0.14023976933340127 0.4194140591002685
+192968232 0.1401299496943792 0.41978082767414643
+193067266 0.14023733700799235 0.41960633405182435
+193166300 0.14008220360544335 0.4200949076499998
+193265334 0.1398020148178829 0.41994896884519406
+193364368 0.13964250002011444 0.4197913656703781
+193463402 0.13958106686812916 0.42004988286530903
+193562436 0.1396810336569519 0.42005942573029037
+193661470 0.13952891828369465 0.4204778018572249
+193760504 0.13913661567258498 0.4206920935659629
+193859538 0.1391445462897334 0.4207775666305571
+193958572 0.13920996449103004 0.42097994164598124
+194057606 0.13889069072167962 0.4209270266476613
+194156640 0.13885360998786864 0.4208536257729164
+194255674 0.13881901651529882 0.4209962697709639
+194354708 0.1387432506228338 0.42128372914557966
+194453742 0.13876434457530604 0.42140451088088465
+194552776 0.13838555858782556 0.42163459295250355
+194651810 0.13855114187698245 0.42184778097919085
+194750844 0.1383537312946368 0.42142786260617215
+194849878 0.13813362286752096 0.422019647183455
+194948912 0.13832890658001576 0.4220470034916318
+195047946 0.1378440376872905 0.4222298514565506
+195146980 0.13821449656210338 0.42250567407234924
+195246014 0.13770105337411712 0.42218601391437005
+195345048 0.13746330220014724 0.42253754797770376
+195444082 0.13754326621217466 0.4226062244194654
+195543116 0.13701850069327198 0.42268461506287697
+195642150 0.13707280189301257 0.42284916949538986
+195741184 0.13720267192401428 0.4228278897129342
+195840218 0.13700227555262634 0.42316324673988864
+195939252 0.1366890820430805 0.42322122085346986
+196038286 0.13678962774888376 0.4229744098835971
+196137320 0.136847565455407 0.4232675324310744
+196236354 0.13635965045133608 0.423376982517945
+196335388 0.1363107228991007 0.42332000953267096
+196434422 0.1364958457299513 0.4236313122027245
+196533456 0.13615107070926508 0.4235199398528883
+196632490 0.13644252988397673 0.4238729676509986
+196731524 0.13578977857419564 0.42391868311028674
+196830558 0.13600123990048088 0.42424690904770695
+196929592 0.1359448326492317 0.4245124600177534
+197028626 0.13584274345620373 0.4241110760281926
+197127660 0.13576687347393032 0.42451532812312437
+197226694 0.13584346960067478 0.42473299060593095
+197325728 0.13555477743288394 0.42469537348377434
+197424762 0.1351720205353612 0.4248937672966619
+197523796 0.13525959558407852 0.4244790617883551
+197622830 0.13480759339550955 0.42503058932022453
+197721864 0.13491211288226806 0.4254020763181194
+197820898 0.13514747695103216 0.425082649540459
+197919932 0.13429435404826268 0.4249949252620509
+198018966 0.13444819610660075 0.42540795105109075
+198118000 0.13452082927788098 0.42502094571428517
+198217034 0.13463178825338026 0.42556531548432597
+198316068 0.13434766046365887 0.4258497435592025
+198415102 0.1341861099898892 0.4258130369348596
+198514136 0.13427983281291614 0.42624899148290096
+198613170 0.13378745759326463 0.42616571003395076
+198712204 0.13375709555333262 0.42645220136615886
+198811238 0.13396739644746672 0.4264053561527298
+198910272 0.1338214808453904 0.4262379315614749
+199009306 0.13378397864002586 0.42639871223018894
+199108340 0.13324210976724934 0.4266715343414341
+199207374 0.13329882033719423 0.4270209229249888
+199306408 0.1331405857918023 0.42707171730333227
+199405442 0.13330614185491263 0.4270375400226716
+199504476 0.13296228745501354 0.42702459542447685
+199603510 0.13291235826879996 0.42732604896291215
+199702544 0.13288592447429193 0.4275049125961961
+199801578 0.1329774288930896 0.42777978096649205
+199900612 0.13246782967224358 0.42743550730120183
+199999646 0.13213678251106709 0.42752343587533653
diff --git a/test/data/t130-2.s1p b/test/data/t130-2.s1p
new file mode 100644
index 00000000..17f63b60
--- /dev/null
+++ b/test/data/t130-2.s1p
@@ -0,0 +1,2021 @@
+# HZ S RI R 50
+50000 -1.0065668550744071 -5.21847118919505e-05
+149034 -1.0066705898801684 0.00017913690339119525
+248068 -1.0067465358167211 0.0003192657007548011
+347102 -1.0068191718433088 0.0004281496189623205
+446136 -1.0065324277806702 0.0004370824506697228
+545170 -1.0069024978595014 0.0008467657185666356
+644204 -1.0068559537998123 0.0009661336630044716
+743238 -1.007036169057217 0.001320173164345892
+842272 -1.0069437110855814 0.001501879660377009
+941306 -1.007053517634155 0.0016942492259571075
+1040340 -1.0070309339917745 0.0017267723292485634
+1139374 -1.0069831415745816 0.0020713995657102788
+1238408 -1.0070382788759202 0.0023568142302916803
+1337442 -1.0071791182583378 0.0024601009047245208
+1436476 -1.007185770073967 0.002744092764231976
+1535510 -1.0069639958847563 0.002886615098440211
+1634544 -1.007135599340358 0.002990373375181818
+1733578 -1.0071906431521296 0.003402191459515937
+1832612 -1.0071004200750917 0.00366307484008775
+1931646 -1.0072864596349638 0.003694829357133327
+2030680 -1.0072223321586296 0.004018313544478848
+2129714 -1.0072476702974622 0.004181225553262037
+2228748 -1.0073014738030124 0.00436206771679868
+2327782 -1.0073956143358505 0.004712196456083312
+2426816 -1.0074773354416429 0.004796858892226288
+2525850 -1.007523922908287 0.004977309787127393
+2624884 -1.0073320727603272 0.005289969206601126
+2723918 -1.0075861590832595 0.005600307105965544
+2822952 -1.0075536972844898 0.005757540360779306
+2921986 -1.0075319323040721 0.005841182870720604
+3021020 -1.0075301695752916 0.00615584081205365
+3120054 -1.0076614316019583 0.006357823958579462
+3219088 -1.0075064123786772 0.006607611842001179
+3318122 -1.00759719855141 0.006718346859290074
+3417156 -1.0076252294616816 0.007067202103357582
+3516190 -1.007697149437979 0.007209155489619754
+3615224 -1.0077372050267246 0.007408543844291445
+3714258 -1.0076399518558745 0.007536613455660791
+3813292 -1.007933018385103 0.007793450203543804
+3912326 -1.0077692547524355 0.008090500358417508
+4011360 -1.0076862888186782 0.008286834825717538
+4110394 -1.0078186581085713 0.008617645073253272
+4209428 -1.0079253064512799 0.008712865187057491
+4308462 -1.0078190944298087 0.00872362453242541
+4407496 -1.0076428422207007 0.009089486525820843
+4506530 -1.0078579311168498 0.009318760688373619
+4605564 -1.0078505850757924 0.00959557726165594
+4704598 -1.0077232612867424 0.009787532629874406
+4803632 -1.0077683793013927 0.010072821910307497
+4902666 -1.0076831907816324 0.010204939419592123
+5001700 -1.007797710488794 0.010336209583120481
+5100734 -1.0077821051745246 0.010634189743553544
+5199768 -1.0078106568535528 0.010797871916087925
+5298802 -1.007827494396194 0.011120391649640756
+5397836 -1.0079662603417885 0.011411431068234246
+5496870 -1.007914094443245 0.011718016164078313
+5595904 -1.0078080176383553 0.011871942034229092
+5694938 -1.0078228668729552 0.011966868648793948
+5793972 -1.0078448891897527 0.012288029602743493
+5893006 -1.0078685924438375 0.01244692769366668
+5992040 -1.0078652611573387 0.012618656633310871
+6091074 -1.0078618827597459 0.01276901748871633
+6190108 -1.007938048682702 0.01304661643845366
+6289142 -1.0080078497270146 0.013275910048124064
+6388176 -1.0079618275070505 0.013435238886366984
+6487210 -1.0080564694058074 0.01364741032011286
+6586244 -1.0079015336342434 0.013962089832800891
+6685278 -1.0079012040116442 0.014298233307640656
+6784312 -1.007954703764716 0.014484818289696241
+6883346 -1.007858891786778 0.014732617260100188
+6982380 -1.0078787312938002 0.01479935164720461
+7081414 -1.0083222031663546 0.015051355139813096
+7180448 -1.0081731260695077 0.015260806595802126
+7279482 -1.0081236289666462 0.015412763718780096
+7378516 -1.008075175007506 0.015700521531907506
+7477550 -1.0082127817571696 0.015873862796949266
+7576584 -1.0080716899025228 0.016050158756237325
+7675618 -1.0081648973249926 0.016420726993847976
+7774652 -1.0080955966767902 0.016462902053208068
+7873686 -1.0081713178681402 0.01673079571859329
+7972720 -1.0081637064509086 0.017085584376372365
+8071754 -1.0082919707959186 0.017211233773304295
+8170788 -1.0081250030868039 0.017543795885015395
+8269822 -1.0082557857769956 0.017751223482696973
+8368856 -1.0080619272784868 0.017887868328909203
+8467890 -1.0083813896467162 0.01815272178330156
+8566924 -1.0084517834565367 0.018333286442355195
+8665958 -1.0083591230443298 0.01840502187587741
+8764992 -1.0083095067739047 0.018908781947598576
+8864026 -1.0084186316278085 0.01889009798046868
+8963060 -1.0083581236547603 0.019221711799568383
+9062094 -1.008296573212954 0.019437364874121833
+9161128 -1.0083664249697724 0.01971172248933552
+9260162 -1.0083889663908299 0.01985349204609129
+9359196 -1.008340262164755 0.020186124248718695
+9458230 -1.0083838085824433 0.020373329260504242
+9557264 -1.0085627452308192 0.020468012629481967
+9656298 -1.008679410935151 0.020685827957425594
+9755332 -1.0087201356318904 0.020907754804674637
+9854366 -1.0087315409591737 0.021153337616485232
+9953400 -1.0088308958863672 0.02136541387193382
+10052434 -1.0081389131511698 0.02200816701644593
+10151468 -1.0081402248625324 0.021717148492085933
+10250502 -1.008112948345486 0.02223713288588422
+10349536 -1.008267124497113 0.022254911328401766
+10448570 -1.0081236028318077 0.022528434338273572
+10547604 -1.008132896195514 0.022715087996868503
+10646638 -1.0082654509893578 0.022869149281963916
+10745672 -1.0082460576327708 0.023165153149516064
+10844706 -1.0082757045740525 0.023407588536500566
+10943740 -1.0083054207949274 0.0236616266831292
+11042774 -1.0082398154084398 0.02378988402164129
+11141808 -1.0082845773945328 0.024008355532086297
+11240842 -1.008267221862981 0.02428883071289177
+11339876 -1.0081647259062492 0.024545619916611465
+11438910 -1.0082780952091537 0.02462229541192619
+11537944 -1.0084517876921513 0.024930107946539305
+11636978 -1.0083269555161225 0.02517242782358692
+11736012 -1.0085108689275415 0.025284771746371747
+11835046 -1.0083017017001183 0.025548866707084367
+11934080 -1.008402681491382 0.025813650572479693
+12033114 -1.0084164335594552 0.02595659507658974
+12132148 -1.0084744755087531 0.026312485845287382
+12231182 -1.008531459049904 0.026529716579307097
+12330216 -1.0083732087267239 0.0267615400959567
+12429250 -1.0085637824145424 0.02701339943729648
+12528284 -1.008619142089878 0.027096094651414982
+12627318 -1.008717856846068 0.027189807632115225
+12726352 -1.0087827240285536 0.027559895562796886
+12825386 -1.0087113391498679 0.027663172988449017
+12924420 -1.0088180471546837 0.027859850325995476
+13023454 -1.0085651995246425 0.028188197270252353
+13122488 -1.0088117974133186 0.028416988429493424
+13221522 -1.0089225727063047 0.028680860572933575
+13320556 -1.008671487046823 0.028861967221281957
+13419590 -1.0087482308793885 0.029068813451958907
+13518624 -1.009072580724447 0.029335023213339383
+13617658 -1.0086986465597834 0.02948797334677913
+13716692 -1.0090170403792742 0.029671853518641204
+13815726 -1.0090631696698684 0.029908946139710436
+13914760 -1.0088510938975306 0.030255754690341297
+14013794 -1.009111252908356 0.030519674696991825
+14112828 -1.0091086329847105 0.03059557743434554
+14211862 -1.008890192033299 0.030791669453362557
+14310896 -1.0089362387919036 0.031081163467258543
+14409930 -1.0088316587103827 0.03123314887726794
+14508964 -1.0089935164640318 0.0314020235065027
+14607998 -1.008817908078855 0.03170815994865987
+14707032 -1.009130357124469 0.031875471886579416
+14806066 -1.0091231469890143 0.03217211557732348
+14905100 -1.009122028412549 0.0321830318480143
+15004134 -1.0091017320695557 0.03240771339681421
+15103168 -1.009339753856298 0.03270843953477111
+15202202 -1.0092553484953675 0.03309274277758294
+15301236 -1.0091528484235779 0.033312220525098406
+15400270 -1.009264319323459 0.03358911619014232
+15499304 -1.0092479015788598 0.03356412645401185
+15598338 -1.0093483758169175 0.033985565512229865
+15697372 -1.009304593802786 0.03411471462010791
+15796406 -1.0092306669179176 0.03445752912270703
+15895440 -1.009412942393386 0.034546159366715117
+15994474 -1.0094456197427961 0.03468704565771518
+16093508 -1.0093744958920328 0.03517679309085436
+16192542 -1.0094766024889872 0.03515497600361746
+16291576 -1.009467604005381 0.035544836453139356
+16390610 -1.0095052844573276 0.035473200987307794
+16489644 -1.0096589214661058 0.035779697733068404
+16588678 -1.0095954725219631 0.036002555633793565
+16687712 -1.009618414683904 0.03629727777266808
+16786746 -1.0096844533604348 0.03652993992144163
+16885780 -1.0095656399204127 0.036658033515767255
+16984814 -1.0097865260920134 0.03711185949531441
+17083848 -1.009832717564232 0.03729820545798012
+17182882 -1.0097511618289101 0.037489021219853084
+17281916 -1.009781169929733 0.03766867622491327
+17380950 -1.0098545455525112 0.037728408472553134
+17479984 -1.0100994021228917 0.038087505942478256
+17579018 -1.0100430581473863 0.03836851428285147
+17678052 -1.0100436252034761 0.038607038456801565
+17777086 -1.0102719054256828 0.03873502670741921
+17876120 -1.0100096390820061 0.03902076229333377
+17975154 -1.0100938285637369 0.03912919646944673
+18074188 -1.0100082129512096 0.03931347460587307
+18173222 -1.0100678611169942 0.03971971361788405
+18272256 -1.0099524069243637 0.0399062801616269
+18371290 -1.0101849338675482 0.04003642022035899
+18470324 -1.0100322564953192 0.04024798068316691
+18569358 -1.010416581877768 0.04056118532018202
+18668392 -1.0102096050791693 0.0406220242577916
+18767426 -1.0102554939109818 0.04076615355923924
+18866460 -1.0099341725825761 0.04103330487959658
+18965494 -1.0101892270432866 0.04150921619627379
+19064528 -1.0102843417208962 0.04168646844065196
+19163562 -1.010571034561453 0.04196199874277709
+19262596 -1.0104440799839065 0.041904988066167076
+19361630 -1.0102715694372282 0.04215966600342724
+19460664 -1.0102332359468842 0.04252326139999206
+19559698 -1.0104447188952335 0.04266772538757455
+19658732 -1.0103510114842733 0.042771671961896435
+19757766 -1.0104090458910733 0.043138453873060645
+19856800 -1.010643757115823 0.04336899421884947
+19955834 -1.0105138449283881 0.043573187997875434
+20054868 -1.0103837340572805 0.04372319066784308
+20153902 -1.0104904729848447 0.044080820680296826
+20252936 -1.0105350690222235 0.044156211815226644
+20351970 -1.0105148115199216 0.044397104789507244
+20451004 -1.0105361533029278 0.04450669113203455
+20550038 -1.010657648061225 0.04488466170388793
+20649072 -1.0105934223756452 0.04506149230632015
+20748106 -1.0107947266740125 0.0453515212134097
+20847140 -1.010837321722036 0.045547140941148166
+20946174 -1.0106158386401105 0.04588453548523358
+21045208 -1.0108134138483629 0.04608792995704593
+21144242 -1.0107904098499554 0.04632889630741858
+21243276 -1.0108601267876591 0.04649938710719939
+21342310 -1.0110501503233889 0.04660754869230478
+21441344 -1.0107337954570044 0.04689402758498151
+21540378 -1.0109338547062956 0.047157578917083574
+21639412 -1.0109621326275195 0.04733380201872117
+21738446 -1.0111087321779086 0.04746654789272162
+21837480 -1.010841945101279 0.04794642245700833
+21936514 -1.011155358028393 0.048006843163150695
+22035548 -1.0112255385984918 0.04843694589757936
+22134582 -1.011118844804392 0.048275944935917706
+22233616 -1.0111818127153873 0.0487140143919105
+22332650 -1.0111063435470238 0.048820097838159424
+22431684 -1.0112439504945552 0.048943086468810404
+22530718 -1.011489571650751 0.049363338717948346
+22629752 -1.011336698105104 0.049545413224020035
+22728786 -1.011382928247136 0.04967914997219731
+22827820 -1.0112248877592065 0.04998080872392873
+22926854 -1.0111360458106038 0.05021665513274986
+23025888 -1.0112977403773078 0.05036011981205628
+23124922 -1.0113964527508035 0.050473155013916256
+23223956 -1.0113799846067 0.05092963354432354
+23322990 -1.011382288267127 0.050829532051334385
+23422024 -1.011419079934983 0.051367780302022166
+23521058 -1.0114728607617254 0.051515852536570275
+23620092 -1.011611425527169 0.051608183962143735
+23719126 -1.0117552162685646 0.052084355900067075
+23818160 -1.0115603242950513 0.052061442596535466
+23917194 -1.0116239784895857 0.05228337749505382
+24016228 -1.0117591889568605 0.05269649704469543
+24115262 -1.0116687596093052 0.052835072571836006
+24214296 -1.0115663335809453 0.052902641299149206
+24313330 -1.0119651247190224 0.053137195638217394
+24412364 -1.0115664767799346 0.05362928817558876
+24511398 -1.0118978954007005 0.053702637286176524
+24610432 -1.011888118903424 0.053952478071441934
+24709466 -1.0120028704104798 0.05412257520028623
+24808500 -1.0120945873353189 0.05426588144079387
+24907534 -1.0118941155398116 0.05468316045787422
+25006568 -1.012153908905683 0.05475312999175587
+25105602 -1.0120579762433697 0.05507498361403067
+25204636 -1.012316742152849 0.05534262535264794
+25303670 -1.0120717990032502 0.055405875676440904
+25402704 -1.0121472714849193 0.05575095472138598
+25501738 -1.0122362471802078 0.05591339308265271
+25600772 -1.0123328513830039 0.05604647148705389
+25699806 -1.0126966622000646 0.056345253398211684
+25798840 -1.0125164766211463 0.056641163065207605
+25897874 -1.0122648595545736 0.05668413215930032
+25996908 -1.0125622489735522 0.057001847000259454
+26095942 -1.012528240080318 0.0571259914925498
+26194976 -1.0126437090857334 0.057437673400313104
+26294010 -1.0124865496506514 0.057572004216415444
+26393044 -1.0126522693165936 0.057998871503085685
+26492078 -1.0127305173045218 0.05828996734080325
+26591112 -1.0126292032552513 0.058295814031812845
+26690146 -1.0127755549196966 0.058576757087304504
+26789180 -1.0127233223858079 0.05887018249521838
+26888214 -1.0128067438100974 0.05892811921654538
+26987248 -1.0130060042278337 0.059125394626381896
+27086282 -1.0130414969451895 0.05959282139591294
+27185316 -1.0127318517171333 0.05971808208436928
+27284350 -1.0131641738781818 0.059740808786746924
+27383384 -1.0129143619791765 0.06010821657469152
+27482418 -1.0128827475240152 0.06018146039646091
+27581452 -1.0131845733567715 0.06039117873326944
+27680486 -1.0130718808424581 0.060599859111779515
+27779520 -1.0131361985469696 0.06085146306122865
+27878554 -1.013150876216248 0.061164185846562004
+27977588 -1.0133899887713738 0.06155594988766473
+28076622 -1.0134559558501972 0.0616128443540888
+28175656 -1.0131899534315458 0.06183644189695098
+28274690 -1.0132480784113451 0.06185244210447203
+28373724 -1.0132831688752495 0.062446396824215294
+28472758 -1.013363323466993 0.06253042388040428
+28571792 -1.013745161198183 0.06271358266298296
+28670826 -1.0135406501138033 0.06290020391844912
+28769860 -1.013714616671308 0.06290606905855363
+28868894 -1.013641381692414 0.06353859818276385
+28967928 -1.0135934059495202 0.06342098472705454
+29066962 -1.0137725333399978 0.0637691088521619
+29165996 -1.0138185503417132 0.06399969757375064
+29265030 -1.0139265430028956 0.0641476721253355
+29364064 -1.013978225153033 0.06425793855668017
+29463098 -1.0138815534130925 0.06473764250565098
+29562132 -1.0140766403584047 0.06485049122435695
+29661166 -1.013811417309569 0.06485475209055078
+29760200 -1.0139827158143848 0.06514219367935478
+29859234 -1.014227797060686 0.06553380254418989
+29958268 -1.0139265857371957 0.0657080122379182
+30057302 -1.0140314199661422 0.0658569085726844
+30156336 -1.0138191301720074 0.06595474618586904
+30255370 -1.0143522211159748 0.06629867287007102
+30354404 -1.0142464456447104 0.06653187009190566
+30453438 -1.0141905139450549 0.06676915528675635
+30552472 -1.0143481709870827 0.06682965234430775
+30651506 -1.0142750684215895 0.06696730761496876
+30750540 -1.014127936088108 0.06732394899655407
+30849574 -1.0143316613652285 0.06742149804466067
+30948608 -1.0141863879418285 0.06781636677487189
+31047642 -1.0145250901762095 0.067796217274548
+31146676 -1.0144625441580162 0.06821199469529168
+31245710 -1.0144773103355968 0.06826119128924635
+31344744 -1.014600472373469 0.06856598671969369
+31443778 -1.0145736542649815 0.06899584542701453
+31542812 -1.014570319236602 0.06909810784234623
+31641846 -1.0145482208552141 0.06959372532264725
+31740880 -1.014756119347479 0.0695676613544406
+31839914 -1.0146631192385633 0.06963213999633314
+31938948 -1.014805567671227 0.06976704141413602
+32037982 -1.0148545116552157 0.07029236163416426
+32137016 -1.0148351212131692 0.07045523234172604
+32236050 -1.0148507010224608 0.07066846658698882
+32335084 -1.0150689490389044 0.070993574840795
+32434118 -1.0149694745802122 0.07114295596686762
+32533152 -1.0150846267700895 0.07126010513543186
+32632186 -1.015236469715417 0.07151918367556148
+32731220 -1.0153491222523998 0.07169772720031602
+32830254 -1.015233680931848 0.07215456103093591
+32929288 -1.0150832176866216 0.07224106988232823
+33028322 -1.015221602701122 0.07220917359392602
+33127356 -1.015090440889947 0.07252584737742426
+33226390 -1.0154832260610946 0.07275811316859929
+33325424 -1.0154032769879289 0.07316862737976156
+33424458 -1.0153730486055268 0.07336056298760905
+33523492 -1.0152642577979867 0.07339889848757773
+33622526 -1.0157310479547055 0.07350799965530298
+33721560 -1.0155976671037594 0.07390647987566709
+33820594 -1.0154980886086766 0.07403778821399411
+33919628 -1.015656456768307 0.07419774303754866
+34018662 -1.0157778309632515 0.07451572596327673
+34117696 -1.0158929347320003 0.0747317122270796
+34216730 -1.0157698741239087 0.07497891289722124
+34315764 -1.015834095517183 0.0752003862063351
+34414798 -1.0158645761429141 0.07533464005654283
+34513832 -1.0159043143242927 0.07552501087064498
+34612866 -1.0159551873312054 0.07611279289529553
+34711900 -1.0157803780157946 0.07593457833418112
+34810934 -1.0159704051225225 0.07621750738736113
+34909968 -1.0160852411563726 0.07648549909389506
+35009002 -1.016248522136367 0.07671667232301796
+35108036 -1.0160913918972863 0.07667026344222566
+35207070 -1.0162896882244083 0.07710351653331768
+35306104 -1.0162488049478446 0.07734185217458962
+35405138 -1.016346585558452 0.07754057443037556
+35504172 -1.0163140589201616 0.07789983641151942
+35603206 -1.0163670916893202 0.07781945475183343
+35702240 -1.0163257993987174 0.07814946645059383
+35801274 -1.0166140960322692 0.078251395485
+35900308 -1.0164126340174593 0.07852599384112918
+35999342 -1.0165977769710037 0.07892832500028728
+36098376 -1.0165159091510465 0.07905421273340296
+36197410 -1.0168094242937231 0.07914399389302831
+36296444 -1.0167441174556202 0.07951243216066824
+36395478 -1.0167748788064939 0.07957313584262111
+36494512 -1.0168451906255045 0.08007416818188791
+36593546 -1.0167763187256371 0.07997586407398191
+36692580 -1.0170578641857562 0.08018401769224731
+36791614 -1.0169221276667593 0.08059162749627137
+36890648 -1.0168542663607916 0.08074626486557389
+36989682 -1.0168694172746258 0.08092076408934996
+37088716 -1.0168911375385854 0.0811819286698096
+37187750 -1.0171655822039385 0.08157515730624666
+37286784 -1.0170973575715399 0.081562274814948
+37385818 -1.0173549716754744 0.08192235925995278
+37484852 -1.0174473699306905 0.08222891834600915
+37583886 -1.0172762662050425 0.08225050865036942
+37682920 -1.0174312276244448 0.08238682389217342
+37781954 -1.0174734640677172 0.08279413122908147
+37880988 -1.0176393474502514 0.0830861563643806
+37980022 -1.0176599235655874 0.08297350458334107
+38079056 -1.0175043449120054 0.08310781236745787
+38178090 -1.017738453470431 0.08349037883242148
+38277124 -1.017639844320575 0.08383070734290714
+38376158 -1.017852154264567 0.08415208501110112
+38475192 -1.0179802074839304 0.0842607482632615
+38574226 -1.0178648662286593 0.08435814824169237
+38673260 -1.0180083300451666 0.08440464525157694
+38772294 -1.0181196578168796 0.0846171860821836
+38871328 -1.0180709746713257 0.08519684214208272
+38970362 -1.0181850386743079 0.08520640473655054
+39069396 -1.0181308838431924 0.08542864112648149
+39168430 -1.0180846019372802 0.08570369114895399
+39267464 -1.0181413835881983 0.08594325540501314
+39366498 -1.0182116594022144 0.08610057897749795
+39465532 -1.018350183305776 0.08645381569740564
+39564566 -1.0185708891444651 0.08633377251804424
+39663600 -1.0185235990398644 0.08666430830742852
+39762634 -1.0184515463500854 0.08704253418372174
+39861668 -1.0187802297763502 0.08717694180970048
+39960702 -1.018577186906943 0.08741524601281077
+40059736 -1.0186661700335844 0.08746909320971138
+40158770 -1.0186616687173575 0.08800334243923652
+40257804 -1.0186260421614222 0.08799887894157657
+40356838 -1.01882978453537 0.08834137261155053
+40455872 -1.0189607538267038 0.08853323864552422
+40554906 -1.0187063944367736 0.0886664373132278
+40653940 -1.0189534886035694 0.088752622899483
+40752974 -1.0188040211148548 0.08915634250562088
+40852008 -1.01919603808637 0.08924185183466303
+40951042 -1.0190453854343344 0.08943563527046115
+41050076 -1.0191268996733165 0.08991703049564684
+41149110 -1.0193006059920888 0.09003151633236749
+41248144 -1.0190998585735935 0.09017531559930157
+41347178 -1.0192266847002196 0.09017291261194324
+41446212 -1.0193502548125846 0.09037943993075123
+41545246 -1.0194835589279883 0.09098214789715944
+41644280 -1.019288046926667 0.09111099702613265
+41743314 -1.0195943644883017 0.091341066353624
+41842348 -1.0195549704218985 0.09116281596345098
+41941382 -1.0195887447920724 0.09173546310871904
+42040416 -1.0196007315352693 0.09188815155777826
+42139450 -1.0194444882737177 0.09195156695822654
+42238484 -1.0196721101442745 0.09244043097014322
+42337518 -1.0196045688484705 0.0923931197969422
+42436552 -1.0195713787801546 0.09272753674688654
+42535586 -1.0199060671081128 0.09274726417854937
+42634620 -1.019628673254141 0.09313629596636619
+42733654 -1.0201188564266586 0.09330988161453219
+42832688 -1.0200872982869762 0.09343935848000542
+42931722 -1.0202500582908587 0.09389419548156386
+43030756 -1.0203637546429727 0.09387158664518115
+43129790 -1.0199071876446213 0.09409770877025322
+43228824 -1.0200784078922893 0.09448296742680021
+43327858 -1.0201905239570135 0.09461020804693071
+43426892 -1.0203625217453545 0.0948635366588901
+43525926 -1.0203364375635937 0.09497780663303346
+43624960 -1.0202808867643798 0.0950706904037349
+43723994 -1.0206248653169057 0.09541207354505231
+43823028 -1.0203722346125168 0.09541966887532495
+43922062 -1.0206239600692615 0.09587847965767425
+44021096 -1.0204211558626957 0.0961685252099509
+44120130 -1.0208605677122031 0.096469443798029
+44219164 -1.0207021127814433 0.09619263416926856
+44318198 -1.02095962254222 0.09639341205205429
+44417232 -1.0209960975303354 0.09677463594844402
+44516266 -1.0208748267584957 0.09680076445393268
+44615300 -1.0211276268910265 0.09722681316627639
+44714334 -1.0212710466472057 0.09732385710551206
+44813368 -1.0211119717476973 0.09768777223755436
+44912402 -1.0213131497163679 0.09781404840945579
+45011436 -1.0211298865941478 0.09798778726694075
+45110470 -1.0212158718123137 0.09818112922376201
+45209504 -1.0215265004269989 0.09848206342468435
+45308538 -1.0215702270376725 0.09874210321408053
+45407572 -1.0218130388968782 0.09892307686210075
+45506606 -1.0214578636964406 0.09924156443594703
+45605640 -1.021801310301428 0.09944591233412965
+45704674 -1.0217054735507307 0.09952083443001297
+45803708 -1.0218276683063388 0.09990528419460414
+45902742 -1.021815523370801 0.10004378039367377
+46001776 -1.0218558782372107 0.10029178640827936
+46100810 -1.022088722546954 0.10041093582798555
+46199844 -1.0219967281118596 0.10059757480066382
+46298878 -1.0223081615571181 0.10083905475596293
+46397912 -1.0218408955833629 0.10091496190808717
+46496946 -1.022188808928597 0.1010088716367956
+46595980 -1.0223541011672874 0.101398196432296
+46695014 -1.0221411363635449 0.101459383565168
+46794048 -1.0223855473099082 0.1018710753744255
+46893082 -1.0224151102898198 0.1023069736732318
+46992116 -1.0223059097250466 0.10225338008460556
+47091150 -1.0223150553655043 0.10273534061955733
+47190184 -1.0227468320800894 0.10271007326785189
+47289218 -1.0226173113573758 0.10283089695574522
+47388252 -1.0224597700708626 0.10301677804643226
+47487286 -1.0226683026368395 0.10340559636047028
+47586320 -1.022874942514845 0.1034153479074488
+47685354 -1.0226747525931328 0.10370078121115489
+47784388 -1.0230872256476038 0.10413611717251464
+47883422 -1.02300242534672 0.10427846592516424
+47982456 -1.0233383171244952 0.10415010645473041
+48081490 -1.0227836692278442 0.10450244281748101
+48180524 -1.0233014598241725 0.10460742188831135
+48279558 -1.0231720019254245 0.10481560186874463
+48378592 -1.0233343937269905 0.10483883744057076
+48477626 -1.023443420696266 0.10549919440185324
+48576660 -1.0235106516675312 0.10519347738789221
+48675694 -1.0233834509443078 0.10537420376884099
+48774728 -1.0233351509477828 0.1060052211640477
+48873762 -1.0234550802173408 0.10622744512398499
+48972796 -1.0236245471918624 0.10652034078248498
+49071830 -1.0237127810551152 0.10648443452735051
+49170864 -1.0237574345276685 0.10689915173002272
+49269898 -1.0237636416097875 0.10702051474428032
+49368932 -1.0234509489240942 0.10736113741974666
+49467966 -1.0233467056733714 0.10744652307802792
+49567000 -1.023607770512492 0.10766593928184964
+49666034 -1.0242118260141837 0.10764637080161052
+49765068 -1.024127761419358 0.10792480081575626
+49864102 -1.0239449522018975 0.10833641031424661
+49963136 -1.0241364370958277 0.10857785579220959
+50062170 -1.0227280666599583 0.10865451848979049
+50161204 -1.022865287547065 0.1086930553696125
+50260238 -1.0228326818873983 0.1093373029031921
+50359272 -1.0241342261758275 0.10892678517798436
+50458306 -1.023602690218962 0.1093941713710488
+50557340 -1.0236917539254018 0.109833708515048
+50656374 -1.0234705477056647 0.10974935706469736
+50755408 -1.0238548995907613 0.11015706942288817
+50854442 -1.0244793010234914 0.1100194931257207
+50953476 -1.024596064140775 0.11041621098343274
+51052510 -1.0244668419554246 0.11073804589223314
+51151544 -1.0247757406371363 0.11052867544495876
+51250578 -1.0246360124644853 0.11070654112953607
+51349612 -1.0246272019942406 0.11092405670447983
+51448646 -1.0240829341898452 0.11156035906442366
+51547680 -1.0249781249126595 0.11173086231305265
+51646714 -1.0241830325903596 0.11209098834927979
+51745748 -1.0242725850078969 0.11199292124193713
+51844782 -1.0253144746853604 0.11213950640990158
+51943816 -1.0246530721205758 0.11254868429437175
+52042850 -1.0246726243160102 0.11254603277986708
+52141884 -1.025011264862116 0.11305082360063189
+52240918 -1.0244641487044495 0.11333162232232141
+52339952 -1.0247517189860158 0.1133826876979449
+52438986 -1.0245360702332456 0.11361516210906819
+52538020 -1.0249699211405852 0.11365081475287378
+52637054 -1.0256383368267377 0.11359830970362311
+52736088 -1.025763302230922 0.11406331519519455
+52835122 -1.0257738181234124 0.1142522450764346
+52934156 -1.0246675744646976 0.11493636515408162
+53033190 -1.0241641322521104 0.11502468529908672
+53132224 -1.0243838813430142 0.1147827781683413
+53231258 -1.0244763545312294 0.11549671835756886
+53330292 -1.0243947588475133 0.11579182168631842
+53429326 -1.0252534099983102 0.11542792815121504
+53528360 -1.0253522082552975 0.11559499956283832
+53627394 -1.0256533748241028 0.11581676266786381
+53726428 -1.0257203990009798 0.11617150833399796
+53825462 -1.0252813206574511 0.11636978460467362
+53924496 -1.0259807956068068 0.11655720086492168
+54023530 -1.0256755759371983 0.11693841285223437
+54122564 -1.0268428755435226 0.11676066312920695
+54221598 -1.0266863411091856 0.11710306783862669
+54320632 -1.0267885937949934 0.11726265374606333
+54419666 -1.0253317932394794 0.11730535553484801
+54518700 -1.0255538150305583 0.11786136424329205
+54617734 -1.0259567890857704 0.11779440164008734
+54716768 -1.0260744308410659 0.11819118744097304
+54815802 -1.0258563551639295 0.11822310145103126
+54914836 -1.0259125105204094 0.11844375477350248
+55013870 -1.0261846551723683 0.11854830962532714
+55112904 -1.0259737632637371 0.1188209296234453
+55211938 -1.0261191744278737 0.11908775058114082
+55310972 -1.0262231007244416 0.11913368343271263
+55410006 -1.0265378598610895 0.11960645675835788
+55509040 -1.0262432879741046 0.11962588516445688
+55608074 -1.0266518575570285 0.11981564770338562
+55707108 -1.026446626525053 0.12012680203157863
+55806142 -1.02672751168399 0.12020261899937452
+55905176 -1.0264465074933165 0.12059602562716192
+56004210 -1.0268041782930022 0.12061479871810536
+56103244 -1.0271198928436325 0.12079849248858437
+56202278 -1.0269380752195174 0.12075145859121342
+56301312 -1.026787599650369 0.12149329723416198
+56400346 -1.0266845893633807 0.12148438250281685
+56499380 -1.0270318498471491 0.12158222649014931
+56598414 -1.0268384513314752 0.1217947450248094
+56697448 -1.0267569948024462 0.12214521596736432
+56796482 -1.0270815450090731 0.12229425117888133
+56895516 -1.0271776361635516 0.12237354386975326
+56994550 -1.0275109355745533 0.12252339909935293
+57093584 -1.027049698350165 0.12272579909868303
+57192618 -1.0277921002566461 0.12282020401770885
+57291652 -1.0276282646728008 0.12283286292103655
+57390686 -1.0275974109032928 0.12353989131984836
+57489720 -1.027445820945532 0.12345968793822956
+57588754 -1.0274409262816357 0.1239485158351014
+57687788 -1.027898035858876 0.12387641064441553
+57786822 -1.0278537395404943 0.12419343469073528
+57885856 -1.0279804104276555 0.12425452920197012
+57984890 -1.0274632657934504 0.12472851998313819
+58083924 -1.0275548518974933 0.12507538746096364
+58182958 -1.0276993143984976 0.12507755983349064
+58281992 -1.02756409426542 0.12531261802660842
+58381026 -1.027681383445674 0.12570095255102298
+58480060 -1.02753542506202 0.1257366766406497
+58579094 -1.028106631038858 0.12572042146378767
+58678128 -1.0281725361969103 0.12617203427199633
+58777162 -1.0282536006027545 0.12596901423783755
+58876196 -1.028103528176948 0.12667986285818852
+58975230 -1.028329013836402 0.12657510728956917
+59074264 -1.0283609996485166 0.1267821663935971
+59173298 -1.0286923831422292 0.1270228904316477
+59272332 -1.0284929512761931 0.12741315276417606
+59371366 -1.028380820360821 0.12746513824111222
+59470400 -1.0288658863112463 0.1274753956553955
+59569434 -1.028662603299429 0.12789575563190797
+59668468 -1.0287290891317882 0.1283048159277468
+59767502 -1.0286510879445914 0.12822601811912812
+59866536 -1.0289789467411272 0.1284064653179665
+59965570 -1.029170485482013 0.12856980897130801
+60064604 -1.0281663383899473 0.12991829369271238
+60163638 -1.0284485287826708 0.1297016266844527
+60262672 -1.028720656003903 0.129973894520158
+60361706 -1.0281719265310818 0.13033689148522437
+60460740 -1.0285706293495105 0.1305066782697603
+60559774 -1.0286568549269648 0.1304128380269547
+60658808 -1.0286053661233199 0.1305747210147699
+60757842 -1.0284895346899805 0.13098351185999996
+60856876 -1.0287646335193295 0.13125629836659514
+60955910 -1.0287698390046067 0.1314474863749222
+61054944 -1.0290322701513415 0.1315415657139156
+61153978 -1.0290676797087648 0.13191321234327216
+61253012 -1.0292619279286266 0.1319963870688644
+61352046 -1.0291732103801527 0.1320687380982286
+61451080 -1.0291385827790467 0.1322761152688498
+61550114 -1.0292178150245292 0.13254255750198896
+61649148 -1.0288285745956156 0.13293046077824847
+61748182 -1.029406156340966 0.1331709940378641
+61847216 -1.0290592602532043 0.1333099300345541
+61946250 -1.029350199020622 0.1332540908008276
+62045284 -1.0296414524613453 0.13357602019488754
+62144318 -1.029575090713194 0.1335102344386629
+62243352 -1.029633176295587 0.13405502663766583
+62342386 -1.0297718433122778 0.13422124427283572
+62441420 -1.0299194419993816 0.13447165002644787
+62540454 -1.0298369554386397 0.13447902148279928
+62639488 -1.0299405897158842 0.13463925272829988
+62738522 -1.0299453293247118 0.1348240566918091
+62837556 -1.029898808068013 0.13507692392292509
+62936590 -1.0300523866448577 0.13517589028608692
+63035624 -1.0300532721965616 0.13523500758387869
+63134658 -1.0300458595284434 0.13570994879181528
+63233692 -1.0299042612149032 0.13566961509935832
+63332726 -1.0303101558728058 0.13582181804257704
+63431760 -1.0305463024897918 0.1363012058489685
+63530794 -1.0304648268543657 0.136247863834043
+63629828 -1.0301740826257657 0.13654098514953897
+63728862 -1.029983305016515 0.13682998005993388
+63827896 -1.0309106745455514 0.13677766859044949
+63926930 -1.0303488007957544 0.13689736582790116
+64025964 -1.0307460331659777 0.13710870297591926
+64124998 -1.030829474449588 0.1371713066834418
+64224032 -1.030828597446385 0.13747275656767816
+64323066 -1.0309708468082457 0.13771114845180357
+64422100 -1.0311573468733377 0.1381292559826887
+64521134 -1.0309435391248352 0.13830646129227978
+64620168 -1.0310165014648776 0.13825598230137137
+64719202 -1.0314494116027677 0.1383802368893403
+64818236 -1.0314181246119687 0.13865441402144216
+64917270 -1.0316544018446105 0.13889526954936074
+65016304 -1.0315066390714211 0.13891867144895018
+65115338 -1.0316887473441885 0.13926653631457495
+65214372 -1.0317771115994214 0.1394413647906478
+65313406 -1.0316861622462337 0.13962238318708114
+65412440 -1.0317415761616826 0.1399572298166962
+65511474 -1.0319188401703367 0.14016014619895692
+65610508 -1.0320875946444235 0.14008169742166146
+65709542 -1.032047947472981 0.14018382117408948
+65808576 -1.032008431789605 0.1408461276989911
+65907610 -1.0322228507212334 0.14076348478547213
+66006644 -1.0321251546970838 0.14090799866199472
+66105678 -1.0320835172371432 0.1410209192406348
+66204712 -1.0322024184670382 0.1412698989997364
+66303746 -1.0324439218153172 0.14131125261181599
+66402780 -1.0322779563725704 0.14176077184569222
+66501814 -1.032327430805827 0.1420042684794463
+66600848 -1.0327314278637427 0.14213423997314995
+66699882 -1.0328518508396896 0.14207764107921486
+66798916 -1.0328108546168773 0.14241827297954876
+66897950 -1.0327633276024224 0.14272031586228043
+66996984 -1.032938045811623 0.14256399781978865
+67096018 -1.0328989019539414 0.1431193789920339
+67195052 -1.0329668391032905 0.14327887267105693
+67294086 -1.0332078835234546 0.14327483449865688
+67393120 -1.03338015027201 0.14335177056613452
+67492154 -1.0331963410956015 0.1436603994911782
+67591188 -1.033457292093641 0.14411527859849732
+67690222 -1.0334896833737681 0.14398357178989185
+67789256 -1.0336064138913237 0.14420455972597104
+67888290 -1.0336223090102457 0.14441145959824767
+67987324 -1.0337809072093316 0.14476072028553172
+68086358 -1.0336761768556626 0.14508747317787185
+68185392 -1.0338014727285834 0.14511428977945934
+68284426 -1.033923115925973 0.14561135349015258
+68383460 -1.0339054703032209 0.1455068479833833
+68482494 -1.0340985059271528 0.14580700679019304
+68581528 -1.0339494339939757 0.14598651646521849
+68680562 -1.0343486863767999 0.14614522282280856
+68779596 -1.0340344786885947 0.14628313577518612
+68878630 -1.0342555250787322 0.14678043155248646
+68977664 -1.034150664250881 0.14663873598923946
+69076698 -1.0343638185980522 0.1471091305756661
+69175732 -1.034387559628551 0.14721083338873675
+69274766 -1.034482363147169 0.14741437489769876
+69373800 -1.0345337370244727 0.1475819855038001
+69472834 -1.0347219015846603 0.14760663558595358
+69571868 -1.0345728333298831 0.1477002776565861
+69670902 -1.0348968279699502 0.14798652466798073
+69769936 -1.0349906249389293 0.1481677394698192
+69868970 -1.0349691342953335 0.14875221580292533
+69968004 -1.0347131688083198 0.14881397603325092
+70067038 -1.0349801897555657 0.1488509994484993
+70166072 -1.0351155828734293 0.1489573723579169
+70265106 -1.0349450319904014 0.14929952179785655
+70364140 -1.0353538759577436 0.14936509655667973
+70463174 -1.035436086726856 0.14977293276524115
+70562208 -1.0353586068284863 0.14975910925965594
+70661242 -1.0352326189693084 0.15019097584039026
+70760276 -1.0356394342773483 0.15007828729639855
+70859310 -1.0354873580585997 0.1505181145096023
+70958344 -1.0354175128393366 0.15058226614165565
+71057378 -1.0355830000337551 0.15066610763082353
+71156412 -1.0357263638343752 0.1509136191579045
+71255446 -1.0357643049825904 0.15115878496862728
+71354480 -1.0358321827703207 0.1514123242365243
+71453514 -1.035620144824117 0.1516085971234561
+71552548 -1.0358719154809841 0.15175794468819023
+71651582 -1.0358309186040155 0.151958114719361
+71750616 -1.0361257892343567 0.15214419947403654
+71849650 -1.0360377984087812 0.15224696699037724
+71948684 -1.0361769151737052 0.1525742928497016
+72047718 -1.0362175640694304 0.15264959048368237
+72146752 -1.0364854159918857 0.15313611203081512
+72245786 -1.0364093915610606 0.1531726560665499
+72344820 -1.036468171140518 0.15314629566120563
+72443854 -1.0366986178792195 0.1534006663950356
+72542888 -1.0367229446014152 0.15394207176209315
+72641922 -1.036774066019679 0.15365117077549073
+72740956 -1.0365475310801648 0.1541854189422284
+72839990 -1.0367773477871098 0.15428861333776162
+72939024 -1.0369013066126047 0.15432636704651262
+73038058 -1.0370509563487968 0.1545762671844014
+73137092 -1.0370324874081476 0.15491308238053966
+73236126 -1.0371086710698285 0.15498559469077688
+73335160 -1.0371830340081358 0.15505410603392852
+73434194 -1.037317813620495 0.15538476530948778
+73533228 -1.0372043448165935 0.15526288994068646
+73632262 -1.0374133267572503 0.15596224033238357
+73731296 -1.037324302243262 0.15605341320624522
+73830330 -1.0373199887162494 0.15604965050672795
+73929364 -1.0374772124703915 0.1563354828002392
+74028398 -1.0376247088882657 0.1564365659820614
+74127432 -1.0377783293436138 0.15664265090171878
+74226466 -1.0379480418966394 0.15677285092530985
+74325500 -1.0376305630569465 0.15715264885464947
+74424534 -1.0377326193207723 0.15711708762842103
+74523568 -1.0382100985378082 0.15728089069714776
+74622602 -1.038054129137446 0.1575200356493587
+74721636 -1.0380552217793961 0.15794626435071923
+74820670 -1.0380950407022316 0.15788077674490994
+74919704 -1.0380804360368912 0.15809046900624993
+75018738 -1.0383065517306325 0.15819930897751883
+75117772 -1.0385638885178536 0.15851685796905285
+75216806 -1.0382869255514553 0.1587993588240569
+75315840 -1.038590080374384 0.15879278621738924
+75414874 -1.0386564676186714 0.159142953296263
+75513908 -1.0385057417960728 0.1594096488456408
+75612942 -1.038733097041371 0.15945910284485695
+75711976 -1.0389430128573602 0.1597660449415584
+75811010 -1.038784394565292 0.15991312338358596
+75910044 -1.0390050043063392 0.16005870966136831
+76009078 -1.0389533509829671 0.16025130036587165
+76108112 -1.0392960391179582 0.16040825087411043
+76207146 -1.039003001978662 0.16067165473891587
+76306180 -1.039382733787268 0.16077350751114838
+76405214 -1.0394495189729895 0.16085928107502653
+76504248 -1.0396206087394901 0.16127438438991504
+76603282 -1.039255472335759 0.1612984758114215
+76702316 -1.0394682910459632 0.16160934620880782
+76801350 -1.0395144931735203 0.16188378580235283
+76900384 -1.039672364669968 0.16211368101321333
+76999418 -1.039533151265614 0.16224555857781214
+77098452 -1.0398400538686228 0.16250411320690947
+77197486 -1.0398653610754116 0.1626000603358517
+77296520 -1.0398324055863384 0.16278711884161137
+77395554 -1.0399744394809125 0.16312450010530177
+77494588 -1.0400849312441043 0.16298541535630987
+77593622 -1.0402107548118948 0.16362589596277527
+77692656 -1.0398380889140282 0.1635999294270473
+77791690 -1.0403456166017246 0.1637567017648827
+77890724 -1.0405674702844747 0.16402164013348206
+77989758 -1.0407640861528813 0.1639725490525859
+78088792 -1.040333656722936 0.16411360615790102
+78187826 -1.0406201929595076 0.16472592978621328
+78286860 -1.0407366785156023 0.16491611188654667
+78385894 -1.0406586208448225 0.16499463732815334
+78484928 -1.0406222693742293 0.16500727027042622
+78583962 -1.040814842760789 0.16508891827414685
+78682996 -1.0406949841351385 0.16563227271337233
+78782030 -1.0408532211318207 0.16540161605469356
+78881064 -1.0410126923620553 0.165678540229083
+78980098 -1.0409891060859358 0.16616665824846913
+79079132 -1.0408958347460953 0.16624949695645447
+79178166 -1.0410879793415082 0.1663357825930982
+79277200 -1.0409936168903993 0.1665626978842612
+79376234 -1.0410287202386799 0.16681850898941272
+79475268 -1.0415003990834069 0.16670721887576606
+79574302 -1.0412262638742271 0.16706740725388236
+79673336 -1.0414521211991943 0.1669143151008053
+79772370 -1.0414234075961768 0.16710622726820187
+79871404 -1.0413582670574206 0.16701418272261953
+79970438 -1.0415597164473387 0.16733000599631812
+80069472 -1.0418193219745473 0.16715036699915214
+80168506 -1.0417206729722588 0.16717306051145994
+80267540 -1.0421481039626257 0.16770913370050092
+80366574 -1.042107641928515 0.16760829760403895
+80465608 -1.0424912864803417 0.16762055701653208
+80564642 -1.0424662909764422 0.1676767914310134
+80663676 -1.042676884035901 0.16769551826275114
+80762710 -1.0428503619456722 0.1681234113324795
+80861744 -1.043180148802043 0.16828627228239804
+80960778 -1.0431161657375712 0.1683756211969105
+81059812 -1.0436010439221421 0.16860342331118258
+81158846 -1.0438634121522685 0.16861042298338683
+81257880 -1.0436671054148055 0.16901428442485775
+81356914 -1.0442002267839514 0.16933087386519194
+81455948 -1.0439020797699132 0.1699235171243004
+81554982 -1.0445369055739633 0.16988687369373814
+81654016 -1.0447962722677402 0.17036178314755693
+81753050 -1.0450019867312526 0.1707580720268007
+81852084 -1.0449702933869922 0.1709014874829908
+81951118 -1.044768761346666 0.17108678270350106
+82050152 -1.0449432255922084 0.17147087294454547
+82149186 -1.0450000753016637 0.17170787981163194
+82248220 -1.0450285827167898 0.1718852867575036
+82347254 -1.045088932366908 0.17252215289980127
+82446288 -1.0447823019927323 0.1725581075452226
+82545322 -1.0449490963524628 0.1723859135009632
+82644356 -1.0451744868275386 0.17291651666804844
+82743390 -1.0448575800427862 0.1732327724950734
+82842424 -1.0452911548474055 0.17302301525845246
+82941458 -1.0450658885444877 0.17328802723655146
+83040492 -1.0454378208493487 0.17347770714936253
+83139526 -1.0452347450866 0.17403479249665588
+83238560 -1.045305826801016 0.1740030866219209
+83337594 -1.0455266575461626 0.1741960401792613
+83436628 -1.0458488890061735 0.17443785175068083
+83535662 -1.0456474591424745 0.17471878239633812
+83634696 -1.045624084783922 0.17437802979044215
+83733730 -1.0457875700925086 0.17464834025403242
+83832764 -1.0457143770124306 0.17500547661038923
+83931798 -1.0457836812300068 0.1753737302405624
+84030832 -1.0456443310818109 0.1754799203702256
+84129866 -1.045881612201641 0.17564419060104325
+84228900 -1.0456328945795144 0.1756606753722061
+84327934 -1.0459819370791943 0.1758766364631679
+84426968 -1.045918467147933 0.17602041871637084
+84526002 -1.0459583164648094 0.17607974861038594
+84625036 -1.046138269030658 0.17609606485827797
+84724070 -1.0462798364300856 0.1766019631594761
+84823104 -1.0461641730235776 0.17666746288769752
+84922138 -1.046554278578438 0.17696487736618033
+85021172 -1.0465193845564849 0.1768037971349549
+85120206 -1.0465563024104019 0.177272446735495
+85219240 -1.046116889536911 0.17747042194915777
+85318274 -1.046865566854937 0.17764536415028145
+85417308 -1.0465733184876616 0.17777866045080182
+85516342 -1.0465863557219282 0.1778496511984686
+85615376 -1.0466526677698895 0.17780904486950813
+85714410 -1.0467933245939316 0.17824818720438051
+85813444 -1.0469462567419008 0.17831491148020318
+85912478 -1.0471727916524605 0.17854040608889674
+86011512 -1.0470578875674692 0.1787243824766952
+86110546 -1.047162612166818 0.1787011812532133
+86209580 -1.0471176149000316 0.17900624597596201
+86308614 -1.0474741313380507 0.17911399816220477
+86407648 -1.0476183342681051 0.17931844702505811
+86506682 -1.0474172709488172 0.17940870865809422
+86605716 -1.0477090815912309 0.17949429201841224
+86704750 -1.047726889149411 0.17977768307745373
+86803784 -1.0479765050523906 0.17995687476613906
+86902818 -1.0479833174095885 0.18009193728544504
+87001852 -1.0479528257265736 0.18004268177596106
+87100886 -1.0478301876571663 0.18040687760194674
+87199920 -1.0481477675819073 0.18061199168756936
+87298954 -1.0481902675964685 0.18096865465714065
+87397988 -1.0482023059095875 0.18072242167134617
+87497022 -1.048385675885992 0.18086741058078937
+87596056 -1.0486109905624401 0.1813379898632266
+87695090 -1.048671926441164 0.18131367181476996
+87794124 -1.0485794466187701 0.1814942178126936
+87893158 -1.0487537260426723 0.1816976976720251
+87992192 -1.0486448515053053 0.18198414240941307
+88091226 -1.0489643824008217 0.18187072528307424
+88190260 -1.0487153319910731 0.1820831195730696
+88289294 -1.0489839225137243 0.18242019134258985
+88388328 -1.0492284658318374 0.18247833733720592
+88487362 -1.0490732880453768 0.1829547540845894
+88586396 -1.0499168620798625 0.18290292081812182
+88685430 -1.049428966199857 0.18283660357267276
+88784464 -1.0495769300815834 0.1830536592436353
+88883498 -1.0495326774946387 0.18367328705738592
+88982532 -1.0495529198956344 0.1834774148478492
+89081566 -1.050005590648382 0.18310966084374208
+89180600 -1.049855520532623 0.18420243293824826
+89279634 -1.0500700070163262 0.18431407095611024
+89378668 -1.049796165326143 0.18450881844481756
+89477702 -1.0501127092113196 0.1842732364375115
+89576736 -1.0504157379754855 0.184717886681777
+89675770 -1.05018580630865 0.18455068783227918
+89774804 -1.0504405681791147 0.18482490054018194
+89873838 -1.050584382772843 0.18514718116456275
+89972872 -1.0504345628370697 0.18521584969508834
+90071906 -1.0508465051524862 0.18498891540284595
+90170940 -1.0506935012100576 0.1855777788945422
+90269974 -1.0509038323815374 0.186018605722356
+90369008 -1.0507838568422634 0.18616979675760267
+90468042 -1.0511332631195478 0.18621600503539748
+90567076 -1.0511710111778687 0.18642274935666747
+90666110 -1.0510072067041076 0.18647837278254856
+90765144 -1.0513381225024194 0.1865023300648445
+90864178 -1.0514738305669917 0.1867385219939102
+90963212 -1.0513667401449092 0.18726548430652531
+91062246 -1.0510752780835841 0.18696522510592217
+91161280 -1.0513743725085283 0.1870235010843863
+91260314 -1.051359168331004 0.18745635154797508
+91359348 -1.05200721593346 0.18737407678343132
+91458382 -1.051803974583575 0.18751130919508388
+91557416 -1.0518217557406428 0.18764184763277658
+91656450 -1.0520131299774962 0.18794216559045065
+91755484 -1.0518161406505298 0.18819575099445798
+91854518 -1.0521472455461154 0.1882259746755056
+91953552 -1.0521798366447008 0.18865357413463102
+92052586 -1.0525330705929703 0.18842035888437303
+92151620 -1.0523320494112038 0.1887972211947934
+92250654 -1.0522294816194973 0.1890391215357192
+92349688 -1.052544471381314 0.1893534213539313
+92448722 -1.052418417022554 0.18944626751484134
+92547756 -1.0527168887030638 0.18928299033085522
+92646790 -1.053228378327836 0.18932966907640594
+92745824 -1.0525243194662268 0.18959393260384372
+92844858 -1.0526474436086233 0.1900279583429968
+92943892 -1.0528893100037373 0.1897954647009534
+93042926 -1.0532404471422745 0.189543706235109
+93141960 -1.05315069060686 0.18994680139461617
+93240994 -1.05338739265195 0.18997708045388004
+93340028 -1.052969579179098 0.19064085289559937
+93439062 -1.05367489042043 0.1902375724579427
+93538096 -1.053391026798305 0.19061130012622016
+93637130 -1.0533813425045406 0.19042785858499522
+93736164 -1.0537840241770944 0.19086176199410226
+93835198 -1.0536219698342209 0.19092908810907722
+93934232 -1.054135832193481 0.19118572261075376
+94033266 -1.0542619422215345 0.19081477882216913
+94132300 -1.0542223669300608 0.19121627627856141
+94231334 -1.0547271194985601 0.19124999036767512
+94330368 -1.055270732873586 0.1914468493153511
+94429402 -1.0552536180801328 0.19151168219142836
+94528436 -1.055894220313701 0.19176527949298605
+94627470 -1.0558496252125622 0.1915433814966702
+94726504 -1.0559790654888697 0.19192496632046488
+94825538 -1.056609989239957 0.19221910056136776
+94924572 -1.0567469962861042 0.19220161600348598
+95023606 -1.0568590266928797 0.19271870467211294
+95122640 -1.0570705721431741 0.19340933125268886
+95221674 -1.0573597139486561 0.19346834721386663
+95320708 -1.0574499760164435 0.19362627855394404
+95419742 -1.0575565122464492 0.19404174078160863
+95518776 -1.0580795304785298 0.19469017459678328
+95617810 -1.058327281080278 0.19487804690443356
+95716844 -1.0580967581998362 0.19516600460440234
+95815878 -1.0586444530214887 0.19539828737123277
+95914912 -1.0586560615656653 0.19587296601090848
+96013946 -1.0586817795273207 0.19621863929211844
+96112980 -1.0580631831395197 0.19694642087242514
+96212014 -1.058366095866788 0.1974019662993645
+96311048 -1.0584601428348337 0.19742027378593244
+96410082 -1.0583428741234773 0.19786866637112907
+96509116 -1.057999121458795 0.198225167567966
+96608150 -1.0578153389066498 0.19865083997336674
+96707184 -1.0580592037650935 0.19890297465026752
+96806218 -1.0582761661345386 0.19890625291164618
+96905252 -1.0580936578330862 0.19894267671465024
+97004286 -1.057882316591407 0.19969036992459327
+97103320 -1.057803467407093 0.1998455122247097
+97202354 -1.0576387937725393 0.1996464082976004
+97301388 -1.057679305751949 0.20019903700900446
+97400422 -1.0577360967349554 0.2002975227003503
+97499456 -1.057628212382835 0.20020671013349933
+97598490 -1.0576559595230213 0.20053042811181565
+97697524 -1.0574032207881265 0.20063361084507186
+97796558 -1.057721951305301 0.2006288793755518
+97895592 -1.057576759945112 0.2008926948610179
+97994626 -1.0572627403203205 0.20128714714518126
+98093660 -1.0574490460874555 0.20115284664028227
+98192694 -1.0573389931307504 0.20140800878238685
+98291728 -1.0574411309657943 0.2015848698705167
+98390762 -1.0575630621386338 0.201699486227093
+98489796 -1.057691777282292 0.20193482440735483
+98588830 -1.0576983974242922 0.20176051729815744
+98687864 -1.057208131547397 0.20162990815832493
+98786898 -1.0577119009646891 0.20195492454225464
+98885932 -1.0582125293529183 0.20203601225143983
+98984966 -1.0574430976265985 0.20220919938898396
+99084000 -1.0579107675876727 0.20268042574605952
+99183034 -1.0573378974083558 0.20302976046334256
+99282068 -1.0580993684249096 0.20280564288238997
+99381102 -1.0583991851796308 0.20272170499494238
+99480136 -1.0583519574964695 0.2026598906133066
+99579170 -1.0578849508614194 0.20324996968222128
+99678204 -1.058232629941955 0.20274040499842108
+99777238 -1.0580416455763044 0.20309060074791588
+99876272 -1.0581979411634133 0.2033746699490521
+99975306 -1.057950631475943 0.20363744996227603
+100074340 -1.0578433975660533 0.20418448900244285
+100173374 -1.058348377291142 0.20378597139634627
+100272408 -1.0584584472028025 0.20386508575367948
+100371442 -1.0585777259662645 0.20436932822644063
+100470476 -1.0587800119093442 0.20401285241398096
+100569510 -1.0589377285711439 0.20456357961864788
+100668544 -1.0591354207090926 0.2046266104547932
+100767578 -1.0590753266835373 0.20469106630490175
+100866612 -1.05892382092059 0.20456537429452348
+100965646 -1.058958291784481 0.20492069099705099
+101064680 -1.0588278305532868 0.2053111249214292
+101163714 -1.059189456754283 0.2051918167710904
+101262748 -1.0592607718715674 0.20535403425809878
+101361782 -1.0592379365757458 0.20571570425471866
+101460816 -1.0594307382064947 0.2056396171437031
+101559850 -1.059629475794208 0.20587181444438357
+101658884 -1.0595854798889677 0.2060749479431602
+101757918 -1.0596394891230756 0.2060006998426898
+101856952 -1.0589330960030476 0.20666804192098825
+101955986 -1.060469046679331 0.20597481709835413
+102055020 -1.0591917868500376 0.20667929319926173
+102154054 -1.059724996888481 0.20659161249912827
+102253088 -1.0599955402137229 0.2068161089223667
+102352122 -1.0597898840981428 0.20654993001321917
+102451156 -1.0596927611583393 0.2071159165759614
+102550190 -1.0607379059177222 0.20785035091037476
+102649224 -1.060922498574818 0.20719910862639562
+102748258 -1.0607553009861905 0.20795634033782814
+102847292 -1.0605037870835723 0.20799498475134817
+102946326 -1.060395230230129 0.20839132302919705
+103045360 -1.0606131241002297 0.208200393785974
+103144394 -1.0612026835681236 0.20841202523945238
+103243428 -1.0602212945886045 0.20862817419693408
+103342462 -1.0606532541500109 0.209114552509745
+103441496 -1.0610812585652416 0.20847472141058193
+103540530 -1.061288314900824 0.2088210918225968
+103639564 -1.0612711933347663 0.20915988097397598
+103738598 -1.0612358866902118 0.20898590165217565
+103837632 -1.0615672348897236 0.20922759146732792
+103936666 -1.0614917812940068 0.20961091717074465
+104035700 -1.0615796609368104 0.20944570747912178
+104134734 -1.0612062323425213 0.20979789047230557
+104233768 -1.0616218511744968 0.21027911012517267
+104332802 -1.0613984854239364 0.2096500438874343
+104431836 -1.0619695839941536 0.21020458133517098
+104530870 -1.0615822407834516 0.21028701266040986
+104629904 -1.0617547489188268 0.2102998981548177
+104728938 -1.0614943157798882 0.21002465543099005
+104827972 -1.0621678845634897 0.21087351029992837
+104927006 -1.0617450734270955 0.21048178508372803
+105026040 -1.0623274346450449 0.21088800362755775
+105125074 -1.0626751368006546 0.21149329246664217
+105224108 -1.0623400203077835 0.21169106383857877
+105323142 -1.0625367643737307 0.21104775193259065
+105422176 -1.0624620073336994 0.21213212627885064
+105521210 -1.0623774112396442 0.2121095069129322
+105620244 -1.0626294971280708 0.2118649987075478
+105719278 -1.0626053552788717 0.21211447453508797
+105818312 -1.062424783195532 0.21239590687868676
+105917346 -1.06288687410697 0.2126528373215423
+106016380 -1.0628934234450789 0.21244088197045327
+106115414 -1.0632650976419904 0.21251780888185018
+106214448 -1.0633219226715942 0.21290895397640364
+106313482 -1.0632822597474108 0.21269633199763163
+106412516 -1.0633049194745157 0.2131583193953317
+106511550 -1.0632633387223582 0.21306621965380432
+106610584 -1.0636435882412834 0.21280582803390646
+106709618 -1.063498872495511 0.21344950151862943
+106808652 -1.0635830846371281 0.2136601519443192
+106907686 -1.0635078178590431 0.2142794327520827
+107006720 -1.0634593008039857 0.2138047887836259
+107105754 -1.0637703049427787 0.2143680550255129
+107204788 -1.0644122559880231 0.21425182716623478
+107303822 -1.0641282274857233 0.21446446286376808
+107402856 -1.0644927624167349 0.21488081040124993
+107501890 -1.0641851481204876 0.21479756656763072
+107600924 -1.0639075932975546 0.21490918439743095
+107699958 -1.064249486189092 0.21489179911387674
+107798992 -1.0646470368180523 0.2151387946197168
+107898026 -1.0643285404021352 0.2153934971852462
+107997060 -1.0645717582745875 0.21537241404001675
+108096094 -1.064335531484884 0.2160572405500234
+108195128 -1.0643453411427446 0.21581323873067795
+108294162 -1.0646824237299368 0.21583368825820223
+108393196 -1.0649448745824772 0.21584607213319096
+108492230 -1.0651846224131774 0.21622214345731403
+108591264 -1.0653278938936472 0.21631033448032447
+108690298 -1.065502553149561 0.2162564457068175
+108789332 -1.065457077633588 0.21678797533267194
+108888366 -1.0653104344230828 0.21694149364800178
+108987400 -1.065112524351347 0.2167141172583691
+109086434 -1.0651420188336647 0.21695833774676648
+109185468 -1.0655529696123893 0.217345546502207
+109284502 -1.065703032265948 0.2173156209266522
+109383536 -1.0660481962979382 0.2176281454144985
+109482570 -1.065785441280904 0.21773359857313657
+109581604 -1.065866996689736 0.21817086933728444
+109680638 -1.0660942271510234 0.21809696876170143
+109779672 -1.0661677639008076 0.21824089321659512
+109878706 -1.0661699058736827 0.21810950460612477
+109977740 -1.0665141583850943 0.21872577753583103
+110076774 -1.0665075879439074 0.21884788944881622
+110175808 -1.0665077905358966 0.21916583895799135
+110274842 -1.0670074093069988 0.21931868465953946
+110373876 -1.0662329888533626 0.21881263937724146
+110472910 -1.0667199496626183 0.2192012248620091
+110571944 -1.0670435866570298 0.2193004323203871
+110670978 -1.067303001281471 0.21976222674849102
+110770012 -1.0668814353493463 0.21881235556183148
+110869046 -1.0667011099260604 0.22007794141652856
+110968080 -1.0669181721156917 0.21974598666898817
+111067114 -1.0675264806730935 0.22011991228799796
+111166148 -1.0671764915357738 0.2202328064240152
+111265182 -1.0675923306751052 0.22066668095429953
+111364216 -1.0674653917590293 0.22064742661623607
+111463250 -1.0679587126239731 0.22065175963949482
+111562284 -1.0680599578685444 0.2210602936234089
+111661318 -1.0676471784488537 0.2208564439772596
+111760352 -1.0674112786639927 0.22077632136997974
+111859386 -1.0674125993634178 0.2209133606132892
+111958420 -1.0678646536496004 0.22148912770518225
+112057454 -1.0681670792425635 0.22115475354627012
+112156488 -1.0679063882909414 0.22181393245285946
+112255522 -1.0685785083781056 0.22209787823987226
+112354556 -1.0684150766441554 0.22212623655325253
+112453590 -1.0683726783931058 0.22238000485453635
+112552624 -1.0686826450084126 0.2222763408691804
+112651658 -1.0686466786544264 0.22238261517975436
+112750692 -1.0684215724352133 0.22262093344109257
+112849726 -1.0687864585472855 0.22323886691818637
+112948760 -1.0688813388668539 0.22235883806873227
+113047794 -1.068995738491165 0.22287188507314903
+113146828 -1.0691664613530814 0.22342760773854095
+113245862 -1.0692316633784327 0.22345313392287722
+113344896 -1.0692452294145833 0.22352904888116587
+113443930 -1.069049697808486 0.22346081383378544
+113542964 -1.0691364510667245 0.2237895840978518
+113641998 -1.0691535039341316 0.22420690000150126
+113741032 -1.0696855116638906 0.2244375887097815
+113840066 -1.0696150621405607 0.224192864088723
+113939100 -1.0695519947209577 0.2242992334707473
+114038134 -1.0696847757240304 0.2245483837382534
+114137168 -1.0700685836718924 0.22452019043269103
+114236202 -1.0701212019438808 0.22460080864502704
+114335236 -1.070354902466465 0.22501110621291476
+114434270 -1.07024094105661 0.22489823698692069
+114533304 -1.0699573785995198 0.225409236863685
+114632338 -1.0702355783519644 0.22539014865483337
+114731372 -1.0703444906443496 0.22539477016069487
+114830406 -1.0703910802708845 0.225755943505616
+114929440 -1.0705423562307257 0.22596370631641313
+115028474 -1.0706302111616148 0.22579348296719853
+115127508 -1.0711399858853266 0.2256569960494834
+115226542 -1.0709632778501696 0.2264128253653536
+115325576 -1.071217535009359 0.2261767831655556
+115424610 -1.0708995456311858 0.22639601408299465
+115523644 -1.07097494764194 0.22714174877130539
+115622678 -1.0710150615553138 0.22666795274720536
+115721712 -1.0716691802768157 0.22700208844291644
+115820746 -1.0713724752515517 0.22757816834476244
+115919780 -1.071601124522205 0.22734724184226787
+116018814 -1.071334377403693 0.22749536465465614
+116117848 -1.0715065302879756 0.22760113612684937
+116216882 -1.0715284466559458 0.22777537817668744
+116315916 -1.0718327220614001 0.22741637921208582
+116414950 -1.0717766166151237 0.22780341842702548
+116513984 -1.0723262224312087 0.22789215295456758
+116613018 -1.0724878555765676 0.2280649801399729
+116712052 -1.072241465528973 0.2283945124778173
+116811086 -1.0719614858049804 0.2284309125863387
+116910120 -1.0726461853808493 0.22887635789510943
+117009154 -1.0723976506957067 0.22872422407629223
+117108188 -1.072691090563179 0.22900795082404396
+117207222 -1.0725929693201375 0.22933108142566735
+117306256 -1.072651835375254 0.22886216201230022
+117405290 -1.0727421274783162 0.22950142946648455
+117504324 -1.072953965851628 0.2292704416534895
+117603358 -1.0735044972131567 0.2298769051289896
+117702392 -1.0735851727494587 0.22967022588995864
+117801426 -1.0731881022264496 0.22992853458215876
+117900460 -1.073255163891431 0.23005746094548163
+117999494 -1.0736808449954423 0.23011130963183007
+118098528 -1.0733799670186084 0.2304157844630041
+118197562 -1.0735123637253463 0.23045605476534803
+118296596 -1.073679138399264 0.23045196755312988
+118395630 -1.0735110328932547 0.23077493365835844
+118494664 -1.0739649564060223 0.2310814729848427
+118593698 -1.0737433589926928 0.23113586937703762
+118692732 -1.0742103881911331 0.23134951141404567
+118791766 -1.074031787786197 0.23156438408427127
+118890800 -1.0741602470518954 0.231811477541758
+118989834 -1.0744216697134616 0.23151755915454628
+119088868 -1.074233291236199 0.23210447400655382
+119187902 -1.0742896638985768 0.23247837625241227
+119286936 -1.0742792170952116 0.2321327082316474
+119385970 -1.0744217636714595 0.2322312717472552
+119485004 -1.0748468160910594 0.2320583263572286
+119584038 -1.0747247685155643 0.23241246101130397
+119683072 -1.0747772737633774 0.23253050847438325
+119782106 -1.0754670066905399 0.23277012888714713
+119881140 -1.075125270358296 0.23263923987034157
+119980174 -1.0751882547419396 0.23301200593608262
+120079208 -1.075158443462879 0.2333721149505361
+120178242 -1.075540905896265 0.23336737452163228
+120277276 -1.0756750177879235 0.23373628383688855
+120376310 -1.0762822985189602 0.23378576282698454
+120475344 -1.0758100919817892 0.23413110255842295
+120574378 -1.075036122371567 0.2338296183568311
+120673412 -1.0759886401194787 0.234287316468458
+120772446 -1.0753229486435862 0.23405179789718764
+120871480 -1.0757352546687347 0.23446648222136787
+120970514 -1.0755886818137923 0.2343691994553109
+121069548 -1.075885452751619 0.23417955676076743
+121168582 -1.0762828358015397 0.2349868950671075
+121267616 -1.0760006050998998 0.23520569604735803
+121366650 -1.0760466202931638 0.2352392344407119
+121465684 -1.0768547327119828 0.23561211591865341
+121564718 -1.0765754020414668 0.23569708043994123
+121663752 -1.0768185887235249 0.2356941030105609
+121762786 -1.0770692078104103 0.23561184752794653
+121861820 -1.076575813646981 0.23516920928153406
+121960854 -1.07695001241495 0.23641707540585993
+122059888 -1.0763457195340536 0.2362159565658212
+122158922 -1.0765291029516326 0.23606013863269126
+122257956 -1.0773097715337667 0.2362251484280284
+122356990 -1.0775708412132796 0.23672251210714865
+122456024 -1.0774691258341986 0.23680951519666807
+122555058 -1.0769218445040447 0.23692422524226414
+122654092 -1.077848212991119 0.23722053740405574
+122753126 -1.077457873777342 0.23719613119332802
+122852160 -1.0778472566385815 0.23710884117184172
+122951194 -1.077907285049871 0.2371215138062474
+123050228 -1.0772297311445587 0.23709612592660373
+123149262 -1.0774866583569762 0.23724053741874684
+123248296 -1.0776165033224112 0.23822146085952983
+123347330 -1.0778744242856808 0.2385640566365962
+123446364 -1.0783589370648476 0.23761022630207043
+123545398 -1.078318066049644 0.23824814793831445
+123644432 -1.0786867296902305 0.23837846474329483
+123743466 -1.077792828385799 0.23857293015876754
+123842500 -1.0779599137846065 0.2382095689500548
+123941534 -1.0781923835613825 0.23894444887821864
+124040568 -1.0784190919735375 0.23843846726944593
+124139602 -1.07871798035057 0.2390680504312646
+124238636 -1.0788271996525982 0.23925659735082133
+124337670 -1.0790458873979498 0.23952494830363633
+124436704 -1.078987555302971 0.2397787329185582
+124535738 -1.0789044640189436 0.23979913521229784
+124634772 -1.079043315392714 0.23970678591312602
+124733806 -1.0792777009615098 0.23978076866682083
+124832840 -1.079253660779165 0.2398622931856358
+124931874 -1.079707355552329 0.24026086808642957
+125030908 -1.0793029097841857 0.24026802666795546
+125129942 -1.0795444202417877 0.24076451147503636
+125228976 -1.0794530986650734 0.24032756512983253
+125328010 -1.0800722505611602 0.2404904780380635
+125427044 -1.0799110047897487 0.24058477127342467
+125526078 -1.0797006666410642 0.24069009632188554
+125625112 -1.0803476557142686 0.24126456897237658
+125724146 -1.0802304990786964 0.24140289882679145
+125823180 -1.0796985975725701 0.24143928657228458
+125922214 -1.0802306251289777 0.24138288296106397
+126021248 -1.0806666438848527 0.24184330441157706
+126120282 -1.080723750644152 0.24191935905745796
+126219316 -1.080681749108679 0.2419230013931039
+126318350 -1.0808029420507843 0.24188344467355902
+126417384 -1.0806352565463742 0.2419947672047908
+126516418 -1.0813848559594035 0.2423015521462145
+126615452 -1.0811901386000815 0.2424185553448789
+126714486 -1.0812291851623714 0.24259204254703198
+126813520 -1.0811909297000866 0.24255886232224097
+126912554 -1.081252048516285 0.2428453009708059
+127011588 -1.0816409251745855 0.2432239899161732
+127110622 -1.0815473425238613 0.24285503923530283
+127209656 -1.08155694471374 0.24316197555854663
+127308690 -1.0814954671872947 0.24330466164382492
+127407724 -1.0821415578586238 0.2435642589930121
+127506758 -1.082126905813569 0.24341723800517595
+127605792 -1.082345133660212 0.24353487038190227
+127704826 -1.0820696726022316 0.24356018347969585
+127803860 -1.0824277778283244 0.2438167957390526
+127902894 -1.0823173800414194 0.24387776435329825
+128001928 -1.0823158540592903 0.24455573666542707
+128100962 -1.0824066180807808 0.24441617155819342
+128199996 -1.0829137608798052 0.24453435467537638
+128299030 -1.0825641984683905 0.24464802828822202
+128398064 -1.0826311393735515 0.24427768242522296
+128497098 -1.0829932945353111 0.24453697272580985
+128596132 -1.082776266625498 0.24520947611660787
+128695166 -1.0830289805050874 0.24513856187676888
+128794200 -1.0831995293389858 0.24460257938355043
+128893234 -1.0832097835572694 0.24537881124668168
+128992268 -1.083332376557234 0.24530660352594086
+129091302 -1.0834402111090444 0.24557147810323618
+129190336 -1.083617723259322 0.2457303685911568
+129289370 -1.0841369025551504 0.2458035436405522
+129388404 -1.0831272388087618 0.24591998227769685
+129487438 -1.0837814100663823 0.24592544341009778
+129586472 -1.0840128235678377 0.24667291893887364
+129685506 -1.083932438264904 0.24644106357874462
+129784540 -1.084137590764717 0.24649421658910017
+129883574 -1.0842057671694763 0.24674600010472503
+129982608 -1.0842122783684625 0.24632099606058722
+130081642 -1.0849597424854183 0.24659499143246988
+130180676 -1.0850400615826457 0.24704835590919977
+130279710 -1.0851173280554125 0.24739752775809537
+130378744 -1.0849210642813858 0.24739439780440173
+130477778 -1.0850147457219719 0.24700956352124717
+130576812 -1.0853246415850362 0.2476983107790411
+130675846 -1.0849088439681347 0.24746862006805703
+130774880 -1.0853271165539569 0.24777910581325321
+130873914 -1.085535752476383 0.24782691080213795
+130972948 -1.085736808485786 0.2476643625422023
+131071982 -1.0856092383809821 0.24807338726512748
+131171016 -1.085685730601805 0.24885672923777333
+131270050 -1.0858977898036766 0.24836314886242547
+131369084 -1.0861526054578114 0.24873647947144928
+131468118 -1.086124753098987 0.24889069265648295
+131567152 -1.0861945466538847 0.249007163060843
+131666186 -1.0862534220914264 0.24898237702810386
+131765220 -1.0871347524421768 0.24944753960327581
+131864254 -1.0871000598092455 0.24940366821988327
+131963288 -1.08686180782872 0.24937666492072333
+132062322 -1.0872185794029623 0.2495832972756577
+132161356 -1.0869733636593204 0.2497621488841653
+132260390 -1.087178900210573 0.2500115885558411
+132359424 -1.087646282190716 0.24997228936996338
+132458458 -1.0872230552240332 0.2506194779876106
+132557492 -1.0880220278899757 0.2502965460166642
+132656526 -1.0876726331379654 0.2506105866425045
+132755560 -1.0880122575887936 0.2510150101723604
+132854594 -1.0877268768186692 0.25131842900670925
+132953628 -1.0884656965558863 0.25131824574219686
+133052662 -1.0879255590203203 0.2515401947922164
+133151696 -1.0882224115734538 0.25206346753000275
+133250730 -1.0884393515498891 0.25180310227378483
+133349764 -1.0884246952422576 0.2521477342417096
+133448798 -1.0886128610745747 0.25279850953967387
+133547832 -1.0885871571991876 0.2526068690925144
+133646866 -1.08869147872486 0.2523121454423121
+133745900 -1.0887256696532195 0.2534369725920296
+133844934 -1.088427075696916 0.2529771504727866
+133943968 -1.0890545866011985 0.2534612421667415
+134043002 -1.0892485700460548 0.25334883126001095
+134142036 -1.0884954431235616 0.2536840853138066
+134241070 -1.0889903625136887 0.25360189267825844
+134340104 -1.088948758008594 0.25363073178926543
+134439138 -1.0887624873538997 0.2538830567756931
+134538172 -1.0885949055558042 0.2537135263485431
+134637206 -1.0889677044151516 0.25410535289163805
+134736240 -1.0891078196968247 0.25416448386590473
+134835274 -1.0891716356061238 0.2543924707590634
+134934308 -1.0895208808084909 0.25490996352747913
+135033342 -1.0886880627659734 0.2548921880836516
+135132376 -1.0894384343552836 0.25482114464911465
+135231410 -1.0890581634350134 0.25506313198664465
+135330444 -1.0894472544624654 0.2549829166904478
+135429478 -1.0892791909802149 0.25536722293850167
+135528512 -1.0896604226457145 0.2550530559853103
+135627546 -1.08939539139418 0.2554374228314511
+135726580 -1.0894793000126366 0.25559320572905747
+135825614 -1.089724749815313 0.2558029694345293
+135924648 -1.0900760755093573 0.25602468027753184
+136023682 -1.0895139730970655 0.25600967954729126
+136122716 -1.0898019265937935 0.2562095472075173
+136221750 -1.0901795984747282 0.2564137730939922
+136320784 -1.0901038507219891 0.25608916445483504
+136419818 -1.0901526891652864 0.25646788278279486
+136518852 -1.0902377808809318 0.2565033655535513
+136617886 -1.0902969192322314 0.25677012267077937
+136716920 -1.0903604520421613 0.25665747584645004
+136815954 -1.0903949317899049 0.2569105417932966
+136914988 -1.0905055073532262 0.25700834792311816
+137014022 -1.0908118465799137 0.2566819828912328
+137113056 -1.0906508874314738 0.2568886840903761
+137212090 -1.090772087116757 0.25742515036592617
+137311124 -1.0907905138464526 0.2573067792831316
+137410158 -1.0907977715053432 0.25721568900491315
+137509192 -1.0911461721155242 0.2574760738577351
+137608226 -1.091333877536669 0.257618018401751
+137707260 -1.0910447960994176 0.2576489438207527
+137806294 -1.091490901540531 0.2578086869344372
+137905328 -1.0910095028312439 0.2576421555820629
+138004362 -1.091328386023552 0.258333233432631
+138103396 -1.091302055584148 0.25860569391728755
+138202430 -1.0909761655564356 0.25841994904088106
+138301464 -1.0916859024217693 0.25849644024424784
+138400498 -1.0918118935734966 0.25853996494807074
+138499532 -1.0922758127447427 0.25926260654716365
+138598566 -1.0919849108137663 0.25870209654512816
+138697600 -1.0919204328188343 0.2591369401700882
+138796634 -1.092030136191573 0.25887271818571217
+138895668 -1.0921570201927382 0.25934448504360924
+138994702 -1.091865765149955 0.2594348790692555
+139093736 -1.0924450714240512 0.25963500793633126
+139192770 -1.0926487648123997 0.2594506986303066
+139291804 -1.0923632772853553 0.2596737425139538
+139390838 -1.092528742686212 0.25990572772797194
+139489872 -1.0922509871989183 0.2603786974242444
+139588906 -1.0926967126178349 0.2601735871896251
+139687940 -1.0924975169433773 0.2600138488133297
+139786974 -1.0933036200340813 0.2600404372905358
+139886008 -1.092936772580514 0.26026895328395155
+139985042 -1.0932898614697708 0.26013529987710965
+140084076 -1.0928429146281176 0.2610558537423987
+140183110 -1.0926230900537968 0.26042874929401255
+140282144 -1.0932678982084485 0.261003452660345
+140381178 -1.0931508691939993 0.260889880162951
+140480212 -1.0933626275048451 0.26103459350563585
+140579246 -1.0934266098346122 0.2610940257533921
+140678280 -1.0936119475459136 0.261777029760267
+140777314 -1.0934248457946818 0.2612688116640346
+140876348 -1.0934965965513301 0.2618327534709479
+140975382 -1.093483587353143 0.26160466452987363
+141074416 -1.0939148849680347 0.2617463978469663
+141173450 -1.0937785645230813 0.26200498833324687
+141272484 -1.0941133140769992 0.26237916866914296
+141371518 -1.0940332990054262 0.26192833772518587
+141470552 -1.094330994151667 0.2621120980920328
+141569586 -1.0943163690023663 0.26244476460852556
+141668620 -1.0942851710497064 0.2626066594425985
+141767654 -1.0944987351791815 0.26267450310849605
+141866688 -1.0943855594257828 0.2626588251418038
+141965722 -1.0947279130777139 0.2631561206549621
+142064756 -1.094879241800203 0.262983979172651
+142163790 -1.0952011016109209 0.262908449902534
+142262824 -1.0949394112013433 0.2632894726823671
+142361858 -1.09504941369096 0.26302276072267605
+142460892 -1.0948873252644797 0.2627978704084063
+142559926 -1.0952053169529674 0.26354518725755877
+142658960 -1.0957307758253751 0.2638263838351226
+142757994 -1.0953383899783704 0.2634692248127482
+142857028 -1.0956204173613389 0.2643019795112052
+142956062 -1.0959276543644763 0.2640132402497369
+143055096 -1.0955392154127597 0.2639130186907991
+143154130 -1.0956572046558488 0.26424218318414017
+143253164 -1.0960265622125618 0.2641832214504725
+143352198 -1.0960150949466907 0.2646068992929944
+143451232 -1.0961250109970666 0.2643457235374666
+143550266 -1.0958686351402263 0.26495534361217116
+143649300 -1.0960574623468378 0.2648213466995888
+143748334 -1.096138742402503 0.26457409369907947
+143847368 -1.096502831741177 0.2650699013791715
+143946402 -1.0965328325932562 0.26495125854741375
+144045436 -1.0964433326351581 0.2648735629363469
+144144470 -1.0970097903661875 0.26561942868098176
+144243504 -1.0969910770017928 0.2658374257795
+144342538 -1.0970559777985995 0.2658252049446146
+144441572 -1.0968128405031008 0.26550529501017184
+144540606 -1.0968516831891262 0.26590863925649877
+144639640 -1.0970707842955851 0.2655384296230246
+144738674 -1.097043933752725 0.2658396477721866
+144837708 -1.097292523998471 0.26597572847931644
+144936742 -1.0972018795371716 0.26634079513649883
+145035776 -1.0976577569132828 0.2665238733712727
+145134810 -1.0972379837129786 0.26688969751113173
+145233844 -1.0979974432461668 0.26677591102295695
+145332878 -1.097575185621892 0.2666655348826894
+145431912 -1.0977861308294432 0.2671595304672374
+145530946 -1.098403756715557 0.2669461699952465
+145629980 -1.0984135774108839 0.266904777177397
+145729014 -1.098232421919104 0.26753681908021737
+145828048 -1.0983299569516243 0.26748084327431987
+145927082 -1.0981183403984092 0.26730537633909
+146026116 -1.0980878182477443 0.26799691276403664
+146125150 -1.098672388334976 0.2675759398266947
+146224184 -1.0987393331203812 0.2677537446615717
+146323218 -1.098678360686321 0.2675045645073858
+146422252 -1.0988785807431656 0.2675425659793348
+146521286 -1.0985024462901478 0.2676375464611503
+146620320 -1.0990906462241814 0.26830732678679675
+146719354 -1.0985989093757267 0.2681097427396463
+146818388 -1.0992087410211868 0.2684056550630874
+146917422 -1.0996019428234696 0.26817849086283085
+147016456 -1.0992281950471667 0.26867080321108044
+147115490 -1.099197758381978 0.2688997337451219
+147214524 -1.0993447639713942 0.26887422594210475
+147313558 -1.0996163628779778 0.2690436654012626
+147412592 -1.099873663199738 0.2693217166449054
+147511626 -1.099604275060612 0.26921871542172904
+147610660 -1.1000257457831364 0.2694321654548351
+147709694 -1.100028744742843 0.26929080347496653
+147808728 -1.099949895506928 0.2694315818344799
+147907762 -1.0999743820763594 0.26969677914685664
+148006796 -1.1000985679839086 0.2696640221675932
+148105830 -1.101005008837766 0.26970061241600607
+148204864 -1.1001027494895386 0.26993375282166493
+148303898 -1.1005488088138422 0.27036229340589546
+148402932 -1.1005365083323997 0.26993634686191764
+148501966 -1.1007483329642764 0.2706193044371305
+148601000 -1.1005985907603602 0.2706230511361099
+148700034 -1.1009430560728217 0.2705672316436669
+148799068 -1.1009507678079318 0.2707962728779179
+148898102 -1.1009525920071803 0.27103515330713535
+148997136 -1.1011199078625384 0.2709683193170659
+149096170 -1.1007912782562843 0.2708538929802446
+149195204 -1.10112008260733 0.2708272156958521
+149294238 -1.1013491114405802 0.27104686050571764
+149393272 -1.101549469154086 0.27166562221674256
+149492306 -1.1013564627646577 0.2717380097722005
+149591340 -1.101410835222421 0.2715651727595615
+149690374 -1.1017536507868697 0.2716507296226188
+149789408 -1.1021239737738788 0.2717807677323637
+149888442 -1.101821907600936 0.2719074844983472
+149987476 -1.1020170025193932 0.2719380333775972
+150086510 -1.1022706827349091 0.27133474203650026
+150185544 -1.1021086109096332 0.272160298100223
+150284578 -1.1026769666156258 0.27216039587104457
+150383612 -1.1026160505042617 0.2725491090268489
+150482646 -1.102529007622767 0.27233525541121434
+150581680 -1.1026153231231954 0.27270594487614047
+150680714 -1.103030992530955 0.27268562813018077
+150779748 -1.1028360574271139 0.27281715931165806
+150878782 -1.1025643760429318 0.27305357119506396
+150977816 -1.1029190196808176 0.2735152272802434
+151076850 -1.1030429136707498 0.27309201143194856
+151175884 -1.1030934591621386 0.27339076212794217
+151274918 -1.1032550050646104 0.273461008396507
+151373952 -1.1037174397978178 0.2732579964057658
+151472986 -1.1035687339095033 0.27355833789062695
+151572020 -1.103429269615397 0.27347526013897705
+151671054 -1.1038113937191842 0.27361029196654485
+151770088 -1.1042840540237182 0.2735588974509724
+151869122 -1.103958675376054 0.2743159353487839
+151968156 -1.1038207634398862 0.27381232700803754
+152067190 -1.104027065311978 0.2747309676595426
+152166224 -1.1039629657488963 0.2745495238716122
+152265258 -1.1041638323971614 0.2746713452758187
+152364292 -1.1043507674208533 0.2746261039156397
+152463326 -1.104340991244078 0.27478615495742303
+152562360 -1.1046882052364824 0.27456280139401434
+152661394 -1.1043122087469 0.2748967813537559
+152760428 -1.104586490177077 0.2749001475786895
+152859462 -1.104965972311483 0.27493471290974697
+152958496 -1.104780654133554 0.2753535612605242
+153057530 -1.1047516180390469 0.2758600206659691
+153156564 -1.104652179085977 0.2756431509884239
+153255598 -1.1050917493194026 0.2756341409671919
+153354632 -1.1050669386442675 0.27574056313809786
+153453666 -1.1051600956862218 0.275959775409341
+153552700 -1.1054072199457357 0.2758676938576699
+153651734 -1.1053570486026578 0.27620877851738496
+153750768 -1.1057946441744817 0.27585785252820777
+153849802 -1.10572479745844 0.27610309398155336
+153948836 -1.1055523875039683 0.2766954635420022
+154047870 -1.1058405534172917 0.2766667104681193
+154146904 -1.1058681201675173 0.27679142456083067
+154245938 -1.105586703892866 0.2763873966028149
+154344972 -1.1061934658091102 0.2767098572615787
+154444006 -1.1063633147107896 0.2768530378090537
+154543040 -1.1066855550620305 0.2771374116144739
+154642074 -1.1065728150457828 0.27711310079547263
+154741108 -1.1068940297960366 0.2772226990653155
+154840142 -1.1064042912038963 0.2771100331865771
+154939176 -1.1068403995984875 0.2770737154736309
+155038210 -1.1067403728291447 0.2774349996211297
+155137244 -1.1068512398380166 0.2771302642114549
+155236278 -1.1068338842945635 0.27731976864140934
+155335312 -1.1069279348830878 0.27741946411174995
+155434346 -1.10709487041733 0.2784797022967073
+155533380 -1.107091859776733 0.27773171865837915
+155632414 -1.1072128851772705 0.2781902686193327
+155731448 -1.1079550205146955 0.2785518888762619
+155830482 -1.1071606364381879 0.2783570836737419
+155929516 -1.1073536112046503 0.2784307948296219
+156028550 -1.1072481349855656 0.2785449672241636
+156127584 -1.1076001913740292 0.2791694243811819
+156226618 -1.107458551258002 0.27891121799265656
+156325652 -1.1082116812201672 0.27878688024192144
+156424686 -1.1077143287598343 0.2795120736427584
+156523720 -1.1085917482657297 0.27899881332603044
+156622754 -1.1081117611146782 0.27895447785889793
+156721788 -1.1076977162853383 0.2794878737555745
+156820822 -1.1084033526771047 0.2792392959563152
+156919856 -1.1083619891326617 0.27948562726421444
+157018890 -1.1086485867793279 0.27974648033295385
+157117924 -1.1085319879977553 0.27954187316039814
+157216958 -1.1087388178043955 0.27932344197725173
+157315992 -1.1094773181150164 0.2805021468896305
+157415026 -1.1092396765165882 0.28027506687789994
+157514060 -1.1092086156907166 0.2802075854617894
+157613094 -1.1088901078176787 0.2803949347695768
+157712128 -1.1089930949748363 0.27984583531044227
+157811162 -1.1089992041970187 0.280327839043867
+157910196 -1.109358497980124 0.28046286553651856
+158009230 -1.1094246625169366 0.28037496788289357
+158108264 -1.1098093971886185 0.2811017135692029
+158207298 -1.109289075872749 0.2804519661718578
+158306332 -1.1098211110238267 0.2813548871617465
+158405366 -1.1099397199520624 0.2811219049142321
+158504400 -1.1100976624283394 0.281100347248518
+158603434 -1.109728666087915 0.28098809207324926
+158702468 -1.1096486785684745 0.28120692216424276
+158801502 -1.1102328161186328 0.2816720484566634
+158900536 -1.1106460240711908 0.28161813577711614
+158999570 -1.1098948480373974 0.28121807582975394
+159098604 -1.110690573011739 0.28142191661835503
+159197638 -1.1110325106555214 0.2818901439457541
+159296672 -1.1103855170306625 0.2817920630290097
+159395706 -1.1106402136143516 0.282087198187914
+159494740 -1.1107015849905755 0.2824947594375689
+159593774 -1.1109719032608134 0.28255111615489703
+159692808 -1.1108508234844077 0.2827744593768462
+159791842 -1.110629629913074 0.2816638384749928
+159890876 -1.1109638218027347 0.28269576040749655
+159989910 -1.1106708995165542 0.28254163757407486
+160088944 -1.1113721884995125 0.282613719213749
+160187978 -1.111694661763684 0.2830422996685382
+160287012 -1.111705800027349 0.2831612539461503
+160386046 -1.1120440381970695 0.28302965725925155
+160485080 -1.1119013055350897 0.28305959310355616
+160584114 -1.1125021007705835 0.2832231832923634
+160683148 -1.1115292184874617 0.2825692107197231
+160782182 -1.1113755659266946 0.28300729550081405
+160881216 -1.1116946639761782 0.28399197990752345
+160980250 -1.1122620952482818 0.2836318326009523
+161079284 -1.1124198441470448 0.2835989744564308
+161178318 -1.112545341181477 0.28373663835812946
+161277352 -1.112634778979919 0.28457249346911384
+161376386 -1.1131091412289078 0.2840185445449697
+161475420 -1.1124132157968323 0.28371674594126073
+161574454 -1.1126343696137226 0.2840275883718255
+161673488 -1.1127407929074167 0.28433037369646463
+161772522 -1.112043669246326 0.2841497297702984
+161871556 -1.1131610314084444 0.28450574092903735
+161970590 -1.1122538503267811 0.28464548167140497
+162069624 -1.1129389338305886 0.2846920165706528
+162168658 -1.1124974127864458 0.2848087462755673
+162267692 -1.1134425235350103 0.28463753973896755
+162366726 -1.1137599955751163 0.2856278122337017
+162465760 -1.1139717061783632 0.28547628291911176
+162564794 -1.1129636976400348 0.28539615529856405
+162663828 -1.1134848567518085 0.28545784921897754
+162762862 -1.1135151467644633 0.28516993401973567
+162861896 -1.1136957692599578 0.2857938141158081
+162960930 -1.1135014100523701 0.2860160968097318
+163059964 -1.1142379935700935 0.2855939726450496
+163158998 -1.114077895496844 0.28557277585359314
+163258032 -1.1140611251484878 0.28563846575496715
+163357066 -1.11465977248481 0.28613993581148744
+163456100 -1.1138936652813605 0.2860119725070745
+163555134 -1.1142945902838493 0.2859719015748927
+163654168 -1.1146526632649125 0.2861339246260018
+163753202 -1.1145065088086041 0.286592158155787
+163852236 -1.1143602209252526 0.2862712754001456
+163951270 -1.1144321148357526 0.2868833107704147
+164050304 -1.1150267328819434 0.28657540102761214
+164149338 -1.1150894652330738 0.2863301883935099
+164248372 -1.114972536586033 0.28668847898544403
+164347406 -1.1152034676546523 0.2875233681996255
+164446440 -1.1155217805133886 0.28743996794002435
+164545474 -1.115489010627505 0.28678161759274234
+164644508 -1.11538703484122 0.2869819965687825
+164743542 -1.1153382716877713 0.2871334376287785
+164842576 -1.1160683676487693 0.287656420023086
+164941610 -1.116005672984957 0.2877482439847291
+165040644 -1.115831310350846 0.2876592169064161
+165139678 -1.1157572076800522 0.2879809912193313
+165238712 -1.1163572158177828 0.2876057175611725
+165337746 -1.1159013428680578 0.2882669304485134
+165436780 -1.1162318787377878 0.28757645814440386
+165535814 -1.1160081800733257 0.2879722641866796
+165634848 -1.1166100607937048 0.28808370438929887
+165733882 -1.1168382603429294 0.28853114912513333
+165832916 -1.116026430200644 0.28837597425553474
+165931950 -1.1168966904042983 0.2888177799774004
+166030984 -1.1173640081657727 0.2890451373354331
+166130018 -1.1167513585631699 0.28898224720507654
+166229052 -1.1169082292144732 0.2886976168443265
+166328086 -1.1169535978906544 0.28877865965953126
+166427120 -1.1169484318688865 0.2886301218479799
+166526154 -1.1172717793935807 0.28865513815293276
+166625188 -1.1170248867123356 0.28911650320979165
+166724222 -1.117297873505457 0.28884253007280963
+166823256 -1.117146589557088 0.28926134680926346
+166922290 -1.1174249052392906 0.2897350085312347
+167021324 -1.1175541709733765 0.2899958598352369
+167120358 -1.118051322307829 0.2899408434750229
+167219392 -1.118254305638266 0.2898663041403726
+167318426 -1.117986994875 0.2891178373886358
+167417460 -1.1178813962016838 0.2900055148689466
+167516494 -1.1179735205205334 0.2900459016716628
+167615528 -1.1181244447136585 0.2903012953861953
+167714562 -1.1184438704643274 0.2906586508957937
+167813596 -1.1179246889638108 0.2897560278393028
+167912630 -1.118824909030792 0.29085464147962226
+168011664 -1.1188504344714187 0.29084204092812566
+168110698 -1.1179872068727152 0.29145604264503405
+168209732 -1.1184930967987914 0.2903206041470585
+168308766 -1.1188626201063039 0.2912811041997735
+168407800 -1.118415569807825 0.29065392920693844
+168506834 -1.1189224852739525 0.2905555469874269
+168605868 -1.1187017529260963 0.2914475377300552
+168704902 -1.1182741099120332 0.2910158446640769
+168803936 -1.11994520300074 0.2915166643009378
+168902970 -1.119201547667262 0.29099352729303063
+169002004 -1.1191384088663459 0.2913772133496614
+169101038 -1.120288049926435 0.29119080108975715
+169200072 -1.1203639237384118 0.2914613575831161
+169299106 -1.1194483071532828 0.2923769174799494
+169398140 -1.119026177575871 0.2919279941773219
+169497174 -1.1196888598499313 0.2918635775161674
+169596208 -1.1202747724783841 0.2921488043944391
+169695242 -1.1204135822223094 0.2912483639949614
+169794276 -1.120601471978484 0.29240682921443584
+169893310 -1.1202292566002474 0.29249323065485916
+169992344 -1.1202726513263985 0.2919862745220331
+170091378 -1.1199946551728475 0.29226303710347934
+170190412 -1.1206549225485627 0.29285094247947424
+170289446 -1.1207223039000416 0.2928752189990259
+170388480 -1.1202028337324226 0.29274867427335366
+170487514 -1.1206621480400778 0.29276847122105865
+170586548 -1.1207064317746986 0.2934242108011977
+170685582 -1.1211123345208838 0.29291437764968015
+170784616 -1.121215224952147 0.2932905827363671
+170883650 -1.1211023376715044 0.2928196110955396
+170982684 -1.1214274964961901 0.2929170053842555
+171081718 -1.1216506032289113 0.2929835680989561
+171180752 -1.121742161985881 0.2932943851397643
+171279786 -1.1210272090595348 0.29358186577453843
+171378820 -1.1217522986567552 0.2937037484400883
+171477854 -1.121570025018921 0.2936345983056953
+171576888 -1.121924195179635 0.29365355618646627
+171675922 -1.1220172753380018 0.29356616163214494
+171774956 -1.1214613389300656 0.2937808328881465
+171873990 -1.1219005501938735 0.2939438579608581
+171973024 -1.1222442274131248 0.2935353575782235
+172072058 -1.121786257138475 0.29395042028461327
+172171092 -1.1226755034472695 0.2945284234290935
+172270126 -1.1223054280281242 0.2946047210334939
+172369160 -1.1225443537045645 0.2943115591181774
+172468194 -1.1221756711261277 0.2951591047915032
+172567228 -1.12253604528725 0.2944864374582865
+172666262 -1.1223239515700487 0.2952450750647709
+172765296 -1.122895560192586 0.2949873913811133
+172864330 -1.1224645710644252 0.2946133706389781
+172963364 -1.1224428063041652 0.29459780745254194
+173062398 -1.1227070729149489 0.29443214081823815
+173161432 -1.1230683577328435 0.2955291023467005
+173260466 -1.1232334841968903 0.29512955826232806
+173359500 -1.122559991011217 0.2955819289970757
+173458534 -1.1226645564044364 0.294993105265526
+173557568 -1.1231639972724015 0.2958852369417611
+173656602 -1.1237278389864664 0.2954259953630152
+173755636 -1.1231349298774147 0.29571746319704384
+173854670 -1.1237165433713259 0.29606299257314334
+173953704 -1.1239142134020799 0.2956965910932977
+174052738 -1.1237076475799777 0.295763402988605
+174151772 -1.123375134624516 0.29615727224135185
+174250806 -1.1246405839724 0.29694151663852125
+174349840 -1.1240458442150107 0.29678780593642734
+174448874 -1.1242881291955384 0.2966057700469284
+174547908 -1.1241975901380643 0.2975165727185441
+174646942 -1.1255699495940532 0.2963270678819262
+174745976 -1.1243870005999141 0.29662698957838973
+174845010 -1.1242735824026744 0.2963375723917603
+174944044 -1.1244891201115221 0.2968110490534526
+175043078 -1.1248056284829933 0.2968763589350464
+175142112 -1.1243957230950827 0.2965096027880551
+175241146 -1.125392842609262 0.29671362707962684
+175340180 -1.1253527730659265 0.29695174042383155
+175439214 -1.1249685807977508 0.29732517944577574
+175538248 -1.1252290274873082 0.2969778930532628
+175637282 -1.124545833463361 0.2972301217795531
+175736316 -1.125375187734289 0.2974181138311134
+175835350 -1.1249400942204095 0.2975492023996863
+175934384 -1.1251774542521167 0.2975792183739165
+176033418 -1.1254580788457427 0.2977785350980145
+176132452 -1.1254636715073765 0.29767584008097747
+176231486 -1.1256054051045186 0.2982016293351003
+176330520 -1.1260280669592961 0.2977514489726406
+176429554 -1.1262177583980664 0.298060440876072
+176528588 -1.1260067756421113 0.29794102200975575
+176627622 -1.1258403839368076 0.29767060552685576
+176726656 -1.1259515816120953 0.2983494495584792
+176825690 -1.1263596150776847 0.29867066166413575
+176924724 -1.1265479120572677 0.29845001081111855
+177023758 -1.125988827105286 0.2985193785863546
+177122792 -1.1263792078701074 0.2989777219000169
+177221826 -1.1266401297150543 0.29887864949281434
+177320860 -1.1269851085819267 0.299039498622123
+177419894 -1.1255433945624598 0.2987058542362215
+177518928 -1.1268628598491284 0.2987106195208795
+177617962 -1.1269144278329837 0.29907062108777205
+177716996 -1.1266731627563995 0.29905105707325563
+177816030 -1.1265826743429088 0.3002304408547334
+177915064 -1.12796985749757 0.30018880454785646
+178014098 -1.1272304193706233 0.29915349196124075
+178113132 -1.1270601588780407 0.3000245828291767
+178212166 -1.1274562582396803 0.29949798047772874
+178311200 -1.127031979331942 0.3003137707458184
+178410234 -1.1277699350827712 0.3000462525523944
+178509268 -1.127710509915176 0.30008501337556015
+178608302 -1.127154144271844 0.2999449438610998
+178707336 -1.1272996367507078 0.2997506677025509
+178806370 -1.1276615578869174 0.30009468210837803
+178905404 -1.1287175915134657 0.29981949882257025
+179004438 -1.1285926673390987 0.3000498584699417
+179103472 -1.1280862644395757 0.3003303803231283
+179202506 -1.1279899272860394 0.30121792056439645
+179301540 -1.1280432223731829 0.3001142120133645
+179400574 -1.1283003245382408 0.3006616719519928
+179499608 -1.1282335904210303 0.30113566550604687
+179598642 -1.1285623084799643 0.30036859358649837
+179697676 -1.1290861452142646 0.300871328448711
+179796710 -1.1286487020761231 0.30087900066926065
+179895744 -1.1293260639654503 0.3009552588886717
+179994778 -1.1289587330505118 0.3009989500361808
+180093812 -1.1282724756670508 0.3013458981486781
+180192846 -1.1289752592590891 0.301265271188679
+180291880 -1.129091193384986 0.30125743494468044
+180390914 -1.1287528113559693 0.30154860656159693
+180489948 -1.1289300514798855 0.30135181201293454
+180588982 -1.130045413986705 0.3020965552272707
+180688016 -1.1293124061884574 0.3017332635376238
+180787050 -1.1296132517682789 0.30217974602973363
+180886084 -1.1297261151885374 0.3022080706377377
+180985118 -1.1305670574656894 0.30148087785811767
+181084152 -1.1300703683323594 0.30195453603899497
+181183186 -1.1297999273971613 0.30161434292198613
+181282220 -1.129875838006314 0.30169058305897484
+181381254 -1.1299887872182137 0.3025397523121176
+181480288 -1.1300064436738142 0.30240178934052825
+181579322 -1.1307119989836705 0.3023240619297681
+181678356 -1.1305994315825094 0.3020269000186965
+181777390 -1.1298689676300795 0.30238361721897133
+181876424 -1.130857153104766 0.3022210996532582
+181975458 -1.1301034987258372 0.3027332751062421
+182074492 -1.1299252185049589 0.3030945370507902
+182173526 -1.1303106308634925 0.30310387492532326
+182272560 -1.1312240505964353 0.3029856688460085
+182371594 -1.1307870100496955 0.303351098330457
+182470628 -1.1304806780527574 0.3028754832132692
+182569662 -1.131335020817709 0.3031580340562041
+182668696 -1.1306041913190705 0.3033908781850423
+182767730 -1.1306096344593088 0.30369269163361035
+182866764 -1.1318998781620127 0.3038865894709288
+182965798 -1.1319582929532368 0.30314852635010986
+183064832 -1.1314341912370562 0.3036554127691506
+183163866 -1.13128930101824 0.3036818562548073
+183262900 -1.1305832227956372 0.3048837009260779
+183361934 -1.1321073166917817 0.3040330957659317
+183460968 -1.1318484989275486 0.3039332026792282
+183560002 -1.1317667494626449 0.3044397651241973
+183659036 -1.1320644673646334 0.3039620276158183
+183758070 -1.1320447028566591 0.30430304909298306
+183857104 -1.1320760446962548 0.3042948218941341
+183956138 -1.1320273308746585 0.30493694216739886
+184055172 -1.1323892054307598 0.304413014264575
+184154206 -1.1319757739008598 0.30499216094350895
+184253240 -1.1320245524177106 0.3047630087224368
+184352274 -1.1326202321615084 0.304818973011696
+184451308 -1.1320944641237858 0.3047276443264626
+184550342 -1.1319190284190228 0.3050155282861202
+184649376 -1.1322596035341925 0.3053480856527534
+184748410 -1.1325740502401407 0.30531338680906883
+184847444 -1.1323529103005245 0.3056294525272027
+184946478 -1.132460438038011 0.3051810630338924
+185045512 -1.1326196920968112 0.3051423418717387
+185144546 -1.1328699483513494 0.30541662651665463
+185243580 -1.1330941667554775 0.3060776409315163
+185342614 -1.1329497232567676 0.30580937143638504
+185441648 -1.1328657676394638 0.3052602483566314
+185540682 -1.13373615677779 0.3057536116063623
+185639716 -1.1328782266779693 0.30554705578015984
+185738750 -1.1327905235474052 0.30588090626827413
+185837784 -1.1333119667500962 0.30622226075159276
+185936818 -1.1334706464714586 0.30652191129507883
+186035852 -1.1339892501324134 0.3062877492699023
+186134886 -1.13422614309468 0.3061411215654382
+186233920 -1.1333324279757506 0.30570605933936
+186332954 -1.1330427984914833 0.3055111349756
+186431988 -1.1340950660462419 0.3069493101834258
+186531022 -1.133882781115175 0.30710927701071233
+186630056 -1.1342362638389667 0.3069456953764585
+186729090 -1.134367495301753 0.30706828820198223
+186828124 -1.133851809584551 0.30715537201809323
+186927158 -1.1344773472375325 0.307009560947124
+187026192 -1.1344957886762426 0.30625762826519787
+187125226 -1.134501969962531 0.30653274114497253
+187224260 -1.1347101153757992 0.3073644792805423
+187323294 -1.1349451390045286 0.30718860905548395
+187422328 -1.1341823102659843 0.30692252191316444
+187521362 -1.1358487567520346 0.30766701713906813
+187620396 -1.1350373745666464 0.30754566321965693
+187719430 -1.1353908513290845 0.30738476362684747
+187818464 -1.1352135033861497 0.30713835752246277
+187917498 -1.1353521328537506 0.3075761079361442
+188016532 -1.1352729113383144 0.30813834252733074
+188115566 -1.135049274106431 0.3078818715299157
+188214600 -1.135424003127081 0.30827797697696935
+188313634 -1.1349606219288009 0.3080846872191764
+188412668 -1.1357428851106948 0.3080745275002801
+188511702 -1.135675804721977 0.3082887738036144
+188610736 -1.1363026504346496 0.3075460587912144
+188709770 -1.1356973453442842 0.30790966441157086
+188808804 -1.1361950874871694 0.3084002997109658
+188907838 -1.1364041468003854 0.3086833769072771
+189006872 -1.1354780262901047 0.30827056447804674
+189105906 -1.1359377784836568 0.30766810837080305
+189204940 -1.1356371538716876 0.30864648117278554
+189303974 -1.1359523215955942 0.30861076716148494
+189403008 -1.1362192830525883 0.3086319891535062
+189502042 -1.1363578289171927 0.30916770509639824
+189601076 -1.1364981778832444 0.3088663839749032
+189700110 -1.1364900628128976 0.30858777394371173
+189799144 -1.1363322787141912 0.3092513581676197
+189898178 -1.1371796172858772 0.30880432073847347
+189997212 -1.136731003662868 0.3093256453026029
+190096246 -1.138181877136298 0.30940082317221806
+190195280 -1.1372890788914745 0.3095953741167748
+190294314 -1.1366649529262212 0.3095976529091902
+190393348 -1.136769730650257 0.309489145085741
+190492382 -1.1373678230119686 0.3105205218059645
+190591416 -1.1379512490433812 0.30924986890996803
+190690450 -1.1375785608351714 0.30978874731501954
+190789484 -1.137169569627655 0.3095884876194883
+190888518 -1.1381686594843574 0.31000166139432483
+190987552 -1.1369241723149277 0.30988295793727216
+191086586 -1.1376795998091234 0.3103244169352854
+191185620 -1.1376024480071076 0.30971482186319543
+191284654 -1.137634859884772 0.310314116351195
+191383688 -1.1376984186725385 0.31055059297651855
+191482722 -1.1380862930702156 0.31077478244428025
+191581756 -1.1381568894921632 0.310605658456901
+191680790 -1.1388136909279858 0.3102927118023206
+191779824 -1.138332693468008 0.31151366532310926
+191878858 -1.1383405155814823 0.3109931335933527
+191977892 -1.1384577075926599 0.31111512244971407
+192076926 -1.1382170605006405 0.31157842473802516
+192175960 -1.1385575301958824 0.3112343661517124
+192274994 -1.1386590171009385 0.3113750754288607
+192374028 -1.1383189553234183 0.3115625691996653
+192473062 -1.1378850988709976 0.3113633491045145
+192572096 -1.1384884026601483 0.31094740950453553
+192671130 -1.1385044223513399 0.31061984561289463
+192770164 -1.1385938196426946 0.3126963841327324
+192869198 -1.1399991204853053 0.31072627970954836
+192968232 -1.1391080680430372 0.31177332762602533
+193067266 -1.1383346410348207 0.31186869995989724
+193166300 -1.1393851340569008 0.3117322401845929
+193265334 -1.138885689245097 0.3112972124194288
+193364368 -1.1390871457677934 0.3118865093530165
+193463402 -1.1394980865716318 0.3122354054250928
+193562436 -1.1391978895650352 0.3128537887352271
+193661470 -1.1395137917144735 0.3122406218796766
+193760504 -1.1390820536602193 0.31203592314263756
+193859538 -1.1397171600092573 0.3133746487478381
+193958572 -1.1401654441828997 0.31192944874694034
+194057606 -1.139786452174782 0.31261295761953417
+194156640 -1.139886205359899 0.3125518527030153
+194255674 -1.140343769201029 0.3121846608710861
+194354708 -1.1402673881831602 0.3124274945108015
+194453742 -1.1406217472109246 0.312530535087353
+194552776 -1.1409837113746715 0.31295778615303266
+194651810 -1.1406863187840646 0.313541191853737
+194750844 -1.1404754481449602 0.31332613125403636
+194849878 -1.1408181085674574 0.31298503398638433
+194948912 -1.1408659663435048 0.3125024489596112
+195047946 -1.141463866977237 0.31305791021304563
+195146980 -1.1405860532380432 0.31412985553128153
+195246014 -1.1408377580190026 0.3133618286579525
+195345048 -1.1405544601844326 0.31327036232275507
+195444082 -1.141850350891299 0.3130869334152068
+195543116 -1.1405216114496775 0.31322591227116175
+195642150 -1.1405784637949312 0.3130501610007566
+195741184 -1.1413412847431312 0.31309393046890366
+195840218 -1.1407623994863498 0.3135361554410034
+195939252 -1.1407638747546298 0.3139540364368516
+196038286 -1.1416503711536177 0.3131130125190026
+196137320 -1.1412787695692475 0.31353526624188816
+196236354 -1.1406190362926962 0.31331303876881916
+196335388 -1.1417527595436088 0.31251823915363963
+196434422 -1.1409876485952801 0.31435024162022557
+196533456 -1.1413344606581073 0.31376136756837936
+196632490 -1.1422426438127435 0.3132084903637231
+196731524 -1.1418342937431707 0.3146226215339604
+196830558 -1.1419857147682944 0.3139895419943126
+196929592 -1.1415208550806581 0.31474773531394684
+197028626 -1.142144859163866 0.3149982780521657
+197127660 -1.1421420700225366 0.31489602195490457
+197226694 -1.1420449852690977 0.3144613495399211
+197325728 -1.1424157671692992 0.31448846321301516
+197424762 -1.1429544435439583 0.3147737397689241
+197523796 -1.1423216479590275 0.31442470918919835
+197622830 -1.1431238757011346 0.3146888460251564
+197721864 -1.1429964596079685 0.31534702306078677
+197820898 -1.1429585373338007 0.3147502428090518
+197919932 -1.1430019490037946 0.3123716657298953
+198018966 -1.1431135666023196 0.3151554927847907
+198118000 -1.1426234427405872 0.31536177000840726
+198217034 -1.143170326212544 0.31497479847657694
+198316068 -1.1426898448266363 0.31492610924236664
+198415102 -1.143831732213643 0.3155470635177979
+198514136 -1.1433148538829323 0.3153722679490706
+198613170 -1.1433526317135656 0.31565884095417707
+198712204 -1.1430751548690086 0.3152742535611089
+198811238 -1.1437215013130642 0.31551568355900234
+198910272 -1.1436003987998464 0.3160178989204372
+199009306 -1.143159538733492 0.3153054493534358
+199108340 -1.1431121030541225 0.31536391732751
+199207374 -1.1438411651167648 0.31495795766680007
+199306408 -1.1427555402434721 0.3168665036282917
+199405442 -1.144151154253777 0.31657034081407365
+199504476 -1.1448735790482554 0.3164768709340913
+199603510 -1.1441517603715132 0.31654191191795494
+199702544 -1.1440924708592775 0.3159138849666406
+199801578 -1.1442956944945173 0.3163037471860155
+199900612 -1.1441406325898404 0.316568541070503
+199999646 -1.1443876877786179 0.3158408069462067
diff --git a/test/test_formatSweepFrequency.py b/test/test_formatSweepFrequency.py
index a3b52315..5db3d595 100644
--- a/test/test_formatSweepFrequency.py
+++ b/test/test_formatSweepFrequency.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/test/test_formatting.py b/test/test_formatting.py
index 86997b2b..8f263f6e 100644
--- a/test/test_formatting.py
+++ b/test/test_formatting.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -52,6 +52,15 @@ def test_format_frequency(self):
self.assertEqual(fmt.format_frequency_short(0), '0.000Hz')
self.assertEqual(fmt.format_frequency_short(-1), '-1.000Hz')
+ self.assertEqual(fmt.format_frequency_chart(1), '1.000')
+ self.assertEqual(fmt.format_frequency_chart(12), '12.00')
+ self.assertEqual(fmt.format_frequency_chart(123), '123.0')
+ self.assertEqual(fmt.format_frequency_chart(1234), '1.234k')
+ self.assertEqual(fmt.format_frequency_chart(1234567), '1.235M')
+ self.assertEqual(fmt.format_frequency_chart(1234567890), '1.235G')
+ self.assertEqual(fmt.format_frequency_chart(0), '0.000')
+ self.assertEqual(fmt.format_frequency_chart(-1), '-1.000')
+
def test_format_frequency_inputs(self):
self.assertEqual(fmt.format_frequency_inputs(1), '1Hz')
self.assertEqual(fmt.format_frequency_inputs(12), '12Hz')
diff --git a/test/test_parseFrequency.py b/test/test_parseFrequency.py
index fe74c8f1..e2736340 100644
--- a/test/test_parseFrequency.py
+++ b/test/test_parseFrequency.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/test/test_rftools.py b/test/test_rftools.py
index 6890727a..f2ece9ba 100644
--- a/test/test_rftools.py
+++ b/test/test_rftools.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/test/test_sitools.py b/test/test_sitools.py
index a0ca4680..f968a19a 100644
--- a/test/test_sitools.py
+++ b/test/test_sitools.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -17,11 +17,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
import unittest
+from math import inf
+from decimal import Decimal # Needed for test_representation()
# Import targets to be tested
from NanoVNASaver.SITools import Format, Value
-from decimal import Decimal
-from math import inf
F_DEFAULT = Format()
diff --git a/test/test_sweep.py b/test/test_sweep.py
index ed1762f2..4f1539a9 100644
--- a/test/test_sweep.py
+++ b/test/test_sweep.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/test/test_touchstone.py b/test/test_touchstone.py
index 0dc0ea5d..d3e9e928 100644
--- a/test/test_touchstone.py
+++ b/test/test_touchstone.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -63,18 +63,18 @@ def test_load(self):
ts = Touchstone("./test/data/valid.s1p")
ts.load()
self.assertEqual(str(ts.opts), "# HZ S RI R 50")
- self.assertEqual(len(ts.s11data), 1010)
- self.assertEqual(len(ts.s21data), 0)
+ self.assertEqual(len(ts.s11), 1010)
+ self.assertEqual(len(ts.s21), 0)
self.assertEqual(ts.r, 50)
ts = Touchstone("./test/data/valid.s2p")
ts.load()
ts.gen_interpolation()
self.assertEqual(str(ts.opts), "# HZ S RI R 50")
- self.assertEqual(len(ts.s11data), 1020)
- self.assertEqual(len(ts.s21data), 1020)
- self.assertEqual(len(ts.s12data), 1020)
- self.assertEqual(len(ts.s22data), 1020)
+ self.assertEqual(len(ts.s11), 1020)
+ self.assertEqual(len(ts.s21), 1020)
+ self.assertEqual(len(ts.s12), 1020)
+ self.assertEqual(len(ts.s22), 1020)
self.assertIn("! Vector Network Analyzer VNA R2", ts.comments)
self.assertEqual(ts.min_freq(), 500000)
self.assertEqual(ts.max_freq(), 900000000)
@@ -107,6 +107,14 @@ def test_load(self):
ts.load()
self.assertRegex(cm.output[0], "No such file or directory")
+ def test_swap(self):
+ ts = Touchstone("./test/data/valid.s2p")
+ ts.load()
+ s11, s21, s12, s22 = ts.sdata
+ ts.swap()
+ s11_, s21_, s12_, s22_ = ts.sdata
+ self.assertEqual([s11_, s21_, s12_, s22_] ,[s22, s12, s21, s11])
+
def test_db_conversation(self):
ts_db = Touchstone("./test/data/attenuator-0643_DB.s2p")
ts_db.load()
@@ -114,12 +122,12 @@ def test_db_conversation(self):
ts_ri.load()
ts_ma = Touchstone("./test/data/attenuator-0643_MA.s2p")
ts_ma.load()
- self.assertEqual(len(ts_db.s11data), len(ts_ri.s11data))
- for dps_db, dps_ri in zip(ts_db.s11data, ts_ri.s11data):
+ self.assertEqual(len(ts_db.s11), len(ts_ri.s11))
+ for dps_db, dps_ri in zip(ts_db.s11, ts_ri.s11):
self.assertAlmostEqual(dps_db.z, dps_ri.z, places=5)
- self.assertEqual(len(ts_db.s11data), len(ts_ma.s11data))
- for dps_db, dps_ma in zip(ts_db.s11data, ts_ma.s11data):
+ self.assertEqual(len(ts_db.s11), len(ts_ma.s11))
+ for dps_db, dps_ma in zip(ts_db.s11, ts_ma.s11):
self.assertAlmostEqual(dps_db.z, dps_ma.z, places=5)
def test_load_scikit(self):
@@ -137,21 +145,21 @@ def test_load_scikit(self):
'WARNING:NanoVNASaver.Touchstone:Reordering data',
])
self.assertEqual(str(ts.opts), "# HZ S RI R 50")
- self.assertEqual(len(ts.s11data), 101)
+ self.assertEqual(len(ts.s11), 101)
self.assertIn("!freq ReS11 ImS11 ReS21 ImS21 ReS12 ImS12 ReS22 ImS22",
ts.comments)
def test_setter(self):
ts = Touchstone("")
dp_list = [Datapoint(1, 0.0, 0.0), Datapoint(3, 1.0, 1.0)]
- ts.s11data = dp_list[:]
- ts.s21data = dp_list[:]
- ts.s12data = dp_list[:]
- ts.s22data = dp_list[:]
- self.assertEqual(ts.s11data, dp_list)
- self.assertEqual(ts.s21data, dp_list)
- self.assertEqual(ts.s12data, dp_list)
- self.assertEqual(ts.s22data, dp_list)
+ ts.s11 = dp_list[:]
+ ts.s21 = dp_list[:]
+ ts.s12 = dp_list[:]
+ ts.s22 = dp_list[:]
+ self.assertEqual(ts.s11, dp_list)
+ self.assertEqual(ts.s21, dp_list)
+ self.assertEqual(ts.s12, dp_list)
+ self.assertEqual(ts.s22, dp_list)
self.assertEqual(ts.min_freq(), 1)
self.assertEqual(ts.max_freq(), 3)
ts.gen_interpolation()
@@ -182,6 +190,6 @@ def test_save(self):
ts.filename = ""
self.assertRaises(FileNotFoundError, ts.save)
- ts.s11data[0] = Datapoint(100, 0.1, 0.1)
+ ts.s11[0] = Datapoint(100, 0.1, 0.1)
self.assertRaisesRegex(
LookupError, "Frequencies of sdata not correlated", ts.saves, 4)
diff --git a/test/test_version.py b/test/test_version.py
index 17312ab1..ac41c171 100644
--- a/test/test_version.py
+++ b/test/test_version.py
@@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020 NanoVNA-Saver Authors
+# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by