Public Access
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e42205a4d | ||
|
|
1f6763f9de | ||
|
|
6668ac5b2b | ||
|
|
ee453c03a7 | ||
|
|
1fb56e20f9 | ||
|
|
ef11824f8b |
No files matched your search
@@ -13,7 +13,7 @@ class StateSimple(StateBase):
|
|||||||
super(StateSimple, self).__init__()
|
super(StateSimple, self).__init__()
|
||||||
if time < 0:
|
if time < 0:
|
||||||
raise AssertionError("time for state is negative!")
|
raise AssertionError("time for state is negative!")
|
||||||
self.time = time
|
self.time = float(time)
|
||||||
self.content = content
|
self.content = content
|
||||||
|
|
||||||
def to_xml(self, indent: str = "") -> str:
|
def to_xml(self, indent: str = "") -> str:
|
||||||
@@ -191,7 +191,7 @@ class Experiment:
|
|||||||
|
|
||||||
Typically only used in very special cases.
|
Typically only used in very special cases.
|
||||||
"""
|
"""
|
||||||
self.state_list.append('<state time="%s">\n' % repr(time))
|
self.state_list.append('<state time="%s">\n' % repr(float(time)))
|
||||||
self.total_time[-1] += time
|
self.total_time[-1] += time
|
||||||
|
|
||||||
## End of *state_start*
|
## End of *state_start*
|
||||||
@@ -404,10 +404,10 @@ class Experiment:
|
|||||||
The state length is 2 µs. Stabilisation time depends on RF source.
|
The state length is 2 µs. Stabilisation time depends on RF source.
|
||||||
|
|
||||||
:param float frequency: frequency to set in Hz
|
:param float frequency: frequency to set in Hz
|
||||||
:param float phase: phase to set
|
:param float phase: phase to set, normalized to the range [0, 360)
|
||||||
:param int ttls: default=0, additional ttl lines to set (integer)
|
:param int ttls: default=0, additional ttl lines to set (integer)
|
||||||
"""
|
"""
|
||||||
s_content = '<analogout id="0" f="%f" phase="%f"/>' % (frequency, phase)
|
s_content = '<analogout id="0" f="%f" phase="%f"/>' % (frequency, phase % 360)
|
||||||
if ttls != 0:
|
if ttls != 0:
|
||||||
s_content += '<ttlout value="0x%06x"/>' % ttls
|
s_content += '<ttlout value="0x%06x"/>' % ttls
|
||||||
self.state_list.append(StateSimple(2e-6, s_content))
|
self.state_list.append(StateSimple(2e-6, s_content))
|
||||||
@@ -582,8 +582,9 @@ class Experiment:
|
|||||||
- The state length for this command is 0.5 µs.
|
- The state length for this command is 0.5 µs.
|
||||||
- The stabilization time for the phase change is RF source dependent. A typical value for PTS310 is 2 µs.
|
- The stabilization time for the phase change is RF source dependent. A typical value for PTS310 is 2 µs.
|
||||||
- The phase is relative to the initial phase set at start of the experiment.
|
- The phase is relative to the initial phase set at start of the experiment.
|
||||||
|
- Negative phases are normalized to the range [0, 360) before writing the job file.
|
||||||
"""
|
"""
|
||||||
s_content = '<analogout phase="%f" />' % (phase)
|
s_content = '<analogout phase="%f" />' % (phase % 360)
|
||||||
if ttls!=0:
|
if ttls!=0:
|
||||||
s_content += '<ttlout value="%d"/>' % ttls
|
s_content += '<ttlout value="%d"/>' % ttls
|
||||||
self.state_list.append(StateSimple(0.5e-6, s_content))
|
self.state_list.append(StateSimple(0.5e-6, s_content))
|
||||||
@@ -680,19 +681,19 @@ class Experiment:
|
|||||||
value=""
|
value=""
|
||||||
elif type(value) is float or isinstance(value, numpy.floating):
|
elif type(value) is float or isinstance(value, numpy.floating):
|
||||||
type_string="Float"
|
type_string="Float"
|
||||||
value=repr(value)
|
value=repr(float(value))
|
||||||
elif type(value) is int or isinstance(value, numpy.integer):
|
elif type(value) is int or isinstance(value, numpy.integer):
|
||||||
type_string="Int"
|
type_string="Int"
|
||||||
value=repr(value)
|
value=repr(int(value))
|
||||||
elif type(value) is int:
|
elif type(value) is int:
|
||||||
type_string="Long"
|
type_string="Long"
|
||||||
value=repr(value)
|
value=repr(value)
|
||||||
elif type(value) is complex or isinstance(value, numpy.complexfloating):
|
elif type(value) is complex or isinstance(value, numpy.complexfloating):
|
||||||
type_string="Complex"
|
type_string="Complex"
|
||||||
value=repr(value)
|
value=repr(complex(value))
|
||||||
elif type(value) is bool or isinstance(value, numpy.bool_):
|
elif type(value) is bool or isinstance(value, numpy.bool_):
|
||||||
type_string="Boolean"
|
type_string="Boolean"
|
||||||
value=repr(value)
|
value=repr(bool(value))
|
||||||
elif type(value) in (str,):
|
elif type(value) in (str,):
|
||||||
type_string="String"
|
type_string="String"
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -3945,7 +3945,7 @@ class MonitorWidgets:
|
|||||||
self.measurementresultline = None
|
self.measurementresultline = None
|
||||||
elif self.measurementresultline is not None:
|
elif self.measurementresultline is not None:
|
||||||
# clear line plot
|
# clear line plot
|
||||||
self.matplot_axes.lines.remove( self.measurementresultline[ 0 ] )
|
self.measurementresultline[ 0 ].remove( )
|
||||||
self.measurementresultline = None
|
self.measurementresultline = None
|
||||||
if hasattr(self, 'matplot_cursor') and self.matplot_cursor is not None:
|
if hasattr(self, 'matplot_cursor') and self.matplot_cursor is not None:
|
||||||
self.matplot_cursor.disconnect_events()
|
self.matplot_cursor.disconnect_events()
|
||||||
@@ -4205,17 +4205,17 @@ class MonitorWidgets:
|
|||||||
|
|
||||||
# remove lines and error bars
|
# remove lines and error bars
|
||||||
if self.measurementresultgraph is not None:
|
if self.measurementresultgraph is not None:
|
||||||
self.matplot_axes.lines.remove( self.measurementresultgraph[ 0 ] )
|
self.measurementresultgraph[ 0 ].remove( )
|
||||||
# remove caps
|
# remove caps
|
||||||
for l in self.measurementresultgraph[ 1 ]:
|
for l in self.measurementresultgraph[ 1 ]:
|
||||||
self.matplot_axes.lines.remove( l )
|
l.remove( )
|
||||||
# and columns
|
# and columns
|
||||||
for l in self.measurementresultgraph[ 2 ]:
|
for l in self.measurementresultgraph[ 2 ]:
|
||||||
self.matplot_axes.collections.remove( l )
|
l.remove( )
|
||||||
self.measurementresultgraph = None
|
self.measurementresultgraph = None
|
||||||
|
|
||||||
if self.measurementresultline is not None:
|
if self.measurementresultline is not None:
|
||||||
self.matplot_axes.lines.remove( self.measurementresultline[ 0 ] )
|
self.measurementresultline[ 0 ].remove( )
|
||||||
self.measurementresultline = None
|
self.measurementresultline = None
|
||||||
|
|
||||||
# remove fit overlay lines
|
# remove fit overlay lines
|
||||||
@@ -4303,13 +4303,13 @@ class MonitorWidgets:
|
|||||||
|
|
||||||
# finally decide about x log plot
|
# finally decide about x log plot
|
||||||
if x_scale == "log10" and xmin > 0:
|
if x_scale == "log10" and xmin > 0:
|
||||||
self.matplot_axes.set_xscale( "log", basex=10.0 )
|
self.matplot_axes.set_xscale( "log", base=10.0 )
|
||||||
self.matplot_axes.fmt_xdata = lambda x: "%g" % x
|
self.matplot_axes.fmt_xdata = lambda x: "%g" % x
|
||||||
#elif x_scale=="log" and xmin>0:
|
#elif x_scale=="log" and xmin>0:
|
||||||
# e scaling implementation not really useful
|
# e scaling implementation not really useful
|
||||||
# self.matplot_axes.set_xscale("log", basex=numpy.e)
|
# self.matplot_axes.set_xscale("log", basex=numpy.e)
|
||||||
if y_scale == "log10" and ymin > 0:
|
if y_scale == "log10" and ymin > 0:
|
||||||
self.matplot_axes.set_yscale( "log", basex=10.0 )
|
self.matplot_axes.set_yscale( "log", base=10.0 )
|
||||||
self.matplot_axes.fmt_ydata = lambda x: "%g" % x
|
self.matplot_axes.fmt_ydata = lambda x: "%g" % x
|
||||||
|
|
||||||
self.__rescale = False
|
self.__rescale = False
|
||||||
|
|||||||
@@ -135,7 +135,8 @@ class ResultReader:
|
|||||||
elif t == "Complex":
|
elif t == "Complex":
|
||||||
self.result_description[k]=complex(v)
|
self.result_description[k]=complex(v)
|
||||||
elif t == "Boolean":
|
elif t == "Boolean":
|
||||||
self.result_description[k]=bool(v)
|
# bool("False") would be True, compare the text instead
|
||||||
|
self.result_description[k]=(v == "True")
|
||||||
elif t == "String":
|
elif t == "String":
|
||||||
self.result_description[k]=v
|
self.result_description[k]=v
|
||||||
else:
|
else:
|
||||||
@@ -502,7 +503,8 @@ class ResultReader:
|
|||||||
elif t == "Complex":
|
elif t == "Complex":
|
||||||
self.result_description[k]=complex(v)
|
self.result_description[k]=complex(v)
|
||||||
elif t == "Boolean":
|
elif t == "Boolean":
|
||||||
self.result_description[k]=bool(v)
|
# bool("False") would be True, compare the text instead
|
||||||
|
self.result_description[k]=(v == "True")
|
||||||
elif t == "String":
|
elif t == "String":
|
||||||
self.result_description[k]=v
|
self.result_description[k]=v
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import re
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from damaris.experiments.Experiment import Experiment
|
||||||
|
|
||||||
|
|
||||||
|
def phase_of_state(state):
|
||||||
|
"extracts the phase value from a state's XML content"
|
||||||
|
match = re.search(r'phase="([-0-9.]+)"', state.content)
|
||||||
|
assert match, "no phase in state: %s" % state.content
|
||||||
|
return float(match.group(1))
|
||||||
|
|
||||||
|
|
||||||
|
class TestExperimentPhase(unittest.TestCase):
|
||||||
|
def test_set_phase_negative_normalized(self):
|
||||||
|
"""Test that set_phase() normalizes negative phases to [0, 360)."""
|
||||||
|
e = Experiment()
|
||||||
|
for degrees, expected in ((-90, 270.0), (-45.5, 314.5),
|
||||||
|
(-270, 90.0), (-360, 0.0)):
|
||||||
|
e.set_phase(degrees)
|
||||||
|
self.assertEqual(phase_of_state(e.state_list.list[-1]), expected)
|
||||||
|
|
||||||
|
def test_set_phase_positive_unchanged(self):
|
||||||
|
"""Test that set_phase() passes positive phases through unchanged."""
|
||||||
|
e = Experiment()
|
||||||
|
for degrees, expected in ((0, 0.0), (90, 90.0), (270, 270.0),
|
||||||
|
(45.5, 45.5)):
|
||||||
|
e.set_phase(degrees)
|
||||||
|
self.assertEqual(phase_of_state(e.state_list.list[-1]), expected)
|
||||||
|
|
||||||
|
def test_set_frequency_negative_phase_normalized(self):
|
||||||
|
"""Test that set_frequency() normalizes a negative phase."""
|
||||||
|
e = Experiment()
|
||||||
|
e.set_frequency(85e6, -90)
|
||||||
|
state = e.state_list.list[-1]
|
||||||
|
self.assertEqual(phase_of_state(state), 270.0)
|
||||||
|
self.assertIn('f="85000000.000000"', state.content)
|
||||||
|
|
||||||
|
def test_rf_pulse_negative_phase_normalized(self):
|
||||||
|
"""Test that rf_pulse() normalizes a negative pulse phase."""
|
||||||
|
e = Experiment(gating=1e-6, rf_sources=[1], rf_gates=[2])
|
||||||
|
e.rf_pulse(length=1e-6, phase=-90, source=0)
|
||||||
|
self.assertEqual(phase_of_state(e.state_list.list[0]), 270.0)
|
||||||
|
|
||||||
|
def test_numpy_scalars_in_description(self):
|
||||||
|
"""Test that description items with numpy scalars (e.g. from lin_range)
|
||||||
|
are written as plain numbers so the result script can parse them again."""
|
||||||
|
import numpy as np
|
||||||
|
from damaris.tools.ranges import lin_range
|
||||||
|
e = Experiment()
|
||||||
|
e.set_description("tau", lin_range(1e-6, 3e-6, 1e-6)[0])
|
||||||
|
e.set_description("n", np.int64(42))
|
||||||
|
xml = e.write_xml_string()
|
||||||
|
items = {key: (typ, text)
|
||||||
|
for key, typ, text in
|
||||||
|
re.findall(r'<item key="([^"]+)" type="([^"]+)">([^<]*)</item>', xml)}
|
||||||
|
self.assertEqual(items["tau"][0], "Float")
|
||||||
|
self.assertEqual(items["n"][0], "Int")
|
||||||
|
self.assertEqual(float(items["tau"][1]), 1e-6)
|
||||||
|
self.assertEqual(int(items["n"][1]), 42)
|
||||||
|
|
||||||
|
def test_numpy_scalar_state_times(self):
|
||||||
|
"""Test that numpy scalar times do not end up as np.float64(...) in the XML."""
|
||||||
|
import numpy as np
|
||||||
|
e = Experiment()
|
||||||
|
e.wait(np.float64(1e-6))
|
||||||
|
e.state_start(np.float64(2e-6))
|
||||||
|
xml = e.write_xml_string()
|
||||||
|
self.assertNotIn("np.float64", xml)
|
||||||
|
self.assertIn('<state time="1e-06"/>', xml)
|
||||||
|
self.assertIn('<state time="2e-06">', xml)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in new issue
Block a user