Compare commits

...
Sign in to create a new pull request.

7 commits

Author SHA1 Message Date
da651c7966 aangepaste start en config
All checks were successful
Git Backup to WebDAV / print-content (push) Successful in 7s
2026-05-30 11:18:08 +02:00
22eb53c068 Na R27 en R28 aanpassing: gebruikt R=331
All checks were successful
Git Backup to WebDAV / print-content (push) Successful in 15s
2026-05-28 22:13:38 +02:00
bc7cb4d360 added noise measurements
All checks were successful
Git Backup to WebDAV / print-content (push) Successful in 16s
2026-05-18 08:41:35 +02:00
a1798d1a29 Added backup workflow
All checks were successful
Git Backup to WebDAV / print-content (push) Successful in 6s
2026-04-15 21:10:12 +02:00
aca4f25ed6 fixed the indentation typo 2026-04-03 16:22:58 +02:00
73e8582835 oud algoritme van 17-mrt-2026 maar dan met versienummer support 2026-04-03 16:19:30 +02:00
f0cc0a2b93 tested and tuned on Veghel Lab system 2026-04-03 12:36:20 +02:00
4 changed files with 131 additions and 53 deletions

View file

@ -0,0 +1,39 @@
name: Git Backup to WebDAV
on:
push:
# This ensures it runs on every branch push
branches:
- '**'
jobs:
print-content:
runs-on: debian-latest
steps:
- name: checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: archive repository
run: |
PROJECT_NAME=$(echo "${{ github.repository }}" | cut -d'/' -f2)
BRANCH_NAME=$(echo "${{ github.ref_name }}" | sed 's/\//-/g')
TIMESTAMP=$(date +'%Y-%m-%d_%H-%M')
# Create the local variable
FINAL_NAME="${PROJECT_NAME}_${BRANCH_NAME}_${TIMESTAMP}.tar.gz"
# CRITICAL: Save it for the next step
echo "PROJECTNAME=$PROJECT_NAME" >> $GITHUB_ENV
echo "FILENAME=$FINAL_NAME" >> $GITHUB_ENV
# Create the archive
git archive --format=tar.gz -v -o "$FINAL_NAME" HEAD
echo "Archive created: $FINAL_NAME"
- name: Upload via Curl
run: |
ls -lh "$FILENAME"
curl -T "$FILENAME" \
-u "${{ secrets.WEBDAV_USER }}:${{ secrets.WEBDAV_PASSWORD }}" \
"${{ secrets.WEBDAV_URL }}/$PROJECTNAME/$FILENAME"

View file

@ -1,9 +1,12 @@
# Configuration file for scanner service
# it is automatically re-loaded between scans
start_freq: 10000.0
stop_freq: 0.1
start_freq: 4000
stop_freq: 0.4
freq_step_multiply: 0.95
allowed_noise_level: 15
skip_scans_for_noisy_freqs: 3
ampl: 1600
interference_freq: 0.052
interference_bandwidth: 0.008
noise_scan: False
hw_version: "2026-05-28" # aangepaste R27 en R28 voor boven 700A
scan_comment: "normale full scan met verbeterde PCB (nu goed boven 700A)"

View file

