mirror of
https://github.com/slsdetectorgroup/slsDetectorPackage.git
synced 2025-06-11 04:17:15 +02:00
merging refactor (replacing)
This commit is contained in:
47
python/CMakeLists.txt
Executable file
47
python/CMakeLists.txt
Executable file
@ -0,0 +1,47 @@
|
||||
add_subdirectory(pybind11)
|
||||
pybind11_add_module(_sls_detector src/main.cpp)
|
||||
|
||||
|
||||
|
||||
target_link_libraries(_sls_detector PUBLIC
|
||||
slsProjectOptions
|
||||
slsProjectWarnings
|
||||
slsDetectorShared
|
||||
slsReceiverShared
|
||||
slsSupportLib
|
||||
zmq )
|
||||
|
||||
|
||||
|
||||
set_target_properties(_sls_detector PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
|
||||
)
|
||||
|
||||
#Copy Python code
|
||||
set( PYTHON_FILES
|
||||
__init__.py
|
||||
adcs.py
|
||||
dacs.py
|
||||
decorators.py
|
||||
detector_property.py
|
||||
detector.py
|
||||
eiger.py
|
||||
errors.py
|
||||
experimental.py
|
||||
jungfrau_ctb.py
|
||||
jungfrau.py
|
||||
registers.py
|
||||
utils.py
|
||||
|
||||
)
|
||||
|
||||
foreach(FILE ${PYTHON_FILES})
|
||||
configure_file( sls_detector/${FILE}
|
||||
${CMAKE_BINARY_DIR}/bin/sls_detector/${FILE} )
|
||||
|
||||
endforeach(FILE ${PYTHON_FILES})
|
||||
|
||||
|
||||
configure_file( scripts/basic.py
|
||||
${CMAKE_BINARY_DIR}/basic.py
|
||||
)
|
674
python/LICENSE
Executable file
674
python/LICENSE
Executable file
@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
63
python/README.md
Executable file
63
python/README.md
Executable file
@ -0,0 +1,63 @@
|
||||
# sls_detector: Python interface to slsDetectorPackage
|
||||
Python interface to the Sls Detector Software.
|
||||
|
||||
### Documentation ###
|
||||
Sphinx built documentation is available here:
|
||||
[https://slsdetectorgroup.github.io/sls_detector/](https://slsdetectorgroup.github.io/sls_detector/)
|
||||
|
||||
|
||||
### Install using conda ###
|
||||
|
||||
Binaries are available using conda. This installs both the detector software and the Python
|
||||
interface.
|
||||
|
||||
```bash
|
||||
#Add conda channels
|
||||
conda config --add channels conda-forge
|
||||
conda config --add channels slsdetectorgroup
|
||||
|
||||
#Install latest version
|
||||
conda install sls_detector
|
||||
|
||||
#Install specific version
|
||||
conda install sls_detector=3.0.1
|
||||
|
||||
#Scientific Linux 6 version (GLIBC2.12)
|
||||
conda install sls_detector=SL6_3.0.1
|
||||
```
|
||||
|
||||
### Building using conda-build ###
|
||||
|
||||
Needs [sls_detector_software](https://github.com/slsdetectorgroup/sls_detector_software) installed.
|
||||
|
||||
```bash
|
||||
#Clone source code
|
||||
git clone https://github.com/slsdetectorgroup/sls_detector.git
|
||||
|
||||
#Checkout the branch needed
|
||||
git checkout 3.0.1
|
||||
|
||||
#Build and install the local version
|
||||
conda-build sls_detector
|
||||
conda install --use-local sls_detector
|
||||
|
||||
|
||||
```
|
||||
### Developer build ###
|
||||
|
||||
IF you if you are developing and are making constant changes to the code it's a bit cumbersome
|
||||
to build with conda and install. Then an easier way is to build the C/C++ parts in the package
|
||||
directory and temporary add this to the path
|
||||
|
||||
```bash
|
||||
#in path/to/sls_detector
|
||||
python setup.py build_ext --inplace
|
||||
```
|
||||
Then in your Python script
|
||||
```python
|
||||
|
||||
import sys
|
||||
sys.path.append('/path/to/sls_detector')
|
||||
from sls_detector import Detector
|
||||
```
|
||||
|
1
python/pybind11
Submodule
1
python/pybind11
Submodule
Submodule python/pybind11 added at a23996fce3
20
python/scripts/basic.py
Executable file
20
python/scripts/basic.py
Executable file
@ -0,0 +1,20 @@
|
||||
import os
|
||||
import sys
|
||||
import numpy as np
|
||||
sys.path.append(os.path.join(os.getcwd(), 'bin'))
|
||||
from sls_detector import Eiger
|
||||
from sls_detector import ExperimentalDetector
|
||||
|
||||
from _sls_detector.io import read_ctb_file
|
||||
|
||||
d = Eiger()
|
||||
e = ExperimentalDetector()
|
||||
|
||||
# ncols = 192
|
||||
# start = 600
|
||||
# end = 1800
|
||||
# nrows = end-start
|
||||
# data = np.zeros((nrows, ncols))
|
||||
|
||||
# for i in range(nrows):
|
||||
# data[i, :] = read_ctb_file(f'/home/l_frojdh/mythendata/MoKbZr_30kV60mA_1s_200V_thr{start+i}_1.raw', 8, 24)
|
125
python/setup.py
Executable file
125
python/setup.py
Executable file
@ -0,0 +1,125 @@
|
||||
"""
|
||||
Setup file for sls_detector
|
||||
Build upon the pybind11 example found here: https://github.com/pybind/python_example
|
||||
"""
|
||||
from setuptools import setup, Extension, find_packages
|
||||
from setuptools.command.build_ext import build_ext
|
||||
import sys
|
||||
import setuptools
|
||||
import os
|
||||
|
||||
__version__ = 'refactor'
|
||||
|
||||
|
||||
def get_conda_path():
|
||||
"""
|
||||
Keep this a function if we need some fancier logic later
|
||||
"""
|
||||
print('Prefix: ', os.environ['CONDA_PREFIX'])
|
||||
return os.environ['CONDA_PREFIX']
|
||||
|
||||
|
||||
class get_pybind_include(object):
|
||||
"""Helper class to determine the pybind11 include path
|
||||
The purpose of this class is to postpone importing pybind11
|
||||
until it is actually installed, so that the ``get_include()``
|
||||
method can be invoked. """
|
||||
|
||||
def __init__(self, user=False):
|
||||
self.user = user
|
||||
|
||||
def __str__(self):
|
||||
import pybind11
|
||||
return pybind11.get_include(self.user)
|
||||
|
||||
|
||||
ext_modules = [
|
||||
Extension(
|
||||
'_sls_detector',
|
||||
['src/main.cpp'],
|
||||
include_dirs=[
|
||||
# Path to pybind11 headers
|
||||
get_pybind_include(),
|
||||
get_pybind_include(user=True),
|
||||
os.path.join(get_conda_path(), 'include/slsDetectorPackage'),
|
||||
|
||||
],
|
||||
libraries=['SlsDetector', 'SlsReceiver', 'zmq'],
|
||||
library_dirs=[
|
||||
os.path.join(get_conda_path(), 'lib'),
|
||||
os.path.join(get_conda_path(), 'bin'),
|
||||
],
|
||||
|
||||
language='c++'
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
# As of Python 3.6, CCompiler has a `has_flag` method.
|
||||
# cf http://bugs.python.org/issue26689
|
||||
def has_flag(compiler, flagname):
|
||||
"""Return a boolean indicating whether a flag name is supported on
|
||||
the specified compiler.
|
||||
"""
|
||||
import tempfile
|
||||
with tempfile.NamedTemporaryFile('w', suffix='.cpp') as f:
|
||||
f.write('int main (int argc, char **argv) { return 0; }')
|
||||
try:
|
||||
compiler.compile([f.name], extra_postargs=[flagname])
|
||||
except setuptools.distutils.errors.CompileError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def cpp_flag(compiler):
|
||||
"""Return the -std=c++[11/14] compiler flag.
|
||||
The c++14 is prefered over c++11 (when it is available).
|
||||
"""
|
||||
if has_flag(compiler, '-std=c++14'):
|
||||
return '-std=c++14'
|
||||
elif has_flag(compiler, '-std=c++11'):
|
||||
return '-std=c++11'
|
||||
else:
|
||||
raise RuntimeError('Unsupported compiler -- at least C++11 support '
|
||||
'is needed!')
|
||||
|
||||
|
||||
class BuildExt(build_ext):
|
||||
"""A custom build extension for adding compiler-specific options."""
|
||||
c_opts = {
|
||||
'msvc': ['/EHsc'],
|
||||
'unix': [],
|
||||
}
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
c_opts['unix'] += ['-stdlib=libc++', '-mmacosx-version-min=10.7']
|
||||
|
||||
def build_extensions(self):
|
||||
ct = self.compiler.compiler_type
|
||||
opts = self.c_opts.get(ct, [])
|
||||
if ct == 'unix':
|
||||
opts.append('-DVERSION_INFO="%s"' % self.distribution.get_version())
|
||||
opts.append(cpp_flag(self.compiler))
|
||||
if has_flag(self.compiler, '-fvisibility=hidden'):
|
||||
opts.append('-fvisibility=hidden')
|
||||
elif ct == 'msvc':
|
||||
opts.append('/DVERSION_INFO=\\"%s\\"' % self.distribution.get_version())
|
||||
for ext in self.extensions:
|
||||
ext.extra_compile_args = opts
|
||||
build_ext.build_extensions(self)
|
||||
|
||||
|
||||
setup(
|
||||
name='sls_detector',
|
||||
version=__version__,
|
||||
author='Erik Frojdh',
|
||||
author_email='erik.frojdh@psi.ch',
|
||||
url='https://github.com/slsdetectorgroup/sls_detector',
|
||||
description='Detector API for SLS Detector Group detectors',
|
||||
long_description='',
|
||||
packages=find_packages(exclude=['contrib', 'docs', 'tests']),
|
||||
ext_modules=ext_modules,
|
||||
install_requires=['pybind11>=2.2'],
|
||||
cmdclass={'build_ext': BuildExt},
|
||||
zip_safe=False,
|
||||
)
|
17
python/simple-integration-tests/eiger/config_test.py
Executable file
17
python/simple-integration-tests/eiger/config_test.py
Executable file
@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Nov 14 16:49:07 2017
|
||||
|
||||
@author: l_frojdh
|
||||
"""
|
||||
|
||||
fw_version = 23
|
||||
detector_type = 'Eiger'
|
||||
known_hostnames = ['beb083', 'beb098']
|
||||
image_size = (512,1024) #rows, cols
|
||||
module_geometry = (1,2) #horizontal, vertical
|
||||
|
||||
#Remember to change these in the settings file as well!
|
||||
settings_path = '/home/l_frojdh/slsDetectorPackage/settingsdir/eiger'
|
||||
file_path = '/home/l_frojdh/out'
|
27
python/simple-integration-tests/eiger/fixtures.py
Executable file
27
python/simple-integration-tests/eiger/fixtures.py
Executable file
@ -0,0 +1,27 @@
|
||||
import pytest
|
||||
|
||||
from sls_detector import Detector
|
||||
|
||||
@pytest.fixture
|
||||
def detector():
|
||||
from sls_detector import Detector
|
||||
return Detector()
|
||||
|
||||
@pytest.fixture
|
||||
def eiger():
|
||||
from sls_detector import Eiger
|
||||
d = Eiger()
|
||||
d.n_frames = 1
|
||||
d.exposure_time = 1
|
||||
d.file_write = False
|
||||
return d
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def jungfrau():
|
||||
from sls_detector import Jungfrau
|
||||
return Jungfrau()
|
||||
|
||||
detector_type = Detector().detector_type
|
||||
eigertest = pytest.mark.skipif(detector_type != 'Eiger', reason = 'Only valid for Eiger')
|
||||
jungfrautest = pytest.mark.skipif(detector_type != 'Jungfrau', reason = 'Only valid for Jungfrau')
|
BIN
python/simple-integration-tests/eiger/settingsdir/standard/5000eV/noise.sn083
Executable file
BIN
python/simple-integration-tests/eiger/settingsdir/standard/5000eV/noise.sn083
Executable file
Binary file not shown.
BIN
python/simple-integration-tests/eiger/settingsdir/standard/5000eV/noise.sn098
Executable file
BIN
python/simple-integration-tests/eiger/settingsdir/standard/5000eV/noise.sn098
Executable file
Binary file not shown.
BIN
python/simple-integration-tests/eiger/settingsdir/standard/5000eV/trimbits.sn000
Executable file
BIN
python/simple-integration-tests/eiger/settingsdir/standard/5000eV/trimbits.sn000
Executable file
Binary file not shown.
BIN
python/simple-integration-tests/eiger/settingsdir/standard/6000eV/noise.sn083
Executable file
BIN
python/simple-integration-tests/eiger/settingsdir/standard/6000eV/noise.sn083
Executable file
Binary file not shown.
BIN
python/simple-integration-tests/eiger/settingsdir/standard/6000eV/noise.sn098
Executable file
BIN
python/simple-integration-tests/eiger/settingsdir/standard/6000eV/noise.sn098
Executable file
Binary file not shown.
BIN
python/simple-integration-tests/eiger/settingsdir/standard/6000eV/trimbits.sn000
Executable file
BIN
python/simple-integration-tests/eiger/settingsdir/standard/6000eV/trimbits.sn000
Executable file
Binary file not shown.
BIN
python/simple-integration-tests/eiger/settingsdir/standard/7000eV/noise.sn083
Executable file
BIN
python/simple-integration-tests/eiger/settingsdir/standard/7000eV/noise.sn083
Executable file
Binary file not shown.
BIN
python/simple-integration-tests/eiger/settingsdir/standard/7000eV/noise.sn098
Executable file
BIN
python/simple-integration-tests/eiger/settingsdir/standard/7000eV/noise.sn098
Executable file
Binary file not shown.
BIN
python/simple-integration-tests/eiger/settingsdir/standard/7000eV/trimbits.sn000
Executable file
BIN
python/simple-integration-tests/eiger/settingsdir/standard/7000eV/trimbits.sn000
Executable file
Binary file not shown.
33
python/simple-integration-tests/eiger/test.config
Executable file
33
python/simple-integration-tests/eiger/test.config
Executable file
@ -0,0 +1,33 @@
|
||||
detsizechan 1024 512
|
||||
|
||||
#hostname for top+bottom+
|
||||
hostname beb083+beb098+
|
||||
|
||||
#top
|
||||
0:rx_tcpport 1954
|
||||
0:lock 0
|
||||
0:rx_udpport 50010
|
||||
0:rx_udpport2 50011
|
||||
0:rx_hostname mpc2048
|
||||
0:flippeddatax 0
|
||||
|
||||
#bottom
|
||||
1:rx_tcpport 1955
|
||||
1:lock 0
|
||||
1:rx_udpport 50004
|
||||
1:rx_udpport2 50005
|
||||
1:rx_hostname mpc2048
|
||||
1:flippeddatax 1
|
||||
|
||||
settingsdir /home/l_frojdh/slsDetectorPackage/settingsdir/eiger
|
||||
outdir /home/l_frojdh/out
|
||||
vthreshold 1500
|
||||
vtr 4000
|
||||
dr 32
|
||||
|
||||
threaded 1
|
||||
tengiga 0
|
||||
vhighvoltage 150
|
||||
iodelay 660
|
||||
|
||||
#gappixels 1
|
2
python/simple-integration-tests/eiger/test.par
Executable file
2
python/simple-integration-tests/eiger/test.par
Executable file
@ -0,0 +1,2 @@
|
||||
vrf 3000
|
||||
vthreshold 1800
|
44
python/simple-integration-tests/eiger/test_dynamic_range.py
Executable file
44
python/simple-integration-tests/eiger/test_dynamic_range.py
Executable file
@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Testing setting dynamic range for Eiger.
|
||||
If the detector is not Eiger the tests are skipped
|
||||
"""
|
||||
import pytest
|
||||
import config_test
|
||||
from fixtures import detector, eiger, jungfrau, eigertest, jungfrautest
|
||||
from sls_detector.errors import DetectorValueError
|
||||
|
||||
|
||||
@eigertest
|
||||
def test_set_dynamic_range_and_make_acq(eiger):
|
||||
eiger.exposure_time = 0.5
|
||||
eiger.n_frames = 2
|
||||
for dr in [4, 8, 16, 32]:
|
||||
eiger.dynamic_range = dr
|
||||
assert eiger.dynamic_range == dr
|
||||
eiger.acq()
|
||||
assert eiger.frames_caught == 2
|
||||
|
||||
|
||||
@eigertest
|
||||
def test_set_dynamic_range_raises(eiger):
|
||||
with pytest.raises(DetectorValueError):
|
||||
eiger.dynamic_range = 1
|
||||
with pytest.raises(DetectorValueError):
|
||||
eiger.dynamic_range = 75
|
||||
with pytest.raises(DetectorValueError):
|
||||
eiger.dynamic_range = -3
|
||||
with pytest.raises(DetectorValueError):
|
||||
eiger.dynamic_range = 12
|
||||
|
||||
@eigertest
|
||||
def test_set_dynamic_range_reduces_speed(eiger):
|
||||
eiger.readout_clock = 'Full Speed'
|
||||
eiger.dynamic_range = 32
|
||||
assert eiger.dynamic_range == 32
|
||||
assert eiger.readout_clock == 'Quarter Speed'
|
||||
|
||||
eiger.dynamic_range = 16
|
||||
assert eiger.dynamic_range == 16
|
||||
assert eiger.readout_clock == 'Half Speed'
|
119
python/simple-integration-tests/eiger/test_eiger_specific.py
Executable file
119
python/simple-integration-tests/eiger/test_eiger_specific.py
Executable file
@ -0,0 +1,119 @@
|
||||
import pytest
|
||||
import config_test
|
||||
import time
|
||||
from sls_detector.errors import DetectorValueError
|
||||
|
||||
from fixtures import eiger, eigertest
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@eigertest
|
||||
def test_set_matrix_reset(eiger):
|
||||
eiger.eiger_matrix_reset = False
|
||||
assert eiger.eiger_matrix_reset == False
|
||||
eiger.eiger_matrix_reset = True
|
||||
assert eiger.eiger_matrix_reset == True
|
||||
|
||||
@eigertest
|
||||
def test_set_tx_delay_left_single(eiger):
|
||||
eiger.tx_delay.left[0] = 130
|
||||
assert eiger.tx_delay.left[0] == 130
|
||||
eiger.tx_delay.left[1] = 150
|
||||
assert eiger.tx_delay.left[1] == 150
|
||||
eiger.tx_delay.left[0] = 0
|
||||
eiger.tx_delay.left[1] = 0
|
||||
assert eiger.tx_delay.left[0] == 0
|
||||
assert eiger.tx_delay.left[1] == 0
|
||||
|
||||
@eigertest
|
||||
def test_set_tx_delay_right_single(eiger):
|
||||
eiger.tx_delay.right[0] = 130
|
||||
assert eiger.tx_delay.right[0] == 130
|
||||
eiger.tx_delay.right[1] = 150
|
||||
assert eiger.tx_delay.right[1] == 150
|
||||
eiger.tx_delay.right[0] = 0
|
||||
eiger.tx_delay.right[1] = 0
|
||||
assert eiger.tx_delay.right[0] == 0
|
||||
assert eiger.tx_delay.right[1] == 0
|
||||
|
||||
@eigertest
|
||||
def test_set_tx_delay_frame_single(eiger):
|
||||
eiger.tx_delay.frame[0] = 500
|
||||
eiger.tx_delay.frame[1] = 600
|
||||
assert eiger.tx_delay.frame[0] == 500
|
||||
assert eiger.tx_delay.frame[1] == 600
|
||||
|
||||
eiger.tx_delay.frame[0] = 0
|
||||
eiger.tx_delay.frame[1] = 0
|
||||
assert eiger.tx_delay.frame[0] == 0
|
||||
assert eiger.tx_delay.frame[1] == 0
|
||||
|
||||
@eigertest
|
||||
def test_tx_delay_from_list(eiger):
|
||||
eiger.tx_delay.left = [123,456]
|
||||
assert eiger.tx_delay.left[:] == [123,456]
|
||||
eiger.tx_delay.right = [789,100]
|
||||
assert eiger.tx_delay.right[:] == [789,100]
|
||||
eiger.tx_delay.frame = [1000,90000]
|
||||
assert eiger.tx_delay.frame[:] == [1000,90000]
|
||||
|
||||
eiger.tx_delay.left = [0, 0]
|
||||
eiger.tx_delay.right = [0, 0]
|
||||
eiger.tx_delay.frame = [0, 0]
|
||||
assert eiger.tx_delay.left[:] == [0, 0]
|
||||
assert eiger.tx_delay.right[:] == [0, 0]
|
||||
assert eiger.tx_delay.frame[:] == [0, 0]
|
||||
|
||||
@eigertest
|
||||
def test_acitve(eiger):
|
||||
eiger.file_write = False
|
||||
eiger.reset_frames_caught()
|
||||
eiger.active[1] = False
|
||||
eiger.acq()
|
||||
assert eiger._api.getFramesCaughtByReceiver(1) == 0
|
||||
assert eiger._api.getFramesCaughtByReceiver(0) == 1
|
||||
eiger.active = True
|
||||
time.sleep(0.5)
|
||||
eiger.acq()
|
||||
assert eiger.frames_caught == 1
|
||||
|
||||
@eigertest
|
||||
def test_set_default_settings(eiger):
|
||||
eiger.default_settings()
|
||||
assert eiger.n_frames == 1
|
||||
assert eiger.exposure_time == 1
|
||||
assert eiger.period == 0
|
||||
assert eiger.n_cycles == 1
|
||||
assert eiger.dynamic_range == 16
|
||||
|
||||
@eigertest
|
||||
def test_flowcontrol10g(eiger):
|
||||
eiger.flowcontrol_10g = True
|
||||
assert eiger.flowcontrol_10g == True
|
||||
eiger.flowcontrol_10g = False
|
||||
assert eiger.flowcontrol_10g == False
|
||||
|
||||
@eigertest
|
||||
def test_read_vcmp(eiger):
|
||||
eiger.vthreshold = 1500
|
||||
assert eiger.vcmp[:] == [1500]*4*eiger.n_modules
|
||||
|
||||
@eigertest
|
||||
def test_set_vcmp(eiger):
|
||||
eiger.vcmp = [1000,1100,1200,1300,1400,1500,1600,1700]
|
||||
assert eiger.vcmp[:] == [1000,1100,1200,1300,1400,1500,1600,1700]
|
||||
eiger.vthreshold = 1500
|
||||
|
||||
#Disabled only works with receiver on the same pc
|
||||
# @eigertest
|
||||
# def test_setup500k():
|
||||
# from sls_detector import Eiger, free_shared_memory
|
||||
# free_shared_memory()
|
||||
# d = Eiger()
|
||||
# d.setup500k(config_test.known_hostnames)
|
||||
# d.acq()
|
||||
# assert d.rx_tcpport == [1954,1955]
|
||||
# assert d.frames_caught == 1
|
||||
# #could assert more setting but if the frame is caught it worked...
|
129
python/simple-integration-tests/eiger/test_firmware.py
Executable file
129
python/simple-integration-tests/eiger/test_firmware.py
Executable file
@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests specific for the firmware.
|
||||
|
||||
Check that register values are correct after starting an exposure
|
||||
|
||||
0x4 exposure time
|
||||
0x5 period
|
||||
0x6 sub exposure time
|
||||
|
||||
"""
|
||||
import pytest
|
||||
import config_test
|
||||
from fixtures import detector, eiger, jungfrau, eigertest, jungfrautest
|
||||
from sls_detector.errors import DetectorValueError
|
||||
from sls_detector.utils import eiger_register_to_time
|
||||
|
||||
# testdata_exptimes = [0.001, 0.002, 0.0236]
|
||||
|
||||
@eigertest
|
||||
def test_short_exposure_time(eiger):
|
||||
t = 1.23
|
||||
eiger.exposure_time = t
|
||||
eiger.file_write = False
|
||||
eiger.start_detector()
|
||||
eiger.stop_detector()
|
||||
|
||||
#Register 0x4 holds exposure time
|
||||
reg = eiger.register[0x4]
|
||||
assert pytest.approx(t, 1e-9) == eiger_register_to_time(reg)
|
||||
|
||||
@eigertest
|
||||
def test_short_minimal_exposure_time(eiger):
|
||||
t = 1e-8
|
||||
eiger.exposure_time = t
|
||||
eiger.file_write = False
|
||||
eiger.start_detector()
|
||||
eiger.stop_detector()
|
||||
|
||||
#Register 0x4 holds exposure time
|
||||
reg = eiger.register[0x4]
|
||||
assert pytest.approx(t, 1e-9) == eiger_register_to_time(reg)
|
||||
|
||||
|
||||
@eigertest
|
||||
def test_long_exposure_time(eiger):
|
||||
t = 623
|
||||
eiger.exposure_time = t
|
||||
eiger.file_write = False
|
||||
eiger.start_detector()
|
||||
eiger.stop_detector()
|
||||
|
||||
# Register 0x4 holds exposure time
|
||||
reg = eiger.register[0x4]
|
||||
assert pytest.approx(t, 1e-9) == eiger_register_to_time(reg)
|
||||
|
||||
|
||||
@eigertest
|
||||
def test_short_period(eiger):
|
||||
t = 0.1
|
||||
eiger.exposure_time = 0.001
|
||||
eiger.period = t
|
||||
eiger.file_write = False
|
||||
eiger.start_detector()
|
||||
eiger.stop_detector()
|
||||
|
||||
# Register 0x5 holds period
|
||||
reg = eiger.register[0x5]
|
||||
assert pytest.approx(t, 1e-9) == eiger_register_to_time(reg)
|
||||
|
||||
|
||||
@eigertest
|
||||
def test_long_period(eiger):
|
||||
t = 8900
|
||||
eiger.exposure_time = 0.001
|
||||
eiger.period = t
|
||||
eiger.file_write = False
|
||||
eiger.start_detector()
|
||||
eiger.stop_detector()
|
||||
|
||||
# Register 0x5 holds period
|
||||
reg = eiger.register[0x5]
|
||||
assert pytest.approx(t, 1e-9) == eiger_register_to_time(reg)
|
||||
|
||||
@eigertest
|
||||
def test_zero_period_with_acq(eiger):
|
||||
t = 0
|
||||
eiger.exposure_time = 0.001
|
||||
eiger.period = t
|
||||
eiger.file_write = False
|
||||
eiger.start_detector()
|
||||
eiger.stop_detector()
|
||||
|
||||
# Register 0x5 holds period
|
||||
reg = eiger.register[0x5]
|
||||
assert pytest.approx(t, 1e-9) == eiger_register_to_time(reg)
|
||||
|
||||
|
||||
testdata_times = [0.001, 0.002, 0.0236]
|
||||
@eigertest
|
||||
@pytest.mark.parametrize("t", testdata_times)
|
||||
def test_subexptime(eiger,t):
|
||||
eiger.sub_exposure_time = t
|
||||
eiger.file_write = False
|
||||
eiger.start_detector()
|
||||
eiger.stop_detector()
|
||||
|
||||
# Register 0x6 holds sub exposure time
|
||||
# time is stored straight as n clocks
|
||||
reg = eiger.register[0x6]
|
||||
assert pytest.approx(t, 1e-9) == reg/100e6
|
||||
|
||||
|
||||
@eigertest
|
||||
@pytest.mark.parametrize("t", testdata_times)
|
||||
def test_subdeadtime(eiger, t):
|
||||
eiger.sub_deadtime = t
|
||||
eiger.sub_exposure_time = 1
|
||||
eiger.sub_exposure_time = 0.001
|
||||
eiger.file_write = False
|
||||
eiger.start_detector()
|
||||
eiger.stop_detector()
|
||||
|
||||
# Register 0x7 holds sub period
|
||||
# time is stored straight as n clocks
|
||||
# exptime+deadtime
|
||||
reg = eiger.register[0x7]
|
||||
assert pytest.approx(t, 1e-7) == (reg/100e6-0.001)
|
187
python/simple-integration-tests/eiger/test_general.py
Executable file
187
python/simple-integration-tests/eiger/test_general.py
Executable file
@ -0,0 +1,187 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
General tests for the Detector class. Should not depend on the connected detector. Aim is to have tests working
|
||||
for both Jungfrau and Eiger.
|
||||
|
||||
NOTE! Uses hostnames from config_test
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import config_test
|
||||
from fixtures import detector
|
||||
from sls_detector.errors import DetectorValueError, DetectorError
|
||||
|
||||
|
||||
|
||||
def test_error_handling(detector):
|
||||
with pytest.raises(DetectorError):
|
||||
detector._provoke_error()
|
||||
|
||||
def test_not_busy(detector):
|
||||
"""Test that the detector is not busy from the start"""
|
||||
assert detector.busy == False
|
||||
|
||||
def test_reset_frames_caught(detector):
|
||||
detector.file_write = False
|
||||
detector.acq()
|
||||
assert detector.frames_caught == 1
|
||||
detector.reset_frames_caught()
|
||||
assert detector.frames_caught == 0
|
||||
|
||||
def test_set_busy_true_then_false(detector):
|
||||
"""Test both cases of assignment"""
|
||||
detector.busy = True
|
||||
assert detector.busy == True
|
||||
detector.busy = False
|
||||
assert detector.busy == False
|
||||
|
||||
def test_set_readout_speed(detector):
|
||||
for s in ['Full Speed', 'Half Speed', 'Quarter Speed', 'Super Slow Speed']:
|
||||
detector.readout_clock = s
|
||||
assert detector.readout_clock == s
|
||||
|
||||
def test_wrong_speed_raises_error(detector):
|
||||
with pytest.raises(KeyError):
|
||||
detector.readout_clock = 'Something strange'
|
||||
|
||||
def test_readout_clock_remains(detector):
|
||||
s = detector.readout_clock
|
||||
try:
|
||||
detector.readout_clock = 'This does not exists'
|
||||
except KeyError:
|
||||
pass
|
||||
assert detector.readout_clock == s
|
||||
|
||||
def test_len_method(detector):
|
||||
"""to test this we need to know the length, this we get from the configuration of hostnames"""
|
||||
assert len(detector) == len(config_test.known_hostnames)
|
||||
|
||||
def test_setting_n_cycles_to_zero_gives_error(detector):
|
||||
with pytest.raises(DetectorValueError):
|
||||
detector.n_cycles = 0
|
||||
|
||||
def test_setting_n_cycles_to_negative_gives_error(detector):
|
||||
with pytest.raises(DetectorValueError):
|
||||
detector.n_cycles = -50
|
||||
|
||||
def test_set_cycles_frome_one_to_ten(detector):
|
||||
for i in range(1,11):
|
||||
detector.n_cycles = i
|
||||
assert detector.n_cycles == i
|
||||
detector.n_cycles = 1
|
||||
assert detector.n_cycles == 1
|
||||
|
||||
def test_get_detector_type(detector):
|
||||
assert detector.detector_type == config_test.detector_type
|
||||
|
||||
|
||||
|
||||
def test_set_file_index(detector):
|
||||
detector.file_index = 5
|
||||
assert detector.file_index == 5
|
||||
|
||||
def test_negative_file_index_raises(detector):
|
||||
with pytest.raises(ValueError):
|
||||
detector.file_index = -8
|
||||
|
||||
def test_setting_file_name(detector):
|
||||
fname = 'hej'
|
||||
detector.file_name = fname
|
||||
assert detector.file_name == fname
|
||||
|
||||
def test_set_file_write(detector):
|
||||
detector.file_write = True
|
||||
assert detector.file_write == True
|
||||
|
||||
detector.file_write = False
|
||||
assert detector.file_write == False
|
||||
|
||||
|
||||
|
||||
def test_set_high_voltage(detector):
|
||||
detector.high_voltage = 55
|
||||
assert detector.high_voltage == 55
|
||||
|
||||
def test_negative_voltage_raises(detector):
|
||||
with pytest.raises(DetectorValueError):
|
||||
detector.high_voltage = -5
|
||||
|
||||
def test_high_voltage_raises_on_to_high(detector):
|
||||
with pytest.raises(DetectorValueError):
|
||||
detector.high_voltage = 500
|
||||
|
||||
|
||||
|
||||
def test_get_image_size(detector):
|
||||
"""Compares with the size in the config file"""
|
||||
assert detector.image_size.rows == config_test.image_size[0]
|
||||
assert detector.image_size.cols == config_test.image_size[1]
|
||||
|
||||
def test_get_module_geometry(detector):
|
||||
"""Compares with the size in the config file"""
|
||||
assert detector.module_geometry.horizontal == config_test.module_geometry[0]
|
||||
assert detector.module_geometry.vertical == config_test.module_geometry[1]
|
||||
|
||||
def test_set_nframes(detector):
|
||||
detector.n_frames = 5
|
||||
assert detector.n_frames == 5
|
||||
detector.n_frames = 1
|
||||
assert detector.n_frames == 1
|
||||
|
||||
def test_set_n_measurements(detector):
|
||||
detector.n_measurements = 7
|
||||
assert detector.n_measurements == 7
|
||||
detector.n_measurements = 1
|
||||
assert detector.n_measurements == 1
|
||||
|
||||
def test_negative_nframes_raises(detector):
|
||||
with pytest.raises(DetectorValueError):
|
||||
detector.n_frames = -2
|
||||
|
||||
def test_nmodules(detector):
|
||||
"""Assume that the number of modules should be the same as the number of hostnames"""
|
||||
assert detector.n_modules == len(config_test.known_hostnames)
|
||||
|
||||
def test_is_detector_online(detector):
|
||||
assert detector.online == True
|
||||
|
||||
def test_set_online(detector):
|
||||
detector.online = False
|
||||
assert detector.online == False
|
||||
detector.online = True
|
||||
assert detector.online == True
|
||||
|
||||
|
||||
|
||||
def test_receiver_is_online(detector):
|
||||
assert detector.receiver_online == True
|
||||
|
||||
def test_set_receiver_online(detector):
|
||||
detector.receiver_online = False
|
||||
assert detector.receiver_online == False
|
||||
detector.receiver_online = True
|
||||
assert detector.receiver_online == True
|
||||
|
||||
def test_set_receiver_online_raises_on_non_bool(detector):
|
||||
with pytest.raises(TypeError):
|
||||
detector.receiver_online = 'probably not this'
|
||||
|
||||
|
||||
|
||||
|
||||
def test_set_period(detector):
|
||||
detector.period = 5.123
|
||||
assert detector.period == 5.123
|
||||
detector.period = 0
|
||||
assert detector.period == 0
|
||||
|
||||
|
||||
|
||||
def test_set_timing_mode(detector):
|
||||
detector.timing_mode = 'trigger'
|
||||
assert detector.timing_mode == 'trigger'
|
||||
detector.timing_mode = 'auto'
|
||||
assert detector.timing_mode == 'auto'
|
||||
|
||||
|
38
python/simple-integration-tests/eiger/test_load_config.py
Executable file
38
python/simple-integration-tests/eiger/test_load_config.py
Executable file
@ -0,0 +1,38 @@
|
||||
|
||||
import pytest
|
||||
import config_test
|
||||
import os
|
||||
dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||
from sls_detector.detector import element_if_equal
|
||||
from sls_detector.errors import DetectorValueError
|
||||
|
||||
|
||||
from fixtures import eiger, eigertest
|
||||
|
||||
|
||||
@eigertest
|
||||
def test_load_config_file_eiger(eiger):
|
||||
"""Load a settings file and assert all settings"""
|
||||
eiger.load_config(os.path.join(dir_path, 'test.config'))
|
||||
|
||||
|
||||
assert eiger.rx_tcpport == [1954, 1955]
|
||||
assert eiger.lock == False
|
||||
assert eiger.rx_udpport == [50010, 50011, 50004, 50005]
|
||||
assert eiger.rx_hostname == 'mpc2048'
|
||||
assert eiger.flipped_data_x[:] == [False, True]
|
||||
assert eiger.settings_path == config_test.settings_path
|
||||
assert eiger.file_path == config_test.file_path
|
||||
assert eiger.vthreshold == 1500
|
||||
assert element_if_equal(eiger.dacs.vtr[:]) == 4000
|
||||
assert eiger.dynamic_range == 32
|
||||
assert eiger.tengiga == False
|
||||
assert eiger.high_voltage == 150
|
||||
assert element_if_equal(eiger.dacs.iodelay[:]) == 660
|
||||
|
||||
@eigertest
|
||||
def test_load_parameters_file_eiger(eiger):
|
||||
"""Load a parametes file and assert the settings in the file"""
|
||||
eiger.load_parameters(os.path.join(dir_path, 'test.par'))
|
||||
assert element_if_equal(eiger.dacs.vrf[:]) == 3000
|
||||
assert eiger.vthreshold == 1800
|
81
python/simple-integration-tests/eiger/test_network.py
Executable file
81
python/simple-integration-tests/eiger/test_network.py
Executable file
@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for network related functions of the detector
|
||||
"""
|
||||
import pytest
|
||||
import config_test
|
||||
from fixtures import eiger, eigertest, detector
|
||||
|
||||
|
||||
# def test_last_client(detector):
|
||||
# import socket
|
||||
# # We probably should check for multiple ip's
|
||||
# myip = socket.gethostbyname_ex(socket.gethostname())[-1][0]
|
||||
# assert detector.last_client_ip == myip
|
||||
|
||||
def test_get_hostname(detector):
|
||||
for detector_host, config_host in zip(detector.hostname, config_test.known_hostnames):
|
||||
assert detector_host == config_host
|
||||
|
||||
def test_hostname_has_same_length_as_n_modules(detector):
|
||||
assert len(detector.hostname) == detector.n_modules
|
||||
|
||||
|
||||
# # def test_get_receiver_hostname(detector):
|
||||
# # """Assume that the receiver are on the local computer"""
|
||||
# # import socket
|
||||
# # host = socket.gethostname().split('.')[0]
|
||||
# # assert detector.rx_hostname == host
|
||||
|
||||
# def test_set_receiver_hostname(detector):
|
||||
# import socket
|
||||
# host = socket.gethostname().split('.')[0]
|
||||
# phony_host = 'madeup'
|
||||
# detector.rx_hostname = phony_host
|
||||
# assert detector.rx_hostname == phony_host
|
||||
# detector.rx_hostname = host
|
||||
# assert detector.rx_hostname == host
|
||||
|
||||
@eigertest
|
||||
def test_set_rx_zmqport_single_value(eiger):
|
||||
eiger.rx_zmqport = 35000
|
||||
assert eiger.rx_zmqport == [35000, 35001, 35002, 35003]
|
||||
|
||||
@eigertest
|
||||
def test_set_rx_zmqport_list(eiger):
|
||||
eiger.rx_zmqport = [37000, 38000]
|
||||
assert eiger.rx_zmqport == [37000, 37001, 38000, 38001]
|
||||
|
||||
@eigertest
|
||||
def test_set_rx_updport(eiger):
|
||||
ports = [60010,60011,60012,60013]
|
||||
eiger.rx_udpport = ports
|
||||
assert eiger.rx_udpport == ports
|
||||
eiger.acq()
|
||||
assert eiger.frames_caught == 1
|
||||
|
||||
@eigertest
|
||||
def test_rx_tcpport(eiger):
|
||||
ports = eiger.rx_tcpport
|
||||
eiger.rx_tcpport = [2000,2001]
|
||||
assert eiger.rx_tcpport == [2000,2001]
|
||||
eiger.rx_tcpport = ports
|
||||
assert eiger.rx_tcpport == ports
|
||||
eiger.acq()
|
||||
assert eiger.frames_caught == 1
|
||||
|
||||
# @eigertest
|
||||
# @pytest.mark.new
|
||||
# def test_enable_disable_tengiga(eiger):
|
||||
# """
|
||||
# This test does not check for dat on the 10Gbit link, only the set and get functions
|
||||
# """
|
||||
# eiger.tengiga = True
|
||||
# assert eiger.tengiga == True
|
||||
# eiger.tengiga = False
|
||||
# assert eiger.tengiga == False
|
||||
|
||||
|
||||
|
||||
#TODO! Add test for Jungfrau
|
54
python/simple-integration-tests/eiger/test_paths_and_files.py
Executable file
54
python/simple-integration-tests/eiger/test_paths_and_files.py
Executable file
@ -0,0 +1,54 @@
|
||||
import pytest
|
||||
import config_test
|
||||
from fixtures import detector, eiger, jungfrau, eigertest, jungfrautest
|
||||
from sls_detector.errors import DetectorValueError
|
||||
|
||||
|
||||
|
||||
|
||||
@eigertest
|
||||
@pytest.mark.local
|
||||
def test_set_path(eiger, tmpdir):
|
||||
import os
|
||||
path = os.path.join(tmpdir.dirname, tmpdir.basename)
|
||||
eiger.file_path = path
|
||||
assert eiger.file_path == path
|
||||
|
||||
@eigertest
|
||||
@pytest.mark.local
|
||||
def test_set_path_and_write_files(eiger, tmpdir):
|
||||
import os
|
||||
prefix = 'testprefix'
|
||||
path = os.path.join(tmpdir.dirname, tmpdir.basename)
|
||||
eiger.file_path = path
|
||||
eiger.file_write = True
|
||||
eiger.exposure_time = 0.1
|
||||
eiger.n_frames = 1
|
||||
eiger.timing_mode = 'auto'
|
||||
eiger.file_name = prefix
|
||||
eiger.file_index = 0
|
||||
eiger.acq()
|
||||
|
||||
files = [f.basename for f in tmpdir.listdir()]
|
||||
|
||||
assert len(files) == 5
|
||||
assert (prefix+'_d0_0.raw' in files) == True
|
||||
assert (prefix+'_d1_0.raw' in files) == True
|
||||
assert (prefix+'_d2_0.raw' in files) == True
|
||||
assert (prefix+'_d3_0.raw' in files) == True
|
||||
|
||||
def test_set_discard_policy(detector):
|
||||
detector.frame_discard_policy = 'nodiscard'
|
||||
assert detector.frame_discard_policy == 'nodiscard'
|
||||
detector.frame_discard_policy = 'discardpartial'
|
||||
assert detector.frame_discard_policy == 'discardpartial'
|
||||
detector.frame_discard_policy = 'discardempty'
|
||||
assert detector.frame_discard_policy == 'discardempty'
|
||||
|
||||
def test_set_discard_policy_raises(detector):
|
||||
with pytest.raises(ValueError):
|
||||
detector.frame_discard_policy = 'adjfvadksvsj'
|
||||
|
||||
def test_set_frames_perfile(detector):
|
||||
detector.frames_per_file = 5000
|
||||
assert detector.frames_per_file == 5000
|
47
python/simple-integration-tests/eiger/test_threshold.py
Executable file
47
python/simple-integration-tests/eiger/test_threshold.py
Executable file
@ -0,0 +1,47 @@
|
||||
import pytest
|
||||
import config_test
|
||||
import time
|
||||
from sls_detector.errors import DetectorValueError
|
||||
import os
|
||||
from fixtures import eiger, eigertest
|
||||
|
||||
|
||||
testdata_th = [0,333,500,1750,2000]
|
||||
|
||||
@eigertest
|
||||
@pytest.mark.parametrize("th", testdata_th)
|
||||
def test_set_vthreshold(eiger, th):
|
||||
eiger.vthreshold = th
|
||||
assert eiger.vthreshold == th
|
||||
|
||||
@eigertest
|
||||
def test_vthreshold_with_different_vcmp(eiger):
|
||||
#When vcmp is different for the chip vthreshold should return -1
|
||||
eiger.vthreshold = 1500
|
||||
eiger.dacs.vcmp_ll = 1400
|
||||
assert eiger.vthreshold == -1
|
||||
|
||||
@eigertest
|
||||
def test_set_settingsdir(eiger):
|
||||
path = os.path.dirname( os.path.realpath(__file__) )
|
||||
path = os.path.join(path, 'settingsdir')
|
||||
eiger.settings_path = path
|
||||
assert eiger.settings_path == path
|
||||
|
||||
@eigertest
|
||||
def test_set_trimmed_energies(eiger):
|
||||
en = [5000,6000,7000]
|
||||
eiger.trimmed_energies = en
|
||||
assert eiger.trimmed_energies == en
|
||||
|
||||
|
||||
#TODO! add checks for vcmp as well and improve naming
|
||||
#TODO! remove dependency on beb number
|
||||
testdata_en = [(5000, 500),(5500,750),(6000,1000),(6200,1100),(7000,1500)]
|
||||
@eigertest
|
||||
@pytest.mark.parametrize('val', testdata_en)
|
||||
def test_set_energy_threshold(eiger, val):
|
||||
eiger.settings = 'standard'
|
||||
eiger.threshold = val[0]
|
||||
assert eiger.threshold == val[0]
|
||||
assert eiger.dacs.vrf[0] == val[1]
|
136
python/simple-integration-tests/eiger/test_time.py
Executable file
136
python/simple-integration-tests/eiger/test_time.py
Executable file
@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests regarding exposure time and period of the detector
|
||||
Set and get test as well as test for duration and on detector
|
||||
measurement of the time.
|
||||
"""
|
||||
import pytest
|
||||
import config_test
|
||||
from fixtures import detector, eiger, jungfrau, eigertest, jungfrautest
|
||||
from sls_detector.errors import DetectorValueError, DetectorError
|
||||
import time
|
||||
|
||||
|
||||
testdata_times = [1e-8, 0.001, 0.5, 3.125, 5.0, 600, 784]
|
||||
@pytest.mark.parametrize("t", testdata_times)
|
||||
def test_set_and_get_exposure_time(eiger, t):
|
||||
"""
|
||||
Test that the exposure time we set in the detector
|
||||
is the same as the one read back
|
||||
"""
|
||||
eiger.exposure_time = t
|
||||
assert eiger.exposure_time == t
|
||||
|
||||
|
||||
def test_negative_exposure_time_raises_error(eiger):
|
||||
with pytest.raises(DetectorValueError):
|
||||
eiger.exposure_time = -15
|
||||
|
||||
|
||||
testdata_times = [0.001, 0.0025, 0.005, 5]
|
||||
@pytest.mark.parametrize("t", testdata_times)
|
||||
def test_set_subexptime(eiger, t):
|
||||
eiger.sub_exposure_time = t
|
||||
assert eiger.sub_exposure_time == t
|
||||
|
||||
|
||||
testdata_times = [-5,6,7,50]
|
||||
@pytest.mark.parametrize("t", testdata_times)
|
||||
def test_set_subextime_too_large_or_neg(eiger, t):
|
||||
with pytest.raises((DetectorError, DetectorValueError)):
|
||||
eiger.sub_exposure_time = t
|
||||
|
||||
|
||||
|
||||
testdata_times = [0.2, 0.5, 1, 2, 5, 7]
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.parametrize("t", testdata_times)
|
||||
def test_measure_exposure_time_from_python(eiger, t):
|
||||
"""
|
||||
The main idea with this test is to make sure the overhead of a
|
||||
single acq is less than tol[s]. This test also catches stupid bugs
|
||||
that would for example not change the exposure time or make acquire
|
||||
not blocking.
|
||||
"""
|
||||
tol = 0.5
|
||||
eiger.dynamic_range = 16
|
||||
eiger.file_write = False
|
||||
eiger.n_frames = 1
|
||||
eiger.exposure_time = t
|
||||
assert eiger.exposure_time == t
|
||||
t0 = time.time()
|
||||
eiger.acq()
|
||||
duration = time.time()-t0
|
||||
assert duration < (t+tol)
|
||||
|
||||
|
||||
testdata_times = [0.5, 1, 3, 5]
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.parametrize("t", testdata_times)
|
||||
def test_measure_period_from_python_and_detector(eiger, t):
|
||||
tol = 0.5
|
||||
nframes = 5
|
||||
eiger.dynamic_range = 16
|
||||
eiger.file_write = False
|
||||
eiger.n_frames = nframes
|
||||
eiger.exposure_time = 0.001
|
||||
eiger.period = t
|
||||
t0 = time.time()
|
||||
eiger.acq()
|
||||
duration = time.time()-t0
|
||||
assert duration < t*(nframes-1)+tol
|
||||
for mp in eiger.measured_period:
|
||||
assert pytest.approx(mp, 1e-5) == t
|
||||
|
||||
|
||||
testdata_times = [0.001, 0.002, 0.003, 0.005, 0.01]
|
||||
@pytest.mark.parametrize("t", testdata_times)
|
||||
def test_measure_subperiod_nonparallel(eiger, t):
|
||||
readout_time = 500e-6
|
||||
eiger.dynamic_range = 32
|
||||
eiger.file_write = False
|
||||
eiger.flags = 'nonparallel'
|
||||
eiger.n_frames = 1
|
||||
eiger.period = 0
|
||||
eiger.exposure_time = 0.5
|
||||
eiger.sub_exposure_time = t
|
||||
eiger.sub_deadtime = 0
|
||||
eiger.acq()
|
||||
for mp in eiger.measured_subperiod:
|
||||
assert pytest.approx(mp, abs=1e-5) == t+readout_time
|
||||
|
||||
|
||||
@pytest.mark.parametrize("t", testdata_times)
|
||||
def test_measure_subperiod_parallel(eiger, t):
|
||||
readout_time = 12e-6
|
||||
eiger.dynamic_range = 32
|
||||
eiger.file_write = False
|
||||
eiger.flags = 'parallel'
|
||||
eiger.n_frames = 1
|
||||
eiger.period = 0
|
||||
eiger.exposure_time = 0.5
|
||||
eiger.sub_exposure_time = t
|
||||
eiger.sub_deadtime = 0
|
||||
eiger.acq()
|
||||
for mp in eiger.measured_subperiod:
|
||||
assert pytest.approx(mp, abs=1e-5) == t+readout_time
|
||||
|
||||
|
||||
@pytest.mark.parametrize("t", testdata_times)
|
||||
def test_measure_subperiod_parallel_when_changing_deadtime(eiger, t):
|
||||
readout_time = 12e-6
|
||||
exposure_time = 0.001
|
||||
eiger.dynamic_range = 32
|
||||
eiger.file_write = False
|
||||
eiger.flags = 'parallel'
|
||||
eiger.n_frames = 1
|
||||
eiger.period = 0
|
||||
eiger.exposure_time = 0.5
|
||||
eiger.sub_exposure_time = exposure_time
|
||||
eiger.sub_deadtime = t
|
||||
eiger.acq()
|
||||
for mp in eiger.measured_subperiod:
|
||||
assert pytest.approx(mp, abs=1e-5) == t+exposure_time
|
34
python/simple-integration-tests/eiger/test_trimbits_and_dacs.py
Executable file
34
python/simple-integration-tests/eiger/test_trimbits_and_dacs.py
Executable file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for trimbit and dac related functions
|
||||
"""
|
||||
import pytest
|
||||
import config_test
|
||||
from fixtures import detector, eiger, jungfrau, eigertest, jungfrautest
|
||||
from sls_detector.errors import DetectorValueError
|
||||
|
||||
|
||||
@eigertest
|
||||
def test_set_trimbits(eiger):
|
||||
"""Limited values due to time"""
|
||||
for i in [17, 32, 60]:
|
||||
print(i)
|
||||
eiger.trimbits = i
|
||||
assert eiger.trimbits == i
|
||||
|
||||
@eigertest
|
||||
def test_set_trimbits_raises_on_too_big(eiger):
|
||||
with pytest.raises(DetectorValueError):
|
||||
eiger.trimbits = 75
|
||||
|
||||
@eigertest
|
||||
def test_set_trimbits_raises_on_negative(eiger):
|
||||
with pytest.raises(DetectorValueError):
|
||||
eiger.trimbits = -5
|
||||
|
||||
|
||||
# @jungfrautest
|
||||
# def test_jungfrau(jungfrau):
|
||||
# """Example of a test that is not run with Eiger connected"""
|
||||
# pass
|
16
python/simple-integration-tests/eiger/test_version_numbers.py
Executable file
16
python/simple-integration-tests/eiger/test_version_numbers.py
Executable file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for hostname related functions of the detector
|
||||
"""
|
||||
import pytest
|
||||
import config_test
|
||||
from fixtures import detector, eiger, jungfrau, eigertest, jungfrautest
|
||||
from sls_detector.errors import DetectorValueError
|
||||
|
||||
|
||||
|
||||
def test_firmware_version(detector):
|
||||
assert detector.firmware_version == config_test.fw_version
|
||||
|
||||
|
42
python/simple-integration-tests/eiger/write_tb_files.py
Executable file
42
python/simple-integration-tests/eiger/write_tb_files.py
Executable file
@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue May 22 14:13:48 2018
|
||||
|
||||
@author: l_frojdh
|
||||
"""
|
||||
import os
|
||||
from sls_detector_tools.io import write_trimbit_file
|
||||
from sls_detector_tools import mask
|
||||
|
||||
energy = [5000, 6000, 7000]
|
||||
vrf = [500, 1000, 1500]
|
||||
|
||||
for i,e in enumerate(energy):
|
||||
dacs = np.array( [[ 0., 0.], #vsvp
|
||||
[4000., 4000.], #vtr
|
||||
[vrf[i], vrf[i]], #vrf
|
||||
[1400., 1400.], #vrs
|
||||
[4000., 4000.], #vsvn
|
||||
[2556., 2556.], #vtgstv
|
||||
[1400., 1400.], #vcmp_ll
|
||||
[1500., 1500.], #vcmp_lr
|
||||
[4000., 4000.], #vcall
|
||||
[1500., 1500.], #vcmp_rl
|
||||
[1100., 1100.], #rxb_rb
|
||||
[1100., 1100.], #rxb_lb
|
||||
[1500., 1500.], #vcmp_rr
|
||||
[1500., 1500.], #vcp
|
||||
[2000., 2000.], #vcn
|
||||
[1550., 1550.], #vis
|
||||
[ 660., 660.], #iodelay
|
||||
[ 0., 0.], #tau
|
||||
])
|
||||
|
||||
tb = np.zeros((256,1024))
|
||||
|
||||
for beb in [83,98]:
|
||||
write_trimbit_file(f'settingsdir/standard/{e}eV/noise.sn{beb:03d}', tb, dacs[:,0])
|
||||
#print(os.getcwd())
|
||||
|
||||
#print( os.path.realpath(__file__))
|
17
python/simple-integration-tests/jungfrau_0_6/config_test.py
Executable file
17
python/simple-integration-tests/jungfrau_0_6/config_test.py
Executable file
@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Nov 14 16:49:07 2017
|
||||
|
||||
@author: l_frojdh
|
||||
"""
|
||||
|
||||
fw_version = 0x180220
|
||||
detector_type = 'Jungfrau'
|
||||
known_hostnames = ['bchip038']
|
||||
image_size = (512,1024) #rows, cols
|
||||
module_geometry = (1,1) #horizontal, vertical
|
||||
|
||||
#Remember to change these in the settings file as well!
|
||||
settings_path = '/home/l_lopez/projects/slsDetectorPackage/settingsdir/jungfrau'
|
||||
file_path = '/home/l_lopez/out'
|
23
python/simple-integration-tests/jungfrau_0_6/fixtures.py
Executable file
23
python/simple-integration-tests/jungfrau_0_6/fixtures.py
Executable file
@ -0,0 +1,23 @@
|
||||
import pytest
|
||||
|
||||
from sls_detector import Detector
|
||||
|
||||
@pytest.fixture
|
||||
def detector():
|
||||
from sls_detector import Detector
|
||||
return Detector()
|
||||
|
||||
@pytest.fixture
|
||||
def eiger():
|
||||
from sls_detector import Eiger
|
||||
return Eiger()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def jungfrau():
|
||||
from sls_detector import Jungfrau
|
||||
return Jungfrau()
|
||||
|
||||
detector_type = Detector().detector_type
|
||||
eigertest = pytest.mark.skipif(detector_type != 'Eiger', reason = 'Only valid for Eiger')
|
||||
jungfrautest = pytest.mark.skipif(detector_type != 'Jungfrau', reason = 'Only valid for Jungfrau')
|
17
python/simple-integration-tests/jungfrau_0_6/test_main.py
Executable file
17
python/simple-integration-tests/jungfrau_0_6/test_main.py
Executable file
@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
General tests for the Jungfrau detector.
|
||||
|
||||
NOTE! Uses hostnames from config_test
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import config_test
|
||||
import tests
|
||||
|
||||
import os
|
||||
dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
pytest.main(['-x', '-s', os.path.join(dir_path, 'tests/test_load_config.py')]) #Test 1
|
||||
pytest.main(['-x', '-s', os.path.join(dir_path, 'tests/test_overtemperature.py')]) #Test 2
|
21
python/simple-integration-tests/jungfrau_0_6/tests/test.config
Executable file
21
python/simple-integration-tests/jungfrau_0_6/tests/test.config
Executable file
@ -0,0 +1,21 @@
|
||||
detsizechan 1024 512
|
||||
|
||||
settingsdir /home/l_lopez/projects/slsDetectorPackage/settingsdir/jungfrau
|
||||
caldir /home/l_lopez/projects/slsDetectorPackage/settingsdir/jungfrau
|
||||
lock 0
|
||||
|
||||
hostname bchip094+
|
||||
|
||||
rx_udpport 1754
|
||||
rx_udpip 10.1.1.107
|
||||
rx_udpmac 90:E2:BA:9A:4F:D4
|
||||
detectorip 10.1.1.9
|
||||
detectormac 00:aa:bb:cc:dd:ee
|
||||
configuremac 0
|
||||
|
||||
powerchip 1
|
||||
timing auto
|
||||
|
||||
outdir /home/l_lopez/out
|
||||
threaded 1
|
||||
high
|
1
python/simple-integration-tests/jungfrau_0_6/tests/test.par
Executable file
1
python/simple-integration-tests/jungfrau_0_6/tests/test.par
Executable file
@ -0,0 +1 @@
|
||||
vhighvoltage 200
|
43
python/simple-integration-tests/jungfrau_0_6/tests/test_load_config.py
Executable file
43
python/simple-integration-tests/jungfrau_0_6/tests/test_load_config.py
Executable file
@ -0,0 +1,43 @@
|
||||
|
||||
import pytest
|
||||
import config_test
|
||||
import os
|
||||
dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
from fixtures import jungfrau, jungfrautest
|
||||
|
||||
|
||||
def load_config_file_jungfrau_test(jungfrau):
|
||||
"""Load a settings file and assert all settings"""
|
||||
|
||||
print('\tStarting load_config_file_jungfrau_test test case')
|
||||
|
||||
jungfrau.free_shared_memory
|
||||
jungfrau.load_config(os.path.join(dir_path, 'test.config'))
|
||||
|
||||
assert jungfrau.lock == False
|
||||
assert jungfrau.rx_udpport == ['1754']
|
||||
assert jungfrau.hostname == ['bchip094']
|
||||
assert jungfrau.firmware_version == config_test.fw_version
|
||||
|
||||
print('\tFinished load_config_file_jungfrau_test test case')
|
||||
|
||||
def load_parameters_file_jungfrau_test(jungfrau):
|
||||
"""Load a parametes file and assert the settings in the file"""
|
||||
|
||||
print('\tStarting load_parameters_file_jungfrau_test test case')
|
||||
|
||||
jungfrau.load_parameters(os.path.join(dir_path, 'test.par'))
|
||||
assert jungfrau.high_voltage == 200
|
||||
|
||||
print('\tFinished load_parameters_file_jungfrau_test test case')
|
||||
|
||||
@jungfrautest
|
||||
def test_main(jungfrau):
|
||||
print('\nTesting configuration file loading')
|
||||
|
||||
load_config_file_jungfrau_test(jungfrau)
|
||||
load_parameters_file_jungfrau_test(jungfrau)
|
||||
|
||||
print('Tested configuration file loading')
|
||||
|
68
python/simple-integration-tests/jungfrau_0_6/tests/test_overtemperature.py
Executable file
68
python/simple-integration-tests/jungfrau_0_6/tests/test_overtemperature.py
Executable file
@ -0,0 +1,68 @@
|
||||
|
||||
import pytest
|
||||
import config_test
|
||||
import time
|
||||
from fixtures import jungfrau, jungfrautest
|
||||
|
||||
def powerchip_test(jungfrau, control):
|
||||
"""
|
||||
|
||||
Test the main overtemperature protection control
|
||||
|
||||
"""
|
||||
#Set test initial conditions
|
||||
print('\tStarting powerchip_test test case')
|
||||
|
||||
jungfrau.power_chip = False
|
||||
jungfrau.temperature_control = control
|
||||
assert jungfrau.power_chip == False
|
||||
jungfrau.temperature_threshold = 35
|
||||
jungfrau.power_chip = True
|
||||
|
||||
|
||||
if jungfrau.temperature_control is True:
|
||||
if jungfrau.temperature_event is True:
|
||||
assert jungfrau.power_chip == False
|
||||
jungfrau.power_chip = True
|
||||
assert jungfrau.power_chip == False
|
||||
jungfrau.temperature_control = False
|
||||
assert jungfrau.power_chip == True
|
||||
jungfrau.temperature_control = True
|
||||
jungfrau.temperature_threshold = 50
|
||||
assert jungfrau.power_chip == False
|
||||
|
||||
print('\t\tWaiting to cool down the board. This may take a while...')
|
||||
while jungfrau.temperature_threshold < jungfrau.temp.fpga[0]:
|
||||
time.sleep(5)
|
||||
print('\t\tJungfrau MCB temperature: {0:.2f} °C'.format(jungfrau.temp.fpga[0]))
|
||||
|
||||
#Leave enough time to let the board cool down a bit more
|
||||
time.sleep(30)
|
||||
jungfrau.reset_temperature_event()
|
||||
|
||||
assert jungfrau.temperature_event == False
|
||||
assert jungfrau.power_chip == True
|
||||
|
||||
else:
|
||||
assert jungfrau.power_chip == True
|
||||
else:
|
||||
print('\t\tWaiting to warm up the board. This may take a while...')
|
||||
while jungfrau.temperature_threshold > jungfrau.temp.fpga[0]:
|
||||
time.sleep(5)
|
||||
print('\t\tJungfrau MCB temperature: {0:.2f} °C'.format(jungfrau.temp.fpga[0]))
|
||||
|
||||
assert jungfrau.temperature_event == False
|
||||
assert jungfrau.power_chip == True
|
||||
|
||||
print('\tFinished powerchip_test test case')
|
||||
|
||||
|
||||
#@jungfrautest
|
||||
def test_main(jungfrau):
|
||||
|
||||
print('\nTesting overtemperature protection control')
|
||||
|
||||
powerchip_test(jungfrau, False)
|
||||
powerchip_test(jungfrau, True)
|
||||
|
||||
print('Tested overtemperature protection control')
|
6
python/sls_detector/__init__.py
Executable file
6
python/sls_detector/__init__.py
Executable file
@ -0,0 +1,6 @@
|
||||
from .detector import Detector, DetectorError, free_shared_memory
|
||||
from .eiger import Eiger
|
||||
from .experimental import ExperimentalDetector
|
||||
from .jungfrau import Jungfrau
|
||||
from .jungfrau_ctb import JungfrauCTB
|
||||
from _sls_detector import DetectorApi
|
40
python/sls_detector/adcs.py
Executable file
40
python/sls_detector/adcs.py
Executable file
@ -0,0 +1,40 @@
|
||||
from functools import partial
|
||||
class Adc:
|
||||
def __init__(self, name, detector):
|
||||
self.name = name
|
||||
self._detector = detector
|
||||
self.get_nmod = self._detector._api.getNumberOfDetectors
|
||||
# Bind functions to get and set the dac
|
||||
self.get = partial(self._detector._api.getAdc, self.name)
|
||||
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""
|
||||
Get dacs either by slice, key or list
|
||||
"""
|
||||
if key == slice(None, None, None):
|
||||
return [self.get(i) / 1000 for i in range(self.get_nmod())]
|
||||
elif isinstance(key, Iterable):
|
||||
return [self.get(k) / 1000 for k in key]
|
||||
else:
|
||||
return self.get(key) / 1000
|
||||
|
||||
def __repr__(self):
|
||||
"""String representation for a single adc in all modules"""
|
||||
degree_sign = u'\N{DEGREE SIGN}'
|
||||
r_str = ['{:14s}: '.format(self.name)]
|
||||
r_str += ['{:6.2f}{:s}C, '.format(self.get(i)/1000, degree_sign) for i in range(self.get_nmod())]
|
||||
return ''.join(r_str).strip(', ')
|
||||
|
||||
|
||||
|
||||
class DetectorAdcs:
|
||||
"""
|
||||
Interface to the ADCs on the readout board
|
||||
"""
|
||||
def __iter__(self):
|
||||
for attr, value in self.__dict__.items():
|
||||
yield value
|
||||
|
||||
def __repr__(self):
|
||||
return '\n'.join([str(t) for t in self])
|
125
python/sls_detector/dacs.py
Executable file
125
python/sls_detector/dacs.py
Executable file
@ -0,0 +1,125 @@
|
||||
from .detector_property import DetectorProperty
|
||||
from functools import partial
|
||||
import numpy as np
|
||||
|
||||
class Dac(DetectorProperty):
|
||||
"""
|
||||
This class represents a dac on the detector. One instance handles all
|
||||
dacs with the same name for a multi detector instance.
|
||||
|
||||
.. note ::
|
||||
|
||||
This class is used to build up DetectorDacs and is in general
|
||||
not directly accessed to the user.
|
||||
|
||||
|
||||
"""
|
||||
def __init__(self, name, low, high, default, detector):
|
||||
|
||||
super().__init__(partial(detector._api.getDac, name),
|
||||
partial(detector._api.setDac, name),
|
||||
detector._api.getNumberOfDetectors,
|
||||
name)
|
||||
|
||||
self.min_value = low
|
||||
self.max_value = high
|
||||
self.default = default
|
||||
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
"""String representation for a single dac in all modules"""
|
||||
r_str = ['{:10s}: '.format(self.__name__)]
|
||||
r_str += ['{:5d}, '.format(self.get(i)) for i in range(self.get_nmod())]
|
||||
return ''.join(r_str).strip(', ')
|
||||
|
||||
|
||||
class DetectorDacs:
|
||||
_dacs = [('vsvp', 0, 4000, 0),
|
||||
('vtr', 0, 4000, 2500),
|
||||
('vrf', 0, 4000, 3300),
|
||||
('vrs', 0, 4000, 1400),
|
||||
('vsvn', 0, 4000, 4000),
|
||||
('vtgstv', 0, 4000, 2556),
|
||||
('vcmp_ll', 0, 4000, 1500),
|
||||
('vcmp_lr', 0, 4000, 1500),
|
||||
('vcall', 0, 4000, 4000),
|
||||
('vcmp_rl', 0, 4000, 1500),
|
||||
('rxb_rb', 0, 4000, 1100),
|
||||
('rxb_lb', 0, 4000, 1100),
|
||||
('vcmp_rr', 0, 4000, 1500),
|
||||
('vcp', 0, 4000, 200),
|
||||
('vcn', 0, 4000, 2000),
|
||||
('vis', 0, 4000, 1550),
|
||||
('iodelay', 0, 4000, 660)]
|
||||
_dacnames = [_d[0] for _d in _dacs]
|
||||
|
||||
def __init__(self, detector):
|
||||
# We need to at least initially know which detector we are connected to
|
||||
self._detector = detector
|
||||
|
||||
# Index to support iteration
|
||||
self._current = 0
|
||||
|
||||
# Populate the dacs
|
||||
for _d in self._dacs:
|
||||
setattr(self, '_'+_d[0], Dac(*_d, detector))
|
||||
|
||||
def __getattr__(self, name):
|
||||
return self.__getattribute__('_' + name)
|
||||
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name in self._dacnames:
|
||||
return self.__getattribute__('_' + name).__setitem__(slice(None, None, None), value)
|
||||
else:
|
||||
super().__setattr__(name, value)
|
||||
|
||||
def __next__(self):
|
||||
if self._current >= len(self._dacs):
|
||||
self._current = 0
|
||||
raise StopIteration
|
||||
else:
|
||||
self._current += 1
|
||||
return self.__getattr__(self._dacnames[self._current-1])
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __repr__(self):
|
||||
r_str = ['========== DACS =========']
|
||||
r_str += [repr(dac) for dac in self]
|
||||
return '\n'.join(r_str)
|
||||
|
||||
def get_asarray(self):
|
||||
"""
|
||||
Read the dacs into a numpy array with dimensions [ndacs, nmodules]
|
||||
"""
|
||||
dac_array = np.zeros((len(self._dacs), self._detector.n_modules))
|
||||
for i, _d in enumerate(self):
|
||||
dac_array[i,:] = _d[:]
|
||||
return dac_array
|
||||
|
||||
def set_from_array(self, dac_array):
|
||||
"""
|
||||
Set the dacs from an numpy array with dac values. [ndacs, nmodules]
|
||||
"""
|
||||
dac_array = dac_array.astype(np.int)
|
||||
for i, _d in enumerate(self):
|
||||
_d[:] = dac_array[i]
|
||||
|
||||
def set_default(self):
|
||||
"""
|
||||
Set all dacs to their default values
|
||||
"""
|
||||
for _d in self:
|
||||
_d[:] = _d.default
|
||||
|
||||
def update_nmod(self):
|
||||
"""
|
||||
Update the cached value of nmod, needs to be run after adding or
|
||||
removing detectors
|
||||
"""
|
||||
for _d in self:
|
||||
_d._n_modules = self._detector.n_modules
|
||||
|
55
python/sls_detector/decorators.py
Executable file
55
python/sls_detector/decorators.py
Executable file
@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Function decorators for the sls_detector.
|
||||
"""
|
||||
from .errors import DetectorError
|
||||
import functools
|
||||
|
||||
|
||||
def error_handling(func):
|
||||
"""
|
||||
Check for errors registered by the slsDetectorSoftware
|
||||
"""
|
||||
@functools.wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
|
||||
# remove any previous errors
|
||||
self._api.clearErrorMask()
|
||||
|
||||
# call function
|
||||
result = func(self, *args, **kwargs)
|
||||
|
||||
# check for new errors
|
||||
m = self.error_mask
|
||||
if m != 0:
|
||||
msg = self.error_message
|
||||
self._api.clearErrorMask()
|
||||
raise DetectorError(msg)
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def property_error_handling(func):
|
||||
"""
|
||||
Check for errors registered by the slsDetectorSoftware
|
||||
"""
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
# remove any previous errors
|
||||
self._detector._api.clearErrorMask()
|
||||
|
||||
# call function
|
||||
result = func(self, *args, **kwargs)
|
||||
|
||||
# check for new errors
|
||||
m = self._detector.error_mask
|
||||
if m != 0:
|
||||
msg = self._detector.error_message
|
||||
self._detector._api.clearErrorMask()
|
||||
raise DetectorError(msg)
|
||||
return result
|
||||
|
||||
return wrapper
|
1364
python/sls_detector/detector.py
Executable file
1364
python/sls_detector/detector.py
Executable file
File diff suppressed because it is too large
Load Diff
51
python/sls_detector/detector_property.py
Executable file
51
python/sls_detector/detector_property.py
Executable file
@ -0,0 +1,51 @@
|
||||
from collections.abc import Iterable
|
||||
import numpy as np
|
||||
|
||||
class DetectorProperty:
|
||||
"""
|
||||
Base class for a detector property that should be accessed by name and index
|
||||
TODO! Calls are not in parallel and exposes object that can be passes around
|
||||
"""
|
||||
def __init__(self, get_func, set_func, nmod_func, name):
|
||||
self.get = get_func
|
||||
self.set = set_func
|
||||
self.get_nmod = nmod_func
|
||||
self.__name__ = name
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key == slice(None, None, None):
|
||||
return [self.get(i) for i in range(self.get_nmod())]
|
||||
elif isinstance(key, Iterable):
|
||||
return [self.get(k) for k in key]
|
||||
else:
|
||||
return self.get(key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
#operate on all values
|
||||
if key == slice(None, None, None):
|
||||
if isinstance(value, (np.integer, int)):
|
||||
for i in range(self.get_nmod()):
|
||||
self.set(i, value)
|
||||
elif isinstance(value, Iterable):
|
||||
for i in range(self.get_nmod()):
|
||||
self.set(i, value[i])
|
||||
else:
|
||||
raise ValueError('Value should be int or np.integer not', type(value))
|
||||
|
||||
#Iterate over some
|
||||
elif isinstance(key, Iterable):
|
||||
if isinstance(value, Iterable):
|
||||
for k,v in zip(key, value):
|
||||
self.set(k,v)
|
||||
|
||||
elif isinstance(value, int):
|
||||
for k in key:
|
||||
self.set(k, value)
|
||||
|
||||
#Set single value
|
||||
elif isinstance(key, int):
|
||||
self.set(key, value)
|
||||
|
||||
def __repr__(self):
|
||||
s = ', '.join(str(v) for v in self[:])
|
||||
return '{}: [{}]'.format(self.__name__, s)
|
597
python/sls_detector/eiger.py
Executable file
597
python/sls_detector/eiger.py
Executable file
@ -0,0 +1,597 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Wed Dec 6 11:51:18 2017
|
||||
|
||||
@author: l_frojdh
|
||||
"""
|
||||
|
||||
import socket
|
||||
from collections.abc import Iterable
|
||||
from collections import namedtuple
|
||||
from functools import partial
|
||||
|
||||
from .adcs import Adc, DetectorAdcs
|
||||
from .dacs import DetectorDacs
|
||||
from .decorators import error_handling
|
||||
from .detector import Detector
|
||||
from .detector_property import DetectorProperty
|
||||
from .utils import element_if_equal
|
||||
from sls_detector.errors import DetectorValueError, DetectorError
|
||||
|
||||
class EigerVcmp:
|
||||
"""
|
||||
Convenience class to be able to loop over vcmp for Eiger
|
||||
|
||||
|
||||
.. todo::
|
||||
|
||||
Support single assignment and perhaps unify with Dac class
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, detector):
|
||||
_names = ['vcmp_ll',
|
||||
'vcmp_lr',
|
||||
'vcmp_rl',
|
||||
'vcmp_rr']
|
||||
self.set = []
|
||||
self.get = []
|
||||
for i in range(detector.n_modules):
|
||||
if i % 2 == 0:
|
||||
name = _names
|
||||
else:
|
||||
name = _names[::-1]
|
||||
for n in name:
|
||||
self.set.append(partial(detector._api.setDac, n, i))
|
||||
self.get.append(partial(detector._api.getDac, n, i))
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key == slice(None, None, None):
|
||||
return [_d() for _d in self.get]
|
||||
return self.get[key]()
|
||||
|
||||
def __setitem__(self, i, value):
|
||||
self.set[i](value)
|
||||
|
||||
def __repr__(self):
|
||||
return 'vcmp: '+ str(self[:])
|
||||
|
||||
|
||||
class EigerDacs(DetectorDacs):
|
||||
_dacs = [('vsvp', 0, 4000, 0),
|
||||
('vtr', 0, 4000, 2500),
|
||||
('vrf', 0, 4000, 3300),
|
||||
('vrs', 0, 4000, 1400),
|
||||
('vsvn', 0, 4000, 4000),
|
||||
('vtgstv', 0, 4000, 2556),
|
||||
('vcmp_ll', 0, 4000, 1500),
|
||||
('vcmp_lr', 0, 4000, 1500),
|
||||
('vcall', 0, 4000, 4000),
|
||||
('vcmp_rl', 0, 4000, 1500),
|
||||
('rxb_rb', 0, 4000, 1100),
|
||||
('rxb_lb', 0, 4000, 1100),
|
||||
('vcmp_rr', 0, 4000, 1500),
|
||||
('vcp', 0, 4000, 200),
|
||||
('vcn', 0, 4000, 2000),
|
||||
('vis', 0, 4000, 1550),
|
||||
('iodelay', 0, 4000, 660)]
|
||||
_dacnames = [_d[0] for _d in _dacs]
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
class DetectorDelays:
|
||||
_delaynames = ['frame', 'left', 'right']
|
||||
|
||||
def __init__(self, detector):
|
||||
# We need to at least initially know which detector we are connected to
|
||||
self._detector = detector
|
||||
|
||||
setattr(self, '_frame', DetectorProperty(detector._api.getDelayFrame,
|
||||
detector._api.setDelayFrame,
|
||||
detector._api.getNumberOfDetectors,
|
||||
'frame'))
|
||||
|
||||
setattr(self, '_left', DetectorProperty(detector._api.getDelayLeft,
|
||||
detector._api.setDelayLeft,
|
||||
detector._api.getNumberOfDetectors,
|
||||
'left'))
|
||||
|
||||
setattr(self, '_right', DetectorProperty(detector._api.getDelayRight,
|
||||
detector._api.setDelayRight,
|
||||
detector._api.getNumberOfDetectors,
|
||||
'right'))
|
||||
# Index to support iteration
|
||||
self._current = 0
|
||||
|
||||
def __getattr__(self, name):
|
||||
return self.__getattribute__('_' + name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name in self._delaynames:
|
||||
return self.__getattribute__('_' + name).__setitem__(slice(None, None, None), value)
|
||||
else:
|
||||
super().__setattr__(name, value)
|
||||
|
||||
def __next__(self):
|
||||
if self._current >= len(self._delaynames):
|
||||
self._current = 0
|
||||
raise StopIteration
|
||||
else:
|
||||
self._current += 1
|
||||
return self.__getattr__(self._delaynames[self._current-1])
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __repr__(self):
|
||||
hn = self._detector.hostname
|
||||
r_str = ['Transmission delay [ns]\n'
|
||||
'{:11s}{:>8s}{:>8s}{:>8s}'.format('', 'left', 'right', 'frame')]
|
||||
for i in range(self._detector.n_modules):
|
||||
r_str.append('{:2d}:{:8s}{:>8d}{:>8d}{:>8d}'.format(i, hn[i], self.left[i], self.right[i], self.frame[i]))
|
||||
return '\n'.join(r_str)
|
||||
|
||||
|
||||
class Eiger(Detector):
|
||||
"""
|
||||
Subclassing Detector to set up correct dacs and detector specific
|
||||
functions.
|
||||
"""
|
||||
_detector_dynamic_range = [4, 8, 16, 32]
|
||||
|
||||
|
||||
_settings = ['standard', 'highgain', 'lowgain', 'veryhighgain', 'verylowgain']
|
||||
"""available settings for Eiger, note almost always standard"""
|
||||
|
||||
def __init__(self, id=0):
|
||||
super().__init__(id)
|
||||
|
||||
self._active = DetectorProperty(self._api.getActive,
|
||||
self._api.setActive,
|
||||
self._api.getNumberOfDetectors,
|
||||
'active')
|
||||
|
||||
self._vcmp = EigerVcmp(self)
|
||||
self._dacs = EigerDacs(self)
|
||||
self._trimbit_limits = namedtuple('trimbit_limits', ['min', 'max'])(0, 63)
|
||||
self._delay = DetectorDelays(self)
|
||||
|
||||
# Eiger specific adcs
|
||||
self._temp = DetectorAdcs()
|
||||
self._temp.fpga = Adc('temp_fpga', self)
|
||||
self._temp.fpgaext = Adc('temp_fpgaext', self)
|
||||
self._temp.t10ge = Adc('temp_10ge', self)
|
||||
self._temp.dcdc = Adc('temp_dcdc', self)
|
||||
self._temp.sodl = Adc('temp_sodl', self)
|
||||
self._temp.sodr = Adc('temp_sodr', self)
|
||||
self._temp.fpgafl = Adc('temp_fpgafl', self)
|
||||
self._temp.fpgafr = Adc('temp_fpgafr', self)
|
||||
|
||||
@property
|
||||
def active(self):
|
||||
"""
|
||||
Is the detector active? Can be used to enable or disable a detector
|
||||
module
|
||||
|
||||
Examples
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
d.active
|
||||
>> active: [True, True]
|
||||
|
||||
d.active[1] = False
|
||||
>> active: [True, False]
|
||||
"""
|
||||
return self._active
|
||||
|
||||
@active.setter
|
||||
def active(self, value):
|
||||
self._active[:] = value
|
||||
|
||||
@property
|
||||
def measured_period(self):
|
||||
return self._api.getMeasuredPeriod()
|
||||
|
||||
@property
|
||||
def measured_subperiod(self):
|
||||
return self._api.getMeasuredSubPeriod()
|
||||
|
||||
@property
|
||||
def add_gappixels(self):
|
||||
"""Enable or disable the (virual) pixels between ASICs
|
||||
|
||||
Examples
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
d.add_gappixels = True
|
||||
|
||||
d.add_gappixels
|
||||
>> True
|
||||
|
||||
"""
|
||||
return self._api.getGapPixels()
|
||||
|
||||
@add_gappixels.setter
|
||||
def add_gappixels(self, value):
|
||||
self._api.setGapPixels(value)
|
||||
|
||||
@property
|
||||
def dacs(self):
|
||||
"""
|
||||
|
||||
An instance of DetectorDacs used for accessing the dacs of a single
|
||||
or multi detector.
|
||||
|
||||
Examples
|
||||
---------
|
||||
|
||||
::
|
||||
|
||||
d = Eiger()
|
||||
|
||||
#Set all vrf to 1500
|
||||
d.dacs.vrf = 1500
|
||||
|
||||
#Check vrf
|
||||
d.dacs.vrf
|
||||
>> vrf : 1500, 1500
|
||||
|
||||
#Set a single vtr
|
||||
d.dacs.vtr[0] = 1800
|
||||
|
||||
#Set vrf with multiple values
|
||||
d.dacs.vrf = [3500,3700]
|
||||
d.dacs.vrf
|
||||
>> vrf : 3500, 3700
|
||||
|
||||
#read into a variable
|
||||
var = d.dacs.vrf[:]
|
||||
|
||||
#set multiple with multiple values, mostly used for large systems
|
||||
d.dacs.vcall[0,1] = [3500,3600]
|
||||
d.dacs.vcall
|
||||
>> vcall : 3500, 3600
|
||||
|
||||
d.dacs
|
||||
>>
|
||||
========== DACS =========
|
||||
vsvp : 0, 0
|
||||
vtr : 4000, 4000
|
||||
vrf : 1900, 1900
|
||||
vrs : 1400, 1400
|
||||
vsvn : 4000, 4000
|
||||
vtgstv : 2556, 2556
|
||||
vcmp_ll : 1500, 1500
|
||||
vcmp_lr : 1500, 1500
|
||||
vcall : 4000, 4000
|
||||
vcmp_rl : 1500, 1500
|
||||
rxb_rb : 1100, 1100
|
||||
rxb_lb : 1100, 1100
|
||||
vcmp_rr : 1500, 1500
|
||||
vcp : 1500, 1500
|
||||
vcn : 2000, 2000
|
||||
vis : 1550, 1550
|
||||
iodelay : 660, 660
|
||||
|
||||
"""
|
||||
return self._dacs
|
||||
|
||||
@property
|
||||
def tx_delay(self):
|
||||
"""
|
||||
Transmission delay of the modules to allow running the detector
|
||||
in a network not supporting the full speed of the detector.
|
||||
|
||||
|
||||
::
|
||||
|
||||
d.tx_delay
|
||||
>>
|
||||
Transmission delay [ns]
|
||||
left right frame
|
||||
0:beb048 0 15000 0
|
||||
1:beb049 100 190000 100
|
||||
|
||||
d.tx_delay.left = [2000,5000]
|
||||
"""
|
||||
return self._delay
|
||||
|
||||
def default_settings(self):
|
||||
"""
|
||||
reset the detector to some type of standard settings
|
||||
mostly used when testing
|
||||
"""
|
||||
self.n_frames = 1
|
||||
self.exposure_time = 1
|
||||
self.period = 0
|
||||
self.n_cycles = 1
|
||||
self.n_measurements = 1
|
||||
self.dynamic_range = 16
|
||||
|
||||
@property
|
||||
def eiger_matrix_reset(self):
|
||||
"""
|
||||
Matrix reset bit for Eiger.
|
||||
|
||||
:py:obj:`True` : Normal operation, the matrix is reset before each acq.
|
||||
:py:obj:`False` : Matrix reset disabled. Used to not reset before
|
||||
reading out analog test pulses.
|
||||
"""
|
||||
return self._api.getCounterBit()
|
||||
|
||||
@eiger_matrix_reset.setter
|
||||
def eiger_matrix_reset(self, value):
|
||||
self._api.setCounterBit(value)
|
||||
|
||||
@property
|
||||
def flowcontrol_10g(self):
|
||||
"""
|
||||
:py:obj:`True` - Flow control enabled :py:obj:`False` flow control disabled.
|
||||
Sets for all moduels, if for some reason access to a single module is needed
|
||||
this can be done trough the C++ API.
|
||||
|
||||
"""
|
||||
fc = self._api.getNetworkParameter('flow_control_10g')
|
||||
return element_if_equal([bool(int(e)) for e in fc])
|
||||
|
||||
@flowcontrol_10g.setter
|
||||
def flowcontrol_10g(self, value):
|
||||
if value is True:
|
||||
v = '1'
|
||||
else:
|
||||
v = '0'
|
||||
self._api.setNetworkParameter('flow_control_10g', v, -1)
|
||||
|
||||
def pulse_all_pixels(self, n):
|
||||
"""
|
||||
Pulse each pixel of the chip **n** times using the analog test pulses.
|
||||
The pulse height is set using d.dacs.vcall with 4000 being 0 and 0 being
|
||||
the highest pulse.
|
||||
|
||||
::
|
||||
|
||||
#Pulse all pixels ten times
|
||||
d.pulse_all_pixels(10)
|
||||
|
||||
#Avoid resetting before acq
|
||||
d.eiger_matrix_reset = False
|
||||
|
||||
d.acq() #take frame
|
||||
|
||||
#Restore normal behaviour
|
||||
d.eiger_matrix_reset = True
|
||||
|
||||
|
||||
"""
|
||||
self._api.pulseAllPixels(n)
|
||||
|
||||
|
||||
def pulse_diagonal(self, n):
|
||||
"""
|
||||
Pulse pixels in super colums in a diagonal fashion. Used for calibration
|
||||
of vcall. Saves time compared to pulsing all pixels.
|
||||
"""
|
||||
self._api.pulseDiagonal(n)
|
||||
|
||||
|
||||
def pulse_chip(self, n):
|
||||
"""
|
||||
Advance the counter by toggling enable. Gives 2*n+2 int the counter
|
||||
|
||||
"""
|
||||
n = int(n)
|
||||
if n >= -1:
|
||||
self._api.pulseChip(n)
|
||||
else:
|
||||
raise ValueError('n must be equal or larger than -1')
|
||||
|
||||
@property
|
||||
def vcmp(self):
|
||||
"""
|
||||
Convenience function to get and set the individual vcmp of chips
|
||||
Used mainly in the calibration code.
|
||||
|
||||
Examples
|
||||
---------
|
||||
|
||||
::
|
||||
|
||||
#Reading
|
||||
d.vcmp[:]
|
||||
>> [500, 500, 500, 500, 500, 500, 500, 500]
|
||||
|
||||
#Setting
|
||||
d.vcmp = [500, 500, 500, 500, 500, 500, 500, 500]
|
||||
|
||||
|
||||
"""
|
||||
|
||||
return self._vcmp
|
||||
|
||||
@vcmp.setter
|
||||
def vcmp(self, values):
|
||||
if len(values) == len(self._vcmp.set):
|
||||
for i, v in enumerate(values):
|
||||
self._vcmp.set[i](v)
|
||||
else:
|
||||
raise ValueError('vcmp only compatible with setting all')
|
||||
|
||||
@property
|
||||
def rx_udpport(self):
|
||||
"""
|
||||
UDP port for the receiver. Each module has two ports referred to
|
||||
as rx_udpport and rx_udpport2 in the command line interface
|
||||
here they are grouped for each detector
|
||||
|
||||
::
|
||||
|
||||
[0:rx_udpport, 0:rx_udpport2, 1:rx_udpport ...]
|
||||
|
||||
Examples
|
||||
-----------
|
||||
|
||||
::
|
||||
|
||||
d.rx_udpport
|
||||
>> [50010, 50011, 50004, 50005]
|
||||
|
||||
d.rx_udpport = [50010, 50011, 50012, 50013]
|
||||
|
||||
"""
|
||||
p0 = self._api.getReceiverUDPPort()
|
||||
p1 = self._api.getReceiverUDPPort2()
|
||||
return [int(val) for pair in zip(p0, p1) for val in pair]
|
||||
|
||||
@rx_udpport.setter
|
||||
def rx_udpport(self, ports):
|
||||
"""Requires iterating over elements two and two for setting ports"""
|
||||
a = iter(ports)
|
||||
for i, p in enumerate(zip(a, a)):
|
||||
self._api.setReceiverUDPPort(p[0], i)
|
||||
self._api.setReceiverUDPPort2(p[1], i)
|
||||
|
||||
@property
|
||||
def rx_zmqport(self):
|
||||
"""
|
||||
Return the receiver zmq ports. Note that Eiger has two ports per receiver!
|
||||
|
||||
::
|
||||
|
||||
detector.rx_zmqport
|
||||
>> [30001, 30002, 30003, 30004]
|
||||
|
||||
|
||||
"""
|
||||
_s = self._api.getReceiverStreamingPort()
|
||||
if _s == '':
|
||||
return []
|
||||
else:
|
||||
return [int(_p) + i for _p in _s for i in range(2)]
|
||||
|
||||
@rx_zmqport.setter
|
||||
def rx_zmqport(self, port):
|
||||
if isinstance(port, Iterable):
|
||||
for i, p in enumerate(port):
|
||||
self._api.setReceiverStreamingPort(p, i)
|
||||
else:
|
||||
self._api.setReceiverStreamingPort(port, -1)
|
||||
|
||||
|
||||
@property
|
||||
def sub_exposure_time(self):
|
||||
"""
|
||||
Sub frame exposure time in *seconds* for Eiger in 32bit autosumming mode
|
||||
|
||||
::
|
||||
|
||||
d.sub_exposure_time
|
||||
>> 0.0023
|
||||
|
||||
d.sub_exposure_time = 0.002
|
||||
|
||||
"""
|
||||
return self._api.getSubExposureTime() / 1e9
|
||||
|
||||
|
||||
@sub_exposure_time.setter
|
||||
def sub_exposure_time(self, t):
|
||||
#TODO! checking here or in the detector?
|
||||
ns_time = int(t * 1e9)
|
||||
if ns_time > 0:
|
||||
self._api.setSubExposureTime(ns_time)
|
||||
else:
|
||||
raise DetectorValueError('Sub exposure time must be larger than 0')
|
||||
|
||||
@property
|
||||
def sub_deadtime(self):
|
||||
"""
|
||||
Deadtime between subexposures. Used to mimize noise by delaying the start of the next
|
||||
subexposure.
|
||||
"""
|
||||
return self._api.getSubExposureDeadTime() / 1e9
|
||||
|
||||
|
||||
@sub_deadtime.setter
|
||||
def sub_deadtime(self, t):
|
||||
ns_time = int(t * 1e9)
|
||||
if ns_time >= 0:
|
||||
self._api.setSubExposureDeadTime(ns_time)
|
||||
else:
|
||||
raise ValueError('Sub deadtime time must be larger or equal to 0')
|
||||
|
||||
@property
|
||||
def temp(self):
|
||||
"""
|
||||
An instance of DetectorAdcs used to read the temperature
|
||||
of different components
|
||||
|
||||
Examples
|
||||
-----------
|
||||
|
||||
::
|
||||
|
||||
detector.temp
|
||||
>>
|
||||
temp_fpga : 36.90°C, 45.60°C
|
||||
temp_fpgaext : 31.50°C, 32.50°C
|
||||
temp_10ge : 0.00°C, 0.00°C
|
||||
temp_dcdc : 36.00°C, 36.00°C
|
||||
temp_sodl : 33.00°C, 34.50°C
|
||||
temp_sodr : 33.50°C, 34.00°C
|
||||
temp_fpgafl : 33.81°C, 30.93°C
|
||||
temp_fpgafr : 27.88°C, 29.15°C
|
||||
|
||||
a = detector.temp.fpga[:]
|
||||
a
|
||||
>> [36.568, 45.542]
|
||||
|
||||
|
||||
"""
|
||||
return self._temp
|
||||
|
||||
@property
|
||||
def tengiga(self):
|
||||
"""Enable 10Gbit/s data output
|
||||
|
||||
Examples
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
d.tengiga
|
||||
>> False
|
||||
|
||||
d.tengiga = True
|
||||
|
||||
"""
|
||||
return self._api.getTenGigabitEthernet()
|
||||
|
||||
@tengiga.setter
|
||||
def tengiga(self, value):
|
||||
self._api.setTenGigabitEthernet(value)
|
||||
|
||||
def set_delays(self, delta):
|
||||
self.tx_delay.left = [delta*(i*2) for i in range(self.n_modules)]
|
||||
self.tx_delay.right = [delta*(i*2+1) for i in range(self.n_modules)]
|
||||
|
||||
|
||||
def setup500k(self, hostnames):
|
||||
"""
|
||||
Setup the Eiger detector to run on the local machine
|
||||
"""
|
||||
|
||||
self.hostname = hostnames
|
||||
self.file_write = False
|
||||
self.image_size = (512, 1024)
|
||||
self.rx_tcpport = [1954, 1955]
|
||||
self.rx_udpport = [50010, 50011, 50004, 50005]
|
||||
self.rx_hostname = socket.gethostname().split('.')[0]
|
||||
self.rx_datastream = False
|
||||
self.file_write = False
|
||||
self.online = True
|
||||
self.receiver_online = True
|
25
python/sls_detector/errors.py
Executable file
25
python/sls_detector/errors.py
Executable file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Thu Dec 14 17:13:55 2017
|
||||
|
||||
@author: l_frojdh
|
||||
"""
|
||||
|
||||
|
||||
class DetectorError(Exception):
|
||||
"""
|
||||
This error should be used when something fails
|
||||
on the detector side
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class DetectorSettingDoesNotExist(Exception):
|
||||
"""This error should be used when the setting does not exist"""
|
||||
pass
|
||||
|
||||
|
||||
class DetectorValueError(Exception):
|
||||
"""This error should be used when the set value is outside the allowed range"""
|
||||
pass
|
16
python/sls_detector/experimental.py
Executable file
16
python/sls_detector/experimental.py
Executable file
@ -0,0 +1,16 @@
|
||||
|
||||
from _sls_detector import multiDetectorApi
|
||||
|
||||
class ExperimentalDetector(multiDetectorApi):
|
||||
def __init__(self):
|
||||
super().__init__(0)
|
||||
|
||||
|
||||
@property
|
||||
def rx_udpip(self):
|
||||
return self._getReceiverUDPIP(-1)
|
||||
|
||||
@rx_udpip.setter
|
||||
def rx_udpip(self, ip):
|
||||
self._setReceiverUDPIP(ip, -1)
|
||||
|
260
python/sls_detector/jungfrau.py
Executable file
260
python/sls_detector/jungfrau.py
Executable file
@ -0,0 +1,260 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Jungfrau detector class and support functions.
|
||||
Inherits from Detector.
|
||||
"""
|
||||
from .adcs import Adc, DetectorAdcs
|
||||
from .decorators import error_handling
|
||||
from .detector import Detector
|
||||
from .dacs import DetectorDacs
|
||||
from .utils import element_if_equal
|
||||
|
||||
|
||||
class JungfrauDacs(DetectorDacs):
|
||||
_dacs = [('vb_comp', 0, 4000, 1220),
|
||||
('vdd_prot', 0, 4000, 3000),
|
||||
('vin_com', 0, 4000, 1053),
|
||||
('vref_prech', 0, 4000, 1450),
|
||||
('vb_pixbuff', 0, 4000, 750),
|
||||
('vb_ds', 0, 4000, 1000),
|
||||
('vref_ds', 0, 4000, 480),
|
||||
('vref_comp', 0, 4000, 420),
|
||||
]
|
||||
_dacnames = [_d[0] for _d in _dacs]
|
||||
|
||||
class Jungfrau(Detector):
|
||||
"""
|
||||
Class used to control a Jungfrau detector. Inherits from the Detector class but a specialized
|
||||
class is needed to provide the correct dacs and unique functions.
|
||||
|
||||
"""
|
||||
_detector_dynamic_range = [4, 8, 16, 32]
|
||||
|
||||
_settings = ['dynamichg0',
|
||||
'dynamicgain',
|
||||
'fixgain1',
|
||||
'fixgain2',
|
||||
'forceswitchg1',
|
||||
'forceswitchg2']
|
||||
"""Available settings for Jungfrau"""
|
||||
|
||||
def __init__(self, multi_id=0):
|
||||
#Init on base calss
|
||||
super().__init__(multi_id)
|
||||
self._dacs = JungfrauDacs(self)
|
||||
|
||||
#Jungfrau specific temps, this can be reduced to a single value?
|
||||
self._temp = DetectorAdcs()
|
||||
self._temp.fpga = Adc('temp_fpga', self)
|
||||
# self._register = Register(self)
|
||||
|
||||
|
||||
@property
|
||||
def dacs(self):
|
||||
"""
|
||||
|
||||
An instance of DetectorDacs used for accessing the dacs of a single
|
||||
or multi detector.
|
||||
|
||||
Examples
|
||||
---------
|
||||
|
||||
::
|
||||
|
||||
#Jungfrau
|
||||
|
||||
|
||||
"""
|
||||
return self._dacs
|
||||
|
||||
@property
|
||||
def power_chip(self):
|
||||
"""Power on or off the ASICs, True for on False for off"""
|
||||
return self._api.isChipPowered()
|
||||
|
||||
@power_chip.setter
|
||||
def power_chip(self, value):
|
||||
self._api.powerChip(value)
|
||||
|
||||
@property
|
||||
def delay(self):
|
||||
"""Delay after trigger [s]"""
|
||||
return self._api.getDelay()/1e9
|
||||
|
||||
@delay.setter
|
||||
def delay(self, t):
|
||||
ns_time = int(t * 1e9)
|
||||
self._api.setDelay(ns_time)
|
||||
|
||||
@property
|
||||
def n_gates(self):
|
||||
return self._api.getNumberOfGates()
|
||||
|
||||
@n_gates.setter
|
||||
def n_gates(self, n):
|
||||
self._api.setNumberOfGates(n)
|
||||
|
||||
@property
|
||||
def n_probes(self):
|
||||
return self._api.getNumberOfProbes()
|
||||
|
||||
@n_probes.setter
|
||||
def n_probes(self, n):
|
||||
self._api.setNumberOfProbes(n)
|
||||
|
||||
@property
|
||||
def storagecell_start(self):
|
||||
"""
|
||||
First storage cell
|
||||
"""
|
||||
return self._api.getStoragecellStart()
|
||||
|
||||
@storagecell_start.setter
|
||||
def storagecell_start(self, value):
|
||||
self._api.setStoragecellStart(value)
|
||||
|
||||
|
||||
@property
|
||||
def n_storagecells(self):
|
||||
"""
|
||||
number of storage cells used for the measurements
|
||||
"""
|
||||
return self._api.getNumberOfStorageCells()
|
||||
|
||||
@n_storagecells.setter
|
||||
def n_storagecells(self, value):
|
||||
self._api.setNumberOfStorageCells(value)
|
||||
|
||||
@property
|
||||
def temp(self):
|
||||
"""
|
||||
An instance of DetectorAdcs used to read the temperature
|
||||
of different components
|
||||
|
||||
Examples
|
||||
-----------
|
||||
|
||||
::
|
||||
|
||||
detector.temp
|
||||
>>
|
||||
temp_fpga : 36.90°C, 45.60°C
|
||||
|
||||
a = detector.temp.fpga[:]
|
||||
a
|
||||
>> [36.568, 45.542]
|
||||
|
||||
|
||||
"""
|
||||
return self._temp
|
||||
|
||||
@property
|
||||
def temperature_threshold(self):
|
||||
"""Threshold for switching of chips"""
|
||||
return self._api.getThresholdTemperature()
|
||||
|
||||
@temperature_threshold.setter
|
||||
def temperature_threshold(self, t):
|
||||
self._api.setThresholdTemperature(t)
|
||||
|
||||
@property
|
||||
def temperature_control(self):
|
||||
"""
|
||||
Monitor the temperature of the detector and switch off chips if temperature_threshold is
|
||||
crossed
|
||||
|
||||
|
||||
Examples
|
||||
---------
|
||||
|
||||
::
|
||||
|
||||
#activate
|
||||
detector.temperature_control = True
|
||||
|
||||
#deactivate
|
||||
detector.temperature_control = False
|
||||
|
||||
|
||||
"""
|
||||
return self._api.getTemperatureControl()
|
||||
|
||||
@temperature_control.setter
|
||||
def temperature_control(self, v):
|
||||
self._api.setTemperatureControl(v)
|
||||
|
||||
@property
|
||||
def temperature_event(self):
|
||||
"""Have the temperature threshold been crossed?
|
||||
|
||||
Returns
|
||||
---------
|
||||
|
||||
:py:obj:`True` if the threshold have been crossed and temperature_control is active
|
||||
otherwise :py:obj:`False`
|
||||
|
||||
"""
|
||||
return self._api.getTemperatureEvent()
|
||||
|
||||
def reset_temperature_event(self):
|
||||
"""Reset the temperature_event. After reset temperature_event is False"""
|
||||
self._api.resetTemperatureEvent()
|
||||
|
||||
@property
|
||||
def rx_udpport(self):
|
||||
"""
|
||||
UDP port for the receiver. Each module have one port.
|
||||
Note! Eiger has two ports
|
||||
|
||||
::
|
||||
|
||||
[0:rx_udpport]
|
||||
|
||||
Examples
|
||||
-----------
|
||||
|
||||
::
|
||||
|
||||
d.rx_udpport
|
||||
>> [50010]
|
||||
|
||||
d.rx_udpport = [50010]
|
||||
|
||||
"""
|
||||
return self._api.getNetworkParameter('rx_udpport')
|
||||
|
||||
|
||||
@rx_udpport.setter
|
||||
def rx_udpport(self, ports):
|
||||
"""Requires iterating over elements two and two for setting ports"""
|
||||
for i, p in enumerate(ports):
|
||||
self._api.setNetworkParameter('rx_udpport', str(p), i)
|
||||
|
||||
@property
|
||||
def detector_mac(self):
|
||||
s = self._api.getNetworkParameter('detectormac')
|
||||
return element_if_equal(s)
|
||||
|
||||
|
||||
@detector_mac.setter
|
||||
def detector_mac(self, mac):
|
||||
if isinstance(mac, list):
|
||||
for i, m in enumerate(mac):
|
||||
self._api.setNetworkParameter('detectormac', m, i)
|
||||
else:
|
||||
self._api.setNetworkParameter('detectormac', mac, -1)
|
||||
|
||||
|
||||
@property
|
||||
def detector_ip(self):
|
||||
s = self._api.getNetworkParameter('detectorip')
|
||||
return element_if_equal(s)
|
||||
|
||||
@detector_ip.setter
|
||||
def detector_ip(self, ip):
|
||||
if isinstance(ip, list):
|
||||
for i, addr in enumerate(ip):
|
||||
self._api.setNetworkParameter('detectorip', addr, i)
|
||||
else:
|
||||
self._api.setNetworkParameter('detectorip', ip, -1)
|
179
python/sls_detector/jungfrau_ctb.py
Executable file
179
python/sls_detector/jungfrau_ctb.py
Executable file
@ -0,0 +1,179 @@
|
||||
from functools import partial
|
||||
from collections.abc import Iterable
|
||||
from collections import namedtuple
|
||||
import socket
|
||||
|
||||
from .detector import Detector
|
||||
from .utils import element_if_equal
|
||||
from .adcs import DetectorAdcs, Adc
|
||||
from .dacs import DetectorDacs
|
||||
from .detector_property import DetectorProperty
|
||||
from .decorators import error_handling
|
||||
from .registers import Register, Adc_register
|
||||
|
||||
class JungfrauCTBDacs(DetectorDacs):
|
||||
_dacs = [('dac0', 0, 4000, 1400),
|
||||
('dac1', 0, 4000, 1200),
|
||||
('dac2', 0, 4000, 900),
|
||||
('dac3', 0, 4000, 1050),
|
||||
('dac4', 0, 4000, 1400),
|
||||
('dac5', 0, 4000, 655),
|
||||
('dac6', 0, 4000, 2000),
|
||||
('dac7', 0, 4000, 1400),
|
||||
('dac8', 0, 4000, 850),
|
||||
('dac9', 0, 4000, 2000),
|
||||
('dac10', 0, 4000, 2294),
|
||||
('dac11', 0, 4000, 983),
|
||||
('dac12', 0, 4000, 1475),
|
||||
('dac13', 0, 4000, 1200),
|
||||
('dac14', 0, 4000, 1600),
|
||||
('dac15', 0, 4000, 1455),
|
||||
('dac16', 0, 4000, 0),
|
||||
('dac17', 0, 4000, 1000),
|
||||
]
|
||||
_dacnames = [_d[0] for _d in _dacs]
|
||||
|
||||
|
||||
|
||||
class JungfrauCTB(Detector):
|
||||
def __init__(self, id = 0):
|
||||
super().__init__(id)
|
||||
self._dacs = JungfrauCTBDacs(self)
|
||||
self._register = Register(self)
|
||||
self._adc_register = Adc_register(self)
|
||||
|
||||
@property
|
||||
def v_a(self):
|
||||
return self._api.getDac_mV('v_a', -1)
|
||||
|
||||
@v_a.setter
|
||||
def v_a(self, value):
|
||||
self._api.setDac_mV('v_a', -1, value)
|
||||
|
||||
@property
|
||||
def v_b(self):
|
||||
return self._api.getDac_mV('v_b', -1)
|
||||
|
||||
@v_b.setter
|
||||
def v_b(self, value):
|
||||
self._api.setDac_mV('v_b', -1, value)
|
||||
|
||||
|
||||
@property
|
||||
def v_c(self):
|
||||
return self._api.getDac_mV('v_c', -1)
|
||||
|
||||
@v_c.setter
|
||||
def v_c(self, value):
|
||||
self._api.setDac_mV('v_c', -1, value)
|
||||
|
||||
@property
|
||||
def v_d(self):
|
||||
return self._api.getDac_mV('v_d', -1)
|
||||
|
||||
@v_d.setter
|
||||
def v_d(self, value):
|
||||
self._api.setDac_mV('v_d', -1, value)
|
||||
|
||||
@property
|
||||
def v_io(self):
|
||||
return self._api.getDac_mV('v_io', -1)
|
||||
|
||||
@v_io.setter
|
||||
def v_io(self, value):
|
||||
self._api.setDac_mV('v_io', -1, value)
|
||||
|
||||
@property
|
||||
def v_limit(self):
|
||||
return self._api.getDac_mV('v_limit', -1)
|
||||
|
||||
@v_limit.setter
|
||||
def v_limit(self, value):
|
||||
self._api.setDac_mV('v_limit', -1, value)
|
||||
|
||||
@property
|
||||
def adc_register(self):
|
||||
return self._adc_register
|
||||
|
||||
# @property
|
||||
# def register(self):
|
||||
# return self._register
|
||||
|
||||
def adcOFF(self):
|
||||
"""Switch off the ADC"""
|
||||
self.adc_register[0x8] = 1
|
||||
|
||||
|
||||
|
||||
@property
|
||||
def dacs(self):
|
||||
"""
|
||||
|
||||
An instance of DetectorDacs used for accessing the dacs of a single
|
||||
or multi detector.
|
||||
|
||||
Examples
|
||||
---------
|
||||
|
||||
::
|
||||
|
||||
#JungfrauCTB
|
||||
|
||||
|
||||
"""
|
||||
return self._dacs
|
||||
|
||||
@property
|
||||
def dbitpipeline(self):
|
||||
return self._api.getDbitPipeline()
|
||||
|
||||
@dbitpipeline.setter
|
||||
def dbitpipeline(self, value):
|
||||
self._api.setDbitPipeline(value)
|
||||
|
||||
|
||||
@property
|
||||
def dbitphase(self):
|
||||
return self._api.getDbitPhase()
|
||||
|
||||
@dbitphase.setter
|
||||
def dbitphase(self, value):
|
||||
self._api.setDbitPhase(value)
|
||||
|
||||
@property
|
||||
def dbitclock(self):
|
||||
return self._api.getDbitClock()
|
||||
|
||||
@dbitclock.setter
|
||||
def dbitclock(self, value):
|
||||
self._api.setDbitClock(value)
|
||||
|
||||
@property
|
||||
def samples(self):
|
||||
return self._api.getJCTBSamples()
|
||||
|
||||
@samples.setter
|
||||
def samples(self, value):
|
||||
self._api.setJCTBSamples(value)
|
||||
|
||||
@property
|
||||
def readout_clock(self):
|
||||
"""
|
||||
Speed of the readout clock relative to the full speed
|
||||
|
||||
|
||||
Examples
|
||||
---------
|
||||
|
||||
::
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
return self._api.getReadoutClockSpeed()
|
||||
|
||||
|
||||
@readout_clock.setter
|
||||
def readout_clock(self, value):
|
||||
self._api.setReadoutClockSpeed(value)
|
18
python/sls_detector/registers.py
Executable file
18
python/sls_detector/registers.py
Executable file
@ -0,0 +1,18 @@
|
||||
from .decorators import error_handling, property_error_handling
|
||||
class Register:
|
||||
def __init__(self, detector):
|
||||
self._detector = detector
|
||||
|
||||
@property_error_handling
|
||||
def __getitem__(self, key):
|
||||
return self._detector._api.readRegister(key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self._detector._api.writeRegister(key, value)
|
||||
|
||||
class Adc_register:
|
||||
def __init__(self, detector):
|
||||
self._detector = detector
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self._detector._api.writeAdcRegister(key, value)
|
32
python/sls_detector/utils.py
Executable file
32
python/sls_detector/utils.py
Executable file
@ -0,0 +1,32 @@
|
||||
"""
|
||||
Utility functions that are useful for testing and troubleshooting
|
||||
but not directly used in controlling the detector
|
||||
"""
|
||||
|
||||
|
||||
def all_equal(mylist):
|
||||
"""If all elements are equal return true otherwise false"""
|
||||
return all(x == mylist[0] for x in mylist)
|
||||
|
||||
|
||||
def element_if_equal(mylist):
|
||||
"""If all elements are equal return only one element"""
|
||||
if all_equal(mylist):
|
||||
if len(mylist) == 0:
|
||||
return None
|
||||
else:
|
||||
return mylist[0]
|
||||
else:
|
||||
return mylist
|
||||
|
||||
|
||||
def eiger_register_to_time(register):
|
||||
"""
|
||||
Decode register value and return time in s. Values are stored in
|
||||
a 32bit register with bits 2->0 containing the exponent and bits
|
||||
31->3 containing the significand (int value)
|
||||
|
||||
"""
|
||||
clocks = register >> 3
|
||||
exponent = register & 0b111
|
||||
return clocks*10**exponent / 100e6
|
20
python/sphinx/Makefile
Executable file
20
python/sphinx/Makefile
Executable file
@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = python -msphinx
|
||||
SPHINXPROJ = sls_detector_tools
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
BIN
python/sphinx/_static/tp_scurve.png
Executable file
BIN
python/sphinx/_static/tp_scurve.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
BIN
python/sphinx/_static/untrimmed.png
Executable file
BIN
python/sphinx/_static/untrimmed.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 1.9 MiB |
134
python/sphinx/code_quality.rst
Executable file
134
python/sphinx/code_quality.rst
Executable file
@ -0,0 +1,134 @@
|
||||
Code quality
|
||||
=============================
|
||||
|
||||
For usability and reliability of the software the code needs to be high quality. For this
|
||||
project it means meeting the four criteria described below. Any addition should pass all of
|
||||
them.
|
||||
|
||||
|
||||
--------------------------------
|
||||
Look, read and feel like Python
|
||||
--------------------------------
|
||||
|
||||
When using classes and functions from the
|
||||
package it should feel like you are using Python tools and be forces
|
||||
to write C++ style code with Python syntax.
|
||||
|
||||
::
|
||||
|
||||
with xray_box.shutter_open():
|
||||
for th in threshold:
|
||||
d.vthreshold = th
|
||||
d.acq()
|
||||
|
||||
should be preferred over
|
||||
|
||||
::
|
||||
|
||||
N = len(threshold)
|
||||
xray_box.open_shutter()
|
||||
for i in range(N):
|
||||
d.dacs.set_dac('vthreshold', threshold[i])
|
||||
d.acq()
|
||||
xray_box.close_shutter()
|
||||
|
||||
even if the difference might seem small.
|
||||
|
||||
--------------------
|
||||
Have documentation
|
||||
--------------------
|
||||
|
||||
Classes and functions should be documented with doc-strings
|
||||
in the source code. Preferably with examples. The syntax to be used
|
||||
is numpy-sphinx.
|
||||
|
||||
::
|
||||
|
||||
def function(arg):
|
||||
"""
|
||||
This is a function that does something
|
||||
|
||||
Parameters
|
||||
----------
|
||||
arg: int
|
||||
An argument
|
||||
|
||||
Returns
|
||||
--------
|
||||
value: double
|
||||
Returns a value
|
||||
|
||||
"""
|
||||
return np.sin(arg+np.pi)
|
||||
|
||||
---------------------------------
|
||||
Pass static analysis with pylint
|
||||
---------------------------------
|
||||
|
||||
Yes, anything less than 9/10 just means that you are lazy. If
|
||||
there is a good reason why to diverge, then we can always
|
||||
add an exception.
|
||||
|
||||
Currently the following additions are made:
|
||||
|
||||
* good-names: x, y, ax, im etc.
|
||||
* function arguments 10
|
||||
* Whitelist: numpy, _sls
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
Tested code
|
||||
-----------------------
|
||||
|
||||
Last but not least... *actually last just because of the long list included.*
|
||||
All code that goes in should have adequate tests. If a new function does not
|
||||
have a minimum of one test it does not get added.
|
||||
|
||||
**Unit-tests with pytest and mocker**
|
||||
|
||||
::
|
||||
|
||||
----------- coverage: platform linux, python 3.6.4-final-0 -----------
|
||||
Name Stmts Miss Cover
|
||||
------------------------------------------------
|
||||
sls_detector/__init__.py 4 0 100%
|
||||
sls_detector/decorators.py 14 3 79%
|
||||
sls_detector/detector.py 461 115 75%
|
||||
sls_detector/eiger.py 150 64 57%
|
||||
sls_detector/errors.py 7 0 100%
|
||||
sls_detector/jungfrau.py 59 26 56%
|
||||
------------------------------------------------
|
||||
TOTAL 695 208 70%
|
||||
|
||||
|
||||
========= 78 passed in 0.60 seconds =========
|
||||
|
||||
|
||||
|
||||
**Simple integration tests**
|
||||
|
||||
These tests require a detector connected. Performs simple tasks like setting
|
||||
exposure time and reading back to double check the value
|
||||
|
||||
::
|
||||
|
||||
----------- coverage: platform linux, python 3.6.4-final-0 -----------
|
||||
Name Stmts Miss Cover
|
||||
------------------------------------------------
|
||||
sls_detector/__init__.py 4 0 100%
|
||||
sls_detector/decorators.py 14 0 100%
|
||||
sls_detector/detector.py 461 103 78%
|
||||
sls_detector/eiger.py 150 20 87%
|
||||
sls_detector/errors.py 7 0 100%
|
||||
sls_detector/jungfrau.py 59 26 56%
|
||||
------------------------------------------------
|
||||
TOTAL 695 149 79%
|
||||
|
||||
|
||||
========= 67 passed, 1 skipped in 16.66 seconds =========
|
||||
|
||||
**Complex integration test**
|
||||
|
||||
Typical measurements. Might require X-rays. Tests are usually evaluated from
|
||||
plots
|
370
python/sphinx/command_line.rst
Executable file
370
python/sphinx/command_line.rst
Executable file
@ -0,0 +1,370 @@
|
||||
Command line to Python
|
||||
=========================
|
||||
|
||||
If you are already familiar with the command line interface to the
|
||||
slsDetectorSoftware here is a quick reference translating to Python commands
|
||||
|
||||
|
||||
.. note ::
|
||||
|
||||
Commands labeled Mythen only or Gotthard only are currently not implemented in the
|
||||
Python class. If you need this functionallity please contact the SLS Detector Group
|
||||
|
||||
.. py:currentmodule:: sls_detector
|
||||
|
||||
.. |ro| replace:: *(read only)*
|
||||
.. |free| replace:: :py:func:`Detector.free_shared_memory`
|
||||
.. |sub| replace:: :py:attr:`Detector.sub_exposure_time`
|
||||
.. |mg| replace:: Mythen and Gotthard only
|
||||
.. |g| replace:: Gotthard only
|
||||
.. |m| replace:: Mythen only
|
||||
.. |msp| replace:: :py:attr:`Detector.measured_subperiod`
|
||||
.. |new_chiptest| replace:: New chip test board only
|
||||
.. |chiptest| replace:: Chip test board only
|
||||
.. |dr| replace:: :py:attr:`Detector.dynamic_range`
|
||||
.. |j| replace:: Jungfrau only
|
||||
.. |te| replace:: :py:attr:`Detector.trimmed_energies`
|
||||
.. |temp_fpgaext| replace:: :py:attr:`Detector.temp`.fpgaext
|
||||
.. |epa| replace:: :py:func:`Eiger.pulse_all_pixels`
|
||||
.. |rfc| replace:: :py:func:`Detector.reset_frames_caught`
|
||||
.. |rfi| replace:: :py:attr:`Detector.receiver_frame_index`
|
||||
.. |ron| replace:: :py:attr:`Detector.receiver_online`
|
||||
.. |flipy| replace:: :py:attr:`Detector.flipped_data_y`
|
||||
.. |flipx| replace:: :py:attr:`Detector.flipped_data_x`
|
||||
.. |adcr| replace:: :py:func:`DetectorApi.writeAdcRegister`
|
||||
.. |sb| replace:: :py:func:`DetectorApi.setBitInRegister`
|
||||
.. |cb| replace:: :py:func:`DetectorApi.clearBitInRegister`
|
||||
.. |tempth| replace:: :py:attr:`Jungfrau.temperature_threshold`
|
||||
.. |tempev| replace:: :py:attr:`Jungfrau.temperature_event`
|
||||
.. |tempco| replace:: :py:attr:`Jungfrau.temperature_control`
|
||||
.. |depr| replace:: *Deprecated/Internal*
|
||||
.. |nimp| replace:: *Not implemented*
|
||||
.. |rudp| replace:: :py:attr:`Detector.rx_realudpsocksize`
|
||||
.. |lci| replace:: :py:attr:`Detector.last_client_ip`
|
||||
.. |rlci| replace:: :py:attr:`Detector.receiver_last_client_ip`
|
||||
.. |fdp| replace:: :py:attr:`Detector.frame_discard_policy`
|
||||
.. |apic| replace:: :py:attr:`Detector.api_compatibility`
|
||||
|
||||
|
||||
------------------------
|
||||
Commands
|
||||
------------------------
|
||||
|
||||
===================== ===================================== ================== =========
|
||||
Command Python Implementation Tests
|
||||
===================== ===================================== ================== =========
|
||||
sls_detector_acquire :py:func:`Detector.acq` OK OK
|
||||
test |depr| \- \-
|
||||
help help(Detector.acq) \- \-
|
||||
exitserver |depr| \- \-
|
||||
exitreceiver |depr| \- \-
|
||||
flippeddatay |flipy| OK \-
|
||||
digitest |depr| \- \-
|
||||
bustest |depr| \- \-
|
||||
digibittest Which detector? \- \-
|
||||
reg :py:attr:`Detector.register` OK \-
|
||||
adcreg |adcr| OK \-
|
||||
setbit |sb| OK \-
|
||||
clearbit |cb| OK \-
|
||||
getbit |nimp| \- \-
|
||||
r_compression Not implemented in receiver \- \-
|
||||
acquire :py:func:`Detector.acq` OK \-
|
||||
busy :py:attr:`Detector.busy` OK Partial
|
||||
status :py:attr:`Detector.status` OK |ro| \-
|
||||
status start :py:func:`Detector.start_detector` OK \-
|
||||
status stop :py:func:`Detector.stop_detector` OK \-
|
||||
data |depr| \- \-
|
||||
frame |depr| \- \-
|
||||
readctr |g| \- \-
|
||||
resetctr |g| \- \-
|
||||
resmat :py:attr:`Eiger.eiger_matrix_reset` OK OK
|
||||
free |free| OK \-
|
||||
hostname :py:attr:`Detector.hostname` OK OK
|
||||
add |nimp| \- \-
|
||||
replace |nimp| \- \-
|
||||
user |nimp| \- \-
|
||||
master |nimp| \- \-
|
||||
sync Which detector? \- \-
|
||||
online :py:attr:`Detector.online` OK \-
|
||||
checkonline |nimp| \- \-
|
||||
activate :py:attr:`Eiger.active` \- \-
|
||||
nmod :py:attr:`Detector.n_modules` OK \-
|
||||
maxmod |depr| \- \-
|
||||
dr |dr| OK OK
|
||||
roi |g| \- \-
|
||||
detsizechan :py:attr:`Detector.image_size` OK \-
|
||||
roimask |nimp| \- \-
|
||||
flippeddatax |flipx| OK \-
|
||||
tengiga :py:attr:`Eiger.tengiga` OK \-
|
||||
gappixels :py:attr:`Eiger.add_gappixels` OK \-
|
||||
flags :py:attr:`Detector.flags` OK \-
|
||||
extsig |mg| \- \-
|
||||
programfpga |j| \- \-
|
||||
resetfpga |j| \- \-
|
||||
powerchip :py:attr:`Jungfrau.powerchip` \- \-
|
||||
led |nimp| \- \-
|
||||
auto_comp_disable |j| \- \-
|
||||
pulse Used in |epa| OK \-
|
||||
pulsenmove Used in |epa| OK \-
|
||||
pulsechip :py:func:`Eiger.pulse_chip` OK \-
|
||||
checkdetversion |apic| \- \-
|
||||
checkrecversion |apic| \- \-
|
||||
moduleversion |m| \- \-
|
||||
detectornumber :py:attr:`Detector.detector_number` OK \-
|
||||
modulenumber |m| \- \-
|
||||
detectorversion :py:attr:`Detector.firmware_version` OK OK
|
||||
softwareversion :py:attr:`Detector.server_version` \- \-
|
||||
thisversion :py:attr:`Detector.client_version` Reads date \-
|
||||
receiverversion :py:attr:`Detector.receiver_version` Reads date \-
|
||||
timing :py:attr:`Detector.timing_mode` OK \-
|
||||
exptime :py:attr:`Detector.exposure_time` OK OK
|
||||
subexptime |sub| OK OK
|
||||
period :py:attr:`Detector.period` OK OK
|
||||
subdeadtime :py:attr:`Eiger.sub_deadtime` OK OK
|
||||
delay :py:attr:`Jungfrau.delay` OK \-
|
||||
gates :py:attr:`Jungfrau.n_gates` OK \-
|
||||
frames :py:attr:`Detector.n_frames` OK OK
|
||||
cycles :py:attr:`Detector.n_cycles` OK \-
|
||||
probes :py:attr:`Jungfrau.n_probes` OK \-
|
||||
measurements :py:attr:`Detector.n_measurements` OK \-
|
||||
samples Chip test board only (new?) \- \-
|
||||
storagecells :py:attr:`Jungfrau.n_storagecells` OK \-
|
||||
storagecell_start :py:attr:`Jungfrau.storagecell_start` OK \-
|
||||
exptimel |mg| \- \-
|
||||
periodl |mg| \- \-
|
||||
delayl |mg| \- \-
|
||||
gatesl |mg| \- \-
|
||||
framesl |mg| \- \-
|
||||
cyclesl |mg| \- \-
|
||||
probesl |mg| \- \-
|
||||
now |nimp| \- \-
|
||||
timestamp |m| \- \-
|
||||
nframes |nimp| \- \-
|
||||
measuredperiod :py:attr:`Detector.measured_period` OK \-
|
||||
measuredsubperiod |msp| \- \-
|
||||
clkdivider :py:attr:`Detector.readout_clock` OK OK
|
||||
setlength |m| \- \-
|
||||
waitstates |m| \- \-
|
||||
totdivider |m| \- \-
|
||||
totdutycycle |m| \- \-
|
||||
phasestep |g| \- \-
|
||||
oversampling |new_chiptest| \- \-
|
||||
adcclk |new_chiptest| \- \-
|
||||
adcphase |new_chiptest| \- \-
|
||||
adcpipeline |new_chiptest| \- \-
|
||||
dbitclk |new_chiptest| \- \-
|
||||
dbitphase |new_chiptest| \- \-
|
||||
dbitpipeline |new_chiptest| \- \-
|
||||
config :py:func:`Detector.load_config` OK \-
|
||||
rx_printconfig |nimp| \- \-
|
||||
parameters :py:func:`Detector.load_parameters` OK \-
|
||||
setup |nimp| \- \-
|
||||
flatfield |nimp| \- \-
|
||||
ffdir |nimp| \- \-
|
||||
ratecorr :py:attr:`Detector.rate_correction` OK \-
|
||||
badchannels |nimp| \- \-
|
||||
angconv |m| \- \-
|
||||
globaloff |nimp| \- \-
|
||||
fineoff |nimp| \- \-
|
||||
binsize |nimp| \- \-
|
||||
angdir |nimp| \- \-
|
||||
moveflag |nimp| \- \-
|
||||
samplex |nimp| \- \-
|
||||
sampley |nimp| \- \-
|
||||
threaded :py:attr:`Detector.threaded` OK \-
|
||||
darkimage |nimp| \- \-
|
||||
gainimage |nimp| \- \-
|
||||
settingsdir :py:attr:`Detector.settings_path` OK \-
|
||||
trimdir |nimp| \- \-
|
||||
caldir |nimp| \- \-
|
||||
trimen :py:attr:`Detector.trimmed_energies` OK \-
|
||||
settings :py:attr:`Detector.settings` OK \-
|
||||
threshold :py:attr:`Detector.threshold` OK \-
|
||||
thresholdnotb |nimp| \- \-
|
||||
trimbits :py:func:`Detector.load_trimbits` OK \-
|
||||
trim |nimp| \- \-
|
||||
trimval :py:attr:`Detector.trimbits` OK OK
|
||||
pedestal |nimp| \- \-
|
||||
vthreshold :py:attr:`Detector.vthreshold` OK \-
|
||||
vcalibration |nimp| \- \-
|
||||
vtrimbit |nimp| \- \-
|
||||
vpreamp |nimp| \- \-
|
||||
vshaper1 |nimp| \- \-
|
||||
vshaper2 |nimp| \- \-
|
||||
vhighvoltage :py:attr:`Detector.high_voltage` OK \-
|
||||
vapower |nimp| \- \-
|
||||
vddpower |nimp| \- \-
|
||||
vshpower |nimp| \- \-
|
||||
viopower |nimp| \- \-
|
||||
vref_ds :py:attr:`Jungfrau.dacs.vref_ds` OK \-
|
||||
vcascn_pb |nimp| \- \-
|
||||
vcascp_pb |nimp| \- \-
|
||||
vout_cm |nimp| \- \-
|
||||
vcasc_out |nimp| \- \-
|
||||
vin_cm |nimp| \- \-
|
||||
vref_comp |nimp| \- \-
|
||||
ib_test_c |nimp| \- \-
|
||||
dac0 |nimp| \- \-
|
||||
dac1 |nimp| \- \-
|
||||
dac2 |nimp| \- \-
|
||||
dac3 |nimp| \- \-
|
||||
dac4 |nimp| \- \-
|
||||
dac5 |nimp| \- \-
|
||||
dac6 |nimp| \- \-
|
||||
dac7 |nimp| \- \-
|
||||
vsvp :py:attr:`Eiger.dacs.vsvp` OK \-
|
||||
vsvn :py:attr:`Eiger.dacs.vsvn` OK \-
|
||||
vtr :py:attr:`Eiger.dacs.vtr` OK \-
|
||||
vrf :py:attr:`Eiger.dacs.vrf` OK \-
|
||||
vrs :py:attr:`Eiger.dacs.vrs` OK \-
|
||||
vtgstv :py:attr:`Eiger.dacs.vtgstv` OK \-
|
||||
vcmp_ll :py:attr:`Eiger.dacs.vcmp_ll` OK \-
|
||||
vcmp_ll :py:attr:`Eiger.dacs.vcmp_ll` OK \-
|
||||
vcall :py:attr:`Eiger.dacs.vcall` OK \-
|
||||
vcmp_rl :py:attr:`Eiger.dacs.vcmp_rl` OK \-
|
||||
vcmp_rr :py:attr:`Eiger.dacs.vcmp_rr` OK \-
|
||||
rxb_rb :py:attr:`Eiger.dacs.rxb_rb` OK \-
|
||||
rxb_lb :py:attr:`Eiger.dacs.rxb_lb` OK \-
|
||||
vcp :py:attr:`Eiger.dacs.vcp` OK \-
|
||||
vcn :py:attr:`Eiger.dacs.vcn` OK \-
|
||||
vis :py:attr:`Eiger.dacs.vis` OK \-
|
||||
iodelay :py:attr:`Eiger.dacs.iodelay` OK \-
|
||||
dac |nimp| \- \-
|
||||
adcvpp |nimp| \- \-
|
||||
v_a |nimp| \- \-
|
||||
v_b |nimp| \- \-
|
||||
v_c |nimp| \- \-
|
||||
v_d |nimp| \- \-
|
||||
v_io |nimp| \- \-
|
||||
v_chip |nimp| \- \-
|
||||
v_limit |nimp| \- \-
|
||||
vIpre |nimp| \- \-
|
||||
VcdSh |nimp| \- \-
|
||||
Vth1 |nimp| \- \-
|
||||
Vth2 |nimp| \- \-
|
||||
Vth3 |nimp| \- \-
|
||||
VPL |nimp| \- \-
|
||||
Vtrim |nimp| \- \-
|
||||
vIbias |nimp| \- \-
|
||||
vIinSh |nimp| \- \-
|
||||
cas |nimp| \- \-
|
||||
casSh |nimp| \- \-
|
||||
vIbiasSh |nimp| \- \-
|
||||
vIcin |nimp| \- \-
|
||||
vIpreOut |nimp| \- \-
|
||||
temp_adc |nimp| \- \-
|
||||
temp_fpga :py:attr:`Detector.temp`.fpga OK \-
|
||||
temp_fpgaext |temp_fpgaext| OK \-
|
||||
temp_10ge :py:attr:`Detector.temp`.t10ge OK \-
|
||||
temp_dcdc :py:attr:`Detector.temp`.dcdc OK \-
|
||||
temp_sodl :py:attr:`Detector.temp`.sodl OK \-
|
||||
temp_sodr :py:attr:`Detector.temp`.sodr OK \-
|
||||
adc |nimp| \- \-
|
||||
temp_fpgafl :py:attr:`Detector.temp`.fpgafl OK \-
|
||||
temp_fpgafr :py:attr:`Detector.temp`.fpgafr OK \-
|
||||
i_a |nimp| \- \-
|
||||
i_b |nimp| \- \-
|
||||
i_c |nimp| \- \-
|
||||
i_d |nimp| \- \-
|
||||
i_io |nimp| \- \-
|
||||
vm_a |nimp| \- \-
|
||||
vm_b |nimp| \- \-
|
||||
vm_c |nimp| \- \-
|
||||
vm_d |nimp| \- \-
|
||||
vm_io |nimp| \- \-
|
||||
temp_threshold |tempth| \- \-
|
||||
temp_control |tempco| \- \-
|
||||
temp_event |tempev| \- \-
|
||||
outdir :py:attr:`Detector.file_path` OK OK
|
||||
fname :py:attr:`Detector.file_name` OK OK
|
||||
index :py:attr:`Detector.file_index` OK OK
|
||||
enablefwrite :py:attr:`Detector.file_write` OK OK
|
||||
overwrite :py:attr:`Detector.file_overwrite` OK \-
|
||||
currentfname |nimp| \- \-
|
||||
fileformat :py:attr:`Detector.file_format` OK \-
|
||||
positions |depr| \- \-
|
||||
startscript |depr| \- \-
|
||||
startscriptpar |depr| \- \-
|
||||
stopscript |depr| \- \-
|
||||
stopscriptpar |depr| \- \-
|
||||
scriptbefore |depr| \- \-
|
||||
scriptbeforepar |depr| \- \-
|
||||
scriptafter |depr| \- \-
|
||||
scriptafterpar |depr| \- \-
|
||||
headerafter |depr| \- \-
|
||||
headerbefore |depr| \- \-
|
||||
headerbeforepar |depr| \- \-
|
||||
headerafterpar |depr| \- \-
|
||||
encallog |depr| \- \-
|
||||
angcallog |depr| \- \-
|
||||
scan0script |depr| \- \-
|
||||
scan0par |depr| \- \-
|
||||
scan0prec |depr| \- \-
|
||||
scan0steps |depr| \- \-
|
||||
scan0range |depr| \- \-
|
||||
scan1script |depr| \- \-
|
||||
scan1par |depr| \- \-
|
||||
scan1prec |depr| \- \-
|
||||
scan1steps |depr| \- \-
|
||||
scan1range |depr| \- \-
|
||||
rx_hostname :py:attr:`Detector.rx_hostname` OK \-
|
||||
rx_udpip :py:attr:`Detector.rx_udpip` OK \-
|
||||
rx_udpmac :py:attr:`Detector.rx_udpmac` OK \-
|
||||
rx_udpport :py:attr:`Detector.rx_udpport` OK \-
|
||||
rx_udpport2 :py:attr:`Detector.rx_udpport` OK \-
|
||||
rx_udpsocksize :py:attr:`Detector.rx_udpsocksize` OK \-
|
||||
rx_realudpsocksize |rudp| OK
|
||||
detectormac :py:attr:`Detector.detector_mac` OK \-
|
||||
detectorip :py:attr:`Detector.detector_ip` OK \-
|
||||
txndelay_left :py:attr:`Eiger.delay`.left OK \-
|
||||
txndelay_right :py:attr:`Eiger.delay`.right OK \-
|
||||
txndelay_frame :py:attr:`Eiger.delay`.frame OK \-
|
||||
flowcontrol_10g :py:attr:`Eiger.flowcontrol_10g` OK \-
|
||||
zmqport :py:attr:`Detector.client_zmqport` Read \-
|
||||
rx_zmqport :py:attr:`Detector.rx_zmqport` Read \-
|
||||
rx_datastream :py:attr:`Detector.rx_datastream` OK \-
|
||||
zmqip :py:attr:`Detector.client_zmqip` OK \-
|
||||
rx_zmqip :py:attr:`Detector.rx_zmqip` Read \-
|
||||
rx_jsonaddheader :py:attr:`Detector.rx_jsonaddheader` OK \-
|
||||
configuremac :py:attr:`Detector.config_network` OK \-
|
||||
rx_tcpport :py:attr:`Detector.rx_tcpport`
|
||||
port |nimp| \- \-
|
||||
stopport |nimp| \- \-
|
||||
lock :py:attr:`Detector.lock` OK \-
|
||||
lastclient :py:attr:`Detector.last_client_ip` OK \-
|
||||
receiver start :py:func:`Detector.start_receiver` OK \-
|
||||
receiver stop :py:func:`Detector.stop_receiver` \- \-
|
||||
r_online |ron| OK \-
|
||||
r_checkonline |nimp| \- \-
|
||||
framescaught :py:attr:`Detector.frames_caught` OK \-
|
||||
resetframescaught |rfc| OK \-
|
||||
frameindex |rfi| OK \-
|
||||
r_lock :py:attr:`Detector.lock_receiver` OK \-
|
||||
r_lastclient |rlci| OK \-
|
||||
r_readfreq |nimp| \- \-
|
||||
rx_fifodepth :py:attr:`Detector.rx_fifodepth` OK \-
|
||||
r_silent |nimp| \- \-
|
||||
r_framesperfile :py:attr:`Detector.n_frames_per_file` OK \-
|
||||
r_discardpolicy |fdp| OK \-
|
||||
r_padding :py:attr:`Detector.file_padding` OK \-
|
||||
adcinvert |chiptest| \- \-
|
||||
adcdisable |chiptest| \- \-
|
||||
pattern |chiptest| \- \-
|
||||
patword |chiptest| \- \-
|
||||
patioctrl |chiptest| \- \-
|
||||
patclkctrl |chiptest| \- \-
|
||||
patlimits |chiptest| \- \-
|
||||
patloop0 |chiptest| \- \-
|
||||
patnloop0 |chiptest| \- \-
|
||||
patwait0 |chiptest| \- \-
|
||||
patwaittime0 |chiptest| \- \-
|
||||
patloop1 |chiptest| \- \-
|
||||
patnloop1 |chiptest| \- \-
|
||||
patwait1 |chiptest| \- \-
|
||||
patwaittime1 |chiptest| \- \-
|
||||
patloop2 |chiptest| \- \-
|
||||
patnloop2 |chiptest| \- \-
|
||||
patwait2 |chiptest| \- \-
|
||||
patwaittime2 |chiptest| \- \-
|
||||
dut_clk |chiptest| \- \-
|
||||
===================== ===================================== ================== =========
|
178
python/sphinx/conf.py
Executable file
178
python/sphinx/conf.py
Executable file
@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# sls_detector_tools documentation build configuration file, created by
|
||||
# sphinx-quickstart on Wed Nov 1 10:17:29 2017.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.doctest',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.mathjax',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinx.ext.napoleon',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.autosummary']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'sls_detector'
|
||||
copyright = '2019, Sls Detector Group'
|
||||
author = 'Erik Frojdh'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '4.0.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '4.0.1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = True
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Custom sidebar templates, must be a dictionary that maps document names
|
||||
# to template names.
|
||||
#
|
||||
# This is required for the alabaster theme
|
||||
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
|
||||
html_sidebars = {
|
||||
'**': [
|
||||
'about.html',
|
||||
'navigation.html',
|
||||
'relations.html', # needs 'show_related': True theme option to display
|
||||
'searchbox.html',
|
||||
'donate.html',
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'sls_detector_doc'
|
||||
napoleon_use_ivar = True
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'sls_detector.tex', 'sls_detector Documentation',
|
||||
'Erik Frojdh', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'sls_detector_tools', 'sls_detector_tools Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'py_sls', 'py_sls Documentation',
|
||||
author, 'py_sls', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
12
python/sphinx/cpp_api.rst
Executable file
12
python/sphinx/cpp_api.rst
Executable file
@ -0,0 +1,12 @@
|
||||
C++ API
|
||||
=====================================================
|
||||
|
||||
|
||||
.. py:currentmodule:: _sls_detector
|
||||
|
||||
.. autoclass:: DetectorApi
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
|
||||
|
67
python/sphinx/error-handling.rst
Executable file
67
python/sphinx/error-handling.rst
Executable file
@ -0,0 +1,67 @@
|
||||
Error handling
|
||||
=========================
|
||||
|
||||
|
||||
Check input in Python
|
||||
----------------------
|
||||
|
||||
As far as possible we try to check the input on the Python side
|
||||
before calling the slsDeteectorsSoftware. Errors should not pass
|
||||
silently but raise an exception
|
||||
|
||||
::
|
||||
|
||||
#Trimbit range for Eiger is 0-63
|
||||
detector.trimbits = 98
|
||||
(...)
|
||||
ValueError: Trimbit setting 98 is outside of range:0-63
|
||||
|
||||
Errors in slsDetectorsSoftware
|
||||
-------------------------------
|
||||
|
||||
The slsDetectorsSoftware uses a mask to record errors from the different
|
||||
detectors. If an error is found we raise a RuntimeError at the end of the
|
||||
call using the error message from slsDetectorsSoftware
|
||||
|
||||
.. todo ::
|
||||
|
||||
Implement this for all functions
|
||||
|
||||
::
|
||||
|
||||
detector.settings = 'bananas'
|
||||
(...)
|
||||
RuntimeError: Detector 0:
|
||||
Could not set settings.
|
||||
Detector 1:
|
||||
Could not set settings.
|
||||
Detector 2:
|
||||
Could not set settings.
|
||||
|
||||
|
||||
Using decorators
|
||||
-------------------
|
||||
|
||||
Using decorators we can reset the error mask before the command and then
|
||||
check it after the command
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#add decorator to check the error mask
|
||||
@error_handling
|
||||
def some_function():
|
||||
a = 1+1
|
||||
return a
|
||||
|
||||
Communication with the detector is usually the biggest overhead so
|
||||
this does not impact performance.
|
||||
|
||||
::
|
||||
|
||||
%timeit d.exposure_time
|
||||
>> 1.52 ms ± 5.42 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
|
||||
|
||||
%timeit d.decorated_exposure_time
|
||||
>> 1.53 ms ± 3.18 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
|
||||
|
||||
|
143
python/sphinx/examples.rst
Executable file
143
python/sphinx/examples.rst
Executable file
@ -0,0 +1,143 @@
|
||||
Examples
|
||||
================
|
||||
|
||||
Some short hints on how to use the detector
|
||||
|
||||
------------------------
|
||||
Simple threshold scan
|
||||
------------------------
|
||||
|
||||
Assuming you have set up your detector with exposure time, period, enabled
|
||||
file writing etc.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from sls_detector import Eiger
|
||||
|
||||
d = Eiger()
|
||||
threshold = range(0, 2000, 200)
|
||||
for th in threshold:
|
||||
d.vthreshold = th
|
||||
d.acq()
|
||||
|
||||
|
||||
If we want to control the shutter of for example, the big X-ray box we can add
|
||||
this line in our code. It then opens the shutter just before the measurement
|
||||
and closes is afterwards.
|
||||
|
||||
::
|
||||
|
||||
with xrf_shutter_open(box, 'Fe'):
|
||||
for th in threshold:
|
||||
d.vthreshold = th
|
||||
d.acq()
|
||||
|
||||
|
||||
-----------------------
|
||||
Reading temperatures
|
||||
-----------------------
|
||||
|
||||
::
|
||||
|
||||
d.temp
|
||||
>>
|
||||
temp_fpga : 43.19°C, 51.83°C
|
||||
temp_fpgaext : 38.50°C, 38.50°C
|
||||
temp_10ge : 39.50°C, 39.50°C
|
||||
temp_dcdc : 42.50°C, 42.50°C
|
||||
temp_sodl : 39.50°C, 40.50°C
|
||||
temp_sodr : 39.50°C, 40.50°C
|
||||
temp_fpgafl : 40.87°C, 37.61°C
|
||||
temp_fpgafr : 34.51°C, 35.63°C
|
||||
|
||||
d.temp.fpga
|
||||
>> temp_fpga : 40.84°C, 39.31°C
|
||||
|
||||
t = d.temp.fpga[0]
|
||||
t
|
||||
>> 40.551
|
||||
|
||||
t = d.temp.fpga[:]
|
||||
t
|
||||
>> [40.566, 39.128]
|
||||
|
||||
|
||||
-----------------------
|
||||
Non blocking acquire
|
||||
-----------------------
|
||||
|
||||
There are mainly two ways to achieve a non blocking acquire when calling from the Python API. One is to manually start
|
||||
the detector and the second one is to launch the normal acquire from a different process. Depending on your measurement
|
||||
it might also be better to run the other task in a seperate process and use acq in the main thread.
|
||||
But lets start looking at the at the manual way:
|
||||
|
||||
::
|
||||
|
||||
import time
|
||||
from sls_detector import Eiger
|
||||
d = Eiger()
|
||||
|
||||
n = 10
|
||||
t = 1
|
||||
|
||||
d.exposure_time = t
|
||||
d.n_frames = n
|
||||
d.reset_frames_caught()
|
||||
|
||||
#Start the measurement
|
||||
t0 = time.time()
|
||||
d.start_receiver()
|
||||
d.start_detector()
|
||||
|
||||
#Wait for the detector to be ready or do other important stuff
|
||||
time.sleep(t*n)
|
||||
|
||||
#check if the detector is ready otherwise wait a bit longer
|
||||
while d.status != 'idle':
|
||||
time.sleep(0.1)
|
||||
|
||||
#Stop the receiver after we got the frames
|
||||
#Detector is already idle so we don't need to stop it
|
||||
d.stop_receiver()
|
||||
|
||||
lost = d.frames_caught - n
|
||||
print(f'{n} frames of {t}s took {time.time()-t0:{.3}}s with {lost} frames lost ')
|
||||
|
||||
#Reset to not interfere with a potential next measurement
|
||||
d.reset_frames_caught()
|
||||
|
||||
Instead launching d.acq() from a different process is a bit easier since the control of receiver and detector
|
||||
is handled in the acq call. However, you need to join the process used otherwise a lot of zombie processes would
|
||||
hang around until the main process exits.
|
||||
|
||||
::
|
||||
|
||||
import time
|
||||
from multiprocessing import Process
|
||||
from sls_detector import Eiger
|
||||
|
||||
def acquire():
|
||||
"""
|
||||
Create a new Eiger object that still referes to the same actual detector
|
||||
and same shared memory. Then launch acq.
|
||||
"""
|
||||
detector = Eiger()
|
||||
detector.acq()
|
||||
|
||||
#This is the detector we use throughout the session
|
||||
d = Eiger()
|
||||
|
||||
#Process to run acquire
|
||||
p = Process(target=acquire)
|
||||
|
||||
#Start the thread and short sleep to allow the acq to start
|
||||
p.start()
|
||||
time.sleep(0.01)
|
||||
|
||||
#Do some other work
|
||||
while d.busy is True:
|
||||
print(d.busy)
|
||||
time.sleep(0.1)
|
||||
|
||||
#Join the process
|
||||
p.join()
|
122
python/sphinx/getting_started.rst
Executable file
122
python/sphinx/getting_started.rst
Executable file
@ -0,0 +1,122 @@
|
||||
Getting started
|
||||
================
|
||||
|
||||
|
||||
------------------------
|
||||
Setting up the detector
|
||||
------------------------
|
||||
|
||||
All configuration of the detector can either be done from the Python
|
||||
API (including loading config file) or externally. The detector setup is
|
||||
discovered from the shared memory when launching a new script. Because the
|
||||
detector usually should remain online longer than a specific script it is
|
||||
recommended to run the receivers seperate.
|
||||
|
||||
---------------------------------
|
||||
Setting and getting attributes
|
||||
---------------------------------
|
||||
|
||||
Most of the detector and software setting are implemented as attributes
|
||||
in the Detector class. When something is assigned it is also set
|
||||
in the detector and when the attribute is called using dot notation it
|
||||
it looked up from the detector.
|
||||
|
||||
::
|
||||
|
||||
#Currently Eiger and Jungfrau but Detector should work for all
|
||||
from sls_detector import Eiger()
|
||||
d = Eiger()
|
||||
|
||||
d.file_write = True
|
||||
d.vthreshold = 1500
|
||||
|
||||
d.frame_index
|
||||
>> 12
|
||||
|
||||
d.file_name
|
||||
>> 'run'
|
||||
|
||||
---------------------------------
|
||||
Working with DACs
|
||||
---------------------------------
|
||||
|
||||
The following examples assumes an Eiger500k detector. But the same syntax
|
||||
works for other detector sizes and models.
|
||||
|
||||
::
|
||||
|
||||
d.dacs
|
||||
>>
|
||||
========== DACS =========
|
||||
vsvp : 0, 0
|
||||
vtr : 4000, 4000
|
||||
vrf : 2000, 2300
|
||||
vrs : 1400, 1400
|
||||
vsvn : 4000, 4000
|
||||
vtgstv : 2556, 2556
|
||||
vcmp_ll : 1500, 1500
|
||||
vcmp_lr : 1500, 1500
|
||||
vcall : 3500, 3600
|
||||
vcmp_rl : 1500, 1500
|
||||
rxb_rb : 1100, 1100
|
||||
rxb_lb : 1100, 1100
|
||||
vcmp_rr : 1500, 1500
|
||||
vcp : 1500, 1500
|
||||
vcn : 2000, 2000
|
||||
vis : 1550, 1550
|
||||
iodelay : 660, 660
|
||||
|
||||
#Read dac values to a variable
|
||||
vrf = d.dacs.vrf[:]
|
||||
|
||||
#Set a dac in a module
|
||||
d.dacs.vrf[0] = 1500
|
||||
d.dacs.vrf[0]
|
||||
>> 1500
|
||||
|
||||
#Set vrf to the same value in all moduels
|
||||
d.dacs.vrf = 1500
|
||||
|
||||
#Set a dac using an iterable
|
||||
d.dacs.vrf = [1500, 1600]
|
||||
d.dacs.vrf
|
||||
>> vrf : 1500, 1600
|
||||
|
||||
#Set dacs iterating on index and values
|
||||
d.dacs.vrf[0,1] = 1300,1400
|
||||
|
||||
|
||||
---------------------------------
|
||||
Operating multiple detectors
|
||||
---------------------------------
|
||||
|
||||
Operating multiple detectors is supported by assigning an id when creating the object. If no id is
|
||||
set it defaults to 0.
|
||||
|
||||
::
|
||||
|
||||
d0 = Eiger() #id is now 0
|
||||
d1 = Jungfrau(1)
|
||||
|
||||
#Or explicitly
|
||||
d1 = Jungfrau(id = 0)
|
||||
|
||||
The detectors now operate independently of each other but can be synchronized using a hardware trigger.
|
||||
|
||||
::
|
||||
|
||||
from sls_detector import Eiger
|
||||
|
||||
d0 = Eiger(0)
|
||||
d1 = Eiger(1)
|
||||
|
||||
d0.load_config('/some/path/T45.config')
|
||||
d1.load_config('/some/path/T62.config')
|
||||
|
||||
d0.n_frames = 1
|
||||
d0.exposure_time = 1
|
||||
d0.timing_mode = 'trigger'
|
||||
|
||||
d1.n_frames = 5
|
||||
d1.exposure_time = 0.2
|
||||
d1.timing_mode = 'trigger'
|
30
python/sphinx/index.rst
Executable file
30
python/sphinx/index.rst
Executable file
@ -0,0 +1,30 @@
|
||||
sls_detector - Python interface for the slsDetectorsPackage
|
||||
==============================================================
|
||||
|
||||
sls_detector provide Python bindings to the slsDetectorsPackage using mainly the
|
||||
multiSlsDetector API. This module contains two parts, a compiled C module to
|
||||
expose the API and a Python class to offer a more Pythonic interface.
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
:caption: Contents:
|
||||
|
||||
installation
|
||||
getting_started
|
||||
code_quality
|
||||
command_line
|
||||
examples
|
||||
error-handling
|
||||
|
||||
sls_detector
|
||||
cpp_api
|
||||
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
90
python/sphinx/installation.rst
Executable file
90
python/sphinx/installation.rst
Executable file
@ -0,0 +1,90 @@
|
||||
Installation
|
||||
=========================
|
||||
|
||||
The easiest way to install the Python API and the slsDetectorPackage is using conda. But other
|
||||
methods are also available.
|
||||
|
||||
---------------------
|
||||
Install using conda
|
||||
---------------------
|
||||
If you don't have it installed get the latest version of `Miniconda`_
|
||||
|
||||
.. _Miniconda: https://conda.io/miniconda.html
|
||||
|
||||
::
|
||||
|
||||
wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh
|
||||
sh Miniconda3-latest-Linux-x86_64.sh
|
||||
|
||||
|
||||
Install sls_detector and sls_detector_lib using:
|
||||
|
||||
::
|
||||
|
||||
#Add conda channels
|
||||
conda config --add channels conda-forge
|
||||
conda config --add channels slsdetectorgroup
|
||||
|
||||
#Install latest version
|
||||
conda install sls_detector
|
||||
|
||||
#Install specific version
|
||||
conda install sls_detector=3.0.1
|
||||
|
||||
------------------------------
|
||||
Local build using conda-build
|
||||
------------------------------
|
||||
|
||||
Needs the `sls_detector_lib`_ installed in order to automatically find headers
|
||||
and shared libraries. Make sure that the branch of sls_detector matches the lib
|
||||
version installed.
|
||||
|
||||
.. _sls_detector_lib: https://github.com/slsdetectorgroup/sls_detector_lib
|
||||
|
||||
::
|
||||
|
||||
#Clone source code
|
||||
git clone https://github.com/slsdetectorgroup/sls_detector.git
|
||||
|
||||
#Checkout the branch needed
|
||||
git checkout 3.0.1
|
||||
|
||||
#Build and install the local version
|
||||
conda-build sls_detector
|
||||
conda install --use-local sls_detector
|
||||
|
||||
|
||||
-----------------------
|
||||
Developer build
|
||||
-----------------------
|
||||
|
||||
IF you if you are developing and are making constant changes to the code it's a bit cumbersome
|
||||
to build with conda and install. Then an easier way is to build the C/C++ parts in the package
|
||||
directory and temporary add this to the path
|
||||
|
||||
::
|
||||
|
||||
#in path/to/sls_detector
|
||||
python setup.py build_ext --inplace
|
||||
|
||||
Then in your Python script
|
||||
|
||||
::
|
||||
|
||||
import sys
|
||||
sys.path.append('/path/to/sls_detector')
|
||||
from sls_detector import Detector
|
||||
|
||||
|
||||
|
||||
--------------
|
||||
Prerequisites
|
||||
--------------
|
||||
|
||||
All dependencies are manged trough conda but for a stand alone build you would need
|
||||
|
||||
* gcc 4.8+
|
||||
* Qwt 6
|
||||
* Qt 4.8
|
||||
* numpy
|
||||
* slsDetectorPackage
|
6
python/sphinx/makedocs.sh
Executable file
6
python/sphinx/makedocs.sh
Executable file
@ -0,0 +1,6 @@
|
||||
make clean
|
||||
make html
|
||||
rm -rf ../docs/
|
||||
mv _build/html/ ../docs/
|
||||
touch ../docs/.nojekyll
|
||||
rm -rf _build
|
8
python/sphinx/modules.rst
Executable file
8
python/sphinx/modules.rst
Executable file
@ -0,0 +1,8 @@
|
||||
sls_detector
|
||||
==================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
sls_detector
|
||||
|
33
python/sphinx/sls_detector.rst
Executable file
33
python/sphinx/sls_detector.rst
Executable file
@ -0,0 +1,33 @@
|
||||
Python classes
|
||||
=====================================================
|
||||
|
||||
|
||||
.. py:currentmodule:: sls_detector
|
||||
|
||||
Detector
|
||||
----------
|
||||
|
||||
.. autoclass:: Detector
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
|
||||
Eiger
|
||||
-------
|
||||
|
||||
.. autoclass:: Eiger
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
|
||||
Jungfrau
|
||||
----------
|
||||
|
||||
.. autoclass:: Jungfrau
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
964
python/src/Detector.h
Executable file
964
python/src/Detector.h
Executable file
@ -0,0 +1,964 @@
|
||||
#ifndef DETECTOR_H
|
||||
#define DETECTOR_H
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "error_defs.h"
|
||||
#include "multiSlsDetector.h"
|
||||
#include "slsDetector.h"
|
||||
// #include "slsDetectorUtils.h"
|
||||
#include "sls_detector_defs.h"
|
||||
// #include "sls_receiver_defs.h"
|
||||
|
||||
class Detector {
|
||||
public:
|
||||
Detector(int i) : det(i), multi_detector_id(i) {
|
||||
// Disable output from std::cout
|
||||
// std::cout.setstate(std::ios_base::failbit);
|
||||
}
|
||||
|
||||
int getMultiDetectorId() { return multi_detector_id; }
|
||||
|
||||
// get image size as [nrow, ncols] return as a pair of ints
|
||||
std::pair<int, int> getImageSize() {
|
||||
std::pair<int, int> image_size{0, 0};
|
||||
image_size.first = det.getMaxNumberOfChannelsPerDetector(
|
||||
slsDetectorDefs::dimension::Y);
|
||||
image_size.second = det.getMaxNumberOfChannelsPerDetector(
|
||||
slsDetectorDefs::dimension::X);
|
||||
return image_size;
|
||||
}
|
||||
|
||||
void setImageSize(const int rows, const int cols) {
|
||||
det.setMaxNumberOfChannelsPerDetector(slsDetectorDefs::dimension::Y,
|
||||
rows);
|
||||
det.setMaxNumberOfChannelsPerDetector(slsDetectorDefs::dimension::X,
|
||||
cols);
|
||||
}
|
||||
|
||||
// blocking command, acquire set number of frames
|
||||
void acquire() { det.acquire(); }
|
||||
|
||||
// for Eiger check status of the module
|
||||
// true active false deactivated
|
||||
bool getActive(int i) { return det.activate(-1, i); }
|
||||
// activate or deactivate a module
|
||||
void setActive(int i, bool value) { det.activate(value, i); }
|
||||
|
||||
int getFramesCaughtByReceiver() {
|
||||
return det.getFramesCaughtByReceiver();
|
||||
// return det.getFramesCaughtByReceiver();
|
||||
}
|
||||
int getFramesCaughtByReceiver(int i) {
|
||||
return det.getFramesCaughtByReceiver(i);
|
||||
}
|
||||
|
||||
void setReceiverFifoDepth(int n_frames) {
|
||||
det.setReceiverFifoDepth(n_frames);
|
||||
}
|
||||
|
||||
void setNumberOfStorageCells(const int64_t num) {
|
||||
det.setTimer(slsDetectorDefs::timerIndex::STORAGE_CELL_NUMBER, num);
|
||||
}
|
||||
int getNumberOfStorageCells() {
|
||||
return det.setTimer(slsDetectorDefs::timerIndex::STORAGE_CELL_NUMBER,
|
||||
-1);
|
||||
}
|
||||
|
||||
void setStoragecellStart(int cell) { det.setStoragecellStart(cell); }
|
||||
|
||||
int getStoragecellStart() { return det.setStoragecellStart(); }
|
||||
|
||||
int getReceiverFifoDepth() { return det.setReceiverFifoDepth(); }
|
||||
|
||||
void resetFramesCaught() { det.resetFramesCaught(); }
|
||||
|
||||
int getReceiverCurrentFrameIndex() {
|
||||
return det.getReceiverCurrentFrameIndex();
|
||||
}
|
||||
|
||||
std::string getReceiverHostname(int det_id = -1) const {
|
||||
return det.getReceiverHostname(det_id);
|
||||
}
|
||||
|
||||
void setReceiverHostname(std::string hostname, int det_id = -1) {
|
||||
det.setReceiverHostname(hostname, det_id);
|
||||
}
|
||||
|
||||
std::string getReceiverUDPIP(int det_id = -1) const {
|
||||
return det.getReceiverUDPIP(det_id);
|
||||
}
|
||||
|
||||
void setReceiverUDPIP(std::string ip, int det_id = -1) {
|
||||
det.setReceiverUDPIP(ip, det_id);
|
||||
}
|
||||
|
||||
std::string getReceiverUDPMAC(int det_id = -1) {
|
||||
return det.getReceiverUDPMAC(det_id);
|
||||
}
|
||||
|
||||
void setReceiverUDPMAC(std::string mac, int det_id = -1) {
|
||||
det.setReceiverUDPMAC(mac, det_id);
|
||||
}
|
||||
|
||||
void startReceiver() { det.startReceiver(); }
|
||||
void stopReceiver() { det.stopReceiver(); }
|
||||
|
||||
bool getTenGigabitEthernet() { return det.enableTenGigabitEthernet(); }
|
||||
void setTenGigabitEthernet(bool value) {
|
||||
det.enableTenGigabitEthernet(value);
|
||||
}
|
||||
|
||||
void setFileFormat(const std::string &format);
|
||||
std::string getFileFormat();
|
||||
|
||||
std::string checkOnline() { return det.checkOnline(); }
|
||||
|
||||
bool getReceiverOnline() { return det.setReceiverOnline(); }
|
||||
void setReceiverOnline(const bool status) { det.setReceiverOnline(status); }
|
||||
|
||||
bool getOnline() { return det.setOnline(); }
|
||||
void setOnline(const bool status) { det.setOnline(status); }
|
||||
|
||||
bool isChipPowered() { return det.powerChip(); }
|
||||
void powerChip(const bool value) { det.powerChip(value); }
|
||||
|
||||
// read register from readout system, used for low level control
|
||||
uint32_t readRegister(const uint32_t addr) {
|
||||
return det.readRegister(addr);
|
||||
}
|
||||
|
||||
// directly write to register in readout system
|
||||
void writeRegister(const uint32_t addr, const uint32_t value) {
|
||||
det.writeRegister(addr, value);
|
||||
}
|
||||
|
||||
// directly write to the ADC register
|
||||
// should this also be unsigned? Probably...
|
||||
void writeAdcRegister(const int addr, const int value) {
|
||||
det.writeAdcRegister(addr, value);
|
||||
}
|
||||
|
||||
void setBitInRegister(const uint32_t reg_addr, const int bit_number) {
|
||||
det.setBit(reg_addr, bit_number);
|
||||
}
|
||||
void clearBitInRegister(const uint32_t reg_addr, const int bit_number) {
|
||||
det.clearBit(reg_addr, bit_number);
|
||||
}
|
||||
|
||||
bool getAcquiringFlag() { return det.getAcquiringFlag(); }
|
||||
|
||||
void setAcquiringFlag(const bool flag) { det.setAcquiringFlag(flag); }
|
||||
|
||||
bool getCounterBit() { return det.setCounterBit(); }
|
||||
void setCounterBit(bool b) { det.setCounterBit(b); }
|
||||
|
||||
slsDetectorDefs::dacIndex dacNameToEnum(std::string dac_name);
|
||||
|
||||
std::pair<int, int> getDetectorGeometry() {
|
||||
std::pair<int, int> g;
|
||||
det.getNumberOfDetectors(g.first, g.second);
|
||||
return g;
|
||||
}
|
||||
|
||||
int getNumberOfDetectors() { return det.getNumberOfDetectors(); }
|
||||
|
||||
std::string getRunStatus() {
|
||||
auto s = det.getRunStatus();
|
||||
return det.runStatusType(s);
|
||||
}
|
||||
|
||||
void startAcquisition() { det.startAcquisition(); }
|
||||
void stopAcquisition() { det.stopAcquisition(); }
|
||||
|
||||
std::string getHostname() { return det.getHostname(); }
|
||||
|
||||
void setHostname(std::string hostname) {
|
||||
det.setHostname(hostname.c_str());
|
||||
}
|
||||
|
||||
int getDynamicRange() { return det.setDynamicRange(-1); }
|
||||
void setDynamicRange(const int dr) { det.setDynamicRange(dr); }
|
||||
|
||||
void pulseChip(const int n) { det.pulseChip(n); }
|
||||
void pulseAllPixels(const int n);
|
||||
void pulseDiagonal(const int n);
|
||||
|
||||
void readConfigurationFile(std::string fname) {
|
||||
det.readConfigurationFile(fname);
|
||||
}
|
||||
void readParametersFile(std::string fname) {
|
||||
det.retrieveDetectorSetup(fname);
|
||||
}
|
||||
|
||||
int64_t getFirmwareVersion() {
|
||||
return det.getId(slsDetectorDefs::DETECTOR_FIRMWARE_VERSION);
|
||||
}
|
||||
int64_t getServerVersion() {
|
||||
return det.getId(slsDetectorDefs::DETECTOR_SOFTWARE_VERSION);
|
||||
}
|
||||
int64_t getClientVersion() {
|
||||
return det.getId(slsDetectorDefs::THIS_SOFTWARE_VERSION);
|
||||
}
|
||||
int64_t getReceiverVersion() {
|
||||
return det.getId(slsDetectorDefs::RECEIVER_VERSION);
|
||||
}
|
||||
|
||||
std::vector<int64_t> getDetectorNumber() { return det.getDetectorNumber(); }
|
||||
|
||||
int getReadoutClockSpeed() {
|
||||
return det.setSpeed(slsDetectorDefs::CLOCK_DIVIDER, -1);
|
||||
}
|
||||
void setReadoutClockSpeed(const int speed) {
|
||||
det.setSpeed(slsDetectorDefs::CLOCK_DIVIDER, speed);
|
||||
}
|
||||
|
||||
void setDbitPipeline(const int value) {
|
||||
det.setSpeed(slsDetectorDefs::DBIT_PIPELINE, value);
|
||||
}
|
||||
int getDbitPipeline() {
|
||||
return det.setSpeed(slsDetectorDefs::DBIT_PIPELINE, -1);
|
||||
}
|
||||
void setDbitPhase(const int value) {
|
||||
det.setSpeed(slsDetectorDefs::DBIT_PHASE, value);
|
||||
}
|
||||
int getDbitPhase() { return det.setSpeed(slsDetectorDefs::DBIT_PHASE, -1); }
|
||||
void setDbitClock(const int value) {
|
||||
det.setSpeed(slsDetectorDefs::DBIT_CLOCK, value);
|
||||
}
|
||||
int getDbitClock() { return det.setSpeed(slsDetectorDefs::DBIT_CLOCK, -1); }
|
||||
std::vector<int> getReceiverPort() const { return det.getReceiverPort(); }
|
||||
|
||||
void setReceiverPort(int det_id, int value) {
|
||||
det.setReceiverPort(value, det_id);
|
||||
}
|
||||
|
||||
void setRateCorrection(std::vector<double> tau) {
|
||||
for (int i = 0; i < det.getNumberOfDetectors(); ++i)
|
||||
det.setRateCorrection(tau[i], i);
|
||||
}
|
||||
|
||||
std::vector<double> getRateCorrection();
|
||||
|
||||
bool getFlippedDataX(int i) {
|
||||
return det.getFlippedData(slsDetectorDefs::dimension::X, i);
|
||||
}
|
||||
|
||||
bool getFlippedDataY(int i) {
|
||||
return det.getFlippedData(slsDetectorDefs::dimension::Y, i);
|
||||
}
|
||||
|
||||
void setFlippedDataX(int i, bool value) {
|
||||
det.setFlippedData(slsDetectorDefs::dimension::X, value, i);
|
||||
}
|
||||
|
||||
void setFlippedDataY(int i, bool value) {
|
||||
det.setFlippedData(slsDetectorDefs::dimension::Y, value, i);
|
||||
}
|
||||
|
||||
/*** Frame and file settings ***/
|
||||
void setFileName(std::string fname) { det.setFileName(fname); }
|
||||
std::string getFileName() { return det.getFileName(); }
|
||||
void setFilePath(std::string path) { det.setFilePath(path); }
|
||||
void setFilePath(std::string path, int i) { det.setFilePath(path, i); }
|
||||
std::string getFilePath() { return det.getFilePath(); }
|
||||
std::string getFilePath(int i) { return det.getFilePath(i); }
|
||||
|
||||
std::string getUserDetails() { return det.getUserDetails(); }
|
||||
|
||||
void setFramesPerFile(const int n_frames) {
|
||||
det.setFramesPerFile(n_frames);
|
||||
}
|
||||
int getFramesPerFile() { return det.setFramesPerFile(); }
|
||||
|
||||
std::string getReceiverFrameDiscardPolicy() {
|
||||
return det.getReceiverFrameDiscardPolicy(
|
||||
det.setReceiverFramesDiscardPolicy());
|
||||
}
|
||||
void setReceiverFramesDiscardPolicy(std::string f) {
|
||||
auto fdp = det.getReceiverFrameDiscardPolicy(f);
|
||||
if (fdp == slsDetectorDefs::GET_FRAME_DISCARD_POLICY) {
|
||||
throw std::invalid_argument("Coult not decode policy: nodiscard, "
|
||||
"discardempty, discardpartial");
|
||||
}
|
||||
det.setReceiverFramesDiscardPolicy(fdp);
|
||||
}
|
||||
|
||||
void setPartialFramesPadding(bool padding) {
|
||||
det.setPartialFramesPadding(padding);
|
||||
}
|
||||
|
||||
bool getPartialFramesPadding() { return det.getPartialFramesPadding(); }
|
||||
|
||||
std::vector<double> getMeasuredPeriod() {
|
||||
std::vector<double> mp;
|
||||
for (int i = 0; i < det.getNumberOfDetectors(); ++i) {
|
||||
auto t = det.getTimeLeft(slsDetectorDefs::MEASURED_PERIOD, i);
|
||||
mp.push_back(static_cast<double>(t) * 1E-9);
|
||||
}
|
||||
return mp;
|
||||
}
|
||||
std::vector<double> getMeasuredSubPeriod() {
|
||||
std::vector<double> mp;
|
||||
for (int i = 0; i < det.getNumberOfDetectors(); ++i) {
|
||||
auto t = det.getTimeLeft(slsDetectorDefs::MEASURED_SUBPERIOD, i);
|
||||
mp.push_back(static_cast<double>(t) * 1E-9);
|
||||
}
|
||||
return mp;
|
||||
}
|
||||
|
||||
bool isClientAndDetectorCompatible() {
|
||||
auto r = det.checkDetectorVersionCompatibility();
|
||||
if (r == 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
bool isClientAndReceiverCompatible() {
|
||||
auto r = det.checkReceiverVersionCompatibility();
|
||||
if (r == 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/*** END Frame and file settings ***/
|
||||
|
||||
void loadTrimbitFile(std::string fname, const int idet) {
|
||||
det.loadSettingsFile(fname, idet);
|
||||
}
|
||||
|
||||
// Eiger: set the energies where the detector is trimmed
|
||||
void setTrimEnergies(std::vector<int> energy) { det.setTrimEn(energy); }
|
||||
|
||||
std::vector<int> getTrimEnergies() { return det.getTrimEn(); }
|
||||
|
||||
/*** Temperature control functions for Jungfrau ***/
|
||||
void setThresholdTemperature(float t) {
|
||||
det.setThresholdTemperature(static_cast<int>(t * 1000), -1);
|
||||
}
|
||||
|
||||
float getThresholdTemperature() {
|
||||
return static_cast<double>(det.setThresholdTemperature(-1, -1)) /
|
||||
1000.0;
|
||||
}
|
||||
|
||||
void setTemperatureControl(bool v) { det.setTemperatureControl(v); }
|
||||
bool getTemperatureControl() { return det.setTemperatureControl(); }
|
||||
|
||||
bool getTemperatureEvent() { return det.setTemperatureEvent(); }
|
||||
void resetTemperatureEvent() { det.setTemperatureEvent(0); }
|
||||
/*** END Temperature control functions for Jungfrau ***/
|
||||
|
||||
void setThresholdEnergy(const int eV) { det.setThresholdEnergy(eV); }
|
||||
|
||||
std::string getSettingsDir() { return det.getSettingsDir(); }
|
||||
void setSettingsDir(std::string dir) { det.setSettingsDir(dir); }
|
||||
|
||||
int getThresholdEnergy() { return det.getThresholdEnergy(); }
|
||||
|
||||
std::string getSettings() {
|
||||
return det.getDetectorSettings(det.getSettings());
|
||||
}
|
||||
|
||||
void setSettings(std::string s) {
|
||||
det.setSettings(det.getDetectorSettings(s));
|
||||
}
|
||||
|
||||
// name to enum translation on the c++ side
|
||||
// should we instead expose the enum to Python?
|
||||
int getDac(std::string dac_name, const int mod_id) {
|
||||
int val = -1;
|
||||
auto dac = dacNameToEnum(dac_name);
|
||||
return det.setDAC(val, dac, 0, mod_id);
|
||||
}
|
||||
|
||||
void setDac(std::string dac_name, const int mod_id, int val) {
|
||||
auto dac = dacNameToEnum(dac_name);
|
||||
det.setDAC(val, dac, 0, mod_id);
|
||||
}
|
||||
|
||||
int getDac_mV(std::string dac_name, const int mod_id) {
|
||||
int val = -1;
|
||||
auto dac = dacNameToEnum(dac_name);
|
||||
return det.setDAC(val, dac, 1, mod_id);
|
||||
}
|
||||
|
||||
void setDac_mV(std::string dac_name, const int mod_id, int value) {
|
||||
auto dac = dacNameToEnum(dac_name);
|
||||
det.setDAC(value, dac, 1, mod_id);
|
||||
}
|
||||
|
||||
// Intended for the JungfrauCTB should we name dacs instead
|
||||
int getDacFromIndex(const int index, const int mod_id) {
|
||||
int val = -1;
|
||||
auto dac = static_cast<slsDetectorDefs::dacIndex>(0);
|
||||
return det.setDAC(val, dac, 0, mod_id);
|
||||
}
|
||||
// Intended for the JungfrauCTB should we name dacs instead
|
||||
int setDacFromIndex(const int index, const int mod_id, int value) {
|
||||
auto dac = static_cast<slsDetectorDefs::dacIndex>(0);
|
||||
return det.setDAC(value, dac, 0, mod_id);
|
||||
}
|
||||
|
||||
// Calling multi do we have a need to lock/unlock a single det?
|
||||
bool getServerLock() { return det.lockServer(-1); }
|
||||
void setServerLock(const bool value) { det.lockServer(value); }
|
||||
bool getReceiverLock() { return det.lockReceiver(-1); }
|
||||
void setReceiverLock(const bool value) { det.lockReceiver(value); }
|
||||
|
||||
int getAdc(std::string adc_name, int mod_id) {
|
||||
auto adc = dacNameToEnum(adc_name);
|
||||
return det.getADC(adc, mod_id);
|
||||
}
|
||||
|
||||
std::vector<std::string> getReadoutFlags();
|
||||
|
||||
// note singular
|
||||
void setReadoutFlag(const std::string flag_name);
|
||||
|
||||
// name to enum transltion of dac
|
||||
int getDacVthreshold() {
|
||||
int val = -1;
|
||||
auto dac = slsDetectorDefs::dacIndex::THRESHOLD;
|
||||
return det.setDAC(val, dac, 0, -1);
|
||||
}
|
||||
|
||||
void setDacVthreshold(const int val) {
|
||||
auto dac = slsDetectorDefs::dacIndex::THRESHOLD;
|
||||
det.setDAC(val, dac, 0, -1);
|
||||
}
|
||||
|
||||
void setFileIndex(const int i) { det.setFileIndex(i); }
|
||||
|
||||
int getFileIndex() { return det.getFileIndex(); }
|
||||
|
||||
// time in ns
|
||||
void setExposureTime(const int64_t t) {
|
||||
det.setTimer(slsDetectorDefs::timerIndex::ACQUISITION_TIME, t);
|
||||
}
|
||||
|
||||
// time in ns
|
||||
int64_t getExposureTime() {
|
||||
return det.setTimer(slsDetectorDefs::timerIndex::ACQUISITION_TIME, -1);
|
||||
}
|
||||
|
||||
void setSubExposureTime(const int64_t t) {
|
||||
det.setTimer(slsDetectorDefs::timerIndex::SUBFRAME_ACQUISITION_TIME, t);
|
||||
}
|
||||
|
||||
int64_t getSubExposureTime() {
|
||||
// time in ns
|
||||
return det.setTimer(
|
||||
slsDetectorDefs::timerIndex::SUBFRAME_ACQUISITION_TIME, -1);
|
||||
}
|
||||
|
||||
void setSubExposureDeadTime(const int64_t t) {
|
||||
det.setTimer(slsDetectorDefs::timerIndex::SUBFRAME_DEADTIME, t);
|
||||
}
|
||||
|
||||
int64_t getSubExposureDeadTime() {
|
||||
// time in ns
|
||||
return det.setTimer(slsDetectorDefs::timerIndex::SUBFRAME_DEADTIME, -1);
|
||||
}
|
||||
|
||||
int64_t getCycles() {
|
||||
return det.setTimer(slsDetectorDefs::timerIndex::CYCLES_NUMBER, -1);
|
||||
}
|
||||
|
||||
void setCycles(const int64_t n_cycles) {
|
||||
det.setTimer(slsDetectorDefs::timerIndex::CYCLES_NUMBER, n_cycles);
|
||||
}
|
||||
|
||||
void setNumberOfMeasurements(const int n_measurements) {
|
||||
det.setTimer(slsDetectorDefs::timerIndex::MEASUREMENTS_NUMBER,
|
||||
n_measurements);
|
||||
}
|
||||
int getNumberOfMeasurements() {
|
||||
return det.setTimer(slsDetectorDefs::timerIndex::MEASUREMENTS_NUMBER,
|
||||
-1);
|
||||
}
|
||||
|
||||
int getNumberOfGates() {
|
||||
return det.setTimer(slsDetectorDefs::timerIndex::GATES_NUMBER, -1);
|
||||
}
|
||||
void setNumberOfGates(const int t) {
|
||||
det.setTimer(slsDetectorDefs::timerIndex::GATES_NUMBER, t);
|
||||
}
|
||||
|
||||
// time in ns
|
||||
int64_t getDelay() {
|
||||
return det.setTimer(slsDetectorDefs::timerIndex::DELAY_AFTER_TRIGGER,
|
||||
-1);
|
||||
}
|
||||
// time in ns
|
||||
void setDelay(const int64_t t) {
|
||||
det.setTimer(slsDetectorDefs::timerIndex::DELAY_AFTER_TRIGGER, t);
|
||||
}
|
||||
// time in ns
|
||||
int64_t getPeriod() {
|
||||
return det.setTimer(slsDetectorDefs::timerIndex::FRAME_PERIOD, -1);
|
||||
}
|
||||
// time in ns
|
||||
void setPeriod(const int64_t t) {
|
||||
det.setTimer(slsDetectorDefs::timerIndex::FRAME_PERIOD, t);
|
||||
}
|
||||
|
||||
int64_t getNumberOfFrames() {
|
||||
return det.setTimer(slsDetectorDefs::timerIndex::FRAME_NUMBER, -1);
|
||||
}
|
||||
|
||||
void setNumberOfFrames(const int64_t nframes) {
|
||||
det.setTimer(slsDetectorDefs::timerIndex::FRAME_NUMBER, nframes);
|
||||
}
|
||||
|
||||
std::string getTimingMode() {
|
||||
return det.externalCommunicationType(
|
||||
det.setExternalCommunicationMode());
|
||||
}
|
||||
void setTimingMode(const std::string mode) {
|
||||
det.setExternalCommunicationMode(det.externalCommunicationType(mode));
|
||||
}
|
||||
|
||||
void freeSharedMemory() { det.freeSharedMemory(); }
|
||||
|
||||
std::vector<std::string> getDetectorType() {
|
||||
std::vector<std::string> detector_type;
|
||||
for (int i = 0; i < det.getNumberOfDetectors(); ++i) {
|
||||
detector_type.push_back(det.getDetectorTypeAsString(i));
|
||||
}
|
||||
return detector_type;
|
||||
}
|
||||
|
||||
void setFileWrite(bool value) { det.setFileWrite(value); }
|
||||
bool getFileWrite() { return det.getFileWrite(); }
|
||||
|
||||
void setFileOverWrite(bool value) { det.setFileOverWrite(value); }
|
||||
|
||||
bool getFileOverWrite() { return det.getFileOverWrite(); }
|
||||
|
||||
void setAllTrimbits(int tb) { det.setAllTrimbits(tb); }
|
||||
int getAllTrimbits() { return det.setAllTrimbits(-1); }
|
||||
bool getRxDataStreamStatus() {
|
||||
return det.enableDataStreamingFromReceiver();
|
||||
}
|
||||
|
||||
void setRxDataStreamStatus(bool state) {
|
||||
det.enableDataStreamingFromReceiver(state);
|
||||
}
|
||||
|
||||
// Get a network parameter for all detectors, looping over individual
|
||||
// detectors return a vector of strings
|
||||
std::vector<int> getReceiverStreamingPort() {
|
||||
std::vector<int> vec;
|
||||
vec.reserve(det.getNumberOfDetectors());
|
||||
for (int i = 0; i < det.getNumberOfDetectors(); ++i) {
|
||||
vec.push_back(det.getReceiverStreamingPort(i));
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
void setReceiverStreamingPort(int value, int det_id) {
|
||||
det.setReceiverDataStreamingOutPort(value, det_id);
|
||||
}
|
||||
|
||||
std::vector<int> getReceiverUDPPort() {
|
||||
std::vector<int> vec;
|
||||
vec.reserve(det.getNumberOfDetectors());
|
||||
for (int i = 0; i < det.getNumberOfDetectors(); ++i) {
|
||||
vec.push_back(det.getReceiverUDPPort(i));
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
std::vector<int> getReceiverUDPPort2() {
|
||||
std::vector<int> vec;
|
||||
vec.reserve(det.getNumberOfDetectors());
|
||||
for (int i = 0; i < det.getNumberOfDetectors(); ++i) {
|
||||
vec.push_back(det.getReceiverUDPPort2(i));
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
void setReceiverUDPPort(int port, int det_id) {
|
||||
det.setReceiverUDPPort(port, det_id);
|
||||
}
|
||||
void setReceiverUDPPort2(int port, int det_id) {
|
||||
det.setReceiverUDPPort2(port, det_id);
|
||||
}
|
||||
|
||||
// //Set network parameter for all modules if det_id == -1 otherwise the
|
||||
// module
|
||||
// //specified with det_id.
|
||||
// void setDetectorNetworkParameter(std::string par_name, std::string par,
|
||||
// const int det_id) {
|
||||
// auto p = networkNameToEnum(par_name);
|
||||
// if (det_id == -1) {
|
||||
// det.setDetectorNetworkParameter(p, par);
|
||||
// } else {
|
||||
// det.setDetectorNetworkParameter(p, par, det_id);
|
||||
// }
|
||||
// }
|
||||
|
||||
void configureNetworkParameters() { det.configureMAC(); }
|
||||
|
||||
std::string getLastClientIP() { return det.getLastClientIP(); }
|
||||
std::string getReceiverLastClientIP() {
|
||||
return det.getReceiverLastClientIP();
|
||||
}
|
||||
|
||||
// get frame delay of module (det_id) in ns
|
||||
int getDelayFrame(int det_id) {
|
||||
auto r = det.setDetectorNetworkParameter(
|
||||
slsDetectorDefs::networkParameter::DETECTOR_TXN_DELAY_FRAME, -1,
|
||||
det_id);
|
||||
return r;
|
||||
}
|
||||
// set frame delay of module (det_id) in ns
|
||||
void setDelayFrame(int det_id, int delay) {
|
||||
// auto delay_str = std::to_string(delay);
|
||||
det.setDetectorNetworkParameter(
|
||||
slsDetectorDefs::networkParameter::DETECTOR_TXN_DELAY_FRAME, delay,
|
||||
det_id);
|
||||
}
|
||||
|
||||
// get delay left of module (det_id) in ns
|
||||
int getDelayLeft(int det_id) {
|
||||
auto r = det.setDetectorNetworkParameter(
|
||||
slsDetectorDefs::networkParameter::DETECTOR_TXN_DELAY_LEFT, -1,
|
||||
det_id);
|
||||
return r;
|
||||
}
|
||||
// set delay left of module (det_id) in ns
|
||||
void setDelayLeft(int det_id, int delay) {
|
||||
// auto delay_str = std::to_string(delay);
|
||||
det.setDetectorNetworkParameter(
|
||||
slsDetectorDefs::networkParameter::DETECTOR_TXN_DELAY_LEFT, delay,
|
||||
det_id);
|
||||
}
|
||||
|
||||
// get delay right of module (det_id) in ns
|
||||
int getDelayRight(const int det_id) {
|
||||
auto r = det.setDetectorNetworkParameter(
|
||||
slsDetectorDefs::networkParameter::DETECTOR_TXN_DELAY_RIGHT, -1,
|
||||
det_id);
|
||||
return r;
|
||||
}
|
||||
|
||||
// set delay right of module (det_id) in ns
|
||||
void setDelayRight(int det_id, int delay) {
|
||||
// auto delay_str = std::to_string(delay);
|
||||
det.setDetectorNetworkParameter(
|
||||
slsDetectorDefs::networkParameter::DETECTOR_TXN_DELAY_RIGHT, delay,
|
||||
det_id);
|
||||
}
|
||||
|
||||
// Check if detector if filling in gap pixels in module
|
||||
// return true if so, currently only in developer
|
||||
bool getGapPixels() { return det.enableGapPixels(-1); }
|
||||
|
||||
// Set to true to have the detector filling in gap pixels
|
||||
// false to disable, currently only in developer
|
||||
void setGapPixels(bool val) { det.enableGapPixels(val); }
|
||||
|
||||
slsDetectorDefs::networkParameter networkNameToEnum(std::string par_name);
|
||||
|
||||
private:
|
||||
multiSlsDetector det;
|
||||
slsDetector *getSlsDetector(int i) const;
|
||||
int multi_detector_id = 0;
|
||||
};
|
||||
|
||||
void Detector::setFileFormat(const std::string &format) {
|
||||
if (format == "binary") {
|
||||
det.setFileFormat(slsDetectorDefs::fileFormat::BINARY);
|
||||
} else if (format == "ascii") {
|
||||
det.setFileFormat(slsDetectorDefs::fileFormat::ASCII);
|
||||
} else if (format == "hdf5") {
|
||||
det.setFileFormat(slsDetectorDefs::fileFormat::HDF5);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Detector::getFileFormat() {
|
||||
auto format =
|
||||
det.setFileFormat(slsDetectorDefs::fileFormat::GET_FILE_FORMAT, -1);
|
||||
switch (format) {
|
||||
case slsDetectorDefs::fileFormat::BINARY:
|
||||
return "binary";
|
||||
case slsDetectorDefs::fileFormat::ASCII:
|
||||
return "ascii";
|
||||
case slsDetectorDefs::fileFormat::HDF5:
|
||||
return "hdf5";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
slsDetectorDefs::networkParameter
|
||||
Detector::networkNameToEnum(std::string par_name) {
|
||||
|
||||
if (par_name == "detectormac") {
|
||||
return slsDetectorDefs::networkParameter::DETECTOR_MAC;
|
||||
} else if (par_name == "detectorip") {
|
||||
return slsDetectorDefs::networkParameter::DETECTOR_IP;
|
||||
} else if (par_name == "rx_hostname") {
|
||||
return slsDetectorDefs::networkParameter::RECEIVER_HOSTNAME;
|
||||
} else if (par_name == "rx_udpip") {
|
||||
return slsDetectorDefs::networkParameter::RECEIVER_UDP_IP;
|
||||
} else if (par_name == "rx_udpport") {
|
||||
return slsDetectorDefs::networkParameter::RECEIVER_UDP_PORT;
|
||||
} else if (par_name == "rx_udpmac") {
|
||||
return slsDetectorDefs::networkParameter::RECEIVER_UDP_MAC;
|
||||
} else if (par_name == "rx_udpport2") {
|
||||
return slsDetectorDefs::networkParameter::RECEIVER_UDP_PORT2;
|
||||
} else if (par_name == "rx_udpsocksize") {
|
||||
return slsDetectorDefs::networkParameter::RECEIVER_UDP_SCKT_BUF_SIZE;
|
||||
} else if (par_name == "rx_realudpsocksize") {
|
||||
return slsDetectorDefs::networkParameter::
|
||||
RECEIVER_REAL_UDP_SCKT_BUF_SIZE;
|
||||
} else if (par_name == "rx_jsonaddheader") {
|
||||
return slsDetectorDefs::networkParameter::ADDITIONAL_JSON_HEADER;
|
||||
} else if (par_name == "delay_left") {
|
||||
return slsDetectorDefs::networkParameter::DETECTOR_TXN_DELAY_LEFT;
|
||||
} else if (par_name == "delay_right") {
|
||||
return slsDetectorDefs::networkParameter::DETECTOR_TXN_DELAY_RIGHT;
|
||||
} else if (par_name == "delay_frame") {
|
||||
return slsDetectorDefs::networkParameter::DETECTOR_TXN_DELAY_FRAME;
|
||||
} else if (par_name == "flow_control_10g") {
|
||||
return slsDetectorDefs::networkParameter::FLOW_CONTROL_10G;
|
||||
} else if (par_name == "client_zmqport") {
|
||||
return slsDetectorDefs::networkParameter::CLIENT_STREAMING_PORT;
|
||||
} else if (par_name == "rx_zmqport") {
|
||||
return slsDetectorDefs::networkParameter::RECEIVER_STREAMING_PORT;
|
||||
} else if (par_name == "rx_zmqip") {
|
||||
return slsDetectorDefs::networkParameter::RECEIVER_STREAMING_SRC_IP;
|
||||
} else if (par_name == "client_zmqip") {
|
||||
return slsDetectorDefs::networkParameter::CLIENT_STREAMING_SRC_IP;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Could not decode network parameter");
|
||||
};
|
||||
|
||||
// slsDetectorDefs::fileFormat Detector::file///
|
||||
|
||||
slsDetectorDefs::dacIndex Detector::dacNameToEnum(std::string dac_name) {
|
||||
// to avoid uninitialised
|
||||
slsDetectorDefs::dacIndex dac = slsDetectorDefs::dacIndex::E_SvP;
|
||||
|
||||
if (dac_name == "vsvp") {
|
||||
dac = slsDetectorDefs::dacIndex::E_SvP;
|
||||
} else if (dac_name == "vtr") {
|
||||
dac = slsDetectorDefs::dacIndex::E_Vtr;
|
||||
} else if (dac_name == "vthreshold") {
|
||||
dac = slsDetectorDefs::dacIndex::THRESHOLD;
|
||||
} else if (dac_name == "vrf") {
|
||||
dac = slsDetectorDefs::dacIndex::E_Vrf;
|
||||
} else if (dac_name == "vrs") {
|
||||
dac = slsDetectorDefs::dacIndex::E_Vrs;
|
||||
} else if (dac_name == "vsvn") {
|
||||
dac = slsDetectorDefs::dacIndex::E_SvN;
|
||||
} else if (dac_name == "vtgstv") {
|
||||
dac = slsDetectorDefs::dacIndex::E_Vtgstv;
|
||||
} else if (dac_name == "vcmp_ll") {
|
||||
dac = slsDetectorDefs::dacIndex::E_Vcmp_ll;
|
||||
} else if (dac_name == "vcmp_lr") {
|
||||
dac = slsDetectorDefs::dacIndex::E_Vcmp_lr;
|
||||
} else if (dac_name == "vcall") {
|
||||
dac = slsDetectorDefs::dacIndex::E_cal;
|
||||
} else if (dac_name == "vcmp_rl") {
|
||||
dac = slsDetectorDefs::dacIndex::E_Vcmp_rl;
|
||||
} else if (dac_name == "rxb_rb") {
|
||||
dac = slsDetectorDefs::dacIndex::E_rxb_rb;
|
||||
} else if (dac_name == "rxb_lb") {
|
||||
dac = slsDetectorDefs::dacIndex::E_rxb_lb;
|
||||
} else if (dac_name == "vcmp_rr") {
|
||||
dac = slsDetectorDefs::dacIndex::E_Vcmp_rr;
|
||||
} else if (dac_name == "vcp") {
|
||||
dac = slsDetectorDefs::dacIndex::E_Vcp;
|
||||
} else if (dac_name == "vcn") {
|
||||
dac = slsDetectorDefs::dacIndex::E_Vcn;
|
||||
} else if (dac_name == "vis") {
|
||||
dac = slsDetectorDefs::dacIndex::E_Vis;
|
||||
} else if (dac_name == "iodelay") {
|
||||
dac = slsDetectorDefs::dacIndex::IO_DELAY;
|
||||
} else if (dac_name == "v_a") {
|
||||
dac = slsDetectorDefs::dacIndex::V_POWER_A;
|
||||
} else if (dac_name == "v_b") {
|
||||
dac = slsDetectorDefs::dacIndex::V_POWER_B;
|
||||
} else if (dac_name == "v_c") {
|
||||
dac = slsDetectorDefs::dacIndex::V_POWER_C;
|
||||
} else if (dac_name == "v_d") {
|
||||
dac = slsDetectorDefs::dacIndex::V_POWER_D;
|
||||
} else if (dac_name == "v_io") {
|
||||
dac = slsDetectorDefs::dacIndex::V_POWER_IO;
|
||||
} else if (dac_name == "v_chip") {
|
||||
dac = slsDetectorDefs::dacIndex::V_POWER_CHIP;
|
||||
} else if (dac_name == "v_limit") {
|
||||
dac = slsDetectorDefs::dacIndex::V_LIMIT;
|
||||
} else if (dac_name == "temp_fpga") {
|
||||
dac = slsDetectorDefs::dacIndex::TEMPERATURE_FPGA;
|
||||
} else if (dac_name == "temp_fpgaext") {
|
||||
dac = slsDetectorDefs::dacIndex::TEMPERATURE_FPGAEXT;
|
||||
} else if (dac_name == "temp_10ge") {
|
||||
dac = slsDetectorDefs::dacIndex::TEMPERATURE_10GE;
|
||||
} else if (dac_name == "temp_dcdc") {
|
||||
dac = slsDetectorDefs::dacIndex::TEMPERATURE_DCDC;
|
||||
} else if (dac_name == "temp_sodl") {
|
||||
dac = slsDetectorDefs::dacIndex::TEMPERATURE_SODL;
|
||||
} else if (dac_name == "temp_sodr") {
|
||||
dac = slsDetectorDefs::dacIndex::TEMPERATURE_SODR;
|
||||
} else if (dac_name == "temp_fpgafl") {
|
||||
dac = slsDetectorDefs::dacIndex::TEMPERATURE_FPGA2;
|
||||
} else if (dac_name == "temp_fpgafr") {
|
||||
dac = slsDetectorDefs::dacIndex::TEMPERATURE_FPGA3;
|
||||
} else if (dac_name == "vhighvoltage") {
|
||||
dac = slsDetectorDefs::dacIndex::HIGH_VOLTAGE;
|
||||
} else if (dac_name == "vb_comp") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(0);
|
||||
} else if (dac_name == "vdd_prot") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(1);
|
||||
} else if (dac_name == "vin_com") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(2);
|
||||
} else if (dac_name == "vref_prech") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(3);
|
||||
} else if (dac_name == "vb_pixbuff") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(4);
|
||||
} else if (dac_name == "vb_ds") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(5);
|
||||
} else if (dac_name == "vref_ds") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(6);
|
||||
} else if (dac_name == "vref_comp") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(7);
|
||||
} else if (dac_name == "dac0") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(0);
|
||||
} else if (dac_name == "dac1") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(1);
|
||||
} else if (dac_name == "dac2") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(2);
|
||||
} else if (dac_name == "dac3") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(3);
|
||||
} else if (dac_name == "dac4") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(4);
|
||||
} else if (dac_name == "dac5") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(5);
|
||||
} else if (dac_name == "dac6") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(6);
|
||||
} else if (dac_name == "dac7") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(7);
|
||||
} else if (dac_name == "dac8") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(8);
|
||||
} else if (dac_name == "dac9") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(9);
|
||||
} else if (dac_name == "dac10") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(10);
|
||||
} else if (dac_name == "dac11") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(11);
|
||||
} else if (dac_name == "dac12") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(12);
|
||||
} else if (dac_name == "dac13") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(13);
|
||||
} else if (dac_name == "dac14") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(14);
|
||||
} else if (dac_name == "dac15") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(15);
|
||||
} else if (dac_name == "dac16") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(16);
|
||||
} else if (dac_name == "dac17") {
|
||||
dac = static_cast<slsDetectorDefs::dacIndex>(17);
|
||||
}
|
||||
|
||||
return dac;
|
||||
}
|
||||
|
||||
std::vector<std::string> Detector::getReadoutFlags() {
|
||||
std::vector<std::string> flags;
|
||||
auto r = det.setReadOutFlags();
|
||||
if (r & slsDetectorDefs::readOutFlags::STORE_IN_RAM)
|
||||
flags.push_back("storeinram");
|
||||
if (r & slsDetectorDefs::readOutFlags::TOT_MODE)
|
||||
flags.push_back("tot");
|
||||
if (r & slsDetectorDefs::readOutFlags::CONTINOUS_RO)
|
||||
flags.push_back("continous");
|
||||
if (r & slsDetectorDefs::readOutFlags::PARALLEL)
|
||||
flags.push_back("parallel");
|
||||
if (r & slsDetectorDefs::readOutFlags::NONPARALLEL)
|
||||
flags.push_back("nonparallel");
|
||||
if (r & slsDetectorDefs::readOutFlags::SAFE)
|
||||
flags.push_back("safe");
|
||||
if (r & slsDetectorDefs::readOutFlags::DIGITAL_ONLY)
|
||||
flags.push_back("digital");
|
||||
if (r & slsDetectorDefs::readOutFlags::ANALOG_AND_DIGITAL)
|
||||
flags.push_back("analog_digital");
|
||||
if (r & slsDetectorDefs::readOutFlags::NOOVERFLOW)
|
||||
flags.push_back("nooverflow");
|
||||
if (r & slsDetectorDefs::readOutFlags::SHOW_OVERFLOW)
|
||||
flags.push_back("overflow");
|
||||
return flags;
|
||||
}
|
||||
|
||||
// note singular
|
||||
void Detector::setReadoutFlag(const std::string flag_name) {
|
||||
if (flag_name == "none")
|
||||
det.setReadOutFlags(slsDetectorDefs::readOutFlags::NORMAL_READOUT);
|
||||
else if (flag_name == "storeinram")
|
||||
det.setReadOutFlags(slsDetectorDefs::readOutFlags::STORE_IN_RAM);
|
||||
else if (flag_name == "tot")
|
||||
det.setReadOutFlags(slsDetectorDefs::readOutFlags::TOT_MODE);
|
||||
else if (flag_name == "continous")
|
||||
det.setReadOutFlags(slsDetectorDefs::readOutFlags::CONTINOUS_RO);
|
||||
else if (flag_name == "parallel")
|
||||
det.setReadOutFlags(slsDetectorDefs::readOutFlags::PARALLEL);
|
||||
else if (flag_name == "nonparallel")
|
||||
det.setReadOutFlags(slsDetectorDefs::readOutFlags::NONPARALLEL);
|
||||
else if (flag_name == "safe")
|
||||
det.setReadOutFlags(slsDetectorDefs::readOutFlags::SAFE);
|
||||
else if (flag_name == "digital")
|
||||
det.setReadOutFlags(slsDetectorDefs::readOutFlags::DIGITAL_ONLY);
|
||||
else if (flag_name == "analog_digital")
|
||||
det.setReadOutFlags(slsDetectorDefs::readOutFlags::ANALOG_AND_DIGITAL);
|
||||
else if (flag_name == "nooverflow")
|
||||
det.setReadOutFlags(slsDetectorDefs::readOutFlags::NOOVERFLOW);
|
||||
else if (flag_name == "overflow")
|
||||
det.setReadOutFlags(slsDetectorDefs::readOutFlags::SHOW_OVERFLOW);
|
||||
else
|
||||
throw std::runtime_error("Flag name not recognized");
|
||||
}
|
||||
|
||||
std::vector<double> Detector::getRateCorrection() {
|
||||
std::vector<double> rate_corr;
|
||||
for (int i = 0; i < det.getNumberOfDetectors(); ++i) {
|
||||
rate_corr.push_back(det.getRateCorrection(i));
|
||||
}
|
||||
return rate_corr;
|
||||
}
|
||||
|
||||
void Detector::pulseAllPixels(int n) {
|
||||
// int pulsePixelNMove(int n=0,int x=0,int y=0);
|
||||
// int pulsePixel(int n=0,int x=0,int y=0);
|
||||
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
det.pulsePixel(0, -255 + j, 0);
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
det.pulsePixelNMove(n, 0, 1);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
void Detector::pulseDiagonal(int n) {
|
||||
// int pulsePixelNMove(int n=0,int x=0,int y=0);
|
||||
// int pulsePixel(int n=0,int x=0,int y=0);
|
||||
|
||||
for (int j = 20; j < 232; j += 16) {
|
||||
det.pulsePixel(0, -255, j);
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
det.pulsePixelNMove(n, 1, 1);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#endif // DETECTOR_H
|
297
python/src/main.cpp
Executable file
297
python/src/main.cpp
Executable file
@ -0,0 +1,297 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "Detector.h"
|
||||
#include "mythenFileIO.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
PYBIND11_MODULE(_sls_detector, m)
|
||||
{
|
||||
m.doc() = R"pbdoc(
|
||||
C/C++ API
|
||||
-----------------------
|
||||
.. warning ::
|
||||
|
||||
This is the compiled c extension. You probably want to look at the
|
||||
interface provided by sls instead.
|
||||
|
||||
)pbdoc";
|
||||
|
||||
py::class_<Detector> DetectorApi(m, "DetectorApi", R"pbdoc(
|
||||
Interface to the multiSlsDetector class through Detector.h These functions
|
||||
are used by the python classes Eiger and Jungfrau and normally it is better
|
||||
to use them than to directly access functions here.
|
||||
|
||||
However it is possible to access these functions...
|
||||
|
||||
::
|
||||
|
||||
#Using the python class
|
||||
from sls_detector import Eiger
|
||||
d = Eiger()
|
||||
d._api.getThresholdEnergy()
|
||||
|
||||
#creating a DetectorApi object (remember to set online flags)
|
||||
from _sls_detector import DetectorApi
|
||||
api = DetectorApi(0)
|
||||
api.setOnline(True)
|
||||
api.setReceiverOnline(True)
|
||||
api.getNumberOfFrames()
|
||||
|
||||
#But the Pythonic way is almost alway simpler
|
||||
d = Eiger()
|
||||
d.n_frames
|
||||
>> 10
|
||||
|
||||
)pbdoc");
|
||||
DetectorApi
|
||||
.def(py::init<int>())
|
||||
.def("freeSharedMemory", &Detector::freeSharedMemory)
|
||||
.def("getMultiDetectorId", &Detector::getMultiDetectorId)
|
||||
.def("acq", &Detector::acquire)
|
||||
.def("getAcquiringFlag", &Detector::getAcquiringFlag)
|
||||
.def("setAcquiringFlag", &Detector::setAcquiringFlag)
|
||||
|
||||
.def("setAllTrimbits", &Detector::setAllTrimbits)
|
||||
.def("getAllTrimbits", &Detector::getAllTrimbits)
|
||||
.def("setCounterBit", &Detector::setCounterBit)
|
||||
.def("getCounterBit", &Detector::getCounterBit)
|
||||
|
||||
.def("getAdc", &Detector::getAdc)
|
||||
.def("getDac", &Detector::getDac)
|
||||
.def("getDac_mV", &Detector::getDac_mV)
|
||||
.def("setDac", &Detector::setDac)
|
||||
.def("setDac_mV", &Detector::setDac_mV)
|
||||
.def("getDacFromIndex", &Detector::getDacFromIndex)
|
||||
.def("setDacFromIndex", &Detector::setDacFromIndex)
|
||||
|
||||
.def("getDbitPipeline", &Detector::getDbitPipeline)
|
||||
.def("setDbitPipeline", &Detector::setDbitPipeline)
|
||||
.def("getDbitPhase", &Detector::getDbitPhase)
|
||||
.def("setDbitPhase", &Detector::setDbitPhase)
|
||||
.def("getDbitClock", &Detector::getDbitClock)
|
||||
.def("setDbitClock", &Detector::setDbitClock)
|
||||
|
||||
.def("setThresholdEnergy", &Detector::setThresholdEnergy)
|
||||
.def("getThresholdEnergy", &Detector::getThresholdEnergy)
|
||||
|
||||
.def("getSettings", &Detector::getSettings)
|
||||
.def("setSettings", &Detector::setSettings)
|
||||
.def("getSettingsDir", &Detector::getSettingsDir)
|
||||
.def("setSettingsDir", &Detector::setSettingsDir)
|
||||
|
||||
.def("loadTrimbitFile", &Detector::loadTrimbitFile)
|
||||
.def("setTrimEnergies", &Detector::setTrimEnergies)
|
||||
.def("getTrimEnergies", &Detector::getTrimEnergies)
|
||||
|
||||
.def("pulseChip", &Detector::pulseChip)
|
||||
.def("pulseAllPixels", &Detector::pulseAllPixels)
|
||||
.def("pulseDiagonal", &Detector::pulseDiagonal)
|
||||
.def("getRunStatus", &Detector::getRunStatus)
|
||||
.def("readConfigurationFile", &Detector::readConfigurationFile)
|
||||
.def("readParametersFile", &Detector::readParametersFile)
|
||||
.def("checkOnline", &Detector::checkOnline)
|
||||
.def("setReadoutClockSpeed", &Detector::setReadoutClockSpeed)
|
||||
.def("getReadoutClockSpeed", &Detector::getReadoutClockSpeed)
|
||||
.def("getHostname", &Detector::getHostname)
|
||||
.def("setHostname", &Detector::setHostname)
|
||||
|
||||
.def("getOnline", &Detector::getOnline)
|
||||
.def("setOnline", &Detector::setOnline)
|
||||
.def("getReceiverOnline", &Detector::getReceiverOnline)
|
||||
.def("setReceiverOnline", &Detector::setReceiverOnline)
|
||||
|
||||
.def("getReceiverPort", &Detector::getReceiverPort)
|
||||
.def("setReceiverPort", &Detector::setReceiverPort)
|
||||
|
||||
.def("isChipPowered", &Detector::isChipPowered)
|
||||
.def("powerChip", &Detector::powerChip)
|
||||
|
||||
.def("readRegister", &Detector::readRegister)
|
||||
.def("writeRegister", &Detector::writeRegister)
|
||||
.def("writeAdcRegister", &Detector::writeAdcRegister)
|
||||
.def("setBitInRegister", &Detector::setBitInRegister)
|
||||
.def("clearBitInRegister", &Detector::clearBitInRegister)
|
||||
|
||||
.def("setDynamicRange", &Detector::setDynamicRange)
|
||||
.def("getDynamicRange", &Detector::getDynamicRange)
|
||||
.def("getFirmwareVersion", &Detector::getFirmwareVersion)
|
||||
.def("getServerVersion", &Detector::getServerVersion)
|
||||
.def("getClientVersion", &Detector::getClientVersion)
|
||||
.def("getReceiverVersion", &Detector::getReceiverVersion)
|
||||
.def("getDetectorNumber", &Detector::getDetectorNumber)
|
||||
.def("getRateCorrection", &Detector::getRateCorrection)
|
||||
.def("setRateCorrection", &Detector::setRateCorrection)
|
||||
|
||||
.def("startAcquisition", &Detector::startAcquisition)
|
||||
.def("stopAcquisition", &Detector::stopAcquisition)
|
||||
.def("startReceiver", &Detector::startReceiver)
|
||||
.def("stopReceiver", &Detector::stopReceiver)
|
||||
|
||||
.def("getFilePath", (std::string(Detector::*)()) & Detector::getFilePath, "Using multiSlsDetector")
|
||||
.def("getFilePath", (std::string(Detector::*)(int)) & Detector::getFilePath, "File path for individual detector")
|
||||
.def("setFilePath", (void (Detector::*)(std::string)) & Detector::setFilePath)
|
||||
.def("setFilePath", (void (Detector::*)(std::string, int)) & Detector::setFilePath)
|
||||
|
||||
.def("setFileName", &Detector::setFileName)
|
||||
.def("getFileName", &Detector::getFileName)
|
||||
.def("setFileIndex", &Detector::setFileIndex)
|
||||
.def("getFileIndex", &Detector::getFileIndex)
|
||||
|
||||
.def("setExposureTime", &Detector::setExposureTime)
|
||||
.def("getExposureTime", &Detector::getExposureTime)
|
||||
.def("setSubExposureTime", &Detector::setSubExposureTime)
|
||||
.def("getSubExposureTime", &Detector::getSubExposureTime)
|
||||
.def("setPeriod", &Detector::setPeriod)
|
||||
.def("getPeriod", &Detector::getPeriod)
|
||||
.def("setSubExposureDeadTime", &Detector::setSubExposureDeadTime)
|
||||
.def("getSubExposureDeadTime", &Detector::getSubExposureDeadTime)
|
||||
|
||||
.def("getCycles", &Detector::getCycles)
|
||||
.def("setCycles", &Detector::setCycles)
|
||||
.def("setNumberOfMeasurements", &Detector::setNumberOfMeasurements)
|
||||
.def("getNumberOfMeasurements", &Detector::getNumberOfMeasurements)
|
||||
.def("getNumberOfGates", &Detector::getNumberOfGates)
|
||||
.def("setNumberOfGates", &Detector::setNumberOfGates)
|
||||
.def("getDelay", &Detector::getDelay)
|
||||
.def("setDelay", &Detector::setDelay)
|
||||
|
||||
|
||||
.def("setStoragecellStart", &Detector::setStoragecellStart)
|
||||
.def("getStoragecellStart", &Detector::getStoragecellStart)
|
||||
.def("setNumberOfStorageCells", &Detector::setNumberOfStorageCells)
|
||||
.def("getNumberOfStorageCells", &Detector::getNumberOfStorageCells)
|
||||
|
||||
.def("getTimingMode", &Detector::getTimingMode)
|
||||
.def("setTimingMode", &Detector::setTimingMode)
|
||||
|
||||
.def("getDetectorType", &Detector::getDetectorType)
|
||||
|
||||
.def("setThresholdTemperature", &Detector::setThresholdTemperature)
|
||||
.def("getThresholdTemperature", &Detector::getThresholdTemperature)
|
||||
.def("setTemperatureControl", &Detector::setTemperatureControl)
|
||||
.def("getTemperatureControl", &Detector::getTemperatureControl)
|
||||
.def("getTemperatureEvent", &Detector::getTemperatureEvent)
|
||||
.def("resetTemperatureEvent", &Detector::resetTemperatureEvent)
|
||||
|
||||
.def("getRxDataStreamStatus", &Detector::getRxDataStreamStatus)
|
||||
.def("setRxDataStreamStatus", &Detector::setRxDataStreamStatus)
|
||||
|
||||
//Network stuff
|
||||
.def("getReceiverHostname", &Detector::getReceiverHostname)
|
||||
.def("setReceiverHostname", &Detector::setReceiverHostname)
|
||||
.def("getReceiverStreamingPort", &Detector::getReceiverStreamingPort)
|
||||
.def("setReceiverStreamingPort", &Detector::setReceiverStreamingPort)
|
||||
.def("getReceiverUDPPort", &Detector::getReceiverUDPPort)
|
||||
.def("getReceiverUDPPort2", &Detector::getReceiverUDPPort2)
|
||||
.def("setReceiverUDPPort", &Detector::setReceiverUDPPort)
|
||||
.def("setReceiverUDPPort2", &Detector::setReceiverUDPPort2)
|
||||
.def("setReceiverUDPIP", &Detector::setReceiverUDPIP)
|
||||
.def("getReceiverUDPIP", &Detector::getReceiverUDPIP)
|
||||
.def("getReceiverUDPMAC", &Detector::getReceiverUDPMAC)
|
||||
.def("setReceiverUDPMAC", &Detector::setReceiverUDPMAC)
|
||||
|
||||
.def("getReceiverPort", &Detector::getReceiverPort)
|
||||
.def("setReceiverPort", &Detector::setReceiverPort)
|
||||
|
||||
.def("configureNetworkParameters", &Detector::configureNetworkParameters)
|
||||
.def("getDelayFrame", &Detector::getDelayFrame)
|
||||
.def("setDelayFrame", &Detector::setDelayFrame)
|
||||
.def("getDelayLeft", &Detector::getDelayLeft)
|
||||
.def("setDelayLeft", &Detector::setDelayLeft)
|
||||
.def("getDelayRight", &Detector::getDelayRight)
|
||||
.def("setDelayRight", &Detector::setDelayRight)
|
||||
.def("getLastClientIP", &Detector::getLastClientIP)
|
||||
.def("getReceiverLastClientIP", &Detector::getReceiverLastClientIP)
|
||||
|
||||
.def("setFramesPerFile", &Detector::setFramesPerFile)
|
||||
.def("getFramesPerFile", &Detector::getFramesPerFile)
|
||||
.def("setReceiverFifoDepth", &Detector::setReceiverFifoDepth)
|
||||
.def("getReceiverFifoDepth", &Detector::getReceiverFifoDepth)
|
||||
|
||||
.def("getReceiverFrameDiscardPolicy", &Detector::getReceiverFrameDiscardPolicy)
|
||||
.def("setReceiverFramesDiscardPolicy", &Detector::setReceiverFramesDiscardPolicy)
|
||||
.def("setPartialFramesPadding", &Detector::setPartialFramesPadding)
|
||||
.def("getPartialFramesPadding", &Detector::getPartialFramesPadding)
|
||||
|
||||
.def("getUserDetails", &Detector::getUserDetails)
|
||||
.def("isClientAndDetectorCompatible", &Detector::isClientAndDetectorCompatible)
|
||||
.def("isClientAndReceiverCompatible", &Detector::isClientAndReceiverCompatible)
|
||||
.def("getMeasuredPeriod", &Detector::getMeasuredPeriod)
|
||||
.def("getMeasuredSubPeriod", &Detector::getMeasuredSubPeriod)
|
||||
|
||||
|
||||
.def("setFileWrite", &Detector::setFileWrite)
|
||||
.def("getFileWrite", &Detector::getFileWrite)
|
||||
.def("setFileOverWrite", &Detector::setFileOverWrite)
|
||||
.def("getFileOverWrite", &Detector::getFileOverWrite)
|
||||
.def("getDacVthreshold", &Detector::getDacVthreshold)
|
||||
.def("setDacVthreshold", &Detector::setDacVthreshold)
|
||||
.def("setNumberOfFrames", &Detector::setNumberOfFrames)
|
||||
.def("getNumberOfFrames", &Detector::getNumberOfFrames)
|
||||
|
||||
//Overloaded calls
|
||||
.def("getFramesCaughtByReceiver", (int (Detector::*)() ) & Detector::getFramesCaughtByReceiver)
|
||||
.def("getFramesCaughtByReceiver", (int (Detector::*)(int)) & Detector::getFramesCaughtByReceiver)
|
||||
|
||||
|
||||
.def("resetFramesCaught", &Detector::resetFramesCaught)
|
||||
.def("getReceiverCurrentFrameIndex", &Detector::getReceiverCurrentFrameIndex)
|
||||
.def("getGapPixels", &Detector::getGapPixels)
|
||||
.def("setGapPixels", &Detector::setGapPixels)
|
||||
.def("getFlippedDataX", &Detector::getFlippedDataX)
|
||||
.def("getFlippedDataY", &Detector::getFlippedDataY)
|
||||
.def("setFlippedDataX", &Detector::setFlippedDataX)
|
||||
.def("setFlippedDataY", &Detector::setFlippedDataY)
|
||||
|
||||
.def("getServerLock", &Detector::getServerLock)
|
||||
.def("setServerLock", &Detector::setServerLock)
|
||||
.def("getReceiverLock", &Detector::getReceiverLock)
|
||||
.def("setReceiverLock", &Detector::setReceiverLock)
|
||||
|
||||
.def("getReadoutFlags", &Detector::getReadoutFlags)
|
||||
.def("setReadoutFlag", &Detector::setReadoutFlag)
|
||||
|
||||
.def("setFileFormat", &Detector::setFileFormat)
|
||||
.def("getFileFormat", &Detector::getFileFormat)
|
||||
|
||||
.def("getActive", &Detector::getActive)
|
||||
.def("setActive", &Detector::setActive)
|
||||
|
||||
.def("getTenGigabitEthernet", &Detector::getTenGigabitEthernet)
|
||||
.def("setTenGigabitEthernet", &Detector::setTenGigabitEthernet)
|
||||
|
||||
.def("getImageSize", &Detector::getImageSize)
|
||||
.def("setImageSize", &Detector::setImageSize)
|
||||
.def("getNumberOfDetectors", &Detector::getNumberOfDetectors)
|
||||
.def("getDetectorGeometry", &Detector::getDetectorGeometry);
|
||||
|
||||
|
||||
|
||||
//Experimental API to use the multi directly and inherit from to reduce
|
||||
//code duplication need to investigate how to handle documentation
|
||||
py::class_<multiSlsDetector> multiDetectorApi(m, "multiDetectorApi");
|
||||
multiDetectorApi
|
||||
.def(py::init<int>())
|
||||
.def_property("busy",
|
||||
py::cpp_function(&multiSlsDetector::getAcquiringFlag),
|
||||
py::cpp_function(&multiSlsDetector::setAcquiringFlag))
|
||||
.def_property_readonly("rx_tcpport",
|
||||
py::cpp_function(&multiSlsDetector::getReceiverPort))
|
||||
.def_property_readonly("detectornumber",
|
||||
py::cpp_function(&multiSlsDetector::getDetectorNumber))
|
||||
.def("_getReceiverUDPIP", &multiSlsDetector::getReceiverUDPIP)
|
||||
.def("_setReceiverUDPIP", &multiSlsDetector::setReceiverUDPIP)
|
||||
;
|
||||
|
||||
py::module io = m.def_submodule("io", "Submodule for io");
|
||||
io.def("read_ctb_file", &read_ctb_file, "some");
|
||||
|
||||
|
||||
#ifdef VERSION_INFO
|
||||
m.attr("__version__") = VERSION_INFO;
|
||||
#else
|
||||
m.attr("__version__") = "dev";
|
||||
#endif
|
||||
}
|
85
python/src/mythenFileIO.h
Normal file
85
python/src/mythenFileIO.h
Normal file
@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include <pybind11/numpy.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
namespace py = pybind11;
|
||||
|
||||
template <size_t bit_index0, size_t bit_index1>
|
||||
std::vector<int> ExtractBits(const std::vector<uint64_t> &data, int dr = 24) {
|
||||
constexpr int mask0 = (1 << bit_index0);
|
||||
constexpr int mask1 = (1 << bit_index1);
|
||||
constexpr int NumStrips = 2;
|
||||
const int NumCompleteSamples = data.size() / dr;
|
||||
const int NumCounters = NumCompleteSamples * NumStrips;
|
||||
|
||||
std::vector<int> result(NumCounters);
|
||||
|
||||
auto ptr = data.data();
|
||||
|
||||
auto strip0 = result.data();
|
||||
auto strip1 = strip0 + NumCompleteSamples;
|
||||
|
||||
for (int j = 0; j != NumCompleteSamples; ++j) {
|
||||
for (int i = 0; i != dr; ++i) {
|
||||
int bit0 = (*ptr & mask0) >> bit_index0;
|
||||
int bit1 = (*ptr++ & mask1) >> bit_index1;
|
||||
*strip0 |= bit0 << i;
|
||||
*strip1 |= bit1 << i;
|
||||
}
|
||||
strip0++;
|
||||
strip1++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <size_t bit_index>
|
||||
std::vector<int> ExtractBits(const std::vector<uint64_t> &data, int dr = 24) {
|
||||
constexpr int mask = (1 << bit_index);
|
||||
const int NumCompleteSamples = data.size() / dr;
|
||||
std::vector<int> result(NumCompleteSamples);
|
||||
auto ptr = data.data();
|
||||
for (auto &r : result) {
|
||||
for (int i = 0; i != dr; ++i) {
|
||||
int bit = (*ptr++ & mask) >> bit_index;
|
||||
r |= bit << i;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> ReadFile(const std::string &fname, int offset = 8,
|
||||
int dr = 24) {
|
||||
const int element_size = static_cast<int>(sizeof(uint64_t));
|
||||
const int byte_offset = element_size * offset;
|
||||
const int expected_size = dr * element_size * 32 * 3;
|
||||
std::ifstream fs(fname, std::ios::binary | std::ios::ate);
|
||||
if (!fs.is_open()) {
|
||||
throw std::runtime_error("File not found: " + std::string(fname));
|
||||
}
|
||||
auto data_size = static_cast<long>(fs.tellg()) - byte_offset;
|
||||
if (data_size != expected_size) {
|
||||
auto diff = data_size - expected_size;
|
||||
std::cout << "WARNING: data size is: " << data_size
|
||||
<< " expected size is: " << expected_size << ", "
|
||||
<< std::abs(diff) << " bytes "
|
||||
<< ((diff < 0) ? "missing" : "too many") << '\n';
|
||||
}
|
||||
std::vector<uint64_t> data(expected_size / element_size);
|
||||
fs.seekg(byte_offset, std::ios::beg);
|
||||
fs.read(reinterpret_cast<char *>(data.data()), expected_size);
|
||||
return data;
|
||||
}
|
||||
|
||||
py::array_t<uint64_t> read_ctb_file(const std::string &fname, int offset = 8,
|
||||
int dr = 24) {
|
||||
auto data = ExtractBits<17, 6>(ReadFile(fname, offset, dr));
|
||||
return py::array(data.size(), data.data());
|
||||
}
|
BIN
python/unit-tests/__pycache__/dac_test.cpython-37-PYTEST.pyc
Executable file
BIN
python/unit-tests/__pycache__/dac_test.cpython-37-PYTEST.pyc
Executable file
Binary file not shown.
BIN
python/unit-tests/__pycache__/detector_test.cpython-37-PYTEST.pyc
Executable file
BIN
python/unit-tests/__pycache__/detector_test.cpython-37-PYTEST.pyc
Executable file
Binary file not shown.
BIN
python/unit-tests/__pycache__/test_detector_property.cpython-37-PYTEST.pyc
Executable file
BIN
python/unit-tests/__pycache__/test_detector_property.cpython-37-PYTEST.pyc
Executable file
Binary file not shown.
BIN
python/unit-tests/__pycache__/test_utils.cpython-37-PYTEST.pyc
Executable file
BIN
python/unit-tests/__pycache__/test_utils.cpython-37-PYTEST.pyc
Executable file
Binary file not shown.
173
python/unit-tests/dac_test.py
Executable file
173
python/unit-tests/dac_test.py
Executable file
@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Testing setting and getting dacs from the detector
|
||||
"""
|
||||
from unittest.mock import Mock, call
|
||||
import pytest
|
||||
from pytest_mock import mocker
|
||||
import numpy as np
|
||||
|
||||
from sls_detector import Eiger
|
||||
from sls_detector import DetectorApi
|
||||
|
||||
|
||||
def test_get_vrf_for_three_mod(mocker):
|
||||
m2= mocker.patch.object(DetectorApi, 'getNumberOfDetectors', autospec=True)
|
||||
m2.return_value = 3
|
||||
m = mocker.patch.object(DetectorApi, 'getDac', autospec=True)
|
||||
m.return_value = 1560
|
||||
d = Eiger()
|
||||
vrf = d.dacs.vrf[:]
|
||||
assert vrf == [1560, 1560, 1560]
|
||||
|
||||
def test_set_vrf_for_three_mod_same_value(mocker):
|
||||
m2= mocker.patch.object(DetectorApi, 'getNumberOfDetectors', autospec=True)
|
||||
m2.return_value = 3
|
||||
m = mocker.patch.object(DetectorApi, 'setDac', autospec=True)
|
||||
# m.return_value = 1560
|
||||
d = Eiger()
|
||||
d.dacs.vrf[:] = 1500
|
||||
calls = [call('vrf', 0, 1500), call('vrf', 1, 1500), call('vrf', 2, 1500)]
|
||||
m.assert_has_calls(calls)
|
||||
assert m.call_count == 3
|
||||
|
||||
def test_set_vrf_for_four_mod_different_value(mocker):
|
||||
m2= mocker.patch.object(DetectorApi, 'getNumberOfDetectors', autospec=True)
|
||||
m2.return_value = 4
|
||||
m = mocker.patch.object(DetectorApi, 'setDac', autospec=True)
|
||||
# m.return_value = 1560
|
||||
d = Eiger()
|
||||
d.dacs.vrf = [1500, 1600, 1800, 1502]
|
||||
calls = [call('vrf', 0, 1500),
|
||||
call('vrf', 1, 1600),
|
||||
call('vrf', 2, 1800),
|
||||
call('vrf', 3, 1502)]
|
||||
m.assert_has_calls(calls)
|
||||
assert m.call_count == 4
|
||||
|
||||
def test_set_vrf_for_four_mod_different_value_slice(mocker):
|
||||
m2= mocker.patch.object(DetectorApi, 'getNumberOfDetectors', autospec=True)
|
||||
m2.return_value = 4
|
||||
m = mocker.patch.object(DetectorApi, 'setDac', autospec=True)
|
||||
# m.return_value = 1560
|
||||
d = Eiger()
|
||||
d.dacs.vrf[:] = [1500, 1600, 1800, 1502]
|
||||
calls = [call('vrf', 0, 1500),
|
||||
call('vrf', 1, 1600),
|
||||
call('vrf', 2, 1800),
|
||||
call('vrf', 3, 1502)]
|
||||
m.assert_has_calls(calls)
|
||||
assert m.call_count == 4
|
||||
|
||||
def test_set_vcp_single_call(mocker):
|
||||
m2= mocker.patch.object(DetectorApi, 'getNumberOfDetectors', autospec=True)
|
||||
m2.return_value = 2
|
||||
m = mocker.patch.object(DetectorApi, 'setDac', autospec=True)
|
||||
# m.return_value = 1560
|
||||
d = Eiger()
|
||||
d.dacs.vcp[1] = 1637
|
||||
m.assert_called_once_with('vcp', 1, 1637)
|
||||
|
||||
def test_iterate_on_index_call_vcn(mocker):
|
||||
m2= mocker.patch.object(DetectorApi, 'getNumberOfDetectors', autospec=True)
|
||||
m2.return_value = 10
|
||||
m = mocker.patch.object(DetectorApi, 'setDac', autospec=True)
|
||||
# m.return_value = 1560
|
||||
d = Eiger()
|
||||
d.dacs.vcn[0,3,8] = 1532
|
||||
calls = [call('vcn', 0, 1532),
|
||||
call('vcn', 3, 1532),
|
||||
call('vcn', 8, 1532)]
|
||||
m.assert_has_calls(calls)
|
||||
assert m.call_count == 3
|
||||
|
||||
def test_set_dac_from_element_in_numpy_array(mocker):
|
||||
m2= mocker.patch.object(DetectorApi, 'getNumberOfDetectors', autospec=True)
|
||||
m2.return_value = 2
|
||||
m = mocker.patch.object(DetectorApi, 'setDac', autospec=True)
|
||||
d = Eiger()
|
||||
|
||||
vrf = np.array((1600,1700,1800))
|
||||
d.dacs.vrf = vrf[0]
|
||||
calls = [call('vrf', 0, 1600),
|
||||
call('vrf', 1, 1600),]
|
||||
m.assert_has_calls(calls)
|
||||
assert m.call_count == 2
|
||||
|
||||
def test_set_dac_from_element_in_numpy_array_using_slice(mocker):
|
||||
m2= mocker.patch.object(DetectorApi, 'getNumberOfDetectors', autospec=True)
|
||||
m2.return_value = 2
|
||||
m = mocker.patch.object(DetectorApi, 'setDac', autospec=True)
|
||||
d = Eiger()
|
||||
|
||||
vrf = np.array((1600,1700,1800))
|
||||
d.dacs.vrf[:] = vrf[0]
|
||||
calls = [call('vrf', 0, 1600),
|
||||
call('vrf', 1, 1600),]
|
||||
m.assert_has_calls(calls)
|
||||
assert m.call_count == 2
|
||||
|
||||
def test_set_eiger_default(mocker):
|
||||
m2= mocker.patch.object(DetectorApi, 'getNumberOfDetectors', autospec=True)
|
||||
m2.return_value = 2
|
||||
m = mocker.patch.object(DetectorApi, 'setDac', autospec=True)
|
||||
# m.return_value = 1560
|
||||
d = Eiger()
|
||||
d.dacs.set_default()
|
||||
calls = [call('vsvp', 0, 0),
|
||||
call('vsvp', 1, 0),
|
||||
call('vtr', 0, 2500),
|
||||
call('vtr', 1, 2500),
|
||||
call('vrf', 0, 3300),
|
||||
call('vrf', 1, 3300),
|
||||
call('vrs', 0, 1400),
|
||||
call('vrs', 1, 1400),
|
||||
call('vsvn', 0, 4000),
|
||||
call('vsvn', 1, 4000),
|
||||
call('vtgstv', 0, 2556),
|
||||
call('vtgstv', 1, 2556),
|
||||
call('vcmp_ll', 0, 1500),
|
||||
call('vcmp_ll', 1, 1500),
|
||||
call('vcmp_lr', 0, 1500),
|
||||
call('vcmp_lr', 1, 1500),
|
||||
call('vcall', 0, 4000),
|
||||
call('vcall', 1, 4000),
|
||||
call('vcmp_rl', 0, 1500),
|
||||
call('vcmp_rl', 1, 1500),
|
||||
call('rxb_rb', 0, 1100),
|
||||
call('rxb_rb', 1, 1100),
|
||||
call('rxb_lb', 0, 1100),
|
||||
call('rxb_lb', 1, 1100),
|
||||
call('vcmp_rr', 0, 1500),
|
||||
call('vcmp_rr', 1, 1500),
|
||||
call('vcp', 0, 200),
|
||||
call('vcp', 1, 200),
|
||||
call('vcn', 0, 2000),
|
||||
call('vcn', 1, 2000),
|
||||
call('vis', 0, 1550),
|
||||
call('vis', 1, 1550),
|
||||
call('iodelay', 0, 660),
|
||||
call('iodelay', 1, 660)]
|
||||
|
||||
m.assert_has_calls(calls)
|
||||
assert m.call_count == 17*2
|
||||
|
||||
def test_set_eiger_set_from_array_call_count(mocker):
|
||||
import numpy as np
|
||||
m2= mocker.patch.object(DetectorApi, 'getNumberOfDetectors', autospec=True)
|
||||
m2.return_value = 3
|
||||
m = mocker.patch.object(DetectorApi, 'setDac', autospec=True)
|
||||
# m.return_value = 1560
|
||||
d = Eiger()
|
||||
d.dacs.set_from_array( np.zeros((17,3)))
|
||||
assert m.call_count == 17*3
|
||||
|
||||
def test_get_fpga_temp(mocker):
|
||||
m2= mocker.patch.object(DetectorApi, 'getNumberOfDetectors', autospec=True)
|
||||
m2.return_value = 2
|
||||
m = mocker.patch.object(DetectorApi, 'getAdc', autospec=True)
|
||||
m.return_value = 34253
|
||||
d = Eiger()
|
||||
t = d.temp.fpga[:]
|
||||
assert t == [34.253, 34.253]
|
489
python/unit-tests/detector_eiger.py
Executable file
489
python/unit-tests/detector_eiger.py
Executable file
@ -0,0 +1,489 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Testing parameters and methods of the Detector class using mocks
|
||||
"""
|
||||
from unittest.mock import Mock
|
||||
import pytest
|
||||
from pytest_mock import mocker
|
||||
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def d():
|
||||
from sls_detector import Eiger
|
||||
return Eiger()
|
||||
|
||||
|
||||
def test_acq_call(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.acq')
|
||||
d.acq()
|
||||
m.assert_called_once_with()
|
||||
|
||||
def test_busy_call(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getAcquiringFlag')
|
||||
m.return_value = False
|
||||
assert d.busy == False
|
||||
|
||||
|
||||
def test_assign_to_detector_type(d):
|
||||
with pytest.raises(AttributeError):
|
||||
d.detector_type = 'Eiger'
|
||||
|
||||
def test_det_type(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getDetectorType')
|
||||
m.return_value = 'Eiger'
|
||||
assert d.detector_type == 'Eiger'
|
||||
|
||||
def test_set_dynamic_range_4(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setDynamicRange')
|
||||
d.dynamic_range = 4
|
||||
m.assert_called_with(4)
|
||||
|
||||
def test_set_dynamic_range_8(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setDynamicRange')
|
||||
d.dynamic_range = 8
|
||||
m.assert_called_with(8)
|
||||
|
||||
|
||||
def test_set_dynamic_range_16(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setDynamicRange')
|
||||
d.dynamic_range = 16
|
||||
m.assert_called_with(16)
|
||||
|
||||
def test_set_dynamic_range_32(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setDynamicRange')
|
||||
d.dynamic_range = 32
|
||||
m.assert_called_with(32)
|
||||
|
||||
def test_set_dynamic_range_raises_exception(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setDynamicRange')
|
||||
with pytest.raises(ValueError):
|
||||
d.dynamic_range = 17
|
||||
|
||||
def test_get_dynamic_range_32(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getDynamicRange')
|
||||
m.return_value = 32
|
||||
dr = d.dynamic_range
|
||||
assert dr == 32
|
||||
|
||||
def test_eiger_matrix_reset(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getCounterBit')
|
||||
m.return_value = True
|
||||
assert d.eiger_matrix_reset == True
|
||||
|
||||
def test_set_eiger_matrix_reset(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setCounterBit')
|
||||
d.eiger_matrix_reset = True
|
||||
m.assert_called_once_with(True)
|
||||
|
||||
|
||||
def test_get_exposure_time(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getExposureTime')
|
||||
m.return_value = 100000000
|
||||
assert d.exposure_time == 0.1
|
||||
|
||||
def test_set_exposure_time(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setExposureTime')
|
||||
d.exposure_time = 1.5
|
||||
m.assert_called_once_with(1500000000)
|
||||
|
||||
def test_set_exposure_time_less_than_zero(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setExposureTime')
|
||||
with pytest.raises(ValueError):
|
||||
d.exposure_time = -7
|
||||
|
||||
|
||||
def test_get_file_index(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getFileIndex')
|
||||
m.return_value = 8
|
||||
assert d.file_index == 8
|
||||
|
||||
def test_set_file_index(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setFileIndex')
|
||||
d.file_index = 9
|
||||
m.assert_called_with(9)
|
||||
|
||||
|
||||
def test_set_file_index_raises_on_neg(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setFileIndex')
|
||||
with pytest.raises(ValueError):
|
||||
d.file_index = -9
|
||||
|
||||
|
||||
def test_get_file_name(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getFileName')
|
||||
d.file_name
|
||||
m.assert_called_once_with()
|
||||
|
||||
def test_set_file_name(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setFileName')
|
||||
d.file_name = 'hej'
|
||||
m.assert_called_once_with('hej')
|
||||
|
||||
def test_get_file_path(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getFilePath')
|
||||
d.file_path
|
||||
m.assert_called_once_with()
|
||||
|
||||
def test_set_file_path_when_path_exists(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setFilePath')
|
||||
#To avoid raising an exception because path is not there
|
||||
mock_os = mocker.patch('os.path.exists')
|
||||
mock_os.return_value = True
|
||||
d.file_path = '/path/to/something/'
|
||||
m.assert_called_once_with('/path/to/something/')
|
||||
|
||||
def test_set_file_path_raises_when_not_exists(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setFilePath')
|
||||
mock_os = mocker.patch('os.path.exists')
|
||||
mock_os.return_value = False
|
||||
with pytest.raises(FileNotFoundError):
|
||||
d.file_path = '/path/to/something/'
|
||||
|
||||
def test_get_file_write(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getFileWrite')
|
||||
m.return_value = False
|
||||
assert d.file_write == False
|
||||
|
||||
def test_set_file_write(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setFileWrite')
|
||||
d.file_write = True
|
||||
m.assert_called_once_with(True)
|
||||
|
||||
|
||||
def test_get_firmware_version(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getFirmwareVersion')
|
||||
m.return_value = 20
|
||||
assert d.firmware_version == 20
|
||||
|
||||
def test_cannot_set_fw_version(d):
|
||||
with pytest.raises(AttributeError):
|
||||
d.firmware_version = 20
|
||||
|
||||
def test_get_high_voltage_call_signature(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getDac')
|
||||
d.high_voltage
|
||||
m.assert_called_once_with('vhighvoltage', -1)
|
||||
|
||||
def test_get_high_voltage(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getDac')
|
||||
m.return_value = 80
|
||||
assert d.high_voltage == 80
|
||||
|
||||
#self._api.setDac('vhighvoltage', -1, voltage)
|
||||
def test_set_high_voltage(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setDac')
|
||||
d.high_voltage = 80
|
||||
m.assert_called_once_with('vhighvoltage', -1, 80)
|
||||
|
||||
def test_decode_hostname_two_names(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getHostname')
|
||||
m.return_value = 'beb059+beb048+'
|
||||
assert d.hostname == ['beb059', 'beb048']
|
||||
|
||||
def test_decode_hostname_four_names(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getHostname')
|
||||
m.return_value = 'beb059+beb048+beb120+beb153+'
|
||||
assert d.hostname == ['beb059', 'beb048', 'beb120', 'beb153']
|
||||
|
||||
def test_decode_hostname_blank(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getHostname')
|
||||
m.return_value = ''
|
||||
assert d.hostname == []
|
||||
|
||||
def test_get_image_size_gives_correct_size(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getImageSize')
|
||||
m.return_value = (512,1024)
|
||||
im_size = d.image_size
|
||||
assert im_size.rows == 512
|
||||
assert im_size.cols == 1024
|
||||
|
||||
|
||||
|
||||
def test_load_config(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.readConfigurationFile')
|
||||
#To avoid raising an exception because path is not there
|
||||
mock_os = mocker.patch('os.path.isfile')
|
||||
mock_os.return_value = True
|
||||
d.load_config('/path/to/my/file.config')
|
||||
m.assert_called_once_with('/path/to/my/file.config')
|
||||
|
||||
def test_load_config_raises_when_file_is_not_found(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.readConfigurationFile')
|
||||
mock_os = mocker.patch('os.path.isfile')
|
||||
mock_os.return_value = False
|
||||
with pytest.raises(FileNotFoundError):
|
||||
d.load_config('/path/to/my/file.config')
|
||||
|
||||
def test_load_parameters(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.readParametersFile')
|
||||
#To avoid raising an exception because path is not there
|
||||
mock_os = mocker.patch('os.path.isfile')
|
||||
mock_os.return_value = True
|
||||
d.load_parameters('/path/to/my/file.par')
|
||||
m.assert_called_once_with('/path/to/my/file.par')
|
||||
|
||||
def test_load_parameters_raises_when_file_is_not_found(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.readParametersFile')
|
||||
mock_os = mocker.patch('os.path.isfile')
|
||||
mock_os.return_value = False
|
||||
with pytest.raises(FileNotFoundError):
|
||||
d.load_parameters('/path/to/my/file.par')
|
||||
|
||||
#getDetectorGeometry
|
||||
def test_get_module_geometry_gives_correct_size(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getDetectorGeometry')
|
||||
m.return_value = (13,7)
|
||||
g = d.module_geometry
|
||||
assert g.vertical == 7
|
||||
assert g.horizontal == 13
|
||||
|
||||
def test_get_module_geometry_access(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getDetectorGeometry')
|
||||
m.return_value = (12,3)
|
||||
assert d.module_geometry[0] == 12
|
||||
assert d.module_geometry[1] == 3
|
||||
assert d.module_geometry.vertical == 3
|
||||
assert d.module_geometry.horizontal == 12
|
||||
|
||||
def test_get_n_frames(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getNumberOfFrames')
|
||||
m.return_value = 3
|
||||
assert d.n_frames == 3
|
||||
|
||||
def test_set_n_frames(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setNumberOfFrames')
|
||||
d.n_frames = 9
|
||||
m.assert_called_once_with(9)
|
||||
|
||||
def test_set_n_frames_raises_on_neg(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setNumberOfFrames')
|
||||
with pytest.raises(ValueError):
|
||||
d.n_frames = -1
|
||||
|
||||
def test_set_n_frames_raises_on_zero(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setNumberOfFrames')
|
||||
with pytest.raises(ValueError):
|
||||
d.n_frames = 0
|
||||
|
||||
def test_get_n_modules(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getNumberOfDetectors')
|
||||
m.return_value = 12
|
||||
assert d.n_modules == 12
|
||||
|
||||
def test_get_period_time(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getPeriod')
|
||||
m.return_value = 130000000
|
||||
assert d.period == 0.13
|
||||
|
||||
def test_set_period_time(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setPeriod')
|
||||
d.period = 1.953
|
||||
m.assert_called_once_with(1953000000)
|
||||
|
||||
def test_set_period_time_less_than_zero(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setPeriod')
|
||||
with pytest.raises(ValueError):
|
||||
d.period = -7
|
||||
|
||||
def test_pulse_chip_call(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.pulseChip')
|
||||
d.pulse_chip(15)
|
||||
m.assert_called_once_with(15)
|
||||
|
||||
def test_pulse_chip_call_minus_one(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.pulseChip')
|
||||
d.pulse_chip(-1)
|
||||
m.assert_called_once_with(-1)
|
||||
|
||||
def test_pulse_chip_asserts_on_smaller_than_minus_one(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.pulseChip')
|
||||
with pytest.raises(ValueError):
|
||||
d.pulse_chip(-3)
|
||||
#--------------------------------------------------------------------subexptime
|
||||
def test_get_sub_exposure_time(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getSubExposureTime')
|
||||
m.return_value = 2370000
|
||||
assert d.sub_exposure_time == 0.00237
|
||||
|
||||
|
||||
def test_set_sub_exposure_time(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setSubExposureTime')
|
||||
d.sub_exposure_time = 0.002
|
||||
m.assert_called_once_with(2000000)
|
||||
|
||||
def test_set_sub_exposure_time_raises_on_zero(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setSubExposureTime')
|
||||
with pytest.raises(ValueError):
|
||||
d.sub_exposure_time = 0
|
||||
|
||||
#-------------------------------------------------------------Rate correction
|
||||
def test_get_rate_correction(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getRateCorrection')
|
||||
m.return_value = [132,129]
|
||||
assert d.rate_correction == [132,129]
|
||||
|
||||
def test_set_rate_correction(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setRateCorrection')
|
||||
mock_n = mocker.patch('_sls_detector.DetectorApi.getNumberOfDetectors')
|
||||
mock_n.return_value = 3
|
||||
d.rate_correction = [123,90,50]
|
||||
m.assert_called_once_with([123,90,50])
|
||||
|
||||
def test_set_rate_correction_raises_on_wrong_number_of_values(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setRateCorrection')
|
||||
mock_n = mocker.patch('_sls_detector.DetectorApi.getNumberOfDetectors')
|
||||
mock_n.return_value = 4
|
||||
with pytest.raises(ValueError):
|
||||
d.rate_correction = [123,90,50]
|
||||
|
||||
#----------------------------------------------------------------Readout clock
|
||||
def test_get_readout_clock_0(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getReadoutClockSpeed')
|
||||
m.return_value = 0
|
||||
assert d.readout_clock == 'Full Speed'
|
||||
|
||||
def test_get_readout_clock_1(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getReadoutClockSpeed')
|
||||
m.return_value = 1
|
||||
assert d.readout_clock == 'Half Speed'
|
||||
|
||||
def test_get_readout_clock_2(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getReadoutClockSpeed')
|
||||
m.return_value = 2
|
||||
assert d.readout_clock == 'Quarter Speed'
|
||||
|
||||
def test_get_readout_clock_3(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getReadoutClockSpeed')
|
||||
m.return_value = 3
|
||||
assert d.readout_clock == 'Super Slow Speed'
|
||||
|
||||
def test_set_readout_clock_0(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setReadoutClockSpeed')
|
||||
d.readout_clock = 'Full Speed'
|
||||
m.assert_called_once_with(0)
|
||||
|
||||
def test_set_readout_clock_1(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setReadoutClockSpeed')
|
||||
d.readout_clock = 'Half Speed'
|
||||
m.assert_called_once_with(1)
|
||||
|
||||
def test_set_readout_clock_2(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setReadoutClockSpeed')
|
||||
d.readout_clock = 'Quarter Speed'
|
||||
m.assert_called_once_with(2)
|
||||
|
||||
def test_set_readout_clock_3(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setReadoutClockSpeed')
|
||||
d.readout_clock = 'Super Slow Speed'
|
||||
m.assert_called_once_with(3)
|
||||
|
||||
#----------------------------------------------------------------rx_datastream
|
||||
def test_get_rx_datastream(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getRxDataStreamStatus')
|
||||
m.return_value = False
|
||||
assert d.rx_datastream == False
|
||||
|
||||
def test_set_rx_datastream(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setRxDataStreamStatus')
|
||||
d.rx_datastream = True
|
||||
m.assert_called_once_with(True)
|
||||
|
||||
def test_get_rx_zmqip(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getNetworkParameter')
|
||||
d.rx_zmqip
|
||||
m.assert_called_once_with('rx_zmqip')
|
||||
|
||||
def test_get_rx_zmqport_call(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getNetworkParameter')
|
||||
d.rx_zmqport
|
||||
m.assert_called_once_with('rx_zmqport')
|
||||
|
||||
def test_get_rx_zmqport_decode(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getNetworkParameter')
|
||||
m.return_value = '30001+30003+'
|
||||
assert d.rx_zmqport == [30001, 30002, 30003, 30004]
|
||||
|
||||
def test_get_rx_zmqport_empty(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getNetworkParameter')
|
||||
m.return_value = ''
|
||||
assert d.rx_zmqport == []
|
||||
|
||||
|
||||
#--------------------------------------------------------------------status
|
||||
def test_status_call(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getRunStatus')
|
||||
d.status
|
||||
m.assert_called_once_with()
|
||||
|
||||
def test_start_acq_call(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.startAcquisition')
|
||||
d.start_acq()
|
||||
m.assert_called_once_with()
|
||||
|
||||
def test_stop_acq_call(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.stopAcquisition')
|
||||
d.stop_acq()
|
||||
m.assert_called_once_with()
|
||||
|
||||
#--------------------------------------------------------------------subexptime
|
||||
def test_get_sub_exposure_time(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getSubExposureTime')
|
||||
m.return_value = 2370000
|
||||
assert d.sub_exposure_time == 0.00237
|
||||
|
||||
|
||||
def test_set_sub_exposure_time(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setSubExposureTime')
|
||||
d.sub_exposure_time = 0.002
|
||||
m.assert_called_once_with(2000000)
|
||||
|
||||
def test_set_sub_exposure_time_raises_on_zero(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setSubExposureTime')
|
||||
with pytest.raises(ValueError):
|
||||
d.sub_exposure_time = 0
|
||||
|
||||
#------------------------------------------------------------------timing mode
|
||||
def test_get_timing_mode(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getTimingMode')
|
||||
d.timing_mode
|
||||
m.assert_called_once_with()
|
||||
|
||||
def test_set_timing_mode(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setTimingMode')
|
||||
d.timing_mode = 'auto'
|
||||
m.assert_called_once_with('auto')
|
||||
|
||||
#----------------------------------------------------------------vthreshold
|
||||
def test_get_vthreshold(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getDac')
|
||||
d.vthreshold
|
||||
m.assert_called_once_with('vthreshold', -1)
|
||||
|
||||
def test_set_vthreshold(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setDac')
|
||||
d.vthreshold = 1675
|
||||
m.assert_called_once_with('vthreshold', -1, 1675)
|
||||
|
||||
#----------------------------------------------------------------trimbits
|
||||
def test_get_trimbits(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getAllTrimbits')
|
||||
d.trimbits
|
||||
m.assert_called_once_with()
|
||||
|
||||
def test_set_trimbits(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setAllTrimbits')
|
||||
d.trimbits = 15
|
||||
m.assert_called_once_with(15)
|
||||
|
||||
def test_set_trimbits_raises_outside_range(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setAllTrimbits')
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
d.trimbits = 69
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
d.trimbits = -5
|
||||
|
||||
|
472
python/unit-tests/detector_test.py
Executable file
472
python/unit-tests/detector_test.py
Executable file
@ -0,0 +1,472 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Testing parameters and methods of the Detector class using mocks
|
||||
"""
|
||||
from unittest.mock import Mock
|
||||
import pytest
|
||||
from pytest_mock import mocker
|
||||
|
||||
import sys
|
||||
sys.path.append('/home/l_frojdh/slsdetectorgrup/sls_detector')
|
||||
|
||||
import _sls_detector
|
||||
from sls_detector.errors import DetectorValueError, DetectorError
|
||||
from sls_detector.utils import all_equal, element_if_equal
|
||||
@pytest.fixture
|
||||
def d():
|
||||
from sls_detector import Detector
|
||||
return Detector()
|
||||
|
||||
|
||||
def test_length_zero(d):
|
||||
assert len(d) == 0
|
||||
|
||||
def test_acq_call(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.acq')
|
||||
d.acq()
|
||||
m.assert_called_once_with()
|
||||
|
||||
|
||||
|
||||
def test_busy_call(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getAcquiringFlag')
|
||||
m.return_value = False
|
||||
assert d.busy == False
|
||||
|
||||
def test_set_busy(d):
|
||||
d.busy = True
|
||||
assert d.busy == True
|
||||
assert d._api.getAcquiringFlag() == True
|
||||
d.busy = False
|
||||
assert d.busy == False
|
||||
assert d._api.getAcquiringFlag() == False
|
||||
|
||||
def test_error_mask(d):
|
||||
d._api.setErrorMask(1)
|
||||
assert d.error_mask == 1
|
||||
d.clear_errors()
|
||||
|
||||
def test_error_handling(d):
|
||||
with pytest.raises(DetectorError):
|
||||
d._provoke_error()
|
||||
|
||||
def test_assign_to_detector_type(d):
|
||||
with pytest.raises(AttributeError):
|
||||
d.detector_type = 'Eiger'
|
||||
|
||||
def test_det_type(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getDetectorType')
|
||||
m.return_value = 'Eiger'
|
||||
assert d.detector_type == 'Eiger'
|
||||
|
||||
|
||||
def test_get_exposure_time(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getExposureTime')
|
||||
m.return_value = 100000000
|
||||
assert d.exposure_time == 0.1
|
||||
|
||||
def test_set_exposure_time(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setExposureTime')
|
||||
d.exposure_time = 1.5
|
||||
m.assert_called_once_with(1500000000)
|
||||
|
||||
def test_set_exposure_time_less_than_zero(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setExposureTime')
|
||||
with pytest.raises(DetectorValueError):
|
||||
d.exposure_time = -7
|
||||
|
||||
|
||||
def test_get_file_index(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getFileIndex')
|
||||
m.return_value = 8
|
||||
assert d.file_index == 8
|
||||
|
||||
def test_set_file_index(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setFileIndex')
|
||||
d.file_index = 9
|
||||
m.assert_called_with(9)
|
||||
|
||||
|
||||
def file_index_with_no_detector(d):
|
||||
assert d.file_index == -100
|
||||
|
||||
def dr_with_no_detector(d):
|
||||
assert d.dynamic_range == -100
|
||||
|
||||
def test_set_file_index_raises_on_neg(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setFileIndex')
|
||||
with pytest.raises(ValueError):
|
||||
d.file_index = -9
|
||||
|
||||
|
||||
def test_get_file_name(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getFileName')
|
||||
d.file_name
|
||||
m.assert_called_once_with()
|
||||
|
||||
def test_set_file_name(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setFileName')
|
||||
d.file_name = 'hej'
|
||||
m.assert_called_once_with('hej')
|
||||
|
||||
def test_get_file_path(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getFilePath')
|
||||
d.file_path
|
||||
m.assert_called_once_with()
|
||||
|
||||
def test_set_file_path_when_path_exists(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setFilePath')
|
||||
#To avoid raising an exception because path is not there
|
||||
mock_os = mocker.patch('os.path.exists')
|
||||
mock_os.return_value = True
|
||||
d.file_path = '/path/to/something/'
|
||||
m.assert_called_once_with('/path/to/something/')
|
||||
|
||||
def test_set_file_path_raises_when_not_exists(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setFilePath')
|
||||
mock_os = mocker.patch('os.path.exists')
|
||||
mock_os.return_value = False
|
||||
with pytest.raises(FileNotFoundError):
|
||||
d.file_path = '/path/to/something/'
|
||||
|
||||
def test_get_file_write(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getFileWrite')
|
||||
m.return_value = False
|
||||
assert d.file_write == False
|
||||
|
||||
def test_set_file_write(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setFileWrite')
|
||||
d.file_write = True
|
||||
m.assert_called_once_with(True)
|
||||
|
||||
|
||||
def test_get_firmware_version(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getFirmwareVersion')
|
||||
m.return_value = 20
|
||||
assert d.firmware_version == 20
|
||||
|
||||
def test_cannot_set_fw_version(d):
|
||||
with pytest.raises(AttributeError):
|
||||
d.firmware_version = 20
|
||||
|
||||
def test_get_high_voltage_call_signature(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getDac')
|
||||
d.high_voltage
|
||||
m.assert_called_once_with('vhighvoltage', -1)
|
||||
|
||||
def test_get_high_voltage(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getDac')
|
||||
m.return_value = 80
|
||||
assert d.high_voltage == 80
|
||||
|
||||
#self._api.setDac('vhighvoltage', -1, voltage)
|
||||
def test_set_high_voltage(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setDac')
|
||||
d.high_voltage = 80
|
||||
m.assert_called_once_with('vhighvoltage', -1, 80)
|
||||
|
||||
def test_decode_hostname_two_names(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getHostname')
|
||||
m.return_value = 'beb059+beb048+'
|
||||
assert d.hostname == ['beb059', 'beb048']
|
||||
|
||||
def test_decode_hostname_four_names(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getHostname')
|
||||
m.return_value = 'beb059+beb048+beb120+beb153+'
|
||||
assert d.hostname == ['beb059', 'beb048', 'beb120', 'beb153']
|
||||
|
||||
def test_decode_hostname_blank(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getHostname')
|
||||
m.return_value = ''
|
||||
assert d.hostname == []
|
||||
|
||||
def test_get_image_size_gives_correct_size(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getImageSize')
|
||||
m.return_value = (512,1024)
|
||||
im_size = d.image_size
|
||||
assert im_size.rows == 512
|
||||
assert im_size.cols == 1024
|
||||
|
||||
|
||||
|
||||
def test_load_config(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.readConfigurationFile')
|
||||
#To avoid raising an exception because path is not there
|
||||
mock_os = mocker.patch('os.path.isfile')
|
||||
mock_os.return_value = True
|
||||
d.load_config('/path/to/my/file.config')
|
||||
m.assert_called_once_with('/path/to/my/file.config')
|
||||
|
||||
def test_load_config_raises_when_file_is_not_found(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.readConfigurationFile')
|
||||
mock_os = mocker.patch('os.path.isfile')
|
||||
mock_os.return_value = False
|
||||
with pytest.raises(FileNotFoundError):
|
||||
d.load_config('/path/to/my/file.config')
|
||||
|
||||
def test_load_parameters(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.readParametersFile')
|
||||
#To avoid raising an exception because path is not there
|
||||
mock_os = mocker.patch('os.path.isfile')
|
||||
mock_os.return_value = True
|
||||
d.load_parameters('/path/to/my/file.par')
|
||||
m.assert_called_once_with('/path/to/my/file.par')
|
||||
|
||||
def test_load_parameters_raises_when_file_is_not_found(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.readParametersFile')
|
||||
mock_os = mocker.patch('os.path.isfile')
|
||||
mock_os.return_value = False
|
||||
with pytest.raises(FileNotFoundError):
|
||||
d.load_parameters('/path/to/my/file.par')
|
||||
|
||||
#getDetectorGeometry
|
||||
def test_get_module_geometry_gives_correct_size(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getDetectorGeometry')
|
||||
m.return_value = (13,7)
|
||||
g = d.module_geometry
|
||||
assert g.vertical == 7
|
||||
assert g.horizontal == 13
|
||||
|
||||
def test_get_module_geometry_access(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getDetectorGeometry')
|
||||
m.return_value = (12,3)
|
||||
assert d.module_geometry[0] == 12
|
||||
assert d.module_geometry[1] == 3
|
||||
assert d.module_geometry.vertical == 3
|
||||
assert d.module_geometry.horizontal == 12
|
||||
|
||||
def test_module_geometry_without_detectors(d):
|
||||
t = d.module_geometry
|
||||
assert t.horizontal == 0
|
||||
assert t.vertical == 0
|
||||
|
||||
def test_get_n_frames(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getNumberOfFrames')
|
||||
m.return_value = 3
|
||||
assert d.n_frames == 3
|
||||
|
||||
def test_set_n_frames(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setNumberOfFrames')
|
||||
d.n_frames = 9
|
||||
m.assert_called_once_with(9)
|
||||
|
||||
def test_nframes_without_detector(d):
|
||||
assert d.n_frames == -100
|
||||
|
||||
def test_set_n_frames_raises_on_neg(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setNumberOfFrames')
|
||||
with pytest.raises(DetectorValueError):
|
||||
d.n_frames = -1
|
||||
|
||||
def test_set_n_frames_raises_on_zero(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setNumberOfFrames')
|
||||
with pytest.raises(DetectorValueError):
|
||||
d.n_frames = 0
|
||||
|
||||
def test_n_cycles_without_detector(d):
|
||||
assert d.n_cycles == -100
|
||||
|
||||
def test_set_n_cycles_raises_on_zero(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setCycles')
|
||||
with pytest.raises(DetectorValueError):
|
||||
d.n_cycles = 0
|
||||
|
||||
def test_set_n_cycles(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setCycles')
|
||||
d.n_cycles = 56
|
||||
m.assert_called_once_with(56)
|
||||
|
||||
|
||||
|
||||
def test_n_measurements_without_detector(d):
|
||||
assert d.n_measurements == -100
|
||||
|
||||
def test_set_n_measurements_raises_on_zero(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setNumberOfMeasurements')
|
||||
with pytest.raises(DetectorValueError):
|
||||
d.n_measurements = 0
|
||||
|
||||
def test_set_n_measurements(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setNumberOfMeasurements')
|
||||
d.n_measurements = 560
|
||||
m.assert_called_once_with(560)
|
||||
|
||||
def test_get_n_modules_no_detector(d):
|
||||
assert d.n_modules == 0
|
||||
|
||||
def test_get_n_modules(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getNumberOfDetectors')
|
||||
m.return_value = 12
|
||||
assert d.n_modules == 12
|
||||
|
||||
def test_get_period_time(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getPeriod')
|
||||
m.return_value = 130000000
|
||||
assert d.period == 0.13
|
||||
|
||||
def test_set_period_time(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setPeriod')
|
||||
d.period = 1.953
|
||||
m.assert_called_once_with(1953000000)
|
||||
|
||||
def test_set_period_time_less_than_zero(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setPeriod')
|
||||
with pytest.raises(ValueError):
|
||||
d.period = -7
|
||||
|
||||
|
||||
def test_get_online(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getOnline')
|
||||
d.online
|
||||
m.assert_called_once_with()
|
||||
|
||||
def test_set_online(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setOnline')
|
||||
d.online = True
|
||||
m.assert_called_once_with(True)
|
||||
|
||||
def test_last_client_ip_no_detector(d):
|
||||
assert d.last_client_ip == ''
|
||||
|
||||
def test_last_cliten_ip_call(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getLastClientIP')
|
||||
d.last_client_ip
|
||||
m.assert_called_once_with()
|
||||
|
||||
#-------------------------------------------------------------Rate correction
|
||||
def test_get_rate_correction(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getRateCorrection')
|
||||
m.return_value = [132,129]
|
||||
assert d.rate_correction == [132,129]
|
||||
|
||||
def test_set_rate_correction(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setRateCorrection')
|
||||
mock_n = mocker.patch('_sls_detector.DetectorApi.getNumberOfDetectors')
|
||||
mock_n.return_value = 3
|
||||
d.rate_correction = [123,90,50]
|
||||
m.assert_called_once_with([123,90,50])
|
||||
|
||||
def test_set_rate_correction_raises_on_wrong_number_of_values(d, mocker):
|
||||
mocker.patch('_sls_detector.DetectorApi.setRateCorrection')
|
||||
mock_n = mocker.patch('_sls_detector.DetectorApi.getNumberOfDetectors')
|
||||
mock_n.return_value = 4
|
||||
with pytest.raises(ValueError):
|
||||
d.rate_correction = [123,90,50]
|
||||
|
||||
#----------------------------------------------------------------Readout clock
|
||||
def test_get_readout_clock_0(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getReadoutClockSpeed')
|
||||
m.return_value = 0
|
||||
assert d.readout_clock == 'Full Speed'
|
||||
|
||||
def test_get_readout_clock_1(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getReadoutClockSpeed')
|
||||
m.return_value = 1
|
||||
assert d.readout_clock == 'Half Speed'
|
||||
|
||||
def test_get_readout_clock_2(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getReadoutClockSpeed')
|
||||
m.return_value = 2
|
||||
assert d.readout_clock == 'Quarter Speed'
|
||||
|
||||
def test_get_readout_clock_3(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getReadoutClockSpeed')
|
||||
m.return_value = 3
|
||||
assert d.readout_clock == 'Super Slow Speed'
|
||||
|
||||
def test_set_readout_clock_0(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setReadoutClockSpeed')
|
||||
d.readout_clock = 'Full Speed'
|
||||
m.assert_called_once_with(0)
|
||||
|
||||
def test_set_readout_clock_1(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setReadoutClockSpeed')
|
||||
d.readout_clock = 'Half Speed'
|
||||
m.assert_called_once_with(1)
|
||||
|
||||
def test_set_readout_clock_2(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setReadoutClockSpeed')
|
||||
d.readout_clock = 'Quarter Speed'
|
||||
m.assert_called_once_with(2)
|
||||
|
||||
def test_set_readout_clock_3(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setReadoutClockSpeed')
|
||||
d.readout_clock = 'Super Slow Speed'
|
||||
m.assert_called_once_with(3)
|
||||
|
||||
#----------------------------------------------------------------rx_datastream
|
||||
def test_get_rx_datastream(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getRxDataStreamStatus')
|
||||
m.return_value = False
|
||||
assert d.rx_datastream == False
|
||||
|
||||
def test_set_rx_datastream(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setRxDataStreamStatus')
|
||||
d.rx_datastream = True
|
||||
m.assert_called_once_with(True)
|
||||
|
||||
def test_get_rx_zmqip(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getNetworkParameter')
|
||||
d.rx_zmqip
|
||||
m.assert_called_once_with('rx_zmqip')
|
||||
|
||||
def test_get_rx_zmqport_call(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getNetworkParameter')
|
||||
d.rx_zmqport
|
||||
m.assert_called_once_with('rx_zmqport')
|
||||
|
||||
def test_get_rx_zmqport_decode(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getNetworkParameter')
|
||||
m.return_value = ['30001', '30003']
|
||||
assert d.rx_zmqport == [30001, 30003]
|
||||
|
||||
def test_get_rx_zmqport_empty(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getNetworkParameter')
|
||||
m.return_value = ''
|
||||
assert d.rx_zmqport == []
|
||||
|
||||
|
||||
#--------------------------------------------------------------------status
|
||||
def test_status_call(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getRunStatus')
|
||||
d.status
|
||||
m.assert_called_once_with()
|
||||
|
||||
def test_start_detecor(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.startAcquisition')
|
||||
d.start_detector()
|
||||
m.assert_called_once_with()
|
||||
|
||||
def test_stop_acq_call(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.stopAcquisition')
|
||||
d.stop_detector()
|
||||
m.assert_called_once_with()
|
||||
|
||||
|
||||
|
||||
#------------------------------------------------------------------timing mode
|
||||
def test_get_timing_mode(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getTimingMode')
|
||||
d.timing_mode
|
||||
m.assert_called_once_with()
|
||||
|
||||
def test_set_timing_mode(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setTimingMode')
|
||||
d.timing_mode = 'auto'
|
||||
m.assert_called_once_with('auto')
|
||||
|
||||
#----------------------------------------------------------------vthreshold
|
||||
def test_get_vthreshold(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.getDac')
|
||||
d.vthreshold
|
||||
m.assert_called_once_with('vthreshold', -1)
|
||||
|
||||
def test_set_vthreshold(d, mocker):
|
||||
m = mocker.patch('_sls_detector.DetectorApi.setDac')
|
||||
d.vthreshold = 1675
|
||||
m.assert_called_once_with('vthreshold', -1, 1675)
|
||||
|
||||
|
||||
|
||||
|
76
python/unit-tests/test_detector_property.py
Executable file
76
python/unit-tests/test_detector_property.py
Executable file
@ -0,0 +1,76 @@
|
||||
import pytest
|
||||
from sls_detector.detector_property import DetectorProperty
|
||||
|
||||
class Holder:
|
||||
"""
|
||||
This class does nothing except hold values
|
||||
for testing of the DetectorProperty class
|
||||
"""
|
||||
def __init__(self, N):
|
||||
self.values = [i for i in range(N)]
|
||||
def get(self, i):
|
||||
return self.values[i]
|
||||
def set(self, i,v):
|
||||
self.values[i] = v
|
||||
def nmod(self):
|
||||
return len(self.values)
|
||||
|
||||
@pytest.fixture
|
||||
def p():
|
||||
h = Holder(5)
|
||||
return DetectorProperty(h.get, h.set, h.nmod, 'prop')
|
||||
|
||||
def test_initialization():
|
||||
def getf(i):
|
||||
return 5
|
||||
def setf():
|
||||
return
|
||||
def nmod():
|
||||
return 3
|
||||
name = 'a property'
|
||||
p = DetectorProperty(getf, setf, nmod, name)
|
||||
assert p.get == getf
|
||||
assert p.set == setf
|
||||
assert p.get_nmod == nmod
|
||||
assert p.__name__ == name
|
||||
|
||||
def test_get_single_value(p):
|
||||
assert p[2] == 2
|
||||
|
||||
def test_get_all_values(p):
|
||||
assert p[:] == [0, 1, 2, 3, 4]
|
||||
|
||||
def test_get_values_by_iterable(p):
|
||||
vals = p[1,3]
|
||||
assert vals == [1,3]
|
||||
|
||||
def test_set_single_value(p):
|
||||
p[2] = 7
|
||||
assert p[:] == [0,1,7,3,4]
|
||||
|
||||
def test_set_all(p):
|
||||
p[:] = 10
|
||||
assert p[:] == [10,10,10,10,10]
|
||||
|
||||
def test_set_all_by_list(p):
|
||||
p[:] = [7,8,9,10,11]
|
||||
assert p[:] == [7,8,9,10,11]
|
||||
|
||||
def test_set_all_bool(p):
|
||||
p[:] = True
|
||||
assert p[:] == [True]*5
|
||||
|
||||
def test_set_by_iter(p):
|
||||
keys = [2,4]
|
||||
vals = [18,23]
|
||||
p[keys] = vals
|
||||
assert p[:] == [0,1,18,3,23]
|
||||
|
||||
def test_set_by_iter_single_val(p):
|
||||
keys = [2,4]
|
||||
val = 9
|
||||
p[keys] = val
|
||||
assert p[:] == [0,1,9,3,9]
|
||||
|
||||
def test_print_values(p):
|
||||
assert repr(p) == 'prop: [0, 1, 2, 3, 4]'
|
50
python/unit-tests/test_utils.py
Executable file
50
python/unit-tests/test_utils.py
Executable file
@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Testing parameters and methods of the Detector class using mocks
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from sls_detector.utils import eiger_register_to_time
|
||||
from sls_detector.utils import all_equal, element_if_equal
|
||||
|
||||
def test_convert_zero():
|
||||
assert eiger_register_to_time(0) == 0
|
||||
|
||||
def test_convert_smallest_unit():
|
||||
assert pytest.approx(eiger_register_to_time(0b1000), 1e-9) == 1e-8
|
||||
|
||||
def test_convert_second_smallest_unit():
|
||||
assert pytest.approx(eiger_register_to_time(0b10000), 1e-9) == 2e-8
|
||||
|
||||
def test_convert_one_ms_using_exponent():
|
||||
assert pytest.approx(eiger_register_to_time(0b1101), 1e-9) == 1e-3
|
||||
|
||||
def test_convert_five_seconds():
|
||||
assert pytest.approx(eiger_register_to_time(0b1001110001000101), 1e-9) == 5.0
|
||||
|
||||
def test_all_equal_int():
|
||||
assert all_equal([5,5]) == True
|
||||
|
||||
def test_all_equal_fails():
|
||||
assert all_equal([5,6]) == False
|
||||
|
||||
def test_all_equal_tuple():
|
||||
assert all_equal(('a', 'a', 'a')) == True
|
||||
|
||||
def test_all_equal_str():
|
||||
assert all_equal('aaa') == True
|
||||
|
||||
def test_all_equal_str_fails():
|
||||
assert all_equal('aaab') == False
|
||||
|
||||
|
||||
|
||||
def test_element_if_equal_int():
|
||||
assert element_if_equal([5,5]) == 5
|
||||
|
||||
def test_element_if_equal_str():
|
||||
assert element_if_equal('hhh') == 'h'
|
||||
|
||||
def test_element_if_equal_int_fails():
|
||||
assert element_if_equal([5, 6, 7]) == [5, 6, 7]
|
Reference in New Issue
Block a user