Compare commits
No commits in common. 'master' and 'master' have entirely different histories.
@ -1,31 +0,0 @@ |
||||
## Automatic Test Scripts |
||||
|
||||
#### Shell |
||||
(Remember to **edit the settings** in `rp_ping_test` and `rp_shutdown` and the **test arguments** passed to `get_and_plot_sayma_data` according to your own setup!) |
||||
|
||||
* `test_mlabs`: Run an automatically-timed test using M-Labs' current setup |
||||
* `test_creotech`: Run an automatically-timed test using Creotech's current setup |
||||
|
||||
## Data Collection & Analytics Tools |
||||
|
||||
#### Python |
||||
(To see detailed usage, add `--help` as an argument when running the script.) |
||||
|
||||
* `rp_get_sayma_data.py`: Acquire and save data from RedPitayas over local network |
||||
* `plot_sayma_data.py`: Load saved data and output phase skew measurements and data plots |
||||
* `analyze_sayma_data.py`: Generate a data analysis report from saved data records |
||||
|
||||
#### Shell |
||||
* `get_and_plot_sayma_data`: Acquire and analyse data from RedPitayas over local network |
||||
* `get_and_plot_remote_sayma_data`: Acquire and analyse data from remote RedPitayas via SSH (often over different networks) to the local file system |
||||
* Requires Python testsuite scripts present on the remote host |
||||
|
||||
## System Utilities |
||||
|
||||
#### Shell |
||||
* `mch_start`: Power on the MCH and start logging serial outputs from Metlino and Saymas |
||||
* `mch_stop`: Power off the MCH and stop logging serial outputs from Metlino and Saymas |
||||
* `rp_ping_test`: Loop to ping a RedPitaya until it succeeds |
||||
* The exit condition is where the RedPitaya is ready-to-use |
||||
* `rp_shutdown`: Shut down the OS of a RedPitaya and loop to ping it until it fails |
||||
* The exit condition is where the RedPitaya is ready for hardware power-off |
@ -1,129 +0,0 @@ |
||||
import numpy as np |
||||
import argparse |
||||
import os |
||||
import datetime |
||||
|
||||
|
||||
DATA_COLUMNS = np.array([ |
||||
["data_time_iso", "datetime64[s]"], |
||||
["phase_radian", "f"], |
||||
]) |
||||
|
||||
|
||||
REPORT_PARAMS_KEYS = [ |
||||
"rpt_time", |
||||
"data_time_start", "data_time_end", "data_count", |
||||
"wave_type", # type of DDS waveform |
||||
"wave_freq", # frequency of the DDS waveform |
||||
"phase_ps_mean", "phase_ps_min", "phase_ps_max", |
||||
"phase_ps_std", # standard deviation |
||||
"phase_ps_meanabsdev", # mean absolute deviation |
||||
] |
||||
|
||||
|
||||
def report(*args, **kwargs): |
||||
if len(args) != 1 or not isinstance(args[0], dict): |
||||
raise SyntaxError("Must only pass 1 dict argument, or >=1 keyword arguments.") |
||||
# Create and store lines of strings for the report |
||||
rpt = [] |
||||
prm = args[0] |
||||
for k in REPORT_PARAMS_KEYS: |
||||
if prm.get(k) is None and kwargs.get(k) is None: |
||||
raise ValueError("Data missing for report item {}.".format(k)) |
||||
# Replace or append item from kwargs onto the prm dict |
||||
if kwargs.get(k) is not None: |
||||
prm[k] = kwargs[k] |
||||
# Report title |
||||
rpt.append("Sayma DAC/TTL Data Analysis") |
||||
rpt.append("") |
||||
# Report header |
||||
rpt.append("Report generated at: {}".format(prm["rpt_time"])) |
||||
rpt.append("------") |
||||
rpt.append("") |
||||
# Report content |
||||
rpt.append("Data collection started at: {}".format(prm["data_time_start"])) |
||||
rpt.append(" ended at: {}".format(prm["data_time_end"])) |
||||
rpt.append("Total number of records: {}".format(prm["data_count"])) |
||||
rpt.append("") |
||||
rpt.append("Target wave type: {}".format(prm["wave_type"])) |
||||
rpt.append(" frequency: {:.2f} MHz".format(prm["wave_freq"]/1e6)) |
||||
rpt.append("") |
||||
rpt.append("Phase skew statistics:") |
||||
rpt.append(" Mean: {:>10.4f} ps".format(prm["phase_ps_mean"])) |
||||
rpt.append(" Minimum: {:>10.4f} ps".format(prm["phase_ps_min"])) |
||||
rpt.append(" Maximum: {:>10.4f} ps".format(prm["phase_ps_max"])) |
||||
rpt.append(" Standard Deviation: {:>10.4f} ps".format(prm["phase_ps_std"])) |
||||
rpt.append(" Max Absolute Deviation: {:>10.4f} ps".format(prm["phase_ps_maxabsdev"])) |
||||
rpt.append(" Mean Absolute Deviation: {:>10.4f} ps".format(prm["phase_ps_meanabsdev"])) |
||||
rpt.append("") |
||||
# TODO: Use jinja2 to produce a report |
||||
return '\n'.join(rpt) |
||||
|
||||
|
||||
def main(): |
||||
# Get timestamp at script start |
||||
now = datetime.datetime.now().replace( |
||||
microsecond=0, # get rid of microseconds when printed |
||||
) |
||||
now_iso = now.isoformat(sep=' ') |
||||
|
||||
parser = argparse.ArgumentParser(description="Data analysis tool for Sayma DAC/TTL") |
||||
parser.add_argument("log", |
||||
help="path of the log file containing the data records; " |
||||
"must be a CSV in excel-tab dialect", |
||||
type=str) |
||||
parser.add_argument("--rpt", |
||||
help="path of the file where the analysis will be reported (and overwrite)", |
||||
type=str) |
||||
args = parser.parse_args() |
||||
|
||||
with open(args.log, 'r') as f: |
||||
print("Analysis of data records started at {}.".format(now_iso)) |
||||
# Create a numpy array from the CSV data (it has no header row) |
||||
# Currently, log files from plot_sayma_data.py uses TAB as delimiter |
||||
data = np.loadtxt(f, delimiter='\t', |
||||
dtype={'names': DATA_COLUMNS[:, 0], 'formats': DATA_COLUMNS[:, 1]}) |
||||
# Clean-up: remove duplicate rows |
||||
data_rows = len(data) # original row count w/ potential duplicates |
||||
data = np.unique(data, axis=0) |
||||
print("{} duplicate rows detected and ignored.".format(data_rows - len(data))) |
||||
data_rows = len(data) # new row count without duplicates |
||||
# Define dict of params for generating the report |
||||
rpt_params = dict() |
||||
rpt_params["rpt_time"] = now_iso |
||||
# Analyse: data collection time & count |
||||
rpt_params["data_time_start"] = str(data["data_time_iso"].min()).replace('T', ' ') |
||||
rpt_params["data_time_end"] = str(data["data_time_iso"].max()).replace('T', ' ') |
||||
rpt_params["data_count"] = data_rows |
||||
# Analyse: phase stats |
||||
# TODO: Check if square waves use the same calculation as sinusoids |
||||
WAVE_TYPES = ["Sinusoid", "Square wave"] |
||||
rpt_params["wave_type"] = WAVE_TYPES[int( |
||||
input(">> Input: target wave type (use the index)\n" |
||||
" ({}): ".format(", ".join(["{}={}".format(i,x) for i,x in enumerate(WAVE_TYPES)]))) |
||||
)] |
||||
rpt_params["wave_freq"] = float( |
||||
input(">> Input: target wave frequency (in Hz): ") |
||||
) |
||||
# Conversion formula: phase_time = (1/wave_freq) * (phase_angle/2/pi) |
||||
data_phase_ps = data["phase_radian"] / rpt_params["wave_freq"] / 2 / np.pi * 1e12 |
||||
rpt_params["phase_ps_mean"] = data_phase_ps.mean() |
||||
rpt_params["phase_ps_min"] = data_phase_ps.min() |
||||
rpt_params["phase_ps_max"] = data_phase_ps.max() |
||||
rpt_params["phase_ps_std"] = data_phase_ps.std() |
||||
rpt_params["phase_ps_maxabsdev"] = \ |
||||
np.absolute(data_phase_ps - data_phase_ps.max()).mean() |
||||
rpt_params["phase_ps_meanabsdev"] = \ |
||||
np.absolute(data_phase_ps - data_phase_ps.mean()).mean() |
||||
# Generate the report |
||||
rpt = report(rpt_params) |
||||
# Print the report |
||||
print(rpt) |
||||
|
||||
|
||||
# TODO: Implement report output feature |
||||
if args.rpt is not None: |
||||
raise NotImplementedError("Report file output feature has not been implemented.") |
||||
|
||||
if __name__ == "__main__": |
||||
main() |
Binary file not shown.
@ -1,13 +0,0 @@ |
||||
#!/bin/sh |
||||
|
||||
# RedPitaya hostname |
||||
export RP_HOST=rp-f0612e |
||||
|
||||
# Check if RedPitaya becomes connectable from network by pinging |
||||
while true |
||||
do |
||||
ping $RP_HOST -c 1 >/dev/null 2>/dev/null && break |
||||
done |
||||
# Print time when RedPitaya ready on network |
||||
TIMESTAMP_PRETTY=$(date '+%Y-%m-%d %H:%M:%S') |
||||
echo "RP becomes connectable at $TIMESTAMP_PRETTY." |
@ -1,26 +0,0 @@ |
||||
#!/bin/sh |
||||
|
||||
# RedPitaya hostname and user information |
||||
# (The user should have privilege to run `shutdown` - check out `visudo`) |
||||
# (Add local SSH key to RP's authorized keys for passwordless login) |
||||
export RP_HOST=rp-f0612e |
||||
export RP_USER=tester |
||||
export RP_KEY=~/.ssh/rp |
||||
|
||||
# Issue shutdown on the RedPitaya |
||||
echo "Requesting shutdown on RP at $RP_HOST." |
||||
if [[ $RP_KEY == "" ]] |
||||
then |
||||
ssh $RP_USER@$RP_HOST "sudo shutdown now" |
||||
else |
||||
ssh -i $RP_KEY $RP_USER@$RP_HOST "sudo shutdown now" |
||||
fi |
||||
|
||||
# Check if RedPitaya has been disconnected from network by pinging |
||||
while ping $RP_HOST -c 1 >/dev/null 2>/dev/null |
||||
do |
||||
true |
||||
done |
||||
# Print time when RedPitaya is down on network |
||||
TIMESTAMP_PRETTY=$(date '+%Y-%m-%d %H:%M:%S') |
||||
echo "RP disconnected at $TIMESTAMP_PRETTY." |
@ -1,30 +0,0 @@ |
||||
#!/bin/sh |
||||
|
||||
# Simultaneously, start testing RP's network connectivity, while |
||||
# powering on MCH and RP using ~/power_strip/ scripts |
||||
# Display power-on time |
||||
TIMESTAMP_PRETTY=$(date '+%Y-%m-%d %H:%M:%S') |
||||
echo "MCH powered on at $TIMESTAMP_PRETTY." |
||||
# Power on MCH |
||||
./mch_start & |
||||
# Wait 2 minutes before measurement |
||||
sleep 120 |
||||
|
||||
# Get and plot Sayma data |
||||
# (Assumption: numpy, matplotlib and scipy have been installed on local) |
||||
# (Assumption: directory ./creotech-raw exists) |
||||
# NOTE: Change the RP hostname and LV/HV arguments according to |
||||
# which two channels you are measuring! |
||||
./get_and_plot_sayma_data creotech-raw creotech-1 1 creotech-1 2 creotech-1:LV,LV & |
||||
# Wait 1 minute before powering off MCH and RP |
||||
sleep 60 |
||||
|
||||
# Wait 30 seconds |
||||
sleep 30 |
||||
|
||||
# Simultaneously power off MCH and RP |
||||
# Display power-off time |
||||
TIMESTAMP_PRETTY=$(date '+%Y-%m-%d %H:%M:%S') |
||||
echo "MCH powered off at $TIMESTAMP_PRETTY." |
||||
# Power off MCH |
||||
./mch_stop |
@ -1,44 +0,0 @@ |
||||
#!/bin/sh |
||||
|
||||
# tnetplug host |
||||
export TNETPLUG_HOST=192.168.1.31 |
||||
export TNETPLUG_PORT=3131 |
||||
|
||||
# Smart USB hub host |
||||
# (uhubctl must be installed on the host) |
||||
export HOST=rpi-3 |
||||
export HOSTPORT=22 |
||||
# Hub port where the RedPitaya is connected |
||||
export HUBPORT=4 |
||||
|
||||
# Simultaneously, start testing RP's network connectivity, while |
||||
# powering on MCH using tnetplug, and RP using uhubctl |
||||
# Display power-on time |
||||
TIMESTAMP_PRETTY=$(date '+%Y-%m-%d %H:%M:%S') |
||||
echo "MCH powered on at $TIMESTAMP_PRETTY." |
||||
# Power on MCH |
||||
echo "A" | nc -W1 $TNETPLUG_HOST $TNETPLUG_PORT & |
||||
# Power on RedPitaya connected to the smart USB hub |
||||
# (assume that only 1 hub is connected) |
||||
ssh -p $HOSTPORT $HOST "uhubctl -a on -p $HUBPORT" & |
||||
# Wait 2 minutes before measurement |
||||
sleep 120 |
||||
|
||||
# Get and plot Sayma data |
||||
# NOTE: Change the RP hostname and LV/HV arguments according to |
||||
# which two channels you are measuring! |
||||
nix-shell -p python3Packages.matplotlib python3Packages.numpy python3Packages.scipy --run "./get_and_plot_sayma_data mlabs-raw mlabs-1 1 mlabs-1 2 mlabs-1:LV,LV" & |
||||
# Wait 1 minute before powering off MCH and RP |
||||
sleep 60 |
||||
|
||||
# Wait 30 seconds |
||||
sleep 30 |
||||
|
||||
# Simultaneously power off MCH and RP |
||||
# Display power-off time |
||||
TIMESTAMP_PRETTY=$(date '+%Y-%m-%d %H:%M:%S') |
||||
echo "MCH powered off at $TIMESTAMP_PRETTY." |
||||
# Power off MCH |
||||
echo "a" | nc -W1 $TNETPLUG_HOST $TNETPLUG_PORT & |
||||
# Power off RedPitaya connected to the smart USB hub |
||||
ssh -p $HOSTPORT $HOST "uhubctl -a off -p $HUBPORT" |
Loading…
Reference in new issue