@ -12,21 +12,22 @@ from datetime import datetime
import yaml
import os
VersionScripSoftware = "2026-05-28"
VersionDspSoftware = "Unknown"
data_rows = [] # global 2-D list
state = {}
blacklist = []
Inoise_baseline = 0.1
PATTERN = re.compile(
r"Va=(?P<Va>-?\d+\.?\d*)\s+"
r"Vp=(?P<Vp>-?\d+\.?\d*)\s+\|\s+"
r"Ia=(?P<Ia>-?\d+\.?\d*)\s+"
r"Ip=(?P<Ip>-?\d+\.?\d*)\s+\|\s+"
r"ZR=(?P<ZR>-?\d+\.?\d*)\s+"
r"ZX=(?P<ZX>-?\d+\.?\d*)"
r".*?irq=\d+\s+" # Skip to 'irq=', match the first digits and space
r"(?P<adc_vmin>[0-9a-fA-F]+)-" # First hex
r"(?P<adc_vmax>[0-9a-fA-F]+)\s+" # Second hex
r"(?P<adc_imin>[0-9a-fA-F]+)-" # Third hex
r"(?P<adc_imax>[0-9a-fA-F]+)" # Fourth hex
r"Va=(?P<Va>[\d.-]+)\s+Vp=(?P<Vp>[\d.-]+)\s*\|\s*"
r"Ia=(?P<Ia>[\d.-]+)\s+Ip=(?P<Ip>[\d.-]+)\s*\|\s*"
r"ZR=(?P<ZR>[\d.-]+)\s+ZX=(?P<ZX>[\d.-]+).*?\|\s*"
r".*?irq=\w+\s+"
r"(?P<adc_vmin>[0-9a-fA-F]+)-(?P<adc_vmax>[0-9a-fA-F]+)\s+"
r"(?P<adc_imin>[0-9a-fA-F]+)-(?P<adc_imax>[0-9a-fA-F]+).*?\|\s*"
r"nv=(?P<nv>[\d.-]+)\s+ni=(?P<ni>[\d.-]+)\s*\|\s*"
)
config_path = '/home/bart/python-scanner/config.yaml'
@ -54,21 +55,36 @@ conn_str = (
)
def dump_into_database():
print("dump into database\n")
try:
with pyodbc.connect(conn_str) as conn:
cursor = conn.cursor()
sweep_insert_time = datetime.now()
print("dump into database\n")
# Add scan details
cursor.execute("""
IF NOT EXISTS (
SELECT 1 FROM ScanDetails WHERE StartTimeOfSweep = ?
)
INSERT INTO ScanDetails
(StartTimeOfSweep, VersionScanScript, VersionDspSoftware, VersionHardware, Comment)
VALUES (?, ?, ?, ?, ?)
""", sweep_insert_time,
sweep_insert_time, VersionScripSoftware, VersionDspSoftware, state["hw_version"], state["scan_comment"])
print("commit1\n")
conn.commit()
with pyodbc.connect(conn_str) as conn:
cursor = conn.cursor()
# Prepare the data: SQL Server expects the columns in order.
if state["noise_scan"]==False:
# regular scan data
formatted_rows = [
[sweep_insert_time, r[0], r[5], r[6], r[1], r[2], r[3], r[4]]
[sweep_insert_time, r[0], r[5], r[6], r[1], r[2], r[3], r[4], r[11], r[12]]
for r in data_rows
]
sql = """
INSERT INTO SequenceValues (StartTimeOfSweep, Freq, ZR, ZX, Vampl, Vphase, Iampl, Iphase)
VALUES (?, ?, ?, ?, ?, ?, ?, ?) \
INSERT INTO SequenceValues (StartTimeOfSweep, Freq, ZR, ZX, Vampl, Vphase, Iampl, Iphase, Vnoise, Inoise)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) \
"""
else:
# noise scan data
@ -87,7 +103,7 @@ def dump_into_database():
cursor.fast_executemany = False # needs less RAM
cursor.executemany(sql, formatted_rows)
print("commit\n")
print("commit2\n")
conn.commit()
print(f"Successfully inserted {len(formatted_rows)} rows.")
@ -134,17 +150,19 @@ def extract_to_dataframe(line):
if not match:
return # silently ignore malformed lines
row = [
float(state["freq"]),
float(match.group("Va")),
float(match.group("Vp")),
float(match.group("Ia")),
float(match.group("Ip")),
float(match.group("ZR")),
float(match.group("ZX")),
int(match.group("adc_vmin"), 16),
int(match.group("adc_vmax"), 16),
int(match.group("adc_imin"), 16),
int(match.group("adc_imax"), 16),
float(state["freq"]), # row 0
float(match.group("Va")), # row 1
float(match.group("Vp")), # row 2
float(match.group("Ia")), # row 3
float(match.group("Ip")), # row 4
float(match.group("ZR")), # row 5
float(match.group("ZX")), # row 6
int(match.group("adc_vmin"), 16), # row 7
int(match.group("adc_vmax"), 16), # row 8
int(match.group("adc_imin"), 16), # row 9
int(match.group("adc_imax"), 16), # row 10
float(match.group("nv")), # row 11
float(match.group("ni")), # row 12
]
row[1] = row[1]/(2*3.14159*row[0]*0.00005 + 1) # compensate Va for pole at 20kHz
row[5] = row[1]/row[3] * math.cos(0.01745*(row[2]-row[4]))
@ -196,25 +214,31 @@ class TelnetReader:
except KeyboardInterrupt:
print("Interrupted by user.")
print(data_rows)
if state["freq"]>0.00001: # check if scan endeed normally (e.g. no abort due to clipping)
#dump_csv("measurements.csv")
dump_into_database()
#append_line_to_file(0, "scan_freq.csv")
#append_line_to_file(1, "scan_Va.csv")
#append_line_to_file(2, "scan_Vp.csv")
#append_line_to_file(3, "scan_Ia.csv")
#append_line_to_file(4, "scan_Ip.csv")
#append_line_to_file(5, "scan_ZR.csv")
#append_line_to_file(6, "scan_ZX.csv")
#append_Nyquist_run("scan_Nyquist.csv")
#state["noise_scan"] = not state["noise_scan"]
if state["freq"]>0.00001: # check if scan ended normally (e.g. no abort due to clipping)
if True: # XXXDB
dump_into_database()
else:
dump_csv("measurements.csv")
#append_line_to_file(0, "scan_freq.csv")
#append_line_to_file(1, "scan_Va.csv")
#append_line_to_file(2, "scan_Vp.csv")
#append_line_to_file(3, "scan_Ia.csv")
#append_line_to_file(4, "scan_Ip.csv")
#append_line_to_file(5, "scan_ZR.csv")
#append_line_to_file(6, "scan_ZX.csv")
#append_Nyquist_run("scan_Nyquist.csv")
#state["noise_scan"] = not state["noise_scan"]
else:
print("scan aborted\r")
gc.collect() # clean up internal memory (garbage collect)
def process_line(self, line):
global state
# print(f"RAW: {line}")
global Inoise_baseline
global VersionDspSoftware
if line.startswith("SW-Version"):
VersionDspSoftware = line.split(":", 1)[1].strip() # remember the DSP software version from its reporting
print(f"DSP SW Version={VersionDspSoftware}\r\n")
if not line.startswith("Va="): # skip lines that are not to be analyzed
return
if state["remaining_receive_lines"] > 0 :
@ -223,7 +247,7 @@ class TelnetReader:
# prepare new frequency measurement
if state["initializing"] == 1:
# since we just start a frequency scan, let's set the R, Max and Amplitude
response = f"\rr516\r" # set Resistance value for scaling HAL sensor
response = f"\rr331\r" # set Resistance value for scaling HAL sensor
self.tn.write(response.encode("utf-8"))
response = f"m1600\r" # set max amplitude value
self.tn.write(response.encode("utf-8"))
@ -236,12 +260,17 @@ class TelnetReader:
# there will be a lot of lines, but they will be skipped as they do not match the pattern
else: # regular loop (not initializing)
extract_to_dataframe(line) # capture the measurement
# print(f"minmax: {data_rows[7]},{data_rows[8]},{data_rows[9]},{data_rows[10] }\n")
if data_rows and (data_rows[-1][7]<5 or data_rows[-1][8]>250 or data_rows[-1][9]<5 or data_rows[-1][10]>250):
#response = f"a{state["ampl"]:.1f}\r" # send ampl to trigger sweep
#self.tn.write(response.encode("utf-8"))
# there will be a lot of lines, but they will be skipped as they do not match the pattern
state["freq"] = 0 # force ending of the scan, and write no data in the database
if state["freq"]>3.0:
Inoise_baseline = 0.8*Inoise_baseline + 0.2*data_rows[-1][12] # remember last baseline around 3Hz (assuming top-down scanning)
if data_rows and (data_rows[-1][11]>state["allowed_noise_level"] or data_rows[-1][12]>state["allowed_noise_level"] or (state["freq"]<3.0 and data_rows[-1][12]>1.25*Inoise_baseline)):
# Too much noise: add to blacklist
print("Too much noise - adding to blacklist")
blacklist.append({"Freq": state["freq"], "NrToSkip": state["skip_scans_for_noisy_freqs"]})
print(f"Blacklist: {blacklist}\r")
del data_rows[-1] # remove this last entry from the list (for DB it is okay, but for CSV things will shift)
else:
if data_rows and (data_rows[-1][7]<5 or data_rows[-1][8]>250 or data_rows[-1][9]<5 or data_rows[-1][10]>250): # XXXDB
state["freq"] = 0 # force ending of the scan, and write no data in the database
# calculate next freq
if state["freq"] > 1.0:
freq_multiplier = state["freq_step_multiply"] # calc next freq
@ -253,8 +282,8 @@ class TelnetReader:
state["freq"] *= freq_multiplier
if (state["freq"]>40) and (state["freq"]<660) and ((state["freq"]%50<2.5) or (-state["freq"]%50<2.5)):
state["freq"] *= freq_multiplier # if near a 50Hz harmonic, skip to the next frequency
while (state["freq"]<0.6) and ((state["freq"]%state["interference_freq"]<state["interference_bandwidth"]) or (-state["freq"]<state["interference_freq"]<state["interference_bandwidth"])):
state["freq"] *= freq_multiplier # if near a 0.052Hz harmonic, skip to the next frequency
# while (state["freq"]<0.6) and ((state["freq"]%state["interference_freq"]<state["interference_bandwidth"]) or (-state["freq"]<state["interference_freq"]<state["interference_bandwidth"])):
# state["freq"] *= freq_multiplier # if near a 0.052Hz harmonic, skip to the next frequency
if state["freq"] <= state["stop_freq"]:
print("Reached stop frequency")
else:
@ -302,6 +331,11 @@ if __name__ == "__main__":
state["initializing"] = 1;
reader.read_loop()
# decrement all freq's in blacklist:
for item in blacklist:
item["NrToSkip"] -= 1
blacklist = [item for item in blacklist if item["NrToSkip"] != 0] # remove all zero items
print(f"Blacklist: {blacklist}")
finally:
reader.disconnect()

2
start
View file

@ -1 +1,3 @@
sudo systemctl restart ser2net.service
sleep 2
systemctl --user start python-scanner