diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/COPYING b/COPYING old mode 100644 new mode 100755 index 818433ec..94a9ed02 --- a/COPYING +++ b/COPYING @@ -1,674 +1,674 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - 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. - - - Copyright (C) - - 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 . - -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: - - Copyright (C) - 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 -. - - 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 -. + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/DSView/CMake/FindFFTW.cmake b/DSView/CMake/FindFFTW.cmake old mode 100644 new mode 100755 diff --git a/DSView/CMake/Findlibusb-1.0.cmake b/DSView/CMake/Findlibusb-1.0.cmake old mode 100644 new mode 100755 diff --git a/DSView/CMake/Findlibzip.cmake b/DSView/CMake/Findlibzip.cmake old mode 100644 new mode 100755 diff --git a/DSView/CMakeLists.txt b/DSView/CMakeLists.txt old mode 100644 new mode 100755 index c1c33e10..6e70b639 --- a/DSView/CMakeLists.txt +++ b/DSView/CMakeLists.txt @@ -104,9 +104,9 @@ find_package(FFTW REQUIRED) set(DS_TITLE DSView) set(DS_DESCRIPTION "A GUI for instruments of DreamSourceLab") -set(DS_VERSION_MAJOR 0) -set(DS_VERSION_MINOR 9) -set(DS_VERSION_MICRO 9) +set(DS_VERSION_MAJOR 1) +set(DS_VERSION_MINOR 0) +set(DS_VERSION_MICRO 0) set(DS_VERSION_STRING ${DS_VERSION_MAJOR}.${DS_VERSION_MINOR}.${DS_VERSION_MICRO} ) @@ -121,95 +121,102 @@ configure_file ( #------------------------------------------------------------------------------- set(DSView_SOURCES - main.cpp - pv/sigsession.cpp - pv/mainwindow.cpp - pv/devicemanager.cpp - pv/data/snapshot.cpp - pv/data/signaldata.cpp - pv/data/logicsnapshot.cpp - pv/data/logic.cpp - pv/data/analogsnapshot.cpp - pv/data/analog.cpp - pv/dialogs/deviceoptions.cpp - pv/prop/property.cpp - pv/prop/int.cpp - pv/prop/enum.cpp - pv/prop/double.cpp - pv/prop/bool.cpp - pv/prop/binding/binding.cpp - pv/toolbars/samplingbar.cpp - pv/view/viewport.cpp - pv/view/view.cpp - pv/view/timemarker.cpp - pv/view/signal.cpp - pv/view/ruler.cpp - pv/view/logicsignal.cpp - pv/view/header.cpp - pv/view/cursor.cpp - pv/view/analogsignal.cpp - pv/prop/binding/deviceoptions.cpp - pv/toolbars/trigbar.cpp - pv/toolbars/filebar.cpp - pv/dock/protocoldock.cpp - pv/dock/triggerdock.cpp - pv/dock/measuredock.cpp - pv/dock/searchdock.cpp - pv/toolbars/logobar.cpp - pv/data/groupsnapshot.cpp - pv/view/groupsignal.cpp - pv/data/group.cpp - pv/dialogs/about.cpp - pv/dialogs/search.cpp - pv/data/dsosnapshot.cpp + main.cpp + pv/sigsession.cpp + pv/mainwindow.cpp + pv/devicemanager.cpp + pv/data/snapshot.cpp + pv/data/signaldata.cpp + pv/data/logicsnapshot.cpp + pv/data/logic.cpp + pv/data/analogsnapshot.cpp + pv/data/analog.cpp + pv/dialogs/deviceoptions.cpp + pv/prop/property.cpp + pv/prop/int.cpp + pv/prop/enum.cpp + pv/prop/double.cpp + pv/prop/bool.cpp + pv/prop/binding/binding.cpp + pv/toolbars/samplingbar.cpp + pv/view/viewport.cpp + pv/view/view.cpp + pv/view/timemarker.cpp + pv/view/signal.cpp + pv/view/ruler.cpp + pv/view/logicsignal.cpp + pv/view/header.cpp + pv/view/cursor.cpp + pv/view/analogsignal.cpp + pv/prop/binding/deviceoptions.cpp + pv/toolbars/trigbar.cpp + pv/toolbars/filebar.cpp + pv/dock/protocoldock.cpp + pv/dock/triggerdock.cpp + pv/dock/measuredock.cpp + pv/dock/searchdock.cpp + pv/toolbars/logobar.cpp + pv/data/groupsnapshot.cpp + pv/view/groupsignal.cpp + pv/data/group.cpp + pv/dialogs/about.cpp + pv/dialogs/search.cpp + pv/data/dsosnapshot.cpp pv/data/dso.cpp - pv/view/dsosignal.cpp - pv/view/dsldial.cpp - pv/dock/dsotriggerdock.cpp - pv/view/trace.cpp - pv/view/selectableitem.cpp - pv/data/decoderstack.cpp - pv/data/decode/rowdata.cpp - pv/data/decode/row.cpp - pv/data/decode/decoder.cpp - pv/data/decode/annotation.cpp - pv/view/decodetrace.cpp - pv/prop/binding/decoderoptions.cpp - pv/widgets/fakelineedit.cpp - pv/widgets/decodermenu.cpp - pv/widgets/decodergroupbox.cpp - pv/prop/string.cpp - pv/device/sessionfile.cpp - pv/device/inputfile.cpp - pv/device/file.cpp - pv/device/devinst.cpp - pv/dialogs/storeprogress.cpp - pv/storesession.cpp - pv/view/devmode.cpp - pv/device/device.cpp - pv/dialogs/waitingdialog.cpp - pv/dialogs/dsomeasure.cpp - pv/dialogs/calibration.cpp - pv/data/decodermodel.cpp - pv/dialogs/protocollist.cpp - pv/dialogs/protocolexp.cpp - pv/dialogs/fftoptions.cpp - pv/data/mathstack.cpp - pv/view/mathtrace.cpp - dsapplication.cpp - pv/widgets/viewstatus.cpp - pv/toolbars/titlebar.cpp - pv/mainframe.cpp - pv/widgets/border.cpp - pv/dialogs/dsmessagebox.cpp - pv/dialogs/shadow.cpp + pv/view/dsosignal.cpp + pv/view/dsldial.cpp + pv/dock/dsotriggerdock.cpp + pv/view/trace.cpp + pv/view/selectableitem.cpp + pv/data/decoderstack.cpp + pv/data/decode/rowdata.cpp + pv/data/decode/row.cpp + pv/data/decode/decoder.cpp + pv/data/decode/annotation.cpp + pv/view/decodetrace.cpp + pv/prop/binding/decoderoptions.cpp + pv/widgets/fakelineedit.cpp + pv/widgets/decodermenu.cpp + pv/widgets/decodergroupbox.cpp + pv/prop/string.cpp + pv/device/sessionfile.cpp + pv/device/inputfile.cpp + pv/device/file.cpp + pv/device/devinst.cpp + pv/dialogs/storeprogress.cpp + pv/storesession.cpp + pv/view/devmode.cpp + pv/device/device.cpp + pv/dialogs/waitingdialog.cpp + pv/dialogs/dsomeasure.cpp + pv/dialogs/calibration.cpp + pv/data/decodermodel.cpp + pv/dialogs/protocollist.cpp + pv/dialogs/protocolexp.cpp + pv/dialogs/fftoptions.cpp + pv/data/mathstack.cpp + pv/view/mathtrace.cpp + dsapplication.cpp + pv/toolbars/titlebar.cpp + pv/mainframe.cpp + pv/widgets/border.cpp + pv/dialogs/dsmessagebox.cpp + pv/dialogs/shadow.cpp pv/dialogs/dsdialog.cpp pv/dialogs/interval.cpp pv/prop/binding/probeoptions.cpp + pv/view/viewstatus.cpp + pv/dialogs/lissajousoptions.cpp + pv/view/lissajoustrace.cpp + pv/view/spectrumtrace.cpp + pv/data/spectrumstack.cpp + pv/dialogs/mathoptions.cpp + pv/dialogs/regionoptions.cpp + pv/view/xcursor.cpp ) set(DSView_HEADERS - pv/sigsession.h + pv/sigsession.h pv/mainwindow.h pv/dialogs/deviceoptions.h pv/prop/property.h @@ -254,7 +261,7 @@ set(DSView_HEADERS pv/dialogs/fftoptions.h pv/data/mathstack.h pv/view/mathtrace.h - pv/widgets/viewstatus.h + pv/view/viewstatus.h pv/toolbars/titlebar.h pv/mainframe.h pv/widgets/border.h @@ -262,6 +269,17 @@ set(DSView_HEADERS pv/dialogs/shadow.h pv/dialogs/dsdialog.h pv/dialogs/interval.h + pv/dialogs/lissajousoptions.h + pv/view/lissajoustrace.h + pv/view/spectrumtrace.h + pv/data/spectrumstack.h + pv/dialogs/mathoptions.h + pv/dialogs/regionoptions.h + pv/view/xcursor.h + pv/view/signal.h + pv/view/logicsignal.h + pv/view/analogsignal.h + pv/view/dsosignal.h ) set(DSView_FORMS @@ -269,7 +287,8 @@ set(DSView_FORMS set(DSView_RESOURCES DSView.qrc - darkstyle/style.qrc + themes/breeze.qrc + languages/language.qrc ) if(ENABLE_DECODE) @@ -401,8 +420,10 @@ set_target_properties(${PROJECT_NAME} PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_ install(TARGETS ${PROJECT_NAME} DESTINATION bin/) install(DIRECTORY res DESTINATION share/${PROJECT_NAME}) install(FILES icons/logo.png DESTINATION share/${PROJECT_NAME} RENAME logo.png) -install(FILES ../NEWS DESTINATION share/${PROJECT_NAME} RENAME NEWS) -install(FILES ../ug.pdf DESTINATION share/${PROJECT_NAME} RENAME ug.pdf) +install(FILES ../NEWS25 DESTINATION share/${PROJECT_NAME} RENAME NEWS25) +install(FILES ../NEWS31 DESTINATION share/${PROJECT_NAME} RENAME NEWS31) +install(FILES ../ug25.pdf DESTINATION share/${PROJECT_NAME} RENAME ug25.pdf) +install(FILES ../ug31.pdf DESTINATION share/${PROJECT_NAME} RENAME ug31.pdf) install(FILES DreamSourceLab.rules DESTINATION /etc/udev/rules.d/) install(FILES DSView.desktop DESTINATION /usr/share/applications/) diff --git a/DSView/COPYING b/DSView/COPYING old mode 100644 new mode 100755 diff --git a/DSView/DSView.desktop b/DSView/DSView.desktop old mode 100644 new mode 100755 diff --git a/DSView/DSView.qrc b/DSView/DSView.qrc old mode 100644 new mode 100755 index 54259634..a50ef6b9 --- a/DSView/DSView.qrc +++ b/DSView/DSView.qrc @@ -1,71 +1,137 @@ - icons/search.png - icons/next.png - icons/pre.png - icons/file.png - icons/save.png - icons/open.png - icons/params.png - stylesheet.qss - icons/down-arrow.png - icons/slider-handle.png - icons/trigger.png - icons/measure.png - icons/search-bar.png - icons/protocol.png - icons/logo_noColor.png - icons/logo_color.png - icons/capture.png - icons/stop.png - icons/start.png - icons/dsl_logo.png - icons/logo.png - icons/hidden.png - icons/shown.png - icons/instant.png - icons/trigger_dis.png - icons/file_dis.png - icons/measure_dis.png - icons/protocol_dis.png - icons/search-bar_dis.png - icons/params_dis.png - icons/gear.png - icons/wait.gif - icons/instant_dis.png - icons/start_dis.png - icons/settings.png - darkstyle/style.qss - icons/export.png - icons/single.png - icons/single_dis.png - icons/math.png - icons/math_dis.png - icons/fft.png icons/Blackman.png + icons/dsl_logo.png icons/Flat_top.png icons/Hamming.png icons/Hann.png + icons/logo.png icons/Rectangle.png - icons/close.png - icons/maximize.png - icons/minimize.png - icons/restore.png - icons/nav.png - icons/oneloop.png - icons/repeat.png - icons/moder.png - icons/moder_dis.png - icons/modes.png - icons/modes_dis.png - icons/add.png - icons/del.png - icons/add_dis.png - icons/del_dis.png - icons/about.png - icons/manual.png - icons/bug.png - icons/support.png - icons/showDoc.png + icons/search.png + icons/dark/about.png + icons/dark/add.png + icons/dark/bug.png + icons/dark/capture.png + icons/dark/close.png + icons/dark/dark.png + icons/dark/del.png + icons/dark/display.png + icons/dark/down-arrow.png + icons/dark/export.png + icons/dark/fft.png + icons/dark/file.png + icons/dark/gear.png + icons/dark/hidden.png + icons/dark/instant.png + icons/dark/light.png + icons/dark/logo_color.png + icons/dark/logo_noColor.png + icons/dark/manual.png + icons/dark/maximize.png + icons/dark/measure.png + icons/dark/minimize.png + icons/dark/moder.png + icons/dark/modes.png + icons/dark/nav.png + icons/dark/next.png + icons/dark/oneloop.png + icons/dark/open.png + icons/dark/params.png + icons/dark/pre.png + icons/dark/protocol.png + icons/dark/repeat.png + icons/dark/restore.png + icons/dark/save.png + icons/dark/search.png + icons/dark/search-bar.png + icons/dark/shown.png + icons/dark/single.png + icons/dark/start.png + icons/dark/stop.png + icons/dark/support.png + icons/dark/trigger.png + icons/light/about.png + icons/light/add.png + icons/light/bug.png + icons/light/capture.png + icons/light/close.png + icons/light/dark.png + icons/light/del.png + icons/light/display.png + icons/light/down-arrow.png + icons/light/export.png + icons/light/fft.png + icons/light/file.png + icons/light/gear.png + icons/light/hidden.png + icons/light/instant.png + icons/light/light.png + icons/light/logo_color.png + icons/light/logo_noColor.png + icons/light/manual.png + icons/light/maximize.png + icons/light/measure.png + icons/light/minimize.png + icons/light/moder.png + icons/light/modes.png + icons/light/nav.png + icons/light/next.png + icons/light/oneloop.png + icons/light/open.png + icons/light/params.png + icons/light/pre.png + icons/light/protocol.png + icons/light/repeat.png + icons/light/restore.png + icons/light/save.png + icons/light/search.png + icons/light/search-bar.png + icons/light/shown.png + icons/light/single.png + icons/light/start.png + icons/light/stop.png + icons/light/support.png + icons/light/trigger.png + icons/mAmplitude.png + icons/mBurst.png + icons/mDelay.png + icons/mFall.png + icons/mFreq.png + icons/mHigh.png + icons/mLow.png + icons/mMax.png + icons/mMean.png + icons/mMin.png + icons/mNduty.png + icons/mNover.png + icons/mNwidth.png + icons/mPcount.png + icons/mPduty.png + icons/mPeriod.png + icons/mPover.png + icons/mPwidth.png + icons/mRise.png + icons/mRms.png + icons/mVpp.png + icons/light/lissajous.png + icons/dark/lissajous.png + icons/light/function.png + icons/dark/function.png + icons/light/math.png + icons/dark/math.png + icons/math.png + icons/Chinese.png + icons/English.png + icons/lissajous.png + icons/light/wait.gif + icons/dark/wait.gif + icons/light/daq.png + icons/light/la.png + icons/light/osc.png + icons/dark/daq.png + icons/dark/la.png + icons/dark/osc.png + icons/showDoc25.png + icons/showDoc31.png diff --git a/DSView/DreamSourceLab.rules b/DSView/DreamSourceLab.rules old mode 100644 new mode 100755 diff --git a/DSView/INSTALL b/DSView/INSTALL old mode 100644 new mode 100755 index 9b20e3a5..bb41a599 --- a/DSView/INSTALL +++ b/DSView/INSTALL @@ -1,49 +1,49 @@ -------------------------------------------------------------------------------- -INSTALL -------------------------------------------------------------------------------- - -Requirements ------------- - - - git - - g++ - - make - - libtool - - pkg-config >= 0.22 - - cmake >= 2.6 - - libglib >= 2.28.0 - - Qt >= 4.5 - - libboost >= 1.42 (including the following libs): - - libboost-system - - libboost-thread - - libsigrok4DSL >= 0.2.0 - - -Building and installing ------------------------ -Get the DSView source code from: www.dreamsourcelab.com/download.html -In order to build it, run: - - $ cd DSView - $ cmake . - $ make - -For installing PulseView: - - $ make install - -See the following wiki page for more (OS-specific) instructions: - - http://sigrok.org/wiki/Building - - -Creating a source distribution package --------------------------------------- - -In order to build a source package begin with an unconfigured source tree. - - $ mkdir dist - $ cd dist - $ cmake .. - $ make package_source - +------------------------------------------------------------------------------- +INSTALL +------------------------------------------------------------------------------- + +Requirements +------------ + + - git + - g++ + - make + - libtool + - pkg-config >= 0.22 + - cmake >= 2.6 + - libglib >= 2.28.0 + - Qt >= 4.5 + - libboost >= 1.42 (including the following libs): + - libboost-system + - libboost-thread + - libsigrok4DSL >= 0.2.0 + + +Building and installing +----------------------- +Get the DSView source code from: www.dreamsourcelab.com/download.html +In order to build it, run: + + $ cd DSView + $ cmake . + $ make + +For installing PulseView: + + $ make install + +See the following wiki page for more (OS-specific) instructions: + + http://sigrok.org/wiki/Building + + +Creating a source distribution package +-------------------------------------- + +In order to build a source package begin with an unconfigured source tree. + + $ mkdir dist + $ cd dist + $ cmake .. + $ make package_source + diff --git a/DSView/NEWS b/DSView/NEWS old mode 100644 new mode 100755 index e7c2c79c..dbbe9709 --- a/DSView/NEWS +++ b/DSView/NEWS @@ -1,5 +1,5 @@ -0.1.0 (2013-12-15) ------------------- - - * Initial release. - +0.1.0 (2013-12-15) +------------------ + + * Initial release. + diff --git a/DSView/README b/DSView/README old mode 100644 new mode 100755 diff --git a/DSView/config.h.in b/DSView/config.h.in old mode 100644 new mode 100755 diff --git a/DSView/darkstyle/rc/branch_closed-on.png b/DSView/darkstyle/rc/branch_closed-on.png deleted file mode 100755 index d081e9b3..00000000 Binary files a/DSView/darkstyle/rc/branch_closed-on.png and /dev/null differ diff --git a/DSView/darkstyle/rc/branch_closed.png b/DSView/darkstyle/rc/branch_closed.png deleted file mode 100755 index d652159a..00000000 Binary files a/DSView/darkstyle/rc/branch_closed.png and /dev/null differ diff --git a/DSView/darkstyle/rc/branch_open-on.png b/DSView/darkstyle/rc/branch_open-on.png deleted file mode 100755 index ec372b27..00000000 Binary files a/DSView/darkstyle/rc/branch_open-on.png and /dev/null differ diff --git a/DSView/darkstyle/rc/branch_open.png b/DSView/darkstyle/rc/branch_open.png deleted file mode 100755 index 66f8e1ac..00000000 Binary files a/DSView/darkstyle/rc/branch_open.png and /dev/null differ diff --git a/DSView/darkstyle/rc/checkbox_checked.png b/DSView/darkstyle/rc/checkbox_checked.png deleted file mode 100755 index 830cfee6..00000000 Binary files a/DSView/darkstyle/rc/checkbox_checked.png and /dev/null differ diff --git a/DSView/darkstyle/rc/checkbox_checked_disabled.png b/DSView/darkstyle/rc/checkbox_checked_disabled.png deleted file mode 100755 index cb63cc2f..00000000 Binary files a/DSView/darkstyle/rc/checkbox_checked_disabled.png and /dev/null differ diff --git a/DSView/darkstyle/rc/checkbox_checked_focus.png b/DSView/darkstyle/rc/checkbox_checked_focus.png deleted file mode 100755 index 3cf0e540..00000000 Binary files a/DSView/darkstyle/rc/checkbox_checked_focus.png and /dev/null differ diff --git a/DSView/darkstyle/rc/checkbox_indeterminate.png b/DSView/darkstyle/rc/checkbox_indeterminate.png deleted file mode 100755 index 41024f76..00000000 Binary files a/DSView/darkstyle/rc/checkbox_indeterminate.png and /dev/null differ diff --git a/DSView/darkstyle/rc/checkbox_indeterminate_disabled.png b/DSView/darkstyle/rc/checkbox_indeterminate_disabled.png deleted file mode 100755 index abdc01d9..00000000 Binary files a/DSView/darkstyle/rc/checkbox_indeterminate_disabled.png and /dev/null differ diff --git a/DSView/darkstyle/rc/checkbox_indeterminate_focus.png b/DSView/darkstyle/rc/checkbox_indeterminate_focus.png deleted file mode 100755 index a9a16f7e..00000000 Binary files a/DSView/darkstyle/rc/checkbox_indeterminate_focus.png and /dev/null differ diff --git a/DSView/darkstyle/rc/checkbox_unchecked.png b/DSView/darkstyle/rc/checkbox_unchecked.png deleted file mode 100755 index 2159aca9..00000000 Binary files a/DSView/darkstyle/rc/checkbox_unchecked.png and /dev/null differ diff --git a/DSView/darkstyle/rc/checkbox_unchecked_disabled.png b/DSView/darkstyle/rc/checkbox_unchecked_disabled.png deleted file mode 100755 index ade721e8..00000000 Binary files a/DSView/darkstyle/rc/checkbox_unchecked_disabled.png and /dev/null differ diff --git a/DSView/darkstyle/rc/checkbox_unchecked_focus.png b/DSView/darkstyle/rc/checkbox_unchecked_focus.png deleted file mode 100755 index 66f5bf56..00000000 Binary files a/DSView/darkstyle/rc/checkbox_unchecked_focus.png and /dev/null differ diff --git a/DSView/darkstyle/rc/close-hover.png b/DSView/darkstyle/rc/close-hover.png deleted file mode 100755 index 657943a6..00000000 Binary files a/DSView/darkstyle/rc/close-hover.png and /dev/null differ diff --git a/DSView/darkstyle/rc/close-pressed.png b/DSView/darkstyle/rc/close-pressed.png deleted file mode 100755 index 937d0059..00000000 Binary files a/DSView/darkstyle/rc/close-pressed.png and /dev/null differ diff --git a/DSView/darkstyle/rc/close.png b/DSView/darkstyle/rc/close.png deleted file mode 100755 index bc0f5761..00000000 Binary files a/DSView/darkstyle/rc/close.png and /dev/null differ diff --git a/DSView/darkstyle/rc/down_arrow.png b/DSView/darkstyle/rc/down_arrow.png deleted file mode 100755 index e271f7f9..00000000 Binary files a/DSView/darkstyle/rc/down_arrow.png and /dev/null differ diff --git a/DSView/darkstyle/rc/down_arrow_disabled.png b/DSView/darkstyle/rc/down_arrow_disabled.png deleted file mode 100755 index 5805d984..00000000 Binary files a/DSView/darkstyle/rc/down_arrow_disabled.png and /dev/null differ diff --git a/DSView/darkstyle/rc/left_arrow.png b/DSView/darkstyle/rc/left_arrow.png deleted file mode 100755 index f808d2d7..00000000 Binary files a/DSView/darkstyle/rc/left_arrow.png and /dev/null differ diff --git a/DSView/darkstyle/rc/left_arrow_disabled.png b/DSView/darkstyle/rc/left_arrow_disabled.png deleted file mode 100755 index f5b9af8a..00000000 Binary files a/DSView/darkstyle/rc/left_arrow_disabled.png and /dev/null differ diff --git a/DSView/darkstyle/rc/radio_checked.png b/DSView/darkstyle/rc/radio_checked.png deleted file mode 100755 index 235e6b0b..00000000 Binary files a/DSView/darkstyle/rc/radio_checked.png and /dev/null differ diff --git a/DSView/darkstyle/rc/radio_checked_disabled.png b/DSView/darkstyle/rc/radio_checked_disabled.png deleted file mode 100755 index bf0051ed..00000000 Binary files a/DSView/darkstyle/rc/radio_checked_disabled.png and /dev/null differ diff --git a/DSView/darkstyle/rc/radio_checked_focus.png b/DSView/darkstyle/rc/radio_checked_focus.png deleted file mode 100755 index 14b1cb1c..00000000 Binary files a/DSView/darkstyle/rc/radio_checked_focus.png and /dev/null differ diff --git a/DSView/darkstyle/rc/radio_unchecked.png b/DSView/darkstyle/rc/radio_unchecked.png deleted file mode 100755 index 9a4def65..00000000 Binary files a/DSView/darkstyle/rc/radio_unchecked.png and /dev/null differ diff --git a/DSView/darkstyle/rc/radio_unchecked_disabled.png b/DSView/darkstyle/rc/radio_unchecked_disabled.png deleted file mode 100755 index 6ece890e..00000000 Binary files a/DSView/darkstyle/rc/radio_unchecked_disabled.png and /dev/null differ diff --git a/DSView/darkstyle/rc/radio_unchecked_focus.png b/DSView/darkstyle/rc/radio_unchecked_focus.png deleted file mode 100755 index 27af8112..00000000 Binary files a/DSView/darkstyle/rc/radio_unchecked_focus.png and /dev/null differ diff --git a/DSView/darkstyle/rc/right_arrow.png b/DSView/darkstyle/rc/right_arrow.png deleted file mode 100755 index 9b0a4e6a..00000000 Binary files a/DSView/darkstyle/rc/right_arrow.png and /dev/null differ diff --git a/DSView/darkstyle/rc/right_arrow_disabled.png b/DSView/darkstyle/rc/right_arrow_disabled.png deleted file mode 100755 index 5c0bee40..00000000 Binary files a/DSView/darkstyle/rc/right_arrow_disabled.png and /dev/null differ diff --git a/DSView/darkstyle/rc/sizegrip.png b/DSView/darkstyle/rc/sizegrip.png deleted file mode 100755 index 350583aa..00000000 Binary files a/DSView/darkstyle/rc/sizegrip.png and /dev/null differ diff --git a/DSView/darkstyle/rc/stylesheet-branch-end.png b/DSView/darkstyle/rc/stylesheet-branch-end.png deleted file mode 100755 index cb5d3b51..00000000 Binary files a/DSView/darkstyle/rc/stylesheet-branch-end.png and /dev/null differ diff --git a/DSView/darkstyle/rc/stylesheet-branch-more.png b/DSView/darkstyle/rc/stylesheet-branch-more.png deleted file mode 100755 index 62711409..00000000 Binary files a/DSView/darkstyle/rc/stylesheet-branch-more.png and /dev/null differ diff --git a/DSView/darkstyle/rc/stylesheet-vline.png b/DSView/darkstyle/rc/stylesheet-vline.png deleted file mode 100755 index 87536cce..00000000 Binary files a/DSView/darkstyle/rc/stylesheet-vline.png and /dev/null differ diff --git a/DSView/darkstyle/rc/transparent.png b/DSView/darkstyle/rc/transparent.png deleted file mode 100755 index 483df251..00000000 Binary files a/DSView/darkstyle/rc/transparent.png and /dev/null differ diff --git a/DSView/darkstyle/rc/undock.png b/DSView/darkstyle/rc/undock.png deleted file mode 100755 index 88691d77..00000000 Binary files a/DSView/darkstyle/rc/undock.png and /dev/null differ diff --git a/DSView/darkstyle/rc/up_arrow.png b/DSView/darkstyle/rc/up_arrow.png deleted file mode 100755 index abcc7245..00000000 Binary files a/DSView/darkstyle/rc/up_arrow.png and /dev/null differ diff --git a/DSView/darkstyle/rc/up_arrow_disabled.png b/DSView/darkstyle/rc/up_arrow_disabled.png deleted file mode 100755 index b9c8e3b5..00000000 Binary files a/DSView/darkstyle/rc/up_arrow_disabled.png and /dev/null differ diff --git a/DSView/darkstyle/style.qrc b/DSView/darkstyle/style.qrc deleted file mode 100755 index 055e402f..00000000 --- a/DSView/darkstyle/style.qrc +++ /dev/null @@ -1,42 +0,0 @@ - - - rc/up_arrow_disabled.png - rc/stylesheet-branch-end.png - rc/branch_closed-on.png - rc/stylesheet-vline.png - rc/branch_closed.png - rc/branch_open-on.png - rc/transparent.png - rc/right_arrow_disabled.png - rc/sizegrip.png - rc/close.png - rc/close-hover.png - rc/close-pressed.png - rc/down_arrow.png - rc/left_arrow.png - rc/stylesheet-branch-more.png - rc/up_arrow.png - rc/right_arrow.png - rc/left_arrow_disabled.png - rc/branch_open.png - rc/down_arrow_disabled.png - rc/undock.png - rc/checkbox_checked_disabled.png - rc/checkbox_checked_focus.png - rc/checkbox_checked.png - rc/checkbox_indeterminate.png - rc/checkbox_indeterminate_focus.png - rc/checkbox_unchecked_disabled.png - rc/checkbox_unchecked_focus.png - rc/checkbox_unchecked.png - rc/radio_checked_disabled.png - rc/radio_checked_focus.png - rc/radio_checked.png - rc/radio_unchecked_disabled.png - rc/radio_unchecked_focus.png - rc/radio_unchecked.png - - - style.qss - - diff --git a/DSView/darkstyle/style.qss b/DSView/darkstyle/style.qss deleted file mode 100755 index 28636f46..00000000 --- a/DSView/darkstyle/style.qss +++ /dev/null @@ -1,1302 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) <2013-2014> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -QProgressBar:horizontal { - border: 1px solid #3A3939; - text-align: center; - padding: 1px; - background: #201F1F; -} -QProgressBar::chunk:horizontal { - background-color: qlineargradient(spread:reflect, x1:1, y1:0.545, x2:1, y2:0, stop:0 rgba(28, 66, 111, 255), stop:1 rgba(37, 87, 146, 255)); -} - -QToolTip -{ - border: 1px solid #3A3939; - background-color: rgb(90, 102, 117);; - color: white; - padding: 1px; - opacity: 200; -} - -QWidget -{ - color: silver; - background-color: #302F2F; - selection-background-color:#3d8ec9; - selection-color: black; - background-clip: border; - border-image: none; - outline: 0; -} - -QWidget:item:hover -{ - background-color: #78879b; - color: black; -} - -QWidget:item:selected -{ - background-color: #3d8ec9; -} - -QCheckBox -{ - spacing: 0px; - outline: none; - color: #bbb; - margin-bottom: 2px; -} - -QCheckBox:disabled -{ - color: #777777; -} -QCheckBox::indicator, -QGroupBox::indicator -{ - width: 18px; - height: 18px; -} -QGroupBox::indicator -{ - margin-left: 2px; -} - -QCheckBox::indicator:unchecked, -QCheckBox::indicator:unchecked:hover, -QGroupBox::indicator:unchecked, -QGroupBox::indicator:unchecked:hover -{ - image: url(:/qss_icons/rc/checkbox_unchecked.png); -} - -QCheckBox::indicator:unchecked:focus, -QCheckBox::indicator:unchecked:pressed, -QGroupBox::indicator:unchecked:focus, -QGroupBox::indicator:unchecked:pressed -{ - border: none; - image: url(:/qss_icons/rc/checkbox_unchecked_focus.png); -} - -QCheckBox::indicator:checked, -QCheckBox::indicator:checked:hover, -QGroupBox::indicator:checked, -QGroupBox::indicator:checked:hover -{ - image: url(:/qss_icons/rc/checkbox_checked.png); -} - -QCheckBox::indicator:checked:focus, -QCheckBox::indicator:checked:pressed, -QGroupBox::indicator:checked:focus, -QGroupBox::indicator:checked:pressed -{ - border: none; - image: url(:/qss_icons/rc/checkbox_checked_focus.png); -} - -QCheckBox::indicator:indeterminate, -QCheckBox::indicator:indeterminate:hover, -QCheckBox::indicator:indeterminate:pressed -QGroupBox::indicator:indeterminate, -QGroupBox::indicator:indeterminate:hover, -QGroupBox::indicator:indeterminate:pressed -{ - image: url(:/qss_icons/rc/checkbox_indeterminate.png); -} - -QCheckBox::indicator:indeterminate:focus, -QGroupBox::indicator:indeterminate:focus -{ - image: url(:/qss_icons/rc/checkbox_indeterminate_focus.png); -} - -QCheckBox::indicator:checked:disabled, -QGroupBox::indicator:checked:disabled -{ - image: url(:/qss_icons/rc/checkbox_checked_disabled.png); -} - -QCheckBox::indicator:unchecked:disabled, -QGroupBox::indicator:unchecked:disabled -{ - image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png); -} - -QRadioButton -{ - spacing: 5px; - outline: none; - color: #bbb; - margin-bottom: 2px; -} - -QRadioButton:disabled -{ - color: #777777; -} -QRadioButton::indicator -{ - width: 21px; - height: 21px; -} - -QRadioButton::indicator:unchecked, -QRadioButton::indicator:unchecked:hover -{ - image: url(:/qss_icons/rc/radio_unchecked.png); -} - -QRadioButton::indicator:unchecked:focus, -QRadioButton::indicator:unchecked:pressed -{ - border: none; - outline: none; - image: url(:/qss_icons/rc/radio_unchecked_focus.png); -} - -QRadioButton::indicator:checked, -QRadioButton::indicator:checked:hover -{ - border: none; - outline: none; - image: url(:/qss_icons/rc/radio_checked.png); -} - -QRadioButton::indicator:checked:focus, -QRadioButton::indicato::menu-arrowr:checked:pressed -{ - border: none; - outline: none; - image: url(:/qss_icons/rc/radio_checked_focus.png); -} - -QRadioButton::indicator:indeterminate, -QRadioButton::indicator:indeterminate:hover, -QRadioButton::indicator:indeterminate:pressed -{ - image: url(:/qss_icons/rc/radio_indeterminate.png); -} - -QRadioButton::indicator:checked:disabled -{ - outline: none; - image: url(:/qss_icons/rc/radio_checked_disabled.png); -} - -QRadioButton::indicator:unchecked:disabled -{ - image: url(:/qss_icons/rc/radio_unchecked_disabled.png); -} - - -QMenuBar -{ - background-color: #302F2F; - color: silver; -} - -QMenuBar::item -{ - background: transparent; -} - -QMenuBar::item:selected -{ - background: transparent; - border: 1px solid #3A3939; -} - -QMenuBar::item:pressed -{ - border: 1px solid #3A3939; - background-color: #3d8ec9; - color: black; - margin-bottom:-1px; - padding-bottom:1px; -} - -QMenu -{ - border: 1px solid #3A3939; - color: silver; - margin: 0px; -} - -QMenu::item -{ - padding: 5px 30px 5px 30px; - margin-left: 2px; - border: 1px solid transparent; /* reserve space for selection border */ -} - -QMenu::item:selected -{ - color: black; -} - -QMenu::separator { - height: 2px; - background: lightblue; - margin-left: 10px; - margin-right: 5px; -} - -QMenu::indicator { - width: 18px; - height: 18px; -} - -/* non-exclusive indicator = check box style indicator - (see QActionGroup::setExclusive) */ -QMenu::indicator:non-exclusive:unchecked { - image: url(:/qss_icons/rc/checkbox_unchecked.png); -} - -QMenu::indicator:non-exclusive:unchecked:selected { - image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png); -} - -QMenu::indicator:non-exclusive:checked { - image: url(:/qss_icons/rc/checkbox_checked.png); -} - -QMenu::indicator:non-exclusive:checked:selected { - image: url(:/qss_icons/rc/checkbox_checked_disabled.png); -} - -/* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */ -QMenu::indicator:exclusive:unchecked { - image: url(:/qss_icons/rc/radio_unchecked.png); -} - -QMenu::indicator:exclusive:unchecked:selected { - image: url(:/qss_icons/rc/radio_unchecked_disabled.png); -} - -QMenu::indicator:exclusive:checked { - image: url(:/qss_icons/rc/radio_checked.png); -} - -QMenu::indicator:exclusive:checked:selected { - image: url(:/qss_icons/rc/radio_checked_disabled.png); -} - -QMenu::right-arrow { - margin: 5px; - image: url(:/qss_icons/rc/right_arrow.png) -} - - -QWidget:disabled -{ - color: #404040; - background-color: #302F2F; -} - -QAbstractItemView -{ - alternate-background-color: #3A3939; - color: silver; - border: 1px solid 3A3939; - border-radius: 2px; - padding: 1px; -} - -QTabWidget:focus, QCheckBox:focus, QRadioButton:focus, QSlider:focus -{ - border: none; -} - -QLineEdit -{ - background-color: #201F1F; - padding: 2px; - border-style: solid; - border: 1px solid #3A3939; - border-radius: 2px; - color: silver; -} - -QGroupBox { - border:1px solid #3A3939; - border-radius: 2px; - margin-top: 20px; -} - -QGroupBox::title { - subcontrol-origin: margin; - subcontrol-position: top center; - padding-left: 10px; - padding-right: 10px; - padding-top: 10px; -} - -QScrollBar:horizontal -{ - height: 15px; - margin: 3px 15px 3px 15px; - border: 1px transparent #2A2929; - border-radius: 4px; - background-color: #2A2929; -} - -QScrollBar::handle:horizontal -{ - background-color: #605F5F; - min-width: 15px; - border-radius: 4px; -} - -QScrollBar::add-line:horizontal -{ - margin: 0px 3px 0px 3px; - border-image: url(:/qss_icons/rc/right_arrow_disabled.png); - width: 10px; - height: 10px; - subcontrol-position: right; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:horizontal -{ - margin: 0px 3px 0px 3px; - border-image: url(:/qss_icons/rc/left_arrow_disabled.png); - height: 10px; - width: 10px; - subcontrol-position: left; - subcontrol-origin: margin; -} - -QScrollBar::add-line:horizontal:hover,QScrollBar::add-line:horizontal:on -{ - border-image: url(:/qss_icons/rc/right_arrow.png); - height: 10px; - width: 10px; - subcontrol-position: right; - subcontrol-origin: margin; -} - - -QScrollBar::sub-line:horizontal:hover, QScrollBar::sub-line:horizontal:on -{ - border-image: url(:/qss_icons/rc/left_arrow.png); - height: 10px; - width: 10px; - subcontrol-position: left; - subcontrol-origin: margin; -} - -QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal -{ - background: none; -} - - -QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal -{ - background: none; -} - -QScrollBar:vertical -{ - background-color: #2A2929; - width: 15px; - margin: 15px 3px 15px 3px; - border: 1px transparent #2A2929; - border-radius: 4px; -} - -QScrollBar::handle:vertical -{ - background-color: #605F5F; - min-height: 15px; - border-radius: 4px; -} - -QScrollBar::sub-line:vertical -{ - margin: 3px 0px 3px 0px; - border-image: url(:/qss_icons/rc/up_arrow_disabled.png); - height: 10px; - width: 10px; - subcontrol-position: top; - subcontrol-origin: margin; -} - -QScrollBar::add-line:vertical -{ - margin: 3px 0px 3px 0px; - border-image: url(:/qss_icons/rc/down_arrow_disabled.png); - height: 10px; - width: 10px; - subcontrol-position: bottom; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:vertical:hover,QScrollBar::sub-line:vertical:on -{ - - border-image: url(:/qss_icons/rc/up_arrow.png); - height: 10px; - width: 10px; - subcontrol-position: top; - subcontrol-origin: margin; -} - - -QScrollBar::add-line:vertical:hover, QScrollBar::add-line:vertical:on -{ - border-image: url(:/qss_icons/rc/down_arrow.png); - height: 10px; - width: 10px; - subcontrol-position: bottom; - subcontrol-origin: margin; -} - -QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical -{ - background: none; -} - - -QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical -{ - background: none; -} - -QTextEdit -{ - background-color: #201F1F; - color: silver; - border: 1px solid #3A3939; - margin: 0; -} - -QPlainTextEdit -{ - background-color: #201F1F;; - color: silver; - border-radius: 2px; - border: 1px solid #3A3939; -} - -QHeaderView::section -{ - background-color: #3A3939; - color: silver; - padding-left: 4px; - border: 1px solid #6c6c6c; -} - -QSizeGrip { - image: url(:/qss_icons/rc/sizegrip.png); - width: 12px; - height: 12px; -} - - -QMenu::separator -{ - height: 1px; - background-color: #3A3939; - color: white; - padding-left: 4px; - margin-left: 10px; - margin-right: 5px; -} - -QFrame -{ - border-radius: 2px; - border: 1px solid #444; -} - -QFrame[frameShape="0"] -{ - border-radius: 2px; - border: 1px transparent #444; -} - -QStackedWidget -{ - border: 1px transparent black; -} - -QToolBar { - border: 1px transparent #393838; - background: 1px solid #302F2F; - font-weight: bold; -} - -QPushButton -{ - color: silver; - background-color: #302F2F; - border-width: 1px; - border-color: #202020; - border-style: solid; - padding-top: 5px; - padding-bottom: 5px; - padding-left: 5px; - padding-right: 5px; - border-radius: 5px; - outline: none; -} - -QPushButton:disabled -{ - background-color: #302F2F; - border-width: 1px; - border-color: #3A3939; - border-style: solid; - padding-top: 5px; - padding-bottom: 5px; - padding-left: 10px; - padding-right: 10px; - border-radius: 5px; - color: #454545; -} - -QComboBox -{ - selection-background-color: #3d8ec9; - background-color: #201F1F; - border-style: solid; - border: 1px solid #3A3939; - border-radius: 2px; - padding: 2px; - min-width: 30px; -} - -QPushButton:checked{ -background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0.0 #302F2F, - stop: 0.5 #6a6868, - stop: 1 #302F2F); -} - -QPushButton:hover -{ -background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0.0 #302F2F, stop: 0.4 #4E4D4D, - stop: 0.5 #6a6868, - stop: 0.6 #4E4D4D, stop: 1 #302F2F); -} - -QComboBox:hover,QAbstractSpinBox:hover,QLineEdit:hover,QPlainTextEdit:hover,QAbstractView:hover,QTreeView:hover -{ - border: 1px solid #606060; - color: silver; -} - -QComboBox:on -{ - background-color: #626873; - padding-top: 3px; - padding-left: 4px; - selection-background-color: #4a4a4a; -} - -QComboBox QAbstractItemView -{ - background-color: #201F1F; - border-radius: 2px; - border: 1px solid #444; - selection-background-color: #3d8ec9; -} - -QComboBox::drop-down -{ - subcontrol-origin: padding; - subcontrol-position: top right; - width: 10px; - - border-left-width: 0px; - border-left-color: darkgray; - border-left-style: solid; - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; -} - -QComboBox::down-arrow -{ - image: url(:/qss_icons/rc/down_arrow_disabled.png); -} - -QComboBox::down-arrow:on, QComboBox::down-arrow:hover, -QComboBox::down-arrow:focus -{ - image: url(:/qss_icons/rc/down_arrow.png); -} - -QAbstractSpinBox { - padding-top: 2px; - padding-bottom: 2px; - border: 1px solid #3A3939; - background-color: #201F1F; - color: silver; - border-radius: 2px; - min-width: 60px; -} - -QAbstractSpinBox:up-button -{ - background-color: transparent; - subcontrol-origin: border; - subcontrol-position: center right; -} - -QAbstractSpinBox:down-button -{ - background-color: transparent; - subcontrol-origin: border; - subcontrol-position: center left; -} - -QAbstractSpinBox::up-arrow,QAbstractSpinBox::up-arrow:disabled,QAbstractSpinBox::up-arrow:off { - image: url(:/qss_icons/rc/up_arrow_disabled.png); - width: 10px; - height: 10px; -} -QAbstractSpinBox::up-arrow:hover -{ - image: url(:/qss_icons/rc/up_arrow.png); -} - - -QAbstractSpinBox::down-arrow,QAbstractSpinBox::down-arrow:disabled,QAbstractSpinBox::down-arrow:off -{ - image: url(:/qss_icons/rc/down_arrow_disabled.png); - width: 10px; - height: 10px; -} -QAbstractSpinBox::down-arrow:hover -{ - image: url(:/qss_icons/rc/down_arrow.png); -} - - -QLabel -{ - border: 0px solid black; - margin-left: 2px; - margin-right: 2px; -} - -QTabWidget{ - border: 1px transparent black; -} - -QTabWidget::pane { - border: 1px transparent #444; - border-radius: 3px; - padding: 3px; -} - -QTabBar -{ - qproperty-drawBase: 0; - left: 5px; /* move to the right by 5px */ -} - -QTabBar:focus -{ - border: 0px transparent black; -} - -QTabBar::close-button { - image: url(:/qss_icons/rc/close.png); - background: transparent; -} - -QTabBar::close-button:hover -{ - image: url(:/qss_icons/rc/close-hover.png); - background: transparent; -} - -QTabBar::close-button:pressed { - image: url(:/qss_icons/rc/close-pressed.png); - background: transparent; -} - -/* TOP TABS */ -QTabBar::tab:top { - color: #b1b1b1; - border: 1px solid #4A4949; - border-bottom: 1px transparent black; - background-color: #302F2F; - padding: 5px; - border-top-left-radius: 2px; - border-top-right-radius: 2px; -} - -QTabBar::tab:top:!selected -{ - color: #b1b1b1; - background-color: #201F1F; - border: 1px transparent #4A4949; - border-bottom: 1px transparent #4A4949; - border-top-left-radius: 0px; - border-top-right-radius: 0px; -} - -QTabBar::tab:top:!selected:hover { - background-color: #48576b; -} - -/* BOTTOM TABS */ -QTabBar::tab:bottom { - color: #b1b1b1; - border: 1px solid #4A4949; - border-top: 1px transparent black; - background-color: #302F2F; - padding: 5px; - border-bottom-left-radius: 2px; - border-bottom-right-radius: 2px; -} - -QTabBar::tab:bottom:!selected -{ - color: #b1b1b1; - background-color: #201F1F; - border: 1px transparent #4A4949; - border-top: 1px transparent #4A4949; - border-bottom-left-radius: 0px; - border-bottom-right-radius: 0px; -} - -QTabBar::tab:bottom:!selected:hover { - background-color: #78879b; -} - -/* LEFT TABS */ -QTabBar::tab:left { - color: #b1b1b1; - border: 1px transparent #4A4949; - border-left: 1px transparent black; - background-color: #48576b; - padding: 5px; - border-top-right-radius: 0px; - border-bottom-right-radius: 0px; -} - -QTabBar::tab:left:!selected -{ - color: #b1b1b1; - background-color: #302F2F; - border: 1px transparent #4A4949; - border-right: 1px transparent #4A4949; - border-top-right-radius: 0px; - border-bottom-right-radius: 0px; -} - -QTabBar::tab:left:hover { - background-color: #48576b; -} - -QTabBar::tab:left:disabled -{ - color: #3A3939; - background-color: #302F2F; - border: 1px transparent #4A4949; - border-right: 1px transparent #4A4949; - border-top-right-radius: 0px; - border-bottom-right-radius: 0px; -} - - - -/* RIGHT TABS */ -QTabBar::tab:right { - color: #b1b1b1; - border: 1px solid #4A4949; - border-right: 1px transparent black; - background-color: #302F2F; - padding: 5px; - border-top-left-radius: 2px; - border-bottom-left-radius: 2px; -} - -QTabBar::tab:right:!selected -{ - color: #b1b1b1; - background-color: #201F1F; - border: 1px transparent #4A4949; - border-right: 1px transparent #4A4949; - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; -} - -QTabBar::tab:right:!selected:hover { - background-color: #48576b; -} - -QTabBar QToolButton::right-arrow:enabled { - image: url(:/qss_icons/rc/right_arrow.png); - } - - QTabBar QToolButton::left-arrow:enabled { - image: url(:/qss_icons/rc/left_arrow.png); - } - -QTabBar QToolButton::right-arrow:disabled { - image: url(:/qss_icons/rc/right_arrow_disabled.png); - } - - QTabBar QToolButton::left-arrow:disabled { - image: url(:/qss_icons/rc/left_arrow_disabled.png); - } - - -QDockWidget { - border: 1px transparent #403F3F; - titlebar-close-icon: url(:/qss_icons/rc/close.png); - titlebar-normal-icon: url(:/qss_icons/rc/undock.png); -} - -QDockWidget::title { - border: 1px solid #282727; - background-color: #2b2a2a; -} - -QDockWidget::close-button, QDockWidget::float-button { - border: 1px solid transparent; - border-radius: 2px; - background: transparent; -} - -QDockWidget::close-button:hover, QDockWidget::float-button:hover { - background: rgba(255, 255, 255, 10); -} - -QDockWidget::close-button:pressed, QDockWidget::float-button:pressed { - padding: 1px -1px -1px 1px; - background: rgba(255, 255, 255, 10); -} - -QTreeView, QListView -{ - border: 1px solid #444; - background-color: #201F1F; -} - -QTreeView:branch:selected, QTreeView:branch:hover -{ - background: url(:/qss_icons/rc/transparent.png); -} - -QTreeView::branch:has-siblings:!adjoins-item { - border-image: url(:/qss_icons/rc/transparent.png); -} - -QTreeView::branch:has-siblings:adjoins-item { - border-image: url(:/qss_icons/rc/transparent.png); -} - -QTreeView::branch:!has-children:!has-siblings:adjoins-item { - border-image: url(:/qss_icons/rc/transparent.png); -} - -QTreeView::branch:has-children:!has-siblings:closed, -QTreeView::branch:closed:has-children:has-siblings { - image: url(:/qss_icons/rc/branch_closed.png); -} - -QTreeView::branch:open:has-children:!has-siblings, -QTreeView::branch:open:has-children:has-siblings { - image: url(:/qss_icons/rc/branch_open.png); -} - -QTreeView::branch:has-children:!has-siblings:closed:hover, -QTreeView::branch:closed:has-children:has-siblings:hover { - image: url(:/qss_icons/rc/branch_closed-on.png); - } - -QTreeView::branch:open:has-children:!has-siblings:hover, -QTreeView::branch:open:has-children:has-siblings:hover { - image: url(:/qss_icons/rc/branch_open-on.png); - } - -QListView::item:!selected:hover, QListView::item:!selected:hover, QTreeView::item:!selected:hover { - background: rgba(0, 0, 0, 0); - outline: 0; - color: #FFFFFF -} - -QListView::item:selected:hover, QListView::item:selected:hover, QTreeView::item:selected:hover { - background: #3d8ec9; - color: #FFFFFF; -} - -QSlider::groove:horizontal { - border: 1px solid #3A3939; - height: 8px; - background: #201F1F; - margin: 2px 0; - border-radius: 2px; -} - -QSlider::groove:horizontal:disabled { - border: 1px solid #3A3939; - height: 8px; - background: #282727; - margin: 2px 0; - border-radius: 2px; -} - -QSlider::handle:horizontal { - background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0.0 silver, stop: 0.2 #a8a8a8, stop: 1 #727272); - border: 1px solid #3A3939; - width: 10px; - height: 14px; - margin: -4px 0; - border-radius: 2px; -} - -QSlider::handle:horizontal:disabled { - background: #4A4949; - border: 1px solid #3A3939; - width: 10px; - height: 14px; - margin: -4px 0; - border-radius: 2px; -} - -QSlider::groove:vertical { - border: 1px solid #3A3939; - width: 8px; - background: #201F1F; - margin: 0 0px; - border-radius: 2px; -} - -QSlider::groove:vertical:disabled { - border: 1px solid #3A3939; - height: 8px; - background: #403F3F; - margin: 2px 0; - border-radius: 2px; -} - -QSlider::handle:vertical { - background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0.0 silver, - stop: 0.2 #a8a8a8, stop: 1 #727272); - border: 1px solid #3A3939; - width: 14px; - height: 10px; - margin: 0 -4px; - border-radius: 2px; -} - -QSlider::handle:vertical:disabled { - background: #4A4949; - border: 1px solid #3A3939; - width: 14px; - height: 10px; - margin: 0 -4px; - border-radius: 2px; -} - -QToolButton#MaximizeButton { - background-color: transparent; - border-left: 1px solid QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0.0 #302F2F, stop: 0.3 #606060, - stop: 0.5 #707070, - stop: 0.7 #606060, stop: 1 #302F2F); - border-right: 1px solid QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0.0 #302F2F, stop: 0.3 #606060, - stop: 0.5 #707070, - stop: 0.7 #606060, stop: 1 #302F2F); - border-radius: 0px; - margin: 0px; - padding: 0px; -} - -QToolButton#MinimizeButton, -QToolButton#CloseButton { - background-color: transparent; - border: 1px transparent #808080; - border-radius: 0px; - margin: 0px; - padding: 0px; -} - -QToolButton#MinimizeButton:hover, QToolButton#MinimizeButton::menu-button:hover, -QToolButton#MaximizeButton:hover, QToolButton#MaximizeButton::menu-button:hover{ - background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0.0 #302F2F, stop: 0.4 #4E4D4D, - stop: 0.5 #4A4949, - stop: 0.6 #4E4D4D, stop: 1 #302F2F); -} - -QToolButton#CloseButton:hover, QToolButton#CloseButton::menu-button:hover { -background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0.0 #302F2F, stop: 0.2 #A82F2F, - stop: 0.5 #E83E4A, - stop: 0.8 #A82F2F, stop: 1 #302F2F); -} - -QToolButton { - background-color: transparent; - border: 1px transparent #4A4949; - border-radius: 2px; - margin: 3px; - padding: 3px; -} - -QToolButton[popupMode="1"] { /* only for MenuButtonPopup */ - padding-right: 20px; /* make way for the popup button */ - border: 1px transparent #4A4949; - border-radius: 5px; -} - -QToolButton[popupMode="2"] { /* only for InstantPopup */ - padding-right: 10px; /* make way for the popup button */ - border: 1px transparent #4A4949; -} - - -QToolButton:hover, QToolButton::menu-button:hover { - background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0.0 #302F2F, stop: 0.4 #4E4D4D, - stop: 0.5 #4A4949, - stop: 0.6 #4E4D4D, stop: 1 #302F2F); -} - -QToolButton:checked, QToolButton:pressed, -QToolButton::menu-button:pressed { - background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0.0 #302F2F, - stop: 0.5 #4A4949, - stop: 1.0 #302F2F); -} - -/* the subcontrol below is used only in the InstantPopup or DelayedPopup mode */ -QToolButton::menu-indicator { - image: url(:/qss_icons/rc/down_arrow.png); - top: -7px; left: -2px; /* shift it a bit */ -} - -/* the subcontrols below are used only in the MenuButtonPopup mode */ -QToolButton::menu-button { - border: 1px transparent #4A4949; - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; - /* 16px width + 4px for border = 20px allocated above */ - width: 16px; - outline: none; -} - -QToolButton::menu-arrow { - image: url(:/qss_icons/rc/down_arrow.png); -} - -QToolButton::menu-arrow:open { - top: 1px; left: 1px; /* shift it a bit */ - border: 1px solid #3A3939; -} - -QPushButton::menu-indicator { - subcontrol-origin: padding; - subcontrol-position: bottom right; - left: 8px; -} - -QTableView -{ - border: 1px transparent #444; - gridline-color: #6c6c6c; - background-color: #201F1F; -} - - -QTableView, QHeaderView -{ - border-radius: 0px; -} - -QTableView::item:pressed, QListView::item:pressed, QTreeView::item:pressed { - background: #78879b; - color: #FFFFFF; -} - -QTableView::item:selected:active, QTreeView::item:selected:active, QListView::item:selected:active { - background: #3d8ec9; - color: #FFFFFF; -} - -QHeaderView -{ - border: 1px transparent; - border-radius: 2px; - margin: 0px; - padding: 0px; -} - -QHeaderView::section { - background-color: #302F2F; - color: silver; - padding: 4px; - border: 1px transparent #6c6c6c; - border-radius: 0px; - text-align: center; -} - -QHeaderView::section::vertical::first, QHeaderView::section::vertical::only-one -{ - border-top: 1px transparent #6c6c6c; -} - -QHeaderView::section::vertical -{ - border-top: transparent; -} - -QHeaderView::section::horizontal::first, QHeaderView::section::horizontal::only-one -{ - border-left: 1px transparent #6c6c6c; -} - -QHeaderView::section::horizontal -{ - border-left: transparent; -} - - -QHeaderView::section:checked - { - color: white; - background-color: #5A5959; - } - - /* style the sort indicator */ -QHeaderView::down-arrow { - image: url(:/qss_icons/rc/down_arrow.png); -} - -QHeaderView::up-arrow { - image: url(:/qss_icons/rc/up_arrow.png); -} - - -QTableCornerButton::section { - background-color: #3A3939; - border: 1px solid #3A3939; - border-radius: 2px; -} - -QToolBox { - padding: 3px; - border: 1px transparent black; -} - -QToolBox::tab { - color: #b1b1b1; - background-color: #302F2F; - border: 1px solid #4A4949; - border-bottom: 1px transparent #302F2F; - border-top-left-radius: 5px; - border-top-right-radius: 5px; -} - - QToolBox::tab:selected { /* italicize selected tabs */ - font: italic; - background-color: #302F2F; - border-color: #3d8ec9; - } - -QStatusBar::item { - border: 1px solid #3A3939; - border-radius: 2px; - } - -QFrame[height="3"], QFrame[width="3"] { - background-color: #444; -} - -QAbstractScrollArea -{ - border-radius: 2px; - border: 0px transparent #3A3939; - background-color: #302F2F; -} - -QSplitter::handle:horizontal, -QMainWindow::separator -{ - background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0.0 #302F2F, - stop: 0.4 #333333, - stop: 0.5 #404040, - stop: 0.6 #333333, - stop: 1 #302F2F); - color: white; - padding-left: 0px; - spacing: 0px; - width: 3px; - border: 0px solid #202020; -} - -QSplitter::handle:horizontal:hover, -QMainWindow::separator:hover -{ - background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0.0 #302F2F, - stop: 0.1 #333333, - stop: 0.5 #404040, - stop: 0.9 #333333, - stop: 1 #302F2F); - color: white; - padding-left: 0px; - spacing: 0px; - width: 3px; - border: 0px solid #202020; -} - -QSplitter::handle:vertical { - background-color: QLinearGradient( x1: 0, y1: 0, x2: 1, y2: 0, - stop: 0.0 #302F2F, - stop: 0.3 #505050, - stop: 0.5 #606060, - stop: 0.7 #505050, - stop: 1 #302F2F); - height: 3px; -} - -QSplitter::handle:vertical:hover { - background-color: QLinearGradient( x1: 0, y1: 0, x2: 1, y2: 0, - stop: 0.0 #302F2F, - stop: 0.1 #505050, - stop: 0.5 #606060, - stop: 0.8 #505050, - stop: 1 #302F2F); - height: 3px; -} diff --git a/DSView/dsapplication.cpp b/DSView/dsapplication.cpp old mode 100644 new mode 100755 diff --git a/DSView/dsapplication.h b/DSView/dsapplication.h old mode 100644 new mode 100755 diff --git a/DSView/extdef.h b/DSView/extdef.h old mode 100644 new mode 100755 diff --git a/DSView/icons/Blackman.png b/DSView/icons/Blackman.png old mode 100644 new mode 100755 diff --git a/DSView/icons/Chinese.png b/DSView/icons/Chinese.png new file mode 100755 index 00000000..f1387113 Binary files /dev/null and b/DSView/icons/Chinese.png differ diff --git a/DSView/icons/English.png b/DSView/icons/English.png new file mode 100755 index 00000000..deee73f0 Binary files /dev/null and b/DSView/icons/English.png differ diff --git a/DSView/icons/Flat_top.png b/DSView/icons/Flat_top.png old mode 100644 new mode 100755 diff --git a/DSView/icons/Hamming.png b/DSView/icons/Hamming.png old mode 100644 new mode 100755 diff --git a/DSView/icons/Hann.png b/DSView/icons/Hann.png old mode 100644 new mode 100755 diff --git a/DSView/icons/Rectangle.png b/DSView/icons/Rectangle.png old mode 100644 new mode 100755 diff --git a/DSView/icons/arrow-loop.png b/DSView/icons/arrow-loop.png deleted file mode 100755 index d3df3243..00000000 Binary files a/DSView/icons/arrow-loop.png and /dev/null differ diff --git a/DSView/icons/close.png b/DSView/icons/close.png deleted file mode 100644 index 529f2ed1..00000000 Binary files a/DSView/icons/close.png and /dev/null differ diff --git a/DSView/icons/about.png b/DSView/icons/dark/about.png similarity index 100% rename from DSView/icons/about.png rename to DSView/icons/dark/about.png diff --git a/DSView/icons/add.png b/DSView/icons/dark/add.png similarity index 100% rename from DSView/icons/add.png rename to DSView/icons/dark/add.png diff --git a/DSView/icons/bug.png b/DSView/icons/dark/bug.png similarity index 100% rename from DSView/icons/bug.png rename to DSView/icons/dark/bug.png diff --git a/DSView/icons/capture.png b/DSView/icons/dark/capture.png old mode 100644 new mode 100755 similarity index 100% rename from DSView/icons/capture.png rename to DSView/icons/dark/capture.png diff --git a/DSView/icons/dark/close.png b/DSView/icons/dark/close.png new file mode 100755 index 00000000..8eb85178 Binary files /dev/null and b/DSView/icons/dark/close.png differ diff --git a/DSView/icons/dark/daq.png b/DSView/icons/dark/daq.png new file mode 100755 index 00000000..69be862f Binary files /dev/null and b/DSView/icons/dark/daq.png differ diff --git a/DSView/icons/dark/dark.png b/DSView/icons/dark/dark.png new file mode 100755 index 00000000..8f822aa8 Binary files /dev/null and b/DSView/icons/dark/dark.png differ diff --git a/DSView/icons/del.png b/DSView/icons/dark/del.png similarity index 100% rename from DSView/icons/del.png rename to DSView/icons/dark/del.png diff --git a/DSView/icons/dark/display.png b/DSView/icons/dark/display.png new file mode 100755 index 00000000..6ca6c96e Binary files /dev/null and b/DSView/icons/dark/display.png differ diff --git a/DSView/icons/down-arrow.png b/DSView/icons/dark/down-arrow.png old mode 100644 new mode 100755 similarity index 100% rename from DSView/icons/down-arrow.png rename to DSView/icons/dark/down-arrow.png diff --git a/DSView/icons/export.png b/DSView/icons/dark/export.png old mode 100644 new mode 100755 similarity index 100% rename from DSView/icons/export.png rename to DSView/icons/dark/export.png diff --git a/DSView/icons/fft.png b/DSView/icons/dark/fft.png old mode 100644 new mode 100755 similarity index 100% rename from DSView/icons/fft.png rename to DSView/icons/dark/fft.png diff --git a/DSView/icons/dark/file.png b/DSView/icons/dark/file.png new file mode 100755 index 00000000..926ac246 Binary files /dev/null and b/DSView/icons/dark/file.png differ diff --git a/DSView/icons/dark/function.png b/DSView/icons/dark/function.png new file mode 100755 index 00000000..718edebb Binary files /dev/null and b/DSView/icons/dark/function.png differ diff --git a/DSView/icons/gear.png b/DSView/icons/dark/gear.png old mode 100644 new mode 100755 similarity index 100% rename from DSView/icons/gear.png rename to DSView/icons/dark/gear.png diff --git a/DSView/icons/hidden.png b/DSView/icons/dark/hidden.png old mode 100644 new mode 100755 similarity index 100% rename from DSView/icons/hidden.png rename to DSView/icons/dark/hidden.png diff --git a/DSView/icons/dark/instant.png b/DSView/icons/dark/instant.png new file mode 100755 index 00000000..c74c674c Binary files /dev/null and b/DSView/icons/dark/instant.png differ diff --git a/DSView/icons/dark/la.png b/DSView/icons/dark/la.png new file mode 100755 index 00000000..d77009bb Binary files /dev/null and b/DSView/icons/dark/la.png differ diff --git a/DSView/icons/dark/light.png b/DSView/icons/dark/light.png new file mode 100755 index 00000000..23882479 Binary files /dev/null and b/DSView/icons/dark/light.png differ diff --git a/DSView/icons/dark/lissajous.png b/DSView/icons/dark/lissajous.png new file mode 100755 index 00000000..632a722d Binary files /dev/null and b/DSView/icons/dark/lissajous.png differ diff --git a/DSView/icons/dark/logo_color.png b/DSView/icons/dark/logo_color.png new file mode 100755 index 00000000..65ae5c6d Binary files /dev/null and b/DSView/icons/dark/logo_color.png differ diff --git a/DSView/icons/dark/logo_noColor.png b/DSView/icons/dark/logo_noColor.png new file mode 100755 index 00000000..9ee4aad6 Binary files /dev/null and b/DSView/icons/dark/logo_noColor.png differ diff --git a/DSView/icons/manual.png b/DSView/icons/dark/manual.png similarity index 100% rename from DSView/icons/manual.png rename to DSView/icons/dark/manual.png diff --git a/DSView/icons/dark/math.png b/DSView/icons/dark/math.png new file mode 100755 index 00000000..f84625f7 Binary files /dev/null and b/DSView/icons/dark/math.png differ diff --git a/DSView/icons/dark/maximize.png b/DSView/icons/dark/maximize.png new file mode 100755 index 00000000..51d6577a Binary files /dev/null and b/DSView/icons/dark/maximize.png differ diff --git a/DSView/icons/dark/measure.png b/DSView/icons/dark/measure.png new file mode 100755 index 00000000..d0ce0421 Binary files /dev/null and b/DSView/icons/dark/measure.png differ diff --git a/DSView/icons/dark/minimize.png b/DSView/icons/dark/minimize.png new file mode 100755 index 00000000..132ef21c Binary files /dev/null and b/DSView/icons/dark/minimize.png differ diff --git a/DSView/icons/dark/moder.png b/DSView/icons/dark/moder.png new file mode 100755 index 00000000..0f13a3fc Binary files /dev/null and b/DSView/icons/dark/moder.png differ diff --git a/DSView/icons/dark/modes.png b/DSView/icons/dark/modes.png new file mode 100755 index 00000000..fca07844 Binary files /dev/null and b/DSView/icons/dark/modes.png differ diff --git a/DSView/icons/nav.png b/DSView/icons/dark/nav.png similarity index 100% rename from DSView/icons/nav.png rename to DSView/icons/dark/nav.png diff --git a/DSView/icons/next.png b/DSView/icons/dark/next.png old mode 100644 new mode 100755 similarity index 100% rename from DSView/icons/next.png rename to DSView/icons/dark/next.png diff --git a/DSView/icons/oneloop.png b/DSView/icons/dark/oneloop.png similarity index 100% rename from DSView/icons/oneloop.png rename to DSView/icons/dark/oneloop.png diff --git a/DSView/icons/open.png b/DSView/icons/dark/open.png old mode 100644 new mode 100755 similarity index 100% rename from DSView/icons/open.png rename to DSView/icons/dark/open.png diff --git a/DSView/icons/dark/osc.png b/DSView/icons/dark/osc.png new file mode 100755 index 00000000..233027f6 Binary files /dev/null and b/DSView/icons/dark/osc.png differ diff --git a/DSView/icons/dark/params.png b/DSView/icons/dark/params.png new file mode 100755 index 00000000..e64cd54b Binary files /dev/null and b/DSView/icons/dark/params.png differ diff --git a/DSView/icons/pre.png b/DSView/icons/dark/pre.png old mode 100644 new mode 100755 similarity index 100% rename from DSView/icons/pre.png rename to DSView/icons/dark/pre.png diff --git a/DSView/icons/dark/protocol.png b/DSView/icons/dark/protocol.png new file mode 100755 index 00000000..75378de6 Binary files /dev/null and b/DSView/icons/dark/protocol.png differ diff --git a/DSView/icons/repeat.png b/DSView/icons/dark/repeat.png similarity index 100% rename from DSView/icons/repeat.png rename to DSView/icons/dark/repeat.png diff --git a/DSView/icons/dark/restore.png b/DSView/icons/dark/restore.png new file mode 100755 index 00000000..2079882a Binary files /dev/null and b/DSView/icons/dark/restore.png differ diff --git a/DSView/icons/save.png b/DSView/icons/dark/save.png old mode 100644 new mode 100755 similarity index 100% rename from DSView/icons/save.png rename to DSView/icons/dark/save.png diff --git a/DSView/icons/dark/search-bar.png b/DSView/icons/dark/search-bar.png new file mode 100755 index 00000000..e3a3298f Binary files /dev/null and b/DSView/icons/dark/search-bar.png differ diff --git a/DSView/icons/dark/search.png b/DSView/icons/dark/search.png new file mode 100755 index 00000000..5a67758e Binary files /dev/null and b/DSView/icons/dark/search.png differ diff --git a/DSView/icons/shown.png b/DSView/icons/dark/shown.png old mode 100644 new mode 100755 similarity index 100% rename from DSView/icons/shown.png rename to DSView/icons/dark/shown.png diff --git a/DSView/icons/dark/single.png b/DSView/icons/dark/single.png new file mode 100755 index 00000000..4c445698 Binary files /dev/null and b/DSView/icons/dark/single.png differ diff --git a/DSView/icons/dark/start.png b/DSView/icons/dark/start.png new file mode 100755 index 00000000..40e181d0 Binary files /dev/null and b/DSView/icons/dark/start.png differ diff --git a/DSView/icons/dark/stop.png b/DSView/icons/dark/stop.png new file mode 100755 index 00000000..7017ec46 Binary files /dev/null and b/DSView/icons/dark/stop.png differ diff --git a/DSView/icons/support.png b/DSView/icons/dark/support.png similarity index 100% rename from DSView/icons/support.png rename to DSView/icons/dark/support.png diff --git a/DSView/icons/dark/trigger.png b/DSView/icons/dark/trigger.png new file mode 100755 index 00000000..9ccd6c0c Binary files /dev/null and b/DSView/icons/dark/trigger.png differ diff --git a/DSView/icons/dark/wait.gif b/DSView/icons/dark/wait.gif new file mode 100755 index 00000000..8cff358c Binary files /dev/null and b/DSView/icons/dark/wait.gif differ diff --git a/DSView/icons/del_dis.png b/DSView/icons/del_dis.png deleted file mode 100755 index d4f418c3..00000000 Binary files a/DSView/icons/del_dis.png and /dev/null differ diff --git a/DSView/icons/file.png b/DSView/icons/file.png deleted file mode 100644 index 78c469cc..00000000 Binary files a/DSView/icons/file.png and /dev/null differ diff --git a/DSView/icons/file_dis.png b/DSView/icons/file_dis.png deleted file mode 100644 index 5c27e1ae..00000000 Binary files a/DSView/icons/file_dis.png and /dev/null differ diff --git a/DSView/icons/instant.png b/DSView/icons/instant.png deleted file mode 100644 index c3e249d3..00000000 Binary files a/DSView/icons/instant.png and /dev/null differ diff --git a/DSView/icons/instant_dis.png b/DSView/icons/instant_dis.png deleted file mode 100644 index 9c24bf07..00000000 Binary files a/DSView/icons/instant_dis.png and /dev/null differ diff --git a/DSView/icons/light/about.png b/DSView/icons/light/about.png new file mode 100755 index 00000000..e1810a34 Binary files /dev/null and b/DSView/icons/light/about.png differ diff --git a/DSView/icons/add_dis.png b/DSView/icons/light/add.png similarity index 50% rename from DSView/icons/add_dis.png rename to DSView/icons/light/add.png index b0349001..fdb05911 100755 Binary files a/DSView/icons/add_dis.png and b/DSView/icons/light/add.png differ diff --git a/DSView/icons/light/bug.png b/DSView/icons/light/bug.png new file mode 100755 index 00000000..56b70a64 Binary files /dev/null and b/DSView/icons/light/bug.png differ diff --git a/DSView/icons/light/capture.png b/DSView/icons/light/capture.png new file mode 100755 index 00000000..f581e561 Binary files /dev/null and b/DSView/icons/light/capture.png differ diff --git a/DSView/icons/light/close.png b/DSView/icons/light/close.png new file mode 100755 index 00000000..a3de9ec9 Binary files /dev/null and b/DSView/icons/light/close.png differ diff --git a/DSView/icons/light/daq.png b/DSView/icons/light/daq.png new file mode 100755 index 00000000..ac80361f Binary files /dev/null and b/DSView/icons/light/daq.png differ diff --git a/DSView/icons/light/dark.png b/DSView/icons/light/dark.png new file mode 100755 index 00000000..889cac2b Binary files /dev/null and b/DSView/icons/light/dark.png differ diff --git a/DSView/icons/light/del.png b/DSView/icons/light/del.png new file mode 100755 index 00000000..d361cfe3 Binary files /dev/null and b/DSView/icons/light/del.png differ diff --git a/DSView/icons/light/display.png b/DSView/icons/light/display.png new file mode 100755 index 00000000..966d0e5a Binary files /dev/null and b/DSView/icons/light/display.png differ diff --git a/DSView/icons/light/down-arrow.png b/DSView/icons/light/down-arrow.png new file mode 100755 index 00000000..d1b987ef Binary files /dev/null and b/DSView/icons/light/down-arrow.png differ diff --git a/DSView/icons/light/export.png b/DSView/icons/light/export.png new file mode 100755 index 00000000..02b23760 Binary files /dev/null and b/DSView/icons/light/export.png differ diff --git a/DSView/icons/light/fft.png b/DSView/icons/light/fft.png new file mode 100755 index 00000000..cc26ab49 Binary files /dev/null and b/DSView/icons/light/fft.png differ diff --git a/DSView/icons/light/file.png b/DSView/icons/light/file.png new file mode 100755 index 00000000..4dfc6ae9 Binary files /dev/null and b/DSView/icons/light/file.png differ diff --git a/DSView/icons/light/function.png b/DSView/icons/light/function.png new file mode 100755 index 00000000..90921019 Binary files /dev/null and b/DSView/icons/light/function.png differ diff --git a/DSView/icons/light/gear.png b/DSView/icons/light/gear.png new file mode 100755 index 00000000..794195c6 Binary files /dev/null and b/DSView/icons/light/gear.png differ diff --git a/DSView/icons/light/hidden.png b/DSView/icons/light/hidden.png new file mode 100755 index 00000000..427eaa41 Binary files /dev/null and b/DSView/icons/light/hidden.png differ diff --git a/DSView/icons/light/instant.png b/DSView/icons/light/instant.png new file mode 100755 index 00000000..8acd5871 Binary files /dev/null and b/DSView/icons/light/instant.png differ diff --git a/DSView/icons/light/la.png b/DSView/icons/light/la.png new file mode 100755 index 00000000..25a361aa Binary files /dev/null and b/DSView/icons/light/la.png differ diff --git a/DSView/icons/light/light.png b/DSView/icons/light/light.png new file mode 100755 index 00000000..ab90576c Binary files /dev/null and b/DSView/icons/light/light.png differ diff --git a/DSView/icons/light/lissajous.png b/DSView/icons/light/lissajous.png new file mode 100755 index 00000000..868be40a Binary files /dev/null and b/DSView/icons/light/lissajous.png differ diff --git a/DSView/icons/light/logo_color.png b/DSView/icons/light/logo_color.png new file mode 100755 index 00000000..65ae5c6d Binary files /dev/null and b/DSView/icons/light/logo_color.png differ diff --git a/DSView/icons/light/logo_noColor.png b/DSView/icons/light/logo_noColor.png new file mode 100755 index 00000000..3a39b2e8 Binary files /dev/null and b/DSView/icons/light/logo_noColor.png differ diff --git a/DSView/icons/light/manual.png b/DSView/icons/light/manual.png new file mode 100755 index 00000000..b0b6a71f Binary files /dev/null and b/DSView/icons/light/manual.png differ diff --git a/DSView/icons/light/math.png b/DSView/icons/light/math.png new file mode 100755 index 00000000..39168f0d Binary files /dev/null and b/DSView/icons/light/math.png differ diff --git a/DSView/icons/light/maximize.png b/DSView/icons/light/maximize.png new file mode 100755 index 00000000..08d93853 Binary files /dev/null and b/DSView/icons/light/maximize.png differ diff --git a/DSView/icons/light/measure.png b/DSView/icons/light/measure.png new file mode 100755 index 00000000..41c711e7 Binary files /dev/null and b/DSView/icons/light/measure.png differ diff --git a/DSView/icons/light/minimize.png b/DSView/icons/light/minimize.png new file mode 100755 index 00000000..e4be4c91 Binary files /dev/null and b/DSView/icons/light/minimize.png differ diff --git a/DSView/icons/light/moder.png b/DSView/icons/light/moder.png new file mode 100755 index 00000000..09e01af2 Binary files /dev/null and b/DSView/icons/light/moder.png differ diff --git a/DSView/icons/light/modes.png b/DSView/icons/light/modes.png new file mode 100755 index 00000000..62a348b0 Binary files /dev/null and b/DSView/icons/light/modes.png differ diff --git a/DSView/icons/light/nav.png b/DSView/icons/light/nav.png new file mode 100755 index 00000000..1f573672 Binary files /dev/null and b/DSView/icons/light/nav.png differ diff --git a/DSView/icons/light/next.png b/DSView/icons/light/next.png new file mode 100755 index 00000000..ebec6e15 Binary files /dev/null and b/DSView/icons/light/next.png differ diff --git a/DSView/icons/light/oneloop.png b/DSView/icons/light/oneloop.png new file mode 100755 index 00000000..4eee0d71 Binary files /dev/null and b/DSView/icons/light/oneloop.png differ diff --git a/DSView/icons/light/open.png b/DSView/icons/light/open.png new file mode 100755 index 00000000..38cd27cb Binary files /dev/null and b/DSView/icons/light/open.png differ diff --git a/DSView/icons/light/osc.png b/DSView/icons/light/osc.png new file mode 100755 index 00000000..2811d95d Binary files /dev/null and b/DSView/icons/light/osc.png differ diff --git a/DSView/icons/light/params.png b/DSView/icons/light/params.png new file mode 100755 index 00000000..6531faa9 Binary files /dev/null and b/DSView/icons/light/params.png differ diff --git a/DSView/icons/light/pre.png b/DSView/icons/light/pre.png new file mode 100755 index 00000000..600842db Binary files /dev/null and b/DSView/icons/light/pre.png differ diff --git a/DSView/icons/light/protocol.png b/DSView/icons/light/protocol.png new file mode 100755 index 00000000..a726aab6 Binary files /dev/null and b/DSView/icons/light/protocol.png differ diff --git a/DSView/icons/light/repeat.png b/DSView/icons/light/repeat.png new file mode 100755 index 00000000..2a582656 Binary files /dev/null and b/DSView/icons/light/repeat.png differ diff --git a/DSView/icons/light/restore.png b/DSView/icons/light/restore.png new file mode 100755 index 00000000..adc7957a Binary files /dev/null and b/DSView/icons/light/restore.png differ diff --git a/DSView/icons/light/save.png b/DSView/icons/light/save.png new file mode 100755 index 00000000..60804f4c Binary files /dev/null and b/DSView/icons/light/save.png differ diff --git a/DSView/icons/light/search-bar.png b/DSView/icons/light/search-bar.png new file mode 100755 index 00000000..5f85363c Binary files /dev/null and b/DSView/icons/light/search-bar.png differ diff --git a/DSView/icons/light/search.png b/DSView/icons/light/search.png new file mode 100755 index 00000000..5a67758e Binary files /dev/null and b/DSView/icons/light/search.png differ diff --git a/DSView/icons/light/shown.png b/DSView/icons/light/shown.png new file mode 100755 index 00000000..cab7c021 Binary files /dev/null and b/DSView/icons/light/shown.png differ diff --git a/DSView/icons/light/single.png b/DSView/icons/light/single.png new file mode 100755 index 00000000..48862af4 Binary files /dev/null and b/DSView/icons/light/single.png differ diff --git a/DSView/icons/light/start.png b/DSView/icons/light/start.png new file mode 100755 index 00000000..8f1b0053 Binary files /dev/null and b/DSView/icons/light/start.png differ diff --git a/DSView/icons/light/stop.png b/DSView/icons/light/stop.png new file mode 100755 index 00000000..7f2b01e5 Binary files /dev/null and b/DSView/icons/light/stop.png differ diff --git a/DSView/icons/light/support.png b/DSView/icons/light/support.png new file mode 100755 index 00000000..0c8abcb5 Binary files /dev/null and b/DSView/icons/light/support.png differ diff --git a/DSView/icons/light/trigger.png b/DSView/icons/light/trigger.png new file mode 100755 index 00000000..83c1d187 Binary files /dev/null and b/DSView/icons/light/trigger.png differ diff --git a/DSView/icons/light/wait.gif b/DSView/icons/light/wait.gif new file mode 100755 index 00000000..97142aa2 Binary files /dev/null and b/DSView/icons/light/wait.gif differ diff --git a/DSView/icons/lissajous.png b/DSView/icons/lissajous.png new file mode 100755 index 00000000..f767b6b6 Binary files /dev/null and b/DSView/icons/lissajous.png differ diff --git a/DSView/icons/load.gif b/DSView/icons/load.gif deleted file mode 100644 index ffcb5622..00000000 Binary files a/DSView/icons/load.gif and /dev/null differ diff --git a/DSView/icons/logo.png b/DSView/icons/logo.png old mode 100644 new mode 100755 diff --git a/DSView/icons/logo.svg b/DSView/icons/logo.svg deleted file mode 100755 index b713c304..00000000 --- a/DSView/icons/logo.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/DSView/icons/logo_128.png b/DSView/icons/logo_128.png deleted file mode 100755 index 0d46a075..00000000 Binary files a/DSView/icons/logo_128.png and /dev/null differ diff --git a/DSView/icons/logo_16.png b/DSView/icons/logo_16.png deleted file mode 100755 index 67042eb3..00000000 Binary files a/DSView/icons/logo_16.png and /dev/null differ diff --git a/DSView/icons/logo_256.png b/DSView/icons/logo_256.png deleted file mode 100755 index ca65932d..00000000 Binary files a/DSView/icons/logo_256.png and /dev/null differ diff --git a/DSView/icons/logo_32.png b/DSView/icons/logo_32.png deleted file mode 100755 index ed7b1a4e..00000000 Binary files a/DSView/icons/logo_32.png and /dev/null differ diff --git a/DSView/icons/logo_48.png b/DSView/icons/logo_48.png deleted file mode 100755 index eb8b4162..00000000 Binary files a/DSView/icons/logo_48.png and /dev/null differ diff --git a/DSView/icons/logo_64.png b/DSView/icons/logo_64.png deleted file mode 100755 index 8691e890..00000000 Binary files a/DSView/icons/logo_64.png and /dev/null differ diff --git a/DSView/icons/logo_color.png b/DSView/icons/logo_color.png deleted file mode 100755 index 0761dd75..00000000 Binary files a/DSView/icons/logo_color.png and /dev/null differ diff --git a/DSView/icons/logo_noColor.png b/DSView/icons/logo_noColor.png deleted file mode 100755 index a532a178..00000000 Binary files a/DSView/icons/logo_noColor.png and /dev/null differ diff --git a/DSView/icons/mAmplitude.png b/DSView/icons/mAmplitude.png new file mode 100755 index 00000000..68814aa7 Binary files /dev/null and b/DSView/icons/mAmplitude.png differ diff --git a/DSView/icons/mBurst.png b/DSView/icons/mBurst.png new file mode 100755 index 00000000..cb449f9c Binary files /dev/null and b/DSView/icons/mBurst.png differ diff --git a/DSView/icons/mDelay.png b/DSView/icons/mDelay.png new file mode 100755 index 00000000..1a992d1a Binary files /dev/null and b/DSView/icons/mDelay.png differ diff --git a/DSView/icons/mFall.png b/DSView/icons/mFall.png new file mode 100755 index 00000000..1b2a9ac3 Binary files /dev/null and b/DSView/icons/mFall.png differ diff --git a/DSView/icons/mFreq.png b/DSView/icons/mFreq.png new file mode 100755 index 00000000..ed8ccf14 Binary files /dev/null and b/DSView/icons/mFreq.png differ diff --git a/DSView/icons/mHigh.png b/DSView/icons/mHigh.png new file mode 100755 index 00000000..665d4c0c Binary files /dev/null and b/DSView/icons/mHigh.png differ diff --git a/DSView/icons/mLow.png b/DSView/icons/mLow.png new file mode 100755 index 00000000..779a0e16 Binary files /dev/null and b/DSView/icons/mLow.png differ diff --git a/DSView/icons/mMax.png b/DSView/icons/mMax.png new file mode 100755 index 00000000..36572e3a Binary files /dev/null and b/DSView/icons/mMax.png differ diff --git a/DSView/icons/mMean.png b/DSView/icons/mMean.png new file mode 100755 index 00000000..a65dd098 Binary files /dev/null and b/DSView/icons/mMean.png differ diff --git a/DSView/icons/mMin.png b/DSView/icons/mMin.png new file mode 100755 index 00000000..b9779a23 Binary files /dev/null and b/DSView/icons/mMin.png differ diff --git a/DSView/icons/mNduty.png b/DSView/icons/mNduty.png new file mode 100755 index 00000000..fb2ea68e Binary files /dev/null and b/DSView/icons/mNduty.png differ diff --git a/DSView/icons/mNover.png b/DSView/icons/mNover.png new file mode 100755 index 00000000..80342c59 Binary files /dev/null and b/DSView/icons/mNover.png differ diff --git a/DSView/icons/mNwidth.png b/DSView/icons/mNwidth.png new file mode 100755 index 00000000..52782332 Binary files /dev/null and b/DSView/icons/mNwidth.png differ diff --git a/DSView/icons/mPcount.png b/DSView/icons/mPcount.png new file mode 100755 index 00000000..eab79072 Binary files /dev/null and b/DSView/icons/mPcount.png differ diff --git a/DSView/icons/mPduty.png b/DSView/icons/mPduty.png new file mode 100755 index 00000000..ab4a48d3 Binary files /dev/null and b/DSView/icons/mPduty.png differ diff --git a/DSView/icons/mPeriod.png b/DSView/icons/mPeriod.png new file mode 100755 index 00000000..d7722656 Binary files /dev/null and b/DSView/icons/mPeriod.png differ diff --git a/DSView/icons/mPover.png b/DSView/icons/mPover.png new file mode 100755 index 00000000..d7077a8f Binary files /dev/null and b/DSView/icons/mPover.png differ diff --git a/DSView/icons/mPwidth.png b/DSView/icons/mPwidth.png new file mode 100755 index 00000000..1bd4c4b3 Binary files /dev/null and b/DSView/icons/mPwidth.png differ diff --git a/DSView/icons/mRise.png b/DSView/icons/mRise.png new file mode 100755 index 00000000..69eef45f Binary files /dev/null and b/DSView/icons/mRise.png differ diff --git a/DSView/icons/mRms.png b/DSView/icons/mRms.png new file mode 100755 index 00000000..a44db727 Binary files /dev/null and b/DSView/icons/mRms.png differ diff --git a/DSView/icons/mVpp.png b/DSView/icons/mVpp.png new file mode 100755 index 00000000..4c42d1a3 Binary files /dev/null and b/DSView/icons/mVpp.png differ diff --git a/DSView/icons/math.png b/DSView/icons/math.png old mode 100644 new mode 100755 index dbb83a78..a468dc6a Binary files a/DSView/icons/math.png and b/DSView/icons/math.png differ diff --git a/DSView/icons/math_dis.png b/DSView/icons/math_dis.png deleted file mode 100644 index 9850dd5e..00000000 Binary files a/DSView/icons/math_dis.png and /dev/null differ diff --git a/DSView/icons/maximize.png b/DSView/icons/maximize.png deleted file mode 100644 index bb4bffb8..00000000 Binary files a/DSView/icons/maximize.png and /dev/null differ diff --git a/DSView/icons/measure.png b/DSView/icons/measure.png deleted file mode 100644 index 461bb7c9..00000000 Binary files a/DSView/icons/measure.png and /dev/null differ diff --git a/DSView/icons/measure_dis.png b/DSView/icons/measure_dis.png deleted file mode 100644 index 3f152eee..00000000 Binary files a/DSView/icons/measure_dis.png and /dev/null differ diff --git a/DSView/icons/minimize.png b/DSView/icons/minimize.png deleted file mode 100644 index 7c9a9fd3..00000000 Binary files a/DSView/icons/minimize.png and /dev/null differ diff --git a/DSView/icons/moder.png b/DSView/icons/moder.png deleted file mode 100755 index d94f826a..00000000 Binary files a/DSView/icons/moder.png and /dev/null differ diff --git a/DSView/icons/moder_dis.png b/DSView/icons/moder_dis.png deleted file mode 100755 index 8bc44721..00000000 Binary files a/DSView/icons/moder_dis.png and /dev/null differ diff --git a/DSView/icons/modes.png b/DSView/icons/modes.png deleted file mode 100755 index c8e465ce..00000000 Binary files a/DSView/icons/modes.png and /dev/null differ diff --git a/DSView/icons/modes_dis.png b/DSView/icons/modes_dis.png deleted file mode 100755 index 375a2a50..00000000 Binary files a/DSView/icons/modes_dis.png and /dev/null differ diff --git a/DSView/icons/params.png b/DSView/icons/params.png deleted file mode 100644 index d6a9fff7..00000000 Binary files a/DSView/icons/params.png and /dev/null differ diff --git a/DSView/icons/params_dis.png b/DSView/icons/params_dis.png deleted file mode 100644 index af2518a7..00000000 Binary files a/DSView/icons/params_dis.png and /dev/null differ diff --git a/DSView/icons/protocol.png b/DSView/icons/protocol.png deleted file mode 100644 index acf434e2..00000000 Binary files a/DSView/icons/protocol.png and /dev/null differ diff --git a/DSView/icons/protocol_dis.png b/DSView/icons/protocol_dis.png deleted file mode 100644 index 3836866f..00000000 Binary files a/DSView/icons/protocol_dis.png and /dev/null differ diff --git a/DSView/icons/restore.png b/DSView/icons/restore.png deleted file mode 100644 index ceaa6235..00000000 Binary files a/DSView/icons/restore.png and /dev/null differ diff --git a/DSView/icons/search-bar.png b/DSView/icons/search-bar.png deleted file mode 100644 index 1e58096d..00000000 Binary files a/DSView/icons/search-bar.png and /dev/null differ diff --git a/DSView/icons/search-bar_dis.png b/DSView/icons/search-bar_dis.png deleted file mode 100644 index 1b2d6955..00000000 Binary files a/DSView/icons/search-bar_dis.png and /dev/null differ diff --git a/DSView/icons/search.png b/DSView/icons/search.png old mode 100644 new mode 100755 diff --git a/DSView/icons/settings.png b/DSView/icons/settings.png deleted file mode 100644 index b0065b51..00000000 Binary files a/DSView/icons/settings.png and /dev/null differ diff --git a/DSView/icons/showDoc.png b/DSView/icons/showDoc.png deleted file mode 100755 index 7a306d11..00000000 Binary files a/DSView/icons/showDoc.png and /dev/null differ diff --git a/DSView/icons/showDoc25.png b/DSView/icons/showDoc25.png new file mode 100755 index 00000000..c1ea34c4 Binary files /dev/null and b/DSView/icons/showDoc25.png differ diff --git a/DSView/icons/showDoc31.png b/DSView/icons/showDoc31.png new file mode 100755 index 00000000..0bbfebeb Binary files /dev/null and b/DSView/icons/showDoc31.png differ diff --git a/DSView/icons/single.png b/DSView/icons/single.png deleted file mode 100644 index c0565af0..00000000 Binary files a/DSView/icons/single.png and /dev/null differ diff --git a/DSView/icons/single_dis.png b/DSView/icons/single_dis.png deleted file mode 100644 index 4e1221f8..00000000 Binary files a/DSView/icons/single_dis.png and /dev/null differ diff --git a/DSView/icons/slider-handle.png b/DSView/icons/slider-handle.png deleted file mode 100644 index ac1f6e22..00000000 Binary files a/DSView/icons/slider-handle.png and /dev/null differ diff --git a/DSView/icons/start.png b/DSView/icons/start.png deleted file mode 100644 index 4c2d7e61..00000000 Binary files a/DSView/icons/start.png and /dev/null differ diff --git a/DSView/icons/start_dis.png b/DSView/icons/start_dis.png deleted file mode 100644 index 56f6e901..00000000 Binary files a/DSView/icons/start_dis.png and /dev/null differ diff --git a/DSView/icons/stop.png b/DSView/icons/stop.png deleted file mode 100644 index 77aa105d..00000000 Binary files a/DSView/icons/stop.png and /dev/null differ diff --git a/DSView/icons/trigger.png b/DSView/icons/trigger.png deleted file mode 100644 index 6b1ca1b8..00000000 Binary files a/DSView/icons/trigger.png and /dev/null differ diff --git a/DSView/icons/trigger_dis.png b/DSView/icons/trigger_dis.png deleted file mode 100644 index eb401cff..00000000 Binary files a/DSView/icons/trigger_dis.png and /dev/null differ diff --git a/DSView/icons/wait.gif b/DSView/icons/wait.gif deleted file mode 100644 index b3ba36a6..00000000 Binary files a/DSView/icons/wait.gif and /dev/null differ diff --git a/DSView/languages/language.qrc b/DSView/languages/language.qrc new file mode 100755 index 00000000..e0eacdf8 --- /dev/null +++ b/DSView/languages/language.qrc @@ -0,0 +1,6 @@ + + + my_25.qm + qt_25.qm + + diff --git a/DSView/languages/my_25.qm b/DSView/languages/my_25.qm new file mode 100755 index 00000000..2430370f Binary files /dev/null and b/DSView/languages/my_25.qm differ diff --git a/DSView/languages/qt_25.qm b/DSView/languages/qt_25.qm new file mode 100755 index 00000000..3e5c146b Binary files /dev/null and b/DSView/languages/qt_25.qm differ diff --git a/DSView/main.cpp b/DSView/main.cpp old mode 100644 new mode 100755 index e8157ff5..7350d34f --- a/DSView/main.cpp +++ b/DSView/main.cpp @@ -63,12 +63,15 @@ int main(int argc, char *argv[]) struct sr_context *sr_ctx = NULL; const char *open_file = NULL; + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QApplication a(argc, argv); // Set some application metadata QApplication::setApplicationVersion(DS_VERSION_STRING); QApplication::setApplicationName("DSView"); - QApplication::setOrganizationDomain("http://www.DreamSourceLab.com"); + QApplication::setOrganizationName("DreamSourceLab"); + QApplication::setOrganizationDomain("www.DreamSourceLab.com"); // Parse arguments while (1) { @@ -115,7 +118,7 @@ int main(int argc, char *argv[]) } else if (argc - optind == 1) open_file = argv[argc - 1]; - // Initialise DS_RES_PATH + // Initialise DS_RES_PATH QDir dir(QCoreApplication::applicationDirPath()); if (dir.cd("..") && dir.cd("share") && @@ -153,11 +156,6 @@ int main(int argc, char *argv[]) // Initialise the main frame pv::MainFrame w(device_manager, open_file); - //QFile qss(":/stylesheet.qss"); - QFile qss(":darkstyle/style.qss"); - qss.open(QFile::ReadOnly); - a.setStyleSheet(qss.readAll()); - qss.close(); w.show(); w.readSettings(); w.show_doc(); diff --git a/DSView/pv/data/analog.cpp b/DSView/pv/data/analog.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/data/analog.h b/DSView/pv/data/analog.h old mode 100644 new mode 100755 diff --git a/DSView/pv/data/analogsnapshot.cpp b/DSView/pv/data/analogsnapshot.cpp old mode 100644 new mode 100755 index bd73ae91..91e11e6d --- a/DSView/pv/data/analogsnapshot.cpp +++ b/DSView/pv/data/analogsnapshot.cpp @@ -123,18 +123,16 @@ void AnalogSnapshot::first_payload(const sr_datafeed_analog &analog, uint64_t to for (unsigned int i = 0; i < _channel_num; i++) { uint64_t envelop_count = _total_sample_count / EnvelopeScaleFactor; for (unsigned int level = 0; level < ScaleStepCount; level++) { - envelop_count = ((envelop_count + EnvelopeDataUnit - 1) / - EnvelopeDataUnit) * EnvelopeDataUnit; +// envelop_count = ((envelop_count + EnvelopeDataUnit - 1) / +// EnvelopeDataUnit) * EnvelopeDataUnit; + _envelope_levels[i][level].count = envelop_count; + if (envelop_count == 0) + break; _envelope_levels[i][level].samples = (EnvelopeSample*)malloc(envelop_count * sizeof(EnvelopeSample)); - _envelope_levels[i][level].max = (uint8_t *)malloc(envelop_count * _unit_bytes); - _envelope_levels[i][level].min = (uint8_t *)malloc(envelop_count * _unit_bytes); - if (!_envelope_levels[i][level].samples || - !_envelope_levels[i][level].max || - !_envelope_levels[i][level].min) { + if (!_envelope_levels[i][level].samples) { isOk = false; break; } - _envelope_levels[i][level].count = envelop_count; envelop_count = envelop_count / EnvelopeScaleFactor; } if (!isOk) @@ -277,7 +275,7 @@ void AnalogSnapshot::append_payload_to_envelope_levels() if (e0.length == 0) continue; - reallocate_envelope(e0); + //reallocate_envelope(e0); dest_ptr = e0.samples + prev_length; @@ -324,13 +322,13 @@ void AnalogSnapshot::append_payload_to_envelope_levels() if (e.ring_length == prev_length) break; - reallocate_envelope(e); + //reallocate_envelope(e); // Subsample the level lower level const EnvelopeSample *src_ptr = el.samples + prev_length * EnvelopeScaleFactor; - const EnvelopeSample *const end_dest_ptr = e.samples + e.ring_length; - dest_ptr = e.samples + prev_length; + const EnvelopeSample *const end_dest_ptr = (e.ring_length == e.count) ? e.samples : e.samples + e.ring_length; + dest_ptr = (prev_length == e.count) ? e.samples : e.samples + prev_length; while(dest_ptr != end_dest_ptr) { const EnvelopeSample * end_src_ptr = src_ptr + EnvelopeScaleFactor; diff --git a/DSView/pv/data/analogsnapshot.h b/DSView/pv/data/analogsnapshot.h old mode 100644 new mode 100755 index 25d1c0d6..644dbfaa --- a/DSView/pv/data/analogsnapshot.h +++ b/DSView/pv/data/analogsnapshot.h @@ -54,8 +54,6 @@ public: uint64_t length; uint64_t samples_num; EnvelopeSample *samples; - uint8_t *max; - uint8_t *min; }; private: diff --git a/DSView/pv/data/decode/annotation.cpp b/DSView/pv/data/decode/annotation.cpp old mode 100644 new mode 100755 index 3581817d..c433a288 --- a/DSView/pv/data/decode/annotation.cpp +++ b/DSView/pv/data/decode/annotation.cpp @@ -45,7 +45,7 @@ Annotation::Annotation(const srd_proto_data *const pdata) : _type = pda->ann_type; const char *const *annotations = (char**)pda->ann_text; - while(*annotations) { + while(*annotations) { _annotations.push_back(QString::fromUtf8(*annotations)); annotations++; } diff --git a/DSView/pv/data/decode/annotation.h b/DSView/pv/data/decode/annotation.h old mode 100644 new mode 100755 diff --git a/DSView/pv/data/decode/decoder.cpp b/DSView/pv/data/decode/decoder.cpp old mode 100644 new mode 100755 index fed10d77..486340cc --- a/DSView/pv/data/decode/decoder.cpp +++ b/DSView/pv/data/decode/decoder.cpp @@ -43,7 +43,8 @@ Decoder::~Decoder() { for (map::const_iterator i = _options_back.begin(); i != _options_back.end(); i++) - g_variant_unref((*i).second); + if ((*i).second) + g_variant_unref((*i).second); } const srd_decoder* Decoder::decoder() const @@ -81,7 +82,9 @@ const std::map& Decoder::options() const void Decoder::set_option(const char *id, GVariant *value) { assert(value); - g_variant_unref(_options_back[id]); + if (_options_back[id]) { + g_variant_unref(_options_back[id]); + } g_variant_ref(value); _options_back[id] = value; _setted = true; diff --git a/DSView/pv/data/decode/decoder.h b/DSView/pv/data/decode/decoder.h old mode 100644 new mode 100755 diff --git a/DSView/pv/data/decode/row.cpp b/DSView/pv/data/decode/row.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/data/decode/row.h b/DSView/pv/data/decode/row.h old mode 100644 new mode 100755 diff --git a/DSView/pv/data/decode/rowdata.cpp b/DSView/pv/data/decode/rowdata.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/data/decode/rowdata.h b/DSView/pv/data/decode/rowdata.h old mode 100644 new mode 100755 diff --git a/DSView/pv/data/decodermodel.cpp b/DSView/pv/data/decodermodel.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/data/decodermodel.h b/DSView/pv/data/decodermodel.h old mode 100644 new mode 100755 diff --git a/DSView/pv/data/decoderstack.cpp b/DSView/pv/data/decoderstack.cpp old mode 100644 new mode 100755 index 8e6a8039..61a66dac --- a/DSView/pv/data/decoderstack.cpp +++ b/DSView/pv/data/decoderstack.cpp @@ -478,7 +478,6 @@ void DecoderStack::decode_data( } uint64_t entry_cnt = 0; - uint8_t chunk_type = 0; uint64_t i = decode_start; char *error = NULL; while(!boost::this_thread::interruption_requested() && @@ -492,6 +491,7 @@ void DecoderStack::decode_data( int sig_index = logic_di->dec_channelmap[j]; if (sig_index == -1) { chunk.push_back(NULL); + chunk_const.push_back(0); } else { if (_snapshot->has_data(sig_index)) { chunk.push_back(_snapshot->get_samples(i, chunk_end, sig_index)); @@ -502,57 +502,17 @@ void DecoderStack::decode_data( } } } + if (chunk_end > decode_end) + chunk_end = decode_end; if (chunk_end - i > MaxChunkSize) chunk_end = i + MaxChunkSize; - if (srd_session_send(session, chunk_type, i, chunk_end, - chunk.data(), chunk_const.data(), &error) != SRD_OK) { + if (srd_session_send(session, i, chunk_end, + chunk.data(), chunk_const.data(), chunk_end - i, &error) != SRD_OK) { _error_message = QString::fromLocal8Bit(error); break; } - - if (logic_di && logic_di->logic_mask != 0 && logic_di->cur_pos < decode_end) { - uint64_t cur_pos = logic_di->cur_pos; - uint64_t sample; - if (logic_di->edge_index == -1) { - std::vector pos_vector; - cur_pos++; - for (int j =0 ; j < logic_di->dec_num_channels; j++) { - int index = logic_di->dec_channelmap[j]; - if (index != -1 && (logic_di->logic_mask & (1 << j))) { - bool last_sample = _snapshot->get_sample(cur_pos - 1, index); - pos_vector.push_back(cur_pos); - _snapshot->get_nxt_edge(pos_vector.back(), last_sample, decode_end, 1, index); - } - } - cur_pos = *std::min_element(pos_vector.begin(), pos_vector.end()); - } else { - bool last_sample = _snapshot->get_sample(cur_pos, logic_di->edge_index); - do { - sample = 0; - cur_pos++; - if (!_snapshot->get_nxt_edge(cur_pos, last_sample, decode_end, 1, logic_di->edge_index)) - break; - for (int j =0 ; j < logic_di->dec_num_channels; j++) { - if (logic_di->logic_mask & (1 << j)) { - int index = logic_di->dec_channelmap[j]; - bool index_sample = _snapshot->get_sample(cur_pos, index); - sample += index_sample << j; - if (index == logic_di->edge_index) - last_sample = index_sample; - } - } - } while(sample != logic_di->exp_logic); - } - - i = cur_pos; - if (i >= decode_end) - i = decode_end; - chunk_type = 0; - } else { - i = chunk_end + 1; - chunk_type = 1; - } + i = chunk_end; { boost::lock_guard lock(_output_mutex); diff --git a/DSView/pv/data/decoderstack.h b/DSView/pv/data/decoderstack.h old mode 100644 new mode 100755 diff --git a/DSView/pv/data/dso.cpp b/DSView/pv/data/dso.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/data/dso.h b/DSView/pv/data/dso.h old mode 100644 new mode 100755 diff --git a/DSView/pv/data/dsosnapshot.cpp b/DSView/pv/data/dsosnapshot.cpp old mode 100644 new mode 100755 index f90b8e71..ae576be2 --- a/DSView/pv/data/dsosnapshot.cpp +++ b/DSView/pv/data/dsosnapshot.cpp @@ -163,16 +163,20 @@ void DsoSnapshot::append_payload(const sr_datafeed_dso &dso) append_data(dso.data, dso.num_samples, _instant); // Generate the first mip-map from the data + //if (_envelope_en) + // append_payload_to_envelope_levels(dso.samplerate_tog); if (_envelope_en) - append_payload_to_envelope_levels(dso.samplerate_tog); + append_payload_to_envelope_levels(true); } } void DsoSnapshot::append_data(void *data, uint64_t samples, bool instant) { if (instant) { + if(_sample_count + samples > _total_sample_count) + samples = _total_sample_count - _sample_count; memcpy((uint8_t*)_data + _sample_count * _channel_num, data, samples*_channel_num); - _sample_count = (_sample_count + samples) % (_total_sample_count + 1); + _sample_count += samples; } else { memcpy((uint8_t*)_data, data, samples*_channel_num); _sample_count = samples; @@ -304,12 +308,17 @@ void DsoSnapshot::append_payload_to_envelope_levels(bool header) const Envelope &el = _envelope_levels[i][level-1]; // Expand the data buffer to fit the new samples - prev_length = e.length; + if (header) + prev_length = 0; + else + prev_length = e.length; e.length = el.length / EnvelopeScaleFactor; // Break off if there are no more samples to computed // if (e.length == prev_length) // break; + if (e.length == 0) + break; if (e.length == prev_length) prev_length = 0; diff --git a/DSView/pv/data/dsosnapshot.h b/DSView/pv/data/dsosnapshot.h old mode 100644 new mode 100755 index 7b56c47a..aa19cca5 --- a/DSView/pv/data/dsosnapshot.h +++ b/DSView/pv/data/dsosnapshot.h @@ -82,7 +82,8 @@ public: void clear(); void init(); - void first_payload(const sr_datafeed_dso &dso, uint64_t total_sample_count, std::map ch_enable, bool instant); + void first_payload(const sr_datafeed_dso &dso, uint64_t total_sample_count, + std::map ch_enable, bool instant); void append_payload(const sr_datafeed_dso &dso); diff --git a/DSView/pv/data/group.cpp b/DSView/pv/data/group.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/data/group.h b/DSView/pv/data/group.h old mode 100644 new mode 100755 diff --git a/DSView/pv/data/groupsnapshot.cpp b/DSView/pv/data/groupsnapshot.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/data/groupsnapshot.h b/DSView/pv/data/groupsnapshot.h old mode 100644 new mode 100755 diff --git a/DSView/pv/data/logic.cpp b/DSView/pv/data/logic.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/data/logic.h b/DSView/pv/data/logic.h old mode 100644 new mode 100755 diff --git a/DSView/pv/data/logicsnapshot.cpp b/DSView/pv/data/logicsnapshot.cpp old mode 100644 new mode 100755 index d0cfec73..05426722 --- a/DSView/pv/data/logicsnapshot.cpp +++ b/DSView/pv/data/logicsnapshot.cpp @@ -222,11 +222,14 @@ void LogicSnapshot::append_cross_payload( _src_ptr = logic.data; uint64_t len = logic.length; + // samples not accurate, lead to a larger _sampole_count + // _sample_count should be fixed in the last packet + // so _total_sample_count must be align to LeafBlock uint64_t samples = ceil(logic.length * 8.0 / _channel_num); if (_sample_count + samples < _total_sample_count) { _sample_count += samples; } else { - len = ceil((_total_sample_count - _sample_count) * _channel_num / 8.0); + //len = ceil((_total_sample_count - _sample_count) * _channel_num / 8.0); _sample_count = _total_sample_count; } @@ -471,7 +474,7 @@ const uint8_t *LogicSnapshot::get_samples(uint64_t start_sample, uint64_t &end_s { //assert(data); assert(start_sample < get_sample_count()); - assert(end_sample < get_sample_count()); + assert(end_sample <= get_sample_count()); assert(start_sample <= end_sample); int order = get_ch_order(sig_index); @@ -481,7 +484,7 @@ const uint8_t *LogicSnapshot::get_samples(uint64_t start_sample, uint64_t &end_s end_sample = (root_index << (LeafBlockPower + RootScalePower)) + (root_pos << LeafBlockPower) + ~(~0ULL << LeafBlockPower); - end_sample = min(end_sample, get_sample_count() - 1); + end_sample = min(end_sample + 1, get_sample_count()); if (order == -1 || _ch_data[order][root_index].lbp[root_pos] == NULL) diff --git a/DSView/pv/data/logicsnapshot.h b/DSView/pv/data/logicsnapshot.h old mode 100644 new mode 100755 diff --git a/DSView/pv/data/mathstack.cpp b/DSView/pv/data/mathstack.cpp old mode 100644 new mode 100755 index e33df5e8..5b9ce7a3 --- a/DSView/pv/data/mathstack.cpp +++ b/DSView/pv/data/mathstack.cpp @@ -36,39 +36,75 @@ using namespace std; namespace pv { namespace data { -const QString MathStack::windows_support[5] = { - QT_TR_NOOP("Rectangle"), - QT_TR_NOOP("Hann"), - QT_TR_NOOP("Hamming"), - QT_TR_NOOP("Blackman"), - QT_TR_NOOP("Flat_top") +const int MathStack::EnvelopeScalePower = 8; +const int MathStack::EnvelopeScaleFactor = 1 << EnvelopeScalePower; +const float MathStack::LogEnvelopeScaleFactor = logf(EnvelopeScaleFactor); +const uint64_t MathStack::EnvelopeDataUnit = 4*1024; // bytes + +const uint64_t MathStack::vDialValue[MathStack::vDialValueCount] = { + 1, + 2, + 5, + 10, + 20, + 50, + 100, + 200, + 500, + 1000, + 2000, + 5000, + 10000, + 20000, + 50000, + 100000, + 200000, + 500000, + 1000000, +}; +const QString MathStack::vDialAddUnit[MathStack::vDialUnitCount] = { + "mV", + "V", +}; +const QString MathStack::vDialMulUnit[MathStack::vDialUnitCount] = { + "mV*V", + "V*V", +}; +const QString MathStack::vDialDivUnit[MathStack::vDialUnitCount] = { + "mV/V", + "V/V", }; -const uint64_t MathStack::length_support[5] = { - 1024, - 2048, - 4096, - 8192, - 16384, -}; - -MathStack::MathStack(pv::SigSession &session, int index) : +MathStack::MathStack(pv::SigSession &session, + boost::shared_ptr dsoSig1, + boost::shared_ptr dsoSig2, + MathType type) : _session(session), - _index(index), - _dc_ignore(true), - _sample_interval(1), + _dsoSig1(dsoSig1), + _dsoSig2(dsoSig2), + _type(type), + _sample_num(0), + _total_sample_num(0), _math_state(Init), - _fft_plan(NULL) + _envelope_en(false), + _envelope_done(false) { + memset(_envelope_level, 0, sizeof(_envelope_level)); } MathStack::~MathStack() { - _xn.clear(); - _xk.clear(); - _power_spectrum.clear(); - if (_fft_plan) - fftw_destroy_plan(_fft_plan); + _math.clear(); + free_envelop(); +} + +void MathStack::free_envelop() +{ + BOOST_FOREACH(Envelope &e, _envelope_level) { + if (e.samples) + free(e.samples); + } + memset(_envelope_level, 0, sizeof(_envelope_level)); } void MathStack::clear() @@ -77,11 +113,13 @@ void MathStack::clear() void MathStack::init() { + _sample_num = 0; + _envelope_done = false; } -int MathStack::get_index() const +MathStack::MathType MathStack::get_type() const { - return _index; + return _type; } uint64_t MathStack::get_sample_num() const @@ -89,159 +127,352 @@ uint64_t MathStack::get_sample_num() const return _sample_num; } -void MathStack::set_sample_num(uint64_t num) +void MathStack::realloc(uint64_t num) { - _sample_num = num; - _xn.resize(_sample_num); - _xk.resize(_sample_num); - _power_spectrum.resize(_sample_num/2+1); - _fft_plan = fftw_plan_r2r_1d(_sample_num, _xn.data(), _xk.data(), - FFTW_R2HC, FFTW_ESTIMATE); -} + if (num != _total_sample_num) { + free_envelop(); + _total_sample_num = num; -int MathStack::get_windows_index() const -{ - return _windows_index; -} - -void MathStack::set_windows_index(int index) -{ - _windows_index = index; -} - -bool MathStack::dc_ignored() const -{ - return _dc_ignore; -} - -void MathStack::set_dc_ignore(bool ignore) -{ - _dc_ignore = ignore; -} - -int MathStack::get_sample_interval() const -{ - return _sample_interval; -} - -void MathStack::set_sample_interval(int interval) -{ - _sample_interval = interval; -} - -const std::vector MathStack::get_windows_support() const -{ - std::vector windows; - for (size_t i = 0; i < sizeof(windows_support)/sizeof(windows_support[0]); i++) - { - windows.push_back(windows_support[i]); + _math.resize(_total_sample_num); + uint64_t envelop_count = _total_sample_num / EnvelopeScaleFactor; + for (unsigned int level = 0; level < ScaleStepCount; level++) { + envelop_count = ((envelop_count + EnvelopeDataUnit - 1) / + EnvelopeDataUnit) * EnvelopeDataUnit; + _envelope_level[level].samples = (EnvelopeSample*)malloc(envelop_count * sizeof(EnvelopeSample)); + envelop_count = envelop_count / EnvelopeScaleFactor; + } } - return windows; } -const std::vector MathStack::get_length_support() const +void MathStack::enable_envelope(bool enable) { - std::vector length; - for (size_t i = 0; i < sizeof(length_support)/sizeof(length_support[0]); i++) - { - length.push_back(length_support[i]); + if (!_envelope_done && enable) + append_to_envelope_level(true); + _envelope_en = enable; +} + +uint64_t MathStack::default_vDialValue() +{ + uint64_t value = 0; + view::dslDial *dial1 = _dsoSig1->get_vDial(); + view::dslDial *dial2 = _dsoSig1->get_vDial(); + const uint64_t dial1_value = dial1->get_value() * dial1->get_factor(); + const uint64_t dial2_value = dial2->get_value() * dial2->get_factor(); + + switch(_type) { + case MATH_ADD: + case MATH_SUB: + value = max(dial1_value, dial2_value); + break; + case MATH_MUL: + value = dial1_value * dial2_value / 1000.0; + break; + case MATH_DIV: + value = dial1_value * 1000.0 / dial2_value; + break; } - return length; -} -const std::vector MathStack::get_fft_spectrum() const -{ - std::vector empty; - if (_math_state == Stopped) - return _power_spectrum; - else - return empty; -} - -double MathStack::get_fft_spectrum(uint64_t index) -{ - double ret = -1; - if (_math_state == Stopped && index < _power_spectrum.size()) - ret = _power_spectrum[index]; - - return ret; -} - -void MathStack::calc_fft() -{ - _math_state = Running; - // Get the dso data - boost::shared_ptr data; - boost::shared_ptr dsoSig; - BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { - if ((dsoSig = dynamic_pointer_cast(s))) { - if (dsoSig->get_index() == _index && dsoSig->enabled()) { - data = dsoSig->dso_data(); - break; - } + for (int i = 0; i < vDialValueCount; i++) { + if (vDialValue[i] >= value) { + value = vDialValue[i]; + break; } } - if (!data) - return; + return value; +} - // Check we have a snapshot of data +view::dslDial * MathStack::get_vDial() +{ + QVector vValue; + QVector vUnit; + view::dslDial *dial1 = _dsoSig1->get_vDial(); + view::dslDial *dial2 = _dsoSig2->get_vDial(); + const uint64_t dial1_min = dial1->get_value(0) * dial1->get_factor(); + const uint64_t dial1_max = dial1->get_value(dial1->get_count() - 1) * dial1->get_factor(); + const uint64_t dial2_min = dial2->get_value(0) * dial2->get_factor(); + const uint64_t dial2_max = dial2->get_value(dial2->get_count() - 1) * dial2->get_factor(); + + switch(_type) { + case MATH_ADD: + case MATH_SUB: + for (int i = 0; i < vDialValueCount; i++) { + if (vDialValue[i] < min(dial1_min, dial2_min)) + continue; + vValue.append(vDialValue[i]); + if (vDialValue[i] > max(dial1_max, dial2_max)) + break; + } + for(int i = 0; i < vDialUnitCount; i++) + vUnit.append(vDialAddUnit[i]); + break; + case MATH_MUL: + for (int i = 0; i < vDialValueCount; i++) { + if (vDialValue[i] < dial1_min * dial2_min / 1000.0) + continue; + vValue.append(vDialValue[i]); + if (vDialValue[i] > dial1_max * dial2_max / 1000.0) + break; + } + for(int i = 0; i < vDialUnitCount; i++) + vUnit.append(vDialMulUnit[i]); + break; + case MATH_DIV: + for (int i = 0; i < vDialValueCount; i++) { + if (vDialValue[i] < min(dial1_min * 1000.0 / dial2_max, dial2_min * 1000.0 / dial1_max)) + continue; + vValue.append(vDialValue[i]); + if (vDialValue[i] > max(dial1_max * 1000.0 / dial2_min, dial2_max * 1000.0 / dial1_min)) + break; + } + for(int i = 0; i < vDialUnitCount; i++) + vUnit.append(vDialDivUnit[i]); + break; + } + + view::dslDial *vDial = new view::dslDial(vValue.count(), vDialValueStep, vValue, vUnit); + return vDial; +} + +QString MathStack::get_unit(int level) +{ + if (level >= vDialUnitCount) + return tr(" "); + + QString unit; + switch(_type) { + case MATH_ADD: + case MATH_SUB: + unit = vDialAddUnit[level]; + break; + case MATH_MUL: + unit = vDialMulUnit[level]; + break; + case MATH_DIV: + unit = vDialDivUnit[level]; + break; + } + + return unit; +} + +double MathStack::get_math_scale() +{ + double scale = 0; + switch(_type) { + case MATH_ADD: + case MATH_SUB: + scale = 1.0 / DS_CONF_DSO_VDIVS; + break; + case MATH_MUL: + //scale = 1.0 / (DS_CONF_DSO_VDIVS * DS_CONF_DSO_VDIVS); + scale = 1.0 / DS_CONF_DSO_VDIVS; + break; + case MATH_DIV: + scale = 1.0 / DS_CONF_DSO_VDIVS; + break; + } + + return scale; +} + +const double* MathStack::get_math(uint64_t start) const +{ + return _math.data() + start; +} + +void MathStack::get_math_envelope_section(EnvelopeSection &s, + uint64_t start, uint64_t end, float min_length) const +{ + assert(end <= get_sample_num()); + assert(start <= end); + assert(min_length > 0); + + if (!_envelope_done) { + s.length = 0; + return; + } + + const unsigned int min_level = max((int)floorf(logf(min_length) / + LogEnvelopeScaleFactor) - 1, 0); + const unsigned int scale_power = (min_level + 1) * + EnvelopeScalePower; + start >>= scale_power; + end >>= scale_power; + + s.start = start << scale_power; + s.scale = 1 << scale_power; + if (_envelope_level[min_level].length == 0) + s.length = 0; + else + s.length = end - start; + + s.samples = _envelope_level[min_level].samples + start; +} + +void MathStack::calc_math() +{ + _math_state = Running; + + const boost::shared_ptr data = _dsoSig1->dso_data(); const deque< boost::shared_ptr > &snapshots = data->get_snapshots(); if (snapshots.empty()) return; - _snapshot = snapshots.front(); - if (_snapshot->get_sample_count() < _sample_num*_sample_interval) + const boost::shared_ptr &snapshot = + snapshots.front(); + if (snapshot->empty()) return; - // Get the samplerate - _samplerate = data->samplerate(); - if (_samplerate == 0.0) - _samplerate = 1.0; + if (_math.size() < _total_sample_num) + return; - // prepare _xn data - const double offset = dsoSig->get_hw_offset(); - const double vscale = dsoSig->get_vDialValue() * dsoSig->get_factor() * DS_CONF_DSO_VDIVS / (1000*255.0); - const uint16_t step = _snapshot->get_channel_num() * _sample_interval; - const uint8_t *const samples = _snapshot->get_samples(0, _sample_num*_sample_interval-1, _index); - double wsum = 0; - for (unsigned int i = 0; i < _sample_num; i++) { - double w = window(i, _windows_index); - _xn[i] = ((double)samples[i*step] - offset) * vscale * w; - wsum += w; + if (!_dsoSig1->enabled() || !_dsoSig2->enabled()) + return; + + const double scale1 = _dsoSig1->get_vDialValue() / 1000.0 * _dsoSig1->get_factor() * DS_CONF_DSO_VDIVS * + _dsoSig1->get_scale() / _dsoSig1->get_view_rect().height(); + const double delta1 = _dsoSig1->get_hw_offset() * scale1; + + const double scale2 = _dsoSig2->get_vDialValue() / 1000.0 * _dsoSig2->get_factor() * DS_CONF_DSO_VDIVS * + _dsoSig2->get_scale() / _dsoSig2->get_view_rect().height(); + const double delta2 = _dsoSig2->get_hw_offset() * scale2; + + const int index1 = _dsoSig1->get_index(); + const int index2 = _dsoSig2->get_index(); + + const int num_channels = snapshot->get_channel_num(); + const uint8_t* value = snapshot->get_samples(0, 0, 0); + _sample_num = snapshot->get_sample_count(); + assert(_sample_num <= _total_sample_num); + + double value1, value2; + for (uint64_t sample = 0; sample < _sample_num; sample++) { + value1 = value[sample * num_channels + index1]; + value2 = value[sample * num_channels + index2]; + switch(_type) { + case MATH_ADD: + _math[sample] = (delta1 - scale1 * value1) + (delta2 - scale2 * value2); + break; + case MATH_SUB: + _math[sample] = (delta1 - scale1 * value1) - (delta2 - scale2 * value2); + break; + case MATH_MUL: + _math[sample] = (delta1 - scale1 * value1) * (delta2 - scale2 * value2); + break; + case MATH_DIV: + _math[sample] = (delta1 - scale1 * value1) / (delta2 - scale2 * value2); + break; + } } - // fft - fftw_execute(_fft_plan); - - // calculate power spectrum - _power_spectrum[0] = abs(_xk[0])/wsum; /* DC component */ - for (unsigned int k = 1; k < (_sample_num + 1) / 2; ++k) /* (k < N/2 rounded up) */ - _power_spectrum[k] = sqrt((_xk[k]*_xk[k] + _xk[_sample_num-k]*_xk[_sample_num-k]) * 2) / wsum; - if (_sample_num % 2 == 0) /* N is even */ - _power_spectrum[_sample_num/2] = abs(_xk[_sample_num/2])/wsum; /* Nyquist freq. */ + if (_envelope_en) + append_to_envelope_level(true); + // stop _math_state = Stopped; } -double MathStack::window(uint64_t i, int type) +void MathStack::reallocate_envelope(Envelope &e) { - const double n_m_1 = _sample_num-1; - switch(type) { - case 1: // Hann window - return 0.5*(1-cos(2*PI*i/n_m_1)); - case 2: // Hamming window - return 0.54-0.46*cos(2*PI*i/n_m_1); - case 3: // Blackman window - return 0.42659-0.49656*cos(2*PI*i/n_m_1) + 0.076849*cos(4*PI*i/n_m_1); - case 4: // Flat_top window - return 1-1.93*cos(2*PI*i/n_m_1)+1.29*cos(4*PI*i/n_m_1)- - 0.388*cos(6*PI*i/n_m_1)+0.028*cos(8*PI*i/n_m_1); - default: - return 1; + const uint64_t new_data_length = ((e.length + EnvelopeDataUnit - 1) / + EnvelopeDataUnit) * EnvelopeDataUnit; + if (new_data_length > e.data_length) + { + e.data_length = new_data_length; } } +void MathStack::append_to_envelope_level(bool header) +{ + Envelope &e0 = _envelope_level[0]; + uint64_t prev_length; + EnvelopeSample *dest_ptr; + + if (header) + prev_length = 0; + else + prev_length = e0.length; + e0.length = _sample_num / EnvelopeScaleFactor; + + if (e0.length == 0) + return; + if (e0.length == prev_length) + prev_length = 0; + + // Expand the data buffer to fit the new samples + reallocate_envelope(e0); + + dest_ptr = e0.samples + prev_length; + + // Iterate through the samples to populate the first level mipmap + const double *const stop_src_ptr = (double*)_math.data() + + e0.length * EnvelopeScaleFactor; + for (const double *src_ptr = (double*)_math.data() + + prev_length * EnvelopeScaleFactor; + src_ptr < stop_src_ptr; src_ptr += EnvelopeScaleFactor) + { + const double * begin_src_ptr = + src_ptr; + const double *const end_src_ptr = + src_ptr + EnvelopeScaleFactor; + + EnvelopeSample sub_sample; + sub_sample.min = *begin_src_ptr; + sub_sample.max = *begin_src_ptr; + //begin_src_ptr += _channel_num; + while (begin_src_ptr < end_src_ptr) + { + sub_sample.min = min(sub_sample.min, *begin_src_ptr); + sub_sample.max = max(sub_sample.max, *begin_src_ptr); + begin_src_ptr ++; + } + *dest_ptr++ = sub_sample; + } + + // Compute higher level mipmaps + for (unsigned int level = 1; level < ScaleStepCount; level++) + { + Envelope &e = _envelope_level[level]; + const Envelope &el = _envelope_level[level-1]; + + // Expand the data buffer to fit the new samples + prev_length = e.length; + e.length = el.length / EnvelopeScaleFactor; + + // Break off if there are no more samples to computed +// if (e.length == prev_length) +// break; + if (e.length == prev_length) + prev_length = 0; + + reallocate_envelope(e); + + // Subsample the level lower level + const EnvelopeSample *src_ptr = + el.samples + prev_length * EnvelopeScaleFactor; + const EnvelopeSample *const end_dest_ptr = e.samples + e.length; + for (dest_ptr = e.samples + prev_length; + dest_ptr < end_dest_ptr; dest_ptr++) + { + const EnvelopeSample *const end_src_ptr = + src_ptr + EnvelopeScaleFactor; + + EnvelopeSample sub_sample = *src_ptr++; + while (src_ptr < end_src_ptr) + { + sub_sample.min = min(sub_sample.min, src_ptr->min); + sub_sample.max = max(sub_sample.max, src_ptr->max); + src_ptr++; + } + + *dest_ptr = sub_sample; + } + } + + _envelope_done = true; +} + } // namespace data } // namespace pv diff --git a/DSView/pv/data/mathstack.h b/DSView/pv/data/mathstack.h old mode 100644 new mode 100755 index 88c5fca3..ffb80db4 --- a/DSView/pv/data/mathstack.h +++ b/DSView/pv/data/mathstack.h @@ -29,8 +29,6 @@ #include #include -#include - #include #include @@ -40,6 +38,7 @@ class SigSession; namespace view { class DsoSignal; +class dslDial; } namespace data { @@ -51,10 +50,6 @@ class MathStack : public QObject, public SignalData { Q_OBJECT -private: - static const QString windows_support[5]; - static const uint64_t length_support[5]; - public: enum math_state { Init, @@ -62,56 +57,95 @@ public: Running }; + enum MathType { + MATH_ADD, + MATH_SUB, + MATH_MUL, + MATH_DIV, + }; + + struct EnvelopeSample + { + double min; + double max; + }; + + struct EnvelopeSection + { + uint64_t start; + unsigned int scale; + uint64_t length; + EnvelopeSample *samples; + }; + +private: + struct Envelope + { + uint64_t length; + uint64_t data_length; + EnvelopeSample *samples; + }; + +private: + static const unsigned int ScaleStepCount = 10; + static const int EnvelopeScalePower; + static const int EnvelopeScaleFactor; + static const float LogEnvelopeScaleFactor; + static const uint64_t EnvelopeDataUnit; + + static const uint64_t vDialValueStep = 1000; + static const int vDialValueCount = 19; + static const uint64_t vDialValue[vDialValueCount]; + static const int vDialUnitCount = 2; + static const QString vDialAddUnit[vDialUnitCount]; + static const QString vDialMulUnit[vDialUnitCount]; + static const QString vDialDivUnit[vDialUnitCount]; + public: - MathStack(pv::SigSession &_session, int index); + MathStack(pv::SigSession &_session, + boost::shared_ptr dsoSig1, + boost::shared_ptr dsoSig2, MathType type); virtual ~MathStack(); void clear(); void init(); + void free_envelop(); + void realloc(uint64_t num); - int get_index() const; - + MathType get_type() const; uint64_t get_sample_num() const; - void set_sample_num(uint64_t num); - int get_windows_index() const; - void set_windows_index(int index); + void enable_envelope(bool enable); - const std::vector get_windows_support() const; - const std::vector get_length_support() const; + uint64_t default_vDialValue(); + view::dslDial *get_vDial(); + QString get_unit(int level); + double get_math_scale(); - bool dc_ignored() const; - void set_dc_ignore(bool ignore); + const double *get_math(uint64_t start) const; + void get_math_envelope_section(EnvelopeSection &s, + uint64_t start, uint64_t end, float min_length) const; - int get_sample_interval() const; - void set_sample_interval(int interval); - - const std::vector get_fft_spectrum() const; - double get_fft_spectrum(uint64_t index); - - void calc_fft(); - - double window(uint64_t i, int type); + void calc_math(); + void reallocate_envelope(Envelope &e); + void append_to_envelope_level(bool header); signals: private: pv::SigSession &_session; + boost::shared_ptr _dsoSig1; + boost::shared_ptr _dsoSig2; - int _index; + MathType _type; uint64_t _sample_num; - int _windows_index; - bool _dc_ignore; - int _sample_interval; - - boost::shared_ptr _snapshot; - - std::unique_ptr _math_thread; + uint64_t _total_sample_num; math_state _math_state; - fftw_plan _fft_plan; - std::vector _xn; - std::vector _xk; - std::vector _power_spectrum; + struct Envelope _envelope_level[ScaleStepCount]; + std::vector _math; + + bool _envelope_en; + bool _envelope_done; }; } // namespace data diff --git a/DSView/pv/data/signaldata.cpp b/DSView/pv/data/signaldata.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/data/signaldata.h b/DSView/pv/data/signaldata.h old mode 100644 new mode 100755 diff --git a/DSView/pv/data/snapshot.cpp b/DSView/pv/data/snapshot.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/data/snapshot.h b/DSView/pv/data/snapshot.h old mode 100644 new mode 100755 diff --git a/DSView/pv/data/spectrumstack.cpp b/DSView/pv/data/spectrumstack.cpp new file mode 100755 index 00000000..d040eb9b --- /dev/null +++ b/DSView/pv/data/spectrumstack.cpp @@ -0,0 +1,247 @@ +/* + * This file is part of the PulseView project. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "spectrumstack.h" + +#include +#include + +#include +#include +#include +#include + +#define PI 3.1415 + +using namespace boost; +using namespace std; + +namespace pv { +namespace data { + +const QString SpectrumStack::windows_support[5] = { + QT_TR_NOOP("Rectangle"), + QT_TR_NOOP("Hann"), + QT_TR_NOOP("Hamming"), + QT_TR_NOOP("Blackman"), + QT_TR_NOOP("Flat_top") +}; + +const uint64_t SpectrumStack::length_support[5] = { + 1024, + 2048, + 4096, + 8192, + 16384, +}; + +SpectrumStack::SpectrumStack(pv::SigSession &session, int index) : + _session(session), + _index(index), + _dc_ignore(true), + _sample_interval(1), + _spectrum_state(Init), + _fft_plan(NULL) +{ +} + +SpectrumStack::~SpectrumStack() +{ + _xn.clear(); + _xk.clear(); + _power_spectrum.clear(); + if (_fft_plan) + fftw_destroy_plan(_fft_plan); +} + +void SpectrumStack::clear() +{ +} + +void SpectrumStack::init() +{ +} + +int SpectrumStack::get_index() const +{ + return _index; +} + +uint64_t SpectrumStack::get_sample_num() const +{ + return _sample_num; +} + +void SpectrumStack::set_sample_num(uint64_t num) +{ + _sample_num = num; + _xn.resize(_sample_num); + _xk.resize(_sample_num); + _power_spectrum.resize(_sample_num/2+1); + _fft_plan = fftw_plan_r2r_1d(_sample_num, _xn.data(), _xk.data(), + FFTW_R2HC, FFTW_ESTIMATE); +} + +int SpectrumStack::get_windows_index() const +{ + return _windows_index; +} + +void SpectrumStack::set_windows_index(int index) +{ + _windows_index = index; +} + +bool SpectrumStack::dc_ignored() const +{ + return _dc_ignore; +} + +void SpectrumStack::set_dc_ignore(bool ignore) +{ + _dc_ignore = ignore; +} + +int SpectrumStack::get_sample_interval() const +{ + return _sample_interval; +} + +void SpectrumStack::set_sample_interval(int interval) +{ + _sample_interval = interval; +} + +const std::vector SpectrumStack::get_windows_support() const +{ + std::vector windows; + for (size_t i = 0; i < sizeof(windows_support)/sizeof(windows_support[0]); i++) + { + windows.push_back(windows_support[i]); + } + return windows; +} + +const std::vector SpectrumStack::get_length_support() const +{ + std::vector length; + for (size_t i = 0; i < sizeof(length_support)/sizeof(length_support[0]); i++) + { + length.push_back(length_support[i]); + } + return length; +} + +const std::vector SpectrumStack::get_fft_spectrum() const +{ + std::vector empty; + if (_spectrum_state == Stopped) + return _power_spectrum; + else + return empty; +} + +double SpectrumStack::get_fft_spectrum(uint64_t index) +{ + double ret = -1; + if (_spectrum_state == Stopped && index < _power_spectrum.size()) + ret = _power_spectrum[index]; + + return ret; +} + +void SpectrumStack::calc_fft() +{ + _spectrum_state = Running; + // Get the dso data + boost::shared_ptr data; + boost::shared_ptr dsoSig; + BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { + if ((dsoSig = dynamic_pointer_cast(s))) { + if (dsoSig->get_index() == _index && dsoSig->enabled()) { + data = dsoSig->dso_data(); + break; + } + } + } + + if (!data) + return; + + // Check we have a snapshot of data + const deque< boost::shared_ptr > &snapshots = + data->get_snapshots(); + if (snapshots.empty()) + return; + _snapshot = snapshots.front(); + + if (_snapshot->get_sample_count() < _sample_num*_sample_interval) + return; + + // Get the samplerate + _samplerate = data->samplerate(); + if (_samplerate == 0.0) + _samplerate = 1.0; + + // prepare _xn data + const int offset = dsoSig->get_hw_offset(); + const double vscale = dsoSig->get_vDialValue() * dsoSig->get_factor() * DS_CONF_DSO_VDIVS / (1000*255.0); + const uint16_t step = _snapshot->get_channel_num() * _sample_interval; + const uint8_t *const samples = _snapshot->get_samples(0, _sample_num*_sample_interval-1, _index); + double wsum = 0; + for (unsigned int i = 0; i < _sample_num; i++) { + double w = window(i, _windows_index); + _xn[i] = (samples[i*step] - offset) * vscale * w; + wsum += w; + } + + // fft + fftw_execute(_fft_plan); + + // calculate power spectrum + _power_spectrum[0] = abs(_xk[0])/wsum; /* DC component */ + for (unsigned int k = 1; k < (_sample_num + 1) / 2; ++k) /* (k < N/2 rounded up) */ + _power_spectrum[k] = sqrt((_xk[k]*_xk[k] + _xk[_sample_num-k]*_xk[_sample_num-k]) * 2) / wsum; + if (_sample_num % 2 == 0) /* N is even */ + _power_spectrum[_sample_num/2] = abs(_xk[_sample_num/2])/wsum; /* Nyquist freq. */ + + _spectrum_state = Stopped; +} + +double SpectrumStack::window(uint64_t i, int type) +{ + const double n_m_1 = _sample_num-1; + switch(type) { + case 1: // Hann window + return 0.5*(1-cos(2*PI*i/n_m_1)); + case 2: // Hamming window + return 0.54-0.46*cos(2*PI*i/n_m_1); + case 3: // Blackman window + return 0.42659-0.49656*cos(2*PI*i/n_m_1) + 0.076849*cos(4*PI*i/n_m_1); + case 4: // Flat_top window + return 1-1.93*cos(2*PI*i/n_m_1)+1.29*cos(4*PI*i/n_m_1)- + 0.388*cos(6*PI*i/n_m_1)+0.028*cos(8*PI*i/n_m_1); + default: + return 1; + } +} + +} // namespace data +} // namespace pv diff --git a/DSView/pv/data/spectrumstack.h b/DSView/pv/data/spectrumstack.h new file mode 100755 index 00000000..caa4d268 --- /dev/null +++ b/DSView/pv/data/spectrumstack.h @@ -0,0 +1,118 @@ +/* + * This file is part of the PulseView project. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DSVIEW_PV_DATA_SPECTRUMSTACK_H +#define DSVIEW_PV_DATA_SPECTRUMSTACK_H + +#include "signaldata.h" + +#include + +#include +#include +#include + +#include + +#include +#include + +namespace pv { + +class SigSession; + +namespace view { +class DsoSignal; +} + +namespace data { + +class DsoSnapshot; +class Dso; + +class SpectrumStack : public QObject, public SignalData +{ + Q_OBJECT + +private: + static const QString windows_support[5]; + static const uint64_t length_support[5]; + +public: + enum spectrum_state { + Init, + Stopped, + Running + }; + +public: + SpectrumStack(pv::SigSession &_session, int index); + virtual ~SpectrumStack(); + void clear(); + void init(); + + int get_index() const; + + uint64_t get_sample_num() const; + void set_sample_num(uint64_t num); + + int get_windows_index() const; + void set_windows_index(int index); + + const std::vector get_windows_support() const; + const std::vector get_length_support() const; + + bool dc_ignored() const; + void set_dc_ignore(bool ignore); + + int get_sample_interval() const; + void set_sample_interval(int interval); + + const std::vector get_fft_spectrum() const; + double get_fft_spectrum(uint64_t index); + + void calc_fft(); + + double window(uint64_t i, int type); + +signals: + +private: + pv::SigSession &_session; + + int _index; + uint64_t _sample_num; + int _windows_index; + bool _dc_ignore; + int _sample_interval; + + boost::shared_ptr _snapshot; + spectrum_state _spectrum_state; + + fftw_plan _fft_plan; + std::vector _xn; + std::vector _xk; + std::vector _power_spectrum; +}; + +} // namespace data +} // namespace pv + +#endif // DSVIEW_PV_DATA_SPECTRUMSTACK_H diff --git a/DSView/pv/device/device.cpp b/DSView/pv/device/device.cpp old mode 100644 new mode 100755 index 8214f415..4eb77b33 --- a/DSView/pv/device/device.cpp +++ b/DSView/pv/device/device.cpp @@ -40,7 +40,7 @@ sr_dev_inst* Device::dev_inst() const return _sdi; } -void Device::use(SigSession *owner) throw(QString) +void Device::use(SigSession *owner) { DevInst::use(owner); diff --git a/DSView/pv/device/device.h b/DSView/pv/device/device.h old mode 100644 new mode 100755 index a98ffc0d..f1ec24f1 --- a/DSView/pv/device/device.h +++ b/DSView/pv/device/device.h @@ -34,7 +34,7 @@ public: sr_dev_inst* dev_inst() const; - void use(SigSession *owner) throw(QString); + void use(SigSession *owner); void release(); diff --git a/DSView/pv/device/devinst.cpp b/DSView/pv/device/devinst.cpp old mode 100644 new mode 100755 index 494a681e..6aeeeb87 --- a/DSView/pv/device/devinst.cpp +++ b/DSView/pv/device/devinst.cpp @@ -50,7 +50,7 @@ void* DevInst::get_id() const return _id; } -void DevInst::use(SigSession *owner) throw(QString) +void DevInst::use(SigSession *owner) { assert(owner); assert(!_owner); @@ -125,7 +125,7 @@ uint64_t DevInst::get_sample_limit() uint64_t sample_limit; GVariant* gvar = get_config(NULL, NULL, SR_CONF_LIMIT_SAMPLES); if (gvar != NULL) { - sample_limit = g_variant_get_uint64(gvar); + sample_limit = g_variant_get_uint64(gvar); g_variant_unref(gvar); } else { sample_limit = 0U; diff --git a/DSView/pv/device/devinst.h b/DSView/pv/device/devinst.h old mode 100644 new mode 100755 index c35fd952..b7c7f8c3 --- a/DSView/pv/device/devinst.h +++ b/DSView/pv/device/devinst.h @@ -53,7 +53,7 @@ protected: public: virtual sr_dev_inst* dev_inst() const = 0; - virtual void use(SigSession *owner) throw(QString); + virtual void use(SigSession *owner); virtual void release(); diff --git a/DSView/pv/device/file.cpp b/DSView/pv/device/file.cpp old mode 100644 new mode 100755 index 04b6eb4a..46376082 --- a/DSView/pv/device/file.cpp +++ b/DSView/pv/device/file.cpp @@ -91,10 +91,43 @@ QJsonArray File::get_decoders() dec_array = sessionDoc.array(); } } + + zip_close(archive); } return dec_array; } +QJsonDocument File::get_session() +{ + struct zip *archive; + struct zip_file *zf; + struct zip_stat zs; + int ret; + char *dec_file; + QJsonDocument sessionDoc; + QJsonParseError error; + + archive = zip_open(_path.toLocal8Bit().data(), 0, &ret); + if (archive) { + /* read "decoders" */ + if (zip_stat(archive, "session", 0, &zs) != -1) { + dec_file = (char *)g_try_malloc(zs.size); + if (dec_file) { + zf = zip_fopen_index(archive, zs.index, 0); + zip_fread(zf, dec_file, zs.size); + zip_fclose(zf); + + //QString sessionData = QString::fromUtf8(dec_file); + sessionDoc = QJsonDocument::fromJson(QByteArray::fromRawData(dec_file, zs.size), &error); + } + } + + zip_close(archive); + } + + return sessionDoc; +} + } // device } // pv diff --git a/DSView/pv/device/file.h b/DSView/pv/device/file.h old mode 100644 new mode 100755 index 000b950b..d4299e7b --- a/DSView/pv/device/file.h +++ b/DSView/pv/device/file.h @@ -26,6 +26,7 @@ #include #include +#include #include "devinst.h" @@ -42,6 +43,8 @@ public: QJsonArray get_decoders(); + QJsonDocument get_session(); + public: QString format_device_title() const; diff --git a/DSView/pv/device/inputfile.cpp b/DSView/pv/device/inputfile.cpp old mode 100644 new mode 100755 index 84d8957b..32b63cc4 --- a/DSView/pv/device/inputfile.cpp +++ b/DSView/pv/device/inputfile.cpp @@ -43,7 +43,7 @@ sr_dev_inst* InputFile::dev_inst() const return _input->sdi; } -void InputFile::use(SigSession *owner) throw(QString) +void InputFile::use(SigSession *owner) { (void)owner; assert(!_input); diff --git a/DSView/pv/device/inputfile.h b/DSView/pv/device/inputfile.h old mode 100644 new mode 100755 index c063deaf..e1c1e179 --- a/DSView/pv/device/inputfile.h +++ b/DSView/pv/device/inputfile.h @@ -39,7 +39,7 @@ public: sr_dev_inst* dev_inst() const; - virtual void use(SigSession *owner) throw(QString); + virtual void use(SigSession *owner); virtual void release(); diff --git a/DSView/pv/device/sessionfile.cpp b/DSView/pv/device/sessionfile.cpp old mode 100644 new mode 100755 index 5540e42b..2a3368a5 --- a/DSView/pv/device/sessionfile.cpp +++ b/DSView/pv/device/sessionfile.cpp @@ -35,7 +35,7 @@ sr_dev_inst* SessionFile::dev_inst() const return _sdi; } -void SessionFile::use(SigSession *owner) throw(QString) +void SessionFile::use(SigSession *owner) { assert(!_sdi); diff --git a/DSView/pv/device/sessionfile.h b/DSView/pv/device/sessionfile.h old mode 100644 new mode 100755 index 51d6d3f2..08783bdd --- a/DSView/pv/device/sessionfile.h +++ b/DSView/pv/device/sessionfile.h @@ -34,7 +34,7 @@ public: sr_dev_inst* dev_inst() const; - virtual void use(SigSession *owner) throw(QString); + virtual void use(SigSession *owner); virtual void release(); diff --git a/DSView/pv/devicemanager.cpp b/DSView/pv/devicemanager.cpp old mode 100644 new mode 100755 index 3b760f93..d7778be5 --- a/DSView/pv/devicemanager.cpp +++ b/DSView/pv/devicemanager.cpp @@ -72,6 +72,21 @@ void DeviceManager::add_device(boost::shared_ptr device) _devices.push_front(device); } +void DeviceManager::del_device(boost::shared_ptr device) +{ + assert(device); + BOOST_FOREACH(shared_ptr dev, _devices) { + assert(dev); + if(dev == device) { + dev->release(); + break; + } + } + if (std::find(_devices.begin(), _devices.end(), device) != + _devices.end()) + _devices.remove(device); +} + std::list > DeviceManager::driver_scan( struct sr_dev_driver *const driver, GSList *const drvopts) { diff --git a/DSView/pv/devicemanager.h b/DSView/pv/devicemanager.h old mode 100644 new mode 100755 index 3f0b313a..426b9aa8 --- a/DSView/pv/devicemanager.h +++ b/DSView/pv/devicemanager.h @@ -61,6 +61,7 @@ public: const std::list< boost::shared_ptr >& devices() const; void add_device(boost::shared_ptr device); + void del_device(boost::shared_ptr device); std::list< boost::shared_ptr > driver_scan( struct sr_dev_driver *const driver, diff --git a/DSView/pv/dialogs/about.cpp b/DSView/pv/dialogs/about.cpp old mode 100644 new mode 100755 index 4b800908..5dea3737 --- a/DSView/pv/dialogs/about.cpp +++ b/DSView/pv/dialogs/about.cpp @@ -54,9 +54,11 @@ About::About(QWidget *parent) : QString url = tr("Website: %1
" "Gitbub: %2
" + "Copyright:%3
" "

") .arg(QApplication::organizationDomain()) - .arg("https://github.com/DreamSourceLab/DSView"); + .arg("https://github.com/DreamSourceLab/DSView") + .arg(tr("© DreamSourceLab. All rights reserved.")); QString thanks = tr("Special Thanks
" "All backers on kickstarter
" @@ -67,9 +69,13 @@ About::About(QWidget *parent) : .arg("http://sigrok.org/"); QString changlogs = tr("Changelogs
"); + #ifndef Q_OS_LINUX + QDir dir(QCoreApplication::applicationDirPath()); + #else QDir dir(DS_RES_PATH); dir.cdUp(); - QString filename = dir.absolutePath() + "/NEWS"; + #endif + QString filename = dir.absolutePath() + "/NEWS" + QString::number(qApp->property("Language").toInt()); QFile news(filename); if (news.open(QIODevice::ReadOnly)) { QTextCodec *code=QTextCodec::codecForName("UTF-8"); diff --git a/DSView/pv/dialogs/about.h b/DSView/pv/dialogs/about.h old mode 100644 new mode 100755 diff --git a/DSView/pv/dialogs/calibration.cpp b/DSView/pv/dialogs/calibration.cpp old mode 100644 new mode 100755 index f3d231d3..ab11ca95 --- a/DSView/pv/dialogs/calibration.cpp +++ b/DSView/pv/dialogs/calibration.cpp @@ -30,6 +30,7 @@ #include #include "../view/trace.h" +#include "../dialogs/dsmessagebox.h" using namespace boost; using namespace std; @@ -39,6 +40,7 @@ namespace dialogs { const QString Calibration::VGAIN = QT_TR_NOOP(" VGAIN"); const QString Calibration::VOFF = QT_TR_NOOP(" VOFF"); +const QString Calibration::VCOMB = QT_TR_NOOP(" VCOMB"); Calibration::Calibration(QWidget *parent) : DSDialog(parent) @@ -52,9 +54,10 @@ Calibration::Calibration(QWidget *parent) : this->setModal(false); _dev_inst = NULL; - _save_btn = new QPushButton(tr("Save"), this); - _reset_btn = new QPushButton(tr("Reset"), this); - _exit_btn = new QPushButton(tr("Exit"), this); + _save_btn = new QPushButton(this); + _abort_btn = new QPushButton(this); + _reset_btn = new QPushButton(this); + _exit_btn = new QPushButton(this); _flayout = new QFormLayout(); _flayout->setVerticalSpacing(10); @@ -64,21 +67,43 @@ Calibration::Calibration(QWidget *parent) : QGridLayout *glayout = new QGridLayout(); glayout->setVerticalSpacing(5); - glayout->addLayout(_flayout, 1, 0, 1, 5); + glayout->addLayout(_flayout, 1, 0, 1, 7); glayout->addWidget(_save_btn, 2, 0); glayout->addWidget(new QWidget(this), 2, 1); glayout->setColumnStretch(1, 1); - glayout->addWidget(_reset_btn, 2, 2); + glayout->addWidget(_abort_btn, 2, 2); glayout->addWidget(new QWidget(this), 2, 3); glayout->setColumnStretch(3, 1); - glayout->addWidget(_exit_btn, 2, 4); + glayout->addWidget(_reset_btn, 2, 4); + glayout->addWidget(new QWidget(this), 2, 5); + glayout->setColumnStretch(5, 1); + glayout->addWidget(_exit_btn, 2, 6); layout()->addLayout(glayout); - setTitle(tr("Manual Calibration")); connect(_save_btn, SIGNAL(clicked()), this, SLOT(on_save())); + connect(_abort_btn, SIGNAL(clicked()), this, SLOT(on_abort())); connect(_reset_btn, SIGNAL(clicked()), this, SLOT(on_reset())); connect(_exit_btn, SIGNAL(clicked()), this, SLOT(reject())); + + retranslateUi(); +} + +void Calibration::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) + retranslateUi(); + DSDialog::changeEvent(event); +} + +void Calibration::retranslateUi() +{ + _save_btn->setText(tr("Save")); + _abort_btn->setText(tr("Abort")); + _reset_btn->setText(tr("Reset")); + _exit_btn->setText(tr("Exit")); + + setTitle(tr("Manual Calibration")); } void Calibration::set_device(boost::shared_ptr dev_inst) @@ -135,12 +160,12 @@ void Calibration::set_device(boost::shared_ptr dev_inst) uint64_t voff = 0; uint16_t voff_range = 0; - gvar = _dev_inst->get_config(probe, NULL, SR_CONF_PROBE_VOFF); + gvar = _dev_inst->get_config(probe, NULL, SR_CONF_PROBE_PREOFF); if (gvar != NULL) { voff = g_variant_get_uint16(gvar); g_variant_unref(gvar); } - gvar = _dev_inst->get_config(probe, NULL, SR_CONF_PROBE_VOFF_RANGE); + gvar = _dev_inst->get_config(probe, NULL, SR_CONF_PROBE_PREOFF_MARGIN); if (gvar != NULL) { voff_range = g_variant_get_uint16(gvar); g_variant_unref(gvar); @@ -155,6 +180,31 @@ void Calibration::set_device(boost::shared_ptr dev_inst) _slider_list.push_back(off_slider); _label_list.push_back(off_label); + bool comb_comp_en = false; + gvar = _dev_inst->get_config(probe, NULL, SR_CONF_PROBE_COMB_COMP_EN); + if (gvar != NULL) { + comb_comp_en = g_variant_get_boolean(gvar); + g_variant_unref(gvar); + } + if (comb_comp_en) { + int16_t comb_comp = 0; + gvar = _dev_inst->get_config(probe, NULL, SR_CONF_PROBE_COMB_COMP); + if (gvar != NULL) { + comb_comp = g_variant_get_int16(gvar); + g_variant_unref(gvar); + } + QSlider *comp_slider = new QSlider(Qt::Horizontal, this); + comp_slider->setRange(-127, 127); + comp_slider->setValue(comb_comp); + comp_slider->setObjectName(VCOMB+probe->index); + QString comp_string = tr("Channel") + QString::number(probe->index) + VCOMB; + QLabel *comp_label = new QLabel(comp_string, this); + _flayout->addRow(comp_label, comp_slider); + _slider_list.push_back(comp_slider); + _label_list.push_back(comp_label); + connect(comp_slider, SIGNAL(valueChanged(int)), this, SLOT(set_value(int))); + } + connect(gain_slider, SIGNAL(valueChanged(int)), this, SLOT(set_value(int))); connect(off_slider, SIGNAL(valueChanged(int)), this, SLOT(set_value(int))); } @@ -194,9 +244,13 @@ void Calibration::set_value(int value) } break; } else if (sc->objectName() == VOFF+probe->index) { - _dev_inst->set_config(probe, NULL, SR_CONF_PROBE_VOFF, + _dev_inst->set_config(probe, NULL, SR_CONF_PROBE_PREOFF, g_variant_new_uint16(value)); break; + } else if (sc->objectName() == VCOMB+probe->index) { + _dev_inst->set_config(probe, NULL, SR_CONF_PROBE_COMB_COMP, + g_variant_new_int16(value)); + break; } } } @@ -212,7 +266,7 @@ void Calibration::on_save() //while( QTime::currentTime() < dieTime ); }); Qt::WindowFlags flags = Qt::CustomizeWindowHint; - QProgressDialog dlg(tr("Save Calibration Result... It can take a while."), + QProgressDialog dlg(tr("Save calibration results... It can take a while."), tr("Cancel"),0,0,this,flags); dlg.setWindowModality(Qt::WindowModal); dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | @@ -227,7 +281,7 @@ void Calibration::on_save() this->show(); } -void Calibration::on_reset() +void Calibration::on_abort() { this->hide(); QFuture future; @@ -239,7 +293,7 @@ void Calibration::on_reset() //while( QTime::currentTime() < dieTime ); }); Qt::WindowFlags flags = Qt::CustomizeWindowHint; - QProgressDialog dlg(tr("Reset Calibration Result... It can take a while."), + QProgressDialog dlg(tr("Reload last calibration results... It can take a while."), tr("Cancel"),0,0,this,flags); dlg.setWindowModality(Qt::WindowModal); dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | @@ -280,12 +334,12 @@ void Calibration::reload_value() uint64_t voff = 0; uint16_t voff_range = 0; - gvar = _dev_inst->get_config(probe, NULL, SR_CONF_PROBE_VOFF); + gvar = _dev_inst->get_config(probe, NULL, SR_CONF_PROBE_PREOFF); if (gvar != NULL) { voff = g_variant_get_uint16(gvar); g_variant_unref(gvar); } - gvar = _dev_inst->get_config(probe, NULL, SR_CONF_PROBE_VOFF_RANGE); + gvar = _dev_inst->get_config(probe, NULL, SR_CONF_PROBE_PREOFF_MARGIN); if (gvar != NULL) { voff_range = g_variant_get_uint16(gvar); g_variant_unref(gvar); @@ -304,5 +358,21 @@ void Calibration::reload_value() } } +void Calibration::on_reset() +{ + + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Attention")); + msg.mBox()->setInformativeText(tr("All calibration settings will become the defualt values!")); + msg.mBox()->addButton(tr("Ok"), QMessageBox::AcceptRole); + msg.mBox()->addButton(tr("Cancel"), QMessageBox::RejectRole); + msg.mBox()->setIcon(QMessageBox::Warning); + if (msg.exec()) { + _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO_DEFAULT, + g_variant_new_boolean(true)); + reload_value(); + } +} + } // namespace dialogs } // namespace pv diff --git a/DSView/pv/dialogs/calibration.h b/DSView/pv/dialogs/calibration.h old mode 100644 new mode 100755 index 9e469f1b..97bf7ce2 --- a/DSView/pv/dialogs/calibration.h +++ b/DSView/pv/dialogs/calibration.h @@ -46,6 +46,7 @@ class Calibration : public DSDialog private: static const QString VGAIN; static const QString VOFF; + static const QString VCOMB; public: Calibration(QWidget *parent); @@ -55,9 +56,14 @@ protected: void accept(); void reject(); +private: + void changeEvent(QEvent *event); + void retranslateUi(); + private slots: void set_value(int value); void on_save(); + void on_abort(); void on_reset(); void reload_value(); @@ -66,6 +72,7 @@ private: toolbars::TitleBar *_titlebar; QPushButton *_save_btn; + QPushButton *_abort_btn; QPushButton *_reset_btn; QPushButton *_exit_btn; QFormLayout *_flayout; diff --git a/DSView/pv/dialogs/deviceoptions.cpp b/DSView/pv/dialogs/deviceoptions.cpp old mode 100644 new mode 100755 index 79fd0191..4ffd8080 --- a/DSView/pv/dialogs/deviceoptions.cpp +++ b/DSView/pv/dialogs/deviceoptions.cpp @@ -144,7 +144,7 @@ QGridLayout * DeviceOptions::get_property_form(QWidget * parent) BOOST_FOREACH(boost::shared_ptr p, properties) { assert(p); - const QString label = p->labeled_widget() ? QString() : p->name(); + const QString label = p->labeled_widget() ? QString() : p->label(); layout->addWidget(new QLabel(label, parent), i, 0); if (label == tr("Operation Mode")) layout->addWidget(p->get_widget(parent, true), i, 1); @@ -196,7 +196,8 @@ void DeviceOptions::logic_probes(QGridLayout &layout) ch_opts->setChecked(true); } } - g_variant_unref(gvar_opts); + if (gvar_opts) + g_variant_unref(gvar_opts); } } @@ -307,12 +308,14 @@ void DeviceOptions::zero_adj() dialogs::DSMessageBox msg(this); msg.mBox()->setText(tr("Information")); msg.mBox()->setInformativeText(tr("Auto Calibration program will be started. Please keep all channels out of singal input. It can take a while!")); - //msg.mBox()->setStandardButtons(QMessageBox::); msg.mBox()->addButton(tr("Ok"), QMessageBox::AcceptRole); msg.mBox()->addButton(tr("Cancel"), QMessageBox::RejectRole); msg.mBox()->setIcon(QMessageBox::Information); + if (msg.exec()) { _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO, g_variant_new_boolean(true)); + } else { + _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO, g_variant_new_boolean(false)); } } @@ -366,6 +369,22 @@ void DeviceOptions::channel_check() _dynamic_box->setVisible(_dynamic_box->title() != NULL); } +void DeviceOptions::analog_channel_check() +{ + QCheckBox* sc=dynamic_cast(sender()); + if(sc != NULL) { + for (const GSList *l = _dev_inst->dev_inst()->channels; l; l = l->next) { + sr_channel *const probe = (sr_channel*)l->data; + assert(probe); + if (sc->property("index").toInt() == probe->index) + _dev_inst->set_config(probe, NULL, SR_CONF_PROBE_MAP_DEFAULT, + g_variant_new_boolean(sc->isChecked())); + } + } + dynamic_widget(_dynamic_layout); + _dynamic_box->setVisible(_dynamic_box->title() != NULL); +} + void DeviceOptions::channel_enable() { if (_dev_inst->dev_inst()->mode == LOGIC) { @@ -491,7 +510,6 @@ void DeviceOptions::analog_probes(QGridLayout &layout) probe_layout->addWidget(en_label, 0, 0, 1, 1); probe_layout->addWidget(probe_checkBox, 0, 1, 1, 3); - pv::prop::binding::ProbeOptions *probe_options_binding = new pv::prop::binding::ProbeOptions(_dev_inst->dev_inst(), probe); const vector< boost::shared_ptr > &properties = @@ -500,9 +518,26 @@ void DeviceOptions::analog_probes(QGridLayout &layout) BOOST_FOREACH(boost::shared_ptr p, properties) { assert(p); - probe_layout->addWidget(new QLabel(p->name(), probe_widget), i, 0, 1, 1); - QWidget *pow = p->get_widget(probe_widget); + const QString label = p->labeled_widget() ? QString() : p->label(); + probe_layout->addWidget(new QLabel(label, probe_widget), i, 0, 1, 1); + + QWidget * pow = p->get_widget(probe_widget); pow->setEnabled(probe_checkBox->isChecked()); + if (p->name().contains("Map Default")) { + pow->setProperty("index", probe->index); + connect(pow, SIGNAL(clicked()), this, SLOT(analog_channel_check())); + } else { + if (probe_checkBox->isChecked() && p->name().contains("Map")) { + bool map_default = true; + GVariant* gvar = _dev_inst->get_config(probe, NULL, SR_CONF_PROBE_MAP_DEFAULT); + if (gvar != NULL) { + map_default =g_variant_get_boolean(gvar); + g_variant_unref(gvar); + } + if (map_default) + pow->setEnabled(false); + } + } probe_layout->addWidget(pow, i, 1, 1, 3); i++; } diff --git a/DSView/pv/dialogs/deviceoptions.h b/DSView/pv/dialogs/deviceoptions.h old mode 100644 new mode 100755 index 1deae441..0b9591f7 --- a/DSView/pv/dialogs/deviceoptions.h +++ b/DSView/pv/dialogs/deviceoptions.h @@ -78,6 +78,7 @@ private slots: void zero_adj(); void mode_check(); void channel_check(); + void analog_channel_check(); void on_calibration(); void channel_enable(); diff --git a/DSView/pv/dialogs/dsdialog.cpp b/DSView/pv/dialogs/dsdialog.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/dialogs/dsdialog.h b/DSView/pv/dialogs/dsdialog.h old mode 100644 new mode 100755 diff --git a/DSView/pv/dialogs/dsmessagebox.cpp b/DSView/pv/dialogs/dsmessagebox.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/dialogs/dsmessagebox.h b/DSView/pv/dialogs/dsmessagebox.h old mode 100644 new mode 100755 diff --git a/DSView/pv/dialogs/dsomeasure.cpp b/DSView/pv/dialogs/dsomeasure.cpp old mode 100644 new mode 100755 index b0cf2716..d1acaa19 --- a/DSView/pv/dialogs/dsomeasure.cpp +++ b/DSView/pv/dialogs/dsomeasure.cpp @@ -21,9 +21,14 @@ #include "dsomeasure.h" #include "../device/devinst.h" +#include "../sigsession.h" +#include "../view/view.h" #include #include +#include +#include +#include #include @@ -34,31 +39,74 @@ using namespace pv::view; namespace pv { namespace dialogs { -DsoMeasure::DsoMeasure(QWidget *parent, boost::shared_ptr dsoSig) : - DSDialog(parent), - _dsoSig(dsoSig), - _button_box(QDialogButtonBox::Ok, +DsoMeasure::DsoMeasure(SigSession &session, View &parent, + unsigned int position, int last_sig_index) : + DSDialog((QWidget *)&parent), + _session(session), + _view(parent), + _position(position), + _button_box(QDialogButtonBox::Reset | QDialogButtonBox::Cancel, Qt::Horizontal, this) { - setMinimumWidth(300); + setMinimumSize(500, 400); - for (int i=DSO_MS_BEGIN+1; iget_ms_string(i), this); - checkBox->setProperty("id", QVariant(i)); - checkBox->setChecked(dsoSig->get_ms_en(i)); - _layout.addWidget(checkBox); - connect(checkBox, SIGNAL(toggled(bool)), this, SLOT(set_measure(bool))); + _measure_tab = new QTabWidget(this); + _measure_tab->setTabPosition(QTabWidget::West); + _measure_tab->setUsesScrollButtons(false); + + BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { + boost::shared_ptr dsoSig; + if ((dsoSig = dynamic_pointer_cast(s)) && dsoSig->enabled()) { + QWidget *measure_widget = new QWidget(this); + this->add_measure(measure_widget, dsoSig); + _measure_tab->addTab(measure_widget, QString::number(dsoSig->get_index())); + _measure_tab->tabBar()->setMinimumHeight(30); + _measure_tab->tabBar()->setPalette(QPalette(Qt::red)); + measure_widget->setProperty("index", dsoSig->get_index()); + if (dsoSig->get_index() == last_sig_index) + _measure_tab->setCurrentIndex(last_sig_index); + } } - _layout.addWidget(&_button_box); + _layout.addWidget(_measure_tab); + _layout.addWidget(&_button_box, Qt::AlignHCenter | Qt::AlignBottom); layout()->addLayout(&_layout); setTitle(tr("Measurements")); - connect(&_button_box, SIGNAL(accepted()), this, SLOT(accept())); - connect(&_button_box, SIGNAL(rejected()), this, SLOT(accept())); + connect(_button_box.button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(reject())); + connect(_button_box.button(QDialogButtonBox::Reset), SIGNAL(clicked()), this, SLOT(reset())); + connect(_session.get_device().get(), SIGNAL(device_updated()), this, SLOT(reject())); +} - connect(_dsoSig->get_device().get(), SIGNAL(device_updated()), this, SLOT(reject())); +void DsoMeasure::add_measure(QWidget *widget, const boost::shared_ptr dsoSig) +{ + const int Column = 5; + const int IconSizeForText = 5; + QGridLayout *layout = new QGridLayout(widget); + layout->setMargin(0); + layout->setSpacing(0); + for (int i=DSO_MS_BEGIN+1; isetProperty("id", QVariant(i)); + button->setIconSize(QSize(48, 48)); + QPixmap msPix(get_ms_icon(i)); + QBitmap msMask = msPix.createMaskFromColor(QColor("black"), Qt::MaskOutColor); + msPix.fill(dsoSig->get_colour()); + msPix.setMask(msMask); + button->setIcon(QIcon(msPix)); + layout->addWidget(button, + ((i-1)/Column)*IconSizeForText, (i-1)%Column, + IconSizeForText-1, 1, + Qt::AlignCenter); + layout->addWidget(new QLabel(get_ms_text(i), this), + ((i-1)/Column)*IconSizeForText+4, (i-1)%Column, + 1, 1, + Qt::AlignCenter); + layout->setColumnMinimumWidth((i-1)%Column, this->width()/Column); + + connect(button, SIGNAL(clicked()), this, SLOT(accept())); + } } void DsoMeasure::set_measure(bool en) @@ -67,21 +115,67 @@ void DsoMeasure::set_measure(bool en) QCheckBox* sc=dynamic_cast(sender()); if(sc != NULL) { QVariant id = sc->property("id"); - _dsoSig->set_ms_en(id.toInt(), sc->isChecked()); } } +QString DsoMeasure::get_ms_icon(int ms_type) +{ + assert(ms_type >= DSO_MS_BEGIN); + assert(ms_type < DSO_MS_END); + const QString icon_name[DSO_MS_END-DSO_MS_BEGIN] = {"blank.png", + "mFreq.png", "mPeriod.png", "mPduty.png", "mNduty.png", "mPcount.png", + "mRise.png", "mFall.png", "mPwidth.png", "mNwidth.png", "mBurst.png", + "mAmplitude.png", "mHigh.png", "mLow.png", "mRms.png", "mMean.png", + "mVpp.png", "mMax.png", "mMin.png", "mPover.png", "mNover.png"}; + return ":/icons/"+icon_name[ms_type]; +} + +QString DsoMeasure::get_ms_text(int ms_type) +{ + assert(ms_type >= DSO_MS_BEGIN); + assert(ms_type < DSO_MS_END); + const QString label_name[DSO_MS_END-DSO_MS_BEGIN] = {tr("NULL"), + tr("Freq"), tr("Period"), tr("+Duty"), tr("-Duty"), tr("+Count"), + tr("Rise"), tr("Fall"), tr("+Width"), tr("-Width"), tr("BrstW"), + tr("Ampl"), tr("High"), tr("Low"), tr("RMS"), tr("Mean"), + tr("PK-PK"), tr("Max"), tr("Min"), tr("+Over"), tr("-Over")}; + return label_name[ms_type]; +} + void DsoMeasure::accept() { using namespace Qt; - QDialog::accept(); + QToolButton* sc=dynamic_cast(sender()); + if(sc != NULL) { + QVariant id = sc->property("id"); + enum DSO_MEASURE_TYPE ms_type = DSO_MEASURE_TYPE(id.toInt()); + BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { + boost::shared_ptr dsoSig; + if ((dsoSig = dynamic_pointer_cast(s))) { + if (_measure_tab->currentWidget()->property("index").toInt() == dsoSig->get_index()) { + _view.get_viewstatus()->set_measure(_position, false, dsoSig->get_index(), ms_type); + break; + } + } + } + } + QDialog::accept(); } void DsoMeasure::reject() { using namespace Qt; + _view.get_viewstatus()->set_measure(_position, true, -1, DSO_MS_BEGIN); + QDialog::reject(); +} + +void DsoMeasure::reset() +{ + using namespace Qt; + + _view.get_viewstatus()->set_measure(_position, false, -1, DSO_MS_BEGIN); QDialog::reject(); } diff --git a/DSView/pv/dialogs/dsomeasure.h b/DSView/pv/dialogs/dsomeasure.h old mode 100644 new mode 100755 index a50689be..ab9c25ea --- a/DSView/pv/dialogs/dsomeasure.h +++ b/DSView/pv/dialogs/dsomeasure.h @@ -23,8 +23,10 @@ #ifndef DSVIEW_PV_DSOMEASURE_H #define DSVIEW_PV_DSOMEASURE_H -#include #include +#include +#include +#include #include @@ -34,8 +36,10 @@ namespace pv { +class SigSession; + namespace view { -class DsoSignal; +class View; } namespace dialogs { @@ -45,20 +49,33 @@ class DsoMeasure : public DSDialog Q_OBJECT public: - DsoMeasure(QWidget *parent, boost::shared_ptr dsoSig); + DsoMeasure(SigSession &session, view::View &parent, + unsigned int position, int last_sig_index); + + static QString get_ms_icon(int ms_type); + static QString get_ms_text(int ms_type); + +private: + void add_measure(QWidget *widget, const boost::shared_ptr dsoSig); private slots: void set_measure(bool en); + void reset(); protected: void accept(); void reject(); private: - boost::shared_ptr _dsoSig; + SigSession &_session; + view::View &_view; + unsigned int _position; + toolbars::TitleBar *_titlebar; - QVBoxLayout _layout; QDialogButtonBox _button_box; + QTabWidget *_measure_tab; + QVBoxLayout _layout; + std::vector _mbtn_vec; }; } // namespace dialogs diff --git a/DSView/pv/dialogs/fftoptions.cpp b/DSView/pv/dialogs/fftoptions.cpp old mode 100644 new mode 100755 index f3793b1a..8fd49544 --- a/DSView/pv/dialogs/fftoptions.cpp +++ b/DSView/pv/dialogs/fftoptions.cpp @@ -27,10 +27,10 @@ #include #include "../sigsession.h" -#include "../data/mathstack.h" +#include "../data/spectrumstack.h" #include "../view/trace.h" #include "../view/dsosignal.h" -#include "../view/mathtrace.h" +#include "../view/spectrumtrace.h" using namespace boost; using namespace std; @@ -75,13 +75,13 @@ FftOptions::FftOptions(QWidget *parent, SigSession &session) : std::vector length; std::vector view_modes; std::vector dbv_ranges; - BOOST_FOREACH(const boost::shared_ptr t, _session.get_math_signals()) { - boost::shared_ptr mathTrace; - if ((mathTrace = dynamic_pointer_cast(t))) { - windows = mathTrace->get_math_stack()->get_windows_support(); - length = mathTrace->get_math_stack()->get_length_support(); - view_modes = mathTrace->get_view_modes_support(); - dbv_ranges = mathTrace->get_dbv_ranges(); + BOOST_FOREACH(const boost::shared_ptr t, _session.get_spectrum_traces()) { + boost::shared_ptr spectrumTraces; + if ((spectrumTraces = dynamic_pointer_cast(t))) { + windows = spectrumTraces->get_spectrum_stack()->get_windows_support(); + length = spectrumTraces->get_spectrum_stack()->get_length_support(); + view_modes = spectrumTraces->get_view_modes_support(); + dbv_ranges = spectrumTraces->get_dbv_ranges(); break; } } @@ -125,19 +125,19 @@ FftOptions::FftOptions(QWidget *parent, SigSession &session) : } // load current settings - BOOST_FOREACH(const boost::shared_ptr t, _session.get_math_signals()) { - boost::shared_ptr mathTrace; - if ((mathTrace = dynamic_pointer_cast(t))) { - if (mathTrace->enabled()) { + BOOST_FOREACH(const boost::shared_ptr t, _session.get_spectrum_traces()) { + boost::shared_ptr spectrumTraces; + if ((spectrumTraces = dynamic_pointer_cast(t))) { + if (spectrumTraces->enabled()) { _en_checkbox->setChecked(true); for (int i = 0; i < _ch_combobox->count(); i++) { - if (mathTrace->get_index() == _ch_combobox->itemData(i).toInt()) { + if (spectrumTraces->get_index() == _ch_combobox->itemData(i).toInt()) { _ch_combobox->setCurrentIndex(i); break; } } for (int i = 0; i < _len_combobox->count(); i++) { - if (mathTrace->get_math_stack()->get_sample_num() == _len_combobox->itemData(i).toULongLong()) { + if (spectrumTraces->get_spectrum_stack()->get_sample_num() == _len_combobox->itemData(i).toULongLong()) { _len_combobox->setCurrentIndex(i); break; } @@ -150,20 +150,20 @@ FftOptions::FftOptions(QWidget *parent, SigSession &session) : qVariantFromValue(i)); } for (int i = 0; i < _interval_combobox->count(); i++) { - if (mathTrace->get_math_stack()->get_sample_interval() == _interval_combobox->itemData(i).toInt()) { + if (spectrumTraces->get_spectrum_stack()->get_sample_interval() == _interval_combobox->itemData(i).toInt()) { _interval_combobox->setCurrentIndex(i); break; } } for (int i = 0; i < _dbv_combobox->count(); i++) { - if (mathTrace->dbv_range() == _dbv_combobox->itemData(i).toLongLong()) { + if (spectrumTraces->dbv_range() == _dbv_combobox->itemData(i).toLongLong()) { _dbv_combobox->setCurrentIndex(i); break; } } - _window_combobox->setCurrentIndex(mathTrace->get_math_stack()->get_windows_index()); - _dc_checkbox->setChecked(mathTrace->get_math_stack()->dc_ignored()); - _view_combobox->setCurrentIndex(mathTrace->view_mode()); + _window_combobox->setCurrentIndex(spectrumTraces->get_spectrum_stack()->get_windows_index()); + _dc_checkbox->setChecked(spectrumTraces->get_spectrum_stack()->dc_ignored()); + _view_combobox->setCurrentIndex(spectrumTraces->view_mode()); } } } @@ -214,26 +214,26 @@ void FftOptions::accept() QDialog::accept(); - BOOST_FOREACH(const boost::shared_ptr t, _session.get_math_signals()) { - boost::shared_ptr mathTrace; - if ((mathTrace = dynamic_pointer_cast(t))) { - mathTrace->set_enable(false); - if (mathTrace->get_index() == _ch_combobox->currentData().toInt()) { - mathTrace->get_math_stack()->set_dc_ignore(_dc_checkbox->isChecked()); - mathTrace->get_math_stack()->set_sample_num(_len_combobox->currentData().toULongLong()); - mathTrace->get_math_stack()->set_sample_interval(_interval_combobox->currentData().toInt()); - mathTrace->get_math_stack()->set_windows_index(_window_combobox->currentData().toInt()); - mathTrace->set_view_mode(_view_combobox->currentData().toUInt()); - //mathTrace->init_zoom(); - mathTrace->set_dbv_range(_dbv_combobox->currentData().toInt()); - mathTrace->set_enable(_en_checkbox->isChecked()); + BOOST_FOREACH(const boost::shared_ptr t, _session.get_spectrum_traces()) { + boost::shared_ptr spectrumTraces; + if ((spectrumTraces = dynamic_pointer_cast(t))) { + spectrumTraces->set_enable(false); + if (spectrumTraces->get_index() == _ch_combobox->currentData().toInt()) { + spectrumTraces->get_spectrum_stack()->set_dc_ignore(_dc_checkbox->isChecked()); + spectrumTraces->get_spectrum_stack()->set_sample_num(_len_combobox->currentData().toULongLong()); + spectrumTraces->get_spectrum_stack()->set_sample_interval(_interval_combobox->currentData().toInt()); + spectrumTraces->get_spectrum_stack()->set_windows_index(_window_combobox->currentData().toInt()); + spectrumTraces->set_view_mode(_view_combobox->currentData().toUInt()); + //spectrumTraces->init_zoom(); + spectrumTraces->set_dbv_range(_dbv_combobox->currentData().toInt()); + spectrumTraces->set_enable(_en_checkbox->isChecked()); if (_session.get_capture_state() == SigSession::Stopped && - mathTrace->enabled()) - mathTrace->get_math_stack()->calc_fft(); + spectrumTraces->enabled()) + spectrumTraces->get_spectrum_stack()->calc_fft(); } } } - _session.mathTraces_rebuild(); + _session.spectrum_rebuild(); } void FftOptions::reject() diff --git a/DSView/pv/dialogs/fftoptions.h b/DSView/pv/dialogs/fftoptions.h old mode 100644 new mode 100755 diff --git a/DSView/pv/dialogs/interval.cpp b/DSView/pv/dialogs/interval.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/dialogs/interval.h b/DSView/pv/dialogs/interval.h old mode 100644 new mode 100755 diff --git a/DSView/pv/dialogs/lissajousoptions.cpp b/DSView/pv/dialogs/lissajousoptions.cpp new file mode 100755 index 00000000..8bc54fcd --- /dev/null +++ b/DSView/pv/dialogs/lissajousoptions.cpp @@ -0,0 +1,197 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2015 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "lissajousoptions.h" +#include "../device/devinst.h" +#include "../sigsession.h" +#include "../view/view.h" +#include "../view/lissajoustrace.h" + +#include +#include +#include +#include +#include + +#include + +using namespace boost; +using namespace std; +using namespace pv::view; + +namespace pv { +namespace dialogs { + +LissajousOptions::LissajousOptions(SigSession &session, QWidget *parent) : + DSDialog(parent), + _session(session), + _button_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, + Qt::Horizontal, this) +{ + setMinimumSize(300, 300); + + _enable = new QCheckBox(this); + + QLabel *lisa_label = new QLabel(this); + lisa_label->setPixmap(QPixmap(":/icons/lissajous.png")); + + _percent = new QSlider(Qt::Horizontal, this); + _percent->setRange(100, 100); + _percent->setEnabled(false); + if (_session.cur_samplelimits() > WellLen) { + int min = WellLen*100.0/_session.cur_samplelimits(); + _percent->setEnabled(true); + _percent->setRange(min, 100); + _percent->setValue(min); + } + + _x_group = new QGroupBox(this); + _y_group = new QGroupBox(this); + QHBoxLayout *xlayout = new QHBoxLayout(); + QHBoxLayout *ylayout = new QHBoxLayout(); + BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { + boost::shared_ptr dsoSig; + if ((dsoSig = dynamic_pointer_cast(s))) { + QString index_str = QString::number(dsoSig->get_index()); + QRadioButton *xradio = new QRadioButton(index_str, _x_group); + xradio->setProperty("index", dsoSig->get_index()); + xlayout->addWidget(xradio); + QRadioButton *yradio = new QRadioButton(index_str, _y_group); + yradio->setProperty("index", dsoSig->get_index()); + ylayout->addWidget(yradio); + _x_radio.append(xradio); + _y_radio.append(yradio); + } + } + _x_group->setLayout(xlayout); + _y_group->setLayout(ylayout); + + + boost::shared_ptr lissajous = _session.get_lissajous_trace(); + if (lissajous) { + _enable->setChecked(lissajous->enabled()); + _percent->setValue(lissajous->percent()); + for (QVector::const_iterator i = _x_radio.begin(); + i != _x_radio.end(); i++) { + if ((*i)->property("index").toInt() == lissajous->xIndex()) { + (*i)->setChecked(true); + break; + } + } + for (QVector::const_iterator i = _y_radio.begin(); + i != _y_radio.end(); i++) { + if ((*i)->property("index").toInt() == lissajous->yIndex()) { + (*i)->setChecked(true); + break; + } + } + } else { + _enable->setChecked(false); + for (QVector::const_iterator i = _x_radio.begin(); + i != _x_radio.end(); i++) { + (*i)->setChecked(true); + break; + } + for (QVector::const_iterator i = _y_radio.begin(); + i != _y_radio.end(); i++) { + (*i)->setChecked(true); + break; + } + } + + _layout = new QGridLayout(); + _layout->setMargin(0); + _layout->setSpacing(0); + _layout->addWidget(lisa_label, 0, 0, 1, 2, Qt::AlignCenter); + _layout->addWidget(_enable, 1, 0, 1, 1); + _layout->addWidget(_percent, 2, 0, 1, 2); + _layout->addWidget(_x_group, 3, 0, 1, 1); + _layout->addWidget(_y_group, 3, 1, 1, 1); + _layout->addWidget(new QLabel(this), 4, 1, 1, 1); + _layout->addWidget(&_button_box, 5, 1, 1, 1, Qt::AlignHCenter | Qt::AlignBottom); + + layout()->addLayout(_layout); + + connect(&_button_box, SIGNAL(rejected()), this, SLOT(reject())); + connect(&_button_box, SIGNAL(accepted()), this, SLOT(accept())); + + retranslateUi(); +} + +void LissajousOptions::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) + retranslateUi(); + DSDialog::changeEvent(event); +} + +void LissajousOptions::retranslateUi() +{ + _enable->setText(tr("Enable")); + _x_group->setTitle(tr("X-axis")); + _y_group->setTitle(tr("Y-axis")); + setTitle(tr("Lissajous Options")); +} + +void LissajousOptions::accept() +{ + using namespace Qt; + QDialog::accept(); + + int xindex = -1; + int yindex = -1; + for (QVector::const_iterator i = _x_radio.begin(); + i != _x_radio.end(); i++) { + if ((*i)->isChecked()) { + xindex = (*i)->property("index").toInt(); + break; + } + } + for (QVector::const_iterator i = _y_radio.begin(); + i != _y_radio.end(); i++) { + if ((*i)->isChecked()) { + yindex = (*i)->property("index").toInt(); + break; + } + } + bool enable = (xindex != -1 && yindex != -1 && _enable->isChecked()); + _session.lissajous_rebuild(enable, xindex, yindex, _percent->value()); + + BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { + boost::shared_ptr dsoSig; + if ((dsoSig = dynamic_pointer_cast(s))) { + dsoSig->set_show(!enable); + } + } + boost::shared_ptr mathTrace = _session.get_math_trace(); + if (mathTrace && mathTrace->enabled()) { + mathTrace->set_show(!enable); + } +} + +void LissajousOptions::reject() +{ + using namespace Qt; + QDialog::reject(); +} + +} // namespace dialogs +} // namespace pv diff --git a/DSView/pv/dialogs/lissajousoptions.h b/DSView/pv/dialogs/lissajousoptions.h new file mode 100755 index 00000000..b3fd13f1 --- /dev/null +++ b/DSView/pv/dialogs/lissajousoptions.h @@ -0,0 +1,85 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2015 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef DSVIEW_PV_LISSAJOUSOPTIONS_H +#define DSVIEW_PV_LISSAJOUSOPTIONS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../view/dsosignal.h" +#include "../toolbars/titlebar.h" +#include "dsdialog.h" + +namespace pv { + +class SigSession; + +namespace view { +class View; +} + +namespace dialogs { + +class LissajousOptions : public DSDialog +{ + Q_OBJECT + +private: + static const int WellLen = SR_Kn(16); + +public: + LissajousOptions(SigSession &session, QWidget *parent); + +private: + void changeEvent(QEvent *event); + void retranslateUi(); + +protected: + void accept(); + void reject(); + +private: + SigSession &_session; + + QCheckBox *_enable; + QGroupBox *_x_group; + QGroupBox *_y_group; + QSlider *_percent; + QVector _x_radio; + QVector _y_radio; + QDialogButtonBox _button_box; + QGridLayout *_layout; +}; + +} // namespace dialogs +} // namespace pv + +#endif // DSVIEW_PV_LISSAJOUSOPTIONS_H diff --git a/DSView/pv/dialogs/mathoptions.cpp b/DSView/pv/dialogs/mathoptions.cpp new file mode 100755 index 00000000..f41b9e26 --- /dev/null +++ b/DSView/pv/dialogs/mathoptions.cpp @@ -0,0 +1,228 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2015 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mathoptions.h" +#include "../device/devinst.h" +#include "../sigsession.h" +#include "../view/view.h" +#include "../view/mathtrace.h" +#include "../data/mathstack.h" + +#include +#include +#include +#include +#include + +#include + +using namespace boost; +using namespace std; +using namespace pv::view; + +namespace pv { +namespace dialogs { + +MathOptions::MathOptions(SigSession &session, QWidget *parent) : + DSDialog(parent), + _session(session), + _button_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, + Qt::Horizontal, this) +{ + setMinimumSize(300, 300); + + _enable = new QCheckBox(this); + + QLabel *lisa_label = new QLabel(this); + lisa_label->setPixmap(QPixmap(":/icons/math.png")); + + _math_group = new QGroupBox(this); + QHBoxLayout *type_layout = new QHBoxLayout(); + QRadioButton *add_radio = new QRadioButton(tr("Add"), _math_group); + add_radio->setProperty("type", data::MathStack::MATH_ADD); + type_layout->addWidget(add_radio); + QRadioButton *sub_radio = new QRadioButton(tr("Substract"), _math_group); + sub_radio->setProperty("type", data::MathStack::MATH_SUB); + type_layout->addWidget(sub_radio); + QRadioButton *mul_radio = new QRadioButton(tr("Multiply"), _math_group); + mul_radio->setProperty("type", data::MathStack::MATH_MUL); + type_layout->addWidget(mul_radio); + QRadioButton *div_radio = new QRadioButton(tr("Divide"), _math_group); + div_radio->setProperty("type", data::MathStack::MATH_DIV); + type_layout->addWidget(div_radio); + _math_radio.append(add_radio); + _math_radio.append(sub_radio); + _math_radio.append(mul_radio); + _math_radio.append(div_radio); + _math_group->setLayout(type_layout); + + _src1_group = new QGroupBox(this); + _src2_group = new QGroupBox(this); + QHBoxLayout *src1_layout = new QHBoxLayout(); + QHBoxLayout *src2_layout = new QHBoxLayout(); + BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { + boost::shared_ptr dsoSig; + if ((dsoSig = dynamic_pointer_cast(s))) { + QString index_str = QString::number(dsoSig->get_index()); + QRadioButton *xradio = new QRadioButton(index_str, _src1_group); + xradio->setProperty("index", dsoSig->get_index()); + src1_layout->addWidget(xradio); + QRadioButton *yradio = new QRadioButton(index_str, _src2_group); + yradio->setProperty("index", dsoSig->get_index()); + src2_layout->addWidget(yradio); + _src1_radio.append(xradio); + _src2_radio.append(yradio); + } + } + _src1_group->setLayout(src1_layout); + _src2_group->setLayout(src2_layout); + + + boost::shared_ptr math = _session.get_math_trace(); + if (math) { + _enable->setChecked(math->enabled()); + for (QVector::const_iterator i = _src1_radio.begin(); + i != _src1_radio.end(); i++) { + if ((*i)->property("index").toInt() == math->src1()) { + (*i)->setChecked(true); + break; + } + } + for (QVector::const_iterator i = _src2_radio.begin(); + i != _src2_radio.end(); i++) { + if ((*i)->property("index").toInt() == math->src2()) { + (*i)->setChecked(true); + break; + } + } + for (QVector::const_iterator i = _math_radio.begin(); + i != _math_radio.end(); i++) { + if ((*i)->property("type").toInt() == math->get_math_stack()->get_type()) { + (*i)->setChecked(true); + break; + } + } + } else { + _enable->setChecked(false); + for (QVector::const_iterator i = _src1_radio.begin(); + i != _src1_radio.end(); i++) { + (*i)->setChecked(true); + break; + } + for (QVector::const_iterator i = _src2_radio.begin(); + i != _src2_radio.end(); i++) { + (*i)->setChecked(true); + break; + } + for (QVector::const_iterator i = _math_radio.begin(); + i != _math_radio.end(); i++) { + (*i)->setChecked(true); + break; + } + } + + _layout = new QGridLayout(); + _layout->setMargin(0); + _layout->setSpacing(0); + _layout->addWidget(lisa_label, 0, 0, 1, 2, Qt::AlignCenter); + _layout->addWidget(_enable, 1, 0, 1, 1); + _layout->addWidget(_math_group, 2, 0, 1, 2); + _layout->addWidget(_src1_group, 3, 0, 1, 1); + _layout->addWidget(_src2_group, 3, 1, 1, 1); + _layout->addWidget(new QLabel(this), 4, 1, 1, 1); + _layout->addWidget(&_button_box, 5, 1, 1, 1, Qt::AlignHCenter | Qt::AlignBottom); + + layout()->addLayout(_layout); + + connect(&_button_box, SIGNAL(rejected()), this, SLOT(reject())); + connect(&_button_box, SIGNAL(accepted()), this, SLOT(accept())); + + retranslateUi(); +} + +void MathOptions::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) + retranslateUi(); + DSDialog::changeEvent(event); +} + +void MathOptions::retranslateUi() +{ + _enable->setText(tr("Enable")); + _math_group->setTitle(tr("Math Type")); + _src1_group->setTitle(tr("1st Source")); + _src2_group->setTitle(tr("2nd Source")); + setTitle(tr("Math Options")); +} + +void MathOptions::accept() +{ + using namespace Qt; + QDialog::accept(); + + int src1 = -1; + int src2 = -1; + data::MathStack::MathType type = data::MathStack::MATH_ADD; + for (QVector::const_iterator i = _src1_radio.begin(); + i != _src1_radio.end(); i++) { + if ((*i)->isChecked()) { + src1 = (*i)->property("index").toInt(); + break; + } + } + for (QVector::const_iterator i = _src2_radio.begin(); + i != _src2_radio.end(); i++) { + if ((*i)->isChecked()) { + src2 = (*i)->property("index").toInt(); + break; + } + } + for (QVector::const_iterator i = _math_radio.begin(); + i != _math_radio.end(); i++) { + if ((*i)->isChecked()) { + type = (data::MathStack::MathType)(*i)->property("type").toInt(); + break; + } + } + bool enable = (src1 != -1 && src2 != -1 && _enable->isChecked()); + boost::shared_ptr dsoSig1; + boost::shared_ptr dsoSig2; + BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { + boost::shared_ptr dsoSig; + if ((dsoSig = dynamic_pointer_cast(s))) { + if (dsoSig->get_index() == src1) + dsoSig1 = dsoSig; + if (dsoSig->get_index() == src2) + dsoSig2 = dsoSig; + } + } + _session.math_rebuild(enable, dsoSig1, dsoSig2, type); +} + +void MathOptions::reject() +{ + using namespace Qt; + QDialog::reject(); +} + +} // namespace dialogs +} // namespace pv diff --git a/DSView/pv/dialogs/mathoptions.h b/DSView/pv/dialogs/mathoptions.h new file mode 100755 index 00000000..d11afa36 --- /dev/null +++ b/DSView/pv/dialogs/mathoptions.h @@ -0,0 +1,86 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2015 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef DSVIEW_PV_MATHOPTIONS_H +#define DSVIEW_PV_MATHOPTIONS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../view/dsosignal.h" +#include "../toolbars/titlebar.h" +#include "dsdialog.h" + +namespace pv { + +class SigSession; + +namespace view { +class View; +} + +namespace dialogs { + +class MathOptions : public DSDialog +{ + Q_OBJECT + +private: + static const int WellLen = SR_Kn(16); + +public: + MathOptions(SigSession &session, QWidget *parent); + +private: + void changeEvent(QEvent *event); + void retranslateUi(); + +protected: + void accept(); + void reject(); + +private: + SigSession &_session; + + QCheckBox *_enable; + QGroupBox *_src1_group; + QGroupBox *_src2_group; + QGroupBox *_math_group; + QVector _src1_radio; + QVector _src2_radio; + QVector _math_radio; + QDialogButtonBox _button_box; + QGridLayout *_layout; +}; + +} // namespace dialogs +} // namespace pv + +#endif // DSVIEW_PV_MATHOPTIONS_H diff --git a/DSView/pv/dialogs/protocolexp.cpp b/DSView/pv/dialogs/protocolexp.cpp old mode 100644 new mode 100755 index 04be836b..dce4d33e --- a/DSView/pv/dialogs/protocolexp.cpp +++ b/DSView/pv/dialogs/protocolexp.cpp @@ -23,6 +23,7 @@ #include +#include #include #include #include @@ -118,7 +119,7 @@ void ProtocolExp::accept() filter.append(";;"); } const QString DIR_KEY("ProtocolExportPath"); - QSettings settings; + QSettings settings(QApplication::organizationName(), QApplication::applicationName()); QString default_filter = _format_combobox->currentText(); QString file_name = QFileDialog::getSaveFileName( this, tr("Export Data"), settings.value(DIR_KEY).toString(),filter,&default_filter); @@ -136,7 +137,7 @@ void ProtocolExp::accept() file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out.setCodec("UTF-8"); - out.setGenerateByteOrderMark(true); + //out.setGenerateByteOrderMark(true); // UTF-8 without BOM QFuture future; future = QtConcurrent::run([&]{ diff --git a/DSView/pv/dialogs/protocolexp.h b/DSView/pv/dialogs/protocolexp.h old mode 100644 new mode 100755 diff --git a/DSView/pv/dialogs/protocollist.cpp b/DSView/pv/dialogs/protocollist.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/dialogs/protocollist.h b/DSView/pv/dialogs/protocollist.h old mode 100644 new mode 100755 diff --git a/DSView/pv/dialogs/regionoptions.cpp b/DSView/pv/dialogs/regionoptions.cpp new file mode 100755 index 00000000..0a113133 --- /dev/null +++ b/DSView/pv/dialogs/regionoptions.cpp @@ -0,0 +1,115 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "regionoptions.h" + +#include + +#include "../sigsession.h" +#include "../view/cursor.h" +#include "../view/view.h" +#include "../device/devinst.h" + +using namespace boost; +using namespace std; + +namespace pv { +namespace dialogs { + +const QString RegionOptions::RegionStart = QT_TR_NOOP("Start"); +const QString RegionOptions::RegionEnd = QT_TR_NOOP("End"); + +RegionOptions::RegionOptions(view::View *view, SigSession &session, QWidget *parent) : + DSDialog(parent), + _session(session), + _view(view), + _button_box(QDialogButtonBox::Ok, + Qt::Horizontal, this) +{ + QHBoxLayout *hlayout = new QHBoxLayout(); + hlayout->setMargin(0); + hlayout->setSpacing(0); + _start_comboBox = new QComboBox(this); + _end_comboBox = new QComboBox(this); + _start_comboBox->addItem(RegionStart); + _end_comboBox->addItem(RegionEnd); + if (_view) { + int index = 1; + for(std::list::iterator i = _view->get_cursorList().begin(); + i != _view->get_cursorList().end(); i++) { + QString curCursor = tr("Cursor ")+QString::number(index); + _start_comboBox->addItem(curCursor); + _end_comboBox->addItem(curCursor); + index++; + } + } + hlayout->addWidget(new QLabel("Start: ", this)); + hlayout->addWidget(_start_comboBox); + hlayout->addWidget(new QLabel(" ", this)); + hlayout->addWidget(new QLabel("End: ", this)); + hlayout->addWidget(_end_comboBox); + + QVBoxLayout *vlayout = new QVBoxLayout(); + vlayout->addLayout(hlayout); + vlayout->addWidget(&_button_box); + + layout()->addLayout(vlayout); + setTitle(tr("Region")); + + connect(&_button_box, SIGNAL(accepted()), this, SLOT(set_region())); + connect(_session.get_device().get(), SIGNAL(device_updated()), this, SLOT(reject())); + +} + +void RegionOptions::set_region() +{ + const uint64_t last_samples = _session.cur_samplelimits() - 1; + const int index1 = _start_comboBox->currentIndex(); + const int index2 = _end_comboBox->currentIndex(); + uint64_t start, end; + + _session.set_save_start(0); + _session.set_save_end(last_samples); + + if (index1 == 0) { + start = 0; + } else { + start = _view->get_cursor_samples(index1-1); + } + if (index2 == 0) { + end = last_samples; + } else { + end = _view->get_cursor_samples(index2-1); + } + + if (start > last_samples) + start = 0; + if (end > last_samples) + end = last_samples; + + _session.set_save_start(min(start, end)); + _session.set_save_end(max(start, end)); + + QDialog::accept(); +} + +} // namespace dialogs +} // namespace pv diff --git a/DSView/pv/dialogs/regionoptions.h b/DSView/pv/dialogs/regionoptions.h new file mode 100755 index 00000000..097baff7 --- /dev/null +++ b/DSView/pv/dialogs/regionoptions.h @@ -0,0 +1,74 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef DSVIEW_PV_REGIONOPTIONS_H +#define DSVIEW_PV_REGIONOPTIONS_H + +#include +#include +#include +#include +#include + +#include + +#include "../toolbars/titlebar.h" +#include "dsdialog.h" + +namespace pv { + +class SigSession; + +namespace view { +class View; +} + +namespace dialogs { + +class RegionOptions : public DSDialog +{ + Q_OBJECT +private: + static const QString RegionStart; + static const QString RegionEnd; + +public: + RegionOptions(view::View *view, SigSession &session, QWidget *parent = 0); + +private slots: + void set_region(); + +private: + SigSession &_session; + view::View *_view; + + QComboBox *_start_comboBox; + QComboBox *_end_comboBox; + + QDialogButtonBox _button_box; + +}; + +} // namespace dialogs +} // namespace pv + +#endif // DSVIEW_PV_REGIONOPTIONS_H diff --git a/DSView/pv/dialogs/search.cpp b/DSView/pv/dialogs/search.cpp old mode 100644 new mode 100755 index 06b029e1..86e69dda --- a/DSView/pv/dialogs/search.cpp +++ b/DSView/pv/dialogs/search.cpp @@ -1,128 +1,128 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "search.h" -#include "../view/logicsignal.h" - -#include -#include - -#include - -namespace pv { -namespace dialogs { - -Search::Search(QWidget *parent, SigSession &session, std::map pattern) : - DSDialog(parent), - _session(session) -{ - - QFont font("Monaco"); - font.setStyleHint(QFont::Monospace); - font.setFixedPitch(true); - //this->setMinimumWidth(350); - - QRegExp value_rx("[10XRFCxrfc]+"); - QValidator *value_validator = new QRegExpValidator(value_rx, this); - - search_buttonBox.addButton(QDialogButtonBox::Ok); - search_buttonBox.addButton(QDialogButtonBox::Cancel); - - QGridLayout *search_layout = new QGridLayout(); - search_layout->setVerticalSpacing(0); - - int index = 0; - BOOST_FOREACH(const boost::shared_ptr sig, - _session.get_signals()) { - assert(sig); - boost::shared_ptr logic_sig; - if ((logic_sig = boost::dynamic_pointer_cast(sig))) { - QLineEdit *search_lineEdit = new QLineEdit(this); - if (pattern.find(logic_sig->get_index()) != pattern.end()) - search_lineEdit->setText(pattern[logic_sig->get_index()]); - else - search_lineEdit->setText("X"); - search_lineEdit->setValidator(value_validator); - search_lineEdit->setMaxLength(1); - search_lineEdit->setInputMask("X"); - search_lineEdit->setFont(font); - _search_lineEdit_vec.push_back(search_lineEdit); - - search_layout->addWidget(new QLabel(logic_sig->get_name()+":"), index, 0, Qt::AlignRight); - search_layout->addWidget(new QLabel(QString::number(logic_sig->get_index())), index, 1, Qt::AlignRight); - search_layout->addWidget(search_lineEdit, index, 2); - - connect(search_lineEdit, SIGNAL(editingFinished()), this, SLOT(format())); - - index++; - } - } - - search_layout->addWidget(new QLabel(" "), index,0); - search_layout->addWidget(new QLabel(tr("X: Don't care\n0: Low level\n1: High level\nR: Rising edge\nF: Falling edge\nC: Rising/Falling edge")), 0, 3, index, 1); - search_layout->addWidget(&search_buttonBox, index+1, 3); - search_layout->setColumnStretch(3, 100); - - layout()->addLayout(search_layout); - setTitle(tr("Search Options")); - - connect(&search_buttonBox, SIGNAL(accepted()), this, SLOT(accept())); - connect(&search_buttonBox, SIGNAL(rejected()), this, SLOT(reject())); - connect(_session.get_device().get(), SIGNAL(device_updated()), this, SLOT(reject())); -} - -Search::~Search() -{ -} - -void Search::accept() -{ - using namespace Qt; - - QDialog::accept(); -} - -void Search::format() -{ - QLineEdit *sc = qobject_cast(sender()); - sc->setText(sc->text().toUpper()); -} - -std::map Search::get_pattern() -{ - std::map pattern; - - int index = 0; - BOOST_FOREACH(const boost::shared_ptr sig, - _session.get_signals()) { - assert(sig); - boost::shared_ptr logic_sig; - if ((logic_sig = boost::dynamic_pointer_cast(sig))) { - pattern[logic_sig->get_index()] = _search_lineEdit_vec[index]->text(); - index++; - } - } - - return pattern; -} - -} // namespace decoder -} // namespace pv +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "search.h" +#include "../view/logicsignal.h" + +#include +#include + +#include + +namespace pv { +namespace dialogs { + +Search::Search(QWidget *parent, SigSession &session, std::map pattern) : + DSDialog(parent), + _session(session) +{ + + QFont font("Monaco"); + font.setStyleHint(QFont::Monospace); + font.setFixedPitch(true); + //this->setMinimumWidth(350); + + QRegExp value_rx("[10XRFCxrfc]+"); + QValidator *value_validator = new QRegExpValidator(value_rx, this); + + search_buttonBox.addButton(QDialogButtonBox::Ok); + search_buttonBox.addButton(QDialogButtonBox::Cancel); + + QGridLayout *search_layout = new QGridLayout(); + search_layout->setVerticalSpacing(0); + + int index = 0; + BOOST_FOREACH(const boost::shared_ptr sig, + _session.get_signals()) { + assert(sig); + boost::shared_ptr logic_sig; + if ((logic_sig = boost::dynamic_pointer_cast(sig))) { + QLineEdit *search_lineEdit = new QLineEdit(this); + if (pattern.find(logic_sig->get_index()) != pattern.end()) + search_lineEdit->setText(pattern[logic_sig->get_index()]); + else + search_lineEdit->setText("X"); + search_lineEdit->setValidator(value_validator); + search_lineEdit->setMaxLength(1); + search_lineEdit->setInputMask("X"); + search_lineEdit->setFont(font); + _search_lineEdit_vec.push_back(search_lineEdit); + + search_layout->addWidget(new QLabel(logic_sig->get_name()+":"), index, 0, Qt::AlignRight); + search_layout->addWidget(new QLabel(QString::number(logic_sig->get_index())), index, 1, Qt::AlignRight); + search_layout->addWidget(search_lineEdit, index, 2); + + connect(search_lineEdit, SIGNAL(editingFinished()), this, SLOT(format())); + + index++; + } + } + + search_layout->addWidget(new QLabel(" "), index,0); + search_layout->addWidget(new QLabel(tr("X: Don't care\n0: Low level\n1: High level\nR: Rising edge\nF: Falling edge\nC: Rising/Falling edge")), 0, 3, index, 1); + search_layout->addWidget(&search_buttonBox, index+1, 3); + search_layout->setColumnStretch(3, 100); + + layout()->addLayout(search_layout); + setTitle(tr("Search Options")); + + connect(&search_buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(&search_buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + connect(_session.get_device().get(), SIGNAL(device_updated()), this, SLOT(reject())); +} + +Search::~Search() +{ +} + +void Search::accept() +{ + using namespace Qt; + + QDialog::accept(); +} + +void Search::format() +{ + QLineEdit *sc = qobject_cast(sender()); + sc->setText(sc->text().toUpper()); +} + +std::map Search::get_pattern() +{ + std::map pattern; + + int index = 0; + BOOST_FOREACH(const boost::shared_ptr sig, + _session.get_signals()) { + assert(sig); + boost::shared_ptr logic_sig; + if ((logic_sig = boost::dynamic_pointer_cast(sig))) { + pattern[logic_sig->get_index()] = _search_lineEdit_vec[index]->text(); + index++; + } + } + + return pattern; +} + +} // namespace decoder +} // namespace pv diff --git a/DSView/pv/dialogs/search.h b/DSView/pv/dialogs/search.h old mode 100644 new mode 100755 index 31e965ef..d1f7950a --- a/DSView/pv/dialogs/search.h +++ b/DSView/pv/dialogs/search.h @@ -1,72 +1,72 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#ifndef DSVIEW_PV_SEARCH_H -#define DSVIEW_PV_SEARCH_H - -#include -#include -#include -#include -#include - -#include "../sigsession.h" -#include "../toolbars/titlebar.h" -#include "dsdialog.h" -#include "../device/devinst.h" - -#include - -namespace pv { -namespace dialogs { - -class Search : public DSDialog -{ - Q_OBJECT - -public: - - Search(QWidget *parent, SigSession &session, std::map pattern); - ~Search(); - - std::map get_pattern(); - -protected: - void accept(); - -signals: - -private slots: - void format(); - -private: - SigSession &_session; - - toolbars::TitleBar *_titlebar; - QVector _search_lineEdit_vec; - QDialogButtonBox search_buttonBox; -}; - -} // namespace decoder -} // namespace pv - -#endif // DSVIEW_PV_SEARCH_H +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef DSVIEW_PV_SEARCH_H +#define DSVIEW_PV_SEARCH_H + +#include +#include +#include +#include +#include + +#include "../sigsession.h" +#include "../toolbars/titlebar.h" +#include "dsdialog.h" +#include "../device/devinst.h" + +#include + +namespace pv { +namespace dialogs { + +class Search : public DSDialog +{ + Q_OBJECT + +public: + + Search(QWidget *parent, SigSession &session, std::map pattern); + ~Search(); + + std::map get_pattern(); + +protected: + void accept(); + +signals: + +private slots: + void format(); + +private: + SigSession &_session; + + toolbars::TitleBar *_titlebar; + QVector _search_lineEdit_vec; + QDialogButtonBox search_buttonBox; +}; + +} // namespace decoder +} // namespace pv + +#endif // DSVIEW_PV_SEARCH_H diff --git a/DSView/pv/dialogs/shadow.cpp b/DSView/pv/dialogs/shadow.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/dialogs/shadow.h b/DSView/pv/dialogs/shadow.h old mode 100644 new mode 100755 diff --git a/DSView/pv/dialogs/storeprogress.cpp b/DSView/pv/dialogs/storeprogress.cpp old mode 100644 new mode 100755 index fc01b217..ac84b808 --- a/DSView/pv/dialogs/storeprogress.cpp +++ b/DSView/pv/dialogs/storeprogress.cpp @@ -70,10 +70,10 @@ void StoreProgress::timeout() QTimer::singleShot(100, this, SLOT(timeout())); } -void StoreProgress::save_run() +void StoreProgress::save_run(QString session_file) { - _info.setText("Saving..."); - if (_store_session.save_start()) + _info.setText(tr("Saving...")); + if (_store_session.save_start(session_file)) show(); else show_error(); @@ -83,7 +83,7 @@ void StoreProgress::save_run() void StoreProgress::export_run() { - _info.setText("Exporting..."); + _info.setText(tr("Exporting...")); if (_store_session.export_start()) show(); else diff --git a/DSView/pv/dialogs/storeprogress.h b/DSView/pv/dialogs/storeprogress.h old mode 100644 new mode 100755 index 30cfc841..28cf5cb3 --- a/DSView/pv/dialogs/storeprogress.h +++ b/DSView/pv/dialogs/storeprogress.h @@ -61,7 +61,7 @@ private: void closeEvent(QCloseEvent* e); public slots: - void save_run(); + void save_run(QString session_file); void export_run(); private slots: diff --git a/DSView/pv/dialogs/waitingdialog.cpp b/DSView/pv/dialogs/waitingdialog.cpp old mode 100644 new mode 100755 index ee89d2ff..f3d77fca --- a/DSView/pv/dialogs/waitingdialog.cpp +++ b/DSView/pv/dialogs/waitingdialog.cpp @@ -41,8 +41,9 @@ namespace dialogs { const QString WaitingDialog::TIPS_WAIT = QT_TR_NOOP("Waiting"); const QString WaitingDialog::TIPS_FINISHED = QT_TR_NOOP("Finished!"); -WaitingDialog::WaitingDialog(QWidget *parent, boost::shared_ptr dev_inst) : +WaitingDialog::WaitingDialog(QWidget *parent, boost::shared_ptr dev_inst, int key) : DSDialog(parent), + _key(key), _dev_inst(dev_inst), _button_box(QDialogButtonBox::Abort, Qt::Horizontal, this) @@ -50,8 +51,9 @@ WaitingDialog::WaitingDialog(QWidget *parent, boost::shared_ptrsetFixedSize((GIF_WIDTH+TIP_WIDTH)*1.2, (GIF_HEIGHT+TIP_HEIGHT)*4); this->setWindowOpacity(0.7); + QString iconPath = ":/icons/" + qApp->property("Style").toString(); label = new QLabel(this); - movie = new QMovie(":/icons/wait.gif"); + movie = new QMovie(iconPath+"/wait.gif"); label->setMovie(movie); label->setAlignment(Qt::AlignCenter); @@ -89,13 +91,11 @@ void WaitingDialog::accept() QFuture future; future = QtConcurrent::run([&]{ - //QTime dieTime = QTime::currentTime().addSecs(1); _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO_SET, g_variant_new_boolean(true)); - //while( QTime::currentTime() < dieTime ); }); Qt::WindowFlags flags = Qt::CustomizeWindowHint; - QProgressDialog dlg(tr("Save Auto Zero Result... It can take a while."), + QProgressDialog dlg(tr("Save calibration Result... It can take a while."), tr("Cancel"),0,0,this,flags); dlg.setWindowModality(Qt::WindowModal); dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | @@ -103,8 +103,8 @@ void WaitingDialog::accept() dlg.setCancelButton(NULL); QFutureWatcher watcher; - watcher.setFuture(future); connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); + watcher.setFuture(future); dlg.exec(); } @@ -119,14 +119,12 @@ void WaitingDialog::reject() QFuture future; future = QtConcurrent::run([&]{ - //QTime dieTime = QTime::currentTime().addSecs(1); - _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO, g_variant_new_boolean(false)); + _dev_inst->set_config(NULL, NULL, _key, g_variant_new_boolean(false)); _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO_LOAD, g_variant_new_boolean(true)); - //while( QTime::currentTime() < dieTime ); }); Qt::WindowFlags flags = Qt::CustomizeWindowHint; - QProgressDialog dlg(tr("Load Current Setting... It can take a while."), + QProgressDialog dlg(tr("Load current setting... It can take a while."), tr("Cancel"),0,0,this,flags); dlg.setWindowModality(Qt::WindowModal); dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | @@ -164,7 +162,7 @@ void WaitingDialog::changeText() tips->setText(TIPS_WAIT); index = 0; - GVariant* gvar = _dev_inst->get_config(NULL, NULL, SR_CONF_ZERO); + GVariant* gvar = _dev_inst->get_config(NULL, NULL, _key); if (gvar != NULL) { bool zero = g_variant_get_boolean(gvar); g_variant_unref(gvar); diff --git a/DSView/pv/dialogs/waitingdialog.h b/DSView/pv/dialogs/waitingdialog.h old mode 100644 new mode 100755 index 68130835..5c06e3c9 --- a/DSView/pv/dialogs/waitingdialog.h +++ b/DSView/pv/dialogs/waitingdialog.h @@ -52,7 +52,7 @@ private: static const QString TIPS_FINISHED; public: - WaitingDialog(QWidget *parent, boost::shared_ptr dev_inst); + WaitingDialog(QWidget *parent, boost::shared_ptr dev_inst, int key); int start(); protected: @@ -64,6 +64,7 @@ private slots: void stop(); private: + int _key; boost::shared_ptr _dev_inst; toolbars::TitleBar *_titlebar; QDialogButtonBox _button_box; diff --git a/DSView/pv/dock/dsotriggerdock.cpp b/DSView/pv/dock/dsotriggerdock.cpp old mode 100644 new mode 100755 index 77f5dcf8..f646f357 --- a/DSView/pv/dock/dsotriggerdock.cpp +++ b/DSView/pv/dock/dsotriggerdock.cpp @@ -1,420 +1,453 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "dsotriggerdock.h" -#include "../sigsession.h" -#include "../device/devinst.h" -#include "../dialogs/dsmessagebox.h" -#include "../view/dsosignal.h" - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -using namespace boost; -using namespace std; - -namespace pv { -namespace dock { - -DsoTriggerDock::DsoTriggerDock(QWidget *parent, SigSession &session) : - QScrollArea(parent), - _session(session) -{ - this->setWidgetResizable(true); - _widget = new QWidget(this); - - QLabel *position_label = new QLabel(tr("Trigger Position: "), _widget); - position_spinBox = new QSpinBox(_widget); - position_spinBox->setRange(0, 99); - position_spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons); - position_slider = new QSlider(Qt::Horizontal, _widget); - position_slider->setRange(0, 99); - connect(position_slider, SIGNAL(valueChanged(int)), position_spinBox, SLOT(setValue(int))); - connect(position_spinBox, SIGNAL(valueChanged(int)), position_slider, SLOT(setValue(int))); - connect(position_slider, SIGNAL(valueChanged(int)), this, SLOT(pos_changed(int))); - - QLabel *holdoff_label = new QLabel(tr("Hold Off Time: "), _widget); - holdoff_comboBox = new QComboBox(_widget); - holdoff_comboBox->addItem(tr("uS"), qVariantFromValue(1000)); - holdoff_comboBox->addItem(tr("mS"), qVariantFromValue(1000000)); - holdoff_comboBox->addItem(tr("S"), qVariantFromValue(1000000000)); - holdoff_comboBox->setCurrentIndex(0); - holdoff_spinBox = new QSpinBox(_widget); - holdoff_spinBox->setRange(0, 999); - holdoff_spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons); - holdoff_slider = new QSlider(Qt::Horizontal, _widget); - holdoff_slider->setRange(0, 999); - connect(holdoff_slider, SIGNAL(valueChanged(int)), holdoff_spinBox, SLOT(setValue(int))); - connect(holdoff_spinBox, SIGNAL(valueChanged(int)), holdoff_slider, SLOT(setValue(int))); - connect(holdoff_slider, SIGNAL(valueChanged(int)), this, SLOT(hold_changed(int))); - connect(holdoff_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(hold_changed(int))); - - QLabel *margin_label = new QLabel(tr("Noise Sensitivity: "), _widget); - margin_slider = new QSlider(Qt::Horizontal, _widget); - margin_slider->setRange(0, 15); - connect(margin_slider, SIGNAL(valueChanged(int)), this, SLOT(margin_changed(int))); - - - QLabel *tSource_labe = new QLabel(tr("Trigger Sources: "), _widget); - QRadioButton *auto_radioButton = new QRadioButton(tr("Auto")); - auto_radioButton->setChecked(true); - QRadioButton *ch0_radioButton = new QRadioButton(tr("Channel 0")); - QRadioButton *ch1_radioButton = new QRadioButton(tr("Channel 1")); - QRadioButton *ch0a1_radioButton = new QRadioButton(tr("Channel 0 && 1")); - QRadioButton *ch0o1_radioButton = new QRadioButton(tr("Channel 0 | 1")); - connect(auto_radioButton, SIGNAL(clicked()), this, SLOT(source_changed())); - connect(ch0_radioButton, SIGNAL(clicked()), this, SLOT(source_changed())); - connect(ch1_radioButton, SIGNAL(clicked()), this, SLOT(source_changed())); - connect(ch0a1_radioButton, SIGNAL(clicked()), this, SLOT(source_changed())); - connect(ch0o1_radioButton, SIGNAL(clicked()), this, SLOT(source_changed())); - - QLabel *tType_labe = new QLabel(tr("Trigger Types: "), _widget); - QRadioButton *rising_radioButton = new QRadioButton(tr("Rising Edge")); - rising_radioButton->setChecked(true); - QRadioButton *falling_radioButton = new QRadioButton(tr("Falling Edge")); - connect(rising_radioButton, SIGNAL(clicked()), this, SLOT(type_changed())); - connect(falling_radioButton, SIGNAL(clicked()), this, SLOT(type_changed())); - - source_group=new QButtonGroup(_widget); - channel_comboBox = new QComboBox(_widget); - type_group=new QButtonGroup(_widget); - - source_group->addButton(auto_radioButton); - source_group->addButton(ch0_radioButton); - source_group->addButton(ch1_radioButton); - source_group->addButton(ch0a1_radioButton); - source_group->addButton(ch0o1_radioButton); - source_group->setId(auto_radioButton, DSO_TRIGGER_AUTO); - source_group->setId(ch0_radioButton, DSO_TRIGGER_CH0); - source_group->setId(ch1_radioButton, DSO_TRIGGER_CH1); - source_group->setId(ch0a1_radioButton, DSO_TRIGGER_CH0A1); - source_group->setId(ch0o1_radioButton, DSO_TRIGGER_CH0O1); - - type_group->addButton(rising_radioButton); - type_group->addButton(falling_radioButton); - type_group->setId(rising_radioButton, DSO_TRIGGER_RISING); - type_group->setId(falling_radioButton, DSO_TRIGGER_FALLING); - - QVBoxLayout *layout = new QVBoxLayout(_widget); - QGridLayout *gLayout = new QGridLayout(); - gLayout->setVerticalSpacing(5); - gLayout->addWidget(position_label, 0, 0); - gLayout->addWidget(position_spinBox, 0, 1); - gLayout->addWidget(new QLabel(tr("%"), _widget), 0, 2); - gLayout->addWidget(position_slider, 1, 0, 1, 4); - - gLayout->addWidget(new QLabel(_widget), 2, 0); - gLayout->addWidget(tSource_labe, 3, 0); - gLayout->addWidget(auto_radioButton, 4, 0); - gLayout->addWidget(channel_comboBox, 4, 1, 1, 3); - gLayout->addWidget(ch0_radioButton, 5, 0); - gLayout->addWidget(ch1_radioButton, 5, 1, 1, 3); - gLayout->addWidget(ch0a1_radioButton, 6, 0); - gLayout->addWidget(ch0o1_radioButton, 6, 1, 1, 3); - - gLayout->addWidget(new QLabel(_widget), 7, 0); - gLayout->addWidget(tType_labe, 8, 0); - gLayout->addWidget(rising_radioButton, 9, 0); - gLayout->addWidget(falling_radioButton, 10, 0); - - gLayout->addWidget(new QLabel(_widget), 11, 0); - gLayout->addWidget(holdoff_label, 12, 0); - gLayout->addWidget(holdoff_spinBox, 12, 1); - gLayout->addWidget(holdoff_comboBox, 12, 2); - gLayout->addWidget(holdoff_slider, 13, 0, 1, 4); - - gLayout->addWidget(new QLabel(_widget), 14, 0); - gLayout->addWidget(margin_label, 15, 0); - gLayout->addWidget(margin_slider, 16, 0, 1, 4); - - gLayout->setColumnStretch(4, 1); - - layout->addLayout(gLayout); - layout->addStretch(1); - _widget->setLayout(layout); - - this->setWidget(_widget); - //_widget->setGeometry(0, 0, sizeHint().width(), 500); - _widget->setObjectName("dsoTriggerWidget"); -} - -DsoTriggerDock::~DsoTriggerDock() -{ -} - -void DsoTriggerDock::paintEvent(QPaintEvent *) -{ -// QStyleOption opt; -// opt.init(this); -// QPainter p(this); -// style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - -void DsoTriggerDock::auto_trig(int index) -{ - source_group->button(DSO_TRIGGER_AUTO)->setChecked(true); - channel_comboBox->setCurrentIndex(index); - source_changed(); - channel_changed(index); -} - -void DsoTriggerDock::pos_changed(int pos) -{ - int ret; - ret = _session.get_device()->set_config(NULL, NULL, - SR_CONF_HORIZ_TRIGGERPOS, - g_variant_new_byte((uint8_t)pos)); - if (!ret) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Trigger Setting Issue")); - msg.mBox()->setInformativeText(tr("Change horiz trigger position failed!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - } - set_trig_pos(pos); -} - -void DsoTriggerDock::hold_changed(int hold) -{ - (void)hold; - int ret; - uint64_t holdoff; - if (holdoff_comboBox->currentData().toDouble() == 1000000000) { - holdoff_slider->setRange(0, 10); - } else { - holdoff_slider->setRange(0, 999); - } - holdoff = holdoff_slider->value() * holdoff_comboBox->currentData().toDouble() / 10; - ret = _session.get_device()->set_config(NULL, NULL, - SR_CONF_TRIGGER_HOLDOFF, - g_variant_new_uint64(holdoff)); - - if (!ret) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Trigger Setting Issue")); - msg.mBox()->setInformativeText(tr("Change trigger hold off time failed!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - } -} - -void DsoTriggerDock::margin_changed(int margin) -{ - int ret; - ret = _session.get_device()->set_config(NULL, NULL, - SR_CONF_TRIGGER_MARGIN, - g_variant_new_byte(margin)); - if (!ret) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Trigger Setting Issue")); - msg.mBox()->setInformativeText(tr("Change trigger value sensitivity failed!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - } -} - -void DsoTriggerDock::source_changed() -{ - int id = source_group->checkedId(); - int ret; - - ret = _session.get_device()->set_config(NULL, NULL, - SR_CONF_TRIGGER_SOURCE, - g_variant_new_byte(id)); - if (!ret) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Trigger Setting Issue")); - msg.mBox()->setInformativeText(tr("Change trigger source failed!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - } -} - -void DsoTriggerDock::channel_changed(int ch) -{ - (void)ch; - int ret; - - ret = _session.get_device()->set_config(NULL, NULL, - SR_CONF_TRIGGER_CHANNEL, - g_variant_new_byte(channel_comboBox->currentData().toInt())); - if (!ret) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Trigger Setting Issue")); - msg.mBox()->setInformativeText(tr("Change trigger channel failed!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - } -} - -void DsoTriggerDock::type_changed() -{ - int id = type_group->checkedId(); - int ret; - - ret = _session.get_device()->set_config(NULL, NULL, - SR_CONF_TRIGGER_SLOPE, - g_variant_new_byte(id)); - if (!ret) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Trigger Setting Issue")); - msg.mBox()->setInformativeText(tr("Change trigger type failed!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - } -} - -void DsoTriggerDock::device_change() -{ - if (_session.get_device()->name() != "DSLogic") { - position_spinBox->setDisabled(true); - position_slider->setDisabled(true); - } else { - position_spinBox->setDisabled(false); - position_slider->setDisabled(false); - } -} - -void DsoTriggerDock::init() -{ - if (_session.get_device()->name().contains("virtual")) { - foreach(QAbstractButton * btn, source_group->buttons()) - btn->setDisabled(true); - foreach(QAbstractButton * btn, type_group->buttons()) - btn->setDisabled(true); - holdoff_slider->setDisabled(true); - holdoff_spinBox->setDisabled(true); - holdoff_comboBox->setDisabled(true); - margin_slider->setDisabled(true); - channel_comboBox->setDisabled(true); - return; - } else { - foreach(QAbstractButton * btn, source_group->buttons()) - btn->setDisabled(false); - foreach(QAbstractButton * btn, type_group->buttons()) - btn->setDisabled(false); - holdoff_slider->setDisabled(false); - holdoff_spinBox->setDisabled(false); - holdoff_comboBox->setDisabled(false); - margin_slider->setDisabled(false); - channel_comboBox->setDisabled(false); - } - - // TRIGGERPOS - GVariant* gvar = _session.get_device()->get_config(NULL, NULL, - SR_CONF_HORIZ_TRIGGERPOS); - if (gvar != NULL) { - uint16_t pos = g_variant_get_byte(gvar); - g_variant_unref(gvar); - position_slider->setValue(pos); - } - - gvar = _session.get_device()->get_config(NULL, NULL, - SR_CONF_TRIGGER_SOURCE); - if (gvar != NULL) { - uint8_t src = g_variant_get_byte(gvar); - g_variant_unref(gvar); - source_group->button(src)->setChecked(true); - } - - // setup channel_comboBox - disconnect(channel_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(channel_changed(int))); - channel_comboBox->clear(); - BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { - boost::shared_ptr dsoSig; - if ((dsoSig = dynamic_pointer_cast(s))) { - channel_comboBox->addItem(dsoSig->get_name(), qVariantFromValue(dsoSig->get_index())); - } - } - gvar = _session.get_device()->get_config(NULL, NULL, - SR_CONF_TRIGGER_CHANNEL); - if (gvar != NULL) { - uint8_t src = g_variant_get_byte(gvar); - g_variant_unref(gvar); - for (int i = 0; i < channel_comboBox->count(); i++) { - if (src == channel_comboBox->itemData(i).toInt()) { - channel_comboBox->setCurrentIndex(i); - break; - } - } - } - connect(channel_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(channel_changed(int))); - - gvar = _session.get_device()->get_config(NULL, NULL, - SR_CONF_TRIGGER_SLOPE); - if (gvar != NULL) { - uint8_t slope = g_variant_get_byte(gvar); - g_variant_unref(gvar); - type_group->button(slope)->setChecked(true); - } - - disconnect(holdoff_slider, SIGNAL(valueChanged(int)), this, SLOT(hold_changed(int))); - disconnect(holdoff_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(hold_changed(int))); - gvar = _session.get_device()->get_config(NULL, NULL, - SR_CONF_TRIGGER_HOLDOFF); - if (gvar != NULL) { - uint64_t holdoff = g_variant_get_uint64(gvar); - g_variant_unref(gvar); - for (int i = holdoff_comboBox->count()-1; i >= 0; i--) { - if (holdoff >= holdoff_comboBox->itemData(i).toDouble()) { - holdoff_comboBox->setCurrentIndex(i); - break; - } - } - if (holdoff_comboBox->currentData().toDouble() == 1000000000) { - holdoff_slider->setRange(0, 10); - } else { - holdoff_slider->setRange(0, 999); - } - holdoff_spinBox->setValue(holdoff * 10.0/holdoff_comboBox->currentData().toDouble()); - } - connect(holdoff_slider, SIGNAL(valueChanged(int)), this, SLOT(hold_changed(int))); - connect(holdoff_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(hold_changed(int))); - - disconnect(margin_slider, SIGNAL(valueChanged(int)), this, SLOT(margin_changed(int))); - gvar = _session.get_device()->get_config(NULL, NULL, - SR_CONF_TRIGGER_MARGIN); - if (gvar != NULL) { - uint8_t margin = g_variant_get_byte(gvar); - g_variant_unref(gvar); - margin_slider->setValue(margin); - } - connect(margin_slider, SIGNAL(valueChanged(int)), this, SLOT(margin_changed(int))); -} - -} // namespace dock -} // namespace pv +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "dsotriggerdock.h" +#include "../sigsession.h" +#include "../device/devinst.h" +#include "../dialogs/dsmessagebox.h" +#include "../view/dsosignal.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost; +using namespace std; + +namespace pv { +namespace dock { + +DsoTriggerDock::DsoTriggerDock(QWidget *parent, SigSession &session) : + QScrollArea(parent), + _session(session) +{ + this->setWidgetResizable(true); + _widget = new QWidget(this); + + _position_label = new QLabel(_widget); + _position_spinBox = new QSpinBox(_widget); + _position_spinBox->setRange(0, 99); + _position_spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons); + _position_slider = new QSlider(Qt::Horizontal, _widget); + _position_slider->setRange(0, 99); + connect(_position_slider, SIGNAL(valueChanged(int)), _position_spinBox, SLOT(setValue(int))); + connect(_position_spinBox, SIGNAL(valueChanged(int)), _position_slider, SLOT(setValue(int))); + connect(_position_slider, SIGNAL(valueChanged(int)), this, SLOT(pos_changed(int))); + + _holdoff_label = new QLabel(_widget); + _holdoff_comboBox = new QComboBox(_widget); + _holdoff_comboBox->addItem(tr("uS"), qVariantFromValue(1000)); + _holdoff_comboBox->addItem(tr("mS"), qVariantFromValue(1000000)); + _holdoff_comboBox->addItem(tr("S"), qVariantFromValue(1000000000)); + _holdoff_comboBox->setCurrentIndex(0); + _holdoff_spinBox = new QSpinBox(_widget); + _holdoff_spinBox->setRange(0, 999); + _holdoff_spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons); + _holdoff_slider = new QSlider(Qt::Horizontal, _widget); + _holdoff_slider->setRange(0, 999); + connect(_holdoff_slider, SIGNAL(valueChanged(int)), _holdoff_spinBox, SLOT(setValue(int))); + connect(_holdoff_spinBox, SIGNAL(valueChanged(int)), _holdoff_slider, SLOT(setValue(int))); + connect(_holdoff_slider, SIGNAL(valueChanged(int)), this, SLOT(hold_changed(int))); + connect(_holdoff_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(hold_changed(int))); + + _margin_label = new QLabel(_widget); + _margin_slider = new QSlider(Qt::Horizontal, _widget); + _margin_slider->setRange(0, 15); + connect(_margin_slider, SIGNAL(valueChanged(int)), this, SLOT(margin_changed(int))); + + + _tSource_label = new QLabel(_widget); + _auto_radioButton = new QRadioButton(_widget); + _auto_radioButton->setChecked(true); + _ch0_radioButton = new QRadioButton(_widget); + _ch1_radioButton = new QRadioButton(_widget); + _ch0a1_radioButton = new QRadioButton(_widget); + _ch0o1_radioButton = new QRadioButton(_widget); + connect(_auto_radioButton, SIGNAL(clicked()), this, SLOT(source_changed())); + connect(_ch0_radioButton, SIGNAL(clicked()), this, SLOT(source_changed())); + connect(_ch1_radioButton, SIGNAL(clicked()), this, SLOT(source_changed())); + connect(_ch0a1_radioButton, SIGNAL(clicked()), this, SLOT(source_changed())); + connect(_ch0o1_radioButton, SIGNAL(clicked()), this, SLOT(source_changed())); + + _tType_label = new QLabel(_widget); + _rising_radioButton = new QRadioButton(_widget); + _rising_radioButton->setChecked(true); + _falling_radioButton = new QRadioButton(_widget); + connect(_rising_radioButton, SIGNAL(clicked()), this, SLOT(type_changed())); + connect(_falling_radioButton, SIGNAL(clicked()), this, SLOT(type_changed())); + + _source_group=new QButtonGroup(_widget); + _channel_comboBox = new QComboBox(_widget); + _type_group=new QButtonGroup(_widget); + + _source_group->addButton(_auto_radioButton); + _source_group->addButton(_ch0_radioButton); + _source_group->addButton(_ch1_radioButton); + _source_group->addButton(_ch0a1_radioButton); + _source_group->addButton(_ch0o1_radioButton); + _source_group->setId(_auto_radioButton, DSO_TRIGGER_AUTO); + _source_group->setId(_ch0_radioButton, DSO_TRIGGER_CH0); + _source_group->setId(_ch1_radioButton, DSO_TRIGGER_CH1); + _source_group->setId(_ch0a1_radioButton, DSO_TRIGGER_CH0A1); + _source_group->setId(_ch0o1_radioButton, DSO_TRIGGER_CH0O1); + + _type_group->addButton(_rising_radioButton); + _type_group->addButton(_falling_radioButton); + _type_group->setId(_rising_radioButton, DSO_TRIGGER_RISING); + _type_group->setId(_falling_radioButton, DSO_TRIGGER_FALLING); + + QVBoxLayout *layout = new QVBoxLayout(_widget); + QGridLayout *gLayout = new QGridLayout(); + gLayout->setVerticalSpacing(5); + gLayout->addWidget(_position_label, 0, 0); + gLayout->addWidget(_position_spinBox, 0, 1); + gLayout->addWidget(new QLabel(tr("%"), _widget), 0, 2); + gLayout->addWidget(_position_slider, 1, 0, 1, 4); + + gLayout->addWidget(new QLabel(_widget), 2, 0); + gLayout->addWidget(_tSource_label, 3, 0); + gLayout->addWidget(_auto_radioButton, 4, 0); + gLayout->addWidget(_channel_comboBox, 4, 1, 1, 3); + gLayout->addWidget(_ch0_radioButton, 5, 0); + gLayout->addWidget(_ch1_radioButton, 5, 1, 1, 3); + gLayout->addWidget(_ch0a1_radioButton, 6, 0); + gLayout->addWidget(_ch0o1_radioButton, 6, 1, 1, 3); + + gLayout->addWidget(new QLabel(_widget), 7, 0); + gLayout->addWidget(_tType_label, 8, 0); + gLayout->addWidget(_rising_radioButton, 9, 0); + gLayout->addWidget(_falling_radioButton, 10, 0); + + gLayout->addWidget(new QLabel(_widget), 11, 0); + gLayout->addWidget(_holdoff_label, 12, 0); + gLayout->addWidget(_holdoff_spinBox, 12, 1); + gLayout->addWidget(_holdoff_comboBox, 12, 2); + gLayout->addWidget(_holdoff_slider, 13, 0, 1, 4); + + gLayout->addWidget(new QLabel(_widget), 14, 0); + gLayout->addWidget(_margin_label, 15, 0); + gLayout->addWidget(_margin_slider, 16, 0, 1, 4); + + gLayout->setColumnStretch(4, 1); + + layout->addLayout(gLayout); + layout->addStretch(1); + _widget->setLayout(layout); + + this->setWidget(_widget); + //_widget->setGeometry(0, 0, sizeHint().width(), sizeHint().height()); + _widget->setObjectName("dsoTriggerWidget"); + + retranslateUi(); +} + +DsoTriggerDock::~DsoTriggerDock() +{ +} + +void DsoTriggerDock::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) + retranslateUi(); + else if (event->type() == QEvent::StyleChange) + reStyle(); + QScrollArea::changeEvent(event); +} + +void DsoTriggerDock::retranslateUi() +{ + _position_label->setText(tr("Trigger Position: ")); + _holdoff_label->setText(tr("Hold Off Time: ")); + _margin_label->setText(tr("Noise Sensitivity: ")); + _tSource_label->setText(tr("Trigger Sources: ")); + _tType_label->setText(tr("Trigger Types: ")); + _rising_radioButton->setText(tr("Rising Edge")); + _falling_radioButton->setText(tr("Falling Edge")); + + _auto_radioButton->setText(tr("Auto")); + _ch0_radioButton->setText(tr("Channel 0")); + _ch1_radioButton->setText(tr("Channel 1")); + _ch0a1_radioButton->setText(tr("Channel 0 && 1")); + _ch0o1_radioButton->setText(tr("Channel 0 | 1")); +} + +void DsoTriggerDock::reStyle() +{ + //QString iconPath = ":/icons/" + qApp->property("Style").toString(); +} + +void DsoTriggerDock::paintEvent(QPaintEvent *) +{ +// QStyleOption opt; +// opt.init(this); +// QPainter p(this); +// style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + +void DsoTriggerDock::auto_trig(int index) +{ + _source_group->button(DSO_TRIGGER_AUTO)->setChecked(true); + _channel_comboBox->setCurrentIndex(index); + source_changed(); + channel_changed(index); +} + +void DsoTriggerDock::pos_changed(int pos) +{ + int ret; + ret = _session.get_device()->set_config(NULL, NULL, + SR_CONF_HORIZ_TRIGGERPOS, + g_variant_new_byte((uint8_t)pos)); + if (!ret) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Trigger Setting Issue")); + msg.mBox()->setInformativeText(tr("Change horiz trigger position failed!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + } + set_trig_pos(pos); +} + +void DsoTriggerDock::hold_changed(int hold) +{ + (void)hold; + int ret; + uint64_t holdoff; + if (_holdoff_comboBox->currentData().toDouble() == 1000000000) { + _holdoff_slider->setRange(0, 10); + } else { + _holdoff_slider->setRange(0, 999); + } + holdoff = _holdoff_slider->value() * _holdoff_comboBox->currentData().toDouble() / 10; + ret = _session.get_device()->set_config(NULL, NULL, + SR_CONF_TRIGGER_HOLDOFF, + g_variant_new_uint64(holdoff)); + + if (!ret) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Trigger Setting Issue")); + msg.mBox()->setInformativeText(tr("Change trigger hold off time failed!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + } +} + +void DsoTriggerDock::margin_changed(int margin) +{ + int ret; + ret = _session.get_device()->set_config(NULL, NULL, + SR_CONF_TRIGGER_MARGIN, + g_variant_new_byte(margin)); + if (!ret) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Trigger Setting Issue")); + msg.mBox()->setInformativeText(tr("Change trigger value sensitivity failed!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + } +} + +void DsoTriggerDock::source_changed() +{ + int id = _source_group->checkedId(); + int ret; + + ret = _session.get_device()->set_config(NULL, NULL, + SR_CONF_TRIGGER_SOURCE, + g_variant_new_byte(id)); + if (!ret) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Trigger Setting Issue")); + msg.mBox()->setInformativeText(tr("Change trigger source failed!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + } +} + +void DsoTriggerDock::channel_changed(int ch) +{ + (void)ch; + int ret; + + ret = _session.get_device()->set_config(NULL, NULL, + SR_CONF_TRIGGER_CHANNEL, + g_variant_new_byte(_channel_comboBox->currentData().toInt())); + if (!ret) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Trigger Setting Issue")); + msg.mBox()->setInformativeText(tr("Change trigger channel failed!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + } +} + +void DsoTriggerDock::type_changed() +{ + int id = _type_group->checkedId(); + int ret; + + ret = _session.get_device()->set_config(NULL, NULL, + SR_CONF_TRIGGER_SLOPE, + g_variant_new_byte(id)); + if (!ret) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Trigger Setting Issue")); + msg.mBox()->setInformativeText(tr("Change trigger type failed!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + } +} + +void DsoTriggerDock::device_change() +{ + if (_session.get_device()->name() != "DSLogic") { + _position_spinBox->setDisabled(true); + _position_slider->setDisabled(true); + } else { + _position_spinBox->setDisabled(false); + _position_slider->setDisabled(false); + } +} + +void DsoTriggerDock::init() +{ + if (_session.get_device()->name().contains("virtual")) { + foreach(QAbstractButton * btn, _source_group->buttons()) + btn->setDisabled(true); + foreach(QAbstractButton * btn, _type_group->buttons()) + btn->setDisabled(true); + _holdoff_slider->setDisabled(true); + _holdoff_spinBox->setDisabled(true); + _holdoff_comboBox->setDisabled(true); + _margin_slider->setDisabled(true); + _channel_comboBox->setDisabled(true); + return; + } else { + foreach(QAbstractButton * btn, _source_group->buttons()) + btn->setDisabled(false); + foreach(QAbstractButton * btn, _type_group->buttons()) + btn->setDisabled(false); + _holdoff_slider->setDisabled(false); + _holdoff_spinBox->setDisabled(false); + _holdoff_comboBox->setDisabled(false); + _margin_slider->setDisabled(false); + _channel_comboBox->setDisabled(false); + } + + // TRIGGERPOS + GVariant* gvar = _session.get_device()->get_config(NULL, NULL, + SR_CONF_HORIZ_TRIGGERPOS); + if (gvar != NULL) { + uint16_t pos = g_variant_get_byte(gvar); + g_variant_unref(gvar); + _position_slider->setValue(pos); + } + + gvar = _session.get_device()->get_config(NULL, NULL, + SR_CONF_TRIGGER_SOURCE); + if (gvar != NULL) { + uint8_t src = g_variant_get_byte(gvar); + g_variant_unref(gvar); + _source_group->button(src)->setChecked(true); + } + + // setup _channel_comboBox + disconnect(_channel_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(channel_changed(int))); + _channel_comboBox->clear(); + BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { + boost::shared_ptr dsoSig; + if ((dsoSig = dynamic_pointer_cast(s))) { + _channel_comboBox->addItem(dsoSig->get_name(), qVariantFromValue(dsoSig->get_index())); + } + } + gvar = _session.get_device()->get_config(NULL, NULL, + SR_CONF_TRIGGER_CHANNEL); + if (gvar != NULL) { + uint8_t src = g_variant_get_byte(gvar); + g_variant_unref(gvar); + for (int i = 0; i < _channel_comboBox->count(); i++) { + if (src == _channel_comboBox->itemData(i).toInt()) { + _channel_comboBox->setCurrentIndex(i); + break; + } + } + } + connect(_channel_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(channel_changed(int))); + + gvar = _session.get_device()->get_config(NULL, NULL, + SR_CONF_TRIGGER_SLOPE); + if (gvar != NULL) { + uint8_t slope = g_variant_get_byte(gvar); + g_variant_unref(gvar); + _type_group->button(slope)->setChecked(true); + } + + disconnect(_holdoff_slider, SIGNAL(valueChanged(int)), this, SLOT(hold_changed(int))); + disconnect(_holdoff_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(hold_changed(int))); + gvar = _session.get_device()->get_config(NULL, NULL, + SR_CONF_TRIGGER_HOLDOFF); + if (gvar != NULL) { + uint64_t holdoff = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + for (int i = _holdoff_comboBox->count()-1; i >= 0; i--) { + if (holdoff >= _holdoff_comboBox->itemData(i).toDouble()) { + _holdoff_comboBox->setCurrentIndex(i); + break; + } + } + if (_holdoff_comboBox->currentData().toDouble() == 1000000000) { + _holdoff_slider->setRange(0, 10); + } else { + _holdoff_slider->setRange(0, 999); + } + _holdoff_spinBox->setValue(holdoff * 10.0/_holdoff_comboBox->currentData().toDouble()); + } + connect(_holdoff_slider, SIGNAL(valueChanged(int)), this, SLOT(hold_changed(int))); + connect(_holdoff_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(hold_changed(int))); + + disconnect(_margin_slider, SIGNAL(valueChanged(int)), this, SLOT(margin_changed(int))); + gvar = _session.get_device()->get_config(NULL, NULL, + SR_CONF_TRIGGER_MARGIN); + if (gvar != NULL) { + uint8_t margin = g_variant_get_byte(gvar); + g_variant_unref(gvar); + _margin_slider->setValue(margin); + } + connect(_margin_slider, SIGNAL(valueChanged(int)), this, SLOT(margin_changed(int))); +} + +} // namespace dock +} // namespace pv diff --git a/DSView/pv/dock/dsotriggerdock.h b/DSView/pv/dock/dsotriggerdock.h old mode 100644 new mode 100755 index ee8401c4..b0971b72 --- a/DSView/pv/dock/dsotriggerdock.h +++ b/DSView/pv/dock/dsotriggerdock.h @@ -1,92 +1,113 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef DSVIEW_PV_DSOTRIGGERDOCK_H -#define DSVIEW_PV_DSOTRIGGERDOCK_H - -#include -#include -#include -#include -#include -#include - -#include - -namespace pv { - -class SigSession; - -namespace dock { - -class DsoTriggerDock : public QScrollArea -{ - Q_OBJECT - -public: - DsoTriggerDock(QWidget *parent, SigSession &session); - ~DsoTriggerDock(); - - void paintEvent(QPaintEvent *); - - void device_change(); - - void init(); - -signals: - void set_trig_pos(int percent); - -public slots: - void auto_trig(int index); - -private slots: - void pos_changed(int pos); - void hold_changed(int hold); - void margin_changed(int margin); - void source_changed(); - void type_changed(); - void channel_changed(int ch); - -private: - -private: - SigSession &_session; - - QWidget *_widget; - - QComboBox *holdoff_comboBox; - QSpinBox *holdoff_spinBox; - QSlider *holdoff_slider; - - QSlider *margin_slider; - - QSpinBox *position_spinBox; - QSlider *position_slider; - - QButtonGroup *source_group; - QComboBox *channel_comboBox; - QButtonGroup *type_group; -}; - -} // namespace dock -} // namespace pv - -#endif // DSVIEW_PV_DSOTRIGGERDOCK_H +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DSVIEW_PV_DSOTRIGGERDOCK_H +#define DSVIEW_PV_DSOTRIGGERDOCK_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace pv { + +class SigSession; + +namespace dock { + +class DsoTriggerDock : public QScrollArea +{ + Q_OBJECT + +public: + DsoTriggerDock(QWidget *parent, SigSession &session); + ~DsoTriggerDock(); + + void paintEvent(QPaintEvent *); + + void device_change(); + + void init(); + +private: + void changeEvent(QEvent *event); + void retranslateUi(); + void reStyle(); + +signals: + void set_trig_pos(int percent); + +public slots: + void auto_trig(int index); + +private slots: + void pos_changed(int pos); + void hold_changed(int hold); + void margin_changed(int margin); + void source_changed(); + void type_changed(); + void channel_changed(int ch); + +private: + +private: + SigSession &_session; + + QWidget *_widget; + + QComboBox *_holdoff_comboBox; + QSpinBox *_holdoff_spinBox; + QSlider *_holdoff_slider; + + QSlider *_margin_slider; + + QSpinBox *_position_spinBox; + QSlider *_position_slider; + + QButtonGroup *_source_group; + QComboBox *_channel_comboBox; + QButtonGroup *_type_group; + + QLabel *_position_label; + QLabel *_holdoff_label; + QLabel *_margin_label; + QLabel *_tSource_label; + QLabel *_tType_label; + QRadioButton *_rising_radioButton; + QRadioButton *_falling_radioButton; + + QRadioButton *_auto_radioButton; + QRadioButton *_ch0_radioButton; + QRadioButton *_ch1_radioButton; + QRadioButton *_ch0a1_radioButton; + QRadioButton *_ch0o1_radioButton; +}; + +} // namespace dock +} // namespace pv + +#endif // DSVIEW_PV_DSOTRIGGERDOCK_H diff --git a/DSView/pv/dock/measuredock.cpp b/DSView/pv/dock/measuredock.cpp old mode 100644 new mode 100755 index 46c78f28..53284d74 --- a/DSView/pv/dock/measuredock.cpp +++ b/DSView/pv/dock/measuredock.cpp @@ -1,661 +1,691 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#include "measuredock.h" -#include "../sigsession.h" -#include "../view/cursor.h" -#include "../view/view.h" -#include "../view/viewport.h" -#include "../view/timemarker.h" -#include "../view/ruler.h" -#include "../view/logicsignal.h" -#include "../data/signaldata.h" -#include "../data/snapshot.h" -#include "../devicemanager.h" -#include "../device/device.h" -#include "../device/file.h" -#include "../dialogs/dsdialog.h" -#include "../dialogs/dsmessagebox.h" - -#include -#include -#include -#include - -using namespace boost; - -namespace pv { -namespace dock { - -using namespace pv::view; - -MeasureDock::MeasureDock(QWidget *parent, View &view, SigSession &session) : - QScrollArea(parent), - _session(session), - _view(view), - _icon_add(":/icons/add.png"), - _icon_add_dis(":/icons/add_dis.png"), - _icon_del(":/icons/del.png"), - _icon_del_dis(":/icons/del_dis.png") -{ - - _widget = new QWidget(this); - //_widget->setSizePolicy(); - - _mouse_groupBox = new QGroupBox(tr("Mouse measurement"), _widget); - _fen_checkBox = new QCheckBox(tr("Enable floating measurement"), _widget); - _fen_checkBox->setChecked(true); - _width_label = new QLabel("#####", _widget); - _period_label = new QLabel("#####", _widget); - _freq_label = new QLabel("#####", _widget); - _duty_label = new QLabel("#####", _widget); - - _mouse_layout = new QGridLayout(); - _mouse_layout->setVerticalSpacing(5); - _mouse_layout->addWidget(_fen_checkBox, 0, 0, 1, 6); - _mouse_layout->addWidget(new QLabel(tr("W: "), _widget), 1, 0); - _mouse_layout->addWidget(_width_label, 1, 1); - _mouse_layout->addWidget(new QLabel(tr("P: "), _widget), 1, 4); - _mouse_layout->addWidget(_period_label, 1, 5); - _mouse_layout->addWidget(new QLabel(tr("F: "), _widget), 2, 4); - _mouse_layout->addWidget(_freq_label, 2, 5); - _mouse_layout->addWidget(new QLabel(tr("D: "), _widget), 2, 0); - _mouse_layout->addWidget(_duty_label, 2, 1); - _mouse_layout->addWidget(new QLabel(_widget), 0, 6); - _mouse_layout->addWidget(new QLabel(_widget), 1, 6); - _mouse_layout->addWidget(new QLabel(_widget), 2, 6); - _mouse_layout->setColumnStretch(5, 1); - _mouse_groupBox->setLayout(_mouse_layout); - - /* cursor distance group */ - _dist_groupBox = new QGroupBox(tr("Cursor Distance"), _widget); - _dist_groupBox->setMinimumWidth(300); - _dist_add_btn = new QToolButton(_widget); - _dist_add_btn->setIcon(_icon_add); - connect(_dist_add_btn, SIGNAL(clicked()), this, SLOT(add_dist_measure())); - - _dist_layout = new QGridLayout(_widget); - _dist_layout->setVerticalSpacing(5); - _dist_layout->addWidget(_dist_add_btn, 0, 0); - _dist_layout->addWidget(new QLabel(_widget), 0, 1, 1, 3); - _dist_layout->addWidget(new QLabel(tr("Time/Samples"), _widget), 0, 4); - _dist_layout->addWidget(new QLabel(_widget), 0, 5, 1, 2); - _dist_layout->setColumnStretch(1, 50); - _dist_layout->setColumnStretch(6, 100); - add_dist_measure(); - _dist_groupBox->setLayout(_dist_layout); - - /* cursor edges group */ - _edge_groupBox = new QGroupBox(tr("Edges"), _widget); - _edge_groupBox->setMinimumWidth(300); - _edge_add_btn = new QToolButton(_widget); - _edge_add_btn->setIcon(_icon_add); - connect(_edge_add_btn, SIGNAL(clicked()), this, SLOT(add_edge_measure())); - - _edge_layout = new QGridLayout(_widget); - _edge_layout->setVerticalSpacing(5); - _edge_layout->addWidget(_edge_add_btn, 0, 0); - _edge_layout->addWidget(new QLabel(_widget), 0, 1, 1, 4); - _edge_layout->addWidget(new QLabel(tr("Channel"), _widget), 0, 5); - _edge_layout->addWidget(new QLabel(tr("Rising/Falling/Edges"), _widget), 0, 6); - _edge_layout->setColumnStretch(1, 50); - //_edge_layout->setColumnStretch(6, 100); - //add_edge_measure(); - _edge_groupBox->setLayout(_edge_layout); - - /* cursors group */ - _cursor_groupBox = new QGroupBox(tr("Cursors"), _widget); - _cursor_layout = new QGridLayout(_widget); - _cursor_layout->addWidget(new QLabel(tr("Time/Samples"), _widget), 0, 2); - _cursor_layout->addWidget(new QLabel(_widget), 0, 3); - _cursor_layout->setColumnStretch(3, 1); - - _cursor_groupBox->setLayout(_cursor_layout); - - QVBoxLayout *layout = new QVBoxLayout(_widget); - layout->addWidget(_mouse_groupBox); - layout->addWidget(_dist_groupBox); - layout->addWidget(_edge_groupBox); - layout->addWidget(_cursor_groupBox); - layout->addStretch(1); - _widget->setLayout(layout); - - connect(_fen_checkBox, SIGNAL(stateChanged(int)), &_view, SLOT(set_measure_en(int))); - connect(&_view, SIGNAL(measure_updated()), this, SLOT(measure_updated())); - - this->setWidget(_widget); - _widget->setGeometry(0, 0, sizeHint().width(), 2000); - _widget->setObjectName("measureWidget"); -} - -MeasureDock::~MeasureDock() -{ -} - -void MeasureDock::paintEvent(QPaintEvent *) -{ -// QStyleOption opt; -// opt.init(this); -// QPainter p(this); -// style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - -void MeasureDock::refresh() -{ - -} - -void MeasureDock::reload() -{ - for (QVector ::const_iterator i = _edge_ch_cmb_vec.begin(); - i != _edge_ch_cmb_vec.end(); i++) { - update_probe_selector(*i); - } - reCalc(); -} - -void MeasureDock::cursor_update() -{ - using namespace pv::data; - - if (!_cursor_pushButton_list.empty()) { - for (QVector ::const_iterator i = _cursor_del_btn_vec.begin(); - i != _cursor_del_btn_vec.end(); i++) - delete (*i); - for (QVector::Iterator i = _cursor_pushButton_list.begin(); - i != _cursor_pushButton_list.end(); i++) - delete (*i); - for (QVector::Iterator i = _curpos_label_list.begin(); - i != _curpos_label_list.end(); i++) - delete (*i); - - _cursor_del_btn_vec.clear(); - _cursor_pushButton_list.clear(); - _curpos_label_list.clear(); - } - - update_dist(); - update_edge(); - - int index = 1; - for(std::list::iterator i = _view.get_cursorList().begin(); - i != _view.get_cursorList().end(); i++) { - QString curCursor = QString::number(index); - - QToolButton *del_btn = new QToolButton(_widget); - del_btn->setIcon(QIcon::fromTheme("measure", - QIcon(":/icons/del.png"))); - del_btn->setCheckable(true); - QPushButton *_cursor_pushButton = new QPushButton(curCursor, _widget); - set_cursor_btn_color(_cursor_pushButton); - QString _cur_text = _view.get_cm_time(index - 1) + "/" + QString::number(_view.get_cursor_samples(index - 1)); - QLabel *_curpos_label = new QLabel(_cur_text, _widget); - _cursor_del_btn_vec.push_back(del_btn); - _cursor_pushButton_list.push_back(_cursor_pushButton); - _curpos_label_list.push_back(_curpos_label); - - _cursor_layout->addWidget(del_btn, 1+index, 0); - _cursor_layout->addWidget(_cursor_pushButton, 1 + index, 1); - _cursor_layout->addWidget(_curpos_label, 1 + index, 2); - - connect(del_btn, SIGNAL(clicked()), this, SLOT(del_cursor())); - connect(_cursor_pushButton, SIGNAL(clicked()), this, SLOT(goto_cursor())); - - index++; - } - - update(); -} - -void MeasureDock::measure_updated() -{ - _width_label->setText(_view.get_measure("width")); - _period_label->setText(_view.get_measure("period")); - _freq_label->setText(_view.get_measure("frequency")); - _duty_label->setText(_view.get_measure("duty")); -} - -void MeasureDock::cursor_moving() -{ - //TimeMarker* grabbed_marker = _view.get_ruler()->get_grabbed_cursor(); - if (_view.cursors_shown()) { - int index = 0; - for(std::list::iterator i = _view.get_cursorList().begin(); - i != _view.get_cursorList().end(); i++) { - QString _cur_text = _view.get_cm_time(index) + "/" + QString::number(_view.get_cursor_samples(index)); - _curpos_label_list.at(index)->setText(_cur_text); - //_curvalue_label_list.at(index)->setText(_view.get_cm_value(index)); - index++; - } - } - - update_dist(); -} - -void MeasureDock::reCalc() -{ - cursor_update(); - update_dist(); - update_edge(); -} - -void MeasureDock::goto_cursor() -{ - int index = 0; - - for (QVector::Iterator i = _cursor_pushButton_list.begin(); - i != _cursor_pushButton_list.end(); i++) { - QPushButton *button = qobject_cast(sender()); - if ((*i) == button) { - _view.set_cursor_middle(index); - break; - } - index++; - } -} - -void MeasureDock::add_dist_measure() -{ - if (_dist_row_widget_vec.size() > Max_Measure_Limits) - return; - - QWidget *row_widget = new QWidget(_widget); - row_widget->setContentsMargins(0,0,0,0); - QHBoxLayout *row_layout = new QHBoxLayout(row_widget); - row_layout->setContentsMargins(0,0,0,0); - row_layout->setSpacing(0); - row_widget->setLayout(row_layout); - _dist_row_widget_vec.push_back(row_widget); - - QToolButton *del_btn = new QToolButton(row_widget); - del_btn->setIcon(QIcon::fromTheme("measure", - QIcon(":/icons/del.png"))); - del_btn->setCheckable(true); - QPushButton *s_btn = new QPushButton(tr(" "), row_widget); - s_btn->setObjectName("dist"); - QPushButton *e_btn = new QPushButton(tr(" "), row_widget); - e_btn->setObjectName("dist"); - QLabel *r_label = new QLabel(row_widget); - QLabel *g_label = new QLabel(tr("-"), row_widget); - g_label->setContentsMargins(0,0,0,0); - _dist_del_btn_vec.push_back(del_btn); - _dist_s_btn_vec.push_back(s_btn); - _dist_e_btn_vec.push_back(e_btn); - _dist_r_label_vec.push_back(r_label); - - connect(del_btn, SIGNAL(clicked()), this, SLOT(del_dist_measure())); - connect(s_btn, SIGNAL(clicked()), this, SLOT(show_all_coursor())); - connect(e_btn, SIGNAL(clicked()), this, SLOT(show_all_coursor())); - - row_layout->addWidget(del_btn); - row_layout->addSpacing(5); - row_layout->addWidget(s_btn); - row_layout->addWidget(g_label); - row_layout->addWidget(e_btn); - row_layout->addSpacing(5); - row_layout->addWidget(r_label, 100); - - _dist_layout->addWidget(row_widget, _dist_row_widget_vec.size(), 0, 1, 7); - -} - -void MeasureDock::del_dist_measure() -{ - int del_index = 0; - for (QVector ::const_iterator i = _dist_del_btn_vec.begin(); - i != _dist_del_btn_vec.end(); i++) { - if ((*i)->isChecked()) { - _dist_layout->removeWidget(_dist_row_widget_vec.at(del_index)); - - delete _dist_del_btn_vec.at(del_index); - delete _dist_s_btn_vec.at(del_index); - delete _dist_e_btn_vec.at(del_index); - delete _dist_r_label_vec.at(del_index); - delete _dist_row_widget_vec.at(del_index); - - _dist_del_btn_vec.remove(del_index); - _dist_s_btn_vec.remove(del_index); - _dist_e_btn_vec.remove(del_index); - _dist_r_label_vec.remove(del_index); - _dist_row_widget_vec.remove(del_index); - - break; - } - del_index++; - } -} - -void MeasureDock::add_edge_measure() -{ - if (_edge_row_widget_vec.size() > Max_Measure_Limits) - return; - - QWidget *row_widget = new QWidget(_widget); - row_widget->setContentsMargins(0,0,0,0); - QHBoxLayout *row_layout = new QHBoxLayout(row_widget); - row_layout->setContentsMargins(0,0,0,0); - row_layout->setSpacing(0); - row_widget->setLayout(row_layout); - _edge_row_widget_vec.push_back(row_widget); - - QToolButton *del_btn = new QToolButton(row_widget); - del_btn->setIcon(QIcon::fromTheme("measure", - QIcon(":/icons/del.png"))); - del_btn->setCheckable(true); - QPushButton *s_btn = new QPushButton(tr(" "), row_widget); - s_btn->setObjectName("edge"); - QPushButton *e_btn = new QPushButton(tr(" "), row_widget); - e_btn->setObjectName("edge"); - QLabel *r_label = new QLabel(row_widget); - QLabel *g_label = new QLabel(tr("-"), row_widget); - g_label->setContentsMargins(0,0,0,0); - QLabel *a_label = new QLabel(tr("@"), row_widget); - a_label->setContentsMargins(0,0,0,0); - QComboBox *ch_cmb = create_probe_selector(row_widget); - _edge_del_btn_vec.push_back(del_btn); - _edge_s_btn_vec.push_back(s_btn); - _edge_e_btn_vec.push_back(e_btn); - _edge_ch_cmb_vec.push_back(ch_cmb); - _edge_r_label_vec.push_back(r_label); - - connect(del_btn, SIGNAL(clicked()), this, SLOT(del_edge_measure())); - connect(s_btn, SIGNAL(clicked()), this, SLOT(show_all_coursor())); - connect(e_btn, SIGNAL(clicked()), this, SLOT(show_all_coursor())); - connect(ch_cmb, SIGNAL(currentIndexChanged(int)), this, SLOT(update_edge())); - - row_layout->addWidget(del_btn); - row_layout->addSpacing(5); - row_layout->addWidget(s_btn); - row_layout->addWidget(g_label); - row_layout->addWidget(e_btn); - row_layout->addWidget(a_label); - row_layout->addWidget(ch_cmb); - row_layout->addSpacing(5); - row_layout->addWidget(r_label, 100); - - _edge_layout->addWidget(row_widget, _edge_row_widget_vec.size(), 0, 1, 7); - -} - -void MeasureDock::del_edge_measure() -{ - int del_index = 0; - for (QVector ::const_iterator i = _edge_del_btn_vec.begin(); - i != _edge_del_btn_vec.end(); i++) { - if ((*i)->isChecked()) { - _edge_layout->removeWidget(_edge_row_widget_vec.at(del_index)); - - delete _edge_del_btn_vec.at(del_index); - delete _edge_s_btn_vec.at(del_index); - delete _edge_e_btn_vec.at(del_index); - delete _edge_r_label_vec.at(del_index); - delete _edge_ch_cmb_vec.at(del_index); - delete _edge_row_widget_vec.at(del_index); - - _edge_del_btn_vec.remove(del_index); - _edge_s_btn_vec.remove(del_index); - _edge_e_btn_vec.remove(del_index); - _edge_r_label_vec.remove(del_index); - _edge_ch_cmb_vec.remove(del_index); - _edge_row_widget_vec.remove(del_index); - - break; - } - del_index++; - } -} - -void MeasureDock::show_all_coursor() -{ - if (_view.get_cursorList().empty()) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Information")); - msg.mBox()->setInformativeText(tr("Please insert cursor before using cursor measure.")); - msg.mBox()->addButton(tr("Ok"), QMessageBox::AcceptRole); - msg.mBox()->setIcon(QMessageBox::Information); - msg.exec(); - - return; - } - - _sel_btn = qobject_cast(sender()); - - //dialogs::DSDialog cursor_dlg(_widget); - QDialog cursor_dlg(_widget); - cursor_dlg.setWindowFlags(Qt::FramelessWindowHint | Qt::Popup | Qt::WindowSystemMenuHint | - Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint); - int index = 0; - QHBoxLayout *hlayout = NULL; - QVBoxLayout *vlayout = new QVBoxLayout(&cursor_dlg); - for(std::list::iterator i = _view.get_cursorList().begin(); - i != _view.get_cursorList().end(); i++) { - QPushButton *cursor_btn = new QPushButton(QString::number(index+1), &cursor_dlg); - set_cursor_btn_color(cursor_btn); - if ((index % 4) == 0) { - hlayout = new QHBoxLayout(&cursor_dlg); - vlayout->addLayout(hlayout); - //cursor_dlg.layout()->addLayout(hlayout); - } - hlayout->addWidget(cursor_btn); - connect(cursor_btn, SIGNAL(clicked()), &cursor_dlg, SLOT(accept())); - connect(cursor_btn, SIGNAL(clicked()), this, SLOT(set_se_cursor())); - index++; - } - while((index++ % 4) != 0) - hlayout->addWidget(new QLabel(&cursor_dlg)); - - cursor_dlg.setLayout(vlayout); - QRect sel_btn_rect = _sel_btn->geometry(); - sel_btn_rect.moveTopLeft(_sel_btn->parentWidget()->mapToGlobal(sel_btn_rect.topLeft())); - cursor_dlg.setGeometry(sel_btn_rect.left(), sel_btn_rect.bottom()+10, - cursor_dlg.width(), cursor_dlg.height()); - cursor_dlg.exec(); -} - -void MeasureDock::set_se_cursor() -{ - QPushButton *sc = qobject_cast(sender()); - if (_sel_btn) - _sel_btn->setText(sc->text()); - - set_cursor_btn_color(_sel_btn); - - if (_sel_btn->objectName() == "dist") - update_dist(); - else if (_sel_btn->objectName() == "edge") - update_edge(); -} - -const view::Cursor* MeasureDock::find_cousor(int index) -{ - int cur_index = 1; - for(std::list::iterator i = _view.get_cursorList().begin(); - i != _view.get_cursorList().end(); i++) { - if (cur_index == index) { - return (*i); - } - } - - return NULL; -} - -void MeasureDock::update_dist() -{ - int dist_index = 0; - for (QVector::Iterator i = _dist_s_btn_vec.begin(); - i != _dist_s_btn_vec.end(); i++) { - bool start_ret, end_ret; - const unsigned int start = (*i)->text().toInt(&start_ret) - 1; - const unsigned int end = _dist_e_btn_vec[dist_index]->text().toInt(&end_ret) - 1; - - if (start_ret) { - if (start + 1 > _view.get_cursorList().size()) { - (*i)->setText(" "); - set_cursor_btn_color((*i)); - start_ret = false; - } - } - if (end_ret) { - if (end + 1 > _view.get_cursorList().size()) { - _dist_e_btn_vec[dist_index]->setText(" "); - set_cursor_btn_color(_dist_e_btn_vec[dist_index]); - end_ret = false; - } - } - - if (start_ret && end_ret) { - int64_t delta = _view.get_cursor_samples(start) - - _view.get_cursor_samples(end); - QString delta_text = _view.get_cm_delta(start, end) + - "/" + QString::number(delta); - if (delta < 0) - delta_text.replace('+', '-'); - _dist_r_label_vec[dist_index]->setText(delta_text); - } else { - _dist_r_label_vec[dist_index]->setText(" "); - } - - dist_index++; - } -} - -void MeasureDock::update_edge() -{ - int edge_index = 0; - for (QVector::Iterator i = _edge_s_btn_vec.begin(); - i != _edge_s_btn_vec.end(); i++) { - bool start_ret, end_ret; - const unsigned int start = (*i)->text().toInt(&start_ret) - 1; - const unsigned int end = _edge_e_btn_vec[edge_index]->text().toInt(&end_ret) - 1; - - if (start_ret) { - if (start + 1 > _view.get_cursorList().size()) { - (*i)->setText(" "); - set_cursor_btn_color((*i)); - start_ret = false; - } - } - if (end_ret) { - if (end + 1 > _view.get_cursorList().size()) { - _edge_e_btn_vec[edge_index]->setText(" "); - set_cursor_btn_color(_edge_e_btn_vec[edge_index]); - end_ret = false; - } - } - - bool mValid = false; - if (start_ret && end_ret) { - uint64_t rising_edges; - uint64_t falling_edges; - const std::vector< boost::shared_ptr > sigs(_session.get_signals()); - for(size_t i = 0; i < sigs.size(); i++) { - const boost::shared_ptr s(sigs[i]); - boost::shared_ptr logicSig; - assert(s); - if ((logicSig = dynamic_pointer_cast(s)) && - (logicSig->enabled()) && - (logicSig->get_index() == _edge_ch_cmb_vec[edge_index]->currentText().toInt())){ - if (logicSig->edges(_view.get_cursor_samples(end), _view.get_cursor_samples(start), rising_edges, falling_edges)) { - QString delta_text = QString::number(rising_edges) + "/" + - QString::number(falling_edges) + "/" + - QString::number(rising_edges + falling_edges); - _edge_r_label_vec[edge_index]->setText(delta_text); - mValid = true; - break; - } - } - } - } - - if (!mValid) - _edge_r_label_vec[edge_index]->setText("-/-/-"); - - edge_index++; - } -} - -void MeasureDock::set_cursor_btn_color(QPushButton *btn) -{ - bool ret; - const unsigned int start = btn->text().toInt(&ret) - 1; - QColor cursor_color = ret ? view::Ruler::CursorColor[start%8] : QColor("#302F2F"); - QString border_width = ret ? "0px" : "1px"; - QString normal = "{background-color:" + cursor_color.name() + - "; color:black" + "; border-width:" + border_width + ";}"; - QString hover = "{background-color:" + cursor_color.darker().name() + - "; color:black" + "; border-width:" + border_width + ";}"; - QString style = "QPushButton:hover" + hover + - "QPushButton" + normal; - btn->setStyleSheet(style); -} - -QComboBox* MeasureDock::create_probe_selector(QWidget *parent) -{ - QComboBox *selector = new QComboBox(parent); - update_probe_selector(selector); - return selector; -} - -void MeasureDock::update_probe_selector(QComboBox *selector) -{ - selector->clear(); - const std::vector< boost::shared_ptr > sigs(_session.get_signals()); - for(size_t i = 0; i < sigs.size(); i++) { - const boost::shared_ptr s(sigs[i]); - assert(s); - - if (dynamic_pointer_cast(s) && s->enabled()) - { - selector->addItem(QString::number(s->get_index())); - } - } -} - -void MeasureDock::del_cursor() -{ - int del_index = 0; - Cursor* cursor = NULL; - for (QVector ::const_iterator i = _cursor_del_btn_vec.begin(); - i != _cursor_del_btn_vec.end(); i++) { - if ((*i)->isChecked()) { - int cur_index = 0; - std::list::iterator ite = _view.get_cursorList().begin(); - while (cur_index++ != del_index) - ite++; - cursor = *ite; - break; - } - del_index++; - } - - if (cursor) - _view.del_cursor(cursor); - if (_view.get_cursorList().empty()) - _view.show_cursors(false); - - cursor_update(); - _view.update(); -} - -} // namespace dock -} // namespace pv +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "measuredock.h" +#include "../sigsession.h" +#include "../view/cursor.h" +#include "../view/view.h" +#include "../view/viewport.h" +#include "../view/timemarker.h" +#include "../view/ruler.h" +#include "../view/logicsignal.h" +#include "../data/signaldata.h" +#include "../data/snapshot.h" +#include "../devicemanager.h" +#include "../device/device.h" +#include "../device/file.h" +#include "../dialogs/dsdialog.h" +#include "../dialogs/dsmessagebox.h" + +#include +#include +#include +#include + +using namespace boost; + +namespace pv { +namespace dock { + +using namespace pv::view; + +MeasureDock::MeasureDock(QWidget *parent, View &view, SigSession &session) : + QScrollArea(parent), + _session(session), + _view(view) +{ + + _widget = new QWidget(this); + //_widget->setSizePolicy(); + + _mouse_groupBox = new QGroupBox(_widget); + _fen_checkBox = new QCheckBox(_widget); + _fen_checkBox->setChecked(true); + _width_label = new QLabel("#####", _widget); + _period_label = new QLabel("#####", _widget); + _freq_label = new QLabel("#####", _widget); + _duty_label = new QLabel("#####", _widget); + + _w_label = new QLabel(_widget); + _p_label = new QLabel(_widget); + _f_label = new QLabel(_widget); + _d_label = new QLabel(_widget); + _mouse_layout = new QGridLayout(); + _mouse_layout->setVerticalSpacing(5); + _mouse_layout->addWidget(_fen_checkBox, 0, 0, 1, 6); + _mouse_layout->addWidget(_w_label, 1, 0); + _mouse_layout->addWidget(_width_label, 1, 1); + _mouse_layout->addWidget(_p_label, 1, 4); + _mouse_layout->addWidget(_period_label, 1, 5); + _mouse_layout->addWidget(_f_label, 2, 4); + _mouse_layout->addWidget(_freq_label, 2, 5); + _mouse_layout->addWidget(_d_label, 2, 0); + _mouse_layout->addWidget(_duty_label, 2, 1); + _mouse_layout->addWidget(new QLabel(_widget), 0, 6); + _mouse_layout->addWidget(new QLabel(_widget), 1, 6); + _mouse_layout->addWidget(new QLabel(_widget), 2, 6); + _mouse_layout->setColumnStretch(5, 1); + _mouse_groupBox->setLayout(_mouse_layout); + + /* cursor distance group */ + _dist_groupBox = new QGroupBox(_widget); + _dist_groupBox->setMinimumWidth(300); + _dist_add_btn = new QToolButton(_widget); + connect(_dist_add_btn, SIGNAL(clicked()), this, SLOT(add_dist_measure())); + + _dist_layout = new QGridLayout(_widget); + _dist_layout->setVerticalSpacing(5); + _dist_layout->addWidget(_dist_add_btn, 0, 0); + _dist_layout->addWidget(new QLabel(_widget), 0, 1, 1, 3); + _dist_layout->addWidget(new QLabel(tr("Time/Samples"), _widget), 0, 4); + _dist_layout->addWidget(new QLabel(_widget), 0, 5, 1, 2); + _dist_layout->setColumnStretch(1, 50); + _dist_layout->setColumnStretch(6, 100); + _dist_groupBox->setLayout(_dist_layout); + + /* cursor edges group */ + _edge_groupBox = new QGroupBox(_widget); + _edge_groupBox->setMinimumWidth(300); + _edge_add_btn = new QToolButton(_widget); + connect(_edge_add_btn, SIGNAL(clicked()), this, SLOT(add_edge_measure())); + + _channel_label = new QLabel(_widget); + _edge_label = new QLabel(_widget); + _edge_layout = new QGridLayout(_widget); + _edge_layout->setVerticalSpacing(5); + _edge_layout->addWidget(_edge_add_btn, 0, 0); + _edge_layout->addWidget(new QLabel(_widget), 0, 1, 1, 4); + _edge_layout->addWidget(_channel_label, 0, 5); + _edge_layout->addWidget(_edge_label, 0, 6); + _edge_layout->setColumnStretch(1, 50); + //_edge_layout->setColumnStretch(6, 100); + //add_edge_measure(); + _edge_groupBox->setLayout(_edge_layout); + + /* cursors group */ + _time_label = new QLabel(_widget); + _cursor_groupBox = new QGroupBox(_widget); + _cursor_layout = new QGridLayout(_widget); + _cursor_layout->addWidget(_time_label, 0, 2); + _cursor_layout->addWidget(new QLabel(_widget), 0, 3); + _cursor_layout->setColumnStretch(3, 1); + + _cursor_groupBox->setLayout(_cursor_layout); + + QVBoxLayout *layout = new QVBoxLayout(_widget); + layout->addWidget(_mouse_groupBox); + layout->addWidget(_dist_groupBox); + layout->addWidget(_edge_groupBox); + layout->addWidget(_cursor_groupBox); + layout->addStretch(1); + _widget->setLayout(layout); + + connect(_fen_checkBox, SIGNAL(stateChanged(int)), &_view, SLOT(set_measure_en(int))); + connect(&_view, SIGNAL(measure_updated()), this, SLOT(measure_updated())); + + this->setWidget(_widget); + _widget->setGeometry(0, 0, sizeHint().width(), 2000); + _widget->setObjectName("measureWidget"); + + retranslateUi(); +} + +MeasureDock::~MeasureDock() +{ +} + +void MeasureDock::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) + retranslateUi(); + else if (event->type() == QEvent::StyleChange) + reStyle(); + QScrollArea::changeEvent(event); +} + +void MeasureDock::retranslateUi() +{ + _mouse_groupBox->setTitle(tr("Mouse measurement")); + _fen_checkBox->setText(tr("Enable floating measurement")); + _dist_groupBox->setTitle(tr("Cursor Distance")); + _edge_groupBox->setTitle(tr("Edges")); + _cursor_groupBox->setTitle(tr("Cursors")); + + _channel_label->setText(tr("Channel")); + _edge_label->setText(tr("Rising/Falling/Edges")); + _time_label->setText(tr("Time/Samples")); + + _w_label->setText(tr("W: ")); + _p_label->setText(tr("P: ")); + _f_label->setText(tr("F: ")); + _d_label->setText(tr("D: ")); +} + +void MeasureDock::reStyle() +{ + QString iconPath = ":/icons/" + qApp->property("Style").toString(); + + _dist_add_btn->setIcon(QIcon(iconPath+"/add.png")); + _edge_add_btn->setIcon(QIcon(iconPath+"/add.png")); + + for (QVector ::const_iterator i = _dist_del_btn_vec.begin(); + i != _dist_del_btn_vec.end(); i++) + (*i)->setIcon(QIcon(iconPath+"/del.png")); + for (QVector ::const_iterator i = _edge_del_btn_vec.begin(); + i != _edge_del_btn_vec.end(); i++) + (*i)->setIcon(QIcon(iconPath+"/del.png")); +} + +void MeasureDock::paintEvent(QPaintEvent *) +{ +// QStyleOption opt; +// opt.init(this); +// QPainter p(this); +// style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + +void MeasureDock::refresh() +{ + +} + +void MeasureDock::reload() +{ + if (_session.get_device()->dev_inst()->mode == LOGIC) + _edge_groupBox->setDisabled(false); + else + _edge_groupBox->setDisabled(true); + + for (QVector ::const_iterator i = _edge_ch_cmb_vec.begin(); + i != _edge_ch_cmb_vec.end(); i++) { + update_probe_selector(*i); + } + reCalc(); +} + +void MeasureDock::cursor_update() +{ + using namespace pv::data; + + if (!_cursor_pushButton_list.empty()) { + for (QVector ::const_iterator i = _cursor_del_btn_vec.begin(); + i != _cursor_del_btn_vec.end(); i++) + (*i)->deleteLater(); + for (QVector::Iterator i = _cursor_pushButton_list.begin(); + i != _cursor_pushButton_list.end(); i++) + (*i)->deleteLater(); + for (QVector::Iterator i = _curpos_label_list.begin(); + i != _curpos_label_list.end(); i++) + (*i)->deleteLater(); + + _cursor_del_btn_vec.clear(); + _cursor_pushButton_list.clear(); + _curpos_label_list.clear(); + } + + update_dist(); + update_edge(); + + int index = 1; + QString iconPath = ":/icons/" + qApp->property("Style").toString(); + for(std::list::iterator i = _view.get_cursorList().begin(); + i != _view.get_cursorList().end(); i++) { + QString curCursor = QString::number(index); + + QToolButton *del_btn = new QToolButton(_widget); + del_btn->setIcon(QIcon(iconPath+"/del.png")); + del_btn->setCheckable(true); + QPushButton *_cursor_pushButton = new QPushButton(curCursor, _widget); + set_cursor_btn_color(_cursor_pushButton); + QString _cur_text = _view.get_cm_time(index - 1) + "/" + QString::number(_view.get_cursor_samples(index - 1)); + QLabel *_curpos_label = new QLabel(_cur_text, _widget); + _cursor_del_btn_vec.push_back(del_btn); + _cursor_pushButton_list.push_back(_cursor_pushButton); + _curpos_label_list.push_back(_curpos_label); + + _cursor_layout->addWidget(del_btn, 1+index, 0); + _cursor_layout->addWidget(_cursor_pushButton, 1 + index, 1); + _cursor_layout->addWidget(_curpos_label, 1 + index, 2); + + connect(del_btn, SIGNAL(clicked()), this, SLOT(del_cursor())); + connect(_cursor_pushButton, SIGNAL(clicked()), this, SLOT(goto_cursor())); + + index++; + } + + update(); +} + +void MeasureDock::measure_updated() +{ + _width_label->setText(_view.get_measure("width")); + _period_label->setText(_view.get_measure("period")); + _freq_label->setText(_view.get_measure("frequency")); + _duty_label->setText(_view.get_measure("duty")); +} + +void MeasureDock::cursor_moving() +{ + //TimeMarker* grabbed_marker = _view.get_ruler()->get_grabbed_cursor(); + if (_view.cursors_shown()) { + int index = 0; + for(std::list::iterator i = _view.get_cursorList().begin(); + i != _view.get_cursorList().end(); i++) { + QString _cur_text = _view.get_cm_time(index) + "/" + QString::number(_view.get_cursor_samples(index)); + _curpos_label_list.at(index)->setText(_cur_text); + //_curvalue_label_list.at(index)->setText(_view.get_cm_value(index)); + index++; + } + } + + update_dist(); +} + +void MeasureDock::reCalc() +{ + cursor_update(); + update_dist(); + update_edge(); +} + +void MeasureDock::goto_cursor() +{ + int index = 0; + + for (QVector::Iterator i = _cursor_pushButton_list.begin(); + i != _cursor_pushButton_list.end(); i++) { + QPushButton *button = qobject_cast(sender()); + if ((*i) == button) { + _view.set_cursor_middle(index); + break; + } + index++; + } +} + +void MeasureDock::add_dist_measure() +{ + if (_dist_row_widget_vec.size() > Max_Measure_Limits) + return; + + QWidget *row_widget = new QWidget(_widget); + row_widget->setContentsMargins(0,0,0,0); + QHBoxLayout *row_layout = new QHBoxLayout(row_widget); + row_layout->setContentsMargins(0,0,0,0); + row_layout->setSpacing(0); + row_widget->setLayout(row_layout); + _dist_row_widget_vec.push_back(row_widget); + + QString iconPath = ":/icons/" + qApp->property("Style").toString(); + QToolButton *del_btn = new QToolButton(row_widget); + del_btn->setIcon(QIcon(iconPath+"/del.png")); + del_btn->setCheckable(true); + QPushButton *s_btn = new QPushButton(tr(" "), row_widget); + s_btn->setObjectName("dist"); + QPushButton *e_btn = new QPushButton(tr(" "), row_widget); + e_btn->setObjectName("dist"); + QLabel *r_label = new QLabel(row_widget); + QLabel *g_label = new QLabel(tr("-"), row_widget); + g_label->setContentsMargins(0,0,0,0); + _dist_del_btn_vec.push_back(del_btn); + _dist_s_btn_vec.push_back(s_btn); + _dist_e_btn_vec.push_back(e_btn); + _dist_r_label_vec.push_back(r_label); + + connect(del_btn, SIGNAL(clicked()), this, SLOT(del_dist_measure())); + connect(s_btn, SIGNAL(clicked()), this, SLOT(show_all_coursor())); + connect(e_btn, SIGNAL(clicked()), this, SLOT(show_all_coursor())); + + row_layout->addWidget(del_btn); + row_layout->addSpacing(5); + row_layout->addWidget(s_btn); + row_layout->addWidget(g_label); + row_layout->addWidget(e_btn); + row_layout->addSpacing(5); + row_layout->addWidget(r_label, 100); + + _dist_layout->addWidget(row_widget, _dist_row_widget_vec.size(), 0, 1, 7); + +} + +void MeasureDock::del_dist_measure() +{ + int del_index = 0; + for (QVector ::const_iterator i = _dist_del_btn_vec.begin(); + i != _dist_del_btn_vec.end(); i++) { + if ((*i)->isChecked()) { + _dist_layout->removeWidget(_dist_row_widget_vec.at(del_index)); + _dist_row_widget_vec.at(del_index)->deleteLater(); + + _dist_del_btn_vec.remove(del_index); + _dist_s_btn_vec.remove(del_index); + _dist_e_btn_vec.remove(del_index); + _dist_r_label_vec.remove(del_index); + _dist_row_widget_vec.remove(del_index); + + break; + } + del_index++; + } +} + +void MeasureDock::add_edge_measure() +{ + if (_edge_row_widget_vec.size() > Max_Measure_Limits) + return; + + QWidget *row_widget = new QWidget(_widget); + row_widget->setContentsMargins(0,0,0,0); + QHBoxLayout *row_layout = new QHBoxLayout(row_widget); + row_layout->setContentsMargins(0,0,0,0); + row_layout->setSpacing(0); + row_widget->setLayout(row_layout); + _edge_row_widget_vec.push_back(row_widget); + + QString iconPath = ":/icons/" + qApp->property("Style").toString(); + QToolButton *del_btn = new QToolButton(row_widget); + del_btn->setIcon(QIcon(iconPath+"/del.png")); + del_btn->setCheckable(true); + QPushButton *s_btn = new QPushButton(tr(" "), row_widget); + s_btn->setObjectName("edge"); + QPushButton *e_btn = new QPushButton(tr(" "), row_widget); + e_btn->setObjectName("edge"); + QLabel *r_label = new QLabel(row_widget); + QLabel *g_label = new QLabel(tr("-"), row_widget); + g_label->setContentsMargins(0,0,0,0); + QLabel *a_label = new QLabel(tr("@"), row_widget); + a_label->setContentsMargins(0,0,0,0); + QComboBox *ch_cmb = create_probe_selector(row_widget); + _edge_del_btn_vec.push_back(del_btn); + _edge_s_btn_vec.push_back(s_btn); + _edge_e_btn_vec.push_back(e_btn); + _edge_ch_cmb_vec.push_back(ch_cmb); + _edge_r_label_vec.push_back(r_label); + + connect(del_btn, SIGNAL(clicked()), this, SLOT(del_edge_measure())); + connect(s_btn, SIGNAL(clicked()), this, SLOT(show_all_coursor())); + connect(e_btn, SIGNAL(clicked()), this, SLOT(show_all_coursor())); + connect(ch_cmb, SIGNAL(currentIndexChanged(int)), this, SLOT(update_edge())); + + row_layout->addWidget(del_btn); + row_layout->addSpacing(5); + row_layout->addWidget(s_btn); + row_layout->addWidget(g_label); + row_layout->addWidget(e_btn); + row_layout->addWidget(a_label); + row_layout->addWidget(ch_cmb); + row_layout->addSpacing(5); + row_layout->addWidget(r_label, 100); + + _edge_layout->addWidget(row_widget, _edge_row_widget_vec.size(), 0, 1, 7); + +} + +void MeasureDock::del_edge_measure() +{ + int del_index = 0; + for (QVector ::const_iterator i = _edge_del_btn_vec.begin(); + i != _edge_del_btn_vec.end(); i++) { + if ((*i)->isChecked()) { + _edge_layout->removeWidget(_edge_row_widget_vec.at(del_index)); + _edge_row_widget_vec.at(del_index)->deleteLater(); + + _edge_del_btn_vec.remove(del_index); + _edge_s_btn_vec.remove(del_index); + _edge_e_btn_vec.remove(del_index); + _edge_r_label_vec.remove(del_index); + _edge_ch_cmb_vec.remove(del_index); + _edge_row_widget_vec.remove(del_index); + + break; + } + del_index++; + } +} + +void MeasureDock::show_all_coursor() +{ + if (_view.get_cursorList().empty()) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Information")); + msg.mBox()->setInformativeText(tr("Please insert cursor before using cursor measure.")); + msg.mBox()->addButton(tr("Ok"), QMessageBox::AcceptRole); + msg.mBox()->setIcon(QMessageBox::Information); + msg.exec(); + + return; + } + + _sel_btn = qobject_cast(sender()); + + QDialog cursor_dlg(_widget); + cursor_dlg.setWindowFlags(Qt::FramelessWindowHint | Qt::Popup | Qt::WindowSystemMenuHint | + Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint); + + int index = 0; + QGridLayout *glayout = new QGridLayout(&cursor_dlg); + for(std::list::iterator i = _view.get_cursorList().begin(); + i != _view.get_cursorList().end(); i++) { + QPushButton *cursor_btn = new QPushButton(QString::number(index+1), &cursor_dlg); + set_cursor_btn_color(cursor_btn); + glayout->addWidget(cursor_btn, index/4, index%4, 1, 1); + + connect(cursor_btn, SIGNAL(clicked()), &cursor_dlg, SLOT(accept())); + connect(cursor_btn, SIGNAL(clicked()), this, SLOT(set_se_cursor())); + index++; + } + + QRect sel_btn_rect = _sel_btn->geometry(); + sel_btn_rect.moveTopLeft(_sel_btn->parentWidget()->mapToGlobal(sel_btn_rect.topLeft())); + cursor_dlg.setGeometry(sel_btn_rect.left(), sel_btn_rect.bottom()+10, + cursor_dlg.width(), cursor_dlg.height()); + cursor_dlg.exec(); +} + +void MeasureDock::set_se_cursor() +{ + QPushButton *sc = qobject_cast(sender()); + if (_sel_btn) + _sel_btn->setText(sc->text()); + + set_cursor_btn_color(_sel_btn); + + if (_sel_btn->objectName() == "dist") + update_dist(); + else if (_sel_btn->objectName() == "edge") + update_edge(); +} + +const view::Cursor* MeasureDock::find_cousor(int index) +{ + int cur_index = 1; + for(std::list::iterator i = _view.get_cursorList().begin(); + i != _view.get_cursorList().end(); i++) { + if (cur_index == index) { + return (*i); + } + } + + return NULL; +} + +void MeasureDock::update_dist() +{ + int dist_index = 0; + for (QVector::Iterator i = _dist_s_btn_vec.begin(); + i != _dist_s_btn_vec.end(); i++) { + bool start_ret, end_ret; + const unsigned int start = (*i)->text().toInt(&start_ret) - 1; + const unsigned int end = _dist_e_btn_vec[dist_index]->text().toInt(&end_ret) - 1; + + if (start_ret) { + if (start + 1 > _view.get_cursorList().size()) { + (*i)->setText(" "); + set_cursor_btn_color((*i)); + start_ret = false; + } + } + if (end_ret) { + if (end + 1 > _view.get_cursorList().size()) { + _dist_e_btn_vec[dist_index]->setText(" "); + set_cursor_btn_color(_dist_e_btn_vec[dist_index]); + end_ret = false; + } + } + + if (start_ret && end_ret) { + int64_t delta = _view.get_cursor_samples(start) - + _view.get_cursor_samples(end); + QString delta_text = _view.get_cm_delta(start, end) + + "/" + QString::number(delta); + if (delta < 0) + delta_text.replace('+', '-'); + _dist_r_label_vec[dist_index]->setText(delta_text); + } else { + _dist_r_label_vec[dist_index]->setText(" "); + } + + dist_index++; + } +} + +void MeasureDock::update_edge() +{ + int edge_index = 0; + for (QVector::Iterator i = _edge_s_btn_vec.begin(); + i != _edge_s_btn_vec.end(); i++) { + bool start_ret, end_ret; + const unsigned int start = (*i)->text().toInt(&start_ret) - 1; + const unsigned int end = _edge_e_btn_vec[edge_index]->text().toInt(&end_ret) - 1; + + if (start_ret) { + if (start + 1 > _view.get_cursorList().size()) { + (*i)->setText(" "); + set_cursor_btn_color((*i)); + start_ret = false; + } + } + if (end_ret) { + if (end + 1 > _view.get_cursorList().size()) { + _edge_e_btn_vec[edge_index]->setText(" "); + set_cursor_btn_color(_edge_e_btn_vec[edge_index]); + end_ret = false; + } + } + + bool mValid = false; + if (start_ret && end_ret) { + uint64_t rising_edges; + uint64_t falling_edges; + const std::vector< boost::shared_ptr > sigs(_session.get_signals()); + for(size_t i = 0; i < sigs.size(); i++) { + const boost::shared_ptr s(sigs[i]); + boost::shared_ptr logicSig; + assert(s); + if ((logicSig = dynamic_pointer_cast(s)) && + (logicSig->enabled()) && + (logicSig->get_index() == _edge_ch_cmb_vec[edge_index]->currentText().toInt())){ + if (logicSig->edges(_view.get_cursor_samples(end), _view.get_cursor_samples(start), rising_edges, falling_edges)) { + QString delta_text = QString::number(rising_edges) + "/" + + QString::number(falling_edges) + "/" + + QString::number(rising_edges + falling_edges); + _edge_r_label_vec[edge_index]->setText(delta_text); + mValid = true; + break; + } + } + } + } + + if (!mValid) + _edge_r_label_vec[edge_index]->setText("-/-/-"); + + edge_index++; + } +} + +void MeasureDock::set_cursor_btn_color(QPushButton *btn) +{ + bool ret; + const unsigned int start = btn->text().toInt(&ret) - 1; + QColor cursor_color = ret ? view::Ruler::CursorColor[start%8] : QColor("#302F2F"); + QString border_width = ret ? "0px" : "1px"; + QString normal = "{background-color:" + cursor_color.name() + + "; color:black" + "; border-width:" + border_width + ";}"; + QString hover = "{background-color:" + cursor_color.darker().name() + + "; color:black" + "; border-width:" + border_width + ";}"; + QString style = "QPushButton:hover" + hover + + "QPushButton" + normal; + btn->setStyleSheet(style); +} + +QComboBox* MeasureDock::create_probe_selector(QWidget *parent) +{ + QComboBox *selector = new QComboBox(parent); + update_probe_selector(selector); + return selector; +} + +void MeasureDock::update_probe_selector(QComboBox *selector) +{ + selector->clear(); + const std::vector< boost::shared_ptr > sigs(_session.get_signals()); + for(size_t i = 0; i < sigs.size(); i++) { + const boost::shared_ptr s(sigs[i]); + assert(s); + + if (dynamic_pointer_cast(s) && s->enabled()) + { + selector->addItem(QString::number(s->get_index())); + } + } +} + +void MeasureDock::del_cursor() +{ + int del_index = 0; + Cursor* cursor = NULL; + for (QVector ::const_iterator i = _cursor_del_btn_vec.begin(); + i != _cursor_del_btn_vec.end(); i++) { + if ((*i)->isChecked()) { + int cur_index = 0; + std::list::iterator ite = _view.get_cursorList().begin(); + while (cur_index++ != del_index) + ite++; + cursor = *ite; + break; + } + del_index++; + } + + if (cursor) + _view.del_cursor(cursor); + if (_view.get_cursorList().empty()) + _view.show_cursors(false); + + cursor_update(); + _view.update(); +} + +} // namespace dock +} // namespace pv diff --git a/DSView/pv/dock/measuredock.h b/DSView/pv/dock/measuredock.h old mode 100644 new mode 100755 index 920033c8..c9f9beeb --- a/DSView/pv/dock/measuredock.h +++ b/DSView/pv/dock/measuredock.h @@ -1,146 +1,155 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef DSVIEW_PV_MEASUREDOCK_H -#define DSVIEW_PV_MEASUREDOCK_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -namespace pv { - -class SigSession; - -namespace view { - class Cursor; - class View; -} - -namespace dock { - -class MeasureDock : public QScrollArea -{ - Q_OBJECT - -private: - static const int Max_Measure_Limits = 16; - -public: - MeasureDock(QWidget *parent, pv::view::View &view, SigSession &session); - ~MeasureDock(); - - void paintEvent(QPaintEvent *); - void reload(); -private: - QComboBox* create_probe_selector(QWidget *parent); - void update_probe_selector(QComboBox *selector); - -signals: - -private slots: - void goto_cursor(); - - void add_dist_measure(); - void del_dist_measure(); - void add_edge_measure(); - void del_edge_measure(); - void show_all_coursor(); - void set_se_cursor(); - const view::Cursor* find_cousor(int index); - void update_dist(); - void update_edge(); - void set_cursor_btn_color(QPushButton *btn); - void del_cursor(); - -public slots: - void cursor_update(); - void cursor_moving(); - void reCalc(); - void measure_updated(); - void refresh(); - -private: - SigSession &_session; - view::View &_view; - - QWidget *_widget; - QGridLayout *_mouse_layout; - QGroupBox *_mouse_groupBox; - QCheckBox *_fen_checkBox; - QLabel *_width_label; - QLabel *_period_label; - QLabel *_freq_label; - QLabel *_duty_label; - - QGridLayout *_dist_layout; - QGroupBox *_dist_groupBox; - QToolButton *_dist_add_btn; - QVector _dist_row_widget_vec; - QVector _dist_del_btn_vec; - QVector _dist_s_btn_vec; - QVector _dist_e_btn_vec; - QVector _dist_r_label_vec; - - QGridLayout *_edge_layout; - QGroupBox *_edge_groupBox; - QToolButton *_edge_add_btn; - QVector _edge_row_widget_vec; - QVector _edge_del_btn_vec; - QVector _edge_s_btn_vec; - QVector _edge_e_btn_vec; - QVector _edge_ch_cmb_vec; - QVector _edge_r_label_vec; - - QPushButton *_sel_btn; - - QGridLayout *_cursor_layout; - QGroupBox *_cursor_groupBox; - QVector _cursor_del_btn_vec; - QVector _cursor_pushButton_list; - QVector _curpos_label_list; - - QIcon _icon_add; - QIcon _icon_add_dis; - QIcon _icon_del; - QIcon _icon_del_dis; -}; - -} // namespace dock -} // namespace pv - -#endif // DSVIEW_PV_MEASUREDOCK_H +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DSVIEW_PV_MEASUREDOCK_H +#define DSVIEW_PV_MEASUREDOCK_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace pv { + +class SigSession; + +namespace view { + class Cursor; + class View; +} + +namespace dock { + +class MeasureDock : public QScrollArea +{ + Q_OBJECT + +private: + static const int Max_Measure_Limits = 16; + +public: + MeasureDock(QWidget *parent, pv::view::View &view, SigSession &session); + ~MeasureDock(); + + void paintEvent(QPaintEvent *); + void reload(); + +private: + void changeEvent(QEvent *event); + void retranslateUi(); + void reStyle(); + +private: + QComboBox* create_probe_selector(QWidget *parent); + void update_probe_selector(QComboBox *selector); + +signals: + +private slots: + void goto_cursor(); + + void del_dist_measure(); + void add_edge_measure(); + void del_edge_measure(); + void show_all_coursor(); + void set_se_cursor(); + const view::Cursor* find_cousor(int index); + void update_dist(); + void update_edge(); + void set_cursor_btn_color(QPushButton *btn); + void del_cursor(); + +public slots: + void add_dist_measure(); + void cursor_update(); + void cursor_moving(); + void reCalc(); + void measure_updated(); + void refresh(); + +private: + SigSession &_session; + view::View &_view; + + QWidget *_widget; + QGridLayout *_mouse_layout; + QGroupBox *_mouse_groupBox; + QCheckBox *_fen_checkBox; + QLabel *_width_label; + QLabel *_period_label; + QLabel *_freq_label; + QLabel *_duty_label; + + QGridLayout *_dist_layout; + QGroupBox *_dist_groupBox; + QToolButton *_dist_add_btn; + QVector _dist_row_widget_vec; + QVector _dist_del_btn_vec; + QVector _dist_s_btn_vec; + QVector _dist_e_btn_vec; + QVector _dist_r_label_vec; + + QGridLayout *_edge_layout; + QGroupBox *_edge_groupBox; + QToolButton *_edge_add_btn; + QVector _edge_row_widget_vec; + QVector _edge_del_btn_vec; + QVector _edge_s_btn_vec; + QVector _edge_e_btn_vec; + QVector _edge_ch_cmb_vec; + QVector _edge_r_label_vec; + + QPushButton *_sel_btn; + + QGridLayout *_cursor_layout; + QGroupBox *_cursor_groupBox; + QVector _cursor_del_btn_vec; + QVector _cursor_pushButton_list; + QVector _curpos_label_list; + + QLabel *_channel_label; + QLabel *_edge_label; + QLabel *_time_label; + QLabel *_w_label; + QLabel *_p_label; + QLabel *_f_label; + QLabel *_d_label; +}; + +} // namespace dock +} // namespace pv + +#endif // DSVIEW_PV_MEASUREDOCK_H diff --git a/DSView/pv/dock/protocoldock.cpp b/DSView/pv/dock/protocoldock.cpp old mode 100644 new mode 100755 index e0b8a50b..93ed2add --- a/DSView/pv/dock/protocoldock.cpp +++ b/DSView/pv/dock/protocoldock.cpp @@ -1,817 +1,841 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "protocoldock.h" -#include "../sigsession.h" -#include "../view/decodetrace.h" -#include "../device/devinst.h" -#include "../data/decodermodel.h" -#include "../data/decoderstack.h" -#include "../dialogs/protocollist.h" -#include "../dialogs/protocolexp.h" -#include "../dialogs/dsmessagebox.h" -#include "../view/view.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace pv { -namespace dock { - -ProtocolDock::ProtocolDock(QWidget *parent, view::View &view, SigSession &session) : - QScrollArea(parent), - _session(session), - _view(view), - _cur_search_index(-1), - _search_edited(false), - _searching(false), - _add_silent(false) -{ - _up_widget = new QWidget(this); - - QHBoxLayout *hori_layout = new QHBoxLayout(); - - _add_button = new QPushButton(_up_widget); - _add_button->setFlat(true); - _add_button->setIcon(QIcon::fromTheme("protocol", - QIcon(":/icons/add.png"))); - _del_all_button = new QPushButton(_up_widget); - _del_all_button->setFlat(true); - _del_all_button->setIcon(QIcon::fromTheme("protocol", - QIcon(":/icons/del.png"))); - _del_all_button->setCheckable(true); - _protocol_combobox = new QComboBox(_up_widget); - - GSList *l = g_slist_sort(g_slist_copy( - (GSList*)srd_decoder_list()), decoder_name_cmp); - for(; l; l = l->next) - { - const srd_decoder *const d = (srd_decoder*)l->data; - assert(d); - - const bool have_probes = (d->channels || d->opt_channels) != 0; - if (true == have_probes) { - _protocol_combobox->addItem(QString::fromUtf8(d->name), qVariantFromValue(l->data)); - } - } - g_slist_free(l); - - hori_layout->addWidget(_add_button); - hori_layout->addWidget(_del_all_button); - hori_layout->addWidget(_protocol_combobox); - hori_layout->addStretch(1); - - connect(_add_button, SIGNAL(clicked()), - this, SLOT(add_protocol())); - connect(_del_all_button, SIGNAL(clicked()), - this, SLOT(del_protocol())); - - _up_layout = new QVBoxLayout(); - _up_layout->addLayout(hori_layout); - _up_layout->addStretch(1); - - _up_widget->setLayout(_up_layout); - _up_widget->setMinimumHeight(150); - -// this->setWidget(_widget); -// _widget->setGeometry(0, 0, sizeHint().width(), 500); -// _widget->setObjectName("protocolWidget"); - - _dn_widget = new QWidget(this); - - _dn_set_button = new QPushButton(_dn_widget); - _dn_set_button->setFlat(true); - _dn_set_button->setIcon(QIcon::fromTheme("protocol", - QIcon(":/icons/gear.png"))); - connect(_dn_set_button, SIGNAL(clicked()), - this, SLOT(set_model())); - - _dn_save_button = new QPushButton(_dn_widget); - _dn_save_button->setFlat(true); - _dn_save_button->setIcon(QIcon::fromTheme("protocol", - QIcon(":/icons/save.png"))); - connect(_dn_save_button, SIGNAL(clicked()), - this, SLOT(export_table_view())); - - _dn_nav_button = new QPushButton(_dn_widget); - _dn_nav_button->setFlat(true); - _dn_nav_button->setIcon(QIcon::fromTheme("protocol", - QIcon(":/icons/nav.png"))); - connect(_dn_nav_button, SIGNAL(clicked()), - this, SLOT(nav_table_view())); - - QHBoxLayout *dn_title_layout = new QHBoxLayout(); - dn_title_layout->addWidget(_dn_set_button, 0, Qt::AlignLeft); - dn_title_layout->addWidget(_dn_save_button, 0, Qt::AlignLeft); - dn_title_layout->addWidget(new QLabel(tr("Protocol List Viewer"), _dn_widget), 1, Qt::AlignLeft); - dn_title_layout->addWidget(_dn_nav_button, 0, Qt::AlignRight); - //dn_title_layout->addStretch(1); - - _table_view = new QTableView(_dn_widget); - _table_view->setModel(_session.get_decoder_model()); - _table_view->setAlternatingRowColors(true); - _table_view->setShowGrid(false); - _table_view->horizontalHeader()->setStretchLastSection(true); - _table_view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); - _table_view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); - - _pre_button = new QPushButton(_dn_widget); - _nxt_button = new QPushButton(_dn_widget); - _pre_button->setIcon(QIcon::fromTheme("protocol", - QIcon(":/icons/pre.png"))); - _nxt_button->setIcon(QIcon::fromTheme("protocol", - QIcon(":/icons/next.png"))); - connect(_pre_button, SIGNAL(clicked()), - this, SLOT(search_pre())); - connect(_nxt_button, SIGNAL(clicked()), - this, SLOT(search_nxt())); - - QPushButton *search_button = new QPushButton(this); - search_button->setIcon(QIcon::fromTheme("protocol", - QIcon(":/icons/search.png"))); - search_button->setFixedWidth(search_button->height()); - search_button->setDisabled(true); - _search_edit = new QLineEdit(_dn_widget); - _search_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - _search_edit->setPlaceholderText(tr("search")); - QHBoxLayout *search_layout = new QHBoxLayout(); - search_layout->addWidget(search_button); - search_layout->addStretch(1); - search_layout->setContentsMargins(0, 0, 0, 0); - _search_edit->setLayout(search_layout); - _search_edit->setTextMargins(search_button->width(), 0, 0, 0); - - _dn_search_layout = new QHBoxLayout(); - _dn_search_layout->addWidget(_pre_button, 0, Qt::AlignLeft); - _dn_search_layout->addWidget(_search_edit, 1, Qt::AlignLeft); - _dn_search_layout->addWidget(_nxt_button, 0, Qt::AlignRight); - - _matchs_label = new QLabel(_dn_widget); - QHBoxLayout *dn_match_layout = new QHBoxLayout(); - dn_match_layout->addWidget(new QLabel(tr("Matching Items:")), 0, Qt::AlignLeft); - dn_match_layout->addWidget(_matchs_label, 0, Qt::AlignLeft); - dn_match_layout->addStretch(1); - - _dn_layout = new QVBoxLayout(); - _dn_layout->addLayout(dn_title_layout); - _dn_layout->addLayout(_dn_search_layout); - _dn_layout->addLayout(dn_match_layout); - _dn_layout->addWidget(_table_view); - - _dn_widget->setLayout(_dn_layout); - _dn_widget->setMinimumHeight(350); - - _split_widget = new QSplitter(this); - _split_widget->insertWidget(0, _up_widget); - _split_widget->insertWidget(1, _dn_widget); - _split_widget->setOrientation(Qt::Vertical); - _split_widget->setCollapsible(0, false); - _split_widget->setCollapsible(1, false); - //_split_widget->setStretchFactor(1, 1); - //_split_widget - - this->setWidgetResizable(true); - this->setWidget(_split_widget); - //_split_widget->setGeometry(0, 0, sizeHint().width(), 500); - _split_widget->setObjectName("protocolWidget"); - - connect(&_session, SIGNAL(decode_done()), this, SLOT(update_model())); - connect(this, SIGNAL(protocol_updated()), this, SLOT(update_model())); - connect(_table_view, SIGNAL(clicked(QModelIndex)), this, SLOT(item_clicked(QModelIndex))); - connect(_table_view->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(column_resize(int, int, int))); - //connect(_table_view->verticalScrollBar(), SIGNAL(sliderMoved()), this, SLOT(sliderMoved())); - connect(_search_edit, SIGNAL(editingFinished()), this, SLOT(search_changed())); -} - -ProtocolDock::~ProtocolDock() -{ -} - -void ProtocolDock::paintEvent(QPaintEvent *) -{ -// QStyleOption opt; -// opt.init(this); -// QPainter p(this); -// style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - -void ProtocolDock::resizeEvent(QResizeEvent *event) -{ - int width = this->visibleRegion().boundingRect().width(); - width = width - _dn_layout->margin() * 2 - - _dn_search_layout->margin() * 2 - - _dn_search_layout->spacing() * 2 - - _pre_button->width()-_nxt_button->width(); - width = std::max(width, 0); - _search_edit->setMinimumWidth(width); - QScrollArea::resizeEvent(event); -} - -int ProtocolDock::decoder_name_cmp(const void *a, const void *b) -{ - return strcmp(((const srd_decoder*)a)->name, - ((const srd_decoder*)b)->name); -} - -bool ProtocolDock::sel_protocol(QString id) -{ - QString name; - GSList *l = g_slist_sort(g_slist_copy( - (GSList*)srd_decoder_list()), decoder_name_cmp); - for(; l; l = l->next) - { - const srd_decoder *const d = (srd_decoder*)l->data; - assert(d); - - const bool have_probes = (d->channels || d->opt_channels) != 0; - if (true == have_probes && - QString::fromUtf8(d->id) == id) { - name = QString::fromUtf8(d->name); - break; - } - } - g_slist_free(l); - - _protocol_combobox->setCurrentText(name); - if (_protocol_combobox->currentText() == name) - return true; - else - return false; -} - -void ProtocolDock::add_protocol() -{ - add_protocol(false); -} - -void ProtocolDock::add_protocol(bool silent) -{ - if (_session.get_device()->dev_inst()->mode != LOGIC) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Protocol Analyzer")); - msg.mBox()->setInformativeText(tr("Protocol Analyzer is only valid in Digital Mode!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - } else { - srd_decoder *const decoder = - (srd_decoder*)(_protocol_combobox->itemData(_protocol_combobox->currentIndex())).value(); - if (_session.add_decoder(decoder, silent)) { - //std::list _sel_probes = dlg.get_sel_probes(); - //QMap & _options = dlg.get_options(); - //QMap _options_index = dlg.get_options_index(); - - QPushButton *_del_button = new QPushButton(_up_widget); - QPushButton *_set_button = new QPushButton(_up_widget); - _del_button->setFlat(true); - _del_button->setIcon(QIcon::fromTheme("protocol", - QIcon(":/icons/del.png"))); - _set_button->setFlat(true); - _set_button->setIcon(QIcon::fromTheme("protocol", - QIcon(":/icons/gear.png"))); - QLabel *_protocol_label = new QLabel(_up_widget); - QLabel *_progress_label = new QLabel(_up_widget); - - _del_button->setCheckable(true); - _protocol_label->setText(_protocol_combobox->currentText()); - - connect(_del_button, SIGNAL(clicked()), - this, SLOT(del_protocol())); - connect(_set_button, SIGNAL(clicked()), - this, SLOT(rst_protocol())); - - _del_button_list.push_back(_del_button); - _set_button_list.push_back(_set_button); - _protocol_label_list.push_back(_protocol_label); - _progress_label_list.push_back(_progress_label); - _protocol_index_list.push_back(_protocol_combobox->currentIndex()); - - QHBoxLayout *hori_layout = new QHBoxLayout(); - hori_layout->addWidget(_set_button); - hori_layout->addWidget(_del_button); - hori_layout->addWidget(_protocol_label); - hori_layout->addWidget(_progress_label); - hori_layout->addStretch(1); - _hori_layout_list.push_back(hori_layout); - _up_layout->insertLayout(_del_button_list.size(), hori_layout); - - // progress connection - const std::vector< boost::shared_ptr > decode_sigs( - _session.get_decode_signals()); - connect(decode_sigs.back().get(), SIGNAL(decoded_progress(int)), this, SLOT(decoded_progress(int))); - - protocol_updated(); - } - } -} - -void ProtocolDock::rst_protocol() -{ - int rst_index = 0; - for (QVector ::const_iterator i = _set_button_list.begin(); - i != _set_button_list.end(); i++) { - QPushButton *button = qobject_cast(sender()); - if ((*i) == button) { - //pv::decoder::DemoConfig dlg(this, _session.get_device(), _protocol_index_list.at(rst_index)); - //dlg.set_config(_session.get_decode_probes(rst_index), _session.get_decode_options_index(rst_index)); - //if (dlg.exec()) { - //std::list _sel_probes = dlg.get_sel_probes(); - //QMap & _options = dlg.get_options(); - //QMap _options_index = dlg.get_options_index(); - - //_session.rst_protocol_analyzer(rst_index, _sel_probes, _options, _options_index); - //} - _session.rst_decoder(rst_index); - break; - } - rst_index++; - } - protocol_updated(); -} - -void ProtocolDock::del_protocol() -{ - if (_del_all_button->isChecked()) { - _del_all_button->setChecked(false); - if (_hori_layout_list.size() > 0) { - int del_index = 0; - for (QVector ::const_iterator i = _hori_layout_list.begin(); - i != _hori_layout_list.end(); i++) { - _up_layout->removeItem((*i)); - delete (*i); - delete _del_button_list.at(del_index); - delete _set_button_list.at(del_index); - delete _protocol_label_list.at(del_index); - delete _progress_label_list.at(del_index); - - _session.remove_decode_signal(0); - del_index++; - } - _hori_layout_list.clear(); - _del_button_list.clear(); - _set_button_list.clear(); - _protocol_label_list.clear(); - _progress_label_list.clear(); - _protocol_index_list.clear(); - } else { - dialogs::DSMessageBox msg(NULL); - msg.mBox()->setText(tr("Protocol Analyzer")); - msg.mBox()->setInformativeText(tr("No Protocol Analyzer to delete!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - } - } else { - int del_index = 0; - for (QVector ::const_iterator i = _del_button_list.begin(); - i != _del_button_list.end(); i++) { - if ((*i)->isChecked()) { - _up_layout->removeItem(_hori_layout_list.at(del_index)); - - delete _hori_layout_list.at(del_index); - delete _del_button_list.at(del_index); - delete _set_button_list.at(del_index); - delete _protocol_label_list.at(del_index); - delete _progress_label_list.at(del_index); - - _hori_layout_list.remove(del_index); - _del_button_list.remove(del_index); - _set_button_list.remove(del_index); - _protocol_label_list.remove(del_index); - _progress_label_list.remove(del_index); - _protocol_index_list.remove(del_index); - - _session.remove_decode_signal(del_index); - break; - } - del_index++; - } - } - protocol_updated(); -} - -void ProtocolDock::del_all_protocol() -{ - if (_hori_layout_list.size() > 0) { - int del_index = 0; - for (QVector ::const_iterator i = _hori_layout_list.begin(); - i != _hori_layout_list.end(); i++) { - _up_layout->removeItem((*i)); - delete (*i); - delete _del_button_list.at(del_index); - delete _set_button_list.at(del_index); - delete _protocol_label_list.at(del_index); - delete _progress_label_list.at(del_index); - - _session.remove_decode_signal(0); - del_index++; - } - _hori_layout_list.clear(); - _del_button_list.clear(); - _set_button_list.clear(); - _protocol_label_list.clear(); - _progress_label_list.clear(); - _protocol_index_list.clear(); - - protocol_updated(); - } -} - -void ProtocolDock::decoded_progress(int progress) -{ - (void) progress; - - int pg = 0; - QString err=""; - const std::vector< boost::shared_ptr > decode_sigs( - _session.get_decode_signals()); - int index = 0; - BOOST_FOREACH(boost::shared_ptr d, decode_sigs) { - pg = d->get_progress(); - if (d->decoder()->out_of_memory()) - err = tr("(Out of Memory)"); - QString progress_str = QString::number(pg) + "%" + err; - if (pg == 100) - _progress_label_list.at(index)->setStyleSheet("color:green;"); - else - _progress_label_list.at(index)->setStyleSheet("color:red;"); - _progress_label_list.at(index)->setText(progress_str); - index++; - } - if (pg == 0 || pg % 10 == 1) - update_model(); -} - -void ProtocolDock::set_model() -{ - pv::dialogs::ProtocolList *protocollist_dlg = new pv::dialogs::ProtocolList(this, _session); - protocollist_dlg->exec(); - resize_table_view(_session.get_decoder_model()); - _model_proxy.setSourceModel(_session.get_decoder_model()); - search_done(); - - // clear mark_index of all DecoderStacks - const std::vector< boost::shared_ptr > decode_sigs( - _session.get_decode_signals()); - BOOST_FOREACH(boost::shared_ptr d, decode_sigs) { - d->decoder()->set_mark_index(-1); - } -} - -void ProtocolDock::update_model() -{ - pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); - const std::vector< boost::shared_ptr > decode_sigs( - _session.get_decode_signals()); - if (decode_sigs.size() == 0) - decoder_model->setDecoderStack(NULL); - else if (!decoder_model->getDecoderStack()) - decoder_model->setDecoderStack(decode_sigs.at(0)->decoder()); - else { - unsigned int index = 0; - BOOST_FOREACH(const boost::shared_ptr d, decode_sigs) { - if (d->decoder() == decoder_model->getDecoderStack()) { - decoder_model->setDecoderStack(d->decoder()); - break; - } - index++; - } - if (index >= decode_sigs.size()) - decoder_model->setDecoderStack(decode_sigs.at(0)->decoder()); - } - _model_proxy.setSourceModel(decoder_model); - search_done(); - resize_table_view(decoder_model); -} - -void ProtocolDock::resize_table_view(data::DecoderModel* decoder_model) -{ - if (decoder_model->getDecoderStack()) { - for (int i = 0; i < decoder_model->columnCount(QModelIndex()) - 1; i++) { - _table_view->resizeColumnToContents(i); - if (_table_view->columnWidth(i) > 200) - _table_view->setColumnWidth(i, 200); - } - int top_row = _table_view->rowAt(0); - int bom_row = _table_view->rowAt(_table_view->height()); - if (bom_row >= top_row && top_row >= 0) { - for (int i = top_row; i <= bom_row; i++) - _table_view->resizeRowToContents(i); - } - } -} - -void ProtocolDock::item_clicked(const QModelIndex &index) -{ - pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); - boost::shared_ptr decoder_stack = decoder_model->getDecoderStack(); - if (decoder_stack) { - pv::data::decode::Annotation ann; - if (decoder_stack->list_annotation(ann, index.column(), index.row())) { - const std::vector< boost::shared_ptr > decode_sigs( - _session.get_decode_signals()); - BOOST_FOREACH(boost::shared_ptr d, decode_sigs) { - d->decoder()->set_mark_index(-1); - } - decoder_stack->set_mark_index((ann.start_sample()+ann.end_sample())/2); - _session.show_region(ann.start_sample(), ann.end_sample(), false); - } - } - _table_view->resizeRowToContents(index.row()); - if (index.column() != _model_proxy.filterKeyColumn()) { - _model_proxy.setFilterKeyColumn(index.column()); - _model_proxy.setSourceModel(decoder_model); - search_done(); - } - QModelIndex filterIndex = _model_proxy.mapFromSource(index); - if (filterIndex.isValid()) { - _cur_search_index = filterIndex.row(); - } else { - if (_model_proxy.rowCount() == 0) { - _cur_search_index = -1; - } else { - uint64_t up = 0; - uint64_t dn = _model_proxy.rowCount() - 1; - do { - uint64_t md = (up + dn)/2; - QModelIndex curIndex = _model_proxy.mapToSource(_model_proxy.index(md,_model_proxy.filterKeyColumn())); - if (index.row() == curIndex.row()) { - _cur_search_index = md; - break; - } else if (md == up) { - if (curIndex.row() < index.row() && up < dn) { - QModelIndex nxtIndex = _model_proxy.mapToSource(_model_proxy.index(md+1,_model_proxy.filterKeyColumn())); - if (nxtIndex.row() < index.row()) - md++; - } - _cur_search_index = md + ((curIndex.row() < index.row()) ? 0.5 : -0.5); - break; - } else if (curIndex.row() < index.row()) { - up = md; - } else if (curIndex.row() > index.row()) { - dn = md; - } - }while(1); - } - } -} - -void ProtocolDock::column_resize(int index, int old_size, int new_size) -{ - (void)index; - (void)old_size; - (void)new_size; - pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); - if (decoder_model->getDecoderStack()) { - int top_row = _table_view->rowAt(0); - int bom_row = _table_view->rowAt(_table_view->height()); - if (bom_row >= top_row && top_row >= 0) { - for (int i = top_row; i <= bom_row; i++) - _table_view->resizeRowToContents(i); - } - } -} - -void ProtocolDock::export_table_view() -{ - pv::dialogs::ProtocolExp *protocolexp_dlg = new pv::dialogs::ProtocolExp(this, _session); - protocolexp_dlg->exec(); -} - -void ProtocolDock::nav_table_view() -{ - uint64_t row_index = 0; - pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); - boost::shared_ptr decoder_stack = decoder_model->getDecoderStack(); - if (decoder_stack) { - uint64_t offset = _view.offset() * (decoder_stack->samplerate() * _view.scale()); - std::map rows = decoder_stack->get_rows_lshow(); - int column = _model_proxy.filterKeyColumn(); - for (std::map::const_iterator i = rows.begin(); - i != rows.end(); i++) { - if ((*i).second && column-- == 0) { - row_index = decoder_stack->get_annotation_index((*i).first, offset); - break; - } - } - QModelIndex index = _model_proxy.mapToSource(_model_proxy.index(row_index, _model_proxy.filterKeyColumn())); - if(index.isValid()){ - _table_view->scrollTo(index); - _table_view->setCurrentIndex(index); - - pv::data::decode::Annotation ann; - decoder_stack->list_annotation(ann, index.column(), index.row()); - const std::vector< boost::shared_ptr > decode_sigs( - _session.get_decode_signals()); - BOOST_FOREACH(boost::shared_ptr d, decode_sigs) { - d->decoder()->set_mark_index(-1); - } - decoder_stack->set_mark_index((ann.start_sample()+ann.end_sample())/2); - _view.set_all_update(true); - _view.update(); - } - } -} - -void ProtocolDock::search_pre() -{ - search_update(); - // now the proxy only contains rows that match the name - // let's take the pre one and map it to the original model - if (_model_proxy.rowCount() == 0) { - _table_view->scrollToTop(); - _table_view->clearSelection(); - _matchs_label->setText(QString::number(0)); - _cur_search_index = -1; - return; - } - int i = 0; - uint64_t rowCount = _model_proxy.rowCount(); - QModelIndex matchingIndex; - pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); - boost::shared_ptr decoder_stack = decoder_model->getDecoderStack(); - do { - _cur_search_index--; - if (_cur_search_index <= -1 || _cur_search_index >= _model_proxy.rowCount()) - _cur_search_index = _model_proxy.rowCount() - 1; - - matchingIndex = _model_proxy.mapToSource(_model_proxy.index(ceil(_cur_search_index),_model_proxy.filterKeyColumn())); - if (!decoder_stack || !matchingIndex.isValid()) - break; - i = 1; - uint64_t row = matchingIndex.row() + 1; - uint64_t col = matchingIndex.column(); - pv::data::decode::Annotation ann; - bool ann_valid; - while(i < _str_list.size()) { - QString nxt = _str_list.at(i); - do { - ann_valid = decoder_stack->list_annotation(ann, col, row); - row++; - }while(ann_valid && (ann.type() < 100 || ann.type() > 999)); - QString source = ann.annotations().at(0); - if (ann_valid && source.contains(nxt)) - i++; - else - break; - } - }while(i < _str_list.size() && --rowCount); - - if(i >= _str_list.size() && matchingIndex.isValid()){ - _table_view->scrollTo(matchingIndex); - _table_view->setCurrentIndex(matchingIndex); - _table_view->clicked(matchingIndex); - } else { - _table_view->scrollToTop(); - _table_view->clearSelection(); - _matchs_label->setText(QString::number(0)); - _cur_search_index = -1; - } -} - -void ProtocolDock::search_nxt() -{ - search_update(); - // now the proxy only contains rows that match the name - // let's take the pre one and map it to the original model - if (_model_proxy.rowCount() == 0) { - _table_view->scrollToTop(); - _table_view->clearSelection(); - _matchs_label->setText(QString::number(0)); - _cur_search_index = -1; - return; - } - int i = 0; - uint64_t rowCount = _model_proxy.rowCount(); - QModelIndex matchingIndex; - pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); - boost::shared_ptr decoder_stack = decoder_model->getDecoderStack(); - do { - _cur_search_index++; - if (_cur_search_index < 0 || _cur_search_index >= _model_proxy.rowCount()) - _cur_search_index = 0; - - matchingIndex = _model_proxy.mapToSource(_model_proxy.index(floor(_cur_search_index),_model_proxy.filterKeyColumn())); - if (!decoder_stack || !matchingIndex.isValid()) - break; - i = 1; - uint64_t row = matchingIndex.row() + 1; - uint64_t col = matchingIndex.column(); - pv::data::decode::Annotation ann; - bool ann_valid; - while(i < _str_list.size()) { - QString nxt = _str_list.at(i); - do { - ann_valid = decoder_stack->list_annotation(ann, col, row); - row++; - }while(ann_valid && (ann.type() < 100 || ann.type() > 999)); - QString source = ann.annotations().at(0); - if (ann_valid && source.contains(nxt)) - i++; - else - break; - } - }while(i < _str_list.size() && --rowCount); - - if(i >= _str_list.size() && matchingIndex.isValid()){ - _table_view->scrollTo(matchingIndex); - _table_view->setCurrentIndex(matchingIndex); - _table_view->clicked(matchingIndex); - } else { - _table_view->scrollToTop(); - _table_view->clearSelection(); - _matchs_label->setText(QString::number(0)); - _cur_search_index = -1; - } -} - -void ProtocolDock::search_done() -{ - QString str = _search_edit->text().trimmed(); - QRegExp rx("(-)"); - _str_list = str.split(rx); - _model_proxy.setFilterFixedString(_str_list.first()); - if (_str_list.size() > 1) - _matchs_label->setText("..."); - else - _matchs_label->setText(QString::number(_model_proxy.rowCount())); -} - -void ProtocolDock::search_changed() -{ - _search_edited = true; - _matchs_label->setText("..."); -} - -void ProtocolDock::search_update() -{ - if (!_search_edited) - return; - - pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); - boost::shared_ptr decoder_stack = decoder_model->getDecoderStack(); - if (!decoder_stack) - return; - - if (decoder_stack->list_annotation_size(_model_proxy.filterKeyColumn()) > ProgressRows) { - QFuture future; - future = QtConcurrent::run([&]{ - search_done(); - }); - Qt::WindowFlags flags = Qt::CustomizeWindowHint; - QProgressDialog dlg(tr("Searching..."), - tr("Cancel"),0,0,this,flags); - dlg.setWindowModality(Qt::WindowModal); - dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | - Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint); - dlg.setCancelButton(NULL); - - QFutureWatcher watcher; - watcher.setFuture(future); - connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); - - dlg.exec(); - } else { - search_done(); - } - _search_edited = false; - //search_done(); -} - - -} // namespace dock -} // namespace pv +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "protocoldock.h" +#include "../sigsession.h" +#include "../view/decodetrace.h" +#include "../device/devinst.h" +#include "../data/decodermodel.h" +#include "../data/decoderstack.h" +#include "../dialogs/protocollist.h" +#include "../dialogs/protocolexp.h" +#include "../dialogs/dsmessagebox.h" +#include "../view/view.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace pv { +namespace dock { + +ProtocolDock::ProtocolDock(QWidget *parent, view::View &view, SigSession &session) : + QScrollArea(parent), + _session(session), + _view(view), + _cur_search_index(-1), + _search_edited(false), + _searching(false), + _add_silent(false) +{ + _up_widget = new QWidget(this); + + QHBoxLayout *hori_layout = new QHBoxLayout(); + + _add_button = new QPushButton(_up_widget); + _add_button->setFlat(true); + _del_all_button = new QPushButton(_up_widget); + _del_all_button->setFlat(true); + _del_all_button->setCheckable(true); + _protocol_combobox = new QComboBox(_up_widget); + + GSList *l = g_slist_sort(g_slist_copy( + (GSList*)srd_decoder_list()), decoder_name_cmp); + for(; l; l = l->next) + { + const srd_decoder *const d = (srd_decoder*)l->data; + assert(d); + + const bool have_probes = (d->channels || d->opt_channels) != 0; + if (true == have_probes) { + _protocol_combobox->addItem(QString::fromUtf8(d->name), qVariantFromValue(l->data)); + } + } + g_slist_free(l); + + hori_layout->addWidget(_add_button); + hori_layout->addWidget(_del_all_button); + hori_layout->addWidget(_protocol_combobox); + hori_layout->addStretch(1); + + connect(_add_button, SIGNAL(clicked()), + this, SLOT(add_protocol())); + connect(_del_all_button, SIGNAL(clicked()), + this, SLOT(del_protocol())); + + _up_layout = new QVBoxLayout(); + _up_layout->addLayout(hori_layout); + _up_layout->addStretch(1); + + _up_widget->setLayout(_up_layout); + _up_widget->setMinimumHeight(150); + +// this->setWidget(_widget); +// _widget->setGeometry(0, 0, sizeHint().width(), 500); +// _widget->setObjectName("protocolWidget"); + + _dn_widget = new QWidget(this); + + _dn_set_button = new QPushButton(_dn_widget); + _dn_set_button->setFlat(true); + connect(_dn_set_button, SIGNAL(clicked()), + this, SLOT(set_model())); + + _dn_save_button = new QPushButton(_dn_widget); + _dn_save_button->setFlat(true); + connect(_dn_save_button, SIGNAL(clicked()), + this, SLOT(export_table_view())); + + _dn_nav_button = new QPushButton(_dn_widget); + _dn_nav_button->setFlat(true); + connect(_dn_nav_button, SIGNAL(clicked()), + this, SLOT(nav_table_view())); + + QHBoxLayout *dn_title_layout = new QHBoxLayout(); + _dn_title_label = new QLabel(_dn_widget); + dn_title_layout->addWidget(_dn_set_button, 0, Qt::AlignLeft); + dn_title_layout->addWidget(_dn_save_button, 0, Qt::AlignLeft); + dn_title_layout->addWidget(_dn_title_label, 1, Qt::AlignLeft); + dn_title_layout->addWidget(_dn_nav_button, 0, Qt::AlignRight); + //dn_title_layout->addStretch(1); + + _table_view = new QTableView(_dn_widget); + _table_view->setModel(_session.get_decoder_model()); + _table_view->setAlternatingRowColors(true); + _table_view->setShowGrid(false); + _table_view->horizontalHeader()->setStretchLastSection(true); + _table_view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + _table_view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + + _pre_button = new QPushButton(_dn_widget); + _nxt_button = new QPushButton(_dn_widget); + connect(_pre_button, SIGNAL(clicked()), + this, SLOT(search_pre())); + connect(_nxt_button, SIGNAL(clicked()), + this, SLOT(search_nxt())); + + _search_button = new QPushButton(this); + _search_button->setFixedWidth(_search_button->height()); + _search_button->setDisabled(true); + _search_edit = new QLineEdit(_dn_widget); + _search_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + QHBoxLayout *search_layout = new QHBoxLayout(); + search_layout->addWidget(_search_button); + search_layout->addStretch(1); + search_layout->setContentsMargins(0, 0, 0, 0); + _search_edit->setLayout(search_layout); + _search_edit->setTextMargins(_search_button->width(), 0, 0, 0); + + _dn_search_layout = new QHBoxLayout(); + _dn_search_layout->addWidget(_pre_button, 0, Qt::AlignLeft); + _dn_search_layout->addWidget(_search_edit, 1, Qt::AlignLeft); + _dn_search_layout->addWidget(_nxt_button, 0, Qt::AlignRight); + + _matchs_label = new QLabel(_dn_widget); + _matchs_title_label = new QLabel(_dn_widget); + QHBoxLayout *dn_match_layout = new QHBoxLayout(); + dn_match_layout->addWidget(_matchs_title_label, 0, Qt::AlignLeft); + dn_match_layout->addWidget(_matchs_label, 0, Qt::AlignLeft); + dn_match_layout->addStretch(1); + + _dn_layout = new QVBoxLayout(); + _dn_layout->addLayout(dn_title_layout); + _dn_layout->addLayout(_dn_search_layout); + _dn_layout->addLayout(dn_match_layout); + _dn_layout->addWidget(_table_view); + + _dn_widget->setLayout(_dn_layout); + _dn_widget->setMinimumHeight(350); + + _split_widget = new QSplitter(this); + _split_widget->insertWidget(0, _up_widget); + _split_widget->insertWidget(1, _dn_widget); + _split_widget->setOrientation(Qt::Vertical); + _split_widget->setCollapsible(0, false); + _split_widget->setCollapsible(1, false); + //_split_widget->setStretchFactor(1, 1); + //_split_widget + + this->setWidgetResizable(true); + this->setWidget(_split_widget); + //_split_widget->setGeometry(0, 0, sizeHint().width(), 500); + _split_widget->setObjectName("protocolWidget"); + + connect(&_session, SIGNAL(decode_done()), this, SLOT(update_model())); + connect(this, SIGNAL(protocol_updated()), this, SLOT(update_model())); + connect(_table_view, SIGNAL(clicked(QModelIndex)), this, SLOT(item_clicked(QModelIndex))); + connect(_table_view->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(column_resize(int, int, int))); + //connect(_table_view->verticalScrollBar(), SIGNAL(sliderMoved()), this, SLOT(sliderMoved())); + connect(_search_edit, SIGNAL(editingFinished()), this, SLOT(search_changed())); + + retranslateUi(); +} + +ProtocolDock::~ProtocolDock() +{ +} + +void ProtocolDock::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) + retranslateUi(); + else if (event->type() == QEvent::StyleChange) + reStyle(); + QScrollArea::changeEvent(event); +} + +void ProtocolDock::retranslateUi() +{ + _search_edit->setPlaceholderText(tr("search")); + _matchs_title_label->setText(tr("Matching Items:")); + _dn_title_label->setText(tr("Protocol List Viewer")); +} + +void ProtocolDock::reStyle() +{ + QString iconPath = ":/icons/" + qApp->property("Style").toString(); + + _add_button->setIcon(QIcon(iconPath+"/add.png")); + _del_all_button->setIcon(QIcon(iconPath+"/del.png")); + _dn_set_button->setIcon(QIcon(iconPath+"/gear.png")); + _dn_save_button->setIcon(QIcon(iconPath+"/save.png")); + _dn_nav_button->setIcon(QIcon(iconPath+"/nav.png")); + _pre_button->setIcon(QIcon(iconPath+"/pre.png")); + _nxt_button->setIcon(QIcon(iconPath+"/next.png")); + _search_button->setIcon(QIcon(iconPath+"/search.png")); + + for (QVector ::const_iterator i = _del_button_list.begin(); + i != _del_button_list.end(); i++) + (*i)->setIcon(QIcon(iconPath+"/del.png")); + + for (QVector ::const_iterator i = _set_button_list.begin(); + i != _set_button_list.end(); i++) + (*i)->setIcon(QIcon(iconPath+"/gear.png")); +} + +void ProtocolDock::paintEvent(QPaintEvent *) +{ +// QStyleOption opt; +// opt.init(this); +// QPainter p(this); +// style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + +void ProtocolDock::resizeEvent(QResizeEvent *event) +{ + int width = this->visibleRegion().boundingRect().width(); + width = width - _dn_layout->margin() * 2 - + _dn_search_layout->margin() * 2 - + _dn_search_layout->spacing() * 2 - + _pre_button->width()-_nxt_button->width(); + width = std::max(width, 0); + _search_edit->setMinimumWidth(width); + QScrollArea::resizeEvent(event); +} + +int ProtocolDock::decoder_name_cmp(const void *a, const void *b) +{ + return strcmp(((const srd_decoder*)a)->name, + ((const srd_decoder*)b)->name); +} + +bool ProtocolDock::sel_protocol(QString id) +{ + QString name; + GSList *l = g_slist_sort(g_slist_copy( + (GSList*)srd_decoder_list()), decoder_name_cmp); + for(; l; l = l->next) + { + const srd_decoder *const d = (srd_decoder*)l->data; + assert(d); + + const bool have_probes = (d->channels || d->opt_channels) != 0; + if (true == have_probes && + QString::fromUtf8(d->id) == id) { + name = QString::fromUtf8(d->name); + break; + } + } + g_slist_free(l); + + _protocol_combobox->setCurrentText(name); + if (_protocol_combobox->currentText() == name) + return true; + else + return false; +} + +void ProtocolDock::add_protocol() +{ + add_protocol(false); +} + +void ProtocolDock::add_protocol(bool silent) +{ + if (_session.get_device()->dev_inst()->mode != LOGIC) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Protocol Analyzer")); + msg.mBox()->setInformativeText(tr("Protocol Analyzer is only valid in Digital Mode!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + } else { + srd_decoder *const decoder = + (srd_decoder*)(_protocol_combobox->itemData(_protocol_combobox->currentIndex())).value(); + if (_session.add_decoder(decoder, silent)) { + //std::list _sel_probes = dlg.get_sel_probes(); + //QMap & _options = dlg.get_options(); + //QMap _options_index = dlg.get_options_index(); + + QString iconPath = ":/icons/" + qApp->property("Style").toString(); + QPushButton *_del_button = new QPushButton(_up_widget); + QPushButton *_set_button = new QPushButton(_up_widget); + _del_button->setFlat(true); + _del_button->setIcon(QIcon(iconPath+"/del.png")); + _set_button->setFlat(true); + _set_button->setIcon(QIcon(iconPath+"/gear.png")); + QLabel *_protocol_label = new QLabel(_up_widget); + QLabel *_progress_label = new QLabel(_up_widget); + + _del_button->setCheckable(true); + _protocol_label->setText(_protocol_combobox->currentText()); + + connect(_del_button, SIGNAL(clicked()), + this, SLOT(del_protocol())); + connect(_set_button, SIGNAL(clicked()), + this, SLOT(rst_protocol())); + + _del_button_list.push_back(_del_button); + _set_button_list.push_back(_set_button); + _protocol_label_list.push_back(_protocol_label); + _progress_label_list.push_back(_progress_label); + _protocol_index_list.push_back(_protocol_combobox->currentIndex()); + + QHBoxLayout *hori_layout = new QHBoxLayout(); + hori_layout->addWidget(_set_button); + hori_layout->addWidget(_del_button); + hori_layout->addWidget(_protocol_label); + hori_layout->addWidget(_progress_label); + hori_layout->addStretch(1); + _hori_layout_list.push_back(hori_layout); + _up_layout->insertLayout(_del_button_list.size(), hori_layout); + + // progress connection + const std::vector< boost::shared_ptr > decode_sigs( + _session.get_decode_signals()); + connect(decode_sigs.back().get(), SIGNAL(decoded_progress(int)), this, SLOT(decoded_progress(int))); + + protocol_updated(); + } + } +} + +void ProtocolDock::rst_protocol() +{ + int rst_index = 0; + for (QVector ::const_iterator i = _set_button_list.begin(); + i != _set_button_list.end(); i++) { + QPushButton *button = qobject_cast(sender()); + if ((*i) == button) { + //pv::decoder::DemoConfig dlg(this, _session.get_device(), _protocol_index_list.at(rst_index)); + //dlg.set_config(_session.get_decode_probes(rst_index), _session.get_decode_options_index(rst_index)); + //if (dlg.exec()) { + //std::list _sel_probes = dlg.get_sel_probes(); + //QMap & _options = dlg.get_options(); + //QMap _options_index = dlg.get_options_index(); + + //_session.rst_protocol_analyzer(rst_index, _sel_probes, _options, _options_index); + //} + _session.rst_decoder(rst_index); + break; + } + rst_index++; + } + protocol_updated(); +} + +void ProtocolDock::del_protocol() +{ + if (_del_all_button->isChecked()) { + _del_all_button->setChecked(false); + if (_hori_layout_list.size() > 0) { + int del_index = 0; + for (QVector ::const_iterator i = _hori_layout_list.begin(); + i != _hori_layout_list.end(); i++) { + _up_layout->removeItem((*i)); + delete (*i); + delete _del_button_list.at(del_index); + delete _set_button_list.at(del_index); + delete _protocol_label_list.at(del_index); + delete _progress_label_list.at(del_index); + + _session.remove_decode_signal(0); + del_index++; + } + _hori_layout_list.clear(); + _del_button_list.clear(); + _set_button_list.clear(); + _protocol_label_list.clear(); + _progress_label_list.clear(); + _protocol_index_list.clear(); + } else { + dialogs::DSMessageBox msg(NULL); + msg.mBox()->setText(tr("Protocol Analyzer")); + msg.mBox()->setInformativeText(tr("No Protocol Analyzer to delete!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + } + } else { + int del_index = 0; + for (QVector ::const_iterator i = _del_button_list.begin(); + i != _del_button_list.end(); i++) { + if ((*i)->isChecked()) { + _up_layout->removeItem(_hori_layout_list.at(del_index)); + + delete _hori_layout_list.at(del_index); + delete _del_button_list.at(del_index); + delete _set_button_list.at(del_index); + delete _protocol_label_list.at(del_index); + delete _progress_label_list.at(del_index); + + _hori_layout_list.remove(del_index); + _del_button_list.remove(del_index); + _set_button_list.remove(del_index); + _protocol_label_list.remove(del_index); + _progress_label_list.remove(del_index); + _protocol_index_list.remove(del_index); + + _session.remove_decode_signal(del_index); + break; + } + del_index++; + } + } + protocol_updated(); +} + +void ProtocolDock::del_all_protocol() +{ + if (_hori_layout_list.size() > 0) { + int del_index = 0; + for (QVector ::const_iterator i = _hori_layout_list.begin(); + i != _hori_layout_list.end(); i++) { + _up_layout->removeItem((*i)); + delete (*i); + delete _del_button_list.at(del_index); + delete _set_button_list.at(del_index); + delete _protocol_label_list.at(del_index); + delete _progress_label_list.at(del_index); + + _session.remove_decode_signal(0); + del_index++; + } + _hori_layout_list.clear(); + _del_button_list.clear(); + _set_button_list.clear(); + _protocol_label_list.clear(); + _progress_label_list.clear(); + _protocol_index_list.clear(); + + protocol_updated(); + } +} + +void ProtocolDock::decoded_progress(int progress) +{ + (void) progress; + + int pg = 0; + QString err=""; + const std::vector< boost::shared_ptr > decode_sigs( + _session.get_decode_signals()); + int index = 0; + BOOST_FOREACH(boost::shared_ptr d, decode_sigs) { + pg = d->get_progress(); + if (d->decoder()->out_of_memory()) + err = tr("(Out of Memory)"); + QString progress_str = QString::number(pg) + "%" + err; + if (pg == 100) + _progress_label_list.at(index)->setStyleSheet("color:green;"); + else + _progress_label_list.at(index)->setStyleSheet("color:red;"); + _progress_label_list.at(index)->setText(progress_str); + index++; + } + if (pg == 0 || pg % 10 == 1) + update_model(); +} + +void ProtocolDock::set_model() +{ + pv::dialogs::ProtocolList *protocollist_dlg = new pv::dialogs::ProtocolList(this, _session); + protocollist_dlg->exec(); + resize_table_view(_session.get_decoder_model()); + _model_proxy.setSourceModel(_session.get_decoder_model()); + search_done(); + + // clear mark_index of all DecoderStacks + const std::vector< boost::shared_ptr > decode_sigs( + _session.get_decode_signals()); + BOOST_FOREACH(boost::shared_ptr d, decode_sigs) { + d->decoder()->set_mark_index(-1); + } +} + +void ProtocolDock::update_model() +{ + pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); + const std::vector< boost::shared_ptr > decode_sigs( + _session.get_decode_signals()); + if (decode_sigs.size() == 0) + decoder_model->setDecoderStack(NULL); + else if (!decoder_model->getDecoderStack()) + decoder_model->setDecoderStack(decode_sigs.at(0)->decoder()); + else { + unsigned int index = 0; + BOOST_FOREACH(const boost::shared_ptr d, decode_sigs) { + if (d->decoder() == decoder_model->getDecoderStack()) { + decoder_model->setDecoderStack(d->decoder()); + break; + } + index++; + } + if (index >= decode_sigs.size()) + decoder_model->setDecoderStack(decode_sigs.at(0)->decoder()); + } + _model_proxy.setSourceModel(decoder_model); + search_done(); + resize_table_view(decoder_model); +} + +void ProtocolDock::resize_table_view(data::DecoderModel* decoder_model) +{ + if (decoder_model->getDecoderStack()) { + for (int i = 0; i < decoder_model->columnCount(QModelIndex()) - 1; i++) { + _table_view->resizeColumnToContents(i); + if (_table_view->columnWidth(i) > 200) + _table_view->setColumnWidth(i, 200); + } + int top_row = _table_view->rowAt(0); + int bom_row = _table_view->rowAt(_table_view->height()); + if (bom_row >= top_row && top_row >= 0) { + for (int i = top_row; i <= bom_row; i++) + _table_view->resizeRowToContents(i); + } + } +} + +void ProtocolDock::item_clicked(const QModelIndex &index) +{ + pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); + boost::shared_ptr decoder_stack = decoder_model->getDecoderStack(); + if (decoder_stack) { + pv::data::decode::Annotation ann; + if (decoder_stack->list_annotation(ann, index.column(), index.row())) { + const std::vector< boost::shared_ptr > decode_sigs( + _session.get_decode_signals()); + BOOST_FOREACH(boost::shared_ptr d, decode_sigs) { + d->decoder()->set_mark_index(-1); + } + decoder_stack->set_mark_index((ann.start_sample()+ann.end_sample())/2); + _session.show_region(ann.start_sample(), ann.end_sample(), false); + } + } + _table_view->resizeRowToContents(index.row()); + if (index.column() != _model_proxy.filterKeyColumn()) { + _model_proxy.setFilterKeyColumn(index.column()); + _model_proxy.setSourceModel(decoder_model); + search_done(); + } + QModelIndex filterIndex = _model_proxy.mapFromSource(index); + if (filterIndex.isValid()) { + _cur_search_index = filterIndex.row(); + } else { + if (_model_proxy.rowCount() == 0) { + _cur_search_index = -1; + } else { + uint64_t up = 0; + uint64_t dn = _model_proxy.rowCount() - 1; + do { + uint64_t md = (up + dn)/2; + QModelIndex curIndex = _model_proxy.mapToSource(_model_proxy.index(md,_model_proxy.filterKeyColumn())); + if (index.row() == curIndex.row()) { + _cur_search_index = md; + break; + } else if (md == up) { + if (curIndex.row() < index.row() && up < dn) { + QModelIndex nxtIndex = _model_proxy.mapToSource(_model_proxy.index(md+1,_model_proxy.filterKeyColumn())); + if (nxtIndex.row() < index.row()) + md++; + } + _cur_search_index = md + ((curIndex.row() < index.row()) ? 0.5 : -0.5); + break; + } else if (curIndex.row() < index.row()) { + up = md; + } else if (curIndex.row() > index.row()) { + dn = md; + } + }while(1); + } + } +} + +void ProtocolDock::column_resize(int index, int old_size, int new_size) +{ + (void)index; + (void)old_size; + (void)new_size; + pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); + if (decoder_model->getDecoderStack()) { + int top_row = _table_view->rowAt(0); + int bom_row = _table_view->rowAt(_table_view->height()); + if (bom_row >= top_row && top_row >= 0) { + for (int i = top_row; i <= bom_row; i++) + _table_view->resizeRowToContents(i); + } + } +} + +void ProtocolDock::export_table_view() +{ + pv::dialogs::ProtocolExp *protocolexp_dlg = new pv::dialogs::ProtocolExp(this, _session); + protocolexp_dlg->exec(); +} + +void ProtocolDock::nav_table_view() +{ + uint64_t row_index = 0; + pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); + boost::shared_ptr decoder_stack = decoder_model->getDecoderStack(); + if (decoder_stack) { + uint64_t offset = _view.offset() * (decoder_stack->samplerate() * _view.scale()); + std::map rows = decoder_stack->get_rows_lshow(); + int column = _model_proxy.filterKeyColumn(); + for (std::map::const_iterator i = rows.begin(); + i != rows.end(); i++) { + if ((*i).second && column-- == 0) { + row_index = decoder_stack->get_annotation_index((*i).first, offset); + break; + } + } + QModelIndex index = _model_proxy.mapToSource(_model_proxy.index(row_index, _model_proxy.filterKeyColumn())); + if(index.isValid()){ + _table_view->scrollTo(index); + _table_view->setCurrentIndex(index); + + pv::data::decode::Annotation ann; + decoder_stack->list_annotation(ann, index.column(), index.row()); + const std::vector< boost::shared_ptr > decode_sigs( + _session.get_decode_signals()); + BOOST_FOREACH(boost::shared_ptr d, decode_sigs) { + d->decoder()->set_mark_index(-1); + } + decoder_stack->set_mark_index((ann.start_sample()+ann.end_sample())/2); + _view.set_all_update(true); + _view.update(); + } + } +} + +void ProtocolDock::search_pre() +{ + search_update(); + // now the proxy only contains rows that match the name + // let's take the pre one and map it to the original model + if (_model_proxy.rowCount() == 0) { + _table_view->scrollToTop(); + _table_view->clearSelection(); + _matchs_label->setText(QString::number(0)); + _cur_search_index = -1; + return; + } + int i = 0; + uint64_t rowCount = _model_proxy.rowCount(); + QModelIndex matchingIndex; + pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); + boost::shared_ptr decoder_stack = decoder_model->getDecoderStack(); + do { + _cur_search_index--; + if (_cur_search_index <= -1 || _cur_search_index >= _model_proxy.rowCount()) + _cur_search_index = _model_proxy.rowCount() - 1; + + matchingIndex = _model_proxy.mapToSource(_model_proxy.index(ceil(_cur_search_index),_model_proxy.filterKeyColumn())); + if (!decoder_stack || !matchingIndex.isValid()) + break; + i = 1; + uint64_t row = matchingIndex.row() + 1; + uint64_t col = matchingIndex.column(); + pv::data::decode::Annotation ann; + bool ann_valid; + while(i < _str_list.size()) { + QString nxt = _str_list.at(i); + do { + ann_valid = decoder_stack->list_annotation(ann, col, row); + row++; + }while(ann_valid && (ann.type() < 100 || ann.type() > 999)); + QString source = ann.annotations().at(0); + if (ann_valid && source.contains(nxt)) + i++; + else + break; + } + }while(i < _str_list.size() && --rowCount); + + if(i >= _str_list.size() && matchingIndex.isValid()){ + _table_view->scrollTo(matchingIndex); + _table_view->setCurrentIndex(matchingIndex); + _table_view->clicked(matchingIndex); + } else { + _table_view->scrollToTop(); + _table_view->clearSelection(); + _matchs_label->setText(QString::number(0)); + _cur_search_index = -1; + } +} + +void ProtocolDock::search_nxt() +{ + search_update(); + // now the proxy only contains rows that match the name + // let's take the pre one and map it to the original model + if (_model_proxy.rowCount() == 0) { + _table_view->scrollToTop(); + _table_view->clearSelection(); + _matchs_label->setText(QString::number(0)); + _cur_search_index = -1; + return; + } + int i = 0; + uint64_t rowCount = _model_proxy.rowCount(); + QModelIndex matchingIndex; + pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); + boost::shared_ptr decoder_stack = decoder_model->getDecoderStack(); + do { + _cur_search_index++; + if (_cur_search_index < 0 || _cur_search_index >= _model_proxy.rowCount()) + _cur_search_index = 0; + + matchingIndex = _model_proxy.mapToSource(_model_proxy.index(floor(_cur_search_index),_model_proxy.filterKeyColumn())); + if (!decoder_stack || !matchingIndex.isValid()) + break; + i = 1; + uint64_t row = matchingIndex.row() + 1; + uint64_t col = matchingIndex.column(); + pv::data::decode::Annotation ann; + bool ann_valid; + while(i < _str_list.size()) { + QString nxt = _str_list.at(i); + do { + ann_valid = decoder_stack->list_annotation(ann, col, row); + row++; + }while(ann_valid && (ann.type() < 100 || ann.type() > 999)); + QString source = ann.annotations().at(0); + if (ann_valid && source.contains(nxt)) + i++; + else + break; + } + }while(i < _str_list.size() && --rowCount); + + if(i >= _str_list.size() && matchingIndex.isValid()){ + _table_view->scrollTo(matchingIndex); + _table_view->setCurrentIndex(matchingIndex); + _table_view->clicked(matchingIndex); + } else { + _table_view->scrollToTop(); + _table_view->clearSelection(); + _matchs_label->setText(QString::number(0)); + _cur_search_index = -1; + } +} + +void ProtocolDock::search_done() +{ + QString str = _search_edit->text().trimmed(); + QRegExp rx("(-)"); + _str_list = str.split(rx); + _model_proxy.setFilterFixedString(_str_list.first()); + if (_str_list.size() > 1) + _matchs_label->setText("..."); + else + _matchs_label->setText(QString::number(_model_proxy.rowCount())); +} + +void ProtocolDock::search_changed() +{ + _search_edited = true; + _matchs_label->setText("..."); +} + +void ProtocolDock::search_update() +{ + if (!_search_edited) + return; + + pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); + boost::shared_ptr decoder_stack = decoder_model->getDecoderStack(); + if (!decoder_stack) + return; + + if (decoder_stack->list_annotation_size(_model_proxy.filterKeyColumn()) > ProgressRows) { + QFuture future; + future = QtConcurrent::run([&]{ + search_done(); + }); + Qt::WindowFlags flags = Qt::CustomizeWindowHint; + QProgressDialog dlg(tr("Searching..."), + tr("Cancel"),0,0,this,flags); + dlg.setWindowModality(Qt::WindowModal); + dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | + Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint); + dlg.setCancelButton(NULL); + + QFutureWatcher watcher; + connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); + watcher.setFuture(future); + + dlg.exec(); + } else { + search_done(); + } + _search_edited = false; + //search_done(); +} + + +} // namespace dock +} // namespace pv diff --git a/DSView/pv/dock/protocoldock.h b/DSView/pv/dock/protocoldock.h old mode 100644 new mode 100755 index 8fb7d600..f5bc4534 --- a/DSView/pv/dock/protocoldock.h +++ b/DSView/pv/dock/protocoldock.h @@ -1,145 +1,153 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#ifndef DSVIEW_PV_PROTOCOLDOCK_H -#define DSVIEW_PV_PROTOCOLDOCK_H - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "../data/decodermodel.h" - -namespace pv { - -class SigSession; - -namespace data { -class DecoderModel; -} - -namespace view { -class View; -} - -namespace dock { - -class ProtocolDock : public QScrollArea -{ - Q_OBJECT - -public: - static const uint64_t ProgressRows = 100000; - -public: - ProtocolDock(QWidget *parent, view::View &view, SigSession &session); - ~ProtocolDock(); - - void del_all_protocol(); - bool sel_protocol(QString name); - void add_protocol(bool silent); - -protected: - void paintEvent(QPaintEvent *); - void resizeEvent(QResizeEvent *); - -signals: - void protocol_updated(); - -private slots: - void add_protocol(); - void rst_protocol(); - void del_protocol(); - void decoded_progress(int progress); - void set_model(); - void update_model(); - void export_table_view(); - void nav_table_view(); - void item_clicked(const QModelIndex &index); - void column_resize(int index, int old_size, int new_size); - void search_pre(); - void search_nxt(); - void search_done(); - void search_changed(); - void search_update(); - -private: - static int decoder_name_cmp(const void *a, const void *b); - void resize_table_view(data::DecoderModel *decoder_model); - -private: - SigSession &_session; - view::View &_view; - QSortFilterProxyModel _model_proxy; - double _cur_search_index; - QStringList _str_list; - - QSplitter *_split_widget; - QWidget *_up_widget; - QWidget *_dn_widget; - QTableView *_table_view; - QPushButton *_pre_button; - QPushButton *_nxt_button; - QLineEdit *_search_edit; - QHBoxLayout *_dn_search_layout; - QVBoxLayout *_dn_layout; - QLabel *_matchs_label; - - QPushButton *_add_button; - QPushButton *_del_all_button; - QComboBox *_protocol_combobox; - QVector _del_button_list; - QVector _set_button_list; - QVector _protocol_label_list; - QVector _progress_label_list; - QVector _protocol_index_list; - QVector _hori_layout_list; - QVBoxLayout *_up_layout; - - QPushButton *_dn_set_button; - QPushButton *_dn_save_button; - QPushButton *_dn_nav_button; - - mutable boost::mutex _search_mutex; - bool _search_edited; - bool _searching; - - bool _add_silent; -}; - -} // namespace dock -} // namespace pv - -#endif // DSVIEW_PV_PROTOCOLDOCK_H +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef DSVIEW_PV_PROTOCOLDOCK_H +#define DSVIEW_PV_PROTOCOLDOCK_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../data/decodermodel.h" + +namespace pv { + +class SigSession; + +namespace data { +class DecoderModel; +} + +namespace view { +class View; +} + +namespace dock { + +class ProtocolDock : public QScrollArea +{ + Q_OBJECT + +public: + static const uint64_t ProgressRows = 100000; + +public: + ProtocolDock(QWidget *parent, view::View &view, SigSession &session); + ~ProtocolDock(); + + void del_all_protocol(); + bool sel_protocol(QString name); + void add_protocol(bool silent); +private: + void changeEvent(QEvent *event); + void retranslateUi(); + void reStyle(); + +protected: + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + +signals: + void protocol_updated(); + +private slots: + void add_protocol(); + void rst_protocol(); + void del_protocol(); + void decoded_progress(int progress); + void set_model(); + void update_model(); + void export_table_view(); + void nav_table_view(); + void item_clicked(const QModelIndex &index); + void column_resize(int index, int old_size, int new_size); + void search_pre(); + void search_nxt(); + void search_done(); + void search_changed(); + void search_update(); + +private: + static int decoder_name_cmp(const void *a, const void *b); + void resize_table_view(data::DecoderModel *decoder_model); + +private: + SigSession &_session; + view::View &_view; + QSortFilterProxyModel _model_proxy; + double _cur_search_index; + QStringList _str_list; + + QSplitter *_split_widget; + QWidget *_up_widget; + QWidget *_dn_widget; + QTableView *_table_view; + QPushButton *_pre_button; + QPushButton *_nxt_button; + QLineEdit *_search_edit; + QHBoxLayout *_dn_search_layout; + QVBoxLayout *_dn_layout; + QLabel *_matchs_label; + QLabel *_matchs_title_label; + QLabel *_dn_title_label; + + QPushButton *_add_button; + QPushButton *_del_all_button; + QComboBox *_protocol_combobox; + QVector _del_button_list; + QVector _set_button_list; + QVector _protocol_label_list; + QVector _progress_label_list; + QVector _protocol_index_list; + QVector _hori_layout_list; + QVBoxLayout *_up_layout; + + QPushButton *_dn_set_button; + QPushButton *_dn_save_button; + QPushButton *_dn_nav_button; + + QPushButton *_search_button; + + mutable boost::mutex _search_mutex; + bool _search_edited; + bool _searching; + + bool _add_silent; +}; + +} // namespace dock +} // namespace pv + +#endif // DSVIEW_PV_PROTOCOLDOCK_H diff --git a/DSView/pv/dock/searchdock.cpp b/DSView/pv/dock/searchdock.cpp old mode 100644 new mode 100755 index d702373e..60c0f594 --- a/DSView/pv/dock/searchdock.cpp +++ b/DSView/pv/dock/searchdock.cpp @@ -1,258 +1,275 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "searchdock.h" -#include "../sigsession.h" -#include "../view/cursor.h" -#include "../view/view.h" -#include "../view/timemarker.h" -#include "../view/ruler.h" -#include "../dialogs/search.h" -#include "../data/snapshot.h" -#include "../data/logicsnapshot.h" -#include "../device/devinst.h" -#include "../dialogs/dsmessagebox.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace pv { -namespace dock { - -using namespace pv::view; -using namespace pv::widgets; - -SearchDock::SearchDock(QWidget *parent, View &view, SigSession &session) : - QWidget(parent), - _session(session), - _view(view) -{ - connect(&_pre_button, SIGNAL(clicked()), - this, SLOT(on_previous())); - connect(&_nxt_button, SIGNAL(clicked()), - this, SLOT(on_next())); - - _pre_button.setIcon(QIcon::fromTheme("searchDock", - QIcon(":/icons/pre.png"))); - _nxt_button.setIcon(QIcon::fromTheme("searchDock", - QIcon(":/icons/next.png"))); - - _search_button = new QPushButton(this); - _search_button->setIcon(QIcon::fromTheme("searchDock", - QIcon(":/icons/search.png"))); - _search_button->setFixedWidth(_search_button->height()); - _search_button->setDisabled(true); - - QLineEdit *_search_parent = new QLineEdit(this); - _search_parent->setVisible(false); - _search_value = new FakeLineEdit(_search_parent); - _search_value->setPlaceholderText(tr("search")); - - QHBoxLayout *search_layout = new QHBoxLayout(); - search_layout->addWidget(_search_button); - search_layout->addStretch(); - search_layout->setContentsMargins(0, 0, 0, 0); - _search_value->setLayout(search_layout); - _search_value->setTextMargins(_search_button->width(), 0, 0, 0); - _search_value->setReadOnly(true); - - connect(_search_value, SIGNAL(trigger()), this, SLOT(on_set())); - - QHBoxLayout *layout = new QHBoxLayout(); - layout->addStretch(1); - layout->addWidget(&_pre_button); - layout->addWidget(_search_value); - layout->addWidget(&_nxt_button); - layout->addStretch(1); - - setLayout(layout); -} - -SearchDock::~SearchDock() -{ -} - -void SearchDock::paintEvent(QPaintEvent *) -{ -// QStyleOption opt; -// opt.init(this); -// QPainter p(this); -// style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - -void SearchDock::on_previous() -{ - bool ret; - int64_t last_pos; - bool last_hit; - const boost::shared_ptr snapshot(_session.get_snapshot(SR_CHANNEL_LOGIC)); - assert(snapshot); - const boost::shared_ptr logic_snapshot = boost::dynamic_pointer_cast(snapshot); - - if (!logic_snapshot || logic_snapshot->empty()) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Search")); - msg.mBox()->setInformativeText(tr("No Sample data!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - return; - } - - const int64_t end = logic_snapshot->get_sample_count() - 1; - last_pos = _view.get_search_pos(); - last_hit = _view.get_search_hit(); - if (last_pos == 0) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Search")); - msg.mBox()->setInformativeText(tr("Search cursor at the start position!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - return; - } else { - QFuture future; - future = QtConcurrent::run([&]{ - last_pos -= last_hit; - ret = logic_snapshot->pattern_search(0, end, false, last_pos, _pattern); - }); - Qt::WindowFlags flags = Qt::CustomizeWindowHint; - QProgressDialog dlg(tr("Search Previous..."), - tr("Cancel"),0,0,this,flags); - dlg.setWindowModality(Qt::WindowModal); - dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | - Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint); - dlg.setCancelButton(NULL); - - QFutureWatcher watcher; - connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); - watcher.setFuture(future); - dlg.exec(); - - if (!ret) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Search")); - msg.mBox()->setInformativeText(tr("Pattern not found!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - return; - } else { - _view.set_search_pos(last_pos, true); - } - } -} - -void SearchDock::on_next() -{ - bool ret; - int64_t last_pos; - const boost::shared_ptr snapshot(_session.get_snapshot(SR_CHANNEL_LOGIC)); - assert(snapshot); - const boost::shared_ptr logic_snapshot = boost::dynamic_pointer_cast(snapshot); - - if (!logic_snapshot || logic_snapshot->empty()) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Search")); - msg.mBox()->setInformativeText(tr("No Sample data!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - return; - } - - const int64_t end = logic_snapshot->get_sample_count() - 1; - last_pos = _view.get_search_pos() + _view.get_search_hit(); - if (last_pos >= end) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Search")); - msg.mBox()->setInformativeText(tr("Search cursor at the end position!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - return; - } else { - QFuture future; - future = QtConcurrent::run([&]{ - ret = logic_snapshot->pattern_search(0, end, true, last_pos, _pattern); - }); - Qt::WindowFlags flags = Qt::CustomizeWindowHint; - QProgressDialog dlg(tr("Search Next..."), - tr("Cancel"),0,0,this,flags); - dlg.setWindowModality(Qt::WindowModal); - dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | - Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint); - dlg.setCancelButton(NULL); - - QFutureWatcher watcher; - connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); - watcher.setFuture(future); - dlg.exec(); - - if (!ret) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Search")); - msg.mBox()->setInformativeText(tr("Pattern not found!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - return; - } else { - _view.set_search_pos(last_pos, true); - } - } -} - -void SearchDock::on_set() -{ - dialogs::Search dlg(this, _session, _pattern); - if (dlg.exec()) { - std::map new_pattern = dlg.get_pattern(); - - QString search_label; - for (auto& iter:new_pattern) { - iter.second.remove(QChar(' '), Qt::CaseInsensitive); - iter.second = iter.second.toUpper(); - search_label.push_back(iter.second); - } - - _search_value->setText(search_label); - QFontMetrics fm = this->fontMetrics(); - _search_value->setFixedWidth(fm.width(search_label)+_search_button->width()+20); - - if (new_pattern != _pattern) { - _view.set_search_pos(_view.get_search_pos(), false); - _pattern = new_pattern; - } - } -} - -} // namespace dock -} // namespace pv +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "searchdock.h" +#include "../sigsession.h" +#include "../view/cursor.h" +#include "../view/view.h" +#include "../view/timemarker.h" +#include "../view/ruler.h" +#include "../dialogs/search.h" +#include "../data/snapshot.h" +#include "../data/logicsnapshot.h" +#include "../device/devinst.h" +#include "../dialogs/dsmessagebox.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace pv { +namespace dock { + +using namespace pv::view; +using namespace pv::widgets; + +SearchDock::SearchDock(QWidget *parent, View &view, SigSession &session) : + QWidget(parent), + _session(session), + _view(view) +{ + connect(&_pre_button, SIGNAL(clicked()), + this, SLOT(on_previous())); + connect(&_nxt_button, SIGNAL(clicked()), + this, SLOT(on_next())); + + _search_button = new QPushButton(this); + _search_button->setFixedWidth(_search_button->height()); + _search_button->setDisabled(true); + + QLineEdit *_search_parent = new QLineEdit(this); + _search_parent->setVisible(false); + _search_value = new FakeLineEdit(_search_parent); + + QHBoxLayout *search_layout = new QHBoxLayout(); + search_layout->addWidget(_search_button); + search_layout->addStretch(); + search_layout->setContentsMargins(0, 0, 0, 0); + _search_value->setLayout(search_layout); + _search_value->setTextMargins(_search_button->width(), 0, 0, 0); + _search_value->setReadOnly(true); + + connect(_search_value, SIGNAL(trigger()), this, SLOT(on_set())); + + QHBoxLayout *layout = new QHBoxLayout(); + layout->addStretch(1); + layout->addWidget(&_pre_button); + layout->addWidget(_search_value); + layout->addWidget(&_nxt_button); + layout->addStretch(1); + + setLayout(layout); + + retranslateUi(); +} + +SearchDock::~SearchDock() +{ +} + +void SearchDock::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) + retranslateUi(); + else if (event->type() == QEvent::StyleChange) + reStyle(); + QWidget::changeEvent(event); +} + +void SearchDock::retranslateUi() +{ + _search_value->setPlaceholderText(tr("search")); +} + +void SearchDock::reStyle() +{ + QString iconPath = ":/icons/" + qApp->property("Style").toString(); + + _pre_button.setIcon(QIcon(iconPath+"/pre.png")); + _nxt_button.setIcon(QIcon(iconPath+"/next.png")); + _search_button->setIcon(QIcon(iconPath+"/search.png")); +} + +void SearchDock::paintEvent(QPaintEvent *) +{ +// QStyleOption opt; +// opt.init(this); +// QPainter p(this); +// style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + +void SearchDock::on_previous() +{ + bool ret; + int64_t last_pos; + bool last_hit; + const boost::shared_ptr snapshot(_session.get_snapshot(SR_CHANNEL_LOGIC)); + assert(snapshot); + const boost::shared_ptr logic_snapshot = boost::dynamic_pointer_cast(snapshot); + + if (!logic_snapshot || logic_snapshot->empty()) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Search")); + msg.mBox()->setInformativeText(tr("No Sample data!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + return; + } + + const int64_t end = logic_snapshot->get_sample_count() - 1; + last_pos = _view.get_search_pos(); + last_hit = _view.get_search_hit(); + if (last_pos == 0) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Search")); + msg.mBox()->setInformativeText(tr("Search cursor at the start position!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + return; + } else { + QFuture future; + future = QtConcurrent::run([&]{ + last_pos -= last_hit; + ret = logic_snapshot->pattern_search(0, end, false, last_pos, _pattern); + }); + Qt::WindowFlags flags = Qt::CustomizeWindowHint; + QProgressDialog dlg(tr("Search Previous..."), + tr("Cancel"),0,0,this,flags); + dlg.setWindowModality(Qt::WindowModal); + dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | + Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint); + dlg.setCancelButton(NULL); + + QFutureWatcher watcher; + connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); + watcher.setFuture(future); + dlg.exec(); + + if (!ret) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Search")); + msg.mBox()->setInformativeText(tr("Pattern not found!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + return; + } else { + _view.set_search_pos(last_pos, true); + } + } +} + +void SearchDock::on_next() +{ + bool ret; + int64_t last_pos; + const boost::shared_ptr snapshot(_session.get_snapshot(SR_CHANNEL_LOGIC)); + assert(snapshot); + const boost::shared_ptr logic_snapshot = boost::dynamic_pointer_cast(snapshot); + + if (!logic_snapshot || logic_snapshot->empty()) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Search")); + msg.mBox()->setInformativeText(tr("No Sample data!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + return; + } + + const int64_t end = logic_snapshot->get_sample_count() - 1; + last_pos = _view.get_search_pos() + _view.get_search_hit(); + if (last_pos >= end) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Search")); + msg.mBox()->setInformativeText(tr("Search cursor at the end position!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + return; + } else { + QFuture future; + future = QtConcurrent::run([&]{ + ret = logic_snapshot->pattern_search(0, end, true, last_pos, _pattern); + }); + Qt::WindowFlags flags = Qt::CustomizeWindowHint; + QProgressDialog dlg(tr("Search Next..."), + tr("Cancel"),0,0,this,flags); + dlg.setWindowModality(Qt::WindowModal); + dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | + Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint); + dlg.setCancelButton(NULL); + + QFutureWatcher watcher; + connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); + watcher.setFuture(future); + dlg.exec(); + + if (!ret) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Search")); + msg.mBox()->setInformativeText(tr("Pattern not found!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + return; + } else { + _view.set_search_pos(last_pos, true); + } + } +} + +void SearchDock::on_set() +{ + dialogs::Search dlg(this, _session, _pattern); + if (dlg.exec()) { + std::map new_pattern = dlg.get_pattern(); + + QString search_label; + for (auto& iter:new_pattern) { + iter.second.remove(QChar(' '), Qt::CaseInsensitive); + iter.second = iter.second.toUpper(); + search_label.push_back(iter.second); + } + + _search_value->setText(search_label); + QFontMetrics fm = this->fontMetrics(); + _search_value->setFixedWidth(fm.width(search_label)+_search_button->width()+20); + + if (new_pattern != _pattern) { + _view.set_search_pos(_view.get_search_pos(), false); + _pattern = new_pattern; + } + } +} + +} // namespace dock +} // namespace pv diff --git a/DSView/pv/dock/searchdock.h b/DSView/pv/dock/searchdock.h old mode 100644 new mode 100755 index b328199a..0f68daad --- a/DSView/pv/dock/searchdock.h +++ b/DSView/pv/dock/searchdock.h @@ -1,90 +1,94 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#ifndef DSVIEW_PV_SEARCHDOCK_H -#define DSVIEW_PV_SEARCHDOCK_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include "../widgets/fakelineedit.h" - -namespace pv { - -class SigSession; - -namespace view { - class View; -} - -namespace widgets { - class FakeLineEdit; -} - -namespace dock { - -class SearchDock : public QWidget -{ - Q_OBJECT - -public: - SearchDock(QWidget *parent, pv::view::View &view, SigSession &session); - ~SearchDock(); - - void paintEvent(QPaintEvent *); - -public slots: - void on_previous(); - void on_next(); - void on_set(); - -private: - SigSession &_session; - view::View &_view; - std::map _pattern; - - QPushButton _pre_button; - QPushButton _nxt_button; - widgets::FakeLineEdit* _search_value; - QPushButton *_search_button; -}; - -} // namespace dock -} // namespace pv - -#endif // DSVIEW_PV_SEARCHDOCK_H +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef DSVIEW_PV_SEARCHDOCK_H +#define DSVIEW_PV_SEARCHDOCK_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "../widgets/fakelineedit.h" + +namespace pv { + +class SigSession; + +namespace view { + class View; +} + +namespace widgets { + class FakeLineEdit; +} + +namespace dock { + +class SearchDock : public QWidget +{ + Q_OBJECT + +public: + SearchDock(QWidget *parent, pv::view::View &view, SigSession &session); + ~SearchDock(); + + void paintEvent(QPaintEvent *); +private: + void changeEvent(QEvent *event); + void retranslateUi(); + void reStyle(); + +public slots: + void on_previous(); + void on_next(); + void on_set(); + +private: + SigSession &_session; + view::View &_view; + std::map _pattern; + + QPushButton _pre_button; + QPushButton _nxt_button; + widgets::FakeLineEdit* _search_value; + QPushButton *_search_button; +}; + +} // namespace dock +} // namespace pv + +#endif // DSVIEW_PV_SEARCHDOCK_H diff --git a/DSView/pv/dock/triggerdock.cpp b/DSView/pv/dock/triggerdock.cpp old mode 100644 new mode 100755 index 0e643669..80c08d97 --- a/DSView/pv/dock/triggerdock.cpp +++ b/DSView/pv/dock/triggerdock.cpp @@ -1,560 +1,618 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "triggerdock.h" -#include "../sigsession.h" -#include "../device/devinst.h" -#include "../dialogs/dsmessagebox.h" - -#include -#include -#include -#include -#include -#include - -#include "libsigrok4DSL/libsigrok.h" - -namespace pv { -namespace dock { - -const int TriggerDock::MinTrigPosition = 1; - -TriggerDock::TriggerDock(QWidget *parent, SigSession &session) : - QScrollArea(parent), - _session(session) -{ - int i; - - _widget = new QWidget(this); - - QFont font("Monaco"); - font.setStyleHint(QFont::Monospace); - font.setFixedPitch(true); - - simple_radioButton = new QRadioButton(tr("Simple Trigger"), _widget); - simple_radioButton->setChecked(true); - adv_radioButton = new QRadioButton(tr("Advanced Trigger"), _widget); - - position_label = new QLabel(tr("Trigger Position: "), _widget); - position_spinBox = new QSpinBox(_widget); - position_spinBox->setRange(MinTrigPosition, DS_MAX_TRIG_PERCENT); - position_spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons); - position_slider = new QSlider(Qt::Horizontal, _widget); - position_slider->setRange(MinTrigPosition, DS_MAX_TRIG_PERCENT); - connect(position_slider, SIGNAL(valueChanged(int)), position_spinBox, SLOT(setValue(int))); - connect(position_spinBox, SIGNAL(valueChanged(int)), position_slider, SLOT(setValue(int))); - - stages_label = new QLabel(tr("Total Trigger Stages: "), _widget); - stages_label->setDisabled(true); - stages_comboBox = new QComboBox(_widget); - for (i = 1; i <= TriggerStages; i++) - stages_comboBox->addItem(QString::number(i)); - //stages_comboBox->setCurrentIndex(stages_comboBox->count() - 1); - stages_comboBox->setDisabled(true); - - stage_tabWidget = new QTabWidget(_widget); - stage_tabWidget->setTabPosition(QTabWidget::East); - //stage_tabWidget->setDisabled(true); - stage_tabWidget->setUsesScrollButtons(false); - - QRegExp value_rx("[10XRFCxrfc ]+"); - QValidator *value_validator = new QRegExpValidator(value_rx, _widget); - for (i = 0; i < TriggerStages; i++) { - QComboBox *_logic_comboBox = new QComboBox(_widget); - _logic_comboBox->addItem(tr("Or")); - _logic_comboBox->addItem(tr("And")); - _logic_comboBox->setCurrentIndex(1); - _logic_comboBox_list.push_back(_logic_comboBox); - - QLineEdit *_value0_lineEdit = new QLineEdit("X X X X X X X X X X X X X X X X", _widget); - _value0_lineEdit->setFont(font); - _value0_lineEdit->setValidator(value_validator); - _value0_lineEdit->setMaxLength(TriggerProbes * 2 - 1); - _value0_lineEdit->setInputMask("X X X X X X X X X X X X X X X X"); - _value0_lineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - _value0_lineEdit_list.push_back(_value0_lineEdit); - QSpinBox *_count_spinBox = new QSpinBox(_widget); - _count_spinBox->setRange(1, INT32_MAX); - _count_spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons); - _count_spinBox_list.push_back(_count_spinBox); - QComboBox *_inv0_comboBox = new QComboBox(_widget); - _inv0_comboBox->addItem(tr("==")); - _inv0_comboBox->addItem(tr("!=")); - _inv0_comboBox_list.push_back(_inv0_comboBox); - - QLineEdit *_value1_lineEdit = new QLineEdit("X X X X X X X X X X X X X X X X", _widget); - _value1_lineEdit->setFont(font); - _value1_lineEdit->setValidator(value_validator); - _value1_lineEdit->setMaxLength(TriggerProbes * 2 - 1); - _value1_lineEdit->setInputMask("X X X X X X X X X X X X X X X X"); - _value1_lineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - _value1_lineEdit_list.push_back(_value1_lineEdit); - QComboBox *_inv1_comboBox = new QComboBox(_widget); - _inv1_comboBox->addItem(tr("==")); - _inv1_comboBox->addItem(tr("!=")); - _inv1_comboBox_list.push_back(_inv1_comboBox); - - QCheckBox *_contiguous_checkbox = new QCheckBox(_widget); - _contiguous_checkbox_list.push_back(_contiguous_checkbox); - - QLabel *value_exp_label = new QLabel("1 1 1 1 1 1\n5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 ", _widget); - QLabel *inv_exp_label = new QLabel(tr("Inv"), _widget); - QLabel *count_exp_label = new QLabel(tr("Counter"), _widget); - value_exp_label->setFont(font); - - QVBoxLayout *stage_layout = new QVBoxLayout(); - QGridLayout *stage_glayout = new QGridLayout(); - stage_glayout->setVerticalSpacing(5); - - stage_glayout->addWidget(value_exp_label, 1, 0); - stage_glayout->addWidget(inv_exp_label, 1, 1); - stage_glayout->addWidget(_value0_lineEdit, 2, 0); - stage_glayout->addWidget(_inv0_comboBox, 2, 1); - stage_glayout->addWidget(_logic_comboBox, 2, 2); - stage_glayout->addWidget(_value1_lineEdit, 3, 0); - stage_glayout->addWidget(_inv1_comboBox, 3, 1); - - stage_glayout->addWidget(new QLabel(_widget), 4, 0); - - stage_glayout->addWidget(new QLabel(tr("Contiguous")), 5, 1, 1, 2); - stage_glayout->addWidget(_contiguous_checkbox, 5, 0, 1, 1, Qt::AlignRight); - stage_glayout->addWidget(count_exp_label, 6, 1, 1, 2); - stage_glayout->addWidget(_count_spinBox, 6, 0); - - stage_layout->addLayout(stage_glayout); - stage_layout->addSpacing(20); - stage_layout->addWidget(new QLabel(tr("X: Don't care\n0: Low level\n1: High level\nR: Rising edge\nF: Falling edge\nC: Rising/Falling edge"))); - stage_layout->addStretch(1); - - QGroupBox *_stage_groupBox = new QGroupBox(tr("Stage")+QString::number(i), _widget); - _stage_groupBox->setFlat(true); - _stage_groupBox->setLayout(stage_layout); - _stage_groupBox_list.push_back(_stage_groupBox); - - stage_tabWidget->addTab((QWidget *)_stage_groupBox, QString::number(i)); - - connect(_value0_lineEdit, SIGNAL(editingFinished()), this, SLOT(value_changed())); - connect(_value1_lineEdit, SIGNAL(editingFinished()), this, SLOT(value_changed())); - } - - _serial_start_label = new QLabel(tr("Start Flag: "), _widget); - _serial_start_lineEdit = new QLineEdit("X X X X X X X X X X X X X X X X", _widget); - _serial_start_lineEdit->setFont(font); - _serial_start_lineEdit->setValidator(value_validator); - _serial_start_lineEdit->setMaxLength(TriggerProbes * 2 - 1); - _serial_start_lineEdit->setInputMask("X X X X X X X X X X X X X X X X"); - _serial_start_lineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - - _serial_stop_label = new QLabel(tr("Stop Flag: "), _widget); - _serial_stop_lineEdit = new QLineEdit("X X X X X X X X X X X X X X X X", _widget); - _serial_stop_lineEdit->setFont(font); - _serial_stop_lineEdit->setValidator(value_validator); - _serial_stop_lineEdit->setMaxLength(TriggerProbes * 2 - 1); - _serial_stop_lineEdit->setInputMask("X X X X X X X X X X X X X X X X"); - _serial_stop_lineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - - _serial_edge_label = new QLabel(tr("Clock Flag: "), _widget); - _serial_edge_lineEdit = new QLineEdit("X X X X X X X X X X X X X X X X", _widget); - _serial_edge_lineEdit->setFont(font); - _serial_edge_lineEdit->setValidator(value_validator); - _serial_edge_lineEdit->setMaxLength(TriggerProbes * 2 - 1); - _serial_edge_lineEdit->setInputMask("X X X X X X X X X X X X X X X X"); - _serial_edge_lineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - - _serial_data_lable = new QLabel(tr("Data Channel: "), _widget); - _serial_data_comboBox = new QComboBox(_widget); - for(i = 0; i < TriggerProbes; i++) - _serial_data_comboBox->addItem(QString::number(i)); - - _serial_value_lable = new QLabel(tr("Data Value: "), _widget); - _serial_value_lineEdit = new QLineEdit("X X X X X X X X X X X X X X X X", _widget); - _serial_value_lineEdit->setFont(font); - _serial_value_lineEdit->setValidator(value_validator); - _serial_value_lineEdit->setMaxLength(TriggerProbes * 2 - 1); - _serial_value_lineEdit->setInputMask("X X X X X X X X X X X X X X X X"); - _serial_value_lineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - - _serial_bits_comboBox = new QComboBox(_widget); - for(i = 1; i <= 16; i++) - _serial_bits_comboBox->addItem(QString::number(i)); - - QLabel *serial_value_exp_label = new QLabel("1 1 1 1 1 1\n5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0", _widget); - serial_value_exp_label->setFont(font); - - QVBoxLayout *serial_layout = new QVBoxLayout(); - QGridLayout *serial_glayout = new QGridLayout(); - serial_glayout->setVerticalSpacing(5); - serial_glayout->addWidget(serial_value_exp_label, 1, 1, 1, 3); - serial_glayout->addWidget(_serial_start_label, 2, 0); - serial_glayout->addWidget(_serial_start_lineEdit, 2, 1, 1, 3); - serial_glayout->addWidget(new QLabel(_widget), 2, 4); - serial_glayout->addWidget(_serial_stop_label, 3, 0); - serial_glayout->addWidget(_serial_stop_lineEdit, 3, 1, 1, 3); - serial_glayout->addWidget(_serial_edge_label, 4, 0); - serial_glayout->addWidget(_serial_edge_lineEdit, 4, 1, 1, 3); - - serial_glayout->addWidget(new QLabel(_widget), 5, 0, 1, 5); - serial_glayout->addWidget(_serial_data_lable, 6, 0); - serial_glayout->addWidget(_serial_data_comboBox, 6, 1); - serial_glayout->addWidget(new QLabel(tr("Data Bits"), _widget), 7, 0); - serial_glayout->addWidget(_serial_bits_comboBox, 7, 1); - serial_glayout->addWidget(_serial_value_lable, 8, 0); - serial_glayout->addWidget(_serial_value_lineEdit, 8, 1, 1, 3); - - serial_layout->addLayout(serial_glayout); - serial_layout->addSpacing(20); - serial_layout->addWidget(new QLabel(tr("X: Don't care\n0: Low level\n1: High level\nR: Rising edge\nF: Falling edge\nC: Rising/Falling edge"))); - serial_layout->addStretch(1); - - _serial_groupBox = new QGroupBox(tr("Serial Trigger"), _widget); - _serial_groupBox->setFlat(true); - _serial_groupBox->setLayout(serial_layout); - //_serial_groupBox->setDisabled(true); - - connect(_serial_start_lineEdit, SIGNAL(editingFinished()), this, SLOT(value_changed())); - connect(_serial_stop_lineEdit, SIGNAL(editingFinished()), this, SLOT(value_changed())); - connect(_serial_edge_lineEdit, SIGNAL(editingFinished()), this, SLOT(value_changed())); - connect(_serial_value_lineEdit, SIGNAL(editingFinished()), this, SLOT(value_changed())); - - - _adv_tabWidget = new QTabWidget(_widget); - _adv_tabWidget->setTabPosition(QTabWidget::North); - _adv_tabWidget->setDisabled(true); - _adv_tabWidget->addTab((QWidget *)stage_tabWidget, tr("Stage Trigger")); - _adv_tabWidget->addTab((QWidget *)_serial_groupBox, tr("Serial Trigger")); - - connect(simple_radioButton, SIGNAL(clicked()), this, SLOT(simple_trigger())); - connect(adv_radioButton, SIGNAL(clicked()), this, SLOT(adv_trigger())); - connect(stages_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(widget_enable(int))); - - - QVBoxLayout *layout = new QVBoxLayout(_widget); - QGridLayout *gLayout = new QGridLayout(); - gLayout->setVerticalSpacing(5); - gLayout->addWidget(simple_radioButton, 0, 0); - gLayout->addWidget(adv_radioButton, 1, 0); - gLayout->addWidget(position_label, 2, 0); - gLayout->addWidget(position_spinBox, 2, 1); - gLayout->addWidget(new QLabel(tr("%"), _widget), 2, 2); - gLayout->addWidget(position_slider, 3, 0, 1, 3); - gLayout->addWidget(stages_label, 4, 0); - gLayout->addWidget(stages_comboBox, 4, 1); - gLayout->addWidget(new QLabel(_widget), 4, 2); - gLayout->setColumnStretch(2, 1); - - layout->addLayout(gLayout); - layout->addWidget(_adv_tabWidget); - layout->addStretch(1); - _widget->setLayout(layout); - - this->setWidget(_widget); - //_widget->setGeometry(0, 0, sizeHint().width(), 1000); - _widget->setObjectName("triggerWidget"); -} - -TriggerDock::~TriggerDock() -{ -} - -void TriggerDock::paintEvent(QPaintEvent *) -{ -// QStyleOption opt; -// opt.init(this); -// QPainter p(this); -// style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - -void TriggerDock::simple_trigger() -{ - stages_label->setDisabled(true); - stages_comboBox->setDisabled(true); - _adv_tabWidget->setDisabled(true); -} - -void TriggerDock::adv_trigger() -{ - if (_session.get_device()->name() == "DSLogic") { - bool stream = false; - GVariant *gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_STREAM); - if (gvar != NULL) { - stream = g_variant_get_boolean(gvar); - g_variant_unref(gvar); - } - if (stream) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Trigger")); - msg.mBox()->setInformativeText(tr("Stream Mode Don't Support Advanced Trigger!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - simple_radioButton->setChecked(true); - } else { - widget_enable(0); - } - } else { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Trigger")); - msg.mBox()->setInformativeText(tr("Advanced Trigger need DSLogic Hardware Support!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - simple_radioButton->setChecked(true); - } -} - -void TriggerDock::widget_enable(int index) -{ - (void) index; - - int i; - int enable_stages; - stages_label->setDisabled(false); - stages_comboBox->setVisible(true); - stages_comboBox->setDisabled(false); - _adv_tabWidget->setDisabled(false); - enable_stages = stages_comboBox->currentText().toInt(); - for (i = 0; i < enable_stages; i++) { - stage_tabWidget->setTabEnabled(i, true); - } - for (i = enable_stages; i < TriggerStages; i++) { - stage_tabWidget->setTabEnabled(i, false); - } -} - -void TriggerDock::value_changed() -{ - QLineEdit* sc=dynamic_cast(sender()); - if(sc != NULL) - sc->setText(sc->text().toUpper()); -} - -void TriggerDock::device_updated() -{ - uint64_t hw_depth; - bool stream = false; - uint8_t maxRange; - uint64_t sample_limits; - GVariant *gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_HW_DEPTH); - if (gvar != NULL) { - hw_depth = g_variant_get_uint64(gvar); - g_variant_unref(gvar); - - if (_session.get_device()->dev_inst()->mode == LOGIC) { - - gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_STREAM); - if (gvar != NULL) { - stream = g_variant_get_boolean(gvar); - g_variant_unref(gvar); - } - - sample_limits = _session.get_device()->get_sample_limit(); - if (stream) - maxRange = 1; - else if (hw_depth >= sample_limits) - maxRange = DS_MAX_TRIG_PERCENT; - else - maxRange = ceil(hw_depth * DS_MAX_TRIG_PERCENT / sample_limits); - position_spinBox->setRange(MinTrigPosition, maxRange); - position_slider->setRange(MinTrigPosition, maxRange); - - if (_session.get_device()->name().contains("virtual") || - stream) { - simple_radioButton->setChecked(true); - simple_trigger(); - } - } - } -} - -bool TriggerDock::commit_trigger() -{ - // trigger position update - ds_trigger_set_pos(position_slider->value()); - - // trigger mode update - if (simple_radioButton->isChecked()) { - ds_trigger_set_mode(SIMPLE_TRIGGER); - return 0; - } else { - ds_trigger_set_en(true); - if (_adv_tabWidget->currentIndex() == 0) - ds_trigger_set_mode(ADV_TRIGGER); - else if (_adv_tabWidget->currentIndex() == 1) - ds_trigger_set_mode(SERIAL_TRIGGER); - - // trigger stage update - ds_trigger_set_stage(stages_comboBox->currentText().toInt() - 1); - - int i; - // trigger value update - if (_adv_tabWidget->currentIndex() == 0) { - for (i = 0; i < stages_comboBox->currentText().toInt(); i++) { - ds_trigger_stage_set_value(i, TriggerProbes, - _value0_lineEdit_list.at(i)->text().toLocal8Bit().data(), - _value1_lineEdit_list.at(i)->text().toLocal8Bit().data()); - } - } else if(_adv_tabWidget->currentIndex() == 1){ - ds_trigger_stage_set_value(0, TriggerProbes, - _serial_start_lineEdit->text().toLocal8Bit().data(), - _serial_stop_lineEdit->text().toLocal8Bit().data()); - ds_trigger_stage_set_value(1, TriggerProbes, - _serial_edge_lineEdit->text().toLocal8Bit().data(), - _value1_lineEdit_list.at(1)->text().toLocal8Bit().data()); - - //_serial_data_comboBox - const int data_channel = _serial_data_comboBox->currentText().toInt(); - char channel[31]; - for(i = 0; i < 31; i++){ - if (i == (30 - 2*data_channel)) - channel[i] = '0'; - else if (i%2 == 0) - channel[i] = 'X'; - else - channel[i] = ' '; - } - ds_trigger_stage_set_value(2, TriggerProbes, - channel, - _value1_lineEdit_list.at(2)->text().toLocal8Bit().data()); - ds_trigger_stage_set_value(STriggerDataStage, TriggerProbes, - _serial_value_lineEdit->text().toLocal8Bit().data(), - _value1_lineEdit_list.at(3)->text().toLocal8Bit().data()); - } - - // trigger logic update - for (i = 0; i < stages_comboBox->currentText().toInt(); i++) { - const char logic = (_contiguous_checkbox_list.at(i)->isChecked() << 1) + - _logic_comboBox_list.at(i)->currentIndex(); - ds_trigger_stage_set_logic(i, TriggerProbes, - logic); - } - - // trigger inv update - for (i = 0; i < stages_comboBox->currentText().toInt(); i++) { - ds_trigger_stage_set_inv(i, TriggerProbes, - _inv0_comboBox_list.at(i)->currentIndex(), - _inv1_comboBox_list.at(i)->currentIndex()); - } - - // trigger count update - if (_adv_tabWidget->currentIndex() == 0) { - for (i = 0; i < stages_comboBox->currentText().toInt(); i++) { - ds_trigger_stage_set_count(i, TriggerProbes, - _count_spinBox_list.at(i)->value(), - 0); - } - } else if(_adv_tabWidget->currentIndex() == 1){ - ds_trigger_stage_set_count(1, TriggerProbes, - 1, - 0); - ds_trigger_stage_set_count(3, TriggerProbes, - _serial_bits_comboBox->currentText().toInt() - 1, - 0); - } - return 1; - } -} - -void TriggerDock::init() -{ - // TRIGGERPOS - //uint16_t pos = ds_trigger_get_pos(); - //position_slider->setValue(pos); -} - -QJsonObject TriggerDock::get_session() -{ - QJsonObject trigSes; - trigSes["advTriggerMode"] = adv_radioButton->isChecked(); - trigSes["triggerPos"] = position_slider->value(); - trigSes["triggerStages"] = stages_comboBox->currentIndex(); - trigSes["triggerTab"] = _adv_tabWidget->currentIndex(); - - for (int i = 0; i < stages_comboBox->count(); i++) { - QString value0_str = "stageTriggerValue0" + QString::number(i); - QString inv0_str = "stageTriggerInv0" + QString::number(i); - QString value1_str = "stageTriggerValue1" + QString::number(i); - QString inv1_str = "stageTriggerInv1" + QString::number(i); - - QString logic_str = "stageTriggerLogic" + QString::number(i); - QString count_str = "stageTriggerCount" + QString::number(i); - QString conti_str = "stageTriggerContiguous" + QString::number(i); - - trigSes[value0_str] = _value0_lineEdit_list.at(i)->text(); - trigSes[value1_str] = _value1_lineEdit_list.at(i)->text(); - trigSes[inv0_str] = _inv0_comboBox_list.at(i)->currentIndex(); - trigSes[inv1_str] = _inv1_comboBox_list.at(i)->currentIndex(); - - trigSes[logic_str] = _logic_comboBox_list.at(i)->currentIndex(); - trigSes[count_str] = _count_spinBox_list.at(i)->value(); - trigSes[conti_str] = _contiguous_checkbox_list.at(i)->isChecked(); - } - - trigSes["serialTriggerStart"] = _serial_start_lineEdit->text(); - trigSes["serialTriggerStop"] = _serial_stop_lineEdit->text(); - trigSes["serialTriggerClock"] = _serial_edge_lineEdit->text(); - trigSes["serialTriggerChannel"] = _serial_data_comboBox->currentIndex(); - trigSes["serialTriggerData"] = _serial_value_lineEdit->text(); - trigSes["serialTriggerBits"] = _serial_bits_comboBox->currentIndex(); - - return trigSes; -} - -void TriggerDock::set_session(QJsonObject ses) -{ - position_slider->setValue(ses["triggerPos"].toDouble()); - stages_comboBox->setCurrentIndex(ses["triggerStages"].toDouble()); - _adv_tabWidget->setCurrentIndex(ses["triggerTab"].toDouble()); - if (ses["advTriggerMode"].toBool()) - adv_radioButton->click(); - else - simple_radioButton->click(); - - for (int i = 0; i < stages_comboBox->count(); i++) { - QString value0_str = "stageTriggerValue0" + QString::number(i); - QString inv0_str = "stageTriggerInv0" + QString::number(i); - QString value1_str = "stageTriggerValue1" + QString::number(i); - QString inv1_str = "stageTriggerInv1" + QString::number(i); - - QString logic_str = "stageTriggerLogic" + QString::number(i); - QString count_str = "stageTriggerCount" + QString::number(i); - QString conti_str = "stageTriggerContiguous" + QString::number(i); - - _value0_lineEdit_list.at(i)->setText(ses[value0_str].toString()); - _value1_lineEdit_list.at(i)->setText(ses[value1_str].toString()); - _inv0_comboBox_list.at(i)->setCurrentIndex(ses[inv0_str].toDouble()); - _inv1_comboBox_list.at(i)->setCurrentIndex(ses[inv1_str].toDouble()); - - _logic_comboBox_list.at(i)->setCurrentIndex(ses[logic_str].toDouble()); - _count_spinBox_list.at(i)->setValue(ses[count_str].toDouble()); - _contiguous_checkbox_list.at(i)->setChecked(ses[conti_str].toBool()); - } - - _serial_start_lineEdit->setText(ses["serialTriggerStart"].toString()); - _serial_stop_lineEdit->setText(ses["serialTriggerStop"].toString()); - _serial_edge_lineEdit->setText(ses["serialTriggerClock"].toString()); - _serial_data_comboBox->setCurrentIndex(ses["serialTriggerChannel"].toDouble()); - _serial_value_lineEdit->setText(ses["serialTriggerData"].toString()); - _serial_bits_comboBox->setCurrentIndex(ses["serialTriggerBits"].toDouble()); -} - -} // namespace dock -} // namespace pv +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "triggerdock.h" +#include "../sigsession.h" +#include "../device/devinst.h" +#include "../dialogs/dsmessagebox.h" + +#include +#include +#include +#include +#include +#include + +#include "libsigrok4DSL/libsigrok.h" + +namespace pv { +namespace dock { + +const int TriggerDock::MinTrigPosition = 1; + +TriggerDock::TriggerDock(QWidget *parent, SigSession &session) : + QScrollArea(parent), + _session(session) +{ + int i; + + _widget = new QWidget(this); + + QFont font("Monaco"); + font.setStyleHint(QFont::Monospace); + font.setFixedPitch(true); + + _simple_radioButton = new QRadioButton(_widget); + _simple_radioButton->setChecked(true); + _adv_radioButton = new QRadioButton(_widget); + + _position_label = new QLabel(_widget); + _position_spinBox = new QSpinBox(_widget); + _position_spinBox->setRange(MinTrigPosition, DS_MAX_TRIG_PERCENT); + _position_spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons); + _position_slider = new QSlider(Qt::Horizontal, _widget); + _position_slider->setRange(MinTrigPosition, DS_MAX_TRIG_PERCENT); + connect(_position_slider, SIGNAL(valueChanged(int)), _position_spinBox, SLOT(setValue(int))); + connect(_position_spinBox, SIGNAL(valueChanged(int)), _position_slider, SLOT(setValue(int))); + + _stages_label = new QLabel(_widget); + _stages_label->setDisabled(true); + stages_comboBox = new QComboBox(_widget); + for (i = 1; i <= TriggerStages; i++) + stages_comboBox->addItem(QString::number(i)); + //stages_comboBox->setCurrentIndex(stages_comboBox->count() - 1); + stages_comboBox->setDisabled(true); + + _stage_tabWidget = new QTabWidget(_widget); + _stage_tabWidget->setTabPosition(QTabWidget::East); + //_stage_tabWidget->setDisabled(true); + _stage_tabWidget->setUsesScrollButtons(false); + + QRegExp value_rx("[10XRFCxrfc ]+"); + QValidator *value_validator = new QRegExpValidator(value_rx, _widget); + for (i = 0; i < TriggerStages; i++) { + QComboBox *_logic_comboBox = new QComboBox(_widget); + _logic_comboBox->addItem(tr("Or")); + _logic_comboBox->addItem(tr("And")); + _logic_comboBox->setCurrentIndex(1); + _logic_comboBox_list.push_back(_logic_comboBox); + + QLineEdit *_value0_lineEdit = new QLineEdit("X X X X X X X X X X X X X X X X", _widget); + _value0_lineEdit->setFont(font); + _value0_lineEdit->setValidator(value_validator); + _value0_lineEdit->setMaxLength(TriggerProbes * 2 - 1); + _value0_lineEdit->setInputMask("X X X X X X X X X X X X X X X X"); + _value0_lineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + _value0_lineEdit_list.push_back(_value0_lineEdit); + QSpinBox *_count_spinBox = new QSpinBox(_widget); + _count_spinBox->setRange(1, INT32_MAX); + _count_spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons); + _count_spinBox_list.push_back(_count_spinBox); + QComboBox *_inv0_comboBox = new QComboBox(_widget); + _inv0_comboBox->addItem(tr("==")); + _inv0_comboBox->addItem(tr("!=")); + _inv0_comboBox_list.push_back(_inv0_comboBox); + + QLineEdit *_value1_lineEdit = new QLineEdit("X X X X X X X X X X X X X X X X", _widget); + _value1_lineEdit->setFont(font); + _value1_lineEdit->setValidator(value_validator); + _value1_lineEdit->setMaxLength(TriggerProbes * 2 - 1); + _value1_lineEdit->setInputMask("X X X X X X X X X X X X X X X X"); + _value1_lineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + _value1_lineEdit_list.push_back(_value1_lineEdit); + QComboBox *_inv1_comboBox = new QComboBox(_widget); + _inv1_comboBox->addItem(tr("==")); + _inv1_comboBox->addItem(tr("!=")); + _inv1_comboBox_list.push_back(_inv1_comboBox); + + QCheckBox *_contiguous_checkbox = new QCheckBox(_widget); + _contiguous_checkbox_list.push_back(_contiguous_checkbox); + + QLabel *value_exp_label = new QLabel("1 1 1 1 1 1\n5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 ", _widget); + QLabel *inv_exp_label = new QLabel(_widget); + _inv_exp_label_list.push_back(inv_exp_label); + QLabel *count_exp_label = new QLabel(_widget); + _count_exp_label_list.push_back(count_exp_label); + value_exp_label->setFont(font); + + QVBoxLayout *stage_layout = new QVBoxLayout(); + QGridLayout *stage_glayout = new QGridLayout(); + stage_glayout->setVerticalSpacing(5); + + stage_glayout->addWidget(value_exp_label, 1, 0); + stage_glayout->addWidget(inv_exp_label, 1, 1); + stage_glayout->addWidget(_value0_lineEdit, 2, 0); + stage_glayout->addWidget(_inv0_comboBox, 2, 1); + stage_glayout->addWidget(_logic_comboBox, 2, 2); + stage_glayout->addWidget(_value1_lineEdit, 3, 0); + stage_glayout->addWidget(_inv1_comboBox, 3, 1); + + stage_glayout->addWidget(new QLabel(_widget), 4, 0); + + QLabel *contiguous_label = new QLabel(_widget); + _contiguous_label_list.push_back(contiguous_label); + stage_glayout->addWidget(contiguous_label, 5, 1, 1, 2); + stage_glayout->addWidget(_contiguous_checkbox, 5, 0, 1, 1, Qt::AlignRight); + stage_glayout->addWidget(count_exp_label, 6, 1, 1, 2); + stage_glayout->addWidget(_count_spinBox, 6, 0); + + stage_layout->addLayout(stage_glayout); + stage_layout->addSpacing(20); + QLabel *stage_note_label = new QLabel(_widget); + _stage_note_label_list.push_back(stage_note_label); + stage_layout->addWidget(stage_note_label); + stage_layout->addStretch(1); + + QGroupBox *stage_groupBox = new QGroupBox(_widget); + stage_groupBox->setFlat(true); + stage_groupBox->setLayout(stage_layout); + _stage_groupBox_list.push_back(stage_groupBox); + + _stage_tabWidget->addTab((QWidget *)stage_groupBox, QString::number(i)); + + connect(_value0_lineEdit, SIGNAL(editingFinished()), this, SLOT(value_changed())); + connect(_value1_lineEdit, SIGNAL(editingFinished()), this, SLOT(value_changed())); + } + + _serial_start_label = new QLabel(_widget); + _serial_start_lineEdit = new QLineEdit("X X X X X X X X X X X X X X X X", _widget); + _serial_start_lineEdit->setFont(font); + _serial_start_lineEdit->setValidator(value_validator); + _serial_start_lineEdit->setMaxLength(TriggerProbes * 2 - 1); + _serial_start_lineEdit->setInputMask("X X X X X X X X X X X X X X X X"); + _serial_start_lineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + + _serial_stop_label = new QLabel(_widget); + _serial_stop_lineEdit = new QLineEdit("X X X X X X X X X X X X X X X X", _widget); + _serial_stop_lineEdit->setFont(font); + _serial_stop_lineEdit->setValidator(value_validator); + _serial_stop_lineEdit->setMaxLength(TriggerProbes * 2 - 1); + _serial_stop_lineEdit->setInputMask("X X X X X X X X X X X X X X X X"); + _serial_stop_lineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + + _serial_edge_label = new QLabel(_widget); + _serial_edge_lineEdit = new QLineEdit("X X X X X X X X X X X X X X X X", _widget); + _serial_edge_lineEdit->setFont(font); + _serial_edge_lineEdit->setValidator(value_validator); + _serial_edge_lineEdit->setMaxLength(TriggerProbes * 2 - 1); + _serial_edge_lineEdit->setInputMask("X X X X X X X X X X X X X X X X"); + _serial_edge_lineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + + _serial_data_label = new QLabel(_widget); + _serial_data_comboBox = new QComboBox(_widget); + for(i = 0; i < TriggerProbes; i++) + _serial_data_comboBox->addItem(QString::number(i)); + + _serial_value_label = new QLabel(_widget); + _serial_value_lineEdit = new QLineEdit("X X X X X X X X X X X X X X X X", _widget); + _serial_value_lineEdit->setFont(font); + _serial_value_lineEdit->setValidator(value_validator); + _serial_value_lineEdit->setMaxLength(TriggerProbes * 2 - 1); + _serial_value_lineEdit->setInputMask("X X X X X X X X X X X X X X X X"); + _serial_value_lineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + + _serial_bits_comboBox = new QComboBox(_widget); + for(i = 1; i <= 16; i++) + _serial_bits_comboBox->addItem(QString::number(i)); + + QLabel *serial_value_exp_label = new QLabel("1 1 1 1 1 1\n5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0", _widget); + serial_value_exp_label->setFont(font); + + QVBoxLayout *serial_layout = new QVBoxLayout(); + QGridLayout *serial_glayout = new QGridLayout(); + serial_glayout->setVerticalSpacing(5); + serial_glayout->addWidget(serial_value_exp_label, 1, 1, 1, 3); + serial_glayout->addWidget(_serial_start_label, 2, 0); + serial_glayout->addWidget(_serial_start_lineEdit, 2, 1, 1, 3); + serial_glayout->addWidget(new QLabel(_widget), 2, 4); + serial_glayout->addWidget(_serial_stop_label, 3, 0); + serial_glayout->addWidget(_serial_stop_lineEdit, 3, 1, 1, 3); + serial_glayout->addWidget(_serial_edge_label, 4, 0); + serial_glayout->addWidget(_serial_edge_lineEdit, 4, 1, 1, 3); + + serial_glayout->addWidget(new QLabel(_widget), 5, 0, 1, 5); + serial_glayout->addWidget(_serial_data_label, 6, 0); + serial_glayout->addWidget(_serial_data_comboBox, 6, 1); + _data_bits_label = new QLabel(_widget); + serial_glayout->addWidget(_data_bits_label, 7, 0); + serial_glayout->addWidget(_serial_bits_comboBox, 7, 1); + serial_glayout->addWidget(_serial_value_label, 8, 0); + serial_glayout->addWidget(_serial_value_lineEdit, 8, 1, 1, 3); + + _serial_note_label = new QLabel(_widget); + serial_layout->addLayout(serial_glayout); + serial_layout->addSpacing(20); + serial_layout->addWidget(_serial_note_label); + serial_layout->addStretch(1); + + _serial_groupBox = new QGroupBox(_widget); + _serial_groupBox->setFlat(true); + _serial_groupBox->setLayout(serial_layout); + //_serial_groupBox->setDisabled(true); + + connect(_serial_start_lineEdit, SIGNAL(editingFinished()), this, SLOT(value_changed())); + connect(_serial_stop_lineEdit, SIGNAL(editingFinished()), this, SLOT(value_changed())); + connect(_serial_edge_lineEdit, SIGNAL(editingFinished()), this, SLOT(value_changed())); + connect(_serial_value_lineEdit, SIGNAL(editingFinished()), this, SLOT(value_changed())); + + + _adv_tabWidget = new QTabWidget(_widget); + _adv_tabWidget->setTabPosition(QTabWidget::North); + _adv_tabWidget->setDisabled(true); + _adv_tabWidget->addTab((QWidget *)_stage_tabWidget, tr("Stage Trigger")); + _adv_tabWidget->addTab((QWidget *)_serial_groupBox, tr("Serial Trigger")); + + connect(_simple_radioButton, SIGNAL(clicked()), this, SLOT(simple_trigger())); + connect(_adv_radioButton, SIGNAL(clicked()), this, SLOT(adv_trigger())); + connect(stages_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(widget_enable(int))); + + + QVBoxLayout *layout = new QVBoxLayout(_widget); + QGridLayout *gLayout = new QGridLayout(); + gLayout->setVerticalSpacing(5); + gLayout->addWidget(_simple_radioButton, 0, 0); + gLayout->addWidget(_adv_radioButton, 1, 0); + gLayout->addWidget(_position_label, 2, 0); + gLayout->addWidget(_position_spinBox, 2, 1); + gLayout->addWidget(new QLabel(tr("%"), _widget), 2, 2); + gLayout->addWidget(_position_slider, 3, 0, 1, 3); + gLayout->addWidget(_stages_label, 4, 0); + gLayout->addWidget(stages_comboBox, 4, 1); + gLayout->addWidget(new QLabel(_widget), 4, 2); + gLayout->setColumnStretch(2, 1); + + layout->addLayout(gLayout); + layout->addWidget(_adv_tabWidget); + layout->addStretch(1); + _widget->setLayout(layout); + + this->setWidget(_widget); + //_widget->setGeometry(0, 0, sizeHint().width(), 1000); + _widget->setObjectName("triggerWidget"); + + retranslateUi(); +} + +TriggerDock::~TriggerDock() +{ +} + +void TriggerDock::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) + retranslateUi(); + else if (event->type() == QEvent::StyleChange) + reStyle(); + QScrollArea::changeEvent(event); +} + +void TriggerDock::retranslateUi() +{ + _simple_radioButton->setText(tr("Simple Trigger")); + _adv_radioButton->setText(tr("Advanced Trigger")); + _position_label->setText(tr("Trigger Position: ")); + _stages_label->setText(tr("Total Trigger Stages: ")); + _serial_start_label->setText(tr("Start Flag: ")); + _serial_stop_label->setText(tr("Stop Flag: ")); + _serial_edge_label->setText(tr("Clock Flag: ")); + _serial_data_label->setText(tr("Data Channel: ")); + _serial_value_label->setText(tr("Data Value: ")); + _serial_groupBox->setTitle(tr("Serial Trigger")); + + _adv_tabWidget->setTabText(0, tr("Stage Trigger")); + _adv_tabWidget->setTabText(1, tr("Serial Trigger")); + _serial_note_label->setText(tr("X: Don't care\n0: Low level\n1: High level\nR: Rising edge\nF: Falling edge\nC: Rising/Falling edge")); + _data_bits_label->setText(tr("Data Bits")); + + for (int i = 0; i < _inv_exp_label_list.length(); i++) + _inv_exp_label_list.at(i)->setText(tr("Inv")); + + for (int i = 0; i < _count_exp_label_list.length(); i++) + _count_exp_label_list.at(i)->setText(tr("Counter")); + + for (int i = 0; i < _contiguous_label_list.length(); i++) + _contiguous_label_list.at(i)->setText(tr("Contiguous")); + + for (int i = 0; i < _stage_groupBox_list.length(); i++) + _stage_groupBox_list.at(i)->setTitle(tr("Stage")+QString::number(i)); + + for (int i = 0; i < _stage_note_label_list.length(); i++) + _stage_note_label_list.at(i)->setText(tr("X: Don't care\n0: Low level\n1: High level\nR: Rising edge\nF: Falling edge\nC: Rising/Falling edge")); +} + +void TriggerDock::reStyle() +{ + //QString iconPath = ":/icons/" + qApp->property("Style").toString(); +} + +void TriggerDock::paintEvent(QPaintEvent *) +{ +// QStyleOption opt; +// opt.init(this); +// QPainter p(this); +// style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + +void TriggerDock::simple_trigger() +{ + _stages_label->setDisabled(true); + stages_comboBox->setDisabled(true); + _adv_tabWidget->setDisabled(true); +} + +void TriggerDock::adv_trigger() +{ + if (_session.get_device()->name() == "DSLogic") { + bool stream = false; + GVariant *gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_STREAM); + if (gvar != NULL) { + stream = g_variant_get_boolean(gvar); + g_variant_unref(gvar); + } + if (stream) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Trigger")); + msg.mBox()->setInformativeText(tr("Stream Mode Don't Support Advanced Trigger!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + _simple_radioButton->setChecked(true); + } else { + widget_enable(0); + } + } else { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Trigger")); + msg.mBox()->setInformativeText(tr("Advanced Trigger need DSLogic Hardware Support!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + _simple_radioButton->setChecked(true); + } +} + +void TriggerDock::widget_enable(int index) +{ + (void) index; + + int i; + int enable_stages; + _stages_label->setDisabled(false); + stages_comboBox->setVisible(true); + stages_comboBox->setDisabled(false); + _adv_tabWidget->setDisabled(false); + enable_stages = stages_comboBox->currentText().toInt(); + for (i = 0; i < enable_stages; i++) { + _stage_tabWidget->setTabEnabled(i, true); + } + for (i = enable_stages; i < TriggerStages; i++) { + _stage_tabWidget->setTabEnabled(i, false); + } +} + +void TriggerDock::value_changed() +{ + QLineEdit* sc=dynamic_cast(sender()); + if(sc != NULL) + sc->setText(sc->text().toUpper()); +} + +void TriggerDock::device_updated() +{ + uint64_t hw_depth; + bool stream = false; + uint8_t maxRange; + uint64_t sample_limits; + GVariant *gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_HW_DEPTH); + if (gvar != NULL) { + hw_depth = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + + if (_session.get_device()->dev_inst()->mode == LOGIC) { + + gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_STREAM); + if (gvar != NULL) { + stream = g_variant_get_boolean(gvar); + g_variant_unref(gvar); + } + + sample_limits = _session.get_device()->get_sample_limit(); + if (stream) + maxRange = 1; + else if (hw_depth >= sample_limits) + maxRange = DS_MAX_TRIG_PERCENT; + else + maxRange = ceil(hw_depth * DS_MAX_TRIG_PERCENT / sample_limits); + _position_spinBox->setRange(MinTrigPosition, maxRange); + _position_slider->setRange(MinTrigPosition, maxRange); + + if (_session.get_device()->name().contains("virtual") || + stream) { + _simple_radioButton->setChecked(true); + simple_trigger(); + } + } + } +} + +bool TriggerDock::commit_trigger() +{ + // trigger position update + ds_trigger_set_pos(_position_slider->value()); + + // trigger mode update + if (_simple_radioButton->isChecked()) { + ds_trigger_set_mode(SIMPLE_TRIGGER); + return 0; + } else { + ds_trigger_set_en(true); + if (_adv_tabWidget->currentIndex() == 0) + ds_trigger_set_mode(ADV_TRIGGER); + else if (_adv_tabWidget->currentIndex() == 1) + ds_trigger_set_mode(SERIAL_TRIGGER); + + // trigger stage update + ds_trigger_set_stage(stages_comboBox->currentText().toInt() - 1); + + int i; + // trigger value update + if (_adv_tabWidget->currentIndex() == 0) { + for (i = 0; i < stages_comboBox->currentText().toInt(); i++) { + ds_trigger_stage_set_value(i, TriggerProbes, + _value0_lineEdit_list.at(i)->text().toLocal8Bit().data(), + _value1_lineEdit_list.at(i)->text().toLocal8Bit().data()); + } + } else if(_adv_tabWidget->currentIndex() == 1){ + ds_trigger_stage_set_value(0, TriggerProbes, + _serial_start_lineEdit->text().toLocal8Bit().data(), + _serial_stop_lineEdit->text().toLocal8Bit().data()); + ds_trigger_stage_set_value(1, TriggerProbes, + _serial_edge_lineEdit->text().toLocal8Bit().data(), + _value1_lineEdit_list.at(1)->text().toLocal8Bit().data()); + + //_serial_data_comboBox + const int data_channel = _serial_data_comboBox->currentText().toInt(); + char channel[31]; + for(i = 0; i < 31; i++){ + if (i == (30 - 2*data_channel)) + channel[i] = '0'; + else if (i%2 == 0) + channel[i] = 'X'; + else + channel[i] = ' '; + } + ds_trigger_stage_set_value(2, TriggerProbes, + channel, + _value1_lineEdit_list.at(2)->text().toLocal8Bit().data()); + ds_trigger_stage_set_value(STriggerDataStage, TriggerProbes, + _serial_value_lineEdit->text().toLocal8Bit().data(), + _value1_lineEdit_list.at(3)->text().toLocal8Bit().data()); + } + + // trigger logic update + for (i = 0; i < stages_comboBox->currentText().toInt(); i++) { + const char logic = (_contiguous_checkbox_list.at(i)->isChecked() << 1) + + _logic_comboBox_list.at(i)->currentIndex(); + ds_trigger_stage_set_logic(i, TriggerProbes, + logic); + } + + // trigger inv update + for (i = 0; i < stages_comboBox->currentText().toInt(); i++) { + ds_trigger_stage_set_inv(i, TriggerProbes, + _inv0_comboBox_list.at(i)->currentIndex(), + _inv1_comboBox_list.at(i)->currentIndex()); + } + + // trigger count update + if (_adv_tabWidget->currentIndex() == 0) { + for (i = 0; i < stages_comboBox->currentText().toInt(); i++) { + ds_trigger_stage_set_count(i, TriggerProbes, + _count_spinBox_list.at(i)->value(), + 0); + } + } else if(_adv_tabWidget->currentIndex() == 1){ + ds_trigger_stage_set_count(1, TriggerProbes, + 1, + 0); + ds_trigger_stage_set_count(3, TriggerProbes, + _serial_bits_comboBox->currentText().toInt() - 1, + 0); + } + return 1; + } +} + +void TriggerDock::init() +{ + // TRIGGERPOS + //uint16_t pos = ds_trigger_get_pos(); + //_position_slider->setValue(pos); +} + +QJsonObject TriggerDock::get_session() +{ + QJsonObject trigSes; + trigSes["advTriggerMode"] = _adv_radioButton->isChecked(); + trigSes["triggerPos"] = _position_slider->value(); + trigSes["triggerStages"] = stages_comboBox->currentIndex(); + trigSes["triggerTab"] = _adv_tabWidget->currentIndex(); + + for (int i = 0; i < stages_comboBox->count(); i++) { + QString value0_str = "stageTriggerValue0" + QString::number(i); + QString inv0_str = "stageTriggerInv0" + QString::number(i); + QString value1_str = "stageTriggerValue1" + QString::number(i); + QString inv1_str = "stageTriggerInv1" + QString::number(i); + + QString logic_str = "stageTriggerLogic" + QString::number(i); + QString count_str = "stageTriggerCount" + QString::number(i); + QString conti_str = "stageTriggerContiguous" + QString::number(i); + + trigSes[value0_str] = _value0_lineEdit_list.at(i)->text(); + trigSes[value1_str] = _value1_lineEdit_list.at(i)->text(); + trigSes[inv0_str] = _inv0_comboBox_list.at(i)->currentIndex(); + trigSes[inv1_str] = _inv1_comboBox_list.at(i)->currentIndex(); + + trigSes[logic_str] = _logic_comboBox_list.at(i)->currentIndex(); + trigSes[count_str] = _count_spinBox_list.at(i)->value(); + trigSes[conti_str] = _contiguous_checkbox_list.at(i)->isChecked(); + } + + trigSes["serialTriggerStart"] = _serial_start_lineEdit->text(); + trigSes["serialTriggerStop"] = _serial_stop_lineEdit->text(); + trigSes["serialTriggerClock"] = _serial_edge_lineEdit->text(); + trigSes["serialTriggerChannel"] = _serial_data_comboBox->currentIndex(); + trigSes["serialTriggerData"] = _serial_value_lineEdit->text(); + trigSes["serialTriggerBits"] = _serial_bits_comboBox->currentIndex(); + + return trigSes; +} + +void TriggerDock::set_session(QJsonObject ses) +{ + _position_slider->setValue(ses["triggerPos"].toDouble()); + stages_comboBox->setCurrentIndex(ses["triggerStages"].toDouble()); + _adv_tabWidget->setCurrentIndex(ses["triggerTab"].toDouble()); + if (ses["advTriggerMode"].toBool()) + _adv_radioButton->click(); + else + _simple_radioButton->click(); + + for (int i = 0; i < stages_comboBox->count(); i++) { + QString value0_str = "stageTriggerValue0" + QString::number(i); + QString inv0_str = "stageTriggerInv0" + QString::number(i); + QString value1_str = "stageTriggerValue1" + QString::number(i); + QString inv1_str = "stageTriggerInv1" + QString::number(i); + + QString logic_str = "stageTriggerLogic" + QString::number(i); + QString count_str = "stageTriggerCount" + QString::number(i); + QString conti_str = "stageTriggerContiguous" + QString::number(i); + + _value0_lineEdit_list.at(i)->setText(ses[value0_str].toString()); + _value1_lineEdit_list.at(i)->setText(ses[value1_str].toString()); + _inv0_comboBox_list.at(i)->setCurrentIndex(ses[inv0_str].toDouble()); + _inv1_comboBox_list.at(i)->setCurrentIndex(ses[inv1_str].toDouble()); + + _logic_comboBox_list.at(i)->setCurrentIndex(ses[logic_str].toDouble()); + _count_spinBox_list.at(i)->setValue(ses[count_str].toDouble()); + _contiguous_checkbox_list.at(i)->setChecked(ses[conti_str].toBool()); + } + + _serial_start_lineEdit->setText(ses["serialTriggerStart"].toString()); + _serial_stop_lineEdit->setText(ses["serialTriggerStop"].toString()); + _serial_edge_lineEdit->setText(ses["serialTriggerClock"].toString()); + _serial_data_comboBox->setCurrentIndex(ses["serialTriggerChannel"].toDouble()); + _serial_value_lineEdit->setText(ses["serialTriggerData"].toString()); + _serial_bits_comboBox->setCurrentIndex(ses["serialTriggerBits"].toDouble()); +} + +} // namespace dock +} // namespace pv diff --git a/DSView/pv/dock/triggerdock.h b/DSView/pv/dock/triggerdock.h old mode 100644 new mode 100755 index e63390c7..d4338976 --- a/DSView/pv/dock/triggerdock.h +++ b/DSView/pv/dock/triggerdock.h @@ -1,136 +1,148 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#ifndef DSVIEW_PV_TRIGGERDOCK_H -#define DSVIEW_PV_TRIGGERDOCK_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -namespace pv { - -class SigSession; - -namespace dock { - -class TriggerDock : public QScrollArea -{ - Q_OBJECT - -private: - static const int MinTrigPosition; - -public: - TriggerDock(QWidget *parent, SigSession &session); - ~TriggerDock(); - - void paintEvent(QPaintEvent *); - - void init(); - - QJsonObject get_session(); - void set_session(QJsonObject ses); - - /* - * commit trigger setting - * return 0: simple trigger - * 1: advanced trigger - */ - bool commit_trigger(); - -signals: - -public slots: - void simple_trigger(); - void adv_trigger(); - void widget_enable(int index); - - void value_changed(); - - void device_updated(); - -private: - -private: - SigSession &_session; - - QWidget *_widget; - - QRadioButton *simple_radioButton; - QRadioButton *adv_radioButton; - - QLabel *position_label; - QSpinBox *position_spinBox; - QSlider *position_slider; - - QLabel *stages_label; - QComboBox *stages_comboBox; - - QTabWidget *stage_tabWidget; - - QVector _stage_groupBox_list; - QVector _mu_label_list; - QVector _logic_comboBox_list; - QVector _value0_lineEdit_list; - QVector _count_spinBox_list; - QVector _inv0_comboBox_list; - QVector _value1_lineEdit_list; - QVector _inv1_comboBox_list; - QVector _contiguous_checkbox_list; - - QTabWidget *_adv_tabWidget; - QGroupBox *_serial_groupBox; - QLabel *_serial_start_label; - QLineEdit *_serial_start_lineEdit; - QLabel *_serial_stop_label; - QLineEdit *_serial_stop_lineEdit; - QLabel *_serial_edge_label; - QLineEdit *_serial_edge_lineEdit; - QLabel *_serial_data_lable; - QComboBox *_serial_data_comboBox; - QLabel *_serial_value_lable; - QLineEdit *_serial_value_lineEdit; - QLabel *_serial_vcnt_lable; - QComboBox *_serial_bits_comboBox; -}; - -} // namespace dock -} // namespace pv - -#endif // DSVIEW_PV_TRIGGERDOCK_H +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef DSVIEW_PV_TRIGGERDOCK_H +#define DSVIEW_PV_TRIGGERDOCK_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace pv { + +class SigSession; + +namespace dock { + +class TriggerDock : public QScrollArea +{ + Q_OBJECT + +private: + static const int MinTrigPosition; + +public: + TriggerDock(QWidget *parent, SigSession &session); + ~TriggerDock(); + + void paintEvent(QPaintEvent *); + + void init(); + + QJsonObject get_session(); + void set_session(QJsonObject ses); + + /* + * commit trigger setting + * return 0: simple trigger + * 1: advanced trigger + */ + bool commit_trigger(); +private: + void changeEvent(QEvent *event); + void retranslateUi(); + void reStyle(); + +signals: + +public slots: + void simple_trigger(); + void adv_trigger(); + void widget_enable(int index); + + void value_changed(); + + void device_updated(); + +private: + +private: + SigSession &_session; + + QWidget *_widget; + + QRadioButton *_simple_radioButton; + QRadioButton *_adv_radioButton; + + QLabel *_position_label; + QSpinBox *_position_spinBox; + QSlider *_position_slider; + + QLabel *_stages_label; + QComboBox *stages_comboBox; + + QTabWidget *_stage_tabWidget; + + QVector _stage_groupBox_list; + QVector _mu_label_list; + QVector _logic_comboBox_list; + QVector _value0_lineEdit_list; + QVector _count_spinBox_list; + QVector _inv0_comboBox_list; + QVector _value1_lineEdit_list; + QVector _inv1_comboBox_list; + QVector _contiguous_checkbox_list; + + QTabWidget *_adv_tabWidget; + QGroupBox *_serial_groupBox; + QLabel *_serial_start_label; + QLineEdit *_serial_start_lineEdit; + QLabel *_serial_stop_label; + QLineEdit *_serial_stop_lineEdit; + QLabel *_serial_edge_label; + QLineEdit *_serial_edge_lineEdit; + QLabel *_serial_data_label; + QComboBox *_serial_data_comboBox; + QLabel *_serial_value_label; + QLineEdit *_serial_value_lineEdit; + QComboBox *_serial_bits_comboBox; + + QLabel *_serial_note_label; + QLabel *_data_bits_label; + + QVector _inv_exp_label_list; + QVector _count_exp_label_list; + QVector _contiguous_label_list; + QVector _stage_note_label_list; + +}; + +} // namespace dock +} // namespace pv + +#endif // DSVIEW_PV_TRIGGERDOCK_H diff --git a/DSView/pv/mainframe.cpp b/DSView/pv/mainframe.cpp old mode 100644 new mode 100755 index eea4701d..804b751d --- a/DSView/pv/mainframe.cpp +++ b/DSView/pv/mainframe.cpp @@ -69,13 +69,14 @@ MainFrame::MainFrame(DeviceManager &device_manager, _freezing = false; _minimized = false; + // Title + _titleBar = new toolbars::TitleBar(true, this); + _titleBar->installEventFilter(this); + // MainWindow _mainWindow = new MainWindow(device_manager, open_file_name, this); _mainWindow->setWindowFlags(Qt::Widget); - // Title - _titleBar = new toolbars::TitleBar(true, this); - _titleBar->installEventFilter(this); _titleBar->setTitle(_mainWindow->windowTitle()); QVBoxLayout *vbox = new QVBoxLayout(); @@ -349,9 +350,11 @@ bool MainFrame::eventFilter(QObject *object, QEvent *event) void MainFrame::writeSettings() { - QSettings settings; + QSettings settings(QApplication::organizationName(), QApplication::applicationName()); settings.beginGroup("MainFrame"); + settings.setValue("style", qApp->property("Style").toString()); + settings.setValue("language", qApp->property("Language").toInt()); settings.setValue("isMax", isMaximized()); settings.setValue("size", size()); settings.setValue("pos", pos() + @@ -361,7 +364,7 @@ void MainFrame::writeSettings() void MainFrame::readSettings() { - QSettings settings; + QSettings settings(QApplication::organizationName(), QApplication::applicationName()); QDesktopWidget* desktopWidget = QApplication::desktop(); QRect deskRect = desktopWidget->availableGeometry(); QPoint default_upleft = QPoint((deskRect.width() - defWidth)/2, (deskRect.height() - defHeight)/2); @@ -371,6 +374,13 @@ void MainFrame::readSettings() bool isMax = settings.value("isMax", false).toBool(); QSize size = settings.value("size", default_size).toSize(); QPoint pos = settings.value("pos", default_upleft).toPoint(); + // defaut language + if (settings.contains("language")) { + _mainWindow->switchLanguage(settings.value("language").toInt()); + } else { + QLocale locale; + _mainWindow->switchLanguage(locale.language()); + } settings.endGroup(); // check the restored position is vavlid or not @@ -391,6 +401,9 @@ void MainFrame::readSettings() resize(size); move(pos); } + + // restore dockwidgets + _mainWindow->restore_dock(); } void MainFrame::setTaskbarProgress(int progress) @@ -401,14 +414,13 @@ void MainFrame::setTaskbarProgress(int progress) void MainFrame::show_doc() { const QString DOC_KEY("ShowDocuments"); - QSettings settings; - + QSettings settings(QApplication::organizationName(), QApplication::applicationName()); if (!settings.contains(DOC_KEY)) { dialogs::DSDialog dlg(this); dlg.setTitle(tr("Document")); QLabel tipsLabel; - tipsLabel.setPixmap(QPixmap(":/icons/showDoc.png")); + tipsLabel.setPixmap(QPixmap(":/icons/showDoc"+QString::number(_mainWindow->language())+".png")); QMessageBox msg; msg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint); msg.setContentsMargins(0, 0, 0, 0); @@ -426,10 +438,7 @@ void MainFrame::show_doc() dlg.exec(); if (msg.clickedButton() == openButton) { - QDir dir(DS_RES_PATH); - dir.cdUp(); - QDesktopServices::openUrl( - QUrl("file:///"+dir.absolutePath() + "/ug.pdf")); + _mainWindow->openDoc(); } if (msg.clickedButton() == noMoreButton) settings.setValue(DOC_KEY, false); diff --git a/DSView/pv/mainframe.h b/DSView/pv/mainframe.h old mode 100644 new mode 100755 index 9426e8a7..0680946f --- a/DSView/pv/mainframe.h +++ b/DSView/pv/mainframe.h @@ -47,8 +47,8 @@ class MainFrame : public QFrame { Q_OBJECT public: - static const int minWidth = 600; - static const int minHeight = 500; + static const int minWidth = 800; + static const int minHeight = 600; static const int defWidth = 900; static const int defHeight = 680; diff --git a/DSView/pv/mainwindow.cpp b/DSView/pv/mainwindow.cpp old mode 100644 new mode 100755 index c66e9be0..80c1f8ec --- a/DSView/pv/mainwindow.cpp +++ b/DSView/pv/mainwindow.cpp @@ -59,6 +59,7 @@ #include "dialogs/storeprogress.h" #include "dialogs/waitingdialog.h" #include "dialogs/dsmessagebox.h" +#include "dialogs/regionoptions.h" #include "toolbars/samplingbar.h" #include "toolbars/trigbar.h" @@ -112,6 +113,7 @@ MainWindow::MainWindow(DeviceManager &device_manager, void MainWindow::setup_ui() { setObjectName(QString::fromUtf8("MainWindow")); + setContentsMargins(0,0,0,0); layout()->setMargin(0); layout()->setSpacing(0); @@ -124,9 +126,13 @@ void MainWindow::setup_ui() // Setup the sampling bar _sampling_bar = new toolbars::SamplingBar(_session, this); + _sampling_bar->setObjectName("sampling_bar"); _trig_bar = new toolbars::TrigBar(_session, this); + _trig_bar->setObjectName("trig_bar"); _file_bar = new toolbars::FileBar(_session, this); + _file_bar->setObjectName("file_bar"); _logo_bar = new toolbars::LogoBar(_session, this); + _logo_bar->setObjectName("logo_bar"); connect(_trig_bar, SIGNAL(on_protocol(bool)), this, SLOT(on_protocol(bool))); @@ -136,6 +142,8 @@ void MainWindow::setup_ui() SLOT(on_measure(bool))); connect(_trig_bar, SIGNAL(on_search(bool)), this, SLOT(on_search(bool))); + connect(_trig_bar, SIGNAL(setTheme(QString)), this, + SLOT(switchTheme(QString))); connect(_file_bar, SIGNAL(load_file(QString)), this, SLOT(load_file(QString))); connect(_file_bar, SIGNAL(on_save()), this, @@ -148,9 +156,14 @@ void MainWindow::setup_ui() SLOT(load_session(QString))); connect(_file_bar, SIGNAL(store_session(QString)), this, SLOT(store_session(QString))); + connect(_logo_bar, SIGNAL(setLanguage(int)), this, + SLOT(switchLanguage(int))); + connect(_logo_bar, SIGNAL(openDoc()), this, + SLOT(openDoc())); // trigger dock _trigger_dock=new QDockWidget(tr("Trigger Setting..."),this); + _trigger_dock->setObjectName("trigger_dock"); _trigger_dock->setFeatures(QDockWidget::DockWidgetMovable); _trigger_dock->setAllowedAreas(Qt::RightDockWidgetArea); _trigger_dock->setVisible(false); @@ -158,6 +171,7 @@ void MainWindow::setup_ui() _trigger_dock->setWidget(_trigger_widget); _dso_trigger_dock=new QDockWidget(tr("Trigger Setting..."),this); + _dso_trigger_dock->setObjectName("dso_trigger_dock"); _dso_trigger_dock->setFeatures(QDockWidget::DockWidgetMovable); _dso_trigger_dock->setAllowedAreas(Qt::RightDockWidgetArea); _dso_trigger_dock->setVisible(false); @@ -183,6 +197,8 @@ void MainWindow::setup_ui() SLOT(timebase_changed())); connect(_sampling_bar, SIGNAL(show_calibration()), _view, SLOT(show_calibration())); + connect(_trig_bar, SIGNAL(show_lissajous(bool)), _view, + SLOT(show_lissajous(bool))); connect(_dso_trigger_widget, SIGNAL(set_trig_pos(int)), _view, SLOT(set_trig_pos(int))); connect(_view, SIGNAL(auto_trig(int)), _dso_trigger_widget, @@ -198,6 +214,7 @@ void MainWindow::setup_ui() #ifdef ENABLE_DECODE // protocol dock _protocol_dock=new QDockWidget(tr("Protocol"),this); + _protocol_dock->setObjectName("protocol_dock"); _protocol_dock->setFeatures(QDockWidget::DockWidgetMovable); _protocol_dock->setAllowedAreas(Qt::RightDockWidgetArea); _protocol_dock->setVisible(false); @@ -210,6 +227,7 @@ void MainWindow::setup_ui() #endif // measure dock _measure_dock=new QDockWidget(tr("Measurement"),this); + _measure_dock->setObjectName("measure_dock"); _measure_dock->setFeatures(QDockWidget::DockWidgetMovable); _measure_dock->setAllowedAreas(Qt::RightDockWidgetArea); _measure_dock->setVisible(false); @@ -217,6 +235,7 @@ void MainWindow::setup_ui() _measure_dock->setWidget(_measure_widget); // search dock _search_dock=new QDockWidget(tr("Search..."), this); + _search_dock->setObjectName("search_dock"); _search_dock->setFeatures(QDockWidget::NoDockWidgetFeatures); _search_dock->setTitleBarWidget(new QWidget(_search_dock)); _search_dock->setAllowedAreas(Qt::BottomDockWidgetArea); @@ -265,8 +284,8 @@ void MainWindow::setup_ui() connect(_view, SIGNAL(cursor_moved()), _measure_widget, SLOT(reCalc())); connect(_view, SIGNAL(prgRate(int)), this, SIGNAL(prgRate(int))); - connect(_view, SIGNAL(update_device_list()), - this, SLOT(update_device_list()), Qt::DirectConnection); + connect(_view, SIGNAL(device_changed(bool)), + this, SLOT(device_changed(bool)), Qt::DirectConnection); // event filter _view->installEventFilter(this); @@ -285,9 +304,34 @@ void MainWindow::setup_ui() // Populate the device list and select the initially selected device _session.set_default_device(boost::bind(&MainWindow::session_error, this, QString(tr("Set Default Device failed")), _1)); + + // defaut language + QSettings settings(QApplication::organizationName(), QApplication::applicationName()); + QLocale locale; + settings.beginGroup("MainFrame"); + switchLanguage(settings.value("language", locale.language()).toInt()); + switchTheme(settings.value("style", "dark").toString()); + settings.endGroup(); + + // UI initial + _measure_widget->add_dist_measure(); + + // update device update_device_list(); _session.start_hotplug_proc(boost::bind(&MainWindow::session_error, this, QString(tr("Hotplug failed")), _1)); + + retranslateUi(); +} + + +void MainWindow::retranslateUi() +{ + _trigger_dock->setWindowTitle(tr("Trigger Setting...")); + _dso_trigger_dock->setWindowTitle(tr("Trigger Setting...")); + _protocol_dock->setWindowTitle(tr("Protocol")); + _measure_dock->setWindowTitle(tr("Measurement")); + _search_dock->setWindowTitle(tr("Search...")); } void MainWindow::session_error( @@ -302,6 +346,7 @@ void MainWindow::update_device_list() { assert(_sampling_bar); + switchLanguage(_language); _session.stop_capture(); _view->reload(); _trigger_widget->device_updated(); @@ -317,12 +362,6 @@ void MainWindow::update_device_list() shared_ptr file_dev; if((file_dev = dynamic_pointer_cast(selected_device))) { - #ifdef ENABLE_DECODE - // load decoders - StoreSession ss(_session); - ss.load_decoders(_protocol_widget, file_dev->get_decoders()); - #endif - // check version if (selected_device->dev_inst()->mode == LOGIC) { GVariant* gvar = selected_device->get_config(NULL, NULL, SR_CONF_FILE_VERSION); @@ -338,6 +377,15 @@ void MainWindow::update_device_list() } } + #ifdef ENABLE_DECODE + // load decoders + StoreSession ss(_session); + ss.load_decoders(_protocol_widget, file_dev->get_decoders()); + #endif + + // load session + load_session_json(file_dev->get_session(), true); + // load data const QString errorMessage( QString(tr("Failed to capture file data!"))); @@ -355,15 +403,31 @@ void MainWindow::update_device_list() #endif if (dir.exists()) { QString str = dir.absolutePath() + "/"; + QString lang_name = ".ses" + QString::number(_language); QString ses_name = str + selected_device->name() + QString::number(selected_device->dev_inst()->mode) + - ".dsc"; + lang_name + ".dsc"; load_session(ses_name); } } else { _file_bar->set_settings_en(false); _logo_bar->dsl_connected(false); + #ifdef Q_OS_LINUX + QDir dir(DS_RES_PATH); + #else + QDir dir(QCoreApplication::applicationDirPath()); + assert(dir.cd("res")); + #endif + if (dir.exists()) { + QString str = dir.absolutePath() + "/"; + QString ses_name = str + + selected_device->name() + + QString::number(selected_device->dev_inst()->mode) + + ".dsc"; + if (QFileInfo(ses_name).exists()) + load_session(ses_name); + } } _sampling_bar->reload(); _view->status_clear(); @@ -453,6 +517,16 @@ void MainWindow::device_detach() update_device_list(); } +void MainWindow::device_changed(bool close) +{ + if (close) { + _sampling_bar->set_sampling(false); + _session.set_default_device(boost::bind(&MainWindow::session_error, this, + QString(tr("Set Default Device failed")), _1)); + } + update_device_list(); +} + void MainWindow::run_stop() { switch(_session.get_capture_state()) { @@ -601,16 +675,23 @@ void MainWindow::session_save() dir.cd(path); QString driver_name = _session.get_device()->name(); QString mode_name = QString::number(_session.get_device()->dev_inst()->mode); - QString file_name = dir.absolutePath() + "/" + driver_name + mode_name + ".dsc"; + QString lang_name = ".ses" + QString::number(_language); + QString file_name = dir.absolutePath() + "/" + + driver_name + mode_name + + lang_name + ".dsc"; if (strncmp(driver_name.toLocal8Bit(), "virtual", 7) && !file_name.isEmpty()) { store_session(file_name); } } + + QSettings settings(QApplication::organizationName(), QApplication::applicationName()); + settings.setValue("windowState", saveState()); } void MainWindow::closeEvent(QCloseEvent *event) { + // not used, refer to closeEvent of mainFrame session_save(); event->accept(); } @@ -640,7 +721,7 @@ void MainWindow::commit_trigger(bool instant) { int i = 0; const QString TRIG_KEY("WarnofMultiTrig"); - QSettings settings; + QSettings settings(QApplication::organizationName(), QApplication::applicationName()); ds_trigger_init(); @@ -703,7 +784,7 @@ void MainWindow::on_search(bool visible) void MainWindow::on_screenShot() { const QString DIR_KEY("ScreenShotPath"); - QSettings settings; + QSettings settings(QApplication::organizationName(), QApplication::applicationName()); QPixmap pixmap; QDesktopWidget *desktop = QApplication::desktop(); pixmap = QPixmap::grabWindow(desktop->winId(), parentWidget()->pos().x(), parentWidget()->pos().y(), @@ -724,13 +805,35 @@ void MainWindow::on_screenShot() void MainWindow::on_save() { using pv::dialogs::StoreProgress; + +// dialogs::RegionOptions *regionDlg = new dialogs::RegionOptions(_view, _session, this); +// regionDlg->exec(); + + QString session_file; + QDir dir; + #if QT_VERSION >= 0x050400 + QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + #else + QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + #endif + if(dir.mkpath(path)) { + dir.cd(path); + + session_file = dir.absolutePath() + "/DSView-session-XXXXXX"; + store_session(session_file); + } + StoreProgress *dlg = new StoreProgress(_session, this); - dlg->save_run(); + dlg->save_run(session_file); } void MainWindow::on_export() { using pv::dialogs::StoreProgress; + +// dialogs::RegionOptions *regionDlg = new dialogs::RegionOptions(_view, _session, this); +// regionDlg->exec(); + StoreProgress *dlg = new StoreProgress(_session, this); dlg->export_run(); } @@ -745,7 +848,13 @@ bool MainWindow::load_session(QString name) QString sessionData = QString::fromUtf8(sessionFile.readAll()); QJsonDocument sessionDoc = QJsonDocument::fromJson(sessionData.toUtf8()); - QJsonObject sessionObj = sessionDoc.object(); + + return load_session_json(sessionDoc, false); +} + +bool MainWindow::load_session_json(QJsonDocument json, bool file_dev) +{ + QJsonObject sessionObj = json.object(); // check session file version if (!sessionObj.contains("Version") || @@ -754,7 +863,7 @@ bool MainWindow::load_session(QString name) // check device and mode const sr_dev_inst *const sdi = _session.get_device()->dev_inst(); - if (strcmp(sdi->driver->name, sessionObj["Device"].toString().toLocal8Bit()) != 0 || + if ((!file_dev && strcmp(sdi->driver->name, sessionObj["Device"].toString().toLocal8Bit()) != 0) || sdi->mode != sessionObj["DeviceMode"].toDouble()) { dialogs::DSMessageBox msg(this); msg.mBox()->setText(tr("Session Error")); @@ -765,6 +874,20 @@ bool MainWindow::load_session(QString name) return false; } + // check language + if (sessionObj.contains("Language")) { + switchLanguage(sessionObj["Language"].toInt()); + } else { + bool language_matched = _session.get_device()->set_config(NULL, NULL, SR_CONF_OPERATION_MODE, + g_variant_new_string(sessionObj["Operation Mode"].toString().toUtf8())); + if (!language_matched) { + if (_language != QLocale::Chinese) + switchLanguage(QLocale::Chinese); + else + switchLanguage(QLocale::English); + } + } + // clear decoders #ifdef ENABLE_DECODE if (sdi->mode == LOGIC) { @@ -842,14 +965,14 @@ bool MainWindow::load_session(QString name) boost::shared_ptr dsoSig; if ((dsoSig = dynamic_pointer_cast(s))) { dsoSig->load_settings(); - dsoSig->set_zero_vrate(obj["zeroPos"].toDouble(), true); - dsoSig->set_trig_vrate(obj["trigValue"].toDouble()); + dsoSig->set_zero_ratio(obj["zeroPos"].toDouble()); + dsoSig->set_trig_ratio(obj["trigValue"].toDouble()); dsoSig->commit_settings(); } boost::shared_ptr analogSig; if ((analogSig = dynamic_pointer_cast(s))) { - analogSig->set_zero_vrate(obj["zeroPos"].toDouble(), true); + analogSig->set_zero_ratio(obj["zeroPos"].toDouble()); analogSig->commit_settings(); } @@ -876,9 +999,15 @@ bool MainWindow::load_session(QString name) } #endif + // load measure + if (sessionObj.contains("measure")) { + _view->get_viewstatus()->load_session(sessionObj["measure"].toArray()); + } + return true; } + bool MainWindow::store_session(QString name) { QFile sessionFile(name); @@ -894,7 +1023,7 @@ bool MainWindow::store_session(QString name) } QTextStream outStream(&sessionFile); outStream.setCodec("UTF-8"); - outStream.setGenerateByteOrderMark(true); + //outStream.setGenerateByteOrderMark(true); // UTF-8 without BOM GVariant *gvar_opts; GVariant *gvar; @@ -905,6 +1034,7 @@ bool MainWindow::store_session(QString name) sessionVar["Version"]= QJsonValue::fromVariant(Session_Version); sessionVar["Device"] = QJsonValue::fromVariant(sdi->driver->name); sessionVar["DeviceMode"] = QJsonValue::fromVariant(sdi->mode); + sessionVar["Language"] = QJsonValue::fromVariant(_language); if ((sr_config_list(sdi->driver, sdi, NULL, SR_CONF_DEVICE_SESSIONS, &gvar_opts) != SR_OK)) return false; /* Driver supports no device instance sessions. */ @@ -935,7 +1065,10 @@ bool MainWindow::store_session(QString name) s_obj["type"] = s->get_type(); s_obj["enabled"] = s->enabled(); s_obj["name"] = s->get_name(); - s_obj["colour"] = QJsonValue::fromVariant(s->get_colour()); + if (s->get_colour().isValid()) + s_obj["colour"] = QJsonValue::fromVariant(s->get_colour()); + else + s_obj["colour"] = QJsonValue::fromVariant("default"); boost::shared_ptr logicSig; if ((logicSig = dynamic_pointer_cast(s))) { @@ -948,14 +1081,15 @@ bool MainWindow::store_session(QString name) s_obj["vfactor"] = QJsonValue::fromVariant(static_cast(dsoSig->get_factor())); s_obj["coupling"] = dsoSig->get_acCoupling(); s_obj["trigValue"] = dsoSig->get_trig_vrate(); - s_obj["zeroPos"] = dsoSig->get_zero_vrate(); + s_obj["zeroPos"] = dsoSig->get_zero_ratio(); } boost::shared_ptr analogSig; if ((analogSig = dynamic_pointer_cast(s))) { s_obj["vdiv"] = QJsonValue::fromVariant(static_cast(analogSig->get_vdiv())); + s_obj["vfactor"] = QJsonValue::fromVariant(static_cast(analogSig->get_factor())); s_obj["coupling"] = analogSig->get_acCoupling(); - s_obj["zeroPos"] = analogSig->get_zero_vrate(); + s_obj["zeroPos"] = analogSig->get_zero_ratio(); s_obj["mapUnit"] = analogSig->get_mapUnit(); s_obj["mapMin"] = analogSig->get_mapMin(); s_obj["mapMax"] = analogSig->get_mapMax(); @@ -973,6 +1107,10 @@ bool MainWindow::store_session(QString name) sessionVar["decoder"] = ss.json_decoders(); #endif + if (_session.get_device()->dev_inst()->mode == DSO) { + sessionVar["measure"] = _view->get_viewstatus()->get_session(); + } + QJsonDocument sessionDoc(sessionVar); //sessionFile.write(QString::fromUtf8(sessionDoc.toJson())); outStream << QString::fromUtf8(sessionDoc.toJson()); @@ -980,6 +1118,28 @@ bool MainWindow::store_session(QString name) return true; } +void MainWindow::restore_dock() +{ + // default dockwidget size + QSettings settings(QApplication::organizationName(), QApplication::applicationName()); + restoreState(settings.value("windowState").toByteArray()); + if (_session.get_device()->dev_inst()->mode != DSO) { + _dso_trigger_dock->setVisible(false); + _trig_bar->update_trig_btn(_trigger_dock->isVisible()); + } else { + _trigger_dock->setVisible(false); + _trig_bar->update_trig_btn(_dso_trigger_dock->isVisible()); + } + if (_session.get_device()->dev_inst()->mode != LOGIC) { +#ifdef ENABLE_DECODE + on_protocol(false); +#endif + } + _trig_bar->update_protocol_btn(_protocol_dock->isVisible()); + _trig_bar->update_measure_btn(_measure_dock->isVisible()); + _trig_bar->update_search_btn(_search_dock->isVisible()); +} + bool MainWindow::eventFilter(QObject *object, QEvent *event) { (void) object; @@ -1060,7 +1220,7 @@ bool MainWindow::eventFilter(QObject *object, QEvent *event) shared_ptr dsoSig; if ((dsoSig = dynamic_pointer_cast(s))) { if (dsoSig->get_vDialActive()) { - dsoSig->go_vDialNext(); + dsoSig->go_vDialNext(true); update(); break; } @@ -1072,7 +1232,7 @@ bool MainWindow::eventFilter(QObject *object, QEvent *event) shared_ptr dsoSig; if ((dsoSig = dynamic_pointer_cast(s))) { if (dsoSig->get_vDialActive()) { - dsoSig->go_vDialPre(); + dsoSig->go_vDialPre(true); update(); break; } @@ -1087,4 +1247,57 @@ bool MainWindow::eventFilter(QObject *object, QEvent *event) return false; } +int MainWindow::language() const +{ + return _language; +} + +void MainWindow::switchLanguage(int language) +{ + shared_ptr dev = _session.get_device(); + dev->set_config(NULL, NULL, SR_CONF_LANGUAGE, g_variant_new_int16(language)); + + if (_language != language) { + _language = language; + if (_language != QLocale::English) { + _qtTrans.load(":/qt_"+QString::number(_language)); + qApp->installTranslator(&_qtTrans); + _myTrans.load(":/my_"+QString::number(_language)); + qApp->installTranslator(&_myTrans); + retranslateUi(); + } else { + qApp->removeTranslator(&_qtTrans); + qApp->removeTranslator(&_myTrans); + retranslateUi(); + } + qApp->setProperty("Language", _language); + } +} + +void MainWindow::switchTheme(QString style) +{ + if (_style != style) { + _style = style; + qApp->setProperty("Style", _style); + QString qssRes = ":/"+_style+".qss"; + QFile qss(qssRes); + qss.open(QFile::ReadOnly | QFile::Text); + qApp->setStyleSheet(qss.readAll()); + qss.close(); + _session.data_updated(); + } +} + +void MainWindow::openDoc() +{ + #ifndef Q_OS_LINUX + QDir dir(QCoreApplication::applicationDirPath()); + #else + QDir dir(DS_RES_PATH); + dir.cdUp(); + #endif + QDesktopServices::openUrl( + QUrl("file:///"+dir.absolutePath() + "/ug"+QString::number(_language)+".pdf")); +} + } // namespace pv diff --git a/DSView/pv/mainwindow.h b/DSView/pv/mainwindow.h old mode 100644 new mode 100755 index 9b3c3284..16272baf --- a/DSView/pv/mainwindow.h +++ b/DSView/pv/mainwindow.h @@ -79,13 +79,19 @@ protected: private: void setup_ui(); - + void retranslateUi(); void session_error(const QString text, const QString info_text); - bool eventFilter(QObject *object, QEvent *event); public slots: void session_save(); + int language() const; + void openDoc(); + + void switchLanguage(int language); + void switchTheme(QString style); + + void restore_dock(); private slots: void load_file(QString file_name); @@ -126,6 +132,7 @@ private slots: void on_export(); bool load_session(QString name); + bool load_session_json(QJsonDocument json, bool file_dev); bool store_session(QString name); /* @@ -138,11 +145,13 @@ private slots: */ void device_attach(); void device_detach(); + void device_changed(bool close); /* * errors */ void show_error(); + signals: void prgRate(int progress); @@ -188,6 +197,11 @@ private: dock::MeasureDock *_measure_widget; QDockWidget *_search_dock; dock::SearchDock * _search_widget; + + int _language; + QString _style; + QTranslator _qtTrans; + QTranslator _myTrans; }; } // namespace pv diff --git a/DSView/pv/prop/binding/binding.cpp b/DSView/pv/prop/binding/binding.cpp old mode 100644 new mode 100755 index d778fd5f..c5252716 --- a/DSView/pv/prop/binding/binding.cpp +++ b/DSView/pv/prop/binding/binding.cpp @@ -61,7 +61,7 @@ void Binding::add_properties_to_form(QFormLayout *layout, if (p->labeled_widget()) layout->addRow(widget); else - layout->addRow(p->name(), widget); + layout->addRow(p->label(), widget); } } diff --git a/DSView/pv/prop/binding/binding.h b/DSView/pv/prop/binding/binding.h old mode 100644 new mode 100755 diff --git a/DSView/pv/prop/binding/decoderoptions.cpp b/DSView/pv/prop/binding/decoderoptions.cpp old mode 100644 new mode 100755 index 66870403..644db938 --- a/DSView/pv/prop/binding/decoderoptions.cpp +++ b/DSView/pv/prop/binding/decoderoptions.cpp @@ -72,16 +72,16 @@ DecoderOptions::DecoderOptions( shared_ptr prop; if (opt->values) - prop = bind_enum(name, opt, getter, setter); + prop = bind_enum(name, opt, getter, setter); else if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("d"))) - prop = shared_ptr(new Double(name, 2, "", + prop = shared_ptr(new Double(name, name, 2, "", none, none, getter, setter)); else if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("x"))) prop = shared_ptr( - new Int(name, "", none, getter, setter)); + new Int(name, name, "", none, getter, setter)); else if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("s"))) prop = shared_ptr( - new String(name, getter, setter)); + new String(name, name, getter, setter)); else continue; @@ -100,7 +100,7 @@ shared_ptr DecoderOptions::bind_enum( values.push_back(make_pair(var, print_gvariant(var))); } - return shared_ptr(new Enum(name, values, getter, setter)); + return shared_ptr(new Enum(name, name, values, getter, setter)); } GVariant* DecoderOptions::getter(const char *id) diff --git a/DSView/pv/prop/binding/decoderoptions.h b/DSView/pv/prop/binding/decoderoptions.h old mode 100644 new mode 100755 diff --git a/DSView/pv/prop/binding/deviceoptions.cpp b/DSView/pv/prop/binding/deviceoptions.cpp old mode 100644 new mode 100755 index 9a51d50c..deef3fac --- a/DSView/pv/prop/binding/deviceoptions.cpp +++ b/DSView/pv/prop/binding/deviceoptions.cpp @@ -66,16 +66,27 @@ DeviceOptions::DeviceOptions(struct sr_dev_inst *sdi) : if(sr_config_list(_sdi->driver, _sdi, NULL, key, &gvar_list) != SR_OK) gvar_list = NULL; - const QString name(info->label); + const QString name(info->name); + char *label_char = info->label; + GVariant *gvar_tmp = NULL; + if (sr_config_get(_sdi->driver, _sdi, NULL, NULL, SR_CONF_LANGUAGE, &gvar_tmp) == SR_OK) { + if (gvar_tmp != NULL) { + int language = g_variant_get_int16(gvar_tmp); + if (language == QLocale::Chinese) + label_char = info->label_cn; + g_variant_unref(gvar_tmp); + } + } + const QString label(label_char); switch(key) { case SR_CONF_SAMPLERATE: - bind_samplerate(name, gvar_list); + bind_samplerate(name, label, gvar_list); break; case SR_CONF_CAPTURE_RATIO: - bind_int(name, key, "%", pair(0, 100)); + bind_int(name, label, key, "%", pair(0, 100)); break; case SR_CONF_PATTERN_MODE: @@ -94,31 +105,36 @@ DeviceOptions::DeviceOptions(struct sr_dev_inst *sdi) : case SR_CONF_TEST: case SR_CONF_STATUS: case SR_CONF_PROBE_FACTOR: - bind_enum(name, key, gvar_list); + bind_enum(name, label, key, gvar_list); break; case SR_CONF_VTH: - bind_double(name, key, "V", pair(0.0, 5.0), 1, 0.1); + bind_double(name, label, key, "V", pair(0.0, 5.0), 1, 0.1); break; case SR_CONF_RLE: - bind_bool(name, key); + bind_bool(name, label, key); break; case SR_CONF_RLE_SUPPORT: case SR_CONF_CLOCK_TYPE: case SR_CONF_CLOCK_EDGE: case SR_CONF_INSTANT: - bind_bool(name, key); + bind_bool(name, label, key); break; case SR_CONF_TIMEBASE: - bind_enum(name, key, gvar_list, print_timebase); + bind_enum(name, label, key, gvar_list, print_timebase); break; case SR_CONF_PROBE_VDIV: - bind_enum(name, key, gvar_list, print_vdiv); + bind_enum(name, label, key, gvar_list, print_vdiv); break; + + case SR_CONF_BANDWIDTH_LIMIT: + bind_bandwidths(name, label, key, gvar_list); + break; + default: gvar_list = NULL; } @@ -126,7 +142,8 @@ DeviceOptions::DeviceOptions(struct sr_dev_inst *sdi) : if (gvar_list) g_variant_unref(gvar_list); } - g_variant_unref(gvar_opts); + if (gvar_opts) + g_variant_unref(gvar_opts); } GVariant* DeviceOptions::config_getter( @@ -134,8 +151,7 @@ GVariant* DeviceOptions::config_getter( { GVariant *data = NULL; if (sr_config_get(sdi->driver, sdi, NULL, NULL, key, &data) != SR_OK) { - qDebug() << - "WARNING: Failed to get value of config id" << key; + qDebug() << "WARNING: Failed to get value of config id" << key; return NULL; } return data; @@ -145,17 +161,17 @@ void DeviceOptions::config_setter( struct sr_dev_inst *sdi, int key, GVariant* value) { if (sr_config_set(sdi, NULL, NULL, key, value) != SR_OK) - qDebug() << "WARNING: Failed to set value of sample rate"; + qDebug() << "WARNING: Failed to set value of config id" << key; } -void DeviceOptions::bind_bool(const QString &name, int key) +void DeviceOptions::bind_bool(const QString &name, const QString label, int key) { _properties.push_back(boost::shared_ptr( - new Bool(name, bind(config_getter, _sdi, key), + new Bool(name, label, bind(config_getter, _sdi, key), bind(config_setter, _sdi, key, _1)))); } -void DeviceOptions::bind_enum(const QString &name, int key, +void DeviceOptions::bind_enum(const QString &name, const QString label, int key, GVariant *const gvar_list, boost::function printer) { GVariant *gvar; @@ -169,26 +185,26 @@ void DeviceOptions::bind_enum(const QString &name, int key, values.push_back(make_pair(gvar, printer(gvar))); _properties.push_back(boost::shared_ptr( - new Enum(name, values, + new Enum(name, label, values, bind(config_getter, _sdi, key), bind(config_setter, _sdi, key, _1)))); } -void DeviceOptions::bind_int(const QString &name, int key, QString suffix, - optional< std::pair > range) +void DeviceOptions::bind_int(const QString &name, const QString label, int key, QString suffix, + optional< std::pair > range) { _properties.push_back(boost::shared_ptr( - new Int(name, suffix, range, + new Int(name, label, suffix, range, bind(config_getter, _sdi, key), bind(config_setter, _sdi, key, _1)))); } -void DeviceOptions::bind_double(const QString &name, int key, QString suffix, +void DeviceOptions::bind_double(const QString &name, const QString label, int key, QString suffix, optional< std::pair > range, int decimals, boost::optional step) { _properties.push_back(boost::shared_ptr( - new Double(name, decimals, suffix, range, step, + new Double(name, label, decimals, suffix, range, step, bind(config_getter, _sdi, key), bind(config_setter, _sdi, key, _1)))); } @@ -209,8 +225,8 @@ QString DeviceOptions::print_gvariant(GVariant *const gvar) return s; } -void DeviceOptions::bind_samplerate(const QString &name, - GVariant *const gvar_list) +void DeviceOptions::bind_samplerate(const QString &name, const QString label, + GVariant *const gvar_list) { GVariant *gvar_list_samplerates; @@ -227,7 +243,7 @@ void DeviceOptions::bind_samplerate(const QString &name, assert(num_elements == 3); _properties.push_back(boost::shared_ptr( - new Double(name, 0, QObject::tr("Hz"), + new Double(name, label, 0, QObject::tr("Hz"), make_pair((double)elements[0], (double)elements[1]), (double)elements[2], bind(samplerate_double_getter, _sdi), @@ -238,7 +254,7 @@ void DeviceOptions::bind_samplerate(const QString &name, else if ((gvar_list_samplerates = g_variant_lookup_value(gvar_list, "samplerates", G_VARIANT_TYPE("at")))) { - bind_enum(name, SR_CONF_SAMPLERATE, + bind_enum(name, label, SR_CONF_SAMPLERATE, gvar_list_samplerates, print_samplerate); g_variant_unref(gvar_list_samplerates); } @@ -291,6 +307,37 @@ QString DeviceOptions::print_vdiv(GVariant *const gvar) return QString(sr_voltage_string(p, q)); } +void DeviceOptions::bind_bandwidths(const QString &name, const QString label, int key, + GVariant *const gvar_list, boost::function printer) +{ + GVariant *gvar; + GVariantIter iter; + vector< pair > values; + bool bw_limit = FALSE; + + assert(gvar_list); + + GVariant *gvar_tmp = NULL; + if (sr_config_get(_sdi->driver, _sdi, NULL, NULL, SR_CONF_BANDWIDTH, &gvar_tmp) == SR_OK) { + if (gvar_tmp != NULL) { + bw_limit = g_variant_get_boolean(gvar_tmp); + g_variant_unref(gvar_tmp); + } + } + + if (!bw_limit) + return; + + g_variant_iter_init (&iter, gvar_list); + while ((gvar = g_variant_iter_next_value (&iter))) + values.push_back(make_pair(gvar, printer(gvar))); + + _properties.push_back(boost::shared_ptr( + new Enum(name, label, values, + bind(config_getter, _sdi, key), + bind(config_setter, _sdi, key, _1)))); +} + } // binding } // prop } // pv diff --git a/DSView/pv/prop/binding/deviceoptions.h b/DSView/pv/prop/binding/deviceoptions.h old mode 100644 new mode 100755 index 3d52356b..6de00e81 --- a/DSView/pv/prop/binding/deviceoptions.h +++ b/DSView/pv/prop/binding/deviceoptions.h @@ -48,20 +48,20 @@ private: static void config_setter( struct sr_dev_inst *sdi, int key, GVariant* value); - void bind_bool(const QString &name, int key); - void bind_enum(const QString &name, int key, + void bind_bool(const QString &name, const QString label, int key); + void bind_enum(const QString &name, const QString label, int key, GVariant *const gvar_list, boost::function printer = print_gvariant); - void bind_int(const QString &name, int key, QString suffix, + void bind_int(const QString &name, const QString label, int key, QString suffix, boost::optional< std::pair > range); - void bind_double(const QString &name, int key, QString suffix, + void bind_double(const QString &name, const QString label, int key, QString suffix, boost::optional > range, int decimals, boost::optional step); static QString print_gvariant(GVariant *const gvar); - void bind_samplerate(const QString &name, + void bind_samplerate(const QString &name, const QString label, GVariant *const gvar_list); static QString print_samplerate(GVariant *const gvar); static GVariant* samplerate_double_getter( @@ -72,6 +72,10 @@ private: static QString print_timebase(GVariant *const gvar); static QString print_vdiv(GVariant *const gvar); + void bind_bandwidths(const QString &name, const QString label, int key, + GVariant *const gvar_list, + boost::function printer = print_gvariant); + protected: struct sr_dev_inst *const _sdi; }; diff --git a/DSView/pv/prop/binding/probeoptions.cpp b/DSView/pv/prop/binding/probeoptions.cpp old mode 100644 new mode 100755 index 594472af..3b6a101f --- a/DSView/pv/prop/binding/probeoptions.cpp +++ b/DSView/pv/prop/binding/probeoptions.cpp @@ -67,28 +67,43 @@ ProbeOptions::ProbeOptions(struct sr_dev_inst *sdi, if(sr_config_list(_sdi->driver, _sdi, NULL, key, &gvar_list) != SR_OK) gvar_list = NULL; - const QString name(info->label); + const QString name(info->name); + char *label_char = info->label; + GVariant *gvar_tmp = NULL; + if (sr_config_get(_sdi->driver, _sdi, NULL, NULL, SR_CONF_LANGUAGE, &gvar_tmp) == SR_OK) { + if (gvar_tmp != NULL) { + int language = g_variant_get_int16(gvar_tmp); + if (language == QLocale::Chinese) + label_char = info->label_cn; + g_variant_unref(gvar_tmp); + } + } + const QString label(label_char); switch(key) { case SR_CONF_PROBE_VDIV: - bind_vdiv(name, gvar_list); + bind_vdiv(name, label, gvar_list); break; case SR_CONF_PROBE_MAP_MIN: case SR_CONF_PROBE_MAP_MAX: - bind_double(name, key, "", + bind_double(name, label, key, "", pair(-999999.99, 999999.99), 2, 0.01); break; case SR_CONF_PROBE_COUPLING: - bind_coupling(name, gvar_list); + bind_coupling(name, label, gvar_list); break; case SR_CONF_PROBE_MAP_UNIT: - bind_enum(name, key, gvar_list); + bind_enum(name, label, key, gvar_list); break; + case SR_CONF_PROBE_MAP_DEFAULT: + bind_bool(name, label, key); + break; + default: gvar_list = NULL; } @@ -96,7 +111,8 @@ ProbeOptions::ProbeOptions(struct sr_dev_inst *sdi, if (gvar_list) g_variant_unref(gvar_list); } - g_variant_unref(gvar_opts); + if (gvar_opts) + g_variant_unref(gvar_opts); } GVariant* ProbeOptions::config_getter( @@ -120,14 +136,14 @@ void ProbeOptions::config_setter( qDebug() << "WARNING: Failed to set value of sample rate"; } -void ProbeOptions::bind_bool(const QString &name, int key) +void ProbeOptions::bind_bool(const QString &name, const QString label, int key) { _properties.push_back(boost::shared_ptr( - new Bool(name, bind(config_getter, _sdi, _probe, key), + new Bool(name, label, bind(config_getter, _sdi, _probe, key), bind(config_setter, _sdi, _probe, key, _1)))); } -void ProbeOptions::bind_enum(const QString &name, int key, +void ProbeOptions::bind_enum(const QString &name, const QString label, int key, GVariant *const gvar_list, boost::function printer) { GVariant *gvar; @@ -141,32 +157,32 @@ void ProbeOptions::bind_enum(const QString &name, int key, values.push_back(make_pair(gvar, printer(gvar))); _properties.push_back(boost::shared_ptr( - new Enum(name, values, + new Enum(name, label, values, bind(config_getter, _sdi, _probe, key), bind(config_setter, _sdi, _probe, key, _1)))); } -void ProbeOptions::bind_int(const QString &name, int key, QString suffix, - optional< std::pair > range) +void ProbeOptions::bind_int(const QString &name, const QString label, int key, QString suffix, + optional< std::pair > range) { _properties.push_back(boost::shared_ptr( - new Int(name, suffix, range, + new Int(name, label, suffix, range, bind(config_getter, _sdi, _probe, key), bind(config_setter, _sdi, _probe, key, _1)))); } -void ProbeOptions::bind_double(const QString &name, int key, QString suffix, +void ProbeOptions::bind_double(const QString &name, const QString label, int key, QString suffix, optional< std::pair > range, int decimals, boost::optional step) { _properties.push_back(boost::shared_ptr( - new Double(name, decimals, suffix, range, step, + new Double(name, label, decimals, suffix, range, step, bind(config_getter, _sdi, _probe, key), bind(config_setter, _sdi, _probe, key, _1)))); } -void ProbeOptions::bind_vdiv(const QString &name, - GVariant *const gvar_list) +void ProbeOptions::bind_vdiv(const QString &name, const QString label, + GVariant *const gvar_list) { GVariant *gvar_list_vdivs; @@ -175,13 +191,13 @@ void ProbeOptions::bind_vdiv(const QString &name, if ((gvar_list_vdivs = g_variant_lookup_value(gvar_list, "vdivs", G_VARIANT_TYPE("at")))) { - bind_enum(name, SR_CONF_PROBE_VDIV, + bind_enum(name, label, SR_CONF_PROBE_VDIV, gvar_list_vdivs, print_vdiv); g_variant_unref(gvar_list_vdivs); } } -void ProbeOptions::bind_coupling(const QString &name, +void ProbeOptions::bind_coupling(const QString &name, const QString label, GVariant *const gvar_list) { GVariant *gvar_list_coupling; @@ -191,7 +207,7 @@ void ProbeOptions::bind_coupling(const QString &name, if ((gvar_list_coupling = g_variant_lookup_value(gvar_list, "coupling", G_VARIANT_TYPE("ay")))) { - bind_enum(name, SR_CONF_PROBE_COUPLING, + bind_enum(name, label, SR_CONF_PROBE_COUPLING, gvar_list_coupling, print_coupling); g_variant_unref(gvar_list_coupling); } diff --git a/DSView/pv/prop/binding/probeoptions.h b/DSView/pv/prop/binding/probeoptions.h old mode 100644 new mode 100755 index 0bae7efe..3d82c719 --- a/DSView/pv/prop/binding/probeoptions.h +++ b/DSView/pv/prop/binding/probeoptions.h @@ -50,22 +50,22 @@ private: struct sr_dev_inst *sdi, struct sr_channel *probe, int key, GVariant* value); - void bind_bool(const QString &name, int key); - void bind_enum(const QString &name, int key, + void bind_bool(const QString &name, const QString label, int key); + void bind_enum(const QString &name, const QString label, int key, GVariant *const gvar_list, boost::function printer = print_gvariant); - void bind_int(const QString &name, int key, QString suffix, + void bind_int(const QString &name, const QString label, int key, QString suffix, boost::optional< std::pair > range); - void bind_double(const QString &name, int key, QString suffix, + void bind_double(const QString &name, const QString label, int key, QString suffix, boost::optional > range, int decimals, boost::optional step); static QString print_gvariant(GVariant *const gvar); - void bind_vdiv(const QString &name, + void bind_vdiv(const QString &name, const QString label, GVariant *const gvar_list); - void bind_coupling(const QString &name, + void bind_coupling(const QString &name, const QString label, GVariant *const gvar_list); static QString print_vdiv(GVariant *const gvar); diff --git a/DSView/pv/prop/bool.cpp b/DSView/pv/prop/bool.cpp old mode 100644 new mode 100755 index ecc6a2e7..75169b86 --- a/DSView/pv/prop/bool.cpp +++ b/DSView/pv/prop/bool.cpp @@ -32,8 +32,8 @@ using namespace boost; namespace pv { namespace prop { -Bool::Bool(QString name, Getter getter, Setter setter) : - Property(name, getter, setter), +Bool::Bool(QString name, QString label, Getter getter, Setter setter) : + Property(name, label, getter, setter), _check_box(NULL) { } @@ -47,7 +47,7 @@ QWidget* Bool::get_widget(QWidget *parent, bool auto_commit) if (_check_box) return _check_box; - _check_box = new QCheckBox(name(), parent); + _check_box = new QCheckBox(label(), parent); GVariant *const value = _getter ? _getter() : NULL; diff --git a/DSView/pv/prop/bool.h b/DSView/pv/prop/bool.h old mode 100644 new mode 100755 index ec69217e..c9f9e409 --- a/DSView/pv/prop/bool.h +++ b/DSView/pv/prop/bool.h @@ -35,7 +35,7 @@ class Bool : public Property Q_OBJECT; public: - Bool(QString name, Getter getter, Setter setter); + Bool(QString name, QString label, Getter getter, Setter setter); virtual ~Bool(); diff --git a/DSView/pv/prop/double.cpp b/DSView/pv/prop/double.cpp old mode 100644 new mode 100755 index a0c8cec3..2c832d9a --- a/DSView/pv/prop/double.cpp +++ b/DSView/pv/prop/double.cpp @@ -32,14 +32,14 @@ using namespace boost; namespace pv { namespace prop { -Double::Double(QString name, - int decimals, - QString suffix, - optional< pair > range, - optional step, - Getter getter, - Setter setter) : - Property(name, getter, setter), +Double::Double(QString name, QString label, + int decimals, + QString suffix, + optional< pair > range, + optional step, + Getter getter, + Setter setter) : + Property(name, label, getter, setter), _decimals(decimals), _suffix(suffix), _range(range), diff --git a/DSView/pv/prop/double.h b/DSView/pv/prop/double.h old mode 100644 new mode 100755 index 7eb4493d..f80d5170 --- a/DSView/pv/prop/double.h +++ b/DSView/pv/prop/double.h @@ -39,7 +39,7 @@ class Double : public Property Q_OBJECT; public: - Double(QString name, int decimals, QString suffix, + Double(QString name, QString label, int decimals, QString suffix, boost::optional< std::pair > range, boost::optional step, Getter getter, diff --git a/DSView/pv/prop/enum.cpp b/DSView/pv/prop/enum.cpp old mode 100644 new mode 100755 index aad0c08e..08d73114 --- a/DSView/pv/prop/enum.cpp +++ b/DSView/pv/prop/enum.cpp @@ -33,10 +33,10 @@ using namespace std; namespace pv { namespace prop { -Enum::Enum(QString name, - vector > values, - Getter getter, Setter setter) : - Property(name, getter, setter), +Enum::Enum(QString name, QString label, + vector > values, + Getter getter, Setter setter) : + Property(name, label, getter, setter), _values(values), _selector(NULL) { @@ -48,7 +48,8 @@ Enum::Enum(QString name, Enum::~Enum() { for (unsigned int i = 0; i < _values.size(); i++) - g_variant_unref(_values[i].first); + if (_values[i].first) + g_variant_unref(_values[i].first); } QWidget* Enum::get_widget(QWidget *parent, bool auto_commit) @@ -62,14 +63,12 @@ QWidget* Enum::get_widget(QWidget *parent, bool auto_commit) } _selector = new QComboBox(parent); - _selector->setSizeAdjustPolicy(QComboBox::AdjustToContents); for (unsigned int i = 0; i < _values.size(); i++) { const pair &v = _values[i]; _selector->addItem(v.second, qVariantFromValue((void*)v.first)); if (value && g_variant_compare(v.first, value) == 0) _selector->setCurrentIndex(i); } - _selector->view()->setMinimumWidth(_selector->width()+30); g_variant_unref(value); diff --git a/DSView/pv/prop/enum.h b/DSView/pv/prop/enum.h old mode 100644 new mode 100755 index a223565a..6b1214cd --- a/DSView/pv/prop/enum.h +++ b/DSView/pv/prop/enum.h @@ -38,7 +38,7 @@ class Enum : public Property Q_OBJECT; public: - Enum(QString name, std::vector > values, + Enum(QString name, QString label, std::vector > values, Getter getter, Setter setter); virtual ~Enum(); diff --git a/DSView/pv/prop/int.cpp b/DSView/pv/prop/int.cpp old mode 100644 new mode 100755 index b3ea0c69..ac7961ee --- a/DSView/pv/prop/int.cpp +++ b/DSView/pv/prop/int.cpp @@ -49,12 +49,12 @@ using namespace std; namespace pv { namespace prop { -Int::Int(QString name, - QString suffix, - optional< pair > range, - Getter getter, - Setter setter) : - Property(name, getter, setter), +Int::Int(QString name, QString label, + QString suffix, + optional< pair > range, + Getter getter, + Setter setter) : + Property(name, label, getter, setter), _suffix(suffix), _range(range), _value(NULL), diff --git a/DSView/pv/prop/int.h b/DSView/pv/prop/int.h old mode 100644 new mode 100755 index daa4c1d3..0e747540 --- a/DSView/pv/prop/int.h +++ b/DSView/pv/prop/int.h @@ -39,7 +39,7 @@ class Int : public Property Q_OBJECT; public: - Int(QString name, QString suffix, + Int(QString name, QString label, QString suffix, boost::optional< std::pair > range, Getter getter, Setter setter); diff --git a/DSView/pv/prop/property.cpp b/DSView/pv/prop/property.cpp old mode 100644 new mode 100755 index af37d644..de7150b5 --- a/DSView/pv/prop/property.cpp +++ b/DSView/pv/prop/property.cpp @@ -25,10 +25,11 @@ namespace pv { namespace prop { -Property::Property(QString name, Getter getter, Setter setter) : +Property::Property(QString name, QString label, Getter getter, Setter setter) : _getter(getter), _setter(setter), - _name(name) + _name(name), + _label(label) { } @@ -37,6 +38,11 @@ const QString& Property::name() const return _name; } +const QString& Property::label() const +{ + return _label; +} + bool Property::labeled_widget() const { return false; diff --git a/DSView/pv/prop/property.h b/DSView/pv/prop/property.h old mode 100644 new mode 100755 index 117ba9a1..2868eedb --- a/DSView/pv/prop/property.h +++ b/DSView/pv/prop/property.h @@ -44,10 +44,11 @@ public: typedef boost::function Setter; protected: - Property(QString name, Getter getter, Setter setter); + Property(QString name, QString label, Getter getter, Setter setter); public: const QString& name() const; + const QString& label() const; virtual QWidget* get_widget(QWidget *parent, bool auto_commit = false) = 0; @@ -63,6 +64,7 @@ protected: private: QString _name; + QString _label; }; } // prop diff --git a/DSView/pv/prop/string.cpp b/DSView/pv/prop/string.cpp old mode 100644 new mode 100755 index e03064ef..c57525e6 --- a/DSView/pv/prop/string.cpp +++ b/DSView/pv/prop/string.cpp @@ -28,10 +28,10 @@ namespace pv { namespace prop { -String::String(QString name, +String::String(QString name, QString label, Getter getter, Setter setter) : - Property(name, getter, setter), + Property(name, label, getter, setter), _line_edit(NULL) { } diff --git a/DSView/pv/prop/string.h b/DSView/pv/prop/string.h old mode 100644 new mode 100755 index 7be058d2..ef788c7f --- a/DSView/pv/prop/string.h +++ b/DSView/pv/prop/string.h @@ -33,7 +33,7 @@ class String : public Property Q_OBJECT; public: - String(QString name, Getter getter, Setter setter); + String(QString name, QString label, Getter getter, Setter setter); QWidget* get_widget(QWidget *parent, bool auto_commit); diff --git a/DSView/pv/sigsession.cpp b/DSView/pv/sigsession.cpp old mode 100644 new mode 100755 index a44d920f..d40b5217 --- a/DSView/pv/sigsession.cpp +++ b/DSView/pv/sigsession.cpp @@ -41,6 +41,7 @@ #include "data/decoderstack.h" #include "data/decode/decoder.h" #include "data/decodermodel.h" +#include "data/spectrumstack.h" #include "data/mathstack.h" #include "view/analogsignal.h" @@ -48,6 +49,8 @@ #include "view/logicsignal.h" #include "view/groupsignal.h" #include "view/decodetrace.h" +#include "view/spectrumtrace.h" +#include "view/lissajoustrace.h" #include "view/mathtrace.h" #include @@ -109,6 +112,9 @@ SigSession::SigSession(DeviceManager &device_manager) : #ifdef ENABLE_DECODE _decoder_model = new pv::data::DecoderModel(this); #endif + _lissajous_trace = NULL; + _math_trace = NULL; + _dso_feed = false; // Create snapshots & data containers _cur_logic_snapshot.reset(new data::LogicSnapshot()); @@ -148,7 +154,7 @@ boost::shared_ptr SigSession::get_device() const return _dev_inst; } -void SigSession::set_device(boost::shared_ptr dev_inst) throw(QString) +void SigSession::set_device(boost::shared_ptr dev_inst) { using pv::device::Device; @@ -186,7 +192,7 @@ void SigSession::set_device(boost::shared_ptr dev_inst) throw(Q } -void SigSession::set_file(QString name) throw(QString) +void SigSession::set_file(QString name) { // Deslect the old device, because file type detection in File::create // destorys the old session inside libsigrok. @@ -204,6 +210,21 @@ void SigSession::set_file(QString name) throw(QString) } } +void SigSession::close_file(boost::shared_ptr dev_inst) +{ + assert(dev_inst); + try { + dev_inst->device_updated(); + set_repeating(false); + stop_capture(); + capture_state_changed(SigSession::Stopped); + _device_manager.del_device(dev_inst); + } catch(const QString e) { + throw(e); + return; + } +} + void SigSession::set_default_device(boost::function error_handler) { boost::shared_ptr default_device; @@ -274,8 +295,8 @@ void SigSession::set_cur_samplerate(uint64_t samplerate) _logic_data->set_samplerate(_cur_samplerate); if (_analog_data) _analog_data->set_samplerate(_cur_samplerate); - if (_dso_data) - _dso_data->set_samplerate(_cur_samplerate); +// if (_dso_data) +// _dso_data->set_samplerate(_cur_samplerate); // Group if (_group_data) _group_data->set_samplerate(_cur_samplerate); @@ -285,9 +306,9 @@ void SigSession::set_cur_samplerate(uint64_t samplerate) BOOST_FOREACH(const boost::shared_ptr d, _decode_traces) d->decoder()->set_samplerate(_cur_samplerate); #endif - // MathStack - BOOST_FOREACH(const boost::shared_ptr m, _math_traces) - m->get_math_stack()->set_samplerate(_cur_samplerate); + // SpectrumStack + BOOST_FOREACH(const boost::shared_ptr m, _spectrum_traces) + m->get_spectrum_stack()->set_samplerate(_cur_samplerate); cur_samplerate_changed(); } @@ -328,11 +349,11 @@ void SigSession::capture_init() assert(s); boost::shared_ptr dsoSig; if ((dsoSig = dynamic_pointer_cast(s))) { - dsoSig->set_zero_vrate(dsoSig->get_zero_vrate(), true); + dsoSig->set_zero_ratio(dsoSig->get_zero_ratio()); } boost::shared_ptr analogSig; if ((analogSig = dynamic_pointer_cast(s))) { - analogSig->set_zero_vrate(analogSig->get_zero_vrate(), true); + analogSig->set_zero_ratio(analogSig->get_zero_ratio()); } } } @@ -355,13 +376,16 @@ void SigSession::container_init() if (_dso_data) _dso_data->init(); - // MathStack - BOOST_FOREACH(const boost::shared_ptr m, _math_traces) + // SpectrumStack + BOOST_FOREACH(const boost::shared_ptr m, _spectrum_traces) { assert(m); - m->get_math_stack()->init(); + m->get_spectrum_stack()->init(); } + if (_math_trace) + _math_trace->get_math_stack()->init(); + #ifdef ENABLE_DECODE // DecoderModel //pv::data::DecoderModel *decoder_model = get_decoder_model(); @@ -437,6 +461,7 @@ void SigSession::stop_capture() #endif if (get_capture_state() != Running) return; + sr_session_stop(); // Check that sampling stopped @@ -711,7 +736,9 @@ void SigSession::init_signals() _signals = sigs; } - mathTraces_rebuild(); + spectrum_rebuild(); + lissajous_disable(); + math_disable(); //data_updated(); } @@ -788,7 +815,7 @@ void SigSession::reload() } } - mathTraces_rebuild(); + spectrum_rebuild(); } void SigSession::refresh(int holdtime) @@ -796,7 +823,6 @@ void SigSession::refresh(int holdtime) boost::lock_guard lock(_data_mutex); data_lock(); - QTimer::singleShot(holdtime, this, SLOT(data_unlock())); if (_logic_data) { _logic_data->init(); @@ -811,17 +837,21 @@ void SigSession::refresh(int holdtime) } if (_dso_data) { _dso_data->init(); - // MathStack - BOOST_FOREACH(const boost::shared_ptr m, _math_traces) + // SpectrumStack + BOOST_FOREACH(const boost::shared_ptr m, _spectrum_traces) { assert(m); - m->get_math_stack()->init(); + m->get_spectrum_stack()->init(); } + if (_math_trace) + _math_trace->get_math_stack()->init(); } if (_analog_data) { _analog_data->init(); //_cur_analog_snapshot.reset(); } + + QTimer::singleShot(holdtime, this, SLOT(data_unlock())); //data_updated(); _data_updated = true; } @@ -860,20 +890,6 @@ void SigSession::feed_in_header(const sr_dev_inst *sdi) { (void)sdi; _trigger_pos = 0; - _trigger_time = QDateTime::currentDateTime(); - const int64_t secs = -cur_sampletime(); - _trigger_time = _trigger_time.addSecs(secs); - - if (_dev_inst->name() == "virtual-session") { - int64_t time; - GVariant* gvar = _dev_inst->get_config(NULL, NULL, SR_CONF_TRIGGER_TIME); - if (gvar != NULL) { - time = g_variant_get_int64(gvar); - g_variant_unref(gvar); - if (time != 0) - _trigger_time = QDateTime::fromMSecsSinceEpoch(time); - } - } receive_header(); } @@ -905,12 +921,6 @@ void SigSession::feed_in_trigger(const ds_trigger_pos &trigger_pos) _trigger_pos = trigger_pos.real_pos; receive_trigger(_trigger_pos); } - if (_dev_inst->name() != "virtual-session") { - const double time = trigger_pos.real_pos * 1.0 / _cur_samplerate; - _trigger_time = QDateTime::currentDateTime(); - const int64_t secs = time - cur_sampletime(); - _trigger_time = _trigger_time.addSecs(secs); - } } else { int probe_count = 0; int probe_en_count = 0; @@ -951,7 +961,7 @@ void SigSession::feed_in_logic(const sr_datafeed_logic &logic) frame_began(); } else { // Append to the existing data snapshot - _cur_logic_snapshot->append_payload(logic); + _cur_logic_snapshot->append_payload(logic); } if (_cur_logic_snapshot->memory_failed()) { @@ -996,18 +1006,31 @@ void SigSession::feed_in_dso(const sr_datafeed_dso &dso) // Append to the existing data snapshot _cur_dso_snapshot->append_payload(dso); } + + if (dso.num_samples != 0) { + if (_dso_data) + _dso_data->set_samplerate(_dev_inst->get_sample_rate()); + set_dso_feed(true); + } + if (_cur_dso_snapshot->memory_failed()) { _error = Malloc_err; session_error(); return; } - // calculate related math results - BOOST_FOREACH(const boost::shared_ptr m, _math_traces) + // calculate related spectrum results + BOOST_FOREACH(const boost::shared_ptr m, _spectrum_traces) { assert(m); if (m->enabled()) - m->get_math_stack()->calc_fft(); + m->get_spectrum_stack()->calc_fft(); + } + + // calculate related math results + if (_math_trace && _math_trace->enabled()) { + _math_trace->get_math_stack()->realloc(_dev_inst->get_sample_limit()); + _math_trace->get_math_stack()->calc_math(); } _trigger_flag = dso.trig_flag; @@ -1030,6 +1053,16 @@ void SigSession::feed_in_analog(const sr_datafeed_analog &analog) if (_cur_analog_snapshot->last_ended()) { + // reset scale of analog signal + BOOST_FOREACH(const boost::shared_ptr s, _signals) + { + assert(s); + boost::shared_ptr analogSig; + if ((analogSig = dynamic_pointer_cast(s))) { + analogSig->set_scale(analogSig->get_totalHeight()); + } + } + // first payload _cur_analog_snapshot->first_payload(analog, _dev_inst->get_sample_limit(), _dev_inst->dev_inst()->channels); } else { @@ -1421,7 +1454,7 @@ pv::data::DecoderModel* SigSession::get_decoder_model() const } #endif -void SigSession::mathTraces_rebuild() +void SigSession::spectrum_rebuild() { bool has_dso_signal = false; BOOST_FOREACH(const boost::shared_ptr s, _signals) { @@ -1429,31 +1462,82 @@ void SigSession::mathTraces_rebuild() if ((dsoSig = dynamic_pointer_cast(s))) { has_dso_signal = true; // check already have - std::vector< boost::shared_ptr >::iterator iter = _math_traces.begin(); - for(unsigned int i = 0; i < _math_traces.size(); i++, iter++) + std::vector< boost::shared_ptr >::iterator iter = _spectrum_traces.begin(); + for(unsigned int i = 0; i < _spectrum_traces.size(); i++, iter++) if ((*iter)->get_index() == dsoSig->get_index()) break; // if not, rebuild - if (iter == _math_traces.end()) { - boost::shared_ptr math_stack( - new data::MathStack(*this, dsoSig->get_index())); - boost::shared_ptr math_trace( - new view::MathTrace(*this, math_stack, dsoSig->get_index())); - _math_traces.push_back(math_trace); + if (iter == _spectrum_traces.end()) { + boost::shared_ptr spectrum_stack( + new data::SpectrumStack(*this, dsoSig->get_index())); + boost::shared_ptr spectrum_trace( + new view::SpectrumTrace(*this, spectrum_stack, dsoSig->get_index())); + _spectrum_traces.push_back(spectrum_trace); } } } if (!has_dso_signal) - _math_traces.clear(); + _spectrum_traces.clear(); signals_changed(); } -vector< boost::shared_ptr > SigSession::get_math_signals() +vector< boost::shared_ptr > SigSession::get_spectrum_traces() { //lock_guard lock(_signals_mutex); - return _math_traces; + return _spectrum_traces; +} + +void SigSession::lissajous_rebuild(bool enable, int xindex, int yindex, double percent) +{ + _lissajous_trace.reset(new view::LissajousTrace(enable, _dso_data, xindex, yindex, percent)); + signals_changed(); +} + +void SigSession::lissajous_disable() +{ + if (_lissajous_trace) + _lissajous_trace->set_enable(false); +} + +boost::shared_ptr SigSession::get_lissajous_trace() +{ + //lock_guard lock(_signals_mutex); + return _lissajous_trace; +} + +void SigSession::math_rebuild(bool enable, + boost::shared_ptr dsoSig1, + boost::shared_ptr dsoSig2, + data::MathStack::MathType type) +{ + boost::lock_guard lock(_data_mutex); + boost::shared_ptr math_stack( + new data::MathStack(*this, dsoSig1, dsoSig2, type)); + _math_trace.reset(new view::MathTrace(enable, math_stack, dsoSig1, dsoSig2)); + if (_math_trace && _math_trace->enabled()) { + _math_trace->get_math_stack()->realloc(_dev_inst->get_sample_limit()); + _math_trace->get_math_stack()->calc_math(); + } + signals_changed(); +} + +void SigSession::math_disable() +{ + if (_math_trace) + _math_trace->set_enable(false); +} + +boost::shared_ptr SigSession::get_math_trace() +{ + //lock_guard lock(_signals_mutex); + return _math_trace; +} + +void SigSession::set_trigger_time(QDateTime time) +{ + _trigger_time = time; } QDateTime SigSession::get_trigger_time() const @@ -1594,4 +1678,44 @@ int SigSession::get_map_zoom() const return _map_zoom; } +void SigSession::auto_end() +{ + BOOST_FOREACH(const boost::shared_ptr s, _signals) { + boost::shared_ptr dsoSig; + if ((dsoSig = dynamic_pointer_cast(s))) { + dsoSig->auto_end(); + } + } +} + +void SigSession::set_save_start(uint64_t start) +{ + _save_start = start; +} + +void SigSession::set_save_end(uint64_t end) +{ + _save_end = end; +} + +uint64_t SigSession::get_save_start() const +{ + return _save_start; +} + +uint64_t SigSession::get_save_end() const +{ + return _save_end; +} + +bool SigSession::dso_feed() const +{ + return _dso_feed; +} + +void SigSession::set_dso_feed(bool feed) +{ + _dso_feed = feed; +} + } // namespace pv diff --git a/DSView/pv/sigsession.h b/DSView/pv/sigsession.h old mode 100644 new mode 100755 index a22d020c..9295d759 --- a/DSView/pv/sigsession.h +++ b/DSView/pv/sigsession.h @@ -49,6 +49,9 @@ #include #include +#include "view/mathtrace.h" +#include "data/mathstack.h" + struct srd_decoder; struct srd_channel; @@ -68,6 +71,7 @@ class LogicSnapshot; class Group; class GroupSnapshot; class DecoderModel; +class MathStack; } namespace device { @@ -78,6 +82,8 @@ namespace view { class Signal; class GroupSignal; class DecodeTrace; +class SpectrumTrace; +class LissajousTrace; class MathTrace; } @@ -131,11 +137,11 @@ public: /** * Sets device instance that will be used in the next capture session. */ - void set_device(boost::shared_ptr dev_inst) - throw(QString); + void set_device(boost::shared_ptr dev_inst); - void set_file(QString name) - throw(QString); + void set_file(QString name); + + void close_file(boost::shared_ptr dev_inst); void set_default_device(boost::function error_handler); @@ -148,6 +154,7 @@ public: double cur_sampletime() const; void set_cur_samplerate(uint64_t samplerate); void set_cur_samplelimits(uint64_t samplelimits); + void set_trigger_time(QDateTime time); QDateTime get_trigger_time() const; uint64_t get_trigger_pos() const; @@ -182,8 +189,14 @@ public: pv::data::DecoderModel* get_decoder_model() const; #endif - std::vector< boost::shared_ptr > - get_math_signals(); + std::vector< boost::shared_ptr > + get_spectrum_traces(); + + boost::shared_ptr + get_lissajous_trace(); + + boost::shared_ptr + get_math_trace(); void init_signals(); @@ -204,7 +217,14 @@ public: void data_auto_lock(int lock); void data_auto_unlock(); bool get_data_auto_lock(); - void mathTraces_rebuild(); + void spectrum_rebuild(); + void lissajous_rebuild(bool enable, int xindex, int yindex, double percent); + void lissajous_disable(); + void math_rebuild(bool enable, + boost::shared_ptr dsoSig1, + boost::shared_ptr dsoSig2, + data::MathStack::MathType type); + void math_disable(); bool trigd() const; @@ -225,6 +245,14 @@ public: int get_map_zoom() const; + void set_save_start(uint64_t start); + void set_save_end(uint64_t end); + uint64_t get_save_start() const; + uint64_t get_save_end() const; + + bool dso_feed() const; + void set_dso_feed(bool feed); + private: void set_capture_state(capture_state state); @@ -286,7 +314,9 @@ private: std::vector< boost::shared_ptr > _decode_traces; pv::data::DecoderModel *_decoder_model; #endif - std::vector< boost::shared_ptr > _math_traces; + std::vector< boost::shared_ptr > _spectrum_traces; + boost::shared_ptr _lissajous_trace; + boost::shared_ptr _math_trace; mutable boost::mutex _data_mutex; boost::shared_ptr _logic_data; @@ -327,6 +357,11 @@ private: int _map_zoom; + uint64_t _save_start; + uint64_t _save_end; + + bool _dso_feed; + signals: void capture_state_changed(int state); @@ -380,6 +415,8 @@ public slots: // repeat void set_repeating(bool repeat); void set_map_zoom(int index); + // OSC auto + void auto_end(); private slots: void data_lock(); diff --git a/DSView/pv/storesession.cpp b/DSView/pv/storesession.cpp old mode 100644 new mode 100755 index 9c359109..e3834402 --- a/DSView/pv/storesession.cpp +++ b/DSView/pv/storesession.cpp @@ -42,6 +42,7 @@ #include +#include #include using boost::dynamic_pointer_cast; @@ -115,7 +116,7 @@ QList StoreSession::getSuportedExportFormats(){ return list; } -bool StoreSession::save_start() +bool StoreSession::save_start(QString session_file) { std::set type_set; BOOST_FOREACH(const boost::shared_ptr sig, _session.get_signals()) { @@ -141,7 +142,7 @@ bool StoreSession::save_start() } const QString DIR_KEY("SavePath"); - QSettings settings; + QSettings settings(QApplication::organizationName(), QApplication::applicationName()); // Show the dialog _file_name = QFileDialog::getSaveFileName( @@ -167,7 +168,8 @@ bool StoreSession::save_start() } else { int ret = sr_session_save_init(_file_name.toLocal8Bit().data(), meta_file.toLocal8Bit().data(), - decoders_file.toLocal8Bit().data()); + decoders_file.toLocal8Bit().data(), + session_file.toLocal8Bit().data()); if (ret != SR_OK) { _error = tr("Failed to create zip file. Please check write permission of this path."); return false; @@ -186,6 +188,7 @@ void StoreSession::save_proc(shared_ptr snapshot) { assert(snapshot); + int ret = SR_ERR; shared_ptr logic_snapshot; shared_ptr analog_snapshot; shared_ptr dso_snapshot; @@ -214,13 +217,21 @@ void StoreSession::save_proc(shared_ptr snapshot) buf = (uint8_t *)malloc(size); if (buf == NULL) { _has_error = true; - _error = tr("Malloc failed."); - return; + _error = tr("Failed to create zip file. Malloc error."); + } else { + memset(buf, sample ? 0xff : 0x0, size); } - memset(buf, sample ? 0xff : 0x0, size); } - sr_session_append(_file_name.toLocal8Bit().data(), buf, size, + ret = sr_session_append(_file_name.toLocal8Bit().data(), buf, size, i, ch_index, ch_type, File_Version); + if (ret != SR_OK) { + if (!_has_error) { + _has_error = true; + _error = tr("Failed to create zip file. Please check write permission of this path."); + } + progress_updated(); + return; + } _units_stored += size; if (need_malloc) free(buf); @@ -250,26 +261,34 @@ void StoreSession::save_proc(shared_ptr snapshot) uint8_t *tmp = (uint8_t *)malloc(size); if (tmp == NULL) { _has_error = true; - _error = tr("Malloc failed."); - return; + _error = tr("Failed to create zip file. Malloc error."); + } else { + memcpy(tmp, buf, buf_end-buf); + memcpy(tmp+(buf_end-buf), buf_start, buf+size-buf_end); } - memcpy(tmp, buf, buf_end-buf); - memcpy(tmp+(buf_end-buf), buf_start, buf+size-buf_end); - sr_session_append(_file_name.toLocal8Bit().data(), tmp, size, + ret = sr_session_append(_file_name.toLocal8Bit().data(), tmp, size, i, 0, ch_type, File_Version); buf += (size - _unit_count); - free(tmp); + if (tmp) + free(tmp); } else { - sr_session_append(_file_name.toLocal8Bit().data(), buf, size, + ret = sr_session_append(_file_name.toLocal8Bit().data(), buf, size, i, 0, ch_type, File_Version); buf += size; } + if (ret != SR_OK) { + if (!_has_error) { + _has_error = true; + _error = tr("Failed to create zip file. Please check write permission of this path."); + } + progress_updated(); + return; + } _units_stored += size; progress_updated(); } } } - progress_updated(); } @@ -306,14 +325,9 @@ QString StoreSession::meta_gen(boost::shared_ptr snapshot) } fprintf(meta, "[version]\n"); -// if (sdi->mode != LOGIC) -// fprintf(meta, "version = %d\n", 1); // should be updated in next version -// else -// fprintf(meta, "version = %d\n", File_Version); fprintf(meta, "version = %d\n", File_Version); /* metadata */ - fprintf(meta, "[header]\n"); if (sdi->driver) { fprintf(meta, "driver = %s\n", sdi->driver->name); @@ -357,6 +371,18 @@ QString StoreSession::meta_gen(boost::shared_ptr snapshot) fprintf(meta, "bits = %d\n", tmp_u8); g_variant_unref(gvar); } + gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_REF_MIN); + if (gvar != NULL) { + uint32_t tmp_u32 = g_variant_get_uint32(gvar); + fprintf(meta, "ref min = %d\n", tmp_u32); + g_variant_unref(gvar); + } + gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_REF_MAX); + if (gvar != NULL) { + uint32_t tmp_u32 = g_variant_get_uint32(gvar); + fprintf(meta, "ref max = %d\n", tmp_u32); + g_variant_unref(gvar); + } } else if (sdi->mode == LOGIC) { fprintf(meta, "trigger time = %lld\n", _session.get_trigger_time().toMSecsSinceEpoch()); } else if (sdi->mode == ANALOG) { @@ -365,6 +391,18 @@ QString StoreSession::meta_gen(boost::shared_ptr snapshot) uint8_t tmp_u8 = analog_snapshot->get_unit_bytes(); fprintf(meta, "bits = %d\n", tmp_u8*8); } + gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_REF_MIN); + if (gvar != NULL) { + uint32_t tmp_u32 = g_variant_get_uint32(gvar); + fprintf(meta, "ref min = %d\n", tmp_u32); + g_variant_unref(gvar); + } + gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_REF_MAX); + if (gvar != NULL) { + uint32_t tmp_u32 = g_variant_get_uint32(gvar); + fprintf(meta, "ref max = %d\n", tmp_u32); + g_variant_unref(gvar); + } } fprintf(meta, "trigger pos = %" PRIu64 "\n", _session.get_trigger_pos()); @@ -372,6 +410,9 @@ QString StoreSession::meta_gen(boost::shared_ptr snapshot) for (l = sdi->channels; l; l = l->next) { probe = (struct sr_channel *)l->data; if (snapshot->has_data(probe->index)) { + if (sdi->mode == LOGIC && !probe->enabled) + continue; + if (probe->name) fprintf(meta, "probe%d = %s\n", (sdi->mode == LOGIC) ? probe->index : probecnt, probe->name); if (probe->trigger) @@ -380,27 +421,47 @@ QString StoreSession::meta_gen(boost::shared_ptr snapshot) fprintf(meta, " enable%d = %d\n", probecnt, probe->enabled); fprintf(meta, " coupling%d = %d\n", probecnt, probe->coupling); fprintf(meta, " vDiv%d = %" PRIu64 "\n", probecnt, probe->vdiv); - fprintf(meta, " vFactor%d = %d\n", probecnt, probe->vfactor); - fprintf(meta, " vPos%d = %lf\n", probecnt, probe->vpos); + fprintf(meta, " vFactor%d = %" PRIu64 "\n", probecnt, probe->vfactor); + fprintf(meta, " vOffset%d = %d\n", probecnt, probe->hw_offset); fprintf(meta, " vTrig%d = %d\n", probecnt, probe->trig_value); if (sr_status_get(sdi, &status, false, 0, 0) == SR_OK) { if (probe->index == 0) { - fprintf(meta, " period%d = %" PRIu64 "\n", probecnt, status.ch0_period); - fprintf(meta, " pcnt%d = %" PRIu32 "\n", probecnt, status.ch0_pcnt); + fprintf(meta, " period%d = %" PRIu32 "\n", probecnt, status.ch0_cyc_tlen); + fprintf(meta, " pcnt%d = %" PRIu32 "\n", probecnt, status.ch0_cyc_cnt); fprintf(meta, " max%d = %d\n", probecnt, status.ch0_max); fprintf(meta, " min%d = %d\n", probecnt, status.ch0_min); + fprintf(meta, " plen%d = %" PRIu32 "\n", probecnt, status.ch0_cyc_plen); + fprintf(meta, " llen%d = %" PRIu32 "\n", probecnt, status.ch0_cyc_llen); + fprintf(meta, " level%d = %d\n", probecnt, status.ch0_level_valid); + fprintf(meta, " plevel%d = %d\n", probecnt, status.ch0_plevel); + fprintf(meta, " low%d = %" PRIu32 "\n", probecnt, status.ch0_low_level); + fprintf(meta, " high%d = %" PRIu32 "\n", probecnt, status.ch0_high_level); + fprintf(meta, " rlen%d = %" PRIu32 "\n", probecnt, status.ch0_cyc_rlen); + fprintf(meta, " flen%d = %" PRIu32 "\n", probecnt, status.ch0_cyc_flen); + fprintf(meta, " rms%d = %" PRIu64 "\n", probecnt, status.ch0_acc_square); + fprintf(meta, " mean%d = %" PRIu32 "\n", probecnt, status.ch0_acc_mean); } else { - fprintf(meta, " period%d = %" PRIu64 "\n", probecnt, status.ch1_period); - fprintf(meta, " pcnt%d = %" PRIu32 "\n", probecnt, status.ch1_pcnt); + fprintf(meta, " period%d = %" PRIu32 "\n", probecnt, status.ch1_cyc_tlen); + fprintf(meta, " pcnt%d = %" PRIu32 "\n", probecnt, status.ch1_cyc_cnt); fprintf(meta, " max%d = %d\n", probecnt, status.ch1_max); fprintf(meta, " min%d = %d\n", probecnt, status.ch1_min); + fprintf(meta, " plen%d = %" PRIu32 "\n", probecnt, status.ch1_cyc_plen); + fprintf(meta, " llen%d = %" PRIu32 "\n", probecnt, status.ch1_cyc_llen); + fprintf(meta, " level%d = %d\n", probecnt, status.ch1_level_valid); + fprintf(meta, " plevel%d = %d\n", probecnt, status.ch1_plevel); + fprintf(meta, " low%d = %" PRIu32 "\n", probecnt, status.ch1_low_level); + fprintf(meta, " high%d = %" PRIu32 "\n", probecnt, status.ch1_high_level); + fprintf(meta, " rlen%d = %" PRIu32 "\n", probecnt, status.ch1_cyc_rlen); + fprintf(meta, " flen%d = %" PRIu32 "\n", probecnt, status.ch1_cyc_flen); + fprintf(meta, " rms%d = %" PRIu64 "\n", probecnt, status.ch1_acc_square); + fprintf(meta, " mean%d = %" PRIu32 "\n", probecnt, status.ch1_acc_mean); } } } else if (sdi->mode == ANALOG) { fprintf(meta, " enable%d = %d\n", probecnt, probe->enabled); fprintf(meta, " coupling%d = %d\n", probecnt, probe->coupling); fprintf(meta, " vDiv%d = %" PRIu64 "\n", probecnt, probe->vdiv); - fprintf(meta, " vPos%d = %lf\n", probecnt, probe->vpos); + fprintf(meta, " vOffset%d = %d\n", probecnt, probe->hw_offset); fprintf(meta, " mapUnit%d = %s\n", probecnt, probe->map_unit); fprintf(meta, " mapMax%d = %lf\n", probecnt, probe->map_max); fprintf(meta, " mapMin%d = %lf\n", probecnt, probe->map_min); @@ -440,7 +501,7 @@ bool StoreSession::export_start() } const QString DIR_KEY("ExportPath"); - QSettings settings; + QSettings settings(QApplication::organizationName(), QApplication::applicationName()); // Show the dialog QList supportedFormats = getSuportedExportFormats(); @@ -521,7 +582,7 @@ void StoreSession::export_proc(shared_ptr snapshot) file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out.setCodec("UTF-8"); - out.setGenerateByteOrderMark(true); + //out.setGenerateByteOrderMark(true); // UTF-8 without BOM // Meta GString *data_out; @@ -664,7 +725,8 @@ void StoreSession::export_proc(shared_ptr snapshot) file.close(); _outModule->cleanup(&output); g_hash_table_destroy(params); - g_variant_unref(filenameGVariant); + if (filenameGVariant != NULL) + g_variant_unref(filenameGVariant); progress_updated(); } @@ -689,7 +751,7 @@ QString StoreSession::decoders_gen() } QTextStream outStream(&sessionFile); outStream.setCodec("UTF-8"); - outStream.setGenerateByteOrderMark(true); + //outStream.setGenerateByteOrderMark(true); // UTF-8 without BOM QJsonArray dec_array = json_decoders(); QJsonDocument sessionDoc(dec_array); @@ -733,32 +795,24 @@ QJsonArray StoreSession::json_decoders() const srd_decoder_option *const opt = (srd_decoder_option*)l->data; - const std::map& options = dec->options(); - std::map::const_iterator iter = options.find(opt->id); - if (opt->values) { - for (GSList *vl = opt->values; vl; vl = vl->next) { - GVariant *const var = (GVariant*)vl->data; - assert(var); - if (iter == options.end()) { - options_obj[opt->id] = QJsonValue::fromVariant(dec_binding->print_gvariant(opt->def)); - break; - } else if (g_variant_compare((*iter).second, var) == 0) { - options_obj[opt->id] = QJsonValue::fromVariant(dec_binding->print_gvariant(var)); - break; - } - } - } else if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("d"))) { + if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("d"))) { GVariant *const var = dec_binding->getter(opt->id); - options_obj[opt->id] = QJsonValue::fromVariant(g_variant_get_double(var)); - g_variant_unref(var); + if (var != NULL) { + options_obj[opt->id] = QJsonValue::fromVariant(g_variant_get_double(var)); + g_variant_unref(var); + } } else if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("x"))) { GVariant *const var = dec_binding->getter(opt->id); - options_obj[opt->id] = QJsonValue::fromVariant(get_double(var)); - g_variant_unref(var); + if (var != NULL) { + options_obj[opt->id] = QJsonValue::fromVariant(get_integer(var)); + g_variant_unref(var); + } } else if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("s"))) { GVariant *const var = dec_binding->getter(opt->id); - options_obj[opt->id] = QJsonValue::fromVariant(g_variant_get_string(var, NULL)); - g_variant_unref(var); + if (var != NULL) { + options_obj[opt->id] = QJsonValue::fromVariant(g_variant_get_string(var, NULL)); + g_variant_unref(var); + } }else { continue; } @@ -879,43 +933,31 @@ void StoreSession::load_decoders(dock::ProtocolDock *widget, QJsonArray dec_arra const srd_decoder_option *const opt = (srd_decoder_option*)l->data; if (options_obj.contains(opt->id)) { - if (opt->values) { - QString enum_option = options_obj[opt->id].toString(); - for (GSList *vl = opt->values; vl; vl = vl->next) { - GVariant *const var = (GVariant*)vl->data; - assert(var); - if (enum_option == QString::fromUtf8(g_variant_get_string(var, NULL))) { - dec->set_option(opt->id, var); - break; - } - } - } else if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("d"))) { - double d_option = options_obj[opt->id].toDouble(); - dec->set_option(opt->id, g_variant_new_double(d_option)); + GVariant *new_value = NULL; + if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("d"))) { + new_value = g_variant_new_double(options_obj[opt->id].toDouble()); } else if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("x"))) { - int64_t d_option = options_obj[opt->id].toDouble(); - GVariant *new_value = NULL; const GVariantType *const type = g_variant_get_type(opt->def); if (g_variant_type_equal(type, G_VARIANT_TYPE_BYTE)) - new_value = g_variant_new_byte(d_option); + new_value = g_variant_new_byte(options_obj[opt->id].toInt()); else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT16)) - new_value = g_variant_new_int16(d_option); + new_value = g_variant_new_int16(options_obj[opt->id].toInt()); else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT16)) - new_value = g_variant_new_uint16(d_option); + new_value = g_variant_new_uint16(options_obj[opt->id].toInt()); else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT32)) - new_value = g_variant_new_int32(d_option); + new_value = g_variant_new_int32(options_obj[opt->id].toInt()); else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT32)) - new_value = g_variant_new_int32(d_option); + new_value = g_variant_new_uint32(options_obj[opt->id].toInt()); else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT64)) - new_value = g_variant_new_int64(d_option); + new_value = g_variant_new_int64(options_obj[opt->id].toInt()); else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT64)) - new_value = g_variant_new_uint64(d_option); - if (new_value != NULL) - dec->set_option(opt->id, new_value); + new_value = g_variant_new_uint64(options_obj[opt->id].toInt()); } else if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("s"))) { - QString s_option = options_obj[opt->id].toString(); - dec->set_option(opt->id, g_variant_new_string(s_option.toLocal8Bit().data())); + new_value = g_variant_new_string(options_obj[opt->id].toString().toLocal8Bit().data()); } + + if (new_value != NULL) + dec->set_option(opt->id, new_value); } } dec->commit(); @@ -944,7 +986,7 @@ void StoreSession::load_decoders(dock::ProtocolDock *widget, QJsonArray dec_arra } #endif -double StoreSession::get_double(GVariant *var) +double StoreSession::get_integer(GVariant *var) { double val = 0; const GVariantType *const type = g_variant_get_type(var); @@ -970,4 +1012,5 @@ double StoreSession::get_double(GVariant *var) return val; } + } // pv diff --git a/DSView/pv/storesession.h b/DSView/pv/storesession.h old mode 100644 new mode 100755 index 04911dd6..6c568794 --- a/DSView/pv/storesession.h +++ b/DSView/pv/storesession.h @@ -60,7 +60,7 @@ public: const QString& error() const; - bool save_start(); + bool save_start(QString session_file); bool export_start(); @@ -84,7 +84,7 @@ public: private: QList getSuportedExportFormats(); - double get_double(GVariant * var); + double get_integer(GVariant * var); signals: void progress_updated(); diff --git a/DSView/pv/toolbars/filebar.cpp b/DSView/pv/toolbars/filebar.cpp old mode 100644 new mode 100755 index 6b3b8f00..8da52bcb --- a/DSView/pv/toolbars/filebar.cpp +++ b/DSView/pv/toolbars/filebar.cpp @@ -1,229 +1,252 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include - -#include -#include -#include - -#include "filebar.h" -#include "../device/devinst.h" -#include "../dialogs/dsmessagebox.h" - -#include - -namespace pv { -namespace toolbars { - -FileBar::FileBar(SigSession &session, QWidget *parent) : - QToolBar("File Bar", parent), - _enable(true), - _session(session), - _file_button(this) -{ - setMovable(false); - - _action_load = new QAction(this); - _action_load->setText(QApplication::translate( - "File", "&Load...", 0)); - _action_load->setIcon(QIcon::fromTheme("file", - QIcon(":/icons/open.png"))); - _action_load->setObjectName(QString::fromUtf8("actionLoad")); - connect(_action_load, SIGNAL(triggered()), this, SLOT(on_actionLoad_triggered())); - - _action_store = new QAction(this); - _action_store->setText(QApplication::translate( - "File", "S&tore...", 0)); - _action_store->setIcon(QIcon::fromTheme("file", - QIcon(":/icons/save.png"))); - _action_store->setObjectName(QString::fromUtf8("actionStore")); - connect(_action_store, SIGNAL(triggered()), this, SLOT(on_actionStore_triggered())); - - _action_default = new QAction(this); - _action_default->setText(QApplication::translate( - "File", "&Default...", 0)); - _action_default->setIcon(QIcon::fromTheme("file", - QIcon(":/icons/gear.png"))); - _action_default->setObjectName(QString::fromUtf8("actionDefault")); - connect(_action_default, SIGNAL(triggered()), this, SLOT(on_actionDefault_triggered())); - - _menu_session = new QMenu(tr("Settings"), parent); - _menu_session->setIcon(QIcon::fromTheme("file", - QIcon(":/icons/gear.png"))); - _menu_session->setObjectName(QString::fromUtf8("menuSession")); - _menu_session->addAction(_action_load); - _menu_session->addAction(_action_store); - _menu_session->addAction(_action_default); - - _action_open = new QAction(this); - _action_open->setText(QApplication::translate( - "File", "&Open...", 0)); - _action_open->setIcon(QIcon::fromTheme("file", - QIcon(":/icons/open.png"))); - _action_open->setObjectName(QString::fromUtf8("actionOpen")); - connect(_action_open, SIGNAL(triggered()), this, SLOT(on_actionOpen_triggered())); - - _action_save = new QAction(this); - _action_save->setText(QApplication::translate( - "File", "&Save...", 0)); - _action_save->setIcon(QIcon::fromTheme("file", - QIcon(":/icons/save.png"))); - _action_save->setObjectName(QString::fromUtf8("actionSave")); - connect(_action_save, SIGNAL(triggered()), this, SIGNAL(on_save())); - - _action_export = new QAction(this); - _action_export->setText(QApplication::translate("File", "&Export...", 0)); - _action_export->setIcon(QIcon::fromTheme("file",QIcon(":/icons/export.png"))); - _action_export->setObjectName(QString::fromUtf8("actionExport")); - connect(_action_export, SIGNAL(triggered()), this, SIGNAL(on_export())); - - - _action_capture = new QAction(this); - _action_capture->setText(QApplication::translate( - "File", "&Capture...", 0)); - _action_capture->setIcon(QIcon::fromTheme("file", - QIcon(":/icons/capture.png"))); - _action_capture->setObjectName(QString::fromUtf8("actionCapture")); - connect(_action_capture, SIGNAL(triggered()), this, SLOT(on_actionCapture_triggered())); - - _file_button.setPopupMode(QToolButton::InstantPopup); - _file_button.setIcon(QIcon(":/icons/file.png")); - - _menu = new QMenu(this); - _menu->addMenu(_menu_session); - _menu->addAction(_action_open); - _menu->addAction(_action_save); - _menu->addAction(_action_export); - _menu->addAction(_action_capture); - _file_button.setMenu(_menu); - addWidget(&_file_button); -} - -void FileBar::on_actionOpen_triggered() -{ - const QString DIR_KEY("OpenPath"); - QSettings settings; - // Show the dialog - const QString file_name = QFileDialog::getOpenFileName( - this, tr("Open File"), settings.value(DIR_KEY).toString(), tr( - "DSView Data (*.dsl)")); - if (!file_name.isEmpty()) { - QDir CurrentDir; - settings.setValue(DIR_KEY, CurrentDir.absoluteFilePath(file_name)); - load_file(file_name); - } -} - -void FileBar::session_error( - const QString text, const QString info_text) -{ - QMetaObject::invokeMethod(this, "show_session_error", - Qt::QueuedConnection, Q_ARG(QString, text), - Q_ARG(QString, info_text)); -} - -void FileBar::show_session_error( - const QString text, const QString info_text) -{ - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(text); - msg.mBox()->setInformativeText(info_text); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); -} - -void FileBar::on_actionLoad_triggered() -{ - const QString DIR_KEY("SessionLoadPath"); - QSettings settings; - // Show the dialog - const QString file_name = QFileDialog::getOpenFileName( - this, tr("Open Session"), settings.value(DIR_KEY).toString(), tr( - "DSView Session (*.dsc)")); - if (!file_name.isEmpty()) { - QDir CurrentDir; - settings.setValue(DIR_KEY, CurrentDir.absoluteFilePath(file_name)); - load_session(file_name); - } -} - -void FileBar::on_actionDefault_triggered() -{ - QDir dir(DS_RES_PATH); - if (!dir.exists()) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("Session Load")); - msg.mBox()->setInformativeText(tr("Cannot find default session file for this device!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); - return; - } - - QString driver_name = _session.get_device()->name(); - QString mode_name = QString::number(_session.get_device()->dev_inst()->mode); - QString file_name = dir.absolutePath() + "/" + driver_name + mode_name + ".def.dsc"; - if (!file_name.isEmpty()) - load_session(file_name); -} - -void FileBar::on_actionStore_triggered() -{ - const QString DIR_KEY("SessionStorePath"); - QSettings settings; - QString file_name = QFileDialog::getSaveFileName( - this, tr("Save Session"), settings.value(DIR_KEY).toString(), - tr("DSView Session (*.dsc)")); - if (!file_name.isEmpty()) { - QFileInfo f(file_name); - if(f.suffix().compare("dsc")) - file_name.append(tr(".dsc")); - QDir CurrentDir; - settings.setValue(DIR_KEY, CurrentDir.absoluteFilePath(file_name)); - store_session(file_name); - } -} - -void FileBar::on_actionCapture_triggered() -{ - _file_button.close(); - QCoreApplication::sendPostedEvents(); - QTimer::singleShot(100, this, SIGNAL(on_screenShot())); -} - -void FileBar::enable_toggle(bool enable) -{ - _file_button.setDisabled(!enable); - _file_button.setIcon(enable ? QIcon(":/icons/file.png") : - QIcon(":/icons/file_dis.png")); -} - -void FileBar::set_settings_en(bool enable) -{ - _menu_session->setDisabled(!enable); -} - -} // namespace toolbars -} // namespace pv +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include + +#include +#include +#include + +#include "filebar.h" +#include "../device/devinst.h" +#include "../dialogs/dsmessagebox.h" + +#include + +namespace pv { +namespace toolbars { + +FileBar::FileBar(SigSession &session, QWidget *parent) : + QToolBar("File Bar", parent), + _enable(true), + _session(session), + _file_button(this) +{ + setMovable(false); + setContentsMargins(0,0,0,0); + setIconSize(QSize(40, 28)); + + _action_load = new QAction(this); + _action_load->setObjectName(QString::fromUtf8("actionLoad")); + connect(_action_load, SIGNAL(triggered()), this, SLOT(on_actionLoad_triggered())); + + _action_store = new QAction(this); + _action_store->setObjectName(QString::fromUtf8("actionStore")); + connect(_action_store, SIGNAL(triggered()), this, SLOT(on_actionStore_triggered())); + + _action_default = new QAction(this); + _action_default->setObjectName(QString::fromUtf8("actionDefault")); + connect(_action_default, SIGNAL(triggered()), this, SLOT(on_actionDefault_triggered())); + + _menu_session = new QMenu(this); + _menu_session->setObjectName(QString::fromUtf8("menuSession")); + _menu_session->addAction(_action_load); + _menu_session->addAction(_action_store); + _menu_session->addAction(_action_default); + + _action_open = new QAction(this); + _action_open->setObjectName(QString::fromUtf8("actionOpen")); + connect(_action_open, SIGNAL(triggered()), this, SLOT(on_actionOpen_triggered())); + + _action_save = new QAction(this); + _action_save->setObjectName(QString::fromUtf8("actionSave")); + connect(_action_save, SIGNAL(triggered()), this, SIGNAL(on_save())); + + _action_export = new QAction(this); + _action_export->setObjectName(QString::fromUtf8("actionExport")); + connect(_action_export, SIGNAL(triggered()), this, SIGNAL(on_export())); + + + _action_capture = new QAction(this); + _action_capture->setObjectName(QString::fromUtf8("actionCapture")); + connect(_action_capture, SIGNAL(triggered()), this, SLOT(on_actionCapture_triggered())); + + _file_button.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + _file_button.setPopupMode(QToolButton::InstantPopup); + + _menu = new QMenu(this); + _menu->addMenu(_menu_session); + _menu->addAction(_action_open); + _menu->addAction(_action_save); + _menu->addAction(_action_export); + _menu->addAction(_action_capture); + _file_button.setMenu(_menu); + addWidget(&_file_button); + + retranslateUi(); +} + +void FileBar::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) + retranslateUi(); + else if (event->type() == QEvent::StyleChange) + reStyle(); + QToolBar::changeEvent(event); +} + +void FileBar::retranslateUi() +{ + _file_button.setText(tr("File")); + _menu_session->setTitle(tr("Settings")); + _action_load->setText(tr("&Load...")); + _action_store->setText(tr("S&tore...")); + _action_default->setText(tr("&Default...")); + _action_open->setText(tr("&Open...")); + _action_save->setText(tr("&Save...")); + _action_export->setText(tr("&Export...")); + _action_capture->setText(tr("&Capture...")); +} + +void FileBar::reStyle() +{ + QString iconPath = ":/icons/" + qApp->property("Style").toString(); + + _action_load->setIcon(QIcon(iconPath+"/open.png")); + _action_store->setIcon(QIcon(iconPath+"/save.png")); + _action_default->setIcon(QIcon(iconPath+"/gear.png")); + _menu_session->setIcon(QIcon(iconPath+"/gear.png")); + _action_open->setIcon(QIcon(iconPath+"/open.png")); + _action_save->setIcon(QIcon(iconPath+"/save.png")); + _action_export->setIcon(QIcon(iconPath+"/export.png")); + _action_capture->setIcon(QIcon(iconPath+"/capture.png")); + _file_button.setIcon(QIcon(iconPath+"/file.png")); +} + +void FileBar::on_actionOpen_triggered() +{ + const QString DIR_KEY("OpenPath"); + QSettings settings(QApplication::organizationName(), QApplication::applicationName()); + // Show the dialog + const QString file_name = QFileDialog::getOpenFileName( + this, tr("Open File"), settings.value(DIR_KEY).toString(), tr( + "DSView Data (*.dsl)")); + if (!file_name.isEmpty()) { + QDir CurrentDir; + settings.setValue(DIR_KEY, CurrentDir.absoluteFilePath(file_name)); + load_file(file_name); + } +} + +void FileBar::session_error( + const QString text, const QString info_text) +{ + QMetaObject::invokeMethod(this, "show_session_error", + Qt::QueuedConnection, Q_ARG(QString, text), + Q_ARG(QString, info_text)); +} + +void FileBar::show_session_error( + const QString text, const QString info_text) +{ + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(text); + msg.mBox()->setInformativeText(info_text); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); +} + +void FileBar::on_actionLoad_triggered() +{ + const QString DIR_KEY("SessionLoadPath"); + QSettings settings(QApplication::organizationName(), QApplication::applicationName()); + // Show the dialog + const QString file_name = QFileDialog::getOpenFileName( + this, tr("Open Session"), settings.value(DIR_KEY).toString(), tr( + "DSView Session (*.dsc)")); + if (!file_name.isEmpty()) { + QDir CurrentDir; + settings.setValue(DIR_KEY, CurrentDir.absoluteFilePath(file_name)); + load_session(file_name); + } +} + +void FileBar::on_actionDefault_triggered() +{ +#ifdef Q_OS_LINUX + QDir dir(DS_RES_PATH); +#else + QDir dir(QCoreApplication::applicationDirPath()); + assert(dir.cd("res")); +#endif + if (!dir.exists()) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Session Load")); + msg.mBox()->setInformativeText(tr("Cannot find default session file for this device!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + return; + } + + QString driver_name = _session.get_device()->name(); + QString mode_name = QString::number(_session.get_device()->dev_inst()->mode); + int language = QLocale::English; + GVariant *gvar_tmp = _session.get_device()->get_config(NULL, NULL, SR_CONF_LANGUAGE); + if (gvar_tmp != NULL) { + language = g_variant_get_int16(gvar_tmp); + g_variant_unref(gvar_tmp); + } + QString file_name = dir.absolutePath() + "/" + driver_name + mode_name + + ".def"+QString::number(language)+".dsc"; + if (!file_name.isEmpty()) + load_session(file_name); +} + +void FileBar::on_actionStore_triggered() +{ + const QString DIR_KEY("SessionStorePath"); + QSettings settings(QApplication::organizationName(), QApplication::applicationName()); + QString file_name = QFileDialog::getSaveFileName( + this, tr("Save Session"), settings.value(DIR_KEY).toString(), + tr("DSView Session (*.dsc)")); + if (!file_name.isEmpty()) { + QFileInfo f(file_name); + if(f.suffix().compare("dsc")) + file_name.append(tr(".dsc")); + QDir CurrentDir; + settings.setValue(DIR_KEY, CurrentDir.absoluteFilePath(file_name)); + store_session(file_name); + } +} + +void FileBar::on_actionCapture_triggered() +{ + _file_button.close(); + QCoreApplication::sendPostedEvents(); + QTimer::singleShot(100, this, SIGNAL(on_screenShot())); +} + +void FileBar::enable_toggle(bool enable) +{ + _file_button.setDisabled(!enable); +} + +void FileBar::set_settings_en(bool enable) +{ + _menu_session->setDisabled(!enable); +} + +} // namespace toolbars +} // namespace pv diff --git a/DSView/pv/toolbars/filebar.h b/DSView/pv/toolbars/filebar.h old mode 100644 new mode 100755 index 0d85b578..3696fab5 --- a/DSView/pv/toolbars/filebar.h +++ b/DSView/pv/toolbars/filebar.h @@ -1,91 +1,94 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#ifndef DSVIEW_PV_TOOLBARS_FILEBAR_H -#define DSVIEW_PV_TOOLBARS_FILEBAR_H - -#include -#include -#include -#include - -#include "../sigsession.h" - -namespace pv { -namespace toolbars { - -class FileBar : public QToolBar -{ - Q_OBJECT - -public: - explicit FileBar(SigSession &session, QWidget *parent = 0); - - void enable_toggle(bool enable); - - void set_settings_en(bool enable); - -private: - - void session_error( - const QString text, const QString info_text); - void show_session_error( - const QString text, const QString info_text); - -signals: - void load_file(QString); - void on_save(); - void on_export(); - void on_screenShot(); - void load_session(QString); - void store_session(QString); - -private slots: - void on_actionLoad_triggered(); - void on_actionStore_triggered(); - void on_actionDefault_triggered(); - void on_actionOpen_triggered(); - void on_actionCapture_triggered(); - -private: - bool _enable; - SigSession& _session; - - QToolButton _file_button; - - QMenu *_menu; - - QMenu *_menu_session; - QAction *_action_load; - QAction *_action_store; - QAction *_action_default; - - QAction *_action_open; - QAction *_action_save; - QAction *_action_export; - QAction *_action_capture; -}; - -} // namespace toolbars -} // namespace pv - -#endif // DSVIEW_PV_TOOLBARS_FILEBAR_H +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef DSVIEW_PV_TOOLBARS_FILEBAR_H +#define DSVIEW_PV_TOOLBARS_FILEBAR_H + +#include +#include +#include +#include + +#include "../sigsession.h" + +namespace pv { +namespace toolbars { + +class FileBar : public QToolBar +{ + Q_OBJECT + +public: + explicit FileBar(SigSession &session, QWidget *parent = 0); + + void enable_toggle(bool enable); + + void set_settings_en(bool enable); + +private: + void changeEvent(QEvent *event); + void retranslateUi(); + void reStyle(); + + void session_error( + const QString text, const QString info_text); + void show_session_error( + const QString text, const QString info_text); + +signals: + void load_file(QString); + void on_save(); + void on_export(); + void on_screenShot(); + void load_session(QString); + void store_session(QString); + +private slots: + void on_actionLoad_triggered(); + void on_actionStore_triggered(); + void on_actionDefault_triggered(); + void on_actionOpen_triggered(); + void on_actionCapture_triggered(); + +private: + bool _enable; + SigSession& _session; + + QToolButton _file_button; + + QMenu *_menu; + + QMenu *_menu_session; + QAction *_action_load; + QAction *_action_store; + QAction *_action_default; + + QAction *_action_open; + QAction *_action_save; + QAction *_action_export; + QAction *_action_capture; +}; + +} // namespace toolbars +} // namespace pv + +#endif // DSVIEW_PV_TOOLBARS_FILEBAR_H diff --git a/DSView/pv/toolbars/logobar.cpp b/DSView/pv/toolbars/logobar.cpp old mode 100644 new mode 100755 index a969ede4..45575ca5 --- a/DSView/pv/toolbars/logobar.cpp +++ b/DSView/pv/toolbars/logobar.cpp @@ -1,139 +1,213 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include - -#include -#include -#include -#include -#include - -#include "logobar.h" -#include "../dialogs/about.h" -#include "../dialogs/dsmessagebox.h" - -namespace pv { -namespace toolbars { - -LogoBar::LogoBar(SigSession &session, QWidget *parent) : - QToolBar("File Bar", parent), - _enable(true), - _session(session), - _logo_button(this) -{ - setMovable(false); - - _about = new QAction(this); - _about->setText(QApplication::translate( - "File", "&About...", 0)); - _about->setIcon(QIcon::fromTheme("file", - QIcon(":/icons/about.png"))); - _about->setObjectName(QString::fromUtf8("actionAbout")); - _logo_button.addAction(_about); - connect(_about, SIGNAL(triggered()), this, SLOT(on_actionAbout_triggered())); - - _manual = new QAction(this); - _manual->setText(QApplication::translate( - "File", "&Manual", 0)); - _manual->setIcon(QIcon::fromTheme("file", - QIcon(":/icons/manual.png"))); - _manual->setObjectName(QString::fromUtf8("actionManual")); - _logo_button.addAction(_manual); - connect(_manual, SIGNAL(triggered()), this, SLOT(on_actionManual_triggered())); - - _issue = new QAction(this); - _issue->setText(QApplication::translate( - "File", "&Bug Report", 0)); - _issue->setIcon(QIcon::fromTheme("file", - QIcon(":/icons/bug.png"))); - _issue->setObjectName(QString::fromUtf8("actionManual")); - _logo_button.addAction(_issue); - connect(_issue, SIGNAL(triggered()), this, SLOT(on_actionIssue_triggered())); - - _logo_button.setPopupMode(QToolButton::InstantPopup); - _logo_button.setIcon(QIcon(":/icons/logo_noColor.png")); - - QWidget *spacer = new QWidget(this); - spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - addWidget(spacer); - addWidget(&_logo_button); - QWidget *margin = new QWidget(this); - margin->setMinimumWidth(20); - addWidget(margin); -} - -void LogoBar::dsl_connected(bool conn) -{ - if (conn) - _logo_button.setIcon(QIcon(":/icons/logo_color.png")); - else - _logo_button.setIcon(QIcon(":/icons/logo_noColor.png")); -} - -void LogoBar::session_error( - const QString text, const QString info_text) -{ - QMetaObject::invokeMethod(this, "show_session_error", - Qt::QueuedConnection, Q_ARG(QString, text), - Q_ARG(QString, info_text)); -} - -void LogoBar::show_session_error( - const QString text, const QString info_text) -{ - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(text); - msg.mBox()->setInformativeText(info_text); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); -} - -void LogoBar::on_actionAbout_triggered() -{ - dialogs::About dlg(this); - dlg.exec(); -} - -void LogoBar::on_actionManual_triggered() -{ - QDir dir(DS_RES_PATH); - dir.cdUp(); - QDesktopServices::openUrl( - QUrl("file:///"+dir.absolutePath() + "/ug.pdf")); -} - -void LogoBar::on_actionIssue_triggered() -{ - QDir dir(QCoreApplication::applicationDirPath()); - QDesktopServices::openUrl( - QUrl(QLatin1String("https://github.com/DreamSourceLab/DSView/issues"))); -} - -void LogoBar::enable_toggle(bool enable) -{ - _logo_button.setDisabled(!enable); -} - -} // namespace toolbars -} // namespace pv +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "logobar.h" +#include "../dialogs/about.h" +#include "../dialogs/dsmessagebox.h" + +namespace pv { +namespace toolbars { + +LogoBar::LogoBar(SigSession &session, QWidget *parent) : + QToolBar("File Bar", parent), + _enable(true), + _connected(false), + _session(session), + _logo_button(this) +{ + setMovable(false); + setContentsMargins(0,0,0,0); + setIconSize(QSize(40, 28)); + + _action_en = new QAction(this); + _action_en->setObjectName(QString::fromUtf8("actionEn")); + connect(_action_en, SIGNAL(triggered()), this, SLOT(on_actionEn_triggered())); + + _action_cn = new QAction(this); + _action_cn->setObjectName(QString::fromUtf8("actionCn")); + connect(_action_cn, SIGNAL(triggered()), this, SLOT(on_actionCn_triggered())); + + _language = new QMenu(this); + _language->setObjectName(QString::fromUtf8("menuLanguage")); + _language->addAction(_action_cn); + _language->addAction(_action_en); + + _action_en->setIcon(QIcon(":/icons/English.png")); + _action_cn->setIcon(QIcon(":/icons/Chinese.png")); + + _about = new QAction(this); + _about->setObjectName(QString::fromUtf8("actionAbout")); + _logo_button.addAction(_about); + connect(_about, SIGNAL(triggered()), this, SLOT(on_actionAbout_triggered())); + + _manual = new QAction(this); + _manual->setObjectName(QString::fromUtf8("actionManual")); + _logo_button.addAction(_manual); + connect(_manual, SIGNAL(triggered()), this, SIGNAL(openDoc())); + + _issue = new QAction(this); + _issue->setObjectName(QString::fromUtf8("actionManual")); + _logo_button.addAction(_issue); + connect(_issue, SIGNAL(triggered()), this, SLOT(on_actionIssue_triggered())); + + _menu = new QMenu(this); + _menu->addMenu(_language); + _menu->addAction(_about); + _menu->addAction(_manual); + _menu->addAction(_issue); + _logo_button.setMenu(_menu); + + _logo_button.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + _logo_button.setPopupMode(QToolButton::InstantPopup); + + QWidget *spacer = new QWidget(this); + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + addWidget(spacer); + addWidget(&_logo_button); + QWidget *margin = new QWidget(this); + margin->setMinimumWidth(20); + addWidget(margin); + + retranslateUi(); +} + +void LogoBar::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) + retranslateUi(); + else if (event->type() == QEvent::StyleChange) + reStyle(); + QToolBar::changeEvent(event); +} + +void LogoBar::retranslateUi() +{ + _logo_button.setText(tr("Help")); + _action_en->setText("English"); + _action_cn->setText("中文"); + _language->setTitle(tr("&Language")); + _about->setText(tr("&About...")); + _manual->setText(tr("&Manual")); + _issue->setText(tr("&Bug Report")); + + if (qApp->property("Language") == QLocale::Chinese) + _language->setIcon(QIcon(":/icons/Chinese.png")); + else + _language->setIcon(QIcon(":/icons/English.png")); +} + +void LogoBar::reStyle() +{ + QString iconPath = ":/icons/" + qApp->property("Style").toString(); + + _about->setIcon(QIcon(iconPath+"/about.png")); + _manual->setIcon(QIcon(iconPath+"/manual.png")); + _issue->setIcon(QIcon(iconPath+"/bug.png")); + if (_connected) + _logo_button.setIcon(QIcon(iconPath+"/logo_color.png")); + else + _logo_button.setIcon(QIcon(iconPath+"/logo_noColor.png")); +} + +void LogoBar::dsl_connected(bool conn) +{ + _connected = conn; + QString iconPath = ":/icons/" + qApp->property("Style").toString(); + if (_connected) + _logo_button.setIcon(QIcon(iconPath+"/logo_color.png")); + else + _logo_button.setIcon(QIcon(iconPath+"/logo_noColor.png")); +} + +void LogoBar::session_error( + const QString text, const QString info_text) +{ + QMetaObject::invokeMethod(this, "show_session_error", + Qt::QueuedConnection, Q_ARG(QString, text), + Q_ARG(QString, info_text)); +} + +void LogoBar::show_session_error( + const QString text, const QString info_text) +{ + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(text); + msg.mBox()->setInformativeText(info_text); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); +} + +void LogoBar::on_actionEn_triggered() +{ + _language->setIcon(QIcon::fromTheme("file", + QIcon(":/icons/English.png"))); + setLanguage(QLocale::English); +} + +void LogoBar::on_actionCn_triggered() +{ + _language->setIcon(QIcon::fromTheme("file", + QIcon(":/icons/Chinese.png"))); + setLanguage(QLocale::Chinese); +} + +void LogoBar::on_actionAbout_triggered() +{ + dialogs::About dlg(this); + dlg.exec(); +} + +void LogoBar::on_actionManual_triggered() +{ + #ifndef Q_OS_LINUX + QDir dir(QCoreApplication::applicationDirPath()); + #else + QDir dir(DS_RES_PATH); + dir.cdUp(); + #endif + QDesktopServices::openUrl( + QUrl("file:///"+dir.absolutePath() + "/ug.pdf")); +} + +void LogoBar::on_actionIssue_triggered() +{ + QDir dir(QCoreApplication::applicationDirPath()); + QDesktopServices::openUrl( + QUrl(QLatin1String("https://github.com/DreamSourceLab/DSView/issues"))); +} + +void LogoBar::enable_toggle(bool enable) +{ + _logo_button.setDisabled(!enable); +} + +} // namespace toolbars +} // namespace pv diff --git a/DSView/pv/toolbars/logobar.h b/DSView/pv/toolbars/logobar.h old mode 100644 new mode 100755 index d1d2f905..caf686c5 --- a/DSView/pv/toolbars/logobar.h +++ b/DSView/pv/toolbars/logobar.h @@ -1,76 +1,91 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#ifndef DSVIEW_PV_TOOLBARS_LOGOBAR_H -#define DSVIEW_PV_TOOLBARS_LOGOBAR_H - -#include -#include -#include - -#include "../sigsession.h" - -#include - -namespace pv { -namespace toolbars { - -class LogoBar : public QToolBar -{ - Q_OBJECT - -public: - explicit LogoBar(SigSession &session, QWidget *parent = 0); - - void enable_toggle(bool enable); - - void dsl_connected(bool conn); - -private: - void session_error( - const QString text, const QString info_text); - void show_session_error( - const QString text, const QString info_text); - -signals: - -private slots: - void on_actionAbout_triggered(); - void on_actionManual_triggered(); - void on_actionIssue_triggered(); - -private: - bool _enable; - SigSession& _session; - - QToolButton _logo_button; - - QAction *_about; - QAction *_manual; - QAction *_issue; - -}; - -} // namespace toolbars -} // namespace pv - -#endif // DSVIEW_PV_TOOLBARS_LOGOBAR_H +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef DSVIEW_PV_TOOLBARS_LOGOBAR_H +#define DSVIEW_PV_TOOLBARS_LOGOBAR_H + +#include +#include +#include +#include + +#include "../sigsession.h" + +#include + +namespace pv { +namespace toolbars { + +class LogoBar : public QToolBar +{ + Q_OBJECT + +public: + explicit LogoBar(SigSession &session, QWidget *parent = 0); + + void enable_toggle(bool enable); + + void dsl_connected(bool conn); + +private: + void changeEvent(QEvent *event); + void retranslateUi(); + void reStyle(); + + void session_error( + const QString text, const QString info_text); + void show_session_error( + const QString text, const QString info_text); + +signals: + void setLanguage(int language); + void openDoc(); + +private slots: + void on_actionEn_triggered(); + void on_actionCn_triggered(); + void on_actionAbout_triggered(); + void on_actionManual_triggered(); + void on_actionIssue_triggered(); + +private: + bool _enable; + bool _connected; + SigSession& _session; + + QToolButton _logo_button; + + QMenu *_menu; + + QMenu *_language; + QAction *_action_en; + QAction *_action_cn; + + QAction *_about; + QAction *_manual; + QAction *_issue; +}; + +} // namespace toolbars +} // namespace pv + +#endif // DSVIEW_PV_TOOLBARS_LOGOBAR_H diff --git a/DSView/pv/toolbars/samplingbar.cpp b/DSView/pv/toolbars/samplingbar.cpp old mode 100644 new mode 100755 index 365244f9..8cebb45b --- a/DSView/pv/toolbars/samplingbar.cpp +++ b/DSView/pv/toolbars/samplingbar.cpp @@ -64,21 +64,14 @@ SamplingBar::SamplingBar(SigSession &session, QWidget *parent) : _sample_rate(this), _updating_sample_rate(false), _updating_sample_count(false), - _icon_stop(":/icons/stop.png"), - _icon_start(":/icons/start.png"), - _icon_instant(":/icons/instant.png"), - _icon_start_dis(":/icons/start_dis.png"), - _icon_instant_dis(":/icons/instant_dis.png"), _run_stop_button(this), _instant_button(this), _mode_button(this), - _icon_repeat(":/icons/moder.png"), - _icon_single(":/icons/modes.png"), - _icon_repeat_dis(":/icons/moder_dis.png"), - _icon_single_dis(":/icons/modes_dis.png"), _instant(false) { setMovable(false); + setContentsMargins(0,0,0,0); + setIconSize(QSize(40, 28)); layout()->setMargin(0); layout()->setSpacing(0); @@ -91,20 +84,13 @@ SamplingBar::SamplingBar(SigSession &session, QWidget *parent) : connect(&_instant_button, SIGNAL(clicked()), this, SLOT(on_instant_stop())); - _configure_button.setIcon(QIcon::fromTheme("configure", - QIcon(":/icons/params.png"))); - _mode_button.setPopupMode(QToolButton::InstantPopup); - _mode_button.setIcon(_session.get_run_mode() == pv::SigSession::Single ? _icon_single : _icon_repeat); - _run_stop_button.setIcon(_icon_start); - _instant_button.setIcon(_icon_instant); _device_selector.setSizeAdjustPolicy(QComboBox::AdjustToContents); _sample_rate.setSizeAdjustPolicy(QComboBox::AdjustToContents); _sample_count.setSizeAdjustPolicy(QComboBox::AdjustToContents); _device_selector.setMaximumWidth(ComboBoxMaxWidth); - set_sampling(false); connect(&_sample_count, SIGNAL(currentIndexChanged(int)), this, SLOT(on_samplecount_sel(int))); @@ -117,32 +103,83 @@ SamplingBar::SamplingBar(SigSession &session, QWidget *parent) : QWidget *leftMargin = new QWidget(this); leftMargin->setFixedWidth(4); addWidget(leftMargin); + addWidget(&_device_selector); + _configure_button.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); addWidget(&_configure_button); + addWidget(&_sample_count); addWidget(new QLabel(tr(" @ "))); addWidget(&_sample_rate); _action_single = new QAction(this); - _action_single->setText(QApplication::translate("Sampling", "&Single", 0)); - _action_single->setIcon(QIcon::fromTheme("Sampling", - QIcon(":/icons/oneloop.png"))); connect(_action_single, SIGNAL(triggered()), this, SLOT(on_mode())); _action_repeat = new QAction(this); - _action_repeat->setText(QApplication::translate("Sampling", "&Repetitive", 0)); - _action_repeat->setIcon(QIcon::fromTheme("Sampling", - QIcon(":/icons/repeat.png"))); connect(_action_repeat, SIGNAL(triggered()), this, SLOT(on_mode())); _mode_menu = new QMenu(this); _mode_menu->addAction(_action_single); _mode_menu->addAction(_action_repeat); _mode_button.setMenu(_mode_menu); + + _mode_button.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); _mode_action = addWidget(&_mode_button); + _run_stop_button.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); _run_stop_action = addWidget(&_run_stop_button); + _instant_button.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); _instant_action = addWidget(&_instant_button); + + set_sampling(false); + //retranslateUi(); +} + +void SamplingBar::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) + retranslateUi(); + else if (event->type() == QEvent::StyleChange) + reStyle(); + QToolBar::changeEvent(event); +} + +void SamplingBar::retranslateUi() +{ + _configure_button.setText(tr("Options")); + _mode_button.setText(tr("Mode")); + if (_instant) { + if (_session.get_device() && + _session.get_device()->dev_inst()->mode == DSO) + _instant_button.setText(_sampling ? tr("Stop") : tr("Single")); + else + _instant_button.setText(_sampling ? tr("Stop") : tr("Instant")); + _run_stop_button.setText(tr("Start")); + } else { + _run_stop_button.setText(_sampling ? tr("Stop") : tr("Start")); + if (_session.get_device() && + _session.get_device()->dev_inst()->mode == DSO) + _instant_button.setText(tr("Single")); + else + _instant_button.setText(tr("Instant")); + } + + _action_single->setText(tr("&Single")); + _action_repeat->setText(tr("&Repetitive")); +} + +void SamplingBar::reStyle() +{ + QString iconPath = ":/icons/" + qApp->property("Style").toString(); + + _configure_button.setIcon(QIcon(iconPath+"/params.png")); + _mode_button.setIcon(_session.get_run_mode() == pv::SigSession::Single ? QIcon(iconPath+"/modes.png") : + QIcon(iconPath+"/moder.png")); + _run_stop_button.setIcon(_sampling ? QIcon(iconPath+"/stop.png") : + QIcon(iconPath+"/start.png")); + _instant_button.setIcon(QIcon(iconPath+"/instant.png")); + _action_single->setIcon(QIcon(iconPath+"/oneloop.png")); + _action_repeat->setIcon(QIcon(iconPath+"/repeat.png")); } void SamplingBar::set_device_list( @@ -263,9 +300,17 @@ void SamplingBar::zero_adj() if ((dsoSig = dynamic_pointer_cast(s))) dsoSig->set_enable(true); } + const int index_back = _sample_count.currentIndex(); + int i = 0; + for (i = 0; i < _sample_count.count(); i++) + if (_sample_count.itemData(i).value() == ZeroTimeBase) + break; + _sample_count.setCurrentIndex(i); + commit_hori_res(); + run_stop(); - pv::dialogs::WaitingDialog wait(this, get_selected_device()); + pv::dialogs::WaitingDialog wait(this, get_selected_device(), SR_CONF_ZERO); if (wait.start() ==QDialog::Rejected) { BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { @@ -276,6 +321,9 @@ void SamplingBar::zero_adj() if (_session.get_capture_state() == pv::SigSession::Running) on_run_stop(); + + _sample_count.setCurrentIndex(index_back); + commit_hori_res(); } bool SamplingBar::get_sampling() const @@ -292,12 +340,12 @@ void SamplingBar::set_sampling(bool sampling) { lock_guard lock(_sampling_mutex); _sampling = sampling; + QString iconPath = ":/icons/" + qApp->property("Style").toString(); + if (_instant) { - _instant_button.setIcon(sampling ? _icon_stop : _icon_instant); - _run_stop_button.setIcon(sampling ? _icon_start_dis : _icon_start); + _instant_button.setIcon(sampling ? QIcon(iconPath+"/stop.png") : QIcon(iconPath+"/instant.png")); } else { - _run_stop_button.setIcon(sampling ? _icon_stop : _icon_start); - _instant_button.setIcon(sampling ? _icon_instant_dis : _icon_instant); + _run_stop_button.setIcon(sampling ? QIcon(iconPath+"/stop.png") : QIcon(iconPath+"/start.png")); } if (!sampling) { @@ -311,11 +359,12 @@ void SamplingBar::set_sampling(bool sampling) } _mode_button.setEnabled(!sampling); - _mode_button.setIcon(sampling ? (_session.get_run_mode() == pv::SigSession::Single ? _icon_single_dis : _icon_repeat_dis) : - (_session.get_run_mode() == pv::SigSession::Single ? _icon_single : _icon_repeat)); + _mode_button.setIcon(_session.get_run_mode() == pv::SigSession::Single ? QIcon(iconPath+"/modes.png") : + QIcon(iconPath+"/moder.png")); _configure_button.setEnabled(!sampling); - _configure_button.setIcon(sampling ? QIcon(":/icons/params_dis.png") : - QIcon(":/icons/params.png")); + _device_selector.setEnabled(!sampling); + + retranslateUi(); } void SamplingBar::set_sample_rate(uint64_t sample_rate) @@ -679,29 +728,44 @@ double SamplingBar::commit_hori_res() void SamplingBar::commit_settings() { - const double sample_duration = _sample_count.itemData( - _sample_count.currentIndex()).value(); - const uint64_t sample_rate = _sample_rate.itemData( - _sample_rate.currentIndex()).value(); - const uint64_t sample_count = ceil(sample_duration / SR_SEC(1) * - sample_rate); - + bool test = false; const shared_ptr dev_inst = get_selected_device(); - if (dev_inst) { - if (sample_rate != dev_inst->get_sample_rate()) - dev_inst->set_config(NULL, NULL, - SR_CONF_SAMPLERATE, - g_variant_new_uint64(sample_rate)); - if (dev_inst->dev_inst()->mode != DSO) { - if (sample_count != dev_inst->get_sample_limit()) - dev_inst->set_config(NULL, NULL, - SR_CONF_LIMIT_SAMPLES, - g_variant_new_uint64(sample_count)); + if (dev_inst && dev_inst->owner()) { + GVariant *gvar = dev_inst->get_config(NULL, NULL, SR_CONF_TEST); + if (gvar != NULL) { + test = g_variant_get_boolean(gvar); + g_variant_unref(gvar); + } + } - bool rle_mode = _sample_count.currentText().contains(RLEString); - dev_inst->set_config(NULL, NULL, - SR_CONF_RLE, - g_variant_new_boolean(rle_mode)); + if (test) { + update_sample_rate_selector_value(); + update_sample_count_selector_value(); + } else { + const double sample_duration = _sample_count.itemData( + _sample_count.currentIndex()).value(); + const uint64_t sample_rate = _sample_rate.itemData( + _sample_rate.currentIndex()).value(); + + const shared_ptr dev_inst = get_selected_device(); + if (dev_inst) { + if (sample_rate != dev_inst->get_sample_rate()) + dev_inst->set_config(NULL, NULL, + SR_CONF_SAMPLERATE, + g_variant_new_uint64(sample_rate)); + if (dev_inst->dev_inst()->mode != DSO) { + const uint64_t sample_count = ((uint64_t)ceil(sample_duration / SR_SEC(1) * + sample_rate) + 1023ULL) & ~1023ULL; + if (sample_count != dev_inst->get_sample_limit()) + dev_inst->set_config(NULL, NULL, + SR_CONF_LIMIT_SAMPLES, + g_variant_new_uint64(sample_count)); + + bool rle_mode = _sample_count.currentText().contains(RLEString); + dev_inst->set_config(NULL, NULL, + SR_CONF_RLE, + g_variant_new_boolean(rle_mode)); + } } } } @@ -816,6 +880,7 @@ void SamplingBar::on_device_selected() return; _session.stop_capture(); + _session.session_save(); const shared_ptr dev_inst = get_selected_device(); if (!dev_inst) @@ -875,14 +940,13 @@ void SamplingBar::show_session_error( void SamplingBar::reload() { + QString iconPath = ":/icons/" + qApp->property("Style").toString(); if (_session.get_device()->dev_inst()->mode == LOGIC) { - _icon_instant = QIcon(":/icons/instant.png"); - _icon_instant_dis = QIcon(":/icons/instant_dis.png"); - _instant_button.setIcon(_icon_instant); if (_session.get_device()->name() == "virtual-session") { _mode_action->setVisible(false); } else { - _mode_button.setIcon(_session.get_run_mode() == pv::SigSession::Single ? _icon_single : _icon_repeat); + _mode_button.setIcon(_session.get_run_mode() == pv::SigSession::Single ? QIcon(iconPath+"/modes.png") : + QIcon(iconPath+"/moder.png")); _mode_action->setVisible(true); } _run_stop_action->setVisible(true); @@ -894,25 +958,24 @@ void SamplingBar::reload() _instant_action->setVisible(false); enable_toggle(true); } else if (_session.get_device()->dev_inst()->mode == DSO) { - _icon_instant = QIcon(":/icons/single.png"); - _icon_instant_dis = QIcon(":/icons/single_dis.png"); - _instant_button.setIcon(_icon_instant); _mode_action->setVisible(false); _run_stop_action->setVisible(true); _instant_action->setVisible(true); enable_toggle(true); } + retranslateUi(); update(); } void SamplingBar::on_mode() { + QString iconPath = ":/icons/" + qApp->property("Style").toString(); QAction *act = qobject_cast(sender()); if (act == _action_single) { - _mode_button.setIcon(_icon_single); + _mode_button.setIcon(QIcon(iconPath+"/modes.png")); _session.set_run_mode(pv::SigSession::Single); } else if (act == _action_repeat) { - _mode_button.setIcon(_icon_repeat); + _mode_button.setIcon(QIcon(iconPath+"/moder.png")); pv::dialogs::Interval interval_dlg(_session, this); interval_dlg.exec(); _session.set_run_mode(pv::SigSession::Repetitive); diff --git a/DSView/pv/toolbars/samplingbar.h b/DSView/pv/toolbars/samplingbar.h old mode 100644 new mode 100755 index 2aebbce4..b1bd9b31 --- a/DSView/pv/toolbars/samplingbar.h +++ b/DSView/pv/toolbars/samplingbar.h @@ -61,12 +61,13 @@ class SamplingBar : public QToolBar private: static const int ComboBoxMaxWidth = 200; - static const int RefreshShort = 200; + static const int RefreshShort = 500; static const uint64_t LogicMaxSWDepth64 = SR_GB(16); static const uint64_t LogicMaxSWDepth32 = SR_GB(8); static const uint64_t AnalogMaxSWDepth = SR_Mn(100); static const QString RLEString; static const QString DIVString; + static const uint64_t ZeroTimeBase = SR_US(10); public: SamplingBar(SigSession &session, QWidget *parent); @@ -105,6 +106,10 @@ signals: void hide_calibration(); private: + void changeEvent(QEvent *event); + void retranslateUi(); + void reStyle(); + void update_sample_rate_selector_value(); void update_sample_count_selector(); void update_sample_count_selector_value(); @@ -146,11 +151,6 @@ private: bool _updating_sample_rate; bool _updating_sample_count; - QIcon _icon_stop; - QIcon _icon_start; - QIcon _icon_instant; - QIcon _icon_start_dis; - QIcon _icon_instant_dis; QToolButton _run_stop_button; QToolButton _instant_button; QAction* _run_stop_action; @@ -162,11 +162,6 @@ private: QAction *_action_repeat; QAction *_action_single; - QIcon _icon_repeat; - QIcon _icon_single; - QIcon _icon_repeat_dis; - QIcon _icon_single_dis; - bool _instant; }; diff --git a/DSView/pv/toolbars/titlebar.cpp b/DSView/pv/toolbars/titlebar.cpp old mode 100644 new mode 100755 index 066c3dd4..121f7cb9 --- a/DSView/pv/toolbars/titlebar.cpp +++ b/DSView/pv/toolbars/titlebar.cpp @@ -29,6 +29,8 @@ #include #include #include +#include + namespace pv { namespace toolbars { @@ -40,7 +42,8 @@ TitleBar::TitleBar(bool top, QWidget *parent, bool hasClose) : _hasClose(hasClose) { setObjectName("TitleBar"); - setFixedHeight(28); + setContentsMargins(0,0,0,0); + setFixedHeight(32); _title = new QLabel(this); QHBoxLayout *hbox = new QHBoxLayout(this); @@ -49,12 +52,8 @@ TitleBar::TitleBar(bool top, QWidget *parent, bool hasClose) : if (_isTop) { _minimizeButton = new QToolButton(this); _minimizeButton->setObjectName("MinimizeButton"); - _minimizeButton->setIcon(QIcon::fromTheme("titlebar", - QIcon(":/icons/minimize.png"))); _maximizeButton = new QToolButton(this); _maximizeButton->setObjectName("MaximizeButton"); - _maximizeButton->setIcon(QIcon::fromTheme("titlebar", - QIcon(":/icons/maximize.png"))); hbox->addWidget(_minimizeButton); hbox->addWidget(_maximizeButton); @@ -68,8 +67,6 @@ TitleBar::TitleBar(bool top, QWidget *parent, bool hasClose) : if (_isTop || _hasClose) { _closeButton= new QToolButton(this); _closeButton->setObjectName("CloseButton"); - _closeButton->setIcon(QIcon::fromTheme("titlebar", - QIcon(":/icons/close.png"))); hbox->addWidget(_closeButton); connect(_closeButton, SIGNAL( clicked() ), parent, SLOT(close() ) ); } @@ -81,13 +78,33 @@ TitleBar::TitleBar(bool top, QWidget *parent, bool hasClose) : setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); } -void TitleBar::paintEvent(QPaintEvent *) +void TitleBar::changeEvent(QEvent *event) { + if (event->type() == QEvent::StyleChange) + reStyle(); + QWidget::changeEvent(event); +} + +void TitleBar::reStyle() +{ + QString iconPath = ":/icons/" + qApp->property("Style").toString(); + + if (_isTop) { + _minimizeButton->setIcon(QIcon(iconPath+"/minimize.png")); + _maximizeButton->setIcon(QIcon(iconPath+"/maximize.png")); + } + if (_isTop || _hasClose) + _closeButton->setIcon(QIcon(iconPath+"/close.png")); +} + +void TitleBar::paintEvent(QPaintEvent *event) +{ + QStyleOption o; + o.initFrom(this); QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this); + p.setRenderHint(QPainter::Antialiasing, true); - p.setPen(QColor(48, 47, 47, 255)); - p.setBrush(QColor(48, 47, 47, 255)); - p.drawRect(rect()); const int xgap = 2; const int xstart = 10; @@ -110,6 +127,8 @@ void TitleBar::paintEvent(QPaintEvent *) p.setPen(QPen(QColor(109, 50, 156, 255), 2, Qt::SolidLine)); p.drawLine(xstart + xgap*8, height()*0.50, xstart + xgap*8, height()*0.66); p.drawLine(xstart + xgap*10, height()*0.34, xstart + xgap*10, height()*0.50); + + QWidget::paintEvent(event); } void TitleBar::setTitle(QString title) @@ -129,25 +148,23 @@ QString TitleBar::title() const void TitleBar::showMaxRestore() { + QString iconPath = ":/icons/" + qApp->property("Style").toString(); if (parentWidget()->isMaximized()) { - _maximizeButton->setIcon(QIcon::fromTheme("titlebar", - QIcon(":/icons/maximize.png"))); + _maximizeButton->setIcon(QIcon(iconPath+"/maximize.png")); normalShow(); } else { - _maximizeButton->setIcon(QIcon::fromTheme("titlebar", - QIcon(":/icons/restore.png"))); + _maximizeButton->setIcon(QIcon(iconPath+"/restore.png")); maximizedShow(); } } void TitleBar::setRestoreButton(bool max) { + QString iconPath = ":/icons/" + qApp->property("Style").toString(); if (!max) { - _maximizeButton->setIcon(QIcon::fromTheme("titlebar", - QIcon(":/icons/maximize.png"))); + _maximizeButton->setIcon(QIcon(iconPath+"/maximize.png")); } else { - _maximizeButton->setIcon(QIcon::fromTheme("titlebar", - QIcon(":/icons/restore.png"))); + _maximizeButton->setIcon(QIcon(iconPath+"/restore.png")); } } diff --git a/DSView/pv/toolbars/titlebar.h b/DSView/pv/toolbars/titlebar.h old mode 100644 new mode 100755 index 6004411a..d48878b1 --- a/DSView/pv/toolbars/titlebar.h +++ b/DSView/pv/toolbars/titlebar.h @@ -39,6 +39,10 @@ public: QPoint get_startPos() const; QString title() const; +private: + void changeEvent(QEvent *event); + void reStyle(); + signals: void normalShow(); void maximizedShow(); @@ -48,7 +52,7 @@ public slots: void setRestoreButton(bool max); protected: - void paintEvent(QPaintEvent *); + void paintEvent(QPaintEvent *event); void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); diff --git a/DSView/pv/toolbars/trigbar.cpp b/DSView/pv/toolbars/trigbar.cpp old mode 100644 new mode 100755 index fe54f94a..6e40ba38 --- a/DSView/pv/toolbars/trigbar.cpp +++ b/DSView/pv/toolbars/trigbar.cpp @@ -1,197 +1,309 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "trigbar.h" -#include "../sigsession.h" -#include "../device/devinst.h" -#include "../dialogs/fftoptions.h" - -#include - -namespace pv { -namespace toolbars { - -TrigBar::TrigBar(SigSession &session, QWidget *parent) : - QToolBar("Trig Bar", parent), - _session(session), - _enable(true), - _trig_button(this), - _protocol_button(this), - _measure_button(this), - _search_button(this), - _math_button(this) -{ - setMovable(false); - setContentsMargins(0,0,0,0); - - connect(&_trig_button, SIGNAL(clicked()), - this, SLOT(trigger_clicked())); - connect(&_protocol_button, SIGNAL(clicked()), - this, SLOT(protocol_clicked())); - connect(&_measure_button, SIGNAL(clicked()), - this, SLOT(measure_clicked())); - connect(&_search_button, SIGNAL(clicked()), - this, SLOT(search_clicked())); - - _trig_button.setIcon(QIcon::fromTheme("trig", - QIcon(":/icons/trigger.png"))); - _trig_button.setCheckable(true); - _protocol_button.setIcon(QIcon::fromTheme("trig", - QIcon(":/icons/protocol.png"))); -#ifdef ENABLE_DECODE - _protocol_button.setCheckable(true); -#endif - _measure_button.setIcon(QIcon::fromTheme("trig", - QIcon(":/icons/measure.png"))); - _measure_button.setCheckable(true); - _search_button.setIcon(QIcon::fromTheme("trig", - QIcon(":/icons/search-bar.png"))); - _search_button.setCheckable(true); - _math_button.setIcon(QIcon::fromTheme("trig", - QIcon(":/icons/math.png"))); - - _action_fft = new QAction(this); - _action_fft->setText(QApplication::translate( - "Math", "&FFT", 0)); - _action_fft->setIcon(QIcon::fromTheme("Math", - QIcon(":/icons/fft.png"))); - _action_fft->setObjectName(QString::fromUtf8("actionFft")); - connect(_action_fft, SIGNAL(triggered()), this, SLOT(on_actionFft_triggered())); - - _math_menu = new QMenu(this); - _math_menu->setContentsMargins(0,0,0,0); - _math_menu->addAction(_action_fft); - _math_button.setPopupMode(QToolButton::InstantPopup); - _math_button.setMenu(_math_menu); - - _trig_action = addWidget(&_trig_button); - _protocol_action = addWidget(&_protocol_button); - _measure_action = addWidget(&_measure_button); - _search_action = addWidget(&_search_button); - _math_action = addWidget(&_math_button); -} - -void TrigBar::protocol_clicked() -{ - on_protocol(_protocol_button.isChecked()); -} - -void TrigBar::trigger_clicked() -{ - on_trigger(_trig_button.isChecked()); -} - -void TrigBar::update_trig_btn(bool checked) -{ - _trig_button.setChecked(checked); -} - -void TrigBar::measure_clicked() -{ - on_measure(_measure_button.isChecked()); -} - -void TrigBar::search_clicked() -{ - on_search(_search_button.isChecked()); -} - -void TrigBar::enable_toggle(bool enable) -{ - _trig_button.setDisabled(!enable); - _protocol_button.setDisabled(!enable); - _measure_button.setDisabled(!enable); - _search_button.setDisabled(!enable); - _math_button.setDisabled(!enable); - - _trig_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/trigger.png")) : - QIcon::fromTheme("trig", QIcon(":/icons/trigger_dis.png"))); - _protocol_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/protocol.png")) : - QIcon::fromTheme("trig", QIcon(":/icons/protocol_dis.png"))); - _measure_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/measure.png")) : - QIcon::fromTheme("trig", QIcon(":/icons/measure_dis.png"))); - _search_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/search-bar.png")) : - QIcon::fromTheme("trig", QIcon(":/icons/search-bar_dis.png"))); - _math_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/math.png")) : - QIcon::fromTheme("trig", QIcon(":/icons/math_dis.png"))); -} - -void TrigBar::enable_protocol(bool enable) -{ - _protocol_button.setDisabled(!enable); - _protocol_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/protocol.png")) : - QIcon::fromTheme("trig", QIcon(":/icons/protocol_dis.png"))); -} - -void TrigBar::close_all() -{ - if (_trig_button.isChecked()) { - _trig_button.setChecked(false); - on_trigger(false); - } - if (_protocol_button.isChecked()) { - _protocol_button.setChecked(false); - on_protocol(false); - } - if (_measure_button.isChecked()) { - _measure_button.setChecked(false); - on_measure(false); - } - if(_search_button.isChecked()) { - _search_button.setChecked(false); - on_search(false); - } -} - -void TrigBar::reload() -{ - close_all(); - if (_session.get_device()->dev_inst()->mode == LOGIC) { - _trig_action->setVisible(true); - _protocol_action->setVisible(true); - _measure_action->setVisible(true); - _search_action->setVisible(true); - _math_action->setVisible(false); - } else if (_session.get_device()->dev_inst()->mode == ANALOG) { - _trig_action->setVisible(false); - _protocol_action->setVisible(false); - _measure_action->setVisible(true); - _search_action->setVisible(false); - _math_action->setVisible(false); - } else if (_session.get_device()->dev_inst()->mode == DSO) { - _trig_action->setVisible(true); - _protocol_action->setVisible(false); - _measure_action->setVisible(true); - _search_action->setVisible(false); - _math_action->setVisible(true); - } - enable_toggle(true); - update(); -} - -void TrigBar::on_actionFft_triggered() -{ - pv::dialogs::FftOptions fft_dlg(this, _session); - fft_dlg.exec(); -} - -} // namespace toolbars -} // namespace pv +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "trigbar.h" +#include "../sigsession.h" +#include "../device/devinst.h" +#include "../dialogs/fftoptions.h" +#include "../dialogs/lissajousoptions.h" +#include "../dialogs/mathoptions.h" +#include "../view/trace.h" + +#include +#include +#include + +namespace pv { +namespace toolbars { + +const QString TrigBar::DARK_STYLE = "dark"; +const QString TrigBar::LIGHT_STYLE = "light"; + +TrigBar::TrigBar(SigSession &session, QWidget *parent) : + QToolBar("Trig Bar", parent), + _session(session), + _enable(true), + _trig_button(this), + _protocol_button(this), + _measure_button(this), + _search_button(this), + _function_button(this), + _display_button(this) +{ + setMovable(false); + setContentsMargins(0,0,0,0); + setIconSize(QSize(40, 28)); + + connect(&_trig_button, SIGNAL(clicked()), + this, SLOT(trigger_clicked())); + connect(&_protocol_button, SIGNAL(clicked()), + this, SLOT(protocol_clicked())); + connect(&_measure_button, SIGNAL(clicked()), + this, SLOT(measure_clicked())); + connect(&_search_button, SIGNAL(clicked()), + this, SLOT(search_clicked())); + + _trig_button.setCheckable(true); +#ifdef ENABLE_DECODE + _protocol_button.setCheckable(true); +#endif + _measure_button.setCheckable(true); + _search_button.setCheckable(true); + + _action_fft = new QAction(this); + _action_fft->setObjectName(QString::fromUtf8("actionFft")); + connect(_action_fft, SIGNAL(triggered()), this, SLOT(on_actionFft_triggered())); + + _action_math = new QAction(this); + _action_math->setObjectName(QString::fromUtf8("actionMath")); + connect(_action_math, SIGNAL(triggered()), this, SLOT(on_actionMath_triggered())); + + _function_menu = new QMenu(this); + _function_menu->setContentsMargins(0,0,0,0); + _function_menu->addAction(_action_fft); + _function_menu->addAction(_action_math); + _function_button.setPopupMode(QToolButton::InstantPopup); + _function_button.setMenu(_function_menu); + + _action_lissajous = new QAction(this); + _action_lissajous->setObjectName(QString::fromUtf8("actionLissajous")); + connect(_action_lissajous, SIGNAL(triggered()), this, SLOT(on_actionLissajous_triggered())); + + _dark_style = new QAction(this); + _dark_style->setObjectName(QString::fromUtf8("actionDark")); + connect(_dark_style, SIGNAL(triggered()), this, SLOT(on_actionDark_triggered())); + + _light_style = new QAction(this); + _light_style->setObjectName(QString::fromUtf8("actionLight")); + connect(_light_style, SIGNAL(triggered()), this, SLOT(on_actionLight_triggered())); + + _themes = new QMenu(this); + _themes->setObjectName(QString::fromUtf8("menuThemes")); + _themes->addAction(_light_style); + _themes->addAction(_dark_style); + + _display_menu = new QMenu(this); + _display_menu->setContentsMargins(0,0,0,0); + _display_menu->addMenu(_themes); + _display_menu->addAction(_action_lissajous); + _display_button.setPopupMode(QToolButton::InstantPopup); + _display_button.setMenu(_display_menu); + + _trig_button.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + _protocol_button.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + _measure_button.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + _search_button.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + _function_button.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + _display_button.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + + _protocol_button.setContentsMargins(0,0,0,0); + + _trig_action = addWidget(&_trig_button); + _protocol_action = addWidget(&_protocol_button); + _measure_action = addWidget(&_measure_button); + _search_action = addWidget(&_search_button); + _function_action = addWidget(&_function_button); + _display_action = addWidget(&_display_button); + + retranslateUi(); +} + +void TrigBar::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) + retranslateUi(); + else if (event->type() == QEvent::StyleChange) + reStyle(); + QToolBar::changeEvent(event); +} + +void TrigBar::retranslateUi() +{ + _trig_button.setText(tr("Trigger")); + _protocol_button.setText(tr("Decode")); + _measure_button.setText(tr("Measure")); + _search_button.setText(tr("Search")); + _function_button.setText(tr("Function")); + _display_button.setText(tr("Display")); + _themes->setTitle(tr("Themes")); + _dark_style->setText(tr("Dark")); + _light_style->setText(tr("Light")); + _action_lissajous->setText(tr("&Lissajous")); + + _action_fft->setText(tr("FFT")); + _action_math->setText(tr("Math")); +} + +void TrigBar::reStyle() +{ + QString iconPath = ":/icons/" + qApp->property("Style").toString(); + + _trig_button.setIcon(QIcon(iconPath+"/trigger.png")); + _protocol_button.setIcon(QIcon(iconPath+"/protocol.png")); + _measure_button.setIcon(QIcon(iconPath+"/measure.png")); + _search_button.setIcon(QIcon(iconPath+"/search-bar.png")); + _function_button.setIcon(QIcon(iconPath+"/function.png")); + _display_button.setIcon(QIcon(iconPath+"/display.png")); + + _action_fft->setIcon(QIcon(iconPath+"/fft.png")); + _action_math->setIcon(QIcon(iconPath+"/math.png")); + _action_lissajous->setIcon(QIcon(iconPath+"/lissajous.png")); + _dark_style->setIcon(QIcon(iconPath+"/dark.png")); + _light_style->setIcon(QIcon(iconPath+"/light.png")); + _themes->setIcon(QIcon(iconPath+"/"+qApp->property("Style").toString()+".png")); +} + +void TrigBar::protocol_clicked() +{ + on_protocol(_protocol_button.isChecked()); +} + +void TrigBar::trigger_clicked() +{ + on_trigger(_trig_button.isChecked()); +} + +void TrigBar::update_trig_btn(bool checked) +{ + _trig_button.setChecked(checked); +} + +void TrigBar::update_protocol_btn(bool checked) +{ + _protocol_button.setChecked(checked); +} + +void TrigBar::update_measure_btn(bool checked) +{ + _measure_button.setChecked(checked); +} + +void TrigBar::update_search_btn(bool checked) +{ + _search_button.setChecked(checked); +} + +void TrigBar::measure_clicked() +{ + on_measure(_measure_button.isChecked()); +} + +void TrigBar::search_clicked() +{ + on_search(_search_button.isChecked()); +} + +void TrigBar::enable_toggle(bool enable) +{ + _trig_button.setDisabled(!enable); + _protocol_button.setDisabled(!enable); + _measure_button.setDisabled(!enable); + _search_button.setDisabled(!enable); + _function_button.setDisabled(!enable); + _display_button.setDisabled(!enable); +} + +void TrigBar::enable_protocol(bool enable) +{ + _protocol_button.setDisabled(!enable); +} + +void TrigBar::close_all() +{ + if (_trig_button.isChecked()) { + _trig_button.setChecked(false); + on_trigger(false); + } + if (_protocol_button.isChecked()) { + _protocol_button.setChecked(false); + on_protocol(false); + } + if (_measure_button.isChecked()) { + _measure_button.setChecked(false); + on_measure(false); + } + if(_search_button.isChecked()) { + _search_button.setChecked(false); + on_search(false); + } +} + +void TrigBar::reload() +{ + close_all(); + if (_session.get_device()->dev_inst()->mode == LOGIC) { + _trig_action->setVisible(true); + _protocol_action->setVisible(true); + _measure_action->setVisible(true); + _search_action->setVisible(true); + _function_action->setVisible(false); + _action_lissajous->setVisible(false); + } else if (_session.get_device()->dev_inst()->mode == ANALOG) { + _trig_action->setVisible(false); + _protocol_action->setVisible(false); + _measure_action->setVisible(true); + _search_action->setVisible(false); + _function_action->setVisible(false); + _action_lissajous->setVisible(false); + } else if (_session.get_device()->dev_inst()->mode == DSO) { + _trig_action->setVisible(true); + _protocol_action->setVisible(false); + _measure_action->setVisible(true); + _search_action->setVisible(false); + _function_action->setVisible(true); + _action_lissajous->setVisible(true); + } + enable_toggle(true); + update(); +} + +void TrigBar::on_actionFft_triggered() +{ + pv::dialogs::FftOptions fft_dlg(this, _session); + fft_dlg.exec(); +} + +void TrigBar::on_actionMath_triggered() +{ + pv::dialogs::MathOptions math_dlg(_session, this); + math_dlg.exec(); +} + +void TrigBar::on_actionDark_triggered() +{ + setTheme(DARK_STYLE); + _themes->setIcon(QIcon(":/icons/"+DARK_STYLE+"/"+DARK_STYLE+".png")); +} + +void TrigBar::on_actionLight_triggered() +{ + setTheme(LIGHT_STYLE); + _themes->setIcon(QIcon(":/icons/"+LIGHT_STYLE+"/"+LIGHT_STYLE+".png")); +} + +void TrigBar::on_actionLissajous_triggered() +{ + pv::dialogs::LissajousOptions lissajous_dlg(_session, this); + lissajous_dlg.exec(); +} + +} // namespace toolbars +} // namespace pv diff --git a/DSView/pv/toolbars/trigbar.h b/DSView/pv/toolbars/trigbar.h old mode 100644 new mode 100755 index 6c2048b7..6a86218f --- a/DSView/pv/toolbars/trigbar.h +++ b/DSView/pv/toolbars/trigbar.h @@ -1,86 +1,115 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#ifndef DSVIEW_PV_TOOLBARS_TRIGBAR_H -#define DSVIEW_PV_TOOLBARS_TRIGBAR_H - -#include -#include -#include -#include - -namespace pv { - -class SigSession; - -namespace toolbars { - -class TrigBar : public QToolBar -{ - Q_OBJECT -public: - explicit TrigBar(SigSession &session, QWidget *parent = 0); - - void enable_toggle(bool enable); - void enable_protocol(bool enable); - void close_all(); - void reload(); - -signals: - void on_protocol(bool visible); - void on_trigger(bool visible); - void on_measure(bool visible); - void on_search(bool visible); - -public slots: - void protocol_clicked(); - void trigger_clicked(); - void measure_clicked(); - void search_clicked(); - - void update_trig_btn(bool checked); - - void on_actionFft_triggered(); - -private: - SigSession& _session; - bool _enable; - QToolButton _trig_button; - QToolButton _protocol_button; - QToolButton _measure_button; - QToolButton _search_button; - QToolButton _math_button; - QAction* _trig_action; - QAction* _protocol_action; - QAction* _measure_action; - QAction* _search_action; - QAction* _math_action; - - QMenu* _math_menu; - QAction* _action_fft; - -}; - -} // namespace toolbars -} // namespace pv - -#endif // DSVIEW_PV_TOOLBARS_TRIGBAR_H +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef DSVIEW_PV_TOOLBARS_TRIGBAR_H +#define DSVIEW_PV_TOOLBARS_TRIGBAR_H + +#include +#include +#include +#include + +namespace pv { + +class SigSession; + +namespace toolbars { + +class TrigBar : public QToolBar +{ + Q_OBJECT + +protected: + static const QString DARK_STYLE; + static const QString LIGHT_STYLE; + +public: + explicit TrigBar(SigSession &session, QWidget *parent = 0); + + void enable_toggle(bool enable); + void enable_protocol(bool enable); + void close_all(); + void reload(); + +private: + void changeEvent(QEvent *event); + void retranslateUi(); + void reStyle(); + +signals: + void setTheme(QString style); + void on_protocol(bool visible); + void on_trigger(bool visible); + void on_measure(bool visible); + void on_search(bool visible); + void show_lissajous(bool visible); + +private slots: + void on_actionDark_triggered(); + void on_actionLight_triggered(); + void on_actionLissajous_triggered(); + +public slots: + void protocol_clicked(); + void trigger_clicked(); + void measure_clicked(); + void search_clicked(); + + void update_trig_btn(bool checked); + void update_protocol_btn(bool checked); + void update_measure_btn(bool checked); + void update_search_btn(bool checked); + + void on_actionFft_triggered(); + void on_actionMath_triggered(); + +private: + SigSession& _session; + bool _enable; + QToolButton _trig_button; + QToolButton _protocol_button; + QToolButton _measure_button; + QToolButton _search_button; + QToolButton _function_button; + QToolButton _display_button; + QAction* _trig_action; + QAction* _protocol_action; + QAction* _measure_action; + QAction* _search_action; + QAction* _function_action; + QAction* _display_action; + + QMenu* _function_menu; + QAction* _action_fft; + QAction* _action_math; + + QMenu* _display_menu; + QMenu *_themes; + QAction *_dark_style; + QAction *_light_style; + QAction* _action_lissajous; +}; + +} // namespace toolbars +} // namespace pv + +#endif // DSVIEW_PV_TOOLBARS_TRIGBAR_H diff --git a/DSView/pv/view/analogsignal.cpp b/DSView/pv/view/analogsignal.cpp old mode 100644 new mode 100755 index d583d965..692a9f02 --- a/DSView/pv/view/analogsignal.cpp +++ b/DSView/pv/view/analogsignal.cpp @@ -54,10 +54,42 @@ AnalogSignal::AnalogSignal(boost::shared_ptr dev_inst, _data(data), _rects(NULL) { - _typeWidth = 3; + _typeWidth = 5; _colour = SignalColours[probe->index % countof(SignalColours)]; - _bits = -1; - _zero_vrate = 0.5; + + GVariant *gvar; + // channel bits + gvar = _dev_inst->get_config(NULL, NULL, SR_CONF_UNIT_BITS); + if (gvar != NULL) { + _bits = g_variant_get_byte(gvar); + g_variant_unref(gvar); + } else { + _bits = DefaultBits; + qDebug("Warning: config_get SR_CONF_UNIT_BITS failed, set to %d(default).", DefaultBits); + } + gvar = _dev_inst->get_config(NULL, NULL, SR_CONF_REF_MIN); + if (gvar != NULL) { + _ref_min = g_variant_get_uint32(gvar); + g_variant_unref(gvar); + } else { + _ref_min = 1; + } + gvar = _dev_inst->get_config(NULL, NULL, SR_CONF_REF_MAX); + if (gvar != NULL) { + _ref_max = g_variant_get_uint32(gvar); + g_variant_unref(gvar); + } else { + _ref_max = ((1 << _bits) - 1); + } + + // -- vpos + gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_PROBE_OFFSET); + if (gvar != NULL) { + _zero_offset = g_variant_get_uint16(gvar); + g_variant_unref(gvar); + } else { + qDebug() << "ERROR: config_get SR_CONF_PROBE_OFFSET failed."; + } } AnalogSignal::AnalogSignal(boost::shared_ptr s, @@ -67,12 +99,13 @@ AnalogSignal::AnalogSignal(boost::shared_ptr s, _data(data), _rects(NULL) { - _typeWidth = 3; + _typeWidth = 5; _bits = s->get_bits(); - _zero_vrate = s->get_zero_vrate(); + _ref_min = s->get_ref_min(); + _ref_max = s->get_ref_max(); + _zero_offset = s->get_zero_offset(); _scale = s->get_scale(); - _hw_offset = s->get_hw_offset(); } AnalogSignal::~AnalogSignal() @@ -88,9 +121,9 @@ boost::shared_ptr AnalogSignal::data() const return _data; } -void AnalogSignal::set_scale(float scale) +void AnalogSignal::set_scale(int height) { - _scale = scale; + _scale = height / (_ref_max - _ref_min); } float AnalogSignal::get_scale() const @@ -103,9 +136,25 @@ int AnalogSignal::get_bits() const return _bits; } +double AnalogSignal::get_ref_min() const +{ + return _ref_min; +} + +double AnalogSignal::get_ref_max() const +{ + return _ref_max; +} + int AnalogSignal::get_hw_offset() const { - return _hw_offset; + int hw_offset = 0; + GVariant *gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_PROBE_HW_OFFSET); + if (gvar != NULL) { + hw_offset = g_variant_get_uint16(gvar); + g_variant_unref(gvar); + } + return hw_offset; } int AnalogSignal::commit_settings() @@ -124,9 +173,9 @@ int AnalogSignal::commit_settings() ret = _dev_inst->set_config(_probe, NULL, SR_CONF_PROBE_COUPLING, g_variant_new_byte(_probe->coupling)); - // -- vpos - ret = _dev_inst->set_config(_probe, NULL, SR_CONF_PROBE_VPOS, - g_variant_new_double(_probe->vpos)); + // -- offset + ret = _dev_inst->set_config(_probe, NULL, SR_CONF_PROBE_OFFSET, + g_variant_new_uint16(_probe->offset)); // -- trig_value _dev_inst->set_config(_probe, NULL, SR_CONF_TRIGGER_VALUE, @@ -160,6 +209,17 @@ uint8_t AnalogSignal::get_acCoupling() const return coupling; } +bool AnalogSignal::get_mapDefault() const +{ + bool isDefault = true; + GVariant* gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_PROBE_MAP_DEFAULT); + if (gvar != NULL) { + isDefault = g_variant_get_boolean(gvar); + g_variant_unref(gvar); + } + return isDefault; +} + QString AnalogSignal::get_mapUnit() const { QString unit; @@ -193,55 +253,80 @@ double AnalogSignal::get_mapMax() const return max; } +uint64_t AnalogSignal::get_factor() const +{ + GVariant* gvar; + uint64_t factor; + gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_PROBE_FACTOR); + if (gvar != NULL) { + factor = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + return factor; + } else { + qDebug() << "ERROR: config_get SR_CONF_PROBE_FACTOR failed."; + return 1; + } +} + +int AnalogSignal::ratio2value(double ratio) const +{ + return ratio * (_ref_max - _ref_min) + _ref_min; +} + +int AnalogSignal::ratio2pos(double ratio) const +{ + const int height = get_totalHeight(); + const int top = get_y() - height * 0.5; + return ratio * height + top; +} + +double AnalogSignal::value2ratio(int value) const +{ + return max(0.0, (value - _ref_min) / (_ref_max - _ref_min)); +} + +double AnalogSignal::pos2ratio(int pos) const +{ + const int height = get_totalHeight(); + const int top = get_y() - height / 2; + return min(max(pos - top, 0), height) * 1.0 / height; +} + /** * **/ void AnalogSignal::set_zero_vpos(int pos) { if (enabled()) { - const int height = get_totalHeight(); - const int top = get_y() - height / 2; - set_zero_vrate(min(max(pos - top, 0), height) * 1.0 / height, false); + set_zero_ratio(pos2ratio(pos)); } } int AnalogSignal::get_zero_vpos() const { - return (_zero_vrate - 0.5) * get_totalHeight() + get_y(); + return ratio2pos(get_zero_ratio()); } -void AnalogSignal::set_zero_vrate(double rate, bool force_update) +void AnalogSignal::set_zero_ratio(double ratio) { if (_view->session().get_capture_state() == SigSession::Running) return; - _zero_vrate = rate; - update_vpos(); - - if (force_update) - update_offset(); + _zero_offset = ratio2value(ratio); + _dev_inst->set_config(_probe, NULL, SR_CONF_PROBE_OFFSET, + g_variant_new_uint16(_zero_offset)); } -double AnalogSignal::get_zero_vrate() const +double AnalogSignal::get_zero_ratio() const { - return _zero_vrate; + return value2ratio(_zero_offset); } -void AnalogSignal::update_vpos() +int AnalogSignal::get_zero_offset() const { - double vpos_off = (0.5 - _zero_vrate) * get_vdiv() * DS_CONF_DSO_VDIVS; - _dev_inst->set_config(_probe, NULL, SR_CONF_PROBE_VPOS, - g_variant_new_double(vpos_off)); + return _zero_offset; } -void AnalogSignal::update_offset() -{ - if (_dev_inst->name().contains("virtual") || - _dev_inst->name() == "DSLogic") - _hw_offset = (1 << _bits) * 0.5; - else - _hw_offset = _zero_vrate * ((1 << _bits) - 1); -} /** * Event **/ @@ -256,7 +341,7 @@ void AnalogSignal::resize() /** * Paint **/ -void AnalogSignal::paint_back(QPainter &p, int left, int right) +void AnalogSignal::paint_back(QPainter &p, int left, int right, QColor fore, QColor back) { assert(_view); @@ -268,14 +353,14 @@ void AnalogSignal::paint_back(QPainter &p, int left, int right) const double mapSteps = (get_mapMax() - get_mapMin()) / DIVS; const QString mapUnit = get_mapUnit(); - QPen solidPen(Signal::dsFore); + QPen solidPen(fore); solidPen.setStyle(Qt::SolidLine); p.setPen(solidPen); - p.setBrush(Trace::dsBack); + p.setBrush(back); // paint rule double y = get_y() - height * 0.5; - double mapValue = get_mapMax() + (_zero_vrate - 0.5) * (get_mapMax() - get_mapMin()); + double mapValue = get_mapMax() + (get_zero_ratio() - 0.5) * (get_mapMax() - get_mapMin()); for (i = 0; i < DIVS; i++) { p.drawLine(left, y, left+10, y); if (i == 0 || i == DIVS/2) @@ -305,16 +390,20 @@ void AnalogSignal::paint_back(QPainter &p, int left, int right) QString::number(mapValue,'f',2)+mapUnit); } -void AnalogSignal::paint_mid(QPainter &p, int left, int right) +void AnalogSignal::paint_mid(QPainter &p, int left, int right, QColor fore, QColor back) { + (void)fore; + (void)back; + assert(_data); assert(_view); assert(right >= left); const int height = get_totalHeight(); - const int top = get_y() - height * 0.5; - const int bottom = get_y() + height * 0.5; - const float zeroY = _zero_vrate * height + top; + const float top = get_y() - height * 0.5; + const float bottom = get_y() + height * 0.5; + const float zeroY = ratio2pos(get_zero_ratio()); + const int width = right - left + 1; const double scale = _view->scale(); assert(scale > 0); @@ -334,52 +423,19 @@ void AnalogSignal::paint_mid(QPainter &p, int left, int right) if (order == -1) return; - if (_bits != snapshot->get_unit_bytes()*8) { - _bits = snapshot->get_unit_bytes()*8; - _scale = _totalHeight * 1.0f / ((1 << _bits) - 1); - update_offset(); - } const double pixels_offset = offset; const double samplerate = _data->samplerate(); const int64_t cur_sample_count = snapshot->get_sample_count(); const double samples_per_pixel = samplerate * scale; const uint64_t ring_start = snapshot->get_ring_start(); -// int64_t start_pixel; -// uint64_t start_index; -// int64_t start_skew_pixels; - //const double first_pos = (_view->session().cur_samplelimits() - cur_sample_count) / samples_per_pixel; - // const double start_sample = (snapshot->get_ring_start() + - // (pixels_offset + left - first_pos) * samples_per_pixel); - //start_pixel = floor(first_pos - pixels_offset - left); - // if (start_sample < 0) { - // start_index = 0; - // start_skew_pixels = 0; - // } else { - // start_index = (uint64_t)(start_sample) % cur_sample_count; - // start_skew_pixels = (start_sample - floor(start_sample)) / samples_per_pixel; - // } - // if (start_pixel < left) - // start_pixel = left; - // start_pixel -= start_skew_pixels; - // int64_t show_length = ceil(samples_per_pixel*(right - start_pixel + 1)); - int64_t start_pixel; uint64_t start_index; - const double over_pixel = cur_sample_count / samples_per_pixel - - pixels_offset - right; - if (over_pixel <= left - right) { - return; - } else if (over_pixel <= 0) { - start_index = ring_start; - start_pixel = over_pixel + right - left; - } else { - const double over_sample = over_pixel * samples_per_pixel; - start_index = (uint64_t)(ring_start + floor(over_sample)) % cur_sample_count; - start_pixel = right + (over_sample - floor(over_sample)) / samples_per_pixel; - } + const double index_offset = pixels_offset * samples_per_pixel; + start_index = (uint64_t)(ring_start + floor(index_offset)) % cur_sample_count; + start_pixel = (floor(index_offset) - index_offset) / samples_per_pixel ; - int64_t show_length = ceil(samples_per_pixel*(start_pixel + 1)); + int64_t show_length = min(floor(cur_sample_count - floor(index_offset)), ceil(width*samples_per_pixel + 1)); if (show_length <= 0) return; @@ -387,12 +443,12 @@ void AnalogSignal::paint_mid(QPainter &p, int left, int right) paint_trace(p, snapshot, zeroY, start_pixel, start_index, show_length, samples_per_pixel, order, - top, bottom, right-left); + top, bottom, width); else paint_envelope(p, snapshot, zeroY, start_pixel, start_index, show_length, samples_per_pixel, order, - top, bottom, right-left); + top, bottom, width); } void AnalogSignal::paint_trace(QPainter &p, @@ -400,7 +456,7 @@ void AnalogSignal::paint_trace(QPainter &p, int zeroY, const int start_pixel, const uint64_t start_index, const int64_t sample_count, const double samples_per_pixel, const int order, - const int top, const int bottom, const int width) + const float top, const float bottom, const int width) { (void)width; @@ -413,28 +469,26 @@ void AnalogSignal::paint_trace(QPainter &p, p.setPen(_colour); //p.setPen(QPen(_colour, 2, Qt::SolidLine)); - QPointF *points = new QPointF[sample_count + 2]; + QPointF *points = new QPointF[sample_count]; QPointF *point = points; uint64_t yindex = start_index; - int x = 0; -// const int64_t start_offset = start_pixel - (int64_t)(start_index / samples_per_pixel + 0.5); - //for (int64_t sample = 0; x < right; sample++) { - const int64_t start_offset = start_pixel + (int64_t)(start_index / samples_per_pixel + 0.5); - for (int64_t sample = 0; x >= 0; sample++) { - x = start_offset - (start_index + sample) / samples_per_pixel - 0.5; + + const int hw_offset = get_hw_offset(); + float x = start_pixel; + double pixels_per_sample = 1.0/samples_per_pixel; + for (int64_t sample = 0; sample < sample_count; sample++) { uint64_t index = (yindex * channel_num + order) * unit_bytes; - double yvalue = samples[index]; + float yvalue = samples[index]; for(uint8_t i = 1; i < unit_bytes; i++) yvalue += (samples[++index] << i*8); - yvalue = zeroY + ((int)yvalue - _hw_offset) * _scale; - yvalue = min(max((int)yvalue, top), bottom); + yvalue = zeroY + (yvalue - hw_offset) * _scale; + yvalue = min(max(yvalue, top), bottom); *point++ = QPointF(x, yvalue); - if (sample != 0 && yindex == snapshot->get_ring_end()) { - *point++ = QPointF(0, points[sample].y()); + if (yindex == snapshot->get_ring_end()) break; - } yindex++; yindex %= snapshot->get_sample_count(); + x += pixels_per_sample; } p.drawPolyline(points, point - points); delete[] points; @@ -446,7 +500,7 @@ void AnalogSignal::paint_envelope(QPainter &p, int zeroY, const int start_pixel, const uint64_t start_index, const int64_t sample_count, const double samples_per_pixel, const int order, - const int top, const int bottom, const int width) + const float top, const float bottom, const int width) { using namespace Qt; using pv::data::AnalogSnapshot; @@ -458,38 +512,32 @@ void AnalogSignal::paint_envelope(QPainter &p, return; p.setPen(QPen(NoPen)); - //p.setPen(QPen(_colour, 2, Qt::SolidLine)); p.setBrush(_colour); if (!_rects) - _rects = new QRectF[width+3]; + _rects = new QRectF[width+10]; QRectF *rect = _rects; int px = -1, pre_px; - int y_min = zeroY, y_max = zeroY, pre_y_min = zeroY, pre_y_max = zeroY; + float y_min = zeroY, y_max = zeroY, pre_y_min = zeroY, pre_y_max = zeroY; int pcnt = 0; - const double scale_samples_pre_pixel = samples_per_pixel / e.scale; + const double scale_pixels_per_samples = e.scale / samples_per_pixel; const uint64_t ring_end = max((int64_t)0, (int64_t)snapshot->get_ring_end() / e.scale - 1); -// const int64_t start_offset = start_pixel - -// (int64_t)(e.start / scale_samples_pre_pixel + 0.5); -// for(uint64_t sample = 0; sample < e.length; sample++) { - const int64_t start_offset = start_pixel + - (int64_t)(e.start / scale_samples_pre_pixel + 0.5); + const int hw_offset = get_hw_offset(); + + float x = start_pixel; for(uint64_t sample = 0; sample < e.length; sample++) { const uint64_t ring_index = (e.start + sample) % (_view->session().cur_samplelimits() / e.scale); if (sample != 0 && ring_index == ring_end) break; -// const int x = start_offset + -// (e.start + sample) / scale_samples_pre_pixel + 0.5; - const int x = start_offset - - (e.start + sample) / scale_samples_pre_pixel - 0.5; + const AnalogSnapshot::EnvelopeSample *const ev = e.samples + ((e.start + sample) % e.samples_num); - const int b = min(max((int)(zeroY + (ev->max - _hw_offset) * _scale + 0.5), top), bottom); - const int t = min(max((int)(zeroY + (ev->min - _hw_offset) * _scale + 0.5), top), bottom); + const float b = min(max((float)(zeroY + (ev->max - hw_offset) * _scale + 0.5), top), bottom); + const float t = min(max((float)(zeroY + (ev->min - hw_offset) * _scale + 0.5), top), bottom); pre_px = px; - if(px != x) { + if(px != floor(x)) { if (pre_px != -1) { // We overlap this sample with the previous so that vertical // gaps do not appear during steep rising or falling edges @@ -514,6 +562,7 @@ void AnalogSignal::paint_envelope(QPainter &p, y_max = max(b, y_max); y_min = min(t, y_min); } + x += scale_pixels_per_samples; } p.drawRects(_rects, pcnt); diff --git a/DSView/pv/view/analogsignal.h b/DSView/pv/view/analogsignal.h old mode 100644 new mode 100755 index 82689bdc..b8185764 --- a/DSView/pv/view/analogsignal.h +++ b/DSView/pv/view/analogsignal.h @@ -39,12 +39,17 @@ namespace view { class AnalogSignal : public Signal { + Q_OBJECT + private: static const QColor SignalColours[4]; static const float EnvelopeThreshold; static const int NumSpanY = 5; static const int NumMiniSpanY = 5; static const int NumSpanX = 10; + + static const uint8_t DefaultBits = 8; + public: AnalogSignal(boost::shared_ptr dev_inst, boost::shared_ptr data, @@ -57,9 +62,11 @@ public: boost::shared_ptr data() const; - void set_scale(float scale); + void set_scale(int height); float get_scale() const; int get_bits() const; + double get_ref_min() const; + double get_ref_max() const; int get_hw_offset() const; int commit_settings(); @@ -68,19 +75,28 @@ public: **/ uint64_t get_vdiv() const; uint8_t get_acCoupling() const; + bool get_mapDefault() const; QString get_mapUnit() const; double get_mapMin() const; double get_mapMax() const; + uint64_t get_factor() const; /** * **/ void set_zero_vpos(int pos); int get_zero_vpos() const; - void set_zero_vrate(double rate, bool force_update); - double get_zero_vrate() const; - void update_vpos(); - void update_offset(); + void set_zero_ratio(double ratio); + double get_zero_ratio() const; + int get_zero_offset() const; + + /** + * + */ + int ratio2value(double ratio) const; + int ratio2pos(double ratio) const; + double value2ratio(int value) const; + double pos2ratio(int pos) const; /** * Event @@ -93,7 +109,7 @@ public: * @param left the x-coordinate of the left edge of the signal * @param right the x-coordinate of the right edge of the signal **/ - void paint_back(QPainter &p, int left, int right); + void paint_back(QPainter &p, int left, int right, QColor fore, QColor back); /** * Paints the signal with a QPainter @@ -101,7 +117,7 @@ public: * @param left the x-coordinate of the left edge of the signal. * @param right the x-coordinate of the right edge of the signal. **/ - void paint_mid(QPainter &p, int left, int right); + void paint_mid(QPainter &p, int left, int right, QColor fore, QColor back); private: void paint_trace(QPainter &p, @@ -109,14 +125,14 @@ private: int zeroY, const int start_pixel, const uint64_t start_index, const int64_t sample_count, const double samples_per_pixel, const int order, - const int top, const int bottom, const int width); + const float top, const float bottom, const int width); void paint_envelope(QPainter &p, const boost::shared_ptr &snapshot, int zeroY, const int start_pixel, const uint64_t start_index, const int64_t sample_count, const double samples_per_pixel, const int order, - const int top, const int bottom, const int width); + const float top, const float bottom, const int width); private: boost::shared_ptr _data; @@ -125,8 +141,10 @@ private: float _scale; double _zero_vrate; - int _hw_offset; + int _zero_offset; int _bits; + double _ref_min; + double _ref_max; }; } // namespace view diff --git a/DSView/pv/view/cursor.cpp b/DSView/pv/view/cursor.cpp old mode 100644 new mode 100755 index 4f96b1aa..c4818d64 --- a/DSView/pv/view/cursor.cpp +++ b/DSView/pv/view/cursor.cpp @@ -102,7 +102,7 @@ void Cursor::paint_label(QPainter &p, const QRect &rect, if (close.contains(QPoint(_view.hover_point().x(), _view.hover_point().y()))) p.setBrush(Ruler::CursorColor[(index - 1) % 8]); else if (r.contains(QPoint(_view.hover_point().x(), _view.hover_point().y()))) - p.setBrush(Ruler::HitColor); + p.setBrush(View::Orange); else p.setBrush(Ruler::CursorColor[(index - 1) % 8]); p.drawRect(r); @@ -115,9 +115,9 @@ void Cursor::paint_label(QPainter &p, const QRect &rect, p.drawPolygon(points, countof(points)); if (close.contains(QPoint(_view.hover_point().x(), _view.hover_point().y()))) - p.setBrush(Ruler::WarnColor); + p.setBrush(View::Red); else - p.setBrush(Ruler::HitColor); + p.setBrush(View::Orange); p.drawRect(close); p.setPen(Qt::black); p.drawLine(close.left() + 2, close.top() + 2, close.right() - 2, close.bottom() - 2); diff --git a/DSView/pv/view/cursor.h b/DSView/pv/view/cursor.h old mode 100644 new mode 100755 diff --git a/DSView/pv/view/decodetrace.cpp b/DSView/pv/view/decodetrace.cpp old mode 100644 new mode 100755 index 28f812eb..446da53f --- a/DSView/pv/view/decodetrace.cpp +++ b/DSView/pv/view/decodetrace.cpp @@ -170,9 +170,13 @@ void DecodeTrace::set_view(pv::view::View *view) Trace::set_view(view); } -void DecodeTrace::paint_back(QPainter &p, int left, int right) +void DecodeTrace::paint_back(QPainter &p, int left, int right, QColor fore, QColor back) { - QPen pen(Signal::dsGray); + (void)back; + + QColor backFore = fore; + backFore.setAlpha(View::BackAlpha); + QPen pen(backFore); pen.setStyle(Qt::DotLine); p.setPen(pen); const double sigY = get_y() - (_totalHeight - _view->get_signalHeight())*0.5; @@ -184,7 +188,7 @@ void DecodeTrace::paint_back(QPainter &p, int left, int right) const double endX = _decode_end/samples_per_pixel - _view->offset(); const double regionY = get_y() - _totalHeight*0.5 - ControlRectWidth; - p.setBrush(Signal::dsBlue); + p.setBrush(View::Blue); p.drawLine(startX, regionY, startX, regionY + _totalHeight + ControlRectWidth); p.drawLine(endX, regionY, endX, regionY + _totalHeight + ControlRectWidth); const QPointF start_points[] = { @@ -232,12 +236,12 @@ void DecodeTrace::paint_back(QPainter &p, int left, int right) // p.drawText(r.translated(dx, dy), f, h); // Draw the text - p.setPen(DARK_FORE); + p.setPen(fore); p.drawText(r, f, h); } } -void DecodeTrace::paint_mid(QPainter &p, int left, int right) +void DecodeTrace::paint_mid(QPainter &p, int left, int right, QColor fore, QColor back) { using namespace pv::data::decode; @@ -246,7 +250,6 @@ void DecodeTrace::paint_mid(QPainter &p, int left, int right) if (!err.isEmpty()) { draw_error(p, err, left, right); - //return; } const double scale = _view->scale(); @@ -312,10 +315,10 @@ void DecodeTrace::paint_mid(QPainter &p, int left, int right) draw_annotation(a, p, get_text_colour(), annotation_height, left, right, samples_per_pixel, pixels_offset, y, - 0, min_annWidth); + 0, min_annWidth, fore, back); } } else { - draw_nodetail(p, annotation_height, left, right, y, 0); + draw_nodetail(p, annotation_height, left, right, y, 0, fore, back); } y += annotation_height; _cur_row_headings.push_back(row.title()); @@ -323,20 +326,22 @@ void DecodeTrace::paint_mid(QPainter &p, int left, int right) } } } else { - draw_unshown_row(p, y, annotation_height, left, right, tr("Unshown")); + draw_unshown_row(p, y, annotation_height, left, right, tr("Unshown"), fore, back); y += annotation_height; _cur_row_headings.push_back(dec->decoder()->name); } } } -void DecodeTrace::paint_fore(QPainter &p, int left, int right) +void DecodeTrace::paint_fore(QPainter &p, int left, int right, QColor fore, QColor back) { using namespace pv::data::decode; (void)p; (void)left; (void)right; + (void)fore; + (void)back; } bool DecodeTrace::create_popup() @@ -483,7 +488,7 @@ void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form) void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a, QPainter &p, QColor text_color, int h, int left, int right, double samples_per_pixel, double pixels_offset, int y, - size_t base_colour, double min_annWidth) const + size_t base_colour, double min_annWidth, QColor fore, QColor back) const { const double start = max(a.start_sample() / samples_per_pixel - pixels_offset, (double)left); @@ -498,7 +503,7 @@ void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a, return; if (_decoder_stack->get_mark_index() == (int64_t)(a.start_sample()+ a.end_sample())/2) { - p.setPen(Signal::dsBlue); + p.setPen(View::Blue); int xpos = (start+end)/2; int ypos = get_y()+_totalHeight*0.5 + 1; const QPoint triangle[] = { @@ -520,7 +525,7 @@ void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a, start, y, min_annWidth); else { draw_range(a, p, fill, outline, text_color, h, - start, end, y); + start, end, y, fore, back); if ((a.type()/100 == 2) && (end - start > 20)) { BOOST_FOREACH(boost::shared_ptr dec, _decoder_stack->stack()) { @@ -545,16 +550,18 @@ void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a, void DecodeTrace::draw_nodetail(QPainter &p, int h, int left, int right, int y, - size_t base_colour) const + size_t base_colour, QColor fore, QColor back) const { (void)base_colour; + (void)back; + const QRectF nodetail_rect(left, y - h/2 + 0.5, right - left, h); QString info = tr("Zoom in for details"); int info_left = nodetail_rect.center().x() - p.boundingRect(QRectF(), 0, info).width(); int info_right = nodetail_rect.center().x() + p.boundingRect(QRectF(), 0, info).width(); int height = p.boundingRect(QRectF(), 0, info).height(); - p.setPen(Trace::DARK_FORE); + p.setPen(fore); p.drawLine(left, y, info_left, y); p.drawLine(info_right, y, right, y); p.drawLine(info_left, y, info_left+5, y - height/2 + 0.5); @@ -562,13 +569,15 @@ void DecodeTrace::draw_nodetail(QPainter &p, p.drawLine(info_right, y, info_right-5, y - height/2 + 0.5); p.drawLine(info_right, y, info_right-5, y + height/2 + 0.5); - p.setPen(Trace::DARK_FORE); + p.setPen(fore); p.drawText(nodetail_rect, Qt::AlignCenter | Qt::AlignVCenter, info); } void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p, QColor fill, QColor outline, QColor text_color, int h, double x, int y, double min_annWidth) const { + (void)outline; + const QString text = a.annotations().empty() ? QString() : a.annotations().back(); // const double w = min((double)p.boundingRect(QRectF(), 0, text).width(), @@ -576,7 +585,8 @@ void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter & const double w = min(min_annWidth, (double)h); const QRectF rect(x - w / 2, y - h / 2, w, h); - p.setPen(outline); + //p.setPen(outline); + p.setPen(QPen(Qt::NoPen)); p.setBrush(fill); p.drawRoundedRect(rect, h / 2, h / 2); @@ -589,8 +599,10 @@ void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter & void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p, QColor fill, QColor outline, QColor text_color, int h, double start, - double end, int y) const + double end, int y, QColor fore, QColor back) const { + (void)fore; + const double top = y + .5 - h / 2; const double bottom = y + .5 + h / 2; const vector annotations = a.annotations(); @@ -616,7 +628,7 @@ void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p, QPointF(start + cap_width, bottom) }; - p.setPen(DARK_BACK); + p.setPen(back); p.drawConvexPolygon(pts, countof(pts)); if (annotations.empty()) @@ -646,8 +658,8 @@ void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p, QFont font=p.font(); font.setPointSize(DefaultFontSize); p.setFont(font); - p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText( - best_annotation, Qt::ElideRight, rect.width())); + p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText( + best_annotation, Qt::ElideRight, rect.width())); } void DecodeTrace::draw_error(QPainter &p, const QString &message, @@ -670,14 +682,16 @@ void DecodeTrace::draw_error(QPainter &p, const QString &message, } void DecodeTrace::draw_unshown_row(QPainter &p, int y, int h, int left, - int right, QString info) + int right, QString info, QColor fore, QColor back) { + (void)back; + const QRectF unshown_rect(left, y - h/2 + 0.5, right - left, h); int info_left = unshown_rect.center().x() - p.boundingRect(QRectF(), 0, info).width(); int info_right = unshown_rect.center().x() + p.boundingRect(QRectF(), 0, info).width(); int height = p.boundingRect(QRectF(), 0, info).height(); - p.setPen(Trace::DARK_FORE); + p.setPen(fore); p.drawLine(left, y, info_left, y); p.drawLine(info_right, y, right, y); p.drawLine(info_left, y, info_left+5, y - height/2 + 0.5); @@ -685,7 +699,7 @@ void DecodeTrace::draw_unshown_row(QPainter &p, int y, int h, int left, p.drawLine(info_right, y, info_right-5, y - height/2 + 0.5); p.drawLine(info_right, y, info_right-5, y + height/2 + 0.5); - p.setPen(Trace::DARK_FORE); + p.setPen(fore); p.drawText(unshown_rect, Qt::AlignCenter | Qt::AlignVCenter, info); } @@ -700,18 +714,12 @@ void DecodeTrace::create_decoder_form( const srd_decoder *const decoder = dec->decoder(); assert(decoder); - pv::widgets::DecoderGroupBox *const group = - new pv::widgets::DecoderGroupBox(decoder_stack, dec, parent); - connect(group, SIGNAL(del_stack(boost::shared_ptr&)), - this, SLOT(on_del_stack(boost::shared_ptr&))); - QFormLayout *const decoder_form = new QFormLayout(); decoder_form->setContentsMargins(0,0,0,0); decoder_form->setVerticalSpacing(5); decoder_form->setFormAlignment(Qt::AlignLeft); decoder_form->setLabelAlignment(Qt::AlignLeft); decoder_form->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); - group->add_layout(decoder_form); // Add the mandatory channels for(l = decoder->channels; l; l = l->next) { @@ -750,6 +758,12 @@ void DecodeTrace::create_decoder_form( _bindings.push_back(binding); + // + pv::widgets::DecoderGroupBox *const group = + new pv::widgets::DecoderGroupBox(decoder_stack, dec, decoder_form, parent); + connect(group, SIGNAL(del_stack(boost::shared_ptr&)), + this, SLOT(on_del_stack(boost::shared_ptr&))); + form->addRow(group); _decoder_forms.push_back(group); } @@ -916,7 +930,7 @@ int DecodeTrace::rows_size() return size == 0 ? 1 : size; } -void DecodeTrace::paint_type_options(QPainter &p, int right, const QPoint pt) +void DecodeTrace::paint_type_options(QPainter &p, int right, const QPoint pt, QColor fore) { (void)pt; @@ -924,7 +938,7 @@ void DecodeTrace::paint_type_options(QPainter &p, int right, const QPoint pt) const QRectF group_index_rect = get_rect(CHNLREG, y, right); QString index_string; int last_index; - p.setPen(QPen(DARK_FORE, 1, Qt::DashLine)); + p.setPen(QPen(fore, 1, Qt::DashLine)); p.drawLine(group_index_rect.bottomLeft(), group_index_rect.bottomRight()); std::list::iterator i = _index_list.begin(); last_index = (*i); @@ -938,7 +952,7 @@ void DecodeTrace::paint_type_options(QPainter &p, int right, const QPoint pt) index_string = QString::number((*i)) + "," + index_string; last_index = (*i); } - p.setPen(DARK_FORE); + p.setPen(fore); p.drawText(group_index_rect, Qt::AlignRight | Qt::AlignVCenter, index_string); } diff --git a/DSView/pv/view/decodetrace.h b/DSView/pv/view/decodetrace.h old mode 100644 new mode 100755 index bf93e440..1fcb1257 --- a/DSView/pv/view/decodetrace.h +++ b/DSView/pv/view/decodetrace.h @@ -114,7 +114,7 @@ public: * @param left the x-coordinate of the left edge of the signal. * @param right the x-coordinate of the right edge of the signal. **/ - void paint_back(QPainter &p, int left, int right); + void paint_back(QPainter &p, int left, int right, QColor fore, QColor back); /** * Paints the mid-layer of the trace with a QPainter @@ -122,7 +122,7 @@ public: * @param left the x-coordinate of the left edge of the signal * @param right the x-coordinate of the right edge of the signal **/ - void paint_mid(QPainter &p, int left, int right); + void paint_mid(QPainter &p, int left, int right, QColor fore, QColor back); /** * Paints the foreground layer of the trace with a QPainter @@ -130,7 +130,7 @@ public: * @param left the x-coordinate of the left edge of the signal * @param right the x-coordinate of the right edge of the signal **/ - void paint_fore(QPainter &p, int left, int right); + void paint_fore(QPainter &p, int left, int right, QColor fore, QColor back); bool create_popup(); @@ -146,34 +146,34 @@ public: int get_progress() const; protected: - void paint_type_options(QPainter &p, int right, const QPoint pt); + void paint_type_options(QPainter &p, int right, const QPoint pt, QColor fore); private: void create_popup_form(); void populate_popup_form(QWidget *parent, QFormLayout *form); - void draw_annotation(const pv::data::decode::Annotation &a, QPainter &p, - QColor text_colour, int text_height, int left, int right, - double samples_per_pixel, double pixels_offset, int y, - size_t base_colour, double min_annWidth) const; + void draw_annotation(const pv::data::decode::Annotation &a, QPainter &p, + QColor text_colour, int text_height, int left, int right, + double samples_per_pixel, double pixels_offset, int y, + size_t base_colour, double min_annWidth, QColor fore, QColor back) const; void draw_nodetail(QPainter &p, int text_height, int left, int right, int y, - size_t base_colour) const; + size_t base_colour, QColor fore, QColor back) const; void draw_instant(const pv::data::decode::Annotation &a, QPainter &p, QColor fill, QColor outline, QColor text_color, int h, double x, int y, double min_annWidth) const; - void draw_range(const pv::data::decode::Annotation &a, QPainter &p, - QColor fill, QColor outline, QColor text_color, int h, double start, - double end, int y) const; + void draw_range(const pv::data::decode::Annotation &a, QPainter &p, + QColor fill, QColor outline, QColor text_color, int h, double start, + double end, int y, QColor fore, QColor back) const; void draw_error(QPainter &p, const QString &message, int left, int right); void draw_unshown_row(QPainter &p, int y, int h, int left, - int right, QString info); + int right, QString info, QColor fore, QColor back); void create_decoder_form(boost::shared_ptr &decoder_stack, boost::shared_ptr &dec, diff --git a/DSView/pv/view/devmode.cpp b/DSView/pv/view/devmode.cpp old mode 100644 new mode 100755 index fba98016..8b18d147 --- a/DSView/pv/view/devmode.cpp +++ b/DSView/pv/view/devmode.cpp @@ -24,6 +24,7 @@ #include "trace.h" #include "../sigsession.h" #include "../device/devinst.h" +#include "../device/file.h" #include @@ -44,51 +45,97 @@ namespace view { DevMode::DevMode(QWidget *parent, SigSession &session) : QWidget(parent), - _session(session), - _layout(new QGridLayout(this)) + _session(session) { - _layout->setMargin(5); + _layout = new QHBoxLayout(this); + _layout->setMargin(0); _layout->setSpacing(0); + _layout->setContentsMargins(2, 0, 0, 0); + + _close_button = new QToolButton(this); + _close_button->setObjectName("FileCloseButton"); + _close_button->setContentsMargins(0, 0, 0, 0); + _close_button->setFixedWidth(10); + _close_button->setFixedHeight(height()); + _close_button->setIconSize(QSize(10, 10)); + _close_button->setToolButtonStyle(Qt::ToolButtonIconOnly); + + _pop_menu = new QMenu(this); + + _mode_btn = new QToolButton(this); + _mode_btn->setObjectName("ModeButton"); + _mode_btn->setIconSize(QSize(48, 48)); + _mode_btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + _mode_btn->setContentsMargins(0, 0, 1000, 0); + _mode_btn->setMenu(_pop_menu); + _mode_btn->setPopupMode(QToolButton::InstantPopup); + _mode_btn->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum); + + _layout->addWidget(_close_button); + _layout->addWidget(_mode_btn); + //_layout->addWidget(new QWidget(this)); + _layout->setStretch(1, 100); setLayout(_layout); } + +void DevMode::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) + set_device(); + else if (event->type() == QEvent::StyleChange) + set_device(); + QWidget::changeEvent(event); +} + void DevMode::set_device() { - int index = 0; const boost::shared_ptr dev_inst = _session.get_device(); - assert(dev_inst); - for(std::map::const_iterator i = _mode_button_list.begin(); - i != _mode_button_list.end(); i++) { + for(std::map::const_iterator i = _mode_list.begin(); + i != _mode_list.end(); i++) { (*i).first->setParent(NULL); - _layout->removeWidget((*i).first); + _pop_menu->removeAction((*i).first); delete (*i).first; } - _mode_button_list.clear(); + _mode_list.clear(); + _close_button->setIcon(QIcon()); + _close_button->setDisabled(true); + disconnect(_close_button, SIGNAL(clicked()), this, SLOT(on_close())); + QString iconPath = ":/icons/" + qApp->property("Style").toString(); for (const GSList *l = dev_inst->get_dev_mode_list(); l; l = l->next) { const sr_dev_mode *mode = (const sr_dev_mode *)l->data; + QString icon_name = "/" + QString::fromLocal8Bit(mode->icon); - QPushButton *mode_button = new QPushButton(this); - //mode_button->setFlat(true); - mode_button->setMinimumWidth(32); - mode_button->setText(mode->name); - mode_button->setCheckable(true); + QAction *action = new QAction(this); + action->setIcon(QIcon(iconPath+icon_name)); + if (qApp->property("Language") == QLocale::Chinese) + action->setText(mode->name_cn); + else + action->setText(mode->name); + connect(action, SIGNAL(triggered()), this, SLOT(on_mode_change())); - _mode_button_list[mode_button] = mode; - if (dev_inst->dev_inst()->mode == _mode_button_list[mode_button]->mode) - mode_button->setChecked(true); + _mode_list[action] = mode; + if (dev_inst->dev_inst()->mode == _mode_list[action]->mode) { + _mode_btn->setIcon(QIcon(iconPath+icon_name)); + if (qApp->property("Language") == QLocale::Chinese) + _mode_btn->setText(mode->name_cn); + else + _mode_btn->setText(mode->name); + } + _pop_menu->addAction(action); + } - connect(mode_button, SIGNAL(clicked()), this, SLOT(on_mode_change())); - - _layout->addWidget(mode_button, index / GRID_COLS, index % GRID_COLS); - //layout->addWidget(new QWidget(), index / GRID_COLS, GRID_COLS); - _layout->setColumnStretch(GRID_COLS, 1); - index++; - } + boost::shared_ptr file_dev; + if((file_dev = dynamic_pointer_cast(dev_inst))) { + _close_button->setDisabled(false); + _close_button->setIcon(QIcon(iconPath+"/close.png")); + connect(_close_button, SIGNAL(clicked()), this, SLOT(on_close())); + } update(); } @@ -106,14 +153,14 @@ void DevMode::on_mode_change() { const boost::shared_ptr dev_inst = _session.get_device(); assert(dev_inst); - QPushButton *button = qobject_cast(sender()); - button->setChecked(true); - if (dev_inst->dev_inst()->mode == _mode_button_list[button]->mode) + QAction *action = qobject_cast(sender()); + if (dev_inst->dev_inst()->mode == _mode_list[action]->mode) return; - for(std::map::const_iterator i = _mode_button_list.begin(); - i != _mode_button_list.end(); i++) { - if ((*i).first == button) { + QString iconPath = ":/icons/" + qApp->property("Style").toString(); + for(std::map::const_iterator i = _mode_list.begin(); + i != _mode_list.end(); i++) { + if ((*i).first == action) { if (dev_inst->dev_inst()->mode != (*i).second->mode) { _session.set_run_mode(SigSession::Single); _session.set_repeating(false); @@ -123,15 +170,28 @@ void DevMode::on_mode_change() dev_inst->set_config(NULL, NULL, SR_CONF_DEVICE_MODE, g_variant_new_int16((*i).second->mode)); - mode_changed(); - button->setChecked(true); + + QString icon_name = "/" + QString::fromLocal8Bit((*i).second->icon); + _mode_btn->setIcon(QIcon(iconPath+icon_name)); + if (qApp->property("Language") == QLocale::Chinese) + _mode_btn->setText((*i).second->name_cn); + else + _mode_btn->setText((*i).second->name); + dev_changed(false); } - } else { - (*i).first->setChecked(false); } } } +void DevMode::on_close() +{ + const boost::shared_ptr dev_inst = _session.get_device(); + assert(dev_inst); + + _session.close_file(dev_inst); + dev_changed(true); +} + void DevMode::mousePressEvent(QMouseEvent *event) { assert(event); diff --git a/DSView/pv/view/devmode.h b/DSView/pv/view/devmode.h old mode 100644 new mode 100755 index fac83c34..999f1df0 --- a/DSView/pv/view/devmode.h +++ b/DSView/pv/view/devmode.h @@ -33,8 +33,10 @@ #include #include -#include +#include #include +#include +#include #include @@ -67,21 +69,27 @@ private: void mouseMoveEvent(QMouseEvent *event); void leaveEvent(QEvent *event); + void changeEvent(QEvent *event); + public slots: void set_device(); void on_mode_change(); + void on_close(); private slots: signals: - void mode_changed(); + void dev_changed(bool close); private: SigSession &_session; - QGridLayout * _layout; - std::map _mode_button_list; + QHBoxLayout * _layout; + std::map _mode_list; + QToolButton *_mode_btn; + QMenu *_pop_menu; QPoint _mouse_point; + QToolButton *_close_button; }; } // namespace view diff --git a/DSView/pv/view/dsldial.cpp b/DSView/pv/view/dsldial.cpp old mode 100644 new mode 100755 index 31e9eb8b..f20c872c --- a/DSView/pv/view/dsldial.cpp +++ b/DSView/pv/view/dsldial.cpp @@ -1,166 +1,186 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2014 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "dsldial.h" - -#include -#include - -namespace pv { -namespace view { - -dslDial::dslDial(const uint64_t div, const uint64_t step, - const QVector value, const QVector unit) -{ - assert(div > 0); - assert(step > 0); - assert((uint64_t)value.count() == div); - assert(unit.count() > 0); - - _div = div; - _step = step; - _value = value; - _unit = unit; - _sel = 0; - _factor = 1; -} - -dslDial::~dslDial() -{ -} - -void dslDial::paint(QPainter &p, QRectF dialRect, QColor dialColor, const QPoint pt) -{ - p.setRenderHint(QPainter::Antialiasing, true); - p.setPen(dialColor); - p.setBrush(dialColor); - - int dialMarginAngle = 15 * 16; - int dialStartAngle = 75 * 16; - int dialSpanAngle = -150 * 16; - - // draw dial arc - p.drawArc(dialRect, dialStartAngle + dialMarginAngle, - dialSpanAngle - dialMarginAngle * 2); - // draw ticks - p.save(); - p.translate(dialRect.center()); - p.rotate(270 - dialStartAngle/16); - // draw pointer - p.rotate(-dialSpanAngle/16.0/(_div-1)*_sel); - p.drawEllipse(-3, -3, 6, 6); - p.drawLine(3, 0, 0, dialRect.width()/2-3); - p.drawLine(-3, 0, 0, dialRect.width()/2-3); - p.rotate(+dialSpanAngle/16.0/(_div-1)*_sel); - for (uint64_t i = 0; i < _div; i++) { - // draw major ticks - p.drawLine(0, dialRect.width()/2+3, 0, dialRect.width()/2+8); - // draw minor ticks - for (uint64_t j = 0; (j < 5) && (i < _div - 1); j++) { - p.drawLine(0, dialRect.width()/2+3, 0, dialRect.width()/2+5); - p.rotate(-dialSpanAngle/16/5.0/(_div-1)); - } - } - p.restore(); - // draw value - uint64_t displayValue = _value[_sel]*_factor; - uint64_t displayIndex = 0; - while(displayValue / _step >= 1) { - displayValue = displayValue / _step; - displayIndex++; - } - QString pText = QString::number(displayValue) + _unit[displayIndex] + tr(" / div"); - QFontMetrics fm(p.font()); - const QRectF valueRect = QRectF(dialRect.left(), dialRect.top()-fm.height()-10, dialRect.width(), fm.height()); - p.drawText(valueRect, Qt::AlignCenter, pText); - - // draw +/- - if (dialRect.contains(pt) && pt.x() > dialRect.center().x()) { - const int arcInc = 12; - const QRectF hoverRect = QRectF(dialRect.left()-arcInc, dialRect.top()-arcInc, - dialRect.width()+arcInc*2, dialRect.height()+arcInc*2); - p.drawArc(hoverRect, dialStartAngle + dialSpanAngle/4, dialSpanAngle/2); - p.save(); - p.translate(hoverRect.center()); - const bool inc = pt.y() > dialRect.center().y(); - if (inc) - p.rotate(270-(dialStartAngle/16 + dialSpanAngle/16/4 + dialSpanAngle/16/2)); - else - p.rotate(270-(dialStartAngle/16 + dialSpanAngle/16/4)); - p.drawLine(0, hoverRect.width()/2, - inc ? 10 : -10, hoverRect.width()/2 + 4); - p.restore(); - } -} - -void dslDial::set_sel(uint64_t sel) -{ - assert(sel < _div); - - _sel = sel; -} - -uint64_t dslDial::get_sel() -{ - return _sel; -} - -bool dslDial::isMin() -{ - if(_sel == 0) - return true; - else - return false; -} - -bool dslDial::isMax() -{ - if(_sel == _div - 1) - return true; - else - return false; -} - -uint64_t dslDial::get_value() -{ - return _value[_sel]; -} - -void dslDial::set_value(uint64_t value) -{ - assert(_value.contains(value)); - _sel = _value.indexOf(value, 0); -} - -void dslDial::set_factor(uint64_t factor) -{ - if (_factor != factor) { - _factor = factor; - } -} - -uint64_t dslDial::get_factor() -{ - return _factor; -} - -} // namespace view -} // namespace pv +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2014 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "dsldial.h" + +#include +#include + +namespace pv { +namespace view { + +dslDial::dslDial(const uint64_t div, const uint64_t step, + const QVector value, const QVector unit) +{ + assert(div > 0); + assert(step > 0); + assert((uint64_t)value.count() == div); + assert(unit.count() > 0); + + _div = div; + _step = step; + _value = value; + _unit = unit; + _sel = 0; + _factor = 1; +} + +dslDial::~dslDial() +{ +} + +void dslDial::paint(QPainter &p, QRectF dialRect, QColor dialColor, const QPoint pt, QString &pText) +{ + p.setPen(dialColor); + p.setBrush(dialColor); + + int dialMarginAngle = 15 * 16; + int dialStartAngle = 75 * 16; + int dialSpanAngle = -150 * 16; + + // draw dial arc + p.drawArc(dialRect, dialStartAngle + dialMarginAngle, + dialSpanAngle - dialMarginAngle * 2); + // draw ticks + p.save(); + p.translate(dialRect.center()); + p.rotate(270 - dialStartAngle/16); + // draw pointer + p.rotate(-dialSpanAngle/16.0/(_div-1)*_sel); + p.drawEllipse(-3, -3, 6, 6); + p.drawLine(3, 0, 0, dialRect.width()/2-3); + p.drawLine(-3, 0, 0, dialRect.width()/2-3); + p.rotate(+dialSpanAngle/16.0/(_div-1)*_sel); + for (uint64_t i = 0; i < _div; i++) { + // draw major ticks + p.drawLine(0, dialRect.width()/2+3, 0, dialRect.width()/2+8); + // draw minor ticks + for (uint64_t j = 0; (j < 5) && (i < _div - 1); j++) { + p.drawLine(0, dialRect.width()/2+3, 0, dialRect.width()/2+5); + p.rotate(-dialSpanAngle/16/5.0/(_div-1)); + } + } + p.restore(); + // draw value + uint64_t displayValue = _value[_sel]*_factor; + uint64_t displayIndex = 0; + while(displayValue / _step >= 1) { + displayValue = displayValue / _step; + displayIndex++; + } + pText = QString::number(displayValue) + _unit[displayIndex] + tr("/div"); +// QFontMetrics fm(p.font()); +// const QRectF valueRect = QRectF(dialRect.left(), dialRect.top()-fm.height()-10, dialRect.width(), fm.height()); +// p.drawText(valueRect, Qt::AlignCenter, pText); + + // draw +/- + if (dialRect.contains(pt) && pt.x() > dialRect.center().x()) { + const int arcInc = 12; + const QRectF hoverRect = QRectF(dialRect.left()-arcInc, dialRect.top()-arcInc, + dialRect.width()+arcInc*2, dialRect.height()+arcInc*2); + p.drawArc(hoverRect, dialStartAngle + dialSpanAngle/4, dialSpanAngle/2); + p.save(); + p.translate(hoverRect.center()); + const bool inc = pt.y() > dialRect.center().y(); + if (inc) + p.rotate(270-(dialStartAngle/16 + dialSpanAngle/16/4 + dialSpanAngle/16/2)); + else + p.rotate(270-(dialStartAngle/16 + dialSpanAngle/16/4)); + p.drawLine(0, hoverRect.width()/2, + inc ? 10 : -10, hoverRect.width()/2 + 4); + p.restore(); + } +} + +void dslDial::set_sel(uint64_t sel) +{ + assert(sel < _div); + + _sel = sel; +} + +uint64_t dslDial::get_sel() const +{ + return _sel; +} + +uint64_t dslDial::get_count() const +{ + return _div; +} + +bool dslDial::isMin() +{ + if(_sel == 0) + return true; + else + return false; +} + +bool dslDial::isMax() +{ + if(_sel == _div - 1) + return true; + else + return false; +} + +uint64_t dslDial::get_min() const +{ + return _value[0]; +} + +uint64_t dslDial::get_max() const +{ + return _value[_div-1]; +} + +uint64_t dslDial::get_value() const +{ + return _value[_sel]; +} + +uint64_t dslDial::get_value(uint64_t i) const +{ + assert(i < _div); + return _value[i]; +} + +void dslDial::set_value(uint64_t value) +{ + assert(_value.contains(value)); + _sel = _value.indexOf(value, 0); +} + +void dslDial::set_factor(uint64_t factor) +{ + if (_factor != factor) { + _factor = factor; + } +} + +uint64_t dslDial::get_factor() const +{ + return _factor; +} + +} // namespace view +} // namespace pv diff --git a/DSView/pv/view/dsldial.h b/DSView/pv/view/dsldial.h old mode 100644 new mode 100755 index 1692ff3f..973e0e6e --- a/DSView/pv/view/dsldial.h +++ b/DSView/pv/view/dsldial.h @@ -1,77 +1,81 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef DSVIEW_PV_VIEW_DSLDIAL_H -#define DSVIEW_PV_VIEW_DSLDIAL_H - -#include -#include - -namespace pv { -namespace view { - -class dslDial : public QObject -{ - //Q_OBJECT - -public: - dslDial(const uint64_t div, const uint64_t step, - const QVector value, const QVector unit); - virtual ~dslDial(); - -public: - /** - * Paints the dial with a QPainter - * @param p the QPainter to paint into. - * @param dialRect the rectangle to draw the dial at. - **/ - void paint(QPainter &p, QRectF dialRect, QColor dialColor, - const QPoint pt); - - // set/get current select - void set_sel(uint64_t sel); - uint64_t get_sel(); - - // boundary detection - bool isMin(); - bool isMax(); - - // get current value - uint64_t get_value(); - void set_value(uint64_t value); - - // set/get factor - void set_factor(uint64_t factor); - uint64_t get_factor(); - -private: - uint64_t _div; - uint64_t _step; - QVector _value; - QVector _unit; - uint64_t _sel; - uint64_t _factor; -}; - -} // namespace view -} // namespace pv - -#endif // DSVIEW_PV_VIEW_DSLDIAL_H +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DSVIEW_PV_VIEW_DSLDIAL_H +#define DSVIEW_PV_VIEW_DSLDIAL_H + +#include +#include + +namespace pv { +namespace view { + +class dslDial : public QObject +{ + //Q_OBJECT + +public: + dslDial(const uint64_t div, const uint64_t step, + const QVector value, const QVector unit); + virtual ~dslDial(); + +public: + /** + * Paints the dial with a QPainter + * @param p the QPainter to paint into. + * @param dialRect the rectangle to draw the dial at. + **/ + void paint(QPainter &p, QRectF dialRect, QColor dialColor, + const QPoint pt, QString &pText); + + // set/get current select + void set_sel(uint64_t sel); + uint64_t get_sel() const; + uint64_t get_count() const; + + // boundary detection + bool isMin(); + bool isMax(); + uint64_t get_min() const; + uint64_t get_max() const; + + // get current value + uint64_t get_value() const; + uint64_t get_value(uint64_t i) const; + void set_value(uint64_t value); + + // set/get factor + void set_factor(uint64_t factor); + uint64_t get_factor() const; + +private: + uint64_t _div; + uint64_t _step; + QVector _value; + QVector _unit; + uint64_t _sel; + uint64_t _factor; +}; + +} // namespace view +} // namespace pv + +#endif // DSVIEW_PV_VIEW_DSLDIAL_H diff --git a/DSView/pv/view/dsosignal.cpp b/DSView/pv/view/dsosignal.cpp old mode 100644 new mode 100755 index 951790f3..a8764780 --- a/DSView/pv/view/dsosignal.cpp +++ b/DSView/pv/view/dsosignal.cpp @@ -42,8 +42,8 @@ namespace pv { namespace view { const QString DsoSignal::vDialUnit[DsoSignal::vDialUnitCount] = { - "mv", - "v", + "mV", + "V", }; const QColor DsoSignal::SignalColours[4] = { @@ -63,15 +63,18 @@ DsoSignal::DsoSignal(boost::shared_ptr dev_inst, _data(data), _scale(0), _en_lock(false), + _show(true), _vDialActive(false), + _mValid(false), + _level_valid(false), _autoV(false), _autoH(false), + _autoV_over(false), + _auto_cnt(0), _hover_en(false), _hover_index(0), _hover_point(QPointF(0, 0)), - _hover_value(0), - _ms_gear_hover(false), - _ms_show_hover(false) + _hover_value(0) { QVector vValue; QVector vUnit; @@ -87,9 +90,10 @@ DsoSignal::DsoSignal(boost::shared_ptr dev_inst, GVariant *gvar; GVariantIter iter; g_variant_iter_init(&iter, gvar_list_vdivs); - while(NULL != (gvar = g_variant_iter_next_value(&iter))) + while(NULL != (gvar = g_variant_iter_next_value(&iter))) { vValue.push_back(g_variant_get_uint64(gvar)); - g_variant_unref(gvar); + g_variant_unref(gvar); + } g_variant_unref(gvar_list_vdivs); g_variant_unref(gvar_list); } @@ -114,22 +118,9 @@ boost::shared_ptr DsoSignal::dso_data() const return _data; } -void DsoSignal::set_viewport(pv::view::Viewport *viewport) -{ - Trace::set_viewport(viewport); - - const double ms_left = get_view_rect().right() - (MS_RectWidth + MS_RectMargin) * (get_index() + 1); - const double ms_top = get_view_rect().top() + 5; - for (int i = DSO_MS_BEGIN; i < DSO_MS_END; i++) - _ms_rect[i] = QRect(ms_left, ms_top + MS_RectHeight * i, MS_RectWidth, MS_RectHeight); - _ms_gear_rect = QRect(ms_left+MS_RectRad, ms_top+MS_RectRad, MS_IconSize, MS_IconSize); - _ms_show_rect = QRect(ms_left+MS_RectWidth-MS_RectRad-MS_IconSize, ms_top+MS_RectRad, MS_IconSize, MS_IconSize); - -} - void DsoSignal::set_scale(int height) { - _scale = height * 1.0f / (1 << _bits); + _scale = height / (_ref_max - _ref_min); } float DsoSignal::get_scale() @@ -137,6 +128,21 @@ float DsoSignal::get_scale() return _scale; } +uint8_t DsoSignal::get_bits() +{ + return _bits; +} + +double DsoSignal::get_ref_min() const +{ + return _ref_min; +} + +double DsoSignal::get_ref_max() const +{ + return _ref_max; +} + int DsoSignal::get_name_width() const { return 0; @@ -198,8 +204,11 @@ void DsoSignal::set_vDialActive(bool active) _vDialActive = active; } -bool DsoSignal::go_vDialPre() +bool DsoSignal::go_vDialPre(bool manul) { + if (_autoV && manul) + autoV_end(); + if (enabled() && !_vDial->isMin()) { if (_view->session().get_capture_state() == SigSession::Running) _view->session().refresh(RefreshShort); @@ -209,20 +218,25 @@ bool DsoSignal::go_vDialPre() g_variant_new_uint64(_vDial->get_value())); if (_view->session().get_capture_state() == SigSession::Stopped) _scale *= pre_vdiv/_vDial->get_value(); - update_vpos(); - _view->update_calibration(); + _dev_inst->set_config(_probe, NULL, SR_CONF_PROBE_OFFSET, + g_variant_new_uint16(_zero_offset)); + + _view->vDial_updated(); _view->set_update(_viewport, true); _view->update(); return true; } else { - if (_autoV) + if (_autoV && !_autoV_over) autoV_end(); return false; } } -bool DsoSignal::go_vDialNext() +bool DsoSignal::go_vDialNext(bool manul) { + if (_autoV && manul) + autoV_end(); + if (enabled() && !_vDial->isMax()) { if (_view->session().get_capture_state() == SigSession::Running) _view->session().refresh(RefreshShort); @@ -232,13 +246,15 @@ bool DsoSignal::go_vDialNext() g_variant_new_uint64(_vDial->get_value())); if (_view->session().get_capture_state() == SigSession::Stopped) _scale *= pre_vdiv/_vDial->get_value(); - update_vpos(); - _view->update_calibration(); + _dev_inst->set_config(_probe, NULL, SR_CONF_PROBE_OFFSET, + g_variant_new_uint16(_zero_offset)); + + _view->vDial_updated(); _view->set_update(_viewport, true); _view->update(); return true; } else { - if (_autoV) + if (_autoV && !_autoV_over) autoV_end(); return false; } @@ -270,6 +286,20 @@ bool DsoSignal::load_settings() if (strncmp(_dev_inst->name().toLocal8Bit(), "virtual", 7)) return false; } + gvar = _dev_inst->get_config(NULL, NULL, SR_CONF_REF_MIN); + if (gvar != NULL) { + _ref_min = g_variant_get_uint32(gvar); + g_variant_unref(gvar); + } else { + _ref_min = 1; + } + gvar = _dev_inst->get_config(NULL, NULL, SR_CONF_REF_MAX); + if (gvar != NULL) { + _ref_max = g_variant_get_uint32(gvar); + g_variant_unref(gvar); + } else { + _ref_max = ((1 << _bits) - 1); + } // -- vdiv uint64_t vdiv; @@ -310,24 +340,20 @@ bool DsoSignal::load_settings() // g_variant_new_byte(_acCoupling)); // -- vpos - double vpos; - gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_PROBE_VPOS); + gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_PROBE_OFFSET); if (gvar != NULL) { - vpos = g_variant_get_double(gvar); + _zero_offset = g_variant_get_uint16(gvar); g_variant_unref(gvar); } else { - qDebug() << "ERROR: config_get SR_CONF_PROBE_VPOS failed."; + qDebug() << "ERROR: config_get SR_CONF_PROBE_OFFSET failed."; return false; } - _zero_vrate = min(max((0.5 - vpos / (_vDial->get_value() * DS_CONF_DSO_VDIVS)), 0.0), 1.0); - if (_dev_inst->name().contains("virtual")) - _hw_offset = _zero_vrate * ((1 << _bits) - 1); // -- trig_value gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_TRIGGER_VALUE); if (gvar != NULL) { _trig_value = g_variant_get_byte(gvar); - _trig_delta = get_trig_vrate() - _zero_vrate; + _trig_delta = get_trig_vrate() - get_zero_ratio(); g_variant_unref(gvar); } else { qDebug() << "ERROR: config_get SR_CONF_TRIGGER_VALUE failed."; @@ -359,10 +385,9 @@ int DsoSignal::commit_settings() ret = _dev_inst->set_config(_probe, NULL, SR_CONF_PROBE_COUPLING, g_variant_new_byte(_acCoupling)); - // -- vpos - double vpos_off = (0.5 - (get_zero_vpos() - UpMargin) * 1.0/get_view_rect().height()) * _vDial->get_value() * DS_CONF_DSO_VDIVS; - ret = _dev_inst->set_config(_probe, NULL, SR_CONF_PROBE_VPOS, - g_variant_new_double(vpos_off)); + // -- offset + ret = _dev_inst->set_config(_probe, NULL, SR_CONF_PROBE_OFFSET, + g_variant_new_uint16(_zero_offset)); // -- trig_value _dev_inst->set_config(_probe, NULL, SR_CONF_TRIGGER_VALUE, @@ -371,6 +396,11 @@ int DsoSignal::commit_settings() return ret; } +dslDial * DsoSignal::get_vDial() const +{ + return _vDial; +} + uint64_t DsoSignal::get_vDialValue() const { return _vDial->get_value(); @@ -395,94 +425,96 @@ void DsoSignal::set_acCoupling(uint8_t coupling) } } -int DsoSignal::get_trig_vpos() const +int DsoSignal::ratio2value(double ratio) const { - return get_trig_vrate() * get_view_rect().height() + UpMargin; + return ratio * (_ref_max - _ref_min) + _ref_min; +} + +int DsoSignal::ratio2pos(double ratio) const +{ + return ratio * get_view_rect().height() + get_view_rect().top(); +} + +double DsoSignal::value2ratio(int value) const +{ + return max(0.0, (value - _ref_min) / (_ref_max - _ref_min)); +} + +double DsoSignal::pos2ratio(int pos) const +{ + return min(max(pos - get_view_rect().top(), 0), get_view_rect().height()) * 1.0 / get_view_rect().height(); } double DsoSignal::get_trig_vrate() const { if (_dev_inst->name() == "DSLogic") - return (_trig_value - (1 << (_bits - 1)))* 1.0 / ((1 << _bits) - 1.0) + _zero_vrate; + return value2ratio(_trig_value - ratio2value(0.5)) + get_zero_ratio(); else - return _trig_value * 1.0 / ((1 << _bits) - 1.0); + return value2ratio(_trig_value); } void DsoSignal::set_trig_vpos(int pos, bool delta_change) { assert(_view); if (enabled()) { - double delta = min(max(pos - UpMargin, 0), get_view_rect().height()) * 1.0 / get_view_rect().height(); - if (_dev_inst->name() == "DSLogic") { - delta = delta - _zero_vrate; - delta = min(delta, 0.5); - delta = max(delta, -0.5); - _trig_value = delta * ((1 << _bits) -1) + (1 << (_bits - 1)); - } else { - _trig_value = delta * ((1 << _bits) -1) + 0.5; - } - int margin = TrigMargin; - _trig_value = std::min(std::max(_trig_value, margin), ((1 << _bits) - margin - 1)); - if (delta_change) - _trig_delta = get_trig_vrate() - _zero_vrate; - _dev_inst->set_config(_probe, NULL, SR_CONF_TRIGGER_VALUE, - g_variant_new_byte(_trig_value)); + set_trig_ratio(pos2ratio(pos), delta_change); } } -void DsoSignal::set_trig_vrate(double rate) +void DsoSignal::set_trig_ratio(double ratio, bool delta_change) { - double delta = rate; + double delta = ratio; if (_dev_inst->name() == "DSLogic") { - delta = delta - _zero_vrate; + delta = delta - get_zero_ratio(); delta = min(delta, 0.5); delta = max(delta, -0.5); - _trig_value = delta * ((1 << _bits) - 1) + (1 << (_bits - 1)); + _trig_value = ratio2value(delta + 0.5); } else { - _trig_value = delta * ((1 << _bits) - 1) + 0.5; + _trig_value = ratio2value(delta); } - _trig_delta = get_trig_vrate() - _zero_vrate; + + int margin = TrigMargin; + _trig_value = std::min(std::max(_trig_value, margin), (ratio2value(1) - margin)); + if (delta_change) + _trig_delta = get_trig_vrate() - get_zero_ratio(); _dev_inst->set_config(_probe, NULL, SR_CONF_TRIGGER_VALUE, g_variant_new_byte(_trig_value)); } int DsoSignal::get_zero_vpos() const { - return _zero_vrate * get_view_rect().height() + UpMargin; + return ratio2pos(get_zero_ratio()); } -double DsoSignal::get_zero_vrate() +double DsoSignal::get_zero_ratio() const { - return _zero_vrate; + return value2ratio(_zero_offset); } -double DsoSignal::get_hw_offset() +int DsoSignal::get_hw_offset() const { - return _hw_offset; + int hw_offset = 0; + GVariant *gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_PROBE_HW_OFFSET); + if (gvar != NULL) { + hw_offset = g_variant_get_uint16(gvar); + g_variant_unref(gvar); + } + return hw_offset; } void DsoSignal::set_zero_vpos(int pos) { if (enabled()) { - double delta = _trig_delta* get_view_rect().height(); - set_zero_vrate(min(max(pos - UpMargin, 0), get_view_rect().height()) * 1.0 / get_view_rect().height(), false); - set_trig_vpos(get_zero_vpos() + delta, false); + set_zero_ratio(pos2ratio(pos)); + set_trig_ratio(_trig_delta + get_zero_ratio(), false); } } -void DsoSignal::set_zero_vrate(double rate, bool force_update) +void DsoSignal::set_zero_ratio(double ratio) { - _zero_vrate = rate; - update_vpos(); - - if (!_dev_inst->name().contains("virtual") && - (force_update || - _view->session().get_capture_state() == SigSession::Running)) { - if (_dev_inst->name() == "DSLogic") - _hw_offset = 0x80; - else - _hw_offset = _zero_vrate * ((1 << _bits) - 1); - } + _zero_offset = ratio2value(ratio); + _dev_inst->set_config(_probe, NULL, SR_CONF_PROBE_OFFSET, + g_variant_new_uint16(_zero_offset)); } void DsoSignal::set_factor(uint64_t factor) @@ -523,65 +555,132 @@ uint64_t DsoSignal::get_factor() } } -void DsoSignal::set_ms_show(bool show) +void DsoSignal::set_show(bool show) { - _probe->ms_show = show; - _view->set_update(_viewport, true); + _show = show; } -bool DsoSignal::get_ms_show() const +bool DsoSignal::show() const { - return _probe->ms_show; + return _show; } -bool DsoSignal::get_ms_show_hover() const +QString DsoSignal::get_measure(enum DSO_MEASURE_TYPE type) { - return _ms_show_hover; -} - -bool DsoSignal::get_ms_gear_hover() const -{ - return _ms_gear_hover; -} - -void DsoSignal::set_ms_en(int index, bool en) -{ - assert(index > DSO_MS_BEGIN); - assert(index < DSO_MS_END); - - _probe->ms_en[index] = en; -} - -bool DsoSignal::get_ms_en(int index) const -{ - assert(index > DSO_MS_BEGIN); - assert(index < DSO_MS_END); - - return _probe->ms_en[index]; -} - -QString DsoSignal::get_ms_string(int index) const -{ - assert(index > DSO_MS_BEGIN); - assert(index < DSO_MS_END); - - switch(index) { - case DSO_MS_FREQ: return "Frequency"; - case DSO_MS_PERD: return "Period"; - case DSO_MS_VMAX: return "Vmax"; - case DSO_MS_VMIN: return "Vmin"; - case DSO_MS_VRMS: return "Vrms"; - case DSO_MS_VMEA: return "Vmean"; - case DSO_MS_VP2P: return "Vp-p"; - default: return "Error: Out of Bounds"; + const QString mNone = "--"; + QString mString; + if (_mValid) { + const int hw_offset = get_hw_offset(); + switch(type) { + case DSO_MS_AMPT: + if (_level_valid) + mString = get_voltage(_high - _low, 2); + else + mString = mNone; + break; + case DSO_MS_VHIG: + if (_level_valid) + mString = get_voltage(hw_offset - _low, 2); + else + mString = mNone; + break; + case DSO_MS_VLOW: + if (_level_valid) + mString = get_voltage(hw_offset - _high, 2); + else + mString = mNone; + break; + case DSO_MS_VP2P: + mString = get_voltage(_max - _min, 2); + break; + case DSO_MS_VMAX: + mString = get_voltage(hw_offset - _min, 2); + break; + case DSO_MS_VMIN: + mString = get_voltage(hw_offset - _max, 2); + break; + case DSO_MS_PERD: + mString = get_time(_period); + break; + case DSO_MS_FREQ: + mString = (abs(_period) > 1000000 ? QString::number(1000000000/_period, 'f', 2) + "Hz" : + abs(_period) > 1000 ? QString::number(1000000/_period, 'f', 2) + "kHz" : QString::number(1000/_period, 'f', 2) + "MHz"); + break; + case DSO_MS_VRMS: + mString = get_voltage(_rms, 2); + break; + case DSO_MS_VMEA: + mString = get_voltage(_mean, 2); + break; + case DSO_MS_NOVR: + if (_level_valid) + mString = QString::number((_max - _high) * 100.0 / (_high - _low), 'f', 2) + "%"; + else + mString = mNone; + break; + case DSO_MS_POVR: + if (_level_valid) + mString = QString::number((_low - _min) * 100.0 / (_high - _low), 'f', 2) + "%"; + else + mString = mNone; + break; + case DSO_MS_PDUT: + if (_level_valid) + mString = QString::number(_high_time/_period*100, 'f', 2)+"%"; + else + mString = mNone; + break; + case DSO_MS_NDUT: + if (_level_valid) + mString = QString::number(100 - _high_time/_period*100, 'f', 2)+"%"; + else + mString = mNone; + break; + case DSO_MS_PWDT: + if (_level_valid) + mString = get_time(_high_time); + else + mString = mNone; + break; + case DSO_MS_NWDT: + if (_level_valid) + mString = get_time(_period - _high_time); + else + mString = mNone; + break; + case DSO_MS_RISE: + if (_level_valid) + mString = get_time(_rise_time); + else + mString = mNone; + break; + case DSO_MS_FALL: + if (_level_valid) + mString = get_time(_fall_time); + else + mString = mNone; + break; + case DSO_MS_BRST: + if (_level_valid) + mString = get_time(_burst_time); + else + mString = mNone; + break; + case DSO_MS_PCNT: + if (_level_valid) + mString = (_pcount > 1000000 ? QString::number(_pcount/1000000, 'f', 6) + "M" : + _pcount > 1000 ? QString::number(_pcount/1000, 'f', 3) + "K" : QString::number(_pcount, 'f', 0)); + else + mString = mNone; + break; + default: + mString = "Error"; + break; + } + } else { + mString = mNone; } -} - -void DsoSignal::update_vpos() -{ - double vpos_off = (0.5 - _zero_vrate) * _vDial->get_value() * DS_CONF_DSO_VDIVS; - _dev_inst->set_config(_probe, NULL, SR_CONF_PROBE_VPOS, - g_variant_new_double(vpos_off)); + return mString; } QRect DsoSignal::get_view_rect() const @@ -592,29 +691,35 @@ QRect DsoSignal::get_view_rect() const _viewport->height() - UpMargin - DownMargin); } -void DsoSignal::paint_back(QPainter &p, int left, int right) +void DsoSignal::paint_back(QPainter &p, int left, int right, QColor fore, QColor back) { assert(_view); + if (!_show) + return; + int i, j; const int height = get_view_rect().height(); const int width = right - left; - QPen solidPen(Signal::dsFore); + fore.setAlpha(View::BackAlpha); + + QPen solidPen(fore); solidPen.setStyle(Qt::SolidLine); p.setPen(solidPen); - p.setBrush(Trace::dsBack); + p.setBrush(back.black() > 0x80 ? back.darker() : back.lighter()); p.drawRect(left, UpMargin, width, height); - p.setPen(Trace::dsLightBlue); - p.drawLine(left, UpMargin/2, left + width, UpMargin/2); + // draw zoom region + fore.setAlpha(View::ForeAlpha); + p.setPen(fore); const uint64_t sample_len = _view->session().cur_samplelimits(); const double samplerate = _view->session().cur_samplerate(); const double samples_per_pixel = samplerate * _view->scale(); const double shown_rate = min(samples_per_pixel * width * 1.0 / sample_len, 1.0); const double start = _view->offset() * samples_per_pixel; const double shown_offset = min(start / sample_len, 1.0) * width; - const double shown_len = shown_rate * width; + const double shown_len = max(shown_rate * width, 6.0); const QPointF left_edge[] = {QPoint(shown_offset + 3, UpMargin/2 - 6), QPoint(shown_offset, UpMargin/2 - 6), QPoint(shown_offset, UpMargin/2 + 6), @@ -623,12 +728,16 @@ void DsoSignal::paint_back(QPainter &p, int left, int right) QPoint(shown_offset + shown_len , UpMargin/2 - 6), QPoint(shown_offset + shown_len , UpMargin/2 + 6), QPoint(shown_offset + shown_len - 3, UpMargin/2 + 6)}; + p.drawLine(left, UpMargin/2, shown_offset, UpMargin/2); + p.drawLine(shown_offset + shown_len, UpMargin/2, left + width, UpMargin/2); p.drawPolyline(left_edge, countof(left_edge)); p.drawPolyline(right_edge, countof(right_edge)); - p.setBrush(Trace::dsBlue); + p.setBrush(fore); p.drawRect(shown_offset, UpMargin/2 - 3, shown_len, 6); - QPen dashPen(Signal::dsFore); + // draw divider + fore.setAlpha(View::BackAlpha); + QPen dashPen(fore); dashPen.setStyle(Qt::DashLine); p.setPen(dashPen); const double spanY =height * 1.0 / DS_CONF_DSO_VDIVS; @@ -653,19 +762,32 @@ void DsoSignal::paint_back(QPainter &p, int left, int right) posX - miniSpanX * j, height / 2.0f + UpMargin + 5); } } + _view->set_back(true); } -void DsoSignal::paint_mid(QPainter &p, int left, int right) +void DsoSignal::paint_mid(QPainter &p, int left, int right, QColor fore, QColor back) { + (void)fore; + (void)back; + + if (!_show) + return; + assert(_data); assert(_view); assert(right >= left); + { + if (_view->session().dso_feed()) { + _mValid = false; + _view->session().set_dso_feed(false); + } + } + if (enabled()) { - const float top = get_view_rect().top(); - const int height = get_view_rect().height(); + const int index = get_index(); const int width = right - left; - const float zeroY = _zero_vrate * height + top; + const float zeroY = get_zero_vpos(); const double scale = _view->scale(); assert(scale > 0); @@ -680,14 +802,14 @@ void DsoSignal::paint_mid(QPainter &p, int left, int right) if (snapshot->empty()) return; - if (!snapshot->has_data(get_index())) + if (!snapshot->has_data(index)) return; - const uint16_t number_channels = snapshot->get_channel_num(); + const uint16_t enabled_channels = snapshot->get_channel_num(); const double pixels_offset = offset; - //const double samplerate = _data->samplerate(); + const double samplerate = _data->samplerate(); //const double samplerate = _dev_inst->get_sample_rate(); - const double samplerate = _view->session().cur_samplerate(); + //const double samplerate = _view->session().cur_samplerate(); const int64_t last_sample = max((int64_t)(snapshot->get_sample_count() - 1), (int64_t)0); const double samples_per_pixel = samplerate * scale; const double start = offset * samples_per_pixel; @@ -697,33 +819,78 @@ void DsoSignal::paint_mid(QPainter &p, int left, int right) (int64_t)0), last_sample); const int64_t end_sample = min(max((int64_t)ceil(end) + 1, (int64_t)0), last_sample); + const int hw_offset = get_hw_offset(); if (samples_per_pixel < EnvelopeThreshold) { snapshot->enable_envelope(false); paint_trace(p, snapshot, zeroY, left, - start_sample, end_sample, - pixels_offset, samples_per_pixel, number_channels); + start_sample, end_sample, hw_offset, + pixels_offset, samples_per_pixel, enabled_channels); } else { snapshot->enable_envelope(true); paint_envelope(p, snapshot, zeroY, left, - start_sample, end_sample, - pixels_offset, samples_per_pixel, number_channels); + start_sample, end_sample, hw_offset, + pixels_offset, samples_per_pixel, enabled_channels); + } + + sr_status status; + if (sr_status_get(_dev_inst->dev_inst(), &status, false, 0, 0) == SR_OK) { + _mValid = true; + if (status.measure_valid) { + _min = (index == 0) ? status.ch0_min : status.ch1_min; + _max = (index == 0) ? status.ch0_max : status.ch1_max; + + _level_valid = (index == 0) ? status.ch0_level_valid : status.ch1_level_valid; + _low = (index == 0) ? status.ch0_low_level : status.ch1_low_level; + _high = (index == 0) ? status.ch0_high_level : status.ch1_high_level; + + const uint32_t count = (index == 0) ? status.ch0_cyc_cnt : status.ch1_cyc_cnt; + const bool plevel = (index == 0) ? status.ch0_plevel : status.ch1_plevel; + const bool startXORend = (index == 0) ? (status.ch0_cyc_llen == 0) : (status.ch1_cyc_llen == 0); + const uint16_t total_channels = g_slist_length(_dev_inst->dev_inst()->channels); + const double tfactor = (total_channels / enabled_channels) * SR_GHZ(1) * 1.0 / samplerate; + + double samples = (index == 0) ? status.ch0_cyc_tlen : status.ch1_cyc_tlen; + _period = ((count == 0) ? 0 : samples / count) * tfactor; + + samples = (index == 0) ? status.ch0_cyc_flen : status.ch1_cyc_flen; + _rise_time = ((count == 0) ? 0 : samples / ((plevel && startXORend) ? count : count + 1)) * tfactor; + samples = (index == 0) ? status.ch0_cyc_rlen : status.ch1_cyc_rlen; + _fall_time = ((count == 0) ? 0 : samples / ((!plevel && startXORend) ? count : count + 1)) * tfactor; + + samples = (index == 0) ? (status.ch0_plevel ? status.ch0_cyc_plen - status.ch0_cyc_llen : + status.ch0_cyc_tlen - status.ch0_cyc_plen + status.ch0_cyc_llen) : + (status.ch1_plevel ? status.ch1_cyc_plen - status.ch1_cyc_llen : + status.ch1_cyc_tlen - status.ch1_cyc_plen + status.ch1_cyc_llen); + _high_time = ((count == 0) ? 0 : samples / count) * tfactor; + + samples = (index == 0) ? status.ch0_cyc_tlen + status.ch0_cyc_llen : status.ch1_cyc_flen + status.ch1_cyc_llen; + _burst_time = samples * tfactor; + + _pcount = count + (plevel & !startXORend); + _rms = (index == 0) ? status.ch0_acc_square : status.ch1_acc_square; + _rms = sqrt(_rms / snapshot->get_sample_count()); + _mean = (index == 0) ? status.ch0_acc_mean : status.ch1_acc_mean; + _mean = hw_offset - _mean / snapshot->get_sample_count(); + } } } } -void DsoSignal::paint_fore(QPainter &p, int left, int right) +void DsoSignal::paint_fore(QPainter &p, int left, int right, QColor fore, QColor back) { + if (!_show) + return; + assert(_view); - bool antialiasing = p.Antialiasing; - p.setRenderHint(QPainter::Antialiasing, false); - - QPen pen(Signal::dsGray); + fore.setAlpha(View::BackAlpha); + QPen pen(fore); pen.setStyle(Qt::DotLine); p.setPen(pen); p.drawLine(left, get_zero_vpos(), right, get_zero_vpos()); + fore.setAlpha(View::ForeAlpha); if(enabled()) { const QPointF mouse_point = _view->hover_point(); const QRectF label_rect = get_trig_rect(left, right); @@ -731,7 +898,7 @@ void DsoSignal::paint_fore(QPainter &p, int left, int right) // Paint the trig line const QPointF points[] = { - QPointF(right, get_trig_vpos()), + QPointF(right, ratio2pos(get_trig_vrate())), label_rect.topLeft(), label_rect.topRight(), label_rect.bottomRight(), @@ -742,7 +909,7 @@ void DsoSignal::paint_fore(QPainter &p, int left, int right) p.setBrush(_colour); p.drawPolygon(points, countof(points)); - p.setPen(Qt::white); + p.setPen(fore); const QPointF arrow_points[] = { QPoint(label_rect.left(), label_rect.center().y()), QPoint(label_rect.left(), label_rect.center().y()-1), @@ -770,13 +937,12 @@ void DsoSignal::paint_fore(QPainter &p, int left, int right) p.drawPoints(arrow_points, countof(arrow_points)); // paint the trig voltage - int trigp = get_trig_vpos(); - float t_vol = (_zero_vrate - get_trig_vrate()) * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS; - QString t_vol_s = (_vDial->get_value() >= 500) ? QString::number(t_vol/1000.0f, 'f', 2)+"V" : QString::number(t_vol, 'f', 2)+"mV"; + int trigp = ratio2pos(get_trig_vrate()); + QString t_vol_s = get_voltage(get_zero_vpos() - trigp, 2, true); int vol_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, Qt::AlignLeft | Qt::AlignTop, t_vol_s).width(); const QRectF t_vol_rect = QRectF(right-vol_width, trigp-10, vol_width, 20); - p.setPen(Qt::white); + p.setPen(fore); p.drawText(t_vol_rect, Qt::AlignRight | Qt::AlignVCenter, t_vol_s); // paint the _trig_vpos line @@ -786,14 +952,16 @@ void DsoSignal::paint_fore(QPainter &p, int left, int right) } // Paint the text - p.setPen(Qt::white); + p.setPen(fore); p.drawText(label_rect, Qt::AlignCenter | Qt::AlignVCenter, "T"); // Paint measure - paint_measure(p); - } + if (_view->session().get_capture_state() == SigSession::Stopped) + paint_hover_measure(p, fore, back); - p.setRenderHint(QPainter::Antialiasing, antialiasing); + // autoset + auto_set(); + } } QRectF DsoSignal::get_trig_rect(int left, int right) const @@ -801,13 +969,13 @@ QRectF DsoSignal::get_trig_rect(int left, int right) const (void)left; return QRectF(right + SquareWidth / 2, - get_trig_vpos() - SquareWidth / 2, + ratio2pos(get_trig_vrate()) - SquareWidth / 2, SquareWidth, SquareWidth); } void DsoSignal::paint_trace(QPainter &p, const boost::shared_ptr &snapshot, - int zeroY, int left, const int64_t start, const int64_t end, + int zeroY, int left, const int64_t start, const int64_t end, int hw_offset, const double pixels_offset, const double samples_per_pixel, uint64_t num_channels) { const int64_t sample_count = end - start + 1; @@ -817,7 +985,7 @@ void DsoSignal::paint_trace(QPainter &p, assert(samples); QColor trace_colour = _colour; - trace_colour.setAlpha(150); + trace_colour.setAlpha(View::ForeAlpha); p.setPen(trace_colour); QPointF *points = new QPointF[sample_count]; @@ -827,32 +995,26 @@ void DsoSignal::paint_trace(QPainter &p, float bottom = get_view_rect().bottom(); float x = (start / samples_per_pixel - pixels_offset) + left; double pixels_per_sample = 1.0/samples_per_pixel; - uint8_t offset; + uint8_t value; int64_t sample_end = sample_count*num_channels; for (int64_t sample = 0; sample < sample_end; sample+=num_channels) { - //const float x = (sample / samples_per_pixel - pixels_offset) + left; - //uint8_t offset = samples[(sample - start)*num_channels]; - - //offset = samples[(sample - start)*num_channels]; - offset = samples[sample]; - const float y = min(max(top, zeroY + (offset - _hw_offset) * _scale), bottom); + value = samples[sample]; + const float y = min(max(top, zeroY + (value - hw_offset) * _scale), bottom); *point++ = QPointF(x, y); x += pixels_per_sample; - //*point++ = QPointF(x, top + offset); } p.drawPolyline(points, point - points); p.eraseRect(get_view_rect().right()+1, get_view_rect().top(), _view->viewport()->width() - get_view_rect().width(), get_view_rect().height()); - //delete[] samples; delete[] points; } } void DsoSignal::paint_envelope(QPainter &p, const boost::shared_ptr &snapshot, - int zeroY, int left, const int64_t start, const int64_t end, + int zeroY, int left, const int64_t start, const int64_t end, int hw_offset, const double pixels_offset, const double samples_per_pixel, uint64_t num_channels) { using namespace Qt; @@ -868,7 +1030,7 @@ void DsoSignal::paint_envelope(QPainter &p, p.setPen(QPen(NoPen)); //p.setPen(QPen(_colour, 2, Qt::SolidLine)); QColor envelope_colour = _colour; - envelope_colour.setAlpha(150); + envelope_colour.setAlpha(View::ForeAlpha); p.setBrush(envelope_colour); QRectF *const rects = new QRectF[e.length]; @@ -883,8 +1045,8 @@ void DsoSignal::paint_envelope(QPainter &p, // We overlap this sample with the next so that vertical // gaps do not appear during steep rising or falling edges - const float b = min(max(top, ((max(s->max, (s+1)->min) - _hw_offset) * _scale + zeroY)), bottom); - const float t = min(max(top, ((min(s->min, (s+1)->max) - _hw_offset) * _scale + zeroY)), bottom); + const float b = min(max(top, ((max(s->max, (s+1)->min) - hw_offset) * _scale + zeroY)), bottom); + const float t = min(max(top, ((min(s->min, (s+1)->max) - hw_offset) * _scale + zeroY)), bottom); float h = b - t; if(h >= 0.0f && h <= 1.0f) @@ -901,8 +1063,12 @@ void DsoSignal::paint_envelope(QPainter &p, //delete[] e.samples; } -void DsoSignal::paint_type_options(QPainter &p, int right, const QPoint pt) +void DsoSignal::paint_type_options(QPainter &p, int right, const QPoint pt, QColor fore) { + p.setRenderHint(QPainter::Antialiasing, true); + + QColor foreBack = fore; + foreBack.setAlpha(View::BackAlpha); int y = get_y(); const QRectF vDial_rect = get_rect(DSO_VDIAL, y, right); const QRectF x1_rect = get_rect(DSO_X1, y, right); @@ -912,27 +1078,39 @@ void DsoSignal::paint_type_options(QPainter &p, int right, const QPoint pt) const QRectF chEn_rect = get_rect(DSO_CHEN, y, right); const QRectF auto_rect = get_rect(DSO_AUTO, y, right); - _vDial->paint(p, vDial_rect, _colour, pt); + QString pText; + _vDial->paint(p, vDial_rect, _colour, pt, pText); + QFontMetrics fm(p.font()); + const QRectF valueRect = QRectF(chEn_rect.left(), vDial_rect.top()-fm.height()-10, right, fm.height()); + p.drawText(valueRect, Qt::AlignCenter, pText); + const char *strings[6] = { + QT_TR_NOOP("EN"), + QT_TR_NOOP("DIS"), + QT_TR_NOOP("GND"), + QT_TR_NOOP("DC"), + QT_TR_NOOP("AC"), + QT_TR_NOOP("AUTO"), + }; p.setPen(Qt::transparent); p.setBrush(chEn_rect.contains(pt) ? _colour.darker() : _colour); p.drawRect(chEn_rect); p.setPen(Qt::white); - p.drawText(chEn_rect, Qt::AlignCenter | Qt::AlignVCenter, enabled() ? tr("EN") : tr("DIS")); + p.drawText(chEn_rect, Qt::AlignCenter | Qt::AlignVCenter, enabled() ? tr(strings[0]) : tr(strings[1])); p.setPen(Qt::transparent); - p.setBrush(enabled() ? (acdc_rect.contains(pt) ? _colour.darker() : _colour) : dsDisable); + p.setBrush(enabled() ? (acdc_rect.contains(pt) ? _colour.darker() : _colour) : foreBack); p.drawRect(acdc_rect); p.setPen(Qt::white); - p.drawText(acdc_rect, Qt::AlignCenter | Qt::AlignVCenter, (_acCoupling == SR_GND_COUPLING) ? tr("GND") : - (_acCoupling == SR_DC_COUPLING) ? tr("DC") : tr("AC")); + p.drawText(acdc_rect, Qt::AlignCenter | Qt::AlignVCenter, (_acCoupling == SR_GND_COUPLING) ? tr(strings[2]): + (_acCoupling == SR_DC_COUPLING) ? tr(strings[3]) : tr(strings[4])); if (!_dev_inst->name().contains("virtual")) { p.setPen(Qt::transparent); - p.setBrush(enabled() ? (auto_rect.contains(pt) ? _colour.darker() : _colour) : dsDisable); + p.setBrush(enabled() ? (auto_rect.contains(pt) ? _colour.darker() : _colour) : foreBack); p.drawRect(auto_rect); p.setPen(Qt::white); - p.drawText(auto_rect, Qt::AlignCenter | Qt::AlignVCenter, tr("AUTO")); + p.drawText(auto_rect, Qt::AlignCenter | Qt::AlignVCenter, tr(strings[5])); } // paint the probe factor selector @@ -948,17 +1126,19 @@ void DsoSignal::paint_type_options(QPainter &p, int right, const QPoint pt) } p.setPen(Qt::transparent); - p.setBrush((enabled() && (factor == 100)) ? (x100_rect.contains(pt) ? _colour.darker() : _colour) : (x100_rect.contains(pt) ? _colour.darker() : dsDisable)); + p.setBrush((enabled() && (factor == 100)) ? (x100_rect.contains(pt) ? _colour.darker() : _colour) : (x100_rect.contains(pt) ? _colour.darker() : foreBack)); p.drawRect(x100_rect); - p.setBrush((enabled() && (factor == 10)) ? (x10_rect.contains(pt) ? _colour.darker() : _colour) : (x10_rect.contains(pt) ? _colour.darker() : dsDisable)); + p.setBrush((enabled() && (factor == 10)) ? (x10_rect.contains(pt) ? _colour.darker() : _colour) : (x10_rect.contains(pt) ? _colour.darker() : foreBack)); p.drawRect(x10_rect); - p.setBrush((enabled() && (factor == 1)) ? (x1_rect.contains(pt) ? _colour.darker() : _colour) : (x1_rect.contains(pt) ? _colour.darker() : dsDisable)); + p.setBrush((enabled() && (factor == 1)) ? (x1_rect.contains(pt) ? _colour.darker() : _colour) : (x1_rect.contains(pt) ? _colour.darker() : foreBack)); p.drawRect(x1_rect); p.setPen(Qt::white); p.drawText(x100_rect, Qt::AlignCenter | Qt::AlignVCenter, "x100"); p.drawText(x10_rect, Qt::AlignCenter | Qt::AlignVCenter, "x10"); p.drawText(x1_rect, Qt::AlignCenter | Qt::AlignVCenter, "x1"); + + p.setRenderHint(QPainter::Antialiasing, false); } bool DsoSignal::mouse_press(int right, const QPoint pt) @@ -981,9 +1161,9 @@ bool DsoSignal::mouse_press(int right, const QPoint pt) } else if (enabled()) { if (vDial_rect.contains(pt) && pt.x() > vDial_rect.center().x()) { if (pt.y() > vDial_rect.center().y()) - go_vDialNext(); + go_vDialNext(true); else - go_vDialPre(); + go_vDialPre(true); } else if (_dev_inst->name() != "virtual-session" && acdc_rect.contains(pt)) { if (_dev_inst->name() == "DSLogic") @@ -1014,9 +1194,9 @@ bool DsoSignal::mouse_wheel(int right, const QPoint pt, const int shift) if (vDial_rect.contains(pt)) { if (shift > 0.5) - go_vDialPre(); + go_vDialPre(true); else if (shift < -0.5) - go_vDialNext(); + go_vDialNext(true); return true; } else { return false; @@ -1068,147 +1248,126 @@ QRectF DsoSignal::get_rect(DsoSetRegions type, int y, int right) return QRectF(0, 0, 0, 0); } -void DsoSignal::paint_measure(QPainter &p) +void DsoSignal::paint_hover_measure(QPainter &p, QColor fore, QColor back) { - sr_status status; - int index = get_index(); - const int st_begin = (index == 0) ? SR_STATUS_CH0_BEGIN : SR_STATUS_CH1_BEGIN; - const int st_end = (index == 0) ? SR_STATUS_CH0_END : SR_STATUS_CH1_END; - if (sr_status_get(_dev_inst->dev_inst(), &status, false, st_begin, st_end) == SR_OK) { - _max = (index == 0) ? status.ch0_max : status.ch1_max; - _min = (index == 0) ? status.ch0_min : status.ch1_min; - const uint64_t period = (index == 0) ? status.ch0_period : status.ch1_period; - const uint32_t count = (index == 0) ? status.ch0_pcnt : status.ch1_pcnt; - double value_max = (_hw_offset - _min) * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); - double value_min = (_hw_offset - _max) * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); - double value_p2p = value_max - value_min; - _period = (count == 0) ? 0 : period * 10.0 / count; - const int channel_count = _view->session().get_ch_num(SR_CHANNEL_DSO); - uint64_t sample_rate = _dev_inst->get_sample_rate(); - _period = _period * 200.0 / (channel_count * sample_rate * 1.0 / SR_MHZ(1)); - _ms_string[DSO_MS_VMAX] = tr("Vmax: ") + (abs(value_max) > 1000 ? QString::number(value_max/1000.0, 'f', 2) + "V" : QString::number(value_max, 'f', 2) + "mV"); - _ms_string[DSO_MS_VMIN] = tr("Vmin: ") + (abs(value_min) > 1000 ? QString::number(value_min/1000.0, 'f', 2) + "V" : QString::number(value_min, 'f', 2) + "mV"); - _ms_string[DSO_MS_PERD] = tr("Perd: ") + (abs(_period) > 1000000000 ? QString::number(_period/1000000000, 'f', 2) + "S" : - abs(_period) > 1000000 ? QString::number(_period/1000000, 'f', 2) + "mS" : - abs(_period) > 1000 ? QString::number(_period/1000, 'f', 2) + "uS" : QString::number(_period, 'f', 2) + "nS"); - _ms_string[DSO_MS_FREQ] = tr("Freq: ") + (abs(_period) > 1000000 ? QString::number(1000000000/_period, 'f', 2) + "Hz" : - abs(_period) > 1000 ? QString::number(1000000/_period, 'f', 2) + "kHz" : QString::number(1000/_period, 'f', 2) + "MHz"); - _ms_string[DSO_MS_VP2P] = tr("Vp-p: ") + (abs(value_p2p) > 1000 ? QString::number(value_p2p/1000.0, 'f', 2) + "V" : QString::number(value_p2p, 'f', 2) + "mV"); + const int hw_offset = get_hw_offset(); + // Hover measure + if (_hover_en) { + QString hover_str = get_voltage(hw_offset - _hover_value, 2); + const int hover_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, hover_str).width() + 10; + const int hover_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, hover_str).height(); + QRectF hover_rect(_hover_point.x(), _hover_point.y()-hover_height/2, hover_width, hover_height); + if (hover_rect.right() > get_view_rect().right()) + hover_rect.moveRight(_hover_point.x()); + if (hover_rect.top() < get_view_rect().top()) + hover_rect.moveTop(_hover_point.y()); + if (hover_rect.bottom() > get_view_rect().bottom()) + hover_rect.moveBottom(_hover_point.y()); - if (_probe->ms_show && _probe->ms_en[DSO_MS_VRMS]) { - const deque< boost::shared_ptr > &snapshots = - _data->get_snapshots(); - if (!snapshots.empty()) { - const boost::shared_ptr &snapshot = - snapshots.front(); - const double vrms = snapshot->cal_vrms(_hw_offset, get_index()); - const double value_vrms = vrms * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); - _ms_string[DSO_MS_VRMS] = tr("Vrms: ") + (abs(value_vrms) > 1000 ? QString::number(value_vrms/1000.0, 'f', 2) + "V" : QString::number(value_vrms, 'f', 2) + "mV"); - } - } - - if (_probe->ms_show && _probe->ms_en[DSO_MS_VMEA]) { - const deque< boost::shared_ptr > &snapshots = - _data->get_snapshots(); - if (!snapshots.empty()) { - const boost::shared_ptr &snapshot = - snapshots.front(); - const double vmean = snapshot->cal_vmean(get_index()); - const double value_vmean = (_hw_offset - vmean) * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); - _ms_string[DSO_MS_VMEA] = tr("Vmean: ") + (abs(value_vmean) > 1000 ? QString::number(value_vmean/1000.0, 'f', 2) + "V" : QString::number(value_vmean, 'f', 2) + "mV"); - } - } - } else { - _ms_string[DSO_MS_VMAX] = tr("Vmax: #####"); - _ms_string[DSO_MS_VMIN] = tr("Vmin: #####"); - _ms_string[DSO_MS_PERD] = tr("Perd: #####"); - _ms_string[DSO_MS_FREQ] = tr("Freq: #####"); - _ms_string[DSO_MS_VP2P] = tr("Vp-p: #####"); - _ms_string[DSO_MS_VRMS] = tr("Vrms: #####"); - _ms_string[DSO_MS_VMEA] = tr("Vmean: #####"); + p.setPen(fore); + p.setBrush(back); + p.drawRect(_hover_point.x()-1, _hover_point.y()-1, HoverPointSize, HoverPointSize); + p.drawText(hover_rect, Qt::AlignCenter | Qt::AlignTop | Qt::TextDontClip, hover_str); } - QColor measure_colour = _colour; - measure_colour.setAlpha(180); - QColor back_colour = Qt::white; - back_colour.setAlpha(100); + list::iterator i = _view->get_cursorList().begin(); + while (i != _view->get_cursorList().end()) { + float pt_value; + const QPointF pt = get_point((*i)->index(), pt_value); + QString pt_str = get_voltage(hw_offset - pt_value, 2); + const int pt_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, pt_str).width() + 10; + const int pt_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, pt_str).height(); + QRectF pt_rect(pt.x(), pt.y()-pt_height/2, pt_width, pt_height); + if (pt_rect.right() > get_view_rect().right()) + pt_rect.moveRight(pt.x()); + if (pt_rect.top() < get_view_rect().top()) + pt_rect.moveTop(pt.y()); + if (pt_rect.bottom() > get_view_rect().bottom()) + pt_rect.moveBottom(pt.y()); - bool antialiasing = p.Antialiasing; - p.setRenderHint(QPainter::Antialiasing, true); + p.drawRect(pt.x()-1, pt.y()-1, 2, 2); + p.drawLine(pt.x()-2, pt.y()-2, pt.x()+2, pt.y()+2); + p.drawLine(pt.x()+2, pt.y()-2, pt.x()-2, pt.y()+2); + p.drawText(pt_rect, Qt::AlignCenter | Qt::AlignTop | Qt::TextDontClip, pt_str); - p.setPen(Qt::NoPen); - p.setBrush(measure_colour); - p.drawRoundedRect(_ms_rect[DSO_MS_BEGIN], MS_RectRad, MS_RectRad); - const QPixmap gear_pix(":/icons/settings.png"); - const QPixmap show_pix(_probe->ms_show ? ":/icons/shown.png" : ":/icons/hidden.png"); - if (_ms_gear_hover) { - p.setBrush(back_colour); - p.drawRoundedRect(_ms_gear_rect, MS_RectRad, MS_RectRad); - } else if (_ms_show_hover) { - p.setBrush(back_colour); - p.drawRoundedRect(_ms_show_rect, MS_RectRad, MS_RectRad); + i++; } - p.drawPixmap(_ms_gear_rect, gear_pix); - p.drawPixmap(_ms_show_rect, show_pix); - p.setPen(Qt::white); - p.drawText(_ms_rect[DSO_MS_BEGIN], Qt::AlignCenter | Qt::AlignVCenter, "CH"+QString::number(index)); - - if (_probe->ms_show) { - p.setBrush(back_colour); - int j = DSO_MS_BEGIN+1; - for (int i=DSO_MS_BEGIN+1; ims_en[i]) { - p.setPen(_colour); - p.drawText(_ms_rect[j], Qt::AlignLeft | Qt::AlignVCenter, _ms_string[i]); - p.setPen(Qt::NoPen); - p.drawRoundedRect(_ms_rect[j], MS_RectRad, MS_RectRad); - j++; - } - } - } - p.setRenderHint(QPainter::Antialiasing, antialiasing); +} +void DsoSignal::auto_set() +{ if (_view->session().get_capture_state() == SigSession::Stopped) { if (_autoV) autoV_end(); if (_autoH) autoH_end(); - } - - if (_autoV && !_view->session().get_data_auto_lock()) { - set_zero_vrate(0.5, true); - const uint8_t vscale = abs(_max - _min); - if (_max == 0xff || _min == 0x00 || vscale > 0xCC) { - go_vDialNext(); - } else if (vscale > 0x33) { - autoV_end(); - } else { - go_vDialPre(); + } else { + if (_autoH && _autoV && get_zero_ratio() != 0.5) { + set_zero_ratio(0.5); } - _view->session().data_auto_lock(AutoLock); - } + if (_mValid && !_view->session().get_data_auto_lock()) { + if (_autoH) { + bool roll = false; + GVariant *gvar = _dev_inst->get_config(NULL, NULL, SR_CONF_ROLL); + if (gvar != NULL) { + roll = g_variant_get_boolean(gvar); + g_variant_unref(gvar); + } + const double hori_res = _view->get_hori_res(); + if (_level_valid && ((!roll && _pcount < 3) || _period > 4*hori_res)) { + _view->zoom(-1); + } else if (_level_valid && _pcount > 6 && _period < 1.5*hori_res) { + _view->zoom(1); + } else if (_level_valid) { + autoH_end(); + } + } + if (_autoV) { + const bool over_flag = _max == 0xff || _min == 0x0; + const bool out_flag = _max >= 0xE0 || _min <= 0x20; + const bool under_flag = _max <= 0xA0 && _min >= 0x60; + if (over_flag) { + if (!_autoV_over) + _auto_cnt = 0; + _autoV_over = true; + go_vDialNext(false); + } else if (out_flag) { + go_vDialNext(false); + } else if (!_autoV_over && under_flag) { + go_vDialPre(false); + } else if (!_autoH) { + autoV_end(); + } - if (_autoH && !_view->session().get_data_auto_lock()) { - const double hori_res = _view->get_hori_res(); - if (_period <= 0) { - _view->zoom(-1); - } else if (_period < 1.5*hori_res) { - _view->zoom(1); - } else if (_period > 6*hori_res) { - _view->zoom(-1); - } else { - autoH_end(); + if (_autoV_over && under_flag) { + if (_auto_cnt++ > 16) + _autoV_over = false; + } else { + _auto_cnt = 0; + } + + if (_level_valid) { + _trig_value = (_min+_max)/2; + set_trig_vpos(ratio2pos(get_trig_vrate())); + } + } + if (_autoH || _autoV) + _view->session().data_auto_lock(AutoLock); } - _view->session().data_auto_lock(AutoLock); } } void DsoSignal::autoV_end() { _autoV = false; + _autoV_over = false; _view->auto_trig(get_index()); _trig_value = (_min+_max)/2; - set_trig_vpos(get_trig_vpos(), true); + set_trig_vpos(ratio2pos(get_trig_vrate())); _view->set_update(_viewport, true); _view->update(); } @@ -1236,31 +1395,17 @@ void DsoSignal::auto_start() _view->session().data_auto_lock(AutoLock); _autoV = true; _autoH = true; - QTimer::singleShot(AutoTime, this, SLOT(auto_end())); + QTimer::singleShot(AutoTime, &_view->session(), SLOT(auto_end())); } } bool DsoSignal::measure(const QPointF &p) { - if (_ms_gear_rect.contains(QPoint(p.x(), p.y()))) { - _ms_gear_hover = true; - _view->set_update(_viewport, true); - return false; - } else if (_ms_gear_hover) { - _view->set_update(_viewport, true); - _ms_gear_hover = false; - } - if (_ms_show_rect.contains(QPoint(p.x(), p.y()))) { - _ms_show_hover = true; - _view->set_update(_viewport, true); - return false; - } else if (_ms_show_hover){ - _view->set_update(_viewport, true); - _ms_show_hover = false; - } - _hover_en = false; - if (!enabled()) + if (!enabled() || !show()) + return false; + + if (_view->session().get_capture_state() != SigSession::Stopped) return false; const QRectF window = get_view_rect(); @@ -1287,39 +1432,9 @@ bool DsoSignal::measure(const QPointF &p) if (_hover_index >= snapshot->get_sample_count()) return false; - uint64_t pre_index; - uint64_t nxt_index; - if (_hover_index > 0) - pre_index = _hover_index - 1; - else - pre_index = _hover_index; - if (_hover_index < snapshot->get_sample_count() - 1) - nxt_index = _hover_index + 1; - else - nxt_index = _hover_index; - const uint8_t pre_sample = *snapshot->get_samples(pre_index, pre_index, get_index()); - const uint8_t cur_sample = *snapshot->get_samples(_hover_index, _hover_index, get_index()); - const uint8_t nxt_sample = *snapshot->get_samples(nxt_index, nxt_index, get_index()); - - _hover_value = (_hw_offset - cur_sample) * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); - - float top = get_view_rect().top(); - float bottom = get_view_rect().bottom(); - float zeroP = _zero_vrate * get_view_rect().height() + top; - float pre_x = (pre_index / samples_per_pixel - pixels_offset); - const float pre_y = min(max(top, zeroP + (pre_sample - _hw_offset)* _scale), bottom); - float x = (_hover_index / samples_per_pixel - pixels_offset); - const float y = min(max(top, zeroP + (cur_sample - _hw_offset)* _scale), bottom); - float nxt_x = (nxt_index / samples_per_pixel - pixels_offset); - const float nxt_y = min(max(top, zeroP + (nxt_sample - _hw_offset)* _scale), bottom); - const QRectF slope_rect = QRectF(QPointF(pre_x - 10, pre_y - 10), QPointF(nxt_x + 10, nxt_y + 10)); - if (abs(y-p.y()) < 20 || slope_rect.contains(p)) { - _hover_point = QPointF(x, y); - _hover_en = true; - return true; - } else { - return false; - } + _hover_point = get_point(_hover_index, _hover_value); + _hover_en = true; + return true; } bool DsoSignal::get_hover(uint64_t &index, QPointF &p, double &value) @@ -1333,5 +1448,84 @@ bool DsoSignal::get_hover(uint64_t &index, QPointF &p, double &value) return false; } +QPointF DsoSignal::get_point(uint64_t index, float &value) +{ + QPointF pt = QPointF(0, 0); + + if (!enabled()) + return pt; + + const deque< boost::shared_ptr > &snapshots = + _data->get_snapshots(); + if (snapshots.empty()) + return pt; + + const boost::shared_ptr &snapshot = + snapshots.front(); + if (snapshot->empty()) + return pt; + + const double scale = _view->scale(); + assert(scale > 0); + const int64_t pixels_offset = _view->offset(); + const double samplerate = _view->session().cur_samplerate(); + const double samples_per_pixel = samplerate * scale; + + if (index >= snapshot->get_sample_count()) + return pt; + + value = *snapshot->get_samples(index, index, get_index()); + const float top = get_view_rect().top(); + const float bottom = get_view_rect().bottom(); + const int hw_offset = get_hw_offset(); + const float x = (index / samples_per_pixel - pixels_offset); + const float y = min(max(top, get_zero_vpos() + (value - hw_offset)* _scale), bottom); + pt = QPointF(x, y); + + return pt; +} + +double DsoSignal::get_voltage(uint64_t index) +{ + if (!enabled()) + return 1; + + const deque< boost::shared_ptr > &snapshots = + _data->get_snapshots(); + if (snapshots.empty()) + return 1; + + const boost::shared_ptr &snapshot = + snapshots.front(); + if (snapshot->empty()) + return 1; + + if (index >= snapshot->get_sample_count()) + return 1; + + const double value = *snapshot->get_samples(index, index, get_index()); + const int hw_offset = get_hw_offset(); + return (hw_offset - value) * _scale * + _vDial->get_value() * _vDial->get_factor() * + DS_CONF_DSO_VDIVS / get_view_rect().height(); +} + +QString DsoSignal::get_voltage(double v, int p, bool scaled) +{ + if (scaled) + v = v * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); + else + v = v * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); + return abs(v) >= 1000 ? QString::number(v/1000.0, 'f', p) + "V" : QString::number(v, 'f', p) + "mV"; +} + +QString DsoSignal::get_time(double t) +{ + QString str = (abs(t) > 1000000000 ? QString::number(t/1000000000, 'f', 2) + "S" : + abs(t) > 1000000 ? QString::number(t/1000000, 'f', 2) + "mS" : + abs(t) > 1000 ? QString::number(t/1000, 'f', 2) + "uS" : QString::number(t, 'f', 2) + "nS"); + return str; +} + } // namespace view } // namespace pv diff --git a/DSView/pv/view/dsosignal.h b/DSView/pv/view/dsosignal.h old mode 100644 new mode 100755 index 5bd96ddf..651ad981 --- a/DSView/pv/view/dsosignal.h +++ b/DSView/pv/view/dsosignal.h @@ -40,22 +40,25 @@ namespace view { class DsoSignal : public Signal { + Q_OBJECT + +public: + static const int UpMargin = 30; + static const int DownMargin = 0; + static const int RightMargin = 30; + static const float EnvelopeThreshold; + static const int HoverPointSize = 2; + static const int RefreshShort = 200; + private: static const QColor SignalColours[4]; - static const float EnvelopeThreshold; - static const int HitCursorMargin = 3; static const uint64_t vDialValueStep = 1000; static const uint64_t vDialUnitCount = 2; static const QString vDialUnit[vDialUnitCount]; - static const int UpMargin = 30; - static const int DownMargin = 0; - static const int RightMargin = 30; - static const uint8_t DefaultBits = 8; static const int TrigMargin = 16; - static const int RefreshShort = 200; static const int RefreshLong = 800; static const int AutoTime = 10000; static const int AutoLock = 3; @@ -88,10 +91,12 @@ public: boost::shared_ptr data() const; boost::shared_ptr dso_data() const; - void set_viewport(pv::view::Viewport *viewport); void set_scale(int height); float get_scale(); + uint8_t get_bits(); + double get_ref_min() const; + double get_ref_max() const; int get_name_width() const; @@ -101,19 +106,23 @@ public: void set_enable(bool enable); bool get_vDialActive() const; void set_vDialActive(bool active); - bool go_vDialPre(); - bool go_vDialNext(); + bool go_vDialPre(bool manul); + bool go_vDialNext(bool manul); bool update_capture(bool instant); + dslDial *get_vDial() const; uint64_t get_vDialValue() const; uint16_t get_vDialSel() const; uint8_t get_acCoupling() const; void set_acCoupling(uint8_t coupling); - void set_trig_vpos(int pos, bool delta_change); - int get_trig_vpos() const; - void set_trig_vrate(double rate); + + void set_trig_vpos(int pos, bool delta_change = true); + void set_trig_ratio(double ratio, bool delta_change = true); double get_trig_vrate() const; + void set_factor(uint64_t factor); uint64_t get_factor(); + void set_show(bool show); + bool show() const; bool load_settings(); int commit_settings(); @@ -123,6 +132,7 @@ public: */ bool measure(const QPointF &p); bool get_hover(uint64_t &index, QPointF &p, double &value); + QPointF get_point(uint64_t index, float &value); /** * auto set the vertical and Horizontal scale @@ -130,19 +140,30 @@ public: void auto_start(); void autoV_end(); void autoH_end(); + void auto_end(); /** * Gets the mid-Y position of this signal. */ int get_zero_vpos() const; - double get_zero_vrate(); - double get_hw_offset(); + double get_zero_ratio() const; + int get_hw_offset() const; /** * Sets the mid-Y position of this signal. */ void set_zero_vpos(int pos); - void set_zero_vrate(double rate, bool force_update); - void update_vpos(); + void set_zero_ratio(double ratio); + double get_voltage(uint64_t index); + QString get_voltage(double v, int p, bool scaled = false); + QString get_time(double t); + + /** + * + */ + int ratio2value(double ratio) const; + int ratio2pos(double ratio) const; + double value2ratio(int value) const; + double pos2ratio(int pos) const; /** * Paints the background layer of the trace with a QPainter @@ -150,7 +171,7 @@ public: * @param left the x-coordinate of the left edge of the signal * @param right the x-coordinate of the right edge of the signal **/ - void paint_back(QPainter &p, int left, int right); + void paint_back(QPainter &p, int left, int right, QColor fore, QColor back); /** * Paints the signal with a QPainter @@ -158,7 +179,7 @@ public: * @param left the x-coordinate of the left edge of the signal. * @param right the x-coordinate of the right edge of the signal. **/ - void paint_mid(QPainter &p, int left, int right); + void paint_mid(QPainter &p, int left, int right, QColor fore, QColor back); /** * Paints the signal with a QPainter @@ -166,19 +187,13 @@ public: * @param left the x-coordinate of the left edge of the signal. * @param right the x-coordinate of the right edge of the signal. **/ - void paint_fore(QPainter &p, int left, int right); + void paint_fore(QPainter &p, int left, int right, QColor fore, QColor back); QRect get_view_rect() const; QRectF get_trig_rect(int left, int right) const; - void set_ms_show(bool show); - bool get_ms_show() const; - bool get_ms_show_hover() const; - bool get_ms_gear_hover() const; - void set_ms_en(int index, bool en); - bool get_ms_en(int index) const; - QString get_ms_string(int index) const; + QString get_measure(enum DSO_MEASURE_TYPE type); QRectF get_rect(DsoSetRegions type, int y, int right); @@ -186,61 +201,67 @@ public: bool mouse_wheel(int right, const QPoint pt, const int shift); -public slots: - void auto_end(); protected: - void paint_type_options(QPainter &p, int right, const QPoint pt); + void paint_type_options(QPainter &p, int right, const QPoint pt, QColor fore); private: - void paint_trace(QPainter &p, + void paint_trace(QPainter &p, const boost::shared_ptr &snapshot, - int zeroY, int left, const int64_t start, const int64_t end, + int zeroY, int left, const int64_t start, const int64_t end, int hw_offset, const double pixels_offset, const double samples_per_pixel, uint64_t num_channels); - void paint_envelope(QPainter &p, + void paint_envelope(QPainter &p, const boost::shared_ptr &snapshot, - int zeroY, int left, const int64_t start, const int64_t end, + int zeroY, int left, const int64_t start, const int64_t end, int hw_offset, const double pixels_offset, const double samples_per_pixel, uint64_t num_channels); - void paint_measure(QPainter &p); + void paint_hover_measure(QPainter &p, QColor fore, QColor back); + void auto_set(); private: boost::shared_ptr _data; float _scale; bool _en_lock; + bool _show; dslDial *_vDial; bool _vDialActive; uint8_t _acCoupling; uint8_t _bits; + double _ref_min; + double _ref_max; int _trig_value; double _trig_delta; - double _zero_vrate; - float _hw_offset; + int _zero_offset; + bool _mValid; uint8_t _max; uint8_t _min; double _period; + bool _level_valid; + uint8_t _high; + uint8_t _low; + double _rms; + double _mean; + double _rise_time; + double _fall_time; + double _high_time; + double _burst_time; + uint32_t _pcount; + bool _autoV; bool _autoH; + bool _autoV_over; + uint16_t _auto_cnt; bool _hover_en; uint64_t _hover_index; QPointF _hover_point; - double _hover_value; - - QRect _ms_gear_rect; - QRect _ms_show_rect; - QRect _ms_rect[DSO_MS_END-DSO_MS_BEGIN]; - bool _ms_gear_hover; - bool _ms_show_hover; - bool _ms_show; - bool _ms_en[DSO_MS_END-DSO_MS_BEGIN]; - QString _ms_string[DSO_MS_END-DSO_MS_BEGIN]; + float _hover_value; }; } // namespace view diff --git a/DSView/pv/view/groupsignal.cpp b/DSView/pv/view/groupsignal.cpp old mode 100644 new mode 100755 index 082ce139..1e67f654 --- a/DSView/pv/view/groupsignal.cpp +++ b/DSView/pv/view/groupsignal.cpp @@ -70,8 +70,10 @@ void GroupSignal::set_scale(float scale) _scale = scale; } -void GroupSignal::paint_mid(QPainter &p, int left, int right) +void GroupSignal::paint_mid(QPainter &p, int left, int right, QColor fore, QColor back) { + (void)fore; + (void)back; assert(_data); assert(_view); assert(right >= left); @@ -187,7 +189,7 @@ void GroupSignal::paint_envelope(QPainter &p, delete[] e.samples; } -void GroupSignal::paint_type_options(QPainter &p, int right, const QPoint pt) +void GroupSignal::paint_type_options(QPainter &p, int right, const QPoint pt, QColor fore) { (void)pt; @@ -195,7 +197,7 @@ void GroupSignal::paint_type_options(QPainter &p, int right, const QPoint pt) const QRectF group_index_rect = get_rect(CHNLREG, y, right); QString index_string; int last_index; - p.setPen(QPen(DARK_FORE, 1, Qt::DashLine)); + p.setPen(QPen(fore, 1, Qt::DashLine)); p.drawLine(group_index_rect.bottomLeft(), group_index_rect.bottomRight()); std::list::iterator i = _index_list.begin(); last_index = (*i); @@ -209,7 +211,7 @@ void GroupSignal::paint_type_options(QPainter &p, int right, const QPoint pt) index_string = QString::number((*i)) + "," + index_string; last_index = (*i); } - p.setPen(DARK_FORE); + p.setPen(fore); p.drawText(group_index_rect, Qt::AlignRight | Qt::AlignVCenter, index_string); } diff --git a/DSView/pv/view/groupsignal.h b/DSView/pv/view/groupsignal.h old mode 100644 new mode 100755 index cae5aa8b..358c300f --- a/DSView/pv/view/groupsignal.h +++ b/DSView/pv/view/groupsignal.h @@ -74,12 +74,12 @@ public: * @param left the x-coordinate of the left edge of the signal. * @param right the x-coordinate of the right edge of the signal. **/ - void paint_mid(QPainter &p, int left, int right); + void paint_mid(QPainter &p, int left, int right, QColor fore, QColor back); QRectF get_rect(GroupSetRegions type, int y, int right); protected: - void paint_type_options(QPainter &p, int right, const QPoint pt); + void paint_type_options(QPainter &p, int right, const QPoint pt, QColor fore); private: void paint_trace(QPainter &p, diff --git a/DSView/pv/view/header.cpp b/DSView/pv/view/header.cpp old mode 100644 new mode 100755 index 037f62b3..bd26d73b --- a/DSView/pv/view/header.cpp +++ b/DSView/pv/view/header.cpp @@ -74,6 +74,20 @@ Header::Header(View &parent) : connect(nameEdit, SIGNAL(editingFinished()), this, SLOT(on_action_set_name_triggered())); + + retranslateUi(); +} + +void Header::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) + retranslateUi(); + QWidget::changeEvent(event); +} + +void Header::retranslateUi() +{ + update(); } @@ -111,20 +125,19 @@ void Header::paintEvent(QPaintEvent*) QStyleOption o; o.initFrom(this); QPainter painter(this); - //painter.setRenderHint(QPainter::Antialiasing); style()->drawPrimitive(QStyle::PE_Widget, &o, &painter, this); - //painter.begin(this); - const int w = width(); const vector< boost::shared_ptr > traces( _view.get_traces(ALL_VIEW)); const bool dragging = !_drag_traces.empty(); + QColor fore(QWidget::palette().color(QWidget::foregroundRole())); + fore.setAlpha(View::ForeAlpha); BOOST_FOREACH(const boost::shared_ptr t, traces) { assert(t); - t->paint_label(painter, w, dragging ? QPoint(-1, -1) : _mouse_point); + t->paint_label(painter, w, dragging ? QPoint(-1, -1) : _mouse_point, fore); } painter.end(); @@ -303,7 +316,14 @@ void Header::mouseMoveEvent(QMouseEvent *event) _moveFlag = true; traces_moved(); } - } else if (sig->get_type() == SR_CHANNEL_ANALOG) { + } else if (sig->get_type() == SR_CHANNEL_MATH) { + boost::shared_ptr mathTrace; + if ((mathTrace = dynamic_pointer_cast(sig))) { + mathTrace->set_zero_vpos(y); + _moveFlag = true; + traces_moved(); + } + } else if (sig->get_type() == SR_CHANNEL_ANALOG) { boost::shared_ptr analogSig; if ((analogSig = dynamic_pointer_cast(sig))) { analogSig->set_zero_vpos(y); diff --git a/DSView/pv/view/header.h b/DSView/pv/view/header.h old mode 100644 new mode 100755 index 6bad7117..7362973a --- a/DSView/pv/view/header.h +++ b/DSView/pv/view/header.h @@ -52,6 +52,8 @@ private: const QPoint &pt); private: + void changeEvent(QEvent *event); + void retranslateUi(); void paintEvent(QPaintEvent *event); private: diff --git a/DSView/pv/view/lissajoustrace.cpp b/DSView/pv/view/lissajoustrace.cpp new file mode 100755 index 00000000..f2c5959c --- /dev/null +++ b/DSView/pv/view/lissajoustrace.cpp @@ -0,0 +1,210 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include "../../extdef.h" +#include "lissajoustrace.h" +#include "pv/data/dso.h" +#include "pv/data/dsosnapshot.h" +#include "view.h" +#include "../sigsession.h" +#include "../device/devinst.h" + +#include + +#include +#include + +using namespace boost; +using namespace std; + +namespace pv { +namespace view { + +LissajousTrace::LissajousTrace(bool enable, + boost::shared_ptr data, + int xIndex, int yIndex, int percent): + Trace("Lissajous", xIndex, SR_CHANNEL_LISSAJOUS), + _data(data), + _enable(enable), + _xIndex(xIndex), + _yIndex(yIndex), + _percent(percent) +{ + +} + +LissajousTrace::~LissajousTrace() +{ +} + +bool LissajousTrace::enabled() const +{ + return _enable; +} + +void LissajousTrace::set_enable(bool enable) +{ + _enable = enable; +} + +int LissajousTrace::xIndex() const +{ + return _xIndex; +} + +int LissajousTrace::yIndex() const +{ + return _yIndex; +} + +int LissajousTrace::percent() const +{ + return _percent; +} + +boost::shared_ptr LissajousTrace::get_data() const +{ + return _data; +} + +void LissajousTrace::set_data(boost::shared_ptr data) +{ + _data = data; +} + +int LissajousTrace::rows_size() +{ + return 0; +} + +void LissajousTrace::paint_back(QPainter &p, int left, int right, QColor fore, QColor back) +{ + assert(_view); + + fore.setAlpha(view::View::BackAlpha); + const int height = _viewport->height(); + const int width = right - left; + const int square = min(width, height); + const QPoint leftTop = QPoint(width > square ? (width-square)/2 : 0, + height > square ? (height-square)/2 : 0); + _border = QRect(leftTop.x(), leftTop.y(), square, square); + + QPen solidPen(fore); + solidPen.setStyle(Qt::SolidLine); + p.setPen(solidPen); + p.setBrush(back.black() > 0x80 ? back.darker() : back.lighter()); + p.drawRect(_border); + + QPen dashPen(fore); + dashPen.setStyle(Qt::DashLine); + p.setPen(dashPen); + + const double spanY =square / DIV_NUM; + for (int i = 1; i < DIV_NUM; i++) { + const double posY = _border.top() + spanY * i; + p.drawLine(_border.left(), posY, _border.right(), posY); + } + const double spanX = square / DIV_NUM; + for (int i = 1; i < DIV_NUM; i++) { + const double posX = _border.left() + spanX * i; + p.drawLine(posX, _border.top(), posX, _border.bottom()); + } + + fore.setAlpha(view::View::ForeAlpha); + p.setPen(fore); + p.drawText(_border.marginsRemoved(QMargins(10, 10, 10, 10)), + tr("Lissajous Figure"), Qt::AlignTop | Qt::AlignLeft); + + _view->set_back(true); +} + +void LissajousTrace::paint_mid(QPainter &p, int left, int right, QColor fore, QColor back) +{ + (void)fore; + (void)back; + (void)left; + (void)right; + + assert(_data); + assert(_view); + assert(right >= left); + + if (enabled()) { + const deque< boost::shared_ptr > &snapshots = + _data->get_snapshots(); + if (snapshots.empty()) + return; + const boost::shared_ptr &snapshot = + snapshots.front(); + if (snapshot->empty()) + return; + + int left = _border.left(); + int bottom = _border.bottom(); + double scale = _border.width() / 255.0; + uint64_t sample_count = snapshot->get_sample_count() * min(_percent / 100.0, 1.0); + QPointF *points = new QPointF[sample_count]; + QPointF *point = points; + + int channel_num = snapshot->get_channel_num(); + if (_xIndex >= channel_num || _yIndex >= channel_num) { + p.setPen(view::View::Red); + p.drawText(_border.marginsRemoved(QMargins(10, 30, 10, 30)), + tr("Data source error.")); + } else { + const uint8_t *const samples = snapshot->get_samples(0, sample_count-1, 0); + + for (uint64_t i = 0; i < sample_count; i++) { + *point++ = QPointF(left + samples[i*channel_num + _xIndex] * scale, + bottom - samples[i*channel_num + _yIndex] * scale); + } + p.setPen(view::View::Blue); + //p.drawPoints(points, sample_count); + p.drawPolyline(points, point - points); + delete[] points; + } + } +} + +void LissajousTrace::paint_fore(QPainter &p, int left, int right, QColor fore, QColor back) +{ + (void)p; + (void)left; + (void)right; + (void)fore; + (void)back; + + assert(_view); +} + +void LissajousTrace::paint_label(QPainter &p, int right, const QPoint pt, QColor fore) +{ + (void)p; + (void)right; + (void)pt; + (void)fore; +} + +} // namespace view +} // namespace pv diff --git a/DSView/pv/view/lissajoustrace.h b/DSView/pv/view/lissajoustrace.h new file mode 100755 index 00000000..1b5dd2f3 --- /dev/null +++ b/DSView/pv/view/lissajoustrace.h @@ -0,0 +1,105 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef DSVIEW_PV_LISSAJOUSTRACE_H +#define DSVIEW_PV_LISSAJOUSTRACE_H + +#include "trace.h" + +#include + +namespace pv { + +namespace data { +class Logic; +class Dso; +class Analog; +class DsoSnapshot; +} + +namespace view { + +class LissajousTrace : public Trace +{ + Q_OBJECT + +private: + static const int DIV_NUM = 10; + +public: + LissajousTrace(bool enable, + boost::shared_ptr data, + int xIndex, int yIndex, int percent); + + virtual ~LissajousTrace(); + + bool enabled() const; + void set_enable(bool enable); + int xIndex() const; + int yIndex() const; + int percent() const; + + boost::shared_ptr get_data() const; + void set_data(boost::shared_ptr data); + + int rows_size(); + + /** + * Paints the background layer of the trace with a QPainter + * @param p the QPainter to paint into. + * @param left the x-coordinate of the left edge of the signal + * @param right the x-coordinate of the right edge of the signal + **/ + void paint_back(QPainter &p, int left, int right, QColor fore, QColor back); + + /** + * Paints the signal with a QPainter + * @param p the QPainter to paint into. + * @param left the x-coordinate of the left edge of the signal. + * @param right the x-coordinate of the right edge of the signal. + **/ + void paint_mid(QPainter &p, int left, int right, QColor fore, QColor back); + + /** + * Paints the signal with a QPainter + * @param p the QPainter to paint into. + * @param left the x-coordinate of the left edge of the signal. + * @param right the x-coordinate of the right edge of the signal. + **/ + void paint_fore(QPainter &p, int left, int right, QColor fore, QColor back); + + void paint_label(QPainter &p, int right, const QPoint pt, QColor fore); + +private: + boost::shared_ptr _data; + + bool _enable; + int _xIndex; + int _yIndex; + int _percent; + QRect _border; +}; + +} // namespace view +} // namespace pv + +#endif // DSVIEW_PV_LISSAJOUSTRACE_H diff --git a/DSView/pv/view/logicsignal.cpp b/DSView/pv/view/logicsignal.cpp old mode 100644 new mode 100755 index 9a4d1527..95d57b67 --- a/DSView/pv/view/logicsignal.cpp +++ b/DSView/pv/view/logicsignal.cpp @@ -42,13 +42,6 @@ namespace view { //const float LogicSignal::Oversampling = 2.0f; const float LogicSignal::Oversampling = 1.0f; - -const QColor LogicSignal::EdgeColour(0x80, 0x80, 0x80); -const QColor LogicSignal::HighColour(0x00, 0xC0, 0x00); -const QColor LogicSignal::LowColour(0xC0, 0x00, 0x00); - -const QColor LogicSignal::DEFAULT_COLOR = QColor(150, 150, 150, 255); - const int LogicSignal::StateHeight = 12; const int LogicSignal::StateRound = 5; @@ -59,8 +52,6 @@ LogicSignal::LogicSignal(boost::shared_ptr dev_inst, _data(data), _trig(NONTRIG) { - //_colour = PROBE_COLORS[probe->index % countof(PROBE_COLORS)]; - _colour = DEFAULT_COLOR; } LogicSignal::LogicSignal(boost::shared_ptr s, @@ -128,10 +119,12 @@ bool LogicSignal::commit_trig() } } -void LogicSignal::paint_mid(QPainter &p, int left, int right) +void LogicSignal::paint_mid(QPainter &p, int left, int right, QColor fore, QColor back) { using pv::view::View; + (void)back; + assert(_data); assert(_view); assert(right >= left); @@ -204,7 +197,7 @@ void LogicSignal::paint_mid(QPainter &p, int left, int right) wave_lines.push_back(QLine(preX, preY, x, preY)); } - p.setPen(_colour); + p.setPen(_colour.isValid() ? _colour : fore); p.drawLines(wave_lines.data(), wave_lines.size()); } @@ -231,7 +224,7 @@ void LogicSignal::paint_caps(QPainter &p, QLineF *const lines, p.drawLines(lines, line - lines); } -void LogicSignal::paint_type_options(QPainter &p, int right, const QPoint pt) +void LogicSignal::paint_type_options(QPainter &p, int right, const QPoint pt, QColor fore) { int y = get_y(); const QRectF posTrig_rect = get_rect(POSTRIG, y, right); @@ -241,36 +234,28 @@ void LogicSignal::paint_type_options(QPainter &p, int right, const QPoint pt) const QRectF edgeTrig_rect = get_rect(EDGTRIG, y, right); p.setPen(Qt::NoPen); - p.setBrush(posTrig_rect.contains(pt) ? dsBlue.lighter() : - (_trig == POSTRIG) ? dsBlue : DARK_BACK); + p.setBrush(posTrig_rect.contains(pt) ? View::Blue.lighter() : + (_trig == POSTRIG) ? View::Blue : Qt::transparent); p.drawRect(posTrig_rect); - p.setBrush(higTrig_rect.contains(pt) ? dsBlue.lighter() : - (_trig == HIGTRIG) ? dsBlue : DARK_BACK); + p.setBrush(higTrig_rect.contains(pt) ? View::Blue.lighter() : + (_trig == HIGTRIG) ? View::Blue : Qt::transparent); p.drawRect(higTrig_rect); - p.setBrush(negTrig_rect.contains(pt) ? dsBlue.lighter() : - (_trig == NEGTRIG) ? dsBlue : DARK_BACK); + p.setBrush(negTrig_rect.contains(pt) ? View::Blue.lighter() : + (_trig == NEGTRIG) ? View::Blue : Qt::transparent); p.drawRect(negTrig_rect); - p.setBrush(lowTrig_rect.contains(pt) ? dsBlue.lighter() : - (_trig == LOWTRIG) ? dsBlue : DARK_BACK); + p.setBrush(lowTrig_rect.contains(pt) ? View::Blue.lighter() : + (_trig == LOWTRIG) ? View::Blue : Qt::transparent); p.drawRect(lowTrig_rect); - p.setBrush(edgeTrig_rect.contains(pt) ? dsBlue.lighter() : - (_trig == EDGTRIG) ? dsBlue : DARK_BACK); + p.setBrush(edgeTrig_rect.contains(pt) ? View::Blue.lighter() : + (_trig == EDGTRIG) ? View::Blue : Qt::transparent); p.drawRect(edgeTrig_rect); - p.setPen(QPen(DARK_FORE, 1, Qt::DashLine)); + p.setPen(QPen(fore, 1, Qt::DashLine)); p.setBrush(Qt::transparent); -// p.drawLine(posTrig_rect.right(), posTrig_rect.top(), -// posTrig_rect.right(), posTrig_rect.bottom()); -// p.drawLine(higTrig_rect.right(), higTrig_rect.top(), -// higTrig_rect.right(), higTrig_rect.bottom()); -// p.drawLine(negTrig_rect.right(), negTrig_rect.top(), -// negTrig_rect.right(), negTrig_rect.bottom()); -// p.drawLine(lowTrig_rect.right(), lowTrig_rect.top(), -// lowTrig_rect.right(), lowTrig_rect.bottom()); p.drawLine(posTrig_rect.left(), posTrig_rect.bottom(), edgeTrig_rect.right(), edgeTrig_rect.bottom()); - p.setPen(QPen(DARK_FORE, 2, Qt::SolidLine)); + p.setPen(QPen(fore, 2, Qt::SolidLine)); p.setBrush(Qt::transparent); p.drawLine(posTrig_rect.left() + 5, posTrig_rect.bottom() - 5, posTrig_rect.center().x(), posTrig_rect.bottom() - 5); diff --git a/DSView/pv/view/logicsignal.h b/DSView/pv/view/logicsignal.h old mode 100644 new mode 100755 index 5bdce2da..2cb30d4e --- a/DSView/pv/view/logicsignal.h +++ b/DSView/pv/view/logicsignal.h @@ -41,15 +41,11 @@ namespace view { class LogicSignal : public Signal { + Q_OBJECT + private: static const float Oversampling; - static const QColor EdgeColour; - static const QColor HighColour; - static const QColor LowColour; - - static const QColor DEFAULT_COLOR; - static const int StateHeight; static const int StateRound; @@ -95,7 +91,7 @@ public: * @param left the x-coordinate of the left edge of the signal. * @param right the x-coordinate of the right edge of the signal. **/ - void paint_mid(QPainter &p, int left, int right); + void paint_mid(QPainter &p, int left, int right, QColor fore, QColor back); bool measure(const QPointF &p, uint64_t &index0, uint64_t &index1, uint64_t &index2) const; @@ -112,7 +108,7 @@ public: void paint_mark(QPainter &p, int xstart, int xend, int type); protected: - void paint_type_options(QPainter &p, int right, const QPoint pt); + void paint_type_options(QPainter &p, int right, const QPoint pt, QColor fore); private: diff --git a/DSView/pv/view/mathtrace.cpp b/DSView/pv/view/mathtrace.cpp old mode 100644 new mode 100755 index 2f9fa085..825e4e48 --- a/DSView/pv/view/mathtrace.cpp +++ b/DSView/pv/view/mathtrace.cpp @@ -1,7 +1,8 @@ /* - * This file is part of the PulseView project. + * This file is part of the DSView project. + * DSView is based on PulseView. * - * Copyright (C) 2016 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,20 +20,22 @@ */ #include -#include #include -#include -#include - +#include "../../extdef.h" #include "mathtrace.h" -#include "../sigsession.h" #include "../data/dso.h" #include "../data/dsosnapshot.h" -#include "../view/dsosignal.h" -#include "../view/viewport.h" -#include "../device/devinst.h" #include "../data/mathstack.h" +#include "view.h" +#include "../sigsession.h" +#include "../device/devinst.h" +#include "../view/dsosignal.h" + +#include + +#include +#include using namespace boost; using namespace std; @@ -40,57 +43,33 @@ using namespace std; namespace pv { namespace view { -const int MathTrace::UpMargin = 0; -const int MathTrace::DownMargin = 0; -const int MathTrace::RightMargin = 30; -const QString MathTrace::FFT_ViewMode[2] = { - "Linear RMS", - "DBV RMS" -}; - -const QString MathTrace::FreqPrefixes[9] = - {"", "", "", "", "K", "M", "G", "T", "P"}; -const int MathTrace::FirstSIPrefixPower = -9; -const int MathTrace::LastSIPrefixPower = 15; -const int MathTrace::Pricision = 2; -const int MathTrace::FreqMinorDivNum = 10; -const int MathTrace::TickHeight = 15; -const int MathTrace::VolDivNum = 5; - -const int MathTrace::DbvRanges[4] = { - 100, - 120, - 150, - 200, -}; - -const int MathTrace::HoverPointSize = 3; -const double MathTrace::VerticalRate = 1.0 / 2000.0; - -MathTrace::MathTrace(pv::SigSession &session, - boost::shared_ptr math_stack, int index) : - Trace("FFT("+QString::number(index)+")", index, SR_CHANNEL_FFT), - _session(session), +MathTrace::MathTrace(bool enable, + boost::shared_ptr math_stack, + boost::shared_ptr dsoSig1, + boost::shared_ptr dsoSig2): + Trace("M", dsoSig1->get_index(), SR_CHANNEL_MATH), _math_stack(math_stack), - _enable(false), - _view_mode(0), + _dsoSig1(dsoSig1), + _dsoSig2(dsoSig2), + _enable(enable), + _show(true), + _scale(0), + _zero_vrate(0.5), + _hw_offset(0x80), _hover_en(false), - _scale(1), - _offset(0) + _hover_index(0), + _hover_point(QPointF(0, 0)), + _hover_voltage(0) { - _typeWidth = 0; - const vector< boost::shared_ptr > sigs(_session.get_signals()); - for(size_t i = 0; i < sigs.size(); i++) { - const boost::shared_ptr s(sigs[i]); - assert(s); - if (dynamic_pointer_cast(s) && index == s->get_index()) - _colour = s->get_colour(); - } + _vDial = _math_stack->get_vDial(); + update_vDial(); + _colour = View::Red; + _ref_min = dsoSig1->get_ref_min(); + _ref_max = dsoSig1->get_ref_max(); } MathTrace::~MathTrace() { - } bool MathTrace::enabled() const @@ -103,385 +82,436 @@ void MathTrace::set_enable(bool enable) _enable = enable; } -int MathTrace::view_mode() const +int MathTrace::src1() const { - return _view_mode; + return _dsoSig1->get_index(); } -void MathTrace::set_view_mode(unsigned int mode) +int MathTrace::src2() const { - assert(mode < sizeof(FFT_ViewMode)/sizeof(FFT_ViewMode[0])); - _view_mode = mode; + return _dsoSig2->get_index(); } -std::vector MathTrace::get_view_modes_support() -{ - std::vector modes; - for (unsigned int i = 0; i < sizeof(FFT_ViewMode)/sizeof(FFT_ViewMode[0]); i++) { - modes.push_back(FFT_ViewMode[i]); - } - return modes; -} - -const boost::shared_ptr& MathTrace::get_math_stack() const -{ - return _math_stack; -} - -void MathTrace::init_zoom() -{ - _scale = 1; - _offset = 0; -} - -void MathTrace::zoom(double steps, int offset) -{ - if (!_view) - return; - - const int width = get_view_rect().width(); - double pre_offset = _offset + _scale*offset/width; - _scale *= std::pow(3.0/2.0, -steps); - _scale = max(min(_scale, 1.0), 100.0/_math_stack->get_sample_num()); - _offset = pre_offset - _scale*offset/width; - _offset = max(min(_offset, 1-_scale), 0.0); - - _view->set_update(_viewport, true); - _view->update(); -} - -void MathTrace::set_offset(double delta) -{ - int width = get_view_rect().width(); - _offset = _offset + (delta*_scale / width); - _offset = max(min(_offset, 1-_scale), 0.0); - - _view->set_update(_viewport, true); - _view->update(); -} - -double MathTrace::get_offset() const -{ - return _offset; -} - -void MathTrace::set_scale(double scale) -{ - _scale = max(min(scale, 1.0), 100.0/_math_stack->get_sample_num()); - - _view->set_update(_viewport, true); - _view->update(); -} - -double MathTrace::get_scale() const +float MathTrace::get_scale() { return _scale; } -void MathTrace::set_dbv_range(int range) +int MathTrace::get_name_width() const { - _dbv_range = range; + return 0; } -int MathTrace::dbv_range() const +void MathTrace::update_vDial() { - return _dbv_range; + _vDial->set_value(_math_stack->default_vDialValue()); } -std::vector MathTrace::get_dbv_ranges() +void MathTrace::go_vDialPre() { - std::vector range; - for (unsigned int i = 0; i < sizeof(DbvRanges)/sizeof(DbvRanges[0]); i++) { - range.push_back(DbvRanges[i]); - } - return range; -} + if (enabled() && !_vDial->isMin()) { + if (_view->session().get_capture_state() == SigSession::Running) + _view->session().refresh(DsoSignal::RefreshShort); + const double pre_vdiv = _vDial->get_value(); + _vDial->set_sel(_vDial->get_sel() - 1); -QString MathTrace::format_freq(double freq, unsigned precision) -{ - if (freq <= 0) { - return "0Hz"; - } else { - const int order = floor(log10f(freq)); - assert(order >= FirstSIPrefixPower); - assert(order <= LastSIPrefixPower); - const int prefix = floor((order - FirstSIPrefixPower)/ 3.0f); - const double divider = pow(10.0, max(prefix * 3.0 + FirstSIPrefixPower, 0.0)); + if (_view->session().get_capture_state() == SigSession::Stopped) + _scale *= pre_vdiv/_vDial->get_value(); - QString s; - QTextStream ts(&s); - ts.setRealNumberPrecision(precision); - ts << fixed << freq / divider << - FreqPrefixes[prefix] << "Hz"; - return s; + _view->set_update(_viewport, true); + _view->update(); } } -bool MathTrace::measure(const QPoint &p) +void MathTrace::go_vDialNext() { - _hover_en = false; - if(!_view || !enabled()) - return false; + if (enabled() && !_vDial->isMax()) { + if (_view->session().get_capture_state() == SigSession::Running) + _view->session().refresh(DsoSignal::RefreshShort); + const double pre_vdiv = _vDial->get_value(); + _vDial->set_sel(_vDial->get_sel() + 1); - const QRect window = get_view_rect(); - if (!window.contains(p)) - return false; + if (_view->session().get_capture_state() == SigSession::Stopped) + _scale *= pre_vdiv/_vDial->get_value(); - const std::vector samples(_math_stack->get_fft_spectrum()); - if(samples.empty()) - return false; - - const unsigned int full_size = (_math_stack->get_sample_num()/2); - const double view_off = full_size * _offset; - const double view_size = full_size*_scale; - const double sample_per_pixels = view_size/window.width(); - _hover_index = std::round(p.x() * sample_per_pixels + view_off); - - if (_hover_index < full_size) - _hover_en = true; - - //_view->set_update(_viewport, true); - _view->update(); - return true; + _view->set_update(_viewport, true); + _view->update(); + } } - -void MathTrace::paint_back(QPainter &p, int left, int right) +uint64_t MathTrace::get_vDialValue() const { - if(!_view) - return; - - const int height = get_view_rect().height(); - const int width = right - left; - - QPen solidPen(Signal::dsFore); - solidPen.setStyle(Qt::SolidLine); - p.setPen(solidPen); - p.setBrush(Trace::dsBack); - p.drawRect(left, UpMargin, width, height); + return _vDial->get_value(); } -void MathTrace::paint_mid(QPainter &p, int left, int right) +uint16_t MathTrace::get_vDialSel() const { - if(!_view) - return; - assert(right >= left); + return _vDial->get_sel(); +} +double MathTrace::get_zero_ratio() +{ + return _zero_vrate; +} + +void MathTrace::set_zero_vrate(double rate) +{ + _zero_vrate = rate; + _hw_offset = _zero_vrate * (_ref_max - _ref_min) + _ref_min; +} + +int MathTrace::get_zero_vpos() const +{ + return _zero_vrate * get_view_rect().height() + DsoSignal::UpMargin; +} + +void MathTrace::set_zero_vpos(int pos) +{ if (enabled()) { - const std::vector samples(_math_stack->get_fft_spectrum()); - if(samples.empty()) - return; - - QColor trace_colour = _colour; - trace_colour.setAlpha(150); - p.setPen(trace_colour); - - const int full_size = (_math_stack->get_sample_num()/2); - const double view_off = full_size * _offset; - const int view_start = floor(view_off); - const int view_size = full_size*_scale; - QPointF *points = new QPointF[samples.size()]; - QPointF *point = points; - - const bool dc_ignored = _math_stack->dc_ignored(); - const double height = get_view_rect().height(); - const double width = right - left; - const double pixels_per_sample = width/view_size; - - double vdiv = 0; - double vfactor = 0; - BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { - boost::shared_ptr dsoSig; - if ((dsoSig = dynamic_pointer_cast(s))) { - if(dsoSig->get_index() == _math_stack->get_index()) { - vdiv = dsoSig->get_vDialValue(); - vfactor = dsoSig->get_factor(); - break; - } - } - } - if (_view_mode == 0) { - _vmin = 0; - _vmax = (vdiv*DS_CONF_DSO_HDIVS*vfactor)*VerticalRate; - } else { - _vmax = 20*log10((vdiv*DS_CONF_DSO_HDIVS*vfactor)*VerticalRate); - _vmin = _vmax - _dbv_range; - } - - //const double max_value = *std::max_element(dc_ignored ? ++samples.begin() : samples.begin(), samples.end()); - //const double min_value = *std::min_element(dc_ignored ? ++samples.begin() : samples.begin(), samples.end()); - //_vmax = (_view_mode == 0) ? max_value : 20*log10(max_value); - //_vmin = (_view_mode == 0) ? min_value : 20*log10(min_value); - const double scale = height / (_vmax - _vmin); - - double x = (view_start-view_off)*pixels_per_sample; - uint64_t sample = view_start; - if (dc_ignored && sample == 0) { - sample++; - x += pixels_per_sample; - } - double min_mag = pow(10.0, _vmin/20); - do{ - double mag = samples[sample]; - if (_view_mode != 0) { - if (mag < min_mag) - mag = _vmin; - else - mag = 20*log10(mag); - } - const double y = height - (scale * (mag - _vmin)); - *point++ = QPointF(x, y); - x += pixels_per_sample; - sample++; - }while(x= left); - - (void)left; - (void)right; - const int text_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, - AlignLeft | AlignTop, "8").height(); - const double width = get_view_rect().width(); - const double height = get_view_rect().height(); - double blank_top = 0; - double blank_right = width; - - // horizontal ruler - const double NyFreq = _session.cur_samplerate() / (2.0 * _math_stack->get_sample_interval()); - const double deltaFreq = _session.cur_samplerate() * 1.0 / - (_math_stack->get_sample_num() * _math_stack->get_sample_interval()); - const double FreqRange = NyFreq * _scale; - const double FreqOffset = NyFreq * _offset; - - const int order = (int)floor(log10(FreqRange)); - const double multiplier = (pow(10.0, order) == FreqRange) ? FreqRange/10 : pow(10.0, order); - const double freq_per_pixel = FreqRange / width; - - p.setPen(Trace::DARK_FORE); - p.setBrush(Qt::NoBrush); - double tick_freq = multiplier * (int)floor(FreqOffset / multiplier); - int division = (int)round(tick_freq * FreqMinorDivNum / multiplier); - double x = (tick_freq - FreqOffset) / freq_per_pixel; - do{ - if (division%FreqMinorDivNum == 0) { - QString freq_str = format_freq(tick_freq); - double typical_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, - AlignLeft | AlignTop, freq_str).width() + 10; - p.drawLine(x, 1, x, TickHeight); - if (x > typical_width/2 && (width-x) > typical_width/2) - p.drawText(x-typical_width/2, TickHeight, typical_width, text_height, - AlignCenter | AlignTop | TextDontClip, freq_str); - } else { - p.drawLine(x, 1, x, TickHeight/2); - } - tick_freq += multiplier/FreqMinorDivNum; - division++; - x = (tick_freq - FreqOffset) / freq_per_pixel; - } while(x < width); - blank_top = max(blank_top, (double)TickHeight + text_height); - - // delta Frequency - QString freq_str = QString::fromWCharArray(L" \u0394") + "Freq: " + format_freq(deltaFreq,4); - p.drawText(0, 0, width, get_view_rect().height(), - AlignRight | AlignBottom | TextDontClip, freq_str); - double delta_left = width-p.boundingRect(0, 0, INT_MAX, INT_MAX, - AlignLeft | AlignTop, freq_str).width(); - blank_right = min(delta_left, blank_right); - - // Vertical ruler - const double vRange = _vmax - _vmin; - const double vOffset = _vmin; - const double vol_per_tick = vRange / VolDivNum; - - p.setPen(Trace::DARK_FORE); - p.setBrush(Qt::NoBrush); - double tick_vol = vol_per_tick + vOffset; - double y = height - height / VolDivNum; - const QString unit = (_view_mode == 0) ? "" : "dbv"; - do{ - if (y > text_height && y < (height - text_height)) { - QString vol_str = QString::number(tick_vol, 'f', Pricision) + unit; - double vol_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, - AlignLeft | AlignTop, vol_str).width(); - p.drawLine(width, y, width-TickHeight/2, y); - p.drawText(width-TickHeight-vol_width, y-text_height/2, vol_width, text_height, - AlignCenter | AlignTop | TextDontClip, vol_str); - blank_right = min(width-TickHeight-vol_width, blank_right); - } - tick_vol += vol_per_tick; - y -= height / VolDivNum; - } while(y > 0); - - // Hover measure - if (_hover_en) { - const std::vector samples(_math_stack->get_fft_spectrum()); - if(samples.empty()) - return; - const int full_size = (_math_stack->get_sample_num()/2); - const double view_off = full_size * _offset; - const int view_size = full_size*_scale; - const double scale = height / (_vmax - _vmin); - const double pixels_per_sample = width/view_size; - double x = (_hover_index-view_off)*pixels_per_sample; - double min_mag = pow(10.0, _vmin/20); - _hover_value = samples[_hover_index]; - if (_view_mode != 0) { - if (_hover_value < min_mag) - _hover_value = _vmin; - else - _hover_value = 20*log10(_hover_value); - } - const double y = height - (scale * (_hover_value - _vmin)); - _hover_point = QPointF(x, y); - - p.setPen(QPen(Trace::DARK_FORE, 1, Qt::DashLine)); - p.setBrush(Qt::NoBrush); - p.drawLine(_hover_point.x(), 0, _hover_point.x(), height); - - QString hover_str = QString::number(_hover_value, 'f', 4) + unit + "@" + format_freq(deltaFreq * _hover_index, 4); - const int hover_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, - AlignLeft | AlignTop, hover_str).width(); - const int hover_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, - AlignLeft | AlignTop, hover_str).height(); - QRectF hover_rect(_hover_point.x(), _hover_point.y()-hover_height, hover_width, hover_height); - if (hover_rect.right() > blank_right) - hover_rect.moveRight(min(_hover_point.x(), blank_right)); - if (hover_rect.top() < blank_top) - hover_rect.moveTop(max(_hover_point.y(), blank_top)); - if (hover_rect.top() > 0) - p.drawText(hover_rect, AlignCenter | AlignTop | TextDontClip, hover_str); - - p.setPen(Qt::NoPen); - p.setBrush(Trace::DARK_FORE); - p.drawEllipse(_hover_point, HoverPointSize, HoverPointSize); - } -} - -void MathTrace::paint_type_options(QPainter &p, int right, const QPoint pt) -{ - (void)p; - (void)pt; - (void)right; + _show = show; } QRect MathTrace::get_view_rect() const { assert(_viewport); - return QRect(0, UpMargin, - _viewport->width() - RightMargin, - _viewport->height() - UpMargin - DownMargin); + return QRect(0, DsoSignal::UpMargin, + _viewport->width() - DsoSignal::RightMargin, + _viewport->height() - DsoSignal::UpMargin - DsoSignal::DownMargin); } +void MathTrace::paint_back(QPainter &p, int left, int right, QColor fore, QColor back) +{ + (void)p; + (void)left; + (void)right; + (void)fore; + (void)back; +} + +void MathTrace::paint_mid(QPainter &p, int left, int right, QColor fore, QColor back) +{ + (void)fore; + (void)back; + + if (!_show) + return; + + assert(_math_stack); + assert(_view); + assert(right >= left); + + if (enabled()) { + const float top = get_view_rect().top(); + const int height = get_view_rect().height(); + const int width = right - left; + const float zeroY = _zero_vrate * height + top; + + const double scale = _view->scale(); + assert(scale > 0); + const int64_t offset = _view->offset(); + + const double pixels_offset = offset; + const double samplerate = _view->session().cur_samplerate(); + const int64_t last_sample = max((int64_t)(_math_stack->get_sample_num() - 1), (int64_t)0); + const double samples_per_pixel = samplerate * scale; + const double start = offset * samples_per_pixel; + const double end = start + samples_per_pixel * width; + + const int64_t start_sample = min(max((int64_t)floor(start), + (int64_t)0), last_sample); + const int64_t end_sample = min(max((int64_t)ceil(end) + 1, + (int64_t)0), last_sample); + + _scale = get_view_rect().height() * _math_stack->get_math_scale() * 1000.0 / get_vDialValue(); + + if (samples_per_pixel < DsoSignal::EnvelopeThreshold) { + _math_stack->enable_envelope(false); + paint_trace(p, zeroY, left, + start_sample, end_sample, + pixels_offset, samples_per_pixel); + } else { + _math_stack->enable_envelope(true); + paint_envelope(p, zeroY, left, + start_sample, end_sample, + pixels_offset, samples_per_pixel); + } + } +} + +void MathTrace::paint_fore(QPainter &p, int left, int right, QColor fore, QColor back) +{ + if (!_show) + return; + + assert(_view); + + fore.setAlpha(View::BackAlpha); + QPen pen(fore); + pen.setStyle(Qt::DotLine); + p.setPen(pen); + p.drawLine(left, get_zero_vpos(), right, get_zero_vpos()); + + // Paint measure + fore.setAlpha(View::ForeAlpha); + if (_view->session().get_capture_state() == SigSession::Stopped) + paint_hover_measure(p, fore, back); +} + +void MathTrace::paint_trace(QPainter &p, + int zeroY, int left, const int64_t start, const int64_t end, + const double pixels_offset, const double samples_per_pixel) +{ + const int64_t sample_count = end - start + 1; + + if (sample_count > 0) { + QColor trace_colour = _colour; + trace_colour.setAlpha(View::ForeAlpha); + p.setPen(trace_colour); + + if ((uint64_t)end >= _math_stack->get_sample_num()) + return; + + const double *const values = _math_stack->get_math(start); + assert(values); + + QPointF *points = new QPointF[sample_count]; + QPointF *point = points; + + double top = get_view_rect().top(); + double bottom = get_view_rect().bottom(); + float x = (start / samples_per_pixel - pixels_offset) + left; + double pixels_per_sample = 1.0/samples_per_pixel; + + for (int64_t index = 0; index < sample_count; index++) { + *point++ = QPointF(x, min(max(top, zeroY - (values[index] * _scale)), bottom)); + x += pixels_per_sample; + } + + p.drawPolyline(points, point - points); + p.eraseRect(get_view_rect().right()+1, get_view_rect().top(), + _view->viewport()->width() - get_view_rect().width(), get_view_rect().height()); + + delete[] points; + } +} + +void MathTrace::paint_envelope(QPainter &p, + int zeroY, int left, const int64_t start, const int64_t end, + const double pixels_offset, const double samples_per_pixel) +{ + using namespace Qt; + + data::MathStack::EnvelopeSection e; + _math_stack->get_math_envelope_section(e, start, end, samples_per_pixel); + + if (e.length < 2) + return; + + p.setPen(QPen(NoPen)); + QColor envelope_colour = _colour; + envelope_colour.setAlpha(View::ForeAlpha); + p.setBrush(envelope_colour); + + QRectF *const rects = new QRectF[e.length]; + QRectF *rect = rects; + double top = get_view_rect().top(); + double bottom = get_view_rect().bottom(); + for(uint64_t sample = 0; sample < e.length-1; sample++) { + const float x = ((e.scale * sample + e.start) / + samples_per_pixel - pixels_offset) + left; + const data::MathStack::EnvelopeSample *const s = + e.samples + sample; + + // We overlap this sample with the next so that vertical + // gaps do not appear during steep rising or falling edges + const float b = min(max(top, zeroY - max(s->max, (s+1)->min) * _scale), bottom); + const float t = min(max(top, zeroY - min(s->min, (s+1)->max) * _scale), bottom); + + float h = b - t; + if(h >= 0.0f && h <= 1.0f) + h = 1.0f; + if(h <= 0.0f && h >= -1.0f) + h = -1.0f; + + *rect++ = QRectF(x, t, 1.0f, h); + } + + p.drawRects(rects, e.length); + + delete[] rects; +} + +void MathTrace::paint_type_options(QPainter &p, int right, const QPoint pt, QColor fore) +{ + p.setRenderHint(QPainter::Antialiasing, true); + + QColor foreBack = fore; + foreBack.setAlpha(View::BackAlpha); + int y = get_y(); + const QRectF vDial_rect = get_rect(DSO_VDIAL, y, right); + + QString pText; + _vDial->paint(p, vDial_rect, _colour, pt, pText); + QFontMetrics fm(p.font()); + const QRectF valueRect = QRectF(0, vDial_rect.top()-fm.height()-10, right, fm.height()); + p.drawText(valueRect, Qt::AlignCenter, pText); + + p.setRenderHint(QPainter::Antialiasing, false); +} + +bool MathTrace::mouse_wheel(int right, const QPoint pt, const int shift) +{ + int y = get_y(); + const QRectF vDial_rect = get_rect(DSO_VDIAL, y, right); + + if (vDial_rect.contains(pt)) { + if (shift > 0.5) + go_vDialPre(); + else if (shift < -0.5) + go_vDialNext(); + return true; + } else { + return false; + } + + return true; +} + +QRectF MathTrace::get_rect(MathSetRegions type, int y, int right) +{ + (void)right; + + if (type == DSO_VDIAL) + return QRectF( + get_leftWidth() + SquareWidth*0.5 + Margin, + y - SquareWidth * SquareNum + SquareWidth * 3, + SquareWidth * (SquareNum-1), SquareWidth * (SquareNum-1)); + else + return QRectF(0, 0, 0, 0); +} + +void MathTrace::paint_hover_measure(QPainter &p, QColor fore, QColor back) +{ + // Hover measure + if (_hover_en) { + QString hover_str = get_voltage(_hover_voltage, 2); + const int hover_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, hover_str).width() + 10; + const int hover_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, hover_str).height(); + QRectF hover_rect(_hover_point.x(), _hover_point.y()-hover_height/2, hover_width, hover_height); + if (hover_rect.right() > get_view_rect().right()) + hover_rect.moveRight(_hover_point.x()); + if (hover_rect.top() < get_view_rect().top()) + hover_rect.moveTop(_hover_point.y()); + if (hover_rect.bottom() > get_view_rect().bottom()) + hover_rect.moveBottom(_hover_point.y()); + + p.setPen(fore); + p.setBrush(back); + p.drawRect(_hover_point.x()-1, _hover_point.y()-1, + DsoSignal::HoverPointSize, DsoSignal::HoverPointSize); + p.drawText(hover_rect, Qt::AlignCenter | Qt::AlignTop | Qt::TextDontClip, hover_str); + } + + list::iterator i = _view->get_cursorList().begin(); + while (i != _view->get_cursorList().end()) { + float pt_value; + const QPointF pt = get_point((*i)->index(), pt_value); + QString pt_str = get_voltage(pt_value, 2); + const int pt_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, pt_str).width() + 10; + const int pt_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, pt_str).height(); + QRectF pt_rect(pt.x(), pt.y()-pt_height/2, pt_width, pt_height); + if (pt_rect.right() > get_view_rect().right()) + pt_rect.moveRight(pt.x()); + if (pt_rect.top() < get_view_rect().top()) + pt_rect.moveTop(pt.y()); + if (pt_rect.bottom() > get_view_rect().bottom()) + pt_rect.moveBottom(pt.y()); + + p.drawRect(pt.x()-1, pt.y()-1, 2, 2); + p.drawLine(pt.x()-2, pt.y()-2, pt.x()+2, pt.y()+2); + p.drawLine(pt.x()+2, pt.y()-2, pt.x()-2, pt.y()+2); + p.drawText(pt_rect, Qt::AlignCenter | Qt::AlignTop | Qt::TextDontClip, pt_str); + + i++; + } +} + +bool MathTrace::measure(const QPointF &p) +{ + _hover_en = false; + if (!enabled()) + return false; + + const QRectF window = get_view_rect(); + if (!window.contains(p)) + return false; + + const double scale = _view->scale(); + assert(scale > 0); + const int64_t pixels_offset = _view->offset(); + const double samplerate = _view->session().cur_samplerate(); + const double samples_per_pixel = samplerate * scale; + + _hover_index = floor((p.x() + pixels_offset) * samples_per_pixel+0.5); + if (_hover_index >= _math_stack->get_sample_num()) + return false; + + _hover_point = get_point(_hover_index, _hover_voltage); + _hover_en = true; + return true; +} + +QPointF MathTrace::get_point(uint64_t index, float &value) +{ + QPointF pt = QPointF(0, 0); + + const double scale = _view->scale(); + assert(scale > 0); + const int64_t pixels_offset = _view->offset(); + const double samplerate = _view->session().cur_samplerate(); + const double samples_per_pixel = samplerate * scale; + + const float top = get_view_rect().top(); + const float bottom = get_view_rect().bottom(); + const float zeroP = _zero_vrate * get_view_rect().height() + top; + const float x = (index / samples_per_pixel - pixels_offset); + + value = *_math_stack->get_math(index); + float y = min(max(top, zeroP - (value * _scale)), bottom); + pt = QPointF(x, y); + return pt; +} + +QString MathTrace::get_voltage(double v, int p) +{ + return abs(v) >= 1 ? QString::number(v, 'f', p) + _math_stack->get_unit(1) : + QString::number(v * 1000, 'f', p) + _math_stack->get_unit(0); +} + +QString MathTrace::get_time(double t) +{ + QString str = (abs(t) > 1000000000 ? QString::number(t/1000000000, 'f', 2) + "S" : + abs(t) > 1000000 ? QString::number(t/1000000, 'f', 2) + "mS" : + abs(t) > 1000 ? QString::number(t/1000, 'f', 2) + "uS" : QString::number(t, 'f', 2) + "nS"); + return str; +} + +const boost::shared_ptr& MathTrace::get_math_stack() const +{ + return _math_stack; +} + + } // namespace view } // namespace pv diff --git a/DSView/pv/view/mathtrace.h b/DSView/pv/view/mathtrace.h old mode 100644 new mode 100755 index 99786f44..3bcd9428 --- a/DSView/pv/view/mathtrace.h +++ b/DSView/pv/view/mathtrace.h @@ -1,7 +1,8 @@ /* - * This file is part of the PulseView project. + * This file is part of the DSView project. + * DSView is based on PulseView. * - * Copyright (C) 2016 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,139 +19,156 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef DSVIEW_PV_VIEW_MATHTRACE_H -#define DSVIEW_PV_VIEW_MATHTRACE_H + +#ifndef DSVIEW_PV_MATHTRACE_H +#define DSVIEW_PV_MATHTRACE_H #include "trace.h" -#include -#include - #include -struct srd_channel; - namespace pv { -class SigSession; - -namespace data{ +namespace data { +class Logic; +class Dso; +class Analog; +class DsoSnapshot; class MathStack; } namespace view { +class DsoSignal; + class MathTrace : public Trace { Q_OBJECT -private: - static const int UpMargin; - static const int DownMargin; - static const int RightMargin; - static const QString FFT_ViewMode[2]; - - static const QString FreqPrefixes[9]; - static const int FirstSIPrefixPower; - static const int LastSIPrefixPower; - static const int Pricision; - static const int FreqMinorDivNum; - static const int TickHeight; - static const int VolDivNum; - - static const int DbvRanges[4]; - - static const int HoverPointSize; - - static const double VerticalRate; +public: + enum MathSetRegions { + DSO_NONE = -1, + DSO_VDIAL, + }; public: - MathTrace(pv::SigSession &session, - boost::shared_ptr math_stack, int index); - ~MathTrace(); + MathTrace(bool enable, boost::shared_ptr math_stack, + boost::shared_ptr dsoSig1, + boost::shared_ptr dsoSig2); + + virtual ~MathTrace(); + + float get_scale(); + + int get_name_width() const; + + /** + * + */ + void update_vDial(); + void go_vDialPre(); + void go_vDialNext(); + uint64_t get_vDialValue() const; + uint16_t get_vDialSel() const; bool enabled() const; void set_enable(bool enable); + void set_show(bool show); - void init_zoom(); - void zoom(double steps, int offset); - bool zoom_hit() const; - void set_zoom_hit(bool hit); + int get_zero_vpos() const; + void set_zero_vpos(int pos); - void set_offset(double delta); - double get_offset() const; + int src1() const; + int src2() const; - void set_scale(double scale); - double get_scale() const; + /** + * + */ + bool measure(const QPointF &p); + QPointF get_point(uint64_t index, float &value); - void set_dbv_range(int range); - int dbv_range() const; - std::vector get_dbv_ranges(); - int view_mode() const; - void set_view_mode(unsigned int mode); - std::vector get_view_modes_support(); + /** + * Gets the mid-Y position of this signal. + */ + double get_zero_ratio(); - const boost::shared_ptr& get_math_stack() const; + /** + * Sets the mid-Y position of this signal. + */ + void set_zero_vrate(double rate); - static QString format_freq(double freq, unsigned precision = Pricision); - - bool measure(const QPoint &p); + QString get_voltage(double v, int p); + QString get_time(double t); /** * Paints the background layer of the trace with a QPainter * @param p the QPainter to paint into. + * @param left the x-coordinate of the left edge of the signal + * @param right the x-coordinate of the right edge of the signal + **/ + void paint_back(QPainter &p, int left, int right, QColor fore, QColor back); + + /** + * Paints the signal with a QPainter + * @param p the QPainter to paint into. + * @param left the x-coordinate of the left edge of the signal. + * @param right the x-coordinate of the right edge of the signal. + **/ + void paint_mid(QPainter &p, int left, int right, QColor fore, QColor back); + + /** + * Paints the signal with a QPainter + * @param p the QPainter to paint into. * @param left the x-coordinate of the left edge of the signal. * @param right the x-coordinate of the right edge of the signal. **/ - void paint_back(QPainter &p, int left, int right); - - /** - * Paints the mid-layer of the trace with a QPainter - * @param p the QPainter to paint into. - * @param left the x-coordinate of the left edge of the signal - * @param right the x-coordinate of the right edge of the signal - **/ - void paint_mid(QPainter &p, int left, int right); - - /** - * Paints the foreground layer of the trace with a QPainter - * @param p the QPainter to paint into. - * @param left the x-coordinate of the left edge of the signal - * @param right the x-coordinate of the right edge of the signal - **/ - void paint_fore(QPainter &p, int left, int right); + void paint_fore(QPainter &p, int left, int right, QColor fore, QColor back); QRect get_view_rect() const; + QRectF get_rect(MathSetRegions type, int y, int right); + + bool mouse_wheel(int right, const QPoint pt, const int shift); + + const boost::shared_ptr& get_math_stack() const; + protected: - void paint_type_options(QPainter &p, int right, const QPoint pt); + void paint_type_options(QPainter &p, int right, const QPoint pt, QColor fore); private: + void paint_trace(QPainter &p, + int zeroY, int left, const int64_t start, const int64_t end, + const double pixels_offset, const double samples_per_pixel); -private slots: + void paint_envelope(QPainter &p, + int zeroY, int left, const int64_t start, const int64_t end, + const double pixels_offset, const double samples_per_pixel); + + void paint_hover_measure(QPainter &p, QColor fore, QColor back); private: - pv::SigSession &_session; boost::shared_ptr _math_stack; - + boost::shared_ptr _dsoSig1; + boost::shared_ptr _dsoSig2; bool _enable; - int _view_mode; + bool _show; - double _vmax; - double _vmin; - int _dbv_range; + dslDial *_vDial; + double _ref_min; + double _ref_max; + float _scale; + + double _zero_vrate; + float _hw_offset; - uint64_t _hover_index; bool _hover_en; + uint64_t _hover_index; QPointF _hover_point; - double _hover_value; - - double _scale; - double _offset; + float _hover_voltage; }; } // namespace view } // namespace pv -#endif // DSVIEW_PV_VIEW_FFTTRACE_H +#endif // DSVIEW_PV_MATHTRACE_H diff --git a/DSView/pv/view/ruler.cpp b/DSView/pv/view/ruler.cpp old mode 100644 new mode 100755 index d42fc1b4..9de9a26d --- a/DSView/pv/view/ruler.cpp +++ b/DSView/pv/view/ruler.cpp @@ -59,7 +59,7 @@ const QString Ruler::FreqPrefixes[9] = const int Ruler::FirstSIPrefixPower = -15; const int Ruler::pricision = 2; -const int Ruler::HoverArrowSize = 5; +const int Ruler::HoverArrowSize = 4; const int Ruler::CursorSelWidth = 20; const QColor Ruler::CursorColor[8] = @@ -72,15 +72,6 @@ const QColor Ruler::CursorColor[8] = QColor(231, 126, 34, 200), QColor(232, 76, 61, 200)}; -const QColor Ruler::dsBlue = QColor(17, 133, 209, 255); -const QColor Ruler::dsYellow = QColor(238, 178, 17, 255); -const QColor Ruler::dsRed = QColor(213, 15, 37, 255); -const QColor Ruler::dsGreen = QColor(0, 153, 37, 255); -const QColor Ruler::RULER_COLOR = QColor(255, 255, 255, 255); - -const QColor Ruler::HitColor = dsYellow; -const QColor Ruler::WarnColor = dsRed; - Ruler::Ruler(View &parent) : QWidget(&parent), _view(parent), @@ -183,14 +174,10 @@ void Ruler::paintEvent(QPaintEvent*) QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this); - //p.begin(this); - //QPainter p(this); - //p.setRenderHint(QPainter::Antialiasing); - // Draw tick mark draw_logic_tick_mark(p); - p.setRenderHint(QPainter::Antialiasing); + p.setRenderHint(QPainter::Antialiasing, true); // Draw the hover mark draw_hover_mark(p); @@ -335,105 +322,6 @@ void Ruler::mouseReleaseEvent(QMouseEvent *event) } } -void Ruler::draw_tick_mark(QPainter &p) -{ - using namespace Qt; - - const double SpacingIncrement = 32.0; - const double MinValueSpacing = 16.0; - const int ValueMargin = 15; - - double min_width = SpacingIncrement, typical_width; - double tick_period; - unsigned int prefix; - - // Find tick spacing, and number formatting that does not cause - // value to collide. - do - { - _min_period = _view.scale() * min_width; - - //const int order = (int)floorf(log10f(_min_period)); - const int order = ceil(log10f(_min_period)); - const double order_decimal = pow(10.0, static_cast(order)); - - unsigned int unit = 0; - - do - { - tick_period = order_decimal * ScaleUnits[unit++]; - } while (tick_period < _min_period && unit < countof(ScaleUnits)); - - prefix = ceil((order - FirstSIPrefixPower) / 3.0f); - assert(prefix < countof(SIPrefixes)); - - - typical_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, - AlignLeft | AlignTop, format_time(_view.offset() * _view.scale(), - prefix)).width() + MinValueSpacing; - - min_width += SpacingIncrement; - - } while(typical_width > tick_period / _view.scale()); - - const int text_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, - AlignLeft | AlignTop, "8").height(); - - // Draw the tick marks - p.setPen(dsBlue); - - const double minor_tick_period = tick_period / MinorTickSubdivision; - const double first_major_division = - floor(_view.offset() * _view.scale() / tick_period); - const double first_minor_division = - ceil(_view.offset() * _view.scale() / minor_tick_period); - const double t0 = first_major_division * tick_period; - - int division = (int)round(first_minor_division - - first_major_division * MinorTickSubdivision) - 1; - - const int major_tick_y1 = text_height + ValueMargin * 3; - const int tick_y2 = height(); - const int minor_tick_y1 = (major_tick_y1 + tick_y2) / 2; - - double x; - - do { - const double t = t0 + division * minor_tick_period; - x = t / _view.scale() - _view.offset(); - - if (division % MinorTickSubdivision == 0) - { - // Draw a major tick - p.drawText(x, 2 * ValueMargin, 0, text_height, - AlignCenter | AlignTop | TextDontClip, - format_time(t, prefix)); - p.drawLine(QPointF(x, major_tick_y1), - QPointF(x, tick_y2)); - } - else - { - // Draw a minor tick - p.drawLine(QPointF(x, minor_tick_y1), - QPointF(x, tick_y2)); - } - - division++; - - } while (x < _view.get_view_width()); - - // Draw the cursors - if (!_view.get_cursorList().empty()) { - list::iterator i = _view.get_cursorList().begin(); - int index = 1; - while (i != _view.get_cursorList().end()) { - (*i)->paint_label(p, rect(), prefix, index); - index++; - i++; - } - } -} - void Ruler::draw_logic_tick_mark(QPainter &p) { using namespace Qt; @@ -476,7 +364,9 @@ void Ruler::draw_logic_tick_mark(QPainter &p) AlignLeft | AlignTop, "8").height(); // Draw the tick marks - p.setPen(Trace::DARK_FORE); + QColor fore(QWidget::palette().color(QWidget::foregroundRole())); + fore.setAlpha(View::ForeAlpha); + p.setPen(fore); const double minor_tick_period = tick_period / MinPeriodScale; const int minor_order = (int)floorf(log10f(minor_tick_period)); @@ -564,16 +454,14 @@ void Ruler::draw_hover_mark(QPainter &p) if (x == -1 || _grabbed_marker) return; - p.setPen(QPen(Qt::NoPen)); - p.setBrush(RULER_COLOR); + QColor fore(QWidget::palette().color(QWidget::foregroundRole())); + p.setPen(fore); + p.setBrush(fore); const int b = height() - 1; - const QPointF points[] = { - QPointF(x, b), - QPointF(x - HoverArrowSize, b - HoverArrowSize), - QPointF(x + HoverArrowSize, b - HoverArrowSize) - }; - p.drawPolygon(points, countof(points)); + for (int i = 0; i < HoverArrowSize; i++) + for (int j = -i; j <= i; j++) + p.drawPoint(x-j, b-i); } void Ruler::draw_cursor_sel(QPainter &p) @@ -582,11 +470,11 @@ void Ruler::draw_cursor_sel(QPainter &p) return; p.setPen(QPen(Qt::NoPen)); - p.setBrush(dsBlue); + p.setBrush(View::Blue); const QPoint pos = QPoint(_view.hover_point().x(), _view.hover_point().y()); if (in_cursor_sel_rect(pos) == 0) - p.setBrush(HitColor); + p.setBrush(View::Orange); const int y = height(); const QRectF selRect = get_cursor_sel_rect(0); @@ -616,7 +504,7 @@ void Ruler::draw_cursor_sel(QPainter &p) cursorRect.left(), cursorRect.bottom() - 3); p.setPen(QPen(Qt::NoPen)); if (in_cursor_sel_rect(pos) == index) - p.setBrush(HitColor); + p.setBrush(View::Orange); else p.setBrush(CursorColor[(index - 1)%8]); p.drawRect(cursorRect); diff --git a/DSView/pv/view/ruler.h b/DSView/pv/view/ruler.h old mode 100644 new mode 100755 index d9df2ed4..63dff231 --- a/DSView/pv/view/ruler.h +++ b/DSView/pv/view/ruler.h @@ -50,16 +50,8 @@ private: static const int HoverArrowSize; static const int CursorSelWidth; - static const QColor dsBlue; - static const QColor dsYellow; - static const QColor dsRed; - static const QColor dsGreen; - static const QColor RULER_COLOR; - public: static const QColor CursorColor[8]; - static const QColor HitColor; - static const QColor WarnColor; public: Ruler(View &parent); @@ -86,7 +78,6 @@ private: void leaveEvent(QEvent *); private: - void draw_tick_mark(QPainter &p); void draw_logic_tick_mark(QPainter &p); /** * Draw a hover arrow under the cursor position. diff --git a/DSView/pv/view/selectableitem.cpp b/DSView/pv/view/selectableitem.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/view/selectableitem.h b/DSView/pv/view/selectableitem.h old mode 100644 new mode 100755 diff --git a/DSView/pv/view/signal.cpp b/DSView/pv/view/signal.cpp old mode 100644 new mode 100755 index 54880748..84efd3be --- a/DSView/pv/view/signal.cpp +++ b/DSView/pv/view/signal.cpp @@ -60,12 +60,6 @@ void Signal::set_name(QString name) _probe->name = g_strdup(name.toLocal8Bit().data()); } -void Signal::paint_axis(QPainter &p, int y, int left, int right) -{ - p.setPen(SignalAxisPen); - p.drawLine(QPointF(left, y + 0.5f), QPointF(right, y + 0.5f)); -} - boost::shared_ptr Signal::get_device() const { return _dev_inst; diff --git a/DSView/pv/view/signal.h b/DSView/pv/view/signal.h old mode 100644 new mode 100755 index 944fa7c9..0c37ca9d --- a/DSView/pv/view/signal.h +++ b/DSView/pv/view/signal.h @@ -52,6 +52,8 @@ namespace view { class Signal : public Trace { + Q_OBJECT + private: @@ -89,17 +91,6 @@ public: boost::shared_ptr get_device() const; -protected: - - /** - * Paints a zero axis across the viewport. - * @param p the QPainter to paint into. - * @param y the y-offset of the axis. - * @param left the x-coordinate of the left edge of the view. - * @param right the x-coordinate of the right edge of the view. - */ - void paint_axis(QPainter &p, int y, int left, int right); - protected: boost::shared_ptr _dev_inst; sr_channel *const _probe; diff --git a/DSView/pv/view/spectrumtrace.cpp b/DSView/pv/view/spectrumtrace.cpp new file mode 100755 index 00000000..8b461ee5 --- /dev/null +++ b/DSView/pv/view/spectrumtrace.cpp @@ -0,0 +1,494 @@ +/* + * This file is part of the PulseView project. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include + +#include +#include + +#include "spectrumtrace.h" +#include "../sigsession.h" +#include "../data/dso.h" +#include "../data/dsosnapshot.h" +#include "../view/dsosignal.h" +#include "../view/viewport.h" +#include "../device/devinst.h" +#include "../data/spectrumstack.h" + +using namespace boost; +using namespace std; + +namespace pv { +namespace view { + +const int SpectrumTrace::UpMargin = 0; +const int SpectrumTrace::DownMargin = 0; +const int SpectrumTrace::RightMargin = 30; +const QString SpectrumTrace::FFT_ViewMode[2] = { + "Linear RMS", + "DBV RMS" +}; + +const QString SpectrumTrace::FreqPrefixes[9] = + {"", "", "", "", "K", "M", "G", "T", "P"}; +const int SpectrumTrace::FirstSIPrefixPower = -9; +const int SpectrumTrace::LastSIPrefixPower = 15; +const int SpectrumTrace::Pricision = 2; +const int SpectrumTrace::FreqMinorDivNum = 10; +const int SpectrumTrace::TickHeight = 15; +const int SpectrumTrace::VolDivNum = 5; + +const int SpectrumTrace::DbvRanges[4] = { + 100, + 120, + 150, + 200, +}; + +const int SpectrumTrace::HoverPointSize = 3; +const double SpectrumTrace::VerticalRate = 1.0 / 2000.0; + +SpectrumTrace::SpectrumTrace(pv::SigSession &session, + boost::shared_ptr spectrum_stack, int index) : + Trace("FFT("+QString::number(index)+")", index, SR_CHANNEL_FFT), + _session(session), + _spectrum_stack(spectrum_stack), + _enable(false), + _view_mode(0), + _hover_en(false), + _scale(1), + _offset(0) +{ + _typeWidth = 0; + const vector< boost::shared_ptr > sigs(_session.get_signals()); + for(size_t i = 0; i < sigs.size(); i++) { + const boost::shared_ptr s(sigs[i]); + assert(s); + if (dynamic_pointer_cast(s) && index == s->get_index()) + _colour = s->get_colour(); + } +} + +SpectrumTrace::~SpectrumTrace() +{ + +} + +bool SpectrumTrace::enabled() const +{ + return _enable; +} + +void SpectrumTrace::set_enable(bool enable) +{ + _enable = enable; +} + +int SpectrumTrace::view_mode() const +{ + return _view_mode; +} + +void SpectrumTrace::set_view_mode(unsigned int mode) +{ + assert(mode < sizeof(FFT_ViewMode)/sizeof(FFT_ViewMode[0])); + _view_mode = mode; +} + +std::vector SpectrumTrace::get_view_modes_support() +{ + std::vector modes; + for (unsigned int i = 0; i < sizeof(FFT_ViewMode)/sizeof(FFT_ViewMode[0]); i++) { + modes.push_back(FFT_ViewMode[i]); + } + return modes; +} + +const boost::shared_ptr& SpectrumTrace::get_spectrum_stack() const +{ + return _spectrum_stack; +} + +void SpectrumTrace::init_zoom() +{ + _scale = 1; + _offset = 0; +} + +void SpectrumTrace::zoom(double steps, int offset) +{ + if (!_view) + return; + + const int width = get_view_rect().width(); + double pre_offset = _offset + _scale*offset/width; + _scale *= std::pow(3.0/2.0, -steps); + _scale = max(min(_scale, 1.0), 100.0/_spectrum_stack->get_sample_num()); + _offset = pre_offset - _scale*offset/width; + _offset = max(min(_offset, 1-_scale), 0.0); + + _view->set_update(_viewport, true); + _view->update(); +} + +void SpectrumTrace::set_offset(double delta) +{ + int width = get_view_rect().width(); + _offset = _offset + (delta*_scale / width); + _offset = max(min(_offset, 1-_scale), 0.0); + + _view->set_update(_viewport, true); + _view->update(); +} + +double SpectrumTrace::get_offset() const +{ + return _offset; +} + +void SpectrumTrace::set_scale(double scale) +{ + _scale = max(min(scale, 1.0), 100.0/_spectrum_stack->get_sample_num()); + + _view->set_update(_viewport, true); + _view->update(); +} + +double SpectrumTrace::get_scale() const +{ + return _scale; +} + +void SpectrumTrace::set_dbv_range(int range) +{ + _dbv_range = range; +} + +int SpectrumTrace::dbv_range() const +{ + return _dbv_range; +} + +std::vector SpectrumTrace::get_dbv_ranges() +{ + std::vector range; + for (unsigned int i = 0; i < sizeof(DbvRanges)/sizeof(DbvRanges[0]); i++) { + range.push_back(DbvRanges[i]); + } + return range; +} + +QString SpectrumTrace::format_freq(double freq, unsigned precision) +{ + if (freq <= 0) { + return "0Hz"; + } else { + const int order = floor(log10f(freq)); + assert(order >= FirstSIPrefixPower); + assert(order <= LastSIPrefixPower); + const int prefix = floor((order - FirstSIPrefixPower)/ 3.0f); + const double divider = pow(10.0, max(prefix * 3.0 + FirstSIPrefixPower, 0.0)); + + QString s; + QTextStream ts(&s); + ts.setRealNumberPrecision(precision); + ts << fixed << freq / divider << + FreqPrefixes[prefix] << "Hz"; + return s; + } +} + +bool SpectrumTrace::measure(const QPoint &p) +{ + _hover_en = false; + if(!_view || !enabled()) + return false; + + const QRect window = get_view_rect(); + if (!window.contains(p)) + return false; + + const std::vector samples(_spectrum_stack->get_fft_spectrum()); + if(samples.empty()) + return false; + + const unsigned int full_size = (_spectrum_stack->get_sample_num()/2); + const double view_off = full_size * _offset; + const double view_size = full_size*_scale; + const double sample_per_pixels = view_size/window.width(); + _hover_index = std::round(p.x() * sample_per_pixels + view_off); + + if (_hover_index < full_size) + _hover_en = true; + + //_view->set_update(_viewport, true); + _view->update(); + return true; +} + + +void SpectrumTrace::paint_back(QPainter &p, int left, int right, QColor fore, QColor back) +{ + if(!_view) + return; + + const int height = get_view_rect().height(); + const int width = right - left; + + fore.setAlpha(View::BackAlpha); + QPen solidPen(fore); + solidPen.setStyle(Qt::SolidLine); + p.setPen(solidPen); + p.setBrush(back.black() > 0x80 ? back.darker() : back.lighter()); + p.drawRect(left, UpMargin, width, height); +} + +void SpectrumTrace::paint_mid(QPainter &p, int left, int right, QColor fore, QColor back) +{ + (void)fore; + (void)back; + + if(!_view) + return; + assert(right >= left); + + if (enabled()) { + const std::vector samples(_spectrum_stack->get_fft_spectrum()); + if(samples.empty()) + return; + + QColor trace_colour = _colour; + trace_colour.setAlpha(View::ForeAlpha); + p.setPen(trace_colour); + + const int full_size = (_spectrum_stack->get_sample_num()/2); + const double view_off = full_size * _offset; + const int view_start = floor(view_off); + const int view_size = full_size*_scale; + QPointF *points = new QPointF[samples.size()]; + QPointF *point = points; + + const bool dc_ignored = _spectrum_stack->dc_ignored(); + const double height = get_view_rect().height(); + const double width = right - left; + const double pixels_per_sample = width/view_size; + + double vdiv = 0; + double vfactor = 0; + BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { + boost::shared_ptr dsoSig; + if ((dsoSig = dynamic_pointer_cast(s))) { + if(dsoSig->get_index() == _spectrum_stack->get_index()) { + vdiv = dsoSig->get_vDialValue(); + vfactor = dsoSig->get_factor(); + break; + } + } + } + if (_view_mode == 0) { + _vmin = 0; + _vmax = (vdiv*DS_CONF_DSO_HDIVS*vfactor)*VerticalRate; + } else { + _vmax = 20*log10((vdiv*DS_CONF_DSO_HDIVS*vfactor)*VerticalRate); + _vmin = _vmax - _dbv_range; + } + + //const double max_value = *std::max_element(dc_ignored ? ++samples.begin() : samples.begin(), samples.end()); + //const double min_value = *std::min_element(dc_ignored ? ++samples.begin() : samples.begin(), samples.end()); + //_vmax = (_view_mode == 0) ? max_value : 20*log10(max_value); + //_vmin = (_view_mode == 0) ? min_value : 20*log10(min_value); + const double scale = height / (_vmax - _vmin); + + double x = (view_start-view_off)*pixels_per_sample; + uint64_t sample = view_start; + if (dc_ignored && sample == 0) { + sample++; + x += pixels_per_sample; + } + double min_mag = pow(10.0, _vmin/20); + do{ + double mag = samples[sample]; + if (_view_mode != 0) { + if (mag < min_mag) + mag = _vmin; + else + mag = 20*log10(mag); + } + const double y = height - (scale * (mag - _vmin)); + *point++ = QPointF(x, y); + x += pixels_per_sample; + sample++; + }while(x= left); + + (void)left; + (void)right; + const int text_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, + AlignLeft | AlignTop, "8").height(); + const double width = get_view_rect().width(); + const double height = get_view_rect().height(); + double blank_top = 0; + double blank_right = width; + + // horizontal ruler + const double NyFreq = _session.cur_samplerate() / (2.0 * _spectrum_stack->get_sample_interval()); + const double deltaFreq = _session.cur_samplerate() * 1.0 / + (_spectrum_stack->get_sample_num() * _spectrum_stack->get_sample_interval()); + const double FreqRange = NyFreq * _scale; + const double FreqOffset = NyFreq * _offset; + + const int order = (int)floor(log10(FreqRange)); + const double multiplier = (pow(10.0, order) == FreqRange) ? FreqRange/10 : pow(10.0, order); + const double freq_per_pixel = FreqRange / width; + + p.setPen(fore); + p.setBrush(Qt::NoBrush); + double tick_freq = multiplier * (int)floor(FreqOffset / multiplier); + int division = (int)round(tick_freq * FreqMinorDivNum / multiplier); + double x = (tick_freq - FreqOffset) / freq_per_pixel; + do{ + if (division%FreqMinorDivNum == 0) { + QString freq_str = format_freq(tick_freq); + double typical_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + AlignLeft | AlignTop, freq_str).width() + 10; + p.drawLine(x, 1, x, TickHeight); + if (x > typical_width/2 && (width-x) > typical_width/2) + p.drawText(x-typical_width/2, TickHeight, typical_width, text_height, + AlignCenter | AlignTop | TextDontClip, freq_str); + } else { + p.drawLine(x, 1, x, TickHeight/2); + } + tick_freq += multiplier/FreqMinorDivNum; + division++; + x = (tick_freq - FreqOffset) / freq_per_pixel; + } while(x < width); + blank_top = max(blank_top, (double)TickHeight + text_height); + + // delta Frequency + QString freq_str = QString::fromWCharArray(L" \u0394") + "Freq: " + format_freq(deltaFreq,4); + p.drawText(0, 0, width, get_view_rect().height(), + AlignRight | AlignBottom | TextDontClip, freq_str); + double delta_left = width-p.boundingRect(0, 0, INT_MAX, INT_MAX, + AlignLeft | AlignTop, freq_str).width(); + blank_right = min(delta_left, blank_right); + + // Vertical ruler + const double vRange = _vmax - _vmin; + const double vOffset = _vmin; + const double vol_per_tick = vRange / VolDivNum; + + p.setPen(fore); + p.setBrush(Qt::NoBrush); + double tick_vol = vol_per_tick + vOffset; + double y = height - height / VolDivNum; + const QString unit = (_view_mode == 0) ? "" : "dbv"; + do{ + if (y > text_height && y < (height - text_height)) { + QString vol_str = QString::number(tick_vol, 'f', Pricision) + unit; + double vol_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + AlignLeft | AlignTop, vol_str).width(); + p.drawLine(width, y, width-TickHeight/2, y); + p.drawText(width-TickHeight-vol_width, y-text_height/2, vol_width, text_height, + AlignCenter | AlignTop | TextDontClip, vol_str); + blank_right = min(width-TickHeight-vol_width, blank_right); + } + tick_vol += vol_per_tick; + y -= height / VolDivNum; + } while(y > 0); + + // Hover measure + if (_hover_en) { + const std::vector samples(_spectrum_stack->get_fft_spectrum()); + if(samples.empty()) + return; + const int full_size = (_spectrum_stack->get_sample_num()/2); + const double view_off = full_size * _offset; + const int view_size = full_size*_scale; + const double scale = height / (_vmax - _vmin); + const double pixels_per_sample = width/view_size; + double x = (_hover_index-view_off)*pixels_per_sample; + double min_mag = pow(10.0, _vmin/20); + _hover_value = samples[_hover_index]; + if (_view_mode != 0) { + if (_hover_value < min_mag) + _hover_value = _vmin; + else + _hover_value = 20*log10(_hover_value); + } + const double y = height - (scale * (_hover_value - _vmin)); + _hover_point = QPointF(x, y); + + p.setPen(QPen(fore, 1, Qt::DashLine)); + p.setBrush(Qt::NoBrush); + p.drawLine(_hover_point.x(), 0, _hover_point.x(), height); + + QString hover_str = QString::number(_hover_value, 'f', 4) + unit + "@" + format_freq(deltaFreq * _hover_index, 4); + const int hover_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + AlignLeft | AlignTop, hover_str).width(); + const int hover_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, + AlignLeft | AlignTop, hover_str).height(); + QRectF hover_rect(_hover_point.x(), _hover_point.y()-hover_height, hover_width, hover_height); + if (hover_rect.right() > blank_right) + hover_rect.moveRight(min(_hover_point.x(), blank_right)); + if (hover_rect.top() < blank_top) + hover_rect.moveTop(max(_hover_point.y(), blank_top)); + if (hover_rect.top() > 0) + p.drawText(hover_rect, AlignCenter | AlignTop | TextDontClip, hover_str); + + p.setPen(Qt::NoPen); + p.setBrush(fore); + p.drawEllipse(_hover_point, HoverPointSize, HoverPointSize); + } +} + +void SpectrumTrace::paint_type_options(QPainter &p, int right, const QPoint pt, QColor fore) +{ + (void)p; + (void)pt; + (void)right; + (void)fore; +} + +QRect SpectrumTrace::get_view_rect() const +{ + assert(_viewport); + return QRect(0, UpMargin, + _viewport->width() - RightMargin, + _viewport->height() - UpMargin - DownMargin); +} + +} // namespace view +} // namespace pv diff --git a/DSView/pv/view/spectrumtrace.h b/DSView/pv/view/spectrumtrace.h new file mode 100755 index 00000000..e8a08796 --- /dev/null +++ b/DSView/pv/view/spectrumtrace.h @@ -0,0 +1,156 @@ +/* + * This file is part of the PulseView project. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DSVIEW_PV_VIEW_SPECTRUMTRACE_H +#define DSVIEW_PV_VIEW_SPECTRUMTRACE_H + +#include "trace.h" + +#include +#include + +#include + +struct srd_channel; + +namespace pv { + +class SigSession; + +namespace data{ +class SpectrumStack; +} + +namespace view { + +class SpectrumTrace : public Trace +{ + Q_OBJECT + +private: + static const int UpMargin; + static const int DownMargin; + static const int RightMargin; + static const QString FFT_ViewMode[2]; + + static const QString FreqPrefixes[9]; + static const int FirstSIPrefixPower; + static const int LastSIPrefixPower; + static const int Pricision; + static const int FreqMinorDivNum; + static const int TickHeight; + static const int VolDivNum; + + static const int DbvRanges[4]; + + static const int HoverPointSize; + + static const double VerticalRate; + +public: + SpectrumTrace(pv::SigSession &session, + boost::shared_ptr spectrum_stack, int index); + ~SpectrumTrace(); + + bool enabled() const; + void set_enable(bool enable); + + void init_zoom(); + void zoom(double steps, int offset); + bool zoom_hit() const; + void set_zoom_hit(bool hit); + + void set_offset(double delta); + double get_offset() const; + + void set_scale(double scale); + double get_scale() const; + + void set_dbv_range(int range); + int dbv_range() const; + std::vector get_dbv_ranges(); + + int view_mode() const; + void set_view_mode(unsigned int mode); + std::vector get_view_modes_support(); + + const boost::shared_ptr& get_spectrum_stack() const; + + static QString format_freq(double freq, unsigned precision = Pricision); + + bool measure(const QPoint &p); + + /** + * Paints the background layer of the trace with a QPainter + * @param p the QPainter to paint into. + * @param left the x-coordinate of the left edge of the signal. + * @param right the x-coordinate of the right edge of the signal. + **/ + void paint_back(QPainter &p, int left, int right, QColor fore, QColor back); + + /** + * Paints the mid-layer of the trace with a QPainter + * @param p the QPainter to paint into. + * @param left the x-coordinate of the left edge of the signal + * @param right the x-coordinate of the right edge of the signal + **/ + void paint_mid(QPainter &p, int left, int right, QColor fore, QColor back); + + /** + * Paints the foreground layer of the trace with a QPainter + * @param p the QPainter to paint into. + * @param left the x-coordinate of the left edge of the signal + * @param right the x-coordinate of the right edge of the signal + **/ + void paint_fore(QPainter &p, int left, int right, QColor fore, QColor back); + + QRect get_view_rect() const; + +protected: + void paint_type_options(QPainter &p, int right, const QPoint pt, QColor fore); + +private: + +private slots: + +private: + pv::SigSession &_session; + boost::shared_ptr _spectrum_stack; + + bool _enable; + int _view_mode; + + double _vmax; + double _vmin; + int _dbv_range; + + uint64_t _hover_index; + bool _hover_en; + QPointF _hover_point; + double _hover_value; + + double _scale; + double _offset; +}; + +} // namespace view +} // namespace pv + +#endif // DSVIEW_PV_VIEW_SPECTRUMTRACE_H diff --git a/DSView/pv/view/timemarker.cpp b/DSView/pv/view/timemarker.cpp old mode 100644 new mode 100755 index c907991f..31eb7442 --- a/DSView/pv/view/timemarker.cpp +++ b/DSView/pv/view/timemarker.cpp @@ -84,9 +84,12 @@ void TimeMarker::paint(QPainter &p, const QRect &rect, const bool highlight, int const double scale = _view.scale(); const double samples_per_pixel = sample_rate * scale; const int64_t x = _index/samples_per_pixel - _view.offset(); - QColor color = (order == -1) ? _colour : Ruler::CursorColor[order%8]; - p.setPen((_grabbed | highlight) ? QPen(color.lighter(), 2, Qt::DashLine) : QPen(color, 1, Qt::DashLine)); - p.drawLine(QPoint(x, rect.top()), QPoint(x, rect.bottom())); + if (x <= rect.right()) { + QColor color = (order == -1) ? _colour : Ruler::CursorColor[order%8]; + p.setPen((_grabbed | highlight) ? QPen(color.lighter(), 2, Qt::DashLine) : QPen(color, 1, Qt::DashLine)); + //p.drawLine(QPoint(x, rect.top()), QPoint(x, rect.bottom())); + p.drawLine(QPoint(x, 0), QPoint(x, rect.bottom())); + } } } // namespace view diff --git a/DSView/pv/view/timemarker.h b/DSView/pv/view/timemarker.h old mode 100644 new mode 100755 diff --git a/DSView/pv/view/trace.cpp b/DSView/pv/view/trace.cpp old mode 100644 new mode 100755 index 7ca99fda..f48c0d53 --- a/DSView/pv/view/trace.cpp +++ b/DSView/pv/view/trace.cpp @@ -36,24 +36,6 @@ namespace pv { namespace view { -const QColor Trace::dsBlue = QColor(17, 133, 209, 255); -const QColor Trace::dsYellow = QColor(238, 178, 17, 255); -const QColor Trace::dsRed = QColor(213, 15, 37, 255); -const QColor Trace::dsGreen = QColor(0, 153, 37, 200); -const QColor Trace::dsGray = QColor(0x88, 0x8A, 0x85, 60); -const QColor Trace::dsFore = QColor(0xff, 0xff, 0xff, 60); -const QColor Trace::dsBack = QColor(0x16, 0x18, 0x23, 200); -const QColor Trace::dsDisable = QColor(0x88, 0x8A, 0x85, 200); -const QColor Trace::dsActive = QColor(17, 133, 209, 255); -const QColor Trace::dsLightBlue = QColor(17, 133, 209, 150); -const QColor Trace::dsLightRed = QColor(213, 15, 37, 150); -const QPen Trace::SignalAxisPen = QColor(128, 128, 128, 64); - -const QColor Trace::DARK_BACK = QColor(48, 47, 47, 255); -const QColor Trace::DARK_FORE = QColor(150, 150, 150, 255); -const QColor Trace::DARK_HIGHLIGHT = QColor(32, 32, 32, 255); -const QColor Trace::DARK_BLUE = QColor(17, 133, 209, 255); - const QColor Trace::PROBE_COLORS[8] = { QColor(0x50, 0x50, 0x50), // Black QColor(0x8F, 0x52, 0x02), // Brown @@ -63,17 +45,7 @@ const QColor Trace::PROBE_COLORS[8] = { QColor(0x73, 0xD2, 0x16), // Green QColor(0x34, 0x65, 0xA4), // Blue QColor(0x75, 0x50, 0x7B), // Violet -// QColor(17, 133, 209), -// QColor(17, 133, 209), -// QColor(17, 133, 209), -// QColor(17, 133, 209), -// QColor(17, 133, 209), -// QColor(17, 133, 209), -// QColor(17, 133, 209), -// QColor(17, 133, 209), }; - -const QPen Trace::AxisPen(QColor(128, 128, 128, 64)); const int Trace::LabelHitPadding = 2; Trace::Trace(QString name, uint16_t index, int type) : @@ -239,30 +211,37 @@ pv::view::Viewport* Trace::get_viewport() const return _viewport; } -void Trace::paint_back(QPainter &p, int left, int right) +void Trace::paint_back(QPainter &p, int left, int right, QColor fore, QColor back) { - QPen pen(Signal::dsGray); + (void)back; + + fore.setAlpha(View::BackAlpha); + QPen pen(fore); pen.setStyle(Qt::DotLine); p.setPen(pen); const double sigY = get_y(); p.drawLine(left, sigY, right, sigY); } -void Trace::paint_mid(QPainter &p, int left, int right) +void Trace::paint_mid(QPainter &p, int left, int right, QColor fore, QColor back) { (void)p; (void)left; (void)right; + (void)fore; + (void)back; } -void Trace::paint_fore(QPainter &p, int left, int right) +void Trace::paint_fore(QPainter &p, int left, int right, QColor fore, QColor back) { (void)p; (void)left; (void)right; + (void)fore; + (void)back; } -void Trace::paint_label(QPainter &p, int right, const QPoint pt) +void Trace::paint_label(QPainter &p, int right, const QPoint pt, QColor fore) { if (_type == SR_CHANNEL_FFT && !enabled()) return; @@ -274,24 +253,26 @@ void Trace::paint_label(QPainter &p, int right, const QPoint pt) const QRectF name_rect = get_rect("name", y, right); const QRectF label_rect = get_rect("label", get_zero_vpos(), right); - //p.setRenderHint(QPainter::Antialiasing); // Paint the ColorButton + QColor foreBack = fore; + foreBack.setAlpha(View::BackAlpha); p.setPen(Qt::transparent); - p.setBrush(enabled() ? _colour : dsDisable); + p.setBrush(enabled() ? (_colour.isValid() ? _colour : fore) : foreBack); p.drawRect(color_rect); - if (_type == SR_CHANNEL_DSO) { - p.setPen(enabled() ? Qt::white: dsDisable); + if (_type == SR_CHANNEL_DSO || + _type == SR_CHANNEL_MATH) { + p.setPen(enabled() ? Qt::white: foreBack); p.drawText(color_rect, Qt::AlignCenter | Qt::AlignVCenter, _name); } if (_type != SR_CHANNEL_DSO) { // Paint the signal name - p.setPen(enabled() ? DARK_FORE: dsDisable); + p.setPen(enabled() ? fore: foreBack); p.drawText(name_rect, Qt::AlignLeft | Qt::AlignVCenter, _name); } // Paint the trigButton - paint_type_options(p, right, pt); + paint_type_options(p, right, pt, fore); // Paint the label if (enabled()) { @@ -306,7 +287,8 @@ void Trace::paint_label(QPainter &p, int right, const QPoint pt) p.setPen(Qt::transparent); if (_type == SR_CHANNEL_DSO || _type == SR_CHANNEL_FFT || - _type == SR_CHANNEL_ANALOG) { + _type == SR_CHANNEL_ANALOG || + _type == SR_CHANNEL_MATH) { p.setBrush(_colour); p.drawPolygon(points, countof(points)); } else { @@ -349,17 +331,20 @@ void Trace::paint_label(QPainter &p, int right, const QPoint pt) else if (_type == SR_CHANNEL_DECODER) p.drawText(label_rect, Qt::AlignCenter | Qt::AlignVCenter, "D"); else if (_type == SR_CHANNEL_FFT) + p.drawText(label_rect, Qt::AlignCenter | Qt::AlignVCenter, "F"); + else if (_type == SR_CHANNEL_MATH) p.drawText(label_rect, Qt::AlignCenter | Qt::AlignVCenter, "M"); else p.drawText(label_rect, Qt::AlignCenter | Qt::AlignVCenter, QString::number(_index_list.front())); } } -void Trace::paint_type_options(QPainter &p, int right, const QPoint pt) +void Trace::paint_type_options(QPainter &p, int right, const QPoint pt, QColor fore) { (void)p; (void)right; (void)pt; + (void)fore; } bool Trace::mouse_double_click(int right, const QPoint pt) @@ -400,12 +385,6 @@ int Trace::pt_in_rect(int y, int right, const QPoint &point) return 0; } -void Trace::paint_axis(QPainter &p, int y, int left, int right) -{ - p.setPen(SignalAxisPen); - p.drawLine(QPointF(left, y + 0.5f), QPointF(right, y + 0.5f)); -} - void Trace::compute_text_size(QPainter &p) { _text_size = QSize( diff --git a/DSView/pv/view/trace.h b/DSView/pv/view/trace.h old mode 100644 new mode 100755 index b0a92496..30880f03 --- a/DSView/pv/view/trace.h +++ b/DSView/pv/view/trace.h @@ -49,7 +49,6 @@ class Trace : public SelectableItem protected: static const int Margin = 3; static const int SquareNum = 5; - static const QPen AxisPen; static const int LabelHitPadding; public: @@ -58,24 +57,6 @@ public: static const int NAME = 2; static const int LABEL = 8; - static const QColor dsBlue; - static const QColor dsYellow; - static const QColor dsRed; - static const QColor dsGreen; - static const QColor dsGray; - static const QColor dsFore; - static const QColor dsBack; - static const QColor dsDisable; - static const QColor dsActive; - static const QColor dsLightBlue; - static const QColor dsLightRed; - static const QPen SignalAxisPen; - - static const QColor DARK_BACK; - static const QColor DARK_FORE; - static const QColor DARK_HIGHLIGHT; - static const QColor DARK_BLUE; - static const QColor PROBE_COLORS[8]; protected: @@ -178,7 +159,7 @@ public: * @param left the x-coordinate of the left edge of the signal * @param right the x-coordinate of the right edge of the signal **/ - virtual void paint_back(QPainter &p, int left, int right); + virtual void paint_back(QPainter &p, int left, int right, QColor fore, QColor back); /** * Paints the mid-layer of the trace with a QPainter @@ -186,7 +167,7 @@ public: * @param left the x-coordinate of the left edge of the signal * @param right the x-coordinate of the right edge of the signal **/ - virtual void paint_mid(QPainter &p, int left, int right); + virtual void paint_mid(QPainter &p, int left, int right, QColor fore, QColor back); /** * Paints the foreground layer of the trace with a QPainter @@ -194,7 +175,7 @@ public: * @param left the x-coordinate of the left edge of the signal * @param right the x-coordinate of the right edge of the signal **/ - virtual void paint_fore(QPainter &p, int left, int right); + virtual void paint_fore(QPainter &p, int left, int right, QColor fore, QColor back); /** * Paints the trace label. @@ -203,7 +184,7 @@ public: * area. * @param point the mouse point. */ - virtual void paint_label(QPainter &p, int right, const QPoint pt); + virtual void paint_label(QPainter &p, int right, const QPoint pt, QColor fore); /** * Gets the y-offset of the axis. @@ -258,15 +239,6 @@ protected: */ QColor get_text_colour() const; - /** - * Paints a zero axis across the viewport. - * @param p the QPainter to paint into. - * @param y the y-offset of the axis. - * @param left the x-coordinate of the left edge of the view. - * @param right the x-coordinate of the right edge of the view. - */ - void paint_axis(QPainter &p, int y, int left, int right); - /** * Paints optoins for different trace type. * @param p the QPainter to paint into. @@ -274,7 +246,7 @@ protected: * area. * @param point the mouse point. */ - virtual void paint_type_options(QPainter &p, int right, const QPoint pt); + virtual void paint_type_options(QPainter &p, int right, const QPoint pt, QColor fore); private: diff --git a/DSView/pv/view/view.cpp b/DSView/pv/view/view.cpp old mode 100644 new mode 100755 index e57474a0..8c2823c2 --- a/DSView/pv/view/view.cpp +++ b/DSView/pv/view/view.cpp @@ -40,13 +40,16 @@ #include "dsosignal.h" #include "view.h" #include "viewport.h" -#include "mathtrace.h" +#include "spectrumtrace.h" +#include "lissajoustrace.h" +#include "analogsignal.h" #include "../device/devinst.h" #include "pv/sigsession.h" #include "pv/data/logic.h" #include "pv/data/logicsnapshot.h" #include "pv/dialogs/calibration.h" +#include "pv/dialogs/lissajousoptions.h" using namespace boost; using namespace std; @@ -61,13 +64,21 @@ const int View::MaxScrollValue = INT_MAX / 2; const int View::MaxHeightUnit = 20; //const int View::SignalHeight = 30;s -const int View::SignalMargin = 10; +const int View::SignalMargin = 7; const int View::SignalSnapGridSize = 10; const QColor View::CursorAreaColour(220, 231, 243); const QSizeF View::LabelPadding(4, 4); +const QColor View::Red = QColor(213, 15, 37, 255); +const QColor View::Orange = QColor(238, 178, 17, 255); +const QColor View::Blue = QColor(17, 133, 209, 255); +const QColor View::Green = QColor(0, 153, 37, 255); +const QColor View::Purple = QColor(109, 50, 156, 255); +const QColor View::LightBlue = QColor(17, 133, 209, 200); + + View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget *parent) : QScrollArea(parent), _session(session), @@ -81,8 +92,11 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget _updating_scroll(false), _show_cursors(false), _search_hit(false), + _show_xcursors(false), _hover_point(-1, -1), - _dso_auto(true) + _dso_auto(true), + _show_lissajous(false), + _back_ready(false) { setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); @@ -98,6 +112,8 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget _trace_view_map[SR_CHANNEL_ANALOG] = TIME_VIEW; _trace_view_map[SR_CHANNEL_DSO] = TIME_VIEW; _trace_view_map[SR_CHANNEL_FFT] = FFT_VIEW; + _trace_view_map[SR_CHANNEL_LISSAJOUS] = TIME_VIEW; + _trace_view_map[SR_CHANNEL_MATH] = TIME_VIEW; _active_viewport = NULL; _ruler = new Ruler(*this); @@ -141,7 +157,7 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget layout->setContentsMargins(0,0,0,0); _viewcenter->setLayout(layout); layout->addWidget(_vsplitter, 0, 0); - _viewbottom = new widgets::ViewStatus(_session, this); + _viewbottom = new ViewStatus(_session, *this); _viewbottom->setFixedHeight(StatusHeight); layout->addWidget(_viewbottom, 1, 0); setViewport(_viewcenter); @@ -167,8 +183,8 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget connect(&_session, SIGNAL(repeat_hold(int)), this, SLOT(repeat_show())); - connect(_devmode, SIGNAL(mode_changed()), - this, SLOT(mode_changed()), Qt::DirectConnection); + connect(_devmode, SIGNAL(dev_changed(bool)), + this, SLOT(dev_changed(bool)), Qt::DirectConnection); connect(_header, SIGNAL(traces_moved()), this, SLOT(on_traces_moved())); @@ -185,11 +201,14 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget _ruler->setObjectName(tr("ViewArea_ruler")); _header->setObjectName(tr("ViewArea_header")); + QColor fore(QWidget::palette().color(QWidget::foregroundRole())); + fore.setAlpha(View::BackAlpha); + _show_trig_cursor = false; - _trig_cursor = new Cursor(*this, Trace::dsLightRed, 0); + _trig_cursor = new Cursor(*this, View::Red, 0); _show_search_cursor = false; _search_pos = 0; - _search_cursor = new Cursor(*this, Trace::dsGray, _search_pos); + _search_cursor = new Cursor(*this, fore, _search_pos); _cali = new pv::dialogs::Calibration(this); _cali->hide(); @@ -231,6 +250,7 @@ void View::capture_init() if (_session.get_device()->dev_inst()->mode == ANALOG) set_scale_offset(_maxscale, 0); status_clear(); + _trig_time_setted = false; } void View::zoom(double steps) @@ -245,8 +265,8 @@ void View::set_update(Viewport *viewport, bool need_update) void View::set_all_update(bool need_update) { - BOOST_FOREACH(Viewport *viewport, _viewport_list) - viewport->set_need_update(need_update); + _time_viewport->set_need_update(need_update); + _fft_viewport->set_need_update(need_update); } double View::get_hori_res() @@ -346,7 +366,7 @@ vector< boost::shared_ptr > View::get_traces(int type) const vector< boost::shared_ptr > decode_sigs( _session.get_decode_signals()); #endif - const vector< boost::shared_ptr > maths(_session.get_math_signals()); + const vector< boost::shared_ptr > spectrums(_session.get_spectrum_traces()); vector< boost::shared_ptr > traces; BOOST_FOREACH(boost::shared_ptr t, sigs) { @@ -364,11 +384,21 @@ vector< boost::shared_ptr > View::get_traces(int type) traces.push_back(t); } - BOOST_FOREACH(boost::shared_ptr t, maths) { + BOOST_FOREACH(boost::shared_ptr t, spectrums) { if (type == ALL_VIEW || _trace_view_map[t->get_type()] == type) traces.push_back(t); } + boost::shared_ptr lissajous = _session.get_lissajous_trace(); + if (lissajous && lissajous->enabled() && + (type == ALL_VIEW || _trace_view_map[lissajous->get_type()] == type)) + traces.push_back(lissajous); + + boost::shared_ptr math = _session.get_math_trace(); + if (math && math->enabled() && + (type == ALL_VIEW || _trace_view_map[math->get_type()] == type)) + traces.push_back(math); + stable_sort(traces.begin(), traces.end(), compare_trace_v_offsets); return traces; } @@ -379,7 +409,9 @@ bool View::compare_trace_v_offsets(const boost::shared_ptr &a, assert(a); assert(b); if (a->get_type() != b->get_type()) - return a->get_type() > b->get_type(); + return a->get_type() < b->get_type(); + else if (a->get_type() == SR_CHANNEL_DSO || a->get_type() == SR_CHANNEL_ANALOG) + return a->get_index() < b->get_index(); else return a->get_v_offset() < b->get_v_offset(); } @@ -434,13 +466,27 @@ void View::repeat_unshow() void View::frame_began() { - if (_session.get_device()->dev_inst()->mode == LOGIC) - _viewbottom->set_trig_time(_session.get_trigger_time()); +// if (_session.get_device()->dev_inst()->mode == LOGIC) +// _viewbottom->set_trig_time(_session.get_trigger_time()); _search_hit = false; _search_pos = 0; set_search_pos(_search_pos, _search_hit); } +void View::set_trig_time() +{ + if (!_trig_time_setted && _session.get_device()->dev_inst()->mode == LOGIC) { + _session.set_trigger_time(QDateTime::currentDateTime()); + _viewbottom->set_trig_time(_session.get_trigger_time()); + } + _trig_time_setted = true; +} + +bool View::trig_time_setted() +{ + return _trig_time_setted; +} + void View::receive_end() { if (_session.get_device()->dev_inst()->mode == LOGIC) { @@ -487,12 +533,14 @@ void View::set_trig_pos(int percent) void View::set_search_pos(uint64_t search_pos, bool hit) { //assert(search_pos >= 0); + QColor fore(QWidget::palette().color(QWidget::foregroundRole())); + fore.setAlpha(View::BackAlpha); const double time = search_pos * 1.0 / _session.cur_samplerate(); _search_pos = search_pos; _search_hit = hit; _search_cursor->set_index(search_pos); - _search_cursor->set_colour(hit ? Trace::dsLightBlue : Trace::dsGray); + _search_cursor->set_colour(hit ? View::Blue : fore); if (hit) { set_scale_offset(_scale, (time / _scale) - (get_view_width() / 2)); @@ -557,7 +605,7 @@ void View::update_scroll() { assert(_viewcenter); - const QSize areaSize = _viewcenter->size(); + const QSize areaSize = QSize(get_view_width(), get_view_height()); // Set the horizontal scroll bar int64_t length = 0; @@ -611,21 +659,24 @@ void View::update_scale_offset() viewport_update(); } -void View::mode_changed() +void View::dev_changed(bool close) { - const uint64_t sample_rate = _session.cur_samplerate(); - assert(sample_rate > 0); + if (!close) { + const uint64_t sample_rate = _session.cur_samplerate(); + assert(sample_rate > 0); - if (_session.get_device()->name().contains("virtual")) - _scale = WellSamplesPerPixel * 1.0 / sample_rate; - _scale = max(min(_scale, _maxscale), _minscale); + if (_session.get_device()->name().contains("virtual")) + _scale = WellSamplesPerPixel * 1.0 / sample_rate; + _scale = max(min(_scale, _maxscale), _minscale); + } - update_device_list(); + device_changed(close); } void View::signals_changed() { int total_rows = 0; + int label_size = 0; uint8_t max_height = MaxHeightUnit; vector< boost::shared_ptr > time_traces; vector< boost::shared_ptr > fft_traces; @@ -656,7 +707,7 @@ void View::signals_changed() _vsplitter->refresh(); // Find the _fft_viewport in the stack - std::list< Viewport *>::iterator iter = _viewport_list.begin(); + std::list< QWidget *>::iterator iter = _viewport_list.begin(); for(unsigned int i = 0; i < _viewport_list.size(); i++, iter++) if ((*iter) == _fft_viewport) break; @@ -671,10 +722,12 @@ void View::signals_changed() if (dynamic_pointer_cast(t) || t->enabled()) total_rows += t->rows_size(); + if (t->rows_size() != 0) + label_size++; } const double height = (_time_viewport->height() - - 2 * SignalMargin * time_traces.size()) * 1.0 / total_rows; + - 2 * SignalMargin * label_size) * 1.0 / total_rows; if (_session.get_device()->dev_inst()->mode == LOGIC) { GVariant* gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_MAX_HEIGHT_VALUE); @@ -686,7 +739,7 @@ void View::signals_changed() } else if (_session.get_device()->dev_inst()->mode == DSO) { _signalHeight = (_header->height() - horizontalScrollBar()->height() - - 2 * SignalMargin * time_traces.size()) * 1.0 / total_rows; + - 2 * SignalMargin * label_size) * 1.0 / total_rows; } else { _signalHeight = (int)((height <= 0) ? 1 : height); } @@ -695,6 +748,8 @@ void View::signals_changed() BOOST_FOREACH(boost::shared_ptr t, time_traces) { t->set_view(this); t->set_viewport(_time_viewport); + if (t->rows_size() == 0) + continue; const double traceHeight = _signalHeight*t->rows_size(); t->set_totalHeight((int)traceHeight); t->set_v_offset(next_v_offset + 0.5 * traceHeight + SignalMargin); @@ -704,6 +759,11 @@ void View::signals_changed() if ((dsoSig = dynamic_pointer_cast(t))) { dsoSig->set_scale(dsoSig->get_view_rect().height()); } + + boost::shared_ptr analogSig; + if ((analogSig = dynamic_pointer_cast(t))) { + analogSig->set_scale(analogSig->get_totalHeight()); + } } _time_viewport->clear_measure(); } @@ -777,6 +837,7 @@ int View::headerWidth() void View::resizeEvent(QResizeEvent*) { + reconstruct(); setViewportMargins(headerWidth(), RulerHeight, 0, 0); update_margins(); update_scroll(); @@ -986,15 +1047,15 @@ uint64_t View::get_cursor_samples(int index) void View::set_measure_en(int enable) { - BOOST_FOREACH(Viewport *viewport, _viewport_list) - viewport->set_measure_en(enable); + _time_viewport->set_measure_en(enable); + _fft_viewport->set_measure_en(enable); } void View::on_state_changed(bool stop) { if (stop) { - BOOST_FOREACH(Viewport *viewport, _viewport_list) - viewport->stop_trigger_timer(); + _time_viewport->stop_trigger_timer(); + _fft_viewport->stop_trigger_timer(); } update_scale_offset(); } @@ -1067,11 +1128,22 @@ void View::hide_calibration() _cali->hide(); } -void View::update_calibration() +void View::vDial_updated() { if (_cali->isVisible()) { _cali->set_device(_session.get_device()); } + boost::shared_ptr math_trace = _session.get_math_trace(); + if (math_trace && math_trace->enabled()) { + math_trace->update_vDial(); + } +} + +// -- lissajous figure +void View::show_lissajous(bool show) +{ + _show_lissajous = show; + signals_changed(); } void View::show_region(uint64_t start, uint64_t end, bool keep) @@ -1095,7 +1167,7 @@ void View::show_region(uint64_t start, uint64_t end, bool keep) void View::viewport_update() { _viewcenter->update(); - BOOST_FOREACH(Viewport *viewport, _viewport_list) + BOOST_FOREACH(QWidget *viewport, _viewport_list) viewport->update(); } @@ -1108,16 +1180,34 @@ void View::splitterMoved(int pos, int index) void View::reload() { - show_trig_cursor(false); + clear(); /* * if headerwidth not change, viewport height will not be updated * lead to a wrong signal height */ - if (_session.get_device()->dev_inst()->mode == LOGIC) - _viewbottom->setFixedHeight(StatusHeight); + reconstruct(); +} + +void View::clear() +{ + show_trig_cursor(false); + + if (_session.get_device()->dev_inst()->mode != DSO) { + show_xcursors(false); + } else { + if (!get_xcursorList().empty()) + show_xcursors(true); + } +} + +void View::reconstruct() +{ + if (_session.get_device()->dev_inst()->mode == DSO) + _viewbottom->setFixedHeight(DsoStatusHeight); else - _viewbottom->setFixedHeight(10); + _viewbottom->setFixedHeight(StatusHeight); + _viewbottom->reload(); } void View::repeat_show() @@ -1140,5 +1230,54 @@ bool View::get_dso_trig_moved() const return _time_viewport->get_dso_trig_moved(); } +/* + * horizental cursors + */ +bool View::xcursors_shown() +{ + return _show_xcursors; +} + +void View::show_xcursors(bool show) +{ + _show_xcursors = show; +} + +std::list& View::get_xcursorList() +{ + return _xcursorList; +} + +void View::add_xcursor(QColor color, double value0, double value1) +{ + XCursor *newXCursor = new XCursor(*this, color, value0, value1); + _xcursorList.push_back(newXCursor); + xcursor_update(); +} + +void View::del_xcursor(XCursor* xcursor) +{ + assert(xcursor); + + _xcursorList.remove(xcursor); + delete xcursor; + xcursor_update(); +} + +ViewStatus* View::get_viewstatus() +{ + return _viewbottom; +} + +bool View::back_ready() const +{ + return _back_ready; +} + +void View::set_back(bool ready) +{ + _back_ready = ready; +} + } // namespace view } // namespace pv diff --git a/DSView/pv/view/view.h b/DSView/pv/view/view.h old mode 100644 new mode 100755 index 466c5aa9..ae132557 --- a/DSView/pv/view/view.h +++ b/DSView/pv/view/view.h @@ -41,8 +41,9 @@ #include "../data/signaldata.h" #include "../view/viewport.h" #include "cursor.h" +#include "xcursor.h" #include "signal.h" -#include "../widgets/viewstatus.h" +#include "viewstatus.h" namespace pv { @@ -50,6 +51,11 @@ namespace toolbars { class SamplingBar; } +namespace dialogs { + class Calibration; + class Lissajous; +} + class SigSession; namespace view { @@ -59,6 +65,7 @@ class DevMode; class Ruler; class Trace; class Viewport; +class LissajousFigure; class View : public QScrollArea { Q_OBJECT @@ -84,6 +91,16 @@ public: static const int MaxPixelsPerSample = 100; static const int StatusHeight = 20; + static const int DsoStatusHeight = 55; + + static const int ForeAlpha = 200; + static const int BackAlpha = 100; + static const QColor Red; + static const QColor Orange; + static const QColor Blue; + static const QColor Green; + static const QColor Purple; + static const QColor LightBlue; public: explicit View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget *parent = 0); @@ -163,6 +180,15 @@ public: uint64_t get_search_pos(); + /* + * horizental cursors + */ + bool xcursors_shown(); + void show_xcursors(bool show); + std::list& get_xcursorList(); + void add_xcursor(QColor color, double value0, double value1); + void del_xcursor(XCursor* xcursor); + /* * */ @@ -193,10 +219,19 @@ public: bool get_dso_trig_moved() const; + ViewStatus* get_viewstatus(); + + /* + * back paint status + */ + bool back_ready() const; + void set_back(bool ready); + signals: void hover_point_changed(); void cursor_update(); + void xcursor_update(); void cursor_moving(); void cursor_moved(); @@ -205,7 +240,7 @@ signals: void prgRate(int progress); - void update_device_list(); + void device_changed(bool close); void resize(); @@ -222,6 +257,9 @@ private: const boost::shared_ptr &a, const boost::shared_ptr &b); + void clear(); + void reconstruct(); + private: bool eventFilter(QObject *object, QEvent *event); @@ -237,17 +275,23 @@ public slots: void update_scale_offset(); void show_region(uint64_t start, uint64_t end, bool keep); // -- calibration - void update_calibration(); void hide_calibration(); void status_clear(); void repeat_unshow(); + // -- repeat void repeat_show(); // -- void timebase_changed(); // -- + void vDial_updated(); + // -- void update_hori_res(); + // -- + void set_trig_time(); + bool trig_time_setted(); + private slots: void h_scroll_value_changed(int value); @@ -268,11 +312,13 @@ private slots: // calibration for oscilloscope void show_calibration(); + // lissajous figure + void show_lissajous(bool show); void on_measure_updated(); void splitterMoved(int pos, int index); - void mode_changed(); + void dev_changed(bool close); private: @@ -280,12 +326,13 @@ private: pv::toolbars::SamplingBar *_sampling_bar; QWidget *_viewcenter; - widgets::ViewStatus *_viewbottom; + ViewStatus *_viewbottom; QSplitter *_vsplitter; Viewport * _time_viewport; Viewport * _fft_viewport; + LissajousFigure *_lissajous; Viewport *_active_viewport; - std::list _viewport_list; + std::list _viewport_list; std::map _trace_view_map; Ruler *_ruler; Header *_header; @@ -313,9 +360,16 @@ private: uint64_t _search_pos; bool _search_hit; + bool _show_xcursors; + std::list _xcursorList; + QPoint _hover_point; dialogs::Calibration *_cali; + bool _dso_auto; + bool _show_lissajous; + bool _back_ready; + bool _trig_time_setted; }; } // namespace view diff --git a/DSView/pv/view/viewport.cpp b/DSView/pv/view/viewport.cpp old mode 100644 new mode 100755 index 6bc37af7..33e637f4 --- a/DSView/pv/view/viewport.cpp +++ b/DSView/pv/view/viewport.cpp @@ -26,7 +26,7 @@ #include "signal.h" #include "dsosignal.h" #include "logicsignal.h" -#include "mathtrace.h" +#include "spectrumtrace.h" #include "../device/devinst.h" #include "../data/logic.h" #include "../data/logicsnapshot.h" @@ -70,7 +70,8 @@ Viewport::Viewport(View &parent, View_type type) : _dso_ym_valid(false), _waiting_trig(0), _dso_trig_moved(false), - _curs_moved(false) + _curs_moved(false), + _xcurs_moved(false) { setMouseTracking(true); setAutoFillBackground(true); @@ -90,9 +91,6 @@ Viewport::Viewport(View &parent, View_type type) : _drag_strength = 0; _drag_timer.setSingleShot(true); - _pixmap = new QPixmap(size()); - _pixmap->fill(Qt::transparent); - connect(&trigger_timer, SIGNAL(timeout()), this, SLOT(on_trigger_timer())); connect(&_drag_timer, SIGNAL(timeout()), @@ -100,6 +98,16 @@ Viewport::Viewport(View &parent, View_type type) : connect(&_view.session(), &SigSession::receive_data, this, &Viewport::set_receive_len); + + _cmenu = new QMenu(this); + QAction *yAction = _cmenu->addAction(tr("Add Y-cursor")); + QAction *xAction = _cmenu->addAction(tr("Add X-cursor")); + connect(yAction, SIGNAL(triggered(bool)), this, SLOT(add_cursor_y())); + connect(xAction, SIGNAL(triggered(bool)), this, SLOT(add_cursor_x())); + + setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), + this, SLOT(show_contextmenu(const QPoint&))); } int Viewport::get_total_height() const @@ -132,17 +140,19 @@ void Viewport::paintEvent(QPaintEvent *event) QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this); - //p.begin(this); + QColor fore(QWidget::palette().color(QWidget::foregroundRole())); + QColor back(QWidget::palette().color(QWidget::backgroundRole())); + fore.setAlpha(View::ForeAlpha); + _view.set_back(false); const vector< boost::shared_ptr > traces(_view.get_traces(_type)); BOOST_FOREACH(const boost::shared_ptr t, traces) { assert(t); - t->paint_back(p, 0, _view.get_view_width()); - if (_view.session().get_device()->dev_inst()->mode == DSO) + t->paint_back(p, 0, _view.get_view_width(), fore, back); + if (_view.back_ready()) break; } - p.setRenderHint(QPainter::Antialiasing, false); if (_view.session().get_device()->dev_inst()->mode == LOGIC || _view.session().get_instant()) { switch(_view.session().get_capture_state()) { @@ -150,84 +160,123 @@ void Viewport::paintEvent(QPaintEvent *event) break; case SigSession::Stopped: - paintSignals(p); + paintSignals(p, fore, back); break; case SigSession::Running: if (_view.session().isRepeating() && !transfer_started) { _view.set_capture_status(); - paintSignals(p); + paintSignals(p, fore, back); } else if (_type == TIME_VIEW) { _view.repeat_unshow(); - p.setRenderHint(QPainter::Antialiasing); - paintProgress(p); - p.setRenderHint(QPainter::Antialiasing, false); + paintProgress(p, fore, back); } break; } } else { - paintSignals(p); + paintSignals(p, fore, back); } BOOST_FOREACH(const boost::shared_ptr t, traces) { assert(t); if (t->enabled()) - t->paint_fore(p, 0, _view.get_view_width()); + t->paint_fore(p, 0, _view.get_view_width(), fore, back); } - //p.setRenderHint(QPainter::Antialiasing, false); if (_view.get_signalHeight() != _curSignalHeight) _curSignalHeight = _view.get_signalHeight(); p.end(); } -void Viewport::paintSignals(QPainter &p) +void Viewport::paintSignals(QPainter &p, QColor fore, QColor back) { -// if (_view.session().get_data_lock()) -// return; const vector< boost::shared_ptr > traces(_view.get_traces(_type)); - if (_view.scale() != _curScale || - _view.offset() != _curOffset || - _view.get_signalHeight() != _curSignalHeight || - _need_update) { - _curScale = _view.scale(); - _curOffset = _view.offset(); - _curSignalHeight = _view.get_signalHeight(); + if (_view.session().get_device()->dev_inst()->mode == LOGIC) { + BOOST_FOREACH(const boost::shared_ptr t, traces) + { + assert(t); + if (t->enabled()) + t->paint_mid(p, 0, t->get_view_rect().right(), fore, back); + } + } else { + if (_view.scale() != _curScale || + _view.offset() != _curOffset || + _view.get_signalHeight() != _curSignalHeight || + _need_update) { + _curScale = _view.scale(); + _curOffset = _view.offset(); + _curSignalHeight = _view.get_signalHeight(); - //pixmap = QPixmap(size()); - //pixmap.fill(Qt::transparent); - if (_pixmap->size() == size()) { - _pixmap->fill(Qt::transparent); - QPainter dbp(_pixmap); + pixmap = QPixmap(size()); + pixmap.fill(Qt::transparent); + + QPainter dbp(&pixmap); dbp.initFrom(this); - //p.setRenderHint(QPainter::Antialiasing, false); BOOST_FOREACH(const boost::shared_ptr t, traces) { assert(t); if (t->enabled()) - t->paint_mid(dbp, 0, t->get_view_rect().right()); + t->paint_mid(dbp, 0, t->get_view_rect().right(), fore, back); } _need_update = false; } + p.drawPixmap(0, 0, pixmap); } - if (_pixmap->size() == size()) - p.drawPixmap(0, 0, *_pixmap); // plot cursors + //const QRect xrect = QRect(rect().left(), rect().top(), _view.get_view_width(), rect().height()); + const QRect xrect = _view.get_view_rect(); const double samples_per_pixel = _view.session().cur_samplerate() * _view.scale(); if (_view.cursors_shown() && _type == TIME_VIEW) { list::iterator i = _view.get_cursorList().begin(); int index = 0; while (i != _view.get_cursorList().end()) { const int64_t cursorX = (*i)->index()/samples_per_pixel - _view.offset(); - if (rect().contains(_view.hover_point().x(), _view.hover_point().y()) && + if (xrect.contains(_view.hover_point().x(), _view.hover_point().y()) && qAbs(cursorX - _view.hover_point().x()) <= HitCursorMargin) - (*i)->paint(p, rect(), 1, index); + (*i)->paint(p, xrect, 1, index); else - (*i)->paint(p, rect(), 0, index); + (*i)->paint(p, xrect, 0, index); + i++; + index++; + } + } + + if (_view.xcursors_shown() && _type == TIME_VIEW) { + list::iterator i = _view.get_xcursorList().begin(); + int index = 0; + bool hovered = false; + while (i != _view.get_xcursorList().end()) { + const double cursorX = xrect.left() + (*i)->value(XCursor::XCur_Y)*xrect.width(); + const double cursorY0 = xrect.top() + (*i)->value(XCursor::XCur_X0)*xrect.height(); + const double cursorY1 = xrect.top() + (*i)->value(XCursor::XCur_X1)*xrect.height(); + + if (!hovered && ((*i)->get_close_rect(xrect).contains(_view.hover_point()) || + (*i)->get_map_rect(xrect).contains(_view.hover_point()))) { + (*i)->paint(p, xrect, XCursor::XCur_All, index); + hovered = true; + } else if(!hovered && xrect.contains(_view.hover_point())) { + if (qAbs(cursorX - _view.hover_point().x()) <= HitCursorMargin && + _view.hover_point().y() > min(cursorY0, cursorY1) && + _view.hover_point().y() < max(cursorY0, cursorY1)) { + (*i)->paint(p, xrect, XCursor::XCur_Y, index); + hovered = true; + } else if (qAbs(cursorY0 - _view.hover_point().y()) <= HitCursorMargin) { + (*i)->paint(p, xrect, XCursor::XCur_X0, index); + hovered = true; + } else if (qAbs(cursorY1 - _view.hover_point().y()) <= HitCursorMargin) { + (*i)->paint(p, xrect, XCursor::XCur_X1, index); + hovered = true; + } else { + (*i)->paint(p, xrect, XCursor::XCur_None, index); + } + } else { + (*i)->paint(p, xrect, XCursor::XCur_None, index); + } + i++; index++; } @@ -235,26 +284,26 @@ void Viewport::paintSignals(QPainter &p) if (_type == TIME_VIEW) { if (_view.trig_cursor_shown()) { - _view.get_trig_cursor()->paint(p, rect(), 0, -1); + _view.get_trig_cursor()->paint(p, xrect, 0, -1); } if (_view.search_cursor_shown()) { const int64_t searchX = _view.get_search_cursor()->index()/samples_per_pixel - _view.offset(); - if (rect().contains(_view.hover_point().x(), _view.hover_point().y()) && + if (xrect.contains(_view.hover_point().x(), _view.hover_point().y()) && qAbs(searchX - _view.hover_point().x()) <= HitCursorMargin) - _view.get_search_cursor()->paint(p, rect(), 1, -1); + _view.get_search_cursor()->paint(p, xrect, 1, -1); else - _view.get_search_cursor()->paint(p, rect(), 0, -1); + _view.get_search_cursor()->paint(p, xrect, 0, -1); } // plot zoom rect if (_action_type == LOGIC_ZOOM) { p.setPen(Qt::NoPen); - p.setBrush(Trace::dsLightBlue); + p.setBrush(View::LightBlue); p.drawRect(QRectF(_mouse_down_point, _mouse_point)); } //plot measure arrow - paintMeasure(p); + paintMeasure(p, fore, back); //plot trigger information if (_view.session().get_device()->dev_inst()->mode == DSO && @@ -284,14 +333,16 @@ void Viewport::paintSignals(QPainter &p) type_str = "Trig'd"; } } - p.setPen(Trace::DARK_FORE); + p.setPen(fore); p.drawText(_view.get_view_rect(), Qt::AlignLeft | Qt::AlignTop, type_str); } } } -void Viewport::paintProgress(QPainter &p) +void Viewport::paintProgress(QPainter &p, QColor fore, QColor back) { + (void)back; + using pv::view::Signal; const uint64_t sample_limits = _view.session().cur_samplelimits(); @@ -299,12 +350,13 @@ void Viewport::paintProgress(QPainter &p) double progress = -(_sample_received * 1.0 / sample_limits * 360 * 16); int captured_progress = 0; + p.setRenderHint(QPainter::Antialiasing, true); p.setPen(Qt::gray); p.setBrush(Qt::NoBrush); const QPoint cenPos = QPoint(_view.get_view_width() / 2, height() / 2); const int radius = min(0.3 * _view.get_view_width(), 0.3 * height()); p.drawEllipse(cenPos, radius - 2, radius - 2); - p.setPen(QPen(Trace::dsGreen, 4, Qt::SolidLine)); + p.setPen(QPen(View::Green, 4, Qt::SolidLine)); p.drawArc(cenPos.x() - radius, cenPos.y() - radius, 2* radius, 2 * radius, 180 * 16, progress); p.setPen(Qt::gray); @@ -368,49 +420,58 @@ void Viewport::paintProgress(QPainter &p) const QPoint cenRightPos = QPoint(width / 2 + 0.05 * width, height() / 2); const int trigger_radius = min(0.02 * width, 0.02 * height()); + QColor foreBack = fore; + foreBack.setAlpha(View::BackAlpha); p.setPen(Qt::NoPen); - p.setBrush((timer_cnt % 3) == 0 ? Trace::dsLightBlue : Trace::dsGray); + p.setBrush((timer_cnt % 3) == 0 ? fore : foreBack); p.drawEllipse(cenLeftPos, trigger_radius, trigger_radius); - p.setBrush((timer_cnt % 3) == 1 ? Trace::dsLightBlue : Trace::dsGray); + p.setBrush((timer_cnt % 3) == 1 ? fore : foreBack); p.drawEllipse(cenPos, trigger_radius, trigger_radius); - p.setBrush((timer_cnt % 3) == 2 ? Trace::dsLightBlue : Trace::dsGray); + p.setBrush((timer_cnt % 3) == 2 ? fore : foreBack); p.drawEllipse(cenRightPos, trigger_radius, trigger_radius); bool triggered; if (_view.session().get_capture_status(triggered, captured_progress)){ - p.setPen(Trace::dsLightBlue); + p.setPen(View::Blue); QFont font=p.font(); font.setPointSize(10); font.setBold(true); p.setFont(font); QRect status_rect = QRect(cenPos.x() - radius, cenPos.y() + radius * 0.4, radius * 2, radius * 0.5); - if (triggered) + if (triggered) { p.drawText(status_rect, Qt::AlignCenter | Qt::AlignVCenter, tr("Triggered! ") + QString::number(captured_progress) + tr("% Captured")); - else + _view.set_trig_time(); + } else { p.drawText(status_rect, Qt::AlignCenter | Qt::AlignVCenter, tr("Waiting for Trigger! ") + QString::number(captured_progress) + tr("% Captured")); + } prgRate(captured_progress); } } else { + if (!_view.trig_time_setted()) + _view.set_trig_time(); + const int progress100 = ceil(progress / -3.6 / 16); - p.setPen(Trace::dsGreen); + p.setPen(View::Green); QFont font=p.font(); font.setPointSize(50); font.setBold(true); p.setFont(font); - p.drawText(rect(), Qt::AlignCenter | Qt::AlignVCenter, QString::number(progress100)+"%"); + p.drawText(_view.get_view_rect(), Qt::AlignCenter | Qt::AlignVCenter, QString::number(progress100)+"%"); prgRate(progress100); } - p.setPen(QPen(Trace::dsLightBlue, 4, Qt::SolidLine)); + p.setPen(QPen(View::Blue, 4, Qt::SolidLine)); const int int_radius = max(radius - 4, 0); p.drawArc(cenPos.x() - int_radius, cenPos.y() - int_radius, 2* int_radius, 2 * int_radius, 180 * 16, -captured_progress*3.6*16); QFont font; p.setFont(font); + + p.setRenderHint(QPainter::Antialiasing, false); } void Viewport::mousePressEvent(QMouseEvent *event) @@ -422,28 +483,6 @@ void Viewport::mousePressEvent(QMouseEvent *event) _drag_strength = 0; _time.restart(); - if (event->button() == Qt::LeftButton) { - const vector< boost::shared_ptr > sigs(_view.session().get_signals()); - BOOST_FOREACH(const boost::shared_ptr s, sigs) { - assert(s); - if (!s->enabled()) - continue; - boost::shared_ptr dsoSig; - if ((dsoSig = dynamic_pointer_cast(s))) { - if (dsoSig->get_ms_show_hover()) { - dsoSig->set_ms_show(!dsoSig->get_ms_show()); - break; - } else if (dsoSig->get_ms_gear_hover()) { - pv::dialogs::DsoMeasure dsoMeasureDialog(this, dsoSig); - dsoMeasureDialog.exec(); - break; - } - } - } - - update(); - } - if (_action_type == NO_ACTION && event->button() == Qt::RightButton && _view.session().get_capture_state() == SigSession::Stopped) { @@ -481,7 +520,7 @@ void Viewport::mousePressEvent(QMouseEvent *event) event->button() == Qt::LeftButton) { uint64_t sample_rate = _view.session().cur_samplerate(); const double samples_per_pixel = sample_rate * _view.scale(); - if (_view.search_cursor_shown()) { + if (_action_type == NO_ACTION && _view.search_cursor_shown()) { const int64_t searchX = _view.get_search_cursor()->index()/samples_per_pixel - _view.offset(); if (_view.get_search_cursor()->grabbed()) { _view.get_ruler()->rel_grabbed_cursor(); @@ -490,7 +529,7 @@ void Viewport::mousePressEvent(QMouseEvent *event) _action_type = CURS_MOVE; } } - if (_view.cursors_shown()) { + if (_action_type == NO_ACTION && _view.cursors_shown()) { list::iterator i = _view.get_cursorList().begin(); while (i != _view.get_cursorList().end()) { const int64_t cursorX = (*i)->index()/samples_per_pixel - _view.offset(); @@ -504,6 +543,66 @@ void Viewport::mousePressEvent(QMouseEvent *event) i++; } } + if (_action_type == NO_ACTION && _view.xcursors_shown()) { + list::iterator i = _view.get_xcursorList().begin(); + const QRect xrect = _view.get_view_rect(); + while (i != _view.get_xcursorList().end()) { + const double cursorX = xrect.left() + (*i)->value(XCursor::XCur_Y)*xrect.width(); + const double cursorY0 = xrect.top() + (*i)->value(XCursor::XCur_X0)*xrect.height(); + const double cursorY1 = xrect.top() + (*i)->value(XCursor::XCur_X1)*xrect.height(); + if ((*i)->get_close_rect(xrect).contains(_view.hover_point())) { + _view.del_xcursor(*i); + if (_view.get_xcursorList().empty()) + _view.show_xcursors(false); + break; + } else if ((*i)->get_map_rect(xrect).contains(_view.hover_point())) { + vector< boost::shared_ptr > sigs(_view.session().get_signals()); + vector< boost::shared_ptr >::iterator s = sigs.begin(); + bool sig_looped = ((*i)->channel() == NULL); + bool no_dsoSig = true; + while (1) { + boost::shared_ptr dsoSig; + if ((dsoSig = dynamic_pointer_cast(*s)) && + dsoSig->enabled()) { + no_dsoSig = false; + if (sig_looped) { + (*i)->set_channel(dsoSig); + break; + } else if (dsoSig == (*i)->channel()) { + sig_looped = true; + } + } + s++; + if (s == sigs.end()) { + if (no_dsoSig) { + (*i)->set_channel(NULL); + break; + } + sig_looped = true; + s = sigs.begin(); + } + } + break; + }else if ((*i)->grabbed() != XCursor::XCur_None) { + (*i)->set_grabbed((*i)->grabbed(), false); + } else if (qAbs(cursorX - _view.hover_point().x()) <= HitCursorMargin && + _view.hover_point().y() > min(cursorY0, cursorY1) && + _view.hover_point().y() < max(cursorY0, cursorY1)) { + (*i)->set_grabbed(XCursor::XCur_Y, true); + _action_type = CURS_MOVE; + break; + } else if (qAbs(cursorY0 - _view.hover_point().y()) <= HitCursorMargin) { + (*i)->set_grabbed(XCursor::XCur_X0, true); + _action_type = CURS_MOVE; + break; + } else if (qAbs(cursorY1 - _view.hover_point().y()) <= HitCursorMargin) { + (*i)->set_grabbed(XCursor::XCur_X1, true); + _action_type = CURS_MOVE; + break; + } + i++; + } + } } } @@ -520,7 +619,7 @@ void Viewport::mouseMoveEvent(QMouseEvent *event) } _drag_strength = (_mouse_down_point - event->pos()).x(); } else if (_type == FFT_VIEW) { - BOOST_FOREACH(const boost::shared_ptr t, _view.session().get_math_signals()) { + BOOST_FOREACH(const boost::shared_ptr t, _view.session().get_spectrum_traces()) { assert(t); if(t->enabled()) { double delta = (_mouse_point - event->pos()).x(); @@ -538,7 +637,7 @@ void Viewport::mouseMoveEvent(QMouseEvent *event) if (_drag_sig) { boost::shared_ptr dsoSig; if ((dsoSig = dynamic_pointer_cast(_drag_sig))) { - dsoSig->set_trig_vpos(event->pos().y(), true); + dsoSig->set_trig_vpos(event->pos().y()); _dso_trig_moved = true; } } @@ -592,6 +691,25 @@ void Viewport::mouseMoveEvent(QMouseEvent *event) _view.cursor_moving(); _curs_moved = true; + } else { + if (_view.xcursors_shown()) { + list::iterator i = _view.get_xcursorList().begin(); + const QRect xrect = _view.get_view_rect(); + while (i != _view.get_xcursorList().end()) { + if ((*i)->grabbed() != XCursor::XCur_None) { + if ((*i)->grabbed() == XCursor::XCur_Y) { + double rate = (_view.hover_point().x() - xrect.left()) * 1.0 / xrect.width(); + (*i)->set_value((*i)->grabbed(), min(rate, 1.0)); + } else { + double rate = (_view.hover_point().y() - xrect.top()) * 1.0 / xrect.height(); + (*i)->set_value((*i)->grabbed(), max(rate, 0.0)); + } + _xcurs_moved = true; + break; + } + i++; + } + } } } } @@ -751,6 +869,15 @@ void Viewport::mouseReleaseEvent(QMouseEvent *event) _view.cursor_moved(); _curs_moved = false; } + if (_xcurs_moved && event->button() == Qt::LeftButton) { + _action_type = NO_ACTION; + list::iterator i = _view.get_xcursorList().begin(); + while (i != _view.get_xcursorList().end()) { + (*i)->rel_grabbed(); + i++; + } + _xcurs_moved = false; + } } else if (_action_type == LOGIC_EDGE) { _action_type = NO_ACTION; _edge_rising = 0; @@ -799,7 +926,9 @@ void Viewport::mouseReleaseEvent(QMouseEvent *event) void Viewport::mouseDoubleClickEvent(QMouseEvent *event) { assert (event); - (void)event; + + if (!_view.get_view_rect().contains(event->pos())) + return; if (_view.session().get_device()->dev_inst()->mode == LOGIC && _view.session().get_capture_state() == SigSession::Stopped) { @@ -862,6 +991,29 @@ void Viewport::mouseDoubleClickEvent(QMouseEvent *event) break; } } +// } else if (_view.session().get_device()->dev_inst()->mode == DSO) { +// if (event->button() == Qt::RightButton) { +// double ypos = (event->pos().y() - _view.get_view_rect().top()) * 1.0 / _view.get_view_height(); +// _view.add_xcursor(view::Ruler::CursorColor[_view.get_cursorList().size() % 8], ypos, ypos); +// _view.show_xcursors(true); +// } +// } else if (event->button() == Qt::LeftButton) { +// uint64_t index; +// const uint64_t sample_rate = _view.session().cur_samplerate(); +// const double curX = event->pos().x(); +// index = (_view.offset() + curX) * _view.scale() * sample_rate;; +// _view.add_cursor(view::Ruler::CursorColor[_view.get_cursorList().size() % 8], index); +// _view.show_cursors(true); +// } + } else if (_view.session().get_device()->dev_inst()->mode == ANALOG) { + if (event->button() == Qt::LeftButton) { + uint64_t index; + const uint64_t sample_rate = _view.session().cur_samplerate(); + const double curX = event->pos().x(); + index = (_view.offset() + curX) * _view.scale() * sample_rate;; + _view.add_cursor(view::Ruler::CursorColor[_view.get_cursorList().size() % 8], index); + _view.show_cursors(true); + } } } @@ -870,7 +1022,7 @@ void Viewport::wheelEvent(QWheelEvent *event) assert(event); if (_type == FFT_VIEW) { - BOOST_FOREACH(const boost::shared_ptr t, _view.session().get_math_signals()) { + BOOST_FOREACH(const boost::shared_ptr t, _view.session().get_spectrum_traces()) { assert(t); if(t->enabled()) { t->zoom(event->delta() / 80, event->x()); @@ -930,9 +1082,7 @@ void Viewport::leaveEvent(QEvent *) void Viewport::resizeEvent(QResizeEvent*) { - if (_pixmap) - delete _pixmap; - _pixmap = new QPixmap(size()); + } void Viewport::set_receive_len(quint64 length) @@ -1023,23 +1173,34 @@ void Viewport::measure() _cur_aftY = logicSig->get_y(); _edge_hit = true; break; + } else { + _cur_preX = _edge_start / samples_per_pixel - _view.offset(); + _cur_aftX = _view.hover_point().x(); + _cur_aftY = _view.hover_point().y(); + _edge_end = (_cur_aftX + _view.offset()) * samples_per_pixel; + _edge_hit = false; } - _cur_preX = _edge_start / samples_per_pixel - _view.offset(); - _cur_aftX = _view.hover_point().x(); - _cur_aftY = _view.hover_point().y(); - _edge_hit = false; } } else if ((dsoSig = dynamic_pointer_cast(s))) { - if (_measure_en && dsoSig->measure(_view.hover_point())) { - _measure_type = DSO_VALUE; - break; - } else { - _measure_type = NO_MEASURE; + if (dsoSig->enabled()) { + if (_measure_en && dsoSig->measure(_view.hover_point())) { + _measure_type = DSO_VALUE; + } else { + _measure_type = NO_MEASURE; + } } } } + const boost::shared_ptr mathTrace(_view.session().get_math_trace()); + if (mathTrace && mathTrace->enabled()) { + if (_measure_en && mathTrace->measure(_view.hover_point())) { + _measure_type = DSO_VALUE; + } else { + _measure_type = NO_MEASURE; + } + } } else if (_type == FFT_VIEW) { - BOOST_FOREACH(const boost::shared_ptr t, _view.session().get_math_signals()) { + BOOST_FOREACH(const boost::shared_ptr t, _view.session().get_spectrum_traces()) { assert(t); if(t->enabled()) { t->measure(_mouse_point); @@ -1050,12 +1211,13 @@ void Viewport::measure() measure_updated(); } -void Viewport::paintMeasure(QPainter &p) +void Viewport::paintMeasure(QPainter &p, QColor fore, QColor back) { + QColor active_color = back.black() > 0x80 ? View::Orange : View::Purple; _hover_hit = false; if (_action_type == NO_ACTION && _measure_type == LOGIC_FREQ) { - p.setPen(QColor(17, 133, 209, 255)); + p.setPen(active_color); p.drawLine(QLineF(_cur_preX, _cur_midY, _cur_aftX, _cur_midY)); p.drawLine(QLineF(_cur_preX, _cur_midY, _cur_preX + 2, _cur_midY - 2)); p.drawLine(QLineF(_cur_preX, _cur_midY, _cur_preX + 2, _cur_midY + 2)); @@ -1094,10 +1256,10 @@ void Viewport::paintMeasure(QPainter &p) QRectF measure4_rect = QRectF(org_pos.x(), org_pos.y()+60, (double)typical_width, 20.0); p.setPen(Qt::NoPen); - p.setBrush(QColor(17, 133, 209, 150)); + p.setBrush(View::LightBlue); p.drawRect(measure_rect); - p.setPen(Qt::black); + p.setPen(active_color); p.drawText(measure1_rect, Qt::AlignRight | Qt::AlignVCenter, tr("Width: ") + _mm_width); p.drawText(measure2_rect, Qt::AlignRight | Qt::AlignVCenter, @@ -1118,53 +1280,11 @@ void Viewport::paintMeasure(QPainter &p) uint64_t index; double value; QPointF hpoint; - const int arrow_size = 5; - const int mark_radius = 10; - const int mark_width = 20; - const int mark_cursor_height = 30; if (dsoSig->get_hover(index, hpoint, value)) { - p.setPen(dsoSig->get_colour()); - const QRectF hpoint_rect = QRectF(hpoint.x()-mark_radius/2, hpoint.y()-mark_radius/2, mark_radius, mark_radius); - if (hpoint_rect.contains(_view.hover_point())) { - p.setBrush(dsoSig->get_colour()); - const int cursor_up = hpoint.y()-mark_cursor_height; - const int cursor_dn = hpoint.y()+mark_cursor_height; - const int cursor_lf = hpoint.x()-arrow_size; - const int cursor_md = hpoint.x(); - const int cursor_rt = hpoint.x()+arrow_size; - - const QPointF up_arrow[3] = { - QPointF(cursor_lf, cursor_up+arrow_size), - QPointF(cursor_md, cursor_up), - QPointF(cursor_rt, cursor_up+arrow_size), - }; - const QPointF dn_arrow[3] = { - QPointF(cursor_lf, cursor_dn-arrow_size), - QPointF(cursor_md, cursor_dn), - QPointF(cursor_rt, cursor_dn-arrow_size), - }; - p.drawPolyline(up_arrow, 3); - p.drawPolyline(dn_arrow, 3); - p.drawLine(cursor_md, cursor_up, cursor_md, cursor_dn); - _hover_hit = true; - _hover_sig_index = dsoSig->get_index(); - _hover_sig_value = value; - _hover_index = index; - } else { - p.setBrush(Qt::NoBrush); - } - p.drawEllipse(hpoint, mark_radius, mark_radius); - QString value_c = abs(value) > 1000 ? QString::number(value/1000.0, 'f', 2) + "V" : QString::number(value, 'f', 2) + "mV"; - int value_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, - Qt::AlignLeft | Qt::AlignTop, value_c).width(); - const bool right = dsoSig->get_index()%2 ? hpoint.x() < value_width : hpoint.x() < _view.get_view_width() - value_width; - const bool up = hpoint.y() > 50; - const QPointF hpoint_sec = QPointF(hpoint.x() - (right ? -mark_width : mark_width), hpoint.y() - (up ? mark_width : -mark_width)); - p.drawLine(hpoint, hpoint_sec); - p.drawLine(hpoint_sec, QPointF(hpoint_sec.x() + (right ? value_width : -value_width), hpoint_sec.y())); - p.drawText(QRectF(right ? hpoint_sec.x() : hpoint_sec.x() - value_width, hpoint_sec.y() - mark_width, value_width, mark_width), - Qt::AlignLeft | Qt::AlignBottom, - value_c); + p.setPen(QPen(fore, 1, Qt::DashLine)); + p.setBrush(Qt::NoBrush); + p.drawLine(hpoint.x(), dsoSig->get_view_rect().top(), + hpoint.x(), dsoSig->get_view_rect().bottom()); } } } @@ -1303,8 +1423,7 @@ void Viewport::paintMeasure(QPainter &p) } if (_action_type == LOGIC_EDGE) { - p.setPen(QColor(17, 133, 209, 255)); - + p.setPen(active_color); p.drawLine(QLineF(_cur_preX, _cur_midY-5, _cur_preX, _cur_midY+5)); p.drawLine(QLineF(_cur_aftX, _cur_midY-5, _cur_aftX, _cur_midY+5)); p.drawLine(QLineF(_cur_preX, _cur_midY, _cur_aftX, _cur_midY)); @@ -1316,7 +1435,7 @@ void Viewport::paintMeasure(QPainter &p) typical_width = max(typical_width, p.boundingRect(0, 0, INT_MAX, INT_MAX, Qt::AlignLeft | Qt::AlignTop, _em_falling).width()); - typical_width = typical_width + 30; + typical_width = typical_width + 60; const double width = _view.get_view_width(); const double height = _view.viewport()->height(); @@ -1331,10 +1450,10 @@ void Viewport::paintMeasure(QPainter &p) QRectF measure3_rect = QRectF(org_pos.x(), org_pos.y()+40, (double)typical_width, 20.0); p.setPen(Qt::NoPen); - p.setBrush(QColor(17, 133, 209, 150)); + p.setBrush(View::LightBlue); p.drawRect(measure_rect); - p.setPen(Qt::black); + p.setPen(active_color); p.drawText(measure1_rect, Qt::AlignRight | Qt::AlignVCenter, _em_edges); p.drawText(measure2_rect, Qt::AlignRight | Qt::AlignVCenter, _em_rising); p.drawText(measure3_rect, Qt::AlignRight | Qt::AlignVCenter, _em_falling); @@ -1342,7 +1461,8 @@ void Viewport::paintMeasure(QPainter &p) } if (_action_type == LOGIC_JUMP) { - p.setPen(QColor(238, 178, 17, 255)); + p.setPen(active_color); + p.setBrush(Qt::NoBrush); const QPoint pre_points[] = { QPoint(_cur_preX, _cur_preY), QPoint(_cur_preX-1, _cur_preY-1), @@ -1369,25 +1489,32 @@ void Viewport::paintMeasure(QPainter &p) QPoint(_cur_aftX+2, _cur_aftY+2), }; p.drawPoints(aft_points, countof(aft_points)); - - int64_t delta = max(_edge_start, _edge_end) - min(_edge_start, _edge_end); - QString delta_text = _view.get_index_delta(_edge_start, _edge_end) + - "/" + QString::number(delta); - QFontMetrics fm = this->fontMetrics(); - const int rectW = fm.width(delta_text); - const int rectY = (_cur_aftY >= _cur_preY) ? _cur_preY_top : _cur_preY_bottom; - const int rectX = (_cur_aftX >= _cur_preX) ? _cur_preX : _cur_preX - rectW; - QRectF jump_rect = QRectF(rectX, rectY, rectW, 10); - p.drawText(jump_rect, Qt::AlignCenter | Qt::AlignVCenter, delta_text); - } + int64_t delta = max(_edge_start, _edge_end) - min(_edge_start, _edge_end); + QString delta_text = _view.get_index_delta(_edge_start, _edge_end) + + "/" + QString::number(delta); + QFontMetrics fm = this->fontMetrics(); + const int rectW = fm.width(delta_text) + 60; + const int rectH = fm.height() + 10; + //const int rectY = (_cur_aftY >= _cur_preY) ? _cur_preY_top : _cur_preY_bottom; + //const int rectX = (_cur_aftX >= _cur_preX) ? _cur_preX : _cur_preX - rectW; + const int rectY = (height() - _view.hover_point().y() < rectH + 20) ? _view.hover_point().y() - 10 - rectH : _view.hover_point().y() + 20; + const int rectX = (width() - _view.hover_point().x() < rectW) ? _view.hover_point().x() - rectW : _view.hover_point().x(); + QRectF jump_rect = QRectF(rectX, rectY, rectW, rectH); + + p.setPen(Qt::NoPen); + p.setBrush(View::LightBlue); + p.drawRect(jump_rect); + + p.setPen(active_color); + p.setBrush(Qt::NoBrush); + p.drawText(jump_rect, Qt::AlignCenter | Qt::AlignVCenter, delta_text); QPainterPath path(QPoint(_cur_preX, _cur_preY)); QPoint c1((_cur_preX+_cur_aftX)/2, _cur_preY); QPoint c2((_cur_preX+_cur_aftX)/2, _cur_aftY); path.cubicTo(c1, c2, QPoint(_cur_aftX, _cur_aftY)); p.drawPath(path); - } } } @@ -1481,5 +1608,34 @@ bool Viewport::get_dso_trig_moved() const return _dso_trig_moved; } +void Viewport::show_contextmenu(const QPoint& pos) +{ + if(_cmenu && + _view.session().get_device()->dev_inst()->mode == DSO) + { + _cur_preX = pos.x(); + _cur_preY = pos.y(); + _cmenu->exec(QCursor::pos()); + } +} + +void Viewport::add_cursor_y() +{ + uint64_t index; + const uint64_t sample_rate = _view.session().cur_samplerate(); + //const double curX = _menu_pos.x(); + index = (_view.offset() + _cur_preX) * _view.scale() * sample_rate;; + _view.add_cursor(view::Ruler::CursorColor[_view.get_cursorList().size() % 8], index); + _view.show_cursors(true); +} + +void Viewport::add_cursor_x() +{ + double ypos = (_cur_preY - _view.get_view_rect().top()) * 1.0 / _view.get_view_height(); + _view.add_xcursor(view::Ruler::CursorColor[_view.get_cursorList().size() % 8], ypos, ypos); + _view.show_xcursors(true); +} + + } // namespace view } // namespace pv diff --git a/DSView/pv/view/viewport.h b/DSView/pv/view/viewport.h old mode 100644 new mode 100755 index 005d1c32..8773c5e3 --- a/DSView/pv/view/viewport.h +++ b/DSView/pv/view/viewport.h @@ -87,7 +87,7 @@ public: public: explicit Viewport(View &parent, View_type type); - int get_total_height() const; + int get_total_height() const; QPoint get_mouse_point() const; @@ -110,17 +110,17 @@ protected: void paintEvent(QPaintEvent *event); private: - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); void mouseDoubleClickEvent(QMouseEvent *event); - void wheelEvent(QWheelEvent *event); + void wheelEvent(QWheelEvent *event); void leaveEvent(QEvent *); void resizeEvent(QResizeEvent *e); - void paintSignals(QPainter& p); - void paintProgress(QPainter& p); - void paintMeasure(QPainter &p); + void paintSignals(QPainter& p, QColor fore, QColor back); + void paintProgress(QPainter& p, QColor fore, QColor back); + void paintMeasure(QPainter &p, QColor fore, QColor back); void measure(); @@ -129,6 +129,10 @@ private slots: void on_drag_timer(); void set_receive_len(quint64 length); + void show_contextmenu(const QPoint& pos); + void add_cursor_x(); + void add_cursor_y(); + public slots: void show_wait_trigger(); void unshow_wait_trigger(); @@ -142,11 +146,12 @@ private: View_type _type; bool _need_update; - QPixmap *_pixmap; + QPixmap pixmap; + QMenu *_cmenu; uint64_t _sample_received; QPoint _mouse_point; - QPoint _mouse_down_point; + QPoint _mouse_down_point; int64_t _mouse_down_offset; double _curScale; int64_t _curOffset; @@ -211,6 +216,7 @@ private: int _waiting_trig; bool _dso_trig_moved; bool _curs_moved; + bool _xcurs_moved; }; } // namespace view diff --git a/DSView/pv/view/viewstatus.cpp b/DSView/pv/view/viewstatus.cpp new file mode 100755 index 00000000..d5578a96 --- /dev/null +++ b/DSView/pv/view/viewstatus.cpp @@ -0,0 +1,247 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "viewstatus.h" + +#include +#include +#include +#include +#include + +#include "../view/trace.h" +#include "../sigsession.h" +#include "../device/devinst.h" +#include "../view/view.h" +#include "../view/trace.h" +#include "../dialogs/dsomeasure.h" + +using namespace boost; +using namespace std; + +namespace pv { +namespace view { + +ViewStatus::ViewStatus(SigSession &session, View &parent) : + QWidget(&parent), + _session(session), + _view(parent), + _hit_rect(-1), + _last_sig_index(-1) +{ +} + +void ViewStatus::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + + QColor fore(QWidget::palette().color(QWidget::foregroundRole())); + if (_session.get_device()->dev_inst()->mode == LOGIC) { + fore.setAlpha(View::ForeAlpha); + p.setPen(fore); + p.drawText(this->rect(), Qt::AlignLeft | Qt::AlignVCenter, _rle_depth); + p.drawText(this->rect(), Qt::AlignRight | Qt::AlignVCenter, _trig_time); + + p.setPen(Qt::NoPen); + p.setBrush(View::Blue); + p.drawRect(this->rect().left(), this->rect().bottom() - 3, + _session.get_repeat_hold() * this->rect().width() / 100, 3); + + p.setPen(View::Blue); + p.drawText(this->rect(), Qt::AlignCenter | Qt::AlignVCenter, _capture_status); + } else if (_session.get_device()->dev_inst()->mode == DSO) { + fore.setAlpha(View::BackAlpha); + for(size_t i = 0; i < _mrects.size(); i++) { + int sig_index = std::get<1>(_mrects[i]); + boost::shared_ptr dsoSig = NULL; + const vector< boost::shared_ptr > sigs(_session.get_signals()); + BOOST_FOREACH(const boost::shared_ptr s, sigs) { + assert(s); + if (!s->enabled()) + continue; + if ((dsoSig = dynamic_pointer_cast(s))) { + if (sig_index == dsoSig->get_index()) + break; + else + dsoSig = NULL; + } + } + + bool active = dsoSig && dsoSig->enabled(); + const QRect rect = std::get<0>(_mrects[i]); + p.setPen(Qt::NoPen); + p.setBrush(active ? dsoSig->get_colour() : fore); + p.drawRect(QRect(rect.topLeft(), QSize(10, rect.height()))); + + QPixmap msPix(pv::dialogs::DsoMeasure::get_ms_icon(std::get<2>(_mrects[i]))); + QBitmap msMask = msPix.createMaskFromColor(QColor("black"), Qt::MaskOutColor); + msPix.fill(active ? dsoSig->get_colour() : fore); + msPix.setMask(msMask); + p.drawPixmap(QRect(rect.left()+10, rect.top(), rect.height(), rect.height()), + msPix); + + p.setPen(((int)i == _hit_rect) ? View::Blue : + active ? dsoSig->get_colour() : fore); + p.setBrush(Qt::NoBrush); + p.drawRect(rect); + + enum DSO_MEASURE_TYPE mtype = std::get<2>(_mrects[i]); + if (active && (mtype != DSO_MS_BEGIN)) { + QString title = pv::dialogs::DsoMeasure::get_ms_text(std::get<2>(_mrects[i])) + ":"; + title += dsoSig->get_measure(mtype); + int width = p.boundingRect(rect, title).width(); + p.drawText(QRect(rect.left()+10+rect.height(), rect.top(), width, rect.height()), + Qt::AlignLeft | Qt::AlignVCenter, title); + } else { + p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, tr("Measure") + QString::number(i)); + } + } + } +} + +void ViewStatus::clear() +{ + _trig_time.clear(); + _rle_depth.clear(); + _capture_status.clear(); + update(); +} + +void ViewStatus::reload() +{ + const int COLUMN = 5; + const int ROW = 2; + const int MARGIN = 3; + if (_session.get_device()->dev_inst()->mode == DSO) + { + const double width = _view.get_view_width() * 1.0 / COLUMN; + const int height = (this->height() - 2*MARGIN) / ROW; + for (size_t i = 0; i < COLUMN*ROW; i++) { + QRect rect(this->rect().left() + (i%COLUMN)*width, + this->rect().top() + (i/COLUMN+1)*MARGIN + (i/COLUMN)*height, + width-MARGIN, height); + if (_mrects.size() <= i) { + std::tuple rect_tuple; + std::get<0>(rect_tuple) = rect; + std::get<1>(rect_tuple) = -1; + std::get<2>(rect_tuple) = DSO_MS_BEGIN; + _mrects.push_back(rect_tuple); + } else { + std::get<0>(_mrects[i]) = rect; + } + } + } + update(); +} + +void ViewStatus::repeat_unshow() +{ + _capture_status.clear(); + update(); +} + +void ViewStatus::set_trig_time(QDateTime time) +{ + _trig_time = tr("Trigger Time: ") + time.toString("yyyy-MM-dd hh:mm:ss"); +} + +void ViewStatus::set_rle_depth(uint64_t depth) +{ + _rle_depth = QString::number(depth) + tr(" Samples Captured!"); +} + +void ViewStatus::set_capture_status(bool triggered, int progess) +{ + if (triggered) { + _capture_status = tr("Triggered! ") + QString::number(progess) + tr("% Captured"); + } else { + _capture_status = tr("Waiting for Trigger! ") + QString::number(progess) + tr("% Captured"); + } +} + +void ViewStatus::mousePressEvent(QMouseEvent *event) +{ + assert(event); + + if (event->button() == Qt::LeftButton) { + //BOOST_FOREACH(QRect rect, std::get<0>(_mrects)) { + for(size_t i = 0; i < _mrects.size(); i++) { + const QRect rect = std::get<0>(_mrects[i]); + if (rect.contains(event->pos())) { + _hit_rect = (int)i; + pv::dialogs::DsoMeasure dsoMeasureDialog(_session, _view, i, _last_sig_index); + dsoMeasureDialog.exec(); + break; + } + } + update(); + } +} + +void ViewStatus::set_measure(unsigned int index, bool canceled, + int sig_index, enum DSO_MEASURE_TYPE ms_type) +{ + _hit_rect = -1; + if (!canceled && index < _mrects.size()) { + _last_sig_index = sig_index; + std::get<1>(_mrects[index]) = sig_index; + std::get<2>(_mrects[index]) = ms_type; + } + update(); +} + +QJsonArray ViewStatus::get_session() +{ + QJsonArray measureVar; + for(int i = 0; i < (int)_mrects.size(); i++) { + const int index = std::get<1>(_mrects[i]); + if (index != -1) { + QJsonObject m_obj; + m_obj["site"] = i; + m_obj["index"] = index; + m_obj["type"] = (int)std::get<2>(_mrects[i]); + measureVar.append(m_obj); + } + } + + return measureVar; +} + +void ViewStatus::load_session(QJsonArray measure_array) +{ + if (_session.get_device()->dev_inst()->mode != DSO || + measure_array.empty()) + return; + + foreach (const QJsonValue &measure_value, measure_array) { + QJsonObject m_obj = measure_value.toObject(); + int index = m_obj["site"].toInt(); + int sig_index = m_obj["index"].toInt(); + enum DSO_MEASURE_TYPE ms_type = DSO_MEASURE_TYPE(m_obj["type"].toInt()); + set_measure(index, false, sig_index, ms_type); + } +} + +} // namespace view +} // namespace pv diff --git a/DSView/pv/widgets/viewstatus.h b/DSView/pv/view/viewstatus.h old mode 100644 new mode 100755 similarity index 64% rename from DSView/pv/widgets/viewstatus.h rename to DSView/pv/view/viewstatus.h index a41f9d31..804dfbfa --- a/DSView/pv/widgets/viewstatus.h +++ b/DSView/pv/view/viewstatus.h @@ -19,31 +19,49 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef DSVIEW_PV_WIDGETS_VIEWSTATUS_H -#define DSVIEW_PV_WIDGETS_VIEWSTATUS_H +#ifndef DSVIEW_PV_VIEW_VIEWSTATUS_H +#define DSVIEW_PV_VIEW_VIEWSTATUS_H #include #include #include +#include +#include + +#include +#include + +#include namespace pv { class SigSession; -namespace widgets { +namespace view { +class View; +class DsoSignal; + class ViewStatus : public QWidget { Q_OBJECT public: - explicit ViewStatus(SigSession &session, QWidget *parent = 0); + ViewStatus(SigSession &session, View &parent); void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *); + + void set_measure(unsigned int index, bool canceled, + int sig_index, enum DSO_MEASURE_TYPE ms_type); + + QJsonArray get_session(); + void load_session(QJsonArray meausre_array); signals: public slots: void clear(); + void reload(); void repeat_unshow(); void set_trig_time(QDateTime time); void set_rle_depth(uint64_t depth); @@ -51,13 +69,18 @@ public slots: private: SigSession &_session; + View &_view; + int _hit_rect; QString _trig_time; QString _rle_depth; QString _capture_status; + + int _last_sig_index; + std::vector> _mrects; }; -} // namespace widgets +} // namespace view } // namespace pv -#endif // DSVIEW_PV_WIDGETS_VIEWSTATUS_H +#endif // DSVIEW_PV_VIEW_VIEWSTATUS_H diff --git a/DSView/pv/view/xcursor.cpp b/DSView/pv/view/xcursor.cpp new file mode 100755 index 00000000..24e4e300 --- /dev/null +++ b/DSView/pv/view/xcursor.cpp @@ -0,0 +1,233 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "xcursor.h" + +#include "view.h" +#include "ruler.h" +#include "../device/device.h" +#include "dsosignal.h" + +#include + +#include + +using namespace boost; +using namespace std; + +namespace pv { +namespace view { + +XCursor::XCursor(View &view, QColor &colour, + double value0, double value1) : + _view(view), + _yvalue(0.5), + _value0(value0), + _value1(value1), + _grabbed(XCur_None), + _colour(colour) +{ + _dsoSig = NULL; + const std::vector< boost::shared_ptr > sigs(_view.session().get_signals()); + BOOST_FOREACH(const boost::shared_ptr s, sigs) { + boost::shared_ptr dsoSig; + if (dsoSig = dynamic_pointer_cast(s)) + if (dsoSig->enabled()) { + _dsoSig = dsoSig; + break; + } + } +} + +XCursor::XCursor(const XCursor &x) : + QObject(), + _view(x._view), + _dsoSig(x._dsoSig), + _yvalue(x._yvalue), + _value0(x._value0), + _value1(x._value1), + _grabbed(XCur_None), + _colour(x._colour) +{ +} + +QColor XCursor::colour() const +{ + return _colour; +} + +void XCursor::set_colour(QColor color) +{ + _colour = color; +} + +/** + * Gets/Sets the mapping channel of the marker + */ +boost::shared_ptr XCursor::channel() const +{ + return _dsoSig; +} +void XCursor::set_channel(boost::shared_ptr sig) +{ + _dsoSig = sig; +} + +enum XCursor::XCur_type XCursor::grabbed() const +{ + return _grabbed; +} +void XCursor::set_grabbed(XCur_type type, bool grabbed) +{ + if (_grabbed == XCur_None && grabbed) + _grabbed = type; + else if (_grabbed == type && !grabbed) + _grabbed = XCur_None; +} +void XCursor::rel_grabbed() +{ + _grabbed = XCur_None; +} + +double XCursor::value(XCur_type type) const +{ + if (type == XCur_Y) + return _yvalue; + else if (type == XCur_X0) + return _value0; + else if (type == XCur_X1) + return _value1; + else + return 0; +} + +void XCursor::set_value(XCur_type type, double value) +{ + if (type == XCur_Y) + _yvalue = value; + else if (type == XCur_X0) + _value0 = value; + else if (type == XCur_X1) + _value1 = value; + value_changed(); +} + +void XCursor::paint(QPainter &p, const QRect &rect, XCur_type highlight, int order) +{ + const int arrow = 3; + const int x = rect.left() + _yvalue * rect.width(); + const int y0 = rect.top() + _value0 * rect.height(); + const int y1 = rect.top() + _value1 * rect.height(); + QColor color = (order == -1) ? _colour : Ruler::CursorColor[order%8]; + const bool hit0 = (_grabbed == XCur_X0) | (_grabbed == XCur_None && (highlight == XCur_X0 || highlight == XCur_All)); + p.setPen(hit0 ? QPen(color.lighter(), 2, Qt::DashLine) : QPen(color, 1, Qt::DashLine)); + p.drawLine(QPoint(0, y0), QPoint(rect.right()-_v0_size.width(), y0)); + const bool hit1 = (_grabbed == XCur_X1) | (_grabbed == XCur_None && (highlight == XCur_X1 || highlight == XCur_All)); + p.setPen(hit1 ? QPen(color.lighter(), 2, Qt::DashLine) : QPen(color, 1, Qt::DashLine)); + p.drawLine(QPoint(0, y1), QPoint(rect.right()-_v1_size.width(), y1)); + + if (_dsoSig) { + if ((_grabbed == XCur_Y) | (_grabbed == XCur_None && (highlight == XCur_Y || highlight == XCur_All))) + p.setPen(QPen(_dsoSig->get_colour(), 2, Qt::DashLine)); + else + p.setPen(QPen(_dsoSig->get_colour(), 1, Qt::DashLine)); + p.drawLine(QPoint(x, y0), QPoint(x, y1)); + p.drawLine(QPoint(x-arrow, y0 + ((y0 > y1) ? -arrow : arrow)), QPoint(x, y0)); + p.drawLine(QPoint(x+arrow, y0 + ((y0 > y1) ? -arrow : arrow)), QPoint(x, y0)); + p.drawLine(QPoint(x-arrow, y1 + ((y0 > y1) ? arrow : -arrow)), QPoint(x, y1)); + p.drawLine(QPoint(x+arrow, y1 + ((y0 > y1) ? arrow : -arrow)), QPoint(x, y1)); + + QString vol = " Δ:" + _dsoSig->get_voltage(qAbs(y0 - y1), 2, true); + const QSizeF delta_size = p.boundingRect(rect, Qt::AlignLeft | Qt::AlignTop, vol).size(); + const QRectF delta_rect = QRectF(x, (y0+y1-delta_size.height())/2, delta_size.width(), delta_size.height()); + p.drawText(delta_rect, Qt::AlignCenter, vol); + + vol = _dsoSig->get_voltage((_dsoSig->get_zero_ratio() - _value0)*rect.height(), 2, true); + _v0_size = p.boundingRect(rect, Qt::AlignLeft | Qt::AlignTop, vol).size(); + p.drawText(QRectF(rect.right()-_v0_size.width(), y0-_v0_size.height()/2, _v0_size.width(), _v0_size.height()), + Qt::AlignLeft | Qt::AlignVCenter, + vol); + + vol = _dsoSig->get_voltage((_dsoSig->get_zero_ratio() - _value1)*rect.height(), 2, true); + _v1_size = p.boundingRect(rect, Qt::AlignLeft | Qt::AlignTop, vol).size(); + p.drawText(QRectF(rect.right()-_v1_size.width(), y1-_v1_size.height()/2, _v1_size.width(), _v1_size.height()), + Qt::AlignLeft | Qt::AlignVCenter, + vol); + } + + paint_label(p, rect); +} + +/** + * Gets the map label rectangle. + * @param rect The rectangle of the xcursor area. + * @return Returns the map label rectangle. + */ +QRect XCursor::get_map_rect(const QRect &rect) const +{ + const int width = 10; + const int64_t y = rect.top() + _value0 * rect.height() - width/2; + return QRect(rect.right()+1, y, width, width); +} + +/** + * Gets the close label rectangle. + * @param rect The rectangle of the xcursor area. + * @return Returns the close label rectangle. + */ +QRect XCursor::get_close_rect(const QRect &rect) const +{ + const int width = 10; + const int64_t y = rect.top() + _value1 * rect.height() - width/2; + return QRect(rect.right()+1, y, width, width); +} + +/** + * Paints the labels to the xcursor. + * @param p The painter to draw with. + * @param rect The rectangle of the xcursor area. + */ +void XCursor::paint_label(QPainter &p, const QRect &rect) +{ + if (_dsoSig) { + QRect map_rect = get_map_rect(rect); + p.setPen(Qt::NoPen); + p.setBrush(_dsoSig->get_colour()); + p.drawRect(map_rect); + p.setPen(Qt::white); + p.drawText(map_rect, Qt::AlignCenter | Qt::AlignHCenter, QString::number(_dsoSig->get_index())); + } + + QRect close = get_close_rect(rect); + p.setPen(Qt::NoPen); + if (close.contains(QPoint(_view.hover_point().x(), _view.hover_point().y()))) + p.setBrush(View::Red); + else + p.setBrush(_dsoSig->get_colour()); + p.drawRect(close); + p.setPen(Qt::black); + p.drawLine(close.left() + 2, close.top() + 2, close.right() - 2, close.bottom() - 2); + p.drawLine(close.left() + 2, close.bottom() - 2, close.right() - 2, close.top() + 2); +} + +} // namespace view +} // namespace pv diff --git a/DSView/pv/view/xcursor.h b/DSView/pv/view/xcursor.h new file mode 100755 index 00000000..7f5901ba --- /dev/null +++ b/DSView/pv/view/xcursor.h @@ -0,0 +1,147 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DSVIEW_PV_VIEW_XCURSOR_H +#define DSVIEW_PV_VIEW_XCURSOR_H + +#include +#include +#include + +#include +#include + +#include + +class QPainter; +class QRect; + +namespace pv { +namespace view { + +class View; +class DsoSignal; + +class XCursor : public QObject +{ + Q_OBJECT +public: + enum XCur_type { + XCur_None = -2, + XCur_All = -1, + XCur_Y = 0, + XCur_X0, + XCur_X1 + }; + +public: + /** + * Constructor. + * @param view A reference to the view that owns this marker. + * @param colour A reference to the colour of this cursor. + * @param value0 + * @param value1 + */ + XCursor(View &view, QColor &colour, double value0, double value1); + + /** + * Copy constructor + */ + XCursor(const XCursor &x); + +public: + /** + * Gets/Set the value of the marker. + */ + double value(enum XCur_type type) const; + void set_value(enum XCur_type type, double value); + + /** + * Gets/Sets colour of the marker + */ + QColor colour() const; + void set_colour(QColor color); + + /** + * Gets/Sets the mapping channel of the marker + */ + boost::shared_ptr channel() const; + void set_channel(boost::shared_ptr sig); + + /** + * grab & move + */ + enum XCur_type grabbed() const; + void set_grabbed(enum XCur_type type, bool grabbed); + void rel_grabbed(); + + /** + * Paints the marker to the viewport. + * @param p The painter to draw with. + * @param rect The rectangle of the viewport client area. + */ + void paint(QPainter &p, const QRect &rect, enum XCur_type highlight, int order); + + /** + * Gets the map label rectangle. + * @param rect The rectangle of the xcursor area. + * @return Returns the map label rectangle. + */ + QRect get_map_rect(const QRect &rect) const; + + /** + * Gets the close label rectangle. + * @param rect The rectangle of the xcursor area. + * @return Returns the close label rectangle. + */ + QRect get_close_rect(const QRect &rect) const; + + /** + * Paints the labels to the xcursor. + * @param p The painter to draw with. + * @param rect The rectangle of the xcursor area. + */ + void paint_label(QPainter &p, const QRect &rect); + +signals: + void value_changed(); + +protected: + View &_view; + boost::shared_ptr _dsoSig; + + double _yvalue; + double _value0; + double _value1; + + QSizeF _v0_size; + QSizeF _v1_size; + +private: + enum XCur_type _grabbed; + QColor _colour; +}; + +} // namespace view +} // namespace pv + +#endif // DSVIEW_PV_VIEW_XCURSOR_H diff --git a/DSView/pv/widgets/border.cpp b/DSView/pv/widgets/border.cpp old mode 100644 new mode 100755 index 11e707c1..9ff70e20 --- a/DSView/pv/widgets/border.cpp +++ b/DSView/pv/widgets/border.cpp @@ -22,6 +22,7 @@ #include "border.h" #include "../mainframe.h" +#include #include #include #include @@ -29,11 +30,17 @@ namespace pv { namespace widgets { -const QColor Border::color_border0 = QColor(80, 80, 80, 255); -const QColor Border::color_border1 = QColor(48, 47, 47, 200); -const QColor Border::color_border2 = QColor(48, 47, 47, 150); -const QColor Border::color_border3 = QColor(48, 47, 47, 100); -const QColor Border::color_border4 = QColor(48, 47, 47, 10); +const QColor Border::dark_border0 = QColor(80, 80, 80, 255); +const QColor Border::dark_border1 = QColor(48, 47, 47, 200); +const QColor Border::dark_border2 = QColor(48, 47, 47, 150); +const QColor Border::dark_border3 = QColor(48, 47, 47, 100); +const QColor Border::dark_border4 = QColor(48, 47, 47, 10); + +const QColor Border::light_border0 = QColor(100, 100, 100, 255); +const QColor Border::light_border1 = QColor(150, 150, 150, 150); +const QColor Border::light_border2 = QColor(150, 150, 150, 100); +const QColor Border::light_border3 = QColor(150, 150, 150, 50); +const QColor Border::light_border4 = QColor(150, 150, 150, 0); Border::Border(int type, QWidget *parent) : QWidget(parent), @@ -49,18 +56,34 @@ void Border::paintEvent(QPaintEvent *) painter.setPen(Qt::NoPen); painter.setRenderHint(QPainter::Antialiasing, true); QLinearGradient linearGrad(QPointF(width(), height()), QPointF(0, 0)); - linearGrad.setColorAt(0, color_border0); - linearGrad.setColorAt(0.25, color_border1); - linearGrad.setColorAt(0.5, color_border2); - linearGrad.setColorAt(0.75, color_border3); - linearGrad.setColorAt(1, color_border4); + if (qApp->property("Style").toString() == "dark") { + linearGrad.setColorAt(0, dark_border0); + linearGrad.setColorAt(0.25, dark_border1); + linearGrad.setColorAt(0.5, dark_border2); + linearGrad.setColorAt(0.75, dark_border3); + linearGrad.setColorAt(1, dark_border4); + } else { + linearGrad.setColorAt(0, light_border0); + linearGrad.setColorAt(0.25, light_border1); + linearGrad.setColorAt(0.5, light_border2); + linearGrad.setColorAt(0.75, light_border3); + linearGrad.setColorAt(1, light_border4); + } QRadialGradient radialGrad(QPointF(0, 0), width()); - radialGrad.setColorAt(0, color_border0); - radialGrad.setColorAt(0.25, color_border1); - radialGrad.setColorAt(0.5, color_border2); - radialGrad.setColorAt(0.75, color_border3); - radialGrad.setColorAt(1, color_border4); + if (qApp->property("Style").toString() == "dark") { + radialGrad.setColorAt(0, dark_border0); + radialGrad.setColorAt(0.25, dark_border1); + radialGrad.setColorAt(0.5, dark_border2); + radialGrad.setColorAt(0.75, dark_border3); + radialGrad.setColorAt(1, dark_border4); + } else { + radialGrad.setColorAt(0, light_border0); + radialGrad.setColorAt(0.25, light_border1); + radialGrad.setColorAt(0.5, light_border2); + radialGrad.setColorAt(0.75, light_border3); + radialGrad.setColorAt(1, light_border4); + } if (_type == pv::MainFrame::TopLeft) { QRectF rectangle(0, 0, width()*2, height()*2); diff --git a/DSView/pv/widgets/border.h b/DSView/pv/widgets/border.h old mode 100644 new mode 100755 index 51cde656..bc78997e --- a/DSView/pv/widgets/border.h +++ b/DSView/pv/widgets/border.h @@ -31,11 +31,17 @@ class Border : public QWidget { Q_OBJECT private: - static const QColor color_border0; - static const QColor color_border1; - static const QColor color_border2; - static const QColor color_border3; - static const QColor color_border4; + static const QColor dark_border0; + static const QColor dark_border1; + static const QColor dark_border2; + static const QColor dark_border3; + static const QColor dark_border4; + + static const QColor light_border0; + static const QColor light_border1; + static const QColor light_border2; + static const QColor light_border3; + static const QColor light_border4; public: explicit Border(int type, QWidget *parent = 0); diff --git a/DSView/pv/widgets/decodergroupbox.cpp b/DSView/pv/widgets/decodergroupbox.cpp old mode 100644 new mode 100755 index 86433eba..379b42c8 --- a/DSView/pv/widgets/decodergroupbox.cpp +++ b/DSView/pv/widgets/decodergroupbox.cpp @@ -32,6 +32,8 @@ extern "C" { #include #include #include +#include +#include #include @@ -42,17 +44,21 @@ namespace widgets { DecoderGroupBox::DecoderGroupBox(boost::shared_ptr &decoder_stack, boost::shared_ptr &dec, + QLayout *dec_layout, QWidget *parent) : - QWidget(parent), + QScrollArea(parent), _decoder_stack(decoder_stack), - _dec(dec), - _layout(new QGridLayout(this)) + _dec(dec) { + _widget = new QWidget(this); + _layout = new QGridLayout(_widget); _layout->setContentsMargins(0, 0, 0, 0); _layout->setVerticalSpacing(5); - setLayout(_layout); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setWidgetResizable(true); - _layout->addWidget(new QLabel(QString("

%1

").arg(_dec->decoder()->name), this), + QString iconPath = ":/icons/" + qApp->property("Style").toString(); + _layout->addWidget(new QLabel(QString("

%1

").arg(_dec->decoder()->name), _widget), 0, 0); _layout->setColumnStretch(0, 1); @@ -60,7 +66,7 @@ DecoderGroupBox::DecoderGroupBox(boost::shared_ptr &decoder_ assert(d); const bool have_probes = (d->channels || d->opt_channels) != 0; if (!have_probes) { - _del_button = new QPushButton(QIcon(":/icons/del.png"), QString(), this); + _del_button = new QPushButton(QIcon(iconPath+"/del.png"), QString(), _widget); _layout->addWidget(_del_button, 0, 1); connect(_del_button, SIGNAL(clicked()), this, SLOT(on_del_stack())); } @@ -73,8 +79,8 @@ DecoderGroupBox::DecoderGroupBox(boost::shared_ptr &decoder_ _index++; } _show_button = new QPushButton(QIcon(_dec->shown() ? - ":/icons/shown.png" : - ":/icons/hidden.png"), QString(), this); + iconPath+"/shown.png" : + iconPath+"/hidden.png"), QString(), _widget); _show_button->setProperty("index", -1); connect(_show_button, SIGNAL(clicked()), this, SLOT(tog_icon())); @@ -88,30 +94,44 @@ DecoderGroupBox::DecoderGroupBox(boost::shared_ptr &decoder_ i != rows.end(); i++) { if ((*i).first.decoder() == _dec->decoder()) { QPushButton *show_button = new QPushButton(QIcon((*i).second ? - ":/icons/shown.png" : - ":/icons/hidden.png"), QString(), this); + iconPath+"/shown.png" : + iconPath+"/hidden.png"), QString(), _widget); show_button->setProperty("index", index); connect(show_button, SIGNAL(clicked()), this, SLOT(tog_icon())); _row_show_button.push_back(show_button); - _layout->addWidget(new QLabel((*i).first.title(), this), _row_show_button.size(), 0); + _layout->addWidget(new QLabel((*i).first.title(), _widget), _row_show_button.size(), 0); _layout->addWidget(show_button, _row_show_button.size(), 2); } index++; } + + _layout->addLayout(dec_layout, _row_show_button.size()+1, 0, 1, 3); + + _widget->setLayout(_layout); + setWidget(_widget); + _widget->installEventFilter(this); } DecoderGroupBox::~DecoderGroupBox() { } -void DecoderGroupBox::add_layout(QLayout *layout) +bool DecoderGroupBox::eventFilter(QObject *o, QEvent *e) { - assert(layout); - _layout->addLayout(layout, _row_show_button.size()+1, 0, 1, 3); + if(o == _widget && e->type() == QEvent::Resize) { + setMinimumWidth(_widget->minimumSizeHint().width() + verticalScrollBar()->width()); + QScreen *screen=QGuiApplication::primaryScreen (); + QRect mm=screen->availableGeometry() ; + if (_widget->minimumSizeHint().height() < mm.height()/2) + setMinimumHeight(_widget->minimumSizeHint().height()); + } + + return false; } void DecoderGroupBox::tog_icon() { + QString iconPath = ":/icons/" + qApp->property("Style").toString(); QPushButton *sc = dynamic_cast(sender()); QVariant id = sc->property("index"); int index = id.toInt(); @@ -121,8 +141,8 @@ void DecoderGroupBox::tog_icon() _decoder_stack->stack()) { if (i-- == 0) { dec->show(!dec->shown()); - sc->setIcon(QIcon(dec->shown() ? ":/icons/shown.png" : - ":/icons/hidden.png")); + sc->setIcon(QIcon(dec->shown() ? iconPath+"/shown.png" : + iconPath+"/hidden.png")); break; } } @@ -133,8 +153,8 @@ void DecoderGroupBox::tog_icon() if (index-- == 0) { _decoder_stack->set_rows_gshow((*i).first, !(*i).second); //rows[(*i).first] = !(*i).second; - sc->setIcon(QIcon(rows[(*i).first] ? ":/icons/hidden.png" : - ":/icons/shown.png")); + sc->setIcon(QIcon(rows[(*i).first] ? iconPath+"/hidden.png" : + iconPath+"/shown.png")); break; } } diff --git a/DSView/pv/widgets/decodergroupbox.h b/DSView/pv/widgets/decodergroupbox.h old mode 100644 new mode 100755 index b83eef1c..814e42a6 --- a/DSView/pv/widgets/decodergroupbox.h +++ b/DSView/pv/widgets/decodergroupbox.h @@ -22,9 +22,11 @@ #ifndef DSVIEW_PV_WIDGETS_DECODERGROUPBOX_H #define DSVIEW_PV_WIDGETS_DECODERGROUPBOX_H +#include #include #include #include +#include #include namespace pv { @@ -38,16 +40,16 @@ class Decoder; namespace widgets { -class DecoderGroupBox : public QWidget +class DecoderGroupBox : public QScrollArea { Q_OBJECT public: DecoderGroupBox(boost::shared_ptr &decoder_stack, - boost::shared_ptr &dec, + boost::shared_ptr &dec, QLayout *dec_layout, QWidget *parent = NULL); ~DecoderGroupBox(); - void add_layout(QLayout *layout); + bool eventFilter(QObject *o, QEvent *e); signals: void show_hide_decoder(); @@ -59,11 +61,13 @@ private slots: void on_del_stack(); private: + QWidget *_widget; + boost::shared_ptr &_decoder_stack; boost::shared_ptr &_dec; int _index; - QGridLayout *const _layout; + QGridLayout *_layout; QPushButton *_del_button; QPushButton *_show_button; std::list _row_show_button; diff --git a/DSView/pv/widgets/decodermenu.cpp b/DSView/pv/widgets/decodermenu.cpp old mode 100644 new mode 100755 diff --git a/DSView/pv/widgets/decodermenu.h b/DSView/pv/widgets/decodermenu.h old mode 100644 new mode 100755 diff --git a/DSView/pv/widgets/fakelineedit.cpp b/DSView/pv/widgets/fakelineedit.cpp old mode 100644 new mode 100755 index d8a2930a..eb698ba6 --- a/DSView/pv/widgets/fakelineedit.cpp +++ b/DSView/pv/widgets/fakelineedit.cpp @@ -1,41 +1,41 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "fakelineedit.h" -#include - -namespace pv { -namespace widgets { - -FakeLineEdit::FakeLineEdit(QLineEdit *parent) : - QLineEdit(parent) -{ -} - -void FakeLineEdit::mousePressEvent(QMouseEvent *event) -{ - if (event->button() & Qt::LeftButton) { - trigger(); - } -} - -} // namespace widgets -} // namespace pv +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "fakelineedit.h" +#include + +namespace pv { +namespace widgets { + +FakeLineEdit::FakeLineEdit(QLineEdit *parent) : + QLineEdit(parent) +{ +} + +void FakeLineEdit::mousePressEvent(QMouseEvent *event) +{ + if (event->button() & Qt::LeftButton) { + trigger(); + } +} + +} // namespace widgets +} // namespace pv diff --git a/DSView/pv/widgets/fakelineedit.h b/DSView/pv/widgets/fakelineedit.h old mode 100644 new mode 100755 index 071c59e4..51b1d7e1 --- a/DSView/pv/widgets/fakelineedit.h +++ b/DSView/pv/widgets/fakelineedit.h @@ -1,52 +1,52 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef DSVIEW_PV_WIDGETS_FAKELINEEDIT_H -#define DSVIEW_PV_WIDGETS_FAKELINEEDIT_H - -#include - -namespace pv { - -class SigSession; - -namespace widgets { - -class FakeLineEdit : public QLineEdit -{ - Q_OBJECT -public: - explicit FakeLineEdit(QLineEdit *parent = 0); - -private: - void mousePressEvent(QMouseEvent * event); - -signals: - void trigger(); - -public slots: - -}; - -} // namespace widgets -} // namespace pv - -#endif // DSVIEW_PV_WIDGETS_FAKELINEEDIT_H +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DSVIEW_PV_WIDGETS_FAKELINEEDIT_H +#define DSVIEW_PV_WIDGETS_FAKELINEEDIT_H + +#include + +namespace pv { + +class SigSession; + +namespace widgets { + +class FakeLineEdit : public QLineEdit +{ + Q_OBJECT +public: + explicit FakeLineEdit(QLineEdit *parent = 0); + +private: + void mousePressEvent(QMouseEvent * event); + +signals: + void trigger(); + +public slots: + +}; + +} // namespace widgets +} // namespace pv + +#endif // DSVIEW_PV_WIDGETS_FAKELINEEDIT_H diff --git a/DSView/pv/widgets/viewstatus.cpp b/DSView/pv/widgets/viewstatus.cpp deleted file mode 100644 index 05c79a27..00000000 --- a/DSView/pv/widgets/viewstatus.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2016 DreamSourceLab - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "viewstatus.h" - -#include -#include -#include - -#include "../view/trace.h" -#include "../sigsession.h" - -namespace pv { -namespace widgets { - -ViewStatus::ViewStatus(SigSession &session, QWidget *parent) : - QWidget(parent), - _session(session) -{ -} - -void ViewStatus::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); - - p.setPen(pv::view::Trace::DARK_FORE); - p.drawText(this->rect(), Qt::AlignLeft | Qt::AlignVCenter, _rle_depth); - p.drawText(this->rect(), Qt::AlignRight | Qt::AlignVCenter, _trig_time); - - p.setPen(Qt::NoPen); - p.setBrush(pv::view::Trace::dsLightBlue); - p.drawRect(this->rect().left(), this->rect().bottom() - 3, - _session.get_repeat_hold() * this->rect().width() / 100, 3); - - p.setPen(pv::view::Trace::dsLightBlue); - p.drawText(this->rect(), Qt::AlignCenter | Qt::AlignVCenter, _capture_status); -} - -void ViewStatus::clear() -{ - _trig_time.clear(); - _rle_depth.clear(); - _capture_status.clear(); - update(); -} - -void ViewStatus::repeat_unshow() -{ - _capture_status.clear(); - update(); -} - -void ViewStatus::set_trig_time(QDateTime time) -{ - _trig_time = tr("Trigger Time: ") + time.toString("yyyy-MM-dd hh:mm:ss ddd"); -} - -void ViewStatus::set_rle_depth(uint64_t depth) -{ - _rle_depth = QString::number(depth) + tr(" Samples Captured!"); -} - -void ViewStatus::set_capture_status(bool triggered, int progess) -{ - if (triggered) - _capture_status = tr("Triggered! ") + QString::number(progess) + tr("% Captured"); - else - _capture_status = tr("Waiting for Trigger! ") + QString::number(progess) + tr("% Captured"); -} - -} // namespace widgets -} // namespace pv diff --git a/DSView/res/DSCope.bin b/DSView/res/DSCope.bin index 1780d8af..5a4ff5e6 100755 Binary files a/DSView/res/DSCope.bin and b/DSView/res/DSCope.bin differ diff --git a/DSView/res/DSCope1.def25.dsc b/DSView/res/DSCope1.def25.dsc new file mode 100755 index 00000000..8a9a0bcc --- /dev/null +++ b/DSView/res/DSCope1.def25.dsc @@ -0,0 +1,74 @@ +{ + "Device": "DSCope", + "DeviceMode": 1, + "Horizontal trigger position": "0", + "Language": 25, + "Operation Mode": "正常", + "Time base": "10000", + "Trigger channel": "0", + "Trigger hold off": "0", + "Trigger margin": "8", + "Trigger slope": "0", + "Trigger source": "0", + "Version": 2, + "channel": [ + { + "colour": "#eeb211", + "coupling": 0, + "enabled": true, + "index": 0, + "name": "0", + "trigValue": 0.40157480314960631, + "type": 10001, + "vdiv": 1000, + "vfactor": 1, + "zeroPos": 0.40157480314960631 + }, + { + "colour": "#009925", + "coupling": 0, + "enabled": true, + "index": 1, + "name": "1", + "trigValue": 0.59842519685039375, + "type": 10001, + "vdiv": 1000, + "vfactor": 1, + "zeroPos": 0.59842519685039375 + } + ], + "decoder": [ + ], + "measure": [ + { + "index": 0, + "site": 0, + "type": 1 + }, + { + "index": 0, + "site": 1, + "type": 17 + }, + { + "index": 0, + "site": 2, + "type": 18 + }, + { + "index": 1, + "site": 5, + "type": 1 + }, + { + "index": 1, + "site": 6, + "type": 17 + }, + { + "index": 1, + "site": 7, + "type": 18 + } + ] +} diff --git a/DSView/res/DSCope1.def31.dsc b/DSView/res/DSCope1.def31.dsc new file mode 100755 index 00000000..96b53b73 --- /dev/null +++ b/DSView/res/DSCope1.def31.dsc @@ -0,0 +1,74 @@ +{ + "Device": "DSCope", + "DeviceMode": 1, + "Horizontal trigger position": "0", + "Language": 31, + "Operation Mode": "Normal", + "Time base": "10000", + "Trigger channel": "0", + "Trigger hold off": "0", + "Trigger margin": "8", + "Trigger slope": "0", + "Trigger source": "0", + "Version": 2, + "channel": [ + { + "colour": "#eeb211", + "coupling": 0, + "enabled": true, + "index": 0, + "name": "0", + "trigValue": 0.40157480314960631, + "type": 10001, + "vdiv": 1000, + "vfactor": 1, + "zeroPos": 0.40157480314960631 + }, + { + "colour": "#009925", + "coupling": 0, + "enabled": true, + "index": 1, + "name": "1", + "trigValue": 0.59842519685039375, + "type": 10001, + "vdiv": 1000, + "vfactor": 1, + "zeroPos": 0.59842519685039375 + } + ], + "decoder": [ + ], + "measure": [ + { + "index": 0, + "site": 0, + "type": 1 + }, + { + "index": 0, + "site": 1, + "type": 17 + }, + { + "index": 0, + "site": 2, + "type": 18 + }, + { + "index": 1, + "site": 5, + "type": 1 + }, + { + "index": 1, + "site": 6, + "type": 17 + }, + { + "index": 1, + "site": 7, + "type": 18 + } + ] +} diff --git a/DSView/res/DSCope2.def.dsc b/DSView/res/DSCope2.def25.dsc similarity index 82% rename from DSView/res/DSCope2.def.dsc rename to DSView/res/DSCope2.def25.dsc index a8a37fc9..af3305f3 100755 --- a/DSView/res/DSCope2.def.dsc +++ b/DSView/res/DSCope2.def25.dsc @@ -1,45 +1,48 @@ -{ - "Device": "DSCope", - "DeviceMode": 2, - "Horizontal trigger position": "0", - "Operation Mode": "正常", - "Sample count": "10000000", - "Sample rate": "10000000", - "Time base": "10000", - "Trigger channel": "0", - "Trigger hold off": "0", - "Trigger margin": "8", - "Trigger slope": "0", - "Trigger source": "0", - "Version": 2, - "channel": [ - { - "colour": "#eeb211", - "coupling": 0, - "enabled": true, - "index": 0, - "mapMax": 1, - "mapMin": -1, - "mapUnit": "V", - "name": "0", - "type": 10002, - "vdiv": 1000, - "zeroPos": 0.5 - }, - { - "colour": "#009925", - "coupling": 0, - "enabled": true, - "index": 1, - "mapMax": 1, - "mapMin": -1, - "mapUnit": "V", - "name": "1", - "type": 10002, - "vdiv": 1000, - "zeroPos": 0.5 - } - ], - "decoder": [ - ] -} +{ + "Device": "DSCope", + "DeviceMode": 2, + "Horizontal trigger position": "0", + "Language": 25, + "Operation Mode": "正常", + "Sample count": "10000000", + "Sample rate": "10000000", + "Time base": "10000", + "Trigger channel": "0", + "Trigger hold off": "0", + "Trigger margin": "8", + "Trigger slope": "0", + "Trigger source": "0", + "Version": 2, + "channel": [ + { + "colour": "#eeb211", + "coupling": 0, + "enabled": true, + "index": 0, + "mapMax": 5, + "mapMin": -5, + "mapUnit": "V", + "name": "0", + "type": 10002, + "vdiv": 1000, + "vfactor": 1, + "zeroPos": 0.5 + }, + { + "colour": "#009925", + "coupling": 0, + "enabled": true, + "index": 1, + "mapMax": 5, + "mapMin": -5, + "mapUnit": "V", + "name": "1", + "type": 10002, + "vdiv": 1000, + "vfactor": 1, + "zeroPos": 0.5 + } + ], + "decoder": [ + ] +} diff --git a/DSView/res/DSCope1.def.dsc b/DSView/res/DSCope2.def31.dsc old mode 100644 new mode 100755 similarity index 62% rename from DSView/res/DSCope1.def.dsc rename to DSView/res/DSCope2.def31.dsc index 91a883ed..48be72cf --- a/DSView/res/DSCope1.def.dsc +++ b/DSView/res/DSCope2.def31.dsc @@ -1,41 +1,48 @@ -{ - "Device": "DSCope", - "DeviceMode": 1, - "Horizontal trigger position": "0", - "Operation Mode": "正常", - "Time base": "10000", - "Trigger channel": "0", - "Trigger hold off": "0", - "Trigger margin": "8", - "Trigger slope": "0", - "Trigger source": "0", - "Version": 2, - "channel": [ - { - "colour": "#eeb211", - "coupling": 0, - "enabled": true, - "index": 0, - "name": "0", - "trigValue": 0.50196078431372548, - "type": 10001, - "vdiv": 1000, - "vfactor": 1, - "zeroPos": 0.5 - }, - { - "colour": "#009925", - "coupling": 0, - "enabled": true, - "index": 1, - "name": "1", - "trigValue": 0.50196078431372548, - "type": 10001, - "vdiv": 1000, - "vfactor": 1, - "zeroPos": 0.5 - } - ], - "decoder": [ - ] -} +{ + "Device": "DSCope", + "DeviceMode": 2, + "Horizontal trigger position": "0", + "Language": 31, + "Operation Mode": "Normal", + "Sample count": "10000000", + "Sample rate": "10000000", + "Time base": "10000", + "Trigger channel": "0", + "Trigger hold off": "0", + "Trigger margin": "8", + "Trigger slope": "0", + "Trigger source": "0", + "Version": 2, + "channel": [ + { + "colour": "#eeb211", + "coupling": 0, + "enabled": true, + "index": 0, + "mapMax": 5, + "mapMin": -5, + "mapUnit": "V", + "name": "0", + "type": 10002, + "vdiv": 1000, + "vfactor": 1, + "zeroPos": 0.5 + }, + { + "colour": "#009925", + "coupling": 0, + "enabled": true, + "index": 1, + "mapMax": 5, + "mapMin": -5, + "mapUnit": "V", + "name": "1", + "type": 10002, + "vdiv": 1000, + "vfactor": 1, + "zeroPos": 0.5 + } + ], + "decoder": [ + ] +} diff --git a/DSView/res/DSCope20.bin b/DSView/res/DSCope20.bin index 76610d39..5dc61ea1 100755 Binary files a/DSView/res/DSCope20.bin and b/DSView/res/DSCope20.bin differ diff --git a/DSView/res/DSCopeB20.fw b/DSView/res/DSCopeB20.fw deleted file mode 100755 index 15331d72..00000000 Binary files a/DSView/res/DSCopeB20.fw and /dev/null differ diff --git a/DSView/res/DSCopeC20.bin b/DSView/res/DSCopeC20.bin deleted file mode 100755 index 16cfe87f..00000000 Binary files a/DSView/res/DSCopeC20.bin and /dev/null differ diff --git a/DSView/res/DSCopeC20.fw b/DSView/res/DSCopeC20.fw deleted file mode 100755 index 0b51c8d9..00000000 Binary files a/DSView/res/DSCopeC20.fw and /dev/null differ diff --git a/DSView/res/DSCopeC20B.bin b/DSView/res/DSCopeC20B.bin index 2028946e..9af97408 100755 Binary files a/DSView/res/DSCopeC20B.bin and b/DSView/res/DSCopeC20B.bin differ diff --git a/DSView/res/DSCopeC20B.fw b/DSView/res/DSCopeC20B.fw deleted file mode 100755 index a4d58420..00000000 Binary files a/DSView/res/DSCopeC20B.fw and /dev/null differ diff --git a/DSView/res/DSCopeC20P.bin b/DSView/res/DSCopeC20P.bin index 16cfe87f..b6e37847 100755 Binary files a/DSView/res/DSCopeC20P.bin and b/DSView/res/DSCopeC20P.bin differ diff --git a/DSView/res/DSCopeC20P.fw b/DSView/res/DSCopeC20P.fw deleted file mode 100755 index 6bec3339..00000000 Binary files a/DSView/res/DSCopeC20P.fw and /dev/null differ diff --git a/DSView/res/DSCopeU2B20.bin b/DSView/res/DSCopeU2B20.bin new file mode 100755 index 00000000..0f68bb41 Binary files /dev/null and b/DSView/res/DSCopeU2B20.bin differ diff --git a/DSView/res/DSCopeU2P20.bin b/DSView/res/DSCopeU2P20.bin new file mode 100755 index 00000000..f3c8c234 Binary files /dev/null and b/DSView/res/DSCopeU2P20.bin differ diff --git a/DSView/res/DSLogic0.def.dsc b/DSView/res/DSLogic0.def25.dsc old mode 100644 new mode 100755 similarity index 90% rename from DSView/res/DSLogic0.def.dsc rename to DSView/res/DSLogic0.def25.dsc index 72e091b7..980da53d --- a/DSView/res/DSLogic0.def.dsc +++ b/DSView/res/DSLogic0.def25.dsc @@ -1,278 +1,279 @@ -{ - "Channel Mode": "使用16个通道(最大采样率 20MHz)", - "Device": "DSLogic", - "DeviceMode": 0, - "Enable RLE Compress": 1, - "Filter Targets": "无", - "Horizontal trigger position": "0", - "Max Height": "1X", - "Operation Mode": "Stream模式", - "Sample count": "1000000", - "Sample rate": "1000000", - "Stop Options": "上传已采集的数据", - "Threshold Level": 1, - "Trigger channel": "0", - "Trigger hold off": "0", - "Trigger margin": "8", - "Trigger slope": "0", - "Trigger source": "0", - "Using Clock Negedge": 0, - "Using External Clock": 0, - "Version": 2, - "channel": [ - { - "colour": "#969696", - "enabled": true, - "index": 0, - "name": "0", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#969696", - "enabled": true, - "index": 1, - "name": "1", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#969696", - "enabled": true, - "index": 2, - "name": "2", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#969696", - "enabled": true, - "index": 3, - "name": "3", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#969696", - "enabled": true, - "index": 4, - "name": "4", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#969696", - "enabled": true, - "index": 5, - "name": "5", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#969696", - "enabled": true, - "index": 6, - "name": "6", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#969696", - "enabled": true, - "index": 7, - "name": "7", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#969696", - "enabled": true, - "index": 8, - "name": "8", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#969696", - "enabled": true, - "index": 9, - "name": "9", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#969696", - "enabled": true, - "index": 10, - "name": "10", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#969696", - "enabled": true, - "index": 11, - "name": "11", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#969696", - "enabled": true, - "index": 12, - "name": "12", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#969696", - "enabled": true, - "index": 13, - "name": "13", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#969696", - "enabled": true, - "index": 14, - "name": "14", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#969696", - "enabled": true, - "index": 15, - "name": "15", - "strigger": 0, - "type": 10000 - } - ], - "decoder": [ - ], - "trigger": { - "advTriggerMode": false, - "serialTriggerBits": 0, - "serialTriggerChannel": 0, - "serialTriggerClock": "X X X X X X X X X X X X X X X X", - "serialTriggerData": "X X X X X X X X X X X X X X X X", - "serialTriggerStart": "X X X X X X X X X X X X X X X X", - "serialTriggerStop": "X X X X X X X X X X X X X X X X", - "stageTriggerContiguous0": false, - "stageTriggerContiguous1": false, - "stageTriggerContiguous10": false, - "stageTriggerContiguous11": false, - "stageTriggerContiguous12": false, - "stageTriggerContiguous13": false, - "stageTriggerContiguous14": false, - "stageTriggerContiguous15": false, - "stageTriggerContiguous2": false, - "stageTriggerContiguous3": false, - "stageTriggerContiguous4": false, - "stageTriggerContiguous5": false, - "stageTriggerContiguous6": false, - "stageTriggerContiguous7": false, - "stageTriggerContiguous8": false, - "stageTriggerContiguous9": false, - "stageTriggerCount0": 1, - "stageTriggerCount1": 1, - "stageTriggerCount10": 1, - "stageTriggerCount11": 1, - "stageTriggerCount12": 1, - "stageTriggerCount13": 1, - "stageTriggerCount14": 1, - "stageTriggerCount15": 1, - "stageTriggerCount2": 1, - "stageTriggerCount3": 1, - "stageTriggerCount4": 1, - "stageTriggerCount5": 1, - "stageTriggerCount6": 1, - "stageTriggerCount7": 1, - "stageTriggerCount8": 1, - "stageTriggerCount9": 1, - "stageTriggerInv00": 0, - "stageTriggerInv01": 0, - "stageTriggerInv010": 0, - "stageTriggerInv011": 0, - "stageTriggerInv012": 0, - "stageTriggerInv013": 0, - "stageTriggerInv014": 0, - "stageTriggerInv015": 0, - "stageTriggerInv02": 0, - "stageTriggerInv03": 0, - "stageTriggerInv04": 0, - "stageTriggerInv05": 0, - "stageTriggerInv06": 0, - "stageTriggerInv07": 0, - "stageTriggerInv08": 0, - "stageTriggerInv09": 0, - "stageTriggerInv10": 0, - "stageTriggerInv11": 0, - "stageTriggerInv110": 0, - "stageTriggerInv111": 0, - "stageTriggerInv112": 0, - "stageTriggerInv113": 0, - "stageTriggerInv114": 0, - "stageTriggerInv115": 0, - "stageTriggerInv12": 0, - "stageTriggerInv13": 0, - "stageTriggerInv14": 0, - "stageTriggerInv15": 0, - "stageTriggerInv16": 0, - "stageTriggerInv17": 0, - "stageTriggerInv18": 0, - "stageTriggerInv19": 0, - "stageTriggerLogic0": 1, - "stageTriggerLogic1": 1, - "stageTriggerLogic10": 1, - "stageTriggerLogic11": 1, - "stageTriggerLogic12": 1, - "stageTriggerLogic13": 1, - "stageTriggerLogic14": 1, - "stageTriggerLogic15": 1, - "stageTriggerLogic2": 1, - "stageTriggerLogic3": 1, - "stageTriggerLogic4": 1, - "stageTriggerLogic5": 1, - "stageTriggerLogic6": 1, - "stageTriggerLogic7": 1, - "stageTriggerLogic8": 1, - "stageTriggerLogic9": 1, - "stageTriggerValue00": "X X X X X X X X X X X X X X X X", - "stageTriggerValue01": "X X X X X X X X X X X X X X X X", - "stageTriggerValue010": "X X X X X X X X X X X X X X X X", - "stageTriggerValue011": "X X X X X X X X X X X X X X X X", - "stageTriggerValue012": "X X X X X X X X X X X X X X X X", - "stageTriggerValue013": "X X X X X X X X X X X X X X X X", - "stageTriggerValue014": "X X X X X X X X X X X X X X X X", - "stageTriggerValue015": "X X X X X X X X X X X X X X X X", - "stageTriggerValue02": "X X X X X X X X X X X X X X X X", - "stageTriggerValue03": "X X X X X X X X X X X X X X X X", - "stageTriggerValue04": "X X X X X X X X X X X X X X X X", - "stageTriggerValue05": "X X X X X X X X X X X X X X X X", - "stageTriggerValue06": "X X X X X X X X X X X X X X X X", - "stageTriggerValue07": "X X X X X X X X X X X X X X X X", - "stageTriggerValue08": "X X X X X X X X X X X X X X X X", - "stageTriggerValue09": "X X X X X X X X X X X X X X X X", - "stageTriggerValue10": "X X X X X X X X X X X X X X X X", - "stageTriggerValue11": "X X X X X X X X X X X X X X X X", - "stageTriggerValue110": "X X X X X X X X X X X X X X X X", - "stageTriggerValue111": "X X X X X X X X X X X X X X X X", - "stageTriggerValue112": "X X X X X X X X X X X X X X X X", - "stageTriggerValue113": "X X X X X X X X X X X X X X X X", - "stageTriggerValue114": "X X X X X X X X X X X X X X X X", - "stageTriggerValue115": "X X X X X X X X X X X X X X X X", - "stageTriggerValue12": "X X X X X X X X X X X X X X X X", - "stageTriggerValue13": "X X X X X X X X X X X X X X X X", - "stageTriggerValue14": "X X X X X X X X X X X X X X X X", - "stageTriggerValue15": "X X X X X X X X X X X X X X X X", - "stageTriggerValue16": "X X X X X X X X X X X X X X X X", - "stageTriggerValue17": "X X X X X X X X X X X X X X X X", - "stageTriggerValue18": "X X X X X X X X X X X X X X X X", - "stageTriggerValue19": "X X X X X X X X X X X X X X X X", - "triggerPos": 1, - "triggerStages": 0, - "triggerTab": 0 - } -} +{ + "Channel Mode": "使用16个通道(最大采样率 20MHz)", + "Device": "DSLogic", + "DeviceMode": 0, + "Enable RLE Compress": 0, + "Filter Targets": "无", + "Horizontal trigger position": "0", + "Language": 25, + "Max Height": "1X", + "Operation Mode": "Stream模式", + "Sample count": "1000000", + "Sample rate": "1000000", + "Stop Options": "上传已采集的数据", + "Threshold Level": 1, + "Trigger channel": "0", + "Trigger hold off": "0", + "Trigger margin": "8", + "Trigger slope": "0", + "Trigger source": "0", + "Using Clock Negedge": 0, + "Using External Clock": 0, + "Version": 2, + "channel": [ + { + "colour": "default", + "enabled": true, + "index": 0, + "name": "0", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 1, + "name": "1", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 2, + "name": "2", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 3, + "name": "3", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 4, + "name": "4", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 5, + "name": "5", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 6, + "name": "6", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 7, + "name": "7", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 8, + "name": "8", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 9, + "name": "9", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 10, + "name": "10", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 11, + "name": "11", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 12, + "name": "12", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 13, + "name": "13", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 14, + "name": "14", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 15, + "name": "15", + "strigger": 0, + "type": 10000 + } + ], + "decoder": [ + ], + "trigger": { + "advTriggerMode": false, + "serialTriggerBits": 0, + "serialTriggerChannel": 0, + "serialTriggerClock": "X X X X X X X X X X X X X X X X", + "serialTriggerData": "X X X X X X X X X X X X X X X X", + "serialTriggerStart": "X X X X X X X X X X X X X X X X", + "serialTriggerStop": "X X X X X X X X X X X X X X X X", + "stageTriggerContiguous0": false, + "stageTriggerContiguous1": false, + "stageTriggerContiguous10": false, + "stageTriggerContiguous11": false, + "stageTriggerContiguous12": false, + "stageTriggerContiguous13": false, + "stageTriggerContiguous14": false, + "stageTriggerContiguous15": false, + "stageTriggerContiguous2": false, + "stageTriggerContiguous3": false, + "stageTriggerContiguous4": false, + "stageTriggerContiguous5": false, + "stageTriggerContiguous6": false, + "stageTriggerContiguous7": false, + "stageTriggerContiguous8": false, + "stageTriggerContiguous9": false, + "stageTriggerCount0": 1, + "stageTriggerCount1": 1, + "stageTriggerCount10": 1, + "stageTriggerCount11": 1, + "stageTriggerCount12": 1, + "stageTriggerCount13": 1, + "stageTriggerCount14": 1, + "stageTriggerCount15": 1, + "stageTriggerCount2": 1, + "stageTriggerCount3": 1, + "stageTriggerCount4": 1, + "stageTriggerCount5": 1, + "stageTriggerCount6": 1, + "stageTriggerCount7": 1, + "stageTriggerCount8": 1, + "stageTriggerCount9": 1, + "stageTriggerInv00": 0, + "stageTriggerInv01": 0, + "stageTriggerInv010": 0, + "stageTriggerInv011": 0, + "stageTriggerInv012": 0, + "stageTriggerInv013": 0, + "stageTriggerInv014": 0, + "stageTriggerInv015": 0, + "stageTriggerInv02": 0, + "stageTriggerInv03": 0, + "stageTriggerInv04": 0, + "stageTriggerInv05": 0, + "stageTriggerInv06": 0, + "stageTriggerInv07": 0, + "stageTriggerInv08": 0, + "stageTriggerInv09": 0, + "stageTriggerInv10": 0, + "stageTriggerInv11": 0, + "stageTriggerInv110": 0, + "stageTriggerInv111": 0, + "stageTriggerInv112": 0, + "stageTriggerInv113": 0, + "stageTriggerInv114": 0, + "stageTriggerInv115": 0, + "stageTriggerInv12": 0, + "stageTriggerInv13": 0, + "stageTriggerInv14": 0, + "stageTriggerInv15": 0, + "stageTriggerInv16": 0, + "stageTriggerInv17": 0, + "stageTriggerInv18": 0, + "stageTriggerInv19": 0, + "stageTriggerLogic0": 1, + "stageTriggerLogic1": 1, + "stageTriggerLogic10": 1, + "stageTriggerLogic11": 1, + "stageTriggerLogic12": 1, + "stageTriggerLogic13": 1, + "stageTriggerLogic14": 1, + "stageTriggerLogic15": 1, + "stageTriggerLogic2": 1, + "stageTriggerLogic3": 1, + "stageTriggerLogic4": 1, + "stageTriggerLogic5": 1, + "stageTriggerLogic6": 1, + "stageTriggerLogic7": 1, + "stageTriggerLogic8": 1, + "stageTriggerLogic9": 1, + "stageTriggerValue00": "X X X X X X X X X X X X X X X X", + "stageTriggerValue01": "X X X X X X X X X X X X X X X X", + "stageTriggerValue010": "X X X X X X X X X X X X X X X X", + "stageTriggerValue011": "X X X X X X X X X X X X X X X X", + "stageTriggerValue012": "X X X X X X X X X X X X X X X X", + "stageTriggerValue013": "X X X X X X X X X X X X X X X X", + "stageTriggerValue014": "X X X X X X X X X X X X X X X X", + "stageTriggerValue015": "X X X X X X X X X X X X X X X X", + "stageTriggerValue02": "X X X X X X X X X X X X X X X X", + "stageTriggerValue03": "X X X X X X X X X X X X X X X X", + "stageTriggerValue04": "X X X X X X X X X X X X X X X X", + "stageTriggerValue05": "X X X X X X X X X X X X X X X X", + "stageTriggerValue06": "X X X X X X X X X X X X X X X X", + "stageTriggerValue07": "X X X X X X X X X X X X X X X X", + "stageTriggerValue08": "X X X X X X X X X X X X X X X X", + "stageTriggerValue09": "X X X X X X X X X X X X X X X X", + "stageTriggerValue10": "X X X X X X X X X X X X X X X X", + "stageTriggerValue11": "X X X X X X X X X X X X X X X X", + "stageTriggerValue110": "X X X X X X X X X X X X X X X X", + "stageTriggerValue111": "X X X X X X X X X X X X X X X X", + "stageTriggerValue112": "X X X X X X X X X X X X X X X X", + "stageTriggerValue113": "X X X X X X X X X X X X X X X X", + "stageTriggerValue114": "X X X X X X X X X X X X X X X X", + "stageTriggerValue115": "X X X X X X X X X X X X X X X X", + "stageTriggerValue12": "X X X X X X X X X X X X X X X X", + "stageTriggerValue13": "X X X X X X X X X X X X X X X X", + "stageTriggerValue14": "X X X X X X X X X X X X X X X X", + "stageTriggerValue15": "X X X X X X X X X X X X X X X X", + "stageTriggerValue16": "X X X X X X X X X X X X X X X X", + "stageTriggerValue17": "X X X X X X X X X X X X X X X X", + "stageTriggerValue18": "X X X X X X X X X X X X X X X X", + "stageTriggerValue19": "X X X X X X X X X X X X X X X X", + "triggerPos": 1, + "triggerStages": 0, + "triggerTab": 0 + } +} diff --git a/DSView/res/DSLogic0.def31.dsc b/DSView/res/DSLogic0.def31.dsc new file mode 100755 index 00000000..71acd77b --- /dev/null +++ b/DSView/res/DSLogic0.def31.dsc @@ -0,0 +1,279 @@ +{ + "Channel Mode": "Use 16 Channels (Max 20MHz)", + "Device": "DSLogic", + "DeviceMode": 0, + "Enable RLE Compress": 0, + "Filter Targets": "None", + "Horizontal trigger position": "0", + "Language": 31, + "Max Height": "1X", + "Operation Mode": "Stream Mode", + "Sample count": "1000000", + "Sample rate": "1000000", + "Stop Options": "Upload captured data", + "Threshold Level": 1, + "Trigger channel": "0", + "Trigger hold off": "0", + "Trigger margin": "8", + "Trigger slope": "0", + "Trigger source": "0", + "Using Clock Negedge": 0, + "Using External Clock": 0, + "Version": 2, + "channel": [ + { + "colour": "default", + "enabled": true, + "index": 0, + "name": "0", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 1, + "name": "1", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 2, + "name": "2", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 3, + "name": "3", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 4, + "name": "4", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 5, + "name": "5", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 6, + "name": "6", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 7, + "name": "7", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 8, + "name": "8", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 9, + "name": "9", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 10, + "name": "10", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 11, + "name": "11", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 12, + "name": "12", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 13, + "name": "13", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 14, + "name": "14", + "strigger": 0, + "type": 10000 + }, + { + "colour": "default", + "enabled": true, + "index": 15, + "name": "15", + "strigger": 0, + "type": 10000 + } + ], + "decoder": [ + ], + "trigger": { + "advTriggerMode": false, + "serialTriggerBits": 0, + "serialTriggerChannel": 0, + "serialTriggerClock": "X X X X X X X X X X X X X X X X", + "serialTriggerData": "X X X X X X X X X X X X X X X X", + "serialTriggerStart": "X X X X X X X X X X X X X X X X", + "serialTriggerStop": "X X X X X X X X X X X X X X X X", + "stageTriggerContiguous0": false, + "stageTriggerContiguous1": false, + "stageTriggerContiguous10": false, + "stageTriggerContiguous11": false, + "stageTriggerContiguous12": false, + "stageTriggerContiguous13": false, + "stageTriggerContiguous14": false, + "stageTriggerContiguous15": false, + "stageTriggerContiguous2": false, + "stageTriggerContiguous3": false, + "stageTriggerContiguous4": false, + "stageTriggerContiguous5": false, + "stageTriggerContiguous6": false, + "stageTriggerContiguous7": false, + "stageTriggerContiguous8": false, + "stageTriggerContiguous9": false, + "stageTriggerCount0": 1, + "stageTriggerCount1": 1, + "stageTriggerCount10": 1, + "stageTriggerCount11": 1, + "stageTriggerCount12": 1, + "stageTriggerCount13": 1, + "stageTriggerCount14": 1, + "stageTriggerCount15": 1, + "stageTriggerCount2": 1, + "stageTriggerCount3": 1, + "stageTriggerCount4": 1, + "stageTriggerCount5": 1, + "stageTriggerCount6": 1, + "stageTriggerCount7": 1, + "stageTriggerCount8": 1, + "stageTriggerCount9": 1, + "stageTriggerInv00": 0, + "stageTriggerInv01": 0, + "stageTriggerInv010": 0, + "stageTriggerInv011": 0, + "stageTriggerInv012": 0, + "stageTriggerInv013": 0, + "stageTriggerInv014": 0, + "stageTriggerInv015": 0, + "stageTriggerInv02": 0, + "stageTriggerInv03": 0, + "stageTriggerInv04": 0, + "stageTriggerInv05": 0, + "stageTriggerInv06": 0, + "stageTriggerInv07": 0, + "stageTriggerInv08": 0, + "stageTriggerInv09": 0, + "stageTriggerInv10": 0, + "stageTriggerInv11": 0, + "stageTriggerInv110": 0, + "stageTriggerInv111": 0, + "stageTriggerInv112": 0, + "stageTriggerInv113": 0, + "stageTriggerInv114": 0, + "stageTriggerInv115": 0, + "stageTriggerInv12": 0, + "stageTriggerInv13": 0, + "stageTriggerInv14": 0, + "stageTriggerInv15": 0, + "stageTriggerInv16": 0, + "stageTriggerInv17": 0, + "stageTriggerInv18": 0, + "stageTriggerInv19": 0, + "stageTriggerLogic0": 1, + "stageTriggerLogic1": 1, + "stageTriggerLogic10": 1, + "stageTriggerLogic11": 1, + "stageTriggerLogic12": 1, + "stageTriggerLogic13": 1, + "stageTriggerLogic14": 1, + "stageTriggerLogic15": 1, + "stageTriggerLogic2": 1, + "stageTriggerLogic3": 1, + "stageTriggerLogic4": 1, + "stageTriggerLogic5": 1, + "stageTriggerLogic6": 1, + "stageTriggerLogic7": 1, + "stageTriggerLogic8": 1, + "stageTriggerLogic9": 1, + "stageTriggerValue00": "X X X X X X X X X X X X X X X X", + "stageTriggerValue01": "X X X X X X X X X X X X X X X X", + "stageTriggerValue010": "X X X X X X X X X X X X X X X X", + "stageTriggerValue011": "X X X X X X X X X X X X X X X X", + "stageTriggerValue012": "X X X X X X X X X X X X X X X X", + "stageTriggerValue013": "X X X X X X X X X X X X X X X X", + "stageTriggerValue014": "X X X X X X X X X X X X X X X X", + "stageTriggerValue015": "X X X X X X X X X X X X X X X X", + "stageTriggerValue02": "X X X X X X X X X X X X X X X X", + "stageTriggerValue03": "X X X X X X X X X X X X X X X X", + "stageTriggerValue04": "X X X X X X X X X X X X X X X X", + "stageTriggerValue05": "X X X X X X X X X X X X X X X X", + "stageTriggerValue06": "X X X X X X X X X X X X X X X X", + "stageTriggerValue07": "X X X X X X X X X X X X X X X X", + "stageTriggerValue08": "X X X X X X X X X X X X X X X X", + "stageTriggerValue09": "X X X X X X X X X X X X X X X X", + "stageTriggerValue10": "X X X X X X X X X X X X X X X X", + "stageTriggerValue11": "X X X X X X X X X X X X X X X X", + "stageTriggerValue110": "X X X X X X X X X X X X X X X X", + "stageTriggerValue111": "X X X X X X X X X X X X X X X X", + "stageTriggerValue112": "X X X X X X X X X X X X X X X X", + "stageTriggerValue113": "X X X X X X X X X X X X X X X X", + "stageTriggerValue114": "X X X X X X X X X X X X X X X X", + "stageTriggerValue115": "X X X X X X X X X X X X X X X X", + "stageTriggerValue12": "X X X X X X X X X X X X X X X X", + "stageTriggerValue13": "X X X X X X X X X X X X X X X X", + "stageTriggerValue14": "X X X X X X X X X X X X X X X X", + "stageTriggerValue15": "X X X X X X X X X X X X X X X X", + "stageTriggerValue16": "X X X X X X X X X X X X X X X X", + "stageTriggerValue17": "X X X X X X X X X X X X X X X X", + "stageTriggerValue18": "X X X X X X X X X X X X X X X X", + "stageTriggerValue19": "X X X X X X X X X X X X X X X X", + "triggerPos": 1, + "triggerStages": 0, + "triggerTab": 0 + } +} diff --git a/DSView/res/DSLogic1.def25.dsc b/DSView/res/DSLogic1.def25.dsc new file mode 100755 index 00000000..13f166cf --- /dev/null +++ b/DSView/res/DSLogic1.def25.dsc @@ -0,0 +1,82 @@ +{ + "Channel Mode": "使用通道 0~1 (最大采样率 200MHz)", + "Device": "DSLogic", + "DeviceMode": 1, + "Enable RLE Compress": 1, + "Filter Targets": "无", + "Horizontal trigger position": "0", + "Language": 25, + "Max Height": "1X", + "Operation Mode": "Stream模式", + "Sample count": "1000000", + "Sample rate": "100000000", + "Stop Options": "上传已采集的数据", + "Threshold Level": "1.8/2.5/3.3V Level", + "Trigger hold off": "0", + "Trigger margin": "8", + "Trigger slope": "0", + "Trigger source": "0", + "Using Clock Negedge": 0, + "Using External Clock": 0, + "Version": 2, + "channel": [ + { + "colour": "#eeb211", + "coupling": 0, + "enabled": true, + "index": 0, + "name": "0", + "trigValue": 0.40157480314960631, + "type": 10001, + "vdiv": 1000, + "vfactor": 1, + "zeroPos": 0.40157480314960631 + }, + { + "colour": "#009925", + "coupling": 0, + "enabled": true, + "index": 1, + "name": "1", + "trigValue": 0.59842519685039375, + "type": 10001, + "vdiv": 1000, + "vfactor": 1, + "zeroPos": 0.59842519685039375 + } + ], + "decoder": [ + ], + "measure": [ + { + "index": 0, + "site": 0, + "type": 1 + }, + { + "index": 0, + "site": 1, + "type": 17 + }, + { + "index": 0, + "site": 2, + "type": 18 + }, + { + "index": 1, + "site": 5, + "type": 1 + }, + { + "index": 1, + "site": 6, + "type": 17 + }, + { + "index": 1, + "site": 7, + "type": 18 + } + ] +} diff --git a/DSView/res/DSLogic1.def31.dsc b/DSView/res/DSLogic1.def31.dsc new file mode 100755 index 00000000..35595213 --- /dev/null +++ b/DSView/res/DSLogic1.def31.dsc @@ -0,0 +1,82 @@ +{ + "Channel Mode": "Use Channels 0~1 (Max 200MHz)", + "Device": "DSLogic", + "DeviceMode": 1, + "Enable RLE Compress": 1, + "Filter Targets": "None", + "Horizontal trigger position": "0", + "Language": 31, + "Max Height": "1X", + "Operation Mode": "Stream Mode", + "Sample count": "1000000", + "Sample rate": "100000000", + "Stop Options": "Upload captured data", + "Threshold Level": "1.8/2.5/3.3V Level", + "Trigger hold off": "0", + "Trigger margin": "8", + "Trigger slope": "0", + "Trigger source": "0", + "Using Clock Negedge": 0, + "Using External Clock": 0, + "Version": 2, + "channel": [ + { + "colour": "#eeb211", + "coupling": 0, + "enabled": true, + "index": 0, + "name": "0", + "trigValue": 0.40157480314960631, + "type": 10001, + "vdiv": 1000, + "vfactor": 1, + "zeroPos": 0.40157480314960631 + }, + { + "colour": "#009925", + "coupling": 0, + "enabled": true, + "index": 1, + "name": "1", + "trigValue": 0.59842519685039375, + "type": 10001, + "vdiv": 1000, + "vfactor": 1, + "zeroPos": 0.59842519685039375 + } + ], + "decoder": [ + ], + "measure": [ + { + "index": 0, + "site": 0, + "type": 1 + }, + { + "index": 0, + "site": 1, + "type": 17 + }, + { + "index": 0, + "site": 2, + "type": 18 + }, + { + "index": 1, + "site": 5, + "type": 1 + }, + { + "index": 1, + "site": 6, + "type": 17 + }, + { + "index": 1, + "site": 7, + "type": 18 + } + ] +} diff --git a/DSView/res/DSLogic2.def.dsc b/DSView/res/DSLogic2.def25.dsc old mode 100644 new mode 100755 similarity index 88% rename from DSView/res/DSLogic2.def.dsc rename to DSView/res/DSLogic2.def25.dsc index 6f4fbe28..3878286d --- a/DSView/res/DSLogic2.def.dsc +++ b/DSView/res/DSLogic2.def25.dsc @@ -1,51 +1,52 @@ -{ - "Channel Mode": "使用通道 0~1 (最大采样率 10MHz)", - "Device": "DSLogic", - "DeviceMode": 2, - "Enable RLE Compress": 1, - "Filter Targets": "无", - "Horizontal trigger position": "0", - "Max Height": "1X", - "Operation Mode": "Stream模式", - "Sample count": "1000000", - "Sample rate": "1000000", - "Stop Options": "上传已采集的数据", - "Threshold Level": "1.8/2.5/3.3V Level", - "Trigger hold off": "0", - "Trigger margin": "8", - "Trigger slope": "0", - "Trigger source": "0", - "Using Clock Negedge": 0, - "Using External Clock": 0, - "Version": 2, - "channel": [ - { - "colour": "#eeb211", - "coupling": 0, - "enabled": true, - "index": 0, - "mapMax": 1, - "mapMin": -1, - "mapUnit": "V", - "name": "0", - "type": 10002, - "vdiv": 1000, - "zeroPos": 0.5 - }, - { - "colour": "#009925", - "coupling": 0, - "enabled": true, - "index": 1, - "mapMax": 1, - "mapMin": -1, - "mapUnit": "V", - "name": "1", - "type": 10002, - "vdiv": 1000, - "zeroPos": 0.5 - } - ], - "decoder": [ - ] -} +{ + "Channel Mode": "使用通道 0~1 (最大采样率 10MHz)", + "Device": "DSLogic", + "DeviceMode": 2, + "Enable RLE Compress": 1, + "Filter Targets": "无", + "Horizontal trigger position": "0", + "Language": 25, + "Max Height": "1X", + "Operation Mode": "Stream模式", + "Sample count": "1000000", + "Sample rate": "1000000", + "Stop Options": "上传已采集的数据", + "Threshold Level": "1.8/2.5/3.3V Level", + "Trigger hold off": "0", + "Trigger margin": "8", + "Trigger slope": "0", + "Trigger source": "0", + "Using Clock Negedge": 0, + "Using External Clock": 0, + "Version": 2, + "channel": [ + { + "colour": "#eeb211", + "coupling": 0, + "enabled": true, + "index": 0, + "mapMax": 5, + "mapMin": -5, + "mapUnit": "V", + "name": "0", + "type": 10002, + "vdiv": 1000, + "zeroPos": 0.5 + }, + { + "colour": "#009925", + "coupling": 0, + "enabled": true, + "index": 1, + "mapMax": 5, + "mapMin": -5, + "mapUnit": "V", + "name": "1", + "type": 10002, + "vdiv": 1000, + "zeroPos": 0.5 + } + ], + "decoder": [ + ] +} diff --git a/DSView/res/DSLogic1.def.dsc b/DSView/res/DSLogic2.def31.dsc old mode 100644 new mode 100755 similarity index 65% rename from DSView/res/DSLogic1.def.dsc rename to DSView/res/DSLogic2.def31.dsc index 71cdec06..672b2be3 --- a/DSView/res/DSLogic1.def.dsc +++ b/DSView/res/DSLogic2.def31.dsc @@ -1,49 +1,52 @@ -{ - "Channel Mode": "使用通道 0~1 (最大采样率 200MHz)", - "Device": "DSLogic", - "DeviceMode": 1, - "Enable RLE Compress": 1, - "Filter Targets": "无", - "Horizontal trigger position": "0", - "Max Height": "1X", - "Operation Mode": "Stream模式", - "Sample count": "1000000", - "Sample rate": "100000000", - "Stop Options": "上传已采集的数据", - "Threshold Level": "1.8/2.5/3.3V Level", - "Trigger hold off": "0", - "Trigger margin": "8", - "Trigger slope": "0", - "Trigger source": "0", - "Using Clock Negedge": 0, - "Using External Clock": 0, - "Version": 2, - "channel": [ - { - "colour": "#eeb211", - "coupling": 0, - "enabled": true, - "index": 0, - "name": "0", - "trigValue": 0.5, - "type": 10001, - "vdiv": 1000, - "vfactor": 1, - "zeroPos": 0.5 - }, - { - "colour": "#009925", - "coupling": 0, - "enabled": true, - "index": 1, - "name": "1", - "trigValue": 0.5, - "type": 10001, - "vdiv": 1000, - "vfactor": 1, - "zeroPos": 0.5 - } - ], - "decoder": [ - ] -} +{ + "Channel Mode": "Use Channels 0~1 (Max 10MHz)", + "Device": "DSLogic", + "DeviceMode": 2, + "Enable RLE Compress": 1, + "Filter Targets": "None", + "Horizontal trigger position": "0", + "Language": 31, + "Max Height": "1X", + "Operation Mode": "Stream Mode", + "Sample count": "1000000", + "Sample rate": "1000000", + "Stop Options": "Upload captured data", + "Threshold Level": "1.8/2.5/3.3V Level", + "Trigger hold off": "0", + "Trigger margin": "8", + "Trigger slope": "0", + "Trigger source": "0", + "Using Clock Negedge": 0, + "Using External Clock": 0, + "Version": 2, + "channel": [ + { + "colour": "#eeb211", + "coupling": 0, + "enabled": true, + "index": 0, + "mapMax": 5, + "mapMin": -5, + "mapUnit": "V", + "name": "0", + "type": 10002, + "vdiv": 1000, + "zeroPos": 0.5 + }, + { + "colour": "#009925", + "coupling": 0, + "enabled": true, + "index": 1, + "mapMax": 5, + "mapMin": -5, + "mapUnit": "V", + "name": "1", + "type": 10002, + "vdiv": 1000, + "zeroPos": 0.5 + } + ], + "decoder": [ + ] +} diff --git a/DSView/res/DSLogic33.bin b/DSView/res/DSLogic33.bin index c96a2f7a..6c6cf9eb 100755 Binary files a/DSView/res/DSLogic33.bin and b/DSView/res/DSLogic33.bin differ diff --git a/DSView/res/DSLogic50.bin b/DSView/res/DSLogic50.bin index 13166677..075fb55b 100755 Binary files a/DSView/res/DSLogic50.bin and b/DSView/res/DSLogic50.bin differ diff --git a/DSView/res/DSLogicBasic.bin b/DSView/res/DSLogicBasic.bin index 8175040b..67367c54 100755 Binary files a/DSView/res/DSLogicBasic.bin and b/DSView/res/DSLogicBasic.bin differ diff --git a/DSView/res/DSLogicBasic.fw b/DSView/res/DSLogicBasic.fw deleted file mode 100755 index a25b2d23..00000000 Binary files a/DSView/res/DSLogicBasic.fw and /dev/null differ diff --git a/DSView/res/DSLogicPlus.bin b/DSView/res/DSLogicPlus.bin index 9eb96a7a..8733d447 100755 Binary files a/DSView/res/DSLogicPlus.bin and b/DSView/res/DSLogicPlus.bin differ diff --git a/DSView/res/DSLogicPlus.fw b/DSView/res/DSLogicPlus.fw deleted file mode 100755 index 036fafa6..00000000 Binary files a/DSView/res/DSLogicPlus.fw and /dev/null differ diff --git a/DSView/res/DSLogicPro.bin b/DSView/res/DSLogicPro.bin index 89b1a9b3..554a9b49 100755 Binary files a/DSView/res/DSLogicPro.bin and b/DSView/res/DSLogicPro.bin differ diff --git a/DSView/res/DSLogicU2Basic.bin b/DSView/res/DSLogicU2Basic.bin new file mode 100755 index 00000000..0ab24151 Binary files /dev/null and b/DSView/res/DSLogicU2Basic.bin differ diff --git a/DSView/res/license.txt b/DSView/res/license.txt old mode 100644 new mode 100755 index 8ccdad02..185d8df3 --- a/DSView/res/license.txt +++ b/DSView/res/license.txt @@ -1,20 +1,20 @@ -Copyright (c) 2017 DreamSourceLab - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +Copyright (c) 2017 DreamSourceLab + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/DSView/res/virtual-demo1.dsc b/DSView/res/virtual-demo1.dsc new file mode 100755 index 00000000..18a8d82b --- /dev/null +++ b/DSView/res/virtual-demo1.dsc @@ -0,0 +1,69 @@ +{ + "Device": "virtual-demo", + "DeviceMode": 1, + "Language": 25, + "Pattern mode": "Sine", + "Sample count": "10000", + "Sample rate": "100000000", + "Version": 2, + "channel": [ + { + "colour": "#eeb211", + "coupling": 1, + "enabled": true, + "index": 0, + "name": "0", + "trigValue": 0.5, + "type": 10001, + "vdiv": 1000, + "vfactor": 1, + "zeroPos": 0.37401574803149606 + }, + { + "colour": "#009925", + "coupling": 1, + "enabled": true, + "index": 1, + "name": "1", + "trigValue": 0.5, + "type": 10001, + "vdiv": 1000, + "vfactor": 1, + "zeroPos": 0.62598425196850394 + } + ], + "decoder": [ + ], + "measure": [ + { + "index": 0, + "site": 0, + "type": 1 + }, + { + "index": 0, + "site": 1, + "type": 17 + }, + { + "index": 0, + "site": 2, + "type": 18 + }, + { + "index": 1, + "site": 5, + "type": 1 + }, + { + "index": 1, + "site": 6, + "type": 17 + }, + { + "index": 1, + "site": 7, + "type": 18 + } + ] +} diff --git a/DSView/stylesheet.qss b/DSView/stylesheet.qss old mode 100644 new mode 100755 index 5751426e..8375d1d0 --- a/DSView/stylesheet.qss +++ b/DSView/stylesheet.qss @@ -1,284 +1,284 @@ -palette { - background: rgb(17, 133, 209, 255); - disabled: rgb(200, 200, 200, 255); -} - -QMainWindow { - icon-size: 48px, 48px; -} - -QDialog { - border: none; - background: rgb(17, 133, 209, 255); -} - -QToolBar { - border: none; - /*background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 rgb(0, 75, 165, 255), - stop: 0.05 rgb(31, 164, 227, 255), - stop: 0.5 rgb(17, 133, 209, 255), - stop: 0.95 rgb(0, 102, 185, 255), - stop: 1 rgb(0, 75, 165, 255));*/ - background: rgb(17, 133, 209, 255); - min-height: 50px; -} - -pv--view--View, -pv--view--Viewport{ - margin: 0px; - border: none; - background-color: rgb(255, 255, 255); - padding: 0px; -} - -QToolButton { - border: none; - border-style: flat; - color: white; - font: bold 10ft; - min-height: 50px; - min-width: 50px; -} - -QPushButton:hover, QPushButton:pressed, -QToolButton:hover, QToolButton:pressed { - background-color: rgb(238, 178, 17, 200); -} - -QPushButton:checked, -QToolButton:checked { - background-color: rgb(255, 255, 255, 50); -} - -QPushButton { - padding: 3px; - border: none; - border-style: flat; - border-radius: 4px; - color: white; - background-color: rgb(255, 255, 255, 50); - font: bold 10ft; - min-height: 20px; - min-width: 20px; -} - -/* >>> QToolBar: QLineEdit/QComboBox */ -QLineEdit, -QComboBox:!editable, -QSpinBox { - border: none; - border-radius: 4px; - background-color: white; - padding: 3px; - min-height: 20px; -} - -QLineEdit:disabled, -QComboBox:disabled, -QSpinBox:disabled { - background-color: rgb(200, 200, 200, 255); -} - -QComboBox:!editable { - padding-right: 5px; -} - -/* QComboBox gets the "on" state when the popup is open */ -QComboBox:!editable:on, QToolBar > QComboBox::drop-down:editable:on { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #D3D3D3, stop: 0.4 #D8D8D8, - stop: 0.5 #DDDDDD, stop: 1.0 #E1E1E1); -} - -QComboBox:on { /* shift the text when the popup opens */ - padding-top: 3px; - padding-left: 4px; -} - -QComboBox::drop-down { - subcontrol-origin: padding; - subcontrol-position: top right; - width: 20px; - - border-left-width: 0px; - border-top-right-radius: 4px; /* same radius as the QComboBox */ - border-bottom-right-radius: 4px; -} - -QComboBox::down-arrow { - image: url(:/icons/down-arrow.png); -} -QComboBox::down-arrow:disabled { - image: none; -} - -QComboBox::down-arrow:on { /* shift the arrow when popup is open */ - top: 1px; - left: 1px; -} -/* <<< QToolBar: QLineEdit/QComboBox */ - -/* >>> QDockWidget */ -QDockWidget { - margin: 0px; - background-color: rgb(17, 133, 209, 255); - border: 0px; - padding: 0px; - color: rgb(17, 133, 209, 255); - font-size: 15px; - font-weight: bold; -} - -QDockWidget::title { - margin: 0px; - text-align: left center; - background-color: rgb(255, 255, 255, 255); - border: 0px; - color: white; - padding: 8px; -} -QDockWidget > QWidget{ - margin: 0px; - background-color: rgb(17, 133, 209, 255); - border: 0px; - padding: 0px; -} - -QScrollArea #measureWidget, -QScrollArea #dsoTriggerWidget, -QScrollArea #triggerWidget, -QScrollArea #protocolWidget{ - margin: 0px; - background-color: rgb(17, 133, 209, 255); - border: 0px; - padding: 0px; -} - -QGroupBox { - margin: 0px; - background-color: rgb(17, 133, 209, 255); - border: 0px; - padding: 40px, 10px, 10px, 10px; - color: white; - font-size: 15px; - font-weight: bold; -} - -QGroupBox::title -{ - subcontrol-origin: margin; - subcontrol-position: top center; - padding: 5 20px; -} -QGroupBox:disabled -{ - color: rgb(200, 200, 200, 255); -} - -#triggerWidget > QTabWidget::pane{ - margin: 0px; - background-color: rgb(17, 133, 209, 255); - border: 1px solid rgb(255, 255, 255); - padding: 0px; -} - -#triggerWidget > QTabWidget::pane:disabled{ - border: 1px solid rgb(200, 200, 200, 255); -} - -QTabWidget::pane{ - margin: 0px; - background-color: rgb(17, 133, 209, 255); - border: 0px solid rgb(255, 255, 255); - padding: 0px; -} - -QCheckBox, -QRadioButton, -QLabel { - border: none; - min-height: 20px; -} - -QCheckBox::checked, -QRadioButton::checked { - color: black; -} - -QLabel, -QCheckBox::unchecked, -QRadioButton::unchecked { - color: white; -} - -QCheckBox, -QLabel { - padding: 1px 1px 1px 1px; - margin: 0px; -} - - -QLabel:disabled { - color: rgb(200, 200, 200, 255); -} - -QSlider::groove:horizontal { - border: 1px solid #999999; - height: 2px; - margin: 0px 0; - left: 10px; right: 10px; - border-radius: 4px; -} - -QSlider::handle:horizontal { - border-image:url(:/icons/slider-handle.png); - margin-left: -12px; - margin-right: -12px; - margin-top: -11px; - margin-bottom: -11px; -} - -QSlider::sub-page:horizontal{ - background: qlineargradient(spread:pad, - x1:0, y1:1, x2:0, y2:0, - stop:0 rgba(17, 133, 209, 255), - stop:0.25 rgba(238, 178, 17, 255), - stop:0.75 rgba(238, 178, 17, 255), - stop:1 rgba(17, 133, 209, 255)); - height: 2px; - border-radius: 4px; -} - -QSlider::add-page:horizontal{ - background: qlineargradient(spread:pad, - x1:0, y1:1, x2:0, y2:0, - stop:0 rgba(17, 133, 209, 255), - stop:0.25 rgba(255, 255, 255, 255), - stop:0.75 rgba(255, 255, 255, 255), - stop:1 rgba(17, 133, 209, 255)); - height: 2px; - border-radius: 4px; -} - -/* <<< QTableView */ -QHeaderView::section { - background-color: #646464; - padding: 4px; - font-size: 14pt; - border-style: none; - border-bottom: 1px solid #fffff8; - border-right: 1px solid #fffff8; -} - -QHeaderView::section:horizontal -{ - border-top: 1px solid #fffff8; -} - -QHeaderView::section:vertical -{ - border-left: 1px solid #fffff8; -} - -/* <<< QDockWidget */ +palette { + background: rgb(17, 133, 209, 255); + disabled: rgb(200, 200, 200, 255); +} + +QMainWindow { + icon-size: 48px, 48px; +} + +QDialog { + border: none; + background: rgb(17, 133, 209, 255); +} + +QToolBar { + border: none; + /*background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 rgb(0, 75, 165, 255), + stop: 0.05 rgb(31, 164, 227, 255), + stop: 0.5 rgb(17, 133, 209, 255), + stop: 0.95 rgb(0, 102, 185, 255), + stop: 1 rgb(0, 75, 165, 255));*/ + background: rgb(17, 133, 209, 255); + padding: 0px; +} + +pv--view--View, +pv--view--Viewport{ + margin: 0px; + border: none; + background-color: rgb(255, 255, 255); + padding: 0px; +} + +QToolButton { + border: none; + border-style: flat; + padding: 0px; + margin: 0px; + color: white; + font: bold 9ft; +} + +QPushButton:hover, QPushButton:pressed, +QToolButton:hover, QToolButton:pressed { + background-color: rgb(238, 178, 17, 200); +} + +QPushButton:checked, +QToolButton:checked { + background-color: rgb(255, 255, 255, 50); +} + +QPushButton { + padding: 3px; + border: none; + border-style: flat; + border-radius: 4px; + color: white; + background-color: rgb(255, 255, 255, 50); + font: bold 10ft; + min-height: 20px; + min-width: 20px; +} + +/* >>> QToolBar: QLineEdit/QComboBox */ +QLineEdit, +QComboBox:!editable, +QSpinBox { + border: none; + border-radius: 4px; + background-color: white; + padding: 3px; + min-height: 20px; +} + +QLineEdit:disabled, +QComboBox:disabled, +QSpinBox:disabled { + background-color: rgb(200, 200, 200, 255); +} + +QComboBox:!editable { + padding-right: 5px; +} + +/* QComboBox gets the "on" state when the popup is open */ +QComboBox:!editable:on, QToolBar > QComboBox::drop-down:editable:on { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #D3D3D3, stop: 0.4 #D8D8D8, + stop: 0.5 #DDDDDD, stop: 1.0 #E1E1E1); +} + +QComboBox:on { /* shift the text when the popup opens */ + padding-top: 3px; + padding-left: 4px; +} + +QComboBox::drop-down { + subcontrol-origin: padding; + subcontrol-position: top right; + width: 20px; + + border-left-width: 0px; + border-top-right-radius: 4px; /* same radius as the QComboBox */ + border-bottom-right-radius: 4px; +} + +QComboBox::down-arrow { + image: url(:/icons/down-arrow.png); +} +QComboBox::down-arrow:disabled { + image: none; +} + +QComboBox::down-arrow:on { /* shift the arrow when popup is open */ + top: 1px; + left: 1px; +} +/* <<< QToolBar: QLineEdit/QComboBox */ + +/* >>> QDockWidget */ +QDockWidget { + margin: 0px; + background-color: rgb(17, 133, 209, 255); + border: 0px; + padding: 0px; + color: rgb(17, 133, 209, 255); + font-size: 15px; + font-weight: bold; +} + +QDockWidget::title { + margin: 0px; + text-align: left center; + background-color: rgb(255, 255, 255, 255); + border: 0px; + color: white; + padding: 8px; +} +QDockWidget > QWidget{ + margin: 0px; + background-color: rgb(17, 133, 209, 255); + border: 0px; + padding: 0px; +} + +QScrollArea #measureWidget, +QScrollArea #dsoTriggerWidget, +QScrollArea #triggerWidget, +QScrollArea #protocolWidget{ + margin: 0px; + background-color: rgb(17, 133, 209, 255); + border: 0px; + padding: 0px; +} + +QGroupBox { + margin: 0px; + background-color: rgb(17, 133, 209, 255); + border: 0px; + padding: 40px, 10px, 10px, 10px; + color: white; + font-size: 15px; + font-weight: bold; +} + +QGroupBox::title +{ + subcontrol-origin: margin; + subcontrol-position: top center; + padding: 5 20px; +} +QGroupBox:disabled +{ + color: rgb(200, 200, 200, 255); +} + +#triggerWidget > QTabWidget::pane{ + margin: 0px; + background-color: rgb(17, 133, 209, 255); + border: 1px solid rgb(255, 255, 255); + padding: 0px; +} + +#triggerWidget > QTabWidget::pane:disabled{ + border: 1px solid rgb(200, 200, 200, 255); +} + +QTabWidget::pane{ + margin: 0px; + background-color: rgb(17, 133, 209, 255); + border: 0px solid rgb(255, 255, 255); + padding: 0px; +} + +QCheckBox, +QRadioButton, +QLabel { + border: none; + min-height: 20px; +} + +QCheckBox::checked, +QRadioButton::checked { + color: black; +} + +QLabel, +QCheckBox::unchecked, +QRadioButton::unchecked { + color: white; +} + +QCheckBox, +QLabel { + padding: 1px 1px 1px 1px; + margin: 0px; +} + + +QLabel:disabled { + color: rgb(200, 200, 200, 255); +} + +QSlider::groove:horizontal { + border: 1px solid #999999; + height: 2px; + margin: 0px 0; + left: 10px; right: 10px; + border-radius: 4px; +} + +QSlider::handle:horizontal { + border-image:url(:/icons/slider-handle.png); + margin-left: -12px; + margin-right: -12px; + margin-top: -11px; + margin-bottom: -11px; +} + +QSlider::sub-page:horizontal{ + background: qlineargradient(spread:pad, + x1:0, y1:1, x2:0, y2:0, + stop:0 rgba(17, 133, 209, 255), + stop:0.25 rgba(238, 178, 17, 255), + stop:0.75 rgba(238, 178, 17, 255), + stop:1 rgba(17, 133, 209, 255)); + height: 2px; + border-radius: 4px; +} + +QSlider::add-page:horizontal{ + background: qlineargradient(spread:pad, + x1:0, y1:1, x2:0, y2:0, + stop:0 rgba(17, 133, 209, 255), + stop:0.25 rgba(255, 255, 255, 255), + stop:0.75 rgba(255, 255, 255, 255), + stop:1 rgba(17, 133, 209, 255)); + height: 2px; + border-radius: 4px; +} + +/* <<< QTableView */ +QHeaderView::section { + background-color: #646464; + padding: 4px; + font-size: 14pt; + border-style: none; + border-bottom: 1px solid #fffff8; + border-right: 1px solid #fffff8; +} + +QHeaderView::section:horizontal +{ + border-top: 1px solid #fffff8; +} + +QHeaderView::section:vertical +{ + border-left: 1px solid #fffff8; +} + +/* <<< QDockWidget */ diff --git a/DSView/test/CMakeLists.txt b/DSView/test/CMakeLists.txt old mode 100644 new mode 100755 diff --git a/DSView/test/data/analogsnapshot.cpp b/DSView/test/data/analogsnapshot.cpp old mode 100644 new mode 100755 diff --git a/DSView/test/data/logicsnapshot.cpp b/DSView/test/data/logicsnapshot.cpp old mode 100644 new mode 100755 diff --git a/DSView/test/test.cpp b/DSView/test/test.cpp old mode 100644 new mode 100755 diff --git a/DSView/themes/LICENSE.md b/DSView/themes/LICENSE.md new file mode 100755 index 00000000..ef010153 --- /dev/null +++ b/DSView/themes/LICENSE.md @@ -0,0 +1,26 @@ +The MIT License (MIT) +===================== + +Copyright © `<2013-2014>` `` +Copyright © `<2015-2016>` `` + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the “Software”), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/DSView/themes/README.md b/DSView/themes/README.md new file mode 100755 index 00000000..fd2c406e --- /dev/null +++ b/DSView/themes/README.md @@ -0,0 +1,96 @@ +BreezeStyleSheets +================= + +Breeze and BreezeDark-like stylesheets for Qt Applications. + +C++ Installation +================ + +Copy `breeze.qrc`, `dark.qss`, `light.qss` and the `dark` and `light` folders into your project directory and add the qrc file to your project file. + +For example: + +```qmake +TARGET = app +SOURCES = main.cpp +RESOURCES = breeze.qrc +``` + +To load the stylesheet in C++, load the file using QFile and read the data. For example, to load BreezeDark, run: + +```cpp + +#include +#include +#include + + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + // set stylesheet + QFile file(":/dark.qss"); + file.open(QFile::ReadOnly | QFile::Text); + QTextStream stream(&file); + app.setStyleSheet(stream.readAll()); + + // code goes here + + return app.exec(); +} +``` + +PyQt5 Installation +================== + +To compile the stylesheet for use with PyQt5, compile with the following command `pyrcc5 breeze.qrc -o breeze_resources.py`, and import the stylesheets. Afterwards, to load the stylesheet in Python, load the file using QFile and read the data. For example, to load BreezeDark, run: + + +```python + +from PyQt5 import QtWidgets +from PyQt5.QtCore import QFile, QTextStream +import breeze_resources + + +def main(): + app = QtWidgets.QApplication(sys.argv) + + # set stylesheet + file = QFile(":/dark.qss") + file.open(QFile.ReadOnly | QFile.Text) + stream = QTextStream(file) + app.setStyleSheet(stream.readAll()) + + # code goes here + + app.exec_() +} +``` + +License +======= + +MIT, see [license](/LICENSE.md). + +Example +======= + +**Breeze/BreezeDark** + +Example user interface using the Breeze and BreezeDark stylesheets side-by-side. + +![BreezeDark](/assets/Breeze.gif) + +Acknowledgements +================ + +BreezeStyleSheets is a fork of [QDarkStyleSheet](https://github.com/ColinDuquesnoy/QDarkStyleSheet). + +Contact +======= + +Email: ahuszagh@gmail.com +Twitter: KardOnIce + diff --git a/DSView/themes/breeze.qrc b/DSView/themes/breeze.qrc new file mode 100755 index 00000000..620630a5 --- /dev/null +++ b/DSView/themes/breeze.qrc @@ -0,0 +1,98 @@ + + + light/hmovetoolbar.svg + light/vmovetoolbar.svg + light/hsepartoolbar.svg + light/vsepartoolbars.svg + light/stylesheet-branch-end.svg + light/stylesheet-branch-end-closed.svg + light/stylesheet-branch-end-open.svg + light/stylesheet-vline.svg + light/stylesheet-branch-more.svg + light/branch_closed.svg + light/branch_closed-on.svg + light/branch_open.svg + light/branch_open-on.svg + light/down_arrow.svg + light/down_arrow_disabled.svg + light/down_arrow-hover.svg + light/left_arrow.svg + light/left_arrow_disabled.svg + light/right_arrow.svg + light/right_arrow_disabled.svg + light/up_arrow.svg + light/up_arrow_disabled.svg + light/up_arrow-hover.svg + light/sizegrip.svg + light/transparent.svg + light/close.svg + light/close-hover.svg + light/close-pressed.svg + light/undock.svg + light/undock-hover.svg + dark/hmovetoolbar.svg + dark/vmovetoolbar.svg + dark/hsepartoolbar.svg + dark/vsepartoolbars.svg + dark/stylesheet-branch-end.svg + dark/stylesheet-branch-end-closed.svg + dark/stylesheet-branch-end-open.svg + dark/stylesheet-vline.svg + dark/stylesheet-branch-more.svg + dark/branch_closed.svg + dark/branch_closed-on.svg + dark/branch_open.svg + dark/branch_open-on.svg + dark/down_arrow.svg + dark/down_arrow_disabled.svg + dark/down_arrow-hover.svg + dark/left_arrow.svg + dark/left_arrow_disabled.svg + dark/right_arrow.svg + dark/right_arrow_disabled.svg + dark/up_arrow.svg + dark/up_arrow_disabled.svg + dark/up_arrow-hover.svg + dark/sizegrip.svg + dark/transparent.svg + dark/close.svg + dark/close-hover.svg + dark/close-pressed.svg + dark/undock.svg + dark/undock-hover.svg + light.qss + dark.qss + light/checkbox_checked.svg + light/checkbox_checked_disabled.svg + light/checkbox_checked-hover.svg + light/checkbox_indeterminate.svg + light/checkbox_indeterminate_disabled.svg + light/checkbox_indeterminate-hover.svg + light/checkbox_unchecked.svg + light/checkbox_unchecked_disabled.svg + light/checkbox_unchecked-hover.svg + dark/checkbox_checked.svg + dark/checkbox_checked-hover.svg + dark/checkbox_indeterminate.svg + dark/checkbox_indeterminate-hover.svg + dark/checkbox_unchecked.svg + dark/checkbox_unchecked-hover.svg + dark/checkbox_checked_disabled.svg + dark/checkbox_indeterminate_disabled.svg + dark/checkbox_unchecked_disabled.svg + dark/radio_checked.svg + dark/radio_checked_disabled.svg + dark/radio_checked-hover.svg + dark/radio_unchecked.svg + dark/radio_unchecked_disabled.svg + dark/radio_unchecked-hover.svg + light/radio_checked.svg + light/radio_checked_disabled.svg + light/radio_checked-hover.svg + light/radio_unchecked.svg + light/radio_unchecked_disabled.svg + light/radio_unchecked-hover.svg + light/mode_down_arrow.svg + dark/mode_down_arrow.svg + + diff --git a/DSView/themes/dark.qss b/DSView/themes/dark.qss new file mode 100755 index 00000000..9c4e9d88 --- /dev/null +++ b/DSView/themes/dark.qss @@ -0,0 +1,1615 @@ +/* + * DSView dark stylesheet. + * --------------------------------------------------------------------- + * The MIT License (MIT) + * + * Copyright (c) <2013-2014> + * Copyright (C) 2019 DreamSourceLab + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * --------------------------------------------------------------------- + */ + +QToolTip +{ + border: 1px solid #eff0f1; + background-color: #262626; + alternate-background-color: #3b4045; + color: #eff0f1; + padding: 1px; + opacity: 200; +} + +QWidget +{ + color: #eff0f1; + background-color: #262626; + selection-background-color:#1185D1; + selection-color: #eff0f1; + background-clip: border; + image: none; + border: 0px transparent; + outline: 0; +} + +/* +QWidget:item:hover +{ + background-color: #1185D1; + color: #eff0f1; +} + +QWidget:item:selected +{ + background-color: #1185D1; +} +*/ + +QCheckBox +{ + spacing: 0px; + outline: none; + color: #eff0f1; + margin-bottom: 2px; + opacity: 200; +} + +QCheckBox:disabled +{ + color: #454545; +} + +QGroupBox::indicator +{ + width: 18px; + height: 18px; + margin-left: 2px; +} + +QCheckBox::indicator:unchecked, +QCheckBox::indicator:unchecked:focus +{ + image: url(:/dark/checkbox_unchecked.svg); +} + +QCheckBox::indicator:unchecked:hover, +QCheckBox::indicator:unchecked:pressed, +QGroupBox::indicator:unchecked:hover, +QGroupBox::indicator:unchecked:focus, +QGroupBox::indicator:unchecked:pressed +{ + border: none; + image: url(:/dark/checkbox_unchecked-hover.svg); +} + +QCheckBox::indicator:checked +{ + image: url(:/dark/checkbox_checked.svg); +} + +QCheckBox::indicator:checked:hover, +QCheckBox::indicator:checked:focus, +QCheckBox::indicator:checked:pressed, +QGroupBox::indicator:checked:hover, +QGroupBox::indicator:checked:focus, +QGroupBox::indicator:checked:pressed +{ + border: none; + image: url(:/dark/checkbox_checked-hover.svg); +} + +QCheckBox::indicator:indeterminate +{ + image: url(:/dark/checkbox_indeterminate.svg); +} + +QCheckBox::indicator:indeterminate:focus, +QCheckBox::indicator:indeterminate:hover +QCheckBox::indicator:indeterminate:pressed +{ + image: url(:/dark/checkbox_indeterminate-hover.svg); +} + +QCheckBox::indicator:indeterminate:disabled +{ + image: url(:/dark/checkbox_indeterminate_disabled.svg); +} + +QCheckBox::indicator:checked:disabled, +QGroupBox::indicator:checked:disabled +{ + image: url(:/dark/checkbox_checked_disabled.svg); +} + +QCheckBox::indicator:unchecked:disabled, +QGroupBox::indicator:unchecked:disabled +{ + image: url(:/dark/checkbox_unchecked_disabled.svg); +} + +QRadioButton +{ + spacing: 5px; + outline: none; + color: #eff0f1; + margin-bottom: 2px; +} + +QRadioButton:disabled +{ + color: #454545; +} +QRadioButton::indicator +{ + width: 10px; + height: 10px; +} + +QRadioButton::indicator:unchecked, +QRadioButton::indicator:unchecked:focus +{ + image: url(:/dark/radio_unchecked.svg); +} + + +QRadioButton::indicator:unchecked:hover, +QRadioButton::indicator:unchecked:pressed +{ + border: none; + outline: none; + image: url(:/dark/radio_unchecked-hover.svg); +} + + +QRadioButton::indicator:checked +{ + border: none; + outline: none; + image: url(:/dark/radio_checked.svg); +} + +QRadioButton::indicator:checked:hover, +QRadioButton::indicator:checked:focus, +QRadioButton::indicator:checked:pressed +{ + border: none; + outline: none; + image: url(:/dark/radio_checked-hover.svg); +} + +QRadioButton::indicator:checked:disabled +{ + outline: none; + image: url(:/dark/radio_checked_disabled.svg); +} + +QRadioButton::indicator:unchecked:disabled +{ + image: url(:/dark/radio_unchecked_disabled.svg); +} + +QMenuBar +{ + background-color: #262626; + color: #eff0f1; +} + +QMenuBar::item +{ + background: transparent; +} + +QMenuBar::item:selected +{ + background: transparent; + border: 1px transparent; +} + +QMenuBar::item:pressed +{ + border: 1px transparent; + background-color: #1185D1; + color: #eff0f1; + margin-bottom: -1px; + padding-bottom: 1px; +} + +QMenu +{ + border: 1px transparent; + color: #eff0f1; + margin: 0px; +} + + +QMenu::item +{ + padding: 5px 30px 5px 30px; + margin-left: 2px; + border: 1px solid transparent; /* reserve space for selection border */ +} + +QMenu::item:selected +{ + background-color: #1185D1; + color: #eff0f1; +} + +QMenu::separator +{ + height: 2px; + background: lightblue; + margin-left: 10px; + margin-right: 5px; +} + +QMenu::indicator { + width: 18px; + height: 18px; +} + +/* non-exclusive indicator = check box style indicator + (see QActionGroup::setExclusive) */ +QMenu::indicator:non-exclusive:unchecked +{ + image: url(:/dark/checkbox_unchecked_disabled.svg); +} + +QMenu::indicator:non-exclusive:unchecked:selected +{ + image: url(:/dark/checkbox_unchecked_disabled.svg); +} + +QMenu::indicator:non-exclusive:checked +{ + image: url(:/dark/checkbox_checked.svg); +} + +QMenu::indicator:non-exclusive:checked:selected +{ + image: url(:/dark/checkbox_checked.svg); +} + +/* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */ +QMenu::indicator:exclusive:unchecked +{ + image: url(:/dark/radio_unchecked_disabled.svg); +} + +QMenu::indicator:exclusive:unchecked:selected +{ + image: url(:/dark/radio_unchecked_disabled.svg); +} + +QMenu::indicator:exclusive:checked +{ + image: url(:/dark/radio_checked.svg); +} + +QMenu::indicator:exclusive:checked:selected +{ + image: url(:/dark/radio_checked.svg); +} + +QMenu::right-arrow +{ + margin: 5px; + image: url(:/dark/right_arrow.svg); +} + + +QWidget:disabled +{ + color: #454545; + background-color: #262626; +} + +QAbstractItemView +{ + alternate-background-color: #393939; + color: #eff0f1; + border: 1px transparent; + border-radius: 2px; + padding: 1px +} + +QTabWidget:focus, +QCheckBox:focus, +QRadioButton:focus, +QSlider:focus +{ + border: none; +} + +QLineEdit +{ + background-color: #202020; + padding: 2px; + border-style: solid; + border: 1px solid #393939; + border-radius: 2px; + color: #eff0f1; +} + +QGroupBox +{ + border: 1px solid #393939; + border-radius: 2px; + margin-top: 20px; +} + +QGroupBox::title +{ + subcontrol-origin: margin; + subcontrol-position: top center; + padding-left: 10px; + padding-right: 10px; + padding-top: 10px; +} + +QScrollBar:horizontal +{ + height: 12px; + margin: 3px 12px 3px 12px; + border: 1px transparent; + border-radius: 3px; + background-color: #202020; +} + +QScrollBar::handle:horizontal +{ + background-color: #5F5F5F; + min-width: 20px; + border-radius: 3px; +} + +QScrollBar::add-line:horizontal +{ + margin: 0px 3px 0px 3px; + image: url(:/dark/right_arrow_disabled.svg); + width: 10px; + height: 10px; + subcontrol-position: right; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:horizontal +{ + margin: 0px 3px 0px 3px; + image: url(:/dark/left_arrow_disabled.svg); + width: 10px; + height: 10px; + subcontrol-position: left; + subcontrol-origin: margin; +} + +QScrollBar::add-line:horizontal:hover, +QScrollBar::add-line:horizontal:on +{ + image: url(:/dark/right_arrow.svg); + width: 10px; + height: 10px; + subcontrol-position: right; + subcontrol-origin: margin; +} + + +QScrollBar::sub-line:horizontal:hover, +QScrollBar::sub-line:horizontal:on +{ + image: url(:/dark/left_arrow.svg); + width: 10px; + height: 10px; + subcontrol-position: left; + subcontrol-origin: margin; +} + +QScrollBar::up-arrow:horizontal, +QScrollBar::down-arrow:horizontal +{ + background: none; +} + + +QScrollBar::add-page:horizontal, +QScrollBar::sub-page:horizontal +{ + background: none; +} + +QScrollBar:vertical +{ + background-color: #202020; + width: 12px; + margin: 12px 3px 12px 3px; + border: 1px transparent; + border-radius: 3px; +} + +QScrollBar::handle:vertical +{ + background-color: #5F5F5F; + min-height: 20px; + border-radius: 3px; +} + +QScrollBar::sub-line:vertical +{ + margin: 3px 0px 3px 0px; + image: url(:/dark/up_arrow_disabled.svg); + height: 10px; + width: 10px; + subcontrol-position: top; + subcontrol-origin: margin; +} + +QScrollBar::add-line:vertical +{ + margin: 3px 0px 3px 0px; + image: url(:/dark/down_arrow_disabled.svg); + height: 10px; + width: 10px; + subcontrol-position: bottom; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:vertical:hover, +QScrollBar::sub-line:vertical:on +{ + + image: url(:/dark/up_arrow.svg); + height: 10px; + width: 10px; + subcontrol-position: top; + subcontrol-origin: margin; +} + + +QScrollBar::add-line:vertical:hover, +QScrollBar::add-line:vertical:on +{ + image: url(:/dark/down_arrow.svg); + height: 10px; + width: 10px; + subcontrol-position: bottom; + subcontrol-origin: margin; +} + +QScrollBar::up-arrow:vertical, +QScrollBar::down-arrow:vertical +{ + background: none; +} + + +QScrollBar::add-page:vertical, +QScrollBar::sub-page:vertical +{ + background: none; +} + +QTextEdit +{ + background-color: #202020; + color: #eff0f1; + border: 1px solid #393939; + margin: 0; +} + +QPlainTextEdit +{ + background-color: #232629;; + color: #eff0f1; + border-radius: 2px; + border: 1px solid #393939; +} + +QHeaderView::section +{ + background-color: #76797c; + color: #eff0f1; + padding-left: 4px; + border: 1px solid #76797c; +} + +QSizeGrip +{ + image: url(:/dark/sizegrip.svg); + width: 12px; + height: 12px; +} + +QMenu::separator +{ + height: 1px; + background-color: #76797c; + color: white; + padding-left: 4px; + margin-left: 10px; + margin-right: 5px; +} + +QFrame +{ + border-radius: 2px; + border: 1px solid #444; +} + +QFrame[frameShape="0"] +{ + border-radius: 2px; + border: 1px transparent; +} + +QStackedWidget +{ + border: 1px transparent; +} + +QToolBar +{ + border: 1px transparent; + background: 1px solid #262626; + font-weight: bold; + padding: 0px; +} + +QToolBar::handle:horizontal +{ + image: url(:/dark/hmovetoolbar.svg); + width = 16px; + height = 64px; +} + +QToolBar::handle:vertical +{ + image: url(:/dark/vmovetoolbar.svg); + width = 54px; + height = 10px; +} + +QToolBar::separator:horizontal +{ + image: url(:/dark/hsepartoolbar.svg); + width = 7px; + height = 63px; +} + +QToolBar::separator:vertical +{ + image: url(:/dark/vsepartoolbars.svg); + width = 63px; + height = 7px; +} + +QPushButton +{ + color: #eff0f1; + background-color: #262626; + border-width: 1px; + border-color: #131313; + border-style: solid; + padding: 5px; + border-radius: 5px; + outline: none; +} + +QPushButton:disabled +{ + background-color: #262626; + border-width: 1px; + border-color: #393939; + border-style: solid; + padding-top: 5px; + padding-bottom: 5px; + padding-left: 10px; + padding-right: 10px; + border-radius: 5px; + color: #454545; +} + +QPushButton:focus +{ + color: white; +} + +QPushButton:pressed +{ + background-color: #262626; + padding-top: -15px; + padding-bottom: -17px; +} + + +QPushButton:checked{ + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #262626, + stop: 0.5 #737373, + stop: 1 #262626); +} + +QPushButton:hover +{ + background-color: #262626; + border: 1px solid #1185D1; + color: #eff0f1; + padding-top: 6px; + padding-bottom: 4px; +} + +QPushButton:checked:hover +{ + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #262626, + stop: 0.5 #737373, + stop: 1 #262626); + border: 1px solid #1185D1; + color: #eff0f1; + padding-top: 6px; + padding-bottom: 4px; +} + +QComboBox:hover, +QAbstractSpinBox:hover, +QLineEdit:hover, +QTextEdit:hover, +QPlainTextEdit:hover, +QAbstractView:hover, +QTreeView:hover +{ + border: 1px solid #1185D1; + color: #eff0f1; +} + +QComboBox:on +{ + background-color: #626873; + padding-top: 3px; + padding-left: 4px; + selection-background-color: #4a4a4a; +} + +QComboBox +{ + selection-background-color: #3d8ec9; + background-color: #202020; + border-style: solid; + border: 1px solid #393939; + border-radius: 2px; + padding: 2px; + min-width: 30px; +} + + +QComboBox::drop-down +{ + subcontrol-origin: padding; + subcontrol-position: top right; + width: 10px; + + border-left-width: 0px; + border-left-color: darkgray; + border-left-style: solid; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} + +QComboBox::down-arrow +{ + image: url(:/dark/down_arrow_disabled.svg); +} + +QComboBox::down-arrow:on, +QComboBox::down-arrow:hover, +QComboBox::down-arrow:focus +{ + image: url(:/dark/down_arrow.svg); +} + +QAbstractSpinBox +{ + padding: 2px; + border: 1px solid #76797c; + background-color: #232629; + color: #eff0f1; + border-radius: 2px; + min-width: 60px; +} + +QAbstractSpinBox:up-button +{ + background-color: transparent; + subcontrol-origin: border; + subcontrol-position: center right; +} + +QAbstractSpinBox:down-button +{ + background-color: transparent; + subcontrol-origin: border; + subcontrol-position: center left; +} + +QAbstractSpinBox::up-arrow, +QAbstractSpinBox::up-arrow:disabled, +QAbstractSpinBox::up-arrow:off +{ + image: url(:/dark/up_arrow_disabled.svg); + width: 10px; + height: 10px; +} + +QAbstractSpinBox::up-arrow:hover +{ + image: url(:/dark/up_arrow.svg); +} + +QAbstractSpinBox::down-arrow, +QAbstractSpinBox::down-arrow:disabled, +QAbstractSpinBox::down-arrow:off +{ + image: url(:/dark/down_arrow_disabled.svg); + width: 10px; + height: 10px; +} + +QAbstractSpinBox::down-arrow:hover +{ + image: url(:/dark/down_arrow.svg); +} + +QLabel +{ + border: 0px solid black; + margin-left: 2px; + margin-right: 2px; +} + +/* BORDERS */ +QTabWidget::pane +{ + padding: 0px; + padding-right: 3px; + margin: 0px; +} + +QTabWidget::pane:top +{ + border: 1px transparent; + top: -1px; +} + +QTabWidget::pane:bottom +{ + border: 1px transparent; + bottom: -1px; +} + +QTabWidget::pane:left +{ + border: 1px transparent; + right: -1px; +} + +QTabWidget::pane:right +{ + border: 1px transparent; + left: -1px; +} + + +QTabBar +{ + qproperty-drawBase: 0; + margin: 0px; + padding: 0px; + border-radius: 3px; +} + +QTabBar:focus +{ + border: 0px transparent; +} + +QTabBar::close-button +{ + image: url(:/dark/close.svg); + background: transparent; +} + +QTabBar::close-button:hover +{ + image: url(:/dark/close-hover.svg); + width: 12px; + height: 12px; + background: transparent; +} + +QTabBar::close-button:pressed +{ + image: url(:/dark/close-pressed.svg); + width: 12px; + height: 12px; + background: transparent; +} + +QTabBar::tab +{ + color: white; + background-color: #3d8ec9; + padding: 5px; +} + +QTabBar::tab:!selected +{ + color: white; + background-color: #262626; +} + +QTabBar::tab:disabled +{ + color: #454545; + background-color: #262626; +} + +/* TOP TABS */ +QTabBar::tab:top +{ + border: 1px solid #4C4C4C; + border-bottom: 1px transparent; + border-top-left-radius: 2px; + border-top-right-radius: 2px; +} + +QTabBar::tab:top:!selected +{ + border: 1px solid #4C4C4C; + border-bottom: 1px transparent; + border-top-left-radius: 2px; + border-top-right-radius: 2px; +} + +QTabBar::tab:top:hover +{ + border: 1px solid #1185D1; + border-bottom: 1px transparent; +} + + +/* BOTTOM TABS */ +QTabBar::tab:bottom +{ + border: 1px solid #4C4C4C; + border-top: 1px transparent; + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; +} + +QTabBar::tab:bottom:!selected +{ + border: 1px solid #4C4C4C; + border-top: 1px transparent; + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; +} + +QTabBar::tab:bottom:hover +{ + border: 1px solid #1185D1; + border-top: 1px transparent; +} + + +/* LEFT TABS */ +QTabBar::tab:left +{ + border: 1px solid #4C4C4C; + border-left: 1px transparent; + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; +} + +QTabBar::tab:left:!selected +{ + border: 1px solid #4C4C4C; + border-left: 1px transparent; + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; +} + +QTabBar::tab:left:hover +{ + border: 1px solid #1185D1; + border-left: 1px transparent; +} + + +/* RIGHT TABS */ +QTabBar::tab:right +{ + border: 1px solid #4C4C4C; + border-right: 1px transparent; + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; +} + +QTabBar::tab:right:!selected +{ + border: 1px solid #4C4C4C; + border-right: 1px transparent; + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; +} + +QTabBar::tab:right:hover +{ + border: 1px solid #1185D1; + border-right: 1px transparent; +} + + +QTabBar QToolButton::right-arrow:enabled +{ + image: url(:/dark/right_arrow.svg); +} + +QTabBar QToolButton::left-arrow:enabled +{ + image: url(:/dark/left_arrow.svg); +} + +QTabBar QToolButton::right-arrow:disabled +{ + image: url(:/dark/right_arrow_disabled.svg); +} + +QTabBar QToolButton::left-arrow:disabled +{ + image: url(:/dark/left_arrow_disabled.svg); +} + +QDockWidget +{ + background: #262626; + border: 1px transparent; + titlebar-close-icon: url(:/dark/transparent.svg); + titlebar-normal-icon: url(:/dark/transparent.svg); +} + +QDockWidget::close-button, +QDockWidget::float-button +{ + border: 1px solid transparent; + border-radius: 2px; + background: transparent; +} + +QDockWidget::float-button +{ + image: url(:/dark/undock.svg); +} + +QDockWidget::float-button:hover +{ + image: url(:/dark/undock-hover.svg) ; +} + +QDockWidget::close-button +{ + image: url(:/dark/close.svg) ; +} + +QDockWidget::close-button:hover +{ + image: url(:/dark/close-hover.svg) ; +} + +QDockWidget::close-button:pressed +{ + image: url(:/dark/close-pressed.svg) ; +} + +QTreeView, +QListView +{ + border: 1px solid #76797c; + background-color: #262626; +} + +QTreeView::branch:has-siblings:!adjoins-item +{ + image: url(:/dark/stylesheet-vline.svg) 0; +} + +QTreeView::branch:has-siblings:adjoins-item +{ + image: url(:/dark/stylesheet-branch-more.svg) 0; +} + +QTreeView::branch:!has-children:!has-siblings:adjoins-item +{ + image: url(:/dark/stylesheet-branch-end.svg) 0; +} + +QTreeView::branch:has-children:!has-siblings:closed, +QTreeView::branch:closed:has-children:has-siblings +{ + image: url(:/dark/stylesheet-branch-end-closed.svg) 0; + image: url(:/dark/branch_closed.svg); +} + +QTreeView::branch:open:has-children:!has-siblings, +QTreeView::branch:open:has-children:has-siblings +{ + image: url(:/dark/stylesheet-branch-end-open.svg) 0; + image: url(:/dark/branch_open.svg); +} + + +QSlider::groove:horizontal +{ + border: 1px solid #262626; + height: 1px; + background: #808080; + margin: 0px; + border-radius: 1px; +} + +QSlider::handle:horizontal +{ + background: #262626; + border: 1px solid #1185D1; + width: 7px; + height: 7px; + margin: -4px 0; + border-radius: 4px; +} + +QSlider::groove:vertical +{ + border: 1px solid #262626; + width: 4px; + background: #808080; + margin: 0px; + border-radius: 3px; +} + +QSlider::handle:vertical +{ + background: #262626; + border: 1px solid #626568; + width: 16px; + height: 16px; + margin: 0 -8px; + border-radius: 9px; +} + +QSlider::handle:horizontal:hover, +QSlider::handle:vertical:hover +{ + background: #1185D1; +} + +QSlider::sub-page:horizontal, +QSlider::add-page:vertical +{ + background: #1185D1; + border-radius: 1px; +} + +QSlider::add-page:horizontal, +QSlider::sub-page:vertical +{ + background: #5F5F5F; + border-radius: 1px; +} + +QSlider::handle:disabled +{ + background: #262626; + border: 1px solid #76797C; +} + +QSlider::groove:disabled +{ + background: #76797C; + border: 1px solid #76797C; +} + +QSlider::add-page:disabled, +QSlider::sub-page:disabled +{ + background: #76797C; + border-radius: 1px; +} + +QToolButton#MaximizeButton { + background-color: transparent; + border-left: 1px solid QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #262626, stop: 0.3 #606060, + stop: 0.5 #707070, + stop: 0.7 #606060, stop: 1 #262626); + border-right: 1px solid QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #262626, stop: 0.3 #606060, + stop: 0.5 #707070, + stop: 0.7 #606060, stop: 1 #262626); + border-radius: 0px; + margin: 0px; + padding: 0px; +} + +QToolButton#MinimizeButton, +QToolButton#CloseButton { + background-color: transparent; + border: 1px transparent; + border-radius: 0px; + margin: 0px; + padding: 0px; +} + +QToolButton#FileCloseButton { + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #262626, stop: 0.1 #D50F25, + stop: 0.5 #D50F25, + stop: 0.9 #D50F25, stop: 1 #262626); + border: 1px transparent; + border-radius: 0px; + margin: 0px; + padding: 0px; +} + +QToolButton#MinimizeButton:hover, QToolButton#MinimizeButton::menu-button:hover, +QToolButton#MaximizeButton:hover, QToolButton#MaximizeButton::menu-button:hover{ + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #262626, stop: 0.4 #4E4D4D, + stop: 0.5 #4A4949, + stop: 0.6 #4E4D4D, stop: 1 #262626); +} + +QToolButton#CloseButton:hover, QToolButton#CloseButton::menu-button:hover { +background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #262626, stop: 0.2 #A82F2F, + stop: 0.5 #D50F25, + stop: 0.8 #A82F2F, stop: 1 #262626); +} + +QToolButton#FileCloseButton:hover, QToolButton#FileCloseButton::menu-button:hover { + padding-top: 2px; +} + +QToolButton +{ + background-color: transparent; + border: 0px; + border-radius: 2px; + margin: 0px; + padding-top: 0px; + padding-left: 2px; + padding-right: 2px; + padding-bottom: 2px; +} + +QToolButton[popupMode="1"] /* only for MenuButtonPopup */ +{ + padding-right: 20px; /* make way for the popup button */ + border: 0px; + border-radius: 5px; + margin: 0px; + padding-top: 0px; + padding-left: 2px; + padding-right: 2px; + padding-bottom: 2px; +} + +QToolButton[popupMode="2"] /* only for InstantPopup */ +{ + padding-right: 10px; /* make way for the popup button */ + border: 0px; + margin: 0px; + padding-top: 0px; + padding-left: 2px; + padding-right: 2px; + padding-bottom: 2px; +} + +QToolButton:hover, +QToolButton:checked:hover, +QToolButton::menu-button:hover +QToolButton::menu-button:checked:hover +{ + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #262626, stop: 0.4 #4F4F4F, + stop: 0.5 #4A4A4A, + stop: 0.6 #4F4F4F, stop: 1 #262626); + padding-top: 1px; + padding-left: 2px; + padding-right: 2px; + padding-bottom: 1px; +} + +QToolButton:checked, QToolButton:pressed, +QToolButton::menu-button:pressed { + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #262626, + stop: 0.5 #4A4A4A, + stop: 1.0 #262626); + padding-top: 2px; + padding-left: 2px; + padding-right: 2px; + padding-bottom: 0px; +} + + +/* the subcontrols below are used only in the MenuButtonPopup mode */ +QToolButton::menu-button +{ + border: 0px; + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + /* 16px width + 4px for border + no text = 20px allocated above */ + width: 16px; + outline: none; +} + +QToolButton::menu-arrow +{ + image: url(:/dark/down_arrow.svg); +} + +QToolButton::menu-arrow:open +{ + border: 1px solid #76797c; +} + +QToolButton#ModeButton::menu-arrow +{ + image: url(:/dark/mode_down_arrow.svg); +} + +/* the subcontrol below is used only in the InstantPopup or DelayedPopup mode */ +QToolButton::menu-indicator +{ + image: url(:/dark/down_arrow.svg); + top: -7px; + left: -2px; +} +QToolButton#ModeButton::menu-indicator +{ + image: url(:/dark/mode_down_arrow.svg); + top: -7px; + left: -3px; +} +QToolButton#ModeButton::menu-indicator:hover +{ + top: -5px; +} + +QPushButton::menu-indicator +{ + subcontrol-origin: padding; + subcontrol-position: bottom right; + left: 0px; +} + +QTableView +{ + border: 1px transparent; + gridline-color: #6c6c6c; + background-color: #202020; +} + + +QTableView, +QHeaderView +{ + border-radius: 0px; +} + +QTableView::item, +QListView::item, +QTreeView::item +{ + padding: 3px; +} + +QTableView::item:pressed, +QListView::item:pressed, +QTreeView::item:pressed +{ + background: #1185D1; + color: #eff0f1; +} + +QTableView::item:selected:active, +QTreeView::item:selected:active, +QListView::item:selected:active +{ + background: #1185D1; + color: #eff0f1; +} + +QTableView::item:hover, +QListView::item:hover, +QTreeView::item:hover +{ + border: 1px solid #1185D1; +} + +QHeaderView +{ + border: 1px transparent; + border-radius: 2px; + margin: 0px; + padding: 0px; +} + +QHeaderView::section +{ + background-color: #262626; + color: #eff0f1; + padding: 4px; + border: 1px transparent; + border-radius: 0px; + text-align: center; +} + +QHeaderView::section::vertical::first, +QHeaderView::section::vertical::only-one +{ + border-top: 1px transparent; +} + +QHeaderView::section::vertical +{ + border-top: transparent; +} + +QHeaderView::section::horizontal::first, +QHeaderView::section::horizontal::only-one +{ + border-left: 1px transparent; +} + +QHeaderView::section::horizontal +{ + background-color: #5F5F5F; + border-left: transparent; +} + + +QHeaderView::section:checked +{ + color: white; + background-color: #334e5e; +} + + /* style the sort indicator */ +QHeaderView::down-arrow +{ + image: url(:/dark/down_arrow.svg); +} + +QHeaderView::up-arrow +{ + image: url(:/dark/up_arrow.svg); +} + +QTableCornerButton::section +{ + background-color: #262626; + border: 1px transparent; + border-radius: 2px; +} + +QToolBox +{ + padding: 3px; + border: 1px transparent; +} + +QToolBox:selected +{ + background-color: #262626; + border-color: #1185D1; +} + +QToolBox:hover +{ + border-color: #1185D1; +} + +QStatusBar::item +{ + border: 0px transparent; +} + +QFrame[height="3"], +QFrame[width="3"] +{ + background-color: #76797c; +} + +QAbstractScrollArea +{ + border-radius: 2px; + border: 0px; + background-color: #262626; +} + +QSplitter::handle:horizontal, +QMainWindow::separator +{ + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #262626, + stop: 0.4 #333333, + stop: 0.5 #404040, + stop: 0.6 #333333, + stop: 1 #262626); + color: white; + padding-left: 0px; + spacing: 0px; + width: 3px; + border: 0px solid #202020; +} + +QSplitter::handle:horizontal:hover, +QMainWindow::separator:hover +{ + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #262626, + stop: 0.1 #333333, + stop: 0.5 #404040, + stop: 0.9 #333333, + stop: 1 #262626); + color: white; + padding-left: 0px; + spacing: 0px; + width: 3px; + border: 0px solid #202020; +} + +QSplitter::handle:vertical { + background-color: QLinearGradient( x1: 0, y1: 0, x2: 1, y2: 0, + stop: 0.0 #262626, + stop: 0.3 #505050, + stop: 0.5 #606060, + stop: 0.7 #505050, + stop: 1 #262626); + height: 3px; +} + +QSplitter::handle:vertical:hover { + background-color: QLinearGradient( x1: 0, y1: 0, x2: 1, y2: 0, + stop: 0.0 #262626, + stop: 0.1 #505050, + stop: 0.5 #606060, + stop: 0.8 #505050, + stop: 1 #262626); + height: 3px; +} + + +QProgressBar:horizontal +{ + background-color: #626568; + border: 1px solid #262626; + border-radius: 3px; + height: 5px; + text-align: right; + margin-top: 5px; + margin-bottom: 5px; + margin-right: 50px; + padding: 1px; +} + +QProgressBar::chunk:horizontal +{ + background-color: #1185D1; + border: 1px transparent; + border-radius: 3px; +} + +QSpinBox, +QDoubleSpinBox +{ + padding-right: 0px; + background-color: #202020; + border-style: solid; + border: 1px solid #393939; + border-radius: 2px; + color: #eff0f1; +} + +QSpinBox::up-button, +QDoubleSpinBox::up-button +{ + subcontrol-origin: content; + subcontrol-position: right top; + + width: 16px; + border-width: 1px; +} + +QSpinBox::up-arrow, +QDoubleSpinBox::up-arrow +{ + image: url(:/dark/up_arrow.svg); + width: 9px; + height: 6px; +} + +QSpinBox::up-arrow:hover, +QSpinBox::up-arrow:pressed, +QDoubleSpinBox::up-arrow:hover, +QDoubleSpinBox::up-arrow:pressed +{ + image: url(:/dark/up_arrow-hover.svg); + width: 9px; + height: 6px; +} + +QSpinBox::up-arrow:disabled, +QSpinBox::up-arrow:off, +QDoubleSpinBox::up-arrow:disabled, +QDoubleSpinBox::up-arrow:off +{ + image: url(:/dark/up_arrow_disabled.svg); +} + +QSpinBox::down-button, +QDoubleSpinBox::down-button +{ + subcontrol-origin: content; + subcontrol-position: right bottom; + + width: 16px; + border-width: 1px; +} + +QSpinBox::down-arrow, +QDoubleSpinBox::down-arrow +{ + image: url(:/dark/down_arrow.svg); + width: 9px; + height: 6px; +} + +QSpinBox::down-arrow:hover, +QSpinBox::down-arrow:pressed, +QDoubleSpinBox::down-arrow:hover, +QDoubleSpinBox::down-arrow:pressed +{ + image: url(:/dark/down_arrow-hover.svg); + width: 9px; + height: 6px; +} + +QSpinBox::down-arrow:disabled, +QSpinBox::down-arrow:off, +QDoubleSpinBox::down-arrow:disabled, +QDoubleSpinBox::down-arrow:off +{ + image: url(:/dark/down_arrow_disabled.svg); +} + +QTextBrowser:hover +{ + border: 1px transparent; +} diff --git a/DSView/themes/dark/branch_closed-on.svg b/DSView/themes/dark/branch_closed-on.svg new file mode 100755 index 00000000..8bd398f8 --- /dev/null +++ b/DSView/themes/dark/branch_closed-on.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/branch_closed.svg b/DSView/themes/dark/branch_closed.svg new file mode 100755 index 00000000..f5a072f4 --- /dev/null +++ b/DSView/themes/dark/branch_closed.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/branch_open-on.svg b/DSView/themes/dark/branch_open-on.svg new file mode 100755 index 00000000..4dd0c065 --- /dev/null +++ b/DSView/themes/dark/branch_open-on.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/branch_open.svg b/DSView/themes/dark/branch_open.svg new file mode 100755 index 00000000..0745890d --- /dev/null +++ b/DSView/themes/dark/branch_open.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/checkbox_checked-hover.svg b/DSView/themes/dark/checkbox_checked-hover.svg new file mode 100755 index 00000000..60fd81be --- /dev/null +++ b/DSView/themes/dark/checkbox_checked-hover.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/DSView/themes/dark/checkbox_checked.svg b/DSView/themes/dark/checkbox_checked.svg new file mode 100755 index 00000000..721964b9 --- /dev/null +++ b/DSView/themes/dark/checkbox_checked.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/DSView/themes/dark/checkbox_checked_disabled.svg b/DSView/themes/dark/checkbox_checked_disabled.svg new file mode 100755 index 00000000..f1ee941e --- /dev/null +++ b/DSView/themes/dark/checkbox_checked_disabled.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/DSView/themes/dark/checkbox_indeterminate-hover.svg b/DSView/themes/dark/checkbox_indeterminate-hover.svg new file mode 100755 index 00000000..603feacc --- /dev/null +++ b/DSView/themes/dark/checkbox_indeterminate-hover.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/DSView/themes/dark/checkbox_indeterminate.svg b/DSView/themes/dark/checkbox_indeterminate.svg new file mode 100755 index 00000000..fefb88da --- /dev/null +++ b/DSView/themes/dark/checkbox_indeterminate.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/DSView/themes/dark/checkbox_indeterminate_disabled.svg b/DSView/themes/dark/checkbox_indeterminate_disabled.svg new file mode 100755 index 00000000..0580f0e8 --- /dev/null +++ b/DSView/themes/dark/checkbox_indeterminate_disabled.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/DSView/themes/dark/checkbox_unchecked-hover.svg b/DSView/themes/dark/checkbox_unchecked-hover.svg new file mode 100755 index 00000000..7bcf984a --- /dev/null +++ b/DSView/themes/dark/checkbox_unchecked-hover.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/DSView/themes/dark/checkbox_unchecked.svg b/DSView/themes/dark/checkbox_unchecked.svg new file mode 100755 index 00000000..f3ffb701 --- /dev/null +++ b/DSView/themes/dark/checkbox_unchecked.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/DSView/themes/dark/checkbox_unchecked_disabled.svg b/DSView/themes/dark/checkbox_unchecked_disabled.svg new file mode 100755 index 00000000..7b409f88 --- /dev/null +++ b/DSView/themes/dark/checkbox_unchecked_disabled.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/DSView/themes/dark/close-hover.svg b/DSView/themes/dark/close-hover.svg new file mode 100755 index 00000000..e2b0dd89 --- /dev/null +++ b/DSView/themes/dark/close-hover.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/close-pressed.svg b/DSView/themes/dark/close-pressed.svg new file mode 100755 index 00000000..a0dc2496 --- /dev/null +++ b/DSView/themes/dark/close-pressed.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/close.svg b/DSView/themes/dark/close.svg new file mode 100755 index 00000000..07b50c9e --- /dev/null +++ b/DSView/themes/dark/close.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/down_arrow-hover.svg b/DSView/themes/dark/down_arrow-hover.svg new file mode 100755 index 00000000..408397f9 --- /dev/null +++ b/DSView/themes/dark/down_arrow-hover.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/down_arrow.svg b/DSView/themes/dark/down_arrow.svg new file mode 100755 index 00000000..a50df001 --- /dev/null +++ b/DSView/themes/dark/down_arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/down_arrow_disabled.svg b/DSView/themes/dark/down_arrow_disabled.svg new file mode 100755 index 00000000..af74a307 --- /dev/null +++ b/DSView/themes/dark/down_arrow_disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/hmovetoolbar.svg b/DSView/themes/dark/hmovetoolbar.svg new file mode 100755 index 00000000..e4904db0 --- /dev/null +++ b/DSView/themes/dark/hmovetoolbar.svg @@ -0,0 +1,4 @@ + + + + diff --git a/DSView/themes/dark/hsepartoolbar.svg b/DSView/themes/dark/hsepartoolbar.svg new file mode 100755 index 00000000..89beb226 --- /dev/null +++ b/DSView/themes/dark/hsepartoolbar.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/left_arrow.svg b/DSView/themes/dark/left_arrow.svg new file mode 100755 index 00000000..9c787cec --- /dev/null +++ b/DSView/themes/dark/left_arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/left_arrow_disabled.svg b/DSView/themes/dark/left_arrow_disabled.svg new file mode 100755 index 00000000..2d749e72 --- /dev/null +++ b/DSView/themes/dark/left_arrow_disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/mode_down_arrow.svg b/DSView/themes/dark/mode_down_arrow.svg new file mode 100755 index 00000000..ce625f2a --- /dev/null +++ b/DSView/themes/dark/mode_down_arrow.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/DSView/themes/dark/radio_checked-hover.svg b/DSView/themes/dark/radio_checked-hover.svg new file mode 100755 index 00000000..b0d46a1a --- /dev/null +++ b/DSView/themes/dark/radio_checked-hover.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/DSView/themes/dark/radio_checked.svg b/DSView/themes/dark/radio_checked.svg new file mode 100755 index 00000000..b24c1535 --- /dev/null +++ b/DSView/themes/dark/radio_checked.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/DSView/themes/dark/radio_checked_disabled.svg b/DSView/themes/dark/radio_checked_disabled.svg new file mode 100755 index 00000000..851801c9 --- /dev/null +++ b/DSView/themes/dark/radio_checked_disabled.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/DSView/themes/dark/radio_unchecked-hover.svg b/DSView/themes/dark/radio_unchecked-hover.svg new file mode 100755 index 00000000..6b68d35f --- /dev/null +++ b/DSView/themes/dark/radio_unchecked-hover.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/DSView/themes/dark/radio_unchecked.svg b/DSView/themes/dark/radio_unchecked.svg new file mode 100755 index 00000000..d0347c5c --- /dev/null +++ b/DSView/themes/dark/radio_unchecked.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/DSView/themes/dark/radio_unchecked_disabled.svg b/DSView/themes/dark/radio_unchecked_disabled.svg new file mode 100755 index 00000000..6ca02d80 --- /dev/null +++ b/DSView/themes/dark/radio_unchecked_disabled.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/DSView/themes/dark/right_arrow.svg b/DSView/themes/dark/right_arrow.svg new file mode 100755 index 00000000..b793513f --- /dev/null +++ b/DSView/themes/dark/right_arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/right_arrow_disabled.svg b/DSView/themes/dark/right_arrow_disabled.svg new file mode 100755 index 00000000..4940025e --- /dev/null +++ b/DSView/themes/dark/right_arrow_disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/sizegrip.svg b/DSView/themes/dark/sizegrip.svg new file mode 100755 index 00000000..3388f07d --- /dev/null +++ b/DSView/themes/dark/sizegrip.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/spinup_disabled.svg b/DSView/themes/dark/spinup_disabled.svg new file mode 100755 index 00000000..838436d0 --- /dev/null +++ b/DSView/themes/dark/spinup_disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/stylesheet-branch-end-closed.svg b/DSView/themes/dark/stylesheet-branch-end-closed.svg new file mode 100755 index 00000000..eb73b13a --- /dev/null +++ b/DSView/themes/dark/stylesheet-branch-end-closed.svg @@ -0,0 +1,4 @@ + + + + diff --git a/DSView/themes/dark/stylesheet-branch-end-open.svg b/DSView/themes/dark/stylesheet-branch-end-open.svg new file mode 100755 index 00000000..eb73b13a --- /dev/null +++ b/DSView/themes/dark/stylesheet-branch-end-open.svg @@ -0,0 +1,4 @@ + + + + diff --git a/DSView/themes/dark/stylesheet-branch-end.svg b/DSView/themes/dark/stylesheet-branch-end.svg new file mode 100755 index 00000000..334ca0cd --- /dev/null +++ b/DSView/themes/dark/stylesheet-branch-end.svg @@ -0,0 +1,4 @@ + + + + diff --git a/DSView/themes/dark/stylesheet-branch-more.svg b/DSView/themes/dark/stylesheet-branch-more.svg new file mode 100755 index 00000000..f5250ba3 --- /dev/null +++ b/DSView/themes/dark/stylesheet-branch-more.svg @@ -0,0 +1,4 @@ + + + + diff --git a/DSView/themes/dark/stylesheet-vline.svg b/DSView/themes/dark/stylesheet-vline.svg new file mode 100755 index 00000000..4e7ff6aa --- /dev/null +++ b/DSView/themes/dark/stylesheet-vline.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/transparent.svg b/DSView/themes/dark/transparent.svg new file mode 100755 index 00000000..3a8ca5cf --- /dev/null +++ b/DSView/themes/dark/transparent.svg @@ -0,0 +1 @@ + diff --git a/DSView/themes/dark/undock-hover.svg b/DSView/themes/dark/undock-hover.svg new file mode 100755 index 00000000..6bddbd72 --- /dev/null +++ b/DSView/themes/dark/undock-hover.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/DSView/themes/dark/undock.svg b/DSView/themes/dark/undock.svg new file mode 100755 index 00000000..9ab21971 --- /dev/null +++ b/DSView/themes/dark/undock.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/up_arrow-hover.svg b/DSView/themes/dark/up_arrow-hover.svg new file mode 100755 index 00000000..dd1271a5 --- /dev/null +++ b/DSView/themes/dark/up_arrow-hover.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/up_arrow.svg b/DSView/themes/dark/up_arrow.svg new file mode 100755 index 00000000..9f42239b --- /dev/null +++ b/DSView/themes/dark/up_arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/up_arrow_disabled.svg b/DSView/themes/dark/up_arrow_disabled.svg new file mode 100755 index 00000000..742e1c54 --- /dev/null +++ b/DSView/themes/dark/up_arrow_disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/dark/vmovetoolbar.svg b/DSView/themes/dark/vmovetoolbar.svg new file mode 100755 index 00000000..0a30d45e --- /dev/null +++ b/DSView/themes/dark/vmovetoolbar.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/DSView/themes/dark/vsepartoolbars.svg b/DSView/themes/dark/vsepartoolbars.svg new file mode 100755 index 00000000..00e91ab8 --- /dev/null +++ b/DSView/themes/dark/vsepartoolbars.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/DSView/themes/light.qss b/DSView/themes/light.qss new file mode 100755 index 00000000..f6277182 --- /dev/null +++ b/DSView/themes/light.qss @@ -0,0 +1,1654 @@ +/* + * DSView light stylesheet. + * --------------------------------------------------------------------- + * The MIT License (MIT) + * + * Copyright (c) <2013-2014> + * Copyright (C) 2019 DreamSourceLab + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * --------------------------------------------------------------------- + */ + +QToolTip +{ + background-color: black; + color: white; + padding: 5px; +} + +QWidget +{ + color: #2A2A2A; + background-color: #F8F8F8; + selection-background-color:#1185D1; + selection-color: #2A2A2A; + background-clip: border; + image: none; + border: 0px transparent; + outline: 0; +} + +/* +QWidget:item:hover +{ + background-color: #1185D1; + color: #2A2A2A; +} + +QWidget:item:selected +{ + background-color: #1185D1; +} +*/ + +QCheckBox +{ + spacing: 0px; + outline: none; + color: #2A2A2A; + margin-bottom: 2px; + opacity: 200; +} + +QCheckBox:disabled +{ + color: #b4b4b4; +} + +QGroupBox::indicator +{ + width: 18px; + height: 18px; + margin-left: 2px; +} + +QCheckBox::indicator:unchecked, +QCheckBox::indicator:unchecked:focus +{ + image: url(:/light/checkbox_unchecked.svg); +} + +QCheckBox::indicator:unchecked:hover, +QCheckBox::indicator:unchecked:pressed, +QGroupBox::indicator:unchecked:hover, +QGroupBox::indicator:unchecked:focus, +QGroupBox::indicator:unchecked:pressed +{ + border: none; + image: url(:/light/checkbox_unchecked-hover.svg); +} + +QCheckBox::indicator:checked +{ + image: url(:/light/checkbox_checked.svg); +} + +QCheckBox::indicator:checked:hover, +QCheckBox::indicator:checked:focus, +QCheckBox::indicator:checked:pressed, +QGroupBox::indicator:checked:hover, +QGroupBox::indicator:checked:focus, +QGroupBox::indicator:checked:pressed +{ + border: none; + image: url(:/light/checkbox_checked-hover.svg); +} + +QCheckBox::indicator:indeterminate +{ + image: url(:/light/checkbox_indeterminate.svg); +} + +QCheckBox::indicator:indeterminate:focus, +QCheckBox::indicator:indeterminate:hover +QCheckBox::indicator:indeterminate:pressed +{ + image: url(:/light/checkbox_indeterminate-hover.svg); +} + +QCheckBox::indicator:indeterminate:disabled +{ + image: url(:/light/checkbox_indeterminate_disabled.svg); +} + +QCheckBox::indicator:checked:disabled, +QGroupBox::indicator:checked:disabled +{ + image: url(:/light/checkbox_checked_disabled.svg); +} + +QCheckBox::indicator:unchecked:disabled, +QGroupBox::indicator:unchecked:disabled +{ + image: url(:/light/checkbox_unchecked_disabled.svg); +} + +QRadioButton +{ + spacing: 5px; + outline: none; + color: #2A2A2A; + margin-bottom: 2px; +} + +QRadioButton:disabled +{ + color: #b4b4b4; +} +QRadioButton::indicator +{ + width: 10px; + height: 10px; +} + +QRadioButton::indicator:unchecked, +QRadioButton::indicator:unchecked:focus +{ + image: url(:/light/radio_unchecked.svg); +} + +QRadioButton::indicator:unchecked:hover, +QRadioButton::indicator:unchecked:pressed +{ + border: none; + outline: none; + image: url(:/light/radio_unchecked-hover.svg); +} + +QRadioButton::indicator:checked +{ + border: none; + outline: none; + image: url(:/light/radio_checked.svg); +} + +QRadioButton::indicator:checked:hover, +QRadioButton::indicator:checked:focus, +QRadioButton::indicator:checked:pressed +{ + border: none; + outline: none; + image: url(:/light/radio_checked-hover.svg); +} + +QRadioButton::indicator:checked:disabled +{ + outline: none; + image: url(:/light/radio_checked_disabled.svg); +} + +QRadioButton::indicator:unchecked:disabled +{ + image: url(:/light/radio_unchecked_disabled.svg); +} + +QMenuBar +{ + background-color: #F8F8F8; + color: #2A2A2A; +} + +QMenuBar::item +{ + background: transparent; +} + +QMenuBar::item:selected +{ + background: transparent; + border: 1px transparent; +} + +QMenuBar::item:pressed +{ + border: 1px transparent; + background-color: #1185D1; + color: #2A2A2A; + margin-bottom: -1px; + padding-bottom: 1px; +} + +QMenu +{ + border: 1px transparent; + color: #2A2A2A; + margin: 0px; +} + + +QMenu::item +{ + padding: 5px 30px 5px 30px; + margin-left: 2px; + border: 1px solid transparent; /* reserve space for selection border */ +} + +QMenu::item:selected +{ + background-color: #1185D1; + color: #2A2A2A; +} + +QMenu::separator +{ + height: 2px; + background: lightblue; + margin-left: 10px; + margin-right: 5px; +} + +QMenu::indicator { + width: 18px; + height: 18px; +} + +/* non-exclusive indicator = check box style indicator + (see QActionGroup::setExclusive) */ +QMenu::indicator:non-exclusive:unchecked +{ + image: url(:/light/checkbox_unchecked_disabled.svg); +} + +QMenu::indicator:non-exclusive:unchecked:selected +{ + image: url(:/light/checkbox_unchecked_disabled.svg); +} + +QMenu::indicator:non-exclusive:checked +{ + image: url(:/light/checkbox_checked.svg); +} + +QMenu::indicator:non-exclusive:checked:selected +{ + image: url(:/light/checkbox_checked.svg); +} + +/* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */ +QMenu::indicator:exclusive:unchecked +{ + image: url(:/light/radio_unchecked_disabled.svg); +} + +QMenu::indicator:exclusive:unchecked:selected +{ + image: url(:/light/radio_unchecked_disabled.svg); +} + +QMenu::indicator:exclusive:checked +{ + image: url(:/light/radio_checked.svg); +} + +QMenu::indicator:exclusive:checked:selected +{ + image: url(:/light/radio_checked.svg); +} + +QMenu::right-arrow +{ + margin: 5px; + image: url(:/light/right_arrow.svg); +} + + +QWidget:disabled +{ + color: #b4b4b4; + background-color: #F8F8F8; +} + +QAbstractItemView +{ + alternate-background-color: #E0E0E0; + color: #2A2A2A; + border: 1px transparent; + border-radius: 2px; + padding: 1px +} + +QTabWidget:focus, +QCheckBox:focus, +QRadioButton:focus, +QSlider:focus +{ + border: none; +} + +QLineEdit +{ + background-color: #F8F8F8; + padding: 2px; + border-style: solid; + border: 1px solid #2A2A2A; + border-radius: 2px; + color: #2A2A2A; +} + +QGroupBox +{ + border: 1px solid #2A2A2A; + border-radius: 2px; + margin-top: 20px; +} + +QGroupBox:disabled +{ + border: 1px solid #b4b4b4; +} + +QGroupBox::title +{ + subcontrol-origin: margin; + subcontrol-position: top center; + padding-left: 10px; + padding-right: 10px; + padding-top: 10px; +} + +QScrollBar:horizontal +{ + height: 12px; + margin: 3px 12px 3px 12px; + border: 1px transparent; + border-radius: 3px; + background-color: #F8F8F8; +} + +QScrollBar::handle:horizontal +{ + background-color: #808080; + min-width: 20px; + border-radius: 3px; +} + +QScrollBar::add-line:horizontal +{ + margin: 0px 3px 0px 3px; + image: url(:/light/right_arrow_disabled.svg); + width: 10px; + height: 10px; + subcontrol-position: right; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:horizontal +{ + margin: 0px 3px 0px 3px; + image: url(:/light/left_arrow_disabled.svg); + width: 10px; + height: 10px; + subcontrol-position: left; + subcontrol-origin: margin; +} + +QScrollBar::add-line:horizontal:hover, +QScrollBar::add-line:horizontal:on +{ + image: url(:/light/right_arrow.svg); + width: 10px; + height: 10px; + subcontrol-position: right; + subcontrol-origin: margin; +} + + +QScrollBar::sub-line:horizontal:hover, +QScrollBar::sub-line:horizontal:on +{ + image: url(:/light/left_arrow.svg); + width: 10px; + height: 10px; + subcontrol-position: left; + subcontrol-origin: margin; +} + +QScrollBar::up-arrow:horizontal, +QScrollBar::down-arrow:horizontal +{ + background: none; +} + + +QScrollBar::add-page:horizontal, +QScrollBar::sub-page:horizontal +{ + background: none; +} + +QScrollBar:vertical +{ + background-color: #F8F8F8; + width: 12px; + margin: 12px 3px 12px 3px; + border: 1px transparent; + border-radius: 3px; +} + +QScrollBar::handle:vertical +{ + background-color: #808080; + min-height: 20px; + border-radius: 3px; +} + +QScrollBar::sub-line:vertical +{ + margin: 3px 0px 3px 0px; + image: url(:/light/up_arrow_disabled.svg); + height: 10px; + width: 10px; + subcontrol-position: top; + subcontrol-origin: margin; +} + +QScrollBar::add-line:vertical +{ + margin: 3px 0px 3px 0px; + image: url(:/light/down_arrow_disabled.svg); + height: 10px; + width: 10px; + subcontrol-position: bottom; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:vertical:hover, +QScrollBar::sub-line:vertical:on +{ + + image: url(:/light/up_arrow.svg); + height: 10px; + width: 10px; + subcontrol-position: top; + subcontrol-origin: margin; +} + + +QScrollBar::add-line:vertical:hover, +QScrollBar::add-line:vertical:on +{ + image: url(:/light/down_arrow.svg); + height: 10px; + width: 10px; + subcontrol-position: bottom; + subcontrol-origin: margin; +} + +QScrollBar::up-arrow:vertical, +QScrollBar::down-arrow:vertical +{ + background: none; +} + + +QScrollBar::add-page:vertical, +QScrollBar::sub-page:vertical +{ + background: none; +} + +QTextEdit +{ + background-color: #F8F8F8; + color: #2A2A2A; + border: 1px solid #2A2A2A; + margin: 0; +} + +QPlainTextEdit +{ + background-color: #F8F8F8; + color: #2A2A2A; + border-radius: 2px; + border: 1px solid #2A2A2A; +} + +QHeaderView::section +{ + background-color: #2A2A2A; + color: #2A2A2A; + padding-left: 4px; + border: 1px solid #2A2A2A; +} + +QSizeGrip +{ + image: url(:/light/sizegrip.svg); + width: 12px; + height: 12px; +} + +QMenu::separator +{ + height: 1px; + background-color: #2A2A2A; + color: white; + padding-left: 4px; + margin-left: 10px; + margin-right: 5px; +} + +QFrame +{ + border-radius: 2px; + border: 1px transparent; +} + +QFrame[frameShape="0"] +{ + border-radius: 2px; + border: 1px transparent; +} + +QStackedWidget +{ + border: 1px transparent; +} + +QToolBar +{ + border: 1px transparent; + background: transparent; + font-weight: bold; + padding: 0px; +} + +QToolBar::handle:horizontal +{ + image: url(:/light/hmovetoolbar.svg); + width = 16px; + height = 64px; +} + +QToolBar::handle:vertical +{ + image: url(:/light/vmovetoolbar.svg); + width = 54px; + height = 10px; +} + +QToolBar::separator:horizontal +{ + image: url(:/light/hsepartoolbar.svg); + width = 7px; + height = 63px; +} + +QToolBar::separator:vertical +{ + image: url(:/light/vsepartoolbars.svg); + width = 63px; + height = 7px; +} + +QPushButton +{ + color: #2A2A2A; + background-color: #F8F8F8; + border-width: 1px; + border-color: #2A2A2A; + border-style: solid; + padding: 5px; + border-radius: 5px; + outline: none; +} + +QPushButton:disabled +{ + background-color: #e0e1e2; + border-width: 1px; + border-color: #b4b4b4; + border-style: solid; + padding-top: 5px; + padding-bottom: 5px; + padding-left: 10px; + padding-right: 10px; + border-radius: 5px; + color: #b4b4b4; +} + +QPushButton:focus +{ + color: black; +} + +QPushButton:pressed +{ + background-color: #F8F8F8; + padding-top: -15px; + padding-bottom: -17px; +} + + +QPushButton:checked{ + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #F8F8F8, + stop: 0.5 #A3A3A3, + stop: 1 #F8F8F8); +} + +QPushButton:hover +{ + background-color: #F8F8F8; + border: 1px solid #1185D1; + color: #2A2A2A; + padding-top: 6px; + padding-bottom: 4px; +} + +QPushButton:checked:hover +{ + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #F8F8F8, + stop: 0.5 #A3A3A3, + stop: 1 #F8F8F8); + border: 1px solid #1185D1; + color: #2A2A2A; + padding-top: 6px; + padding-bottom: 4px; +} + +QComboBox:hover, +QAbstractSpinBox:hover, +QLineEdit:hover, +QTextEdit:hover, +QPlainTextEdit:hover, +QAbstractView:hover, +QTreeView:hover +{ + border: 1px solid #1185D1; + color: #2A2A2A; +} + +QComboBox:hover:pressed, +QPushButton:hover:pressed, +QAbstractSpinBox:hover:pressed, +QLineEdit:hover:pressed, +QTextEdit:hover:pressed, +QPlainTextEdit:hover:pressed, +QAbstractView:hover:pressed, +QTreeView:hover:pressed +{ + background-color: #F8F8F8; +} + +QComboBox:disabled, +QAbstractSpinBox:disabled, +QLineEdit:disabled, +QTextEdit:disabled, +QPlainTextEdit:disabled, +QAbstractView:disabled, +QTreeView:disabled +{ + border: 1px solid #b4b4b4; +} + +QComboBox:on +{ + padding-top: 3px; + padding-left: 4px; + selection-background-color: #4a4a4a; +} + +QComboBox +{ + selection-background-color: #1185D1; + background-color: #F8F8F8; + border-style: solid; + border: 1px solid #2A2A2A; + border-radius: 2px; + padding: 2px; + min-width: 30px; +} + +QComboBox QAbstractItemView +{ + background-color: #F8F8F8; + border-radius: 2px; + border: 1px solid #2A2A2A; + selection-background-color: #1185D1; +} + +QComboBox::drop-down +{ + subcontrol-origin: padding; + subcontrol-position: top right; + width: 10px; + + border-left-width: 0px; + border-left-color: darkgray; + border-left-style: solid; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} + +QComboBox::down-arrow +{ + image: url(:/light/down_arrow_disabled.svg); +} + +QComboBox::down-arrow:on, +QComboBox::down-arrow:hover, +QComboBox::down-arrow:focus +{ + image: url(:/light/down_arrow.svg); +} + +QAbstractSpinBox +{ + padding: 2px; + border: 1px solid #2A2A2A; + background-color: #D9D8D7; + color: #2A2A2A; + border-radius: 2px; + min-width: 60px; +} + +QAbstractSpinBox:up-button +{ + background-color: transparent; + subcontrol-origin: border; + subcontrol-position: center right; +} + +QAbstractSpinBox:down-button +{ + background-color: transparent; + subcontrol-origin: border; + subcontrol-position: center left; +} + +QAbstractSpinBox::up-arrow, +QAbstractSpinBox::up-arrow:disabled, +QAbstractSpinBox::up-arrow:off +{ + image: url(:/light/up_arrow_disabled.svg); + width: 10px; + height: 10px; +} + +QAbstractSpinBox::up-arrow:hover +{ + image: url(:/light/up_arrow.svg); +} + +QAbstractSpinBox::down-arrow, +QAbstractSpinBox::down-arrow:disabled, +QAbstractSpinBox::down-arrow:off +{ + image: url(:/light/down_arrow_disabled.svg); + width: 10px; + height: 10px; +} + +QAbstractSpinBox::down-arrow:hover +{ + image: url(:/light/down_arrow.svg); +} + +QLabel +{ + border: 0px solid black; + margin-left: 2px; + margin-right: 2px; +} + +/* BORDERS */ +QTabWidget::pane +{ + padding: 0px; + padding-right: 3px; + margin: 0px; +} + +QTabWidget::pane:top +{ + border: 1px transparent; + top: -1px; +} + +QTabWidget::pane:bottom +{ + border: 1px transparent; + bottom: -1px; +} + +QTabWidget::pane:left +{ + border: 1px transparent; + right: -1px; +} + +QTabWidget::pane:right +{ + border: 1px transparent; + left: -1px; +} + +QTabBar +{ + qproperty-drawBase: 0; + margin: 0px; + padding: 0px; + border-radius: 3px; +} + +QTabBar:focus +{ + border: 0px transparent; +} + +QTabBar::close-button +{ + image: url(:/light/close.svg); + background: transparent; +} + +QTabBar::close-button:hover +{ + image: url(:/light/close-hover.svg); + width: 12px; + height: 12px; + background: transparent; +} + +QTabBar::close-button:pressed +{ + image: url(:/light/close-pressed.svg); + width: 12px; + height: 12px; + background: transparent; +} + +QTabBar::tab +{ + color: white; + background-color: #3d8ec9; + padding: 5px; +} + +QTabBar::tab:!selected +{ + color: white; + background-color: #F8F8F8; +} + +QTabBar::tab:disabled +{ + color: #b4b4b4; + background-color: #F8F8F8; +} + +/* TOP TABS */ +QTabBar::tab:top +{ + border: 1px solid #b4b4b4; + border-bottom: 1px transparent; + border-top-left-radius: 2px; + border-top-right-radius: 2px; +} + +QTabBar::tab:top:!selected +{ + border: 1px solid #b4b4b4; + border-bottom: 1px transparent; + border-top-left-radius: 2px; + border-top-right-radius: 2px; +} + +QTabBar::tab:top:hover +{ + border: 1px solid #1185D1; + border-bottom: 1px transparent; +} + + +/* BOTTOM TABS */ +QTabBar::tab:bottom +{ + border: 1px solid #b4b4b4; + border-top: 1px transparent; + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; +} + +QTabBar::tab:bottom:!selected +{ + border: 1px solid #b4b4b4; + border-top: 1px transparent; + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; +} + +QTabBar::tab:bottom:hover +{ + border: 1px solid #1185D1; + border-top: 1px transparent; +} + + +/* LEFT TABS */ +QTabBar::tab:left +{ + border: 1px solid #b4b4b4; + border-left: 1px transparent; + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; +} + +QTabBar::tab:left:!selected +{ + border: 1px solid #b4b4b4; + border-left: 1px transparent; + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; +} + +QTabBar::tab:left:hover +{ + border: 1px solid #1185D1; + border-left: 1px transparent; +} + + +/* RIGHT TABS */ +QTabBar::tab:right +{ + border: 1px solid #b4b4b4; + border-right: 1px transparent; + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; +} + +QTabBar::tab:right:!selected +{ + border: 1px solid #b4b4b4; + border-right: 1px transparent; + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; +} + +QTabBar::tab:right:hover +{ + border: 1px solid #1185D1; + border-right: 1px transparent; +} + + +QTabBar QToolButton::right-arrow:enabled +{ + image: url(:/light/right_arrow.svg); +} + +QTabBar QToolButton::left-arrow:enabled +{ + image: url(:/light/left_arrow.svg); +} + +QTabBar QToolButton::right-arrow:disabled +{ + image: url(:/light/right_arrow_disabled.svg); +} + +QTabBar QToolButton::left-arrow:disabled +{ + image: url(:/light/left_arrow_disabled.svg); +} + +QDockWidget +{ + background: #F8F8F8; + border: 1px transparent; + titlebar-close-icon: url(:/light/transparent.svg); + titlebar-normal-icon: url(:/light/transparent.svg); +} + +QDockWidget::close-button, +QDockWidget::float-button +{ + border: 1px solid transparent; + border-radius: 2px; + background: transparent; +} + +QDockWidget::float-button +{ + image: url(:/dark/undock.svg); +} + +QDockWidget::float-button:hover +{ + image: url(:/dark/undock-hover.svg) ; +} + +QDockWidget::close-button +{ + image: url(:/dark/close.svg) ; +} + +QDockWidget::close-button:hover +{ + image: url(:/dark/close-hover.svg) ; +} + +QDockWidget::close-button:pressed +{ + image: url(:/dark/close-pressed.svg) ; +} + +QTreeView, +QListView +{ + border: 1px solid #2A2A2A; + background-color: #F8F8F8; +} + + +QTreeView::branch:has-siblings:!adjoins-item +{ + image: url(:/light/stylesheet-vline.svg) 0; +} + +QTreeView::branch:has-siblings:adjoins-item +{ + image: url(:/light/stylesheet-branch-more.svg) 0; +} + +QTreeView::branch:!has-children:!has-siblings:adjoins-item +{ + image: url(:/light/stylesheet-branch-end.svg) 0; +} + +QTreeView::branch:has-children:!has-siblings:closed, +QTreeView::branch:closed:has-children:has-siblings +{ + image: url(:/light/stylesheet-branch-end-closed.svg) 0; + image: url(:/light/branch_closed.svg); +} + +QTreeView::branch:open:has-children:!has-siblings, +QTreeView::branch:open:has-children:has-siblings +{ + image: url(:/light/stylesheet-branch-end-open.svg) 0; + image: url(:/light/branch_open.svg); +} + +QTableView::item, +QListView::item, +QTreeView::item +{ + padding: 3px; +} + +QTableView::item:!selected:hover, +QListView::item:!selected:hover, +QTreeView::item:!selected:hover +{ + background-color: rgba(61, 173, 232, 0.1); + outline: 0; + color: #2A2A2A; + padding: 3px; +} + +QSlider::groove:horizontal +{ + border: 1px solid #F8F8F8; + height: 1px; + background: #808080; + margin: 0px; + border-radius: 1px; +} + +QSlider::handle:horizontal +{ + background: #F8F8F8; + border: 1px solid #1185D1; + width: 7px; + height: 7px; + margin: -4px 0; + border-radius: 4px; +} + +QSlider::groove:vertical +{ + border: 1px solid #F8F8F8; + width: 4px; + background: #808080; + margin: 0px; + border-radius: 3px; +} + +QSlider::handle:vertical +{ + background: #F8F8F8; + border: 1px solid #1185D1; + width: 16px; + height: 16px; + margin: 0 -8px; + border-radius: 9px; +} + +QSlider::handle:horizontal:hover, +QSlider::handle:vertical:hover +{ + background: #1185D1; +} + +QSlider::sub-page:horizontal, +QSlider::add-page:vertical +{ + background: #1185D1; + border-radius: 1px; +} + +QSlider::add-page:horizontal, +QSlider::sub-page:vertical +{ + background: #808080; + border-radius: 1px; +} + +QSlider::handle:disabled +{ + background: #F8F8F8; + border: 1px solid #B4B4B4; +} + +QSlider::groove:disabled +{ + background: #B4B4B4; + border: 1px solid #B4B4B4; +} + +QSlider::add-page:disabled, +QSlider::sub-page:disabled +{ + background: #B4B4B4; + border-radius: 1px; +} + +QToolButton#MaximizeButton { + background-color: transparent; + border-left: 1px solid QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #F8F8F8, stop: 0.3 #606060, + stop: 0.5 #707070, + stop: 0.7 #606060, stop: 1 #F8F8F8); + border-right: 1px solid QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #F8F8F8, stop: 0.3 #606060, + stop: 0.5 #707070, + stop: 0.7 #606060, stop: 1 #F8F8F8); + border-radius: 0px; + margin: 0px; + padding: 0px; +} + +QToolButton#MinimizeButton, +QToolButton#CloseButton { + background-color: transparent; + border: 1px transparent; + border-radius: 0px; + margin: 0px; + padding: 0px; +} + +QToolButton#FileCloseButton { + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #F8F8F8, stop: 0.1 #D50F25, + stop: 0.5 #D50F25, + stop: 0.9 #D50F25, stop: 1 #F8F8F8); + border: 1px transparent; + border-radius: 0px; + margin: 0px; + padding: 0px; +} + +QToolButton#MinimizeButton:hover, QToolButton#MinimizeButton::menu-button:hover, +QToolButton#MaximizeButton:hover, QToolButton#MaximizeButton::menu-button:hover{ + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #F8F8F8, stop: 0.4 #4E4D4D, + stop: 0.5 #4A4949, + stop: 0.6 #4E4D4D, stop: 1 #F8F8F8); +} + +QToolButton#CloseButton:hover, QToolButton#CloseButton::menu-button:hover { +background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #F8F8F8, stop: 0.1 #D50F25, + stop: 0.5 #D50F25, + stop: 0.9 #D50F25, stop: 1 #F8F8F8); +} + +QToolButton#FileCloseButton:hover, QToolButton#FileCloseButton::menu-button:hover { + padding-top: 2px; +} + +QToolButton +{ + background-color: transparent; + border: 0px; + border-radius: 0px; + margin: 0px; + padding-top: 0px; + padding-left: 2px; + padding-right: 2px; + padding-bottom: 2px; +} + +QToolButton[popupMode="1"] /* only for MenuButtonPopup */ +{ + padding-right: 20px; /* make way for the popup button */ + border: 0px; + border-radius: 0px; + margin: 0px; + padding-top: 0px; + padding-left: 2px; + padding-right: 2px; + padding-bottom: 2px; +} + +QToolButton[popupMode="2"] /* only for InstantPopup */ +{ + padding-right: 10px; /* make way for the popup button */ + border: 0px; + margin: 0px; + padding-top: 0px; + padding-left: 2px; + padding-right: 2px; + padding-bottom: 2px; +} + +QToolButton:hover, +QToolButton::menu-button:hover +{ + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #F8F8F8, stop: 0.9 #F8F8F8, + stop: 0.95 #1185D1, stop: 1 #1185D1); + padding-top: 1px; + padding-left: 2px; + padding-right: 2px; + padding-bottom: 1px; +} + +QToolButton:checked, QToolButton:pressed, +QToolButton::menu-button:pressed { + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #F8F8F8, stop: 0.9 #F8F8F8, + stop: 0.95 #1185D1, stop: 1 #1185D1); + padding-top: 2px; + padding-left: 2px; + padding-right: 2px; + padding-bottom: 0px; +} + + +/* the subcontrols below are used only in the MenuButtonPopup mode */ +QToolButton::menu-button +{ + border: 0px; + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + /* 16px width + 4px for border + no text = 20px allocated above */ + width: 16px; + outline: none; +} + +QToolButton::menu-arrow +{ + image: url(:/light/down_arrow.svg); +} +QToolButton::menu-arrow:open +{ + border: 1px solid #2A2A2A; +} + +QToolButton#ModeButton::menu-arrow +{ + image: url(:/light/mode_down_arrow.svg); +} + +/* the subcontrol below is used only in the InstantPopup or DelayedPopup mode */ +QToolButton::menu-indicator +{ + image: url(:/light/down_arrow.svg); + top: -7px; + left: -2px; +} + +QToolButton#ModeButton::menu-indicator +{ + image: url(:/light/mode_down_arrow.svg); + top: -7px; + left: -3px; +} +QToolButton#ModeButton::menu-indicator:hover +{ + top: -5px; +} + +QPushButton::menu-indicator +{ + subcontrol-origin: padding; + subcontrol-position: bottom right; + left: 0px; +} + +QTableView +{ + border: 1px transparent; + gridline-color: #2A2A2A; + background-color: #FCFCFC; +} + + +QTableView, +QHeaderView +{ + border-radius: 0px; +} + +QTableView::item:pressed +{ + background: #1185D1; + color: #2A2A2A; +} + +QTableView::item:selected:active +{ + background: #1185D1; + color: #2A2A2A; +} + +QTableView::item:selected:hover +{ + background-color: #47b8f3; + color: #2A2A2A; +} + +QListView::item:pressed, +QTreeView::item:pressed +{ + background: #1185D1; + color: #2A2A2A; +} + +QTreeView::item:selected:active, +QListView::item:selected:active +{ + background: #1185D1; + color: #2A2A2A; +} + +QTableView::item:hover, +QListView::item:hover, +QTreeView::item:hover +{ + border: 1px solid #1185D1; +} + + +QHeaderView +{ + border: 1px transparent; + border-radius: 2px; + margin: 0px; + padding: 0px; +} + +QHeaderView::section +{ + background-color: #F8F8F8; + color: #2A2A2A; + padding: 4px; + border: 1px transparent; + border-radius: 0px; + text-align: center; +} + +QHeaderView::section::vertical::first, +QHeaderView::section::vertical::only-one +{ + border-top: 1px transparent; +} + +QHeaderView::section::vertical +{ + border-top: transparent; +} + +QHeaderView::section::horizontal::first, +QHeaderView::section::horizontal::only-one +{ + border-left: 1px transparent; +} + +QHeaderView::section::horizontal +{ + background-color: #D0D0D0; + border-left: transparent; +} + + +QHeaderView::section:checked + + { + color: black; + background-color: #b9dae7; + } + + /* style the sort indicator */ +QHeaderView::down-arrow +{ + image: url(:/light/down_arrow.svg); +} + +QHeaderView::up-arrow +{ + image: url(:/light/up_arrow.svg); +} + +QTableCornerButton::section +{ + background-color: #F8F8F8; + border: 1px transparent; + border-radius: 0px; +} + +QToolBox +{ + padding: 3px; + border: 1px transparent; +} + +QToolBox:selected +{ + background-color: #F8F8F8; + border-color: #1185D1; +} + +QToolBox:hover +{ + border-color: #1185D1; +} + +QStatusBar::item +{ + border: 0px transparent; +} + +QSplitter::handle:horizontal +QMainWindow::separator +{ + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #F8F8F8, + stop: 0.4 #D0D0D0, + stop: 0.5 #808080, + stop: 0.6 #D0D0D0, + stop: 1 #F8F8F8); + color: white; + padding-left: 0px; + spacing: 0px; + width: 2px; + border: 0px solid #202020; +} + +QSplitter::handle:horizontal:hover, +QMainWindow::separator:hover +{ + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #F8F8F8, + stop: 0.1 #D0D0D0, + stop: 0.5 #808080, + stop: 0.9 #D0D0D0, + stop: 1 #F8F8F8); + color: white; + padding-left: 0px; + spacing: 0px; + width: 2px; + border: 0px solid #202020; +} + +QSplitter::handle:vertical { + background-color: QLinearGradient( x1: 0, y1: 0, x2: 1, y2: 0, + stop: 0.0 #F8F8F8, + stop: 0.3 #D0D0D0, + stop: 0.5 #808080, + stop: 0.7 #D0D0D0, + stop: 1 #F8F8F8); + height: 3px; +} + +QSplitter::handle:vertical:hover { + background-color: QLinearGradient( x1: 0, y1: 0, x2: 1, y2: 0, + stop: 0.0 #F8F8F8, + stop: 0.1 #D0D0D0, + stop: 0.5 #808080, + stop: 0.8 #D0D0D0, + stop: 1 #F8F8F8); + height: 3px; +} + + +QProgressBar:horizontal +{ + background-color: #BABEC2; + border: 1px solid #F8F8F8; + border-radius: 3px; + height: 5px; + text-align: right; + margin-top: 5px; + margin-bottom: 5px; + margin-right: 50px; + padding: 1px; +} + +QProgressBar::chunk:horizontal +{ + background-color: #1185D1; + border: 1px transparent; + border-radius: 3px; +} + +QAbstractSpinBox +{ + background-color: #F8F8F8; +} + +QSpinBox, +QDoubleSpinBox +{ + padding-right: 0px; +} + +QSpinBox::up-button, +QDoubleSpinBox::up-button +{ + subcontrol-origin: content; + subcontrol-position: right top; + + width: 16px; + border-width: 1px; +} + +QSpinBox::up-arrow, +QDoubleSpinBox::up-arrow +{ + image: url(:/light/up_arrow.svg); + width: 9px; + height: 6px; +} + +QSpinBox::up-arrow:hover, +QSpinBox::up-arrow:pressed, +QDoubleSpinBox::up-arrow:hover, +QDoubleSpinBox::up-arrow:pressed +{ + image: url(:/light/up_arrow-hover.svg); + width: 9px; + height: 6px; +} + +QSpinBox::up-arrow:disabled, +QSpinBox::up-arrow:off, +QDoubleSpinBox::up-arrow:disabled, +QDoubleSpinBox::up-arrow:off +{ + image: url(:/light/up_arrow_disabled.svg); +} + +QSpinBox::down-button, +QDoubleSpinBox::down-button +{ + subcontrol-origin: content; + subcontrol-position: right bottom; + + width: 16px; + border-width: 1px; +} + +QSpinBox::down-arrow, +QDoubleSpinBox::down-arrow +{ + image: url(:/light/down_arrow.svg); + width: 9px; + height: 6px; +} + +QSpinBox::down-arrow:hover, +QSpinBox::down-arrow:pressed, +QDoubleSpinBox::down-arrow:hover, +QDoubleSpinBox::down-arrow:pressed +{ + image: url(:/light/down_arrow-hover.svg); + width: 9px; + height: 6px; +} + +QSpinBox::down-arrow:disabled, +QSpinBox::down-arrow:off, +QDoubleSpinBox::down-arrow:disabled, +QDoubleSpinBox::down-arrow:off +{ + image: url(:/light/down_arrow_disabled.svg); +} + +QTextBrowser:hover +{ + border: 1px transparent; +} diff --git a/DSView/themes/light/branch_closed-on.svg b/DSView/themes/light/branch_closed-on.svg new file mode 100755 index 00000000..23c54218 --- /dev/null +++ b/DSView/themes/light/branch_closed-on.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/branch_closed.svg b/DSView/themes/light/branch_closed.svg new file mode 100755 index 00000000..286c1a9e --- /dev/null +++ b/DSView/themes/light/branch_closed.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/branch_open-on.svg b/DSView/themes/light/branch_open-on.svg new file mode 100755 index 00000000..9e759272 --- /dev/null +++ b/DSView/themes/light/branch_open-on.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/branch_open.svg b/DSView/themes/light/branch_open.svg new file mode 100755 index 00000000..514a3125 --- /dev/null +++ b/DSView/themes/light/branch_open.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/checkbox_checked-hover.svg b/DSView/themes/light/checkbox_checked-hover.svg new file mode 100755 index 00000000..60fd81be --- /dev/null +++ b/DSView/themes/light/checkbox_checked-hover.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/DSView/themes/light/checkbox_checked.svg b/DSView/themes/light/checkbox_checked.svg new file mode 100755 index 00000000..46a70727 --- /dev/null +++ b/DSView/themes/light/checkbox_checked.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/DSView/themes/light/checkbox_checked_disabled.svg b/DSView/themes/light/checkbox_checked_disabled.svg new file mode 100755 index 00000000..e75612d1 --- /dev/null +++ b/DSView/themes/light/checkbox_checked_disabled.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/DSView/themes/light/checkbox_indeterminate-hover.svg b/DSView/themes/light/checkbox_indeterminate-hover.svg new file mode 100755 index 00000000..603feacc --- /dev/null +++ b/DSView/themes/light/checkbox_indeterminate-hover.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/DSView/themes/light/checkbox_indeterminate.svg b/DSView/themes/light/checkbox_indeterminate.svg new file mode 100755 index 00000000..9666eaf4 --- /dev/null +++ b/DSView/themes/light/checkbox_indeterminate.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/DSView/themes/light/checkbox_indeterminate_disabled.svg b/DSView/themes/light/checkbox_indeterminate_disabled.svg new file mode 100755 index 00000000..25a9d064 --- /dev/null +++ b/DSView/themes/light/checkbox_indeterminate_disabled.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/DSView/themes/light/checkbox_unchecked-hover.svg b/DSView/themes/light/checkbox_unchecked-hover.svg new file mode 100755 index 00000000..7bcf984a --- /dev/null +++ b/DSView/themes/light/checkbox_unchecked-hover.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/DSView/themes/light/checkbox_unchecked.svg b/DSView/themes/light/checkbox_unchecked.svg new file mode 100755 index 00000000..dd1518dc --- /dev/null +++ b/DSView/themes/light/checkbox_unchecked.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/DSView/themes/light/checkbox_unchecked_disabled.svg b/DSView/themes/light/checkbox_unchecked_disabled.svg new file mode 100755 index 00000000..871cf38b --- /dev/null +++ b/DSView/themes/light/checkbox_unchecked_disabled.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/DSView/themes/light/close-hover.svg b/DSView/themes/light/close-hover.svg new file mode 100755 index 00000000..cb44c781 --- /dev/null +++ b/DSView/themes/light/close-hover.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/close-pressed.svg b/DSView/themes/light/close-pressed.svg new file mode 100755 index 00000000..a0dc2496 --- /dev/null +++ b/DSView/themes/light/close-pressed.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/close.svg b/DSView/themes/light/close.svg new file mode 100755 index 00000000..07b50c9e --- /dev/null +++ b/DSView/themes/light/close.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/down_arrow-hover.svg b/DSView/themes/light/down_arrow-hover.svg new file mode 100755 index 00000000..408397f9 --- /dev/null +++ b/DSView/themes/light/down_arrow-hover.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/down_arrow.svg b/DSView/themes/light/down_arrow.svg new file mode 100755 index 00000000..34c5d6a3 --- /dev/null +++ b/DSView/themes/light/down_arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/down_arrow_disabled.svg b/DSView/themes/light/down_arrow_disabled.svg new file mode 100755 index 00000000..af74a307 --- /dev/null +++ b/DSView/themes/light/down_arrow_disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/hmovetoolbar.svg b/DSView/themes/light/hmovetoolbar.svg new file mode 100755 index 00000000..57e54c95 --- /dev/null +++ b/DSView/themes/light/hmovetoolbar.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/DSView/themes/light/hsepartoolbar.svg b/DSView/themes/light/hsepartoolbar.svg new file mode 100755 index 00000000..a4464253 --- /dev/null +++ b/DSView/themes/light/hsepartoolbar.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/left_arrow.svg b/DSView/themes/light/left_arrow.svg new file mode 100755 index 00000000..f77acf4c --- /dev/null +++ b/DSView/themes/light/left_arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/left_arrow_disabled.svg b/DSView/themes/light/left_arrow_disabled.svg new file mode 100755 index 00000000..2d749e72 --- /dev/null +++ b/DSView/themes/light/left_arrow_disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/mode_down_arrow.svg b/DSView/themes/light/mode_down_arrow.svg new file mode 100755 index 00000000..ce625f2a --- /dev/null +++ b/DSView/themes/light/mode_down_arrow.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/DSView/themes/light/radio_checked-hover.svg b/DSView/themes/light/radio_checked-hover.svg new file mode 100755 index 00000000..b0d46a1a --- /dev/null +++ b/DSView/themes/light/radio_checked-hover.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/DSView/themes/light/radio_checked.svg b/DSView/themes/light/radio_checked.svg new file mode 100755 index 00000000..444044fe --- /dev/null +++ b/DSView/themes/light/radio_checked.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/DSView/themes/light/radio_checked_disabled.svg b/DSView/themes/light/radio_checked_disabled.svg new file mode 100755 index 00000000..96ad27a5 --- /dev/null +++ b/DSView/themes/light/radio_checked_disabled.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/DSView/themes/light/radio_unchecked-hover.svg b/DSView/themes/light/radio_unchecked-hover.svg new file mode 100755 index 00000000..6b68d35f --- /dev/null +++ b/DSView/themes/light/radio_unchecked-hover.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/DSView/themes/light/radio_unchecked.svg b/DSView/themes/light/radio_unchecked.svg new file mode 100755 index 00000000..ed159d05 --- /dev/null +++ b/DSView/themes/light/radio_unchecked.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/DSView/themes/light/radio_unchecked_disabled.svg b/DSView/themes/light/radio_unchecked_disabled.svg new file mode 100755 index 00000000..e558384b --- /dev/null +++ b/DSView/themes/light/radio_unchecked_disabled.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/DSView/themes/light/right_arrow.svg b/DSView/themes/light/right_arrow.svg new file mode 100755 index 00000000..a43ea2b6 --- /dev/null +++ b/DSView/themes/light/right_arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/right_arrow_disabled.svg b/DSView/themes/light/right_arrow_disabled.svg new file mode 100755 index 00000000..4940025e --- /dev/null +++ b/DSView/themes/light/right_arrow_disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/sizegrip.svg b/DSView/themes/light/sizegrip.svg new file mode 100755 index 00000000..3388f07d --- /dev/null +++ b/DSView/themes/light/sizegrip.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/spinup_disabled.svg b/DSView/themes/light/spinup_disabled.svg new file mode 100755 index 00000000..838436d0 --- /dev/null +++ b/DSView/themes/light/spinup_disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/stylesheet-branch-end-closed.svg b/DSView/themes/light/stylesheet-branch-end-closed.svg new file mode 100755 index 00000000..a31f5c0c --- /dev/null +++ b/DSView/themes/light/stylesheet-branch-end-closed.svg @@ -0,0 +1,4 @@ + + + + diff --git a/DSView/themes/light/stylesheet-branch-end-open.svg b/DSView/themes/light/stylesheet-branch-end-open.svg new file mode 100755 index 00000000..a31f5c0c --- /dev/null +++ b/DSView/themes/light/stylesheet-branch-end-open.svg @@ -0,0 +1,4 @@ + + + + diff --git a/DSView/themes/light/stylesheet-branch-end.svg b/DSView/themes/light/stylesheet-branch-end.svg new file mode 100755 index 00000000..a1c0a428 --- /dev/null +++ b/DSView/themes/light/stylesheet-branch-end.svg @@ -0,0 +1,4 @@ + + + + diff --git a/DSView/themes/light/stylesheet-branch-more.svg b/DSView/themes/light/stylesheet-branch-more.svg new file mode 100755 index 00000000..ebef8398 --- /dev/null +++ b/DSView/themes/light/stylesheet-branch-more.svg @@ -0,0 +1,4 @@ + + + + diff --git a/DSView/themes/light/stylesheet-vline.svg b/DSView/themes/light/stylesheet-vline.svg new file mode 100755 index 00000000..688177ea --- /dev/null +++ b/DSView/themes/light/stylesheet-vline.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/transparent.svg b/DSView/themes/light/transparent.svg new file mode 100755 index 00000000..3a8ca5cf --- /dev/null +++ b/DSView/themes/light/transparent.svg @@ -0,0 +1 @@ + diff --git a/DSView/themes/light/undock-hover.svg b/DSView/themes/light/undock-hover.svg new file mode 100755 index 00000000..6bddbd72 --- /dev/null +++ b/DSView/themes/light/undock-hover.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/DSView/themes/light/undock.svg b/DSView/themes/light/undock.svg new file mode 100755 index 00000000..9ab21971 --- /dev/null +++ b/DSView/themes/light/undock.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/up_arrow-hover.svg b/DSView/themes/light/up_arrow-hover.svg new file mode 100755 index 00000000..dd1271a5 --- /dev/null +++ b/DSView/themes/light/up_arrow-hover.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/up_arrow.svg b/DSView/themes/light/up_arrow.svg new file mode 100755 index 00000000..b02bb266 --- /dev/null +++ b/DSView/themes/light/up_arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/up_arrow_disabled.svg b/DSView/themes/light/up_arrow_disabled.svg new file mode 100755 index 00000000..742e1c54 --- /dev/null +++ b/DSView/themes/light/up_arrow_disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/DSView/themes/light/vmovetoolbar.svg b/DSView/themes/light/vmovetoolbar.svg new file mode 100755 index 00000000..0a30d45e --- /dev/null +++ b/DSView/themes/light/vmovetoolbar.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/DSView/themes/light/vsepartoolbars.svg b/DSView/themes/light/vsepartoolbars.svg new file mode 100755 index 00000000..00e91ab8 --- /dev/null +++ b/DSView/themes/light/vsepartoolbars.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/INSTALL b/INSTALL old mode 100644 new mode 100755 diff --git a/NEWS b/NEWS deleted file mode 100644 index 71377365..00000000 --- a/NEWS +++ /dev/null @@ -1,254 +0,0 @@ -0.99 (2018-07-08) ------------------ - --common - *add boundary for the main window - *fix the double border issue #130 - *fix measure no update when change cursor in Rule area - *add sample duration setting instead of sample count - *improve save/load and export support - *fix other minor issues - - --for logic analyzer - *add more decoders - *add edges distance measure between channels - *add RLE compress options - - --for oscilloscope - *add DSCopeC20/DSCopeC20P devices support - *improve UI of channel settings - *improve autoset function - - --for data acquisiton - *add DAQ mode for DSCope serial devices - -0.98 (2017-07-29) ------------------ - --common - * fix hardware initialization issue(always red led indicator) when power on - * improve move operations of cursors/label/trigger level, support both press-drag and select-move-release mode - * update the manually, and integrate into the menu of the software - * improve the content of 'About' menu, add 'changelogs' information - * firmware/HDL update - - --for logic analyzer - * improve display scale of decoder contents under exception case - * fix display issue of progressbar of saving dsl file, when sample depth greater than 4G - * fix export issue when part of channels enabled - * fix pattern search issue. - * fix out of boundary issue of scroll bar, when sample depth greater than 8G - - --for oscilloscope - * fix auto calibration issue - * improve noise processing method - -0.97 (2017-06-02) ------------------ - --common - * fix file save fail issue - * fix window missing issue when disconnect external monitor - * fix crash issue when open illegal files - * improve graphics rendering and operations fluency - * fix window display issue: maximize/minimize/show/hide...etc - - --for logic analyzer - * Enhance stream mode function to achieve 100M@3channels / 50M@6channels / 25M@12channels / 20M@16channels - * improve memory structure, extend sample depth to solve memory alloc fail issue - * resolve the coupling between channel data, unused channels don't occupy memory space - * add stop options to support upload already captured data under buffer mode - * add save/load function for protocol settings - * add cursor-based edge statistics function - * add repetitive mode to support repeat trigger acquisition - * improve search function to support start position setting - * improve advanced trigger - * improve cursor measurement method, add 16 sets of measurement between cursors - * fix trigger issue when sample rate greater than 100MHz - * fix display issue of trigger position - * implement free combination of channels under stream mode - * improve file saving display window, and add cancel button - * fix partial decoding issue - * fix messy code issue when adding protocol decoder - * improve the stability of protocol decoder - * add mapping function from graphic decode to list decode - * add mapping flag between graphic decode and list decode - * add channel height option for file device - * add auto snap to edge function when insert cursor - - --for oscilloscope - * fix dsl file loading issue - * fix waveform delay display issue after trigger - * fix crash issue when enable/disable channels - * fix vertical position moving issue under stop state - * fix window background rendering issue - -0.96 (2016-08-12) ------------------ - --common - * change color matching of UI - * fix memory leakage issues - * improve stability of usb transfer - * add new hardware support - * improve interaction of mouse operations - * improve stability of multi-thread operations - * change usb driver under windows system - * add path memory for file operations - * custom windows title of UI - * change path of setting files, avoid authority issues - - --for logic analyzer - * add list viewer for results of protocol decoders - * add search function for protocol list viewer - * add file export for results of protocol decoders - * improve speed and memory usage of protocol decoders - * improve protocol decoders scripts (i2c, uart, spi already done) - * add start and end settings for protocol decoders - * improve serial trigger, add counter for serial value - * improve display and settings for protocol decoders - * add display of trigger time - * add multi-row search for protocol list viewer - * add trigger position and time for file save - - --for oscilloscope - * add FFT function for signal analyzer - * add auto/trigger shift function - * improve zero adjustment program - * add manual zero adjustment - * add gain adjustment for each vdiv - * fix signal display issue when resize window - * fix crash issue when change measure settings - * fix run-without-display issue - * add trigger sensitivity setting - * add display of trigger status - * fix measure setting reset issue - -0.95 (2015-12-14) ------------------ - --for logic analyzer - * add RLE compress mode - * improve mode setting strategy, fix channel number - * add edge count function - * fix memory leakage when running protocol decoder - * add signal height setting, improve display issue - * fix crash issue when adding group signal - * fix channel enable/disable issue - * fix channel display issue when load file input - * add cursor by mouse double-click - * fix other issues - - --for oscilloscope - * add file export/load function - * fix wait time issue when trigger set - * improve display of signal measure - * add Vrms/Vmean/Vp-p measure - * fix setting issue when loading session - * improve add/del of measure ruler - * fix other issues - -0.94 (2015-06-29) ------------------ - * add session load/store function - * add drag-and-drop effect in viewport window - * improve display effect of dock windows, add scroll bar - * fix other GUI issues - - --for logic analyzer - * upgrade advance trigger counter from 16bit to 32bit - * add serial trigger function - * fix memory leakage issue when loading *.dsl files - * fix delete group signal issue when no data captured - - --for oscilloscope - * add measurement of width, period, frequency and duty cycle - * fix instant capture issue @ demo mode - * add trigger hold-off time function - * fix channels enable/disable issue - * fix data error issue of instant capture before normal capture - * fix trigger position issue when only one channel enabled - * fix trigger level can't be changed issue when only one channel enabled - -0.93 (2015-05-08) ------------------ - * Upgrade to qt5 library - * Support stdc++11 - * Improve measure function @ LA mode - * Add duty cycle measure @ LA mode - * Fix out of range issue @ LA mode - * Add export function, support csv/vcd/gnuplot/zip @ LA mode; and csv @ DSO mode - * Add x1/x10/x100 probe options @ DSO mode - * Add measure function @ DSO mode - * Add voltage display of trigger value @ DSO mode - * Fix wave disappear issue @ DSO mode - * Fix trigger issue @ DSO stream mode - * Fix data repeat when set trigger @ LA stream mode - * Keep channel settings when reload occurred @ LA mode - * Fix decoder issue when capture part of data @ LA mode - * Fix stack decoder add issue @ LA mode - * Fix other bugs - -0.92 (2015-04-15) ------------------ - * Add DSLogic Pro and DSCope support - * Add Stream mode to DSLogic & DSLogic Pro - * Support up to 1G samples @ LA mode - * Add zero adjustment @ Oscilloscope mode - * Support zero skew parameters restore - * Enhance stability - * Fix other bugs - -0.4 (2014-09-24) ------------------ - * Add protocol decoders (38) support - * Improve the hardware configuration under various platforms - * Add different thresholds support - * Add options for input filter under logic analyzer mode - * Add instant capture(trigger ignore) under logic analyzer mode - * Ad detail capture status display under logic abrary - * Support stdc++11 - * Improve measure function @ LA mode - * Add duty cycle measure @ LA mode - * Fix out of range issue @ LA mode - * Add export function, support csv/vcd/gnuplot/zip @ LA mode; and csv @ DSO mode - * Add x1/x10/x100 probe options @ DSO mode - * Add measure function @ DSO mode - * Add voltage display of trigger value @ DSO mode - * Fix wave disappear issue @ DSO mode - * Fix trigger issue @ DSO stream mode - * Fix data repeat when set trigger @ LA stream mode - * Keep channel settings when reload occurred @ LA mode - * Fix decoder issue when capture part of data @ LA mode - * Fix stack decoder add issue @ LA mode - * Fix other bugsnalyzer mode - * Add quick button for mode shift - * Redesign the GUI for oscilloscope - * Add flexible sample rate support under oscilloscope mode - * Fix trigger value setting issue - * Improve trigger method under oscilloscope mode - * Add shortcut keys for major operations - * Fix other bugs - -0.3 (2014-06-29) ------------------ - * Add DSLogic Oscilloscope module support. - * Fix data display issue when not all channels are enabled. - * Fix issue of data mirrored to other channels. - * Fix radiobutton/checkbox display issue under certain windows theme. - * Add new simple trigger type - * Fix bugs of trigger setting and detection - * Fix other minor issues. - * Clean up the git repository - -0.2.1 (2014-05-08) ------------------- - * Add wireless extension hardware support. - * Fix libusb_error_io issue on Linux when sample rate >= 100MHz. - * Fix channel enable/disable bug. - * Fix device options config issue. - * Fix some display issues of UI. - -0.2.0 (2014-04-13) ------------------- - * Add DSLogic hardware support. - -0.1.0 (2013-12-15) ------------------- - * Initial release. - diff --git a/NEWS25 b/NEWS25 new file mode 100755 index 00000000..4519101d --- /dev/null +++ b/NEWS25 @@ -0,0 +1,290 @@ +1.00 -- (2019-09-09) +----------------- + (通用) + *增加多语言支持及动态切换 + *增加两种界面风格(清新和暗黑)及动态切换 + *改善4K/高DPI显示支持 + *增加文件类型设备的关闭功能 + *增加文件保存遇到问题时的信息提示 + *VCD等导出文件的格式修正 + *其他显示问题 + + (逻辑分析仪) + *迭代到V3版本的协议解码库 + *增加多个新的协议解码,解码器总数增长到97个 + *改进某些解码器的选项和解码速度 + *修复某些解码器的选项设置无法保存和载入的问题 + *修复某些采样率下,时间测量不准确问题 + *修复stream模式的通道选择 + *修复200M及以上采样率不能设置滤波器的问题 + *修复长时间采样数据进行解码时,特定采样点的边沿识别问题 + *修复区域解码时,解析进度的显示问题 + *改进通道间信号的时间差测量 + + (示波器) + *改进示波器的自动测量功能,实现20种参数的自动测量 + *改进自动测量的选择和显示方式 + *实现x-y模式的李萨茹图显示 + *实现加/减/乘/除的数学运算通道 + *增加水平方向的测量标尺 + *改进频率测量方式,以当前触发电平为参考点 + *修复水平分辨率改变时,上一次采样波形的缩放和频率显示问题 + *修复csv导出数据和实际显示电压值有偏差的问题 + *改进低频信号以及特殊信号的频率自动测量算法 + *改进示波器的autoset功能 + *改进示波器的自动校准程序 + + (数据记录仪) + *增加默认纵轴的显示标尺设置 + +0.99 -- (2018-05-30) +----------------- + (通用) + *为主窗口添加边界的分界线 + *修复双标题窗口的问题 #130 + *修复在标尺区域用右键移动光标,测量结果不更新的问题 + *添加采样时长设置,替代之前的采样深度设置 + *改进保存/打开以及导出文件的支持 + *其他小问题 + + (逻辑分析仪) + *添加更多的解码器 + *添加跨通道的边沿距离测量工具 + *增加RLE硬件压缩选项 + + (示波器) + *添加DSCopeC20/DSCopeC20P设备支持 + *改进通道选项设置的UI界面 + *改进自动设置功能 + + (数据记录仪) + *为DSCope系列设备,添加DAQ模式功能 + +v0.98 -- (2017-07-29) +---------- + (通用) + *解决某些USB端口上电无法正确配置硬件,指示灯一直为红色的问题 + *改进所有移动操作,同时支持press-drag和select-move-release两种操作模式 + *更新文档,并集成到软件的菜单中 + *改进“关于”菜单的内容显示,增加changelogs等历史信息 + *固件更新 + + (逻辑分析仪) + *改进异常情况下解码内容的显示比例控制 + *修复深度大于4G时,文件保存进度的显示问题 + *修复在部分通道使能的情况下,导出文本文件不正确的问题 + *修复查找特定波形时,结果与设置不符,以及越界崩溃的问题 + *修复深度为16G时,滑动窗口的滚动条越界的问题 + + (示波器) + *修复示波器自动调零不能完成的问题 + *改进示波器背景噪声的处理方式 + + +v0.971 -- (2017-06-07) +---------- + *解决导出csv文件软件崩溃的问题 + *解决某些精简系统下,运行找不到dll文件的问题 + *增加一些解码协议(DMX512等) + *解决老版本硬件插拔过程中驱动切换导致无法正确配置的问题 + +v0.97rc3 -- (2017-05-09) +---------- + (通用) + *增加窗口隐藏状态下采样进度的显示 + *解决软件初始化窗口状态错误,无法最大化/最小化问题 + *解决中文安装目录下,硬件初始化不成功的问题 + *解决外接显示器切换时,软件窗口无法显示的问题 + *解决鼠标从窗口边沿移回后显示状态不正确的问题 + *解决点击状态栏无法切换窗口显示/隐藏的问题 + *解决打开非法文件软件崩溃的问题 + *改进图形的渲染方式,提高软件运行的流畅度 + + (逻辑分析仪) + *增加64位程序,扩展程序可用存储空间 + *增强stream模式,扩展最大采样深度到16G + *增加buffer模式下停止选项,允许中途停止上传已采集的数据 + *增加协议解码中数据采样点的图形化标识 + *增加基于光标的边沿统计功能 + *增加协议搜索的位置标记,以及固定显示比例的选项 + *解决协议解码出现buffer empty error的bug + *解决删除stack解码时,软件崩溃的问题 + *解决大深度下,触发位置导致数据显示错误的问题 + *解决某些情况下协议解码设置出现乱码的问题 + *更新协议解码器的实现,增强稳定性 + *增加插入光标时,边沿的自动吸附功能 + + (示波器) + *解决示波器单通道运行状态下,打开另外一个通道,软件崩溃的问题 + *解决stop状态下,移动垂直位置后,零点偏移的问题 + *解决单双通道切换时,数据对应错乱的问题 + *解决窗口背景被重复渲染,导致窗口颜色变化的问题 + +v0.97rc1 -- (2016-12-21) +---------- + (逻辑分析仪) + *增强stream模式的功能,实现3通道@100M/6通道@50M/12通道@25/16通道@20M + *改进内存结构,扩展存储深度,解决大内存申请失败的问题 + *解决通道数据间的耦合,未使能的通道不占用内存 + *增加协议设置的保存和自动载入 + *增加循环模式,支持自动触发采集 + *重新实现数据渲染的算法,使得波形显示更加精确 + *改进高级触发的逻辑,增加连续模式(脉宽)触发 + *改进光标测量的方法,支持16组任意光标间的距离显示 + *解决采样率大于100MHz时,某些情况不能正确触发的问题 + *解决触发位置显示不准确的问题 + *实现400M采样率下的串行触发 + *实现stream模式下的任意通道配置 + *改进文件保存过程的进度显示,添加保存取消功能 + *解决某些情况保存文件不成功的问题 + *解决输入非法文件软件崩溃的问题 + *解决区域解码没有图形显示的问题 + *增加时间轴显示区域映射到协议列表显示的功能 + *增加文件设备的最大高度选项,以及选项的保存和自动载入 + + (示波器) + *解决dsl文件无法正常载入的问题 + *解决单次触发波形延迟显示的问题 + +v0.96 -- (2016-08-03) +---------- + **增加文件操作的路径记忆功能 + **改进usb传输的稳定性 + **自定义UI窗口的title,使显示风格一致 + **修改配置存储路径,避免文件读写权限问题 + *优化UI显示,改为深背景色 + *修正运行过程中产生的内存泄露问题 + *增加新版示波器硬件的支持 + *改进鼠标交互的操作逻辑 + *改进多线程同步导致程序不稳定的问题 + *优化和统一设备驱动程序 + + (逻辑分析仪) + **增加协议列表内容的多行数据搜索 + **增加保存文件的触发位置和绝对触发时间 + *增加协议列表显示功能 + *增加协议内容搜索功能 + *增加协议数据导出为文本文件 + *优化协议解析模块的速度和内存使用 + *优化协议解析脚本(目前已优化的协议包括i2c, uart和spi) + *增加协议解析的起始和结束位置自定义功能 + *增强串行触发,增加匹配内容的counter + *改进协议解析的显示效果和设置 + *添加触发的绝对时间显示 + + (示波器) + **增加auto/trigger自动切换功能 + **增加触发灵敏度设置 + **增加触发状态显示 + **修正测量显示在某些情况下自动重置的问题 + *增加FFT频域分析模式和显示(最大支持16K深度,支持加窗函数,支持线性和db显示模式) + *改进调零的过程,优化参数读取和设置 + *支持手动微调调零的结果 + *增加手动调整每一级放大倍数的功能 + *修正窗口大小变化时波形显示幅度的问题 + *修正测量选项在某些模式下导致软件崩溃的问题 + *修正在某些模式下启动运行无波形显示的问题 + + +v0.95 -- (2015-12-08) +---------- + (逻辑分析仪) + *增加RLE压缩模式,扩展高采样率下的采样深度 + *修改模式设置逻辑,固定通道数 + *增加逻辑分析仪的边沿计数,通道内单击然后移动鼠标就可以对任意通道进行边沿计数 + *修正多次运行协议解析时的内存泄露问题 + *增加信号最高高度的设置,解决通道数量较少时的显示问题 + *修正添加组信号时程序崩溃的情况 + *修正使能不连续通道之后出现重复通道的问题 + *修正保存文件再载入时的通道显示错误 + *增加添加标签的快捷键(双击鼠标左键) + *修正其他显示问题和bug + + (示波器) + *增加文件导出和载入功能 + *修正触发模式下,大时基到小时基的等待时间 + *改进波形参数的显示和设置方式 + *增加Vrms/Vmean/Vp-p的计算和显示 + *修正session载入设置时的问题 + *改进测量标尺Add和Del的逻辑 + *修正其他显示问题和bug + +v0.94 -- (2015-06-20) +---------- + *增加配置保存和自动载入功能 + *增加波形观察窗口的拖拽滑动效果 + *改进dock窗口的显示,增加滚动条 + *修改其他界面显示问题 + + (逻辑分析仪) + *升级高级触发计数,从16bit到32bit + *增加串行总线的高级触发功能 + *修正载入文件时的内存泄露问题 + *修正无采样数据时,删除group通道导致软件崩溃的问题 + + (示波器) + *增加波形窗口宽度、周期、频率、占空比以及幅度的测量 + *修正demo模式下单次采样崩溃的问题 + *增加触发释抑时间的设置功能 + *修正单双通道切换导致的崩溃或者无波形输出的问题 + *修正正常采样之前开始单次采样时,波形显示不正确的问题 + *修正单通道单次采样触发点的定位问题 + *修正某些情况下单通道使能时触发电平无法调节的问题 + +v0.931 ——(2015-05-21) +---------- + *修正Demo模式下的触发位置设置超出范围的问题 + *修正示波器通道1的频率测量问题 + *修正示波器方波输出频率为1KHz + *修正无法保存(save)文件的问题 + *升级逻辑分析仪的触发计数到32bit + +v0.93 ——(2015-05-06) +---------- + *升级到qt5的库 + *修改stdc++11的编译问题 + *改善逻辑分析仪的测量功能,提高测量的精确度 + *增加逻辑分析仪模式下占空比的测量 + *解决逻辑分析仪测量时超出数据类型边界的错误 + *增加export选项,导出逻辑分析仪数据为csv/vcd/gnuplot/zip文件 + *增加export选项,导出示波器数据为csv文件 + *增加示波器x1/x10/x100的探头选项 + *在示波器模式下增加鼠标跟随测量以及插入光标的操作 + *修正示波器的频率测量问题,并改进测量精度 + *增加触发位置的电压值显示功能 + *改进示波器设置触发后波形偶尔消失的问题 + *改进示波器触发点的检测和生成 + *修正示波器stream模式下的触发功能 + *修正逻辑分析仪stream模式下设置触发位置后,采样数据重复的问题 + *修正模式切换后设置无法恢复的问题 + *采样参数切换时,保留通道参数的设置 + *修正采样中途停止后,协议解析无法完成的问题 + *修正其他的显示问题 + +v0.92 ——(2015-03-28) +---------- + *改进硬件配置的逻辑,解决部分USB端口无法使用的问题 + *改进USB数据传输的稳定性,解决某些情况下出现的“Failed to Start Session”错误 + *解决逻辑分析仪内存申请失败时软件出现崩溃的情况,改为跳出提示窗口 + *解决逻辑分析仪在Stream模式下信号采样时钟的频率不正确的问题 + *解决逻辑分析仪的协议解析中可视按钮的设置问题 + *解决示波器单通道使能时采样波形出现锯齿的问题 + *改进示波器的波形显示方式,在低频时采用滚动显示的方式显示连续波形 + *时间轴的从最长400ms/div增加到10s/div + *改进触发电平的设置,使其跟随垂直方向的偏移 + *解决示波器不同通道之间时间轴调节不同步的问题 + + +v0.91 ——(2015-03-18) +---------- + *解决DSCope运行过程双击显示区域弹出错误窗口的问题 + *解决DSCope调零过程中导致波形无法正常显示的问题 + *解决DSCope在单通道模式下,信号频率测量不正常的问题 + *解决DSCope的采样深度和采样频率的设置调节问题 + *解决DSCope的垂直调节和水平调节转盘的使能错误问题 + *其他的bug修正 + + +v0.9 ——(2015-03-16) +---------- + *支持DSLogic Pro和DSCope硬件的初始版本 diff --git a/NEWS31 b/NEWS31 new file mode 100755 index 00000000..7e4016de --- /dev/null +++ b/NEWS31 @@ -0,0 +1,296 @@ +1.00 -- (2019-09-09) +----------------- + --common + *add multi-language support and dynamic switch + *add two themes (dark/light) support and dynamic switch + *improve 4K/high DPI display support + *add close function for file type device + *add message tips when encounter file save errors + *fix file format of export file, like VCD + *fix other minor issues + + --for logic analyzer + *add support for V3 version decode library + *add more decoders, the number of decoders come up to 97 + *improve options and speed for certain decoders + *fix load&store issue of some decode options + *fix wrong measure result at certain sample rate + *fix channel selection of stream mode + *fix filter issue above 100M sample rate + *fix decode error for long time capture + *fix progress display issue when set region decode + *improve measurement bwteent edges in different channels + + --for oscilloscope + *improve the auto measure funciton, support up to 20 measurements + *improve UI for auto measure + *implement x-y mode display for lissajous-figure + *implement Add/Sub/Mul/Div math channel + *add horizontal measure cursors + *improve measure of frequency, refer to current trigger level + *fix captured wave display issue when change horizontal resolution + *fix inconsistant issue when export csv file + *improve algorithm of frequency measure, especially for low frequency and special signals + *improve autoset function + *improve auto calibraiton function + + --for data acquisiton + *add default settings of Y-axis + +0.99 (2018-05-30) +----------------- + --common + *add boundary for the main window + *fix the double border issue #130 + *fix measure no update when change cursor in Rule area + *add sample duration setting instead of sample count + *improve save/load and export support + *fix other minor issues + + --for logic analyzer + *add more decoders + *add edges distance measure between channels + *add RLE compress options + + --for oscilloscope + *add DSCopeC20/DSCopeC20P devices support + *improve UI of channel settings + *improve autoset function + + --for data acquisiton + *add DAQ mode for DSCope serial devices + +0.98 (2017-07-29) +----------------- + --common + *fix hardware initialization issue(always red led indicator) when power on + *improve move operations of cursors/label/trigger level, support both press-drag and select-move-release mode + *update the manually, and integrate into the menu of the software + *improve the content of 'About' menu, add 'changelogs' information + *firmware/HDL update + + --for logic analyzer + *improve display scale of decoder contents under exception case + *fix display issue of progressbar of saving dsl file, when sample depth greater than 4G + *fix export issue when part of channels enalbed + *fix pattern search issue. + *fix out of boundary issue of scroll bar, when sample depth greater than 8G + + --for oscilloscope + *fix auto calibraiton issue + *improve noise processing method + +0.97 (2017-06-02) +----------------- + --common + *fix file save fail issue + *fix window missing issue when disconnect external monitor + *fix crash issue when open illegal files + *improve graphics rendering and operations fluency + *fix window display issue: maximize/minimize/show/hide...etc + + --for logic analyzer + *Enhance stream mode funtion to achieve 100M@3channels / 50M@6channels / 25M@12channels / 20M@16channels + *improve memory structure, extend sample depth to solve memory alloc fail issue + *resolve the coupling between channel data, unused channels don't occupy memory space + *add stop options to support upload already captured data under buffer mode + *add save/load function for protocol settings + *add cursor-based edge statistics function + *add repetitive mode to support repeat trigger acquisiton + *improve search funciton to support start position setting + *improve advanced trigger + *improve cursor measurement method, add 16 sets of measurement between cursors + *fix trigger issue when sample rate greater than 100MHz + *fix display issue of trigger position + *implement free combination of channels under stream mode + *improve file saving display window, and add cancel button + *fix partial decoding issue + *fix messy code issue when add protocol decoder + *improve the stability of protocol decoder + *add mapping funtion from graphic decode to list decode + *add mapping flag between grapic decode and list decode + *add channel height option for file device + *add auto snap to edge function when insert cursor + + --for oscilloscope + *fix dsl file loading issue + *fix waveform delay display issue after trigger + *fix crash issue when enable/disable channels + *fix vertical position moving issue under stop state + *fix window background rendering issue + +0.96 (2016-08-12) +----------------- + --common + *change color matching of UI + *fix memory leakage issues + *improve stability of usb transfer + *add new hardware support + *improve interaction of mouse operations + *improve stability of multi-thread operations + *change usb driver under windows system + *add path memory for file operations + *custom windows title of UI + *change path of setting files, avoid authority issues + + --for logic analyzer + *add list viewer for results of protocol decoders + *add search function for protocol list viewer + *add file export for results of protocol decoders + *improve speed and memory usage of protocol decoders + *improve protocol decoders scripts (i2c, uart, spi already done) + *add start and end settings for protocol decoders + *improve serial trigger, add counter for serial value + *improve display and settings for protocol decoders + *add display of trigger time + *add multi-row search for protocol list viewer + *add trigger position and time for file save + + --for oscilloscope + *add FFT function for signal analyzer + *add auto/trigger shift function + *improve zero adjustment program + *add manual zero adjustment + *add gain adjustment for each vdiv + *fix signal display issue when resize window + *fix crash issue when change measure settings + *fix run-without-display issue + *add trigger sensitivity setting + *add display of trigger status + *fix measure setting reset issue + +0.95 (2015-12-14) +----------------- + --for logic analyzer + *add RLE compress mode + *improve mode setting strategy, fix channel number + *add edge count function + *fix memory leakage when run protocol decoder + *add signal height setting, improve display issue + *fix crash issue when add group signal + *fix channel enable/disable issue + *fix channel display issue when load file input + *add cursor by mouse double-click + *fix other issues + + --for oscilloscope + *add file export/load function + *fix wait time issue when trigger setted + *improve display of signal measure + *add Vrms/Vmean/Vp-p measure + *fix setting issue when load session + *imporve add/del of measure ruler + *fix other issues + +0.94 (2015-06-29) +----------------- + * add session load/store function + * add drag-and-drop effect in viewport window + * improve display effect of dock windows, add scroll bar + * fix other GUI issues + + --for logic analyzer + * upgrade advance trigger counter from 16bit to 32bit + * add serial trigger function + * fix memory leakage issue when load *.dsl files + * fix delete group signal issue when no data captured + + --for oscilloscope + * add measurement of width, period, frequency and duty cycle + * fix instant capture issue @ demo mode + * add trigger hold-off time function + * fix channels enable/disable issue + * fix data error issue of instant capture before normal capture + * fix trigger position issue when only one channel enabled + * fix trigger level can't be changed issue when only one channel enabled + +0.93 (2015-05-08) +----------------- + * Upgrade to qt5 library + * Support stdc++11 + * Improve measure function @ LA mode + * Add duty cycle measure @ LA mode + * Fix out of range issue @ LA mode + * Add export function, support csv/vcd/gnuplot/zip @ LA mode; and csv @ DSO mode + * Add x1/x10/x100 probe options @ DSO mode + * Add measure function @ DSO mode + * Add voltage display of trigger value @ DSO mode + * Fix wave disappear issue @ DSO mode + * Fix trigger issue @ DSO stream mode + * Fix data repeat when set trigger @ LA stream mode + * Keep channel settings when reload occurred @ LA mode + * Fix decoder issue when capture part of data @ LA mode + * Fix stack decoder add issue @ LA mode + * Fix other bugs + +0.92 (2015-04-15) +----------------- + * Add DSLogic Pro and DSCope support + * Add Stream mode to DSLogic & DSLogic Pro + * Support up to 1G samples @ LA mode + * Add zero adjustment @ Oscilloscope mode + * Support zero skew parameters restore + * Enhance stbility + * Fix other bugs + +0.4 (2014-09-24) +----------------- + * Add protocol decoders (38) support + * Improve the hardware configuration under various platforms + * Add different thresholds support + * Add options for input filter under logic analyzer mode + * Add instant capture(trigger ignore) under logic analyzer mode + * Ad detail capture status display under logic abrary + * Support stdc++11 + * Improve measure function @ LA mode + * Add duty cycle measure @ LA mode + * Fix out of range issue @ LA mode + * Add export function, support csv/vcd/gnuplot/zip @ LA mode; and csv @ DSO mode + * Add x1/x10/x100 probe options @ DSO mode + * Add measure function @ DSO mode + * Add voltage display of trigger value @ DSO mode + * Fix wave disappear issue @ DSO mode + * Fix trigger issue @ DSO stream mode + * Fix data repeat when set trigger @ LA stream mode + * Keep channel settings when reload occurred @ LA mode + * Fix decoder issue when capture part of data @ LA mode + * Fix stack decoder add issue @ LA mode + * Fix other bugsnalyzer mode + * Add quick button for mode shift + * Redesign the GUI for oscilloscope + * Add flexible sample rate support under oscilloscope mode + * Fix trigger value setting issue + * Improve trigger method under oscilloscope mode + * Add shortcut keys for major operations + * Fix other bugs + +0.3 (2014-06-29) +----------------- + * Add DSLogic Oscilloscope module support. + * Fix data display issue when not all channels be enabled. + * Fix issue of data mirrored to other channels. + * Fix radiobutton/checkbox display issue under certain windows theme. + * Add new simple trigger type + * Fix bugs of trigger setting and detection + * Fix other minior issues. + * Clean up the git repository + +0.2.1 (2014-05-08) +------------------ + * Add wireless extension hardware support. + * Fix libusb_error_io issue on Linux when sample rate >= 100MHz. + * Fix channel enable/disable bug. + * Fix device options config issue. + * Fix some display issues of UI. + +0.2.0 (2014-04-13) +------------------ + * Add DSLogic hardware support. + +0.1.0 (2013-12-15) +------------------ + * Initial release. + + + + diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 87e40a93..56fd931e --- a/README.md +++ b/README.md @@ -1,27 +1,27 @@ -# DSView - -DSView is an GUI programe for supporting various instruments from [DreamSourceLab](http://www.dreamsourcelab.com), including logic analyzer, oscilloscope, etc. DSView is based on sigrok project. - -The sigrok project aims at creating a portable, cross-platform, Free/Libre/Open-Source signal analysis software suite that supports various device types (such as logic analyzers, oscilloscopes, multimeters, and more). - -# Status - -DSView software is in a usable state and has had official tarball releases. However, it is still work in progress. Some basic functionality is available and working, but other things are still on the TODO list. - -# Usefull links - -- [dreamsourcelab.com](http://www.dreamsourcelab.com) -- [kickstarter.com](www.kickstarter.com/projects/dreamsourcelab/dslogic-multifunction-instruments-for-everyone) -- [sigrok.org](http://sigrok.org) - -# Copyright and license - -DSView software is licensed under the terms of the GNU General Public License -(GPL), version 3 or later. - -While some individual source code files are licensed under the GPLv2+, and -some files are licensed under the GPLv3+, this doesn't change the fact that -the program as a whole is licensed under the terms of the GPLv3+ (e.g. also -due to the fact that it links against GPLv3+ libraries). - -Please see the individual source files for the full list of copyright holders. +# DSView + +DSView is an GUI programe for supporting various instruments from [DreamSourceLab](http://www.dreamsourcelab.com), including logic analyzer, oscilloscope, etc. DSView is based on sigrok project. + +The sigrok project aims at creating a portable, cross-platform, Free/Libre/Open-Source signal analysis software suite that supports various device types (such as logic analyzers, oscilloscopes, multimeters, and more). + +# Status + +DSView software is in a usable state and has had official tarball releases. However, it is still work in progress. Some basic functionality is available and working, but other things are still on the TODO list. + +# Usefull links + +- [dreamsourcelab.com](http://www.dreamsourcelab.com) +- [kickstarter.com](www.kickstarter.com/projects/dreamsourcelab/dslogic-multifunction-instruments-for-everyone) +- [sigrok.org](http://sigrok.org) + +# Copyright and license + +DSView software is licensed under the terms of the GNU General Public License +(GPL), version 3 or later. + +While some individual source code files are licensed under the GPLv2+, and +some files are licensed under the GPLv3+, this doesn't change the fact that +the program as a whole is licensed under the terms of the GPLv3+ (e.g. also +due to the fact that it links against GPLv3+ libraries). + +Please see the individual source files for the full list of copyright holders. diff --git a/libsigrok4DSL/AUTHORS b/libsigrok4DSL/AUTHORS old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/COPYING b/libsigrok4DSL/COPYING old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/ChangeLog b/libsigrok4DSL/ChangeLog old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/INSTALL b/libsigrok4DSL/INSTALL old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/Makefile.am b/libsigrok4DSL/Makefile.am old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/NEWS b/libsigrok4DSL/NEWS old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/README b/libsigrok4DSL/README old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/backend.c b/libsigrok4DSL/backend.c old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/configure.ac b/libsigrok4DSL/configure.ac old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/dsdevice.c b/libsigrok4DSL/dsdevice.c old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/error.c b/libsigrok4DSL/error.c old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/hardware/DSL/Makefile.am b/libsigrok4DSL/hardware/DSL/Makefile.am old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/hardware/DSL/command.c b/libsigrok4DSL/hardware/DSL/command.c old mode 100644 new mode 100755 index 1048a950..82170f08 --- a/libsigrok4DSL/hardware/DSL/command.c +++ b/libsigrok4DSL/hardware/DSL/command.c @@ -1,74 +1,74 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2013 Bert Vermeulen - * - * 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 . - */ - -#include "libsigrok.h" -#include "libsigrok-internal.h" - -#include "command.h" -#include "dsl.h" -#include - -SR_PRIV int command_ctl_wr(libusb_device_handle *devhdl, struct ctl_wr_cmd cmd) -{ - int ret; - - /* Send the control command. */ - ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR | - LIBUSB_ENDPOINT_OUT, CMD_CTL_WR, 0x0000, 0x0000, - (unsigned char *)&cmd, cmd.header.size+sizeof(struct ctl_header), 3000); - if (ret < 0) { - sr_err("Unable to send CMD_CTL_WR command(dest:%d/offset:%d/size:%d): %s.", - cmd.header.dest, cmd.header.offset, cmd.header.size, - libusb_error_name(ret)); - return SR_ERR; - } - - return SR_OK; -} - -SR_PRIV int command_ctl_rd(libusb_device_handle *devhdl, struct ctl_rd_cmd cmd) -{ - int ret; - - /* Send the control message. */ - ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR | - LIBUSB_ENDPOINT_OUT, CMD_CTL_RD_PRE, 0x0000, 0x0000, - (unsigned char *)&cmd, sizeof(struct ctl_header), 3000); - if (ret < 0) { - sr_err("Unable to send CMD_CTL_RD_PRE command(dest:%d/offset:%d/size:%d): %s.", - cmd.header.dest, cmd.header.offset, cmd.header.size, - libusb_error_name(ret)); - return SR_ERR; - } - - g_usleep(10*1000); - - /* Send the control message. */ - ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR | - LIBUSB_ENDPOINT_IN, CMD_CTL_RD, 0x0000, 0x0000, - (unsigned char *)cmd.data, cmd.header.size, 3000); - - if (ret < 0) { - sr_err("Unable to send CMD_CTL_RD command: %s.", - libusb_error_name(ret)); - return SR_ERR; - } - - return SR_OK; -} +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2013 Bert Vermeulen + * + * 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 . + */ + +#include "libsigrok.h" +#include "libsigrok-internal.h" + +#include "command.h" +#include "dsl.h" +#include + +SR_PRIV int command_ctl_wr(libusb_device_handle *devhdl, struct ctl_wr_cmd cmd) +{ + int ret; + + /* Send the control command. */ + ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_ENDPOINT_OUT, CMD_CTL_WR, 0x0000, 0x0000, + (unsigned char *)&cmd, cmd.header.size+sizeof(struct ctl_header), 3000); + if (ret < 0) { + sr_err("Unable to send CMD_CTL_WR command(dest:%d/offset:%d/size:%d): %s.", + cmd.header.dest, cmd.header.offset, cmd.header.size, + libusb_error_name(ret)); + return SR_ERR; + } + + return SR_OK; +} + +SR_PRIV int command_ctl_rd(libusb_device_handle *devhdl, struct ctl_rd_cmd cmd) +{ + int ret; + + /* Send the control message. */ + ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_ENDPOINT_OUT, CMD_CTL_RD_PRE, 0x0000, 0x0000, + (unsigned char *)&cmd, sizeof(struct ctl_header), 3000); + if (ret < 0) { + sr_err("Unable to send CMD_CTL_RD_PRE command(dest:%d/offset:%d/size:%d): %s.", + cmd.header.dest, cmd.header.offset, cmd.header.size, + libusb_error_name(ret)); + return SR_ERR; + } + + g_usleep(10*1000); + + /* Send the control message. */ + ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_ENDPOINT_IN, CMD_CTL_RD, 0x0000, 0x0000, + (unsigned char *)cmd.data, cmd.header.size, 3000); + + if (ret < 0) { + sr_err("Unable to send CMD_CTL_RD command: %s.", + libusb_error_name(ret)); + return SR_ERR; + } + + return SR_OK; +} diff --git a/libsigrok4DSL/hardware/DSL/command.h b/libsigrok4DSL/hardware/DSL/command.h old mode 100644 new mode 100755 index d657a08b..bebad27c --- a/libsigrok4DSL/hardware/DSL/command.h +++ b/libsigrok4DSL/hardware/DSL/command.h @@ -1,169 +1,173 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2013 Bert Vermeulen - * - * 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 . - */ - -#ifndef LIBDSL_HARDWARE_COMMAND_H -#define LIBDSL_HARDWARE_COMMAND_H - -#include -#include "libsigrok.h" -#include "libsigrok-internal.h" - -/* Protocol commands */ -#define CMD_CTL_WR 0xb0 -#define CMD_CTL_RD_PRE 0xb1 -#define CMD_CTL_RD 0xb2 - -// read only -#define bmGPIF_DONE (1 << 7) -#define bmFPGA_DONE (1 << 6) -#define bmFPGA_INIT_B (1 << 5) -// write only -#define bmCH_CH0 (1 << 7) -#define bmCH_COM (1 << 6) -#define bmCH_CH1 (1 << 5) -// read/write -#define bmSYS_OVERFLOW (1 << 4) -#define bmSYS_CLR (1 << 3) -#define bmSYS_EN (1 << 2) -#define bmLED_RED (1 << 1) -#define bmLED_GREEN (1 << 0) - -#define bmWR_PROG_B (1 << 2) -#define bmWR_INTRDY (1 << 7) -#define bmWR_WORDWIDE (1 << 0) - -#define VTH_ADDR 0x78 -#define CTR1_ADDR 0x71 -#define CTR0_ADDR 0x70 -#define COMB_ADDR 0x68 -#define EI2C_ADDR 0x60 - -#define EI2C_CTR_OFF 0x2 -#define EI2C_RXR_OFF 0x3 -#define EI2C_SR_OFF 0x4 -#define EI2C_TXR_OFF 0x3 -#define EI2C_CR_OFF 0x4 -#define EI2C_SEL_OFF 0x7 - -#define bmEI2C_EN (1 << 7) - -#define bmEI2C_STA (1 << 7) -#define bmEI2C_STO (1 << 6) -#define bmEI2C_RD (1 << 5) -#define bmEI2C_WR (1 << 4) -#define bmEI2C_NACK (1 << 3) - -#define bmEI2C_RXNACK (1 << 7) -#define bmEI2C_TIP (1 << 1) - -#define EI2C_AWR 0xA0 -#define EI2C_ARD 0xA1 - -enum { - DSL_CTL_FW_VERSION = 0, - DSL_CTL_REVID_VERSION = 1, - DSL_CTL_HW_STATUS = 2, - DSL_CTL_PROG_B = 3, - DSL_CTL_SYS = 4, - DSL_CTL_LED = 5, - DSL_CTL_INTRDY = 6, - DSL_CTL_WORDWIDE = 7, - - DSL_CTL_START = 8, - DSL_CTL_STOP = 9, - DSL_CTL_BULK_WR = 10, - DSL_CTL_REG = 11, - DSL_CTL_NVM = 12, - - DSL_CTL_I2C_DSO = 13, - DSL_CTL_I2C_REG = 14, - DSL_CTL_I2C_STATUS = 15, - - DSL_CTL_DSO_EN0 = 16, - DSL_CTL_DSO_DC0 = 17, - DSL_CTL_DSO_ATT0 = 18, - DSL_CTL_DSO_EN1 = 19, - DSL_CTL_DSO_DC1 = 20, - DSL_CTL_DSO_ATT1 = 21, - - DSL_CTL_AWG_WR = 22, -}; - -#pragma pack(push, 1) // byte align -struct version_info { - uint8_t major; - uint8_t minor; -}; - -struct cmd_zero_info { - uint8_t zero_addr; - uint8_t voff0; - uint8_t voff1; - uint8_t voff2; - uint8_t voff3; - uint8_t voff4; - uint8_t voff5; - uint8_t voff6; - uint8_t voff7; - uint8_t voff8; - uint8_t voff9; - uint8_t voff10; - uint8_t voff11; - uint8_t voff12; - uint8_t voff13; - uint8_t voff14; - uint8_t voff15; - uint8_t diff0; - uint8_t diff1; - uint8_t trans0; - uint8_t trans1; -}; - -struct cmd_vga_info { - uint8_t vga_addr; - uint16_t vga0; - uint16_t vga1; - uint16_t vga2; - uint16_t vga3; - uint16_t vga4; - uint16_t vga5; - uint16_t vga6; - uint16_t vga7; -}; - -struct ctl_header { - uint8_t dest; - uint16_t offset; - uint8_t size; -}; -struct ctl_wr_cmd { - struct ctl_header header; - uint8_t data[60]; -}; -struct ctl_rd_cmd { - struct ctl_header header; - uint8_t *data; -}; -#pragma pack(pop) - - -SR_PRIV int command_ctl_wr(libusb_device_handle *devhdl, struct ctl_wr_cmd cmd); -SR_PRIV int command_ctl_rd(libusb_device_handle *devhdl, struct ctl_rd_cmd cmd); - -#endif +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2013 Bert Vermeulen + * + * 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 . + */ + +#ifndef LIBDSL_HARDWARE_COMMAND_H +#define LIBDSL_HARDWARE_COMMAND_H + +#include +#include "libsigrok.h" +#include "libsigrok-internal.h" + +/* Protocol commands */ +#define CMD_CTL_WR 0xb0 +#define CMD_CTL_RD_PRE 0xb1 +#define CMD_CTL_RD 0xb2 + +// read only +#define bmGPIF_DONE (1 << 7) +#define bmFPGA_DONE (1 << 6) +#define bmFPGA_INIT_B (1 << 5) +// write only +#define bmCH_CH0 (1 << 7) +#define bmCH_COM (1 << 6) +#define bmCH_CH1 (1 << 5) +// read/write +#define bmSYS_OVERFLOW (1 << 4) +#define bmSYS_CLR (1 << 3) +#define bmSYS_EN (1 << 2) +#define bmLED_RED (1 << 1) +#define bmLED_GREEN (1 << 0) + +#define bmWR_PROG_B (1 << 2) +#define bmWR_INTRDY (1 << 7) +#define bmWR_WORDWIDE (1 << 0) + +#define VTH_ADDR 0x78 +#define CTR1_ADDR 0x71 +#define CTR0_ADDR 0x70 +#define COMB_ADDR 0x68 +#define EI2C_ADDR 0x60 +#define ADCC_ADDR 0x48 + +#define EI2C_CTR_OFF 0x2 +#define EI2C_RXR_OFF 0x3 +#define EI2C_SR_OFF 0x4 +#define EI2C_TXR_OFF 0x3 +#define EI2C_CR_OFF 0x4 +#define EI2C_SEL_OFF 0x7 + +#define bmEI2C_EN (1 << 7) + +#define bmEI2C_STA (1 << 7) +#define bmEI2C_STO (1 << 6) +#define bmEI2C_RD (1 << 5) +#define bmEI2C_WR (1 << 4) +#define bmEI2C_NACK (1 << 3) + +#define bmEI2C_RXNACK (1 << 7) +#define bmEI2C_TIP (1 << 1) + +#define EI2C_AWR 0x82 +#define EI2C_ARD 0x83 + +enum { + DSL_CTL_FW_VERSION = 0, + DSL_CTL_REVID_VERSION = 1, + DSL_CTL_HW_STATUS = 2, + DSL_CTL_PROG_B = 3, + DSL_CTL_SYS = 4, + DSL_CTL_LED = 5, + DSL_CTL_INTRDY = 6, + DSL_CTL_WORDWIDE = 7, + + DSL_CTL_START = 8, + DSL_CTL_STOP = 9, + DSL_CTL_BULK_WR = 10, + DSL_CTL_REG = 11, + DSL_CTL_NVM = 12, + + DSL_CTL_I2C_DSO = 13, + DSL_CTL_I2C_REG = 14, + DSL_CTL_I2C_STATUS = 15, + + DSL_CTL_DSO_EN0 = 16, + DSL_CTL_DSO_DC0 = 17, + DSL_CTL_DSO_ATT0 = 18, + DSL_CTL_DSO_EN1 = 19, + DSL_CTL_DSO_DC1 = 20, + DSL_CTL_DSO_ATT1 = 21, + + DSL_CTL_AWG_WR = 22, + DSL_CTL_I2C_PROBE = 23, + DSL_CTL_I2C_EXT = 24, +}; + +#pragma pack(push, 1) // byte align +struct version_info { + uint8_t major; + uint8_t minor; +}; + +struct cmd_zero_info { + uint8_t zero_addr; + uint8_t voff0; + uint8_t voff1; + uint8_t voff2; + uint8_t voff3; + uint8_t voff4; + uint8_t voff5; + uint8_t voff6; + uint8_t voff7; + uint8_t voff8; + uint8_t voff9; + uint8_t voff10; + uint8_t voff11; + uint8_t voff12; + uint8_t voff13; + uint8_t voff14; + uint8_t voff15; + uint8_t diff0; + uint8_t diff1; + uint8_t trans0; + uint8_t trans1; + uint8_t comb_comp; +}; + +struct cmd_vga_info { + uint8_t vga_addr; + uint16_t vga0; + uint16_t vga1; + uint16_t vga2; + uint16_t vga3; + uint16_t vga4; + uint16_t vga5; + uint16_t vga6; + uint16_t vga7; +}; + +struct ctl_header { + uint8_t dest; + uint16_t offset; + uint8_t size; +}; +struct ctl_wr_cmd { + struct ctl_header header; + uint8_t data[60]; +}; +struct ctl_rd_cmd { + struct ctl_header header; + uint8_t *data; +}; +#pragma pack(pop) + + +SR_PRIV int command_ctl_wr(libusb_device_handle *devhdl, struct ctl_wr_cmd cmd); +SR_PRIV int command_ctl_rd(libusb_device_handle *devhdl, struct ctl_rd_cmd cmd); + +#endif diff --git a/libsigrok4DSL/hardware/DSL/dscope.c b/libsigrok4DSL/hardware/DSL/dscope.c old mode 100644 new mode 100755 index 1166b3fc..250b51af --- a/libsigrok4DSL/hardware/DSL/dscope.c +++ b/libsigrok4DSL/hardware/DSL/dscope.c @@ -1,1509 +1,1865 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2013 Bert Vermeulen - * Copyright (C) 2013 DreamSourceLab - * - * 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 . - */ - -#include "libsigrok.h" -#include "libsigrok-internal.h" - -#include "dsl.h" -#include "command.h" - - -enum { - /** Normal */ - OP_NORMAL = 0, - /** Internal pattern test mode */ - OP_INTEST = 1, -}; -static const char *opmodes[] = { - "Normal", - "Internal Test", -}; - -static const int32_t hwoptions[] = { - SR_CONF_OPERATION_MODE, -}; - -static const int32_t sessions_dso[] = { - SR_CONF_OPERATION_MODE, - SR_CONF_TIMEBASE, - SR_CONF_TRIGGER_SLOPE, - SR_CONF_TRIGGER_SOURCE, - SR_CONF_TRIGGER_CHANNEL, - SR_CONF_HORIZ_TRIGGERPOS, - SR_CONF_TRIGGER_HOLDOFF, - SR_CONF_TRIGGER_MARGIN, -}; - -static const int32_t sessions_daq[] = { - SR_CONF_SAMPLERATE, - SR_CONF_LIMIT_SAMPLES, - SR_CONF_OPERATION_MODE, - SR_CONF_TIMEBASE, - SR_CONF_TRIGGER_SLOPE, - SR_CONF_TRIGGER_SOURCE, - SR_CONF_TRIGGER_CHANNEL, - SR_CONF_HORIZ_TRIGGERPOS, - SR_CONF_TRIGGER_HOLDOFF, - SR_CONF_TRIGGER_MARGIN, -}; - -static const uint8_t zero_base_addr = 0x40; -static const uint8_t zero_big_addr = 0x20; - -SR_PRIV struct sr_dev_driver DSCope_driver_info; -static struct sr_dev_driver *di = &DSCope_driver_info; - -static uint16_t get_default_voff(const struct sr_dev_inst *sdi, const struct sr_channel* ch) -{ - int i; - struct DSL_context *devc = sdi->priv; - for (i = 0; vga_defaults[i].id; i++) { - if (vga_defaults[i].id == devc->profile->dev_caps.vga_id && - vga_defaults[i].key == ch->vdiv) { - if (ch->index == 1) - return vga_defaults[i].voff_comp; - else - return vga_defaults[i].voff; - } - } - - return 0; -} - -static struct DSL_context *DSCope_dev_new(const struct DSL_profile *prof) -{ - struct DSL_context *devc; - unsigned int i; - - if (!(devc = g_try_malloc(sizeof(struct DSL_context)))) { - sr_err("Device context malloc failed."); - return NULL; - } - - for (i = 0; i < ARRAY_SIZE(channel_modes); i++) - assert(channel_modes[i].id == i); - - devc->channel = NULL; - devc->profile = prof; - devc->fw_updated = 0; - devc->cur_samplerate = devc->profile->dev_caps.default_samplerate; - devc->limit_samples = devc->profile->dev_caps.default_samplelimit; - devc->clock_type = FALSE; - devc->clock_edge = FALSE; - devc->instant = FALSE; - devc->op_mode = OP_NORMAL; - devc->test_mode = SR_TEST_NONE; - devc->stream = FALSE; - devc->ch_mode = devc->profile->dev_caps.default_channelmode; - devc->th_level = SR_TH_3V3; - devc->filter = SR_FILTER_NONE; - devc->timebase = 10000; - devc->trigger_slope = DSO_TRIGGER_RISING; - devc->trigger_source = DSO_TRIGGER_AUTO; - devc->trigger_holdoff = 0; - devc->trigger_hpos = 0x0; - devc->trigger_hrate = 0; - devc->zero = FALSE; - devc->data_lock = FALSE; - devc->cali = FALSE; - devc->trigger_margin = 8; - devc->trigger_channel = 0; - devc->rle_mode = FALSE; - - dsl_adjust_samplerate(devc); - return devc; -} - -static int dev_clear(void) -{ - return std_dev_clear(di, NULL); -} - -static int init(struct sr_context *sr_ctx) -{ - return std_hw_init(sr_ctx, di, LOG_PREFIX); -} - -static GSList *scan(GSList *options) -{ - struct drv_context *drvc; - struct DSL_context *devc; - struct sr_dev_inst *sdi; - struct sr_usb_dev_inst *usb; - struct sr_config *src; - const struct DSL_profile *prof; - GSList *l, *devices, *conn_devices; - struct libusb_device_descriptor des; - libusb_device **devlist; - int devcnt, ret, i, j; - const char *conn; - - drvc = di->priv; - - conn = NULL; - for (l = options; l; l = l->next) { - src = l->data; - switch (src->key) { - case SR_CONF_CONN: - conn = g_variant_get_string(src->data, NULL); - break; - } - } - if (conn) - conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn); - else - conn_devices = NULL; - - /* Find all DSCope compatible devices and upload firmware to them. */ - devices = NULL; - libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); - for (i = 0; devlist[i]; i++) { - if (conn) { - usb = NULL; - for (l = conn_devices; l; l = l->next) { - usb = l->data; - if (usb->bus == libusb_get_bus_number(devlist[i]) - && usb->address == libusb_get_device_address(devlist[i])) - break; - } - if (!l) - /* This device matched none of the ones that - * matched the conn specification. */ - continue; - } - - if ((ret = libusb_get_device_descriptor( devlist[i], &des)) != 0) { - sr_warn("Failed to get device descriptor: %s.", - libusb_error_name(ret)); - continue; - } - - prof = NULL; - for (j = 0; supported_DSCope[j].vid; j++) { - if (des.idVendor == supported_DSCope[j].vid && - des.idProduct == supported_DSCope[j].pid) { - prof = &supported_DSCope[j]; - } - } - - /* Skip if the device was not found. */ - if (!prof) - continue; - - devcnt = g_slist_length(drvc->instances); - devc = DSCope_dev_new(prof); - if (!devc) - return NULL; - sdi = sr_dev_inst_new(channel_modes[devc->ch_mode].mode, devcnt, SR_ST_INITIALIZING, - prof->vendor, prof->model, prof->model_version); - if (!sdi) { - g_free(devc); - return NULL; - } - sdi->priv = devc; - sdi->driver = di; - - drvc->instances = g_slist_append(drvc->instances, sdi); - //devices = g_slist_append(devices, sdi); - - /* Fill in probelist according to this device's profile. */ - if (dsl_setup_probes(sdi, channel_modes[devc->ch_mode].num) != SR_OK) - return NULL; - - if (dsl_check_conf_profile(devlist[i])) { - /* Already has the firmware, so fix the new address. */ - sr_dbg("Found an DSCope device."); - sdi->status = SR_ST_INACTIVE; - sdi->inst_type = SR_INST_USB; - sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), - libusb_get_device_address(devlist[i]), NULL); - /* only report device after firmware is ready */ - devices = g_slist_append(devices, sdi); - } else { - char *firmware; - if (!(firmware = g_try_malloc(strlen(DS_RES_PATH)+strlen(prof->firmware)+1))) { - sr_err("Firmware path malloc error!"); - return NULL; - } - strcpy(firmware, DS_RES_PATH); - strcat(firmware, prof->firmware); - if (ezusb_upload_firmware(devlist[i], USB_CONFIGURATION, - firmware) == SR_OK) - /* Store when this device's FW was updated. */ - devc->fw_updated = g_get_monotonic_time(); - else - sr_err("Firmware upload failed for " - "device %d.", devcnt); - g_free(firmware); - sdi->inst_type = SR_INST_USB; - sdi->conn = sr_usb_dev_inst_new (libusb_get_bus_number(devlist[i]), - 0xff, NULL); - } - } - libusb_free_device_list(devlist, 1); - g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free); - - return devices; -} - -static GSList *dev_list(void) -{ - return ((struct drv_context *)(di->priv))->instances; -} - -static const GSList *dev_mode_list(const struct sr_dev_inst *sdi) -{ - return dsl_mode_list(sdi); -} - -static uint64_t dso_vga(const struct sr_channel* ch) -{ - int i; - for (i = 0; ch->vga_ptr && (ch->vga_ptr+i)->id; i++) { - if ((ch->vga_ptr+i)->key == ch->vdiv) - return (ch->vga_ptr+i)->vgain; - } - - return 0; -} - -static uint64_t dso_voff(const struct sr_channel* ch) -{ - int i; - for (i = 0; ch->vga_ptr && (ch->vga_ptr+i)->id; i++) { - if ((ch->vga_ptr+i)->key == ch->vdiv) - return (ch->vga_ptr+i)->voff; - } - return 0; -} - -static uint64_t dso_vpos(const struct sr_dev_inst *sdi, const struct sr_channel* ch) -{ - uint64_t vpos = 0; - int vpos_coarse, vpos_fine; - int trans_coarse, trans_fine; - struct DSL_context *devc = sdi->priv; - const double voltage = (devc->zero && devc->zero_comb == -1) ? 0 : ch->vpos; - if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_PREOFF) { - trans_coarse = (ch->vpos_trans & 0xFF00) >> 8; - trans_fine = (ch->vpos_trans & 0x00FF); - if (ch->vdiv < 500) { - vpos_coarse = floor(-voltage*DSCOPE_TRANS_CMULTI/trans_coarse + 0.5); - vpos_fine = floor((voltage + vpos_coarse*trans_coarse/DSCOPE_TRANS_CMULTI)*1000.0/trans_fine + 0.5); - } else { - vpos_coarse = floor(-voltage/trans_coarse + 0.5); - vpos_fine = floor((voltage + vpos_coarse*trans_coarse)*DSCOPE_TRANS_FMULTI/trans_fine + 0.5); - } - //vpos = (vpos_coarse << 16) + vpos_fine; - } else { - vpos = ((ch->vdiv*5.0) - voltage)/(ch->vdiv*10.0)*ch->vpos_trans; - } - - const uint64_t voff = dso_voff(ch); - if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_PREOFF) - return ((vpos_coarse+DSCOPE_CONSTANT_BIAS+(voff>>10)) << 16)+vpos_fine+(voff&0x03ff); - else - return vpos+voff; -} - -static uint64_t dso_cmd_gen(const struct sr_dev_inst *sdi, struct sr_channel* ch, int id) -{ - struct DSL_context *devc; - uint64_t cmd = 0; - uint64_t vpos; - GSList *l; - const int ch_bit = 7; - devc = sdi->priv; - - switch (id) { - case SR_CONF_PROBE_EN: - case SR_CONF_PROBE_COUPLING: - if (devc->zero || sdi->mode == ANALOG || dsl_en_ch_num(sdi) == 2) { - cmd += 0x0E00; - //cmd += 0x000; - } else if (dsl_en_ch_num(sdi) == 1) { - if (((ch->index == 0) && ch->enabled) || ((ch->index == 1) && !ch->enabled)) - cmd += 0x1600; - else if (((ch->index == 1) && ch->enabled) || ((ch->index == 0) && !ch->enabled)) - cmd += 0x1A00; - } else { - return 0x0; - } - - cmd += ch->index << ch_bit; - if (devc->zero || ch->coupling == SR_DC_COUPLING) - cmd += 0x100; - else if (ch->coupling == SR_GND_COUPLING) - cmd &= 0xFFFFFDFF; - break; - case SR_CONF_PROBE_VDIV: - case SR_CONF_TIMEBASE: - cmd += 0x8; - cmd += ch->index << ch_bit; - // --VGAIN - cmd += dso_vga(ch); - break; - case SR_CONF_PROBE_VPOS: - cmd += 0x10; - cmd += ch->index << ch_bit; - vpos = dso_vpos(sdi, ch); - cmd += (vpos << 8); - break; - case SR_CONF_SAMPLERATE: - cmd += 0x18; - uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(channel_modes[devc->ch_mode].max_samplerate * 1.0 / devc->cur_samplerate / dsl_en_ch_num(sdi)); - cmd += divider << 8; - break; - case SR_CONF_HORIZ_TRIGGERPOS: - cmd += 0x20; - cmd += devc->trigger_hpos << 8; - break; - case SR_CONF_TRIGGER_SLOPE: - cmd += 0x28; - cmd += devc->trigger_slope << 8; - break; - case SR_CONF_TRIGGER_SOURCE: - cmd += 0x30; - cmd += devc->zero ? 0x0 : devc->trigger_source << 8; - break; - case SR_CONF_TRIGGER_VALUE: - cmd += 0x38; - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - cmd += probe->trig_value << (8 * (probe->index + 1)); - } - break; - case SR_CONF_TRIGGER_MARGIN: - cmd += 0x40; - cmd += ((uint64_t)devc->trigger_margin << 8); - break; - case SR_CONF_TRIGGER_HOLDOFF: - cmd += 0x58; - cmd += ((uint64_t)devc->trigger_holdoff << 8); - break; - case SR_CONF_DSO_SYNC: - cmd = 0xa5a5a500; - break; - default: - cmd = 0xFFFFFFFF; - } - - return cmd; -} - -static int dso_init(const struct sr_dev_inst *sdi) -{ - int ret; - GSList *l; - - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_COUPLING)); - if (ret != SR_OK) { - sr_err("DSO set coupling of channel %d command failed!", probe->index); - return ret; - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_VDIV)); - if (ret != SR_OK) { - sr_err("Set VDIV of channel %d command failed!", probe->index); - return ret; - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_VPOS)); - if (ret != SR_OK) { - sr_err("Set VPOS of channel %d command failed!", probe->index); - return ret; - } - } - - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); - if (ret != SR_OK) { - sr_err("Set Sample Rate command failed!"); - return ret; - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_HORIZ_TRIGGERPOS)); - if (ret != SR_OK) { - sr_err("Set Horiz Trigger Position command failed!"); - return ret; - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_HOLDOFF)); - if (ret != SR_OK) { - sr_err("Set Trigger Holdoff Time command failed!"); - return ret; - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SLOPE)); - if (ret != SR_OK) { - sr_err("Set Trigger Slope command failed!"); - return ret; - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); - if (ret != SR_OK) { - sr_err("Set Trigger Source command failed!"); - return ret; - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_VALUE)); - if (ret != SR_OK) { - sr_err("Set Trigger Value command failed!"); - return ret; - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_MARGIN)); - if (ret != SR_OK) { - sr_err("Set Trigger Margin command failed!"); - return ret; - } - return ret; -} - -static gboolean dso_load_eep(struct sr_dev_inst *sdi, struct sr_channel *probe, gboolean fpga_done) -{ - struct DSL_context *devc; - int ret, i; - uint16_t real_zero_addr; - - devc = sdi->priv; - struct cmd_zero_info zero_info; - uint8_t dst_addr = (zero_base_addr + - probe->index * (sizeof(struct cmd_zero_info) + sizeof(struct cmd_vga_info))); - zero_info.zero_addr = dst_addr; - if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_SEEP) - real_zero_addr = zero_info.zero_addr; - else - real_zero_addr = (zero_big_addr << 8) + zero_info.zero_addr; - if ((ret = dsl_rd_nvm(sdi, (unsigned char *)&zero_info, real_zero_addr, sizeof(struct cmd_zero_info))) != SR_OK) { - return FALSE; - sr_err("%s: Send Get Zero command failed!", __func__); - } else { - if (zero_info.zero_addr == dst_addr) { - uint8_t* voff_ptr = &zero_info.zero_addr + 1; - for (i = 0; probe->vga_ptr && (probe->vga_ptr+i)->id; i++) { - (probe->vga_ptr+i)->voff = (*(voff_ptr + 2*i+1) << 8) + *(voff_ptr + 2*i); - } - if (i != 0) { - probe->comb_diff_top = *(voff_ptr + 2*i); - probe->comb_diff_bom = *(voff_ptr + 2*i + 1); - probe->vpos_trans = *(voff_ptr + 2*i + 2) + (*(voff_ptr + 2*i + 3) << 8); - if (!fpga_done) { - const double slope = (probe->comb_diff_bom - probe->comb_diff_top)/(2.0*255.0); - for (i = 0; i < 256; i++) { - ret = dsl_wr_reg(sdi, COMB_ADDR + probe->index*2, i); - int value = i+i*slope+probe->comb_diff_top*0.5+0.5; - value = (value < 0) ? 0 : - (value > 255) ? 255 : value; - ret = dsl_wr_reg(sdi, COMB_ADDR + probe->index*2 + 1, value); - } - } - } - } else { - return FALSE; - } - } - - struct cmd_vga_info vga_info; - vga_info.vga_addr = dst_addr + sizeof(struct cmd_zero_info); - if (devc ->profile->dev_caps.feature_caps & CAPS_FEATURE_SEEP) - real_zero_addr = vga_info.vga_addr; - else - real_zero_addr = (zero_big_addr << 8) + vga_info.vga_addr; - if ((ret = dsl_rd_nvm(sdi, (unsigned char *)&vga_info, real_zero_addr, sizeof(struct cmd_vga_info))) != SR_OK) { - return FALSE; - sr_err("%s: Send Get Zero command failed!", __func__); - } else { - if (vga_info.vga_addr == dst_addr + sizeof(struct cmd_zero_info)) { - uint16_t* vgain_ptr = &vga_info.vga0; - for (i = 0; probe->vga_ptr && (probe->vga_ptr+i)->id; i++) { - (probe->vga_ptr+i)->vgain = *(vgain_ptr + i) << 8; - } - } else { - return FALSE; - } - } - - return TRUE; -} - -static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, - const struct sr_channel *ch, - const struct sr_channel_group *cg) -{ - unsigned int i; - int ret; - struct DSL_context *devc = sdi->priv; - - ret = dsl_config_get(id, data, sdi, ch, cg); - if (ret != SR_OK) { - switch (id) { - case SR_CONF_OPERATION_MODE: - if (!sdi) - return SR_ERR; - *data = g_variant_new_string(opmodes[devc->op_mode]); - break; - case SR_CONF_CALI: - if (!sdi) - return SR_ERR; - *data = g_variant_new_boolean(devc->cali); - break; - case SR_CONF_TEST: - if (!sdi) - return SR_ERR; - *data = g_variant_new_boolean(FALSE); - break; - case SR_CONF_STREAM: - if (!sdi) - return SR_ERR; - *data = g_variant_new_boolean(devc->stream); - break; - case SR_CONF_MAX_DSO_SAMPLERATE: - if (!sdi) - return SR_ERR; - *data = g_variant_new_uint64(channel_modes[devc->ch_mode].max_samplerate); - break; - case SR_CONF_MAX_DSO_SAMPLELIMITS: - if (!sdi) - return SR_ERR; - *data = g_variant_new_uint64(devc->profile->dev_caps.dso_depth); - break; - case SR_CONF_HW_DEPTH: - if (!sdi) - return SR_ERR; - *data = g_variant_new_uint64(devc->profile->dev_caps.hw_depth / channel_modes[devc->ch_mode].unit_bits); - break; - case SR_CONF_PROBE_VGAIN: - if (!sdi || !ch) - return SR_ERR; - *data = g_variant_new_uint64(dso_vga(ch)>>8); - break; - case SR_CONF_PROBE_VGAIN_DEFAULT: - if (!sdi || !ch) - return SR_ERR; - for (i = 0; vga_defaults[i].id; i++) { - if (vga_defaults[i].id == devc->profile->dev_caps.vga_id && - vga_defaults[i].key == ch->vdiv) { - *data = g_variant_new_uint64(vga_defaults[i].vgain >> 8); - break; - } - } - break; - case SR_CONF_PROBE_VGAIN_RANGE: - if (!sdi) - return SR_ERR; - uint16_t vgain_default = 0; - for (i = 0; vga_defaults[i].id; i++) { - if (vga_defaults[i].id == devc->profile->dev_caps.vga_id && - vga_defaults[i].key == ch->vdiv) { - vgain_default = vga_defaults[i].vgain; - break; - } - } - vgain_default = (vgain_default>>8) & 0x0FFF; - *data = g_variant_new_uint16(min(CALI_VGAIN_RANGE, vgain_default*2)); - break; - case SR_CONF_PROBE_VOFF: - if (!sdi || !ch) - return SR_ERR; - uint16_t voff = dso_voff(ch); - uint16_t voff_default = get_default_voff(sdi, ch); - if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_PREOFF) { - int voff_skew_coarse = (voff >> 10) - (voff_default >> 10); - int voff_skew_fine = (voff & 0x03ff) - (voff_default & 0x03ff); - double trans_coarse = (ch->vdiv < 500) ? (ch->vpos_trans >> 8)/DSCOPE_TRANS_CMULTI : (ch->vpos_trans >> 8); - double trans_fine = (ch->vdiv < 500) ? (ch->vpos_trans & 0x00ff) / 1000.0 : (ch->vpos_trans & 0x00ff) / DSCOPE_TRANS_FMULTI; - double voff_rate = (voff_skew_coarse*trans_coarse - voff_skew_fine*trans_fine) / ch->vdiv; - voff = (voff_rate * 0.5 + 0.5) * devc->profile->dev_caps.default_pwmmargin; - } - *data = g_variant_new_uint16(voff); - break; - case SR_CONF_PROBE_VOFF_DEFAULT: - if (!sdi || !ch) - return SR_ERR; - *data = g_variant_new_uint16(get_default_voff(sdi, ch)); - break; - case SR_CONF_PROBE_VOFF_RANGE: - if (!sdi) - return SR_ERR; - *data = g_variant_new_uint16(devc->profile->dev_caps.default_pwmmargin); - break; - case SR_CONF_VLD_CH_NUM: - if (!sdi) - return SR_ERR; - *data = g_variant_new_int16(channel_modes[devc->ch_mode].num); - break; - default: - return SR_ERR_NA; - } - } - - return SR_OK; -} - -static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, - struct sr_channel *ch, - struct sr_channel_group *cg ) -{ - struct DSL_context *devc; - const char *stropt; - int ret, num_probes; - struct sr_usb_dev_inst *usb; - struct libusb_device_handle *hdl; - struct ctl_wr_cmd wr_cmd; - unsigned int i; - - (void)cg; - - if (sdi->status != SR_ST_ACTIVE) { - return SR_ERR; - } - - devc = sdi->priv; - usb = sdi->conn; - hdl = usb->devhdl; - - ret = SR_OK; - - if (id == SR_CONF_CLOCK_TYPE) { - devc->clock_type = g_variant_get_boolean(data); - } else if (id == SR_CONF_CLOCK_EDGE) { - devc->clock_edge = g_variant_get_boolean(data); - } else if (id == SR_CONF_LIMIT_SAMPLES) { - devc->limit_samples = g_variant_get_uint64(data); - } else if (id == SR_CONF_PROBE_VDIV) { - ch->vdiv = g_variant_get_uint64(data); - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_VDIV)); - if (ret == SR_OK) - sr_dbg("%s: setting VDIV of channel %d to %d mv", - __func__, ch->index, ch->vdiv); - else - sr_dbg("%s: setting VDIV of channel %d to %d mv failed", - __func__, ch->index, ch->vdiv); - } else if (id == SR_CONF_PROBE_FACTOR) { - ch->vfactor = g_variant_get_uint64(data); - sr_dbg("%s: setting Factor of channel %d to %d", __func__, - ch->index, ch->vfactor); - } else if (id == SR_CONF_TIMEBASE) { - devc->timebase = g_variant_get_uint64(data); - } else if (id == SR_CONF_PROBE_COUPLING) { - ch->coupling = g_variant_get_byte(data); - if (ch->coupling == SR_GND_COUPLING) - ch->coupling = SR_DC_COUPLING; - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_COUPLING)); - if (ret == SR_OK) - sr_dbg("%s: setting AC COUPLING of channel %d to %d", - __func__, ch->index, ch->coupling); - else - sr_dbg("%s: setting AC COUPLING of channel %d to %d failed", - __func__, ch->index, ch->coupling); - } else if (id == SR_CONF_TRIGGER_SLOPE) { - devc->trigger_slope = g_variant_get_byte(data); - if (sdi->mode == DSO) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SLOPE)); - } - if (ret == SR_OK) - sr_dbg("%s: setting DSO Trigger Slope to %d", - __func__, devc->trigger_slope); - else - sr_dbg("%s: setting DSO Trigger Slope to %d failed", - __func__, devc->trigger_slope); - } else if (id == SR_CONF_TRIGGER_VALUE) { - ch->trig_value = g_variant_get_byte(data); - if (sdi->mode == DSO) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_TRIGGER_VALUE)); - } - if (ret == SR_OK) - sr_dbg("%s: setting channel %d Trigger Value to %d", - __func__, ch->index, ch->trig_value); - else - sr_dbg("%s: setting DSO Trigger Value to %d failed", - __func__, ch->index, ch->trig_value); - } else if (id == SR_CONF_HORIZ_TRIGGERPOS) { - if (sdi->mode == DSO) { - devc->trigger_hrate = g_variant_get_byte(data); - //devc->trigger_hpos = devc->trigger_hrate * dsl_en_ch_num(sdi) * devc->limit_samples / 200.0; - /* - * devc->trigger_hpos should be updated before each acquisition - * because the samplelimits may changed - */ - devc->trigger_hpos = devc->trigger_hrate * dsl_en_ch_num(sdi) * devc->limit_samples / 200.0; - if ((ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_HORIZ_TRIGGERPOS))) == SR_OK) - sr_dbg("%s: setting DSO Horiz Trigger Position to %d", - __func__, devc->trigger_hpos); - else - sr_dbg("%s: setting DSO Horiz Trigger Position to %d failed", - __func__, devc->trigger_hpos); - } else { - devc->trigger_hpos = g_variant_get_byte(data) * devc->limit_samples / 100.0; - } - } else if (id == SR_CONF_TRIGGER_HOLDOFF) { - devc->trigger_holdoff = g_variant_get_uint64(data); - if (sdi->mode == DSO) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_HOLDOFF)); - } - if (ret == SR_OK) - sr_dbg("%s: setting Trigger Holdoff Time to %d", - __func__, devc->trigger_holdoff); - else - sr_dbg("%s: setting Trigger Holdoff Time to %d failed", - __func__, devc->trigger_holdoff); - } else if (id == SR_CONF_TRIGGER_MARGIN) { - devc->trigger_margin = g_variant_get_byte(data); - if (sdi->mode == DSO) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_MARGIN)); - } - if (ret == SR_OK) - sr_dbg("%s: setting Trigger Margin to %d", - __func__, devc->trigger_margin); - else - sr_dbg("%s: setting Trigger Margin to %d failed", - __func__, devc->trigger_margin); - } else if (id == SR_CONF_SAMPLERATE) { - devc->cur_samplerate = g_variant_get_uint64(data); - if(sdi->mode == DSO) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); - } - } else if (id == SR_CONF_INSTANT) { - devc->instant = g_variant_get_boolean(data); - if (sdi->mode == DSO && dsl_en_ch_num(sdi) != 0) { - if (devc->instant) - devc->limit_samples = devc->profile->dev_caps.hw_depth / channel_modes[devc->ch_mode].unit_bits / dsl_en_ch_num(sdi); - else - devc->limit_samples = devc->profile->dev_caps.dso_depth / dsl_en_ch_num(sdi); - } - } else if (id == SR_CONF_DEVICE_MODE) { - sdi->mode = g_variant_get_int16(data); - num_probes = 0; - if (sdi->mode == DSO) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_DSO_SYNC)); - if (ret != SR_OK) - sr_dbg("%s: DSO configuration sync failed", __func__); - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, sdi->channels->data, SR_CONF_PROBE_VDIV)); - if (ret == SR_OK) - sr_dbg("%s: Initial setting for DSO mode", __func__); - else - sr_dbg("%s: Initial setting for DSO mode failed", __func__); - devc->op_mode = OP_NORMAL; - devc->test_mode = SR_TEST_NONE; - devc->instant = FALSE; - for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { - if (channel_modes[i].mode == DSO && - devc->profile->dev_caps.channels & (1 << i)) { - devc->ch_mode = channel_modes[i].id; - num_probes = channel_modes[i].num; - devc->stream = channel_modes[i].stream; - devc->cur_samplerate = channel_modes[i].max_samplerate / num_probes; - break; - } - } - devc->limit_samples = devc->profile->dev_caps.dso_depth / num_probes; - } else if (sdi->mode == ANALOG) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, sdi->channels->data, SR_CONF_PROBE_VDIV)); - if (ret == SR_OK) - sr_dbg("%s: Initial setting for DSO mode", __func__); - else - sr_dbg("%s: Initial setting for DSO mode failed", __func__); - devc->op_mode = OP_NORMAL; - devc->test_mode = SR_TEST_NONE; - devc->instant = TRUE; - for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { - if (channel_modes[i].mode == ANALOG && - devc->profile->dev_caps.channels & (1 << i)) { - devc->ch_mode = channel_modes[i].id; - num_probes = channel_modes[i].num; - devc->stream = channel_modes[i].stream; - devc->cur_samplerate = channel_modes[i].max_samplerate; - break; - } - } - devc->limit_samples = devc->cur_samplerate; - } - assert(num_probes != 0); - dsl_adjust_probes(sdi, num_probes); - dsl_adjust_samplerate(devc); - sr_dbg("%s: setting mode to %d", __func__, sdi->mode); - } else if (id == SR_CONF_OPERATION_MODE) { - stropt = g_variant_get_string(data, NULL); - if (!strcmp(stropt, opmodes[OP_NORMAL])) { - devc->op_mode = OP_NORMAL; - devc->test_mode = SR_TEST_NONE; - } else if (!strcmp(stropt, opmodes[OP_INTEST])) { - devc->op_mode = OP_INTEST; - devc->test_mode = SR_TEST_INTERNAL; - } else { - ret = SR_ERR; - } - sr_dbg("%s: setting pattern to %d", - __func__, devc->op_mode); - } else if (id == SR_CONF_PROBE_EN) { - ch->enabled = g_variant_get_boolean(data); - - if (sdi->mode == DSO) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_EN)); - if (ch->index == 0) { - wr_cmd.header.dest = DSL_CTL_DSO_EN0; - wr_cmd.data[0] = ch->enabled ? bmCH_CH0 : (uint8_t)~bmCH_CH0; - } else { - wr_cmd.header.dest = DSL_CTL_DSO_EN1; - wr_cmd.data[0] = ch->enabled ? bmCH_CH1 : (uint8_t)~bmCH_CH1; - } - wr_cmd.header.size = 1; - ret = command_ctl_wr(hdl, wr_cmd); - if (dsl_en_ch_num(sdi) != 0) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); - devc->limit_samples = devc->profile->dev_caps.dso_depth / dsl_en_ch_num(sdi); - } - } else if (sdi->mode == ANALOG) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_EN)); - if (ch->index == 0) { - wr_cmd.header.dest = DSL_CTL_DSO_EN0; - wr_cmd.data[0] = bmCH_CH0; - } else { - wr_cmd.header.dest = DSL_CTL_DSO_EN1; - wr_cmd.data[0] = bmCH_CH1; - } - wr_cmd.header.size = 1; - ret = command_ctl_wr(hdl, wr_cmd); - } - if (ret == SR_OK) - sr_dbg("%s: setting ENABLE of channel %d to %d", - __func__, ch->index, ch->enabled); - else - sr_dbg("%s: setting ENABLE of channel %d to %d failed", - __func__, ch->index, ch->enabled); - } else if (id == SR_CONF_PROBE_VPOS) { - ch->vpos = g_variant_get_double(data); - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_VPOS)); - if (ret == SR_OK) - sr_dbg("%s: setting VPOS of channel %d to %lf mv", - __func__, ch->index, ch->vpos); - else - sr_dbg("%s: setting VPOS of channel %d to %lf mv failed", - __func__, ch->index, ch->vpos); - } else if (id == SR_CONF_TRIGGER_SOURCE) { - devc->trigger_source = (devc->trigger_source & 0xf0) + (g_variant_get_byte(data) & 0x0f); - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); - if (ret == SR_OK) - sr_dbg("%s: setting DSO Trigger Source to %d", - __func__, devc->trigger_source); - else - sr_dbg("%s: setting DSO Trigger Source to %d failed", - __func__, devc->trigger_source); - } else if (id == SR_CONF_TRIGGER_CHANNEL) { - devc->trigger_source = (g_variant_get_byte(data) << 4) + (devc->trigger_source & 0x0f); - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); - if (ret == SR_OK) - sr_dbg("%s: setting DSO Trigger Source to %d", - __func__, devc->trigger_source); - else - sr_dbg("%s: setting DSO Trigger Source to %d failed", - __func__, devc->trigger_source); - } else if (id == SR_CONF_ZERO) { - devc->zero = g_variant_get_boolean(data); - if (devc->zero) { - devc->zero_stage = -1; - devc->zero_pcnt = 0; - devc->zero_comb = -1; - GSList *l; - unsigned int i, j; - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - probe->vpos_trans = devc->profile->dev_caps.default_pwmtrans; - if (probe->vga_ptr != NULL) { - for (i = 0; devc->profile->dev_caps.vdivs[i]; i++) { - for (j = 0; j < ARRAY_SIZE(vga_defaults); j++) { - if (vga_defaults[j].id == devc->profile->dev_caps.vga_id && - vga_defaults[j].key == devc->profile->dev_caps.vdivs[i]) { - (probe->vga_ptr+i)->id = vga_defaults[j].id; - (probe->vga_ptr+i)->key = vga_defaults[j].key; - (probe->vga_ptr+i)->vgain = vga_defaults[j].vgain; - (probe->vga_ptr+i)->voff = vga_defaults[j].voff; - break; - } - } - } - } - } - } - } else if (id == SR_CONF_CALI) { - devc->cali = g_variant_get_boolean(data); - } else if (id == SR_CONF_ZERO_LOAD) { - GSList *l; - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - if (!dso_load_eep(sdi, probe, FALSE)) { - config_set(SR_CONF_ZERO, g_variant_new_boolean(TRUE), sdi, NULL, NULL); - sr_info("Zero have not been setted!"); - break; - } - } - } else if (id == SR_CONF_ZERO_SET) { - GSList *l; - struct cmd_zero_info zero_info; - struct cmd_vga_info vga_info; - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - zero_info.zero_addr = zero_base_addr + - probe->index * (sizeof(struct cmd_zero_info) + sizeof(struct cmd_vga_info)); - int i; - uint16_t real_zero_addr; - uint8_t *voff_ptr = &zero_info.zero_addr + 1; - for (i = 0; probe->vga_ptr && (probe->vga_ptr+i)->id; i++) { - *(voff_ptr+2*i) = (probe->vga_ptr+i)->voff & 0x00ff; - *(voff_ptr+2*i+1) = (probe->vga_ptr+i)->voff >> 8; - } - if (i != 0) { - *(voff_ptr+2*i) = probe->comb_diff_top; - *(voff_ptr+2*i+1) = probe->comb_diff_bom; - *(voff_ptr+2*i+2) = (probe->vpos_trans&0x00FF); - *(voff_ptr+2*i+3) = (probe->vpos_trans>>8); - - vga_info.vga_addr = zero_info.zero_addr + sizeof(struct cmd_zero_info); - uint16_t *vgain_ptr = &vga_info.vga0; - for (i=0; probe->vga_ptr && (probe->vga_ptr+i)->id; i++){ - *(vgain_ptr+i) = (probe->vga_ptr+i)->vgain >> 8; - } - ret = dsl_wr_reg(sdi, CTR0_ADDR, bmEEWP); - if (ret == SR_OK) { - if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_SEEP) - real_zero_addr = zero_info.zero_addr; - else - real_zero_addr = (zero_big_addr << 8) + zero_info.zero_addr; - ret = dsl_wr_nvm(sdi, (unsigned char *)&zero_info, real_zero_addr, sizeof(struct cmd_zero_info)); - } - if (ret == SR_OK) { - if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_SEEP) - real_zero_addr = vga_info.vga_addr; - else - real_zero_addr = (zero_big_addr << 8) + vga_info.vga_addr; - ret = dsl_wr_nvm(sdi, (unsigned char *)&vga_info, real_zero_addr, sizeof(struct cmd_vga_info)); - } - ret = dsl_wr_reg(sdi, CTR0_ADDR, bmNONE); - const double slope = (probe->comb_diff_bom - probe->comb_diff_top)/(2.0*255.0); - for (i = 0; i < 256; i++) { - ret = dsl_wr_reg(sdi, COMB_ADDR + probe->index*2, i); - int value = i+i*slope+probe->comb_diff_top*0.5+0.5; - value = (value < 0) ? 0 : - (value > 255) ? 255 : value; - ret = dsl_wr_reg(sdi, COMB_ADDR + probe->index*2 + 1, value); - } - } - } - } else if (id == SR_CONF_VOCM) { - const uint8_t vocm = g_variant_get_byte(data); - ret = dsl_wr_reg(sdi, COMB_ADDR+4, vocm); - } else if (id == SR_CONF_PROBE_VGAIN) { - const uint64_t vgain = g_variant_get_uint64(data) << 8; - int i; - for (i = 0; ch->vga_ptr && (ch->vga_ptr+i)->id; i++) { - if ((ch->vga_ptr+i)->key == ch->vdiv) { - (ch->vga_ptr+i)->vgain = vgain; - } - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_VDIV)); - if (ret == SR_OK) - sr_dbg("%s: setting VDIV of channel %d to %d mv", - __func__, ch->index, ch->vdiv); - else - sr_dbg("%s: setting VDIV of channel %d to %d mv failed", - __func__, ch->index, ch->vdiv); - } else if (id == SR_CONF_PROBE_VOFF) { - uint16_t voff = g_variant_get_uint16(data); - if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_PREOFF) { - double voltage_off = (2.0 * voff / devc->profile->dev_caps.default_pwmmargin - 1) * ch->vdiv; - double trans_coarse = (ch->vdiv < 500) ? (ch->vpos_trans >> 8)/DSCOPE_TRANS_CMULTI : (ch->vpos_trans >> 8); - double trans_fine = (ch->vdiv < 500) ? (ch->vpos_trans & 0x00ff) / 1000.0 : (ch->vpos_trans & 0x00ff) / DSCOPE_TRANS_FMULTI; - - uint16_t default_voff = get_default_voff(sdi, ch); - int voff_coarse = floor(voltage_off / trans_coarse + 0.5); - int voff_fine = floor(-(voltage_off - voff_coarse*trans_coarse)/trans_fine + 0.5); - voff_coarse = (default_voff >> 10) + voff_coarse; - voff_fine = (default_voff&0x03ff) + voff_fine; - voff = (voff_coarse << 10) + voff_fine; - } - int i; - for (i = 0; ch->vga_ptr && (ch->vga_ptr+i)->id; i++) { - if ((ch->vga_ptr+i)->key == ch->vdiv) { - (ch->vga_ptr+i)->voff = voff; - } - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_VPOS)); - if (ret == SR_OK) - sr_dbg("%s: setting VPOS of channel %d to %lf mv", - __func__, ch->index, ch->vpos); - else - sr_dbg("%s: setting VPOS of channel %d to %lf mv failed", - __func__, ch->index, ch->vpos); - } else if (id == SR_CONF_PROBE_MAP_UNIT) { - ch->map_unit = g_variant_get_string(data, NULL); - } else if (id == SR_CONF_PROBE_MAP_MIN) { - ch->map_min = g_variant_get_double(data); - } else if (id == SR_CONF_PROBE_MAP_MAX) { - ch->map_max = g_variant_get_double(data); - } else { - ret = SR_ERR_NA; - } - - return ret; -} - -static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi, - const struct sr_channel_group *cg) -{ - (void)cg; - - if (dsl_config_list(key, data, sdi, cg) == SR_OK) - return SR_OK; - - switch (key) { - case SR_CONF_DEVICE_OPTIONS: -// *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, -// hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t)); - *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), - hwoptions, ARRAY_SIZE(hwoptions)*sizeof(int32_t), TRUE, NULL, NULL); - break; - case SR_CONF_DEVICE_SESSIONS: - if (sdi->mode == DSO) - *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), - sessions_dso, ARRAY_SIZE(sessions_dso)*sizeof(int32_t), TRUE, NULL, NULL); - else if (sdi->mode == ANALOG) - *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), - sessions_daq, ARRAY_SIZE(sessions_daq)*sizeof(int32_t), TRUE, NULL, NULL); - break; - case SR_CONF_OPERATION_MODE: - *data = g_variant_new_strv(opmodes, ARRAY_SIZE(opmodes)); - break; - - default: - return SR_ERR_NA; - } - - return SR_OK; -} - -static int dso_zero(const struct sr_dev_inst *sdi) -{ - struct DSL_context *devc = sdi->priv; - GSList *l; - int ret; - struct sr_usb_dev_inst *usb; - struct libusb_device_handle *hdl; - struct ctl_wr_cmd wr_cmd; - - static double vpos_back[2]; - static uint64_t vdiv_back[2]; - struct sr_channel *probe0 = NULL, *probe1 = NULL; - - usb = sdi->conn; - hdl = usb->devhdl; - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - if (probe->index == 0) - probe0 = probe; - if (probe->index == 1) - probe1 = probe; - } - - if (devc->zero_stage == -1) { - // initialize before Auto Calibration - if (dso_init(sdi) == SR_OK) - devc->zero_stage = 0; - } else if ((probe0->vga_ptr+devc->zero_stage)->key == 0) { - ret = SR_OK; - if (!(devc->profile->dev_caps.feature_caps & CAPS_FEATURE_PREOFF)) { - if (devc->zero_pcnt == 0) { - devc->zero_comb = 0; - vpos_back[0] = probe0->vpos; - probe0->vpos = (probe0->vga_ptr+devc->zero_stage-1)->key * -4.8; - vdiv_back[0] = probe0->vdiv; - probe0->vdiv = (probe0->vga_ptr+devc->zero_stage-1)->key; - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe0, SR_CONF_PROBE_VPOS)); - } else if (devc->zero_pcnt == 4) { - const double voff = 255*0.98 - (devc->mstatus.ch0_max + devc->mstatus.ch0_min) / 2.0; - if (abs(voff) < 0.5) { - probe0->vpos = vpos_back[0]; - } else { - probe0->vpos_trans += voff; - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe0, SR_CONF_PROBE_VPOS)); - devc->zero_pcnt = 1; - } - } else if (devc->zero_pcnt == 5) { - devc->zero_comb = 0; - vpos_back[1] = probe1->vpos; - probe1->vpos = (probe1->vga_ptr+devc->zero_stage-1)->key * -4.8; - vdiv_back[1] = probe1->vdiv; - probe1->vdiv = (probe1->vga_ptr+devc->zero_stage-1)->key; - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe1, SR_CONF_PROBE_VPOS)); - } else if (devc->zero_pcnt == 9) { - const double voff = 255*0.98 - (devc->mstatus.ch1_max + devc->mstatus.ch1_min) / 2.0; - if (abs(voff) < 0.5) { - probe1->vpos = vpos_back[1]; - } else { - probe1->vpos_trans += voff; - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe1, SR_CONF_PROBE_VPOS)); - devc->zero_pcnt = 6; - } - } - } - - if (devc->zero_pcnt == 10) { - ret = dsl_wr_reg(sdi, COMB_ADDR+6, 0b1101); - wr_cmd.header.dest = DSL_CTL_DSO_EN1; - wr_cmd.data[0] = (uint8_t)~bmCH_CH1; - wr_cmd.header.size = 1; - ret = command_ctl_wr(hdl, wr_cmd); - - devc->zero_comb = 0; - vpos_back[0] = probe0->vpos; - probe0->vpos = (probe0->vga_ptr+devc->zero_stage-1)->key * 4.5; - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe0, SR_CONF_PROBE_VPOS)); - } else if (devc->zero_pcnt == 15) { - probe0->comb_diff_top = (devc->mstatus.ch0_max - devc->mstatus.ch1_max) + - (devc->mstatus.ch0_min - devc->mstatus.ch1_min); - probe0->vpos = (probe0->vga_ptr+devc->zero_stage-1)->key * -4.5; - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe0, SR_CONF_PROBE_VPOS)); - } else if (devc->zero_pcnt == 20) { - probe0->comb_diff_bom = (devc->mstatus.ch0_max - devc->mstatus.ch1_max) + - (devc->mstatus.ch0_min - devc->mstatus.ch1_min); - probe0->vpos = vpos_back[0]; - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe0, SR_CONF_PROBE_VPOS)); - } - - if (devc->zero_pcnt == 25) { - ret = dsl_wr_reg(sdi, COMB_ADDR+6, 0b1110); - wr_cmd.header.dest = DSL_CTL_DSO_EN1; - wr_cmd.data[0] = bmCH_CH1; - wr_cmd.header.size = 1; - ret = command_ctl_wr(hdl, wr_cmd); - wr_cmd.header.dest = DSL_CTL_DSO_EN0; - wr_cmd.data[0] = (uint8_t)~bmCH_CH0; - wr_cmd.header.size = 1; - ret = command_ctl_wr(hdl, wr_cmd); - - devc->zero_comb = 1; - vpos_back[1] = probe1->vpos; - probe1->vpos = (probe1->vga_ptr+devc->zero_stage-1)->key * 4.5; - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe1, SR_CONF_PROBE_VPOS)); - } else if (devc->zero_pcnt == 30) { - probe1->comb_diff_top = (devc->mstatus.ch1_max - devc->mstatus.ch0_max) + - (devc->mstatus.ch1_min - devc->mstatus.ch0_min); - probe1->vpos = (probe1->vga_ptr+devc->zero_stage-1)->key * -4.5; - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe1, SR_CONF_PROBE_VPOS)); - } else if (devc->zero_pcnt == 35) { - probe1->comb_diff_bom = (devc->mstatus.ch1_max - devc->mstatus.ch0_max) + - (devc->mstatus.ch1_min - devc->mstatus.ch0_min); - probe1->vpos = vpos_back[1]; - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe1, SR_CONF_PROBE_VPOS)); - } - - if (devc->zero_pcnt == 40) { - if (!(devc->profile->dev_caps.feature_caps & CAPS_FEATURE_PREOFF)) { - probe0->vdiv = vdiv_back[0]; - probe1->vdiv = vdiv_back[1]; - } - ret = dsl_wr_reg(sdi, COMB_ADDR+6, 0b0011); - wr_cmd.header.dest = DSL_CTL_DSO_EN0; - wr_cmd.data[0] = bmCH_CH0; - wr_cmd.header.size = 1; - ret = command_ctl_wr(hdl, wr_cmd); - - devc->zero = FALSE; - dso_init(sdi); - } - - if (ret == SR_OK) - devc->zero_pcnt++; - } else { - if (devc->zero_pcnt == 0) { - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - uint64_t vdiv_back = probe->vdiv; - probe->vdiv = (probe->vga_ptr+devc->zero_stage)->key; - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_VDIV)); - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_VPOS)); - probe->vdiv = vdiv_back; - } - } - - if (devc->zero_pcnt == 4) { - const double voff0 = 255/2.0 - (devc->mstatus.ch0_max + devc->mstatus.ch0_min)/2.0; - const double voff1 = 255/2.0 - (devc->mstatus.ch1_max + devc->mstatus.ch1_min)/2.0; - if (abs(voff0) < 0.5 && abs(voff1) < 0.5) { - devc->zero_stage++; - } else { - if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_PREOFF) { - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - double trans_coarse = ((probe->vga_ptr+devc->zero_stage)->key < 500) ? (probe->vpos_trans >> 8)/DSCOPE_TRANS_CMULTI : (probe->vpos_trans >> 8); - double trans_fine = ((probe->vga_ptr+devc->zero_stage)->key < 500) ? (probe->vpos_trans & 0x00ff) / 1000.0 : (probe->vpos_trans & 0x00ff) / DSCOPE_TRANS_FMULTI; - - double voltage_off = ((probe->index == 0) ? voff0 : voff1) * (probe->vga_ptr+devc->zero_stage)->key * 10 / 255.0; - uint16_t last_voff = (probe->vga_ptr+devc->zero_stage)->voff; - int voff_coarse = floor(voltage_off / trans_coarse + 0.5); - int voff_fine = floor(-(voltage_off - voff_coarse*trans_coarse)/trans_fine + 0.5); - voff_coarse = (last_voff >> 10) + voff_coarse; - voff_fine = (last_voff&0x03ff) + voff_fine; - (probe->vga_ptr+devc->zero_stage)->voff = (voff_coarse << 10) + voff_fine; - } - } else { - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - if (probe->index == 0) - (probe->vga_ptr+devc->zero_stage)->voff += voff0; - else - (probe->vga_ptr+devc->zero_stage)->voff += voff1; - } - } - } - devc->zero_pcnt = 0; - } else { - devc->zero_pcnt++; - } - } - - return ret; -} - -static int dev_open(struct sr_dev_inst *sdi) -{ - gboolean fpga_done; - int ret; - GSList *l; - gboolean zeroed; - - if ((ret = dsl_dev_open(di, sdi, &fpga_done)) == SR_OK) { - // load zero informations - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - zeroed = dso_load_eep(sdi, probe, fpga_done); - if (!zeroed) - break; - } - if (!zeroed) { - config_set(SR_CONF_ZERO, g_variant_new_boolean(TRUE), sdi, NULL, NULL); - sr_info("Zero have not been setted!"); - } - if (!fpga_done) - dso_init(sdi); - } - - return ret; -} - -static int dev_close(struct sr_dev_inst *sdi) -{ - int ret; - ret = dsl_dev_close(sdi); - return ret; -} - -static int cleanup(void) -{ - int ret; - struct drv_context *drvc; - - if (!(drvc = di->priv)) - return SR_OK; - - ret = dev_clear(); - - g_free(drvc); - di->priv = NULL; - - return ret; -} - -static void remove_sources(struct DSL_context *devc) -{ - int i; - sr_info("%s: remove fds from polling", __func__); - /* Remove fds from polling. */ - for (i = 0; devc->usbfd[i] != -1; i++) - sr_source_remove(devc->usbfd[i]); - g_free(devc->usbfd); -} - -static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) -{ - int completed = 0; - struct timeval tv; - struct drv_context *drvc; - struct DSL_context *devc; - - (void)fd; - (void)revents; - - drvc = di->priv; - devc = sdi->priv; - - tv.tv_sec = tv.tv_usec = 0; - libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv, &completed); - - if (devc->zero && devc->trf_completed) { - dso_zero(sdi); - } - - if (devc->status == DSL_FINISH) { - remove_sources(devc); - } - - devc->trf_completed = 0; - return TRUE; -} - -static int dev_acquisition_start(struct sr_dev_inst *sdi, void *cb_data) -{ - (void)cb_data; - - struct DSL_context *devc; - struct sr_usb_dev_inst *usb; - struct drv_context *drvc; - const struct libusb_pollfd **lupfd; - unsigned int i; - int ret; - struct ctl_wr_cmd wr_cmd; - - if (sdi->status != SR_ST_ACTIVE) - return SR_ERR_DEV_CLOSED; - - drvc = di->priv; - devc = sdi->priv; - usb = sdi->conn; - - //devc->cb_data = cb_data; - devc->cb_data = sdi; - devc->num_samples = 0; - devc->empty_transfer_count = 0; - devc->status = DSL_INIT; - devc->num_transfers = 0; - devc->submitted_transfers = 0; - devc->actual_samples = (devc->limit_samples + 1023) & ~1023; - devc->abort = FALSE; - devc->mstatus_valid = FALSE; - devc->overflow = FALSE; - - /* Configures devc->trigger_* and devc->sample_wide */ - if (dsl_configure_probes(sdi) != SR_OK) { - sr_err("%s: Failed to configure probes.", __func__); - return SR_ERR; - } - - /* Stop Previous GPIF acquisition */ - wr_cmd.header.dest = DSL_CTL_STOP; - wr_cmd.header.size = 0; - if ((ret = command_ctl_wr(usb->devhdl, wr_cmd)) != SR_OK) { - sr_err("%s: Stop DSCope acquisition failed!", __func__); - return ret; - } else { - sr_info("%s: Stop Previous DSCope acquisition!", __func__); - } - - /* Arm FPGA before acquisition start*/ - if ((ret = dsl_fpga_arm(sdi)) != SR_OK) { - sr_err("%s: Arm FPGA failed!", __func__); - return ret; - } - - if (devc->zero && devc->zero_stage == -1) { - // initialize before Auto Calibration - if ((ret = dso_init(sdi)) == SR_OK) { - devc->zero_stage = 0; - } else { - sr_err("%s: DSO zero initialization failed!", __func__); - return ret; - } - devc->zero_stage = 0; - } - - /* - * settings must be updated before acquisition - */ - if (sdi->mode == DSO) { - devc->trigger_hpos = devc->trigger_hrate * dsl_en_ch_num(sdi) * devc->limit_samples / 200.0; - if ((ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_HORIZ_TRIGGERPOS))) == SR_OK) - sr_dbg("%s: setting DSO Horiz Trigger Position to %d", - __func__, devc->trigger_hpos); - else - sr_dbg("%s: setting DSO Horiz Trigger Position to %d failed", - __func__, devc->trigger_hpos); - } - - /* setup and submit usb transfer */ - if ((ret = dsl_start_transfers(devc->cb_data)) != SR_OK) { - sr_err("%s: Could not submit usb transfer" - "(%d)%d", __func__, ret, errno); - return ret; - } - - /* setup callback function for data transfer */ - lupfd = libusb_get_pollfds(drvc->sr_ctx->libusb_ctx); - for (i = 0; lupfd[i]; i++); - if (!(devc->usbfd = g_try_malloc(sizeof(struct libusb_pollfd) * (i + 1)))) - return SR_ERR; - for (i = 0; lupfd[i]; i++) { - sr_source_add(lupfd[i]->fd, lupfd[i]->events, - dsl_get_timeout(devc), receive_data, sdi); - devc->usbfd[i] = lupfd[i]->fd; - } - devc->usbfd[i] = -1; - free(lupfd); - - wr_cmd.header.dest = DSL_CTL_START; - wr_cmd.header.size = 0; - if ((ret = command_ctl_wr(usb->devhdl, wr_cmd)) != SR_OK) { - devc->status = DSL_ERROR; - devc->abort = TRUE; - return ret; - } - devc->status = DSL_START; - - /* Send header packet to the session bus. */ - //std_session_send_df_header(cb_data, LOG_PREFIX); - std_session_send_df_header(sdi, LOG_PREFIX); - - return SR_OK; -} - -static int dev_acquisition_stop(const struct sr_dev_inst *sdi, void *cb_data) -{ - int ret = dsl_dev_acquisition_stop(sdi, cb_data); - return ret; -} - -static int dev_status_get(const struct sr_dev_inst *sdi, struct sr_status *status, gboolean prg, int begin, int end) -{ - int ret = dsl_dev_status_get(sdi, status, prg, begin, end); - return ret; -} - -SR_PRIV struct sr_dev_driver DSCope_driver_info = { - .name = "DSCope", - .longname = "DSCope (generic driver for DScope oscilloscope)", - .api_version = 1, - .init = init, - .cleanup = cleanup, - .scan = scan, - .dev_list = dev_list, - .dev_mode_list = dev_mode_list, - .dev_clear = dev_clear, - .config_get = config_get, - .config_set = config_set, - .config_list = config_list, - .dev_open = dev_open, - .dev_close = dev_close, - .dev_status_get = dev_status_get, - .dev_acquisition_start = dev_acquisition_start, - .dev_acquisition_stop = dev_acquisition_stop, - .priv = NULL, -}; +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2013 Bert Vermeulen + * Copyright (C) 2013 DreamSourceLab + * + * 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 . + */ + +#include "libsigrok.h" +#include "libsigrok-internal.h" + +#include "dsl.h" +#include "command.h" + + +enum { + /** Normal */ + OP_NORMAL = 0, + /** Internal pattern test mode */ + OP_INTEST = 1, +}; + +static const char *opmodes_cn[] = { + "正常", + "内部测试", +}; + +static const char *opmodes[] = { + "Normal", + "Internal Test", +}; + +enum { + BW_FULL = 0, + BW_20M = 1, +}; + +static const char *bandwidths_cn[] = { + "全带宽", + "20MHz", +}; + +static const char *bandwidths[] = { + "Full Bandwidth", + "20MHz", +}; + +static const int32_t hwoptions[] = { + SR_CONF_OPERATION_MODE, + SR_CONF_BANDWIDTH_LIMIT, +}; + +static const int32_t sessions_dso[] = { + SR_CONF_OPERATION_MODE, + SR_CONF_TIMEBASE, + SR_CONF_TRIGGER_SLOPE, + SR_CONF_TRIGGER_SOURCE, + SR_CONF_TRIGGER_CHANNEL, + SR_CONF_HORIZ_TRIGGERPOS, + SR_CONF_TRIGGER_HOLDOFF, + SR_CONF_TRIGGER_MARGIN, +}; + +static const int32_t sessions_daq[] = { + SR_CONF_SAMPLERATE, + SR_CONF_LIMIT_SAMPLES, + SR_CONF_OPERATION_MODE, + SR_CONF_TIMEBASE, + SR_CONF_TRIGGER_SLOPE, + SR_CONF_TRIGGER_SOURCE, + SR_CONF_TRIGGER_CHANNEL, + SR_CONF_HORIZ_TRIGGERPOS, + SR_CONF_TRIGGER_HOLDOFF, + SR_CONF_TRIGGER_MARGIN, +}; + +static const uint8_t zero_base_addr = 0x40; +static const uint8_t zero_big_addr = 0x20; + +SR_PRIV struct sr_dev_driver DSCope_driver_info; +static struct sr_dev_driver *di = &DSCope_driver_info; + +static const char ** get_opmodes(struct DSL_context *devc) +{ + if (devc->language == LANGUAGE_CN) + return opmodes_cn; + else + return opmodes; +} + +static const char ** get_bandwidths(struct DSL_context *devc) +{ + if (devc->language == LANGUAGE_CN) + return bandwidths_cn; + else + return bandwidths; +} + +static uint16_t get_default_preoff(const struct sr_dev_inst *sdi, const struct sr_channel* ch) +{ + int i; + struct DSL_context *devc = sdi->priv; + for (i = 0; vga_defaults[i].id; i++) { + if (vga_defaults[i].id == devc->profile->dev_caps.vga_id && + vga_defaults[i].key == ch->vdiv) { + if (ch->index == 1) + return vga_defaults[i].preoff_comp; + else + return vga_defaults[i].preoff; + } + } + + return 0; +} + +static struct DSL_context *DSCope_dev_new(const struct DSL_profile *prof) +{ + struct DSL_context *devc; + unsigned int i; + + if (!(devc = g_try_malloc(sizeof(struct DSL_context)))) { + sr_err("Device context malloc failed."); + return NULL; + } + + for (i = 0; i < ARRAY_SIZE(channel_modes); i++) + assert(channel_modes[i].id == i); + + devc->channel = NULL; + devc->profile = prof; + devc->fw_updated = 0; + devc->cur_samplerate = devc->profile->dev_caps.default_samplerate; + devc->limit_samples = devc->profile->dev_caps.default_samplelimit; + devc->clock_type = FALSE; + devc->clock_edge = FALSE; + devc->instant = FALSE; + devc->op_mode = OP_NORMAL; + devc->test_mode = SR_TEST_NONE; + devc->stream = FALSE; + devc->ch_mode = devc->profile->dev_caps.default_channelmode; + devc->th_level = SR_TH_3V3; + devc->filter = SR_FILTER_NONE; + devc->timebase = 10000; + devc->trigger_slope = DSO_TRIGGER_RISING; + devc->trigger_source = DSO_TRIGGER_AUTO; + devc->trigger_holdoff = 0; + devc->trigger_hpos = 0x0; + devc->trigger_hrate = 0; + devc->zero = FALSE; + devc->tune = FALSE; + devc->data_lock = FALSE; + devc->cali = FALSE; + devc->trigger_margin = 8; + devc->trigger_channel = 0; + devc->rle_mode = FALSE; + devc->status = DSL_FINISH; + devc->bw_limit = BW_FULL; + + dsl_adjust_samplerate(devc); + return devc; +} + +static int dev_clear(void) +{ + return std_dev_clear(di, NULL); +} + +static int init(struct sr_context *sr_ctx) +{ + return std_hw_init(sr_ctx, di, LOG_PREFIX); +} + +static GSList *scan(GSList *options) +{ + struct drv_context *drvc; + struct DSL_context *devc; + struct sr_dev_inst *sdi; + struct sr_usb_dev_inst *usb; + struct sr_config *src; + const struct DSL_profile *prof; + GSList *l, *devices, *conn_devices; + struct libusb_device_descriptor des; + libusb_device **devlist; + int devcnt, ret, i, j; + const char *conn; + + drvc = di->priv; + + conn = NULL; + for (l = options; l; l = l->next) { + src = l->data; + switch (src->key) { + case SR_CONF_CONN: + conn = g_variant_get_string(src->data, NULL); + break; + } + } + if (conn) + conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn); + else + conn_devices = NULL; + + /* Find all DSCope compatible devices and upload firmware to them. */ + devices = NULL; + libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); + for (i = 0; devlist[i]; i++) { + if (conn) { + usb = NULL; + for (l = conn_devices; l; l = l->next) { + usb = l->data; + if (usb->bus == libusb_get_bus_number(devlist[i]) + && usb->address == libusb_get_device_address(devlist[i])) + break; + } + if (!l) + /* This device matched none of the ones that + * matched the conn specification. */ + continue; + } + + if ((ret = libusb_get_device_descriptor( devlist[i], &des)) != 0) { + sr_warn("Failed to get device descriptor: %s.", + libusb_error_name(ret)); + continue; + } + + prof = NULL; + for (j = 0; supported_DSCope[j].vid; j++) { + if (des.idVendor == supported_DSCope[j].vid && + des.idProduct == supported_DSCope[j].pid) { + prof = &supported_DSCope[j]; + } + } + + /* Skip if the device was not found. */ + if (!prof) + continue; + + devcnt = g_slist_length(drvc->instances); + devc = DSCope_dev_new(prof); + if (!devc) + return NULL; + sdi = sr_dev_inst_new(channel_modes[devc->ch_mode].mode, devcnt, SR_ST_INITIALIZING, + prof->vendor, prof->model, prof->model_version); + if (!sdi) { + g_free(devc); + return NULL; + } + sdi->priv = devc; + sdi->driver = di; + + drvc->instances = g_slist_append(drvc->instances, sdi); + //devices = g_slist_append(devices, sdi); + + /* Fill in probelist according to this device's profile. */ + if (dsl_setup_probes(sdi, channel_modes[devc->ch_mode].num) != SR_OK) + return NULL; + + if (dsl_check_conf_profile(devlist[i])) { + /* Already has the firmware, so fix the new address. */ + sr_dbg("Found an DSCope device."); + sdi->status = SR_ST_INACTIVE; + sdi->inst_type = SR_INST_USB; + sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), + libusb_get_device_address(devlist[i]), NULL); + /* only report device after firmware is ready */ + devices = g_slist_append(devices, sdi); + } else { + char *firmware; + if (!(firmware = g_try_malloc(strlen(DS_RES_PATH)+strlen(prof->firmware)+1))) { + sr_err("Firmware path malloc error!"); + return NULL; + } + strcpy(firmware, DS_RES_PATH); + strcat(firmware, prof->firmware); + if (ezusb_upload_firmware(devlist[i], USB_CONFIGURATION, + firmware) == SR_OK) + /* Store when this device's FW was updated. */ + devc->fw_updated = g_get_monotonic_time(); + else + sr_err("Firmware upload failed for " + "device %d.", devcnt); + g_free(firmware); + sdi->inst_type = SR_INST_USB; + sdi->conn = sr_usb_dev_inst_new (libusb_get_bus_number(devlist[i]), + 0xff, NULL); + } + } + libusb_free_device_list(devlist, 1); + g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free); + + return devices; +} + +static GSList *dev_list(void) +{ + return ((struct drv_context *)(di->priv))->instances; +} + +static const GSList *dev_mode_list(const struct sr_dev_inst *sdi) +{ + return dsl_mode_list(sdi); +} + +static uint64_t dso_vga(const struct sr_channel* ch) +{ + int i; + for (i = 0; ch->vga_ptr && (ch->vga_ptr+i)->id; i++) { + if ((ch->vga_ptr+i)->key == ch->vdiv) + return (ch->vga_ptr+i)->vgain; + } + + return 0; +} + +static uint64_t dso_preoff(const struct sr_channel* ch) +{ + int i; + for (i = 0; ch->vga_ptr && (ch->vga_ptr+i)->id; i++) { + if ((ch->vga_ptr+i)->key == ch->vdiv) + return (ch->vga_ptr+i)->preoff; + } + return 0; +} + +static uint64_t dso_offset(const struct sr_dev_inst *sdi, const struct sr_channel* ch) +{ + uint64_t pwm_off = 0; + int offset_coarse, offset_fine; + int trans_coarse, trans_fine; + struct DSL_context *devc = sdi->priv; + const double offset_mid = (1 << (ch->bits - 1)); + const double offset_max = ((1 << ch->bits) - 1.0); + const uint64_t offset = devc->zero ? ch->zero_offset : ch->hw_offset; + double comb_off = 2.0 / (pow(10, 24.0*ch->comb_comp/20/4096) - 1); +// const double comb_compensate = ((devc->profile->dev_caps.feature_caps & CAPS_FEATURE_HMCAD1511) && +// (dsl_en_ch_num(sdi) == 1))? (offset - offset_mid) / comb_off : 0; + const double comb_compensate = ((ch->comb_comp != 0) && (dsl_en_ch_num(sdi) == 1)) ? (offset - offset_mid) / comb_off : 0; + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_PREOFF) { + trans_coarse = (ch->vpos_trans & 0xFF00) >> 8; + trans_fine = (ch->vpos_trans & 0x00FF); + const double voltage = (offset_mid - offset) / offset_max * ch->vdiv * DS_CONF_DSO_VDIVS; + if (ch->vdiv < 500) { + offset_coarse = floor(-voltage*DSCOPE_TRANS_CMULTI/trans_coarse + 0.5); + offset_fine = floor((voltage + offset_coarse*trans_coarse/DSCOPE_TRANS_CMULTI)*1000.0/trans_fine + 0.5); + } else { + offset_coarse = floor(-voltage/trans_coarse + 0.5); + offset_fine = floor((voltage + offset_coarse*trans_coarse)*DSCOPE_TRANS_FMULTI/trans_fine + 0.5); + } + } else { + pwm_off = (offset + comb_compensate) / offset_max * ch->vpos_trans; + } + + const uint64_t preoff = dso_preoff(ch); + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_PREOFF) + return (offset << 32) + + ((offset_coarse + DSCOPE_CONSTANT_BIAS + (preoff>>10)) << 16) + offset_fine + + (preoff & 0x03ff); + else + return (offset << 32) + + pwm_off + preoff; +} + +static uint64_t dso_cmd_gen(const struct sr_dev_inst *sdi, struct sr_channel* ch, int id) +{ + struct DSL_context *devc; + uint64_t cmd = 0; + uint64_t offset; + GSList *l; + const int ch_bit = 7; + devc = sdi->priv; + + switch (id) { + case SR_CONF_PROBE_EN: + case SR_CONF_PROBE_COUPLING: + if (devc->zero || sdi->mode == ANALOG || dsl_en_ch_num(sdi) == 2) { + cmd += 0x0E00; + //cmd += 0x000; + } else if (dsl_en_ch_num(sdi) == 1) { + if (((ch->index == 0) && ch->enabled) || ((ch->index == 1) && !ch->enabled)) + cmd += 0x1600; + else if (((ch->index == 1) && ch->enabled) || ((ch->index == 0) && !ch->enabled)) + cmd += 0x1A00; + } else { + return 0x0; + } + + cmd += ch->index << ch_bit; + if (devc->zero || ch->coupling == SR_DC_COUPLING) + cmd += 0x100; + else if (ch->coupling == SR_GND_COUPLING) + cmd &= 0xFFFFFDFF; + break; + case SR_CONF_PROBE_VDIV: + case SR_CONF_TIMEBASE: + cmd += 0x8; + cmd += ch->index << ch_bit; + // --VGAIN + uint64_t vgain = dso_vga(ch); +// if ((devc->profile->dev_caps.feature_caps & CAPS_FEATURE_HMCAD1511) && +// (dsl_en_ch_num(sdi) == 1)) + if ((ch->comb_comp != 0) && (dsl_en_ch_num(sdi) == 1)) + vgain += (uint64_t)(ch->comb_comp) << 8; + cmd += vgain; + break; + case SR_CONF_PROBE_OFFSET: + cmd += 0x10; + cmd += ch->index << ch_bit; + ch->hw_offset = ch->offset; + offset = dso_offset(sdi, ch); + cmd += (offset << 8); + break; + case SR_CONF_SAMPLERATE: + cmd += 0x18; + uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(channel_modes[devc->ch_mode].max_samplerate * 1.0 / devc->cur_samplerate / dsl_en_ch_num(sdi)); + cmd += divider << 8; + break; + case SR_CONF_HORIZ_TRIGGERPOS: + cmd += 0x20; + cmd += devc->trigger_hpos << 8; + break; + case SR_CONF_TRIGGER_SLOPE: + cmd += 0x28; + cmd += devc->trigger_slope << 8; + break; + case SR_CONF_TRIGGER_SOURCE: + cmd += 0x30; + cmd += devc->zero ? 0x0 : devc->trigger_source << 8; + break; + case SR_CONF_TRIGGER_VALUE: + cmd += 0x38; + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + cmd += probe->trig_value << (8 * (probe->index + 1)); + } + break; + case SR_CONF_TRIGGER_MARGIN: + cmd += 0x40; + cmd += ((uint64_t)devc->trigger_margin << 8); + break; + case SR_CONF_TRIGGER_HOLDOFF: + cmd += 0x58; + cmd += ((uint64_t)devc->trigger_holdoff << 8); + break; + case SR_CONF_DSO_SYNC: + cmd = 0xa5a5a500; + break; + default: + cmd = 0xFFFFFFFF; + } + + return cmd; +} + +static int dso_init(const struct sr_dev_inst *sdi) +{ + int ret; + GSList *l; + + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_COUPLING)); + if (ret != SR_OK) { + sr_err("DSO set coupling of channel %d command failed!", probe->index); + return ret; + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_VDIV)); + if (ret != SR_OK) { + sr_err("Set VDIV of channel %d command failed!", probe->index); + return ret; + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_OFFSET)); + if (ret != SR_OK) { + sr_err("Set OFFSET of channel %d command failed!", probe->index); + return ret; + } + } + + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); + if (ret != SR_OK) { + sr_err("Set Sample Rate command failed!"); + return ret; + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_HORIZ_TRIGGERPOS)); + if (ret != SR_OK) { + sr_err("Set Horiz Trigger Position command failed!"); + return ret; + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_HOLDOFF)); + if (ret != SR_OK) { + sr_err("Set Trigger Holdoff Time command failed!"); + return ret; + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SLOPE)); + if (ret != SR_OK) { + sr_err("Set Trigger Slope command failed!"); + return ret; + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); + if (ret != SR_OK) { + sr_err("Set Trigger Source command failed!"); + return ret; + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_VALUE)); + if (ret != SR_OK) { + sr_err("Set Trigger Value command failed!"); + return ret; + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_MARGIN)); + if (ret != SR_OK) { + sr_err("Set Trigger Margin command failed!"); + return ret; + } + return ret; +} + +static gboolean dso_load_eep(struct sr_dev_inst *sdi, struct sr_channel *probe, gboolean fpga_done) +{ + struct DSL_context *devc; + int ret, i; + uint16_t real_zero_addr; + + devc = sdi->priv; + struct cmd_zero_info zero_info; + uint8_t dst_addr = (zero_base_addr + + probe->index * (sizeof(struct cmd_zero_info) + sizeof(struct cmd_vga_info))); + zero_info.zero_addr = dst_addr; + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_SEEP) + real_zero_addr = zero_info.zero_addr; + else + real_zero_addr = (zero_big_addr << 8) + zero_info.zero_addr; + if ((ret = dsl_rd_nvm(sdi, (unsigned char *)&zero_info, real_zero_addr, sizeof(struct cmd_zero_info))) != SR_OK) { + return FALSE; + sr_err("%s: Send Get Zero command failed!", __func__); + } else { + if (zero_info.zero_addr == dst_addr) { + uint8_t* preoff_ptr = &zero_info.zero_addr + 1; + for (i = 0; probe->vga_ptr && (probe->vga_ptr+i)->id; i++) { + (probe->vga_ptr+i)->preoff = (*(preoff_ptr + 2*i+1) << 8) + *(preoff_ptr + 2*i); + } + if (i != 0) { + probe->comb_diff_top = *(preoff_ptr + 2*i); + probe->comb_diff_bom = *(preoff_ptr + 2*i + 1); + probe->vpos_trans = *(preoff_ptr + 2*i + 2) + (*(preoff_ptr + 2*i + 3) << 8); + probe->comb_comp = *(preoff_ptr + 2*i + 4); + if (!fpga_done) { + const double slope = (probe->comb_diff_bom - probe->comb_diff_top)/(2.0*255.0); + for (i = 0; i < 256; i++) { + ret = dsl_wr_reg(sdi, COMB_ADDR + probe->index*2, i); + int value = i+i*slope+probe->comb_diff_top*0.5+0.5; + value = (value < 0) ? 0 : + (value > 255) ? 255 : value; + ret = dsl_wr_reg(sdi, COMB_ADDR + probe->index*2 + 1, value); + } + } + } + } else { + return FALSE; + } + } + + struct cmd_vga_info vga_info; + vga_info.vga_addr = dst_addr + sizeof(struct cmd_zero_info); + if (devc ->profile->dev_caps.feature_caps & CAPS_FEATURE_SEEP) + real_zero_addr = vga_info.vga_addr; + else + real_zero_addr = (zero_big_addr << 8) + vga_info.vga_addr; + if ((ret = dsl_rd_nvm(sdi, (unsigned char *)&vga_info, real_zero_addr, sizeof(struct cmd_vga_info))) != SR_OK) { + return FALSE; + sr_err("%s: Send Get Zero command failed!", __func__); + } else { + if (vga_info.vga_addr == dst_addr + sizeof(struct cmd_zero_info)) { + uint16_t* vgain_ptr = &vga_info.vga0; + for (i = 0; probe->vga_ptr && (probe->vga_ptr+i)->id; i++) { + (probe->vga_ptr+i)->vgain = *(vgain_ptr + i) << 8; + } + } else { + return FALSE; + } + } + + return TRUE; +} + +static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, + const struct sr_channel *ch, + const struct sr_channel_group *cg) +{ + unsigned int i; + int ret; + struct DSL_context *devc = sdi->priv; + + ret = dsl_config_get(id, data, sdi, ch, cg); + if (ret != SR_OK) { + switch (id) { + case SR_CONF_OPERATION_MODE: + if (!sdi) + return SR_ERR; + *data = g_variant_new_string(get_opmodes(devc)[devc->op_mode]); + break; + case SR_CONF_BANDWIDTH_LIMIT: + if (!sdi) + return SR_ERR; + *data = g_variant_new_string(get_bandwidths(devc)[devc->bw_limit]); + break; + case SR_CONF_CALI: + if (!sdi) + return SR_ERR; + *data = g_variant_new_boolean(devc->cali); + break; + case SR_CONF_TEST: + if (!sdi) + return SR_ERR; + *data = g_variant_new_boolean(FALSE); + break; + case SR_CONF_STREAM: + if (!sdi) + return SR_ERR; + *data = g_variant_new_boolean(devc->stream); + break; + case SR_CONF_MAX_DSO_SAMPLERATE: + if (!sdi) + return SR_ERR; + *data = g_variant_new_uint64(channel_modes[devc->ch_mode].max_samplerate); + break; + case SR_CONF_MAX_DSO_SAMPLELIMITS: + if (!sdi) + return SR_ERR; + *data = g_variant_new_uint64(devc->profile->dev_caps.dso_depth); + break; + case SR_CONF_HW_DEPTH: + if (!sdi) + return SR_ERR; + *data = g_variant_new_uint64(devc->profile->dev_caps.hw_depth / channel_modes[devc->ch_mode].unit_bits); + break; + case SR_CONF_PROBE_VGAIN: + if (!sdi || !ch) + return SR_ERR; + *data = g_variant_new_uint64(dso_vga(ch)>>8); + break; + case SR_CONF_PROBE_COMB_COMP_EN: + if (!sdi || !ch) + return SR_ERR; + *data = g_variant_new_boolean(devc->profile->dev_caps.feature_caps & CAPS_FEATURE_HMCAD1511); + break; + case SR_CONF_PROBE_COMB_COMP: + if (!sdi || !ch) + return SR_ERR; + *data = g_variant_new_int16(ch->comb_comp); + break; + case SR_CONF_PROBE_VGAIN_DEFAULT: + if (!sdi || !ch) + return SR_ERR; + for (i = 0; vga_defaults[i].id; i++) { + if (vga_defaults[i].id == devc->profile->dev_caps.vga_id && + vga_defaults[i].key == ch->vdiv) { + *data = g_variant_new_uint64(vga_defaults[i].vgain >> 8); + break; + } + } + break; + case SR_CONF_PROBE_VGAIN_RANGE: + if (!sdi) + return SR_ERR; + uint16_t vgain_default = 0; + for (i = 0; vga_defaults[i].id; i++) { + if (vga_defaults[i].id == devc->profile->dev_caps.vga_id && + vga_defaults[i].key == ch->vdiv) { + vgain_default = vga_defaults[i].vgain; + break; + } + } + vgain_default = (vgain_default>>8) & 0x0FFF; + *data = g_variant_new_uint16(min(CALI_VGAIN_RANGE, vgain_default*2)); + break; + case SR_CONF_PROBE_PREOFF: + if (!sdi || !ch) + return SR_ERR; + uint16_t preoff = dso_preoff(ch); + uint16_t preoff_default = get_default_preoff(sdi, ch); + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_PREOFF) { + int preoff_skew_coarse = (preoff >> 10) - (preoff_default >> 10); + int preoff_skew_fine = (preoff & 0x03ff) - (preoff_default & 0x03ff); + double trans_coarse = (ch->vdiv < 500) ? (ch->vpos_trans >> 8)/DSCOPE_TRANS_CMULTI : (ch->vpos_trans >> 8); + double trans_fine = (ch->vdiv < 500) ? (ch->vpos_trans & 0x00ff) / 1000.0 : (ch->vpos_trans & 0x00ff) / DSCOPE_TRANS_FMULTI; + double preoff_rate = (preoff_skew_coarse*trans_coarse - preoff_skew_fine*trans_fine) / ch->vdiv; + preoff = (preoff_rate * 0.5 + 0.5) * devc->profile->dev_caps.default_pwmmargin; + } + *data = g_variant_new_uint16(preoff); + break; + case SR_CONF_PROBE_PREOFF_DEFAULT: + if (!sdi || !ch) + return SR_ERR; + *data = g_variant_new_uint16(get_default_preoff(sdi, ch)); + break; + case SR_CONF_PROBE_PREOFF_MARGIN: + if (!sdi) + return SR_ERR; + *data = g_variant_new_uint16(devc->profile->dev_caps.default_pwmmargin); + break; + case SR_CONF_PROBE_MAP_DEFAULT: + if (!sdi || !ch) + return SR_ERR; + *data = g_variant_new_boolean(ch->map_default); + break; + case SR_CONF_PROBE_MAP_UNIT: + if (!sdi || !ch) + return SR_ERR; + *data = g_variant_new_string(ch->map_unit); + break; + case SR_CONF_PROBE_MAP_MIN: + if (!sdi || !ch) + return SR_ERR; + *data = g_variant_new_double(ch->map_min); + break; + case SR_CONF_PROBE_MAP_MAX: + if (!sdi || !ch) + return SR_ERR; + *data = g_variant_new_double(ch->map_max); + break; + case SR_CONF_VLD_CH_NUM: + if (!sdi) + return SR_ERR; + *data = g_variant_new_int16(channel_modes[devc->ch_mode].vld_num); + break; + default: + return SR_ERR_NA; + } + } + + return SR_OK; +} + +static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, + struct sr_channel *ch, + struct sr_channel_group *cg ) +{ + struct DSL_context *devc; + const char *stropt; + int ret, num_probes; + struct sr_usb_dev_inst *usb; + struct libusb_device_handle *hdl; + struct ctl_wr_cmd wr_cmd; + unsigned int i; + GSList *l; + + (void)cg; + + if (sdi->status != SR_ST_ACTIVE) { + return SR_ERR; + } + + devc = sdi->priv; + usb = sdi->conn; + hdl = usb->devhdl; + + ret = dsl_config_set(id, data, sdi, ch, cg); + if (ret == SR_OK) + return ret; + + ret = SR_OK; + if (id == SR_CONF_CLOCK_TYPE) { + devc->clock_type = g_variant_get_boolean(data); + } else if (id == SR_CONF_CLOCK_EDGE) { + devc->clock_edge = g_variant_get_boolean(data); + } else if (id == SR_CONF_LIMIT_SAMPLES) { + devc->limit_samples = g_variant_get_uint64(data); + } else if (id == SR_CONF_PROBE_VDIV) { + ch->vdiv = g_variant_get_uint64(data); + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_VDIV)); + if (ret == SR_OK) + sr_dbg("%s: setting VDIV of channel %d to %d mv", + __func__, ch->index, ch->vdiv); + else + sr_dbg("%s: setting VDIV of channel %d to %d mv failed", + __func__, ch->index, ch->vdiv); + } else if (id == SR_CONF_PROBE_FACTOR) { + ch->vfactor = g_variant_get_uint64(data); + sr_dbg("%s: setting Factor of channel %d to %d", __func__, + ch->index, ch->vfactor); + } else if (id == SR_CONF_TIMEBASE) { + devc->timebase = g_variant_get_uint64(data); + } else if (id == SR_CONF_PROBE_COUPLING) { + ch->coupling = g_variant_get_byte(data); + if (ch->coupling == SR_GND_COUPLING) + ch->coupling = SR_DC_COUPLING; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_COUPLING)); + if (ret == SR_OK) + sr_dbg("%s: setting AC COUPLING of channel %d to %d", + __func__, ch->index, ch->coupling); + else + sr_dbg("%s: setting AC COUPLING of channel %d to %d failed", + __func__, ch->index, ch->coupling); + } else if (id == SR_CONF_TRIGGER_SLOPE) { + devc->trigger_slope = g_variant_get_byte(data); + if (sdi->mode == DSO) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SLOPE)); + } + if (ret == SR_OK) + sr_dbg("%s: setting DSO Trigger Slope to %d", + __func__, devc->trigger_slope); + else + sr_dbg("%s: setting DSO Trigger Slope to %d failed", + __func__, devc->trigger_slope); + } else if (id == SR_CONF_TRIGGER_VALUE) { + ch->trig_value = g_variant_get_byte(data); + if (sdi->mode == DSO) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_TRIGGER_VALUE)); + } + if (ret == SR_OK) + sr_dbg("%s: setting channel %d Trigger Value to %d", + __func__, ch->index, ch->trig_value); + else + sr_dbg("%s: setting DSO Trigger Value to %d failed", + __func__, ch->index, ch->trig_value); + } else if (id == SR_CONF_HORIZ_TRIGGERPOS) { + if (sdi->mode == DSO) { + devc->trigger_hrate = g_variant_get_byte(data); + //devc->trigger_hpos = devc->trigger_hrate * dsl_en_ch_num(sdi) * devc->limit_samples / 200.0; + /* + * devc->trigger_hpos should be updated before each acquisition + * because the samplelimits may changed + */ + devc->trigger_hpos = devc->trigger_hrate * dsl_en_ch_num(sdi) * devc->limit_samples / 200.0; + if ((ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_HORIZ_TRIGGERPOS))) == SR_OK) + sr_dbg("%s: setting DSO Horiz Trigger Position to %d", + __func__, devc->trigger_hpos); + else + sr_dbg("%s: setting DSO Horiz Trigger Position to %d failed", + __func__, devc->trigger_hpos); + } else { + devc->trigger_hpos = g_variant_get_byte(data) * devc->limit_samples / 100.0; + } + } else if (id == SR_CONF_TRIGGER_HOLDOFF) { + devc->trigger_holdoff = g_variant_get_uint64(data); + if (sdi->mode == DSO) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_HOLDOFF)); + } + if (ret == SR_OK) + sr_dbg("%s: setting Trigger Holdoff Time to %d", + __func__, devc->trigger_holdoff); + else + sr_dbg("%s: setting Trigger Holdoff Time to %d failed", + __func__, devc->trigger_holdoff); + } else if (id == SR_CONF_TRIGGER_MARGIN) { + devc->trigger_margin = g_variant_get_byte(data); + if (sdi->mode == DSO) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_MARGIN)); + } + if (ret == SR_OK) + sr_dbg("%s: setting Trigger Margin to %d", + __func__, devc->trigger_margin); + else + sr_dbg("%s: setting Trigger Margin to %d failed", + __func__, devc->trigger_margin); + } else if (id == SR_CONF_SAMPLERATE) { + devc->cur_samplerate = g_variant_get_uint64(data); + if(sdi->mode == DSO) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); + } + } else if (id == SR_CONF_INSTANT) { + devc->instant = g_variant_get_boolean(data); + if (sdi->mode == DSO && dsl_en_ch_num(sdi) != 0) { + if (devc->instant) + devc->limit_samples = devc->profile->dev_caps.hw_depth / channel_modes[devc->ch_mode].unit_bits / dsl_en_ch_num(sdi); + else + devc->limit_samples = devc->profile->dev_caps.dso_depth / dsl_en_ch_num(sdi); + } + } else if (id == SR_CONF_DEVICE_MODE) { + sdi->mode = g_variant_get_int16(data); + num_probes = 0; + if (sdi->mode == DSO) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_DSO_SYNC)); + if (ret != SR_OK) + sr_dbg("%s: DSO configuration sync failed", __func__); + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, sdi->channels->data, SR_CONF_PROBE_VDIV)); + if (ret == SR_OK) + sr_dbg("%s: Initial setting for DSO mode", __func__); + else + sr_dbg("%s: Initial setting for DSO mode failed", __func__); + devc->op_mode = OP_NORMAL; + devc->test_mode = SR_TEST_NONE; + devc->instant = FALSE; + for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { + if (channel_modes[i].mode == DSO && + devc->profile->dev_caps.channels & (1 << i)) { + devc->ch_mode = channel_modes[i].id; + num_probes = channel_modes[i].num; + devc->stream = channel_modes[i].stream; + devc->cur_samplerate = channel_modes[i].max_samplerate / num_probes; + break; + } + } + devc->limit_samples = devc->profile->dev_caps.dso_depth / num_probes; + } else if (sdi->mode == ANALOG) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, sdi->channels->data, SR_CONF_PROBE_VDIV)); + if (ret == SR_OK) + sr_dbg("%s: Initial setting for DSO mode", __func__); + else + sr_dbg("%s: Initial setting for DSO mode failed", __func__); + devc->op_mode = OP_NORMAL; + devc->test_mode = SR_TEST_NONE; + devc->instant = TRUE; + for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { + if (channel_modes[i].mode == ANALOG && + devc->profile->dev_caps.channels & (1 << i)) { + devc->ch_mode = channel_modes[i].id; + num_probes = channel_modes[i].num; + devc->stream = channel_modes[i].stream; + devc->cur_samplerate = channel_modes[i].max_samplerate; + break; + } + } + devc->limit_samples = devc->cur_samplerate; + } + assert(num_probes != 0); + dsl_adjust_probes(sdi, num_probes); + dsl_adjust_samplerate(devc); + sr_dbg("%s: setting mode to %d", __func__, sdi->mode); + } else if (id == SR_CONF_OPERATION_MODE) { + stropt = g_variant_get_string(data, NULL); + if (!strcmp(stropt, get_opmodes(devc)[OP_NORMAL])) { + devc->op_mode = OP_NORMAL; + devc->test_mode = SR_TEST_NONE; + } else if (!strcmp(stropt, get_opmodes(devc)[OP_INTEST])) { + devc->op_mode = OP_INTEST; + devc->test_mode = SR_TEST_INTERNAL; + } else { + ret = SR_ERR; + } + sr_dbg("%s: setting pattern to %d", + __func__, devc->op_mode); + } else if (id == SR_CONF_BANDWIDTH_LIMIT) { + stropt = g_variant_get_string(data, NULL); + if (!strcmp(stropt, get_bandwidths(devc)[BW_FULL])) { + devc->bw_limit = BW_FULL; + dsl_wr_reg(sdi, CTR0_ADDR, bmBW20M_CLR); + } else if (!strcmp(stropt, get_bandwidths(devc)[BW_20M])) { + devc->bw_limit = BW_20M; + dsl_wr_reg(sdi, CTR0_ADDR, bmBW20M_SET); + } else { + ret = SR_ERR; + } + sr_dbg("%s: setting bandwidth limit to %d", + __func__, devc->bw_limit); + } else if (id == SR_CONF_PROBE_EN) { + ch->enabled = g_variant_get_boolean(data); + + if (sdi->mode == DSO) { + if (devc->status == DSL_DATA && + devc->profile->dev_caps.feature_caps & CAPS_FEATURE_HMCAD1511) { + if (dsl_en_ch_num(sdi) == 2) { + dsl_config_adc(sdi, adc_dual_ch03); + } else if (dsl_en_ch_num(sdi) == 1) { + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + if (probe->enabled && probe->index == 0) { + dsl_config_adc(sdi, adc_single_ch0); + break; + } else if (probe->enabled && probe->index == 1) { + dsl_config_adc(sdi, adc_single_ch3); + break; + } + } + } + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_EN)); + if (ch->index == 0) { + wr_cmd.header.dest = DSL_CTL_DSO_EN0; + wr_cmd.data[0] = ch->enabled ? bmCH_CH0 : (uint8_t)~bmCH_CH0; + } else { + wr_cmd.header.dest = DSL_CTL_DSO_EN1; + wr_cmd.data[0] = ch->enabled ? bmCH_CH1 : (uint8_t)~bmCH_CH1; + } + wr_cmd.header.size = 1; + ret = command_ctl_wr(hdl, wr_cmd); + if (dsl_en_ch_num(sdi) != 0) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); + devc->limit_samples = devc->profile->dev_caps.dso_depth / dsl_en_ch_num(sdi); + } + } else if (sdi->mode == ANALOG) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_EN)); + if (ch->index == 0) { + wr_cmd.header.dest = DSL_CTL_DSO_EN0; + wr_cmd.data[0] = bmCH_CH0; + } else { + wr_cmd.header.dest = DSL_CTL_DSO_EN1; + wr_cmd.data[0] = bmCH_CH1; + } + wr_cmd.header.size = 1; + ret = command_ctl_wr(hdl, wr_cmd); + } + if (ret == SR_OK) + sr_dbg("%s: setting ENABLE of channel %d to %d", + __func__, ch->index, ch->enabled); + else + sr_dbg("%s: setting ENABLE of channel %d to %d failed", + __func__, ch->index, ch->enabled); + } else if (id == SR_CONF_PROBE_OFFSET) { + ch->offset = g_variant_get_uint16(data); + if (devc->status != DSL_FINISH) + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_OFFSET)); + else + ret = SR_OK; + if (ret == SR_OK) + sr_dbg("%s: setting OFFSET of channel %d to %d", + __func__, ch->index, ch->offset); + else + sr_dbg("%s: setting OFFSET of channel %d to %d failed", + __func__, ch->index, ch->offset); + } else if (id == SR_CONF_TRIGGER_SOURCE) { + devc->trigger_source = (devc->trigger_source & 0xf0) + (g_variant_get_byte(data) & 0x0f); + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); + if (ret == SR_OK) + sr_dbg("%s: setting DSO Trigger Source to %d", + __func__, devc->trigger_source); + else + sr_dbg("%s: setting DSO Trigger Source to %d failed", + __func__, devc->trigger_source); + } else if (id == SR_CONF_TRIGGER_CHANNEL) { + devc->trigger_source = (g_variant_get_byte(data) << 4) + (devc->trigger_source & 0x0f); + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); + if (ret == SR_OK) + sr_dbg("%s: setting DSO Trigger Source to %d", + __func__, devc->trigger_source); + else + sr_dbg("%s: setting DSO Trigger Source to %d failed", + __func__, devc->trigger_source); + } else if (id == SR_CONF_ZERO) { + devc->zero = g_variant_get_boolean(data); + if (devc->zero) { + devc->zero_stage = -1; + devc->zero_pcnt = 0; + GSList *l; + unsigned int i, j; + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + probe->vpos_trans = devc->profile->dev_caps.default_pwmtrans; + probe->comb_comp = devc->profile->dev_caps.default_comb_comp; + if (probe->vga_ptr != NULL) { + for (i = 0; devc->profile->dev_caps.vdivs[i]; i++) { + for (j = 0; j < ARRAY_SIZE(vga_defaults); j++) { + if (vga_defaults[j].id == devc->profile->dev_caps.vga_id && + vga_defaults[j].key == devc->profile->dev_caps.vdivs[i]) { + (probe->vga_ptr+i)->id = vga_defaults[j].id; + (probe->vga_ptr+i)->key = vga_defaults[j].key; + //(probe->vga_ptr+i)->vgain = vga_defaults[j].vgain; + (probe->vga_ptr+i)->preoff = vga_defaults[j].preoff; + break; + } + } + } + } + } + } + } else if (id == SR_CONF_ZERO_DEFAULT) { + unsigned int i, j; + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + probe->vpos_trans = devc->profile->dev_caps.default_pwmtrans; + probe->comb_comp = devc->profile->dev_caps.default_comb_comp; + if (probe->vga_ptr != NULL) { + for (i = 0; devc->profile->dev_caps.vdivs[i]; i++) { + for (j = 0; j < ARRAY_SIZE(vga_defaults); j++) { + if (vga_defaults[j].id == devc->profile->dev_caps.vga_id && + vga_defaults[j].key == devc->profile->dev_caps.vdivs[i]) { + (probe->vga_ptr+i)->id = vga_defaults[j].id; + (probe->vga_ptr+i)->key = vga_defaults[j].key; + (probe->vga_ptr+i)->vgain = vga_defaults[j].vgain; + (probe->vga_ptr+i)->preoff = vga_defaults[j].preoff; + break; + } + } + } + } + } + } else if (id == SR_CONF_CALI) { + devc->cali = g_variant_get_boolean(data); + } else if (id == SR_CONF_ZERO_LOAD) { + GSList *l; + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + if (!dso_load_eep(sdi, probe, FALSE)) { + config_set(SR_CONF_ZERO, g_variant_new_boolean(TRUE), sdi, NULL, NULL); + sr_info("Zero have not been setted!"); + break; + } + } + } else if (id == SR_CONF_ZERO_SET) { + GSList *l; + struct cmd_zero_info zero_info; + struct cmd_vga_info vga_info; + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + zero_info.zero_addr = zero_base_addr + + probe->index * (sizeof(struct cmd_zero_info) + sizeof(struct cmd_vga_info)); + int i; + uint16_t real_zero_addr; + uint8_t *preoff_ptr = &zero_info.zero_addr + 1; + for (i = 0; probe->vga_ptr && (probe->vga_ptr+i)->id; i++) { + *(preoff_ptr+2*i) = (probe->vga_ptr+i)->preoff & 0x00ff; + *(preoff_ptr+2*i+1) = (probe->vga_ptr+i)->preoff >> 8; + } + if (i != 0) { + *(preoff_ptr+2*i) = probe->comb_diff_top; + *(preoff_ptr+2*i+1) = probe->comb_diff_bom; + *(preoff_ptr+2*i+2) = (probe->vpos_trans&0x00FF); + *(preoff_ptr+2*i+3) = (probe->vpos_trans>>8); + *(preoff_ptr+2*i+4) = probe->comb_comp; + + vga_info.vga_addr = zero_info.zero_addr + sizeof(struct cmd_zero_info); + uint16_t *vgain_ptr = &vga_info.vga0; + for (i=0; probe->vga_ptr && (probe->vga_ptr+i)->id; i++){ + *(vgain_ptr+i) = (probe->vga_ptr+i)->vgain >> 8; + } + ret = dsl_wr_reg(sdi, CTR0_ADDR, bmEEWP); + if (ret == SR_OK) { + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_SEEP) + real_zero_addr = zero_info.zero_addr; + else + real_zero_addr = (zero_big_addr << 8) + zero_info.zero_addr; + ret = dsl_wr_nvm(sdi, (unsigned char *)&zero_info, real_zero_addr, sizeof(struct cmd_zero_info)); + } + if (ret == SR_OK) { + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_SEEP) + real_zero_addr = vga_info.vga_addr; + else + real_zero_addr = (zero_big_addr << 8) + vga_info.vga_addr; + ret = dsl_wr_nvm(sdi, (unsigned char *)&vga_info, real_zero_addr, sizeof(struct cmd_vga_info)); + } + ret = dsl_wr_reg(sdi, CTR0_ADDR, bmNONE); + + if (!(devc->profile->dev_caps.feature_caps & CAPS_FEATURE_HMCAD1511)) { + const double slope = (probe->comb_diff_bom - probe->comb_diff_top)/(2.0*255.0); + for (i = 0; i < 256; i++) { + ret = dsl_wr_reg(sdi, COMB_ADDR + probe->index*2, i); + int value = i+i*slope+probe->comb_diff_top*0.5+0.5; + value = (value < 0) ? 0 : + (value > 255) ? 255 : value; + ret = dsl_wr_reg(sdi, COMB_ADDR + probe->index*2 + 1, value); + } + } + } + } + } else if (id == SR_CONF_VOCM) { + const uint8_t vocm = g_variant_get_byte(data); + ret = dsl_wr_reg(sdi, COMB_ADDR+4, vocm); + } else if (id == SR_CONF_PROBE_VGAIN) { + const uint64_t vgain = g_variant_get_uint64(data) << 8; + int i; + for (i = 0; ch->vga_ptr && (ch->vga_ptr+i)->id; i++) { + if ((ch->vga_ptr+i)->key == ch->vdiv) { + (ch->vga_ptr+i)->vgain = vgain; + } + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_VDIV)); + if (ret == SR_OK) + sr_dbg("%s: setting VDIV of channel %d to %d mv", + __func__, ch->index, ch->vdiv); + else + sr_dbg("%s: setting VDIV of channel %d to %d mv failed", + __func__, ch->index, ch->vdiv); + } else if (id == SR_CONF_PROBE_PREOFF) { + uint16_t preoff = g_variant_get_uint16(data); + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_PREOFF) { + double voltage_off = (2.0 * preoff / devc->profile->dev_caps.default_pwmmargin - 1) * ch->vdiv; + double trans_coarse = (ch->vdiv < 500) ? (ch->vpos_trans >> 8)/DSCOPE_TRANS_CMULTI : (ch->vpos_trans >> 8); + double trans_fine = (ch->vdiv < 500) ? (ch->vpos_trans & 0x00ff) / 1000.0 : (ch->vpos_trans & 0x00ff) / DSCOPE_TRANS_FMULTI; + + uint16_t default_preoff = get_default_preoff(sdi, ch); + int preoff_coarse = floor(voltage_off / trans_coarse + 0.5); + int preoff_fine = floor(-(voltage_off - preoff_coarse*trans_coarse)/trans_fine + 0.5); + preoff_coarse = (default_preoff >> 10) + preoff_coarse; + preoff_fine = (default_preoff&0x03ff) + preoff_fine; + preoff = (preoff_coarse << 10) + preoff_fine; + } + int i; + for (i = 0; ch->vga_ptr && (ch->vga_ptr+i)->id; i++) { + if ((ch->vga_ptr+i)->key == ch->vdiv) { + (ch->vga_ptr+i)->preoff = preoff; + } + } + if (devc->status != DSL_FINISH) + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_OFFSET)); + else + ret = SR_OK; + + if (ret == SR_OK) + sr_dbg("%s: setting OFFSET of channel %d to %d", + __func__, ch->index, ch->offset); + else + sr_dbg("%s: setting OFFSET of channel %d to %d failed", + __func__, ch->index, ch->offset); + } else if (id == SR_CONF_PROBE_COMB_COMP) { + ch->comb_comp = g_variant_get_int16(data); + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_VDIV)); + if (ret == SR_OK) + sr_dbg("%s: setting COMB_COMP of channel %d to %d mv", + __func__, ch->index, ch->comb_comp); + else + sr_dbg("%s: setting COMB_COMP of channel %d to %d mv failed", + __func__, ch->index, ch->comb_comp); + } else { + ret = SR_ERR_NA; + } + + return ret; +} + +static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi, + const struct sr_channel_group *cg) +{ + struct DSL_context *devc; + + (void)cg; + devc = sdi->priv; + + if (dsl_config_list(key, data, sdi, cg) == SR_OK) + return SR_OK; + + switch (key) { + case SR_CONF_DEVICE_OPTIONS: +// *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, +// hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t)); + *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), + hwoptions, ARRAY_SIZE(hwoptions)*sizeof(int32_t), TRUE, NULL, NULL); + break; + case SR_CONF_DEVICE_SESSIONS: + if (sdi->mode == DSO) + *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), + sessions_dso, ARRAY_SIZE(sessions_dso)*sizeof(int32_t), TRUE, NULL, NULL); + else if (sdi->mode == ANALOG) + *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), + sessions_daq, ARRAY_SIZE(sessions_daq)*sizeof(int32_t), TRUE, NULL, NULL); + break; + case SR_CONF_OPERATION_MODE: + *data = g_variant_new_strv(get_opmodes(devc), ARRAY_SIZE(opmodes)); + break; + case SR_CONF_BANDWIDTH_LIMIT: + *data = g_variant_new_strv(get_bandwidths(devc), ARRAY_SIZE(bandwidths)); + break; + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +static int dso_zero(const struct sr_dev_inst *sdi) +{ + struct DSL_context *devc = sdi->priv; + GSList *l; + int ret; + struct sr_usb_dev_inst *usb; + struct libusb_device_handle *hdl; + struct ctl_wr_cmd wr_cmd; + + static uint64_t vdiv_back[2]; + struct sr_channel *probe0 = NULL, *probe1 = NULL; + const uint16_t offset_top = 20; + const uint16_t offset_bom = ((1 << channel_modes[devc->ch_mode].unit_bits) - 1) - offset_top; + const uint16_t offset_mid = (1 << (channel_modes[devc->ch_mode].unit_bits - 1)); + const uint16_t max_trans = ((1 << 10) - 1); + const uint8_t value_min = 0; + const uint8_t value_max = (1 << channel_modes[devc->ch_mode].unit_bits) - 1; + + const int zero_interval = 10; + const double margin_pass = 0.3; + int end_cnt = 0; + static gboolean trans_fix_done = FALSE; + static gboolean mid_zero_done = FALSE; + static double margin[2]; + //static double offset[2]; + + usb = sdi->conn; + hdl = usb->devhdl; + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + if (probe->index == 0) + probe0 = probe; + if (probe->index == 1) + probe1 = probe; + vdiv_back[probe->index] = probe->vdiv; + } + + if (!trans_fix_done && devc->zero_stage == 0) { + ret = SR_OK; + if (!(devc->profile->dev_caps.feature_caps & CAPS_FEATURE_PREOFF)) { + if (devc->zero_pcnt == 0*zero_interval) { + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + probe->zero_offset = offset_bom; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_OFFSET)); + } + } + if (devc->zero_pcnt == 1*zero_interval) { + margin[0] = (devc->mstatus.ch0_acc_mean * 1.0 / devc->limit_samples); + margin[1] = (devc->mstatus.ch1_acc_mean * 1.0 / devc->limit_samples); + if (margin[0] >= value_max || margin[1] >= value_max) + ret = SR_ERR; + } + if (devc->zero_pcnt == 1*zero_interval+1) { + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + probe->zero_offset = offset_top; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_OFFSET)); + } + } + if (devc->zero_pcnt == 2*zero_interval) { + double top0 = (devc->mstatus.ch0_acc_mean * 1.0 / devc->limit_samples); + double top1 = (devc->mstatus.ch1_acc_mean * 1.0 / devc->limit_samples); + if (top0 <= value_min || top1 <= value_min) { + ret = SR_ERR; + } else { + margin[0] -= top0; + margin[1] -= top1; + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + margin[probe->index] -= (offset_bom - offset_top); + if (fabs(margin[probe->index]) > margin_pass) { + margin[probe->index] = margin[probe->index] > 0 ? ceil(margin[probe->index]) : floor(margin[probe->index]); + probe->vpos_trans = min(probe->vpos_trans - margin[probe->index], max_trans); + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe0, SR_CONF_PROBE_OFFSET)); + } else { + margin[probe->index] = 0; + } + } + trans_fix_done = (margin[0] == 0) && (margin[1] == 0); + devc->zero_pcnt = trans_fix_done ? 0*zero_interval : 0*zero_interval-1; + } + } + } else { + trans_fix_done = TRUE; + } + + if (!trans_fix_done && ret == SR_OK) + devc->zero_pcnt++; + } else if (!mid_zero_done) { + if (devc->zero_pcnt == 0) { + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + probe->vdiv = (probe->vga_ptr+devc->zero_stage)->key; + if (probe->vdiv == 0) { + probe->vdiv = vdiv_back[probe->index]; + mid_zero_done = TRUE; + break; + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_VDIV)); + probe->zero_offset = offset_mid; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_OFFSET)); + // must after offset setting + probe->vdiv = vdiv_back[probe->index]; + } + } + + if (devc->zero_pcnt == zero_interval) { + margin[0] = offset_mid - (devc->mstatus.ch0_acc_mean * 1.0 / devc->limit_samples); + margin[1] = offset_mid - (devc->mstatus.ch1_acc_mean * 1.0 / devc->limit_samples); + if (fabs(margin[0]) < margin_pass && fabs(margin[1]) < margin_pass) { + devc->zero_stage++; + } else { + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_PREOFF) { + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + double trans_coarse = ((probe->vga_ptr+devc->zero_stage)->key < 500) ? (probe->vpos_trans >> 8)/DSCOPE_TRANS_CMULTI : (probe->vpos_trans >> 8); + double trans_fine = ((probe->vga_ptr+devc->zero_stage)->key < 500) ? (probe->vpos_trans & 0x00ff) / 1000.0 : (probe->vpos_trans & 0x00ff) / DSCOPE_TRANS_FMULTI; + + double voltage_margin = margin[probe->index] * (probe->vga_ptr+devc->zero_stage)->key * 10 / 255.0; + uint16_t last_preoff = (probe->vga_ptr+devc->zero_stage)->preoff; + int preoff_coarse = floor(voltage_margin / trans_coarse + 0.5); + int preoff_fine = floor(-(voltage_margin - preoff_coarse*trans_coarse)/trans_fine + 0.5); + preoff_coarse = (last_preoff >> 10) + preoff_coarse; + preoff_fine = (last_preoff&0x03ff) + preoff_fine; + (probe->vga_ptr+devc->zero_stage)->preoff = (preoff_coarse << 10) + preoff_fine; + } + } else { + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + (probe->vga_ptr+devc->zero_stage)->preoff += margin[probe->index] > 0 ? ceil(margin[probe->index]) : floor(margin[probe->index]); + } + } + } + devc->zero_pcnt = 0; + } else if (!mid_zero_done) { + devc->zero_pcnt++; + } + } else { + ret = SR_OK; + end_cnt = 0*zero_interval + 1; + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_HMCAD1511) { +// if (devc->zero_pcnt == 0*zero_interval+1) { +// for(l = sdi->channels; l; l = l->next) { +// struct sr_channel *probe = (struct sr_channel *)l->data; +// probe->zero_offset = offset_bom; +// ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_OFFSET)); +// } +// } +// if (devc->zero_pcnt == 1*zero_interval) { +// offset[0] = (devc->mstatus.ch0_acc_mean * 1.0 / devc->limit_samples); +// offset[1] = (devc->mstatus.ch1_acc_mean * 1.0 / devc->limit_samples); +// if (margin[0] >= value_max || margin[1] >= value_max) +// ret = SR_ERR; +// } +// if (devc->zero_pcnt == 1*zero_interval+1) { +// dsl_config_adc(sdi, adc_single_ch0); +// probe0->enabled = TRUE; +// probe1->enabled = FALSE; + +// probe0->zero_offset = offset_bom; +// ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe0, SR_CONF_PROBE_OFFSET)); +// } +// if (devc->zero_pcnt == 2*zero_interval) { +// margin[0] = offset[0] - (devc->mstatus.ch0_acc_mean * 1.0 / devc->limit_samples); +// if (fabs(margin[0]) > margin_pass) { +// probe0->comb_comp -= margin[0] > 0 ? ceil(margin[0]) : floor(margin[0]); +// ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe0, SR_CONF_PROBE_OFFSET)); +// devc->zero_pcnt = 1*zero_interval+1; +// } +// } +// if (devc->zero_pcnt == 2*zero_interval+1) { +// dsl_config_adc(sdi, adc_single_ch3); +// probe0->enabled = FALSE; +// probe1->enabled = TRUE; + +// probe1->zero_offset = offset_bom; +// ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe1, SR_CONF_PROBE_OFFSET)); +// } +// if (devc->zero_pcnt == 3*zero_interval) { +// margin[1] = offset[1] - (devc->mstatus.ch1_acc_mean * 1.0 / devc->limit_samples); +// if (fabs(margin[1]) > margin_pass) { +// probe1->comb_comp -= margin[1] > 0 ? ceil(margin[1]) : floor(margin[1]); +// ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe1, SR_CONF_PROBE_OFFSET)); +// devc->zero_pcnt = 2*zero_interval+1; +// } +// } +// end_cnt = 3*zero_interval + 1; + } else { + if (devc->zero_pcnt == 0*zero_interval+1) { + ret = dsl_wr_reg(sdi, COMB_ADDR+6, 0b1101); + wr_cmd.header.dest = DSL_CTL_DSO_EN1; + wr_cmd.data[0] = (uint8_t)~bmCH_CH1; + wr_cmd.header.size = 1; + ret = command_ctl_wr(hdl, wr_cmd); + + probe0->zero_offset = offset_top; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe0, SR_CONF_PROBE_OFFSET)); + } else if (devc->zero_pcnt == 1*zero_interval) { + probe0->comb_diff_top = ((devc->mstatus.ch0_acc_mean * 2.0 - devc->mstatus.ch1_acc_mean * 2.0) / devc->limit_samples); + probe0->zero_offset = offset_bom; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe0, SR_CONF_PROBE_OFFSET)); + } else if (devc->zero_pcnt == 2*zero_interval) { + probe0->comb_diff_bom = ((devc->mstatus.ch0_acc_mean * 2.0 - devc->mstatus.ch1_acc_mean * 2.0) / devc->limit_samples); + } + + if (devc->zero_pcnt == 2*zero_interval+1) { + ret = dsl_wr_reg(sdi, COMB_ADDR+6, 0b1110); + wr_cmd.header.dest = DSL_CTL_DSO_EN1; + wr_cmd.data[0] = bmCH_CH1; + wr_cmd.header.size = 1; + ret = command_ctl_wr(hdl, wr_cmd); + wr_cmd.header.dest = DSL_CTL_DSO_EN0; + wr_cmd.data[0] = (uint8_t)~bmCH_CH0; + wr_cmd.header.size = 1; + ret = command_ctl_wr(hdl, wr_cmd); + + probe1->zero_offset = offset_top; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe1, SR_CONF_PROBE_OFFSET)); + } else if (devc->zero_pcnt == 3*zero_interval) { + probe1->comb_diff_top = ((devc->mstatus.ch1_acc_mean * 2.0 - devc->mstatus.ch0_acc_mean * 2.0) / devc->limit_samples); + probe1->zero_offset = offset_bom; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe1, SR_CONF_PROBE_OFFSET)); + } else if (devc->zero_pcnt == 4*zero_interval) { + probe1->comb_diff_bom = ((devc->mstatus.ch1_acc_mean * 2.0 - devc->mstatus.ch0_acc_mean * 2.0) / devc->limit_samples); + } + + end_cnt = 4*zero_interval+1; + } + + if (ret == SR_OK) + devc->zero_pcnt++; + + if (devc->zero_pcnt == end_cnt) { + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + probe->vdiv = vdiv_back[probe->index]; +// if (!(devc->profile->dev_caps.feature_caps & CAPS_FEATURE_HMCAD1511)) +// probe->comb_comp = ((1 << channel_modes[devc->ch_mode].unit_bits) - 1) * 0.7 * 2 / +// min(abs(probe->comb_diff_top), abs(probe->comb_diff_bom)); + + } + + ret = dsl_wr_reg(sdi, COMB_ADDR+6, 0b0011); + wr_cmd.header.dest = DSL_CTL_DSO_EN0; + wr_cmd.data[0] = bmCH_CH0; + wr_cmd.header.size = 1; + ret = command_ctl_wr(hdl, wr_cmd); + wr_cmd.header.dest = DSL_CTL_DSO_EN1; + wr_cmd.data[0] = bmCH_CH1; + wr_cmd.header.size = 1; + ret = command_ctl_wr(hdl, wr_cmd); + + devc->zero = FALSE; + trans_fix_done = FALSE; + mid_zero_done = FALSE; + dso_init(sdi); + } + } + + return ret; +} + +static int dso_tune(const struct sr_dev_inst *sdi) +{ + struct DSL_context *devc = sdi->priv; + int ret = SR_OK; + double margin; + static uint64_t vdiv_back = 0; + static uint16_t offset_back = 0; + static int coupling_back = SR_DC_COUPLING; + const uint8_t mux0[8] = {0x09, 0x0f, 0x0b, 0x0d, 0x07, 0x05, 0x01, 0x03}; + const uint8_t mux1[8] = {0x09, 0x0f, 0x0b, 0x0d, 0x0e, 0x0c, 0x08, 0x0a}; + const uint8_t *mux = (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_POGOPIN) ? mux1 : mux0; + + if (devc->tune_probe && devc->tune_stage == -1) { + vdiv_back = devc->tune_probe->vdiv; + offset_back = devc->tune_probe->offset; + coupling_back = devc->tune_probe->coupling; + + devc->tune_stage = 0; + ret = dsl_wr_ext(sdi, 0x03, 0x00); + ret = dsl_wr_ext(sdi, 0x01, mux[devc->tune_stage]); + devc->tune_probe->vdiv = (devc->tune_probe->vga_ptr + devc->tune_stage)->key; + devc->tune_probe->offset = (1 << (channel_modes[devc->ch_mode].unit_bits - 1)); + devc->tune_probe->coupling = SR_AC_COUPLING; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, devc->tune_probe, SR_CONF_PROBE_VDIV)); + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, devc->tune_probe, SR_CONF_PROBE_OFFSET)); + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, devc->tune_probe, SR_CONF_PROBE_COUPLING)); + } else if (devc->tune_probe && devc->profile->dev_caps.vdivs[devc->tune_stage] != 0) { + if (devc->tune_pcnt == 10) { + devc->tune_pcnt = 0; + margin = (devc->tune_probe->coupling == SR_AC_COUPLING) ? 127.5 : 25.5; + if (devc->tune_probe->index == 0) + margin -= (devc->mstatus.ch0_acc_mean * 1.0 / devc->limit_samples); + else + margin -= (devc->mstatus.ch1_acc_mean * 1.0 / devc->limit_samples); + + if ((devc->tune_probe->coupling == SR_AC_COUPLING) && (abs(margin) < 0.5)) { + devc->tune_probe->coupling = SR_DC_COUPLING; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, devc->tune_probe, SR_CONF_PROBE_COUPLING)); + } else if (devc->tune_probe->coupling == SR_AC_COUPLING){ + (devc->tune_probe->vga_ptr+devc->tune_stage)->preoff += margin; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, devc->tune_probe, SR_CONF_PROBE_OFFSET)); + } else if ((devc->tune_probe->coupling == SR_DC_COUPLING) && (abs(margin) < 0.5)) { + devc->tune_stage++; + if (devc->profile->dev_caps.vdivs[devc->tune_stage] != 0) { + ret = dsl_wr_ext(sdi, 0x01, mux[devc->tune_stage]); + devc->tune_probe->vdiv = (devc->tune_probe->vga_ptr + devc->tune_stage)->key; + devc->tune_probe->coupling = SR_AC_COUPLING; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, devc->tune_probe, SR_CONF_PROBE_VDIV)); + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, devc->tune_probe, SR_CONF_PROBE_OFFSET)); + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, devc->tune_probe, SR_CONF_PROBE_COUPLING)); + }else { + ret = dsl_wr_ext(sdi, 0x01, mux[0]); + devc->tune_probe->vdiv = vdiv_back; + devc->tune_probe->offset = offset_back; + devc->tune_probe->coupling = coupling_back; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, devc->tune_probe, SR_CONF_PROBE_VDIV)); + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, devc->tune_probe, SR_CONF_PROBE_OFFSET)); + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, devc->tune_probe, SR_CONF_PROBE_COUPLING)); + devc->tune = FALSE; + } + } else if (devc->tune_probe->coupling == SR_DC_COUPLING){ + (devc->tune_probe->vga_ptr + devc->tune_stage)->vgain -= ceil(margin*1024); + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, devc->tune_probe, SR_CONF_PROBE_VDIV)); + } + } + if (ret == SR_OK) + devc->tune_pcnt++; + } + + return ret; +} + +static int dev_open(struct sr_dev_inst *sdi) +{ + gboolean fpga_done; + int ret; + GSList *l; + gboolean zeroed; + struct DSL_context *devc = sdi->priv; + + if ((ret = dsl_dev_open(di, sdi, &fpga_done)) == SR_OK) { + // load zero informations + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + zeroed = dso_load_eep(sdi, probe, fpga_done); + if (!zeroed) + break; + } + if (!zeroed) { + config_set(SR_CONF_ZERO, g_variant_new_boolean(TRUE), sdi, NULL, NULL); + sr_info("Zero have not been setted!"); + } + if (!fpga_done) { + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_HMCAD1511) { + dsl_config_adc(sdi, adc_init_fix); + dsl_config_adc(sdi, adc_clk_init_1g); + dsl_config_adc(sdi, adc_power_down); + } + dso_init(sdi); + } + } + + return ret; +} + +static int dev_close(struct sr_dev_inst *sdi) +{ + int ret; + ret = dsl_dev_close(sdi); + return ret; +} + +static int cleanup(void) +{ + int ret; + struct drv_context *drvc; + + if (!(drvc = di->priv)) + return SR_OK; + + ret = dev_clear(); + + g_free(drvc); + di->priv = NULL; + + return ret; +} + +static void remove_sources(struct DSL_context *devc) +{ + int i; + sr_info("%s: remove fds from polling", __func__); + /* Remove fds from polling. */ + for (i = 0; devc->usbfd[i] != -1; i++) + sr_source_remove(devc->usbfd[i]); + g_free(devc->usbfd); +} + +static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) +{ + int completed = 0; + struct timeval tv; + struct drv_context *drvc; + struct DSL_context *devc; + + (void)fd; + (void)revents; + + drvc = di->priv; + devc = sdi->priv; + + tv.tv_sec = tv.tv_usec = 0; + libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv, &completed); + + if (devc->zero && devc->trf_completed) { + dso_zero(sdi); + } + if (devc->tune && devc->trf_completed) { + dso_tune(sdi); + } + + if (devc->status == DSL_FINISH) { + remove_sources(devc); + } + + devc->trf_completed = 0; + return TRUE; +} + +static int dev_acquisition_start(struct sr_dev_inst *sdi, void *cb_data) +{ + (void)cb_data; + + struct DSL_context *devc; + struct sr_usb_dev_inst *usb; + struct drv_context *drvc; + const struct libusb_pollfd **lupfd; + unsigned int i; + int ret; + struct ctl_wr_cmd wr_cmd; + GSList *l; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR_DEV_CLOSED; + + drvc = di->priv; + devc = sdi->priv; + usb = sdi->conn; + + //devc->cb_data = cb_data; + devc->cb_data = sdi; + devc->num_samples = 0; + devc->empty_transfer_count = 0; + devc->status = DSL_INIT; + devc->num_transfers = 0; + devc->submitted_transfers = 0; + devc->actual_samples = (devc->limit_samples + 1023ULL) & ~1023ULL; + devc->abort = FALSE; + devc->mstatus_valid = FALSE; + devc->overflow = FALSE; + devc->instant_tail_bytes = dsl_header_size(devc); + + /* Configures devc->trigger_* and devc->sample_wide */ + if (dsl_configure_probes(sdi) != SR_OK) { + sr_err("%s: Failed to configure probes.", __func__); + return SR_ERR; + } + + /* Stop Previous GPIF acquisition */ + wr_cmd.header.dest = DSL_CTL_STOP; + wr_cmd.header.size = 0; + if ((ret = command_ctl_wr(usb->devhdl, wr_cmd)) != SR_OK) { + sr_err("%s: Stop DSCope acquisition failed!", __func__); + return ret; + } else { + sr_info("%s: Stop Previous DSCope acquisition!", __func__); + } + + /* Arm FPGA before acquisition start*/ + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_HMCAD1511) { + if (dsl_en_ch_num(sdi) == 2) { + dsl_config_adc(sdi, adc_dual_ch03); + } else if (dsl_en_ch_num(sdi) == 1) { + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + if (probe->enabled && probe->index == 0) { + dsl_config_adc(sdi, adc_single_ch0); + break; + } else if (probe->enabled && probe->index == 1) { + dsl_config_adc(sdi, adc_single_ch3); + break; + } + } + } + //dsl_config_adc(sdi, adc_power_up); + } + if ((ret = dsl_fpga_arm(sdi)) != SR_OK) { + sr_err("%s: Arm FPGA failed!", __func__); + return ret; + } + + if (devc->zero && devc->zero_stage == -1) { + // initialize before Auto Calibration + if ((ret = dso_init(sdi)) != SR_OK) { + sr_err("%s: DSO zero initialization failed!", __func__); + return ret; + } + devc->zero_stage = 0; + } + + /* + * settings must be updated before acquisition + */ + if (sdi->mode == DSO) { + devc->trigger_hpos = devc->trigger_hrate * dsl_en_ch_num(sdi) * devc->limit_samples / 200.0; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_HORIZ_TRIGGERPOS)); + if (ret != SR_OK) + sr_dbg("%s: setting DSO Horiz Trigger Position to %d failed", __func__, devc->trigger_hpos); + + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_VDIV)); + if (ret != SR_OK) + sr_err("%s: Set VDIV of channel %d command failed!", __func__, probe->index); + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_OFFSET)); + if (ret != SR_OK) + sr_err("%s: Set OFFSET of channel %d command failed!", __func__, probe->index); + } + } + + /* setup and submit usb transfer */ + if ((ret = dsl_start_transfers(devc->cb_data)) != SR_OK) { + sr_err("%s: Could not submit usb transfer" + "(%d)%d", __func__, ret, errno); + return ret; + } + + /* setup callback function for data transfer */ + lupfd = libusb_get_pollfds(drvc->sr_ctx->libusb_ctx); + for (i = 0; lupfd[i]; i++); + if (!(devc->usbfd = g_try_malloc(sizeof(struct libusb_pollfd) * (i + 1)))) + return SR_ERR; + for (i = 0; lupfd[i]; i++) { + sr_source_add(lupfd[i]->fd, lupfd[i]->events, + dsl_get_timeout(sdi), receive_data, sdi); + devc->usbfd[i] = lupfd[i]->fd; + } + devc->usbfd[i] = -1; + free(lupfd); + + wr_cmd.header.dest = DSL_CTL_START; + wr_cmd.header.size = 0; + if ((ret = command_ctl_wr(usb->devhdl, wr_cmd)) != SR_OK) { + devc->status = DSL_ERROR; + devc->abort = TRUE; + return ret; + } + devc->status = DSL_START; + + /* Send header packet to the session bus. */ + //std_session_send_df_header(cb_data, LOG_PREFIX); + std_session_send_df_header(sdi, LOG_PREFIX); + + return SR_OK; +} + +static int dev_acquisition_stop(const struct sr_dev_inst *sdi, void *cb_data) +{ + struct DSL_context *devc = sdi->priv; + int ret = dsl_dev_acquisition_stop(sdi, cb_data); + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_HMCAD1511) { + dsl_config_adc(sdi, adc_power_down); + } + return ret; +} + +static int dev_status_get(const struct sr_dev_inst *sdi, struct sr_status *status, gboolean prg, int begin, int end) +{ + int ret = dsl_dev_status_get(sdi, status, prg, begin, end); + return ret; +} + +SR_PRIV struct sr_dev_driver DSCope_driver_info = { + .name = "DSCope", + .longname = "DSCope (generic driver for DScope oscilloscope)", + .api_version = 1, + .init = init, + .cleanup = cleanup, + .scan = scan, + .dev_list = dev_list, + .dev_mode_list = dev_mode_list, + .dev_clear = dev_clear, + .config_get = config_get, + .config_set = config_set, + .config_list = config_list, + .dev_open = dev_open, + .dev_close = dev_close, + .dev_status_get = dev_status_get, + .dev_acquisition_start = dev_acquisition_start, + .dev_acquisition_stop = dev_acquisition_stop, + .priv = NULL, +}; diff --git a/libsigrok4DSL/hardware/DSL/dsl.c b/libsigrok4DSL/hardware/DSL/dsl.c index cf7a02eb..e6d97786 100755 --- a/libsigrok4DSL/hardware/DSL/dsl.c +++ b/libsigrok4DSL/hardware/DSL/dsl.c @@ -1,1824 +1,2067 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2017 DreamSourceLab - * - * 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 . - */ - -#include "libsigrok.h" -#include "libsigrok-internal.h" -#include "command.h" -#include "dsl.h" - -#include -#include -#include - -extern struct ds_trigger *trigger; - -static const unsigned int single_buffer_time = 20; -static const unsigned int total_buffer_time = 100; -static const unsigned int instant_buffer_size = 1024 * 1024; -static uint16_t test_init = 1; - -static const int32_t probeOptions[] = { - SR_CONF_PROBE_COUPLING, - SR_CONF_PROBE_VDIV, - SR_CONF_PROBE_MAP_UNIT, - SR_CONF_PROBE_MAP_MIN, - SR_CONF_PROBE_MAP_MAX, -}; - -static const int32_t probeSessions[] = { - SR_CONF_PROBE_COUPLING, - SR_CONF_PROBE_VDIV, - SR_CONF_PROBE_MAP_UNIT, - SR_CONF_PROBE_MAP_MIN, - SR_CONF_PROBE_MAP_MAX, -}; - -static const uint8_t probeCoupling[] = { - SR_DC_COUPLING, - SR_AC_COUPLING, -}; - -const char *probeMapUnits[] = { - "V", - "A", - "℃", - "℉", - "g", - "m", - "m/s", -}; - -static const char *probe_names[] = { - "0", "1", "2", "3", "4", "5", "6", "7", - "8", "9", "10", "11", "12", "13", "14", "15", - NULL, -}; - -static struct sr_dev_mode mode_list[] = { - {"LA", LOGIC}, - {"DAQ", ANALOG}, - {"OSC", DSO}, -}; - -SR_PRIV void dsl_probe_init(struct sr_dev_inst *sdi) -{ - int i; - GSList *l; - struct DSL_context *devc = sdi->priv; - - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - probe->vdiv = 1000; - probe->vfactor = 1; - probe->vpos = 0; - probe->coupling = SR_DC_COUPLING; - probe->trig_value = 0x80; - probe->vpos_trans = devc->profile->dev_caps.default_pwmtrans; - probe->ms_show = TRUE; - for (i = DSO_MS_BEGIN; i < DSO_MS_END; i++) - probe->ms_en[i] = default_ms_en[i]; - probe->map_unit = probeMapUnits[0]; - probe->map_min = -1; - probe->map_max = 1; - if (devc->profile->dev_caps.vdivs && - probe->vga_ptr == NULL) { - for (i = 0; devc->profile->dev_caps.vdivs[i]; i++); - probe->vga_ptr = g_try_malloc((i+1)*sizeof(struct DSL_vga)); - (probe->vga_ptr + i)->id = 0; - (probe->vga_ptr + i)->key = 0; - (probe->vga_ptr + i)->vgain = 0; - (probe->vga_ptr + i)->voff = 0; - (probe->vga_ptr + i)->voff_comp = 0; - for (i = 0; devc->profile->dev_caps.vdivs[i]; i++) { - (probe->vga_ptr + i)->id = devc->profile->dev_caps.vga_id; - (probe->vga_ptr + i)->key = devc->profile->dev_caps.vdivs[i]; - } - } - } -} - -SR_PRIV int dsl_setup_probes(struct sr_dev_inst *sdi, int num_probes) -{ - uint16_t j; - struct sr_channel *probe; - struct DSL_context *devc = sdi->priv; - - for (j = 0; j < num_probes; j++) { - if (!(probe = sr_channel_new(j, channel_modes[devc->ch_mode].type, - TRUE, probe_names[j]))) - return SR_ERR; - sdi->channels = g_slist_append(sdi->channels, probe); - } - dsl_probe_init(sdi); - return SR_OK; -} - -SR_PRIV int dsl_adjust_probes(struct sr_dev_inst *sdi, int num_probes) -{ - uint16_t j; - struct sr_channel *probe; - struct DSL_context *devc = sdi->priv; - GSList *l; - - assert(num_probes > 0); - - j = g_slist_length(sdi->channels); - while(j < num_probes) { - if (!(probe = sr_channel_new(j, channel_modes[devc->ch_mode].type, - TRUE, probe_names[j]))) - return SR_ERR; - sdi->channels = g_slist_append(sdi->channels, probe); - j++; - } - - while(j > num_probes) { - sdi->channels = g_slist_delete_link(sdi->channels, g_slist_last(sdi->channels)); - j--; - } - - for(l = sdi->channels; l; l = l->next) { - probe = (struct sr_channel *)l->data; - probe->enabled = TRUE; - probe->type = channel_modes[devc->ch_mode].type; - } - return SR_OK; -} - -SR_PRIV const GSList *dsl_mode_list(const struct sr_dev_inst *sdi) -{ - struct DSL_context *devc; - GSList *l = NULL; - unsigned int i; - - devc = sdi->priv; - for (i = 0; i < ARRAY_SIZE(mode_list); i++) { - if (devc->profile->dev_caps.mode_caps & (1 << i)) - l = g_slist_append(l, &mode_list[i]); - } - - return l; -} - -SR_PRIV void dsl_adjust_samplerate(struct DSL_context *devc) -{ - devc->samplerates_max_index = ARRAY_SIZE(samplerates) - 1; - while (samplerates[devc->samplerates_max_index] > - channel_modes[devc->ch_mode].max_samplerate) - devc->samplerates_max_index--; - - devc->samplerates_min_index = 0; - while (samplerates[devc->samplerates_min_index] < - channel_modes[devc->ch_mode].min_samplerate) - devc->samplerates_min_index++; - - assert(devc->samplerates_max_index >= devc->samplerates_min_index); - - if (devc->cur_samplerate > samplerates[devc->samplerates_max_index]) - devc->cur_samplerate = samplerates[devc->samplerates_max_index]; - - if (devc->cur_samplerate < samplerates[devc->samplerates_min_index]) - devc->cur_samplerate = samplerates[devc->samplerates_min_index]; -} - -SR_PRIV int dsl_en_ch_num(const struct sr_dev_inst *sdi) -{ - GSList *l; - int channel_en_cnt = 0; - - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - channel_en_cnt += probe->enabled; - } - channel_en_cnt += (channel_en_cnt == 0); - - return channel_en_cnt; -} - -/** - * Check the USB configuration to determine if this is an dsl device. - * - * @return TRUE if the device's configuration profile match dsl hardware - * configuration, FALSE otherwise. - */ -SR_PRIV gboolean dsl_check_conf_profile(libusb_device *dev) -{ - struct libusb_device_descriptor des; - struct libusb_device_handle *hdl; - gboolean ret; - unsigned char strdesc[64]; - - hdl = NULL; - ret = FALSE; - while (!ret) { - /* Assume the FW has not been loaded, unless proven wrong. */ - if (libusb_get_device_descriptor(dev, &des) != 0) - break; - - if (libusb_open(dev, &hdl) != 0) - break; - - if (libusb_get_string_descriptor_ascii(hdl, - des.iManufacturer, strdesc, sizeof(strdesc)) < 0) - break; - if (strncmp((const char *)strdesc, "DreamSourceLab", 14)) - break; - - if (libusb_get_string_descriptor_ascii(hdl, - des.iProduct, strdesc, sizeof(strdesc)) < 0) - break; - if (strncmp((const char *)strdesc, "USB-based DSL Instrument v2", 27)) - break; - - /* If we made it here, it must be an dsl device. */ - ret = TRUE; - } - if (hdl) - libusb_close(hdl); - - return ret; -} - -static int hw_dev_open(struct sr_dev_driver *di, struct sr_dev_inst *sdi) -{ - libusb_device **devlist; - struct sr_usb_dev_inst *usb; - struct libusb_device_descriptor des; - struct DSL_context *devc; - struct drv_context *drvc; - struct version_info vi; - int ret, skip, i, device_count; - struct ctl_rd_cmd rd_cmd; - uint8_t rd_cmd_data[2]; - - drvc = di->priv; - devc = sdi->priv; - usb = sdi->conn; - - if (sdi->status == SR_ST_ACTIVE) { - /* Device is already in use. */ - return SR_ERR; - } - - skip = 0; - device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); - if (device_count < 0) { - sr_err("Failed to get device list: %s.", - libusb_error_name(device_count)); - return SR_ERR; - } - - for (i = 0; i < device_count; i++) { - if ((ret = libusb_get_device_descriptor(devlist[i], &des))) { - sr_err("Failed to get device descriptor: %s.", - libusb_error_name(ret)); - continue; - } - - if (des.idVendor != devc->profile->vid - || des.idProduct != devc->profile->pid) - continue; - - if (sdi->status == SR_ST_INITIALIZING) { - if (skip != sdi->index) { - /* Skip devices of this type that aren't the one we want. */ - skip += 1; - continue; - } - } else if (sdi->status == SR_ST_INACTIVE) { - /* - * This device is fully enumerated, so we need to find - * this device by vendor, product, bus and address. - */ - if (libusb_get_bus_number(devlist[i]) != usb->bus - || libusb_get_device_address(devlist[i]) != usb->address) - /* This is not the one. */ - continue; - } - - if (!(ret = libusb_open(devlist[i], &usb->devhdl))) { - if (usb->address == 0xff) - /* - * First time we touch this device after FW - * upload, so we don't know the address yet. - */ - usb->address = libusb_get_device_address(devlist[i]); - } else { - sr_err("Failed to open device: %s.", - libusb_error_name(ret)); - break; - } - - rd_cmd.header.dest = DSL_CTL_FW_VERSION; - rd_cmd.header.size = 2; - rd_cmd.data = rd_cmd_data; - if ((ret = command_ctl_rd(usb->devhdl, rd_cmd)) != SR_OK) { - sr_err("Failed to get firmware version."); - break; - } - vi.major = rd_cmd_data[0]; - vi.minor = rd_cmd_data[1]; - - /* - * Different versions may have incompatible issue, - * Mark for up level process - */ - if (vi.major != DSL_REQUIRED_VERSION_MAJOR) { - sr_err("Expected firmware version %d.%d, " - "got %d.%d.", DSL_REQUIRED_VERSION_MAJOR, DSL_REQUIRED_VERSION_MINOR, - vi.major, vi.minor); - sdi->status = SR_ST_INCOMPATIBLE; - } else { - sdi->status = SR_ST_ACTIVE; - } - - sr_info("Opened device %d on %d.%d, " - "interface %d, firmware %d.%d.", - sdi->index, usb->bus, usb->address, - USB_INTERFACE, vi.major, vi.minor); - - break; - } - libusb_free_device_list(devlist, 1); - - if ((sdi->status != SR_ST_ACTIVE) && - (sdi->status != SR_ST_INCOMPATIBLE)) - return SR_ERR; - - return SR_OK; -} - -SR_PRIV int dsl_configure_probes(const struct sr_dev_inst *sdi) -{ - struct DSL_context *devc; - struct sr_channel *probe; - GSList *l; - int probe_bit, stage, i; - char *tc; - - devc = sdi->priv; - for (i = 0; i < NUM_TRIGGER_STAGES; i++) { - devc->trigger_mask[i] = 0; - devc->trigger_value[i] = 0; - } - - stage = -1; - for (l = sdi->channels; l; l = l->next) { - probe = (struct sr_channel *)l->data; - if (probe->enabled == FALSE) - continue; - - probe_bit = 1 << (probe->index); - if (!(probe->trigger)) - continue; - - stage = 0; - for (tc = probe->trigger; *tc; tc++) { - devc->trigger_mask[stage] |= probe_bit; - if (*tc == '1') - devc->trigger_value[stage] |= probe_bit; - stage++; - if (stage > NUM_TRIGGER_STAGES) - return SR_ERR; - } - } - - return SR_OK; -} - -SR_PRIV uint64_t dsl_channel_depth(const struct sr_dev_inst *sdi) -{ - struct DSL_context *devc = sdi->priv; - int ch_num = dsl_en_ch_num(sdi); - return devc->profile->dev_caps.hw_depth / (ch_num ? ch_num : 1); -} - -SR_PRIV int dsl_wr_reg(const struct sr_dev_inst *sdi, uint8_t addr, uint8_t value) -{ - struct sr_usb_dev_inst *usb; - struct libusb_device_handle *hdl; - struct ctl_wr_cmd wr_cmd; - int ret; - - usb = sdi->conn; - hdl = usb->devhdl; - - wr_cmd.header.dest = DSL_CTL_I2C_REG; - wr_cmd.header.offset = addr; - wr_cmd.header.size = 1; - wr_cmd.data[0] = value; - if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) { - sr_err("Sent DSL_CTL_I2C_REG command failed."); - return SR_ERR; - } - - return SR_OK; -} - -SR_PRIV int dsl_rd_reg(const struct sr_dev_inst *sdi, uint8_t addr, uint8_t *value) -{ - struct sr_usb_dev_inst *usb; - struct ctl_rd_cmd rd_cmd; - int ret; - - usb = sdi->conn; - - rd_cmd.header.dest = DSL_CTL_I2C_STATUS; - rd_cmd.header.offset = addr; - rd_cmd.header.size = 1; - rd_cmd.data = value; - if ((ret = command_ctl_rd(usb->devhdl, rd_cmd)) != SR_OK) { - sr_err("Sent DSL_CTL_I2C_STATUS read command failed."); - return SR_ERR; - } - - return SR_OK; -} - -SR_PRIV int dsl_wr_ext(const struct sr_dev_inst *sdi, uint8_t addr, uint8_t value) -{ - uint8_t rdata; - int ret; - - // write addr + wr - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_TXR_OFF, EI2C_AWR); - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STA | bmEI2C_WR); - // check done - ret = dsl_rd_reg(sdi, EI2C_ADDR+EI2C_SR_OFF, &rdata); - if (rdata & bmEI2C_RXNACK) { - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STO | bmEI2C_WR); - return SR_ERR; - } - - // write offset + wr - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_TXR_OFF, addr); - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_WR); - // check done - ret = dsl_rd_reg(sdi, EI2C_ADDR+EI2C_SR_OFF, &rdata); - if (rdata & bmEI2C_RXNACK) { - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STO | bmEI2C_WR); - return SR_ERR; - } - - // write value + wr - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_TXR_OFF, value); - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STO | bmEI2C_WR); - // check done - ret = dsl_rd_reg(sdi, EI2C_ADDR+EI2C_SR_OFF, &rdata); - if (rdata & bmEI2C_RXNACK) { - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STO | bmEI2C_WR); - return SR_ERR; - } - - return ret; -} - -SR_PRIV int dsl_rd_ext(const struct sr_dev_inst *sdi, unsigned char *ctx, uint16_t addr, uint8_t len) -{ - uint8_t rdata; - int ret; - - // write addr + wr - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_TXR_OFF, EI2C_AWR); - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STA | bmEI2C_WR); - // check done - ret = dsl_rd_reg(sdi, EI2C_ADDR+EI2C_SR_OFF, &rdata); - if (rdata & bmEI2C_RXNACK) { - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STO | bmEI2C_WR); - return SR_ERR; - } - - // write offset + wr - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_TXR_OFF, addr); - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_WR); - // check done - ret = dsl_rd_reg(sdi, EI2C_ADDR+EI2C_SR_OFF, &rdata); - if (rdata & bmEI2C_RXNACK) { - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STO | bmEI2C_WR); - return SR_ERR; - } - - // write read addr + wr - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_TXR_OFF, EI2C_ARD); - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STA | bmEI2C_WR); - // check done - ret = dsl_rd_reg(sdi, EI2C_ADDR+EI2C_SR_OFF, &rdata); - if (rdata & bmEI2C_RXNACK) { - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STO | bmEI2C_WR); - return SR_ERR; - } - - while(--len) { - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_RD); - ret = dsl_rd_reg(sdi, EI2C_ADDR+EI2C_RXR_OFF, ctx); - ctx++; - } - ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STO | bmEI2C_RD | bmEI2C_NACK); - ret = dsl_rd_reg(sdi, EI2C_ADDR+EI2C_RXR_OFF, ctx); - - return ret; -} - -SR_PRIV int dsl_wr_dso(const struct sr_dev_inst *sdi, uint64_t cmd) -{ - struct sr_usb_dev_inst *usb; - struct libusb_device_handle *hdl; - struct ctl_wr_cmd wr_cmd; - int ret; - - usb = sdi->conn; - hdl = usb->devhdl; - - wr_cmd.header.dest = DSL_CTL_I2C_DSO; - wr_cmd.header.offset = 0; - wr_cmd.header.size = 8; - wr_cmd.data[0] = (uint8_t)cmd; - wr_cmd.data[1] = (uint8_t)(cmd >> 8); - wr_cmd.data[2] = (uint8_t)(cmd >> 16); - wr_cmd.data[3] = (uint8_t)(cmd >> 24); - wr_cmd.data[4] = (uint8_t)(cmd >> 32); - wr_cmd.data[5] = (uint8_t)(cmd >> 40); - wr_cmd.data[6] = (uint8_t)(cmd >> 48); - wr_cmd.data[7] = (uint8_t)(cmd >> 56); - if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) { - sr_err("Sent DSL_CTL_I2C_DSO command failed."); - return SR_ERR; - } - - return SR_OK; -} - -SR_PRIV int dsl_wr_nvm(const struct sr_dev_inst *sdi, unsigned char *ctx, uint16_t addr, uint8_t len) -{ - struct sr_usb_dev_inst *usb; - struct libusb_device_handle *hdl; - struct ctl_wr_cmd wr_cmd; - int ret; - int i; - - usb = sdi->conn; - hdl = usb->devhdl; - - wr_cmd.header.dest = DSL_CTL_NVM; - wr_cmd.header.offset = addr; - wr_cmd.header.size = len; - for (i = 0; i < len; i++) - wr_cmd.data[i] = *(ctx+i); - if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) { - sr_err("Sent DSL_CTL_NVM write command failed."); - return SR_ERR; - } - - return SR_OK; -} - -SR_PRIV int dsl_rd_nvm(const struct sr_dev_inst *sdi, unsigned char *ctx, uint16_t addr, uint8_t len) -{ - struct sr_usb_dev_inst *usb; - struct libusb_device_handle *hdl; - struct ctl_rd_cmd rd_cmd; - int ret; - - usb = sdi->conn; - hdl = usb->devhdl; - - rd_cmd.header.dest = DSL_CTL_NVM; - rd_cmd.header.size = len; - rd_cmd.header.offset = addr; - rd_cmd.data = ctx; - if ((ret = command_ctl_rd(hdl, rd_cmd)) != SR_OK) { - sr_err("Sent DSL_CTL_NVM read command failed."); - return SR_ERR; - } - - return SR_OK; -} - -SR_PRIV int dsl_fpga_arm(const struct sr_dev_inst *sdi) -{ - struct DSL_context *devc; - struct sr_usb_dev_inst *usb; - struct libusb_device_handle *hdl; - struct DSL_setting setting; - int ret; - int transferred; - int i; - GSList *l; - uint32_t tmp_u32; - uint64_t tmp_u64; - const int ch_num = dsl_en_ch_num(sdi); - uint32_t arm_size; - struct ctl_wr_cmd wr_cmd; - struct ctl_rd_cmd rd_cmd; - uint8_t rd_cmd_data; - - devc = sdi->priv; - usb = sdi->conn; - hdl = usb->devhdl; - - setting.sync = 0xf5a5f5a5; - setting.mode_header = 0x0001; - setting.divider_header = 0x0102; - setting.count_header = 0x0302; - setting.trig_pos_header = 0x0502; - setting.trig_glb_header = 0x0701; - setting.ch_en_header = 0x0801; - setting.trig_header = 0x40a0; - setting.end_sync = 0xfa5afa5a; - - // basic configuration -// setting.mode = (trigger->trigger_en << TRIG_EN_BIT) + -// (devc->clock_type << CLK_TYPE_BIT) + -// (devc->clock_edge << CLK_EDGE_BIT) + -// (devc->rle_mode << RLE_MODE_BIT) + -// ((sdi->mode == DSO) << DSO_MODE_BIT) + -// ((((devc->cur_samplerate == (2 * DSLOGIC_MAX_LOGIC_SAMPLERATE)) && sdi->mode != DSO) || (sdi->mode == ANALOG)) << HALF_MODE_BIT) + -// ((devc->cur_samplerate == (4 * DSLOGIC_MAX_LOGIC_SAMPLERATE)) << QUAR_MODE_BIT) + -// ((sdi->mode == ANALOG) << ANALOG_MODE_BIT) + -// ((devc->filter == SR_FILTER_1T) << FILTER_BIT) + -// (devc->instant << INSTANT_BIT) + -// ((trigger->trigger_mode == SERIAL_TRIGGER) << STRIG_MODE_BIT) + -// ((devc->stream) << STREAM_MODE_BIT) + -// ((devc->op_mode == SR_OP_LA_LPTEST) << LPB_TEST_BIT) + -// ((devc->op_mode == SR_OP_LA_EXTEST) << EXT_TEST_BIT) + -// ((devc->op_mode == SR_OP_LA_INTEST) << INT_TEST_BIT); - setting.mode = (trigger->trigger_en << TRIG_EN_BIT) + - (devc->clock_type << CLK_TYPE_BIT) + - (devc->clock_edge << CLK_EDGE_BIT) + - (devc->rle_mode << RLE_MODE_BIT) + - ((sdi->mode == DSO) << DSO_MODE_BIT) + - (((devc->cur_samplerate == (2 * channel_modes[devc->ch_mode].hw_max_samplerate)) && sdi->mode != DSO) << HALF_MODE_BIT) + - ((devc->cur_samplerate == (4 * channel_modes[devc->ch_mode].hw_max_samplerate)) << QUAR_MODE_BIT) + - ((sdi->mode == ANALOG) << ANALOG_MODE_BIT) + - ((devc->filter == SR_FILTER_1T) << FILTER_BIT) + - (devc->instant << INSTANT_BIT) + - ((trigger->trigger_mode == SERIAL_TRIGGER) << STRIG_MODE_BIT) + - ((devc->stream) << STREAM_MODE_BIT) + - ((devc->test_mode == SR_TEST_LOOPBACK) << LPB_TEST_BIT) + - ((devc->test_mode == SR_TEST_EXTERNAL) << EXT_TEST_BIT) + - ((devc->test_mode == SR_TEST_INTERNAL) << INT_TEST_BIT); - - // sample rate divider - tmp_u32 = (sdi->mode == DSO) ? (uint32_t)ceil(channel_modes[devc->ch_mode].max_samplerate * 1.0 / devc->cur_samplerate / ch_num) : - (sdi->mode == ANALOG) ? (uint32_t)ceil(channel_modes[devc->ch_mode].hw_max_samplerate * 1.0 / max(devc->cur_samplerate, channel_modes[devc->ch_mode].hw_min_samplerate)) : - (uint32_t)ceil(channel_modes[devc->ch_mode].hw_max_samplerate * 1.0 / devc->cur_samplerate); - devc->unit_pitch = ceil(channel_modes[devc->ch_mode].hw_min_samplerate * 1.0 / devc->cur_samplerate); - setting.div_l = tmp_u32 & 0x0000ffff; - setting.div_h = tmp_u32 >> 16; - - // capture counter - tmp_u64 = (sdi->mode == DSO) ? (devc->actual_samples / (channel_modes[devc->ch_mode].num / ch_num)) : - (devc->limit_samples); - tmp_u64 >>= 4; // hardware minimum unit 64 - setting.cnt_l = tmp_u64 & 0x0000ffff; - setting.cnt_h = tmp_u64 >> 16; - - // trigger position - // must be align to minimum parallel bits - tmp_u32 = max((uint32_t)(trigger->trigger_pos / 100.0 * devc->limit_samples), DSLOGIC_ATOMIC_SAMPLES); - if (devc->stream) - tmp_u32 = min(tmp_u32, dsl_channel_depth(sdi) * 10 / 100); - else - tmp_u32 = min(tmp_u32, dsl_channel_depth(sdi) * DS_MAX_TRIG_PERCENT / 100); - setting.tpos_l = tmp_u32 & DSLOGIC_ATOMIC_MASK; - setting.tpos_h = tmp_u32 >> 16; - - // trigger global settings - setting.trig_glb = ((ch_num & 0xf) << 4) + - trigger->trigger_stages; - - // channel enable mapping - setting.ch_en = 0; - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - setting.ch_en += probe->enabled << probe->index; - } - - // trigger advanced configuration - if (trigger->trigger_mode == SIMPLE_TRIGGER) { - setting.trig_mask0[0] = ds_trigger_get_mask0(TriggerStages); - setting.trig_mask1[0] = ds_trigger_get_mask1(TriggerStages); - - setting.trig_value0[0] = ds_trigger_get_value0(TriggerStages); - setting.trig_value1[0] = ds_trigger_get_value1(TriggerStages); - - setting.trig_edge0[0] = ds_trigger_get_edge0(TriggerStages); - setting.trig_edge1[0] = ds_trigger_get_edge1(TriggerStages); - - if (setting.mode & (1 << QUAR_MODE_BIT)) { - setting.trig_mask0[0] = ((setting.trig_mask0[0] & 0x0f) << 12) + - ((setting.trig_mask0[0] & 0x0f) << 8) + - ((setting.trig_mask0[0] & 0x0f) << 4) + - ((setting.trig_mask0[0] & 0x0f) << 0); - setting.trig_mask1[0] = ((setting.trig_mask1[0] & 0x0f) << 12) + - ((setting.trig_mask1[0] & 0x0f) << 8) + - ((setting.trig_mask1[0] & 0x0f) << 4) + - ((setting.trig_mask1[0] & 0x0f) << 0); - setting.trig_value0[0] = ((setting.trig_value0[0] & 0x0f) << 12) + - ((setting.trig_value0[0] & 0x0f) << 8) + - ((setting.trig_value0[0] & 0x0f) << 4) + - ((setting.trig_value0[0] & 0x0f) << 0); - setting.trig_value1[0] = ((setting.trig_value1[0] & 0x0f) << 12) + - ((setting.trig_value1[0] & 0x0f) << 8) + - ((setting.trig_value1[0] & 0x0f) << 4) + - ((setting.trig_value1[0] & 0x0f) << 0); - setting.trig_edge0[0] = ((setting.trig_edge0[0] & 0x0f) << 12) + - ((setting.trig_edge0[0] & 0x0f) << 8) + - ((setting.trig_edge0[0] & 0x0f) << 4) + - ((setting.trig_edge0[0] & 0x0f) << 0); - setting.trig_edge1[0] = ((setting.trig_edge1[0] & 0x0f) << 12) + - ((setting.trig_edge1[0] & 0x0f) << 8) + - ((setting.trig_edge1[0] & 0x0f) << 4) + - ((setting.trig_edge1[0] & 0x0f) << 0); - } else if (setting.mode & (1 << HALF_MODE_BIT)) { - setting.trig_mask0[0] = ((setting.trig_mask0[0] & 0xff) << 8) + - ((setting.trig_mask0[0] & 0xff) << 0); - setting.trig_mask1[0] = ((setting.trig_mask1[0] & 0xff) << 8) + - ((setting.trig_mask1[0] & 0xff) << 0); - setting.trig_value0[0] = ((setting.trig_value0[0] & 0xff) << 8) + - ((setting.trig_value0[0] & 0xff) << 0); - setting.trig_value1[0] = ((setting.trig_value1[0] & 0xff) << 8) + - ((setting.trig_value1[0] & 0xff) << 0); - setting.trig_edge0[0] = ((setting.trig_edge0[0] & 0xff) << 8) + - ((setting.trig_edge0[0] & 0xff) << 0); - setting.trig_edge1[0] = ((setting.trig_edge1[0] & 0xff) << 8) + - ((setting.trig_edge1[0] & 0xff) << 0); - } - - setting.trig_logic0[0] = (trigger->trigger_logic[TriggerStages] << 1) + trigger->trigger0_inv[TriggerStages]; - setting.trig_logic1[0] = (trigger->trigger_logic[TriggerStages] << 1) + trigger->trigger1_inv[TriggerStages]; - - setting.trig_count[0] = trigger->trigger0_count[TriggerStages]; - - for (i = 1; i < NUM_TRIGGER_STAGES; i++) { - setting.trig_mask0[i] = 0xffff; - setting.trig_mask1[i] = 0xffff; - - setting.trig_value0[i] = 0; - setting.trig_value1[i] = 0; - - setting.trig_edge0[i] = 0; - setting.trig_edge1[i] = 0; - - setting.trig_logic0[i] = 2; - setting.trig_logic1[i] = 2; - - setting.trig_count[i] = 0; - } - } else { - for (i = 0; i < NUM_TRIGGER_STAGES; i++) { - setting.trig_mask0[i] = ds_trigger_get_mask0(i); - setting.trig_mask1[i] = ds_trigger_get_mask1(i); - - setting.trig_value0[i] = ds_trigger_get_value0(i); - setting.trig_value1[i] = ds_trigger_get_value1(i); - - setting.trig_edge0[i] = ds_trigger_get_edge0(i); - setting.trig_edge1[i] = ds_trigger_get_edge1(i); - - if (setting.mode & (1 << STRIG_MODE_BIT) && i == STriggerDataStage) { - // serial trigger, data mask/value should not be duplicated - } else { - if (setting.mode & (1 << QUAR_MODE_BIT)) { - setting.trig_mask0[i] = ((setting.trig_mask0[i] & 0x0f) << 12) + - ((setting.trig_mask0[i] & 0x0f) << 8) + - ((setting.trig_mask0[i] & 0x0f) << 4) + - ((setting.trig_mask0[i] & 0x0f) << 0); - setting.trig_mask1[i] = ((setting.trig_mask1[i] & 0x0f) << 12) + - ((setting.trig_mask1[i] & 0x0f) << 8) + - ((setting.trig_mask1[i] & 0x0f) << 4) + - ((setting.trig_mask1[i] & 0x0f) << 0); - setting.trig_value0[i] = ((setting.trig_value0[i] & 0x0f) << 12) + - ((setting.trig_value0[i] & 0x0f) << 8) + - ((setting.trig_value0[i] & 0x0f) << 4) + - ((setting.trig_value0[i] & 0x0f) << 0); - setting.trig_value1[i] = ((setting.trig_value1[i] & 0x0f) << 12) + - ((setting.trig_value1[i] & 0x0f) << 8) + - ((setting.trig_value1[i] & 0x0f) << 4) + - ((setting.trig_value1[i] & 0x0f) << 0); - setting.trig_edge0[i] = ((setting.trig_edge0[i] & 0x0f) << 12) + - ((setting.trig_edge0[i] & 0x0f) << 8) + - ((setting.trig_edge0[i] & 0x0f) << 4) + - ((setting.trig_edge0[i] & 0x0f) << 0); - setting.trig_edge1[i] = ((setting.trig_edge1[i] & 0x0f) << 12) + - ((setting.trig_edge1[i] & 0x0f) << 8) + - ((setting.trig_edge1[i] & 0x0f) << 4) + - ((setting.trig_edge1[i] & 0x0f) << 0); - } else if (setting.mode & (1 << HALF_MODE_BIT)) { - setting.trig_mask0[i] = ((setting.trig_mask0[i] & 0xff) << 8) + - ((setting.trig_mask0[i] & 0xff) << 0); - setting.trig_mask1[i] = ((setting.trig_mask1[i] & 0xff) << 8) + - ((setting.trig_mask1[i] & 0xff) << 0); - setting.trig_value0[i] = ((setting.trig_value0[i] & 0xff) << 8) + - ((setting.trig_value0[i] & 0xff) << 0); - setting.trig_value1[i] = ((setting.trig_value1[i] & 0xff) << 8) + - ((setting.trig_value1[i] & 0xff) << 0); - setting.trig_edge0[i] = ((setting.trig_edge0[i] & 0xff) << 8) + - ((setting.trig_edge0[i] & 0xff) << 0); - setting.trig_edge1[i] = ((setting.trig_edge1[i] & 0xff) << 8) + - ((setting.trig_edge1[i] & 0xff) << 0); - } - } - - setting.trig_logic0[i] = (trigger->trigger_logic[i] << 1) + trigger->trigger0_inv[i]; - setting.trig_logic1[i] = (trigger->trigger_logic[i] << 1) + trigger->trigger1_inv[i]; - - setting.trig_count[i] = trigger->trigger0_count[i]; - } - } - - // set GPIF to be wordwide - wr_cmd.header.dest = DSL_CTL_WORDWIDE; - wr_cmd.header.size = 1; - wr_cmd.data[0] = bmWR_WORDWIDE; - if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) { - sr_err("Sent DSL_CTL_WORDWIDE command failed."); - return SR_ERR; - } - - // send bulk write control command - arm_size = sizeof(struct DSL_setting) / sizeof(uint16_t); - wr_cmd.header.dest = DSL_CTL_BULK_WR; - wr_cmd.header.size = 3; - wr_cmd.data[0] = (uint8_t)arm_size; - wr_cmd.data[1] = (uint8_t)(arm_size >> 8); - wr_cmd.data[2] = (uint8_t)(arm_size >> 16); - if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) { - sr_err("Sent bulk write command of arm FPGA failed."); - return SR_ERR; - } - //command_fpga_setting(hdl, arm_size); - - // send bulk data - ret = libusb_bulk_transfer(hdl, 2 | LIBUSB_ENDPOINT_OUT, - (unsigned char *)&setting, - sizeof(struct DSL_setting), - &transferred, 1000); - if (ret < 0) { - sr_err("Unable to arm FPGA of dsl device: %s.", - libusb_error_name(ret)); - return SR_ERR; - } else if (transferred != sizeof(struct DSL_setting)) { - sr_err("Arm FPGA error: expacted transfer size %d; actually %d", - sizeof(struct DSL_setting), transferred); - return SR_ERR; - } - - // assert INTRDY high (indicate data end) - wr_cmd.header.dest = DSL_CTL_INTRDY; - wr_cmd.header.size = 1; - wr_cmd.data[0] = bmWR_INTRDY; - if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) - return SR_ERR; - - - // check FPGA_DONE bit - rd_cmd.header.dest = DSL_CTL_HW_STATUS; - rd_cmd.header.size = 1; - rd_cmd_data = 0; - rd_cmd.data = &rd_cmd_data; - if ((ret = command_ctl_rd(hdl, rd_cmd)) != SR_OK) - return SR_ERR; - if (rd_cmd_data & bmGPIF_DONE) { - sr_info("Arm FPGA done"); - return SR_OK; - } else { - return SR_ERR; - } -} - -SR_PRIV int dsl_fpga_config(struct libusb_device_handle *hdl, const char *filename) -{ - FILE *fw; - int chunksize, ret; - unsigned char *buf; - int transferred; - uint64_t filesize; - struct ctl_wr_cmd wr_cmd; - struct ctl_rd_cmd rd_cmd; - uint8_t rd_cmd_data; - struct stat f_stat; - - sr_info("Configure FPGA using %s", filename); - if ((fw = fopen(filename, "rb")) == NULL) { - sr_err("Unable to open FPGA bit file %s for reading: %s", - filename, strerror(errno)); - return SR_ERR; - } - - if (stat(filename, &f_stat) == -1) - return SR_ERR; - - filesize = (uint64_t)f_stat.st_size; - - if (!(buf = g_try_malloc(filesize))) { - sr_err("FPGA configure buf malloc failed."); - return SR_ERR; - } - - // step0: assert PROG_B low - wr_cmd.header.dest = DSL_CTL_PROG_B; - wr_cmd.header.size = 1; - wr_cmd.data[0] = ~bmWR_PROG_B; - if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) - return SR_ERR; - - // step1: turn off GREEN/RED led - wr_cmd.header.dest = DSL_CTL_LED; - wr_cmd.header.size = 1; - wr_cmd.data[0] = ~bmLED_GREEN & ~bmLED_RED; - if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) - return SR_ERR; - - // step2: assert PORG_B high - wr_cmd.header.dest = DSL_CTL_PROG_B; - wr_cmd.header.size = 1; - wr_cmd.data[0] = bmWR_PROG_B; - if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) - return SR_ERR; - - // step3: wait INIT_B go high - rd_cmd.header.dest = DSL_CTL_HW_STATUS; - rd_cmd.header.size = 1; - rd_cmd_data = 0; - rd_cmd.data = &rd_cmd_data; - while(1) { - if ((ret = command_ctl_rd(hdl, rd_cmd)) != SR_OK) - return SR_ERR; - if (rd_cmd_data & bmFPGA_INIT_B) - break; - } - - // step4: send config ctl command - wr_cmd.header.dest = DSL_CTL_WORDWIDE; - wr_cmd.header.size = 1; - wr_cmd.data[0] = ~bmWR_WORDWIDE; - if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) { - sr_err("Sent DSL_CTL_WORDWIDE command failed."); - return SR_ERR; - } - wr_cmd.header.dest = DSL_CTL_INTRDY; - wr_cmd.header.size = 1; - wr_cmd.data[0] = (uint8_t)~bmWR_INTRDY; - if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) - return SR_ERR; - - wr_cmd.header.dest = DSL_CTL_BULK_WR; - wr_cmd.header.size = 3; - wr_cmd.data[0] = (uint8_t)filesize; - wr_cmd.data[1] = (uint8_t)(filesize >> 8); - wr_cmd.data[2] = (uint8_t)(filesize >> 16); - if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) { - sr_err("Configure FPGA error: send command fpga_config failed."); - return SR_ERR; - } - - // step5: send config data - chunksize = fread(buf, 1, filesize, fw); - if (chunksize == 0) - return SR_ERR; - - ret = libusb_bulk_transfer(hdl, 2 | LIBUSB_ENDPOINT_OUT, - buf, chunksize, - &transferred, 1000); - fclose(fw); - g_free(buf); - - if (ret < 0) { - sr_err("Unable to configure FPGA of dsl device: %s.", - libusb_error_name(ret)); - return SR_ERR; - } else if (transferred != chunksize) { - sr_err("Configure FPGA error: expacted transfer size %d; actually %d.", - chunksize, transferred); - return SR_ERR; - } - - // step6: assert INTRDY high (indicate data end) - wr_cmd.header.dest = DSL_CTL_INTRDY; - wr_cmd.header.size = 1; - wr_cmd.data[0] = bmWR_INTRDY; - if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) - return SR_ERR; - - // step7: check GPIF_DONE - rd_cmd.header.dest = DSL_CTL_HW_STATUS; - rd_cmd.header.size = 1; - rd_cmd_data = 0; - rd_cmd.data = &rd_cmd_data; - while ((ret = command_ctl_rd(hdl, rd_cmd)) == SR_OK) { - if (rd_cmd_data & bmGPIF_DONE) { - break; - } - } - - // step8: assert INTRDY low - wr_cmd.header.dest = DSL_CTL_INTRDY; - wr_cmd.header.size = 1; - wr_cmd.data[0] = (uint8_t)~bmWR_INTRDY; - if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) - return SR_ERR; - - // step9: check FPGA_DONE bit - rd_cmd.header.dest = DSL_CTL_HW_STATUS; - rd_cmd.header.size = 1; - rd_cmd_data = 0; - rd_cmd.data = &rd_cmd_data; - if ((ret = command_ctl_rd(hdl, rd_cmd)) != SR_OK) - return SR_ERR; - if (rd_cmd_data & bmFPGA_DONE) { - // step10: turn on GREEN led - wr_cmd.header.dest = DSL_CTL_LED; - wr_cmd.data[0] = bmLED_GREEN; - if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) - return SR_ERR; - } else { - return SR_ERR; - } - - sr_info("FPGA configure done: %d bytes.", chunksize); - return SR_OK; -} - -SR_PRIV int dsl_config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, - const struct sr_channel *ch, - const struct sr_channel_group *cg) -{ - struct DSL_context *devc = sdi->priv; - struct sr_usb_dev_inst *usb; - char str[128]; - - (void)cg; - - switch (id) { - case SR_CONF_CONN: - if (!sdi || !sdi->conn) - return SR_ERR_ARG; - usb = sdi->conn; - if (usb->address == 255) - /* Device still needs to re-enumerate after firmware - * upload, so we don't know its (future) address. */ - return SR_ERR; - snprintf(str, 128, "%d.%d", usb->bus, usb->address); - *data = g_variant_new_string(str); - break; - case SR_CONF_LIMIT_SAMPLES: - if (!sdi) - return SR_ERR; - *data = g_variant_new_uint64(devc->limit_samples); - break; - case SR_CONF_SAMPLERATE: - if (!sdi) - return SR_ERR; - *data = g_variant_new_uint64(devc->cur_samplerate); - break; - case SR_CONF_RLE_SUPPORT: - if (!sdi) - return SR_ERR; - *data = g_variant_new_boolean(devc->rle_support); - break; - case SR_CONF_CLOCK_TYPE: - if (!sdi) - return SR_ERR; - *data = g_variant_new_boolean(devc->clock_type); - break; - case SR_CONF_CLOCK_EDGE: - if (!sdi) - return SR_ERR; - *data = g_variant_new_boolean(devc->clock_edge); - break; - case SR_CONF_INSTANT: - if (!sdi) - return SR_ERR; - *data = g_variant_new_boolean(devc->instant); - break; - case SR_CONF_PROBE_VDIV: - if (!ch) - return SR_ERR; - *data = g_variant_new_uint64(ch->vdiv); - break; - case SR_CONF_PROBE_FACTOR: - if (!ch) - return SR_ERR; - *data = g_variant_new_uint64(ch->vfactor); - break; - case SR_CONF_PROBE_VPOS: - if (!ch) - return SR_ERR; - *data = g_variant_new_double(ch->vpos); - break; - case SR_CONF_TIMEBASE: - if (!sdi) - return SR_ERR; - *data = g_variant_new_uint64(devc->timebase); - break; - case SR_CONF_MAX_TIMEBASE: - if (!sdi) - return SR_ERR; - *data = g_variant_new_uint64(min(MAX_TIMEBASE, - SR_SEC(1) * - devc->profile->dev_caps.dso_depth / - channel_modes[devc->ch_mode].num / - channel_modes[devc->ch_mode].min_samplerate / - DS_CONF_DSO_HDIVS)); - break; - case SR_CONF_PROBE_COUPLING: - if (!ch) - return SR_ERR; - *data = g_variant_new_byte(ch->coupling); - break; - case SR_CONF_PROBE_EN: - if (!ch) - return SR_ERR; - *data = g_variant_new_boolean(ch->enabled); - break; - case SR_CONF_TRIGGER_SLOPE: - if (!sdi) - return SR_ERR; - *data = g_variant_new_byte(devc->trigger_slope); - break; - case SR_CONF_TRIGGER_SOURCE: - if (!sdi) - return SR_ERR; - *data = g_variant_new_byte(devc->trigger_source&0x0f); - break; - case SR_CONF_TRIGGER_CHANNEL: - if (!sdi) - return SR_ERR; - *data = g_variant_new_byte(devc->trigger_source>>4); - break; - case SR_CONF_TRIGGER_VALUE: - if (!ch) - return SR_ERR; - *data = g_variant_new_byte(ch->trig_value); - break; - case SR_CONF_HORIZ_TRIGGERPOS: - if (!sdi) - return SR_ERR; - if (sdi->mode == DSO) { - *data = g_variant_new_byte(devc->trigger_hrate); - } else { - *data = g_variant_new_byte(devc->trigger_hpos); - } - break; - case SR_CONF_TRIGGER_HOLDOFF: - if (!sdi) - return SR_ERR; - *data = g_variant_new_uint64(devc->trigger_holdoff); - break; - case SR_CONF_TRIGGER_MARGIN: - if (!sdi) - return SR_ERR; - *data = g_variant_new_byte(devc->trigger_margin); - break; - case SR_CONF_HAVE_ZERO: - if (!sdi) - return SR_ERR; - *data = g_variant_new_boolean(devc->profile->dev_caps.feature_caps & CAPS_FEATURE_ZERO); - break; - case SR_CONF_ZERO: - if (!sdi) - return SR_ERR; - if (sdi->mode == DSO) - *data = g_variant_new_boolean(devc->zero); - else - *data = g_variant_new_boolean(FALSE); - break; - case SR_CONF_ROLL: - if (!sdi) - return SR_ERR; - *data = g_variant_new_boolean(devc->roll); - break; - case SR_CONF_UNIT_BITS: - if (!sdi) - return SR_ERR; - *data = g_variant_new_byte(channel_modes[devc->ch_mode].unit_bits); - break; - case SR_CONF_PROBE_MAP_UNIT: - if (!sdi || !ch) - return SR_ERR; - *data = g_variant_new_string(ch->map_unit); - break; - case SR_CONF_PROBE_MAP_MIN: - if (!sdi || !ch) - return SR_ERR; - *data = g_variant_new_double(ch->map_min); - break; - case SR_CONF_PROBE_MAP_MAX: - if (!sdi || !ch) - return SR_ERR; - *data = g_variant_new_double(ch->map_max); - break; - default: - return SR_ERR_NA; - } - - return SR_OK; -} - -SR_PRIV int dsl_config_list(int key, GVariant **data, const struct sr_dev_inst *sdi, - const struct sr_channel_group *cg) -{ - struct DSL_context *devc; - GVariant *gvar; - GVariantBuilder gvb; - int i; - - (void)cg; - devc = sdi->priv; - - switch (key) { - case SR_CONF_SAMPLERATE: - g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}")); -// gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates, -// ARRAY_SIZE(samplerates), sizeof(uint64_t)); - gvar = g_variant_new_from_data(G_VARIANT_TYPE("at"), - samplerates + devc->samplerates_min_index, - (devc->samplerates_max_index - devc->samplerates_min_index + 1) * sizeof(uint64_t), TRUE, NULL, NULL); - g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar); - *data = g_variant_builder_end(&gvb); - break; - - case SR_CONF_PROBE_CONFIGS: - *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), - probeOptions, ARRAY_SIZE(probeOptions)*sizeof(int32_t), TRUE, NULL, NULL); - break; - case SR_CONF_PROBE_SESSIONS: - *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), - probeSessions, ARRAY_SIZE(probeSessions)*sizeof(int32_t), TRUE, NULL, NULL); - break; - case SR_CONF_PROBE_VDIV: - g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}")); - for (i = 0; devc->profile->dev_caps.vdivs[i]; i++); - gvar = g_variant_new_from_data(G_VARIANT_TYPE("at"), - devc->profile->dev_caps.vdivs, i*sizeof(uint64_t), TRUE, NULL, NULL); - g_variant_builder_add(&gvb, "{sv}", "vdivs", gvar); - *data = g_variant_builder_end(&gvb); - break; - case SR_CONF_PROBE_COUPLING: - g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}")); - gvar = g_variant_new_from_data(G_VARIANT_TYPE("ay"), - probeCoupling, ARRAY_SIZE(probeCoupling)*sizeof(uint8_t), TRUE, NULL, NULL); - g_variant_builder_add(&gvb, "{sv}", "coupling", gvar); - *data = g_variant_builder_end(&gvb); - break; - case SR_CONF_PROBE_MAP_UNIT: - *data = g_variant_new_strv(probeMapUnits, ARRAY_SIZE(probeMapUnits)); - break; - default: - return SR_ERR_NA; - } - - return SR_OK; -} - -SR_PRIV int dsl_dev_open(struct sr_dev_driver *di, struct sr_dev_inst *sdi, gboolean *fpga_done) -{ - struct sr_usb_dev_inst *usb; - struct DSL_context *devc; - int ret; - uint8_t hw_info; - struct ctl_rd_cmd rd_cmd; - - devc = sdi->priv; - usb = sdi->conn; - - /* - * If the firmware was recently uploaded, no dev_open operation should be called. - * Just wait for renumerate -> detach -> attach - */ - ret = SR_ERR; - if (devc->fw_updated > 0) { - return SR_ERR; - } else { - sr_info("%s: Firmware upload was not needed.", __func__); - ret = hw_dev_open(di, sdi); - } - - if (ret != SR_OK) { - sr_err("%s: Unable to open device.", __func__); - return SR_ERR; - } - - ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE); - if (ret != 0) { - switch(ret) { - case LIBUSB_ERROR_BUSY: - sr_err("%s: Unable to claim USB interface. Another " - "program or driver has already claimed it.", __func__); - break; - case LIBUSB_ERROR_NO_DEVICE: - sr_err("%s: Device has been disconnected.", __func__); - break; - default: - sr_err("%s: Unable to claim interface: %s.", - __func__, libusb_error_name(ret)); - break; - } - - return SR_ERR; - } - - rd_cmd.header.dest = DSL_CTL_HW_STATUS; - rd_cmd.header.size = 1; - hw_info = 0; - rd_cmd.data = &hw_info; - if ((ret = command_ctl_rd(usb->devhdl, rd_cmd)) != SR_OK) { - sr_err("Failed to get hardware infos."); - return SR_ERR; - } - *fpga_done = (hw_info & bmFPGA_DONE) != 0; - - if ((sdi->status == SR_ST_ACTIVE) && !(*fpga_done)) { - char *fpga_bit; - if (!(fpga_bit = g_try_malloc(strlen(DS_RES_PATH)+strlen(devc->profile->fpga_bit33)+1))) { - sr_err("fpag_bit path malloc error!"); - return SR_ERR_MALLOC; - } - strcpy(fpga_bit, DS_RES_PATH); - switch(devc->th_level) { - case SR_TH_3V3: - strcat(fpga_bit, devc->profile->fpga_bit33); - break; - case SR_TH_5V0: - strcat(fpga_bit, devc->profile->fpga_bit50); - break; - default: - return SR_ERR; - } - ret = dsl_fpga_config(usb->devhdl, fpga_bit); - g_free(fpga_bit); - if (ret != SR_OK) { - sr_err("%s: Configure FPGA failed!", __func__); - return SR_ERR; - } - } - - - return SR_OK; -} - -SR_PRIV int dsl_dev_close(struct sr_dev_inst *sdi) -{ - struct sr_usb_dev_inst *usb; - - usb = sdi->conn; - if (usb->devhdl == NULL) - return SR_ERR; - - sr_info("%s: Closing device %d on %d.%d interface %d.", - sdi->driver->name, sdi->index, usb->bus, usb->address, USB_INTERFACE); - libusb_release_interface(usb->devhdl, USB_INTERFACE); - libusb_close(usb->devhdl); - usb->devhdl = NULL; - sdi->status = SR_ST_INACTIVE; - - return SR_OK; -} - -SR_PRIV int dsl_dev_acquisition_stop(const struct sr_dev_inst *sdi, void *cb_data) -{ - (void)cb_data; - - struct DSL_context *devc; - struct sr_usb_dev_inst *usb; - int ret; - struct ctl_wr_cmd wr_cmd; - - devc = sdi->priv; - usb = sdi->conn; - - if (!devc->abort) { - devc->abort = TRUE; - dsl_wr_reg(sdi, CTR0_ADDR, bmFORCE_RDY); - } else if (devc->status == DSL_FINISH) { - /* Stop GPIF acquisition */ - wr_cmd.header.dest = DSL_CTL_STOP; - wr_cmd.header.size = 0; - if ((ret = command_ctl_wr(usb->devhdl, wr_cmd)) != SR_OK) - sr_err("%s: Sent acquisition stop command failed!", __func__); - else - sr_info("%s: Sent acquisition stop command!", __func__); - } - - return SR_OK; -} - -SR_PRIV int dsl_dev_status_get(const struct sr_dev_inst *sdi, struct sr_status *status, gboolean prg, int begin, int end) -{ - int ret = SR_ERR; - struct ctl_rd_cmd rd_cmd; - - if (sdi) { - struct DSL_context *devc; - struct sr_usb_dev_inst *usb; - - devc = sdi->priv; - usb = sdi->conn; - if (prg && (devc->status == DSL_START)) { - rd_cmd.header.dest = DSL_CTL_I2C_STATUS; - rd_cmd.header.offset = begin; - rd_cmd.header.size = end - begin + 1; - rd_cmd.data = (unsigned char*)status; - ret = command_ctl_rd(usb->devhdl, rd_cmd); - } else if (devc->mstatus_valid) { - *status = devc->mstatus; - ret = SR_OK; - } - } - - return ret; -} - -static unsigned int to_bytes_per_ms(struct DSL_context *devc) -{ - struct sr_dev_inst *sdi = devc->cb_data; - if (sdi->mode == LOGIC) { - if (devc->cur_samplerate > SR_MHZ(100)) - return SR_MHZ(100) / 1000 * dsl_en_ch_num(sdi) / 8; - else - return ceil(devc->cur_samplerate / 1000.0 * dsl_en_ch_num(sdi) / 8); - } else { - if (devc->cur_samplerate > SR_MHZ(100)) - return SR_MHZ(100) / 1000.0 * dsl_en_ch_num(sdi); - else - return ceil(devc->cur_samplerate / 1000.0 * dsl_en_ch_num(sdi)); - } -} - -static size_t get_buffer_size(struct DSL_context *devc) -{ - size_t s; - - /* - * The buffer should be large enough to hold 10ms of data and - * a multiple of 512. - */ - s = single_buffer_time * to_bytes_per_ms(devc); - //s = to_bytes_per_ms(devc->cur_samplerate); - return (s + 511) & ~511; -} - -static unsigned int get_number_of_transfers(struct DSL_context *devc) -{ - unsigned int n; - /* Total buffer size should be able to hold about 100ms of data. */ - n = ceil(total_buffer_time * 1.0f * to_bytes_per_ms(devc) / get_buffer_size(devc)); - - if (n > NUM_SIMUL_TRANSFERS) - return NUM_SIMUL_TRANSFERS; - - return n; -} - -SR_PRIV unsigned int dsl_get_timeout(struct DSL_context *devc) -{ - size_t total_size; - unsigned int timeout; - - total_size = get_buffer_size(devc) * get_number_of_transfers(devc); - timeout = total_size / to_bytes_per_ms(devc); - - if (devc->stream) - return timeout + timeout / 4; /* Leave a headroom of 25% percent. */ - else - return 1000; -} - -static void finish_acquisition(struct DSL_context *devc) -{ - struct sr_datafeed_packet packet; - - sr_info("%s: send SR_DF_END packet", __func__); - /* Terminate session. */ - packet.type = SR_DF_END; - packet.status = SR_PKT_OK; - sr_session_send(devc->cb_data, &packet); - - if (devc->num_transfers != 0) { - devc->num_transfers = 0; - g_free(devc->transfers); - } - - devc->status = DSL_FINISH; -} - -static void free_transfer(struct libusb_transfer *transfer) -{ - struct DSL_context *devc; - unsigned int i; - - devc = transfer->user_data; - - g_free(transfer->buffer); - transfer->buffer = NULL; - libusb_free_transfer(transfer); - - for (i = 0; i < devc->num_transfers; i++) { - if (devc->transfers[i] == transfer) { - devc->transfers[i] = NULL; - break; - } - } - - devc->submitted_transfers--; - if (devc->submitted_transfers == 0) - finish_acquisition(devc); -} - -static void resubmit_transfer(struct libusb_transfer *transfer) -{ - int ret; - - if ((ret = libusb_submit_transfer(transfer)) == LIBUSB_SUCCESS) - return; - - free_transfer(transfer); - /* TODO: Stop session? */ - - sr_err("%s: %s", __func__, libusb_error_name(ret)); -} - -static void receive_transfer(struct libusb_transfer *transfer) -{ - struct sr_datafeed_packet packet; - struct sr_datafeed_logic logic; - struct sr_datafeed_dso dso; - struct sr_datafeed_analog analog; - uint64_t cur_sample_count = 0; - - uint8_t *cur_buf = transfer->buffer; - struct DSL_context *devc = transfer->user_data; - struct sr_dev_inst *sdi = devc->cb_data; - - if (devc->status == DSL_START) - devc->status = DSL_DATA; - - if (devc->abort) - devc->status = DSL_STOP; - - sr_info("%" PRIu64 ": receive_transfer(): status %d; timeout %d; received %d bytes.", - g_get_monotonic_time(), transfer->status, transfer->timeout, transfer->actual_length); - - switch (transfer->status) { - case LIBUSB_TRANSFER_COMPLETED: - case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though. */ - break; - default: - devc->status = DSL_ERROR; - break; - } - - packet.status = SR_PKT_OK; - if (devc->status == DSL_DATA && - transfer->actual_length != 0) { - /* Send the incoming transfer to the session bus. */ - // check packet type - if (sdi->mode == LOGIC) { - packet.type = SR_DF_LOGIC; - packet.payload = &logic; - cur_sample_count = transfer->actual_length * 8 / dsl_en_ch_num(sdi) ; - logic.length = transfer->actual_length; - logic.format = LA_CROSS_DATA; - logic.data_error = 0; - logic.data = cur_buf; - } else if (sdi->mode == DSO) { - if (!devc->instant) { - const uint32_t mstatus_offset = devc->actual_samples / (channel_modes[devc->ch_mode].num/dsl_en_ch_num(sdi)); - devc->mstatus.pkt_id = *((const uint16_t*)cur_buf + mstatus_offset); - devc->mstatus.ch0_max = *((const uint8_t*)cur_buf + mstatus_offset*2 + 1*2); - devc->mstatus.ch0_min = *((const uint8_t*)cur_buf + mstatus_offset*2 + 3); - devc->mstatus.ch0_period = *((const uint32_t*)cur_buf + mstatus_offset/2 + 2/2); - devc->mstatus.ch0_period += ((uint64_t)*((const uint32_t*)cur_buf + mstatus_offset/2 + 4/2)) << 32; - devc->mstatus.ch0_pcnt = *((const uint32_t*)cur_buf + mstatus_offset/2 + 6/2); - devc->mstatus.ch1_max = *((const uint8_t*)cur_buf + mstatus_offset*2 + 9*2); - devc->mstatus.ch1_min = *((const uint8_t*)cur_buf + mstatus_offset*2 + 19); - devc->mstatus.ch1_period = *((const uint32_t*)cur_buf + mstatus_offset/2 + 10/2); - devc->mstatus.ch1_period += ((uint64_t)*((const uint32_t*)cur_buf + mstatus_offset/2 + 12/2)) << 32; - devc->mstatus.ch1_pcnt = *((const uint32_t*)cur_buf + mstatus_offset/2 + 14/2); - devc->mstatus.vlen = *((const uint32_t*)cur_buf + mstatus_offset/2 + 16/2) & 0x7fffffff; - devc->mstatus.stream_mode = *((const uint32_t*)cur_buf + mstatus_offset/2 + 16/2) & 0x80000000; - devc->mstatus.sample_divider = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2) & 0x0fffffff; - devc->mstatus.sample_divider_tog = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2) & 0x80000000; - devc->mstatus.trig_flag = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2) & 0x40000000; - } else { - devc->mstatus.vlen = instant_buffer_size; - } - - const uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(channel_modes[devc->ch_mode].max_samplerate * 1.0 / devc->cur_samplerate / dsl_en_ch_num(sdi)); - if ((devc->mstatus.pkt_id == DSO_PKTID && - devc->mstatus.sample_divider == divider && - devc->mstatus.vlen != 0 && - devc->mstatus.vlen <= (uint32_t)(transfer->actual_length - 512) / 2) || - devc->instant) { - devc->roll = (devc->mstatus.stream_mode != 0); - devc->mstatus_valid = devc->instant ? FALSE : TRUE; - packet.type = SR_DF_DSO; - packet.payload = &dso; - dso.probes = sdi->channels; - cur_sample_count = min(2 * devc->mstatus.vlen / dsl_en_ch_num(sdi), devc->limit_samples); - dso.num_samples = cur_sample_count; - dso.mq = SR_MQ_VOLTAGE; - dso.unit = SR_UNIT_VOLT; - dso.mqflags = SR_MQFLAG_AC; - dso.samplerate_tog = (devc->mstatus.sample_divider_tog != 0); - dso.trig_flag = (devc->mstatus.trig_flag != 0); - dso.data = cur_buf; - } else { - packet.type = SR_DF_DSO; - packet.status = SR_PKT_DATA_ERROR; - devc->mstatus_valid = FALSE; - } - } else if (sdi->mode == ANALOG) { - packet.type = SR_DF_ANALOG; - packet.payload = &analog; - analog.probes = sdi->channels; - cur_sample_count = transfer->actual_length / (((channel_modes[devc->ch_mode].unit_bits + 7) / 8) * g_slist_length(analog.probes)); - analog.num_samples = cur_sample_count; - analog.unit_bits = channel_modes[devc->ch_mode].unit_bits;; - analog.unit_pitch = devc->unit_pitch; - analog.mq = SR_MQ_VOLTAGE; - analog.unit = SR_UNIT_VOLT; - analog.mqflags = SR_MQFLAG_AC; - analog.data = cur_buf; - } - - if ((devc->limit_samples && devc->num_bytes < devc->actual_bytes) || - sdi->mode != LOGIC ) { - const uint64_t remain_length= devc->actual_bytes - devc->num_bytes; - logic.length = min(logic.length, remain_length); - - /* send data to session bus */ - if (!devc->overflow) { - if (packet.status == SR_PKT_OK) - sr_session_send(sdi, &packet); - } else { - packet.type = SR_DF_OVERFLOW; - packet.payload = NULL; - sr_session_send(sdi, &packet); - } - } - - devc->num_samples += cur_sample_count; - devc->num_bytes += logic.length; - if (sdi->mode == LOGIC && - devc->limit_samples && - devc->num_bytes >= devc->actual_bytes) { - devc->status = DSL_STOP; - } else if ((sdi->mode == DSO && devc->instant) && - devc->limit_samples && - devc->num_samples >= devc->actual_samples) { - devc->status = DSL_STOP; - } - } - - if (devc->status == DSL_DATA) - resubmit_transfer(transfer); - else - free_transfer(transfer); - - devc->trf_completed = 1; -} - -static void receive_trigger_pos(struct libusb_transfer *transfer) -{ - struct DSL_context *devc; - struct sr_datafeed_packet packet; - struct ds_trigger_pos *trigger_pos; - const struct sr_dev_inst *sdi; - uint64_t remain_cnt; - - packet.status = SR_PKT_OK; - devc = transfer->user_data; - sdi = devc->cb_data; - trigger_pos = (struct ds_trigger_pos *)transfer->buffer; - if (devc->status != DSL_ABORT) - devc->status = DSL_ERROR; - if (!devc->abort && transfer->status == LIBUSB_TRANSFER_COMPLETED && - trigger_pos->check_id == TRIG_CHECKID) { - sr_info("%" PRIu64 ": receive_trigger_pos(): status %d; timeout %d; received %d bytes.", - g_get_monotonic_time(), transfer->status, transfer->timeout, transfer->actual_length); - remain_cnt = trigger_pos->remain_cnt_h; - remain_cnt = (remain_cnt << 32) + trigger_pos->remain_cnt_l; - if (transfer->actual_length == sizeof(struct ds_trigger_pos)) { - if (sdi->mode != LOGIC || - devc->stream || - remain_cnt < devc->limit_samples) { - if (sdi->mode == LOGIC && (!devc->stream || (devc->status == DSL_ABORT))) { - devc->actual_samples = devc->limit_samples - remain_cnt; - devc->actual_bytes = devc->actual_samples / DSLOGIC_ATOMIC_SAMPLES * dsl_en_ch_num(sdi) * DSLOGIC_ATOMIC_SIZE; - devc->actual_samples = devc->actual_bytes / dsl_en_ch_num(sdi) * 8; - } - - packet.type = SR_DF_TRIGGER; - packet.payload = trigger_pos; - sr_session_send(sdi, &packet); - - devc->status = DSL_DATA; - } - } - } else if (!devc->abort) { - sr_err("%s: trigger packet data error.", __func__); - packet.type = SR_DF_TRIGGER; - packet.payload = trigger_pos; - packet.status = SR_PKT_DATA_ERROR; - sr_session_send(sdi, &packet); - } - - free_transfer(transfer); -} - -SR_PRIV int dsl_start_transfers(const struct sr_dev_inst *sdi) -{ - struct DSL_context *devc; - struct sr_usb_dev_inst *usb; - struct libusb_transfer *transfer; - unsigned int i, num_transfers; - int ret; - unsigned char *buf; - size_t size; - unsigned int dso_buffer_size; - struct ds_trigger_pos *trigger_pos; - - devc = sdi->priv; - usb = sdi->conn; - test_init = 1; - - if (devc->instant) - dso_buffer_size = min(instant_buffer_size * channel_modes[devc->ch_mode].num, - devc->profile->dev_caps.hw_depth / channel_modes[devc->ch_mode].unit_bits); - else - dso_buffer_size = devc->actual_samples * dsl_en_ch_num(sdi) + 512; - - num_transfers = (devc->stream) ? get_number_of_transfers(devc) : 1; - size = (sdi->mode == DSO) ? dso_buffer_size : - (devc->stream) ? get_buffer_size(devc) : instant_buffer_size; - - /* trigger packet transfer */ - if (!(trigger_pos = g_try_malloc0(sizeof(struct ds_trigger_pos)))) { - sr_err("%s: USB trigger_pos buffer malloc failed.", __func__); - return SR_ERR_MALLOC; - } - devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * (num_transfers + 1)); - if (!devc->transfers) { - sr_err("%s: USB transfer malloc failed.", __func__); - return SR_ERR_MALLOC; - } - transfer = libusb_alloc_transfer(0); - libusb_fill_bulk_transfer(transfer, usb->devhdl, - 6 | LIBUSB_ENDPOINT_IN, (unsigned char *)trigger_pos, sizeof(struct ds_trigger_pos), - (libusb_transfer_cb_fn)receive_trigger_pos, devc, 0); - if ((ret = libusb_submit_transfer(transfer)) != 0) { - sr_err("%s: Failed to submit trigger_pos transfer: %s.", - __func__, libusb_error_name(ret)); - libusb_free_transfer(transfer); - g_free(trigger_pos); - devc->status = DSL_ERROR; - return SR_ERR; - } else { - devc->num_transfers++; - devc->transfers[0] = transfer; - devc->submitted_transfers++; - } - - /* data packet transfer */ - for (i = 1; i <= num_transfers; i++) { - if (!(buf = g_try_malloc(size))) { - sr_err("%s: USB transfer buffer malloc failed.", __func__); - return SR_ERR_MALLOC; - } - transfer = libusb_alloc_transfer(0); - libusb_fill_bulk_transfer(transfer, usb->devhdl, - 6 | LIBUSB_ENDPOINT_IN, buf, size, - (libusb_transfer_cb_fn)receive_transfer, devc, 0); - if ((ret = libusb_submit_transfer(transfer)) != 0) { - sr_err("%s: Failed to submit transfer: %s.", - __func__, libusb_error_name(ret)); - libusb_free_transfer(transfer); - g_free(buf); - devc->status = DSL_ERROR; - devc->abort = TRUE; - return SR_ERR; - } - devc->transfers[i] = transfer; - devc->submitted_transfers++; - devc->num_transfers++; - } - - return SR_OK; -} +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2017 DreamSourceLab + * + * 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 . + */ + +#include "libsigrok.h" +#include "libsigrok-internal.h" +#include "command.h" +#include "dsl.h" + +#include +#include +#include + +extern struct ds_trigger *trigger; + +static const int32_t probeOptions[] = { + SR_CONF_PROBE_COUPLING, + SR_CONF_PROBE_VDIV, + SR_CONF_PROBE_MAP_DEFAULT, + SR_CONF_PROBE_MAP_UNIT, + SR_CONF_PROBE_MAP_MIN, + SR_CONF_PROBE_MAP_MAX, +}; + +static const int32_t probeSessions[] = { + SR_CONF_PROBE_COUPLING, + SR_CONF_PROBE_VDIV, + SR_CONF_PROBE_MAP_DEFAULT, + SR_CONF_PROBE_MAP_UNIT, + SR_CONF_PROBE_MAP_MIN, + SR_CONF_PROBE_MAP_MAX, +}; + +static const uint8_t probeCoupling[] = { + SR_DC_COUPLING, + SR_AC_COUPLING, +}; + +const char *probeMapUnits[] = { + "V", + "A", + "℃", + "℉", + "g", + "m", + "m/s", +}; + +static const char *probe_names[] = { + "0", "1", "2", "3", "4", "5", "6", "7", + "8", "9", "10", "11", "12", "13", "14", "15", + NULL, +}; + +static struct sr_dev_mode mode_list[] = { + {LOGIC, "Logic Analyzer", "逻辑分析仪", "la.png"}, + {ANALOG, "Data Acquisition", "数据记录仪", "daq.png"}, + {DSO, "Oscilloscope", "示波器", "osc.png"}, +}; + +SR_PRIV void dsl_probe_init(struct sr_dev_inst *sdi) +{ + unsigned int i, j; + GSList *l; + struct DSL_context *devc = sdi->priv; + + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + probe->bits = channel_modes[devc->ch_mode].unit_bits; + probe->vdiv = 1000; + probe->vfactor = 1; + probe->offset = (1 << (probe->bits - 1)); + probe->hw_offset = (1 << (probe->bits - 1)); + probe->coupling = SR_DC_COUPLING; + probe->trig_value = (1 << (probe->bits - 1)); + probe->vpos_trans = devc->profile->dev_caps.default_pwmtrans; + probe->comb_comp = devc->profile->dev_caps.default_comb_comp; + + probe->map_default = TRUE; + probe->map_unit = probeMapUnits[0]; + probe->map_min = -(probe->vdiv * probe->vfactor * DS_CONF_DSO_VDIVS / 2000.0); + probe->map_max = probe->vdiv * probe->vfactor * DS_CONF_DSO_VDIVS / 2000.0; + if (devc->profile->dev_caps.vdivs && probe->vga_ptr == NULL) { + for (i = 0; devc->profile->dev_caps.vdivs[i]; i++); + probe->vga_ptr = g_try_malloc((i+1)*sizeof(struct DSL_vga)); + + for (i = 0; devc->profile->dev_caps.vdivs[i]; i++) { + (probe->vga_ptr + i)->id = devc->profile->dev_caps.vga_id; + (probe->vga_ptr + i)->key = devc->profile->dev_caps.vdivs[i]; + for (j = 0; j < ARRAY_SIZE(vga_defaults); j++) { + if (vga_defaults[j].id == devc->profile->dev_caps.vga_id && + vga_defaults[j].key == devc->profile->dev_caps.vdivs[i]) { + (probe->vga_ptr+i)->vgain = vga_defaults[j].vgain; + (probe->vga_ptr+i)->preoff = vga_defaults[j].preoff; + (probe->vga_ptr + i)->preoff_comp = 0; + } + } + } + + // end flag must have + (probe->vga_ptr + i)->id = 0; + (probe->vga_ptr + i)->key = 0; + (probe->vga_ptr+i)->vgain = 0; + (probe->vga_ptr+i)->preoff = 0; + (probe->vga_ptr + i)->preoff_comp = 0; + } + } +} + +SR_PRIV int dsl_setup_probes(struct sr_dev_inst *sdi, int num_probes) +{ + uint16_t j; + struct sr_channel *probe; + struct DSL_context *devc = sdi->priv; + + for (j = 0; j < num_probes; j++) { + if (!(probe = sr_channel_new(j, channel_modes[devc->ch_mode].type, + TRUE, probe_names[j]))) + return SR_ERR; + sdi->channels = g_slist_append(sdi->channels, probe); + } + dsl_probe_init(sdi); + return SR_OK; +} + +SR_PRIV int dsl_adjust_probes(struct sr_dev_inst *sdi, int num_probes) +{ + uint16_t j; + struct sr_channel *probe; + struct DSL_context *devc = sdi->priv; + GSList *l; + + assert(num_probes > 0); + + j = g_slist_length(sdi->channels); + while(j < num_probes) { + if (!(probe = sr_channel_new(j, channel_modes[devc->ch_mode].type, + TRUE, probe_names[j]))) + return SR_ERR; + sdi->channels = g_slist_append(sdi->channels, probe); + j++; + } + + while(j > num_probes) { + sdi->channels = g_slist_delete_link(sdi->channels, g_slist_last(sdi->channels)); + j--; + } + + for(l = sdi->channels; l; l = l->next) { + probe = (struct sr_channel *)l->data; + probe->enabled = TRUE; + probe->type = channel_modes[devc->ch_mode].type; + } + return SR_OK; +} + +SR_PRIV const GSList *dsl_mode_list(const struct sr_dev_inst *sdi) +{ + struct DSL_context *devc; + GSList *l = NULL; + unsigned int i; + + devc = sdi->priv; + for (i = 0; i < ARRAY_SIZE(mode_list); i++) { + if (devc->profile->dev_caps.mode_caps & (1 << i)) + l = g_slist_append(l, &mode_list[i]); + } + + return l; +} + +SR_PRIV void dsl_adjust_samplerate(struct DSL_context *devc) +{ + int i; + for (i = 0; devc->profile->dev_caps.samplerates[i]; i++) { + if (devc->profile->dev_caps.samplerates[i] > + channel_modes[devc->ch_mode].max_samplerate) + break; + } + devc->samplerates_max_index = i-1; + + for (i = 0; devc->profile->dev_caps.samplerates[i]; i++) { + if (devc->profile->dev_caps.samplerates[i] >= + channel_modes[devc->ch_mode].min_samplerate) + break; + } + devc->samplerates_min_index = i; + + assert(devc->samplerates_max_index >= devc->samplerates_min_index); + + if (devc->cur_samplerate > devc->profile->dev_caps.samplerates[devc->samplerates_max_index]) + devc->cur_samplerate = devc->profile->dev_caps.samplerates[devc->samplerates_max_index]; + + if (devc->cur_samplerate < devc->profile->dev_caps.samplerates[devc->samplerates_min_index]) + devc->cur_samplerate = devc->profile->dev_caps.samplerates[devc->samplerates_min_index]; +} + +SR_PRIV int dsl_en_ch_num(const struct sr_dev_inst *sdi) +{ + GSList *l; + int channel_en_cnt = 0; + + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + channel_en_cnt += probe->enabled; + } + channel_en_cnt += (channel_en_cnt == 0); + + return channel_en_cnt; +} + +/** + * Check the USB configuration to determine if this is an dsl device. + * + * @return TRUE if the device's configuration profile match dsl hardware + * configuration, FALSE otherwise. + */ +SR_PRIV gboolean dsl_check_conf_profile(libusb_device *dev) +{ + struct libusb_device_descriptor des; + struct libusb_device_handle *hdl; + gboolean ret; + unsigned char strdesc[64]; + + hdl = NULL; + ret = FALSE; + while (!ret) { + /* Assume the FW has not been loaded, unless proven wrong. */ + if (libusb_get_device_descriptor(dev, &des) != 0) + break; + + if (libusb_open(dev, &hdl) != 0) + break; + + if (libusb_get_string_descriptor_ascii(hdl, + des.iManufacturer, strdesc, sizeof(strdesc)) < 0) + break; + if (strncmp((const char *)strdesc, "DreamSourceLab", 14)) + break; + + if (libusb_get_string_descriptor_ascii(hdl, + des.iProduct, strdesc, sizeof(strdesc)) < 0) + break; + if (strncmp((const char *)strdesc, "USB-based DSL Instrument v2", 27)) + break; + + /* If we made it here, it must be an dsl device. */ + ret = TRUE; + } + if (hdl) + libusb_close(hdl); + + return ret; +} + +static int hw_dev_open(struct sr_dev_driver *di, struct sr_dev_inst *sdi) +{ + libusb_device **devlist; + struct sr_usb_dev_inst *usb; + struct libusb_device_descriptor des; + struct DSL_context *devc; + struct drv_context *drvc; + struct version_info vi; + int ret, skip, i, device_count; + struct ctl_rd_cmd rd_cmd; + uint8_t rd_cmd_data[2]; + + drvc = di->priv; + devc = sdi->priv; + usb = sdi->conn; + + if (sdi->status == SR_ST_ACTIVE) { + /* Device is already in use. */ + return SR_ERR; + } + + skip = 0; + device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); + if (device_count < 0) { + sr_err("Failed to get device list: %s.", + libusb_error_name(device_count)); + return SR_ERR; + } + + for (i = 0; i < device_count; i++) { + if ((ret = libusb_get_device_descriptor(devlist[i], &des))) { + sr_err("Failed to get device descriptor: %s.", + libusb_error_name(ret)); + continue; + } + + if (des.idVendor != devc->profile->vid + || des.idProduct != devc->profile->pid) + continue; + + if (sdi->status == SR_ST_INITIALIZING) { + if (skip != sdi->index) { + /* Skip devices of this type that aren't the one we want. */ + skip += 1; + continue; + } + } else if (sdi->status == SR_ST_INACTIVE) { + /* + * This device is fully enumerated, so we need to find + * this device by vendor, product, bus and address. + */ + if (libusb_get_bus_number(devlist[i]) != usb->bus + || libusb_get_device_address(devlist[i]) != usb->address) + /* This is not the one. */ + continue; + } + + if (!(ret = libusb_open(devlist[i], &usb->devhdl))) { + if (usb->address == 0xff) + /* + * First time we touch this device after FW + * upload, so we don't know the address yet. + */ + usb->address = libusb_get_device_address(devlist[i]); + } else { + sr_err("Failed to open device: %s.", + libusb_error_name(ret)); + break; + } + + rd_cmd.header.dest = DSL_CTL_FW_VERSION; + rd_cmd.header.size = 2; + rd_cmd.data = rd_cmd_data; + if ((ret = command_ctl_rd(usb->devhdl, rd_cmd)) != SR_OK) { + sr_err("Failed to get firmware version."); + break; + } + vi.major = rd_cmd_data[0]; + vi.minor = rd_cmd_data[1]; + + /* + * Different versions may have incompatible issue, + * Mark for up level process + */ + if (vi.major != DSL_REQUIRED_VERSION_MAJOR) { + sr_err("Expected firmware version %d.%d, " + "got %d.%d.", DSL_REQUIRED_VERSION_MAJOR, DSL_REQUIRED_VERSION_MINOR, + vi.major, vi.minor); + sdi->status = SR_ST_INCOMPATIBLE; + } else { + sdi->status = SR_ST_ACTIVE; + } + + sr_info("Opened device %d on %d.%d, " + "interface %d, firmware %d.%d.", + sdi->index, usb->bus, usb->address, + USB_INTERFACE, vi.major, vi.minor); + + break; + } + libusb_free_device_list(devlist, 1); + + if ((sdi->status != SR_ST_ACTIVE) && + (sdi->status != SR_ST_INCOMPATIBLE)) + return SR_ERR; + + return SR_OK; +} + +SR_PRIV int dsl_configure_probes(const struct sr_dev_inst *sdi) +{ + struct DSL_context *devc; + struct sr_channel *probe; + GSList *l; + int probe_bit, stage, i; + char *tc; + + devc = sdi->priv; + for (i = 0; i < NUM_TRIGGER_STAGES; i++) { + devc->trigger_mask[i] = 0; + devc->trigger_value[i] = 0; + } + + stage = -1; + for (l = sdi->channels; l; l = l->next) { + probe = (struct sr_channel *)l->data; + if (probe->enabled == FALSE) + continue; + + probe_bit = 1 << (probe->index); + if (!(probe->trigger)) + continue; + + stage = 0; + for (tc = probe->trigger; *tc; tc++) { + devc->trigger_mask[stage] |= probe_bit; + if (*tc == '1') + devc->trigger_value[stage] |= probe_bit; + stage++; + if (stage > NUM_TRIGGER_STAGES) + return SR_ERR; + } + } + + return SR_OK; +} + +SR_PRIV uint64_t dsl_channel_depth(const struct sr_dev_inst *sdi) +{ + struct DSL_context *devc = sdi->priv; + int ch_num = dsl_en_ch_num(sdi); + return devc->profile->dev_caps.hw_depth / (ch_num ? ch_num : 1); +} + +SR_PRIV int dsl_wr_reg(const struct sr_dev_inst *sdi, uint8_t addr, uint8_t value) +{ + struct sr_usb_dev_inst *usb; + struct libusb_device_handle *hdl; + struct ctl_wr_cmd wr_cmd; + int ret; + + usb = sdi->conn; + hdl = usb->devhdl; + + wr_cmd.header.dest = DSL_CTL_I2C_REG; + wr_cmd.header.offset = addr; + wr_cmd.header.size = 1; + wr_cmd.data[0] = value; + if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) { + sr_err("Sent DSL_CTL_I2C_REG command failed."); + return SR_ERR; + } + + return SR_OK; +} + +SR_PRIV int dsl_rd_reg(const struct sr_dev_inst *sdi, uint8_t addr, uint8_t *value) +{ + struct sr_usb_dev_inst *usb; + struct ctl_rd_cmd rd_cmd; + int ret; + + usb = sdi->conn; + + rd_cmd.header.dest = DSL_CTL_I2C_STATUS; + rd_cmd.header.offset = addr; + rd_cmd.header.size = 1; + rd_cmd.data = value; + if ((ret = command_ctl_rd(usb->devhdl, rd_cmd)) != SR_OK) { + sr_err("Sent DSL_CTL_I2C_STATUS read command failed."); + return SR_ERR; + } + + return SR_OK; +} + +SR_PRIV int dsl_wr_ext(const struct sr_dev_inst *sdi, uint8_t addr, uint8_t value) +{ + struct sr_usb_dev_inst *usb; + struct libusb_device_handle *hdl; + struct ctl_wr_cmd wr_cmd; + struct DSL_context *devc = sdi->priv; + uint8_t rdata; + int ret; + + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_POGOPIN) { + usb = sdi->conn; + hdl = usb->devhdl; + + wr_cmd.header.dest = DSL_CTL_I2C_EXT; + wr_cmd.header.offset = addr; + wr_cmd.header.size = 1; + wr_cmd.data[0] = value; + if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) { + sr_err("Sent DSL_CTL_I2C_EXT command failed."); + return SR_ERR; + } + } else { + // write addr + wr + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_TXR_OFF, EI2C_AWR); + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STA | bmEI2C_WR); + // check done + ret = dsl_rd_reg(sdi, EI2C_ADDR+EI2C_SR_OFF, &rdata); + if (rdata & bmEI2C_RXNACK) { + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STO | bmEI2C_WR); + return SR_ERR; + } + + // write offset + wr + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_TXR_OFF, addr); + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_WR); + // check done + ret = dsl_rd_reg(sdi, EI2C_ADDR+EI2C_SR_OFF, &rdata); + if (rdata & bmEI2C_RXNACK) { + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STO | bmEI2C_WR); + return SR_ERR; + } + + // write value + wr + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_TXR_OFF, value); + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STO | bmEI2C_WR); + // check done + ret = dsl_rd_reg(sdi, EI2C_ADDR+EI2C_SR_OFF, &rdata); + if (rdata & bmEI2C_RXNACK) { + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STO | bmEI2C_WR); + return SR_ERR; + } + } + + return ret; +} + +SR_PRIV int dsl_rd_ext(const struct sr_dev_inst *sdi, unsigned char *ctx, uint16_t addr, uint8_t len) +{ + struct sr_usb_dev_inst *usb; + struct libusb_device_handle *hdl; + struct ctl_rd_cmd rd_cmd; + struct DSL_context *devc = sdi->priv; + uint8_t rdata; + int ret; + + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_POGOPIN) { + usb = sdi->conn; + hdl = usb->devhdl; + + rd_cmd.header.dest = DSL_CTL_I2C_EXT; + rd_cmd.header.size = len; + rd_cmd.header.offset = addr; + rd_cmd.data = ctx; + if ((ret = command_ctl_rd(hdl, rd_cmd)) != SR_OK) { + sr_err("Sent DSL_CTL_I2C_EXT read command failed."); + return SR_ERR; + } + } else { + // write addr + wr + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_TXR_OFF, EI2C_AWR); + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STA | bmEI2C_WR); + // check done + ret = dsl_rd_reg(sdi, EI2C_ADDR+EI2C_SR_OFF, &rdata); + if (rdata & bmEI2C_RXNACK) { + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STO | bmEI2C_WR); + return SR_ERR; + } + + // write offset + wr + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_TXR_OFF, addr); + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_WR); + // check done + ret = dsl_rd_reg(sdi, EI2C_ADDR+EI2C_SR_OFF, &rdata); + if (rdata & bmEI2C_RXNACK) { + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STO | bmEI2C_WR); + return SR_ERR; + } + + // write read addr + wr + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_TXR_OFF, EI2C_ARD); + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STA | bmEI2C_WR); + // check done + ret = dsl_rd_reg(sdi, EI2C_ADDR+EI2C_SR_OFF, &rdata); + if (rdata & bmEI2C_RXNACK) { + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STO | bmEI2C_WR); + return SR_ERR; + } + + while(--len) { + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_RD); + ret = dsl_rd_reg(sdi, EI2C_ADDR+EI2C_RXR_OFF, ctx); + ctx++; + } + ret = dsl_wr_reg(sdi, EI2C_ADDR+EI2C_CR_OFF, bmEI2C_STO | bmEI2C_RD | bmEI2C_NACK); + ret = dsl_rd_reg(sdi, EI2C_ADDR+EI2C_RXR_OFF, ctx); + } + + return ret; +} + +SR_PRIV int dsl_wr_dso(const struct sr_dev_inst *sdi, uint64_t cmd) +{ + struct sr_usb_dev_inst *usb; + struct libusb_device_handle *hdl; + struct ctl_wr_cmd wr_cmd; + int ret; + + usb = sdi->conn; + hdl = usb->devhdl; + + wr_cmd.header.dest = DSL_CTL_I2C_DSO; + wr_cmd.header.offset = 0; + wr_cmd.header.size = 8; + wr_cmd.data[0] = (uint8_t)cmd; + wr_cmd.data[1] = (uint8_t)(cmd >> 8); + wr_cmd.data[2] = (uint8_t)(cmd >> 16); + wr_cmd.data[3] = (uint8_t)(cmd >> 24); + wr_cmd.data[4] = (uint8_t)(cmd >> 32); + wr_cmd.data[5] = (uint8_t)(cmd >> 40); + wr_cmd.data[6] = (uint8_t)(cmd >> 48); + wr_cmd.data[7] = (uint8_t)(cmd >> 56); + if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) { + sr_err("Sent DSL_CTL_I2C_DSO command failed."); + return SR_ERR; + } + + return SR_OK; +} + +SR_PRIV int dsl_wr_nvm(const struct sr_dev_inst *sdi, unsigned char *ctx, uint16_t addr, uint8_t len) +{ + struct sr_usb_dev_inst *usb; + struct libusb_device_handle *hdl; + struct ctl_wr_cmd wr_cmd; + int ret; + int i; + + usb = sdi->conn; + hdl = usb->devhdl; + + wr_cmd.header.dest = DSL_CTL_NVM; + wr_cmd.header.offset = addr; + wr_cmd.header.size = len; + for (i = 0; i < len; i++) + wr_cmd.data[i] = *(ctx+i); + if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) { + sr_err("Sent DSL_CTL_NVM write command failed."); + return SR_ERR; + } + + return SR_OK; +} + +SR_PRIV int dsl_rd_nvm(const struct sr_dev_inst *sdi, unsigned char *ctx, uint16_t addr, uint8_t len) +{ + struct sr_usb_dev_inst *usb; + struct libusb_device_handle *hdl; + struct ctl_rd_cmd rd_cmd; + int ret; + + usb = sdi->conn; + hdl = usb->devhdl; + + rd_cmd.header.dest = DSL_CTL_NVM; + rd_cmd.header.size = len; + rd_cmd.header.offset = addr; + rd_cmd.data = ctx; + if ((ret = command_ctl_rd(hdl, rd_cmd)) != SR_OK) { + sr_err("Sent DSL_CTL_NVM read command failed."); + return SR_ERR; + } + + return SR_OK; +} + +SR_PRIV int dsl_config_adc(const struct sr_dev_inst *sdi, const struct DSL_adc_config *config) +{ + while(config->dest) { + assert((config->cnt > 0) && (config->cnt <= 4)); + if (config->delay >0) + g_usleep(config->delay*1000); + for (int i = 0; i < config->cnt; i++) { + dsl_wr_reg(sdi, config->dest, config->byte[i]); + } + config++; + } + return SR_OK; +} + +SR_PRIV int dsl_rd_probe(const struct sr_dev_inst *sdi, unsigned char *ctx, uint16_t addr, uint8_t len) +{ + struct sr_usb_dev_inst *usb; + struct libusb_device_handle *hdl; + struct ctl_rd_cmd rd_cmd; + int ret; + + usb = sdi->conn; + hdl = usb->devhdl; + + rd_cmd.header.dest = DSL_CTL_I2C_PROBE; + rd_cmd.header.size = len; + rd_cmd.header.offset = addr; + rd_cmd.data = ctx; + if ((ret = command_ctl_rd(hdl, rd_cmd)) != SR_OK) { + sr_err("Sent DSL_CTL_NVM read command failed."); + return SR_ERR; + } + + return SR_OK; +} + +SR_PRIV int dsl_fpga_arm(const struct sr_dev_inst *sdi) +{ + struct DSL_context *devc; + struct sr_usb_dev_inst *usb; + struct libusb_device_handle *hdl; + struct DSL_setting setting; + int ret; + int transferred; + int i; + GSList *l; + uint32_t tmp_u32; + uint64_t tmp_u64; + const int ch_num = dsl_en_ch_num(sdi); + uint32_t arm_size; + struct ctl_wr_cmd wr_cmd; + struct ctl_rd_cmd rd_cmd; + uint8_t rd_cmd_data; + + devc = sdi->priv; + usb = sdi->conn; + hdl = usb->devhdl; + + setting.sync = 0xf5a5f5a5; + setting.mode_header = 0x0001; + setting.divider_header = 0x0102; + setting.count_header = 0x0302; + setting.trig_pos_header = 0x0502; + setting.trig_glb_header = 0x0701; + setting.ch_en_header = 0x0801; + setting.dso_count_header = 0x0902; + setting.trig_header = 0x40a0; + setting.end_sync = 0xfa5afa5a; + setting.misc_align = 0xffff; + + // basic configuration + setting.mode = (trigger->trigger_en << TRIG_EN_BIT) + + (devc->clock_type << CLK_TYPE_BIT) + + (devc->clock_edge << CLK_EDGE_BIT) + + (devc->rle_mode << RLE_MODE_BIT) + + ((sdi->mode == DSO) << DSO_MODE_BIT) + + (((devc->cur_samplerate == (2 * channel_modes[devc->ch_mode].hw_max_samplerate)) && sdi->mode != DSO) << HALF_MODE_BIT) + + ((devc->cur_samplerate == (4 * channel_modes[devc->ch_mode].hw_max_samplerate)) << QUAR_MODE_BIT) + + ((sdi->mode == ANALOG) << ANALOG_MODE_BIT) + + ((devc->filter == SR_FILTER_1T) << FILTER_BIT) + + (devc->instant << INSTANT_BIT) + + ((trigger->trigger_mode == SERIAL_TRIGGER) << STRIG_MODE_BIT) + + ((devc->stream) << STREAM_MODE_BIT) + + ((devc->test_mode == SR_TEST_LOOPBACK) << LPB_TEST_BIT) + + ((devc->test_mode == SR_TEST_EXTERNAL) << EXT_TEST_BIT) + + ((devc->test_mode == SR_TEST_INTERNAL) << INT_TEST_BIT); + + // sample rate divider + tmp_u32 = (sdi->mode == DSO) ? (uint32_t)ceil(channel_modes[devc->ch_mode].max_samplerate * 1.0 / devc->cur_samplerate / ch_num) : + (sdi->mode == ANALOG) ? (uint32_t)ceil(channel_modes[devc->ch_mode].hw_max_samplerate * 1.0 / max(devc->cur_samplerate, channel_modes[devc->ch_mode].hw_min_samplerate)) : + (uint32_t)ceil(channel_modes[devc->ch_mode].hw_max_samplerate * 1.0 / devc->cur_samplerate); + devc->unit_pitch = ceil(channel_modes[devc->ch_mode].hw_min_samplerate * 1.0 / devc->cur_samplerate); + setting.div_h = ((tmp_u32 >= channel_modes[devc->ch_mode].pre_div) ? channel_modes[devc->ch_mode].pre_div - 1U : tmp_u32 - 1U) << 8; + tmp_u32 = (uint32_t)ceil(tmp_u32 * 1.0 / channel_modes[devc->ch_mode].pre_div); + setting.div_l = tmp_u32 & 0x0000ffff; + setting.div_h += tmp_u32 >> 16; + + // capture counter + tmp_u64 = (sdi->mode == DSO) ? (devc->actual_samples / (channel_modes[devc->ch_mode].num / ch_num)) : + (devc->actual_samples); + tmp_u64 >>= 4; // hardware minimum unit 64 + setting.cnt_l = tmp_u64 & 0x0000ffff; + setting.cnt_h = tmp_u64 >> 16; + tmp_u64 = (sdi->mode == DSO) ? (devc->limit_samples / (channel_modes[devc->ch_mode].num / ch_num)) : + (devc->actual_samples); + setting.dso_cnt_l = tmp_u64 & 0x0000ffff; + setting.dso_cnt_h = tmp_u64 >> 16; + + // trigger position + // must be align to minimum parallel bits + tmp_u32 = max((uint32_t)(trigger->trigger_pos / 100.0 * devc->limit_samples), DSLOGIC_ATOMIC_SAMPLES); + if (devc->stream) + tmp_u32 = min(tmp_u32, dsl_channel_depth(sdi) * 10 / 100); + else + tmp_u32 = min(tmp_u32, dsl_channel_depth(sdi) * DS_MAX_TRIG_PERCENT / 100); + setting.tpos_l = tmp_u32 & DSLOGIC_ATOMIC_MASK; + setting.tpos_h = tmp_u32 >> 16; + + // trigger global settings + setting.trig_glb = ((ch_num & 0xf) << 4) + + trigger->trigger_stages; + + // channel enable mapping + setting.ch_en = 0; + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + setting.ch_en += probe->enabled << probe->index; + } + + // trigger advanced configuration + if (trigger->trigger_mode == SIMPLE_TRIGGER) { + setting.trig_mask0[0] = ds_trigger_get_mask0(TriggerStages); + setting.trig_mask1[0] = ds_trigger_get_mask1(TriggerStages); + + setting.trig_value0[0] = ds_trigger_get_value0(TriggerStages); + setting.trig_value1[0] = ds_trigger_get_value1(TriggerStages); + + setting.trig_edge0[0] = ds_trigger_get_edge0(TriggerStages); + setting.trig_edge1[0] = ds_trigger_get_edge1(TriggerStages); + + if (setting.mode & (1 << QUAR_MODE_BIT)) { + setting.trig_mask0[0] = ((setting.trig_mask0[0] & 0x0f) << 12) + + ((setting.trig_mask0[0] & 0x0f) << 8) + + ((setting.trig_mask0[0] & 0x0f) << 4) + + ((setting.trig_mask0[0] & 0x0f) << 0); + setting.trig_mask1[0] = ((setting.trig_mask1[0] & 0x0f) << 12) + + ((setting.trig_mask1[0] & 0x0f) << 8) + + ((setting.trig_mask1[0] & 0x0f) << 4) + + ((setting.trig_mask1[0] & 0x0f) << 0); + setting.trig_value0[0] = ((setting.trig_value0[0] & 0x0f) << 12) + + ((setting.trig_value0[0] & 0x0f) << 8) + + ((setting.trig_value0[0] & 0x0f) << 4) + + ((setting.trig_value0[0] & 0x0f) << 0); + setting.trig_value1[0] = ((setting.trig_value1[0] & 0x0f) << 12) + + ((setting.trig_value1[0] & 0x0f) << 8) + + ((setting.trig_value1[0] & 0x0f) << 4) + + ((setting.trig_value1[0] & 0x0f) << 0); + setting.trig_edge0[0] = ((setting.trig_edge0[0] & 0x0f) << 12) + + ((setting.trig_edge0[0] & 0x0f) << 8) + + ((setting.trig_edge0[0] & 0x0f) << 4) + + ((setting.trig_edge0[0] & 0x0f) << 0); + setting.trig_edge1[0] = ((setting.trig_edge1[0] & 0x0f) << 12) + + ((setting.trig_edge1[0] & 0x0f) << 8) + + ((setting.trig_edge1[0] & 0x0f) << 4) + + ((setting.trig_edge1[0] & 0x0f) << 0); + } else if (setting.mode & (1 << HALF_MODE_BIT)) { + setting.trig_mask0[0] = ((setting.trig_mask0[0] & 0xff) << 8) + + ((setting.trig_mask0[0] & 0xff) << 0); + setting.trig_mask1[0] = ((setting.trig_mask1[0] & 0xff) << 8) + + ((setting.trig_mask1[0] & 0xff) << 0); + setting.trig_value0[0] = ((setting.trig_value0[0] & 0xff) << 8) + + ((setting.trig_value0[0] & 0xff) << 0); + setting.trig_value1[0] = ((setting.trig_value1[0] & 0xff) << 8) + + ((setting.trig_value1[0] & 0xff) << 0); + setting.trig_edge0[0] = ((setting.trig_edge0[0] & 0xff) << 8) + + ((setting.trig_edge0[0] & 0xff) << 0); + setting.trig_edge1[0] = ((setting.trig_edge1[0] & 0xff) << 8) + + ((setting.trig_edge1[0] & 0xff) << 0); + } + + setting.trig_logic0[0] = (trigger->trigger_logic[TriggerStages] << 1) + trigger->trigger0_inv[TriggerStages]; + setting.trig_logic1[0] = (trigger->trigger_logic[TriggerStages] << 1) + trigger->trigger1_inv[TriggerStages]; + + setting.trig_count[0] = trigger->trigger0_count[TriggerStages]; + + for (i = 1; i < NUM_TRIGGER_STAGES; i++) { + setting.trig_mask0[i] = 0xffff; + setting.trig_mask1[i] = 0xffff; + + setting.trig_value0[i] = 0; + setting.trig_value1[i] = 0; + + setting.trig_edge0[i] = 0; + setting.trig_edge1[i] = 0; + + setting.trig_logic0[i] = 2; + setting.trig_logic1[i] = 2; + + setting.trig_count[i] = 0; + } + } else { + for (i = 0; i < NUM_TRIGGER_STAGES; i++) { + setting.trig_mask0[i] = ds_trigger_get_mask0(i); + setting.trig_mask1[i] = ds_trigger_get_mask1(i); + + setting.trig_value0[i] = ds_trigger_get_value0(i); + setting.trig_value1[i] = ds_trigger_get_value1(i); + + setting.trig_edge0[i] = ds_trigger_get_edge0(i); + setting.trig_edge1[i] = ds_trigger_get_edge1(i); + + if (setting.mode & (1 << STRIG_MODE_BIT) && i == STriggerDataStage) { + // serial trigger, data mask/value should not be duplicated + } else { + if (setting.mode & (1 << QUAR_MODE_BIT)) { + setting.trig_mask0[i] = ((setting.trig_mask0[i] & 0x0f) << 12) + + ((setting.trig_mask0[i] & 0x0f) << 8) + + ((setting.trig_mask0[i] & 0x0f) << 4) + + ((setting.trig_mask0[i] & 0x0f) << 0); + setting.trig_mask1[i] = ((setting.trig_mask1[i] & 0x0f) << 12) + + ((setting.trig_mask1[i] & 0x0f) << 8) + + ((setting.trig_mask1[i] & 0x0f) << 4) + + ((setting.trig_mask1[i] & 0x0f) << 0); + setting.trig_value0[i] = ((setting.trig_value0[i] & 0x0f) << 12) + + ((setting.trig_value0[i] & 0x0f) << 8) + + ((setting.trig_value0[i] & 0x0f) << 4) + + ((setting.trig_value0[i] & 0x0f) << 0); + setting.trig_value1[i] = ((setting.trig_value1[i] & 0x0f) << 12) + + ((setting.trig_value1[i] & 0x0f) << 8) + + ((setting.trig_value1[i] & 0x0f) << 4) + + ((setting.trig_value1[i] & 0x0f) << 0); + setting.trig_edge0[i] = ((setting.trig_edge0[i] & 0x0f) << 12) + + ((setting.trig_edge0[i] & 0x0f) << 8) + + ((setting.trig_edge0[i] & 0x0f) << 4) + + ((setting.trig_edge0[i] & 0x0f) << 0); + setting.trig_edge1[i] = ((setting.trig_edge1[i] & 0x0f) << 12) + + ((setting.trig_edge1[i] & 0x0f) << 8) + + ((setting.trig_edge1[i] & 0x0f) << 4) + + ((setting.trig_edge1[i] & 0x0f) << 0); + } else if (setting.mode & (1 << HALF_MODE_BIT)) { + setting.trig_mask0[i] = ((setting.trig_mask0[i] & 0xff) << 8) + + ((setting.trig_mask0[i] & 0xff) << 0); + setting.trig_mask1[i] = ((setting.trig_mask1[i] & 0xff) << 8) + + ((setting.trig_mask1[i] & 0xff) << 0); + setting.trig_value0[i] = ((setting.trig_value0[i] & 0xff) << 8) + + ((setting.trig_value0[i] & 0xff) << 0); + setting.trig_value1[i] = ((setting.trig_value1[i] & 0xff) << 8) + + ((setting.trig_value1[i] & 0xff) << 0); + setting.trig_edge0[i] = ((setting.trig_edge0[i] & 0xff) << 8) + + ((setting.trig_edge0[i] & 0xff) << 0); + setting.trig_edge1[i] = ((setting.trig_edge1[i] & 0xff) << 8) + + ((setting.trig_edge1[i] & 0xff) << 0); + } + } + + setting.trig_logic0[i] = (trigger->trigger_logic[i] << 1) + trigger->trigger0_inv[i]; + setting.trig_logic1[i] = (trigger->trigger_logic[i] << 1) + trigger->trigger1_inv[i]; + + setting.trig_count[i] = trigger->trigger0_count[i]; + } + } + + if (!(devc->profile->dev_caps.feature_caps & CAPS_FEATURE_USB30)) { + // set GPIF to be wordwide + wr_cmd.header.dest = DSL_CTL_WORDWIDE; + wr_cmd.header.size = 1; + wr_cmd.data[0] = bmWR_WORDWIDE; + if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) { + sr_err("Sent DSL_CTL_WORDWIDE command failed."); + return SR_ERR; + } + } + + // send bulk write control command + arm_size = sizeof(struct DSL_setting) / sizeof(uint16_t); + wr_cmd.header.dest = DSL_CTL_BULK_WR; + wr_cmd.header.size = 3; + wr_cmd.data[0] = (uint8_t)arm_size; + wr_cmd.data[1] = (uint8_t)(arm_size >> 8); + wr_cmd.data[2] = (uint8_t)(arm_size >> 16); + if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) { + sr_err("Sent bulk write command of arm FPGA failed."); + return SR_ERR; + } + // check sys_clr dessert + rd_cmd.header.dest = DSL_CTL_HW_STATUS; + rd_cmd.header.size = 1; + rd_cmd_data = 0; + rd_cmd.data = &rd_cmd_data; + while(1) { + if ((ret = command_ctl_rd(hdl, rd_cmd)) != SR_OK) + return SR_ERR; + if (rd_cmd_data & bmSYS_CLR) + break; + } + + // send bulk data + ret = libusb_bulk_transfer(hdl, 2 | LIBUSB_ENDPOINT_OUT, + (unsigned char *)&setting, + sizeof(struct DSL_setting), + &transferred, 1000); + if (ret < 0) { + sr_err("Unable to arm FPGA of dsl device: %s.", + libusb_error_name(ret)); + return SR_ERR; + } else if (transferred != sizeof(struct DSL_setting)) { + sr_err("Arm FPGA error: expacted transfer size %d; actually %d", + sizeof(struct DSL_setting), transferred); + return SR_ERR; + } + + // assert INTRDY high (indicate data end) + wr_cmd.header.dest = DSL_CTL_INTRDY; + wr_cmd.header.size = 1; + wr_cmd.data[0] = bmWR_INTRDY; + if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) + return SR_ERR; + + + // check FPGA_DONE bit + rd_cmd.header.dest = DSL_CTL_HW_STATUS; + rd_cmd.header.size = 1; + rd_cmd_data = 0; + rd_cmd.data = &rd_cmd_data; + if ((ret = command_ctl_rd(hdl, rd_cmd)) != SR_OK) + return SR_ERR; + if (rd_cmd_data & bmGPIF_DONE) { + sr_info("Arm FPGA done"); + return SR_OK; + } else { + return SR_ERR; + } +} + +SR_PRIV int dsl_fpga_config(struct libusb_device_handle *hdl, const char *filename) +{ + FILE *fw; + int chunksize, ret; + unsigned char *buf; + int transferred; + uint64_t filesize; + struct ctl_wr_cmd wr_cmd; + struct ctl_rd_cmd rd_cmd; + uint8_t rd_cmd_data; + struct stat f_stat; + + sr_info("Configure FPGA using %s", filename); + if ((fw = fopen(filename, "rb")) == NULL) { + sr_err("Unable to open FPGA bit file %s for reading: %s", + filename, strerror(errno)); + return SR_ERR; + } + + if (stat(filename, &f_stat) == -1) + return SR_ERR; + + filesize = (uint64_t)f_stat.st_size; + + if (!(buf = g_try_malloc(filesize))) { + sr_err("FPGA configure buf malloc failed."); + return SR_ERR; + } + + // step0: assert PROG_B low + wr_cmd.header.dest = DSL_CTL_PROG_B; + wr_cmd.header.size = 1; + wr_cmd.data[0] = ~bmWR_PROG_B; + if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) + return SR_ERR; + + // step1: turn off GREEN/RED led + wr_cmd.header.dest = DSL_CTL_LED; + wr_cmd.header.size = 1; + wr_cmd.data[0] = ~bmLED_GREEN & ~bmLED_RED; + if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) + return SR_ERR; + + // step2: assert PORG_B high + wr_cmd.header.dest = DSL_CTL_PROG_B; + wr_cmd.header.size = 1; + wr_cmd.data[0] = bmWR_PROG_B; + if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) + return SR_ERR; + + // step3: wait INIT_B go high + rd_cmd.header.dest = DSL_CTL_HW_STATUS; + rd_cmd.header.size = 1; + rd_cmd_data = 0; + rd_cmd.data = &rd_cmd_data; + while(1) { + if ((ret = command_ctl_rd(hdl, rd_cmd)) != SR_OK) + return SR_ERR; + if (rd_cmd_data & bmFPGA_INIT_B) + break; + } + + // step4: send config ctl command + wr_cmd.header.dest = DSL_CTL_INTRDY; + wr_cmd.header.size = 1; + wr_cmd.data[0] = (uint8_t)~bmWR_INTRDY; + if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) + return SR_ERR; + + wr_cmd.header.dest = DSL_CTL_BULK_WR; + wr_cmd.header.size = 3; + wr_cmd.data[0] = (uint8_t)filesize; + wr_cmd.data[1] = (uint8_t)(filesize >> 8); + wr_cmd.data[2] = (uint8_t)(filesize >> 16); + if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) { + sr_err("Configure FPGA error: send command fpga_config failed."); + return SR_ERR; + } + + // step5: send config data + chunksize = fread(buf, 1, filesize, fw); + if (chunksize == 0) + return SR_ERR; + + ret = libusb_bulk_transfer(hdl, 2 | LIBUSB_ENDPOINT_OUT, + buf, chunksize, + &transferred, 1000); + fclose(fw); + g_free(buf); + + if (ret < 0) { + sr_err("Unable to configure FPGA of dsl device: %s.", + libusb_error_name(ret)); + return SR_ERR; + } else if (transferred != chunksize) { + sr_err("Configure FPGA error: expacted transfer size %d; actually %d.", + chunksize, transferred); + return SR_ERR; + } + + // step6: assert INTRDY high (indicate data end) + wr_cmd.header.dest = DSL_CTL_INTRDY; + wr_cmd.header.size = 1; + wr_cmd.data[0] = bmWR_INTRDY; + if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) + return SR_ERR; + + // step7: check GPIF_DONE + rd_cmd.header.dest = DSL_CTL_HW_STATUS; + rd_cmd.header.size = 1; + rd_cmd_data = 0; + rd_cmd.data = &rd_cmd_data; + while ((ret = command_ctl_rd(hdl, rd_cmd)) == SR_OK) { + if (rd_cmd_data & bmGPIF_DONE) { + break; + } + } + + // step8: assert INTRDY low + wr_cmd.header.dest = DSL_CTL_INTRDY; + wr_cmd.header.size = 1; + wr_cmd.data[0] = (uint8_t)~bmWR_INTRDY; + if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) + return SR_ERR; + + // step9: check FPGA_DONE bit + rd_cmd.header.dest = DSL_CTL_HW_STATUS; + rd_cmd.header.size = 1; + rd_cmd_data = 0; + rd_cmd.data = &rd_cmd_data; + while ((ret = command_ctl_rd(hdl, rd_cmd)) == SR_OK) { + if (rd_cmd_data & bmFPGA_DONE) { + // step10: turn on GREEN led + wr_cmd.header.dest = DSL_CTL_LED; + wr_cmd.data[0] = bmLED_GREEN; + if ((ret = command_ctl_wr(hdl, wr_cmd)) == SR_OK) + break; + } + } + + // step10: recover GPIF to be wordwide + wr_cmd.header.dest = DSL_CTL_WORDWIDE; + wr_cmd.header.size = 1; + wr_cmd.data[0] = bmWR_WORDWIDE; + if ((ret = command_ctl_wr(hdl, wr_cmd)) != SR_OK) { + sr_err("Sent DSL_CTL_WORDWIDE command failed."); + return SR_ERR; + } + + sr_info("FPGA configure done: %d bytes.", chunksize); + return SR_OK; +} + +SR_PRIV int dsl_config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, + const struct sr_channel *ch, + const struct sr_channel_group *cg) +{ + struct DSL_context *devc = sdi->priv; + struct sr_usb_dev_inst *usb; + char str[128]; + + (void)cg; + + switch (id) { + case SR_CONF_LANGUAGE: + if (!sdi) + return SR_ERR; + *data = g_variant_new_int16(devc->language); + break; + case SR_CONF_CONN: + if (!sdi || !sdi->conn) + return SR_ERR_ARG; + usb = sdi->conn; + if (usb->address == 255) + /* Device still needs to re-enumerate after firmware + * upload, so we don't know its (future) address. */ + return SR_ERR; + snprintf(str, 128, "%d.%d", usb->bus, usb->address); + *data = g_variant_new_string(str); + break; + case SR_CONF_LIMIT_SAMPLES: + if (!sdi) + return SR_ERR; + *data = g_variant_new_uint64(devc->limit_samples); + break; + case SR_CONF_SAMPLERATE: + if (!sdi) + return SR_ERR; + *data = g_variant_new_uint64(devc->cur_samplerate); + break; + case SR_CONF_RLE_SUPPORT: + if (!sdi) + return SR_ERR; + *data = g_variant_new_boolean(devc->rle_support); + break; + case SR_CONF_CLOCK_TYPE: + if (!sdi) + return SR_ERR; + *data = g_variant_new_boolean(devc->clock_type); + break; + case SR_CONF_CLOCK_EDGE: + if (!sdi) + return SR_ERR; + *data = g_variant_new_boolean(devc->clock_edge); + break; + case SR_CONF_INSTANT: + if (!sdi) + return SR_ERR; + *data = g_variant_new_boolean(devc->instant); + break; + case SR_CONF_PROBE_VDIV: + if (!ch) + return SR_ERR; + *data = g_variant_new_uint64(ch->vdiv); + break; + case SR_CONF_PROBE_FACTOR: + if (!ch) + return SR_ERR; + *data = g_variant_new_uint64(ch->vfactor); + break; + case SR_CONF_PROBE_OFFSET: + if (!ch) + return SR_ERR; + *data = g_variant_new_uint16(ch->offset); + break; + case SR_CONF_PROBE_HW_OFFSET: + if (!ch) + return SR_ERR; + *data = g_variant_new_uint16(ch->hw_offset); + break; + case SR_CONF_TIMEBASE: + if (!sdi) + return SR_ERR; + *data = g_variant_new_uint64(devc->timebase); + break; + case SR_CONF_MAX_TIMEBASE: + if (!sdi) + return SR_ERR; + *data = g_variant_new_uint64(min(MAX_TIMEBASE, + SR_SEC(1) * + devc->profile->dev_caps.dso_depth / + channel_modes[devc->ch_mode].num / + channel_modes[devc->ch_mode].min_samplerate / + DS_CONF_DSO_HDIVS)); + break; + case SR_CONF_PROBE_COUPLING: + if (!ch) + return SR_ERR; + *data = g_variant_new_byte(ch->coupling); + break; + case SR_CONF_PROBE_EN: + if (!ch) + return SR_ERR; + *data = g_variant_new_boolean(ch->enabled); + break; + case SR_CONF_TRIGGER_SLOPE: + if (!sdi) + return SR_ERR; + *data = g_variant_new_byte(devc->trigger_slope); + break; + case SR_CONF_TRIGGER_SOURCE: + if (!sdi) + return SR_ERR; + *data = g_variant_new_byte(devc->trigger_source&0x0f); + break; + case SR_CONF_TRIGGER_CHANNEL: + if (!sdi) + return SR_ERR; + *data = g_variant_new_byte(devc->trigger_source>>4); + break; + case SR_CONF_TRIGGER_VALUE: + if (!ch) + return SR_ERR; + *data = g_variant_new_byte(ch->trig_value); + break; + case SR_CONF_HORIZ_TRIGGERPOS: + if (!sdi) + return SR_ERR; + if (sdi->mode == DSO) { + *data = g_variant_new_byte(devc->trigger_hrate); + } else { + *data = g_variant_new_byte(devc->trigger_hpos); + } + break; + case SR_CONF_TRIGGER_HOLDOFF: + if (!sdi) + return SR_ERR; + *data = g_variant_new_uint64(devc->trigger_holdoff); + break; + case SR_CONF_TRIGGER_MARGIN: + if (!sdi) + return SR_ERR; + *data = g_variant_new_byte(devc->trigger_margin); + break; + case SR_CONF_HAVE_ZERO: + if (!sdi) + return SR_ERR; + *data = g_variant_new_boolean(devc->profile->dev_caps.feature_caps & CAPS_FEATURE_ZERO); + break; + case SR_CONF_ZERO: + if (!sdi) + return SR_ERR; + if (sdi->mode == DSO) + *data = g_variant_new_boolean(devc->zero); + else + *data = g_variant_new_boolean(FALSE); + break; + case SR_CONF_ROLL: + if (!sdi) + return SR_ERR; + *data = g_variant_new_boolean(devc->roll); + break; + case SR_CONF_UNIT_BITS: + if (!sdi) + return SR_ERR; + *data = g_variant_new_byte(channel_modes[devc->ch_mode].unit_bits); + break; + case SR_CONF_REF_MIN: + if (!sdi) + return SR_ERR; + *data = g_variant_new_uint32(devc->profile->dev_caps.ref_min); + break; + case SR_CONF_REF_MAX: + if (!sdi) + return SR_ERR; + *data = g_variant_new_uint32(devc->profile->dev_caps.ref_max); + break; + case SR_CONF_PROBE_MAP_DEFAULT: + if (!sdi || !ch) + return SR_ERR; + *data = g_variant_new_boolean(ch->map_default); + break; + case SR_CONF_PROBE_MAP_UNIT: + if (!sdi || !ch) + return SR_ERR; + *data = g_variant_new_string(ch->map_unit); + break; + case SR_CONF_PROBE_MAP_MIN: + if (!sdi || !ch) + return SR_ERR; + *data = g_variant_new_double(ch->map_min); + break; + case SR_CONF_PROBE_MAP_MAX: + if (!sdi || !ch) + return SR_ERR; + *data = g_variant_new_double(ch->map_max); + break; + case SR_CONF_ACTUAL_SAMPLES: + if (!sdi) + return SR_ERR; + *data = g_variant_new_uint64(devc->actual_samples); + break; + case SR_CONF_BANDWIDTH: + if (!sdi) + return SR_ERR; + *data = g_variant_new_boolean(devc->profile->dev_caps.feature_caps & CAPS_FEATURE_20M); + break; + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +SR_PRIV int dsl_config_set(int id, GVariant *data, struct sr_dev_inst *sdi, + struct sr_channel *ch, + struct sr_channel_group *cg ) +{ + (void)cg; + struct DSL_context *devc = sdi->priv; + int ret = SR_OK; + + if (id == SR_CONF_LANGUAGE) { + devc->language = g_variant_get_int16(data); + } else if (id == SR_CONF_PROBE_MAP_DEFAULT) { + ch->map_default = g_variant_get_boolean(data); + if (ch->map_default) { + ch->map_unit = probeMapUnits[0]; + ch->map_min = -(ch->vdiv * ch->vfactor * DS_CONF_DSO_VDIVS / 2000.0); + ch->map_max = ch->vdiv * ch->vfactor * DS_CONF_DSO_VDIVS / 2000.0; + } + } else if (id == SR_CONF_PROBE_MAP_UNIT) { + if (ch->map_default) + ch->map_unit = probeMapUnits[0]; + else + ch->map_unit = g_variant_get_string(data, NULL); + } else if (id == SR_CONF_PROBE_MAP_MIN) { + if (ch->map_default) + ch->map_min = -(ch->vdiv * ch->vfactor * DS_CONF_DSO_VDIVS / 2000.0); + else + ch->map_min = g_variant_get_double(data); + } else if (id == SR_CONF_PROBE_MAP_MAX) { + if (ch->map_default) + ch->map_max = ch->vdiv * ch->vfactor * DS_CONF_DSO_VDIVS / 2000.0; + else + ch->map_max = g_variant_get_double(data); + } else { + ret = SR_ERR_NA; + } + + return ret; +} + +SR_PRIV int dsl_config_list(int key, GVariant **data, const struct sr_dev_inst *sdi, + const struct sr_channel_group *cg) +{ + struct DSL_context *devc; + GVariant *gvar; + GVariantBuilder gvb; + int i; + + (void)cg; + devc = sdi->priv; + + switch (key) { + case SR_CONF_SAMPLERATE: + g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}")); +// gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates, +// ARRAY_SIZE(samplerates), sizeof(uint64_t)); + gvar = g_variant_new_from_data(G_VARIANT_TYPE("at"), + devc->profile->dev_caps.samplerates + devc->samplerates_min_index, + (devc->samplerates_max_index - devc->samplerates_min_index + 1) * sizeof(uint64_t), TRUE, NULL, NULL); + g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar); + *data = g_variant_builder_end(&gvb); + break; + + case SR_CONF_PROBE_CONFIGS: + *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), + probeOptions, ARRAY_SIZE(probeOptions)*sizeof(int32_t), TRUE, NULL, NULL); + break; + case SR_CONF_PROBE_SESSIONS: + *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), + probeSessions, ARRAY_SIZE(probeSessions)*sizeof(int32_t), TRUE, NULL, NULL); + break; + case SR_CONF_PROBE_VDIV: + g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}")); + for (i = 0; devc->profile->dev_caps.vdivs[i]; i++); + gvar = g_variant_new_from_data(G_VARIANT_TYPE("at"), + devc->profile->dev_caps.vdivs, i*sizeof(uint64_t), TRUE, NULL, NULL); + g_variant_builder_add(&gvb, "{sv}", "vdivs", gvar); + *data = g_variant_builder_end(&gvb); + break; + case SR_CONF_PROBE_COUPLING: + g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}")); + gvar = g_variant_new_from_data(G_VARIANT_TYPE("ay"), + probeCoupling, ARRAY_SIZE(probeCoupling)*sizeof(uint8_t), TRUE, NULL, NULL); + g_variant_builder_add(&gvb, "{sv}", "coupling", gvar); + *data = g_variant_builder_end(&gvb); + break; + case SR_CONF_PROBE_MAP_UNIT: + *data = g_variant_new_strv(probeMapUnits, ARRAY_SIZE(probeMapUnits)); + break; + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +SR_PRIV int dsl_dev_open(struct sr_dev_driver *di, struct sr_dev_inst *sdi, gboolean *fpga_done) +{ + struct sr_usb_dev_inst *usb; + struct DSL_context *devc; + int ret; + uint8_t hw_info; + struct ctl_rd_cmd rd_cmd; + + devc = sdi->priv; + usb = sdi->conn; + + /* + * If the firmware was recently uploaded, no dev_open operation should be called. + * Just wait for renumerate -> detach -> attach + */ + ret = SR_ERR; + if (devc->fw_updated > 0) { + return SR_ERR; + } else { + sr_info("%s: Firmware upload was not needed.", __func__); + ret = hw_dev_open(di, sdi); + } + + if (ret != SR_OK) { + sr_err("%s: Unable to open device.", __func__); + return SR_ERR; + } + + ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE); + if (ret != 0) { + switch(ret) { + case LIBUSB_ERROR_BUSY: + sr_err("%s: Unable to claim USB interface. Another " + "program or driver has already claimed it.", __func__); + break; + case LIBUSB_ERROR_NO_DEVICE: + sr_err("%s: Device has been disconnected.", __func__); + break; + default: + sr_err("%s: Unable to claim interface: %s.", + __func__, libusb_error_name(ret)); + break; + } + + return SR_ERR; + } + + rd_cmd.header.dest = DSL_CTL_HW_STATUS; + rd_cmd.header.size = 1; + hw_info = 0; + rd_cmd.data = &hw_info; + if ((ret = command_ctl_rd(usb->devhdl, rd_cmd)) != SR_OK) { + sr_err("Failed to get hardware infos."); + return SR_ERR; + } + *fpga_done = (hw_info & bmFPGA_DONE) != 0; + + if ((sdi->status == SR_ST_ACTIVE) && !(*fpga_done)) { + char *fpga_bit; + if (!(fpga_bit = g_try_malloc(strlen(DS_RES_PATH)+strlen(devc->profile->fpga_bit33)+1))) { + sr_err("fpag_bit path malloc error!"); + return SR_ERR_MALLOC; + } + strcpy(fpga_bit, DS_RES_PATH); + switch(devc->th_level) { + case SR_TH_3V3: + strcat(fpga_bit, devc->profile->fpga_bit33); + break; + case SR_TH_5V0: + strcat(fpga_bit, devc->profile->fpga_bit50); + break; + default: + return SR_ERR; + } + ret = dsl_fpga_config(usb->devhdl, fpga_bit); + g_free(fpga_bit); + if (ret != SR_OK) { + sr_err("%s: Configure FPGA failed!", __func__); + return SR_ERR; + } + } + + + return SR_OK; +} + +SR_PRIV int dsl_dev_close(struct sr_dev_inst *sdi) +{ + struct sr_usb_dev_inst *usb; + + usb = sdi->conn; + if (usb->devhdl == NULL) + return SR_ERR; + + sr_info("%s: Closing device %d on %d.%d interface %d.", + sdi->driver->name, sdi->index, usb->bus, usb->address, USB_INTERFACE); + libusb_release_interface(usb->devhdl, USB_INTERFACE); + libusb_close(usb->devhdl); + usb->devhdl = NULL; + sdi->status = SR_ST_INACTIVE; + + return SR_OK; +} + +SR_PRIV int dsl_dev_acquisition_stop(const struct sr_dev_inst *sdi, void *cb_data) +{ + (void)cb_data; + + struct DSL_context *devc; + struct sr_usb_dev_inst *usb; + int ret; + struct ctl_wr_cmd wr_cmd; + + devc = sdi->priv; + usb = sdi->conn; + + if (!devc->abort) { + devc->abort = TRUE; + dsl_wr_reg(sdi, CTR0_ADDR, bmFORCE_RDY); + } else if (devc->status == DSL_FINISH) { + /* Stop GPIF acquisition */ + wr_cmd.header.dest = DSL_CTL_STOP; + wr_cmd.header.size = 0; + if ((ret = command_ctl_wr(usb->devhdl, wr_cmd)) != SR_OK) + sr_err("%s: Sent acquisition stop command failed!", __func__); + else + sr_info("%s: Sent acquisition stop command!", __func__); + } + + return SR_OK; +} + +SR_PRIV int dsl_dev_status_get(const struct sr_dev_inst *sdi, struct sr_status *status, gboolean prg, int begin, int end) +{ + int ret = SR_ERR; + struct ctl_rd_cmd rd_cmd; + + if (sdi) { + struct DSL_context *devc; + struct sr_usb_dev_inst *usb; + + devc = sdi->priv; + usb = sdi->conn; + if (prg && (devc->status == DSL_START)) { + rd_cmd.header.dest = DSL_CTL_I2C_STATUS; + rd_cmd.header.offset = begin; + rd_cmd.header.size = end - begin + 1; + rd_cmd.data = (unsigned char*)status; + ret = command_ctl_rd(usb->devhdl, rd_cmd); + } else if (devc->mstatus_valid) { + *status = devc->mstatus; + ret = SR_OK; + } + } + + return ret; +} + +static unsigned int get_single_buffer_time(const struct DSL_context *devc) +{ + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_USB30) + return 100; + else + return 20; +} + +static unsigned int get_total_buffer_time(const struct DSL_context *devc) +{ + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_USB30) + return 500; + else + return 100; +} + +static unsigned int to_bytes_per_ms(struct DSL_context *devc) +{ + struct sr_dev_inst *sdi = devc->cb_data; + if (sdi->mode == LOGIC) { + return ceil(devc->cur_samplerate / 1000.0 * dsl_en_ch_num(sdi) / 8); + } else { + if (devc->cur_samplerate > SR_MHZ(100)) + return SR_MHZ(100) / 1000.0 * dsl_en_ch_num(sdi); + else + return ceil(devc->cur_samplerate / 1000.0 * dsl_en_ch_num(sdi)); + } +} + +SR_PRIV int dsl_header_size(const struct DSL_context *devc) +{ + int size; + + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_USB30) + size = SR_KB(1); + else + size = SR_B(512); + return size; +} + +static size_t get_buffer_size(const struct sr_dev_inst *sdi) +{ + size_t s; + struct DSL_context *devc; + devc = sdi->priv; + + /* + * The buffer should be large enough to hold 10ms of data and + * a multiple of 512. + */ + if (sdi->mode == DSO) { + s = (devc->instant) ? devc->profile->dev_caps.dso_depth : devc->actual_samples * dsl_en_ch_num(sdi) + dsl_header_size(devc); + } else { + s = (devc->stream) ? get_single_buffer_time(devc) * to_bytes_per_ms(devc) : 1024*1024; + } + return (s + 511ULL) & ~511ULL; +} + +static unsigned int get_number_of_transfers(const struct sr_dev_inst *sdi) +{ + unsigned int n; + struct DSL_context *devc; + devc = sdi->priv; + + #ifndef _WIN32 + /* Total buffer size should be able to hold about 100ms of data. */ + n = (devc->stream) ? ceil(get_total_buffer_time(devc) * 1.0f * to_bytes_per_ms(devc) / get_buffer_size(sdi)) : 1; + #else + n = (devc->stream) ? ceil(get_total_buffer_time(devc) * 1.0f * to_bytes_per_ms(devc) / get_buffer_size(sdi)) : 4; + #endif + + if (n > NUM_SIMUL_TRANSFERS) + return NUM_SIMUL_TRANSFERS; + + return n; +} + +SR_PRIV unsigned int dsl_get_timeout(const struct sr_dev_inst *sdi) +{ + size_t total_size; + unsigned int timeout; + struct DSL_context *devc; + devc = sdi->priv; + + total_size = get_buffer_size(sdi) * get_number_of_transfers(sdi); + timeout = total_size / to_bytes_per_ms(devc); + + if (devc->stream) + return timeout + timeout / 4; /* Leave a headroom of 25% percent. */ + else + return 1000; +} + +static void finish_acquisition(struct DSL_context *devc) +{ + struct sr_datafeed_packet packet; + + sr_info("%s: send SR_DF_END packet", __func__); + /* Terminate session. */ + packet.type = SR_DF_END; + packet.status = SR_PKT_OK; + sr_session_send(devc->cb_data, &packet); + + if (devc->num_transfers != 0) { + devc->num_transfers = 0; + g_free(devc->transfers); + } + + devc->status = DSL_FINISH; +} + +static void free_transfer(struct libusb_transfer *transfer) +{ + struct DSL_context *devc; + unsigned int i; + + devc = transfer->user_data; + + g_free(transfer->buffer); + transfer->buffer = NULL; + libusb_free_transfer(transfer); + + for (i = 0; i < devc->num_transfers; i++) { + if (devc->transfers[i] == transfer) { + devc->transfers[i] = NULL; + break; + } + } + + devc->submitted_transfers--; + if (devc->submitted_transfers == 0) + finish_acquisition(devc); +} + +static void resubmit_transfer(struct libusb_transfer *transfer) +{ + int ret; + + if ((ret = libusb_submit_transfer(transfer)) == LIBUSB_SUCCESS) + return; + + free_transfer(transfer); + /* TODO: Stop session? */ + + sr_err("%s: %s", __func__, libusb_error_name(ret)); +} + +static void get_measure(const struct sr_dev_inst *sdi, uint8_t *buf, uint32_t offset) +{ + uint64_t u64_tmp; + struct DSL_context *devc = sdi->priv; + + devc->mstatus.pkt_id = *((const uint16_t*)buf + offset); + devc->mstatus.vlen = *((const uint32_t*)buf + offset/2 + 2/2) & 0x0fffffff; + devc->mstatus.stream_mode = (*((const uint32_t*)buf + offset/2 + 2/2) & 0x80000000) != 0; + devc->mstatus.measure_valid = *((const uint32_t*)buf + offset/2 + 2/2) & 0x40000000; + devc->mstatus.sample_divider = *((const uint32_t*)buf + offset/2 + 4/2) & 0x0fffffff; + devc->mstatus.sample_divider_tog = (*((const uint32_t*)buf + offset/2 + 4/2) & 0x80000000) != 0; + devc->mstatus.trig_flag = (*((const uint32_t*)buf + offset/2 + 4/2) & 0x40000000) != 0; + + devc->mstatus.ch0_max = *((const uint8_t*)buf + offset*2 + 33*2); + devc->mstatus.ch0_min = *((const uint8_t*)buf + offset*2 + 33*2+1); + devc->mstatus.ch0_cyc_cnt = *((const uint32_t*)buf + offset/2 + 34/2); + devc->mstatus.ch0_cyc_tlen = *((const uint32_t*)buf + offset/2 + 36/2); + devc->mstatus.ch0_cyc_plen = *((const uint32_t*)buf + offset/2 + 38/2); + devc->mstatus.ch0_cyc_llen = *((const uint32_t*)buf + offset/2 + 40/2); + devc->mstatus.ch0_level_valid = (*((const uint32_t*)buf + offset/2 + 42/2) & 0x00008000) != 0; + devc->mstatus.ch0_plevel = (*((const uint32_t*)buf + offset/2 + 42/2) & 0x00004000) != 0; + devc->mstatus.ch0_high_level = *((const uint8_t*)buf + offset*2 + 43*2); + devc->mstatus.ch0_low_level = *((const uint8_t*)buf + offset*2 + 43*2+1); + devc->mstatus.ch0_cyc_rlen = *((const uint32_t*)buf + offset/2 + 44/2); + devc->mstatus.ch0_cyc_flen = *((const uint32_t*)buf + offset/2 + 46/2); + devc->mstatus.ch0_acc_square = *((const uint64_t*)buf + offset/4 + 48/4); + devc->mstatus.ch0_acc_mean = *((const uint32_t*)buf + offset/2 + 52/2); + + devc->mstatus.ch1_max = *((const uint8_t*)buf + offset*2 + 65*2); + devc->mstatus.ch1_min = *((const uint8_t*)buf + offset*2 + 65*2+1); + devc->mstatus.ch1_cyc_cnt = *((const uint32_t*)buf + offset/2 + 66/2); + devc->mstatus.ch1_cyc_tlen = *((const uint32_t*)buf + offset/2 + 68/2); + devc->mstatus.ch1_cyc_plen = *((const uint32_t*)buf + offset/2 + 70/2); + devc->mstatus.ch1_cyc_llen = *((const uint32_t*)buf + offset/2 + 72/2); + devc->mstatus.ch1_level_valid = (*((const uint32_t*)buf + offset/2 + 74/2) & 0x00008000) != 0; + devc->mstatus.ch1_plevel = (*((const uint32_t*)buf + offset/2 + 74/2) & 0x00004000) != 0; + devc->mstatus.ch1_high_level = *((const uint8_t*)buf + offset*2 + 75*2); + devc->mstatus.ch1_low_level = *((const uint8_t*)buf + offset*2 + 75*2+1); + devc->mstatus.ch1_cyc_rlen = *((const uint32_t*)buf + offset/2 + 76/2); + devc->mstatus.ch1_cyc_flen = *((const uint32_t*)buf + offset/2 + 78/2); + devc->mstatus.ch1_acc_square = *((const uint64_t*)buf + offset/4 + 80/4); + devc->mstatus.ch1_acc_mean = *((const uint32_t*)buf + offset/2 + 84/2); + + if (1 == dsl_en_ch_num(sdi)) { + u64_tmp = devc->mstatus.ch0_acc_square + devc->mstatus.ch1_acc_square; + devc->mstatus.ch0_acc_square = u64_tmp; + devc->mstatus.ch1_acc_square = u64_tmp; + u64_tmp = devc->mstatus.ch0_acc_mean + devc->mstatus.ch1_acc_mean; + devc->mstatus.ch0_acc_mean = u64_tmp; + devc->mstatus.ch0_acc_mean = u64_tmp; + } +} + +static void receive_transfer(struct libusb_transfer *transfer) +{ + struct sr_datafeed_packet packet; + struct sr_datafeed_logic logic; + struct sr_datafeed_dso dso; + struct sr_datafeed_analog analog; + uint64_t cur_sample_count = 0; + + uint8_t *cur_buf = transfer->buffer; + struct DSL_context *devc = transfer->user_data; + struct sr_dev_inst *sdi = devc->cb_data; + + if (devc->status == DSL_START) + devc->status = DSL_DATA; + + if (devc->abort) + devc->status = DSL_STOP; + + sr_info("%" PRIu64 ": receive_transfer(): status %d; timeout %d; received %d bytes.", + g_get_monotonic_time(), transfer->status, transfer->timeout, transfer->actual_length); + + switch (transfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though. */ + break; + default: + devc->status = DSL_ERROR; + break; + } + + packet.status = SR_PKT_OK; + if (devc->status == DSL_DATA && + transfer->actual_length != 0) { + /* Send the incoming transfer to the session bus. */ + // check packet type + if (sdi->mode == LOGIC) { + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + cur_sample_count = transfer->actual_length * 8 / dsl_en_ch_num(sdi) ; + logic.length = transfer->actual_length; + logic.format = LA_CROSS_DATA; + logic.data_error = 0; + logic.data = cur_buf; + } else if (sdi->mode == DSO) { + if (!devc->instant) { + const uint32_t offset = devc->actual_samples / (channel_modes[devc->ch_mode].num/dsl_en_ch_num(sdi)); + get_measure(sdi, cur_buf, offset); + } else { + devc->mstatus.vlen = get_buffer_size(sdi) / channel_modes[devc->ch_mode].num; + } + + const uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(channel_modes[devc->ch_mode].max_samplerate * 1.0 / devc->cur_samplerate / dsl_en_ch_num(sdi)); + if ((devc->mstatus.pkt_id == DSO_PKTID && + devc->mstatus.sample_divider == divider && + devc->mstatus.vlen != 0 && + devc->mstatus.vlen <= (uint32_t)(transfer->actual_length - dsl_header_size(devc)) / 2) || + devc->instant) { + devc->roll = (devc->mstatus.stream_mode != 0); + devc->mstatus_valid = devc->instant ? FALSE : TRUE; + packet.type = SR_DF_DSO; + packet.payload = &dso; + dso.probes = sdi->channels; + cur_sample_count = min(channel_modes[devc->ch_mode].num * devc->mstatus.vlen / dsl_en_ch_num(sdi), devc->limit_samples); + dso.num_samples = cur_sample_count; + dso.mq = SR_MQ_VOLTAGE; + dso.unit = SR_UNIT_VOLT; + dso.mqflags = SR_MQFLAG_AC; + dso.samplerate_tog = (devc->mstatus.sample_divider_tog != 0); + dso.trig_flag = (devc->mstatus.trig_flag != 0); + dso.data = cur_buf; + } else { + packet.type = SR_DF_DSO; + packet.status = SR_PKT_DATA_ERROR; + devc->mstatus_valid = FALSE; + } + } else if (sdi->mode == ANALOG) { + packet.type = SR_DF_ANALOG; + packet.payload = &analog; + analog.probes = sdi->channels; + cur_sample_count = transfer->actual_length / (((channel_modes[devc->ch_mode].unit_bits + 7) / 8) * g_slist_length(analog.probes)); + analog.num_samples = cur_sample_count; + analog.unit_bits = channel_modes[devc->ch_mode].unit_bits;; + analog.unit_pitch = devc->unit_pitch; + analog.mq = SR_MQ_VOLTAGE; + analog.unit = SR_UNIT_VOLT; + analog.mqflags = SR_MQFLAG_AC; + analog.data = cur_buf; + } + + if ((devc->limit_samples && devc->num_bytes < devc->actual_bytes) || + sdi->mode != LOGIC ) { + const uint64_t remain_length= devc->actual_bytes - devc->num_bytes; + logic.length = min(logic.length, remain_length); + + /* send data to session bus */ + if (!devc->overflow) { + if (packet.status == SR_PKT_OK) + sr_session_send(sdi, &packet); + } else { + packet.type = SR_DF_OVERFLOW; + packet.payload = NULL; + sr_session_send(sdi, &packet); + } + } + + devc->num_samples += cur_sample_count; + devc->num_bytes += logic.length; + if (sdi->mode == LOGIC && + devc->limit_samples && + devc->num_bytes >= devc->actual_bytes) { + devc->status = DSL_STOP; + } else if ((sdi->mode == DSO && devc->instant) && + devc->limit_samples && + devc->num_samples >= devc->actual_samples) { + int over_bytes = (devc->num_samples - devc->actual_samples) * dsl_en_ch_num(sdi); + if (over_bytes >= devc->instant_tail_bytes) { + const uint32_t offset = (transfer->actual_length - over_bytes) / 2; + get_measure(sdi, cur_buf, offset); + if (devc->mstatus.pkt_id == DSO_PKTID) + devc->mstatus_valid = TRUE; + devc->status = DSL_STOP; + } else { + + } + } + } + + if (devc->status == DSL_DATA) + resubmit_transfer(transfer); + else + free_transfer(transfer); + + devc->trf_completed = 1; +} + +static void receive_header(struct libusb_transfer *transfer) +{ + struct DSL_context *devc; + struct sr_datafeed_packet packet; + struct ds_trigger_pos *trigger_pos; + const struct sr_dev_inst *sdi; + uint64_t remain_cnt; + + packet.status = SR_PKT_OK; + devc = transfer->user_data; + sdi = devc->cb_data; + trigger_pos = (struct ds_trigger_pos *)transfer->buffer; + + if (devc->status != DSL_ABORT) + devc->status = DSL_ERROR; + if (!devc->abort && transfer->status == LIBUSB_TRANSFER_COMPLETED && + trigger_pos->check_id == TRIG_CHECKID) { + sr_info("%" PRIu64 ": receive_trigger_pos(): status %d; timeout %d; received %d bytes.", + g_get_monotonic_time(), transfer->status, transfer->timeout, transfer->actual_length); + remain_cnt = trigger_pos->remain_cnt_h; + remain_cnt = (remain_cnt << 32) + trigger_pos->remain_cnt_l; + if (transfer->actual_length == dsl_header_size(devc)) { + if (sdi->mode != LOGIC || + devc->stream || + remain_cnt < devc->limit_samples) { + if (sdi->mode == LOGIC && (!devc->stream || (devc->status == DSL_ABORT))) { + devc->actual_samples = devc->limit_samples - remain_cnt; + devc->actual_bytes = devc->actual_samples / DSLOGIC_ATOMIC_SAMPLES * dsl_en_ch_num(sdi) * DSLOGIC_ATOMIC_SIZE; + devc->actual_samples = devc->actual_bytes / dsl_en_ch_num(sdi) * 8; + } + + packet.type = SR_DF_TRIGGER; + packet.payload = trigger_pos; + sr_session_send(sdi, &packet); + + devc->status = DSL_DATA; + } + } + } else if (!devc->abort) { + sr_err("%s: trigger packet data error.", __func__); + packet.type = SR_DF_TRIGGER; + packet.payload = trigger_pos; + packet.status = SR_PKT_DATA_ERROR; + sr_session_send(sdi, &packet); + } + + free_transfer(transfer); +} + +SR_PRIV int dsl_start_transfers(const struct sr_dev_inst *sdi) +{ + struct DSL_context *devc; + struct sr_usb_dev_inst *usb; + struct libusb_transfer *transfer; + unsigned int i, num_transfers; + int ret; + unsigned char *buf; + size_t size; + struct ds_trigger_pos *trigger_pos; + + devc = sdi->priv; + usb = sdi->conn; + + num_transfers = get_number_of_transfers(sdi); + size = get_buffer_size(sdi); + + /* trigger packet transfer */ + if (!(trigger_pos = g_try_malloc0(dsl_header_size(devc)))) { + sr_err("%s: USB trigger_pos buffer malloc failed.", __func__); + return SR_ERR_MALLOC; + } + + devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * (num_transfers + 1)); + if (!devc->transfers) { + sr_err("%s: USB transfer malloc failed.", __func__); + return SR_ERR_MALLOC; + } + transfer = libusb_alloc_transfer(0); + libusb_fill_bulk_transfer(transfer, usb->devhdl, + 6 | LIBUSB_ENDPOINT_IN, (unsigned char *)trigger_pos, dsl_header_size(devc), + (libusb_transfer_cb_fn)receive_header, devc, 0); + if ((ret = libusb_submit_transfer(transfer)) != 0) { + sr_err("%s: Failed to submit trigger_pos transfer: %s.", + __func__, libusb_error_name(ret)); + libusb_free_transfer(transfer); + g_free(trigger_pos); + devc->status = DSL_ERROR; + return SR_ERR; + } else { + devc->num_transfers++; + devc->transfers[0] = transfer; + devc->submitted_transfers++; + } + + /* data packet transfer */ + for (i = 1; i <= num_transfers; i++) { + if (!(buf = g_try_malloc(size))) { + sr_err("%s: USB transfer buffer malloc failed.", __func__); + return SR_ERR_MALLOC; + } + transfer = libusb_alloc_transfer(0); + libusb_fill_bulk_transfer(transfer, usb->devhdl, + 6 | LIBUSB_ENDPOINT_IN, buf, size, + (libusb_transfer_cb_fn)receive_transfer, devc, 0); + if ((ret = libusb_submit_transfer(transfer)) != 0) { + sr_err("%s: Failed to submit transfer: %s.", + __func__, libusb_error_name(ret)); + libusb_free_transfer(transfer); + g_free(buf); + devc->status = DSL_ERROR; + devc->abort = TRUE; + return SR_ERR; + } + devc->transfers[i] = transfer; + devc->submitted_transfers++; + devc->num_transfers++; + } + + return SR_OK; +} diff --git a/libsigrok4DSL/hardware/DSL/dsl.h b/libsigrok4DSL/hardware/DSL/dsl.h old mode 100644 new mode 100755 index dee269ba..e1c36a41 --- a/libsigrok4DSL/hardware/DSL/dsl.h +++ b/libsigrok4DSL/hardware/DSL/dsl.h @@ -1,698 +1,1044 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2013 Bert Vermeulen - * Copyright (C) 2013 DreamSourceLab - * - * 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 . - */ - -#ifndef LIBDSL_HARDWARE_DSL_H -#define LIBDSL_HARDWARE_DSL_H - -#include -#include "libsigrok.h" -#include "libsigrok-internal.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - -#undef min -#define min(a,b) ((a)<(b)?(a):(b)) -#undef max -#define max(a,b) ((a)>(b)?(a):(b)) - -/* Message logging helpers with subsystem-specific prefix string. */ -#define LOG_PREFIX "DSL Hardware: " -#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) -#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) -#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) -#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) -#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) -#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) - -#define USB_INTERFACE 0 -#define USB_CONFIGURATION 1 -#define NUM_TRIGGER_STAGES 16 -#define NUM_SIMUL_TRANSFERS 64 - -#define DSL_REQUIRED_VERSION_MAJOR 2 -#define DSL_REQUIRED_VERSION_MINOR 0 - -/* hardware Capabilities */ -#define CAPS_MODE_LOGIC (1 << 0) -#define CAPS_MODE_ANALOG (1 << 1) -#define CAPS_MODE_DSO (1 << 2) - -#define CAPS_FEATURE_NONE 0 -// voltage threshold -#define CAPS_FEATURE_VTH (1 << 0) -// with external buffer -#define CAPS_FEATURE_BUF (1 << 1) -// pre offset control -#define CAPS_FEATURE_PREOFF (1 << 2) -// small startup eemprom -#define CAPS_FEATURE_SEEP (1 << 3) -// zero calibration ability -#define CAPS_FEATURE_ZERO (1 << 4) -/* end */ - - -#define DSLOGIC_ATOMIC_BITS 6 -#define DSLOGIC_ATOMIC_SAMPLES (1 << DSLOGIC_ATOMIC_BITS) -#define DSLOGIC_ATOMIC_SIZE (1 << (DSLOGIC_ATOMIC_BITS - 3)) -#define DSLOGIC_ATOMIC_MASK (0xFFFF << DSLOGIC_ATOMIC_BITS) - -/* - * for basic configuration - */ -#define TRIG_EN_BIT 0 -#define CLK_TYPE_BIT 1 -#define CLK_EDGE_BIT 2 -#define RLE_MODE_BIT 3 -#define DSO_MODE_BIT 4 -#define HALF_MODE_BIT 5 -#define QUAR_MODE_BIT 6 -#define ANALOG_MODE_BIT 7 -#define FILTER_BIT 8 -#define INSTANT_BIT 9 -#define STRIG_MODE_BIT 11 -#define STREAM_MODE_BIT 12 -#define LPB_TEST_BIT 13 -#define EXT_TEST_BIT 14 -#define INT_TEST_BIT 15 - -#define bmNONE 0 -#define bmEEWP (1 << 0) -#define bmFORCE_RDY (1 << 1) -#define bmFORCE_STOP (1 << 2) -#define bmSCOPE_SET (1 << 3) -#define bmSCOPE_CLR (1 << 4) - -/* - * packet content check - */ -#define TRIG_CHECKID 0x55555555 -#define DSO_PKTID 0xa500 - -/* - * for DSCope device - * trans: x << 8 + y - * x = vpos(coarse), each step(1024 total) indicate x(mv) at 1/20 attenuation, and x/10(mv) at 1/2 attenuation - * y = voff(fine), each step(1024 total) indicate y/100(mv) at 1/20 attenuation, adn y/1000(mv) at 1/2 attenuation - * voff: x << 10 + y - * x = vpos(coarse) default bias - * y = voff(fine) default bias - * the final offset: x+DSCOPE_CONSTANT_BIAS->vpos(coarse); y->voff(fine) - */ -#define DSCOPE_CONSTANT_BIAS 160 -#define DSCOPE_TRANS_CMULTI 10 -#define DSCOPE_TRANS_FMULTI 100.0 - -/* - * for DSCope20 device - * trans: the whole windows offset map to the offset pwm(1024 total) - * voff: offset pwm constant bias to balance circuit offset - */ -#define CALI_VGAIN_RANGE 100 - -struct DSL_caps { - uint64_t mode_caps; - uint64_t feature_caps; - uint64_t channels; - uint64_t hw_depth; - uint64_t dso_depth; - uint8_t intest_channel; - const uint64_t *vdivs; - uint8_t vga_id; - uint16_t default_channelmode; - uint64_t default_samplerate; - uint64_t default_samplelimit; - uint16_t default_pwmtrans; - uint16_t default_pwmmargin; -}; - -struct DSL_profile { - uint16_t vid; - uint16_t pid; - - const char *vendor; - const char *model; - const char *model_version; - - const char *firmware; - - const char *fpga_bit33; - const char *fpga_bit50; - - struct DSL_caps dev_caps; -}; - -static const uint64_t vdivs10to2000[] = { - SR_mV(10), - SR_mV(20), - SR_mV(50), - SR_mV(100), - SR_mV(200), - SR_mV(500), - SR_V(1), - SR_V(2), - 0, -}; - -struct DSL_vga { - uint8_t id; - uint64_t key; - uint64_t vgain; - uint16_t voff; - uint16_t voff_comp; -}; -static const struct DSL_vga vga_defaults[] = { - {1, 10, 0x162400, (32<<10)+558, (32<<10)+558}, - {1, 20, 0x14C000, (32<<10)+558, (32<<10)+558}, - {1, 50, 0x12E800, (32<<10)+558, (32<<10)+558}, - {1, 100, 0x118000, (32<<10)+558, (32<<10)+558}, - {1, 200, 0x102400, (32<<10)+558, (32<<10)+558}, - {1, 500, 0x2E800, (32<<10)+558, (32<<10)+558}, - {1, 1000, 0x18000, (32<<10)+558, (32<<10)+558}, - {1, 2000, 0x02400, (32<<10)+558, (32<<10)+558}, - - {2, 10, 0x1DA800, 45, 1024-920-45}, - {2, 20, 0x1A7200, 45, 1024-920-45}, - {2, 50, 0x164200, 45, 1024-920-45}, - {2, 100, 0x131800, 45, 1024-920-45}, - {2, 200, 0xBD000, 45, 1024-920-45}, - {2, 500, 0x7AD00, 45, 1024-920-45}, - {2, 1000, 0x48800, 45, 1024-920-45}, - {2, 2000, 0x12000, 45, 1024-920-45}, - - {3, 10, 0x1C5C00, 45, 1024-920-45}, - {3, 20, 0x19EB00, 45, 1024-920-45}, - {3, 50, 0x16AE00, 45, 1024-920-45}, - {3, 100, 0x143D00, 45, 1024-920-45}, - {3, 200, 0xB1000, 45, 1024-920-45}, - {3, 500, 0x7F000, 45, 1024-920-45}, - {3, 1000, 0x57200, 45, 1024-920-45}, - {3, 2000, 0x2DD00, 45, 1024-920-45}, - - {0, 0, 0, 0, 0} -}; - -enum CHANNEL_ID { - DSL_STREAM20x16 = 0, - DSL_STREAM25x12, - DSL_STREAM50x6, - DSL_STREAM100x3, - DSL_BUFFER100x16, - DSL_BUFFER200x8, - DSL_BUFFER400x4, - DSL_ANALOG10x2, - DSL_DSO200x2, -}; - -struct DSL_channels { - enum CHANNEL_ID id; - enum OPERATION_MODE mode; - enum CHANNEL_TYPE type; - gboolean stream; - uint16_t num; - uint8_t unit_bits; - uint64_t min_samplerate; - uint64_t max_samplerate; - uint64_t hw_min_samplerate; - uint64_t hw_max_samplerate; - const char *descr; -}; - -static const struct DSL_channels channel_modes[] = { - // LA Stream - {DSL_STREAM20x16, LOGIC, SR_CHANNEL_LOGIC, TRUE, 16, 1, SR_KHZ(10), SR_MHZ(20), - SR_KHZ(10), SR_MHZ(100), "Use 16 Channels (Max 20MHz)"}, - {DSL_STREAM25x12, LOGIC, SR_CHANNEL_LOGIC, TRUE, 12, 1, SR_KHZ(10), SR_MHZ(25), - SR_KHZ(10), SR_MHZ(100), "Use 12 Channels (Max 25MHz)"}, - {DSL_STREAM50x6, LOGIC, SR_CHANNEL_LOGIC, TRUE, 6, 1, SR_KHZ(10), SR_MHZ(50), - SR_KHZ(10), SR_MHZ(100), "Use 6 Channels (Max 50MHz)"}, - {DSL_STREAM100x3, LOGIC, SR_CHANNEL_LOGIC, TRUE, 3, 1, SR_KHZ(10), SR_MHZ(100), - SR_KHZ(10), SR_MHZ(100), "Use 3 Channels (Max 100MHz)"}, - - // LA Buffer - {DSL_BUFFER100x16, LOGIC, SR_CHANNEL_LOGIC, FALSE, 16, 1, SR_KHZ(10), SR_MHZ(100), - SR_KHZ(10), SR_MHZ(100), "Use Channels 0~15 (Max 100MHz)"}, - {DSL_BUFFER200x8, LOGIC, SR_CHANNEL_LOGIC, FALSE, 8, 1, SR_KHZ(10), SR_MHZ(200), - SR_KHZ(10), SR_MHZ(100), "Use Channels 0~7 (Max 200MHz)"}, - {DSL_BUFFER400x4, LOGIC, SR_CHANNEL_LOGIC, FALSE, 4, 1, SR_KHZ(10), SR_MHZ(400), - SR_KHZ(10), SR_MHZ(100), "Use Channels 0~3 (Max 400MHz)"}, - - // DAQ - {DSL_ANALOG10x2, ANALOG, SR_CHANNEL_ANALOG, TRUE, 2, 8, SR_HZ(10), SR_MHZ(10), - SR_KHZ(10), SR_MHZ(100), "Use Channels 0~1 (Max 10MHz)"}, - - // OSC - {DSL_DSO200x2, DSO, SR_CHANNEL_DSO, FALSE, 2, 8, SR_KHZ(10), SR_MHZ(200), - SR_KHZ(10), SR_MHZ(100), "Use Channels 0~1 (Max 200MHz)"} -}; - -static const struct DSL_profile supported_DSLogic[] = { - /* - * DSLogic - */ - {0x2A0E, 0x0001, "DreamSourceLab", "DSLogic", NULL, - "DSLogic.fw", - "DSLogic33.bin", - "DSLogic50.bin", - {CAPS_MODE_LOGIC | CAPS_MODE_ANALOG | CAPS_MODE_DSO, - CAPS_FEATURE_SEEP | CAPS_FEATURE_BUF, - (1 << DSL_STREAM20x16) | (1 << DSL_STREAM25x12) | (1 << DSL_STREAM50x6) | (1 << DSL_STREAM100x3) | - (1 << DSL_BUFFER100x16) | (1 << DSL_BUFFER200x8) | (1 << DSL_BUFFER400x4) | - (1 << DSL_ANALOG10x2) | - (1 << DSL_DSO200x2), - SR_MB(256), - SR_Mn(2), - DSL_BUFFER100x16, - vdivs10to2000, - 0, - DSL_STREAM20x16, - SR_MHZ(1), - SR_Mn(1), - 0, - 0} - }, - - {0x2A0E, 0x0003, "DreamSourceLab", "DSLogic Pro", NULL, - "DSLogicPro.fw", - "DSLogicPro.bin", - "DSLogicPro.bin", - {CAPS_MODE_LOGIC, - CAPS_FEATURE_SEEP | CAPS_FEATURE_VTH | CAPS_FEATURE_BUF, - (1 << DSL_STREAM20x16) | (1 << DSL_STREAM25x12) | (1 << DSL_STREAM50x6) | (1 << DSL_STREAM100x3) | - (1 << DSL_BUFFER100x16) | (1 << DSL_BUFFER200x8) | (1 << DSL_BUFFER400x4), - SR_MB(256), - 0, - DSL_BUFFER100x16, - 0, - 0, - DSL_STREAM20x16, - SR_MHZ(1), - SR_Mn(1), - 0, - 0} - }, - - {0x2A0E, 0x0020, "DreamSourceLab", "DSLogic PLus", NULL, - "DSLogicPlus.fw", - "DSLogicPlus.bin", - "DSLogicPlus.bin", - {CAPS_MODE_LOGIC, - CAPS_FEATURE_VTH | CAPS_FEATURE_BUF, - (1 << DSL_STREAM20x16) | (1 << DSL_STREAM25x12) | (1 << DSL_STREAM50x6) | (1 << DSL_STREAM100x3) | - (1 << DSL_BUFFER100x16) | (1 << DSL_BUFFER200x8) | (1 << DSL_BUFFER400x4), - SR_MB(256), - 0, - DSL_BUFFER100x16, - 0, - 0, - DSL_STREAM20x16, - SR_MHZ(1), - SR_Mn(1), - 0, - 0} - }, - - {0x2A0E, 0x0021, "DreamSourceLab", "DSLogic Basic", NULL, - "DSLogicBasic.fw", - "DSLogicBasic.bin", - "DSLogicBasic.bin", - {CAPS_MODE_LOGIC, - CAPS_FEATURE_VTH, - (1 << DSL_STREAM20x16) | (1 << DSL_STREAM25x12) | (1 << DSL_STREAM50x6) | (1 << DSL_STREAM100x3) | - (1 << DSL_BUFFER100x16) | (1 << DSL_BUFFER200x8) | (1 << DSL_BUFFER400x4), - SR_KB(256), - 0, - DSL_STREAM20x16, - 0, - 0, - DSL_STREAM20x16, - SR_MHZ(1), - SR_Mn(1), - 0, - 0} - }, - - { 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} -}; - -static const struct DSL_profile supported_DSCope[] = { - /* - * DSCope - */ - {0x2A0E, 0x0002, "DreamSourceLab", "DSCope", NULL, - "DSCope.fw", - "DSCope.bin", - "DSCope.bin", - {CAPS_MODE_ANALOG | CAPS_MODE_DSO, - CAPS_FEATURE_ZERO | CAPS_FEATURE_PREOFF | CAPS_FEATURE_SEEP | CAPS_FEATURE_BUF, - (1 << DSL_ANALOG10x2) | - (1 << DSL_DSO200x2), - SR_MB(256), - SR_Mn(2), - 0, - vdivs10to2000, - 1, - DSL_DSO200x2, - SR_MHZ(100), - SR_Mn(1), - (129<<8)+167, - 1024-920} - }, - - {0x2A0E, 0x0004, "DreamSourceLab", "DSCope20", NULL, - "DSCope20.fw", - "DSCope20.bin", - "DSCope20.bin", - {CAPS_MODE_ANALOG | CAPS_MODE_DSO, - CAPS_FEATURE_ZERO | CAPS_FEATURE_SEEP | CAPS_FEATURE_BUF, - (1 << DSL_ANALOG10x2) | - (1 << DSL_DSO200x2), - SR_MB(256), - SR_Mn(2), - 0, - vdivs10to2000, - 2, - DSL_DSO200x2, - SR_MHZ(100), - SR_Mn(1), - 920, - 1024-920} - }, - - {0x2A0E, 0x0022, "DreamSourceLab", "DSCope B20", NULL, - "DSCopeB20.fw", - "DSCope20.bin", - "DSCope20.bin", - {CAPS_MODE_ANALOG | CAPS_MODE_DSO, - CAPS_FEATURE_ZERO | CAPS_FEATURE_BUF, - (1 << DSL_ANALOG10x2) | - (1 << DSL_DSO200x2), - SR_MB(256), - SR_Mn(2), - 0, - vdivs10to2000, - 2, - DSL_DSO200x2, - SR_MHZ(100), - SR_Mn(1), - 920, - 1024-920} - }, - - {0x2A0E, 0x0023, "DreamSourceLab", "DSCope C20", NULL, - "DSCopeC20.fw", - "DSCopeC20.bin", - "DSCopeC20.bin", - {CAPS_MODE_ANALOG | CAPS_MODE_DSO, - CAPS_FEATURE_ZERO | CAPS_FEATURE_BUF, - (1 << DSL_ANALOG10x2) | - (1 << DSL_DSO200x2), - SR_MB(256), - SR_Mn(2), - 0, - vdivs10to2000, - 3, - DSL_DSO200x2, - SR_MHZ(100), - SR_Mn(1), - 920, - 1024-920} - }, - - - {0x2A0E, 0x0024, "DreamSourceLab", "DSCope C20P", NULL, - "DSCopeC20P.fw", - "DSCopeC20P.bin", - "DSCopeC20P.bin", - {CAPS_MODE_ANALOG | CAPS_MODE_DSO, - CAPS_FEATURE_ZERO | CAPS_FEATURE_BUF, - (1 << DSL_ANALOG10x2) | - (1 << DSL_DSO200x2), - SR_MB(256), - SR_Mn(2), - 0, - vdivs10to2000, - 3, - DSL_DSO200x2, - SR_MHZ(100), - SR_Mn(1), - 920, - 1024-920} - }, - - {0x2A0E, 0x0025, "DreamSourceLab", "DSCope C20", NULL, - "DSCopeC20B.fw", - "DSCopeC20B.bin", - "DSCopeC20B.bin", - {CAPS_MODE_ANALOG | CAPS_MODE_DSO, - CAPS_FEATURE_ZERO, - (1 << DSL_ANALOG10x2) | - (1 << DSL_DSO200x2), - SR_KB(256), - SR_Kn(20), - 0, - vdivs10to2000, - 3, - DSL_DSO200x2, - SR_MHZ(100), - SR_Kn(10), - 920, - 1024-920} - }, - - { 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} -}; - -static const gboolean default_ms_en[] = { - FALSE, /* DSO_MS_BEGIN */ - TRUE, /* DSO_MS_FREQ */ - FALSE, /* DSO_MS_PERD */ - TRUE, /* DSO_MS_VMAX */ - TRUE, /* DSO_MS_VMIN */ - FALSE, /* DSO_MS_VRMS */ - FALSE, /* DSO_MS_VMEA */ - FALSE, /* DSO_MS_VP2P */ -}; - -enum { - DSL_ERROR = -1, - DSL_INIT = 0, - DSL_START = 1, - DSL_READY = 2, - DSL_TRIGGERED = 3, - DSL_DATA = 4, - DSL_STOP = 5, - DSL_FINISH = 7, - DSL_ABORT = 8, -}; - -struct DSL_context { - const struct DSL_profile *profile; - /* - * Since we can't keep track of an DSL device after upgrading - * the firmware (it renumerates into a different device address - * after the upgrade) this is like a global lock. No device will open - * until a proper delay after the last device was upgraded. - */ - int64_t fw_updated; - - /* Device/capture settings */ - uint64_t cur_samplerate; - uint64_t limit_samples; - uint64_t actual_samples; - uint64_t actual_bytes; - - /* Operational settings */ - gboolean clock_type; - gboolean clock_edge; - gboolean rle_mode; - gboolean rle_support; - gboolean instant; - uint16_t op_mode; - gboolean stream; - uint8_t test_mode; - uint16_t buf_options; - enum CHANNEL_ID ch_mode; - uint16_t samplerates_min_index; - uint16_t samplerates_max_index; - uint16_t th_level; - double vth; - uint16_t filter; - uint16_t trigger_mask[NUM_TRIGGER_STAGES]; - uint16_t trigger_value[NUM_TRIGGER_STAGES]; - int trigger_stage; - uint16_t trigger_buffer[NUM_TRIGGER_STAGES]; - uint64_t timebase; - uint8_t max_height; - uint8_t trigger_channel; - uint8_t trigger_slope; - uint8_t trigger_source; - uint8_t trigger_hrate; - uint32_t trigger_hpos; - uint32_t trigger_holdoff; - uint8_t trigger_margin; - gboolean zero; - gboolean cali; - int zero_stage; - int zero_pcnt; - int zero_comb; - gboolean roll; - gboolean data_lock; - uint16_t unit_pitch; - - uint64_t num_samples; - uint64_t num_bytes; - int submitted_transfers; - int empty_transfer_count; - - void *cb_data; - unsigned int num_transfers; - struct libusb_transfer **transfers; - int *usbfd; - - int pipe_fds[2]; - GIOChannel *channel; - - int status; - int trf_completed; - gboolean mstatus_valid; - struct sr_status mstatus; - gboolean abort; - gboolean overflow; -}; - -/* - * hardware setting for each capture - */ -struct DSL_setting { - uint32_t sync; - - uint16_t mode_header; // 0 - uint16_t mode; - uint16_t divider_header; // 1-2 - uint16_t div_l; - uint16_t div_h; - uint16_t count_header; // 3-4 - uint16_t cnt_l; - uint16_t cnt_h; - uint16_t trig_pos_header; // 5-6 - uint16_t tpos_l; - uint16_t tpos_h; - uint16_t trig_glb_header; // 7 - uint16_t trig_glb; - uint16_t ch_en_header; // 8 - uint16_t ch_en; - - uint16_t trig_header; // 64 - uint16_t trig_mask0[NUM_TRIGGER_STAGES]; - uint16_t trig_mask1[NUM_TRIGGER_STAGES]; - uint16_t trig_value0[NUM_TRIGGER_STAGES]; - uint16_t trig_value1[NUM_TRIGGER_STAGES]; - uint16_t trig_edge0[NUM_TRIGGER_STAGES]; - uint16_t trig_edge1[NUM_TRIGGER_STAGES]; - uint16_t trig_logic0[NUM_TRIGGER_STAGES]; - uint16_t trig_logic1[NUM_TRIGGER_STAGES]; - uint32_t trig_count[NUM_TRIGGER_STAGES]; - - uint32_t end_sync; -}; - -static const uint64_t samplerates[] = { - SR_HZ(10), - SR_HZ(20), - SR_HZ(50), - SR_HZ(100), - SR_HZ(200), - SR_HZ(500), - SR_KHZ(1), - SR_KHZ(2), - SR_KHZ(5), - SR_KHZ(10), - SR_KHZ(20), - SR_KHZ(40), - SR_KHZ(50), - SR_KHZ(100), - SR_KHZ(200), - SR_KHZ(400), - SR_KHZ(500), - SR_MHZ(1), - SR_MHZ(2), - SR_MHZ(4), - SR_MHZ(5), - SR_MHZ(10), - SR_MHZ(20), - SR_MHZ(25), - SR_MHZ(40), - SR_MHZ(50), - SR_MHZ(100), - SR_MHZ(200), - SR_MHZ(400), - SR_MHZ(500), - SR_MHZ(800), - SR_GHZ(1), - SR_GHZ(2), - SR_GHZ(5), - SR_GHZ(10), -}; - -SR_PRIV int dsl_adjust_probes(struct sr_dev_inst *sdi, int num_probes); -SR_PRIV int dsl_setup_probes(struct sr_dev_inst *sdi, int num_probes); -SR_PRIV const GSList *dsl_mode_list(const struct sr_dev_inst *sdi); -SR_PRIV void dsl_adjust_samplerate(struct DSL_context *devc); - -SR_PRIV int dsl_en_ch_num(const struct sr_dev_inst *sdi); -SR_PRIV gboolean dsl_check_conf_profile(libusb_device *dev); -SR_PRIV int dsl_configure_probes(const struct sr_dev_inst *sdi); -SR_PRIV uint64_t dsl_channel_depth(const struct sr_dev_inst *sdi); - -SR_PRIV int dsl_wr_reg(const struct sr_dev_inst *sdi, uint8_t addr, uint8_t value); -SR_PRIV int dsl_rd_reg(const struct sr_dev_inst *sdi, uint8_t addr, uint8_t *value); -SR_PRIV int dsl_wr_ext(const struct sr_dev_inst *sdi, uint8_t addr, uint8_t value); -SR_PRIV int dsl_rd_ext(const struct sr_dev_inst *sdi, unsigned char *ctx, uint16_t addr, uint8_t len); - -SR_PRIV int dsl_wr_dso(const struct sr_dev_inst *sdi, uint64_t cmd); -SR_PRIV int dsl_wr_nvm(const struct sr_dev_inst *sdi, unsigned char *ctx, uint16_t addr, uint8_t len); -SR_PRIV int dsl_rd_nvm(const struct sr_dev_inst *sdi, unsigned char *ctx, uint16_t addr, uint8_t len); - -SR_PRIV int dsl_fpga_arm(const struct sr_dev_inst *sdi); -SR_PRIV int dsl_fpga_config(struct libusb_device_handle *hdl, const char *filename); - -SR_PRIV int dsl_config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, - const struct sr_channel *ch, - const struct sr_channel_group *cg); -SR_PRIV int dsl_config_list(int key, GVariant **data, const struct sr_dev_inst *sdi, - const struct sr_channel_group *cg); - -SR_PRIV int dsl_dev_open(struct sr_dev_driver *di, struct sr_dev_inst *sdi, gboolean *fpga_done); -SR_PRIV int dsl_dev_close(struct sr_dev_inst *sdi); -SR_PRIV int dsl_dev_acquisition_stop(const struct sr_dev_inst *sdi, void *cb_data); -SR_PRIV int dsl_dev_status_get(const struct sr_dev_inst *sdi, struct sr_status *status, gboolean prg, int begin, int end); - -SR_PRIV unsigned int dsl_get_timeout(struct DSL_context *devc); -SR_PRIV int dsl_start_transfers(const struct sr_dev_inst *sdi); - -#endif +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2013 Bert Vermeulen + * Copyright (C) 2013 DreamSourceLab + * + * 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 . + */ + +#ifndef LIBDSL_HARDWARE_DSL_H +#define LIBDSL_HARDWARE_DSL_H + +#include +#include "libsigrok.h" +#include "libsigrok-internal.h" +#include "command.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#undef min +#define min(a,b) ((a)<(b)?(a):(b)) +#undef max +#define max(a,b) ((a)>(b)?(a):(b)) + +/* Message logging helpers with subsystem-specific prefix string. */ +#define LOG_PREFIX "DSL Hardware: " +#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) +#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) +#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) +#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) +#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) +#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) + +#define USB_INTERFACE 0 +#define USB_CONFIGURATION 1 +#define NUM_TRIGGER_STAGES 16 +#define NUM_SIMUL_TRANSFERS 64 + +#define DSL_REQUIRED_VERSION_MAJOR 2 +#define DSL_REQUIRED_VERSION_MINOR 0 + +/* hardware Capabilities */ +#define CAPS_MODE_LOGIC (1 << 0) +#define CAPS_MODE_ANALOG (1 << 1) +#define CAPS_MODE_DSO (1 << 2) + +#define CAPS_FEATURE_NONE 0 +// voltage threshold +#define CAPS_FEATURE_VTH (1 << 0) +// with external buffer +#define CAPS_FEATURE_BUF (1 << 1) +// pre offset control +#define CAPS_FEATURE_PREOFF (1 << 2) +// small startup eemprom +#define CAPS_FEATURE_SEEP (1 << 3) +// zero calibration ability +#define CAPS_FEATURE_ZERO (1 << 4) +// use HMCAD1511 adc chip +#define CAPS_FEATURE_HMCAD1511 (1 << 5) +// usb 3.0 +#define CAPS_FEATURE_USB30 (1 << 6) +// pogopin panel +#define CAPS_FEATURE_POGOPIN (1 << 7) +// use ADF4360-7 vco chip +#define CAPS_FEATURE_ADF4360 (1 << 8) +// 20M bandwidth limitation +#define CAPS_FEATURE_20M (1 << 8) +/* end */ + + +#define DSLOGIC_ATOMIC_BITS 6 +#define DSLOGIC_ATOMIC_SAMPLES (1 << DSLOGIC_ATOMIC_BITS) +#define DSLOGIC_ATOMIC_SIZE (1 << (DSLOGIC_ATOMIC_BITS - 3)) +#define DSLOGIC_ATOMIC_MASK (0xFFFF << DSLOGIC_ATOMIC_BITS) + +/* + * for basic configuration + */ +#define TRIG_EN_BIT 0 +#define CLK_TYPE_BIT 1 +#define CLK_EDGE_BIT 2 +#define RLE_MODE_BIT 3 +#define DSO_MODE_BIT 4 +#define HALF_MODE_BIT 5 +#define QUAR_MODE_BIT 6 +#define ANALOG_MODE_BIT 7 +#define FILTER_BIT 8 +#define INSTANT_BIT 9 +#define STRIG_MODE_BIT 11 +#define STREAM_MODE_BIT 12 +#define LPB_TEST_BIT 13 +#define EXT_TEST_BIT 14 +#define INT_TEST_BIT 15 + +#define bmNONE 0 +#define bmEEWP (1 << 0) +#define bmFORCE_RDY (1 << 1) +#define bmFORCE_STOP (1 << 2) +#define bmSCOPE_SET (1 << 3) +#define bmSCOPE_CLR (1 << 4) +#define bmBW20M_SET (1 << 5) +#define bmBW20M_CLR (1 << 6) + +/* + * packet content check + */ +#define TRIG_CHECKID 0x55555555 +#define DSO_PKTID 0xa500 + +/* + * for DSCope device + * trans: x << 8 + y + * x = vpos(coarse), each step(1024 total) indicate x(mv) at 1/20 attenuation, and x/10(mv) at 1/2 attenuation + * y = voff(fine), each step(1024 total) indicate y/100(mv) at 1/20 attenuation, adn y/1000(mv) at 1/2 attenuation + * voff: x << 10 + y + * x = vpos(coarse) default bias + * y = voff(fine) default bias + * the final offset: x+DSCOPE_CONSTANT_BIAS->vpos(coarse); y->voff(fine) + */ +#define DSCOPE_CONSTANT_BIAS 160 +#define DSCOPE_TRANS_CMULTI 10 +#define DSCOPE_TRANS_FMULTI 100.0 + +/* + * for DSCope20 device + * trans: the whole windows offset map to the offset pwm(1024 total) + * voff: offset pwm constant bias to balance circuit offset + */ +#define CALI_VGAIN_RANGE 100 + +enum LANGUAGE { + LANGUAGE_CN = 25, + LANGUAGE_EN = 31, +}; + +struct DSL_caps { + uint64_t mode_caps; + uint64_t feature_caps; + uint64_t channels; + uint64_t hw_depth; + uint64_t dso_depth; + uint8_t intest_channel; + const uint64_t *vdivs; + const uint64_t *samplerates; + uint8_t vga_id; + uint16_t default_channelmode; + uint64_t default_samplerate; + uint64_t default_samplelimit; + uint16_t default_pwmtrans; + uint16_t default_pwmmargin; + uint32_t ref_min; + uint32_t ref_max; + uint16_t default_comb_comp; +}; + +struct DSL_profile { + uint16_t vid; + uint16_t pid; + + const char *vendor; + const char *model; + const char *model_version; + + const char *firmware; + + const char *fpga_bit33; + const char *fpga_bit50; + + struct DSL_caps dev_caps; +}; + +static const uint64_t vdivs10to2000[] = { + SR_mV(10), + SR_mV(20), + SR_mV(50), + SR_mV(100), + SR_mV(200), + SR_mV(500), + SR_V(1), + SR_V(2), + 0, +}; + +static const uint64_t samplerates100[] = { + SR_HZ(10), + SR_HZ(20), + SR_HZ(50), + SR_HZ(100), + SR_HZ(200), + SR_HZ(500), + SR_KHZ(1), + SR_KHZ(2), + SR_KHZ(5), + SR_KHZ(10), + SR_KHZ(20), + SR_KHZ(40), + SR_KHZ(50), + SR_KHZ(100), + SR_KHZ(200), + SR_KHZ(400), + SR_KHZ(500), + SR_MHZ(1), + SR_MHZ(2), + SR_MHZ(4), + SR_MHZ(5), + SR_MHZ(10), + SR_MHZ(20), + SR_MHZ(25), + SR_MHZ(50), + SR_MHZ(100), + 0, +}; + +static const uint64_t samplerates400[] = { + SR_HZ(10), + SR_HZ(20), + SR_HZ(50), + SR_HZ(100), + SR_HZ(200), + SR_HZ(500), + SR_KHZ(1), + SR_KHZ(2), + SR_KHZ(5), + SR_KHZ(10), + SR_KHZ(20), + SR_KHZ(40), + SR_KHZ(50), + SR_KHZ(100), + SR_KHZ(200), + SR_KHZ(400), + SR_KHZ(500), + SR_MHZ(1), + SR_MHZ(2), + SR_MHZ(4), + SR_MHZ(5), + SR_MHZ(10), + SR_MHZ(20), + SR_MHZ(25), + SR_MHZ(50), + SR_MHZ(100), + SR_MHZ(200), + SR_MHZ(400), + 0, +}; + +static const uint64_t samplerates1000[] = { + SR_HZ(10), + SR_HZ(20), + SR_HZ(50), + SR_HZ(100), + SR_HZ(200), + SR_HZ(500), + SR_KHZ(1), + SR_KHZ(2), + SR_KHZ(5), + SR_KHZ(10), + SR_KHZ(20), + SR_KHZ(40), + SR_KHZ(50), + SR_KHZ(100), + SR_KHZ(200), + SR_KHZ(400), + SR_KHZ(500), + SR_MHZ(1), + SR_MHZ(2), + SR_MHZ(4), + SR_MHZ(5), + SR_MHZ(10), + SR_MHZ(20), + SR_MHZ(25), + SR_MHZ(50), + SR_MHZ(100), + SR_MHZ(125), + SR_MHZ(250), + SR_MHZ(500), + SR_GHZ(1), + 0, +}; + +struct DSL_vga { + uint8_t id; + uint64_t key; + uint64_t vgain; + uint16_t preoff; + uint16_t preoff_comp; +}; +static const struct DSL_vga vga_defaults[] = { + {1, 10, 0x162400, (32<<10)+558, (32<<10)+558}, + {1, 20, 0x14C000, (32<<10)+558, (32<<10)+558}, + {1, 50, 0x12E800, (32<<10)+558, (32<<10)+558}, + {1, 100, 0x118000, (32<<10)+558, (32<<10)+558}, + {1, 200, 0x102400, (32<<10)+558, (32<<10)+558}, + {1, 500, 0x2E800, (32<<10)+558, (32<<10)+558}, + {1, 1000, 0x18000, (32<<10)+558, (32<<10)+558}, + {1, 2000, 0x02400, (32<<10)+558, (32<<10)+558}, + + {2, 10, 0x1DA800, 45, 1024-920-45}, + {2, 20, 0x1A7200, 45, 1024-920-45}, + {2, 50, 0x164200, 45, 1024-920-45}, + {2, 100, 0x131800, 45, 1024-920-45}, + {2, 200, 0xBD000, 45, 1024-920-45}, + {2, 500, 0x7AD00, 45, 1024-920-45}, + {2, 1000, 0x48800, 45, 1024-920-45}, + {2, 2000, 0x12000, 45, 1024-920-45}, + + {3, 10, 0x1C5C00, 45, 1024-920-45}, + {3, 20, 0x19EB00, 45, 1024-920-45}, + {3, 50, 0x16AE00, 45, 1024-920-45}, + {3, 100, 0x143D00, 45, 1024-920-45}, + {3, 200, 0xB1000, 45, 1024-920-45}, + {3, 500, 0x7F000, 45, 1024-920-45}, + {3, 1000, 0x57200, 45, 1024-920-45}, + {3, 2000, 0x2DD00, 45, 1024-920-45}, + + {4, 10, 0x1C6C00, 45, 1024-945-45}, + {4, 20, 0x19E000, 45, 1024-945-45}, + {4, 50, 0x16A800, 45, 1024-945-45}, + {4, 100, 0x142800, 45, 1024-945-45}, + {4, 200, 0xC7F00, 45, 1024-945-45}, + {4, 500, 0x94000, 45, 1024-945-45}, + {4, 1000, 0x6CF00, 45, 1024-945-45}, + {4, 2000, 0x44F00, 45, 1024-945-45}, + + {0, 0, 0, 0, 0} +}; + +enum CHANNEL_ID { + DSL_STREAM20x16 = 0, + DSL_STREAM25x12, + DSL_STREAM50x6, + DSL_STREAM100x3, + DSL_STREAM100x16, + DSL_STREAM125x16, + DSL_STREAM250x12, + DSL_STREAM500x6, + DSL_STREAM1000x3, + + DSL_BUFFER100x16, + DSL_BUFFER200x8, + DSL_BUFFER400x4, + DSL_BUFFER500x16, + DSL_BUFFER1000x8, + + DSL_ANALOG10x2, + + DSL_DSO200x2, + DSL_DSO1000x2, +}; + +struct DSL_channels { + enum CHANNEL_ID id; + enum OPERATION_MODE mode; + enum CHANNEL_TYPE type; + gboolean stream; + uint16_t num; + uint16_t vld_num; + uint8_t unit_bits; + uint64_t min_samplerate; + uint64_t max_samplerate; + uint64_t hw_min_samplerate; + uint64_t hw_max_samplerate; + uint8_t pre_div; + const char *descr; + const char *descr_cn; +}; + +static const struct DSL_channels channel_modes[] = { + // LA Stream + {DSL_STREAM20x16, LOGIC, SR_CHANNEL_LOGIC, TRUE, 16, 16, 1, SR_KHZ(10), SR_MHZ(20), + SR_KHZ(10), SR_MHZ(100), 1, "Use 16 Channels (Max 20MHz)", "使用16个通道(最大采样率 20MHz)"}, + {DSL_STREAM25x12, LOGIC, SR_CHANNEL_LOGIC, TRUE, 16, 12, 1, SR_KHZ(10), SR_MHZ(25), + SR_KHZ(10), SR_MHZ(100), 1, "Use 12 Channels (Max 25MHz)", "使用12个通道(最大采样率 25MHz)"}, + {DSL_STREAM50x6, LOGIC, SR_CHANNEL_LOGIC, TRUE, 16, 6, 1, SR_KHZ(10), SR_MHZ(50), + SR_KHZ(10), SR_MHZ(100), 1, "Use 6 Channels (Max 50MHz)", "使用6个通道(最大采样率 50MHz)"}, + {DSL_STREAM100x3, LOGIC, SR_CHANNEL_LOGIC, TRUE, 16, 3, 1, SR_KHZ(10), SR_MHZ(100), + SR_KHZ(10), SR_MHZ(100), 1, "Use 3 Channels (Max 100MHz)", "使用3个通道(最大采样率 100MHz)"}, + {DSL_STREAM100x16, LOGIC, SR_CHANNEL_LOGIC, TRUE, 16, 16, 1, SR_KHZ(10), SR_MHZ(100), + SR_KHZ(10), SR_MHZ(500), 5, "Use 16 Channels (Max 100MHz)", "使用16个通道(最大采样率 100MHz)"}, + {DSL_STREAM125x16, LOGIC, SR_CHANNEL_LOGIC, TRUE, 16, 16, 1, SR_KHZ(10), SR_MHZ(125), + SR_KHZ(10), SR_MHZ(500), 5, "Use 16 Channels (Max 125MHz)", "使用16个通道(最大采样率 125MHz)"}, + {DSL_STREAM250x12, LOGIC, SR_CHANNEL_LOGIC, TRUE, 16, 12, 1, SR_KHZ(10), SR_MHZ(250), + SR_KHZ(10), SR_MHZ(500), 5, "Use 12 Channels (Max 250MHz)", "使用12个通道(最大采样率 250MHz)"}, + {DSL_STREAM500x6, LOGIC, SR_CHANNEL_LOGIC, TRUE, 16, 6, 1, SR_KHZ(10), SR_MHZ(500), + SR_KHZ(10), SR_MHZ(500), 5, "Use 6 Channels (Max 500MHz)", "使用6个通道(最大采样率 500MHz)"}, + {DSL_STREAM1000x3, LOGIC, SR_CHANNEL_LOGIC, TRUE, 16, 3, 1, SR_KHZ(10), SR_GHZ(1), + SR_KHZ(10), SR_MHZ(500), 5, "Use 3 Channels (Max 1GHz)", "使用3个通道(最大采样率 1GHz)"}, + + // LA Buffer + {DSL_BUFFER100x16, LOGIC, SR_CHANNEL_LOGIC, FALSE, 16, 16, 1, SR_KHZ(10), SR_MHZ(100), + SR_KHZ(10), SR_MHZ(100), 1, "Use Channels 0~15 (Max 100MHz)", "使用通道 0~15 (最大采样率 100MHz)"}, + {DSL_BUFFER200x8, LOGIC, SR_CHANNEL_LOGIC, FALSE, 8, 8, 1, SR_KHZ(10), SR_MHZ(200), + SR_KHZ(10), SR_MHZ(100), 1, "Use Channels 0~7 (Max 200MHz)", "使用通道 0~7 (最大采样率 200MHz)"}, + {DSL_BUFFER400x4, LOGIC, SR_CHANNEL_LOGIC, FALSE, 4, 4, 1, SR_KHZ(10), SR_MHZ(400), + SR_KHZ(10), SR_MHZ(100), 1, "Use Channels 0~3 (Max 400MHz)", "使用通道 0~3 (最大采样率 400MHz)"}, + {DSL_BUFFER500x16, LOGIC, SR_CHANNEL_LOGIC, FALSE, 16, 16, 1, SR_KHZ(10), SR_MHZ(500), + SR_KHZ(10), SR_MHZ(500), 5, "Use Channels 0~15 (Max 500MHz)", "使用通道 0~15 (最大采样率 500MHz)"}, + {DSL_BUFFER1000x8, LOGIC, SR_CHANNEL_LOGIC, FALSE, 8, 8, 1, SR_KHZ(10), SR_GHZ(1), + SR_KHZ(10), SR_MHZ(500), 5, "Use Channels 0~7 (Max 1GHz)", "使用通道 0~7 (最大采样率 1GHz)"}, + + // DAQ + {DSL_ANALOG10x2, ANALOG, SR_CHANNEL_ANALOG, TRUE, 2, 2, 8, SR_HZ(10), SR_MHZ(10), + SR_KHZ(10), SR_MHZ(100), 1, "Use Channels 0~1 (Max 10MHz)", "使用通道 0~1 (最大采样率 10MHz)"}, + + // OSC + {DSL_DSO200x2, DSO, SR_CHANNEL_DSO, FALSE, 2, 2, 8, SR_KHZ(10), SR_MHZ(200), + SR_KHZ(10), SR_MHZ(100), 1, "Use Channels 0~1 (Max 200MHz)", "使用通道 0~1 (最大采样率 200MHz)"}, + {DSL_DSO1000x2, DSO, SR_CHANNEL_DSO, FALSE, 2, 2, 8, SR_KHZ(10), SR_GHZ(1), + SR_KHZ(10), SR_MHZ(100), 1, "Use Channels 0~1 (Max 1GHz)", "使用通道 0~1 (最大采样率 1GHz)"} +}; + +static const struct DSL_profile supported_DSLogic[] = { + /* + * DSLogic + */ + {0x2A0E, 0x0001, "DreamSourceLab", "DSLogic", NULL, + "DSLogic.fw", + "DSLogic33.bin", + "DSLogic50.bin", + {CAPS_MODE_LOGIC, + CAPS_FEATURE_SEEP | CAPS_FEATURE_BUF, + (1 << DSL_STREAM20x16) | (1 << DSL_STREAM25x12) | (1 << DSL_STREAM50x6) | (1 << DSL_STREAM100x3) | + (1 << DSL_BUFFER100x16) | (1 << DSL_BUFFER200x8) | (1 << DSL_BUFFER400x4) | + (1 << DSL_ANALOG10x2) | + (1 << DSL_DSO200x2), + SR_MB(256), + SR_Mn(2), + DSL_BUFFER100x16, + vdivs10to2000, + samplerates400, + 0, + DSL_STREAM20x16, + SR_MHZ(1), + SR_Mn(1), + 0, + 0, + 0, + 0, + 0} + }, + + {0x2A0E, 0x0003, "DreamSourceLab", "DSLogic Pro", NULL, + "DSLogicPro.fw", + "DSLogicPro.bin", + "DSLogicPro.bin", + {CAPS_MODE_LOGIC, + CAPS_FEATURE_SEEP | CAPS_FEATURE_VTH | CAPS_FEATURE_BUF, + (1 << DSL_STREAM20x16) | (1 << DSL_STREAM25x12) | (1 << DSL_STREAM50x6) | (1 << DSL_STREAM100x3) | + (1 << DSL_BUFFER100x16) | (1 << DSL_BUFFER200x8) | (1 << DSL_BUFFER400x4), + SR_MB(256), + 0, + DSL_BUFFER100x16, + 0, + samplerates400, + 0, + DSL_STREAM20x16, + SR_MHZ(1), + SR_Mn(1), + 0, + 0, + 0, + 0, + 0} + }, + + {0x2A0E, 0x0020, "DreamSourceLab", "DSLogic PLus", NULL, + "DSLogicPlus.fw", + "DSLogicPlus.bin", + "DSLogicPlus.bin", + {CAPS_MODE_LOGIC, + CAPS_FEATURE_VTH | CAPS_FEATURE_BUF, + (1 << DSL_STREAM20x16) | (1 << DSL_STREAM25x12) | (1 << DSL_STREAM50x6) | (1 << DSL_STREAM100x3) | + (1 << DSL_BUFFER100x16) | (1 << DSL_BUFFER200x8) | (1 << DSL_BUFFER400x4), + SR_MB(256), + 0, + DSL_BUFFER100x16, + 0, + samplerates400, + 0, + DSL_STREAM20x16, + SR_MHZ(1), + SR_Mn(1), + 0, + 0, + 0, + 0, + 0} + }, + + {0x2A0E, 0x0021, "DreamSourceLab", "DSLogic Basic", NULL, + "DSLogicBasic.fw", + "DSLogicBasic.bin", + "DSLogicBasic.bin", + {CAPS_MODE_LOGIC, + CAPS_FEATURE_VTH, + (1 << DSL_STREAM20x16) | (1 << DSL_STREAM25x12) | (1 << DSL_STREAM50x6) | (1 << DSL_STREAM100x3) | + (1 << DSL_BUFFER100x16) | (1 << DSL_BUFFER200x8) | (1 << DSL_BUFFER400x4), + SR_KB(256), + 0, + DSL_STREAM20x16, + 0, + samplerates400, + 0, + DSL_STREAM20x16, + SR_MHZ(1), + SR_Mn(1), + 0, + 0, + 0, + 0, + 0} + }, + + {0x2A0E, 0x0029, "DreamSourceLab", "DSLogic U2Basic", NULL, + "DSLogicU2Basic.fw", + "DSLogicU2Basic.bin", + "DSLogicU2Basic.bin", + {CAPS_MODE_LOGIC, + CAPS_FEATURE_VTH, + (1 << DSL_STREAM20x16) | (1 << DSL_STREAM25x12) | (1 << DSL_STREAM50x6) | (1 << DSL_STREAM100x3) | + (1 << DSL_BUFFER100x16), + SR_MB(64), + 0, + DSL_STREAM20x16, + 0, + samplerates100, + 0, + DSL_STREAM20x16, + SR_MHZ(1), + SR_Mn(1), + 0, + 0, + 0, + 0, + 0} + }, + + { 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} +}; + +static const struct DSL_profile supported_DSCope[] = { + /* + * DSCope + */ + {0x2A0E, 0x0002, "DreamSourceLab", "DSCope", NULL, + "DSCope.fw", + "DSCope.bin", + "DSCope.bin", + {CAPS_MODE_ANALOG | CAPS_MODE_DSO, + CAPS_FEATURE_ZERO | CAPS_FEATURE_PREOFF | CAPS_FEATURE_SEEP | CAPS_FEATURE_BUF, + (1 << DSL_ANALOG10x2) | + (1 << DSL_DSO200x2), + SR_MB(256), + SR_Mn(2), + 0, + vdivs10to2000, + samplerates400, + 1, + DSL_DSO200x2, + SR_MHZ(100), + SR_Mn(1), + (129<<8)+167, + 1024-920, + 1, + 255, + 0} + }, + + {0x2A0E, 0x0004, "DreamSourceLab", "DSCope20", NULL, + "DSCope20.fw", + "DSCope20.bin", + "DSCope20.bin", + {CAPS_MODE_ANALOG | CAPS_MODE_DSO, + CAPS_FEATURE_ZERO | CAPS_FEATURE_SEEP | CAPS_FEATURE_BUF, + (1 << DSL_ANALOG10x2) | + (1 << DSL_DSO200x2), + SR_MB(256), + SR_Mn(2), + 0, + vdivs10to2000, + samplerates400, + 2, + DSL_DSO200x2, + SR_MHZ(100), + SR_Mn(1), + 920, + 1024-920, + 1, + 255, + 0} + }, + + {0x2A0E, 0x0022, "DreamSourceLab", "DSCope B20", NULL, + "DSCopeB20.fw", + "DSCope20.bin", + "DSCope20.bin", + {CAPS_MODE_ANALOG | CAPS_MODE_DSO, + CAPS_FEATURE_ZERO | CAPS_FEATURE_BUF, + (1 << DSL_ANALOG10x2) | + (1 << DSL_DSO200x2), + SR_MB(256), + SR_Mn(2), + 0, + vdivs10to2000, + samplerates400, + 2, + DSL_DSO200x2, + SR_MHZ(100), + SR_Mn(1), + 920, + 1024-920, + 1, + 255, + 0} + }, + + {0x2A0E, 0x0023, "DreamSourceLab", "DSCope C20", NULL, + "DSCopeC20.fw", + "DSCopeC20P.bin", + "DSCopeC20P.bin", + {CAPS_MODE_ANALOG | CAPS_MODE_DSO, + CAPS_FEATURE_ZERO | CAPS_FEATURE_BUF, + (1 << DSL_ANALOG10x2) | + (1 << DSL_DSO200x2), + SR_MB(256), + SR_Mn(2), + 0, + vdivs10to2000, + samplerates400, + 3, + DSL_DSO200x2, + SR_MHZ(100), + SR_Mn(1), + 920, + 1024-920, + 1, + 255, + 0} + }, + + + {0x2A0E, 0x0024, "DreamSourceLab", "DSCope C20P", NULL, + "DSCopeC20P.fw", + "DSCopeC20P.bin", + "DSCopeC20P.bin", + {CAPS_MODE_ANALOG | CAPS_MODE_DSO, + CAPS_FEATURE_ZERO | CAPS_FEATURE_BUF | CAPS_FEATURE_POGOPIN, + (1 << DSL_ANALOG10x2) | + (1 << DSL_DSO200x2), + SR_MB(256), + SR_Mn(2), + 0, + vdivs10to2000, + samplerates400, + 3, + DSL_DSO200x2, + SR_MHZ(100), + SR_Mn(1), + 920, + 1024-920, + 1, + 255, + 0} + }, + + {0x2A0E, 0x0025, "DreamSourceLab", "DSCope C20", NULL, + "DSCopeC20B.fw", + "DSCopeC20B.bin", + "DSCopeC20B.bin", + {CAPS_MODE_ANALOG | CAPS_MODE_DSO, + CAPS_FEATURE_ZERO, + (1 << DSL_ANALOG10x2) | + (1 << DSL_DSO200x2), + SR_KB(256), + SR_Kn(20), + 0, + vdivs10to2000, + samplerates400, + 3, + DSL_DSO200x2, + SR_MHZ(100), + SR_Kn(10), + 920, + 1024-920, + 1, + 255, + 0} + }, + + {0x2A0E, 0x0026, "DreamSourceLab", "DSCope U2B20", NULL, + "DSCopeU2B20.fw", + "DSCopeU2B20.bin", + "DSCopeU2B20.bin", + {CAPS_MODE_ANALOG | CAPS_MODE_DSO, + CAPS_FEATURE_ZERO, + (1 << DSL_ANALOG10x2) | + (1 << DSL_DSO200x2), + SR_KB(256), + SR_Kn(20), + 0, + vdivs10to2000, + samplerates400, + 4, + DSL_DSO200x2, + SR_MHZ(100), + SR_Kn(10), + 945, + 1024-945, + 10, + 245, + 22} + }, + + {0x2A0E, 0x0027, "DreamSourceLab", "DSCope U2P20", NULL, + "DSCopeU2P20.fw", + "DSCopeU2P20.bin", + "DSCopeU2P20.bin", + {CAPS_MODE_ANALOG | CAPS_MODE_DSO, + CAPS_FEATURE_ZERO | CAPS_FEATURE_BUF | CAPS_FEATURE_POGOPIN, + (1 << DSL_ANALOG10x2) | + (1 << DSL_DSO200x2), + SR_MB(256), + SR_Mn(2), + 0, + vdivs10to2000, + samplerates400, + 4, + DSL_DSO200x2, + SR_MHZ(100), + SR_Mn(1), + 945, + 1024-945, + 10, + 245, + 22} + }, + + + { 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} +}; + +static const gboolean default_ms_en[] = { + FALSE, /* DSO_MS_BEGIN */ + TRUE, /* DSO_MS_FREQ */ + FALSE, /* DSO_MS_PERD */ + TRUE, /* DSO_MS_VMAX */ + TRUE, /* DSO_MS_VMIN */ + FALSE, /* DSO_MS_VRMS */ + FALSE, /* DSO_MS_VMEA */ + FALSE, /* DSO_MS_VP2P */ +}; + +enum { + DSL_ERROR = -1, + DSL_INIT = 0, + DSL_START = 1, + DSL_READY = 2, + DSL_TRIGGERED = 3, + DSL_DATA = 4, + DSL_STOP = 5, + DSL_FINISH = 7, + DSL_ABORT = 8, +}; + +struct DSL_context { + const struct DSL_profile *profile; + /* + * Since we can't keep track of an DSL device after upgrading + * the firmware (it renumerates into a different device address + * after the upgrade) this is like a global lock. No device will open + * until a proper delay after the last device was upgraded. + */ + int64_t fw_updated; + + /* Device/capture settings */ + uint64_t cur_samplerate; + uint64_t limit_samples; + uint64_t actual_samples; + uint64_t actual_bytes; + + /* Operational settings */ + gboolean clock_type; + gboolean clock_edge; + gboolean rle_mode; + gboolean rle_support; + gboolean instant; + uint16_t op_mode; + gboolean stream; + uint8_t test_mode; + uint16_t buf_options; + enum CHANNEL_ID ch_mode; + uint16_t samplerates_min_index; + uint16_t samplerates_max_index; + uint16_t th_level; + double vth; + uint16_t filter; + uint16_t trigger_mask[NUM_TRIGGER_STAGES]; + uint16_t trigger_value[NUM_TRIGGER_STAGES]; + int trigger_stage; + uint16_t trigger_buffer[NUM_TRIGGER_STAGES]; + uint64_t timebase; + uint8_t max_height; + uint8_t trigger_channel; + uint8_t trigger_slope; + uint8_t trigger_source; + uint8_t trigger_hrate; + uint32_t trigger_hpos; + uint64_t trigger_holdoff; + uint8_t trigger_margin; + gboolean zero; + gboolean cali; + gboolean tune; + int16_t tune_index; + int zero_stage; + int zero_pcnt; + int tune_stage; + int tune_pcnt; + struct sr_channel *tune_probe; + gboolean roll; + gboolean data_lock; + uint16_t unit_pitch; + + uint64_t num_samples; + uint64_t num_bytes; + int submitted_transfers; + int empty_transfer_count; + int instant_tail_bytes; + + void *cb_data; + unsigned int num_transfers; + struct libusb_transfer **transfers; + int *usbfd; + + int pipe_fds[2]; + GIOChannel *channel; + + int status; + int trf_completed; + gboolean mstatus_valid; + struct sr_status mstatus; + gboolean abort; + gboolean overflow; + int bw_limit; + + int language; +}; + +/* + * hardware setting for each capture + */ +struct DSL_setting { + uint32_t sync; + + uint16_t mode_header; // 0 + uint16_t mode; + uint16_t divider_header; // 1-2 + uint16_t div_l; + uint16_t div_h; + uint16_t count_header; // 3-4 + uint16_t cnt_l; + uint16_t cnt_h; + uint16_t trig_pos_header; // 5-6 + uint16_t tpos_l; + uint16_t tpos_h; + uint16_t trig_glb_header; // 7 + uint16_t trig_glb; + uint16_t ch_en_header; // 8 + uint16_t ch_en; + uint16_t dso_count_header; // 9-10 + uint16_t dso_cnt_l; + uint16_t dso_cnt_h; + uint16_t misc_align; + + uint16_t trig_header; // 64 + uint16_t trig_mask0[NUM_TRIGGER_STAGES]; + uint16_t trig_mask1[NUM_TRIGGER_STAGES]; + uint16_t trig_value0[NUM_TRIGGER_STAGES]; + uint16_t trig_value1[NUM_TRIGGER_STAGES]; + uint16_t trig_edge0[NUM_TRIGGER_STAGES]; + uint16_t trig_edge1[NUM_TRIGGER_STAGES]; + uint16_t trig_logic0[NUM_TRIGGER_STAGES]; + uint16_t trig_logic1[NUM_TRIGGER_STAGES]; + uint32_t trig_count[NUM_TRIGGER_STAGES]; + + uint32_t end_sync; +}; + +struct DSL_adc_config { + uint8_t dest; + uint8_t cnt; + uint8_t delay; + uint8_t byte[4]; +}; +static const struct DSL_adc_config adc_single_ch0[] = { + {ADCC_ADDR+1, 3, 0, {0x03, 0x01, 0x00, 0x00}}, // reset & power down + {ADCC_ADDR, 4, 0, {0x00, 0x01, 0x00, 0x31}}, // 1x channel 1/1 clock + {ADCC_ADDR+1, 1, 0, {0x01, 0x00, 0x00, 0x00}}, // power up + {ADCC_ADDR, 4, 0, {0x00, 0x02, 0x02, 0x3A}}, // adc0: ch0 adc1: ch0 + {ADCC_ADDR, 4, 0, {0x00, 0x02, 0x02, 0x3B}}, // adc2: ch0 adc3: ch0 + {ADCC_ADDR, 4, 0, {0x00, 0x00, 0x00, 0x42}}, // phase_ddr: 270 deg + {ADCC_ADDR, 4, 0, {0x00, 0x34, 0x00, 0x50}}, // adc core current: -40% (lower performance) / VCM: +-700uA + {ADCC_ADDR, 4, 0, {0x00, 0x22, 0x02, 0x11}}, // lvds drive strength: 1.5mA(RSDS) + {ADCC_ADDR, 4, 0, {0x00, 0x7F, 0x00, 0x24}}, // invert all channel + {ADCC_ADDR, 4, 0, {0x00, 0x00, 0x00, 0x55}}, // full-scale range: -10% + {ADCC_ADDR, 4, 0, {0x00, 0x02, 0x00, 0x33}}, // x-gain disabled / fine-gain enabled + {ADCC_ADDR, 4, 0, {0x00, 0x00, 0x03, 0x2B}}, // coarse_gain: 3dB(1.4125x) + {0, 0, 0, {0, 0, 0, 0}} +}; +static const struct DSL_adc_config adc_single_ch3[] = { + {ADCC_ADDR+1, 3, 0, {0x03, 0x01, 0x00, 0x00}}, // reset & power down + {ADCC_ADDR, 4, 0, {0x00, 0x01, 0x00, 0x31}}, // 1x channel 1/1 clock + {ADCC_ADDR+1, 1, 0, {0x01, 0x00, 0x00, 0x00}}, // power up + {ADCC_ADDR, 4, 0, {0x00, 0x10, 0x10, 0x3A}}, // adc0: ch3 adc1: ch3 + {ADCC_ADDR, 4, 0, {0x00, 0x10, 0x10, 0x3B}}, // adc2: ch3 adc3: ch3 + {ADCC_ADDR, 4, 0, {0x00, 0x00, 0x00, 0x42}}, // phase_ddr: 270 deg + {ADCC_ADDR, 4, 0, {0x00, 0x34, 0x00, 0x50}}, // adc core current: -40% (lower performance) / VCM: +-700uA + {ADCC_ADDR, 4, 0, {0x00, 0x22, 0x02, 0x11}}, // lvds drive strength: 1.5mA(RSDS) + {ADCC_ADDR, 4, 0, {0x00, 0x00, 0x00, 0x24}}, // invert none channel + {ADCC_ADDR, 4, 0, {0x00, 0x00, 0x00, 0x55}}, // full-scale range: -10% + {ADCC_ADDR, 4, 0, {0x00, 0x02, 0x00, 0x33}}, // x-gain disabled / fine-gain enabled + {ADCC_ADDR, 4, 0, {0x00, 0x00, 0x03, 0x2B}}, // coarse_gain: 3dB(1.4125x) + {0, 0, 0, {0, 0, 0, 0}} +}; +static const struct DSL_adc_config adc_dual_ch03[] = { + {ADCC_ADDR+1, 3, 0, {0x03, 0x01, 0x00, 0x00}}, // reset & power down + {ADCC_ADDR, 4, 0, {0x00, 0x02, 0x01, 0x31}}, // 2x channel 1/2 clock + {ADCC_ADDR+1, 1, 0, {0x01, 0x00, 0x00, 0x00}}, // power up + {ADCC_ADDR, 4, 0, {0x00, 0x02, 0x02, 0x3A}}, // adc0: ch0 adc1: ch0 + {ADCC_ADDR, 4, 0, {0x00, 0x10, 0x10, 0x3B}}, // adc2: ch3 adc3: ch3 + {ADCC_ADDR, 4, 0, {0x00, 0x00, 0x00, 0x42}}, // phase_ddr: 270 deg + {ADCC_ADDR, 4, 0, {0x00, 0x34, 0x00, 0x50}}, // adc core current: -40% (lower performance) / VCM: +-700uA + {ADCC_ADDR, 4, 0, {0x00, 0x22, 0x02, 0x11}}, // lvds drive strength: 1.5mA(RSDS) + {ADCC_ADDR, 4, 0, {0x00, 0x11, 0x00, 0x24}}, // invert 0 channel + {ADCC_ADDR, 4, 0, {0x00, 0x00, 0x00, 0x55}}, // full-scale range: -10% + {ADCC_ADDR, 4, 0, {0x00, 0x02, 0x00, 0x33}}, // x-gain disabled / fine-gain enabled + {ADCC_ADDR, 4, 0, {0x00, 0x33, 0x00, 0x2B}}, // coarse_gain: 3dB(1.4125x) +// {ADCC_ADDR, 4, 0, {0x00, 0x03, 0x00, 0x33}}, // x-gain enabled / fine-gain enabled +// {ADCC_ADDR, 4, 0, {0x00, 0x11, 0x00, 0x2B}}, // coarse_gain: 1.25x + //{ADCC_ADDR, 4, 0, {0x00, 0x40, 0x00, 0x34}}, // fine_gain: 1.0077x + //{ADCC_ADDR, 4, 0, {0x00, 0x00, 0x60, 0x35}}, // fine_gain: 1.0077x + //{ADCC_ADDR, 4, 0, {0x00, 0x10, 0x00, 0x25}}, // fix pattern test + //{ADCC_ADDR, 4, 0, {0x00, 0x00, 0xab, 0x26}}, // test pattern + {0, 0, 0, {0, 0, 0, 0}} +}; +static const struct DSL_adc_config adc_init_fix[] = { + {ADCC_ADDR+1, 3, 0, {0x03, 0x01, 0x00, 0x00}}, // reset & power down + {ADCC_ADDR, 4, 0, {0x00, 0x02, 0x01, 0x31}}, // 2x channel 1/2 clock + {ADCC_ADDR+1, 1, 0, {0x01, 0x00, 0x00, 0x00}}, // power up + {ADCC_ADDR, 4, 0, {0x00, 0x02, 0x02, 0x3A}}, // adc0: ch0 adc1: ch0 + {ADCC_ADDR, 4, 0, {0x00, 0x10, 0x10, 0x3B}}, // adc2: ch3 adc3: ch3 + {ADCC_ADDR, 4, 0, {0x00, 0x00, 0x00, 0x42}}, // phase_ddr: 270 deg + {ADCC_ADDR, 4, 0, {0x00, 0x34, 0x00, 0x50}}, // adc core current: -40% (lower performance) / VCM: +-700uA + {ADCC_ADDR, 4, 0, {0x00, 0x22, 0x02, 0x11}}, // lvds drive strength: 1.5mA(RSDS) + {ADCC_ADDR, 4, 0, {0x00, 0x10, 0x00, 0x25}}, // fix pattern test + {ADCC_ADDR, 4, 0, {0x00, 0x00, 0x55, 0x26}}, // test pattern + {0, 0, 0, {0, 0, 0, 0}} +}; +static const struct DSL_adc_config adc_clk_init_1g[] = { + {ADCC_ADDR+2, 1, 0, {0x01, 0x00, 0x00, 0x00}}, // power up + {ADCC_ADDR, 4, 0, {0x01, 0x61, 0x00, 0x30}}, // + {ADCC_ADDR, 4, 0, {0x01, 0x40, 0xF1, 0x46}}, // + {ADCC_ADDR, 4, 10, {0x01, 0x62, 0x3D, 0x00}}, // + {0, 0, 0, {0, 0, 0, 0}} +}; +static const struct DSL_adc_config adc_clk_init_500m[] = { + {ADCC_ADDR+2, 1, 0, {0x01, 0x00, 0x00, 0x00}}, // power up + {ADCC_ADDR, 4, 0, {0x01, 0x61, 0x00, 0x30}}, // + {ADCC_ADDR, 4, 0, {0x01, 0x40, 0xF1, 0x46}}, // + {ADCC_ADDR, 4, 10, {0x01, 0x62, 0x3D, 0x40}}, // + {0, 0, 0, {0, 0, 0, 0}} +}; +static const struct DSL_adc_config adc_power_down[] = { + //{ADCC_ADDR+2, 1, 0, {0x00, 0x00, 0x00, 0x00}}, // ADC_CLK power down + {ADCC_ADDR+1, 1, 0, {0x00, 0x00, 0x00, 0x00}}, // ADC power down + {0, 0, 0, {0, 0, 0, 0}} +}; +static const struct DSL_adc_config adc_power_up[] = { + {ADCC_ADDR+1, 1, 0, {0x01, 0x00, 0x00, 0x00}}, // ADC power up + //{ADCC_ADDR+2, 1, 0, {0x01, 0x00, 0x00, 0x00}}, // ADC_CLK power up + {0, 0, 0, {0, 0, 0, 0}} +}; + +SR_PRIV int dsl_adjust_probes(struct sr_dev_inst *sdi, int num_probes); +SR_PRIV int dsl_setup_probes(struct sr_dev_inst *sdi, int num_probes); +SR_PRIV const GSList *dsl_mode_list(const struct sr_dev_inst *sdi); +SR_PRIV void dsl_adjust_samplerate(struct DSL_context *devc); + +SR_PRIV int dsl_en_ch_num(const struct sr_dev_inst *sdi); +SR_PRIV gboolean dsl_check_conf_profile(libusb_device *dev); +SR_PRIV int dsl_configure_probes(const struct sr_dev_inst *sdi); +SR_PRIV uint64_t dsl_channel_depth(const struct sr_dev_inst *sdi); + +SR_PRIV int dsl_wr_reg(const struct sr_dev_inst *sdi, uint8_t addr, uint8_t value); +SR_PRIV int dsl_rd_reg(const struct sr_dev_inst *sdi, uint8_t addr, uint8_t *value); +SR_PRIV int dsl_wr_ext(const struct sr_dev_inst *sdi, uint8_t addr, uint8_t value); +SR_PRIV int dsl_rd_ext(const struct sr_dev_inst *sdi, unsigned char *ctx, uint16_t addr, uint8_t len); + +SR_PRIV int dsl_wr_dso(const struct sr_dev_inst *sdi, uint64_t cmd); +SR_PRIV int dsl_wr_nvm(const struct sr_dev_inst *sdi, unsigned char *ctx, uint16_t addr, uint8_t len); +SR_PRIV int dsl_rd_nvm(const struct sr_dev_inst *sdi, unsigned char *ctx, uint16_t addr, uint8_t len); +SR_PRIV int dsl_rd_probe(const struct sr_dev_inst *sdi, unsigned char *ctx, uint16_t addr, uint8_t len); + +SR_PRIV int dsl_config_adc(const struct sr_dev_inst *sdi, const struct DSL_adc_config *config); + +SR_PRIV int dsl_fpga_arm(const struct sr_dev_inst *sdi); +SR_PRIV int dsl_fpga_config(struct libusb_device_handle *hdl, const char *filename); + +SR_PRIV int dsl_config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, + const struct sr_channel *ch, + const struct sr_channel_group *cg); +SR_PRIV int dsl_config_set(int id, GVariant *data, struct sr_dev_inst *sdi, + struct sr_channel *ch, + struct sr_channel_group *cg ); +SR_PRIV int dsl_config_list(int key, GVariant **data, const struct sr_dev_inst *sdi, + const struct sr_channel_group *cg); + +SR_PRIV int dsl_dev_open(struct sr_dev_driver *di, struct sr_dev_inst *sdi, gboolean *fpga_done); +SR_PRIV int dsl_dev_close(struct sr_dev_inst *sdi); +SR_PRIV int dsl_dev_acquisition_stop(const struct sr_dev_inst *sdi, void *cb_data); +SR_PRIV int dsl_dev_status_get(const struct sr_dev_inst *sdi, struct sr_status *status, gboolean prg, int begin, int end); + +SR_PRIV unsigned int dsl_get_timeout(const struct sr_dev_inst *sdi); +SR_PRIV int dsl_start_transfers(const struct sr_dev_inst *sdi); +SR_PRIV int dsl_header_size(const struct DSL_context *devc); + +#endif diff --git a/libsigrok4DSL/hardware/DSL/dslogic.c b/libsigrok4DSL/hardware/DSL/dslogic.c index d7fc766c..3d3dd015 100755 --- a/libsigrok4DSL/hardware/DSL/dslogic.c +++ b/libsigrok4DSL/hardware/DSL/dslogic.c @@ -1,1291 +1,1360 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2013 Bert Vermeulen - * Copyright (C) 2013 DreamSourceLab - * - * 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 . - */ - -#include "libsigrok.h" -#include "libsigrok-internal.h" - -#include "dsl.h" -#include "command.h" - - -enum { - /** Buffer mode */ - OP_BUFFER = 0, - /** Stream mode */ - OP_STREAM = 1, - /** Internal pattern test mode */ - OP_INTEST = 2, - /** External pattern test mode */ - OP_EXTEST = 3, - /** SDRAM loopback test mode */ - OP_LPTEST = 4, -}; -static const char *opmodes[] = { - "Buffer Mode", - "Stream Mode", - "Internal Test", - "External Test", - "DRAM Loopback Test", -}; - -static const char *bufoptions[] = { - "Stop immediately", - "Upload captured data", -}; - -static const char *thresholds[] = { - "1.8/2.5/3.3V Level", - "5.0V Level", -}; - -static const char *filters[] = { - "None", - "1 Sample Clock", -}; - -static const char *maxHeights[] = { - "1X", - "2X", - "3X", - "4X", - "5X", -}; - -static const int32_t hwoptions[] = { - SR_CONF_OPERATION_MODE, - SR_CONF_BUFFER_OPTIONS, - SR_CONF_THRESHOLD, - SR_CONF_FILTER, - SR_CONF_MAX_HEIGHT, - SR_CONF_RLE_SUPPORT, - SR_CONF_CLOCK_TYPE, - SR_CONF_CLOCK_EDGE, -}; - -static const int32_t hwoptions_pro[] = { - SR_CONF_OPERATION_MODE, - SR_CONF_BUFFER_OPTIONS, - SR_CONF_VTH, - SR_CONF_FILTER, - SR_CONF_MAX_HEIGHT, - SR_CONF_RLE_SUPPORT, - SR_CONF_CLOCK_TYPE, - SR_CONF_CLOCK_EDGE, -}; - -static const int32_t sessions[] = { - SR_CONF_MAX_HEIGHT, - SR_CONF_OPERATION_MODE, - SR_CONF_BUFFER_OPTIONS, - SR_CONF_CHANNEL_MODE, - SR_CONF_SAMPLERATE, - SR_CONF_LIMIT_SAMPLES, - SR_CONF_RLE_SUPPORT, - SR_CONF_CLOCK_TYPE, - SR_CONF_CLOCK_EDGE, - SR_CONF_THRESHOLD, - SR_CONF_FILTER, - SR_CONF_TRIGGER_SLOPE, - SR_CONF_TRIGGER_SOURCE, - SR_CONF_HORIZ_TRIGGERPOS, - SR_CONF_TRIGGER_HOLDOFF, - SR_CONF_TRIGGER_MARGIN, -}; - -static const int32_t sessions_pro[] = { - SR_CONF_MAX_HEIGHT, - SR_CONF_OPERATION_MODE, - SR_CONF_BUFFER_OPTIONS, - SR_CONF_CHANNEL_MODE, - SR_CONF_SAMPLERATE, - SR_CONF_LIMIT_SAMPLES, - SR_CONF_RLE_SUPPORT, - SR_CONF_CLOCK_TYPE, - SR_CONF_CLOCK_EDGE, - SR_CONF_VTH, - SR_CONF_FILTER, - SR_CONF_TRIGGER_SLOPE, - SR_CONF_TRIGGER_SOURCE, - SR_CONF_TRIGGER_CHANNEL, - SR_CONF_HORIZ_TRIGGERPOS, - SR_CONF_TRIGGER_HOLDOFF, - SR_CONF_TRIGGER_MARGIN, -}; - - -static uint16_t opmodes_show_count = 3; - -SR_PRIV struct sr_dev_driver DSLogic_driver_info; -static struct sr_dev_driver *di = &DSLogic_driver_info; - -static struct DSL_context *DSLogic_dev_new(const struct DSL_profile *prof) -{ - struct DSL_context *devc; - unsigned int i; - - if (!(devc = g_try_malloc(sizeof(struct DSL_context)))) { - sr_err("Device context malloc failed."); - return NULL; - } - - for (i = 0; i < ARRAY_SIZE(channel_modes); i++) - assert(channel_modes[i].id == i); - - devc->channel = NULL; - devc->profile = prof; - devc->fw_updated = 0; - devc->cur_samplerate = devc->profile->dev_caps.default_samplerate; - devc->limit_samples = devc->profile->dev_caps.default_samplelimit; - devc->clock_type = FALSE; - devc->clock_edge = FALSE; - devc->rle_mode = FALSE; - devc->instant = FALSE; - devc->op_mode = OP_STREAM; - devc->test_mode = SR_TEST_NONE; - devc->stream = (devc->op_mode == OP_STREAM); - devc->buf_options = SR_BUF_UPLOAD; - devc->ch_mode = devc->profile->dev_caps.default_channelmode; - devc->th_level = SR_TH_3V3; - devc->vth = 1.0; - devc->filter = SR_FILTER_NONE; - devc->timebase = 10000; - devc->trigger_slope = DSO_TRIGGER_RISING; - devc->trigger_source = DSO_TRIGGER_AUTO; - devc->trigger_hpos = 0x0; - devc->trigger_hrate = 0; - devc->trigger_holdoff = 0; - devc->zero = FALSE; - - devc->mstatus_valid = FALSE; - devc->data_lock = FALSE; - devc->max_height = 0; - devc->trigger_margin = 8; - devc->trigger_channel = 0; - - dsl_adjust_samplerate(devc); - - return devc; -} - -static int dev_clear(void) -{ - return std_dev_clear(di, NULL); -} - -static int init(struct sr_context *sr_ctx) -{ - return std_hw_init(sr_ctx, di, LOG_PREFIX); -} - -static GSList *scan(GSList *options) -{ - struct drv_context *drvc; - struct DSL_context *devc; - struct sr_dev_inst *sdi; - struct sr_usb_dev_inst *usb; - struct sr_config *src; - const struct DSL_profile *prof; - GSList *l, *devices, *conn_devices; - struct libusb_device_descriptor des; - libusb_device **devlist; - int devcnt, ret, i, j; - const char *conn; - - drvc = di->priv; - - conn = NULL; - for (l = options; l; l = l->next) { - src = l->data; - switch (src->key) { - case SR_CONF_CONN: - conn = g_variant_get_string(src->data, NULL); - break; - } - } - if (conn) - conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn); - else - conn_devices = NULL; - - /* Find all DSLogic compatible devices and upload firmware to them. */ - devices = NULL; - libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); - for (i = 0; devlist[i]; i++) { - if (conn) { - usb = NULL; - for (l = conn_devices; l; l = l->next) { - usb = l->data; - if (usb->bus == libusb_get_bus_number(devlist[i]) - && usb->address == libusb_get_device_address(devlist[i])) - break; - } - if (!l) - /* This device matched none of the ones that - * matched the conn specification. */ - continue; - } - - if ((ret = libusb_get_device_descriptor( devlist[i], &des)) != 0) { - sr_warn("Failed to get device descriptor: %s.", - libusb_error_name(ret)); - continue; - } - - prof = NULL; - for (j = 0; supported_DSLogic[j].vid; j++) { - if (des.idVendor == supported_DSLogic[j].vid && - des.idProduct == supported_DSLogic[j].pid) { - prof = &supported_DSLogic[j]; - } - } - - /* Skip if the device was not found. */ - if (!prof) - continue; - - devcnt = g_slist_length(drvc->instances); - devc = DSLogic_dev_new(prof); - if (!devc) - return NULL; - sdi = sr_dev_inst_new(channel_modes[devc->ch_mode].mode, devcnt, SR_ST_INITIALIZING, - prof->vendor, prof->model, prof->model_version); - if (!sdi) { - g_free(devc); - return NULL; - } - sdi->priv = devc; - sdi->driver = di; - - drvc->instances = g_slist_append(drvc->instances, sdi); - //devices = g_slist_append(devices, sdi); - - /* Fill in probelist according to this device's profile. */ - if (dsl_setup_probes(sdi, channel_modes[devc->ch_mode].num) != SR_OK) - return NULL; - - if (dsl_check_conf_profile(devlist[i])) { - /* Already has the firmware, so fix the new address. */ - sr_dbg("Found an DSLogic device."); - sdi->status = SR_ST_INACTIVE; - sdi->inst_type = SR_INST_USB; - sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), - libusb_get_device_address(devlist[i]), NULL); - /* only report device after firmware is ready */ - devices = g_slist_append(devices, sdi); - } else { - char *firmware; - if (!(firmware = g_try_malloc(strlen(DS_RES_PATH)+strlen(prof->firmware)+1))) { - sr_err("Firmware path malloc error!"); - return NULL; - } - strcpy(firmware, DS_RES_PATH); - strcat(firmware, prof->firmware); - if (ezusb_upload_firmware(devlist[i], USB_CONFIGURATION, - firmware) == SR_OK) - /* Store when this device's FW was updated. */ - devc->fw_updated = g_get_monotonic_time(); - else - sr_err("Firmware upload failed for " - "device %d.", devcnt); - g_free(firmware); - sdi->inst_type = SR_INST_USB; - sdi->conn = sr_usb_dev_inst_new (libusb_get_bus_number(devlist[i]), - 0xff, NULL); - } - } - libusb_free_device_list(devlist, 1); - g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free); - - return devices; -} - -static GSList *dev_list(void) -{ - return ((struct drv_context *)(di->priv))->instances; -} - -static const GSList *dev_mode_list(const struct sr_dev_inst *sdi) -{ - return dsl_mode_list(sdi); -} - -static uint64_t dso_cmd_gen(const struct sr_dev_inst *sdi, struct sr_channel* ch, int id) -{ - struct DSL_context *devc; - uint64_t cmd = 0; - int channel_cnt = 0; - GSList *l; - struct sr_channel *en_probe = ch; - - devc = sdi->priv; - - switch (id) { - case SR_CONF_PROBE_VDIV: - case SR_CONF_PROBE_EN: - case SR_CONF_TIMEBASE: - case SR_CONF_PROBE_COUPLING: - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - if (probe->enabled) { - channel_cnt += probe->index + 0x1; - en_probe = probe; - } - } - if (channel_cnt == 0) - return 0x0; - - // --VDBS - if (channel_cnt != 1) - en_probe = ch; - switch(en_probe->vdiv){ - case 5: cmd += 0x247000; break; - case 10: cmd += 0x23D000; break; - case 20: cmd += 0x22F000; break; - case 50: cmd += 0x21C800; break; - case 100: cmd += 0x20E800; break; - case 200: cmd += 0x200800; break; - case 500: cmd += 0x2F000; break; - case 1000: cmd += 0x21100; break; - case 2000: cmd += 0x13000; break; - case 5000: cmd += 0x00800; break; - default: cmd += 0x21100; break; - } - // --DC/AC - if (channel_cnt == 1) { - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - if (probe->coupling == SR_AC_COUPLING) - cmd += 0x100000; - break; - } - } else { - if(ch->coupling == SR_AC_COUPLING) - cmd += 0x100000; - } - - // --Channel - if (sdi->mode != LOGIC) { - if(channel_cnt == 1) - cmd += 0xC00000; - else if(ch->index == 0) - cmd += 0x400000; - else if(ch->index == 1) - cmd += 0x800000; - else - cmd += 0x000000; - } - - // --Header - cmd += 0x55000000; - break; - case SR_CONF_SAMPLERATE: - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - channel_cnt += probe->enabled; - } - cmd += 0x18; - uint32_t divider = (uint32_t)ceil(channel_modes[devc->ch_mode].max_samplerate * 1.0 / devc->cur_samplerate / channel_cnt); - cmd += divider << 8; - break; - case SR_CONF_HORIZ_TRIGGERPOS: - cmd += 0x20; - cmd += devc->trigger_hpos << 8; - break; - case SR_CONF_TRIGGER_SLOPE: - cmd += 0x28; - cmd += devc->trigger_slope << 8; - break; - case SR_CONF_TRIGGER_SOURCE: - cmd += 0x30; - cmd += devc->trigger_source << 8; - break; - case SR_CONF_TRIGGER_VALUE: - cmd += 0x38; - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - cmd += probe->trig_value << (8 * (probe->index + 1)); - } - break; - case SR_CONF_TRIGGER_MARGIN: - cmd += 0x40; - cmd += ((uint64_t)devc->trigger_margin << 8); - break; - case SR_CONF_TRIGGER_HOLDOFF: - cmd += 0x58; - cmd += devc->trigger_holdoff << 8; - break; - case SR_CONF_DSO_SYNC: - cmd = 0xa5a5a500; - break; - default: - cmd = 0xFFFFFFFF; - } - - return cmd; -} - -static int dso_init(const struct sr_dev_inst *sdi) -{ - int ret; - GSList *l; - - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_COUPLING)); - if (ret != SR_OK) { - sr_err("DSO set coupling of channel %d command failed!", probe->index); - return ret; - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_VDIV)); - if (ret != SR_OK) { - sr_err("Set VDIV of channel %d command failed!", probe->index); - return ret; - } - } - - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); - if (ret != SR_OK) { - sr_err("Set Sample Rate command failed!"); - return ret; - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_HORIZ_TRIGGERPOS)); - if (ret != SR_OK) { - sr_err("Set Horiz Trigger Position command failed!"); - return ret; - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_HOLDOFF)); - if (ret != SR_OK) { - sr_err("Set Trigger Holdoff Time command failed!"); - return ret; - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SLOPE)); - if (ret != SR_OK) { - sr_err("Set Trigger Slope command failed!"); - return ret; - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); - if (ret != SR_OK) { - sr_err("Set Trigger Source command failed!"); - return ret; - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_VALUE)); - if (ret != SR_OK) { - sr_err("Set Trigger Value command failed!"); - return ret; - } - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_MARGIN)); - if (ret != SR_OK) { - sr_err("Set Trigger Margin command failed!"); - return ret; - } - return ret; -} - -static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, - const struct sr_channel *ch, - const struct sr_channel_group *cg) -{ - struct DSL_context *devc = sdi->priv; - int ret; - - ret = dsl_config_get(id, data, sdi, ch, cg); - if (ret != SR_OK) { - switch (id) { - case SR_CONF_OPERATION_MODE: - if (!sdi) - return SR_ERR; - *data = g_variant_new_string(opmodes[devc->op_mode]); - break; - case SR_CONF_FILTER: - if (!sdi) - return SR_ERR; - *data = g_variant_new_string(filters[devc->filter]); - break; - case SR_CONF_RLE: - if (!sdi) - return SR_ERR; - *data = g_variant_new_boolean(devc->rle_mode); - break; - case SR_CONF_TEST: - if (!sdi) - return SR_ERR; - *data = g_variant_new_boolean(devc->test_mode != SR_TEST_NONE); - break; - case SR_CONF_ACTUAL_SAMPLES: - if (!sdi) - return SR_ERR; - *data = g_variant_new_uint64(devc->actual_samples); - break; - case SR_CONF_WAIT_UPLOAD: - if (!sdi) - return SR_ERR; - if (devc->buf_options == SR_BUF_UPLOAD && - devc->status == DSL_START) { - devc->status = DSL_ABORT; - dsl_wr_reg(sdi, CTR0_ADDR, bmFORCE_STOP); - *data = g_variant_new_boolean(TRUE); - } else { - *data = g_variant_new_boolean(FALSE); - } - break; - case SR_CONF_BUFFER_OPTIONS: - if (!sdi) - return SR_ERR; - *data = g_variant_new_string(bufoptions[devc->buf_options]); - break; - case SR_CONF_CHANNEL_MODE: - if (!sdi) - return SR_ERR; - *data = g_variant_new_string(channel_modes[devc->ch_mode].descr); - break; - case SR_CONF_MAX_HEIGHT: - if (!sdi) - return SR_ERR; - *data = g_variant_new_string(maxHeights[devc->max_height]); - break; - case SR_CONF_MAX_HEIGHT_VALUE: - if (!sdi) - return SR_ERR; - *data = g_variant_new_byte(devc->max_height); - break; - case SR_CONF_THRESHOLD: - if (!sdi) - return SR_ERR; - *data = g_variant_new_string(thresholds[devc->th_level]); - break; - case SR_CONF_VTH: - if (!sdi) - return SR_ERR; - *data = g_variant_new_double(devc->vth); - break; - case SR_CONF_STREAM: - if (!sdi) - return SR_ERR; - *data = g_variant_new_boolean(devc->stream); - break; - case SR_CONF_MAX_DSO_SAMPLERATE: - if (!sdi) - return SR_ERR; - *data = g_variant_new_uint64(channel_modes[devc->ch_mode].max_samplerate); - break; - case SR_CONF_MAX_DSO_SAMPLELIMITS: - if (!sdi) - return SR_ERR; - *data = g_variant_new_uint64(devc->profile->dev_caps.dso_depth); - break; - case SR_CONF_HW_DEPTH: - if (!sdi) - return SR_ERR; - *data = g_variant_new_uint64(dsl_channel_depth(sdi)); - break; - case SR_CONF_VLD_CH_NUM: - if (!sdi) - return SR_ERR; - *data = g_variant_new_int16(channel_modes[devc->ch_mode].num); - break; - default: - return SR_ERR_NA; - } - } - - return SR_OK; -} - -static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, - struct sr_channel *ch, - struct sr_channel_group *cg ) -{ - struct DSL_context *devc; - const char *stropt; - int ret, num_probes = 0; - struct sr_usb_dev_inst *usb; - unsigned int i; - - (void)cg; - - if (sdi->status != SR_ST_ACTIVE) { - return SR_ERR; - } - - devc = sdi->priv; - usb = sdi->conn; - - ret = SR_OK; - - if (id == SR_CONF_CLOCK_TYPE) { - devc->clock_type = g_variant_get_boolean(data); - } else if (id == SR_CONF_RLE_SUPPORT) { - devc->rle_support = g_variant_get_boolean(data); - } else if (id == SR_CONF_CLOCK_EDGE) { - devc->clock_edge = g_variant_get_boolean(data); - } else if (id == SR_CONF_LIMIT_SAMPLES) { - devc->limit_samples = g_variant_get_uint64(data); - } else if (id == SR_CONF_PROBE_VDIV) { - ch->vdiv = g_variant_get_uint64(data); - if (sdi->mode != LOGIC) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_VDIV)); - } - if (ret == SR_OK) - sr_dbg("%s: setting VDIV of channel %d to %d mv", - __func__, ch->index, ch->vdiv); - else - sr_dbg("%s: setting VDIV of channel %d to %d mv failed", - __func__, ch->index, ch->vdiv); - } else if (id == SR_CONF_PROBE_FACTOR) { - ch->vfactor = g_variant_get_uint64(data); - sr_dbg("%s: setting Factor of channel %d to %d", __func__, - ch->index, ch->vfactor); - } else if (id == SR_CONF_TIMEBASE) { - devc->timebase = g_variant_get_uint64(data); - } else if (id == SR_CONF_PROBE_COUPLING) { - ch->coupling = g_variant_get_byte(data); - if (ch->coupling == SR_GND_COUPLING) - ch->coupling = SR_DC_COUPLING; - if (sdi->mode != LOGIC) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_COUPLING)); - } - if (ret == SR_OK) - sr_dbg("%s: setting AC COUPLING of channel %d to %d", - __func__, ch->index, ch->coupling); - else - sr_dbg("%s: setting AC COUPLING of channel %d to %d failed", - __func__, ch->index, ch->coupling); - } else if (id == SR_CONF_TRIGGER_SLOPE) { - devc->trigger_slope = g_variant_get_byte(data); - if (sdi->mode == DSO) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SLOPE)); - } - if (ret == SR_OK) - sr_dbg("%s: setting DSO Trigger Slope to %d", - __func__, devc->trigger_slope); - else - sr_dbg("%s: setting DSO Trigger Slope to %d failed", - __func__, devc->trigger_slope); - } else if (id == SR_CONF_TRIGGER_VALUE) { - ch->trig_value = g_variant_get_byte(data); - if (sdi->mode == DSO) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_TRIGGER_VALUE)); - } - if (ret == SR_OK) - sr_dbg("%s: setting channel %d Trigger Value to %d", - __func__, ch->index, ch->trig_value); - else - sr_dbg("%s: setting DSO Trigger Value to %d failed", - __func__, ch->index, ch->trig_value); - } else if (id == SR_CONF_HORIZ_TRIGGERPOS) { - if (sdi->mode == DSO) { - devc->trigger_hrate = g_variant_get_byte(data); - //devc->trigger_hpos = devc->trigger_hrate * dsl_en_ch_num(sdi) * devc->limit_samples / 200.0; - /* - * devc->trigger_hpos should be updated before each acquisition - * because the samplelimits may changed - */ - devc->trigger_hpos = devc->trigger_hrate * dsl_en_ch_num(sdi) * devc->limit_samples / 200.0; - if ((ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_HORIZ_TRIGGERPOS))) == SR_OK) - sr_dbg("%s: setting DSO Horiz Trigger Position to %d", - __func__, devc->trigger_hpos); - else - sr_dbg("%s: setting DSO Horiz Trigger Position to %d failed", - __func__, devc->trigger_hpos); - } else { - devc->trigger_hpos = g_variant_get_byte(data) * devc->limit_samples / 100.0; - } - } else if (id == SR_CONF_TRIGGER_HOLDOFF) { - devc->trigger_holdoff = g_variant_get_uint64(data); - if (sdi->mode == DSO) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_HOLDOFF)); - } - if (ret == SR_OK) - sr_dbg("%s: setting Trigger Holdoff Time to %d", - __func__, devc->trigger_holdoff); - else - sr_dbg("%s: setting Trigger Holdoff Time to %d failed", - __func__, devc->trigger_holdoff); - } else if (id == SR_CONF_TRIGGER_MARGIN) { - devc->trigger_margin = g_variant_get_byte(data); - if (sdi->mode == DSO) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_MARGIN)); - } - if (ret == SR_OK) - sr_dbg("%s: setting Trigger Margin to %d", - __func__, devc->trigger_margin); - else - sr_dbg("%s: setting Trigger Margin to %d failed", - __func__, devc->trigger_margin); - } else if (id == SR_CONF_SAMPLERATE) { - if (devc->test_mode == SR_TEST_NONE) { - devc->cur_samplerate = g_variant_get_uint64(data); - if(sdi->mode != LOGIC) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); - } - } - } else if (id == SR_CONF_FILTER) { - stropt = g_variant_get_string(data, NULL); - if (!strcmp(stropt, filters[SR_FILTER_NONE])) { - devc->filter = SR_FILTER_NONE; - } else if (!strcmp(stropt, filters[SR_FILTER_1T])) { - devc->filter = SR_FILTER_1T; - } else { - ret = SR_ERR; - } - sr_dbg("%s: setting filter to %d", - __func__, devc->filter); - } else if (id == SR_CONF_RLE) { - devc->rle_mode = g_variant_get_boolean(data); - } else if (id == SR_CONF_INSTANT) { - if (sdi->mode == DSO) { - devc->instant = g_variant_get_boolean(data); - if (dsl_en_ch_num(sdi) != 0) { - if (devc->instant) - devc->limit_samples = devc->profile->dev_caps.hw_depth / dsl_en_ch_num(sdi); - else - devc->limit_samples = devc->profile->dev_caps.dso_depth / dsl_en_ch_num(sdi); - } - } - } else if (id == SR_CONF_DEVICE_MODE) { - sdi->mode = g_variant_get_int16(data); - if (sdi->mode == LOGIC) { - dsl_wr_reg(sdi, CTR0_ADDR, bmSCOPE_CLR); - for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { - if (channel_modes[i].mode == LOGIC && - devc->profile->dev_caps.channels & (1 << i)) { - devc->ch_mode = channel_modes[i].id; - num_probes = channel_modes[i].num; - devc->stream = channel_modes[i].stream; - dsl_adjust_samplerate(devc); - break; - } - } - } else if (sdi->mode == DSO) { - dsl_wr_reg(sdi, CTR0_ADDR, bmSCOPE_SET); - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_DSO_SYNC)); - if (ret != SR_OK) - sr_dbg("%s: DSO configuration sync failed", __func__); - for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { - if (channel_modes[i].mode == DSO && - devc->profile->dev_caps.channels & (1 << i)) { - devc->ch_mode = channel_modes[i].id; - num_probes = channel_modes[i].num; - devc->stream = channel_modes[i].stream; - devc->cur_samplerate = channel_modes[i].max_samplerate / num_probes; - dsl_adjust_samplerate(devc); - break; - } - } - devc->limit_samples = devc->profile->dev_caps.dso_depth / num_probes; - } else if (sdi->mode == ANALOG) { - dsl_wr_reg(sdi, CTR0_ADDR, bmSCOPE_SET); - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_DSO_SYNC)); - if (ret != SR_OK) - sr_dbg("%s: DAQ configuration sync failed", __func__); - devc->op_mode = OP_STREAM; - devc->test_mode = SR_TEST_NONE; - for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { - if (channel_modes[i].mode == ANALOG && - devc->profile->dev_caps.channels & (1 << i)) { - devc->ch_mode = channel_modes[i].id; - num_probes = channel_modes[i].num; - devc->stream = channel_modes[i].stream; - dsl_adjust_samplerate(devc); - break; - } - } - } else { - ret = SR_ERR; - } - assert(num_probes != 0); - sr_dev_probes_free(sdi); - dsl_setup_probes(sdi, num_probes); - sr_dbg("%s: setting mode to %d", __func__, sdi->mode); - if (sdi->mode != LOGIC) { - dso_init(sdi); - } - } else if (id == SR_CONF_OPERATION_MODE) { - stropt = g_variant_get_string(data, NULL); - if (sdi->mode == LOGIC) { - if (!strcmp(stropt, opmodes[OP_BUFFER]) && (devc->op_mode != OP_BUFFER)) { - devc->op_mode = OP_BUFFER; - devc->test_mode = SR_TEST_NONE; - devc->stream = FALSE; - for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { - if (channel_modes[i].mode == LOGIC && - channel_modes[i].stream == devc->stream && - devc->profile->dev_caps.channels & (1 << i)) { - devc->ch_mode = channel_modes[i].id; - break; - } - } - } else if (!strcmp(stropt, opmodes[OP_STREAM]) && (devc->op_mode != OP_STREAM)) { - devc->op_mode = OP_STREAM; - devc->test_mode = SR_TEST_NONE; - devc->stream = TRUE; - for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { - if (channel_modes[i].mode == LOGIC && - channel_modes[i].stream == devc->stream && - devc->profile->dev_caps.channels & (1 << i)) { - devc->ch_mode = channel_modes[i].id; - break; - } - } - } else if (!strcmp(stropt, opmodes[OP_INTEST]) && (devc->op_mode != OP_INTEST)) { - devc->op_mode = OP_INTEST; - devc->test_mode = SR_TEST_INTERNAL; - devc->ch_mode = devc->profile->dev_caps.intest_channel; - devc->stream = !(devc->profile->dev_caps.feature_caps & CAPS_FEATURE_BUF); - } else { - ret = SR_ERR; - } - dsl_adjust_probes(sdi, channel_modes[devc->ch_mode].num); - dsl_adjust_samplerate(devc); - if (devc->op_mode == OP_INTEST) { - devc->cur_samplerate = devc->stream ? channel_modes[devc->ch_mode].max_samplerate / 2 : - channel_modes[devc->ch_mode].max_samplerate; - devc->limit_samples = devc->stream ? devc->cur_samplerate * 3 : - devc->profile->dev_caps.hw_depth / dsl_en_ch_num(sdi); - } - } - sr_dbg("%s: setting pattern to %d", - __func__, devc->op_mode); - } else if (id == SR_CONF_BUFFER_OPTIONS) { - stropt = g_variant_get_string(data, NULL); - if (sdi->mode == LOGIC) { - if (!strcmp(stropt, bufoptions[SR_BUF_STOP])) - devc->buf_options = SR_BUF_STOP; - else if (!strcmp(stropt, bufoptions[SR_BUF_UPLOAD])) - devc->buf_options = SR_BUF_UPLOAD; - } - } else if (id == SR_CONF_CHANNEL_MODE) { - stropt = g_variant_get_string(data, NULL); - if (sdi->mode == LOGIC) { - for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { - if (!strcmp(stropt, channel_modes[i].descr)) { - devc->ch_mode = channel_modes[i].id; - break; - } - } - dsl_adjust_probes(sdi, channel_modes[devc->ch_mode].num); - dsl_adjust_samplerate(devc); - } - sr_dbg("%s: setting channel mode to %d", - __func__, devc->ch_mode); - } else if (id == SR_CONF_THRESHOLD) { - if (sdi->mode == LOGIC) { - stropt = g_variant_get_string(data, NULL); - if (strcmp(stropt, thresholds[devc->th_level])) { - if (!strcmp(stropt, thresholds[SR_TH_3V3])) { - devc->th_level = SR_TH_3V3; - } else if (!strcmp(stropt, thresholds[SR_TH_5V0])) { - devc->th_level = SR_TH_5V0; - } else { - ret = SR_ERR; - } - char *fpga_bit; - if (!(fpga_bit = g_try_malloc(strlen(DS_RES_PATH)+strlen(devc->profile->fpga_bit33)+1))) { - sr_err("fpag_bit path malloc error!"); - return SR_ERR_MALLOC; - } - strcpy(fpga_bit, DS_RES_PATH); - switch(devc->th_level) { - case SR_TH_3V3: - strcat(fpga_bit, devc->profile->fpga_bit33); - break; - case SR_TH_5V0: - strcat(fpga_bit, devc->profile->fpga_bit50); - break; - default: - return SR_ERR; - } - ret = dsl_fpga_config(usb->devhdl, fpga_bit); - g_free(fpga_bit); - if (ret != SR_OK) { - sr_err("Configure FPGA failed!"); - } - } - sr_dbg("%s: setting threshold to %d", - __func__, devc->th_level); - } - } else if (id == SR_CONF_VTH) { - devc->vth = g_variant_get_double(data); - ret = dsl_wr_reg(sdi, VTH_ADDR, (uint8_t)(devc->vth/5.0*255)); - } else if (id == SR_CONF_MAX_HEIGHT) { - stropt = g_variant_get_string(data, NULL); - for (i = 0; i < ARRAY_SIZE(maxHeights); i++) { - if (!strcmp(stropt, maxHeights[i])) { - devc->max_height = i; - break; - } - } - sr_dbg("%s: setting Signal Max Height to %d", - __func__, devc->max_height); - } else if (id == SR_CONF_PROBE_EN) { - ch->enabled = g_variant_get_boolean(data); - if (sdi->mode == DSO) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_EN)); - uint16_t channel_cnt = 0; - GSList *l; - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - channel_cnt += probe->enabled; - } - if (channel_cnt != 0) - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); - } - if (ret == SR_OK) - sr_dbg("%s: setting ENABLE of channel %d to %d", - __func__, ch->index, ch->enabled); - else - sr_dbg("%s: setting ENABLE of channel %d to %d", - __func__, ch->index, ch->enabled); - } else if (id == SR_CONF_PROBE_VPOS) { - ch->vpos = g_variant_get_double(data); - sr_dbg("%s: setting VPOS of channel %d to %lf", __func__, - ch->index, ch->vpos); - } else if (id == SR_CONF_TRIGGER_SOURCE) { - devc->trigger_source = g_variant_get_byte(data); - if (sdi->mode == DSO) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); - } - if (ret == SR_OK) - sr_dbg("%s: setting DSO Trigger Source to %d", - __func__, devc->trigger_source); - else - sr_dbg("%s: setting DSO Trigger Source to %d failed", - __func__, devc->trigger_source); - } else if (id == SR_CONF_TRIGGER_CHANNEL) { - devc->trigger_source = (g_variant_get_byte(data) << 4) + (devc->trigger_source & 0x0f); - if (sdi->mode == DSO) { - ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); - } - if (ret == SR_OK) - sr_dbg("%s: setting DSO Trigger Source to %d", - __func__, devc->trigger_source); - else - sr_dbg("%s: setting DSO Trigger Source to %d failed", - __func__, devc->trigger_source); - } else if (id == SR_CONF_STREAM) { - devc->stream = g_variant_get_boolean(data); - } else if (id == SR_CONF_PROBE_MAP_UNIT) { - ch->map_unit = g_variant_get_string(data, NULL); - } else if (id == SR_CONF_PROBE_MAP_MIN) { - ch->map_min = g_variant_get_double(data); - } else if (id == SR_CONF_PROBE_MAP_MAX) { - ch->map_max = g_variant_get_double(data); - } else { - ret = SR_ERR_NA; - } - - return ret; -} - -static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi, - const struct sr_channel_group *cg) -{ - struct DSL_context *devc; - GVariantBuilder gvb; - unsigned int i; - - (void)cg; - devc = sdi->priv; - - if (dsl_config_list(key, data, sdi, cg) == SR_OK) { - return SR_OK; - } - - switch (key) { - case SR_CONF_DEVICE_OPTIONS: - if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_VTH) - *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), - hwoptions_pro, ARRAY_SIZE(hwoptions_pro)*sizeof(int32_t), TRUE, NULL, NULL); - else - *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), - hwoptions, ARRAY_SIZE(hwoptions)*sizeof(int32_t), TRUE, NULL, NULL); - break; - case SR_CONF_DEVICE_SESSIONS: - if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_VTH) - *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), - sessions_pro, ARRAY_SIZE(sessions_pro)*sizeof(int32_t), TRUE, NULL, NULL); - else - *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), - sessions, ARRAY_SIZE(sessions)*sizeof(int32_t), TRUE, NULL, NULL); - break; - case SR_CONF_OPERATION_MODE: - *data = g_variant_new_strv(opmodes, opmodes_show_count); - break; - case SR_CONF_BUFFER_OPTIONS: - *data = g_variant_new_strv(bufoptions, ARRAY_SIZE(bufoptions)); - break; - case SR_CONF_CHANNEL_MODE: - g_variant_builder_init(&gvb, G_VARIANT_TYPE("as")); - for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { - if (channel_modes[i].stream == devc->stream && - devc->profile->dev_caps.channels & (1 << i)) { - g_variant_builder_add(&gvb, "s", channel_modes[i].descr); - if (devc->test_mode != SR_TEST_NONE) - break; - } - } - *data = g_variant_builder_end(&gvb); - break; - case SR_CONF_THRESHOLD: - *data = g_variant_new_strv(thresholds, ARRAY_SIZE(thresholds)); - break; - case SR_CONF_FILTER: - *data = g_variant_new_strv(filters, ARRAY_SIZE(filters)); - break; - case SR_CONF_MAX_HEIGHT: - *data = g_variant_new_strv(maxHeights, ARRAY_SIZE(maxHeights)); - break; - default: - return SR_ERR_NA; - } - - return SR_OK; -} - -static int dev_open(struct sr_dev_inst *sdi) -{ - gboolean fpga_done; - int ret; - struct DSL_context *devc; - - devc = sdi->priv; - - if ((ret = dsl_dev_open(di, sdi, &fpga_done)) == SR_OK) { - // set threshold - ret = dsl_wr_reg(sdi, VTH_ADDR, (uint8_t)(devc->vth/5.0*255)); - } - - return ret; -} - -static int dev_close(struct sr_dev_inst *sdi) -{ - int ret; - ret = dsl_dev_close(sdi); - return ret; -} - -static int cleanup(void) -{ - int ret; - struct drv_context *drvc; - - if (!(drvc = di->priv)) - return SR_OK; - - ret = dev_clear(); - - g_free(drvc); - di->priv = NULL; - - return ret; -} - -static void remove_sources(struct DSL_context *devc) -{ - int i; - sr_info("%s: remove fds from polling", __func__); - /* Remove fds from polling. */ - for (i = 0; devc->usbfd[i] != -1; i++) - sr_source_remove(devc->usbfd[i]); - g_free(devc->usbfd); -} - -static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) -{ - int completed = 0; - struct timeval tv; - struct drv_context *drvc; - struct DSL_context *devc; - struct sr_usb_dev_inst *usb; - struct ctl_rd_cmd rd_cmd; - uint8_t hw_info; - int ret; - - (void)fd; - (void)revents; - - drvc = di->priv; - devc = sdi->priv; - usb = sdi->conn; - - tv.tv_sec = tv.tv_usec = 0; - libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv, &completed); - - // overflow check - if (devc->stream && devc->trf_completed) { - rd_cmd.header.dest = DSL_CTL_HW_STATUS; - rd_cmd.header.size = 1; - hw_info = 0; - rd_cmd.data = &hw_info; - if ((ret = command_ctl_rd(usb->devhdl, rd_cmd)) != SR_OK) - sr_err("Failed to get hardware infos."); - else - devc->overflow = (hw_info & bmSYS_OVERFLOW) != 0; - } - - if (devc->status == DSL_FINISH) { - /* Remove polling */ - remove_sources(devc); - } - - devc->trf_completed = 0; - return TRUE; -} - -static int dev_acquisition_start(struct sr_dev_inst *sdi, void *cb_data) -{ - (void)cb_data; - - struct DSL_context *devc; - struct sr_usb_dev_inst *usb; - struct drv_context *drvc; - const struct libusb_pollfd **lupfd; - unsigned int i; - int ret; - struct ctl_wr_cmd wr_cmd; - - if (sdi->status != SR_ST_ACTIVE) - return SR_ERR_DEV_CLOSED; - - drvc = di->priv; - devc = sdi->priv; - usb = sdi->conn; - - //devc->cb_data = cb_data; - devc->cb_data = sdi; - devc->num_samples = 0; - devc->num_bytes = 0; - devc->empty_transfer_count = 0; - devc->status = DSL_INIT; - devc->num_transfers = 0; - devc->submitted_transfers = 0; - if (sdi->mode != LOGIC) - devc->actual_samples = (devc->limit_samples + 1023) & ~1023; - else - devc->actual_samples = devc->limit_samples; - devc->actual_bytes = devc->actual_samples / DSLOGIC_ATOMIC_SAMPLES * dsl_en_ch_num(sdi) * DSLOGIC_ATOMIC_SIZE; - devc->abort = FALSE; - devc->mstatus_valid = FALSE; - devc->overflow = FALSE; - - /* Configures devc->trigger_* and devc->sample_wide */ - if (dsl_configure_probes(sdi) != SR_OK) { - sr_err("%s: Failed to configure probes.", __func__); - return SR_ERR; - } - - /* Stop Previous GPIF acquisition */ - wr_cmd.header.dest = DSL_CTL_STOP; - wr_cmd.header.size = 0; - if ((ret = command_ctl_wr(usb->devhdl, wr_cmd)) != SR_OK) { - sr_err("%s: Stop DSLogic acquisition failed!", __func__); - return ret; - } else { - sr_info("%s: Stop Previous DSLogic acquisition!", __func__); - } - - /* Setting FPGA before acquisition start*/ - if ((ret = dsl_fpga_arm(sdi)) != SR_OK) { - sr_err("%s: Arm FPGA failed!", __func__); - return ret; - } - - /* - * settings must be updated before acquisition - */ - if (sdi->mode == DSO) { - devc->trigger_hpos = devc->trigger_hrate * dsl_en_ch_num(sdi) * devc->limit_samples / 200.0; - if ((ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_HORIZ_TRIGGERPOS))) == SR_OK) - sr_dbg("%s: setting DSO Horiz Trigger Position to %d", - __func__, devc->trigger_hpos); - else - sr_dbg("%s: setting DSO Horiz Trigger Position to %d failed", - __func__, devc->trigger_hpos); - } - - /* setup and submit usb transfer */ - if ((ret = dsl_start_transfers(devc->cb_data)) != SR_OK) { - sr_err("%s: Could not submit usb transfer" - "(%d)%d", __func__, ret, errno); - return ret; - } - - /* setup callback function for data transfer */ - lupfd = libusb_get_pollfds(drvc->sr_ctx->libusb_ctx); - for (i = 0; lupfd[i]; i++); - if (!(devc->usbfd = g_try_malloc(sizeof(struct libusb_pollfd) * (i + 1)))) - return SR_ERR; - for (i = 0; lupfd[i]; i++) { - sr_source_add(lupfd[i]->fd, lupfd[i]->events, - dsl_get_timeout(devc), receive_data, sdi); - devc->usbfd[i] = lupfd[i]->fd; - } - devc->usbfd[i] = -1; - free(lupfd); - - wr_cmd.header.dest = DSL_CTL_START; - wr_cmd.header.size = 0; - if ((ret = command_ctl_wr(usb->devhdl, wr_cmd)) != SR_OK) { - devc->status = DSL_ERROR; - devc->abort = TRUE; - return ret; - } - devc->status = DSL_START; - - /* Send header packet to the session bus. */ - //std_session_send_df_header(cb_data, LOG_PREFIX); - std_session_send_df_header(sdi, LOG_PREFIX); - - return SR_OK; -} - -static int dev_acquisition_stop(const struct sr_dev_inst *sdi, void *cb_data) -{ - int ret = dsl_dev_acquisition_stop(sdi, cb_data); - return ret; -} - -static int dev_status_get(const struct sr_dev_inst *sdi, struct sr_status *status, gboolean prg, int begin, int end) -{ - int ret = dsl_dev_status_get(sdi, status, prg, begin, end); - return ret; -} - -SR_PRIV struct sr_dev_driver DSLogic_driver_info = { - .name = "DSLogic", - .longname = "DSLogic (generic driver for DSLogic LA)", - .api_version = 1, - .init = init, - .cleanup = cleanup, - .scan = scan, - .dev_list = dev_list, - .dev_mode_list = dev_mode_list, - .dev_clear = dev_clear, - .config_get = config_get, - .config_set = config_set, - .config_list = config_list, - .dev_open = dev_open, - .dev_close = dev_close, - .dev_status_get = dev_status_get, - .dev_acquisition_start = dev_acquisition_start, - .dev_acquisition_stop = dev_acquisition_stop, - .priv = NULL, -}; +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2013 Bert Vermeulen + * Copyright (C) 2013 DreamSourceLab + * + * 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 . + */ + +#include "libsigrok.h" +#include "libsigrok-internal.h" + +#include "dsl.h" +#include "command.h" + + +enum { + /** Buffer mode */ + OP_BUFFER = 0, + /** Stream mode */ + OP_STREAM = 1, + /** Internal pattern test mode */ + OP_INTEST = 2, + /** External pattern test mode */ + OP_EXTEST = 3, + /** SDRAM loopback test mode */ + OP_LPTEST = 4, +}; + +static const char *opmodes_cn[] = { + "Buffer模式", + "Stream模式", + "内部测试", + "外部测试", + "内存回环测试", +}; + +static const char *bufoptions_cn[] = { + "立即停止", + "上传已采集的数据", +}; + +static const char *thresholds_cn[] = { + "1.8/2.5/3.3V Level", + "5.0V Level", +}; + +static const char *filters_cn[] = { + "无", + "1个采样周期", +}; + +static const char *opmodes[] = { + "Buffer Mode", + "Stream Mode", + "Internal Test", + "External Test", + "DRAM Loopback Test", +}; + +static const char *bufoptions[] = { + "Stop immediately", + "Upload captured data", +}; + +static const char *thresholds[] = { + "1.8/2.5/3.3V Level", + "5.0V Level", +}; + +static const char *filters[] = { + "None", + "1 Sample Clock", +}; + +static const char *maxHeights[] = { + "1X", + "2X", + "3X", + "4X", + "5X", +}; + +static const int32_t hwoptions[] = { + SR_CONF_OPERATION_MODE, + SR_CONF_BUFFER_OPTIONS, + SR_CONF_THRESHOLD, + SR_CONF_FILTER, + SR_CONF_MAX_HEIGHT, + SR_CONF_RLE_SUPPORT, + SR_CONF_CLOCK_TYPE, + SR_CONF_CLOCK_EDGE, +}; + +static const int32_t hwoptions_pro[] = { + SR_CONF_OPERATION_MODE, + SR_CONF_BUFFER_OPTIONS, + SR_CONF_VTH, + SR_CONF_FILTER, + SR_CONF_MAX_HEIGHT, + SR_CONF_RLE_SUPPORT, + SR_CONF_CLOCK_TYPE, + SR_CONF_CLOCK_EDGE, +}; + +static const int32_t sessions[] = { + SR_CONF_MAX_HEIGHT, + SR_CONF_OPERATION_MODE, + SR_CONF_BUFFER_OPTIONS, + SR_CONF_CHANNEL_MODE, + SR_CONF_SAMPLERATE, + SR_CONF_LIMIT_SAMPLES, + SR_CONF_RLE_SUPPORT, + SR_CONF_CLOCK_TYPE, + SR_CONF_CLOCK_EDGE, + SR_CONF_THRESHOLD, + SR_CONF_FILTER, + SR_CONF_TRIGGER_SLOPE, + SR_CONF_TRIGGER_SOURCE, + SR_CONF_HORIZ_TRIGGERPOS, + SR_CONF_TRIGGER_HOLDOFF, + SR_CONF_TRIGGER_MARGIN, +}; + +static const int32_t sessions_pro[] = { + SR_CONF_MAX_HEIGHT, + SR_CONF_OPERATION_MODE, + SR_CONF_BUFFER_OPTIONS, + SR_CONF_CHANNEL_MODE, + SR_CONF_SAMPLERATE, + SR_CONF_LIMIT_SAMPLES, + SR_CONF_RLE_SUPPORT, + SR_CONF_CLOCK_TYPE, + SR_CONF_CLOCK_EDGE, + SR_CONF_VTH, + SR_CONF_FILTER, + SR_CONF_TRIGGER_SLOPE, + SR_CONF_TRIGGER_SOURCE, + SR_CONF_TRIGGER_CHANNEL, + SR_CONF_HORIZ_TRIGGERPOS, + SR_CONF_TRIGGER_HOLDOFF, + SR_CONF_TRIGGER_MARGIN, +}; + + +static uint16_t opmodes_show_count = 3; + +SR_PRIV struct sr_dev_driver DSLogic_driver_info; +static struct sr_dev_driver *di = &DSLogic_driver_info; + +static const char ** get_opmodes(struct DSL_context *devc) +{ + if (devc->language == LANGUAGE_CN) + return opmodes_cn; + else + return opmodes; +} + +static const char ** get_bufoptions(struct DSL_context *devc) +{ + if (devc->language == LANGUAGE_CN) + return bufoptions_cn; + else + return bufoptions; +} + +static const char ** get_thresholds(struct DSL_context *devc) +{ + if (devc->language == LANGUAGE_CN) + return thresholds_cn; + else + return thresholds; +} + +static const char ** get_filters(struct DSL_context *devc) +{ + if (devc->language == LANGUAGE_CN) + return filters_cn; + else + return filters; +} + +static struct DSL_context *DSLogic_dev_new(const struct DSL_profile *prof) +{ + struct DSL_context *devc; + unsigned int i; + + if (!(devc = g_try_malloc(sizeof(struct DSL_context)))) { + sr_err("Device context malloc failed."); + return NULL; + } + + for (i = 0; i < ARRAY_SIZE(channel_modes); i++) + assert(channel_modes[i].id == i); + + devc->channel = NULL; + devc->profile = prof; + devc->fw_updated = 0; + devc->cur_samplerate = devc->profile->dev_caps.default_samplerate; + devc->limit_samples = devc->profile->dev_caps.default_samplelimit; + devc->clock_type = FALSE; + devc->clock_edge = FALSE; + devc->rle_mode = FALSE; + devc->instant = FALSE; + devc->op_mode = OP_STREAM; + devc->test_mode = SR_TEST_NONE; + devc->ch_mode = devc->profile->dev_caps.default_channelmode; + devc->stream = (devc->op_mode == OP_STREAM); + devc->buf_options = SR_BUF_UPLOAD; + devc->th_level = SR_TH_3V3; + devc->vth = 1.0; + devc->filter = SR_FILTER_NONE; + devc->timebase = 10000; + devc->trigger_slope = DSO_TRIGGER_RISING; + devc->trigger_source = DSO_TRIGGER_AUTO; + devc->trigger_hpos = 0x0; + devc->trigger_hrate = 0; + devc->trigger_holdoff = 0; + devc->zero = FALSE; + devc->status = DSL_FINISH; + + devc->mstatus_valid = FALSE; + devc->data_lock = FALSE; + devc->max_height = 0; + devc->trigger_margin = 8; + devc->trigger_channel = 0; + + dsl_adjust_samplerate(devc); + + return devc; +} + +static int dev_clear(void) +{ + return std_dev_clear(di, NULL); +} + +static int init(struct sr_context *sr_ctx) +{ + return std_hw_init(sr_ctx, di, LOG_PREFIX); +} + +static GSList *scan(GSList *options) +{ + struct drv_context *drvc; + struct DSL_context *devc; + struct sr_dev_inst *sdi; + struct sr_usb_dev_inst *usb; + struct sr_config *src; + const struct DSL_profile *prof; + GSList *l, *devices, *conn_devices; + struct libusb_device_descriptor des; + libusb_device **devlist; + int devcnt, ret, i, j; + const char *conn; + + drvc = di->priv; + + conn = NULL; + for (l = options; l; l = l->next) { + src = l->data; + switch (src->key) { + case SR_CONF_CONN: + conn = g_variant_get_string(src->data, NULL); + break; + } + } + if (conn) + conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn); + else + conn_devices = NULL; + + /* Find all DSLogic compatible devices and upload firmware to them. */ + devices = NULL; + libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); + for (i = 0; devlist[i]; i++) { + if (conn) { + usb = NULL; + for (l = conn_devices; l; l = l->next) { + usb = l->data; + if (usb->bus == libusb_get_bus_number(devlist[i]) + && usb->address == libusb_get_device_address(devlist[i])) + break; + } + if (!l) + /* This device matched none of the ones that + * matched the conn specification. */ + continue; + } + + if ((ret = libusb_get_device_descriptor( devlist[i], &des)) != 0) { + sr_warn("Failed to get device descriptor: %s.", + libusb_error_name(ret)); + continue; + } + + prof = NULL; + for (j = 0; supported_DSLogic[j].vid; j++) { + if (des.idVendor == supported_DSLogic[j].vid && + des.idProduct == supported_DSLogic[j].pid) { + prof = &supported_DSLogic[j]; + } + } + + /* Skip if the device was not found. */ + if (!prof) + continue; + + devcnt = g_slist_length(drvc->instances); + devc = DSLogic_dev_new(prof); + if (!devc) + return NULL; + sdi = sr_dev_inst_new(channel_modes[devc->ch_mode].mode, devcnt, SR_ST_INITIALIZING, + prof->vendor, prof->model, prof->model_version); + if (!sdi) { + g_free(devc); + return NULL; + } + sdi->priv = devc; + sdi->driver = di; + + drvc->instances = g_slist_append(drvc->instances, sdi); + //devices = g_slist_append(devices, sdi); + + /* Fill in probelist according to this device's profile. */ + if (dsl_setup_probes(sdi, channel_modes[devc->ch_mode].num) != SR_OK) + return NULL; + + if (dsl_check_conf_profile(devlist[i])) { + /* Already has the firmware, so fix the new address. */ + sr_dbg("Found an DSLogic device."); + sdi->status = SR_ST_INACTIVE; + sdi->inst_type = SR_INST_USB; + sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), + libusb_get_device_address(devlist[i]), NULL); + /* only report device after firmware is ready */ + devices = g_slist_append(devices, sdi); + } else { + char *firmware; + if (!(firmware = g_try_malloc(strlen(DS_RES_PATH)+strlen(prof->firmware)+1))) { + sr_err("Firmware path malloc error!"); + return NULL; + } + strcpy(firmware, DS_RES_PATH); + strcat(firmware, prof->firmware); + if (ezusb_upload_firmware(devlist[i], USB_CONFIGURATION, + firmware) == SR_OK) + /* Store when this device's FW was updated. */ + devc->fw_updated = g_get_monotonic_time(); + else + sr_err("Firmware upload failed for " + "device %d.", devcnt); + g_free(firmware); + sdi->inst_type = SR_INST_USB; + sdi->conn = sr_usb_dev_inst_new (libusb_get_bus_number(devlist[i]), + 0xff, NULL); + } + } + libusb_free_device_list(devlist, 1); + g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free); + + return devices; +} + +static GSList *dev_list(void) +{ + return ((struct drv_context *)(di->priv))->instances; +} + +static const GSList *dev_mode_list(const struct sr_dev_inst *sdi) +{ + return dsl_mode_list(sdi); +} + +static uint64_t dso_cmd_gen(const struct sr_dev_inst *sdi, struct sr_channel* ch, int id) +{ + struct DSL_context *devc; + uint64_t cmd = 0; + int channel_cnt = 0; + GSList *l; + struct sr_channel *en_probe = ch; + + devc = sdi->priv; + + switch (id) { + case SR_CONF_PROBE_VDIV: + case SR_CONF_PROBE_EN: + case SR_CONF_TIMEBASE: + case SR_CONF_PROBE_COUPLING: + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + if (probe->enabled) { + channel_cnt += probe->index + 0x1; + en_probe = probe; + } + } + if (channel_cnt == 0) + return 0x0; + + // --VDBS + if (channel_cnt != 1) + en_probe = ch; + switch(en_probe->vdiv){ + case 5: cmd += 0x247000; break; + case 10: cmd += 0x23D000; break; + case 20: cmd += 0x22F000; break; + case 50: cmd += 0x21C800; break; + case 100: cmd += 0x20E800; break; + case 200: cmd += 0x200800; break; + case 500: cmd += 0x2F000; break; + case 1000: cmd += 0x21100; break; + case 2000: cmd += 0x13000; break; + case 5000: cmd += 0x00800; break; + default: cmd += 0x21100; break; + } + // --DC/AC + if (channel_cnt == 1) { + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + if (probe->coupling == SR_AC_COUPLING) + cmd += 0x100000; + break; + } + } else { + if(ch->coupling == SR_AC_COUPLING) + cmd += 0x100000; + } + + // --Channel + if (sdi->mode != LOGIC) { + if(channel_cnt == 1) + cmd += 0xC00000; + else if(ch->index == 0) + cmd += 0x400000; + else if(ch->index == 1) + cmd += 0x800000; + else + cmd += 0x000000; + } + + // --Header + cmd += 0x55000000; + break; + case SR_CONF_SAMPLERATE: + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + channel_cnt += probe->enabled; + } + cmd += 0x18; + uint32_t divider = (uint32_t)ceil(channel_modes[devc->ch_mode].max_samplerate * 1.0 / devc->cur_samplerate / channel_cnt); + cmd += divider << 8; + break; + case SR_CONF_HORIZ_TRIGGERPOS: + cmd += 0x20; + cmd += devc->trigger_hpos << 8; + break; + case SR_CONF_TRIGGER_SLOPE: + cmd += 0x28; + cmd += devc->trigger_slope << 8; + break; + case SR_CONF_TRIGGER_SOURCE: + cmd += 0x30; + cmd += devc->trigger_source << 8; + break; + case SR_CONF_TRIGGER_VALUE: + cmd += 0x38; + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + cmd += probe->trig_value << (8 * (probe->index + 1)); + } + break; + case SR_CONF_TRIGGER_MARGIN: + cmd += 0x40; + cmd += ((uint64_t)devc->trigger_margin << 8); + break; + case SR_CONF_TRIGGER_HOLDOFF: + cmd += 0x58; + cmd += devc->trigger_holdoff << 8; + break; + case SR_CONF_DSO_SYNC: + cmd = 0xa5a5a500; + break; + default: + cmd = 0xFFFFFFFF; + } + + return cmd; +} + +static int dso_init(const struct sr_dev_inst *sdi) +{ + int ret; + GSList *l; + + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_COUPLING)); + if (ret != SR_OK) { + sr_err("DSO set coupling of channel %d command failed!", probe->index); + return ret; + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, probe, SR_CONF_PROBE_VDIV)); + if (ret != SR_OK) { + sr_err("Set VDIV of channel %d command failed!", probe->index); + return ret; + } + } + + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); + if (ret != SR_OK) { + sr_err("Set Sample Rate command failed!"); + return ret; + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_HORIZ_TRIGGERPOS)); + if (ret != SR_OK) { + sr_err("Set Horiz Trigger Position command failed!"); + return ret; + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_HOLDOFF)); + if (ret != SR_OK) { + sr_err("Set Trigger Holdoff Time command failed!"); + return ret; + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SLOPE)); + if (ret != SR_OK) { + sr_err("Set Trigger Slope command failed!"); + return ret; + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); + if (ret != SR_OK) { + sr_err("Set Trigger Source command failed!"); + return ret; + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_VALUE)); + if (ret != SR_OK) { + sr_err("Set Trigger Value command failed!"); + return ret; + } + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_MARGIN)); + if (ret != SR_OK) { + sr_err("Set Trigger Margin command failed!"); + return ret; + } + return ret; +} + +static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, + const struct sr_channel *ch, + const struct sr_channel_group *cg) +{ + struct DSL_context *devc = sdi->priv; + int ret; + + ret = dsl_config_get(id, data, sdi, ch, cg); + if (ret != SR_OK) { + switch (id) { + case SR_CONF_OPERATION_MODE: + if (!sdi) + return SR_ERR; + *data = g_variant_new_string(get_opmodes(devc)[devc->op_mode]); + break; + case SR_CONF_FILTER: + if (!sdi) + return SR_ERR; + *data = g_variant_new_string(get_filters(devc)[devc->filter]); + break; + case SR_CONF_RLE: + if (!sdi) + return SR_ERR; + *data = g_variant_new_boolean(devc->rle_mode); + break; + case SR_CONF_TEST: + if (!sdi) + return SR_ERR; + *data = g_variant_new_boolean(devc->test_mode != SR_TEST_NONE); + break; + case SR_CONF_WAIT_UPLOAD: + if (!sdi) + return SR_ERR; + if (devc->buf_options == SR_BUF_UPLOAD && + devc->status == DSL_START) { + devc->status = DSL_ABORT; + dsl_wr_reg(sdi, CTR0_ADDR, bmFORCE_STOP); + *data = g_variant_new_boolean(TRUE); + } else { + *data = g_variant_new_boolean(FALSE); + } + break; + case SR_CONF_BUFFER_OPTIONS: + if (!sdi) + return SR_ERR; + *data = g_variant_new_string(get_bufoptions(devc)[devc->buf_options]); + break; + case SR_CONF_CHANNEL_MODE: + if (!sdi) + return SR_ERR; + if (devc->language == LANGUAGE_CN) + *data = g_variant_new_string(channel_modes[devc->ch_mode].descr_cn); + else + *data = g_variant_new_string(channel_modes[devc->ch_mode].descr); + break; + case SR_CONF_MAX_HEIGHT: + if (!sdi) + return SR_ERR; + *data = g_variant_new_string(maxHeights[devc->max_height]); + break; + case SR_CONF_MAX_HEIGHT_VALUE: + if (!sdi) + return SR_ERR; + *data = g_variant_new_byte(devc->max_height); + break; + case SR_CONF_THRESHOLD: + if (!sdi) + return SR_ERR; + *data = g_variant_new_string(get_thresholds(devc)[devc->th_level]); + break; + case SR_CONF_VTH: + if (!sdi) + return SR_ERR; + *data = g_variant_new_double(devc->vth); + break; + case SR_CONF_STREAM: + if (!sdi) + return SR_ERR; + *data = g_variant_new_boolean(devc->stream); + break; + case SR_CONF_MAX_DSO_SAMPLERATE: + if (!sdi) + return SR_ERR; + *data = g_variant_new_uint64(channel_modes[devc->ch_mode].max_samplerate); + break; + case SR_CONF_MAX_DSO_SAMPLELIMITS: + if (!sdi) + return SR_ERR; + *data = g_variant_new_uint64(devc->profile->dev_caps.dso_depth); + break; + case SR_CONF_HW_DEPTH: + if (!sdi) + return SR_ERR; + *data = g_variant_new_uint64(dsl_channel_depth(sdi)); + break; + case SR_CONF_VLD_CH_NUM: + if (!sdi) + return SR_ERR; + *data = g_variant_new_int16(channel_modes[devc->ch_mode].vld_num); + break; + default: + return SR_ERR_NA; + } + } + + return SR_OK; +} + +static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, + struct sr_channel *ch, + struct sr_channel_group *cg ) +{ + struct DSL_context *devc; + const char *stropt; + int ret, num_probes = 0; + struct sr_usb_dev_inst *usb; + unsigned int i; + + (void)cg; + + if (sdi->status != SR_ST_ACTIVE) { + return SR_ERR; + } + + devc = sdi->priv; + usb = sdi->conn; + + ret = dsl_config_set(id, data, sdi, ch, cg); + if (ret == SR_OK) + return ret; + + ret = SR_OK; + if (id == SR_CONF_CLOCK_TYPE) { + devc->clock_type = g_variant_get_boolean(data); + } else if (id == SR_CONF_RLE_SUPPORT) { + devc->rle_support = g_variant_get_boolean(data); + } else if (id == SR_CONF_CLOCK_EDGE) { + devc->clock_edge = g_variant_get_boolean(data); + } else if (id == SR_CONF_LIMIT_SAMPLES) { + devc->limit_samples = g_variant_get_uint64(data); + } else if (id == SR_CONF_PROBE_VDIV) { + ch->vdiv = g_variant_get_uint64(data); + if (sdi->mode != LOGIC) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_VDIV)); + } + if (ret == SR_OK) + sr_dbg("%s: setting VDIV of channel %d to %d mv", + __func__, ch->index, ch->vdiv); + else + sr_dbg("%s: setting VDIV of channel %d to %d mv failed", + __func__, ch->index, ch->vdiv); + } else if (id == SR_CONF_PROBE_FACTOR) { + ch->vfactor = g_variant_get_uint64(data); + sr_dbg("%s: setting Factor of channel %d to %d", __func__, + ch->index, ch->vfactor); + } else if (id == SR_CONF_TIMEBASE) { + devc->timebase = g_variant_get_uint64(data); + } else if (id == SR_CONF_PROBE_COUPLING) { + ch->coupling = g_variant_get_byte(data); + if (ch->coupling == SR_GND_COUPLING) + ch->coupling = SR_DC_COUPLING; + if (sdi->mode != LOGIC) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_COUPLING)); + } + if (ret == SR_OK) + sr_dbg("%s: setting AC COUPLING of channel %d to %d", + __func__, ch->index, ch->coupling); + else + sr_dbg("%s: setting AC COUPLING of channel %d to %d failed", + __func__, ch->index, ch->coupling); + } else if (id == SR_CONF_TRIGGER_SLOPE) { + devc->trigger_slope = g_variant_get_byte(data); + if (sdi->mode == DSO) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SLOPE)); + } + if (ret == SR_OK) + sr_dbg("%s: setting DSO Trigger Slope to %d", + __func__, devc->trigger_slope); + else + sr_dbg("%s: setting DSO Trigger Slope to %d failed", + __func__, devc->trigger_slope); + } else if (id == SR_CONF_TRIGGER_VALUE) { + ch->trig_value = g_variant_get_byte(data); + if (sdi->mode == DSO) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_TRIGGER_VALUE)); + } + if (ret == SR_OK) + sr_dbg("%s: setting channel %d Trigger Value to %d", + __func__, ch->index, ch->trig_value); + else + sr_dbg("%s: setting DSO Trigger Value to %d failed", + __func__, ch->index, ch->trig_value); + } else if (id == SR_CONF_HORIZ_TRIGGERPOS) { + if (sdi->mode == DSO) { + devc->trigger_hrate = g_variant_get_byte(data); + //devc->trigger_hpos = devc->trigger_hrate * dsl_en_ch_num(sdi) * devc->limit_samples / 200.0; + /* + * devc->trigger_hpos should be updated before each acquisition + * because the samplelimits may changed + */ + devc->trigger_hpos = devc->trigger_hrate * dsl_en_ch_num(sdi) * devc->limit_samples / 200.0; + if ((ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_HORIZ_TRIGGERPOS))) == SR_OK) + sr_dbg("%s: setting DSO Horiz Trigger Position to %d", + __func__, devc->trigger_hpos); + else + sr_dbg("%s: setting DSO Horiz Trigger Position to %d failed", + __func__, devc->trigger_hpos); + } else { + devc->trigger_hpos = g_variant_get_byte(data) * devc->limit_samples / 100.0; + } + } else if (id == SR_CONF_TRIGGER_HOLDOFF) { + devc->trigger_holdoff = g_variant_get_uint64(data); + if (sdi->mode == DSO) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_HOLDOFF)); + } + if (ret == SR_OK) + sr_dbg("%s: setting Trigger Holdoff Time to %d", + __func__, devc->trigger_holdoff); + else + sr_dbg("%s: setting Trigger Holdoff Time to %d failed", + __func__, devc->trigger_holdoff); + } else if (id == SR_CONF_TRIGGER_MARGIN) { + devc->trigger_margin = g_variant_get_byte(data); + if (sdi->mode == DSO) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_MARGIN)); + } + if (ret == SR_OK) + sr_dbg("%s: setting Trigger Margin to %d", + __func__, devc->trigger_margin); + else + sr_dbg("%s: setting Trigger Margin to %d failed", + __func__, devc->trigger_margin); + } else if (id == SR_CONF_SAMPLERATE) { + if (devc->test_mode == SR_TEST_NONE) { + devc->cur_samplerate = g_variant_get_uint64(data); + if(sdi->mode != LOGIC) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); + } + } + } else if (id == SR_CONF_FILTER) { + stropt = g_variant_get_string(data, NULL); + if (!strcmp(stropt, get_filters(devc)[SR_FILTER_NONE])) { + devc->filter = SR_FILTER_NONE; + } else if (!strcmp(stropt, get_filters(devc)[SR_FILTER_1T])) { + devc->filter = SR_FILTER_1T; + } else { + ret = SR_ERR; + } + sr_dbg("%s: setting filter to %d", + __func__, devc->filter); + } else if (id == SR_CONF_RLE) { + devc->rle_mode = g_variant_get_boolean(data); + } else if (id == SR_CONF_INSTANT) { + if (sdi->mode == DSO) { + devc->instant = g_variant_get_boolean(data); + if (dsl_en_ch_num(sdi) != 0) { + if (devc->instant) + devc->limit_samples = devc->profile->dev_caps.hw_depth / dsl_en_ch_num(sdi); + else + devc->limit_samples = devc->profile->dev_caps.dso_depth / dsl_en_ch_num(sdi); + } + } + } else if (id == SR_CONF_DEVICE_MODE) { + sdi->mode = g_variant_get_int16(data); + if (sdi->mode == LOGIC) { + dsl_wr_reg(sdi, CTR0_ADDR, bmSCOPE_CLR); + for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { + if (channel_modes[i].mode == LOGIC && + devc->profile->dev_caps.channels & (1 << i)) { + devc->ch_mode = channel_modes[i].id; + num_probes = channel_modes[i].num; + devc->stream = channel_modes[i].stream; + dsl_adjust_samplerate(devc); + break; + } + } + } else if (sdi->mode == DSO) { + dsl_wr_reg(sdi, CTR0_ADDR, bmSCOPE_SET); + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_DSO_SYNC)); + if (ret != SR_OK) + sr_dbg("%s: DSO configuration sync failed", __func__); + for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { + if (channel_modes[i].mode == DSO && + devc->profile->dev_caps.channels & (1 << i)) { + devc->ch_mode = channel_modes[i].id; + num_probes = channel_modes[i].num; + devc->stream = channel_modes[i].stream; + devc->cur_samplerate = channel_modes[i].max_samplerate / num_probes; + dsl_adjust_samplerate(devc); + break; + } + } + devc->limit_samples = devc->profile->dev_caps.dso_depth / num_probes; + } else if (sdi->mode == ANALOG) { + dsl_wr_reg(sdi, CTR0_ADDR, bmSCOPE_SET); + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_DSO_SYNC)); + if (ret != SR_OK) + sr_dbg("%s: DAQ configuration sync failed", __func__); + devc->op_mode = OP_STREAM; + devc->test_mode = SR_TEST_NONE; + for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { + if (channel_modes[i].mode == ANALOG && + devc->profile->dev_caps.channels & (1 << i)) { + devc->ch_mode = channel_modes[i].id; + num_probes = channel_modes[i].num; + devc->stream = channel_modes[i].stream; + dsl_adjust_samplerate(devc); + break; + } + } + } else { + ret = SR_ERR; + } + assert(num_probes != 0); + sr_dev_probes_free(sdi); + dsl_setup_probes(sdi, num_probes); + sr_dbg("%s: setting mode to %d", __func__, sdi->mode); + if (sdi->mode != LOGIC) { + dso_init(sdi); + } + } else if (id == SR_CONF_OPERATION_MODE) { + stropt = g_variant_get_string(data, NULL); + if (sdi->mode == LOGIC) { + if (!strcmp(stropt, get_opmodes(devc)[OP_BUFFER])) { + if (devc->op_mode != OP_BUFFER) { + devc->op_mode = OP_BUFFER; + devc->test_mode = SR_TEST_NONE; + devc->stream = FALSE; + for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { + if (channel_modes[i].mode == LOGIC && + channel_modes[i].stream == devc->stream && + devc->profile->dev_caps.channels & (1 << i)) { + devc->ch_mode = channel_modes[i].id; + break; + } + } + } + } else if (!strcmp(stropt, get_opmodes(devc)[OP_STREAM])) { + if (devc->op_mode != OP_STREAM) { + devc->op_mode = OP_STREAM; + devc->test_mode = SR_TEST_NONE; + devc->stream = TRUE; + for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { + if (channel_modes[i].mode == LOGIC && + channel_modes[i].stream == devc->stream && + devc->profile->dev_caps.channels & (1 << i)) { + devc->ch_mode = channel_modes[i].id; + break; + } + } + } + } else if (!strcmp(stropt, get_opmodes(devc)[OP_INTEST])) { + if (devc->op_mode != OP_INTEST) { + devc->op_mode = OP_INTEST; + devc->test_mode = SR_TEST_INTERNAL; + devc->ch_mode = devc->profile->dev_caps.intest_channel; + devc->stream = !(devc->profile->dev_caps.feature_caps & CAPS_FEATURE_BUF); + } + } else { + ret = SR_ERR; + } + dsl_adjust_probes(sdi, channel_modes[devc->ch_mode].num); + dsl_adjust_samplerate(devc); + if (devc->op_mode == OP_INTEST) { + devc->cur_samplerate = devc->stream ? channel_modes[devc->ch_mode].max_samplerate / 10 : + channel_modes[devc->ch_mode].max_samplerate; + devc->limit_samples = devc->stream ? devc->cur_samplerate * 3 : + devc->profile->dev_caps.hw_depth / dsl_en_ch_num(sdi); + } + } + sr_dbg("%s: setting pattern to %d", + __func__, devc->op_mode); + } else if (id == SR_CONF_BUFFER_OPTIONS) { + stropt = g_variant_get_string(data, NULL); + if (sdi->mode == LOGIC) { + if (!strcmp(stropt, get_bufoptions(devc)[SR_BUF_STOP])) + devc->buf_options = SR_BUF_STOP; + else if (!strcmp(stropt, get_bufoptions(devc)[SR_BUF_UPLOAD])) + devc->buf_options = SR_BUF_UPLOAD; + } + } else if (id == SR_CONF_CHANNEL_MODE) { + stropt = g_variant_get_string(data, NULL); + if (sdi->mode == LOGIC) { + for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { + if (devc->language == LANGUAGE_CN) { + if (!strcmp(stropt, channel_modes[i].descr_cn)) { + devc->ch_mode = channel_modes[i].id; + break; + } + } else { + if (!strcmp(stropt, channel_modes[i].descr)) { + devc->ch_mode = channel_modes[i].id; + break; + } + } + } + dsl_adjust_probes(sdi, channel_modes[devc->ch_mode].num); + dsl_adjust_samplerate(devc); + } + sr_dbg("%s: setting channel mode to %d", + __func__, devc->ch_mode); + } else if (id == SR_CONF_THRESHOLD) { + if (sdi->mode == LOGIC) { + stropt = g_variant_get_string(data, NULL); + if (strcmp(stropt, get_thresholds(devc)[devc->th_level])) { + if (!strcmp(stropt, get_thresholds(devc)[SR_TH_3V3])) { + devc->th_level = SR_TH_3V3; + } else if (!strcmp(stropt, get_thresholds(devc)[SR_TH_5V0])) { + devc->th_level = SR_TH_5V0; + } else { + ret = SR_ERR; + } + char *fpga_bit; + if (!(fpga_bit = g_try_malloc(strlen(DS_RES_PATH)+strlen(devc->profile->fpga_bit33)+1))) { + sr_err("fpag_bit path malloc error!"); + return SR_ERR_MALLOC; + } + strcpy(fpga_bit, DS_RES_PATH); + switch(devc->th_level) { + case SR_TH_3V3: + strcat(fpga_bit, devc->profile->fpga_bit33); + break; + case SR_TH_5V0: + strcat(fpga_bit, devc->profile->fpga_bit50); + break; + default: + return SR_ERR; + } + ret = dsl_fpga_config(usb->devhdl, fpga_bit); + g_free(fpga_bit); + if (ret != SR_OK) { + sr_err("Configure FPGA failed!"); + } + } + sr_dbg("%s: setting threshold to %d", + __func__, devc->th_level); + } + } else if (id == SR_CONF_VTH) { + devc->vth = g_variant_get_double(data); + ret = dsl_wr_reg(sdi, VTH_ADDR, (uint8_t)(devc->vth/5.0*255)); + } else if (id == SR_CONF_MAX_HEIGHT) { + stropt = g_variant_get_string(data, NULL); + for (i = 0; i < ARRAY_SIZE(maxHeights); i++) { + if (!strcmp(stropt, maxHeights[i])) { + devc->max_height = i; + break; + } + } + sr_dbg("%s: setting Signal Max Height to %d", + __func__, devc->max_height); + } else if (id == SR_CONF_PROBE_EN) { + ch->enabled = g_variant_get_boolean(data); + if (sdi->mode == DSO) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, ch, SR_CONF_PROBE_EN)); + uint16_t channel_cnt = 0; + GSList *l; + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + channel_cnt += probe->enabled; + } + if (channel_cnt != 0) + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); + } + if (ret == SR_OK) + sr_dbg("%s: setting ENABLE of channel %d to %d", + __func__, ch->index, ch->enabled); + else + sr_dbg("%s: setting ENABLE of channel %d to %d", + __func__, ch->index, ch->enabled); + } else if (id == SR_CONF_PROBE_OFFSET) { + ch->offset = g_variant_get_uint16(data); + sr_dbg("%s: setting OFFSET of channel %d to %d", __func__, + ch->index, ch->offset); + } else if (id == SR_CONF_PROBE_HW_OFFSET) { + ch->hw_offset = g_variant_get_uint16(data); + sr_dbg("%s: setting OFFSET of channel %d to %d", __func__, + ch->index, ch->offset); + } else if (id == SR_CONF_TRIGGER_SOURCE) { + devc->trigger_source = g_variant_get_byte(data); + if (sdi->mode == DSO) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); + } + if (ret == SR_OK) + sr_dbg("%s: setting DSO Trigger Source to %d", + __func__, devc->trigger_source); + else + sr_dbg("%s: setting DSO Trigger Source to %d failed", + __func__, devc->trigger_source); + } else if (id == SR_CONF_TRIGGER_CHANNEL) { + devc->trigger_source = (g_variant_get_byte(data) << 4) + (devc->trigger_source & 0x0f); + if (sdi->mode == DSO) { + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); + } + if (ret == SR_OK) + sr_dbg("%s: setting DSO Trigger Source to %d", + __func__, devc->trigger_source); + else + sr_dbg("%s: setting DSO Trigger Source to %d failed", + __func__, devc->trigger_source); + } else if (id == SR_CONF_STREAM) { + devc->stream = g_variant_get_boolean(data); + } else { + ret = SR_ERR_NA; + } + + return ret; +} + +static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi, + const struct sr_channel_group *cg) +{ + struct DSL_context *devc; + GVariantBuilder gvb; + unsigned int i; + + (void)cg; + devc = sdi->priv; + + if (dsl_config_list(key, data, sdi, cg) == SR_OK) { + return SR_OK; + } + + switch (key) { + case SR_CONF_DEVICE_OPTIONS: + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_VTH) + *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), + hwoptions_pro, ARRAY_SIZE(hwoptions_pro)*sizeof(int32_t), TRUE, NULL, NULL); + else + *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), + hwoptions, ARRAY_SIZE(hwoptions)*sizeof(int32_t), TRUE, NULL, NULL); + break; + case SR_CONF_DEVICE_SESSIONS: + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_VTH) + *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), + sessions_pro, ARRAY_SIZE(sessions_pro)*sizeof(int32_t), TRUE, NULL, NULL); + else + *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), + sessions, ARRAY_SIZE(sessions)*sizeof(int32_t), TRUE, NULL, NULL); + break; + case SR_CONF_OPERATION_MODE: + *data = g_variant_new_strv(get_opmodes(devc), opmodes_show_count); + break; + case SR_CONF_BUFFER_OPTIONS: + *data = g_variant_new_strv(get_bufoptions(devc), ARRAY_SIZE(bufoptions)); + break; + case SR_CONF_CHANNEL_MODE: + g_variant_builder_init(&gvb, G_VARIANT_TYPE("as")); + for (i = 0; i < ARRAY_SIZE(channel_modes); i++) { + if (channel_modes[i].stream == devc->stream && + devc->profile->dev_caps.channels & (1 << i)) { + if (devc->language == LANGUAGE_CN) + g_variant_builder_add(&gvb, "s", channel_modes[i].descr_cn); + else + g_variant_builder_add(&gvb, "s", channel_modes[i].descr); + if (devc->test_mode != SR_TEST_NONE) + break; + } + } + *data = g_variant_builder_end(&gvb); + break; + case SR_CONF_THRESHOLD: + *data = g_variant_new_strv(get_thresholds(devc), ARRAY_SIZE(thresholds)); + break; + case SR_CONF_FILTER: + *data = g_variant_new_strv(get_filters(devc), ARRAY_SIZE(filters)); + break; + case SR_CONF_MAX_HEIGHT: + *data = g_variant_new_strv(maxHeights, ARRAY_SIZE(maxHeights)); + break; + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +static int dev_open(struct sr_dev_inst *sdi) +{ + gboolean fpga_done; + int ret; + struct DSL_context *devc; + + devc = sdi->priv; + + if ((ret = dsl_dev_open(di, sdi, &fpga_done)) == SR_OK) { + // set threshold + ret = dsl_wr_reg(sdi, VTH_ADDR, (uint8_t)(devc->vth/5.0*255)); + if (devc->profile->dev_caps.feature_caps & CAPS_FEATURE_ADF4360) { + dsl_config_adc(sdi, adc_clk_init_500m); + } + } + + return ret; +} + +static int dev_close(struct sr_dev_inst *sdi) +{ + int ret; + ret = dsl_dev_close(sdi); + return ret; +} + +static int cleanup(void) +{ + int ret; + struct drv_context *drvc; + + if (!(drvc = di->priv)) + return SR_OK; + + ret = dev_clear(); + + g_free(drvc); + di->priv = NULL; + + return ret; +} + +static void remove_sources(struct DSL_context *devc) +{ + int i; + sr_info("%s: remove fds from polling", __func__); + /* Remove fds from polling. */ + for (i = 0; devc->usbfd[i] != -1; i++) + sr_source_remove(devc->usbfd[i]); + g_free(devc->usbfd); +} + +static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) +{ + int completed = 0; + struct timeval tv; + struct drv_context *drvc; + struct DSL_context *devc; + struct sr_usb_dev_inst *usb; + struct ctl_rd_cmd rd_cmd; + uint8_t hw_info; + int ret; + + (void)fd; + (void)revents; + + drvc = di->priv; + devc = sdi->priv; + usb = sdi->conn; + + tv.tv_sec = tv.tv_usec = 0; + libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv, &completed); + + // overflow check + if (devc->stream && devc->trf_completed) { + rd_cmd.header.dest = DSL_CTL_HW_STATUS; + rd_cmd.header.size = 1; + hw_info = 0; + rd_cmd.data = &hw_info; + if ((ret = command_ctl_rd(usb->devhdl, rd_cmd)) != SR_OK) + sr_err("Failed to get hardware infos."); + else + devc->overflow = (hw_info & bmSYS_OVERFLOW) != 0; + } + + if (devc->status == DSL_FINISH) { + /* Remove polling */ + remove_sources(devc); + } + + devc->trf_completed = 0; + return TRUE; +} + +static int dev_acquisition_start(struct sr_dev_inst *sdi, void *cb_data) +{ + (void)cb_data; + + struct DSL_context *devc; + struct sr_usb_dev_inst *usb; + struct drv_context *drvc; + const struct libusb_pollfd **lupfd; + unsigned int i; + int ret; + struct ctl_wr_cmd wr_cmd; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR_DEV_CLOSED; + + drvc = di->priv; + devc = sdi->priv; + usb = sdi->conn; + + //devc->cb_data = cb_data; + devc->cb_data = sdi; + devc->num_samples = 0; + devc->num_bytes = 0; + devc->empty_transfer_count = 0; + devc->status = DSL_INIT; + devc->num_transfers = 0; + devc->submitted_transfers = 0; + devc->actual_samples = (devc->limit_samples + 1023ULL) & ~1023ULL; + devc->actual_bytes = devc->actual_samples / DSLOGIC_ATOMIC_SAMPLES * dsl_en_ch_num(sdi) * DSLOGIC_ATOMIC_SIZE; + devc->abort = FALSE; + devc->mstatus_valid = FALSE; + devc->overflow = FALSE; + + /* Configures devc->trigger_* and devc->sample_wide */ + if (dsl_configure_probes(sdi) != SR_OK) { + sr_err("%s: Failed to configure probes.", __func__); + return SR_ERR; + } + + /* Stop Previous GPIF acquisition */ + wr_cmd.header.dest = DSL_CTL_STOP; + wr_cmd.header.size = 0; + if ((ret = command_ctl_wr(usb->devhdl, wr_cmd)) != SR_OK) { + sr_err("%s: Stop DSLogic acquisition failed!", __func__); + return ret; + } else { + sr_info("%s: Stop Previous DSLogic acquisition!", __func__); + } + + /* Setting FPGA before acquisition start*/ + if ((ret = dsl_fpga_arm(sdi)) != SR_OK) { + sr_err("%s: Arm FPGA failed!", __func__); + return ret; + } + + /* + * settings must be updated before acquisition + */ + if (sdi->mode == DSO) { + devc->trigger_hpos = devc->trigger_hrate * dsl_en_ch_num(sdi) * devc->limit_samples / 200.0; + ret = dsl_wr_dso(sdi, dso_cmd_gen(sdi, NULL, SR_CONF_HORIZ_TRIGGERPOS)); + if (ret != SR_OK) + sr_dbg("%s: setting DSO Horiz Trigger Position to %d failed", __func__, devc->trigger_hpos); + } + + /* setup and submit usb transfer */ + if ((ret = dsl_start_transfers(devc->cb_data)) != SR_OK) { + sr_err("%s: Could not submit usb transfer" + "(%d)%d", __func__, ret, errno); + return ret; + } + + /* setup callback function for data transfer */ + lupfd = libusb_get_pollfds(drvc->sr_ctx->libusb_ctx); + for (i = 0; lupfd[i]; i++); + if (!(devc->usbfd = g_try_malloc(sizeof(struct libusb_pollfd) * (i + 1)))) + return SR_ERR; + for (i = 0; lupfd[i]; i++) { + sr_source_add(lupfd[i]->fd, lupfd[i]->events, + dsl_get_timeout(sdi), receive_data, sdi); + devc->usbfd[i] = lupfd[i]->fd; + } + devc->usbfd[i] = -1; + free(lupfd); + + wr_cmd.header.dest = DSL_CTL_START; + wr_cmd.header.size = 0; + if ((ret = command_ctl_wr(usb->devhdl, wr_cmd)) != SR_OK) { + devc->status = DSL_ERROR; + devc->abort = TRUE; + return ret; + } + devc->status = DSL_START; + + /* Send header packet to the session bus. */ + //std_session_send_df_header(cb_data, LOG_PREFIX); + std_session_send_df_header(sdi, LOG_PREFIX); + + return SR_OK; +} + +static int dev_acquisition_stop(const struct sr_dev_inst *sdi, void *cb_data) +{ + int ret = dsl_dev_acquisition_stop(sdi, cb_data); + return ret; +} + +static int dev_status_get(const struct sr_dev_inst *sdi, struct sr_status *status, gboolean prg, int begin, int end) +{ + int ret = dsl_dev_status_get(sdi, status, prg, begin, end); + return ret; +} + +SR_PRIV struct sr_dev_driver DSLogic_driver_info = { + .name = "DSLogic", + .longname = "DSLogic (generic driver for DSLogic LA)", + .api_version = 1, + .init = init, + .cleanup = cleanup, + .scan = scan, + .dev_list = dev_list, + .dev_mode_list = dev_mode_list, + .dev_clear = dev_clear, + .config_get = config_get, + .config_set = config_set, + .config_list = config_list, + .dev_open = dev_open, + .dev_close = dev_close, + .dev_status_get = dev_status_get, + .dev_acquisition_start = dev_acquisition_start, + .dev_acquisition_stop = dev_acquisition_stop, + .priv = NULL, +}; diff --git a/libsigrok4DSL/hardware/Makefile.am b/libsigrok4DSL/hardware/Makefile.am old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/hardware/common/Makefile.am b/libsigrok4DSL/hardware/common/Makefile.am old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/hardware/common/ezusb.c b/libsigrok4DSL/hardware/common/ezusb.c old mode 100644 new mode 100755 index b02e5313..d05d2a79 --- a/libsigrok4DSL/hardware/common/ezusb.c +++ b/libsigrok4DSL/hardware/common/ezusb.c @@ -1,142 +1,142 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2013 Bert Vermeulen - * - * 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 . - */ - -/* - * Helper functions for the Cypress EZ-USB / FX2 series chips. - */ -#include "libsigrok.h" -#include "libsigrok-internal.h" -//#include -#include -#include -#include -#include -#include - -/* Message logging helpers with subsystem-specific prefix string. */ -#define LOG_PREFIX "ezusb: " -#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) -#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) -#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) -#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) -#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) -#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) - -SR_PRIV int ezusb_reset(struct libusb_device_handle *hdl, int set_clear) -{ - int ret; - unsigned char buf[1]; - - sr_info("setting CPU reset mode %s...", - set_clear ? "on" : "off"); - buf[0] = set_clear ? 1 : 0; - ret = libusb_control_transfer(hdl, LIBUSB_REQUEST_TYPE_VENDOR, 0xa0, - 0xe600, 0x0000, buf, 1, 3000); - if (ret < 0) - sr_err("Unable to send control request: %s.", - libusb_error_name(ret)); - - return ret; -} - -SR_PRIV int ezusb_install_firmware(libusb_device_handle *hdl, - const char *filename) -{ - FILE *fw; - int offset, chunksize, ret, result; - unsigned char buf[4096]; - - sr_info("Uploading firmware at %s", filename); - if ((fw = fopen(filename, "rb")) == NULL) { - sr_err("Unable to open firmware file %s for reading: %s", - filename, strerror(errno)); - return SR_ERR; - } - - result = SR_OK; - offset = 0; - while (1) { - chunksize = fread(buf, 1, 4096, fw); - if (chunksize == 0) - break; - ret = libusb_control_transfer(hdl, LIBUSB_REQUEST_TYPE_VENDOR | - LIBUSB_ENDPOINT_OUT, 0xa0, offset, - 0x0000, buf, chunksize, 3000); - if (ret < 0) { - sr_err("Unable to send firmware to device: %s.", - libusb_error_name(ret)); - result = SR_ERR; - break; - } - sr_info("Uploaded %d bytes", chunksize); - offset += chunksize; - } - fclose(fw); - sr_info("Firmware upload done"); - - return result; -} - -SR_PRIV int ezusb_upload_firmware(libusb_device *dev, int configuration, - const char *filename) -{ - struct libusb_device_handle *hdl; - int ret; - - sr_info("uploading firmware to device on %d.%d", - libusb_get_bus_number(dev), libusb_get_device_address(dev)); - - if ((ret = libusb_open(dev, &hdl)) < 0) { - sr_err("failed to open device: %s.", libusb_error_name(ret)); - return SR_ERR; - } - -/* - * The libusbx darwin backend is broken: it can report a kernel driver being - * active, but detaching it always returns an error. - */ -#if !defined(__APPLE__) - if (libusb_kernel_driver_active(hdl, 0) == 1) { - if ((ret = libusb_detach_kernel_driver(hdl, 0)) < 0) { - sr_err("failed to detach kernel driver: %s", - libusb_error_name(ret)); - return SR_ERR; - } - } -#endif - - if ((ret = libusb_set_configuration(hdl, configuration)) < 0) { - sr_err("Unable to set configuration: %s", - libusb_error_name(ret)); - return SR_ERR; - } - - if ((ezusb_reset(hdl, 1)) < 0) - return SR_ERR; - - if (ezusb_install_firmware(hdl, filename) < 0) - return SR_ERR; - - if ((ezusb_reset(hdl, 0)) < 0) - return SR_ERR; - - libusb_close(hdl); - - return SR_OK; -} +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2013 Bert Vermeulen + * + * 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 . + */ + +/* + * Helper functions for the Cypress EZ-USB / FX2 series chips. + */ +#include "libsigrok.h" +#include "libsigrok-internal.h" +//#include +#include +#include +#include +#include +#include + +/* Message logging helpers with subsystem-specific prefix string. */ +#define LOG_PREFIX "ezusb: " +#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) +#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) +#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) +#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) +#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) +#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) + +SR_PRIV int ezusb_reset(struct libusb_device_handle *hdl, int set_clear) +{ + int ret; + unsigned char buf[1]; + + sr_info("setting CPU reset mode %s...", + set_clear ? "on" : "off"); + buf[0] = set_clear ? 1 : 0; + ret = libusb_control_transfer(hdl, LIBUSB_REQUEST_TYPE_VENDOR, 0xa0, + 0xe600, 0x0000, buf, 1, 3000); + if (ret < 0) + sr_err("Unable to send control request: %s.", + libusb_error_name(ret)); + + return ret; +} + +SR_PRIV int ezusb_install_firmware(libusb_device_handle *hdl, + const char *filename) +{ + FILE *fw; + int offset, chunksize, ret, result; + unsigned char buf[4096]; + + sr_info("Uploading firmware at %s", filename); + if ((fw = fopen(filename, "rb")) == NULL) { + sr_err("Unable to open firmware file %s for reading: %s", + filename, strerror(errno)); + return SR_ERR; + } + + result = SR_OK; + offset = 0; + while (1) { + chunksize = fread(buf, 1, 4096, fw); + if (chunksize == 0) + break; + ret = libusb_control_transfer(hdl, LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_ENDPOINT_OUT, 0xa0, offset, + 0x0000, buf, chunksize, 3000); + if (ret < 0) { + sr_err("Unable to send firmware to device: %s.", + libusb_error_name(ret)); + result = SR_ERR; + break; + } + sr_info("Uploaded %d bytes", chunksize); + offset += chunksize; + } + fclose(fw); + sr_info("Firmware upload done"); + + return result; +} + +SR_PRIV int ezusb_upload_firmware(libusb_device *dev, int configuration, + const char *filename) +{ + struct libusb_device_handle *hdl; + int ret; + + sr_info("uploading firmware to device on %d.%d", + libusb_get_bus_number(dev), libusb_get_device_address(dev)); + + if ((ret = libusb_open(dev, &hdl)) < 0) { + sr_err("failed to open device: %s.", libusb_error_name(ret)); + return SR_ERR; + } + +/* + * The libusbx darwin backend is broken: it can report a kernel driver being + * active, but detaching it always returns an error. + */ +#if !defined(__APPLE__) + if (libusb_kernel_driver_active(hdl, 0) == 1) { + if ((ret = libusb_detach_kernel_driver(hdl, 0)) < 0) { + sr_err("failed to detach kernel driver: %s", + libusb_error_name(ret)); + return SR_ERR; + } + } +#endif + + if ((ret = libusb_set_configuration(hdl, configuration)) < 0) { + sr_err("Unable to set configuration: %s", + libusb_error_name(ret)); + return SR_ERR; + } + + if ((ezusb_reset(hdl, 1)) < 0) + return SR_ERR; + + if (ezusb_install_firmware(hdl, filename) < 0) + return SR_ERR; + + if ((ezusb_reset(hdl, 0)) < 0) + return SR_ERR; + + libusb_close(hdl); + + return SR_OK; +} diff --git a/libsigrok4DSL/hardware/common/usb.c b/libsigrok4DSL/hardware/common/usb.c old mode 100644 new mode 100755 index ac3f6fe6..09c287ea --- a/libsigrok4DSL/hardware/common/usb.c +++ b/libsigrok4DSL/hardware/common/usb.c @@ -1,245 +1,245 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2013 Bert Vermeulen - * - * 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 . - */ - -#include "libsigrok.h" -#include "libsigrok-internal.h" -#include -#include -//#include - -/* SR_CONF_CONN takes one of these: */ -#define CONN_USB_VIDPID "^([0-9a-z]{4})\\.([0-9a-z]{4})$" -#define CONN_USB_BUSADDR "^(\\d+)\\.(\\d+)$" - -/* Some USBTMC-specific enums, as defined in the USBTMC standard. */ -#define SUBCLASS_USBTMC 0x03 -#define USBTMC_USB488 0x01 - -/* Message logging helpers with subsystem-specific prefix string. */ -#define LOG_PREFIX "usb: " -#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) -#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) -#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) -#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) -#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) -#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) - -/** - * Find USB devices according to a connection string. - * - * @param usb_ctx libusb context to use while scanning. - * @param conn Connection string specifying the device(s) to match. This - * can be of the form ".
", or ".". - * - * @return A GSList of struct sr_usb_dev_inst, with bus and address fields - * matching the device that matched the connection string. The GSList and - * its contents must be freed by the caller. - */ -SR_PRIV GSList *sr_usb_find(libusb_context *usb_ctx, const char *conn) -{ - struct sr_usb_dev_inst *usb; - struct libusb_device **devlist; - struct libusb_device_descriptor des; - GSList *devices; - GRegex *reg; - GMatchInfo *match; - int vid, pid, bus, addr, b, a, ret, i; - char *mstr; - - vid = pid = bus = addr = 0; - reg = g_regex_new(CONN_USB_VIDPID, 0, 0, NULL); - if (g_regex_match(reg, conn, 0, &match)) { - if ((mstr = g_match_info_fetch(match, 1))) - vid = strtoul(mstr, NULL, 16); - g_free(mstr); - - if ((mstr = g_match_info_fetch(match, 2))) - pid = strtoul(mstr, NULL, 16); - g_free(mstr); - sr_dbg("Trying to find USB device with VID:PID = %04x:%04x.", - vid, pid); - } else { - //g_match_info_unref(match); - g_match_info_free(match); - g_regex_unref(reg); - reg = g_regex_new(CONN_USB_BUSADDR, 0, 0, NULL); - if (g_regex_match(reg, conn, 0, &match)) { - if ((mstr = g_match_info_fetch(match, 1))) - bus = strtoul(mstr, NULL, 10); - g_free(mstr); - - if ((mstr = g_match_info_fetch(match, 2))) - addr = strtoul(mstr, NULL, 10); - g_free(mstr); - sr_dbg("Trying to find USB device with bus.address = " - "%d.%d.", bus, addr); - } - } - //g_match_info_unref(match); - g_match_info_free(match); - g_regex_unref(reg); - - if (vid + pid + bus + addr == 0) { - sr_err("Neither VID:PID nor bus.address was specified."); - return NULL; - } - - if (bus > 64) { - sr_err("Invalid bus specified: %d.", bus); - return NULL; - } - - if (addr > 127) { - sr_err("Invalid address specified: %d.", addr); - return NULL; - } - - /* Looks like a valid USB device specification, but is it connected? */ - devices = NULL; - libusb_get_device_list(usb_ctx, &devlist); - for (i = 0; devlist[i]; i++) { - if ((ret = libusb_get_device_descriptor(devlist[i], &des))) { - sr_err("Failed to get device descriptor: %s.", - libusb_error_name(ret)); - continue; - } - - if (vid + pid && (des.idVendor != vid || des.idProduct != pid)) - continue; - - b = libusb_get_bus_number(devlist[i]); - a = libusb_get_device_address(devlist[i]); - if (bus + addr && (b != bus || a != addr)) - continue; - - sr_dbg("Found USB device (VID:PID = %04x:%04x, bus.address = " - "%d.%d).", des.idVendor, des.idProduct, b, a); - - usb = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), - libusb_get_device_address(devlist[i]), NULL); - devices = g_slist_append(devices, usb); - } - libusb_free_device_list(devlist, 1); - - sr_dbg("Found %d device(s).", g_slist_length(devices)); - - return devices; -} - -/** - * Find USB devices supporting the USBTMC class - * - * @param usb_ctx libusb context to use while scanning. - * - * @return A GSList of struct sr_usb_dev_inst, with bus and address fields - * indicating devices with USBTMC support. - */ -SR_PRIV GSList *sr_usb_find_usbtmc(libusb_context *usb_ctx) -{ - struct sr_usb_dev_inst *usb; - struct libusb_device **devlist; - struct libusb_device_descriptor des; - struct libusb_config_descriptor *confdes; - const struct libusb_interface_descriptor *intfdes; - GSList *devices; - int confidx, intfidx, ret, i; - - devices = NULL; - libusb_get_device_list(usb_ctx, &devlist); - for (i = 0; devlist[i]; i++) { - if ((ret = libusb_get_device_descriptor(devlist[i], &des))) { - sr_err("Failed to get device descriptor: %s.", - libusb_error_name(ret)); - continue; - } - - for (confidx = 0; confidx < des.bNumConfigurations; confidx++) { - if (libusb_get_config_descriptor(devlist[i], confidx, &confdes) != 0) { - sr_err("Failed to get configuration descriptor."); - break; - } - for (intfidx = 0; intfidx < confdes->bNumInterfaces; intfidx++) { - intfdes = confdes->interface[intfidx].altsetting; - if (intfdes->bInterfaceClass != LIBUSB_CLASS_APPLICATION - || intfdes->bInterfaceSubClass != SUBCLASS_USBTMC - || intfdes->bInterfaceProtocol != USBTMC_USB488) - continue; - sr_dbg("Found USBTMC device (VID:PID = %04x:%04x, bus.address = " - "%d.%d).", des.idVendor, des.idProduct, - libusb_get_bus_number(devlist[i]), - libusb_get_device_address(devlist[i])); - - usb = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), - libusb_get_device_address(devlist[i]), NULL); - devices = g_slist_append(devices, usb); - } - libusb_free_config_descriptor(confdes); - } - } - libusb_free_device_list(devlist, 1); - - sr_dbg("Found %d device(s).", g_slist_length(devices)); - - return devices; -} - -SR_PRIV int sr_usb_open(libusb_context *usb_ctx, struct sr_usb_dev_inst *usb) -{ - struct libusb_device **devlist; - struct libusb_device_descriptor des; - int ret, r, cnt, i, a, b; - - sr_dbg("Trying to open USB device %d.%d.", usb->bus, usb->address); - - if ((cnt = libusb_get_device_list(usb_ctx, &devlist)) < 0) { - sr_err("Failed to retrieve device list: %s.", - libusb_error_name(cnt)); - return SR_ERR; - } - - ret = SR_ERR; - for (i = 0; i < cnt; i++) { - if ((r = libusb_get_device_descriptor(devlist[i], &des)) < 0) { - sr_err("Failed to get device descriptor: %s.", - libusb_error_name(r)); - continue; - } - - b = libusb_get_bus_number(devlist[i]); - a = libusb_get_device_address(devlist[i]); - if (b != usb->bus || a != usb->address) - continue; - - if ((r = libusb_open(devlist[i], &usb->devhdl)) < 0) { - sr_err("Failed to open device: %s.", - libusb_error_name(r)); - break; - } - - sr_dbg("Opened USB device (VID:PID = %04x:%04x, bus.address = " - "%d.%d).", des.idVendor, des.idProduct, b, a); - - ret = SR_OK; - break; - } - - libusb_free_device_list(devlist, 1); - - return ret; -} +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2013 Bert Vermeulen + * + * 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 . + */ + +#include "libsigrok.h" +#include "libsigrok-internal.h" +#include +#include +//#include + +/* SR_CONF_CONN takes one of these: */ +#define CONN_USB_VIDPID "^([0-9a-z]{4})\\.([0-9a-z]{4})$" +#define CONN_USB_BUSADDR "^(\\d+)\\.(\\d+)$" + +/* Some USBTMC-specific enums, as defined in the USBTMC standard. */ +#define SUBCLASS_USBTMC 0x03 +#define USBTMC_USB488 0x01 + +/* Message logging helpers with subsystem-specific prefix string. */ +#define LOG_PREFIX "usb: " +#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) +#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) +#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) +#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) +#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) +#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) + +/** + * Find USB devices according to a connection string. + * + * @param usb_ctx libusb context to use while scanning. + * @param conn Connection string specifying the device(s) to match. This + * can be of the form ".
", or ".". + * + * @return A GSList of struct sr_usb_dev_inst, with bus and address fields + * matching the device that matched the connection string. The GSList and + * its contents must be freed by the caller. + */ +SR_PRIV GSList *sr_usb_find(libusb_context *usb_ctx, const char *conn) +{ + struct sr_usb_dev_inst *usb; + struct libusb_device **devlist; + struct libusb_device_descriptor des; + GSList *devices; + GRegex *reg; + GMatchInfo *match; + int vid, pid, bus, addr, b, a, ret, i; + char *mstr; + + vid = pid = bus = addr = 0; + reg = g_regex_new(CONN_USB_VIDPID, 0, 0, NULL); + if (g_regex_match(reg, conn, 0, &match)) { + if ((mstr = g_match_info_fetch(match, 1))) + vid = strtoul(mstr, NULL, 16); + g_free(mstr); + + if ((mstr = g_match_info_fetch(match, 2))) + pid = strtoul(mstr, NULL, 16); + g_free(mstr); + sr_dbg("Trying to find USB device with VID:PID = %04x:%04x.", + vid, pid); + } else { + //g_match_info_unref(match); + g_match_info_free(match); + g_regex_unref(reg); + reg = g_regex_new(CONN_USB_BUSADDR, 0, 0, NULL); + if (g_regex_match(reg, conn, 0, &match)) { + if ((mstr = g_match_info_fetch(match, 1))) + bus = strtoul(mstr, NULL, 10); + g_free(mstr); + + if ((mstr = g_match_info_fetch(match, 2))) + addr = strtoul(mstr, NULL, 10); + g_free(mstr); + sr_dbg("Trying to find USB device with bus.address = " + "%d.%d.", bus, addr); + } + } + //g_match_info_unref(match); + g_match_info_free(match); + g_regex_unref(reg); + + if (vid + pid + bus + addr == 0) { + sr_err("Neither VID:PID nor bus.address was specified."); + return NULL; + } + + if (bus > 64) { + sr_err("Invalid bus specified: %d.", bus); + return NULL; + } + + if (addr > 127) { + sr_err("Invalid address specified: %d.", addr); + return NULL; + } + + /* Looks like a valid USB device specification, but is it connected? */ + devices = NULL; + libusb_get_device_list(usb_ctx, &devlist); + for (i = 0; devlist[i]; i++) { + if ((ret = libusb_get_device_descriptor(devlist[i], &des))) { + sr_err("Failed to get device descriptor: %s.", + libusb_error_name(ret)); + continue; + } + + if (vid + pid && (des.idVendor != vid || des.idProduct != pid)) + continue; + + b = libusb_get_bus_number(devlist[i]); + a = libusb_get_device_address(devlist[i]); + if (bus + addr && (b != bus || a != addr)) + continue; + + sr_dbg("Found USB device (VID:PID = %04x:%04x, bus.address = " + "%d.%d).", des.idVendor, des.idProduct, b, a); + + usb = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), + libusb_get_device_address(devlist[i]), NULL); + devices = g_slist_append(devices, usb); + } + libusb_free_device_list(devlist, 1); + + sr_dbg("Found %d device(s).", g_slist_length(devices)); + + return devices; +} + +/** + * Find USB devices supporting the USBTMC class + * + * @param usb_ctx libusb context to use while scanning. + * + * @return A GSList of struct sr_usb_dev_inst, with bus and address fields + * indicating devices with USBTMC support. + */ +SR_PRIV GSList *sr_usb_find_usbtmc(libusb_context *usb_ctx) +{ + struct sr_usb_dev_inst *usb; + struct libusb_device **devlist; + struct libusb_device_descriptor des; + struct libusb_config_descriptor *confdes; + const struct libusb_interface_descriptor *intfdes; + GSList *devices; + int confidx, intfidx, ret, i; + + devices = NULL; + libusb_get_device_list(usb_ctx, &devlist); + for (i = 0; devlist[i]; i++) { + if ((ret = libusb_get_device_descriptor(devlist[i], &des))) { + sr_err("Failed to get device descriptor: %s.", + libusb_error_name(ret)); + continue; + } + + for (confidx = 0; confidx < des.bNumConfigurations; confidx++) { + if (libusb_get_config_descriptor(devlist[i], confidx, &confdes) != 0) { + sr_err("Failed to get configuration descriptor."); + break; + } + for (intfidx = 0; intfidx < confdes->bNumInterfaces; intfidx++) { + intfdes = confdes->interface[intfidx].altsetting; + if (intfdes->bInterfaceClass != LIBUSB_CLASS_APPLICATION + || intfdes->bInterfaceSubClass != SUBCLASS_USBTMC + || intfdes->bInterfaceProtocol != USBTMC_USB488) + continue; + sr_dbg("Found USBTMC device (VID:PID = %04x:%04x, bus.address = " + "%d.%d).", des.idVendor, des.idProduct, + libusb_get_bus_number(devlist[i]), + libusb_get_device_address(devlist[i])); + + usb = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), + libusb_get_device_address(devlist[i]), NULL); + devices = g_slist_append(devices, usb); + } + libusb_free_config_descriptor(confdes); + } + } + libusb_free_device_list(devlist, 1); + + sr_dbg("Found %d device(s).", g_slist_length(devices)); + + return devices; +} + +SR_PRIV int sr_usb_open(libusb_context *usb_ctx, struct sr_usb_dev_inst *usb) +{ + struct libusb_device **devlist; + struct libusb_device_descriptor des; + int ret, r, cnt, i, a, b; + + sr_dbg("Trying to open USB device %d.%d.", usb->bus, usb->address); + + if ((cnt = libusb_get_device_list(usb_ctx, &devlist)) < 0) { + sr_err("Failed to retrieve device list: %s.", + libusb_error_name(cnt)); + return SR_ERR; + } + + ret = SR_ERR; + for (i = 0; i < cnt; i++) { + if ((r = libusb_get_device_descriptor(devlist[i], &des)) < 0) { + sr_err("Failed to get device descriptor: %s.", + libusb_error_name(r)); + continue; + } + + b = libusb_get_bus_number(devlist[i]); + a = libusb_get_device_address(devlist[i]); + if (b != usb->bus || a != usb->address) + continue; + + if ((r = libusb_open(devlist[i], &usb->devhdl)) < 0) { + sr_err("Failed to open device: %s.", + libusb_error_name(r)); + break; + } + + sr_dbg("Opened USB device (VID:PID = %04x:%04x, bus.address = " + "%d.%d).", des.idVendor, des.idProduct, b, a); + + ret = SR_OK; + break; + } + + libusb_free_device_list(devlist, 1); + + return ret; +} diff --git a/libsigrok4DSL/hardware/demo/Makefile.am b/libsigrok4DSL/hardware/demo/Makefile.am old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/hardware/demo/demo.c b/libsigrok4DSL/hardware/demo/demo.c old mode 100644 new mode 100755 index be5a9dee..a0732699 --- a/libsigrok4DSL/hardware/demo/demo.c +++ b/libsigrok4DSL/hardware/demo/demo.c @@ -36,11 +36,13 @@ static const int32_t sessions[] = { SR_CONF_SAMPLERATE, SR_CONF_LIMIT_SAMPLES, SR_CONF_PATTERN_MODE, + SR_CONF_MAX_HEIGHT, }; static const int32_t probeOptions[] = { SR_CONF_PROBE_COUPLING, SR_CONF_PROBE_VDIV, + SR_CONF_PROBE_MAP_DEFAULT, SR_CONF_PROBE_MAP_UNIT, SR_CONF_PROBE_MAP_MIN, SR_CONF_PROBE_MAP_MAX, @@ -49,6 +51,7 @@ static const int32_t probeOptions[] = { static const int32_t probeSessions[] = { SR_CONF_PROBE_COUPLING, SR_CONF_PROBE_VDIV, + SR_CONF_PROBE_MAP_DEFAULT, SR_CONF_PROBE_MAP_UNIT, SR_CONF_PROBE_MAP_MIN, SR_CONF_PROBE_MAP_MAX, @@ -106,21 +109,24 @@ static void adjust_samplerate(struct demo_context *devc) static void probe_init(struct sr_dev_inst *sdi) { - int i; GSList *l; + struct demo_context *devc = sdi->priv; + for (l = sdi->channels; l; l = l->next) { struct sr_channel *probe = (struct sr_channel *)l->data; + probe->bits = channel_modes[devc->ch_mode].unit_bits; probe->vdiv = 1000; probe->vfactor = 1; probe->coupling = SR_AC_COUPLING; - probe->trig_value = 0x80; - probe->vpos = (probe->index == 0 ? 0.5 : -0.5)*probe->vdiv; - probe->ms_show = TRUE; - for (i = DSO_MS_BEGIN; i < DSO_MS_END; i++) - probe->ms_en[i] = default_ms_en[i]; + probe->trig_value = (1 << (probe->bits - 1)); + probe->hw_offset = (1 << (probe->bits - 1)); + probe->offset = probe->hw_offset + + (probe->index - (channel_modes[devc->ch_mode].num - 1) /2.0) * (1 << (probe->bits - 2)); + + probe->map_default = TRUE; probe->map_unit = probeMapUnits[0]; - probe->map_min = -1; - probe->map_max = 1; + probe->map_min = -(probe->vdiv * probe->vfactor * DS_CONF_DSO_VDIVS / 2000.0); + probe->map_max = probe->vdiv * probe->vfactor * DS_CONF_DSO_VDIVS / 2000.0; } } @@ -306,6 +312,9 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, case SR_CONF_TEST: *data = g_variant_new_boolean(FALSE); break; + case SR_CONF_LANGUAGE: + *data = g_variant_new_int16(devc->language); + break; case SR_CONF_INSTANT: *data = g_variant_new_boolean(devc->instant); break; @@ -318,8 +327,11 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, case SR_CONF_MAX_HEIGHT_VALUE: *data = g_variant_new_byte(devc->max_height); break; - case SR_CONF_PROBE_VPOS: - *data = g_variant_new_double(ch->vpos); + case SR_CONF_PROBE_OFFSET: + *data = g_variant_new_uint16(ch->offset); + break; + case SR_CONF_PROBE_HW_OFFSET: + *data = g_variant_new_uint16(ch->hw_offset); break; case SR_CONF_PROBE_VDIV: *data = g_variant_new_uint64(ch->vdiv); @@ -356,6 +368,11 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, case SR_CONF_UNIT_BITS: *data = g_variant_new_byte(channel_modes[devc->ch_mode].unit_bits); break; + case SR_CONF_PROBE_MAP_DEFAULT: + if (!sdi || !ch) + return SR_ERR; + *data = g_variant_new_boolean(ch->map_default); + break; case SR_CONF_PROBE_MAP_UNIT: if (!sdi || !ch) return SR_ERR; @@ -496,7 +513,6 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, ret = SR_OK; } else if (id == SR_CONF_PROBE_VDIV) { tmp_u64 = g_variant_get_uint64(data); - ch->vpos = (tmp_u64 * 1.0 / ch->vdiv) * ch->vpos; ch->vdiv = tmp_u64; sr_dbg("%s: setting VDIV of channel %d to %" PRIu64, __func__, ch->index, ch->vdiv); @@ -506,10 +522,10 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, sr_dbg("%s: setting FACTOR of channel %d to %" PRIu64, __func__, ch->index, ch->vfactor); ret = SR_OK; - } else if (id == SR_CONF_PROBE_VPOS) { - //ch->vpos = g_variant_get_double(data); - sr_dbg("%s: setting VPOS of channel %d to %lf", __func__, - ch->index, ch->vpos); + } else if (id == SR_CONF_PROBE_OFFSET) { + ch->offset = g_variant_get_uint16(data); + sr_dbg("%s: setting OFFSET of channel %d to %d", __func__, + ch->index, ch->offset); ret = SR_OK; } else if (id == SR_CONF_TIMEBASE) { devc->timebase = g_variant_get_uint64(data); @@ -536,14 +552,34 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, sr_dbg("%s: setting channel %d Trigger Value to %d", __func__, ch->index, ch->trig_value); ret = SR_OK; + } else if (id == SR_CONF_PROBE_MAP_DEFAULT) { + ch->map_default = g_variant_get_boolean(data); + if (ch->map_default) { + ch->map_unit = probeMapUnits[0]; + ch->map_min = -(ch->vdiv * ch->vfactor * DS_CONF_DSO_VDIVS / 2000.0); + ch->map_max = ch->vdiv * ch->vfactor * DS_CONF_DSO_VDIVS / 2000.0; + } + ret = SR_OK; } else if (id == SR_CONF_PROBE_MAP_UNIT) { - ch->map_unit = g_variant_get_string(data, NULL); + if (ch->map_default) + ch->map_unit = probeMapUnits[0]; + else + ch->map_unit = g_variant_get_string(data, NULL); ret = SR_OK; } else if (id == SR_CONF_PROBE_MAP_MIN) { - ch->map_min = g_variant_get_double(data); + if (ch->map_default) + ch->map_min = -(ch->vdiv * ch->vfactor * DS_CONF_DSO_VDIVS / 2000.0); + else + ch->map_min = g_variant_get_double(data); ret = SR_OK; } else if (id == SR_CONF_PROBE_MAP_MAX) { - ch->map_max = g_variant_get_double(data); + if (ch->map_default) + ch->map_max = ch->vdiv * ch->vfactor * DS_CONF_DSO_VDIVS / 2000.0; + else + ch->map_max = g_variant_get_double(data); + ret = SR_OK; + } else if (id == SR_CONF_LANGUAGE) { + devc->language = g_variant_get_int16(data); ret = SR_OK; } else { ret = SR_ERR_NA; @@ -629,14 +665,12 @@ static void samples_generator(uint16_t *buf, uint64_t size, uint64_t i, pre0_i, pre1_i; GSList *l; struct sr_channel *probe; - int offset; unsigned int start_rand; double span = 1; const uint64_t len = ARRAY_SIZE(sinx) - 1; const int *pre_buf; uint16_t tmp_u16 = 0; unsigned int ch_num = en_ch_num(sdi) ? en_ch_num(sdi) : 1; - uint64_t index = 0; switch (devc->sample_generator) { case PATTERN_SINE: /* Sine */ @@ -659,11 +693,7 @@ static void samples_generator(uint16_t *buf, uint64_t size, break; } - if (sdi->mode == DSO && devc->samples_counter == devc->limit_samples && - size != devc->limit_samples) { - for (i = 0; i < devc->limit_samples; i++) - *(buf + i) = *(buf + ((i + size)%devc->limit_samples)); - } else if (sdi->mode == LOGIC) { + if (sdi->mode == LOGIC) { for (i = 0; i < size; i++) { //index = (i/10/g_slist_length(sdi->channels)+start_rand)%len; //*(buf + i) = (uint16_t)(((const_dc+pre_buf[index]) << 8) + (const_dc+pre_buf[index])); @@ -679,23 +709,14 @@ static void samples_generator(uint16_t *buf, uint64_t size, *(buf + i) = *(buf + i - 1); } } -// } else if (sdi->mode == ANALOG) { -// for (i = 0; i < size; i++) { -// *(buf + i) = 0x8080; -// if (i % (int)ceil(size / 7.0)) -// *(buf + i) = 0x7E7E + (rand() & 0x0300) + (rand() & 0x003); -// else if (rand() > INT_MAX / 4) -// *(buf + i) = 0x7878 + (rand() & 0x0F00) + (rand() & 0x00F); -// else if (rand() < INT_MAX / 8) -// *(buf + i) = 0x6060 + (rand() & 0x3F00) + (rand() & 0x03F); -// } } else { if (sdi->mode == DSO) { - index = devc->pre_index; - span = channel_modes[devc->ch_mode].max_samplerate / devc->cur_samplerate; + if (ch_num == 1) + span = 2 * channel_modes[devc->ch_mode].max_samplerate / devc->cur_samplerate; + else + span = channel_modes[devc->ch_mode].max_samplerate / devc->cur_samplerate; } else if (sdi->mode == ANALOG) { span = len * 20.0 / devc->limit_samples; - index = 0; } if (devc->pre_index == 0) { @@ -703,52 +724,51 @@ static void samples_generator(uint16_t *buf, uint64_t size, devc->mstatus.ch0_min = 255; devc->mstatus.ch1_max = 0; devc->mstatus.ch1_min = 255; - devc->mstatus.ch0_period = 0; - devc->mstatus.ch0_pcnt = 1; - devc->mstatus.ch1_period = 0; - devc->mstatus.ch1_pcnt = 1; + devc->mstatus.ch0_cyc_tlen = 0; + devc->mstatus.ch0_cyc_cnt = 1; + devc->mstatus.ch1_cyc_tlen = 0; + devc->mstatus.ch1_cyc_cnt = 1; + devc->mstatus.ch0_level_valid = TRUE; + devc->mstatus.ch0_plevel = TRUE; + devc->mstatus.ch1_level_valid = TRUE; + devc->mstatus.ch1_plevel = TRUE; } if (sdi->mode == DSO) - memset(buf+devc->pre_index, 0, size*sizeof(uint16_t)); + memset(buf, 0, size*sizeof(uint16_t)); else if (sdi->mode == ANALOG) memset(buf, 0, size*sizeof(uint16_t)); for (l = sdi->channels; l; l = l->next) { - if (sdi->mode == DSO) - start_rand = (devc->pre_index == 0) ? rand()%len : 0; - else - start_rand = devc->pre_index * span; + start_rand = devc->pre_index * span; probe = (struct sr_channel *)l->data; - offset = ceil((0.5 - (probe->vpos/probe->vdiv/10.0)) * 255); - //offset = 128; pre0_i = devc->pre_index; pre1_i = devc->pre_index; - for (i = index; i < index + size; i++) { + for (i = 0; i < size; i++) { if (probe->coupling == SR_DC_COUPLING) { - *(buf + i) += (uint8_t)(offset + (1000.0/probe->vdiv) * (pre_buf[(uint64_t)(i*span+start_rand)%len] - const_dc)) << (probe->index * 8); + *(buf + i) += (uint8_t)(probe->hw_offset + (1000.0/probe->vdiv) * (pre_buf[(uint64_t)(i*span+start_rand)%len] - const_dc)) << (probe->index * 8); } else if (probe->coupling == SR_AC_COUPLING) { - *(buf + i) += (uint8_t)(offset + (1000.0/probe->vdiv) * pre_buf[(uint64_t)(i*span+start_rand)%len]) << (probe->index * 8); + *(buf + i) += (uint8_t)(probe->hw_offset + (1000.0/probe->vdiv) * pre_buf[(uint64_t)(i*span+start_rand)%len]) << (probe->index * 8); } else { - *(buf + i) += offset << (probe->index * 8); + *(buf + i) += probe->hw_offset << (probe->index * 8); } if (probe->index == 0) { devc->mstatus.ch0_max = MAX(devc->mstatus.ch0_max, (*(buf + i) & 0x00ff)); devc->mstatus.ch0_min = MIN(devc->mstatus.ch0_min, (*(buf + i) & 0x00ff)); - if (i > devc->pre_index && - pre_buf[(uint64_t)(i*span+start_rand)%len] < 0 && + if (pre_buf[(uint64_t)(i*span+start_rand)%len] < 0 && pre_buf[(uint64_t)((i-1)*span+start_rand)%len] > 0) { - devc->mstatus.ch0_period = 2*(i - pre0_i)*pow(10, 8)/channel_modes[devc->ch_mode].max_samplerate; + devc->mstatus.ch0_cyc_tlen = 2*(i - pre0_i)*pow(10, 8)/channel_modes[devc->ch_mode].max_samplerate; + devc->mstatus.ch0_cyc_cnt++; pre0_i = i; } } else { devc->mstatus.ch1_max = MAX(devc->mstatus.ch1_max, ((*(buf + i) & 0xff00) >> 8)); devc->mstatus.ch1_min = MIN(devc->mstatus.ch1_min, ((*(buf + i) & 0xff00) >> 8)); - if (i > devc->pre_index && - pre_buf[(uint64_t)(i*span+start_rand)%len] < 0 && + if (pre_buf[(uint64_t)(i*span+start_rand)%len] < 0 && pre_buf[(uint64_t)((i-1)*span+start_rand)%len] > 0) { - devc->mstatus.ch1_period = 2*(i - pre1_i)*pow(10, 8)/channel_modes[devc->ch_mode].max_samplerate; + devc->mstatus.ch1_cyc_tlen = 2*(i - pre1_i)*pow(10, 8)/channel_modes[devc->ch_mode].max_samplerate; + devc->mstatus.ch1_cyc_cnt++; pre1_i = i; } } @@ -765,6 +785,39 @@ static void samples_generator(uint16_t *buf, uint64_t size, break; } } + + devc->mstatus.ch0_cyc_tlen *= devc->mstatus.ch0_cyc_cnt; + devc->mstatus.ch1_cyc_tlen *= devc->mstatus.ch1_cyc_cnt; + + devc->mstatus.ch0_high_level = devc->mstatus.ch0_max; + devc->mstatus.ch0_low_level = devc->mstatus.ch0_min; + devc->mstatus.ch1_high_level = devc->mstatus.ch1_max; + devc->mstatus.ch1_low_level = devc->mstatus.ch1_min; + devc->mstatus.ch0_cyc_llen = 0; + devc->mstatus.ch1_cyc_llen = 0; + devc->mstatus.ch0_cyc_plen = devc->mstatus.ch0_cyc_tlen / 2; + devc->mstatus.ch1_cyc_plen = devc->mstatus.ch1_cyc_tlen / 2; + devc->mstatus.ch0_cyc_rlen = devc->mstatus.ch0_cyc_tlen / 4; + devc->mstatus.ch0_cyc_flen = devc->mstatus.ch0_cyc_tlen / 4; + devc->mstatus.ch1_cyc_rlen = devc->mstatus.ch1_cyc_tlen / 4; + devc->mstatus.ch1_cyc_flen = devc->mstatus.ch1_cyc_tlen / 4; + + for (l = sdi->channels; l; l = l->next) { + probe = (struct sr_channel *)l->data; + if (probe->index == 0) { + devc->mstatus.ch0_acc_mean = (probe->coupling == SR_AC_COUPLING) ? probe->hw_offset * devc->limit_samples_show : + (devc->mstatus.ch0_max + devc->mstatus.ch0_min) / 2.0 * devc->limit_samples_show; + devc->mstatus.ch0_acc_square = (probe->coupling == SR_AC_COUPLING) ? pow((devc->mstatus.ch0_max - probe->hw_offset) * 0.707, 2) * devc->limit_samples_show : + pow((devc->mstatus.ch0_max - devc->mstatus.ch0_min) * 0.707, 2) * devc->limit_samples_show; + } else { + devc->mstatus.ch1_acc_mean = (probe->coupling == SR_AC_COUPLING) ? probe->hw_offset * devc->limit_samples_show : + (devc->mstatus.ch1_max + devc->mstatus.ch1_min) / 2.0 * devc->limit_samples_show; + devc->mstatus.ch1_acc_square = (probe->coupling == SR_AC_COUPLING) ? pow((devc->mstatus.ch1_max - probe->hw_offset) * 0.707, 2) * devc->limit_samples_show : + pow((devc->mstatus.ch1_max - devc->mstatus.ch1_min) * 0.707, 2) * devc->limit_samples_show; + } + } + + devc->mstatus.measure_valid = TRUE; } } @@ -798,12 +851,8 @@ static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) if (devc->limit_samples) { if (sdi->mode == DSO && !devc->instant) { samples_to_send = ceil(samples_elaspsed); - samples_to_send = MIN(samples_to_send, - devc->limit_samples - devc->pre_index); } else if (sdi->mode == ANALOG) { samples_to_send = ceil(samples_elaspsed); - samples_to_send = MIN(samples_to_send, - devc->limit_samples - devc->pre_index); } else { samples_to_send = ceil(samples_elaspsed); samples_to_send += devc->samples_not_sent; @@ -820,8 +869,22 @@ static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) if (samples_to_send > 0 && !devc->stop) { sending_now = MIN(samples_to_send, (sdi->mode == DSO ) ? DSO_BUFSIZE : BUFSIZE); + if (sdi->mode == DSO && !devc->instant) { + if (en_ch_num(sdi) == 1) { + devc->samples_counter += sending_now / 2; + devc->samples_counter = min(devc->samples_counter, devc->limit_samples_show / 2); + } else { + devc->samples_counter += sending_now; + devc->samples_counter = min(devc->samples_counter, devc->limit_samples_show); + } + } else { + devc->samples_counter += sending_now; + } + if (sdi->mode == ANALOG) samples_generator(devc->buf, sending_now*2, sdi, devc); + else if (sdi->mode == DSO) + samples_generator(devc->buf, devc->samples_counter, sdi, devc); else samples_generator(devc->buf, sending_now, sdi, devc); @@ -856,11 +919,6 @@ static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) } } - devc->samples_counter += sending_now; - if (sdi->mode == DSO && !devc->instant && - devc->samples_counter > devc->limit_samples) - devc->samples_counter = devc->limit_samples; - if (devc->trigger_stage == 0){ //samples_to_send -= sending_now; if (sdi->mode == LOGIC) { @@ -896,11 +954,12 @@ static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) } if (sdi->mode == DSO && !devc->instant) { - devc->pre_index += sending_now; - if (devc->pre_index >= devc->limit_samples) + if ((uint64_t)dso.num_samples < devc->limit_samples_show) devc->pre_index = 0; + else + devc->pre_index += sending_now; } else if (sdi->mode == ANALOG) { - devc->pre_index =(devc->pre_index + sending_now) % devc->limit_samples; + devc->pre_index += sending_now; } sr_session_send(sdi, &packet); diff --git a/libsigrok4DSL/hardware/demo/demo.h b/libsigrok4DSL/hardware/demo/demo.h old mode 100644 new mode 100755 index d6ef2cd7..9ee4c8d0 --- a/libsigrok4DSL/hardware/demo/demo.h +++ b/libsigrok4DSL/hardware/demo/demo.h @@ -1,360 +1,362 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2013 Bert Vermeulen - * Copyright (C) 2013 DreamSourceLab - * - * 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 . - */ - -#ifndef LIBDSL_HARDWARE_DEMO_H -#define LIBDSL_HARDWARE_DEMO_H - -#include -#include "libsigrok.h" -#include "libsigrok-internal.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#ifdef _WIN32 -#include -#include -#define pipe(fds) _pipe(fds, 4096, _O_BINARY) -#endif - -#undef min -#define min(a,b) ((a)<(b)?(a):(b)) -#undef max -#define max(a,b) ((a)>(b)?(a):(b)) - -/* Message logging helpers with subsystem-specific prefix string. */ -#define LOG_PREFIX "demo: " -#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) -#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) -#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) -#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) -#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) -#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) - -/* hardware Capabilities */ -#define CAPS_MODE_LOGIC (1 << 0) -#define CAPS_MODE_ANALOG (1 << 1) -#define CAPS_MODE_DSO (1 << 2) - -#define CAPS_FEATURE_NONE 0 -// zero calibration ability -#define CAPS_FEATURE_ZERO (1 << 4) -/* end */ - -static struct sr_dev_mode mode_list[] = { - {"LA", LOGIC}, - {"DAQ", ANALOG}, - {"OSC", DSO}, -}; - -/* Supported patterns which we can generate */ -enum DEMO_PATTERN { - PATTERN_SINE = 0, - PATTERN_SQUARE = 1, - PATTERN_TRIANGLE = 2, - PATTERN_SAWTOOTH = 3, - PATTERN_RANDOM = 4, -}; - -static const char *pattern_strings[] = { - "Sine", - "Square", - "Triangle", - "Sawtooth", - "Random", -}; - -struct DEMO_caps { - uint64_t mode_caps; - uint64_t feature_caps; - uint64_t channels; - uint64_t hw_depth; - uint64_t dso_depth; - uint8_t intest_channel; - const uint64_t *vdivs; - uint8_t vga_id; - uint16_t default_channelmode; - enum DEMO_PATTERN default_pattern; - uint64_t default_timebase; -}; - -struct DEMO_profile { - const char *vendor; - const char *model; - const char *model_version; - - struct DEMO_caps dev_caps; -}; - -static const uint64_t vdivs10to2000[] = { - SR_mV(10), - SR_mV(20), - SR_mV(50), - SR_mV(100), - SR_mV(200), - SR_mV(500), - SR_V(1), - SR_V(2), - 0, -}; - -enum CHANNEL_ID { - DEMO_LOGIC100x16 = 0, - DEMO_ANALOG10x2, - DEMO_DSO200x2, -}; - -struct DEMO_channels { - enum CHANNEL_ID id; - enum OPERATION_MODE mode; - enum CHANNEL_TYPE type; - - uint16_t num; - uint8_t unit_bits; - uint64_t default_samplerate; - uint64_t default_samplelimit; - uint64_t min_samplerate; - uint64_t max_samplerate; - - const char *descr; -}; - -static const struct DEMO_channels channel_modes[] = { - // LA Stream - {DEMO_LOGIC100x16, LOGIC, SR_CHANNEL_LOGIC, 16, 1, SR_MHZ(1), SR_Mn(1), - SR_KHZ(10), SR_MHZ(100), "Use 16 Channels (Max 20MHz)"}, - - // DAQ - {DEMO_ANALOG10x2, ANALOG, SR_CHANNEL_ANALOG, 2, 8, SR_MHZ(1), SR_Mn(1), - SR_HZ(10), SR_MHZ(10), "Use Channels 0~1 (Max 10MHz)"}, - - // OSC - {DEMO_DSO200x2, DSO, SR_CHANNEL_DSO, 2, 8, SR_MHZ(100), SR_Kn(10), - SR_HZ(100), SR_MHZ(200), "Use Channels 0~1 (Max 200MHz)"} -}; - -static const struct DEMO_profile supported_Demo[] = { - /* - * Demo - */ - {"DreamSourceLab", "Demo Device", NULL, - {CAPS_MODE_LOGIC | CAPS_MODE_ANALOG | CAPS_MODE_DSO, - CAPS_FEATURE_NONE, - (1 << DEMO_LOGIC100x16) | - (1 << DEMO_ANALOG10x2) | - (1 << DEMO_DSO200x2), - SR_Mn(100), - SR_Kn(20), - 0, - vdivs10to2000, - 0, - DEMO_LOGIC100x16, - PATTERN_SINE, - SR_NS(500)} - }, - - { 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} -}; - -struct demo_context { - const struct DEMO_profile *profile; - - int pipe_fds[2]; - GIOChannel *channel; - uint64_t cur_samplerate; - uint64_t limit_samples; - uint64_t limit_samples_show; - uint64_t limit_msec; - uint8_t sample_generator; - uint64_t samples_counter; - void *cb_data; - int64_t starttime; - int stop; - uint64_t timebase; - enum CHANNEL_ID ch_mode; - uint16_t samplerates_min_index; - uint16_t samplerates_max_index; - gboolean instant; - uint8_t max_height; - uint64_t samples_not_sent; - - uint16_t *buf; - uint64_t pre_index; - struct sr_status mstatus; - - int trigger_stage; - uint16_t trigger_mask; - uint16_t trigger_value; - uint16_t trigger_edge; - uint8_t trigger_slope; - uint8_t trigger_source; -}; - -static const uint64_t samplerates[] = { - SR_HZ(10), - SR_HZ(20), - SR_HZ(50), - SR_HZ(100), - SR_HZ(200), - SR_HZ(500), - SR_KHZ(1), - SR_KHZ(2), - SR_KHZ(5), - SR_KHZ(10), - SR_KHZ(20), - SR_KHZ(40), - SR_KHZ(50), - SR_KHZ(100), - SR_KHZ(200), - SR_KHZ(400), - SR_KHZ(500), - SR_MHZ(1), - SR_MHZ(2), - SR_MHZ(4), - SR_MHZ(5), - SR_MHZ(10), - SR_MHZ(20), - SR_MHZ(25), - SR_MHZ(40), - SR_MHZ(50), - SR_MHZ(100), - SR_MHZ(200), - SR_MHZ(400), - SR_MHZ(500), - SR_MHZ(800), - SR_GHZ(1), - SR_GHZ(2), - SR_GHZ(5), - SR_GHZ(10), -}; - -static const char *probeMapUnits[] = { - "V", - "A", - "°C", - "°F", - "g", - "m", - "m/s", -}; - -/* We name the probes 0-7 on our demo driver. */ -static const char *probe_names[] = { - "0", "1", "2", "3", - "4", "5", "6", "7", - "8", "9", "10", "11", - "12", "13", "14", "15", - NULL, -}; - -static const gboolean default_ms_en[] = { - FALSE, /* DSO_MS_BEGIN */ - TRUE, /* DSO_MS_FREQ */ - FALSE, /* DSO_MS_PERD */ - TRUE, /* DSO_MS_VMAX */ - TRUE, /* DSO_MS_VMIN */ - FALSE, /* DSO_MS_VRMS */ - FALSE, /* DSO_MS_VMEA */ - FALSE, /* DSO_MS_VP2P */ -}; - -static const char *maxHeights[] = { - "1X", - "2X", - "3X", - "4X", - "5X", -}; - -static const int const_dc = 1.95 / 10 * 255; -static const int sinx[] = { - 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 18, 20, 21, 23, 24, 26, 27, 28, - 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 41, 42, 43, 44, 45, 45, 46, 47, 47, - 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, 48, 48, - 47, 47, 46, 46, 45, 44, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 31, 30, - 29, 28, 26, 25, 24, 22, 21, 19, 18, 16, 15, 13, 12, 10, 9, 7, 6, 4, 2, 1, - -1, -2, -4, -6, -7, -9, -10, -12, -13, -15, -16, -18, -19, -21, -22, -24, -25, -26, -28, -29, --30, -31, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -44, -45, -46, -46, -47, -47, --48, -48, -49, -49, -49, -50, -50, -50, -50, -50, -50, -50, -50, -50, -49, -49, -49, -49, -48, -48, --47, -47, -46, -45, -45, -44, -43, -42, -41, -41, -40, -39, -38, -37, -35, -34, -33, -32, -31, -30, --28, -27, -26, -24, -23, -21, -20, -18, -17, -16, -14, -12, -11, -9, -8, -6, -5, -3, -2, 0, -}; - -static const int sqrx[] = { - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, --50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, --50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, --50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, --50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, --50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -}; - -static const int trix[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, - 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, - 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, - 0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, --20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, --40, -41, -42, -43, -44, -45, -46, -47, -48, -49, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, --40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, --20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, -}; - -static const int sawx[] = { - 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, - 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, - 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, - 30, 30, 31, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, 39, 39, - 40, 40, 41, 41, 42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47, 48, 48, 49, 50, --50, -49, -48, -48, -47, -47, -46, -46, -45, -45, -44, -44, -43, -43, -42, -42, -41, -41, -40, -40, --39, -39, -38, -38, -37, -37, -36, -36, -35, -35, -34, -34, -33, -33, -32, -32, -31, -31, -30, -30, --29, -29, -28, -28, -27, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, --19, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, - -9, -9, -8, -8, -7, -7, -6, -6, -5, -5, -4, -4, -3, -3, -2, -2, -1, -1, 0, 0, -}; - -static const int ranx[] = { - -4, 47, -49, -1, -3, 6, -29, 26, 1, 14, -39, -38, 36, 17, 26, -37, -2, 27, -20, -15, --49, -46, 36, 16, 29, 23, -30, -3, 28, -2, -6, 46, 43, 50, -42, 30, 48, -50, -38, -30, - 7, -36, -20, -24, -10, -34, -24, 3, -48, 46, -11, 22, 19, 28, 39, -49, -31, 34, 2, -29, - 9, 35, 8, 10, 38, 30, 17, 48, -3, -6, -28, 46, -19, 18, -43, -9, -31, -32, -41, 16, --10, 46, -4, 4, -32, -43, -45, -39, -33, 28, 24, -17, -43, 42, -7, 36, -44, -5, 9, 39, - 17, -40, 12, 16, -42, -1, 2, -9, 50, -8, 27, 27, 14, 8, -18, 12, -8, 26, -8, 12, --35, 49, 35, 2, -26, -24, -31, 33, 15, -47, 34, 46, -1, -12, 14, 32, -25, -31, -35, -18, --48, -21, -5, 1, -27, -14, 12, 49, -11, 33, 31, 35, -36, 19, 20, 44, 29, -48, 14, -43, - 1, 30, -12, 44, 20, 49, 29, -43, 42, 30, -34, 24, 20, -40, 33, -12, 13, -45, 45, -24, --41, 36, -8, 46, 47, -34, 28, -39, 7, -32, 38, -27, 28, -3, -8, 43, -37, -24, 6, 3, -}; - -#endif +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2013 Bert Vermeulen + * Copyright (C) 2013 DreamSourceLab + * + * 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 . + */ + +#ifndef LIBDSL_HARDWARE_DEMO_H +#define LIBDSL_HARDWARE_DEMO_H + +#include +#include "libsigrok.h" +#include "libsigrok-internal.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#ifdef _WIN32 +#include +#include +#define pipe(fds) _pipe(fds, 4096, _O_BINARY) +#endif + +#undef min +#define min(a,b) ((a)<(b)?(a):(b)) +#undef max +#define max(a,b) ((a)>(b)?(a):(b)) + +/* Message logging helpers with subsystem-specific prefix string. */ +#define LOG_PREFIX "demo: " +#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) +#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) +#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) +#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) +#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) +#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) + +/* hardware Capabilities */ +#define CAPS_MODE_LOGIC (1 << 0) +#define CAPS_MODE_ANALOG (1 << 1) +#define CAPS_MODE_DSO (1 << 2) + +#define CAPS_FEATURE_NONE 0 +// zero calibration ability +#define CAPS_FEATURE_ZERO (1 << 4) +/* end */ + +static struct sr_dev_mode mode_list[] = { + {LOGIC, "Logic Analyzer", "逻辑分析仪", "la.png"}, + {ANALOG, "Data Acquisition", "数据记录仪", "daq.png"}, + {DSO, "Oscilloscope", "示波器", "osc.png"}, +}; + +/* Supported patterns which we can generate */ +enum DEMO_PATTERN { + PATTERN_SINE = 0, + PATTERN_SQUARE = 1, + PATTERN_TRIANGLE = 2, + PATTERN_SAWTOOTH = 3, + PATTERN_RANDOM = 4, +}; + +static const char *pattern_strings[] = { + "Sine", + "Square", + "Triangle", + "Sawtooth", + "Random", +}; + +struct DEMO_caps { + uint64_t mode_caps; + uint64_t feature_caps; + uint64_t channels; + uint64_t hw_depth; + uint64_t dso_depth; + uint8_t intest_channel; + const uint64_t *vdivs; + uint8_t vga_id; + uint16_t default_channelmode; + enum DEMO_PATTERN default_pattern; + uint64_t default_timebase; +}; + +struct DEMO_profile { + const char *vendor; + const char *model; + const char *model_version; + + struct DEMO_caps dev_caps; +}; + +static const uint64_t vdivs10to2000[] = { + SR_mV(10), + SR_mV(20), + SR_mV(50), + SR_mV(100), + SR_mV(200), + SR_mV(500), + SR_V(1), + SR_V(2), + 0, +}; + +enum CHANNEL_ID { + DEMO_LOGIC100x16 = 0, + DEMO_ANALOG10x2, + DEMO_DSO200x2, +}; + +struct DEMO_channels { + enum CHANNEL_ID id; + enum OPERATION_MODE mode; + enum CHANNEL_TYPE type; + + uint16_t num; + uint8_t unit_bits; + uint64_t default_samplerate; + uint64_t default_samplelimit; + uint64_t min_samplerate; + uint64_t max_samplerate; + + const char *descr; +}; + +static const struct DEMO_channels channel_modes[] = { + // LA Stream + {DEMO_LOGIC100x16, LOGIC, SR_CHANNEL_LOGIC, 16, 1, SR_MHZ(1), SR_Mn(1), + SR_KHZ(10), SR_MHZ(100), "Use 16 Channels (Max 20MHz)"}, + + // DAQ + {DEMO_ANALOG10x2, ANALOG, SR_CHANNEL_ANALOG, 2, 8, SR_MHZ(1), SR_Mn(1), + SR_HZ(10), SR_MHZ(10), "Use Channels 0~1 (Max 10MHz)"}, + + // OSC + {DEMO_DSO200x2, DSO, SR_CHANNEL_DSO, 2, 8, SR_MHZ(100), SR_Kn(10), + SR_HZ(100), SR_MHZ(200), "Use Channels 0~1 (Max 200MHz)"} +}; + +static const struct DEMO_profile supported_Demo[] = { + /* + * Demo + */ + {"DreamSourceLab", "Demo Device", NULL, + {CAPS_MODE_LOGIC | CAPS_MODE_ANALOG | CAPS_MODE_DSO, + CAPS_FEATURE_NONE, + (1 << DEMO_LOGIC100x16) | + (1 << DEMO_ANALOG10x2) | + (1 << DEMO_DSO200x2), + SR_Mn(100), + SR_Kn(20), + 0, + vdivs10to2000, + 0, + DEMO_LOGIC100x16, + PATTERN_SINE, + SR_NS(500)} + }, + + { 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} +}; + +struct demo_context { + const struct DEMO_profile *profile; + + int pipe_fds[2]; + GIOChannel *channel; + uint64_t cur_samplerate; + uint64_t limit_samples; + uint64_t limit_samples_show; + uint64_t limit_msec; + uint8_t sample_generator; + uint64_t samples_counter; + void *cb_data; + int64_t starttime; + int stop; + uint64_t timebase; + enum CHANNEL_ID ch_mode; + uint16_t samplerates_min_index; + uint16_t samplerates_max_index; + gboolean instant; + uint8_t max_height; + uint64_t samples_not_sent; + + uint16_t *buf; + uint64_t pre_index; + struct sr_status mstatus; + + int trigger_stage; + uint16_t trigger_mask; + uint16_t trigger_value; + uint16_t trigger_edge; + uint8_t trigger_slope; + uint8_t trigger_source; + + int language; +}; + +static const uint64_t samplerates[] = { + SR_HZ(10), + SR_HZ(20), + SR_HZ(50), + SR_HZ(100), + SR_HZ(200), + SR_HZ(500), + SR_KHZ(1), + SR_KHZ(2), + SR_KHZ(5), + SR_KHZ(10), + SR_KHZ(20), + SR_KHZ(40), + SR_KHZ(50), + SR_KHZ(100), + SR_KHZ(200), + SR_KHZ(400), + SR_KHZ(500), + SR_MHZ(1), + SR_MHZ(2), + SR_MHZ(4), + SR_MHZ(5), + SR_MHZ(10), + SR_MHZ(20), + SR_MHZ(25), + SR_MHZ(40), + SR_MHZ(50), + SR_MHZ(100), + SR_MHZ(200), + SR_MHZ(400), + SR_MHZ(500), + SR_MHZ(800), + SR_GHZ(1), + SR_GHZ(2), + SR_GHZ(5), + SR_GHZ(10), +}; + +static const char *probeMapUnits[] = { + "V", + "A", + "°C", + "°F", + "g", + "m", + "m/s", +}; + +/* We name the probes 0-7 on our demo driver. */ +static const char *probe_names[] = { + "0", "1", "2", "3", + "4", "5", "6", "7", + "8", "9", "10", "11", + "12", "13", "14", "15", + NULL, +}; + +static const gboolean default_ms_en[] = { + FALSE, /* DSO_MS_BEGIN */ + TRUE, /* DSO_MS_FREQ */ + FALSE, /* DSO_MS_PERD */ + TRUE, /* DSO_MS_VMAX */ + TRUE, /* DSO_MS_VMIN */ + FALSE, /* DSO_MS_VRMS */ + FALSE, /* DSO_MS_VMEA */ + FALSE, /* DSO_MS_VP2P */ +}; + +static const char *maxHeights[] = { + "1X", + "2X", + "3X", + "4X", + "5X", +}; + +static const int const_dc = 1.95 / 10 * 255; +static const int sinx[] = { + 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 18, 20, 21, 23, 24, 26, 27, 28, + 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 41, 42, 43, 44, 45, 45, 46, 47, 47, + 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, 48, 48, + 47, 47, 46, 46, 45, 44, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 31, 30, + 29, 28, 26, 25, 24, 22, 21, 19, 18, 16, 15, 13, 12, 10, 9, 7, 6, 4, 2, 1, + -1, -2, -4, -6, -7, -9, -10, -12, -13, -15, -16, -18, -19, -21, -22, -24, -25, -26, -28, -29, +-30, -31, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -44, -45, -46, -46, -47, -47, +-48, -48, -49, -49, -49, -50, -50, -50, -50, -50, -50, -50, -50, -50, -49, -49, -49, -49, -48, -48, +-47, -47, -46, -45, -45, -44, -43, -42, -41, -41, -40, -39, -38, -37, -35, -34, -33, -32, -31, -30, +-28, -27, -26, -24, -23, -21, -20, -18, -17, -16, -14, -12, -11, -9, -8, -6, -5, -3, -2, 0, +}; + +static const int sqrx[] = { + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, +-50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, +-50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, +-50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, +-50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, +-50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, +}; + +static const int trix[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, + 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + 0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, +-20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, +-40, -41, -42, -43, -44, -45, -46, -47, -48, -49, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, +-40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, +-20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, +}; + +static const int sawx[] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, + 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, + 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, + 30, 30, 31, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, 39, 39, + 40, 40, 41, 41, 42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47, 48, 48, 49, 50, +-50, -49, -48, -48, -47, -47, -46, -46, -45, -45, -44, -44, -43, -43, -42, -42, -41, -41, -40, -40, +-39, -39, -38, -38, -37, -37, -36, -36, -35, -35, -34, -34, -33, -33, -32, -32, -31, -31, -30, -30, +-29, -29, -28, -28, -27, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, +-19, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, + -9, -9, -8, -8, -7, -7, -6, -6, -5, -5, -4, -4, -3, -3, -2, -2, -1, -1, 0, 0, +}; + +static const int ranx[] = { + -4, 47, -49, -1, -3, 6, -29, 26, 1, 14, -39, -38, 36, 17, 26, -37, -2, 27, -20, -15, +-49, -46, 36, 16, 29, 23, -30, -3, 28, -2, -6, 46, 43, 50, -42, 30, 48, -50, -38, -30, + 7, -36, -20, -24, -10, -34, -24, 3, -48, 46, -11, 22, 19, 28, 39, -49, -31, 34, 2, -29, + 9, 35, 8, 10, 38, 30, 17, 48, -3, -6, -28, 46, -19, 18, -43, -9, -31, -32, -41, 16, +-10, 46, -4, 4, -32, -43, -45, -39, -33, 28, 24, -17, -43, 42, -7, 36, -44, -5, 9, 39, + 17, -40, 12, 16, -42, -1, 2, -9, 50, -8, 27, 27, 14, 8, -18, 12, -8, 26, -8, 12, +-35, 49, 35, 2, -26, -24, -31, 33, 15, -47, 34, 46, -1, -12, 14, 32, -25, -31, -35, -18, +-48, -21, -5, 1, -27, -14, 12, 49, -11, 33, 31, 35, -36, 19, 20, 44, 29, -48, 14, -43, + 1, 30, -12, 44, 20, 49, 29, -43, 42, 30, -34, 24, 20, -40, 33, -12, 13, -45, 45, -24, +-41, 36, -8, 46, 47, -34, 28, -39, 7, -32, 38, -27, 28, -3, -8, 43, -37, -24, 6, 3, +}; + +#endif diff --git a/libsigrok4DSL/hwdriver.c b/libsigrok4DSL/hwdriver.c old mode 100644 new mode 100755 index 9adc1f65..2d921dc1 --- a/libsigrok4DSL/hwdriver.c +++ b/libsigrok4DSL/hwdriver.c @@ -51,77 +51,81 @@ */ static struct sr_config_info sr_config_info_data[] = { {SR_CONF_CONN, SR_T_CHAR, "conn", - "Connection", "Connection", NULL}, + "Connection", "Connection", "连接", NULL}, {SR_CONF_SERIALCOMM, SR_T_CHAR, "serialcomm", - "Serial communication", "Serial communication", NULL}, + "Serial communication", "Serial communication", "串口通讯", NULL}, {SR_CONF_SAMPLERATE, SR_T_UINT64, "samplerate", - "Sample rate", "Sample rate", NULL}, + "Sample rate", "Sample rate", "采样率", NULL}, {SR_CONF_LIMIT_SAMPLES, SR_T_UINT64, "samplecount", - "Sample count", "Sample count", NULL}, + "Sample count", "Sample count", "采样深度", NULL}, {SR_CONF_ACTUAL_SAMPLES, SR_T_UINT64, "samplecount", - "Sample count", "Sample count", NULL}, + "Sample count", "Sample count", "实际采样数", NULL}, {SR_CONF_CLOCK_TYPE, SR_T_BOOL, "clocktype", - "Using External Clock", "Using External Clock", NULL}, + "Using External Clock", "Using External Clock", "使用外部输入时钟采样", NULL}, {SR_CONF_CLOCK_EDGE, SR_T_BOOL, "clockedge", - "Using Clock Negedge", "Using Clock Negedge", NULL}, + "Using Clock Negedge", "Using Clock Negedge", "使用时钟下降沿采样", NULL}, {SR_CONF_CAPTURE_RATIO, SR_T_UINT64, "captureratio", - "Pre-trigger capture ratio", "Pre-trigger capture ratio", NULL}, + "Pre-trigger capture ratio", "Pre-trigger capture ratio", "触发前采样比例", NULL}, {SR_CONF_PATTERN_MODE, SR_T_CHAR, "pattern", - "Pattern mode", "Pattern mode", NULL}, + "Pattern mode", "Pattern mode", "信号模式", NULL}, {SR_CONF_RLE, SR_T_BOOL, "rle", - "Run Length Encoding", "Run Length Encoding", NULL}, + "Run Length Encoding", "Run Length Encoding", "RLE编码", NULL}, {SR_CONF_WAIT_UPLOAD, SR_T_BOOL, "buf_upload", - "Wait Buffer Upload", "Wait Buffer Upload", NULL}, + "Wait Buffer Upload", "Wait Buffer Upload", "上传已采集数据", NULL}, {SR_CONF_TRIGGER_SLOPE, SR_T_UINT8, "triggerslope", - "Trigger slope", "Trigger slope", NULL}, + "Trigger slope", "Trigger slope", "触发沿", NULL}, {SR_CONF_TRIGGER_SOURCE, SR_T_UINT8, "triggersource", - "Trigger source", "Trigger source", NULL}, + "Trigger source", "Trigger source", "触发源", NULL}, {SR_CONF_TRIGGER_CHANNEL, SR_T_UINT8, "triggerchannel", - "Trigger channel", "Trigger channel", NULL}, + "Trigger channel", "Trigger channel", "触发通道", NULL}, {SR_CONF_HORIZ_TRIGGERPOS, SR_T_UINT8, "horiz_triggerpos", - "Horizontal trigger position", "Horizontal trigger position", NULL}, + "Horizontal trigger position", "Horizontal trigger position", "触发位置", NULL}, {SR_CONF_TRIGGER_HOLDOFF, SR_T_UINT64, "triggerholdoff", - "Trigger hold off", "Trigger hold off", NULL}, + "Trigger hold off", "Trigger hold off", "触发释抑时间", NULL}, {SR_CONF_TRIGGER_MARGIN, SR_T_UINT8, "triggermargin", - "Trigger margin", "Trigger margin", NULL}, + "Trigger margin", "Trigger margin", "触发灵敏度", NULL}, {SR_CONF_BUFFERSIZE, SR_T_UINT64, "buffersize", - "Buffer size", "Buffer size", NULL}, + "Buffer size", "Buffer size", "缓存大小", NULL}, {SR_CONF_TIMEBASE, SR_T_UINT64, "timebase", - "Time base", "Time base", NULL}, + "Time base", "Time base", "时基", NULL}, {SR_CONF_MAX_HEIGHT, SR_T_CHAR, "height", - "Max Height", "Max Height", NULL}, + "Max Height", "Max Height", "最大高度", NULL}, {SR_CONF_MAX_HEIGHT_VALUE, SR_T_UINT8, "height", - "Max Height", "Max Height", NULL}, + "Max Height", "Max Height", "最大高度值", NULL}, {SR_CONF_FILTER, SR_T_CHAR, "filter", - "Filter Targets", "Filter Targets", NULL}, + "Filter Targets", "Filter Targets", "滤波器设置", NULL}, {SR_CONF_DATALOG, SR_T_BOOL, "datalog", - "Datalog", "Datalog", NULL}, + "Datalog", "Datalog", "数据记录", NULL}, {SR_CONF_OPERATION_MODE, SR_T_CHAR, "operation", - "Operation Mode", "Operation Mode", NULL}, + "Operation Mode", "Operation Mode", "运行模式", NULL}, {SR_CONF_BUFFER_OPTIONS, SR_T_CHAR, "stopoptions", - "Stop Options", "Stop Options", NULL}, + "Stop Options", "Stop Options", "停止选项", NULL}, {SR_CONF_CHANNEL_MODE, SR_T_CHAR, "channel", - "Channel Mode", "Channel Mode", NULL}, + "Channel Mode", "Channel Mode", "通道模式", NULL}, {SR_CONF_THRESHOLD, SR_T_CHAR, "threshold", - "Threshold Level", "Threshold Level", NULL}, + "Threshold Level", "Threshold Level", "阈值电压", NULL}, {SR_CONF_VTH, SR_T_FLOAT, "threshold", - "Threshold Level", "Threshold Level", NULL}, + "Threshold Level", "Threshold Level", "阈值电压", NULL}, {SR_CONF_RLE_SUPPORT, SR_T_BOOL, "rle", - "Enable RLE Compress", "Enable RLE Compress", NULL}, + "Enable RLE Compress", "Enable RLE Compress", "RLE硬件压缩", NULL}, + {SR_CONF_BANDWIDTH_LIMIT, SR_T_CHAR, "bandwidth", + "Bandwidth Limit", "Bandwidth Limit", "带宽限制", NULL}, {SR_CONF_PROBE_COUPLING, SR_T_CHAR, "coupling", - "Coupling", "Coupling", NULL}, + "Coupling", "Coupling", "耦合", NULL}, {SR_CONF_PROBE_VDIV, SR_T_RATIONAL_VOLT, "vdiv", - "Volts/div", "Volts/div", NULL}, + "Volts/div", "Volts/div", "电压/格", NULL}, {SR_CONF_PROBE_FACTOR, SR_T_UINT64, "factor", - "Probe Factor", "Probe Factor", NULL}, + "Probe Factor", "Probe Factor", "探头衰减", NULL}, + {SR_CONF_PROBE_MAP_DEFAULT, SR_T_BOOL, "mdefault", + "Map Default", "Map Default", "默认电压", NULL}, {SR_CONF_PROBE_MAP_UNIT, SR_T_CHAR, "munit", - "Map Unit", "Map Unit", NULL}, + "Map Unit", "Map Unit", "对应单位", NULL}, {SR_CONF_PROBE_MAP_MIN, SR_T_FLOAT, "MMIN", - "Map Min", "Map Min", NULL}, + "Map Min", "Map Min", "对应最小值", NULL}, {SR_CONF_PROBE_MAP_MAX, SR_T_FLOAT, "MMAX", - "Map Max", "Map Max", NULL}, - {0, 0, NULL, NULL, NULL, NULL}, + "Map Max", "Map Max", "对应最大值", NULL}, + {0, 0, NULL, NULL, NULL, NULL, NULL}, }; /** @cond PRIVATE */ diff --git a/libsigrok4DSL/input/Makefile.am b/libsigrok4DSL/input/Makefile.am old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/input/in_binary.c b/libsigrok4DSL/input/in_binary.c old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/input/in_vcd.c b/libsigrok4DSL/input/in_vcd.c old mode 100644 new mode 100755 index c4dcd523..41ff6c0b --- a/libsigrok4DSL/input/in_vcd.c +++ b/libsigrok4DSL/input/in_vcd.c @@ -63,6 +63,7 @@ #include #include #include +#include /* Message logging helpers with subsystem-specific prefix string. */ #define LOG_PREFIX "input/vcd: " @@ -97,10 +98,10 @@ static gboolean read_until(FILE *file, GString *dest, char mode) return FALSE; } - if (mode == 'W' && g_ascii_isspace(c)) + if (mode == 'W' && isspace(c)) return TRUE; - if (mode == 'N' && !g_ascii_isspace(c)) + if (mode == 'N' && !isspace(c)) { ungetc(c, file); return TRUE; @@ -436,7 +437,7 @@ static void parse_contents(FILE *file, const struct sr_dev_inst *sdi, struct con /* Read one space-delimited token at a time. */ while (read_until(file, NULL, 'N') && read_until(file, token, 'W')) { - if (token->str[0] == '#' && g_ascii_isdigit(token->str[1])) + if (token->str[0] == '#' && isdigit(token->str[1])) { /* Numeric value beginning with # is a new timestamp value */ uint64_t timestamp; diff --git a/libsigrok4DSL/input/in_wav.c b/libsigrok4DSL/input/in_wav.c old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/input/input.c b/libsigrok4DSL/input/input.c old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/libsigrok-internal.h b/libsigrok4DSL/libsigrok-internal.h old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/libsigrok.h b/libsigrok4DSL/libsigrok.h old mode 100644 new mode 100755 index dbecf032..872f53e7 --- a/libsigrok4DSL/libsigrok.h +++ b/libsigrok4DSL/libsigrok.h @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -308,11 +309,24 @@ enum DSO_MEASURE_TYPE { DSO_MS_BEGIN = 0, DSO_MS_FREQ, DSO_MS_PERD, - DSO_MS_VMAX, - DSO_MS_VMIN, + DSO_MS_PDUT, + DSO_MS_NDUT, + DSO_MS_PCNT, + DSO_MS_RISE, + DSO_MS_FALL, + DSO_MS_PWDT, + DSO_MS_NWDT, + DSO_MS_BRST, + DSO_MS_AMPT, + DSO_MS_VHIG, + DSO_MS_VLOW, DSO_MS_VRMS, DSO_MS_VMEA, DSO_MS_VP2P, + DSO_MS_VMAX, + DSO_MS_VMIN, + DSO_MS_POVR, + DSO_MS_NOVR, DSO_MS_END, }; @@ -594,12 +608,14 @@ struct sr_output_module { enum CHANNEL_TYPE { + SR_CHANNEL_DECODER = 9998, + SR_CHANNEL_GROUP = 9999, SR_CHANNEL_LOGIC = 10000, SR_CHANNEL_DSO, SR_CHANNEL_ANALOG, - SR_CHANNEL_GROUP, - SR_CHANNEL_DECODER, SR_CHANNEL_FFT, + SR_CHANNEL_LISSAJOUS, + SR_CHANNEL_MATH, }; enum OPERATION_MODE { @@ -615,16 +631,20 @@ struct sr_channel { gboolean enabled; char *name; char *trigger; + uint8_t bits; uint64_t vdiv; - uint16_t vfactor; - double vpos; + uint64_t vfactor; + uint16_t offset; + uint16_t zero_offset; + uint16_t hw_offset; uint16_t vpos_trans; uint8_t coupling; uint8_t trig_value; int8_t comb_diff_top; int8_t comb_diff_bom; - gboolean ms_show; - gboolean ms_en[DSO_MS_END - DSO_MS_BEGIN]; + int8_t comb_comp; + + gboolean map_default; const char *map_unit; double map_min; double map_max; @@ -652,6 +672,7 @@ struct sr_config_info { char *id; char *name; char *label; + char *label_cn; char *description; }; @@ -673,22 +694,43 @@ struct sr_status { uint8_t captured_cnt1; uint8_t captured_cnt0; - uint8_t ch0_max; - uint8_t ch0_min; - uint64_t ch0_period; - uint32_t ch0_pcnt; - uint8_t ch1_max; - uint8_t ch1_min; - uint64_t ch1_period; - uint32_t ch1_pcnt; - + uint16_t pkt_id; uint32_t vlen; gboolean stream_mode; + gboolean measure_valid; uint32_t sample_divider; gboolean sample_divider_tog; gboolean trig_flag; - uint16_t pkt_id; + uint8_t ch0_max; + uint8_t ch0_min; + uint32_t ch0_cyc_cnt; + uint32_t ch0_cyc_tlen; + uint32_t ch0_cyc_plen; + uint32_t ch0_cyc_llen; + gboolean ch0_level_valid; + gboolean ch0_plevel; + uint8_t ch0_low_level; + uint8_t ch0_high_level; + uint32_t ch0_cyc_rlen; + uint32_t ch0_cyc_flen; + uint64_t ch0_acc_square; + uint32_t ch0_acc_mean; + + uint8_t ch1_max; + uint8_t ch1_min; + uint32_t ch1_cyc_cnt; + uint32_t ch1_cyc_tlen; + uint32_t ch1_cyc_plen; + uint32_t ch1_cyc_llen; + gboolean ch1_level_valid; + gboolean ch1_plevel; + uint8_t ch1_low_level; + uint8_t ch1_high_level; + uint32_t ch1_cyc_rlen; + uint32_t ch1_cyc_flen; + uint64_t ch1_acc_square; + uint32_t ch1_acc_mean; }; enum { @@ -808,6 +850,8 @@ enum { /** How many bits for each sample */ SR_CONF_UNIT_BITS, + SR_CONF_REF_MIN, + SR_CONF_REF_MAX, /** Valid channel number */ SR_CONF_VLD_CH_NUM, @@ -817,6 +861,7 @@ enum { SR_CONF_ZERO, SR_CONF_ZERO_SET, SR_CONF_ZERO_LOAD, + SR_CONF_ZERO_DEFAULT, SR_CONF_VOCM, SR_CONF_CALI, @@ -825,6 +870,16 @@ enum { SR_CONF_STATUS_PCNT, SR_CONF_STATUS_MAX, SR_CONF_STATUS_MIN, + SR_CONF_STATUS_PLEN, + SR_CONF_STATUS_LLEN, + SR_CONF_STATUS_LEVEL, + SR_CONF_STATUS_PLEVEL, + SR_CONF_STATUS_LOW, + SR_CONF_STATUS_HIGH, + SR_CONF_STATUS_RLEN, + SR_CONF_STATUS_FLEN, + SR_CONF_STATUS_RMS, + SR_CONF_STATUS_MEAN, /** Stream */ SR_CONF_STREAM, @@ -835,6 +890,10 @@ enum { /** Test */ SR_CONF_TEST, SR_CONF_EEPROM, + SR_CONF_TUNE, + SR_CONF_TUNE_SEL, + SR_CONF_EXTEND_ID, + SR_CONF_EXTEND_DATA, /** The device supports setting its sample interval, in ms. */ SR_CONF_SAMPLE_INTERVAL, @@ -876,6 +935,10 @@ enum { SR_CONF_MAX_DSO_SAMPLELIMITS, SR_CONF_HW_DEPTH, + /** bandwidth */ + SR_CONF_BANDWIDTH, + SR_CONF_BANDWIDTH_LIMIT, + /*--- Probe configuration -------------------------------------------*/ /** Probe options */ SR_CONF_PROBE_CONFIGS, @@ -895,23 +958,25 @@ enum { /** Factor */ SR_CONF_PROBE_FACTOR, - /** Vertical position */ - SR_CONF_PROBE_VPOS, - /** Mapping */ + SR_CONF_PROBE_MAP_DEFAULT, SR_CONF_PROBE_MAP_UNIT, SR_CONF_PROBE_MAP_MIN, SR_CONF_PROBE_MAP_MAX, /** Vertical offset */ - SR_CONF_PROBE_VOFF, - SR_CONF_PROBE_VOFF_DEFAULT, - SR_CONF_PROBE_VOFF_RANGE, + SR_CONF_PROBE_OFFSET, + SR_CONF_PROBE_HW_OFFSET, + SR_CONF_PROBE_PREOFF, + SR_CONF_PROBE_PREOFF_DEFAULT, + SR_CONF_PROBE_PREOFF_MARGIN, /** VGain */ SR_CONF_PROBE_VGAIN, SR_CONF_PROBE_VGAIN_DEFAULT, SR_CONF_PROBE_VGAIN_RANGE, + SR_CONF_PROBE_COMB_COMP_EN, + SR_CONF_PROBE_COMB_COMP, /*--- Special stuff -------------------------------------------------*/ @@ -936,6 +1001,9 @@ enum { /** The device supports setting the number of data blocks. */ SR_CONF_NUM_BLOCKS, + /** language (string code) **/ + SR_CONF_LANGUAGE, + /*--- Acquisition modes ---------------------------------------------*/ /** @@ -1081,8 +1149,10 @@ enum { }; struct sr_dev_mode { - char *name; int mode; + char *name; + char *name_cn; + char *icon; }; struct sr_dev_driver { @@ -1192,7 +1262,6 @@ struct ds_trigger_pos { uint32_t remain_cnt_l; uint32_t remain_cnt_h; uint32_t status; - unsigned char first_block[488]; }; typedef int (*sr_receive_data_callback_t)(int fd, int revents, const struct sr_dev_inst *sdi); diff --git a/libsigrok4DSL/libsigrok4DSL.pc.in b/libsigrok4DSL/libsigrok4DSL.pc.in old mode 100644 new mode 100755 index d3b653f6..49300b54 --- a/libsigrok4DSL/libsigrok4DSL.pc.in +++ b/libsigrok4DSL/libsigrok4DSL.pc.in @@ -1,15 +1,15 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: libsigrok4DSL -Description: Backend library of DSView software based on libsigrok -URL: http://www.dreamsourcelab.com -Requires: glib-2.0 -Requires.private: @SR_PKGLIBS@ -Version: @VERSION@ -Libs: -L${libdir} -lsigrok4DSL -Libs.private: -lm -Cflags: -I${includedir} - +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsigrok4DSL +Description: Backend library of DSView software based on libsigrok +URL: http://www.dreamsourcelab.com +Requires: glib-2.0 +Requires.private: @SR_PKGLIBS@ +Version: @VERSION@ +Libs: -L${libdir} -lsigrok4DSL +Libs.private: -lm +Cflags: -I${includedir} + diff --git a/libsigrok4DSL/log.c b/libsigrok4DSL/log.c old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/output/Makefile.am b/libsigrok4DSL/output/Makefile.am old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/output/csv.c b/libsigrok4DSL/output/csv.c old mode 100644 new mode 100755 index bac5db66..a1bcf3ea --- a/libsigrok4DSL/output/csv.c +++ b/libsigrok4DSL/output/csv.c @@ -34,8 +34,10 @@ struct context { char separator; gboolean header_done; int *channel_index; - float *channel_vdiv; - double *channel_vpos; + int *channel_unit; + float *channel_scale; + uint16_t *channel_offset; + uint8_t *channel_bits; double *channel_mmin; double *channel_mmax; uint64_t mask; @@ -62,6 +64,7 @@ static int init(struct sr_output *o, GHashTable *options) struct sr_channel *ch; GSList *l; int i; + float range; if (!o || !o->sdi) return SR_ERR_ARG; @@ -83,8 +86,10 @@ static int init(struct sr_output *o, GHashTable *options) ctx->num_enabled_channels++; } ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels); - ctx->channel_vdiv = g_malloc(sizeof(float) * ctx->num_enabled_channels); - ctx->channel_vpos = g_malloc(sizeof(double) * ctx->num_enabled_channels); + ctx->channel_unit = g_malloc(sizeof(int) * ctx->num_enabled_channels); + ctx->channel_scale = g_malloc(sizeof(float) * ctx->num_enabled_channels); + ctx->channel_offset = g_malloc(sizeof(uint16_t) * ctx->num_enabled_channels); + ctx->channel_bits = g_malloc(sizeof(uint8_t) * ctx->num_enabled_channels); ctx->channel_mmax = g_malloc(sizeof(double) * ctx->num_enabled_channels); ctx->channel_mmin = g_malloc(sizeof(double) * ctx->num_enabled_channels); @@ -98,8 +103,12 @@ static int init(struct sr_output *o, GHashTable *options) ctx->channel_index[i] = ch->index; //ctx->mask |= (1 << ch->index); ctx->mask |= (1 << i); - ctx->channel_vdiv[i] = ch->vdiv * ch->vfactor >= 500 ? ch->vdiv * ch->vfactor / 100.0f : ch->vdiv * ch->vfactor * 10.0f; - ctx->channel_vpos[i] = ch->vdiv * ch->vfactor >= 500 ? ch->vpos / 1000 : ch->vpos; + range = ch->vdiv * ch->vfactor * DS_CONF_DSO_VDIVS; + ctx->channel_unit[i] = (range >= 5000000) ? 1000000 : + (range >= 5000) ? 1000 : 1; + ctx->channel_scale[i] = range / ctx->channel_unit[i]; + ctx->channel_offset[i] = ch->hw_offset; + ctx->channel_bits[i] = ch->bits; ctx->channel_mmax[i] = ch->map_max; ctx->channel_mmin[i] = ch->map_min; i++; @@ -165,7 +174,8 @@ static GString *gen_header(const struct sr_output *o) if (!ch->enabled) continue; if (ctx->type == SR_CHANNEL_DSO) { - char *unit_s = (ch->vdiv * ch->vfactor) >= 500 ? "V" : "mV"; + char *unit_s = ctx->channel_unit[i] >= 1000000 ? "kV" : + ctx->channel_unit[i] >= 1000 ? "V" : "mV"; g_string_append_printf(header, " %s (Unit: %s),", ch->name, unit_s); } else if (ctx->type == SR_CHANNEL_ANALOG) { g_string_append_printf(header, " %s (Unit: %s),", ch->name, ch->map_unit); @@ -251,7 +261,9 @@ static int receive(const struct sr_output *o, const struct sr_datafeed_packet *p for (j = 0; j < ctx->num_enabled_channels; j++) { idx = ctx->channel_index[j]; p = dso->data + i * ctx->num_enabled_channels + idx * ((ctx->num_enabled_channels > 1) ? 1 : 0); - g_string_append_printf(*out, "%0.2f", (128 - *p) * ctx->channel_vdiv[j] / 255 - ctx->channel_vpos[j]); + g_string_append_printf(*out, "%0.3f", (ctx->channel_offset[j] - *p) * + ctx->channel_scale[j] / + ((1 << ctx->channel_bits[j]) - 2.0)); g_string_append_c(*out, ctx->separator); } diff --git a/libsigrok4DSL/output/gnuplot.c b/libsigrok4DSL/output/gnuplot.c old mode 100644 new mode 100755 index 3491eb3b..92b01324 --- a/libsigrok4DSL/output/gnuplot.c +++ b/libsigrok4DSL/output/gnuplot.c @@ -102,8 +102,10 @@ static GString *gen_header(const struct sr_output *o) if (ctx->samplerate == 0) { if (sr_config_get(o->sdi->driver, o->sdi, NULL, NULL, SR_CONF_SAMPLERATE, &gvar) == SR_OK) { - ctx->samplerate = g_variant_get_uint64(gvar); - g_variant_unref(gvar); + if (gvar != NULL) { + ctx->samplerate = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + } } } diff --git a/libsigrok4DSL/output/output.c b/libsigrok4DSL/output/output.c old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/output/srzip.c b/libsigrok4DSL/output/srzip.c old mode 100644 new mode 100755 index e75871b9..aa9af27d --- a/libsigrok4DSL/output/srzip.c +++ b/libsigrok4DSL/output/srzip.c @@ -64,8 +64,10 @@ static int zip_create(const struct sr_output *o) if (outc->samplerate == 0) { if (sr_config_get(o->sdi->driver, o->sdi, NULL, NULL, SR_CONF_SAMPLERATE, &gvar) == SR_OK) { - outc->samplerate = g_variant_get_uint64(gvar); - g_variant_unref(gvar); + if (gvar != NULL) { + outc->samplerate = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + } } } diff --git a/libsigrok4DSL/output/vcd.c b/libsigrok4DSL/output/vcd.c old mode 100644 new mode 100755 index a622368a..e5bc6022 --- a/libsigrok4DSL/output/vcd.c +++ b/libsigrok4DSL/output/vcd.c @@ -111,8 +111,10 @@ static GString *gen_header(const struct sr_output *o) if (ctx->samplerate == 0) { if (sr_config_get(o->sdi->driver, o->sdi, NULL, NULL, SR_CONF_SAMPLERATE, &gvar) == SR_OK) { - ctx->samplerate = g_variant_get_uint64(gvar); - g_variant_unref(gvar); + if (gvar != NULL) { + ctx->samplerate = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + } } } if (ctx->samplerate != 0) { diff --git a/libsigrok4DSL/proto.h b/libsigrok4DSL/proto.h old mode 100644 new mode 100755 index b02e6e9c..ca375db9 --- a/libsigrok4DSL/proto.h +++ b/libsigrok4DSL/proto.h @@ -104,7 +104,7 @@ SR_API int sr_session_datafeed_callback_add(sr_datafeed_callback_t cb, SR_API int sr_session_start(void); SR_API int sr_session_run(void); SR_API int sr_session_stop(void); -SR_API int sr_session_save_init(const char *filename, const char *metafile, const char *decfile); +SR_API int sr_session_save_init(const char *filename, const char *metafile, const char *decfile, const char *sesfile); SR_API int sr_session_append(const char *filename, const unsigned char *buf, uint64_t size, int chunk_num, int index, int type, int version); SR_API int sr_session_source_add(int fd, int events, int timeout, diff --git a/libsigrok4DSL/session.c b/libsigrok4DSL/session.c old mode 100644 new mode 100755 index b16e2605..f3f822f1 --- a/libsigrok4DSL/session.c +++ b/libsigrok4DSL/session.c @@ -422,8 +422,8 @@ SR_API int sr_session_run(void) g_mutex_lock(&session->stop_mutex); sr_session_stop_sync(); - session->running = FALSE; session->abort_session = FALSE; + session->running = FALSE; g_mutex_unlock(&session->stop_mutex); return SR_OK; } diff --git a/libsigrok4DSL/session_driver.c b/libsigrok4DSL/session_driver.c old mode 100644 new mode 100755 index f67edbd9..c50f5cca --- a/libsigrok4DSL/session_driver.c +++ b/libsigrok4DSL/session_driver.c @@ -65,6 +65,7 @@ static const uint64_t vdivs[] = { }; struct session_vdev { + int language; int version; char *sessionfile; char *capturefile; @@ -85,6 +86,8 @@ struct session_vdev { int enabled_probes; uint64_t timebase; uint8_t unit_bits; + uint32_t ref_min; + uint32_t ref_max; uint8_t max_height; struct sr_status mstatus; }; @@ -95,6 +98,10 @@ static const int hwoptions[] = { SR_CONF_MAX_HEIGHT, }; +static const int32_t sessions[] = { + SR_CONF_MAX_HEIGHT, +}; + static const int32_t probeOptions[] = { SR_CONF_PROBE_MAP_UNIT, SR_CONF_PROBE_MAP_MIN, @@ -111,6 +118,12 @@ static const char *probeMapUnits[] = { "m/s", }; +static struct sr_dev_mode mode_list[] = { + {LOGIC, "Logic Analyzer", "逻辑分析仪", "la.png"}, + {ANALOG, "Data Acquisition", "数据记录仪", "daq.png"}, + {DSO, "Oscilloscope", "示波器", "osc.png"}, +}; + static int trans_data(struct sr_dev_inst *sdi) { // translate for old format @@ -145,6 +158,16 @@ static int trans_data(struct sr_dev_inst *sdi) return SR_OK; } +static int file_close(struct session_vdev *vdev) +{ + int ret = zip_close(vdev->archive); + if (ret == -1) { + sr_info("error close session file: %s", zip_strerror(vdev->archive)); + return SR_ERR; + } + return SR_OK; +} + static int receive_data(int fd, int revents, const struct sr_dev_inst *cb_sdi) { struct sr_dev_inst *sdi; @@ -208,6 +231,7 @@ static int receive_data(int fd, int revents, const struct sr_dev_inst *cb_sdi) packet.status = SR_PKT_SOURCE_ERROR; sr_session_send(cb_sdi, &packet); sr_session_source_remove(-1); + file_close(vdev); return FALSE; } @@ -277,6 +301,7 @@ static int receive_data(int fd, int revents, const struct sr_dev_inst *cb_sdi) packet.type = SR_DF_END; sr_session_send(cb_sdi, &packet); sr_session_source_remove(-1); + file_close(vdev); } return TRUE; @@ -292,6 +317,19 @@ static int init(struct sr_context *sr_ctx) return SR_OK; } +static const GSList *dev_mode_list(const struct sr_dev_inst *sdi) +{ + GSList *l = NULL; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mode_list); i++) { + if (sdi->mode == mode_list[i].mode) + l = g_slist_append(l, &mode_list[i]); + } + + return l; +} + static int dev_clear(void) { GSList *l; @@ -324,7 +362,10 @@ static int dev_open(struct sr_dev_inst *sdi) vdev->file_opened = FALSE; vdev->num_blocks = 0; vdev->unit_bits = 1; + vdev->ref_min = 0; + vdev->ref_max = 0; vdev->max_height = 0; + vdev->mstatus.measure_valid = TRUE; dev_insts = g_slist_append(dev_insts, sdi); @@ -355,6 +396,12 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, struct session_vdev *vdev; switch (id) { + case SR_CONF_LANGUAGE: + if (!sdi) + return SR_ERR; + vdev = sdi->priv; + *data = g_variant_new_int16(vdev->language); + break; case SR_CONF_SAMPLERATE: if (sdi) { vdev = sdi->priv; @@ -395,6 +442,26 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, } else return SR_ERR; break; + case SR_CONF_REF_MIN: + if (sdi) { + vdev = sdi->priv; + if (vdev->ref_min == 0) + return SR_ERR; + else + *data = g_variant_new_uint32(vdev->ref_min); + } else + return SR_ERR; + break; + case SR_CONF_REF_MAX: + if (sdi) { + vdev = sdi->priv; + if (vdev->ref_max == 0) + return SR_ERR; + else + *data = g_variant_new_uint32(vdev->ref_max); + } else + return SR_ERR; + break; case SR_CONF_PROBE_EN: if (sdi && ch) { *data = g_variant_new_boolean(ch->enabled); @@ -419,12 +486,18 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, } else return SR_ERR; break; - case SR_CONF_PROBE_VPOS: + case SR_CONF_PROBE_OFFSET: if (sdi && ch) { - *data = g_variant_new_double(ch->vpos); + *data = g_variant_new_uint16(ch->offset); } else return SR_ERR; break; + case SR_CONF_PROBE_HW_OFFSET: + if (sdi && ch) { + *data = g_variant_new_uint16(ch->hw_offset); + } else + return SR_ERR; + break; case SR_CONF_PROBE_MAP_UNIT: if (!sdi || !ch) return SR_ERR; @@ -508,6 +581,9 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, vdev = sdi->priv; switch (id) { + case SR_CONF_LANGUAGE: + vdev->language = g_variant_get_int16(data); + break; case SR_CONF_SAMPLERATE: vdev->samplerate = g_variant_get_uint64(data); samplerates[0] = vdev->samplerate; @@ -521,6 +597,14 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, vdev->unit_bits = g_variant_get_byte(data); sr_info("Setting unit bits to %d.", vdev->unit_bits); break; + case SR_CONF_REF_MIN: + vdev->ref_min = g_variant_get_uint32(data); + sr_info("Setting ref min to %d.", vdev->ref_min); + break; + case SR_CONF_REF_MAX: + vdev->ref_max = g_variant_get_uint32(data); + sr_info("Setting ref max to %d.", vdev->ref_max); + break; case SR_CONF_SESSIONFILE: vdev->sessionfile = g_strdup(g_variant_get_bytestring(data)); sr_info("Setting sessionfile to '%s'.", vdev->sessionfile); @@ -552,10 +636,14 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, break; case SR_CONF_CAPTURE_NUM_PROBES: vdev->num_probes = g_variant_get_uint64(data); - if (sdi->mode == LOGIC) { - if (!(vdev->logic_buf = g_try_malloc(CHUNKSIZE/16*vdev->num_probes))) { - sr_err("%s: vdev->logic_buf malloc failed", __func__); + if (vdev->version == 1) { + if (sdi->mode == LOGIC) { + if (!(vdev->logic_buf = g_try_malloc(CHUNKSIZE/16*vdev->num_probes))) { + sr_err("%s: vdev->logic_buf malloc failed", __func__); + } } + } else { + vdev->logic_buf = NULL; } break; case SR_CONF_PROBE_EN: @@ -570,9 +658,13 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, case SR_CONF_PROBE_FACTOR: ch->vfactor = g_variant_get_uint64(data); break; - case SR_CONF_PROBE_VPOS: - ch->vpos = g_variant_get_double(data); + case SR_CONF_PROBE_OFFSET: + ch->offset = g_variant_get_uint16(data); break; + case SR_CONF_PROBE_HW_OFFSET: + ch->hw_offset = g_variant_get_uint16(data); + ch->offset = ch->hw_offset; + break; case SR_CONF_PROBE_MAP_UNIT: ch->map_unit = g_variant_get_string(data, NULL); break; @@ -587,27 +679,87 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, break; case SR_CONF_STATUS_PERIOD: if (ch->index == 0) - vdev->mstatus.ch0_period = g_variant_get_uint64(data); + vdev->mstatus.ch0_cyc_tlen = g_variant_get_uint32(data); else - vdev->mstatus.ch1_period = g_variant_get_uint64(data); + vdev->mstatus.ch1_cyc_tlen = g_variant_get_uint32(data); break; case SR_CONF_STATUS_PCNT: if (ch->index == 0) - vdev->mstatus.ch0_pcnt = g_variant_get_uint64(data); + vdev->mstatus.ch0_cyc_cnt = g_variant_get_uint32(data); else - vdev->mstatus.ch1_pcnt = g_variant_get_uint64(data); + vdev->mstatus.ch1_cyc_cnt = g_variant_get_uint32(data); break; case SR_CONF_STATUS_MAX: if (ch->index == 0) - vdev->mstatus.ch0_max = g_variant_get_uint64(data); + vdev->mstatus.ch0_max = g_variant_get_byte(data); else - vdev->mstatus.ch1_max = g_variant_get_uint64(data); + vdev->mstatus.ch1_max = g_variant_get_byte(data); break; case SR_CONF_STATUS_MIN: if (ch->index == 0) - vdev->mstatus.ch0_min = g_variant_get_uint64(data); + vdev->mstatus.ch0_min = g_variant_get_byte(data); else - vdev->mstatus.ch1_min = g_variant_get_uint64(data); + vdev->mstatus.ch1_min = g_variant_get_byte(data); + break; + case SR_CONF_STATUS_PLEN: + if (ch->index == 0) + vdev->mstatus.ch0_cyc_plen = g_variant_get_uint32(data); + else + vdev->mstatus.ch1_cyc_plen = g_variant_get_uint32(data); + break; + case SR_CONF_STATUS_LLEN: + if (ch->index == 0) + vdev->mstatus.ch0_cyc_llen = g_variant_get_uint32(data); + else + vdev->mstatus.ch0_cyc_llen = g_variant_get_uint32(data); + break; + case SR_CONF_STATUS_LEVEL: + if (ch->index == 0) + vdev->mstatus.ch0_level_valid = g_variant_get_boolean(data); + else + vdev->mstatus.ch1_level_valid = g_variant_get_boolean(data); + break; + case SR_CONF_STATUS_PLEVEL: + if (ch->index == 0) + vdev->mstatus.ch0_plevel = g_variant_get_boolean(data); + else + vdev->mstatus.ch1_plevel = g_variant_get_boolean(data); + break; + case SR_CONF_STATUS_LOW: + if (ch->index == 0) + vdev->mstatus.ch0_low_level = g_variant_get_byte(data); + else + vdev->mstatus.ch1_low_level = g_variant_get_byte(data); + break; + case SR_CONF_STATUS_HIGH: + if (ch->index == 0) + vdev->mstatus.ch0_high_level = g_variant_get_byte(data); + else + vdev->mstatus.ch1_high_level = g_variant_get_byte(data); + break; + case SR_CONF_STATUS_RLEN: + if (ch->index == 0) + vdev->mstatus.ch0_cyc_rlen = g_variant_get_uint32(data); + else + vdev->mstatus.ch1_cyc_rlen = g_variant_get_uint32(data); + break; + case SR_CONF_STATUS_FLEN: + if (ch->index == 0) + vdev->mstatus.ch0_cyc_flen = g_variant_get_uint32(data); + else + vdev->mstatus.ch1_cyc_flen = g_variant_get_uint32(data); + break; + case SR_CONF_STATUS_RMS: + if (ch->index == 0) + vdev->mstatus.ch0_acc_square = g_variant_get_uint64(data); + else + vdev->mstatus.ch1_acc_square = g_variant_get_uint64(data); + break; + case SR_CONF_STATUS_MEAN: + if (ch->index == 0) + vdev->mstatus.ch0_acc_mean = g_variant_get_uint32(data); + else + vdev->mstatus.ch1_acc_mean = g_variant_get_uint32(data); break; case SR_CONF_MAX_HEIGHT: stropt = g_variant_get_string(data, NULL); @@ -620,6 +772,9 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, sr_dbg("%s: setting Signal Max Height to %d", __func__, vdev->max_height); break; + case SR_CONF_INSTANT: + case SR_CONF_RLE: + break; default: sr_err("Unknown capability: %d.", id); return SR_ERR; @@ -646,6 +801,10 @@ static int config_list(int key, GVariant **data, *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), hwoptions, ARRAY_SIZE(hwoptions)*sizeof(int32_t), TRUE, NULL, NULL); break; + case SR_CONF_DEVICE_SESSIONS: + *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), + sessions, ARRAY_SIZE(sessions)*sizeof(int32_t), TRUE, NULL, NULL); + break; case SR_CONF_SAMPLERATE: g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}")); // gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates, @@ -786,7 +945,7 @@ SR_PRIV struct sr_dev_driver session_driver = { .cleanup = dev_clear, .scan = NULL, .dev_list = NULL, - .dev_mode_list = NULL, + .dev_mode_list = dev_mode_list, .dev_clear = dev_clear, .config_get = config_get, .config_set = config_set, diff --git a/libsigrok4DSL/session_file.c b/libsigrok4DSL/session_file.c old mode 100644 new mode 100755 index 5b14cc0c..14c6a752 --- a/libsigrok4DSL/session_file.c +++ b/libsigrok4DSL/session_file.c @@ -100,6 +100,11 @@ SR_PRIV int sr_sessionfile_check(const char *filename) return SR_ERR; } + if ((ret = zip_close(archive)) == -1) { + sr_info("error saving session file: %s", zip_strerror(archive)); + return SR_ERR; + } + return SR_OK; } @@ -219,6 +224,14 @@ SR_API int sr_session_load(const char *filename) tmp_u64 = strtoull(val, NULL, 10); sdi->driver->config_set(SR_CONF_UNIT_BITS, g_variant_new_byte(tmp_u64), sdi, NULL, NULL); + } else if (!strcmp(keys[j], "ref min")) { + tmp_u64 = strtoull(val, NULL, 10); + sdi->driver->config_set(SR_CONF_REF_MIN, + g_variant_new_uint32(tmp_u64), sdi, NULL, NULL); + } else if (!strcmp(keys[j], "ref max")) { + tmp_u64 = strtoull(val, NULL, 10); + sdi->driver->config_set(SR_CONF_REF_MAX, + g_variant_new_uint32(tmp_u64), sdi, NULL, NULL); } else if (!strcmp(keys[j], "trigger time")) { tmp_64 = strtoll(val, NULL, 10); sdi->driver->config_set(SR_CONF_TRIGGER_TIME, @@ -300,17 +313,17 @@ SR_API int sr_session_load(const char *filename) sdi->driver->config_set(SR_CONF_PROBE_FACTOR, g_variant_new_uint64(tmp_u64), sdi, probe, NULL); } - } else if (!strncmp(keys[j], "vPos", 4)) { - probenum = strtoul(keys[j]+4, NULL, 10); - tmp_double = strtod(val, NULL); + } else if (!strncmp(keys[j], "vOffset", 7)) { + probenum = strtoul(keys[j]+7, NULL, 10); + tmp_u64 = strtoull(val, NULL, 10); if (probenum < g_slist_length(sdi->channels)) { probe = g_slist_nth(sdi->channels, probenum)->data; - sdi->driver->config_set(SR_CONF_PROBE_VPOS, - g_variant_new_double(tmp_double), sdi, probe, NULL); + sdi->driver->config_set(SR_CONF_PROBE_HW_OFFSET, + g_variant_new_uint16(tmp_u64), sdi, probe, NULL); } } else if (!strncmp(keys[j], "vTrig", 5)) { probenum = strtoul(keys[j]+5, NULL, 10); - tmp_u64 = strtod(val, NULL); + tmp_u64 = strtoull(val, NULL, 10); if (probenum < g_slist_length(sdi->channels)) { probe = g_slist_nth(sdi->channels, probenum)->data; sdi->driver->config_set(SR_CONF_TRIGGER_VALUE, @@ -322,7 +335,7 @@ SR_API int sr_session_load(const char *filename) if (probenum < g_slist_length(sdi->channels)) { probe = g_slist_nth(sdi->channels, probenum)->data; sdi->driver->config_set(SR_CONF_STATUS_PERIOD, - g_variant_new_uint64(tmp_u64), sdi, probe, NULL); + g_variant_new_uint32(tmp_u64), sdi, probe, NULL); } } else if (!strncmp(keys[j], "pcnt", 4)) { probenum = strtoul(keys[j]+4, NULL, 10); @@ -330,7 +343,7 @@ SR_API int sr_session_load(const char *filename) if (probenum < g_slist_length(sdi->channels)) { probe = g_slist_nth(sdi->channels, probenum)->data; sdi->driver->config_set(SR_CONF_STATUS_PCNT, - g_variant_new_uint64(tmp_u64), sdi, probe, NULL); + g_variant_new_uint32(tmp_u64), sdi, probe, NULL); } } else if (!strncmp(keys[j], "max", 3)) { probenum = strtoul(keys[j]+3, NULL, 10); @@ -338,7 +351,7 @@ SR_API int sr_session_load(const char *filename) if (probenum < g_slist_length(sdi->channels)) { probe = g_slist_nth(sdi->channels, probenum)->data; sdi->driver->config_set(SR_CONF_STATUS_MAX, - g_variant_new_uint64(tmp_u64), sdi, probe, NULL); + g_variant_new_byte(tmp_u64), sdi, probe, NULL); } } else if (!strncmp(keys[j], "min", 3)) { probenum = strtoul(keys[j]+3, NULL, 10); @@ -346,8 +359,88 @@ SR_API int sr_session_load(const char *filename) if (probenum < g_slist_length(sdi->channels)) { probe = g_slist_nth(sdi->channels, probenum)->data; sdi->driver->config_set(SR_CONF_STATUS_MIN, + g_variant_new_byte(tmp_u64), sdi, probe, NULL); + } + } else if (!strncmp(keys[j], "plen", 4)) { + probenum = strtoul(keys[j]+4, NULL, 10); + tmp_u64 = strtoull(val, NULL, 10); + if (probenum < g_slist_length(sdi->channels)) { + probe = g_slist_nth(sdi->channels, probenum)->data; + sdi->driver->config_set(SR_CONF_STATUS_PLEN, + g_variant_new_uint32(tmp_u64), sdi, probe, NULL); + } + } else if (!strncmp(keys[j], "llen", 4)) { + probenum = strtoul(keys[j]+4, NULL, 10); + tmp_u64 = strtoull(val, NULL, 10); + if (probenum < g_slist_length(sdi->channels)) { + probe = g_slist_nth(sdi->channels, probenum)->data; + sdi->driver->config_set(SR_CONF_STATUS_LLEN, + g_variant_new_uint32(tmp_u64), sdi, probe, NULL); + } + } else if (!strncmp(keys[j], "level", 5)) { + probenum = strtoul(keys[j]+5, NULL, 10); + tmp_u64 = strtoull(val, NULL, 10); + if (probenum < g_slist_length(sdi->channels)) { + probe = g_slist_nth(sdi->channels, probenum)->data; + sdi->driver->config_set(SR_CONF_STATUS_LEVEL, + g_variant_new_boolean(tmp_u64), sdi, probe, NULL); + } + } else if (!strncmp(keys[j], "plevel", 6)) { + probenum = strtoul(keys[j]+6, NULL, 10); + tmp_u64 = strtoull(val, NULL, 10); + if (probenum < g_slist_length(sdi->channels)) { + probe = g_slist_nth(sdi->channels, probenum)->data; + sdi->driver->config_set(SR_CONF_STATUS_PLEVEL, + g_variant_new_boolean(tmp_u64), sdi, probe, NULL); + } + } else if (!strncmp(keys[j], "low", 3)) { + probenum = strtoul(keys[j]+3, NULL, 10); + tmp_u64 = strtoull(val, NULL, 10); + if (probenum < g_slist_length(sdi->channels)) { + probe = g_slist_nth(sdi->channels, probenum)->data; + sdi->driver->config_set(SR_CONF_STATUS_LOW, + g_variant_new_byte(tmp_u64), sdi, probe, NULL); + } + } else if (!strncmp(keys[j], "high", 4)) { + probenum = strtoul(keys[j]+4, NULL, 10); + tmp_u64 = strtoull(val, NULL, 10); + if (probenum < g_slist_length(sdi->channels)) { + probe = g_slist_nth(sdi->channels, probenum)->data; + sdi->driver->config_set(SR_CONF_STATUS_HIGH, + g_variant_new_byte(tmp_u64), sdi, probe, NULL); + } + } else if (!strncmp(keys[j], "rlen", 4)) { + probenum = strtoul(keys[j]+4, NULL, 10); + tmp_u64 = strtoull(val, NULL, 10); + if (probenum < g_slist_length(sdi->channels)) { + probe = g_slist_nth(sdi->channels, probenum)->data; + sdi->driver->config_set(SR_CONF_STATUS_RLEN, + g_variant_new_uint32(tmp_u64), sdi, probe, NULL); + } + } else if (!strncmp(keys[j], "flen", 4)) { + probenum = strtoul(keys[j]+4, NULL, 10); + tmp_u64 = strtoull(val, NULL, 10); + if (probenum < g_slist_length(sdi->channels)) { + probe = g_slist_nth(sdi->channels, probenum)->data; + sdi->driver->config_set(SR_CONF_STATUS_FLEN, + g_variant_new_uint32(tmp_u64), sdi, probe, NULL); + } + } else if (!strncmp(keys[j], "rms", 3)) { + probenum = strtoul(keys[j]+3, NULL, 10); + tmp_u64 = strtoull(val, NULL, 10); + if (probenum < g_slist_length(sdi->channels)) { + probe = g_slist_nth(sdi->channels, probenum)->data; + sdi->driver->config_set(SR_CONF_STATUS_RMS, g_variant_new_uint64(tmp_u64), sdi, probe, NULL); } + } else if (!strncmp(keys[j], "mean", 4)) { + probenum = strtoul(keys[j]+4, NULL, 10); + tmp_u64 = strtoull(val, NULL, 10); + if (probenum < g_slist_length(sdi->channels)) { + probe = g_slist_nth(sdi->channels, probenum)->data; + sdi->driver->config_set(SR_CONF_STATUS_MEAN, + g_variant_new_uint32(tmp_u64), sdi, probe, NULL); + } } else if (!strncmp(keys[j], "mapUnit", 7)) { probenum = strtoul(keys[j]+7, NULL, 10); if (probenum < g_slist_length(sdi->channels)) { @@ -380,6 +473,12 @@ SR_API int sr_session_load(const char *filename) g_strfreev(sections); g_key_file_free(kf); + + if ((ret = zip_close(archive)) == -1) { + sr_info("error close session file: %s", zip_strerror(archive)); + return SR_ERR; + } + return SR_OK; } @@ -398,7 +497,7 @@ SR_API int sr_session_load(const char *filename) * * @since 0.3.0 */ -SR_API int sr_session_save_init(const char *filename, const char *metafile, const char *decfile) +SR_API int sr_session_save_init(const char *filename, const char *metafile, const char *decfile, const char *sesfile) { struct zip *zipfile; struct zip_source *metasrc; @@ -438,6 +537,19 @@ SR_API int sr_session_save_init(const char *filename, const char *metafile, cons } } + // session file + if (sesfile != NULL) { + if (!(metasrc = zip_source_file(zipfile, sesfile, 0, -1))) { + unlink(sesfile); + return SR_ERR; + } + + if (zip_add(zipfile, "session", metasrc) == -1) { + unlink(sesfile); + return SR_ERR; + } + } + if ((ret = zip_close(zipfile)) == -1) { sr_info("error saving zipfile: %s", zip_strerror(zipfile)); unlink(metafile); @@ -482,9 +594,11 @@ SR_API int sr_session_append(const char *filename, const unsigned char *buf, // if ((ret = sr_sessionfile_check(filename)) != SR_OK) // return ret; + if (buf == NULL) + goto err; if (!(archive = zip_open(filename, 0, &ret))) - return SR_ERR; + goto err; if (version == 2) { type_name = (type == SR_CHANNEL_LOGIC) ? "L" : @@ -496,17 +610,21 @@ SR_API int sr_session_append(const char *filename, const unsigned char *buf, } if (!(logicsrc = zip_source_buffer(archive, buf, size, FALSE))) { - return SR_ERR; + goto err; } if (zip_file_add(archive, chunk_name, logicsrc, ZIP_FL_OVERWRITE) == -1) { - return SR_ERR; + goto err; } if ((ret = zip_close(archive)) == -1) { sr_info("error saving session file: %s", zip_strerror(archive)); - return SR_ERR; + goto err; } return SR_OK; + +err: + unlink(filename); + return SR_ERR; } /** @} */ diff --git a/libsigrok4DSL/std.c b/libsigrok4DSL/std.c old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/strutil.c b/libsigrok4DSL/strutil.c old mode 100644 new mode 100755 index 5ced3fd6..812baf0a --- a/libsigrok4DSL/strutil.c +++ b/libsigrok4DSL/strutil.c @@ -168,7 +168,7 @@ SR_API char *sr_samplerate_string(uint64_t samplerate) */ SR_API char *sr_samplecount_string(uint64_t samplecount) { - return sr_iec_string_u64(samplecount, " Samples"); + return sr_si_string_u64(samplecount, " Samples"); } /** diff --git a/libsigrok4DSL/tests/Makefile.am b/libsigrok4DSL/tests/Makefile.am old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/tests/check_core.c b/libsigrok4DSL/tests/check_core.c old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/tests/check_driver_all.c b/libsigrok4DSL/tests/check_driver_all.c old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/tests/check_main.c b/libsigrok4DSL/tests/check_main.c old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/tests/check_strutil.c b/libsigrok4DSL/tests/check_strutil.c old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/tests/lib.c b/libsigrok4DSL/tests/lib.c old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/tests/lib.h b/libsigrok4DSL/tests/lib.h old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/trigger.c b/libsigrok4DSL/trigger.c old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/version.c b/libsigrok4DSL/version.c old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/version.h b/libsigrok4DSL/version.h old mode 100644 new mode 100755 diff --git a/libsigrok4DSL/version.h.in b/libsigrok4DSL/version.h.in old mode 100644 new mode 100755 diff --git a/libsigrokdecode4DSL/AUTHORS b/libsigrokdecode4DSL/AUTHORS old mode 100644 new mode 100755 diff --git a/libsigrokdecode4DSL/COPYING b/libsigrokdecode4DSL/COPYING old mode 100644 new mode 100755 diff --git a/libsigrokdecode4DSL/Doxyfile b/libsigrokdecode4DSL/Doxyfile old mode 100644 new mode 100755 index a53ec1d9..47550ce0 --- a/libsigrokdecode4DSL/Doxyfile +++ b/libsigrokdecode4DSL/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.6 +# Doxyfile 1.8.13 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -46,10 +46,10 @@ PROJECT_NUMBER = "unreleased development snapshot" PROJECT_BRIEF = "sigrok protocol decoding library" -# With the PROJECT_LOGO tag one can specify an logo or icon that is included in -# the documentation. The maximum height of the logo should not exceed 55 pixels -# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo -# to the output directory. +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. PROJECT_LOGO = contrib/sigrok-logo-notext.png @@ -60,7 +60,7 @@ PROJECT_LOGO = contrib/sigrok-logo-notext.png OUTPUT_DIRECTORY = doxy -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where @@ -68,7 +68,15 @@ OUTPUT_DIRECTORY = doxy # performance problems for the file system. # The default value is: NO. -CREATE_SUBDIRS = YES +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this @@ -85,14 +93,14 @@ CREATE_SUBDIRS = YES OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the @@ -127,7 +135,7 @@ ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. @@ -197,9 +205,9 @@ MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a -# new page for each member. If set to NO, the documentation of a member will be -# part of the file/class/namespace that contains it. +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO @@ -261,11 +269,14 @@ OPTIMIZE_OUTPUT_VHDL = NO # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. # -# Note For files without extension you can use no_extension as a placeholder. +# Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. @@ -282,10 +293,19 @@ EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES @@ -325,13 +345,20 @@ SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first +# tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent @@ -390,7 +417,7 @@ LOOKUP_CACHE_SIZE = 0 # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. @@ -400,35 +427,35 @@ LOOKUP_CACHE_SIZE = 0 EXTRACT_ALL = YES -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = NO -# This flag is only useful for Objective-C code. When set to YES local methods, +# This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO only methods in the interface are +# included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. @@ -453,21 +480,21 @@ HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set -# to NO these classes will be included in the various overviews. This option has -# no effect if EXTRACT_ALL is enabled. +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO these declarations will be +# (class|struct|union) declarations. If set to NO, these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO these +# documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. @@ -481,7 +508,7 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES upper-case letters are also +# names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. @@ -490,12 +517,19 @@ INTERNAL_DOCS = NO CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES the +# their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -523,14 +557,14 @@ INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. +# name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. Note that +# name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. @@ -575,27 +609,25 @@ SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO -# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the -# todo list. This list is created by putting \todo commands in the -# documentation. +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES -# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the -# test list. This list is created by putting \test commands in the -# documentation. +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. @@ -620,8 +652,8 @@ ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES the list -# will mention the files that were used to generate the documentation. +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES @@ -669,8 +701,7 @@ LAYOUT_FILE = # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. Do not use file names with spaces, bibtex cannot handle them. See -# also \cite for info how to create references. +# search path. See also \cite for info how to create references. CITE_BIB_FILES = @@ -686,7 +717,7 @@ CITE_BIB_FILES = QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. @@ -694,7 +725,7 @@ QUIET = NO WARNINGS = YES -# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. @@ -711,12 +742,18 @@ WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO doxygen will only warn about wrong or incomplete parameter -# documentation, but not about the absence of documentation. +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = YES +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated @@ -740,7 +777,7 @@ WARN_LOGFILE = # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with -# spaces. +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = . @@ -756,12 +793,17 @@ INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. FILE_PATTERNS = @@ -778,7 +820,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = config.h libsigrokdecode-internal.h exception.c \ +EXCLUDE = build config.h libsigrokdecode-internal.h exception.c \ module_sigrokdecode.c type_decoder.c type_logic.c \ util.c @@ -861,6 +903,10 @@ IMAGE_PATH = # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. INPUT_FILTER = @@ -870,11 +916,15 @@ INPUT_FILTER = # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER ) will also be used to filter the input files that are used for +# INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. @@ -934,7 +984,7 @@ REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. @@ -981,6 +1031,25 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse-libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1011,7 +1080,7 @@ IGNORE_PREFIX = # Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES @@ -1073,13 +1142,15 @@ HTML_FOOTER = HTML_STYLESHEET = -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- -# defined cascading style sheet that is included after the standard style sheets +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. -# Doxygen will copy the style sheet file to the output directory. For an example -# see the documentation. +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = @@ -1095,7 +1166,7 @@ HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the stylesheet and background images according to +# will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 @@ -1126,8 +1197,9 @@ HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: YES. +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES @@ -1223,28 +1295,29 @@ GENERATE_HTMLHELP = NO CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = -# The GENERATE_CHI flag controls if a separate .chi index file is generated ( -# YES) or that it should be included in the master .chm file ( NO). +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = -# The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1357,7 +1430,7 @@ DISABLE_INDEX = NO # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has @@ -1385,7 +1458,7 @@ ENUM_VALUES_PER_LINE = 1 TREEVIEW_WIDTH = 250 -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1414,7 +1487,7 @@ FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. @@ -1484,11 +1557,11 @@ SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using Javascript. There -# are two flavours of web server based searching depending on the -# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for -# searching and an index file used by the script. When EXTERNAL_SEARCH is -# enabled the indexing and searching needs to be provided by external tools. See -# the section "External Indexing and Searching" for details. +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. # The default value is: NO. # This tag requires that the tag SEARCHENGINE is set to YES. @@ -1500,7 +1573,7 @@ SERVER_BASED_SEARCH = NO # external search engine pointed to by the SEARCHENGINE_URL option to obtain the # search results. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library # Xapian (see: http://xapian.org/). # @@ -1513,7 +1586,7 @@ EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will return the search results when EXTERNAL_SEARCH is enabled. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library # Xapian (see: http://xapian.org/). See the section "External Indexing and # Searching" for details. @@ -1551,7 +1624,7 @@ EXTRA_SEARCH_MAPPINGS = # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output. +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. GENERATE_LATEX = NO @@ -1582,7 +1655,7 @@ LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex -# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1600,9 +1673,12 @@ COMPACT_LATEX = NO PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names -# that should be included in the LaTeX output. To get the times font for -# instance you can specify -# EXTRA_PACKAGES=times +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} # If left blank no extra packages will be included. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1616,23 +1692,36 @@ EXTRA_PACKAGES = # # Note: Only use a user-defined header if you know what you are doing! The # following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will -# replace them by respectively the title of the page, the current date and time, -# only the current date, the version number of doxygen, the project name (see -# PROJECT_NAME), or the project number (see PROJECT_NUMBER). +# $datetime, $date, $doxygenversion, $projectname, $projectnumber, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty +# string, for the replacement values of the other commands the user is referred +# to HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the # generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. +# chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. # # Note: Only use a user-defined footer if you know what you are doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the LATEX_OUTPUT output # directory. Note that the files will be copied as-is; there are no commands or @@ -1650,8 +1739,8 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES to get a +# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# the PDF file directly from the LaTeX files. Set this option to YES, to get a # higher quality PDF documentation. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1692,11 +1781,19 @@ LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The # RTF output is optimized for Word 97 and may not look too pretty with other RTF # readers/editors. # The default value is: NO. @@ -1711,7 +1808,7 @@ GENERATE_RTF = NO RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1748,11 +1845,21 @@ RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = +# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code +# with syntax highlighting in the RTF output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_SOURCE_CODE = NO + #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for # classes and files. # The default value is: NO. @@ -1776,6 +1883,13 @@ MAN_OUTPUT = man MAN_EXTENSION = .3 +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + # If the MAN_LINKS tag is set to YES and doxygen generates man output, then it # will generate one additional man file for each entity documented in the real # man page(s). These additional files only source the real man page, but without @@ -1789,7 +1903,7 @@ MAN_LINKS = NO # Configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that # captures the structure of the code including all documentation. # The default value is: NO. @@ -1803,19 +1917,7 @@ GENERATE_XML = NO XML_OUTPUT = xml -# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a -# validating XML parser to check the syntax of the XML files. -# This tag requires that the tag GENERATE_XML is set to YES. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify a XML DTD, which can be used by a -# validating XML parser to check the syntax of the XML files. -# This tag requires that the tag GENERATE_XML is set to YES. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to # the XML output. Note that enabling this will significantly increase the size # of the XML output. @@ -1828,7 +1930,7 @@ XML_PROGRAMLISTING = YES # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- -# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files # that can be used to generate PDF. # The default value is: NO. @@ -1842,14 +1944,23 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook +# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the +# program listings (including syntax highlighting and cross-referencing +# information) to the DOCBOOK output. Note that enabling this will significantly +# increase the size of the DOCBOOK output. +# The default value is: NO. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_PROGRAMLISTING = NO + #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen -# Definitions (see http://autogen.sf.net) file that captures the structure of -# the code including all documentation. Note that this feature is still -# experimental and incomplete at the moment. +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sf.net) file that captures the +# structure of the code including all documentation. Note that this feature is +# still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO @@ -1858,7 +1969,7 @@ GENERATE_AUTOGEN_DEF = NO # Configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module # file that captures the structure of the code including all documentation. # # Note that this feature is still experimental and incomplete at the moment. @@ -1866,7 +1977,7 @@ GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI # output from the Perl module output. # The default value is: NO. @@ -1874,9 +1985,9 @@ GENERATE_PERLMOD = NO PERLMOD_LATEX = NO -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely # formatted so it can be parsed by a human reader. This is useful if you want to -# understand what is going on. On the other hand, if this tag is set to NO the +# understand what is going on. On the other hand, if this tag is set to NO, the # size of the Perl module output will be much smaller and Perl will parse it # just the same. # The default value is: YES. @@ -1896,14 +2007,14 @@ PERLMOD_MAKEVAR_PREFIX = # Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all # C-preprocessor directives found in the sources and include files. # The default value is: YES. ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names -# in the source code. If set to NO only conditional compilation will be +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be # performed. Macro expansion can be done in a controlled way by setting # EXPAND_ONLY_PREDEF to YES. # The default value is: NO. @@ -1919,7 +2030,7 @@ MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES -# If the SEARCH_INCLUDES tag is set to YES the includes files in the +# If the SEARCH_INCLUDES tag is set to YES, the include files in the # INCLUDE_PATH will be searched if a #include is found. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. @@ -1949,7 +2060,6 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -# This gets rid of the SRD_API function prefix in the Doxygen output. PREDEFINED = SRD_API= # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this @@ -1962,9 +2072,9 @@ PREDEFINED = SRD_API= EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will -# remove all refrences to function-like macros that are alone on a line, have an -# all uppercase name, and do not end with a semicolon. Such function macros are -# typically used for boiler-plate code, and will confuse the parser if not +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not # removed. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. @@ -1984,7 +2094,7 @@ SKIP_FUNCTION_MACROS = YES # where loc1 and loc2 can be relative or absolute paths or URLs. See the # section "Linking to external documentation" for more information about the use # of tag files. -# Note: Each tag file must have an unique name (where the name does NOT include +# Note: Each tag file must have a unique name (where the name does NOT include # the path). If a tag file is not located in the directory in which doxygen is # run, you must also specify the path to the tagfile here. @@ -1996,20 +2106,21 @@ TAGFILES = GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES all external class will be listed in the -# class index. If set to NO only the inherited external classes will be listed. +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. # The default value is: NO. ALLEXTERNALS = NO -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in -# the modules index. If set to NO, only the current project's groups will be +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. EXTERNAL_GROUPS = YES -# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in # the related pages index. If set to NO, only the current project's pages will # be listed. # The default value is: YES. @@ -2026,7 +2137,7 @@ PERL_PATH = /usr/bin/perl # Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram +# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram # (in HTML and LaTeX) for classes with base or super classes. Setting the tag to # NO turns the diagrams off. Note that this option also works with HAVE_DOT # disabled, but it is recommended to install and use dot, since it yields more @@ -2051,7 +2162,7 @@ MSCGEN_PATH = DIA_PATH = -# If set to YES, the inheritance and collaboration graphs will hide inheritance +# If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. @@ -2062,7 +2173,7 @@ HIDE_UNDOC_RELATIONS = NO # http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO -# The default value is: NO. +# The default value is: YES. HAVE_DOT = YES @@ -2076,7 +2187,7 @@ HAVE_DOT = YES DOT_NUM_THREADS = 0 -# When you want a differently looking font n the dot files that doxygen +# When you want a differently looking font in the dot files that doxygen # generates you can specify the font name using DOT_FONTNAME. You need to make # sure dot is able to find the font, which can be done by putting it in a # standard location or by setting the DOTFONTPATH environment variable or by @@ -2124,7 +2235,7 @@ COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. # The default value is: NO. @@ -2176,7 +2287,8 @@ INCLUDED_BY_GRAPH = YES # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2187,7 +2299,8 @@ CALL_GRAPH = YES # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2210,11 +2323,17 @@ GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). -# Possible values are: png, jpg, gif and svg. +# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, +# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, +# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2257,6 +2376,24 @@ MSCFILE_DIRS = DIAFILE_DIRS = +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file. If left blank, it is assumed +# PlantUML is not used or called during a preprocessing step. Doxygen will +# generate a warning when it encounters a \startuml command in this case and +# will not generate output for the diagram. + +PLANTUML_JAR_PATH = + +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes # larger than this value, doxygen will truncate the graph, which is visualized @@ -2293,7 +2430,7 @@ MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support # this, this feature is disabled by default. @@ -2310,7 +2447,7 @@ DOT_MULTI_TARGETS = YES GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot # files that are used to generate the various graphs. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. diff --git a/libsigrokdecode4DSL/HACKING b/libsigrokdecode4DSL/HACKING old mode 100644 new mode 100755 index 6aaedac1..09cdd939 --- a/libsigrokdecode4DSL/HACKING +++ b/libsigrokdecode4DSL/HACKING @@ -5,29 +5,32 @@ HACKING Coding style ------------ -This project is programmed using the Linux kernel coding style, see -http://lxr.linux.no/linux/Documentation/CodingStyle for details. +This project is programmed using the Linux kernel coding style: + + https://www.kernel.org/doc/html/latest/process/coding-style.html Please use the same style for any code contributions, thanks! The Python decoders should follow the usual Python conventions and use Python idioms as far as it makes sense. The coding style should mostly follow -the Python PEP-8, which includes the convention of 4 spaces for indentation. -See http://www.python.org/dev/peps/pep-0008/ for details. +the Python PEP-8, which includes the convention of 4 spaces for indentation: + + http://www.python.org/dev/peps/pep-0008/ Contributions ------------- - - Patches should be sent to the development mailinglist at + - In order to contribute you should ideally clone the git repository and + let us know (preferably via IRC, or via the mailing list) from where to + pull/review your changes. You can use github.com, or any other public git + hosting site. + + - Alternatively, patches can be sent to the development mailinglist at sigrok-devel@lists.sourceforge.net (please subscribe to the list first). https://lists.sourceforge.net/lists/listinfo/sigrok-devel - - Alternatively, you can also clone the git repository and let us know - from where to pull/review your changes. You can use gitorious.org, - github.com, or any other public git hosting site. - Random notes ------------ @@ -173,6 +176,47 @@ Protocol decoder guidelines Not recommended: 'FIND_ADDRESS', 'Get Temperature', 'start' + - Protocol decoder tags: + + - Every decoder must have a "tags" list (>= 1 items, alphabetically sorted). + + - All tag names start with a capital letter. Subsequent words of the name + are not capitalized, e.g. "Retro computing", "Debug/trace". + + - All tag names should use singular form ("Sensor", not "Sensors"). + + Common tags: + + - Analog/digital: Decoders related A/D conversion, e.g. ADCs and DACs. + - Audio: Decoders related to audio protocols, e.g. I2S, S/PDIF. + - Automotive: Decoders related to automotive protocols, e.g. CAN, FlexRay. + - Clock/timing: Decoders related to time keeping, timing, and clocks/RTCs. + - Debug/trace: Decoders related to microcontroller/CPU debugging, tracing, + programming/flashing protocols, e.g. SWD, JTAG, AVR ISP, ARM ETMv3. + - Display: Decoders related to display technologies, e.g. DVI, HDMI, + TFT, OLED, LCD, HD44780, EDID, and various LED protocols. + - Embedded/industrial: Decoders related to protocols used in embedded + systems, industrial systems, or automation (e.g. SPI, Modbus, Profibus). + - Encoding: Decoders related to generic encoding / line coding systems, + e.g. Manchester, Miller, Gray code, OOK, and similar. + - IC: Decoders for specific (families of) ICs (i.e. not IC-independent, + generic protocols like UART, SPI, CAN, or USB). + - IR: Decoders related to infrared (e.g. remote control) protocols. + - Lighting: Decoders related to lighting technologies, e.g. DALI, DMX512. + - Memory: Decoders related to memories (e.g. NOR/NAND flash, EEPROM, + SDRAM, SRAM, various other volatile or non-volatile memories). + - Networking: Decoders related to (wired) networking technologies. + - PC: Decoders related to protocols used in personal computers (desktop, + workstation, laptop, server). This is not meant to be restricted to + "IBM PC" or "x86/Intel", Apple/Commodore/Atari/SPARC etc. are fine too. + - RFID: Decoders related to RFID protocols, e.g. EM4100, T55xx. + - Retro computing: Decoders related to retro computing, e.g. MCS-48, Z80. + - Security/crypto: Decoders related to security or cryptography. + - Sensor: Decoders for sensors or all kinds, e.g. temperature or humidity. + - Util: Random utility/helper decoders. + - Wireless/RF: Decoders related to various wireless/RF technologies, e.g. + Bluetooth, BLE, Wifi, or 2.4GHz/433MHz custom protocols. + Testsuite --------- diff --git a/libsigrokdecode4DSL/Makefile.am b/libsigrokdecode4DSL/Makefile.am old mode 100644 new mode 100755 index 2e0f8910..5cc7974b --- a/libsigrokdecode4DSL/Makefile.am +++ b/libsigrokdecode4DSL/Makefile.am @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ACLOCAL_AMFLAGS = -I m4 @@ -45,7 +44,6 @@ libsigrokdecode4DSL_la_SOURCES = \ exception.c \ module_sigrokdecode.c \ type_decoder.c \ - type_logic.c \ error.c \ version.c diff --git a/libsigrokdecode4DSL/NEWS b/libsigrokdecode4DSL/NEWS old mode 100644 new mode 100755 index a98c7fb9..369dbe34 --- a/libsigrokdecode4DSL/NEWS +++ b/libsigrokdecode4DSL/NEWS @@ -1,22 +1,174 @@ -0.4.1 (2016-12-27) +0.5.2 (2018-09-12) ------------------ -Note: This release does NOT change the libsigrokdecode public C API. This -means existing libsigrokdecode frontends should not require any changes. -However, individual PDs and their output changed, which may require -some adaptations on the user's side in some cases. +Note: This release does NOT change the libsigrokdecode public C API in +incompatible ways. This means existing frontends should not require any +changes. However, individual PDs and their output changed, which may +require some adaptations on the user's side in some cases. * New supported protocol decoders: + - mcs48 Intel MCS-48 external memory access protocol + - st7735 Sitronix ST7735 TFT controller protocol + - atsha204a Microchip ATSHA204A CryptoAuthentication device + - cfp 100 Gigabit C form-factor pluggable (CFP) protocol + - jtag_ejtag MIPS EJTAG protocol + - swim STM8 Single Wire Interface Module (SWIM) protocol + - cec HDMI Consumer Electronics Control (CEC) protocol + - miller Miller encoding protocol + - ook On-off keying protocol + - ook_oregon Oregon Scientific weather sensor protocol + - ook_vis OOK visualisation in various formats + * Various minor performance improvements in the backend. + * Fix an issue causing decoder termination to take a long time. + * Fix multiple self.put() memory leaks (bug #329). + * Fix multiple memory leaks reported by scan-build. + * Show a backtrace when the decode aborts due to an error (bug #1158). + * Perform the log level check for non-default handlers as well (bug #698). + * Fix multiple compiler warnings. + * Compiler: Add -Wshadow -Wformat=2 -Wno-format-nonliteral -Wfloat-equal. + * Build system: Add Python 3.7 support. + * Fix a 'make install' issue that happens with Python >= 3.7. + * Add two missing Python GIL release operations. + * libsigrokdecode API changes: + - Add the srd_log_callback_get() API call. + - Add the SRD_ERR_TERM_REQ error code. + * dcf77: + - Annotate invalid dow and month fields, do not abort execution (bug #1173). + - Annotate unexpected bit numbers and values, do not abort execution. + * spiflash: + - Add basic Adesto AT45DBxx support (WRITE1/2, STATUS). + - Add Adesto AT45DB161E metadata. + * jtag: + - Fix shifting of registers (bug #1066). + * jtag_stm32: + - Fix handling of boundary scan TAP and incorrect state transitions. + * eeprom24xx: + - Add ON Semi CAT24C256 and CAT24M01 support. + * counter: + - Emit wider annotations to improve readability in UIs (bug #1210). + - Add support for user specified initial counter values (bug #1229). + - Add an option to ignore a certain number of edges. + - Let the user decide how to handle the initial dead_cycles state. + * mdio: + - Accept MDIO READ without previous ADDRESS command (bug #1258). + * edid: + - Add support for extension blocks. + - Various annotation improvements. + * spi: + - Fix an issue causing an AttributeError during certain decodes. + * usb_power_delivery: + - Emit more details in annotations (power, PDO type, RDO info). + - Add PPS support. + - Add better USB Power Delivery 3.0 support. + +0.5.1 (2018-04-12) +------------------ + +Note: This release does NOT change the libsigrokdecode public C API in +incompatible ways. This means existing frontends should not require any +changes. However, individual PDs and their output changed, which may +require some adaptations on the user's side in some cases. + + * New supported protocol decoders: + - ac97 Audio and modem control for PC systems + - ds243x Maxim DS243x series 1-Wire EEPROM protocol + - maple_bus Maple bus peripheral protocol for SEGA Dreamcast + - morse Demodulated morse code protocol + - graycode Accumulate rotary encoder increments, timing statistics + - counter Count number of edges + - sda2506 Serial nonvolatile 1-Kbit EEPROM + - rc_encode PT2262/HX2262/SC5262 remote control encoder protocol + * Updated build requirements: + - libglib >= 2.34 + * New API calls: + - srd_searchpaths_get(): Retrieve PD search paths. + - srd_session_terminate_reset(): Terminate currently executing decoders + in a session, reset internal state. Allows re-using a session. + * can: + - Introduce clock synchronization (bug #990). + - Fix incorrect stuff bit handling (bug #1085). + * i2c: + - Don't drop the first sample of input data. + - Make input samplerate optional (bug #1076). + * i2s: + - Binary output: Make 32-bit WAV files, set file size to 4GB. + - Fix unportable use of struct.pack(). + - Make samplerate meta data optional. + * stepper_motor: + - Make samplerate meta data optional. + * pwm: + - Fixup sample numbers for binary output. + * onewire_network: + - Fixup start samplenumber for annotations. + * lpc: + - Improve robustness when decoding unexpected input data. + * ir_nec: + - Make timing margin relative. + - Optionally accept input signals that include the carrier. + * usb_request: + - Handle CONTROL transfer protocol stalls. + * usb_power_delivery: + - Enforce numerical order of RDO/PDO flag annotations, enforce check + order for start-of-packet sequences (bug #1090). + * parallel: + - Cope with sparse input maps, assume zero for unconnected pins (bug #1088). + - Unbreak and improve "word" de-multiplexing. + - Implement consistent number formatting. + - Expand 'wordsize' description (bits vs cycles). + * spi: + - Cope when a sample rate of 0 was configured (bug #1118). + * Acquire/release the Python GIL where needed to avoid threading issues. + This allows frontends to call libsigrokdecode API functions from different + threads without running into issues. + * Convert all protocol decoders to PD API version 3. + * Support for the old PD API version 2 has been removed. + * Unbreak the execution of Decoder.wait() with empty condition. + * self.wait(): Accept more forms of "unconditional wait" (None, no args). + * Fix a compiler warning on Mac OS X. + * configure.ac: Add missing PKG_PROG_PKG_CONFIG and AC_C_BIGENDIAN. + * Add a reset() method to all PDs which resets internal variable state. + * Clarify and improve channel mapping debug output. + * Print decoder and system search paths in debug output. + * Drop overly verbose log messages about (un)loading PDs. + * HACKING: Prefer git pull requests over mailing list patches. + +0.5.0 (2017-06-12) +------------------ + +Note: This release DOES change the libsigrokdecode public C API. This +means it is NOT backwards-compatible and frontends will need updates. + + * New supported protocol decoders: + - ade77xx Poly phase multifunction energy metering IC protocol + - adf435x Wideband synthesizer with integrated VCO - aud Renesas/Hitachi Advanced User Debugger (AUD) protocol - avr_pdi Atmel proprietary interface for the ATxmega MCU + - dali DALI lighting control protocol + - dsi DSI lighting control protocol - dmx512 Professional lighting control protocol + - eeprom93xx 93xx series Microwire EEPROM protocol (bug #931) - em4305 EM4205/EM4305 100-150kHz RFID protocol - gpib IEEE-488 GPIB / HPIB protocol + - iec Commodore serial IEEE-488 (IEC) bus protocol + - microwire 3-wire, half-duplex, synchronous serial bus (bug #931) - ps2 PS/2 keyboard/mouse interface - rgb_led_ws281x RGB LED string protocol (WS281x) - ssi32 Synchronous Serial Interface (32bit) protocol - t55xx T55xx 100-150kHz RFID protocol - wiegand Wiegand interface for electronic entry systems + * Add support for the new query-based PD v3 decoder API. + - Decoders using PD v3 API can benefit from both readability improvements + as well as performance improvements. Up to 10x speedup has been measured + in some situations (depends a lot on the decoder, the amount of data, + the amount of edges in the signals, and the amount of oversampling). + - The majority of all decoders have been converted to PD API v3. + - For the time being, both APIs (2 and 3) will remain supported until all + decoders have been converted to API version 3. Then, support for API + version 2 will be dropped. + * Add support for specifying the (assumed) initial pin states before the + first sample that is being decoded. This can be set to 0/low, 1/high, or + to "use the same value as in the first sample". + * Add support for adding multiple instances of a decoder (bug #868). * Introduce a decoders/common facility for code shared between PDs (bug #804). * Supply metadata to all stacked decoders, not just low-level ones (bug #664). * Various Doxygen/documentation improvements. @@ -45,6 +197,8 @@ some adaptations on the user's side in some cases. - Various annotation improvements. * timing: - Add frequency and averaging support. + - Disable averaging feature if it's set to zero. + - Add edge-edge mode and delta calculation. * midi: - Decode note names and percussion names (e.g. 'G2', 'Tambourine'). - Decode instrument names and drum_kit names (e.g. 'GS Orchestra Kit'). @@ -80,11 +234,30 @@ some adaptations on the user's side in some cases. - Default to hex format datavalue annotations. * spi: - Fix binary output for wordsizes > 8 (bug #686). + * sdcard_sd: + - Don't try to decode commands that are not yet supported. + - Always show CMD argument in hex. + - Fix incorrect CMD8 annotations. + * can: + - Add warnings for various invalid bits and fields. + - Skip stuff bit inspection where not applicable (bug #656). + * eeprom24xx: + - Add Xicor X24C02 support. + * onewire_link: + - Replace PD with a new, improved implementation (bugs #666, #926). + - Overdrive speed is now detected automatically. + - Some no longer needed decoder options have been dropped. * Various decoders: - Fix some incorrect PD license metadata fields. - Fix typos, whitespace issues and random other cosmetics in some PDs. - Use consistent __init__() format across all PDs. - Move some duplicated code snippets/lists to decoders/common. + * Rename the maxim_ds28ea00 decoder to ds28ea00. + * Show library versions in the debug output. + * configure: Also check for Python 3.6. + * configure summary: Show build type (static/shared) and linker flags. + * Remove FSF postal address from boiler plate license text. + * HACKING: Update URL to Linux kernel coding style. 0.4.0 (2016-01-29) ------------------ diff --git a/libsigrokdecode4DSL/README b/libsigrokdecode4DSL/README old mode 100644 new mode 100755 index 686efeff..784be277 --- a/libsigrokdecode4DSL/README +++ b/libsigrokdecode4DSL/README @@ -34,7 +34,7 @@ Requirements - automake >= 1.11 (only needed when building from git) - libtool (only needed when building from git) - pkg-config >= 0.22 - - libglib >= 2.28.0 + - libglib >= 2.34 - Python >= 3.2 - check >= 0.9.4 (optional, only needed to run unit tests) - doxygen (optional, only needed for the C API docs) diff --git a/libsigrokdecode4DSL/configure.ac b/libsigrokdecode4DSL/configure.ac old mode 100644 new mode 100755 index 002372ae..80746d64 --- a/libsigrokdecode4DSL/configure.ac +++ b/libsigrokdecode4DSL/configure.ac @@ -21,7 +21,7 @@ AC_PREREQ([2.63]) # libsigrokdecode package version number (NOT the same as shared lib version!). -AC_INIT([libsigrokdecode4DSL], [0.5.0], +AC_INIT([libsigrokdecode4DSL], [0.6.0], [support@dreamsourcelab.com], [libsigrokdecode4DSL], [http://www.dreamsourcelab.com]) AC_CONFIG_MACRO_DIR([m4]) @@ -46,7 +46,7 @@ AM_PROG_CC_C_O # Set the standard the C library headers should conform to. AH_VERBATIM([_POSIX_C_SOURCE], [/* The targeted POSIX standard. */ #ifndef _POSIX_C_SOURCE -# define _POSIX_C_SOURCE 200809L +# define _POSIX_C_SOURCE 200112L #endif]) # Get compiler versions. @@ -63,10 +63,14 @@ SR_PKG_VERSION_SET([SRD_PACKAGE_VERSION], [AC_PACKAGE_VERSION]) # The algorithm for determining which number to change (and how) is nontrivial! # http://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info # Format: current:revision:age. -SR_LIB_VERSION_SET([SRD_LIB_VERSION], [3:0:0]) +SR_LIB_VERSION_SET([SRD_LIB_VERSION], [4:0:0]) AM_CONDITIONAL([WIN32], [test -z "${host_os##mingw*}" || test -z "${host_os##cygwin*}"]) +# Initialize pkg-config. +# We require at least 0.22, as "Requires.private" behaviour changed there. +PKG_PROG_PKG_CONFIG + ############################ ## Package dependencies ## ############################ @@ -86,14 +90,14 @@ SR_PKG_CHECK_SUMMARY([srd_pkglibs_summary]) # Python 3 is always needed. SR_PKG_CHECK([python3], [SRD_PKGLIBS], - [python3 >= 3.2], [python-3.5 >= 3.5], [python-3.4 >= 3.4], [python-3.3 >= 3.3], [python-3.2 >= 3.2]) + [python3 >= 3.2], [python-3.7 >= 3.7], [python-3.6 >= 3.6], [python-3.5 >= 3.5], [python-3.4 >= 3.4], [python-3.3 >= 3.3], [python-3.2 >= 3.2]) AS_IF([test "x$sr_have_python3" = xno], [AC_MSG_ERROR([Cannot find Python 3 development headers.])]) # We also need to find the name of the python3 executable (for 'make install'). # Some OSes call this python3, some call it python3.2, etc. etc. AC_ARG_VAR([PYTHON3], [Python 3 interpreter]) -AC_CHECK_PROGS([PYTHON3], [python3.5 python3.4 python3.3 python3.2 python3]) +AC_CHECK_PROGS([PYTHON3], [python3.7 python3.6 python3.5 python3.4 python3.3 python3.2 python3]) AS_IF([test "x$PYTHON3" = x], [AC_MSG_ERROR([Cannot find Python 3 interpreter.])]) @@ -115,7 +119,7 @@ SR_CHECK_COMPILE_FLAGS([SRD_EXTRA_CFLAGS], [C99], [-std=c99 -c99 -AC99 -qlanglvl SR_CHECK_COMPILE_FLAGS([SRD_EXTRA_CFLAGS], [visibility], [-fvisibility=hidden]) # Select suitable compiler warning flags. -SR_ARG_ENABLE_WARNINGS([SRD_WFLAGS], [-Wall], [-Wall -Wextra -Wmissing-prototypes]) +SR_ARG_ENABLE_WARNINGS([SRD_WFLAGS], [-Wall], [-Wall -Wextra -Wmissing-prototypes -Wshadow -Wformat=2 -Wno-format-nonliteral -Wfloat-equal]) # Link against libm, this is required (among other things) by Python. SRD_EXTRA_LIBS= @@ -123,6 +127,8 @@ SR_SEARCH_LIBS([SRD_EXTRA_LIBS], [pow], [m]) AC_SYS_LARGEFILE +AC_C_BIGENDIAN + ############################## ## Finalize configuration ## ############################## @@ -131,11 +137,14 @@ AC_SUBST([SRD_PKGLIBS]) # Retrieve the compile and link flags for all modules combined. # Also, bail out at this point if any module dependency is not met. -PKG_CHECK_MODULES([LIBSIGROKDECODE], [glib-2.0 >= 2.28.0 $SRD_PKGLIBS]) +PKG_CHECK_MODULES([LIBSIGROKDECODE], [glib-2.0 >= 2.34 $SRD_PKGLIBS]) PKG_CHECK_MODULES([TESTS], [$SRD_PKGLIBS_TESTS glib-2.0 $SRD_PKGLIBS]) srd_glib_version=`$PKG_CONFIG --modversion glib-2.0 2>&AS_MESSAGE_LOG_FD` +AC_DEFINE_UNQUOTED([CONF_HOST], ["$host"], + [The canonical host libsigrokdecode will run on.]) + AC_CONFIG_FILES([Makefile libsigrokdecode4DSL.pc]) AC_OUTPUT @@ -148,6 +157,7 @@ libsigrokdecode configuration summary: - Prefix.......................... $prefix - Building on..................... $build - Building for.................... $host + - Building shared / static........ $enable_shared / $enable_static Compile configuration: - C compiler...................... $CC @@ -155,9 +165,10 @@ Compile configuration: - C compiler flags................ $CFLAGS - Additional C compiler flags..... $SRD_EXTRA_CFLAGS - C compiler warnings............. $SRD_WFLAGS + - Linker flags.................... $LDFLAGS Detected libraries (required): - - glib-2.0 >= 2.28.0.............. $srd_glib_version + - glib-2.0 >= 2.34................ $srd_glib_version $srd_pkglibs_summary Detected libraries (optional): $srd_pkglibs_opt_summary diff --git a/libsigrokdecode4DSL/contrib/sigrok-logo-notext.png b/libsigrokdecode4DSL/contrib/sigrok-logo-notext.png old mode 100644 new mode 100755 diff --git a/libsigrokdecode4DSL/decoder.c b/libsigrokdecode4DSL/decoder.c old mode 100644 new mode 100755 index 4797844e..4906b5d9 --- a/libsigrokdecode4DSL/decoder.c +++ b/libsigrokdecode4DSL/decoder.c @@ -3,7 +3,7 @@ * * Copyright (C) 2010 Uwe Hermann * Copyright (C) 2012 Bert Vermeulen - * Copyright (C) 2016 DreamSourceLab + * Copyright (C) 2019 DreamSourceLab * * 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 @@ -153,11 +153,15 @@ static void decoder_option_free(void *data) static void decoder_free(struct srd_decoder *dec) { + PyGILState_STATE gstate; + if (!dec) return; + gstate = PyGILState_Ensure(); Py_XDECREF(dec->py_dec); Py_XDECREF(dec->py_mod); + PyGILState_Release(gstate); g_slist_free_full(dec->options, &decoder_option_free); g_slist_free_full(dec->binary, (GDestroyNotify)&g_strfreev); @@ -166,6 +170,9 @@ static void decoder_free(struct srd_decoder *dec) g_slist_free_full(dec->opt_channels, &channel_free); g_slist_free_full(dec->channels, &channel_free); + g_slist_free_full(dec->outputs, g_free); + g_slist_free_full(dec->inputs, g_free); + g_slist_free_full(dec->tags, g_free); g_free(dec->license); g_free(dec->desc); g_free(dec->longname); @@ -182,10 +189,15 @@ static int get_channels(const struct srd_decoder *d, const char *attr, struct srd_channel *pdch; GSList *pdchl; ssize_t i; + PyGILState_STATE gstate; - if (!PyObject_HasAttrString(d->py_dec, attr)) + gstate = PyGILState_Ensure(); + + if (!PyObject_HasAttrString(d->py_dec, attr)) { /* No channels of this type specified. */ + PyGILState_Release(gstate); return SRD_OK; + } pdchl = NULL; @@ -209,7 +221,7 @@ static int get_channels(const struct srd_decoder *d, const char *attr, "a list of dict elements.", d->name, attr); goto err_out; } - pdch = g_malloc0(sizeof(struct srd_channel)); + pdch = g_malloc(sizeof(struct srd_channel)); /* Add to list right away so it doesn't get lost. */ pdchl = g_slist_prepend(pdchl, pdch); @@ -229,14 +241,18 @@ static int get_channels(const struct srd_decoder *d, const char *attr, Py_DECREF(py_channellist); *out_pdchl = pdchl; + PyGILState_Release(gstate); + return SRD_OK; except_out: - srd_exception_catch(NULL, "Failed to get %s list of %s decoder", + srd_exception_catch(NULL, "Failed to get %s list of %s decoder", attr, d->name); + err_out: g_slist_free_full(pdchl, &channel_free); Py_XDECREF(py_channellist); + PyGILState_Release(gstate); return SRD_ERR_PYTHON; } @@ -248,10 +264,15 @@ static int get_options(struct srd_decoder *d) struct srd_decoder_option *o; GVariant *gvar; ssize_t opt, i; + PyGILState_STATE gstate; - if (!PyObject_HasAttrString(d->py_dec, "options")) + gstate = PyGILState_Ensure(); + + if (!PyObject_HasAttrString(d->py_dec, "options")) { /* No options, that's fine. */ + PyGILState_Release(gstate); return SRD_OK; + } options = NULL; @@ -283,7 +304,7 @@ static int get_options(struct srd_decoder *d) py_str = PyDict_GetItemString(py_opt, "id"); if (!py_str) { - srd_err("Protocol decoder %s option %zd has no id.", + srd_err("Protocol decoder %s option %zd has no ID.", d->name, opt); goto err_out; } @@ -309,8 +330,10 @@ static int get_options(struct srd_decoder *d) py_values = PyDict_GetItemString(py_opt, "values"); if (py_values) { - /* A default is required if a list of values is - * given, since it's used to verify their type. */ + /* + * A default is required if a list of values is + * given, since it's used to verify their type. + */ if (!o->def) { srd_err("No default for option '%s'.", o->id); goto err_out; @@ -325,7 +348,7 @@ static int get_options(struct srd_decoder *d) if (!py_item) goto except_out; - if (Py_TYPE(py_default) != Py_TYPE(py_item)) { + if (py_default && (Py_TYPE(py_default) != Py_TYPE(py_item))) { srd_err("All values for option '%s' must be " "of the same type as the default.", o->id); @@ -344,20 +367,22 @@ static int get_options(struct srd_decoder *d) } d->options = options; Py_DECREF(py_opts); + PyGILState_Release(gstate); return SRD_OK; except_out: - srd_exception_catch(NULL, "Failed to get %s decoder options", d->name); + srd_exception_catch(NULL, "Failed to get %s decoder options", d->name); + err_out: g_slist_free_full(options, &decoder_option_free); Py_XDECREF(py_opts); + PyGILState_Release(gstate); return SRD_ERR_PYTHON; } -/* Convert annotation class attribute to GSList of char **. - */ +/* Convert annotation class attribute to GSList of char **. */ static int get_annotations(struct srd_decoder *dec) { PyObject *py_annlist, *py_ann; @@ -366,9 +391,14 @@ static int get_annotations(struct srd_decoder *dec) ssize_t i; int ann_type = 7; unsigned int j; + PyGILState_STATE gstate; - if (!PyObject_HasAttrString(dec->py_dec, "annotations")) + gstate = PyGILState_Ensure(); + + if (!PyObject_HasAttrString(dec->py_dec, "annotations")) { + PyGILState_Release(gstate); return SRD_OK; + } annotations = NULL; @@ -400,9 +430,9 @@ static int get_annotations(struct srd_decoder *dec) if (PyTuple_Size(py_ann) == 3) { ann_type = 0; - for (j = 0; j < strlen(annpair[0]); j++) - ann_type = ann_type * 10 + (annpair[0][j] - '0'); - dec->ann_types = g_slist_append(dec->ann_types, GINT_TO_POINTER(ann_type)); + for (j = 0; j < strlen(annpair[0]); j++) + ann_type = ann_type * 10 + (annpair[0][j] - '0'); + dec->ann_types = g_slist_append(dec->ann_types, GINT_TO_POINTER(ann_type)); } else if (PyTuple_Size(py_ann) == 2) { dec->ann_types = g_slist_append(dec->ann_types, GINT_TO_POINTER(ann_type)); ann_type++; @@ -410,20 +440,22 @@ static int get_annotations(struct srd_decoder *dec) } dec->annotations = annotations; Py_DECREF(py_annlist); + PyGILState_Release(gstate); return SRD_OK; except_out: - srd_exception_catch(NULL, "Failed to get %s decoder annotations", dec->name); + srd_exception_catch(NULL, "Failed to get %s decoder annotations", dec->name); + err_out: g_slist_free_full(annotations, (GDestroyNotify)&g_strfreev); Py_XDECREF(py_annlist); + PyGILState_Release(gstate); return SRD_ERR_PYTHON; } -/* Convert annotation_rows to GSList of 'struct srd_decoder_annotation_row'. - */ +/* Convert annotation_rows to GSList of 'struct srd_decoder_annotation_row'. */ static int get_annotation_rows(struct srd_decoder *dec) { PyObject *py_ann_rows, *py_ann_row, *py_ann_classes, *py_item; @@ -431,9 +463,14 @@ static int get_annotation_rows(struct srd_decoder *dec) struct srd_decoder_annotation_row *ann_row; ssize_t i, k; size_t class_idx; + PyGILState_STATE gstate; - if (!PyObject_HasAttrString(dec->py_dec, "annotation_rows")) + gstate = PyGILState_Ensure(); + + if (!PyObject_HasAttrString(dec->py_dec, "annotation_rows")) { + PyGILState_Release(gstate); return SRD_OK; + } annotation_rows = NULL; @@ -506,30 +543,37 @@ static int get_annotation_rows(struct srd_decoder *dec) } dec->annotation_rows = annotation_rows; Py_DECREF(py_ann_rows); + PyGILState_Release(gstate); return SRD_OK; except_out: - srd_exception_catch(NULL, "Failed to get %s decoder annotation rows", + srd_exception_catch(NULL, "Failed to get %s decoder annotation rows", dec->name); + err_out: g_slist_free_full(annotation_rows, &annotation_row_free); Py_XDECREF(py_ann_rows); + PyGILState_Release(gstate); return SRD_ERR_PYTHON; } -/* Convert binary classes to GSList of char **. - */ +/* Convert binary classes to GSList of char **. */ static int get_binary_classes(struct srd_decoder *dec) { PyObject *py_bin_classes, *py_bin_class; GSList *bin_classes; char **bin; ssize_t i; + PyGILState_STATE gstate; - if (!PyObject_HasAttrString(dec->py_dec, "binary")) + gstate = PyGILState_Ensure(); + + if (!PyObject_HasAttrString(dec->py_dec, "binary")) { + PyGILState_Release(gstate); return SRD_OK; + } bin_classes = NULL; @@ -562,37 +606,45 @@ static int get_binary_classes(struct srd_decoder *dec) } dec->binary = bin_classes; Py_DECREF(py_bin_classes); + PyGILState_Release(gstate); return SRD_OK; except_out: - srd_exception_catch(NULL, "Failed to get %s decoder binary classes", + srd_exception_catch(NULL, "Failed to get %s decoder binary classes", dec->name); + err_out: g_slist_free_full(bin_classes, (GDestroyNotify)&g_strfreev); Py_XDECREF(py_bin_classes); + PyGILState_Release(gstate); return SRD_ERR_PYTHON; } -/* Check whether the Decoder class defines the named method. - */ +/* Check whether the Decoder class defines the named method. */ static int check_method(PyObject *py_dec, const char *mod_name, const char *method_name) { PyObject *py_method; int is_callable; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); py_method = PyObject_GetAttrString(py_dec, method_name); if (!py_method) { - srd_exception_catch(NULL, "Protocol decoder %s Decoder class " + srd_exception_catch(NULL, "Protocol decoder %s Decoder class " "has no %s() method", mod_name, method_name); + PyGILState_Release(gstate); return SRD_ERR_PYTHON; } is_callable = PyCallable_Check(py_method); Py_DECREF(py_method); + PyGILState_Release(gstate); + if (!is_callable) { srd_err("Protocol decoder %s Decoder class attribute '%s' " "is not a method.", mod_name, method_name); @@ -608,20 +660,27 @@ static int check_method(PyObject *py_dec, const char *mod_name, * @param d The decoder to use. Must not be NULL. * * @return The API version of the decoder, or 0 upon errors. + * + * @private */ SRD_PRIV long srd_decoder_apiver(const struct srd_decoder *d) { PyObject *py_apiver; long apiver; + PyGILState_STATE gstate; if (!d) return 0; + gstate = PyGILState_Ensure(); + py_apiver = PyObject_GetAttrString(d->py_dec, "api_version"); apiver = (py_apiver && PyLong_Check(py_apiver)) ? PyLong_AsLong(py_apiver) : 0; Py_XDECREF(py_apiver); + PyGILState_Release(gstate); + return apiver; } @@ -641,6 +700,7 @@ SRD_API int srd_decoder_load(const char *module_name) long apiver; int is_subclass; const char *fail_txt; + PyGILState_STATE gstate; if (!srd_check_init()) return SRD_ERR; @@ -648,13 +708,14 @@ SRD_API int srd_decoder_load(const char *module_name) if (!module_name) return SRD_ERR_ARG; + gstate = PyGILState_Ensure(); + if (PyDict_GetItemString(PyImport_GetModuleDict(), module_name)) { /* Module was already imported. */ + PyGILState_Release(gstate); return SRD_OK; } - srd_dbg("Loading protocol decoder '%s'.", module_name); - d = g_malloc0(sizeof(struct srd_decoder)); fail_txt = NULL; @@ -698,15 +759,20 @@ SRD_API int srd_decoder_load(const char *module_name) * PDs of different API versions are incompatible and cannot work. */ apiver = srd_decoder_apiver(d); - if (apiver != 2) { - srd_exception_catch(NULL, "Only PD API version 2 is supported, " + if (apiver != 3) { + srd_exception_catch(NULL, "Only PD API version 3 is supported, " "decoder %s has version %ld", module_name, apiver); fail_txt = "API version mismatch"; goto err_out; } - /* Check Decoder class for required methods. - */ + /* Check Decoder class for required methods. */ + + if (check_method(d->py_dec, module_name, "reset") != SRD_OK) { + fail_txt = "no 'reset()' method"; + goto err_out; + } + if (check_method(d->py_dec, module_name, "start") != SRD_OK) { fail_txt = "no 'start()' method"; goto err_out; @@ -743,6 +809,21 @@ SRD_API int srd_decoder_load(const char *module_name) goto err_out; } + if (py_attr_as_strlist(d->py_dec, "inputs", &(d->inputs)) != SRD_OK) { + fail_txt = "missing or malformed 'inputs' attribute"; + goto err_out; + } + + if (py_attr_as_strlist(d->py_dec, "outputs", &(d->outputs)) != SRD_OK) { + fail_txt = "missing or malformed 'outputs' attribute"; + goto err_out; + } + + if (py_attr_as_strlist(d->py_dec, "tags", &(d->tags)) != SRD_OK) { + fail_txt = "missing or malformed 'tags' attribute"; + goto err_out; + } + /* All options and their default values. */ if (get_options(d) != SRD_OK) { fail_txt = "cannot get options"; @@ -777,23 +858,26 @@ SRD_API int srd_decoder_load(const char *module_name) goto err_out; } + PyGILState_Release(gstate); + /* Append it to the list of loaded decoders. */ pd_list = g_slist_append(pd_list, d); return SRD_OK; except_out: - if (fail_txt) { - srd_exception_catch(NULL, "Failed to load decoder %s: %s", + /* Don't show a message for the "common" directory, it's not a PD. */ + if (strcmp(module_name, "common")) { + srd_exception_catch(NULL, "Failed to load decoder %s: %s", module_name, fail_txt); - fail_txt = NULL; - } else { - srd_exception_catch(NULL, "Failed to load decoder %s", module_name); } + fail_txt = NULL; + err_out: if (fail_txt) srd_err("Failed to load decoder %s: %s", module_name, fail_txt); decoder_free(d); + PyGILState_Release(gstate); return SRD_ERR_PYTHON; } @@ -812,6 +896,7 @@ SRD_API char *srd_decoder_doc_get(const struct srd_decoder *dec) { PyObject *py_str; char *doc; + PyGILState_STATE gstate; if (!srd_check_init()) return NULL; @@ -819,12 +904,14 @@ SRD_API char *srd_decoder_doc_get(const struct srd_decoder *dec) if (!dec) return NULL; + gstate = PyGILState_Ensure(); + if (!PyObject_HasAttrString(dec->py_mod, "__doc__")) - return NULL; + goto err; if (!(py_str = PyObject_GetAttrString(dec->py_mod, "__doc__"))) { - srd_exception_catch(NULL, "Failed to get docstring"); - return NULL; + srd_exception_catch(NULL, "Failed to get docstring"); + goto err; } doc = NULL; @@ -832,7 +919,14 @@ SRD_API char *srd_decoder_doc_get(const struct srd_decoder *dec) py_str_as_str(py_str, &doc); Py_DECREF(py_str); + PyGILState_Release(gstate); + return doc; + +err: + PyGILState_Release(gstate); + + return NULL; } /** @@ -855,8 +949,6 @@ SRD_API int srd_decoder_unload(struct srd_decoder *dec) if (!dec) return SRD_ERR_ARG; - srd_dbg("Unloading protocol decoder '%s'.", dec->name); - /* * Since any instances of this decoder need to be released as well, * but they could be anywhere in the stack, just free the entire @@ -865,7 +957,7 @@ SRD_API int srd_decoder_unload(struct srd_decoder *dec) */ for (l = sessions; l; l = l->next) { sess = l->data; - srd_inst_free_all(sess, NULL); + srd_inst_free_all(sess); } /* Remove the PD from the list of loaded decoders. */ @@ -876,16 +968,19 @@ SRD_API int srd_decoder_unload(struct srd_decoder *dec) return SRD_OK; } -static void srd_decoder_load_all_zip_path(char *path) +static void srd_decoder_load_all_zip_path(char *zip_path) { PyObject *zipimport_mod, *zipimporter_class, *zipimporter; PyObject *prefix_obj, *files, *key, *value, *set, *modname; Py_ssize_t pos = 0; char *prefix; size_t prefix_len; + PyGILState_STATE gstate; set = files = prefix_obj = zipimporter = zipimporter_class = NULL; + gstate = PyGILState_Ensure(); + zipimport_mod = py_import_by_name("zipimport"); if (zipimport_mod == NULL) goto err_out; @@ -894,7 +989,7 @@ static void srd_decoder_load_all_zip_path(char *path) if (zipimporter_class == NULL) goto err_out; - zipimporter = PyObject_CallFunction(zipimporter_class, "s", path); + zipimporter = PyObject_CallFunction(zipimporter_class, "s", zip_path); if (zipimporter == NULL) goto err_out; @@ -954,6 +1049,7 @@ err_out: Py_XDECREF(zipimporter_class); Py_XDECREF(zipimport_mod); PyErr_Clear(); + PyGILState_Release(gstate); } static void srd_decoder_load_all_path(char *path) @@ -962,21 +1058,21 @@ static void srd_decoder_load_all_path(char *path) const gchar *direntry; if (!(dir = g_dir_open(path, 0, NULL))) { - /* Not really fatal */ - /* Try zipimport method too */ + /* Not really fatal. Try zipimport method too. */ srd_decoder_load_all_zip_path(path); return; } - /* This ignores errors returned by srd_decoder_load(). That + /* + * This ignores errors returned by srd_decoder_load(). That * function will have logged the cause, but in any case we - * want to continue anyway. */ + * want to continue anyway. + */ while ((direntry = g_dir_read_name(dir)) != NULL) { /* The directory name is the module name (e.g. "i2c"). */ srd_decoder_load(direntry); } g_dir_close(dir); - } /** @@ -999,6 +1095,13 @@ SRD_API int srd_decoder_load_all(void) return SRD_OK; } +static void srd_decoder_unload_cb(void *arg, void *ignored) +{ + (void)ignored; + + srd_decoder_unload((struct srd_decoder *)arg); +} + /** * Unload all loaded protocol decoders. * @@ -1008,7 +1111,7 @@ SRD_API int srd_decoder_load_all(void) */ SRD_API int srd_decoder_unload_all(void) { - g_slist_foreach(pd_list, (GFunc)srd_decoder_unload, NULL); + g_slist_foreach(pd_list, srd_decoder_unload_cb, NULL); g_slist_free(pd_list); pd_list = NULL; diff --git a/libsigrokdecode4DSL/decoders/0-i2c/__init__.py b/libsigrokdecode4DSL/decoders/0-i2c/__init__.py index c3b0cd50..2a36b060 100755 --- a/libsigrokdecode4DSL/decoders/0-i2c/__init__.py +++ b/libsigrokdecode4DSL/decoders/0-i2c/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/0-i2c/pd.py b/libsigrokdecode4DSL/decoders/0-i2c/pd.py index 754d4a2a..c9251dbe 100755 --- a/libsigrokdecode4DSL/decoders/0-i2c/pd.py +++ b/libsigrokdecode4DSL/decoders/0-i2c/pd.py @@ -1,8 +1,8 @@ ## ## This file is part of the libsigrokdecode project. ## -## Copyright (C) 2010-2014 Uwe Hermann -## Copyright (C) 2016 DreamSourceLab +## Copyright (C) 2010-2016 Uwe Hermann +## Copyright (C) 2019 DreamSourceLab ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -15,12 +15,10 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## # TODO: Look into arbitration, collision detection, clock synchronisation, etc. -# TODO: Implement support for 10bit slave addresses. # TODO: Implement support for inverting SDA/SCL levels (0->1 and 1->0). # TODO: Implement support for detecting various bus errors. @@ -57,20 +55,14 @@ proto = { 'STOP': [2, 'Stop', 'P'], 'ACK': [3, 'ACK', 'A'], 'NACK': [4, 'NACK', 'N'], - 'READ': [5, 'Read', 'R'], - 'WRITE': [6, 'Write', 'W'], - 'BIT': [7, 'Bit', 'B'], - 'ADDRESS READ': [8, 'Address read', 'AR'], - 'ADDRESS WRITE': [9, 'Address write', 'AW'], - 'DATA READ': [10, 'Data read', 'DR'], - 'DATA WRITE': [11, 'Data write', 'DW'], + 'ADDRESS READ': [5, 'Address read', 'AR'], + 'ADDRESS WRITE': [6, 'Address write', 'AW'], + 'DATA READ': [7, 'Data read', 'DR'], + 'DATA WRITE': [8, 'Data write', 'DW'], } -class SamplerateError(Exception): - pass - class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = '0:i2c' name = '0:I²C' longname = 'Inter-Integrated Circuit' @@ -78,9 +70,10 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['logic'] outputs = ['i2c'] + tags = ['Embedded/industrial'] channels = ( - {'id': 'scl', 'name': 'SCL', 'desc': 'Serial clock line'}, - {'id': 'sda', 'name': 'SDA', 'desc': 'Serial data line'}, + {'id': 'scl', 'type': 8, 'name': 'SCL', 'desc': 'Serial clock line'}, + {'id': 'sda', 'type': 108, 'name': 'SDA', 'desc': 'Serial data line'}, ) options = ( {'id': 'address_format', 'desc': 'Displayed slave address format', @@ -92,9 +85,6 @@ class Decoder(srd.Decoder): ('1', 'stop', 'Stop condition'), ('5', 'ack', 'ACK'), ('0', 'nack', 'NACK'), - ('12', 'read', 'Read'), - ('11', 'write', 'Write'), - ('108', 'bit', 'Data/address bit'), ('112', 'address-read', 'Address read'), ('111', 'address-write', 'Address write'), ('110', 'data-read', 'Data read'), @@ -102,123 +92,51 @@ class Decoder(srd.Decoder): ('1000', 'warnings', 'Human-readable warnings'), ) annotation_rows = ( - #('bits', 'Bits', (7,)), - ('addr-data', 'Address/Data', (0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11)), - #('warnings', 'Warnings', (12,)), - ) - binary = ( - ('address-read', 'Address read'), - ('address-write', 'Address write'), - ('data-read', 'Data read'), - ('data-write', 'Data write'), + ('addr-data', 'Address/Data', (0, 1, 2, 3, 4, 5, 6, 7, 8)), + ('warnings', 'Warnings', (9,)), ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.ss = self.es = self.ss_byte = -1 - self.samplenum = None self.bitcount = 0 self.databyte = 0 self.wr = -1 self.is_repeat_start = 0 self.state = 'FIND START' - self.oldscl = self.oldsda = -1 self.pdu_start = None - #self.pdu_bits = 0 - #self.bits = [] + self.pdu_bits = 0 + self.bits = [] def metadata(self, key, value): if key == srd.SRD_CONF_SAMPLERATE: self.samplerate = value def start(self): - self.out_python = self.register(srd.OUTPUT_PYTHON) self.out_ann = self.register(srd.OUTPUT_ANN) - self.out_binary = self.register(srd.OUTPUT_BINARY) - self.out_bitrate = self.register(srd.OUTPUT_META, - meta=(int, 'Bitrate', 'Bitrate from Start bit to Stop bit')) - if not self.samplerate: - raise SamplerateError('Cannot decode without samplerate.') def putx(self, data): self.put(self.ss, self.es, self.out_ann, data) - #def putp(self, data): - # self.put(self.ss, self.es, self.out_python, data) - - #def putb(self, data): - # self.put(self.ss, self.es, self.out_binary, data) - - def found_start(self, scl, sda): + def handle_start(self): self.ss, self.es = self.samplenum, self.samplenum self.pdu_start = self.samplenum - #self.pdu_bits = 0 + self.pdu_bits = 0 cmd = 'START REPEAT' if (self.is_repeat_start == 1) else 'START' - #self.putp([cmd, None]) self.putx([proto[cmd][0], proto[cmd][1:]]) self.state = 'FIND ADDRESS' self.bitcount = self.databyte = 0 self.is_repeat_start = 1 self.wr = -1 - #self.bits = [] - - # Gather 7 bits of address, 1 bit of rd/wr, plus the ACK/NACK bit. - def found_address(self, scl, sda): - # Address and data are transmitted MSB-first. - self.databyte <<= 1 - self.databyte |= sda - - # Remember the start of the first data/address bit. - if self.bitcount == 0: - self.ss_byte = self.samplenum - if self.bitcount == 1: - self.bitwidth = self.samplenum - self.ss_byte - - # Store individual bits and their start/end samplenumbers. - # In the list, index 0 represents the MSB (I²C transmits MSB-first). - #self.bits.append([sda, self.samplenum, self.samplenum]) - #if self.bitcount > 0: - # self.bits[self.bitcount-1][2] = self.samplenum - #if self.bitcount == 7: - # self.bits[7][2] += self.bitwidth - - # Return if we haven't collected all 8 + 1 bits, yet. - if self.bitcount < 7: - self.bitcount += 1 - return - - # The READ/WRITE bit is only in address bytes, not data bytes. - self.wr = 0 if (self.databyte & 1) else 1 - if self.options['address_format'] == 'shifted': - self.databyte = self.databyte >> 1 - cmd = 'ADDRESS WRITE' if self.wr else 'ADDRESS READ' - #bin_class = 1 if self.wr else 0 - - self.ss, self.es = self.ss_byte, self.samplenum - - #self.putp(['BITS', self.bits]) - #self.putp([cmd, self.databyte]) - - #self.putb([bin_class, bytes([self.databyte])]) - - #for bit in self.bits: - # self.put(bit[1], bit[2], self.out_ann, [5, ['%d' % bit[0]]]) - - self.putx([proto[cmd][0], ['%s: %02X' % (proto[cmd][1], self.databyte), - '%s: %02X' % (proto[cmd][2], self.databyte), '%02X' % self.databyte]]) - - cmd = 'WRITE' if self.wr else 'READ' - self.ss, self.es = self.samplenum, self.samplenum + self.bitwidth - w = ['Write', 'Wr', 'W'] if self.wr else ['Read', 'Rd', 'R'] - self.putx([proto[cmd][0], w]) - - # Done with this packet. - self.bitcount = self.databyte = 0 - #self.bits = [] - self.state = 'FIND ACK' + self.bits = [] # Gather 8 bits of data plus the ACK/NACK bit. - def found_data(self, scl, sda): + def handle_address_or_data(self, scl, sda): + self.pdu_bits += 1 + # Address and data are transmitted MSB-first. self.databyte <<= 1 self.databyte |= sda @@ -226,118 +144,111 @@ class Decoder(srd.Decoder): # Remember the start of the first data/address bit. if self.bitcount == 0: self.ss_byte = self.samplenum - if self.bitcount == 1: - self.bitwidth = self.samplenum - self.ss_byte # Store individual bits and their start/end samplenumbers. - # In the list, index 0 represents the MSB (I²C transmits MSB-first). - #self.bits.append([sda, self.samplenum, self.samplenum]) - #if self.bitcount > 0: - # self.bits[self.bitcount-1][2] = self.samplenum - #if self.bitcount == 7: - # self.bits[7][2] += self.bitwidth + # In the list, index 0 represents the LSB (I²C transmits MSB-first). + self.bits.insert(0, [sda, self.samplenum, self.samplenum]) + if self.bitcount > 0: + self.bits[1][2] = self.samplenum + if self.bitcount == 7: + self.bitwidth = self.bits[1][2] - self.bits[2][2] + self.bits[0][2] += self.bitwidth # Return if we haven't collected all 8 + 1 bits, yet. if self.bitcount < 7: self.bitcount += 1 return - cmd = 'DATA WRITE' if self.wr else 'DATA READ' - #bin_class = 3 if self.wr else 2 + d = self.databyte + if self.state == 'FIND ADDRESS': + # The READ/WRITE bit is only in address bytes, not data bytes. + self.wr = 0 if (self.databyte & 1) else 1 + if self.options['address_format'] == 'shifted': + d = d >> 1 + + bin_class = -1 + if self.state == 'FIND ADDRESS' and self.wr == 1: + cmd = 'ADDRESS WRITE' + bin_class = 1 + elif self.state == 'FIND ADDRESS' and self.wr == 0: + cmd = 'ADDRESS READ' + bin_class = 0 + elif self.state == 'FIND DATA' and self.wr == 1: + cmd = 'DATA WRITE' + bin_class = 3 + elif self.state == 'FIND DATA' and self.wr == 0: + cmd = 'DATA READ' + bin_class = 2 self.ss, self.es = self.ss_byte, self.samplenum + self.bitwidth - #self.putp(['BITS', self.bits]) - #self.putp([cmd, self.databyte]) + if cmd.startswith('ADDRESS'): + self.ss, self.es = self.samplenum, self.samplenum + self.bitwidth + w = ['Write', 'Wr', 'W'] if self.wr else ['Read', 'Rd', 'R'] + self.putx([proto[cmd][0], w]) + self.ss, self.es = self.ss_byte, self.samplenum - #self.putb([bin_class, bytes([self.databyte])]) - - #for bit in self.bits: - # self.put(bit[1], bit[2], self.out_ann, [5, ['%d' % bit[0]]]) - - self.putx([proto[cmd][0], ['%s: %02X' % (proto[cmd][1], self.databyte), - '%s: %02X' % (proto[cmd][2], self.databyte), '%02X' % self.databyte]]) + self.putx([proto[cmd][0], ['%s: %02X' % (proto[cmd][1], d), + '%s: %02X' % (proto[cmd][2], d), '%02X' % d]]) # Done with this packet. self.bitcount = self.databyte = 0 - #self.bits = [] + self.bits = [] self.state = 'FIND ACK' def get_ack(self, scl, sda): self.ss, self.es = self.samplenum, self.samplenum + self.bitwidth cmd = 'NACK' if (sda == 1) else 'ACK' - #self.putp([cmd, None]) self.putx([proto[cmd][0], proto[cmd][1:]]) # There could be multiple data bytes in a row, so either find # another data byte or a STOP condition next. self.state = 'FIND DATA' - def found_stop(self, scl, sda): - # Meta bitrate - #elapsed = 1 / float(self.samplerate) * (self.samplenum - self.pdu_start + 1) - #bitrate = int(1 / elapsed * self.pdu_bits) - #self.put(self.ss_byte, self.samplenum, self.out_bitrate, bitrate) - + def handle_stop(self): cmd = 'STOP' self.ss, self.es = self.samplenum, self.samplenum - #self.putp([cmd, None]) self.putx([proto[cmd][0], proto[cmd][1:]]) self.state = 'FIND START' self.is_repeat_start = 0 self.wr = -1 - #self.bits = [] - - def decode(self, ss, es, logic): - for (self.samplenum, pins) in logic: - - (scl, sda) = pins - #self.pdu_bits += 1 - logic.logic_mask = 0b11 - logic.cur_pos = self.samplenum - logic.edge_index = -1 + self.bits = [] + def decode(self): + while True: # State machine. if self.state == 'FIND START': - # START condition (S): SDA = falling, SCL = high - if (self.oldsda == 1 and sda == 0) and scl == 1: - self.found_start(scl, sda) - logic.exp_logic = 0b01 - logic.logic_mask = 0b01 - logic.edge_index = 0 - scl = 0 - else: - logic.exp_logic = 0b01 - logic.logic_mask = 0b11 - logic.edge_index = 1 - sda = 1 + # Wait for a START condition (S): SCL = high, SDA = falling. + self.wait({0: 'h', 1: 'f'}) + self.handle_start() elif self.state == 'FIND ADDRESS': - # Data sampling of receiver: SCL = rising - if self.oldscl == 0 and scl == 1: - self.found_address(scl, sda) - # START condition (S): SDA = falling, SCL = high - elif (self.oldsda == 1 and sda == 0) and scl == 1: - self.found_start(scl, sda) - # STOP condition (P): SDA = rising, SCL = high - elif (self.oldsda == 0 and sda == 1) and scl == 1: - self.found_stop(scl, sda) - elif self.state == 'FIND DATA': - # Data sampling of receiver: SCL = rising - if self.oldscl == 0 and scl == 1: - self.found_data(scl, sda) - # START condition (S): SDA = falling, SCL = high - elif (self.oldsda == 1 and sda == 0) and scl == 1: - self.found_start(scl, sda) - # STOP condition (P): SDA = rising, SCL = high - elif (self.oldsda == 0 and sda == 1) and scl == 1: - self.found_stop(scl, sda) - elif self.state == 'FIND ACK': - # Data sampling of receiver: SCL = rising - if self.oldscl == 0 and scl == 1: - self.get_ack(scl, sda) - logic.exp_logic = 0b01 - logic.logic_mask = 0b01 - logic.edge_index = 0 - scl = 0 + # Wait for any of the following conditions (or combinations): + # a) Data sampling of receiver: SCL = rising, and/or + # b) START condition (S): SCL = high, SDA = falling, and/or + # c) STOP condition (P): SCL = high, SDA = rising + (scl, sda) = self.wait([{0: 'r'}, {0: 'h', 1: 'f'}, {0: 'h', 1: 'r'}]) - # Save current SDA/SCL values for the next round. - self.oldscl, self.oldsda = scl, sda + # Check which of the condition(s) matched and handle them. + if (self.matched & (0b1 << 0)): + self.handle_address_or_data(scl, sda) + elif (self.matched & (0b1 << 1)): + self.handle_start() + elif (self.matched & (0b1 << 2)): + self.handle_stop() + elif self.state == 'FIND DATA': + # Wait for any of the following conditions (or combinations): + # a) Data sampling of receiver: SCL = rising, and/or + # b) START condition (S): SCL = high, SDA = falling, and/or + # c) STOP condition (P): SCL = high, SDA = rising + (scl, sda) = self.wait([{0: 'r'}, {0: 'h', 1: 'f'}, {0: 'h', 1: 'r'}]) + + # Check which of the condition(s) matched and handle them. + if (self.matched & (0b1 << 0)): + self.handle_address_or_data(scl, sda) + elif (self.matched & (0b1 << 1)): + self.handle_start() + elif (self.matched & (0b1 << 2)): + self.handle_stop() + elif self.state == 'FIND ACK': + # Wait for a data/ack bit: SCL = rising. + (scl, sda) = self.wait({0: 'r'}) + self.get_ack(scl, sda) diff --git a/libsigrokdecode4DSL/decoders/0-spi/__init__.py b/libsigrokdecode4DSL/decoders/0-spi/__init__.py index f76bb064..dc5cbc05 100755 --- a/libsigrokdecode4DSL/decoders/0-spi/__init__.py +++ b/libsigrokdecode4DSL/decoders/0-spi/__init__.py @@ -14,14 +14,14 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' The SPI (Serial Peripheral Interface) protocol decoder supports synchronous SPI(-like) protocols with a clock line, a MISO and MOSI line for data transfer in two directions, and an optional CS# pin. + Either MISO or MOSI (but not both) can be optional. If CS# is supplied, data is only decoded when CS# is asserted (clock diff --git a/libsigrokdecode4DSL/decoders/0-spi/pd.py b/libsigrokdecode4DSL/decoders/0-spi/pd.py index 90898f78..0116755d 100755 --- a/libsigrokdecode4DSL/decoders/0-spi/pd.py +++ b/libsigrokdecode4DSL/decoders/0-spi/pd.py @@ -3,7 +3,7 @@ ## ## Copyright (C) 2011 Gareth McMullin ## Copyright (C) 2012-2014 Uwe Hermann -## Copyright (C) 2016 DreamSourceLab +## Copyright (C) 2019 DreamSourceLab ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -16,8 +16,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -72,14 +71,11 @@ spi_mode = { (1, 1): 3, # Mode 3 } -class SamplerateError(Exception): - pass - class ChannelError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = '0:spi' name = '0:SPI' longname = 'Serial Peripheral Interface' @@ -87,20 +83,21 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['logic'] outputs = ['spi'] + tags = ['Embedded/industrial'] channels = ( - {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'}, + {'id': 'clk', 'type': 0, 'name': 'CLK', 'desc': 'Clock'}, ) optional_channels = ( - {'id': 'miso', 'name': 'MISO', 'desc': 'Master in, slave out'}, - {'id': 'mosi', 'name': 'MOSI', 'desc': 'Master out, slave in'}, - {'id': 'cs', 'name': 'CS#', 'desc': 'Chip-select'}, + {'id': 'miso', 'type': 107, 'name': 'MISO', 'desc': 'Master in, slave out'}, + {'id': 'mosi', 'type': 109, 'name': 'MOSI', 'desc': 'Master out, slave in'}, + {'id': 'cs', 'type': -1, 'name': 'CS#', 'desc': 'Chip-select'}, ) options = ( {'id': 'cs_polarity', 'desc': 'CS# polarity', 'default': 'active-low', 'values': ('active-low', 'active-high')}, - {'id': 'cpol', 'desc': 'Clock polarity', 'default': 0, + {'id': 'cpol', 'desc': 'Clock polarity (CPOL)', 'default': 0, 'values': (0, 1)}, - {'id': 'cpha', 'desc': 'Clock phase', 'default': 0, + {'id': 'cpha', 'desc': 'Clock phase (CPHA)', 'default': 0, 'values': (0, 1)}, {'id': 'bitorder', 'desc': 'Bit order', 'default': 'msb-first', 'values': ('msb-first', 'lsb-first')}, @@ -109,185 +106,169 @@ class Decoder(srd.Decoder): annotations = ( ('106', 'miso-data', 'MISO data'), ('108', 'mosi-data', 'MOSI data'), - ('107', 'miso-bits', 'MISO bits'), - ('109', 'mosi-bits', 'MOSI bits'), - ('1000', 'warnings', 'Human-readable warnings'), ) annotation_rows = ( ('miso-data', 'MISO data', (0,)), - #('miso-bits', 'MISO bits', (2,)), ('mosi-data', 'MOSI data', (1,)), - #('mosi-bits', 'MOSI bits', (3,)), - #('other', 'Other', (4,)), - ) - binary = ( - ('miso', 'MISO'), - ('mosi', 'MOSI'), ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None - self.oldclk = -1 self.bitcount = 0 self.misodata = self.mosidata = 0 self.misobits = [] self.mosibits = [] - self.misobytes = [] - self.mosibytes = [] self.ss_block = -1 self.samplenum = -1 self.ss_transfer = -1 self.cs_was_deasserted = False - self.oldcs = None - self.oldpins = None self.have_cs = self.have_miso = self.have_mosi = None - self.no_cs_notification = False - self.mode = None - self.active_low = None - self.pin_checked = False - self.ws = None - self.bitwidth = 0 - - def metadata(self, key, value): - if key == srd.SRD_CONF_SAMPLERATE: - self.samplerate = value def start(self): - self.out_python = self.register(srd.OUTPUT_PYTHON) self.out_ann = self.register(srd.OUTPUT_ANN) - self.out_binary = self.register(srd.OUTPUT_BINARY) - self.out_bitrate = self.register(srd.OUTPUT_META, - meta=(int, 'Bitrate', 'Bitrate during transfers')) - if not self.samplerate: - raise SamplerateError('Cannot decode without samplerate.') - #Sample data on rising/falling clock edge (depends on mode). - self.mode = spi_mode[self.options['cpol'], self.options['cpha']] - self.active_low = (self.options['cs_polarity'] == 'active-low') - self.ws = self.options['wordsize'] + self.bw = (self.options['wordsize'] + 7) // 8 + + def metadata(self, key, value): + if key == srd.SRD_CONF_SAMPLERATE: + self.samplerate = value def putw(self, data): self.put(self.ss_block, self.samplenum, self.out_ann, data) def putdata(self): # Pass MISO and MOSI bits and then data to the next PD up the stack. + so = self.misodata if self.have_miso else None + si = self.mosidata if self.have_mosi else None + if self.have_miso: - ss, es = self.misobits[0][1], self.misobits[self.ws-1][2] - self.put(ss, es, self.out_python, ['BITS', self.mosibits, self.misobits]) - self.misobytes.append(Data(ss=ss, es=es, val=self.misodata)) - for bit in self.misobits: - self.put(bit[1], bit[2], self.out_ann, [2, ['%d' % bit[0]]]) - self.put(ss, es, self.out_ann, [0, ['%02X' % self.misodata]]) - # self.put(ss, es, self.out_binary, [0, bytes([self.misodata])]) + ss, es = self.misobits[-1][1], self.misobits[0][2] + if self.have_mosi: + ss, es = self.mosibits[-1][1], self.mosibits[0][2] + + # Dataword annotations. + if self.have_miso: + self.put(ss, es, self.out_ann, [0, ['%02X' % self.misodata]]) if self.have_mosi: - ss, es = self.mosibits[0][1], self.mosibits[self.ws-1][2] - self.put(ss, es, self.out_python, ['DATA', self.mosidata, self.misodata]) - self.mosibytes.append(Data(ss=ss, es=es, val=self.mosidata)) - for bit in self.mosibits: - self.put(bit[1], bit[2], self.out_ann, [3, ['%d' % bit[0]]]) self.put(ss, es, self.out_ann, [1, ['%02X' % self.mosidata]]) - # self.put(ss, es, self.out_binary, [1, bytes([si])]) def reset_decoder_state(self): self.misodata = 0 if self.have_miso else None self.mosidata = 0 if self.have_mosi else None - #self.misobits = [] if self.have_miso else None - #self.mosibits = [] if self.have_mosi else None + self.misobits = [] if self.have_miso else None + self.mosibits = [] if self.have_mosi else None self.bitcount = 0 + def cs_asserted(self, cs): + active_low = (self.options['cs_polarity'] == 'active-low') + return (cs == 0) if active_low else (cs == 1) + def handle_bit(self, miso, mosi, clk, cs): # If this is the first bit of a dataword, save its sample number. if self.bitcount == 0: self.ss_block = self.samplenum - # self.cs_was_deasserted = (cs == self.deasserted_cs) - if self.bitcount == 1: - self.bitwidth = self.samplenum - self.ss_block + self.cs_was_deasserted = \ + not self.cs_asserted(cs) if self.have_cs else False + + ws = self.options['wordsize'] + bo = self.options['bitorder'] - shift_cnt = (self.ws - 1 - self.bitcount) if (self.options['bitorder'] == 'msb-first') else self.bitcount # Receive MISO bit into our shift register. if self.have_miso: - self.misodata |= miso << shift_cnt - #self.misobits.append([miso, self.samplenum, es]) - #if self.bitcount > 0: - # self.misobits[self.bitcount-1][2] = self.samplenum + if bo == 'msb-first': + self.misodata |= miso << (ws - 1 - self.bitcount) + else: + self.misodata |= miso << self.bitcount + # Receive MOSI bit into our shift register. if self.have_mosi: - self.mosidata |= mosi << shift_cnt - #self.mosibits.append([mosi, self.samplenum, es]) - #if self.bitcount > 0: - # self.mosibits[self.bitcount-1][2] = self.samplenum + if bo == 'msb-first': + self.mosidata |= mosi << (ws - 1 - self.bitcount) + else: + self.mosidata |= mosi << self.bitcount + + # Guesstimate the endsample for this bit (can be overridden below). + es = self.samplenum + if self.bitcount > 0: + if self.have_miso: + es += self.samplenum - self.misobits[0][1] + elif self.have_mosi: + es += self.samplenum - self.mosibits[0][1] + + if self.have_miso: + self.misobits.insert(0, [miso, self.samplenum, es]) + if self.have_mosi: + self.mosibits.insert(0, [mosi, self.samplenum, es]) + + if self.bitcount > 0 and self.have_miso: + self.misobits[1][2] = self.samplenum + if self.bitcount > 0 and self.have_mosi: + self.mosibits[1][2] = self.samplenum self.bitcount += 1 + # Continue to receive if not enough bits were received, yet. - if self.bitcount != self.ws: + if self.bitcount != ws: return - es = self.samplenum + self.bitwidth - if self.have_miso: - self.put(self.ss_block, es, self.out_ann, [0, ['%02X' % self.misodata]]) - if self.have_mosi: - self.put(self.ss_block, es, self.out_ann, [1, ['%02X' % self.mosidata]]) - - # Meta bitrate. - #elapsed = 1 / float(self.samplerate) - #elapsed *= (self.samplenum - self.ss_block + 1) - #bitrate = int(1 / elapsed * self.options['wordsize']) - #self.put(self.ss_block, self.samplenum, self.out_bitrate, bitrate) - - #if self.have_cs and self.cs_was_deasserted: - # self.putw([4, ['CS# was deasserted during this data word!']]) + self.putdata() self.reset_decoder_state() - def decode(self, ss, es, logic): - # Either MISO or MOSI can be omitted (but not both). CS# is optional. - for (self.samplenum, pins) in logic: - (clk, miso, mosi, cs) = pins - if not self.pin_checked: - self.have_miso = (miso in (0, 1)) - self.have_mosi = (mosi in (0, 1)) - self.have_cs = (cs in (0, 1)) - # Either MISO or MOSI (but not both) can be omitted. - if not (self.have_miso or self.have_mosi): - raise ChannelError('Either MISO or MOSI (or both) pins required.') - if (self.mode == 0 or self.mode == 3): - self.exp_oldclk = 0 - self.exp_clk = 1 - else: - self.exp_oldclk = 1 - self.exp_clk = 0 - self.logic_mask = 0b1001 if self.have_cs else 0b0001 - self.exp_logic = 0b0000 if self.active_low else 0b1000 - self.asserted_oldcs = 1 if self.active_low else 0 - self.asserted_cs = 0 if self.active_low else 1 - self.deasserted_oldcs = 0 if self.active_low else 1 - self.deasserted_cs = 1 if self.active_low else 0 - self.pin_checked = True + def find_clk_edge(self, miso, mosi, clk, cs, first): + if self.have_cs and (first or (self.matched & (0b1 << self.have_cs))): + # Send all CS# pin value changes. + oldcs = None if first else 1 - cs - logic.logic_mask = self.logic_mask - logic.cur_pos = self.samplenum - logic.edge_index = -1 - #logic.itercnt += 1 + # Reset decoder state when CS# changes (and the CS# pin is used). + self.reset_decoder_state() - # Tell stacked decoders that we don't have a CS# signal. - #if not self.no_cs_notification and not self.have_cs: - # self.put(0, 0, self.out_python, ['CS-CHANGE', None, None]) - # self.no_cs_notification = True - - if (self.oldcs, cs) == (self.asserted_oldcs, self.asserted_cs): - #self.ss_transfer = self.samplenum - #self.misobytes = [] - #self.mosibytes = [] - self.reset_decoder_state() - elif (self.oldcs, cs) == (self.deasserted_oldcs, self.deasserted_cs): - #self.put(self.ss_transfer, self.samplenum, self.out_python, - # ['TRANSFER', self.mosibytes, self.misobytes]) - logic.exp_logic = self.exp_logic - cs = self.asserted_oldcs - logic.logic_mask = 0b1000 - logic.edge_index = 3 - elif not self.have_cs or cs == self.asserted_cs: - if (self.oldclk, clk) == (self.exp_oldclk, self.exp_clk): - #Sample on rising/falling clock edge - self.handle_bit(miso, mosi, clk, cs) + # We only care about samples if CS# is asserted. + if self.have_cs and not self.cs_asserted(cs): + return - self.oldclk, self.oldcs = clk, cs + # Ignore sample if the clock pin hasn't changed. + if first or not (self.matched & (0b1 << 0)): + return + + # Found the correct clock edge, now get the SPI bit(s). + self.handle_bit(miso, mosi, clk, cs) + + def decode(self): + # The CLK input is mandatory. Other signals are (individually) + # optional. Yet either MISO or MOSI (or both) must be provided. + # Tell stacked decoders when we don't have a CS# signal. + if not self.has_channel(0): + raise ChannelError('CLK pin required.') + self.have_miso = self.has_channel(1) + self.have_mosi = self.has_channel(2) + if not self.have_miso and not self.have_mosi: + raise ChannelError('Either MISO or MOSI (or both) pins required.') + self.have_cs = self.has_channel(3) + + # We want all CLK changes. We want all CS changes if CS is used. + # Map 'have_cs' from boolean to an integer index. This simplifies + # evaluation in other locations. + # Sample data on rising/falling clock edge (depends on mode). + mode = spi_mode[self.options['cpol'], self.options['cpha']] + if mode == 0 or mode == 3: # Sample on rising clock edge + wait_cond = [{0: 'r'}] + else: # Sample on falling clock edge + wait_cond = [{0: 'f'}] + + if self.have_cs: + self.have_cs = len(wait_cond) + wait_cond.append({3: 'e'}) + + # "Pixel compatibility" with the v2 implementation. Grab and + # process the very first sample before checking for edges. The + # previous implementation did this by seeding old values with + # None, which led to an immediate "change" in comparison. + (clk, miso, mosi, cs) = self.wait({}) + self.find_clk_edge(miso, mosi, clk, cs, True) + + while True: + (clk, miso, mosi, cs) = self.wait(wait_cond) + self.find_clk_edge(miso, mosi, clk, cs, False) diff --git a/libsigrokdecode4DSL/decoders/0-uart/__init__.py b/libsigrokdecode4DSL/decoders/0-uart/__init__.py index efe0e523..ce6136f1 100755 --- a/libsigrokdecode4DSL/decoders/0-uart/__init__.py +++ b/libsigrokdecode4DSL/decoders/0-uart/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/0-uart/pd.py b/libsigrokdecode4DSL/decoders/0-uart/pd.py index 1d89d1dd..ca4639f9 100755 --- a/libsigrokdecode4DSL/decoders/0-uart/pd.py +++ b/libsigrokdecode4DSL/decoders/0-uart/pd.py @@ -2,7 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2011-2014 Uwe Hermann -## Copyright (C) 2016 DreamSourceLab +## Copyright (C) 2019 DreamSourceLab ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -15,24 +15,24 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd +from common.srdhelper import bitpack from math import floor, ceil ''' OUTPUT_PYTHON format: Packet: -[, ] +[, , ] This is the list of s and their respective values: - 'STARTBIT': The data is the (integer) value of the start bit (0/1). - 'DATA': This is always a tuple containing two items: - 1st item: the (integer) value of the UART data. Valid values - range from 0 to 512 (as the data can be up to 9 bits in size). + range from 0 to 511 (as the data can be up to 9 bits in size). - 2nd item: the list of individual data bits and their ss/es numbers. - 'PARITYBIT': The data is the (integer) value of the parity bit (0/1). - 'STOPBIT': The data is the (integer) value of the stop bit (0 or 1). @@ -40,7 +40,9 @@ This is the list of s and their respective values: - 'INVALID STOPBIT': The data is the (integer) value of the stop bit (0/1). - 'PARITY ERROR': The data is a tuple with two entries. The first one is the expected parity value, the second is the actual parity value. - - TODO: Frame error? + - 'FRAME': The data is always a tuple containing two items: The (integer) + value of the UART data, and a boolean which reflects the validity of the + UART frame. ''' @@ -68,8 +70,11 @@ def parity_ok(parity_type, parity_bit, data, num_data_bits): class SamplerateError(Exception): pass +class ChannelError(Exception): + pass + class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = '0:uart' name = '0:UART' longname = 'Universal Asynchronous Receiver/Transmitter' @@ -77,11 +82,12 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['logic'] outputs = ['uart'] + tags = ['Embedded/industrial'] channels = ( - {'id': 'rxtx', 'name': 'RX/TX', 'desc': 'UART transceive line'}, + {'id': 'rxtx', 'type': 209, 'name': 'RX/TX', 'desc': 'UART transceive line'}, ) options = ( - {'id': 'baudrate', 'desc': 'Baud rate', 'default': 9600}, + {'id': 'baudrate', 'desc': 'Baud rate', 'default': 115200}, {'id': 'num_data_bits', 'desc': 'Data bits', 'default': 8, 'values': (5, 6, 7, 8, 9)}, {'id': 'parity_type', 'desc': 'Parity type', 'default': 'none', @@ -92,7 +98,7 @@ class Decoder(srd.Decoder): 'values': (0.0, 0.5, 1.0, 1.5)}, {'id': 'bit_order', 'desc': 'Bit order', 'default': 'lsb-first', 'values': ('lsb-first', 'msb-first')}, - {'id': 'format', 'desc': 'Data format', 'default': 'ascii', + {'id': 'format', 'desc': 'Data format', 'default': 'hex', 'values': ('ascii', 'dec', 'hex', 'oct', 'bin')}, {'id': 'invert', 'desc': 'Invert Signal?', 'default': 'no', 'values': ('yes', 'no')}, @@ -104,63 +110,44 @@ class Decoder(srd.Decoder): ('0', 'parity-err', 'parity error bits'), ('1', 'stop', 'stop bits'), ('1000', 'warnings', 'warnings'), - ('109', 'data-bits', 'data bits'), ) annotation_rows = ( ('data', 'RX/TX', (0, 1, 2, 3, 4)), - #('data-bits', 'Bits', (6,)), - #('warnings', 'Warnings', (5,)), - ) - binary = ( - ('rxtx', 'RX/TX dump'), + ('warnings', 'Warnings', (5,)), ) + idle_state = 'WAIT FOR START BIT' - def put_ann_bit(self, width, data): - s = self.bitstart - self.put(floor(s), floor(s + width), self.out_ann, data) + def putx(self, data): + s, halfbit = self.startsample, self.bit_width / 2.0 + self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_ann, data) - def put_python_bit(self, width, data): - s = self.bitstart - self.put(floor(s), floor(s + width), self.out_python, data) + def putg(self, data): + s, halfbit = self.samplenum, self.bit_width / 2.0 + self.put(s - floor(halfbit), s + ceil(halfbit), self.out_ann, data) - def put_ann_byte(self, data): - ss, s = self.bytestart, self.bitstart - self.put(floor(ss), floor(s + self.bit_width), self.out_ann, data) - - def put_python_byte(self, data): - ss, s = self.bytestart, self.bitstart - self.put(floor(ss), floor(s + self.bit_width), self.out_python, data) - - def put_binary_byte(self, data): - ss, s = self.bytestart, self.bitstart - self.put(floor(ss), floor(s + self.bit_width), self.out_binary, data) + def putgse(self, ss, es, data): + self.put(ss, es, self.out_ann, data) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.samplenum = 0 + self.frame_start = -1 + self.frame_valid = None self.startbit = -1 - self.bitcount = 0 - self.databyte = 0 + self.cur_data_bit = 0 + self.datavalue = 0 self.paritybit = -1 self.stopbit1 = -1 - self.bitstart = -1 - self.bytestart = -1 - self.state = 'FIND START' - self.oldbit = -1 + self.startsample = -1 + self.state = 'WAIT FOR START BIT' self.databits = [] def start(self): - self.out_python = self.register(srd.OUTPUT_PYTHON) - self.out_binary = self.register(srd.OUTPUT_BINARY) self.out_ann = self.register(srd.OUTPUT_ANN) - if not self.samplerate: - raise SamplerateError('Cannot decode without samplerate.') - if self.samplerate < self.options['baudrate']*4: - raise SamplerateError('Samplerate is too low for current baudrate setting, 4x at least!') - if self.options['invert'] == 'yes': - self.exp_logic = 1 - else: - self.exp_logic = 0 + self.bw = (self.options['num_data_bits'] + 7) // 8 def metadata(self, key, value): if key == srd.SRD_CONF_SAMPLERATE: @@ -168,144 +155,192 @@ class Decoder(srd.Decoder): # The width of one UART bit in number of samples. self.bit_width = float(self.samplerate) / float(self.options['baudrate']) - def frame_start(self, signal): + def get_sample_point(self, bitnum): + # Determine absolute sample number of a bit slot's sample point. + # bitpos is the samplenumber which is in the middle of the + # specified UART bit (0 = start bit, 1..x = data, x+1 = parity bit + # (if used) or the first stop bit, and so on). + # The samples within bit are 0, 1, ..., (bit_width - 1), therefore + # index of the middle sample within bit window is (bit_width - 1) / 2. + bitpos = self.frame_start + (self.bit_width - 1) / 2.0 + bitpos += bitnum * self.bit_width + return bitpos + + def wait_for_start_bit(self, signal): # Save the sample number where the start bit begins. - self.bitstart = self.samplenum + self.frame_start = self.samplenum + self.frame_valid = True + self.state = 'GET START BIT' def get_start_bit(self, signal): self.startbit = signal - # The startbit must be 0. If not, we report an error. - #if self.startbit != 0: - #self.put_python_bit(self.bit_width, ['INVALID STARTBIT', 0, self.startbit]) - #self.put_ann_bit(self.bit_width, [5, ['Frame error', 'Frame err', 'FE']]) - # TODO: Abort? Ignore rest of the frame? - - self.bitcount = 0 - self.databyte = 0 - self.state = 'GET DATA BITS' - - #self.put_python_bit(self.bit_width, ['STARTBIT', 0, self.startbit]) - self.put_ann_bit(self.bit_width, [1, ['Start bit', 'Start', 'S']]) - - def get_data_bits(self, signal): - # Save the sample number of where the bit begins. - self.bitstart += self.bit_width - if self.bitcount == 0 : - self.bytestart = self.bitstart - - # Get the next data bit in LSB-first or MSB-first fashion. - if self.options['bit_order'] == 'lsb-first': - self.databyte >>= 1 - self.databyte |= \ - (signal << (self.options['num_data_bits'] - 1)) - else: - self.databyte <<= 1 - self.databyte |= (signal << 0) - - #self.put_ann_bit(self.bit_width, [6, ['%d' % signal]]) - - # Store individual data bits and their start/end samplenumbers. - #s, halfbit = self.samplenum, int(self.bit_width / 2) - #self.databits.append([signal, s - halfbit, s + halfbit]) - - # Return here, unless we already received all data bits. - if self.bitcount < self.options['num_data_bits'] - 1: - self.bitcount += 1 + # The startbit must be 0. If not, we report an error and wait + # for the next start bit (assuming this one was spurious). + if self.startbit != 0: + self.putg([5, ['Frame error', 'Frame err', 'FE']]) + self.frame_valid = False + es = self.samplenum + ceil(self.bit_width / 2.0) + self.state = 'WAIT FOR START BIT' return + self.cur_data_bit = 0 + self.datavalue = 0 + self.startsample = -1 + + self.putg([1, ['Start bit', 'Start', 'S']]) + + self.state = 'GET DATA BITS' + + def get_data_bits(self, signal): + # Save the sample number of the middle of the first data bit. + if self.startsample == -1: + self.startsample = self.samplenum + + # Store individual data bits and their start/end samplenumbers. + s, halfbit = self.samplenum, int(self.bit_width / 2) + self.databits.append([signal, s - halfbit, s + halfbit]) + + # Return here, unless we already received all data bits. + self.cur_data_bit += 1 + if self.cur_data_bit < self.options['num_data_bits']: + return + + # Convert accumulated data bits to a data value. + bits = [b[0] for b in self.databits] + if self.options['bit_order'] == 'msb-first': + bits.reverse() + self.datavalue = bitpack(bits) + + b = self.datavalue + formatted = self.format_value(b) + if formatted is not None: + self.putx([0, [formatted]]) + + self.databits = [] + + # Advance to either reception of the parity bit, or reception of + # the STOP bits if parity is not applicable. + self.state = 'GET PARITY BIT' if self.options['parity_type'] == 'none': self.state = 'GET STOP BITS' + + def format_value(self, v): + # Format value 'v' according to configured options. + # Reflects the user selected kind of representation, as well as + # the number of data bits in the UART frames. + + fmt, bits = self.options['format'], self.options['num_data_bits'] + + # Assume "is printable" for values from 32 to including 126, + # below 32 is "control" and thus not printable, above 127 is + # "not ASCII" in its strict sense, 127 (DEL) is not printable, + # fall back to hex representation for non-printables. + if fmt == 'ascii': + if v in range(32, 126 + 1): + return chr(v) + hexfmt = "[{:02X}]" if bits <= 8 else "[{:03X}]" + return hexfmt.format(v) + + # Mere number to text conversion without prefix and padding + # for the "decimal" output format. + if fmt == 'dec': + return "{:d}".format(v) + + # Padding with leading zeroes for hex/oct/bin formats, but + # without a prefix for density -- since the format is user + # specified, there is no ambiguity. + if fmt == 'hex': + digits = (bits + 4 - 1) // 4 + fmtchar = "X" + elif fmt == 'oct': + digits = (bits + 3 - 1) // 3 + fmtchar = "o" + elif fmt == 'bin': + digits = bits + fmtchar = "b" else: - self.state = 'GET PARITY BIT' + fmtchar = None + if fmtchar is not None: + fmt = "{{:0{:d}{:s}}}".format(digits, fmtchar) + return fmt.format(v) - #self.put_python_byte(['DATA', 0, (self.databyte, self.databits)]) - - b, f = self.databyte, self.options['format'] - if f == 'ascii': - c = chr(b) if b in range(30, 126 + 1) else '[%02X]' % b - self.put_ann_byte([0, [c]]) - elif f == 'dec': - self.put_ann_byte([0, [str(b)]]) - elif f == 'hex': - self.put_ann_byte([0, [hex(b)[2:].zfill(2).upper()]]) - elif f == 'oct': - self.put_ann_byte([0, [oct(b)[2:].zfill(3)]]) - elif f == 'bin': - self.put_ann_byte([0, [bin(b)[2:].zfill(8)]]) - - #self.put_binary_byte([0, bytes([b])]) - #self.put_binary_byte([2, bytes([b])]) - - #self.databits = [] + return None def get_parity_bit(self, signal): - # Save the sample number of where the bit begins. - self.bitstart += self.bit_width self.paritybit = signal - self.state = 'GET STOP BITS' if parity_ok(self.options['parity_type'], self.paritybit, - self.databyte, self.options['num_data_bits']): - # self.put_python_bit(self.bit_width, ['PARITYBIT', 0, self.paritybit]) - self.put_ann_bit(self.bit_width, [2, ['Parity bit', 'Parity', 'P']]) + self.datavalue, self.options['num_data_bits']): + self.putg([2, ['Parity bit', 'Parity', 'P']]) else: - # # TODO: Return expected/actual parity values. - # self.put_python_bit(self.bit_width, ['PARITY ERROR', 0, (0, 1)]) # FIXME: Dummy tuple... - self.put_ann_bit(self.bit_width, [3, ['Parity error', 'Parity err', 'PE']]) + # TODO: Return expected/actual parity values. + self.putg([3, ['Parity error', 'Parity err', 'PE']]) + self.frame_valid = False + + self.state = 'GET STOP BITS' # TODO: Currently only supports 1 stop bit. def get_stop_bits(self, signal): - # Save the sample number of where the bit begins. - self.bitstart += self.bit_width + self.stopbit1 = signal - #self.stopbit1 = signal # Stop bits must be 1. If not, we report an error. - #if self.stopbit1 != 1: - # self.put_python_bit(self.bit_width, ['INVALID STOPBIT', 0, self.stopbit1]) - # self.put_ann_bit(self.bit_width, [5, ['Frame error', 'Frame err', 'FE']]) - # TODO: Abort? Ignore the frame? Other? + if self.stopbit1 != 1: + self.putg([5, ['Frame error', 'Frame err', 'FE']]) + self.frame_valid = False - self.state = 'FIND START' - #self.put_python_bit(int(self.bit_width * self.options['num_stop_bits']), ['STOPBIT', 0, self.stopbit1]) - self.put_ann_bit(int(self.bit_width * self.options['num_stop_bits']), [4, ['Stop bit', 'Stop', 'T']]) + self.putg([2, ['Stop bit', 'Stop', 'T']]) - def decode(self, ss, es, logic): - for (self.samplenum, pins) in logic: + # Pass the complete UART frame to upper layers. + es = self.samplenum + ceil(self.bit_width / 2.0) - # In default case, the iteration gap is 1 - logic.logic_mask = 0 - logic.edge_index = 0 + self.state = 'WAIT FOR START BIT' - (signal,) = pins + def get_wait_cond(self, inv): + # Return condititions that are suitable for Decoder.wait(). Those + # conditions either match the falling edge of the START bit, or + # the sample point of the next bit time. + state = self.state + if state == 'WAIT FOR START BIT': + return {0: 'r' if inv else 'f'} + if state == 'GET START BIT': + bitnum = 0 + elif state == 'GET DATA BITS': + bitnum = 1 + self.cur_data_bit + elif state == 'GET PARITY BIT': + bitnum = 1 + self.options['num_data_bits'] + elif state == 'GET STOP BITS': + bitnum = 1 + self.options['num_data_bits'] + bitnum += 0 if self.options['parity_type'] == 'none' else 1 + want_num = ceil(self.get_sample_point(bitnum)) + return {'skip': want_num - self.samplenum} - if self.options['invert'] == 'yes': - signal = not signal + def inspect_sample(self, signal, inv): + # Inspect a sample returned by .wait() for the specified UART line. + if inv: + signal = not signal - # State machine. - if self.state == 'FIND START': - if (self.oldbit == 1 and signal == 0): - self.frame_start(signal) - logic.itercnt += (self.bit_width - 1) / 2.0 - else: - logic.exp_logic = self.exp_logic - logic.logic_mask = 1 - logic.cur_pos = self.samplenum - signal = 1 - elif self.state == 'GET START BIT': - self.get_start_bit(signal) - logic.itercnt += self.bit_width - elif self.state == 'GET DATA BITS': - self.get_data_bits(signal) - logic.itercnt += self.bit_width - elif self.state == 'GET PARITY BIT': - self.get_parity_bit(signal) - logic.itercnt += self.bit_width - elif self.state == 'GET STOP BITS': - self.get_stop_bits(signal) - logic.itercnt += (self.options['num_stop_bits'] - 0.75) * self.bit_width - signal = 0 + state = self.state + if state == 'WAIT FOR START BIT': + self.wait_for_start_bit(signal) + elif state == 'GET START BIT': + self.get_start_bit(signal) + elif state == 'GET DATA BITS': + self.get_data_bits(signal) + elif state == 'GET PARITY BIT': + self.get_parity_bit(signal) + elif state == 'GET STOP BITS': + self.get_stop_bits(signal) - # Save current RX/TX values for the next round. - self.oldbit = signal + def decode(self): + if not self.samplerate: + raise SamplerateError('Cannot decode without samplerate.') + + inv = self.options['invert'] == 'yes' + + while True: + conds = self.get_wait_cond(inv) + (rxtx, ) = self.wait(conds) + if (self.matched & (0b1 << 0)): + self.inspect_sample(rxtx, inv) diff --git a/libsigrokdecode4DSL/decoders/1-i2c/__init__.py b/libsigrokdecode4DSL/decoders/1-i2c/__init__.py index c3b0cd50..2a36b060 100755 --- a/libsigrokdecode4DSL/decoders/1-i2c/__init__.py +++ b/libsigrokdecode4DSL/decoders/1-i2c/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/1-i2c/pd.py b/libsigrokdecode4DSL/decoders/1-i2c/pd.py index dc8c77f8..3390c74f 100755 --- a/libsigrokdecode4DSL/decoders/1-i2c/pd.py +++ b/libsigrokdecode4DSL/decoders/1-i2c/pd.py @@ -1,8 +1,8 @@ ## ## This file is part of the libsigrokdecode project. ## -## Copyright (C) 2010-2014 Uwe Hermann -## Copyright (C) 2016 DreamSourceLab +## Copyright (C) 2010-2016 Uwe Hermann +## Copyright (C) 2019 DreamSourceLab ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -15,12 +15,10 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## # TODO: Look into arbitration, collision detection, clock synchronisation, etc. -# TODO: Implement support for 10bit slave addresses. # TODO: Implement support for inverting SDA/SCL levels (0->1 and 1->0). # TODO: Implement support for detecting various bus errors. @@ -57,20 +55,15 @@ proto = { 'STOP': [2, 'Stop', 'P'], 'ACK': [3, 'ACK', 'A'], 'NACK': [4, 'NACK', 'N'], - 'READ': [5, 'Read', 'R'], - 'WRITE': [6, 'Write', 'W'], - 'BIT': [7, 'Bit', 'B'], - 'ADDRESS READ': [8, 'Address read', 'AR'], - 'ADDRESS WRITE': [9, 'Address write', 'AW'], - 'DATA READ': [10, 'Data read', 'DR'], - 'DATA WRITE': [11, 'Data write', 'DW'], + 'BIT': [5, 'Bit', 'B'], + 'ADDRESS READ': [6, 'Address read', 'AR'], + 'ADDRESS WRITE': [7, 'Address write', 'AW'], + 'DATA READ': [8, 'Data read', 'DR'], + 'DATA WRITE': [9, 'Data write', 'DW'], } -class SamplerateError(Exception): - pass - class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = '1:i2c' name = '1:I²C' longname = 'Inter-Integrated Circuit' @@ -78,6 +71,7 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['logic'] outputs = ['i2c'] + tags = ['Embedded/industrial'] channels = ( {'id': 'scl', 'type': 8, 'name': 'SCL', 'desc': 'Serial clock line'}, {'id': 'sda', 'type': 108, 'name': 'SDA', 'desc': 'Serial data line'}, @@ -92,8 +86,6 @@ class Decoder(srd.Decoder): ('1', 'stop', 'Stop condition'), ('5', 'ack', 'ACK'), ('0', 'nack', 'NACK'), - ('12', 'read', 'Read'), - ('11', 'write', 'Write'), ('208', 'bit', 'Data/address bit'), ('112', 'address-read', 'Address read'), ('111', 'address-write', 'Address write'), @@ -102,9 +94,9 @@ class Decoder(srd.Decoder): ('1000', 'warnings', 'Human-readable warnings'), ) annotation_rows = ( - ('bits', 'Bits', (7,)), - ('addr-data', 'Address/Data', (0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11)), - ('warnings', 'Warnings', (12,)), + ('bits', 'Bits', (5,)), + ('addr-data', 'Address/Data', (0, 1, 2, 3, 4, 6, 7, 8, 9)), + ('warnings', 'Warnings', (10,)), ) binary = ( ('address-read', 'Address read'), @@ -114,17 +106,18 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.ss = self.es = self.ss_byte = -1 - self.samplenum = None self.bitcount = 0 self.databyte = 0 self.wr = -1 self.is_repeat_start = 0 self.state = 'FIND START' - self.oldscl = self.oldsda = -1 self.pdu_start = None - #self.pdu_bits = 0 + self.pdu_bits = 0 self.bits = [] def metadata(self, key, value): @@ -137,8 +130,6 @@ class Decoder(srd.Decoder): self.out_binary = self.register(srd.OUTPUT_BINARY) self.out_bitrate = self.register(srd.OUTPUT_META, meta=(int, 'Bitrate', 'Bitrate from Start bit to Stop bit')) - if not self.samplerate: - raise SamplerateError('Cannot decode without samplerate.') def putx(self, data): self.put(self.ss, self.es, self.out_ann, data) @@ -146,13 +137,13 @@ class Decoder(srd.Decoder): def putp(self, data): self.put(self.ss, self.es, self.out_python, data) - #def putb(self, data): - # self.put(self.ss, self.es, self.out_binary, data) + def putb(self, data): + self.put(self.ss, self.es, self.out_binary, data) - def found_start(self, scl, sda): + def handle_start(self): self.ss, self.es = self.samplenum, self.samplenum self.pdu_start = self.samplenum - #self.pdu_bits = 0 + self.pdu_bits = 0 cmd = 'START REPEAT' if (self.is_repeat_start == 1) else 'START' self.putp([cmd, None]) self.putx([proto[cmd][0], proto[cmd][1:]]) @@ -162,62 +153,10 @@ class Decoder(srd.Decoder): self.wr = -1 self.bits = [] - # Gather 7 bits of address, 1 bit of rd/wr, plus the ACK/NACK bit. - def found_address(self, scl, sda): - # Address and data are transmitted MSB-first. - self.databyte <<= 1 - self.databyte |= sda - - # Remember the start of the first data/address bit. - if self.bitcount == 0: - self.ss_byte = self.samplenum - - # Store individual bits and their start/end samplenumbers. - # In the list, index 0 represents the MSB (I²C transmits MSB-first). - self.bits.insert(0, [sda, self.samplenum, self.samplenum]) - if self.bitcount > 0: - self.bits[1][2] = self.samplenum - if self.bitcount == 7: - self.bitwidth = self.bits[1][2] - self.bits[2][2] - self.bits[0][2] += self.bitwidth - - # Return if we haven't collected all 8 + 1 bits, yet. - if self.bitcount < 7: - self.bitcount += 1 - return - - # The READ/WRITE bit is only in address bytes, not data bytes. - self.wr = 0 if (self.databyte & 1) else 1 - if self.options['address_format'] == 'shifted': - self.databyte = self.databyte >> 1 - cmd = 'ADDRESS WRITE' if self.wr else 'ADDRESS READ' - #bin_class = 1 if self.wr else 0 - - self.ss, self.es = self.ss_byte, self.samplenum - - self.putp(['BITS', self.bits]) - self.putp([cmd, self.databyte]) - - #self.putb([bin_class, bytes([self.databyte])]) - - for bit in reversed(self.bits): - self.put(bit[1], bit[2], self.out_ann, [7, ['%d' % bit[0]]]) - - self.putx([proto[cmd][0], ['%s: %02X' % (proto[cmd][1], self.databyte), - '%s: %02X' % (proto[cmd][2], self.databyte), '%02X' % self.databyte]]) - - cmd = 'WRITE' if self.wr else 'READ' - self.ss, self.es = self.samplenum, self.samplenum + self.bitwidth - w = ['Write', 'Wr', 'W'] if self.wr else ['Read', 'Rd', 'R'] - self.putx([proto[cmd][0], w]) - - # Done with this packet. - self.bitcount = self.databyte = 0 - self.bits = [] - self.state = 'FIND ACK' - # Gather 8 bits of data plus the ACK/NACK bit. - def found_data(self, scl, sda): + def handle_address_or_data(self, scl, sda): + self.pdu_bits += 1 + # Address and data are transmitted MSB-first. self.databyte <<= 1 self.databyte |= sda @@ -227,34 +166,58 @@ class Decoder(srd.Decoder): self.ss_byte = self.samplenum # Store individual bits and their start/end samplenumbers. - # In the list, index 0 represents the MSB (I²C transmits MSB-first). + # In the list, index 0 represents the LSB (I²C transmits MSB-first). self.bits.insert(0, [sda, self.samplenum, self.samplenum]) if self.bitcount > 0: self.bits[1][2] = self.samplenum if self.bitcount == 7: self.bitwidth = self.bits[1][2] - self.bits[2][2] - self.bits[0][2] += self.bitwidth + self.bits[0][2] += self.bitwidth # Return if we haven't collected all 8 + 1 bits, yet. if self.bitcount < 7: self.bitcount += 1 return - cmd = 'DATA WRITE' if self.wr else 'DATA READ' - #bin_class = 3 if self.wr else 2 + d = self.databyte + if self.state == 'FIND ADDRESS': + # The READ/WRITE bit is only in address bytes, not data bytes. + self.wr = 0 if (self.databyte & 1) else 1 + if self.options['address_format'] == 'shifted': + d = d >> 1 + + bin_class = -1 + if self.state == 'FIND ADDRESS' and self.wr == 1: + cmd = 'ADDRESS WRITE' + bin_class = 1 + elif self.state == 'FIND ADDRESS' and self.wr == 0: + cmd = 'ADDRESS READ' + bin_class = 0 + elif self.state == 'FIND DATA' and self.wr == 1: + cmd = 'DATA WRITE' + bin_class = 3 + elif self.state == 'FIND DATA' and self.wr == 0: + cmd = 'DATA READ' + bin_class = 2 self.ss, self.es = self.ss_byte, self.samplenum + self.bitwidth self.putp(['BITS', self.bits]) - self.putp([cmd, self.databyte]) + self.putp([cmd, d]) - #self.putb([bin_class, bytes([self.databyte])]) + self.putb([bin_class, bytes([d])]) - for bit in reversed(self.bits): - self.put(bit[1], bit[2], self.out_ann, [7, ['%d' % bit[0]]]) + for bit in self.bits: + self.put(bit[1], bit[2], self.out_ann, [5, ['%d' % bit[0]]]) - self.putx([proto[cmd][0], ['%s: %02X' % (proto[cmd][1], self.databyte), - '%s: %02X' % (proto[cmd][2], self.databyte), '%02X' % self.databyte]]) + if cmd.startswith('ADDRESS'): + self.ss, self.es = self.samplenum, self.samplenum + self.bitwidth + w = ['Write', 'Wr', 'W'] if self.wr else ['Read', 'Rd', 'R'] + self.putx([proto[cmd][0], w]) + self.ss, self.es = self.ss_byte, self.samplenum + + self.putx([proto[cmd][0], ['%s: %02X' % (proto[cmd][1], d), + '%s: %02X' % (proto[cmd][2], d), '%02X' % d]]) # Done with this packet. self.bitcount = self.databyte = 0 @@ -270,11 +233,12 @@ class Decoder(srd.Decoder): # another data byte or a STOP condition next. self.state = 'FIND DATA' - def found_stop(self, scl, sda): + def handle_stop(self): # Meta bitrate - #elapsed = 1 / float(self.samplerate) * (self.samplenum - self.pdu_start + 1) - #bitrate = int(1 / elapsed * self.pdu_bits) - #self.put(self.ss_byte, self.samplenum, self.out_bitrate, bitrate) + if self.samplerate: + elapsed = 1 / float(self.samplerate) * (self.samplenum - self.pdu_start + 1) + bitrate = int(1 / elapsed * self.pdu_bits) + self.put(self.ss_byte, self.samplenum, self.out_bitrate, bitrate) cmd = 'STOP' self.ss, self.es = self.samplenum, self.samplenum @@ -285,57 +249,42 @@ class Decoder(srd.Decoder): self.wr = -1 self.bits = [] - def decode(self, ss, es, logic): - for (self.samplenum, pins) in logic: - - (scl, sda) = pins - #self.pdu_bits += 1 - logic.logic_mask = 0b11 - logic.cur_pos = self.samplenum - logic.edge_index = -1 - + def decode(self): + while True: # State machine. if self.state == 'FIND START': - # START condition (S): SDA = falling, SCL = high - if (self.oldsda == 1 and sda == 0) and scl == 1: - self.found_start(scl, sda) - logic.exp_logic = 0b01 - logic.logic_mask = 0b01 - logic.edge_index = 0 - scl = 0 - else: - logic.exp_logic = 0b01 - logic.logic_mask = 0b11 - logic.edge_index = 1 - sda = 1 + # Wait for a START condition (S): SCL = high, SDA = falling. + self.wait({0: 'h', 1: 'f'}) + self.handle_start() elif self.state == 'FIND ADDRESS': - # Data sampling of receiver: SCL = rising - if self.oldscl == 0 and scl == 1: - self.found_address(scl, sda) - # START condition (S): SDA = falling, SCL = high - elif (self.oldsda == 1 and sda == 0) and scl == 1: - self.found_start(scl, sda) - # STOP condition (P): SDA = rising, SCL = high - elif (self.oldsda == 0 and sda == 1) and scl == 1: - self.found_stop(scl, sda) - elif self.state == 'FIND DATA': - # Data sampling of receiver: SCL = rising - if self.oldscl == 0 and scl == 1: - self.found_data(scl, sda) - # START condition (S): SDA = falling, SCL = high - elif (self.oldsda == 1 and sda == 0) and scl == 1: - self.found_start(scl, sda) - # STOP condition (P): SDA = rising, SCL = high - elif (self.oldsda == 0 and sda == 1) and scl == 1: - self.found_stop(scl, sda) - elif self.state == 'FIND ACK': - # Data sampling of receiver: SCL = rising - if self.oldscl == 0 and scl == 1: - self.get_ack(scl, sda) - logic.exp_logic = 0b01 - logic.logic_mask = 0b01 - logic.edge_index = 0 - scl = 0 + # Wait for any of the following conditions (or combinations): + # a) Data sampling of receiver: SCL = rising, and/or + # b) START condition (S): SCL = high, SDA = falling, and/or + # c) STOP condition (P): SCL = high, SDA = rising + (scl, sda) = self.wait([{0: 'r'}, {0: 'h', 1: 'f'}, {0: 'h', 1: 'r'}]) - # Save current SDA/SCL values for the next round. - self.oldscl, self.oldsda = scl, sda + # Check which of the condition(s) matched and handle them. + if (self.matched & (0b1 << 0)): + self.handle_address_or_data(scl, sda) + elif (self.matched & (0b1 << 1)): + self.handle_start() + elif (self.matched & (0b1 << 2)): + self.handle_stop() + elif self.state == 'FIND DATA': + # Wait for any of the following conditions (or combinations): + # a) Data sampling of receiver: SCL = rising, and/or + # b) START condition (S): SCL = high, SDA = falling, and/or + # c) STOP condition (P): SCL = high, SDA = rising + (scl, sda) = self.wait([{0: 'r'}, {0: 'h', 1: 'f'}, {0: 'h', 1: 'r'}]) + + # Check which of the condition(s) matched and handle them. + if (self.matched & (0b1 << 0)): + self.handle_address_or_data(scl, sda) + elif (self.matched & (0b1 << 1)): + self.handle_start() + elif (self.matched & (0b1 << 2)): + self.handle_stop() + elif self.state == 'FIND ACK': + # Wait for a data/ack bit: SCL = rising. + (scl, sda) = self.wait({0: 'r'}) + self.get_ack(scl, sda) diff --git a/libsigrokdecode4DSL/decoders/1-spi/__init__.py b/libsigrokdecode4DSL/decoders/1-spi/__init__.py index f76bb064..dc5cbc05 100755 --- a/libsigrokdecode4DSL/decoders/1-spi/__init__.py +++ b/libsigrokdecode4DSL/decoders/1-spi/__init__.py @@ -14,14 +14,14 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' The SPI (Serial Peripheral Interface) protocol decoder supports synchronous SPI(-like) protocols with a clock line, a MISO and MOSI line for data transfer in two directions, and an optional CS# pin. + Either MISO or MOSI (but not both) can be optional. If CS# is supplied, data is only decoded when CS# is asserted (clock diff --git a/libsigrokdecode4DSL/decoders/1-spi/pd.py b/libsigrokdecode4DSL/decoders/1-spi/pd.py index f6dba532..91e9e01e 100755 --- a/libsigrokdecode4DSL/decoders/1-spi/pd.py +++ b/libsigrokdecode4DSL/decoders/1-spi/pd.py @@ -3,7 +3,7 @@ ## ## Copyright (C) 2011 Gareth McMullin ## Copyright (C) 2012-2014 Uwe Hermann -## Copyright (C) 2016 DreamSourceLab +## Copyright (C) 2019 DreamSourceLab ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -16,8 +16,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -72,14 +71,11 @@ spi_mode = { (1, 1): 3, # Mode 3 } -class SamplerateError(Exception): - pass - class ChannelError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = '1:spi' name = '1:SPI' longname = 'Serial Peripheral Interface' @@ -87,6 +83,7 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['logic'] outputs = ['spi'] + tags = ['Embedded/industrial'] channels = ( {'id': 'clk', 'type': 0, 'name': 'CLK', 'desc': 'Clock'}, ) @@ -98,13 +95,15 @@ class Decoder(srd.Decoder): options = ( {'id': 'cs_polarity', 'desc': 'CS# polarity', 'default': 'active-low', 'values': ('active-low', 'active-high')}, - {'id': 'cpol', 'desc': 'Clock polarity', 'default': 0, + {'id': 'cpol', 'desc': 'Clock polarity (CPOL)', 'default': 0, 'values': (0, 1)}, - {'id': 'cpha', 'desc': 'Clock phase', 'default': 0, + {'id': 'cpha', 'desc': 'Clock phase (CPHA)', 'default': 0, 'values': (0, 1)}, {'id': 'bitorder', 'desc': 'Bit order', 'default': 'msb-first', 'values': ('msb-first', 'lsb-first')}, {'id': 'wordsize', 'desc': 'Word size', 'default': 8}, + {'id': 'frame', 'desc': 'Frame Decoder', 'default': 'no', + 'values': ('yes', 'no')}, ) annotations = ( ('106', 'miso-data', 'MISO data'), @@ -112,13 +111,18 @@ class Decoder(srd.Decoder): ('207', 'miso-bits', 'MISO bits'), ('209', 'mosi-bits', 'MOSI bits'), ('1000', 'warnings', 'Human-readable warnings'), + + ('6', 'miso-transfer', 'MISO transfer'), + ('8', 'mosi-transfer', 'MOSI transfer'), ) annotation_rows = ( - ('miso-data', 'MISO data', (0,)), ('miso-bits', 'MISO bits', (2,)), - ('mosi-data', 'MOSI data', (1,)), + ('miso-data', 'MISO data', (0,)), + ('miso-transfer', 'MISO transfer', (5,)), ('mosi-bits', 'MOSI bits', (3,)), - #('other', 'Other', (4,)), + ('mosi-data', 'MOSI data', (1,)), + ('mosi-transfer', 'MOSI transfer', (6,)), + ('other', 'Other', (4,)), ) binary = ( ('miso', 'MISO'), @@ -126,8 +130,10 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None - self.oldclk = -1 self.bitcount = 0 self.misodata = self.mosidata = 0 self.misobits = [] @@ -138,19 +144,7 @@ class Decoder(srd.Decoder): self.samplenum = -1 self.ss_transfer = -1 self.cs_was_deasserted = False - self.oldcs = None - self.oldpins = None self.have_cs = self.have_miso = self.have_mosi = None - self.no_cs_notification = False - self.mode = None - self.active_low = None - self.pin_checked = False - self.ws = None - self.bitwidth = 0 - - def metadata(self, key, value): - if key == srd.SRD_CONF_SAMPLERATE: - self.samplerate = value def start(self): self.out_python = self.register(srd.OUTPUT_PYTHON) @@ -158,34 +152,53 @@ class Decoder(srd.Decoder): self.out_binary = self.register(srd.OUTPUT_BINARY) self.out_bitrate = self.register(srd.OUTPUT_META, meta=(int, 'Bitrate', 'Bitrate during transfers')) - if not self.samplerate: - raise SamplerateError('Cannot decode without samplerate.') - #Sample data on rising/falling clock edge (depends on mode). - self.mode = spi_mode[self.options['cpol'], self.options['cpha']] - self.active_low = (self.options['cs_polarity'] == 'active-low') - self.ws = self.options['wordsize'] + self.bw = (self.options['wordsize'] + 7) // 8 + + def metadata(self, key, value): + if key == srd.SRD_CONF_SAMPLERATE: + self.samplerate = value def putw(self, data): self.put(self.ss_block, self.samplenum, self.out_ann, data) - def putdata(self): + def putdata(self, frame): # Pass MISO and MOSI bits and then data to the next PD up the stack. + so = self.misodata if self.have_miso else None + si = self.mosidata if self.have_mosi else None + so_bits = self.misobits if self.have_miso else None + si_bits = self.mosibits if self.have_mosi else None + if self.have_miso: ss, es = self.misobits[-1][1], self.misobits[0][2] - self.put(ss, es, self.out_python, ['BITS', self.mosibits, self.misobits]) - self.misobytes.append(Data(ss=ss, es=es, val=self.misodata)) - for bit in reversed(self.misobits): - self.put(bit[1], bit[2], self.out_ann, [2, ['%d' % bit[0]]]) - self.put(ss, es, self.out_ann, [0, ['%02X' % self.misodata]]) - # self.put(ss, es, self.out_binary, [0, bytes([self.misodata])]) + bdata = so.to_bytes(self.bw, byteorder='big') + self.put(ss, es, self.out_binary, [0, bdata]) if self.have_mosi: ss, es = self.mosibits[-1][1], self.mosibits[0][2] - self.put(ss, es, self.out_python, ['DATA', self.mosidata, self.misodata]) - self.mosibytes.append(Data(ss=ss, es=es, val=self.mosidata)) - for bit in reversed(self.mosibits): + bdata = si.to_bytes(self.bw, byteorder='big') + self.put(ss, es, self.out_binary, [1, bdata]) + + self.put(ss, es, self.out_python, ['BITS', si_bits, so_bits]) + self.put(ss, es, self.out_python, ['DATA', si, so]) + + if frame: + if self.have_miso: + self.misobytes.append(Data(ss=ss, es=es, val=so)) + if self.have_mosi: + self.mosibytes.append(Data(ss=ss, es=es, val=si)) + + # Bit annotations. + if self.have_miso: + for bit in self.misobits: + self.put(bit[1], bit[2], self.out_ann, [2, ['%d' % bit[0]]]) + if self.have_mosi: + for bit in self.mosibits: self.put(bit[1], bit[2], self.out_ann, [3, ['%d' % bit[0]]]) + + # Dataword annotations. + if self.have_miso: + self.put(ss, es, self.out_ann, [0, ['%02X' % self.misodata]]) + if self.have_mosi: self.put(ss, es, self.out_ann, [1, ['%02X' % self.mosidata]]) - # self.put(ss, es, self.out_binary, [1, bytes([self.mosibits])]) def reset_decoder_state(self): self.misodata = 0 if self.have_miso else None @@ -194,101 +207,145 @@ class Decoder(srd.Decoder): self.mosibits = [] if self.have_mosi else None self.bitcount = 0 - def handle_bit(self, miso, mosi, clk, cs): + def cs_asserted(self, cs): + active_low = (self.options['cs_polarity'] == 'active-low') + return (cs == 0) if active_low else (cs == 1) + + def handle_bit(self, miso, mosi, clk, cs, frame): # If this is the first bit of a dataword, save its sample number. if self.bitcount == 0: self.ss_block = self.samplenum - self.cs_was_deasserted = (cs == self.deasserted_cs) - if self.bitcount == 1: - self.bitwidth = self.samplenum - self.ss_block + self.cs_was_deasserted = \ + not self.cs_asserted(cs) if self.have_cs else False + + ws = self.options['wordsize'] + bo = self.options['bitorder'] - shift_cnt = (self.ws - 1 - self.bitcount) if (self.options['bitorder'] == 'msb-first') else self.bitcount # Receive MISO bit into our shift register. - es = self.samplenum if self.have_miso: - self.misodata |= miso << shift_cnt - if self.bitcount > 0: - es += self.samplenum - self.misobits[0][1] - self.misobits.insert(0, [miso, self.samplenum, es]) - if self.bitcount > 0: - self.misobits[1][2] = self.samplenum + if bo == 'msb-first': + self.misodata |= miso << (ws - 1 - self.bitcount) + else: + self.misodata |= miso << self.bitcount + # Receive MOSI bit into our shift register. - es = self.samplenum if self.have_mosi: - self.mosidata |= mosi << shift_cnt - if self.bitcount > 0: + if bo == 'msb-first': + self.mosidata |= mosi << (ws - 1 - self.bitcount) + else: + self.mosidata |= mosi << self.bitcount + + # Guesstimate the endsample for this bit (can be overridden below). + es = self.samplenum + if self.bitcount > 0: + if self.have_miso: + es += self.samplenum - self.misobits[0][1] + elif self.have_mosi: es += self.samplenum - self.mosibits[0][1] + + if self.have_miso: + self.misobits.insert(0, [miso, self.samplenum, es]) + if self.have_mosi: self.mosibits.insert(0, [mosi, self.samplenum, es]) - if self.bitcount > 0: - self.mosibits[1][2] = self.samplenum + + if self.bitcount > 0 and self.have_miso: + self.misobits[1][2] = self.samplenum + if self.bitcount > 0 and self.have_mosi: + self.mosibits[1][2] = self.samplenum self.bitcount += 1 + # Continue to receive if not enough bits were received, yet. - if self.bitcount != self.ws: + if self.bitcount != ws: return - self.putdata() + self.putdata(frame) # Meta bitrate. - #elapsed = 1 / float(self.samplerate) - #elapsed *= (self.samplenum - self.ss_block + 1) - #bitrate = int(1 / elapsed * self.options['wordsize']) - #self.put(self.ss_block, self.samplenum, self.out_bitrate, bitrate) + if self.samplerate: + elapsed = 1 / float(self.samplerate) + elapsed *= (self.samplenum - self.ss_block + 1) + bitrate = int(1 / elapsed * ws) + self.put(self.ss_block, self.samplenum, self.out_bitrate, bitrate) - #if self.have_cs and self.cs_was_deasserted: - # self.putw([4, ['CS# was deasserted during this data word!']]) + if self.have_cs and self.cs_was_deasserted: + self.putw([4, ['CS# was deasserted during this data word!']]) self.reset_decoder_state() - def decode(self, ss, es, logic): - # Either MISO or MOSI can be omitted (but not both). CS# is optional. - for (self.samplenum, pins) in logic: - (clk, miso, mosi, cs) = pins - if not self.pin_checked: - self.have_miso = (miso in (0, 1)) - self.have_mosi = (mosi in (0, 1)) - self.have_cs = (cs in (0, 1)) - # Either MISO or MOSI (but not both) can be omitted. - if not (self.have_miso or self.have_mosi): - raise ChannelError('Either MISO or MOSI (or both) pins required.') - if (self.mode == 0 or self.mode == 3): - self.exp_oldclk = 0 - self.exp_clk = 1 - else: - self.exp_oldclk = 1 - self.exp_clk = 0 - self.logic_mask = 0b1001 if self.have_cs else 0b0001 - self.exp_logic = 0b0000 if self.active_low else 0b1000 - self.asserted_oldcs = 1 if self.active_low else 0 - self.asserted_cs = 0 if self.active_low else 1 - self.deasserted_oldcs = 0 if self.active_low else 1 - self.deasserted_cs = 1 if self.active_low else 0 - self.pin_checked = True + def find_clk_edge(self, miso, mosi, clk, cs, first, frame): + if self.have_cs and (first or (self.matched & (0b1 << self.have_cs))): + # Send all CS# pin value changes. + oldcs = None if first else 1 - cs + self.put(self.samplenum, self.samplenum, self.out_python, + ['CS-CHANGE', oldcs, cs]) - logic.logic_mask = self.logic_mask - logic.cur_pos = self.samplenum - logic.edge_index = -1 + if frame: + if self.cs_asserted(cs): + self.ss_transfer = self.samplenum + self.misobytes = [] + self.mosibytes = [] + elif self.ss_transfer != -1: + if self.have_miso: + self.put(self.ss_transfer, self.samplenum, self.out_ann, + [5, [' '.join(format(x.val, '02X') for x in self.misobytes)]]) + if self.have_mosi: + self.put(self.ss_transfer, self.samplenum, self.out_ann, + [6, [' '.join(format(x.val, '02X') for x in self.mosibytes)]]) + self.put(self.ss_transfer, self.samplenum, self.out_python, + ['TRANSFER', self.mosibytes, self.misobytes]) - # Tell stacked decoders that we don't have a CS# signal. - if not self.no_cs_notification and not self.have_cs: - self.put(0, 0, self.out_python, ['CS-CHANGE', None, None]) - self.no_cs_notification = True - - if (self.oldcs, cs) == (self.asserted_oldcs, self.asserted_cs): - self.ss_transfer = self.samplenum - self.misobytes = [] - self.mosibytes = [] - self.reset_decoder_state() - elif (self.oldcs, cs) == (self.deasserted_oldcs, self.deasserted_cs): - self.put(self.ss_transfer, self.samplenum, self.out_python, - ['TRANSFER', self.mosibytes, self.misobytes]) - logic.exp_logic = self.exp_logic - cs = self.asserted_oldcs - logic.logic_mask = 0b1000 - logic.edge_index = 3 - elif not self.have_cs or cs == self.asserted_cs: - if (self.oldclk, clk) == (self.exp_oldclk, self.exp_clk): - #Sample on rising/falling clock edge - self.handle_bit(miso, mosi, clk, cs) + # Reset decoder state when CS# changes (and the CS# pin is used). + self.reset_decoder_state() - self.oldclk, self.oldcs = clk, cs + # We only care about samples if CS# is asserted. + if self.have_cs and not self.cs_asserted(cs): + return + + # Ignore sample if the clock pin hasn't changed. + if first or not (self.matched & (0b1 << 0)): + return + + # Found the correct clock edge, now get the SPI bit(s). + self.handle_bit(miso, mosi, clk, cs, frame) + + def decode(self): + # The CLK input is mandatory. Other signals are (individually) + # optional. Yet either MISO or MOSI (or both) must be provided. + # Tell stacked decoders when we don't have a CS# signal. + if not self.has_channel(0): + raise ChannelError('CLK pin required.') + self.have_miso = self.has_channel(1) + self.have_mosi = self.has_channel(2) + if not self.have_miso and not self.have_mosi: + raise ChannelError('Either MISO or MOSI (or both) pins required.') + self.have_cs = self.has_channel(3) + if not self.have_cs: + self.put(0, 0, self.out_python, ['CS-CHANGE', None, None]) + + frame = self.options['frame'] == 'yes' + + # We want all CLK changes. We want all CS changes if CS is used. + # Map 'have_cs' from boolean to an integer index. This simplifies + # evaluation in other locations. + # Sample data on rising/falling clock edge (depends on mode). + mode = spi_mode[self.options['cpol'], self.options['cpha']] + if mode == 0 or mode == 3: # Sample on rising clock edge + wait_cond = [{0: 'r'}] + else: # Sample on falling clock edge + wait_cond = [{0: 'f'}] + + if self.have_cs: + self.have_cs = len(wait_cond) + wait_cond.append({3: 'e'}) + + # "Pixel compatibility" with the v2 implementation. Grab and + # process the very first sample before checking for edges. The + # previous implementation did this by seeding old values with + # None, which led to an immediate "change" in comparison. + (clk, miso, mosi, cs) = self.wait({}) + self.find_clk_edge(miso, mosi, clk, cs, True, frame) + + while True: + (clk, miso, mosi, cs) = self.wait(wait_cond) + self.find_clk_edge(miso, mosi, clk, cs, False, frame) diff --git a/libsigrokdecode4DSL/decoders/1-uart/__init__.py b/libsigrokdecode4DSL/decoders/1-uart/__init__.py index efe0e523..ce6136f1 100755 --- a/libsigrokdecode4DSL/decoders/1-uart/__init__.py +++ b/libsigrokdecode4DSL/decoders/1-uart/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/1-uart/pd.py b/libsigrokdecode4DSL/decoders/1-uart/pd.py index 159f1a37..81442e16 100755 --- a/libsigrokdecode4DSL/decoders/1-uart/pd.py +++ b/libsigrokdecode4DSL/decoders/1-uart/pd.py @@ -2,7 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2011-2014 Uwe Hermann -## Copyright (C) 2016 DreamSourceLab +## Copyright (C) 2019 DreamSourceLab ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -15,24 +15,24 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd +from common.srdhelper import bitpack from math import floor, ceil ''' OUTPUT_PYTHON format: Packet: -[, ] +[, , ] This is the list of s and their respective values: - 'STARTBIT': The data is the (integer) value of the start bit (0/1). - 'DATA': This is always a tuple containing two items: - 1st item: the (integer) value of the UART data. Valid values - range from 0 to 512 (as the data can be up to 9 bits in size). + range from 0 to 511 (as the data can be up to 9 bits in size). - 2nd item: the list of individual data bits and their ss/es numbers. - 'PARITYBIT': The data is the (integer) value of the parity bit (0/1). - 'STOPBIT': The data is the (integer) value of the stop bit (0 or 1). @@ -40,7 +40,10 @@ This is the list of s and their respective values: - 'INVALID STOPBIT': The data is the (integer) value of the stop bit (0/1). - 'PARITY ERROR': The data is a tuple with two entries. The first one is the expected parity value, the second is the actual parity value. - - TODO: Frame error? + - 'BREAK': The data is always 0. + - 'FRAME': The data is always a tuple containing two items: The (integer) + value of the UART data, and a boolean which reflects the validity of the + UART frame. ''' @@ -68,8 +71,11 @@ def parity_ok(parity_type, parity_bit, data, num_data_bits): class SamplerateError(Exception): pass +class ChannelError(Exception): + pass + class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = '1:uart' name = '1:UART' longname = 'Universal Asynchronous Receiver/Transmitter' @@ -77,11 +83,12 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['logic'] outputs = ['uart'] + tags = ['Embedded/industrial'] channels = ( {'id': 'rxtx', 'type': 209, 'name': 'RX/TX', 'desc': 'UART transceive line'}, ) options = ( - {'id': 'baudrate', 'desc': 'Baud rate', 'default': 9600}, + {'id': 'baudrate', 'desc': 'Baud rate', 'default': 115200}, {'id': 'num_data_bits', 'desc': 'Data bits', 'default': 8, 'values': (5, 6, 7, 8, 9)}, {'id': 'parity_type', 'desc': 'Parity type', 'default': 'none', @@ -92,7 +99,7 @@ class Decoder(srd.Decoder): 'values': (0.0, 0.5, 1.0, 1.5)}, {'id': 'bit_order', 'desc': 'Bit order', 'default': 'lsb-first', 'values': ('lsb-first', 'msb-first')}, - {'id': 'format', 'desc': 'Data format', 'default': 'ascii', + {'id': 'format', 'desc': 'Data format', 'default': 'hex', 'values': ('ascii', 'dec', 'hex', 'oct', 'bin')}, {'id': 'invert', 'desc': 'Invert Signal?', 'default': 'no', 'values': ('yes', 'no')}, @@ -105,62 +112,69 @@ class Decoder(srd.Decoder): ('1', 'stop', 'stop bits'), ('1000', 'warnings', 'warnings'), ('209', 'data-bits', 'data bits'), + ('10', 'break', 'break'), ) annotation_rows = ( ('data', 'RX/TX', (0, 1, 2, 3, 4)), ('data-bits', 'Bits', (6,)), ('warnings', 'Warnings', (5,)), + ('break', 'break', (7,)), ) binary = ( ('rxtx', 'RX/TX dump'), ) + idle_state = 'WAIT FOR START BIT' - def put_ann_bit(self, width, data): - s = self.bitstart - self.put(floor(s), floor(s + width), self.out_ann, data) + def putx(self, data): + s, halfbit = self.startsample, self.bit_width / 2.0 + self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_ann, data) - def put_python_bit(self, width, data): - s = self.bitstart - self.put(floor(s), floor(s + width), self.out_python, data) + def putpx(self, data): + s, halfbit = self.startsample, self.bit_width / 2.0 + self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_python, data) - def put_ann_byte(self, data): - ss, s = self.bytestart, self.bitstart - self.put(floor(ss), floor(s + self.bit_width), self.out_ann, data) + def putg(self, data): + s, halfbit = self.samplenum, self.bit_width / 2.0 + self.put(s - floor(halfbit), s + ceil(halfbit), self.out_ann, data) - def put_python_byte(self, data): - ss, s = self.bytestart, self.bitstart - self.put(floor(ss), floor(s + self.bit_width), self.out_python, data) + def putp(self, data): + s, halfbit = self.samplenum, self.bit_width / 2.0 + self.put(s - floor(halfbit), s + ceil(halfbit), self.out_python, data) + + def putgse(self, ss, es, data): + self.put(ss, es, self.out_ann, data) + + def putpse(self, ss, es, data): + self.put(ss, es, self.out_python, data) + + def putbin(self, data): + s, halfbit = self.startsample, self.bit_width / 2.0 + self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_binary, data) - def put_binary_byte(self, data): - ss, s = self.bytestart, self.bitstart - self.put(floor(ss), floor(s + self.bit_width), self.out_binary, data) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.samplenum = 0 + self.frame_start = -1 + self.frame_valid = None self.startbit = -1 - self.bitcount = 0 - self.databyte = 0 + self.cur_data_bit = 0 + self.datavalue = 0 self.paritybit = -1 self.stopbit1 = -1 - self.bitstart = -1 - self.bytestart = -1 - self.state = 'FIND START' - self.oldbit = -1 + self.startsample = -1 + self.state = 'WAIT FOR START BIT' self.databits = [] + self.break_start = None def start(self): self.out_python = self.register(srd.OUTPUT_PYTHON) self.out_binary = self.register(srd.OUTPUT_BINARY) self.out_ann = self.register(srd.OUTPUT_ANN) - if not self.samplerate: - raise SamplerateError('Cannot decode without samplerate.') - if self.samplerate < self.options['baudrate']*4: - raise SamplerateError('Samplerate is too low for current baudrate setting, 4x at least!') - if self.options['invert'] == 'yes': - self.exp_logic = 1 - else: - self.exp_logic = 0 + self.bw = (self.options['num_data_bits'] + 7) // 8 def metadata(self, key, value): if key == srd.SRD_CONF_SAMPLERATE: @@ -168,144 +182,251 @@ class Decoder(srd.Decoder): # The width of one UART bit in number of samples. self.bit_width = float(self.samplerate) / float(self.options['baudrate']) - def frame_start(self, signal): + def get_sample_point(self, bitnum): + # Determine absolute sample number of a bit slot's sample point. + # bitpos is the samplenumber which is in the middle of the + # specified UART bit (0 = start bit, 1..x = data, x+1 = parity bit + # (if used) or the first stop bit, and so on). + # The samples within bit are 0, 1, ..., (bit_width - 1), therefore + # index of the middle sample within bit window is (bit_width - 1) / 2. + bitpos = self.frame_start + (self.bit_width - 1) / 2.0 + bitpos += bitnum * self.bit_width + return bitpos + + def wait_for_start_bit(self, signal): # Save the sample number where the start bit begins. - self.bitstart = self.samplenum + self.frame_start = self.samplenum + self.frame_valid = True + self.state = 'GET START BIT' def get_start_bit(self, signal): self.startbit = signal - # The startbit must be 0. If not, we report an error. + # The startbit must be 0. If not, we report an error and wait + # for the next start bit (assuming this one was spurious). if self.startbit != 0: - self.put_python_bit(self.bit_width, ['INVALID STARTBIT', 0, self.startbit]) - self.put_ann_bit(self.bit_width, [5, ['Frame error', 'Frame err', 'FE']]) - # TODO: Abort? Ignore rest of the frame? + self.putp(['INVALID STARTBIT', 0, self.startbit]) + self.putg([5, ['Frame error', 'Frame err', 'FE']]) + self.frame_valid = False + es = self.samplenum + ceil(self.bit_width / 2.0) + self.putpse(self.frame_start, es, ['FRAME', 0, + (self.datavalue[rxtx], self.frame_valid[rxtx])]) + self.state = 'WAIT FOR START BIT' + return + + self.cur_data_bit = 0 + self.datavalue = 0 + self.startsample = -1 + + self.putp(['STARTBIT', 0, self.startbit]) + self.putg([1, ['Start bit', 'Start', 'S']]) - self.bitcount = 0 - self.databyte = 0 self.state = 'GET DATA BITS' - self.put_python_bit(self.bit_width, ['STARTBIT', 0, self.startbit]) - self.put_ann_bit(self.bit_width, [1, ['Start bit', 'Start', 'S']]) - def get_data_bits(self, signal): - # Save the sample number of where the bit begins. - self.bitstart += self.bit_width - if self.bitcount == 0 : - self.bytestart = self.bitstart + # Save the sample number of the middle of the first data bit. + if self.startsample == -1: + self.startsample = self.samplenum - # Get the next data bit in LSB-first or MSB-first fashion. - if self.options['bit_order'] == 'lsb-first': - self.databyte >>= 1 - self.databyte |= \ - (signal << (self.options['num_data_bits'] - 1)) - else: - self.databyte <<= 1 - self.databyte |= (signal << 0) - - self.put_ann_bit(self.bit_width, [6, ['%d' % signal]]) + self.putg([6, ['%d' % signal]]) # Store individual data bits and their start/end samplenumbers. s, halfbit = self.samplenum, int(self.bit_width / 2) self.databits.append([signal, s - halfbit, s + halfbit]) # Return here, unless we already received all data bits. - if self.bitcount < self.options['num_data_bits'] - 1: - self.bitcount += 1 + self.cur_data_bit += 1 + if self.cur_data_bit < self.options['num_data_bits']: return - if self.options['parity_type'] == 'none': - self.state = 'GET STOP BITS' - else: - self.state = 'GET PARITY BIT' + # Convert accumulated data bits to a data value. + bits = [b[0] for b in self.databits] + if self.options['bit_order'] == 'msb-first': + bits.reverse() + self.datavalue = bitpack(bits) + self.putpx(['DATA', 0, (self.datavalue, self.databits)]) - self.put_python_byte(['DATA', 0, (self.databyte, self.databits)]) + b = self.datavalue + formatted = self.format_value(b) + if formatted is not None: + self.putx([0, [formatted]]) - b, f = self.databyte, self.options['format'] - if f == 'ascii': - c = chr(b) if b in range(30, 126 + 1) else '[%02X]' % b - self.put_ann_byte([0, [c]]) - elif f == 'dec': - self.put_ann_byte([0, [str(b)]]) - elif f == 'hex': - self.put_ann_byte([0, [hex(b)[2:].zfill(2).upper()]]) - elif f == 'oct': - self.put_ann_byte([0, [oct(b)[2:].zfill(3)]]) - elif f == 'bin': - self.put_ann_byte([0, [bin(b)[2:].zfill(8)]]) - - #self.put_binary_byte([0, bytes([b])]) - #self.put_binary_byte([2, bytes([b])]) + bdata = b.to_bytes(self.bw, byteorder='big') + self.putbin([0, bdata]) + self.putbin([1, bdata]) self.databits = [] + # Advance to either reception of the parity bit, or reception of + # the STOP bits if parity is not applicable. + self.state = 'GET PARITY BIT' + if self.options['parity_type'] == 'none': + self.state = 'GET STOP BITS' + + def format_value(self, v): + # Format value 'v' according to configured options. + # Reflects the user selected kind of representation, as well as + # the number of data bits in the UART frames. + + fmt, bits = self.options['format'], self.options['num_data_bits'] + + # Assume "is printable" for values from 32 to including 126, + # below 32 is "control" and thus not printable, above 127 is + # "not ASCII" in its strict sense, 127 (DEL) is not printable, + # fall back to hex representation for non-printables. + if fmt == 'ascii': + if v in range(32, 126 + 1): + return chr(v) + hexfmt = "[{:02X}]" if bits <= 8 else "[{:03X}]" + return hexfmt.format(v) + + # Mere number to text conversion without prefix and padding + # for the "decimal" output format. + if fmt == 'dec': + return "{:d}".format(v) + + # Padding with leading zeroes for hex/oct/bin formats, but + # without a prefix for density -- since the format is user + # specified, there is no ambiguity. + if fmt == 'hex': + digits = (bits + 4 - 1) // 4 + fmtchar = "X" + elif fmt == 'oct': + digits = (bits + 3 - 1) // 3 + fmtchar = "o" + elif fmt == 'bin': + digits = bits + fmtchar = "b" + else: + fmtchar = None + if fmtchar is not None: + fmt = "{{:0{:d}{:s}}}".format(digits, fmtchar) + return fmt.format(v) + + return None + def get_parity_bit(self, signal): - # Save the sample number of where the bit begins. - self.bitstart += self.bit_width self.paritybit = signal - self.state = 'GET STOP BITS' if parity_ok(self.options['parity_type'], self.paritybit, - self.databyte, self.options['num_data_bits']): - self.put_python_bit(self.bit_width, ['PARITYBIT', 0, self.paritybit]) - self.put_ann_bit(self.bit_width, [2, ['Parity bit', 'Parity', 'P']]) + self.datavalue, self.options['num_data_bits']): + self.putp(['PARITYBIT', 0, self.paritybit]) + self.putg([2, ['Parity bit', 'Parity', 'P']]) else: - # # TODO: Return expected/actual parity values. - self.put_python_bit(self.bit_width, ['PARITY ERROR', 0, (0, 1)]) # FIXME: Dummy tuple... - self.put_ann_bit(self.bit_width, [3, ['Parity error', 'Parity err', 'PE']]) + # TODO: Return expected/actual parity values. + self.putp(['PARITY ERROR', 0, (0, 1)]) # FIXME: Dummy tuple... + self.putg([3, ['Parity error', 'Parity err', 'PE']]) + self.frame_valid = False + + self.state = 'GET STOP BITS' # TODO: Currently only supports 1 stop bit. def get_stop_bits(self, signal): - # Save the sample number of where the bit begins. - self.bitstart += self.bit_width - self.stopbit1 = signal + # Stop bits must be 1. If not, we report an error. if self.stopbit1 != 1: - self.put_python_bit(self.bit_width, ['INVALID STOPBIT', 0, self.stopbit1]) - self.put_ann_bit(self.bit_width, [5, ['Frame error', 'Frame err', 'FE']]) - # TODO: Abort? Ignore the frame? Other? + self.putp(['INVALID STOPBIT', 0, self.stopbit1]) + self.putg([5, ['Frame error', 'Frame err', 'FE']]) + self.frame_valid = False - self.state = 'FIND START' - self.put_python_bit(int(self.bit_width * self.options['num_stop_bits']), ['STOPBIT', 0, self.stopbit1]) - self.put_ann_bit(int(self.bit_width * self.options['num_stop_bits']), [4, ['Stop bit', 'Stop', 'T']]) + self.putp(['STOPBIT', 0, self.stopbit1]) + self.putg([2, ['Stop bit', 'Stop', 'T']]) - def decode(self, ss, es, logic): - for (self.samplenum, pins) in logic: + # Pass the complete UART frame to upper layers. + es = self.samplenum + ceil(self.bit_width / 2.0) + self.putpse(self.frame_start, es, ['FRAME', 0, + (self.datavalue, self.frame_valid)]) - # In default case, the iteration gap is 1 - logic.logic_mask = 0 - logic.edge_index = 0 + self.state = 'WAIT FOR START BIT' - (signal,) = pins + def handle_break(self): + self.putpse(self.frame_start, self.samplenum, + ['BREAK', 0, 0]) + self.putgse(self.frame_start, self.samplenum, + [7, ['Break condition', 'Break', 'Brk', 'B']]) + self.state = 'WAIT FOR START BIT' - if self.options['invert'] == 'yes': - signal = not signal + def get_wait_cond(self, inv): + # Return condititions that are suitable for Decoder.wait(). Those + # conditions either match the falling edge of the START bit, or + # the sample point of the next bit time. + state = self.state + if state == 'WAIT FOR START BIT': + return {0: 'r' if inv else 'f'} + if state == 'GET START BIT': + bitnum = 0 + elif state == 'GET DATA BITS': + bitnum = 1 + self.cur_data_bit + elif state == 'GET PARITY BIT': + bitnum = 1 + self.options['num_data_bits'] + elif state == 'GET STOP BITS': + bitnum = 1 + self.options['num_data_bits'] + bitnum += 0 if self.options['parity_type'] == 'none' else 1 + want_num = ceil(self.get_sample_point(bitnum)) + return {'skip': want_num - self.samplenum} - # State machine. - if self.state == 'FIND START': - if (self.oldbit == 1 and signal == 0): - self.frame_start(signal) - logic.itercnt += (self.bit_width - 1) / 2.0 - else: - logic.exp_logic = self.exp_logic - logic.logic_mask = 1 - logic.cur_pos = self.samplenum - signal = 1 - elif self.state == 'GET START BIT': - self.get_start_bit(signal) - logic.itercnt += self.bit_width - elif self.state == 'GET DATA BITS': - self.get_data_bits(signal) - logic.itercnt += self.bit_width - elif self.state == 'GET PARITY BIT': - self.get_parity_bit(signal) - logic.itercnt += self.bit_width - elif self.state == 'GET STOP BITS': - self.get_stop_bits(signal) - logic.itercnt += (self.options['num_stop_bits'] - 0.75) * self.bit_width - signal = 0 + def inspect_sample(self, signal, inv): + # Inspect a sample returned by .wait() for the specified UART line. + if inv: + signal = not signal - # Save current RX/TX values for the next round. - self.oldbit = signal + state = self.state + if state == 'WAIT FOR START BIT': + self.wait_for_start_bit(signal) + elif state == 'GET START BIT': + self.get_start_bit(signal) + elif state == 'GET DATA BITS': + self.get_data_bits(signal) + elif state == 'GET PARITY BIT': + self.get_parity_bit(signal) + elif state == 'GET STOP BITS': + self.get_stop_bits(signal) + + def inspect_edge(self, signal, inv): + # Inspect edges, independently from traffic, to detect break conditions. + if inv: + signal = not signal + if not signal: + # Signal went low. Start another interval. + self.break_start = self.samplenum + return + # Signal went high. Was there an extended period with low signal? + if self.break_start is None: + return + diff = self.samplenum - self.break_start + if diff >= self.break_min_sample_count: + self.handle_break() + self.break_start = None + + def decode(self): + if not self.samplerate: + raise SamplerateError('Cannot decode without samplerate.') + + inv = self.options['invert'] == 'yes' + cond_data_idx = None + + # Determine the number of samples for a complete frame's time span. + # A period of low signal (at least) that long is a break condition. + frame_samples = 1 # START + frame_samples += self.options['num_data_bits'] + frame_samples += 0 if self.options['parity_type'] == 'none' else 1 + frame_samples += self.options['num_stop_bits'] + frame_samples *= self.bit_width + self.break_min_sample_count = ceil(frame_samples) + cond_edge_idx = None + + while True: + conds = [] + + cond_data_idx = len(conds) + conds.append(self.get_wait_cond(inv)) + cond_edge_idx = len(conds) + conds.append({0: 'e'}) + + (rxtx, ) = self.wait(conds) + if cond_data_idx is not None and (self.matched & (0b1 << cond_data_idx)): + self.inspect_sample(rxtx, inv) + if cond_edge_idx is not None and (self.matched & (0b1 << cond_edge_idx)): + self.inspect_edge(rxtx, inv) diff --git a/libsigrokdecode4DSL/decoders/ac97/__init__.py b/libsigrokdecode4DSL/decoders/ac97/__init__.py new file mode 100755 index 00000000..8b96e8aa --- /dev/null +++ b/libsigrokdecode4DSL/decoders/ac97/__init__.py @@ -0,0 +1,36 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Gerhard Sittig +## +## 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 2 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 . +## + +''' +AC'97 (Audio Codec '97) was specifically designed by Intel for audio and +modem I/O functionality in mainstream PC systems. See the specification in +http://download.intel.com/support/motherboards/desktop/sb/ac97_r23.pdf + +AC'97 communicates full duplex data (SDATA_IN, SDATA_OUT), where bits +are clocked by the BIT_CLK and frames are signalled by the SYNC signals. +A low active RESET# line completes the set of signals. + +Frames repeat at a nominal frequency of 48kHz, and consist of 256 bits +each. One 16bit slot contains management information, twelve 20bit slots +follow which carry data for three management and nine audio/modem channels. +Optionally two slots of one frame can get combined for higher resolution +on fewer channels, or double data rate. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/ac97/pd.py b/libsigrokdecode4DSL/decoders/ac97/pd.py new file mode 100755 index 00000000..3f79eefa --- /dev/null +++ b/libsigrokdecode4DSL/decoders/ac97/pd.py @@ -0,0 +1,505 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Gerhard Sittig +## Copyright (C) 2019 DreamSourceLab +## +## 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 2 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 . +## + +# This implementation is incomplete. TODO items: +# - Support the optional RESET# pin, detect cold and warm reset. +# - Split slot values into audio samples of their respective width and +# frequency (either on user provided parameters, or from inspection of +# decoded register access). + +import sigrokdecode as srd + +class ChannelError(Exception): + pass + +class Pins: + (SYNC, BIT_CLK, SDATA_OUT, SDATA_IN, RESET) = range(5) + +class Ann: + ( + BITS_OUT, BITS_IN, + SLOT_OUT_RAW, SLOT_OUT_TAG, SLOT_OUT_ADDR, SLOT_OUT_DATA, + SLOT_OUT_03, SLOT_OUT_04, SLOT_OUT_05, SLOT_OUT_06, + SLOT_OUT_07, SLOT_OUT_08, SLOT_OUT_09, SLOT_OUT_10, + SLOT_OUT_11, SLOT_OUT_IO, + SLOT_IN_RAW, SLOT_IN_TAG, SLOT_IN_ADDR, SLOT_IN_DATA, + SLOT_IN_03, SLOT_IN_04, SLOT_IN_05, SLOT_IN_06, + SLOT_IN_07, SLOT_IN_08, SLOT_IN_09, SLOT_IN_10, + SLOT_IN_11, SLOT_IN_IO, + WARN, ERROR, + ) = range(32) + ( + BIN_FRAME_OUT, + BIN_FRAME_IN, + BIN_SLOT_RAW_OUT, + BIN_SLOT_RAW_IN, + ) = range(4) + +class Decoder(srd.Decoder): + api_version = 3 + id = 'ac97' + name = "AC '97" + longname = "Audio Codec '97" + desc = 'Audio and modem control for PC systems.' + license = 'gplv2+' + inputs = ['logic'] + outputs = [] + tags = ['Audio', 'PC'] + channels = ( + {'id': 'sync', 'name': 'SYNC', 'desc': 'Frame synchronization'}, + {'id': 'clk', 'name': 'BIT_CLK', 'desc': 'Data bits clock'}, + ) + optional_channels = ( + {'id': 'out', 'name': 'SDATA_OUT', 'desc': 'Data output'}, + {'id': 'in', 'name': 'SDATA_IN', 'desc': 'Data input'}, + {'id': 'rst', 'name': 'RESET#', 'desc': 'Reset line'}, + ) + annotations = ( + ('bit-out', 'Output bits'), + ('bit-in', 'Input bits'), + ('slot-out-raw', 'Output raw value'), + ('slot-out-tag', 'Output TAG'), + ('slot-out-cmd-addr', 'Output command address'), + ('slot-out-cmd-data', 'Output command data'), + ('slot-out-03', 'Output slot 3'), + ('slot-out-04', 'Output slot 4'), + ('slot-out-05', 'Output slot 5'), + ('slot-out-06', 'Output slot 6'), + ('slot-out-07', 'Output slot 7'), + ('slot-out-08', 'Output slot 8'), + ('slot-out-09', 'Output slot 9'), + ('slot-out-10', 'Output slot 10'), + ('slot-out-11', 'Output slot 11'), + ('slot-out-io-ctrl', 'Output I/O control'), + ('slot-in-raw', 'Input raw value'), + ('slot-in-tag', 'Input TAG'), + ('slot-in-sts-addr', 'Input status address'), + ('slot-in-sts-data', 'Input status data'), + ('slot-in-03', 'Input slot 3'), + ('slot-in-04', 'Input slot 4'), + ('slot-in-05', 'Input slot 5'), + ('slot-in-06', 'Input slot 6'), + ('slot-in-07', 'Input slot 7'), + ('slot-in-08', 'Input slot 8'), + ('slot-in-09', 'Input slot 9'), + ('slot-in-10', 'Input slot 10'), + ('slot-in-11', 'Input slot 11'), + ('slot-in-io-sts', 'Input I/O status'), + # TODO: Add more annotation classes: + # TAG: 'ready', 'valid', 'id', 'rsv' + # CMD ADDR: 'r/w', 'addr', 'unused' + # CMD DATA: 'data', 'unused' + # 3-11: 'data', 'unused', 'double data' + ('warning', 'Warning'), + ('error', 'Error'), + ) + annotation_rows = ( + ('bits-out', 'Output bits', (Ann.BITS_OUT,)), + ('slots-out-raw', 'Output numbers', (Ann.SLOT_OUT_RAW,)), + ('slots-out', 'Output slots', ( + Ann.SLOT_OUT_TAG, Ann.SLOT_OUT_ADDR, Ann.SLOT_OUT_DATA, + Ann.SLOT_OUT_03, Ann.SLOT_OUT_04, Ann.SLOT_OUT_05, Ann.SLOT_OUT_06, + Ann.SLOT_OUT_07, Ann.SLOT_OUT_08, Ann.SLOT_OUT_09, Ann.SLOT_OUT_10, + Ann.SLOT_OUT_11, Ann.SLOT_OUT_IO,)), + ('bits-in', 'Input bits', (Ann.BITS_IN,)), + ('slots-in-raw', 'Input numbers', (Ann.SLOT_IN_RAW,)), + ('slots-in', 'Input slots', ( + Ann.SLOT_IN_TAG, Ann.SLOT_IN_ADDR, Ann.SLOT_IN_DATA, + Ann.SLOT_IN_03, Ann.SLOT_IN_04, Ann.SLOT_IN_05, Ann.SLOT_IN_06, + Ann.SLOT_IN_07, Ann.SLOT_IN_08, Ann.SLOT_IN_09, Ann.SLOT_IN_10, + Ann.SLOT_IN_11, Ann.SLOT_IN_IO,)), + ('warnings', 'Warnings', (Ann.WARN,)), + ('errors', 'Errors', (Ann.ERROR,)), + ) + binary = ( + ('frame-out', 'Frame bits, output data'), + ('frame-in', 'Frame bits, input data'), + ('slot-raw-out', 'Raw slot bits, output data'), + ('slot-raw-in', 'Raw slot bits, input data'), + # TODO: Which (other) binary classes to implement? + # - Are binary annotations per audio slot useful? + # - Assume 20bit per slot, in 24bit units? Or assume 16bit + # audio samples? Observe register access and derive width + # of the audio data? Dump channels 3-11 or 1-12? + ) + + def putx(self, ss, es, cls, data): + self.put(ss, es, self.out_ann, [cls, data]) + + def putf(self, frombit, bitcount, cls, data): + ss = self.frame_ss_list[frombit] + es = self.frame_ss_list[frombit + bitcount] + self.putx(ss, es, cls, data) + + def putb(self, frombit, bitcount, cls, data): + ss = self.frame_ss_list[frombit] + es = self.frame_ss_list[frombit + bitcount] + self.put(ss, es, self.out_binary, [cls, data]) + + def __init__(self): + self.out_binary = None + self.out_ann = None + self.reset() + + def reset(self): + self.frame_ss_list = None + self.frame_slot_lens = [0, 16] + [16 + 20 * i for i in range(1, 13)] + self.frame_total_bits = self.frame_slot_lens[-1] + self.handle_slots = { + 0: self.handle_slot_00, + 1: self.handle_slot_01, + 2: self.handle_slot_02, + } + + def start(self): + if not self.out_binary: + self.out_binary = self.register(srd.OUTPUT_BINARY) + if not self.out_ann: + self.out_ann = self.register(srd.OUTPUT_ANN) + + def metadata(self, key, value): + if key == srd.SRD_CONF_SAMPLERATE: + self.samplerate = value + + def bits_to_int(self, bits): + # Convert MSB-first bit sequence to integer value. + if not bits: + return 0 + count = len(bits) + value = sum([2 ** (count - 1 - i) for i in range(count) if bits[i]]) + return value + + def bits_to_bin_ann(self, bits): + # Convert MSB-first bit sequence to binary annotation data. + # It's assumed that the number of bits does not (in useful ways) + # fit into an integer, and we need to create an array of bytes + # from the data afterwards, anyway. Hence the separate routine + # and the conversion of eight bits each. + out = [] + count = len(bits) + while count > 0: + count -= 8 + by, bits = bits[:8], bits[8:] + by = self.bits_to_int(by) + out.append(by) + out = bytes(out) + return out + + def int_to_nibble_text(self, value, bitcount): + # Convert number to hex digits for given bit count. + digits = (bitcount + 3) // 4 + text = '{{:0{:d}x}}'.format(digits).format(value) + return text + + def get_bit_field(self, data, size, off, count): + shift = size - off - count + data >>= shift + mask = (1 << count) - 1 + data &= mask + return data + + def flush_frame_bits(self): + # Flush raw frame bits to binary annotation. + anncls = Ann.BIN_FRAME_OUT + data = self.frame_bits_out[:] + count = len(data) + data = self.bits_to_bin_ann(data) + self.putb(0, count, anncls, data) + + anncls = Ann.BIN_FRAME_IN + data = self.frame_bits_in[:] + count = len(data) + data = self.bits_to_bin_ann(data) + self.putb(0, count, anncls, data) + + def start_frame(self, ss): + # Mark the start of a frame. + if self.frame_ss_list: + # Flush bits if we had a frame before the frame which is + # starting here. + self.flush_frame_bits() + self.frame_ss_list = [ss] + self.frame_bits_out = [] + self.frame_bits_in = [] + self.frame_slot_data_out = [] + self.frame_slot_data_in = [] + self.have_slots = {True: None, False: None} + + def handle_slot_dummy(self, slotidx, bitidx, bitcount, is_out, data): + # Handle slot x, default/fallback handler. + # Only process data of slots 1-12 when slot 0 says "valid". + if not self.have_slots[is_out]: + return + if not self.have_slots[is_out][slotidx]: + return + + # Emit a naive annotation with just the data bits that we saw + # for the slot (hex nibbles for density). For audio data this + # can be good enough. Slots with special meaning should not end + # up calling the dummy handler. + text = self.int_to_nibble_text(data, bitcount) + anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG + self.putf(bitidx, bitcount, anncls + slotidx, [text]) + + # Emit binary output for the data that is contained in slots + # which end up calling the default handler. This transparently + # should translate to "the slots with audio data", as other + # slots which contain management data should have their specific + # handler routines. In the present form, this approach might be + # good enough to get a (header-less) audio stream for typical + # setups where only line-in or line-out are in use. + # + # TODO: Improve this early prototype implementation. For now the + # decoder just exports the upper 16 bits of each audio channel + # that happens to be valid. For an improved implementation, it + # either takes user provided specs or more smarts like observing + # register access (if the capture includes it). + anncls = Ann.BIN_SLOT_RAW_OUT if is_out else Ann.BIN_SLOT_RAW_IN + data_bin = data >> 4 + data_bin &= 0xffff + data_bin = data_bin.to_bytes(2, byteorder = 'big') + self.putb(bitidx, bitcount, anncls, data_bin) + + def handle_slot_00(self, slotidx, bitidx, bitcount, is_out, data): + # Handle slot 0, TAG. + slotpos = self.frame_slot_lens[slotidx] + fieldoff = 0 + anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG + + fieldlen = 1 + ready = self.get_bit_field(data, bitcount, fieldoff, fieldlen) + text = ['READY: 1', 'READY', 'RDY', 'R'] if ready else ['ready: 0', 'rdy', '-'] + self.putf(slotpos + fieldoff, fieldlen, anncls, text) + fieldoff += fieldlen + + fieldlen = 12 + valid = self.get_bit_field(data, bitcount, fieldoff, fieldlen) + text = ['VALID: {:3x}'.format(valid), '{:3x}'.format(valid)] + self.putf(slotpos + fieldoff, fieldlen, anncls, text) + have_slots = [True] + [False] * 12 + for idx in range(12): + have_slots[idx + 1] = bool(valid & (1 << (11 - idx))) + self.have_slots[is_out] = have_slots + fieldoff += fieldlen + + fieldlen = 1 + rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen) + if rsv != 0: + text = ['reserved bit error', 'rsv error', 'rsv'] + self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text) + fieldoff += fieldlen + + # TODO: Will input slot 0 have a Codec ID, or 3 reserved bits? + fieldlen = 2 + codec = self.get_bit_field(data, bitcount, fieldoff, fieldlen) + text = ['CODEC: {:1x}'.format(codec), '{:1x}'.format(codec)] + self.putf(slotpos + fieldoff, fieldlen, anncls, text) + fieldoff += fieldlen + + def handle_slot_01(self, slotidx, bitidx, bitcount, is_out, data): + # Handle slot 1, command/status address. + slotpos = self.frame_slot_lens[slotidx] + if not self.have_slots[is_out]: + return + if not self.have_slots[is_out][slotidx]: + return + fieldoff = 0 + anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG + anncls += slotidx + + fieldlen = 1 + if is_out: + is_read = self.get_bit_field(data, bitcount, fieldoff, fieldlen) + text = ['READ', 'RD', 'R'] if is_read else ['WRITE', 'WR', 'W'] + self.putf(slotpos + fieldoff, fieldlen, anncls, text) + # TODO: Check for the "atomic" constraint? Some operations + # involve address _and_ data, which cannot be spread across + # several frames. Slot 0 and 1 _must_ be provided within the + # same frame (the test should occur in the handler for slot + # 2 of course, in slot 1 we don't know what will follow). + else: + rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen) + if rsv != 0: + text = ['reserved bit error', 'rsv error', 'rsv'] + self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text) + fieldoff += fieldlen + + fieldlen = 7 + regaddr = self.get_bit_field(data, bitcount, fieldoff, fieldlen) + # TODO: Present 0-63 or 0-126 as the address of the 16bit register? + text = ['ADDR: {:2x}'.format(regaddr), '{:2x}'.format(regaddr)] + self.putf(slotpos + fieldoff, fieldlen, anncls, text) + if regaddr & 0x01: + text = ['odd register address', 'odd reg addr', 'odd addr', 'odd'] + self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text) + fieldoff += fieldlen + + # Strictly speaking there are 10 data request bits and 2 reserved + # bits for input slots, and 12 reserved bits for output slots. We + # test for 10 and 2 bits, to simplify the logic. Only in case of + # non-zero reserved bits for outputs this will result in "a little + # strange" an annotation. This is a cosmetic issue, we don't mind. + fieldlen = 10 + reqdata = self.get_bit_field(data, bitcount, fieldoff, fieldlen) + if is_out and reqdata != 0: + text = ['reserved bit error', 'rsv error', 'rsv'] + self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text) + if not is_out: + text = ['REQ: {:3x}'.format(reqdata), '{:3x}'.format(reqdata)] + self.putf(slotpos + fieldoff, fieldlen, anncls, text) + fieldoff += fieldlen + + fieldlen = 2 + rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen) + if rsv != 0: + text = ['reserved bit error', 'rsv error', 'rsv'] + self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text) + fieldoff += fieldlen + + def handle_slot_02(self, slotidx, bitidx, bitcount, is_out, data): + # Handle slot 2, command/status data. + slotpos = self.frame_slot_lens[slotidx] + if not self.have_slots[is_out]: + return + if not self.have_slots[is_out][slotidx]: + return + fieldoff = 0 + anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG + anncls += slotidx + + fieldlen = 16 + rwdata = self.get_bit_field(data, bitcount, fieldoff, fieldlen) + # TODO: Check for zero output data when the operation is a read. + # TODO: Check for the "atomic" constraint. + text = ['DATA: {:4x}'.format(rwdata), '{:4x}'.format(rwdata)] + self.putf(slotpos + fieldoff, fieldlen, anncls, text) + fieldoff += fieldlen + + fieldlen = 4 + rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen) + if rsv != 0: + text = ['reserved bits error', 'rsv error', 'rsv'] + self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text) + fieldoff += fieldlen + + # TODO: Implement other slots. + # - 1: cmd/status addr (check status vs command) + # - 2: cmd/status data (check status vs command) + # - 3-11: audio out/in + # - 12: io control/status (modem GPIO(?)) + + def handle_slot(self, slotidx, data_out, data_in): + # Process a received slot of a frame. + func = self.handle_slots.get(slotidx, self.handle_slot_dummy) + bitidx = self.frame_slot_lens[slotidx] + bitcount = self.frame_slot_lens[slotidx + 1] - bitidx + if data_out is not None: + func(slotidx, bitidx, bitcount, True, data_out) + if data_in is not None: + func(slotidx, bitidx, bitcount, False, data_in) + + def handle_bits(self, ss, es, bit_out, bit_in): + # Process a received pair of bits. + # Emit the bits' annotations. Only interpret the data when we + # are in a frame (have seen the start of the frame, and don't + # exceed the expected number of bits in a frame). + if bit_out is not None: + self.putx(ss, es, Ann.BITS_OUT, ['{:d}'.format(bit_out)]) + if bit_in is not None: + self.putx(ss, es, Ann.BITS_IN, ['{:d}'.format(bit_in)]) + if self.frame_ss_list is None: + return + self.frame_ss_list.append(es) + have_len = len(self.frame_ss_list) - 1 + if have_len > self.frame_total_bits: + return + + # Accumulate the bits within the frame, until one slot of the + # frame has become available. + slot_idx = 0 + if bit_out is not None: + self.frame_bits_out.append(bit_out) + slot_idx = len(self.frame_slot_data_out) + if bit_in is not None: + self.frame_bits_in.append(bit_in) + slot_idx = len(self.frame_slot_data_in) + want_len = self.frame_slot_lens[slot_idx + 1] + if have_len != want_len: + return + prev_len = self.frame_slot_lens[slot_idx] + + # Convert bits to integer values. This shall simplify extraction + # of bit fields in multiple other locations. + slot_data_out = None + if bit_out is not None: + slot_bits = self.frame_bits_out[prev_len:] + slot_data = self.bits_to_int(slot_bits) + self.frame_slot_data_out.append(slot_data) + slot_data_out = slot_data + slot_data_in = None + if bit_in is not None: + slot_bits = self.frame_bits_in[prev_len:] + slot_data = self.bits_to_int(slot_bits) + self.frame_slot_data_in.append(slot_data) + slot_data_in = slot_data + + # Emit simple annotations for the integer values, until upper + # layer decode stages will be implemented. + slot_len = have_len - prev_len + slot_ss = self.frame_ss_list[prev_len] + slot_es = self.frame_ss_list[have_len] + if slot_data_out is not None: + slot_text = self.int_to_nibble_text(slot_data_out, slot_len) + self.putx(slot_ss, slot_es, Ann.SLOT_OUT_RAW, [slot_text]) + if slot_data_in is not None: + slot_text = self.int_to_nibble_text(slot_data_in, slot_len) + self.putx(slot_ss, slot_es, Ann.SLOT_IN_RAW, [slot_text]) + + self.handle_slot(slot_idx, slot_data_out, slot_data_in) + + def decode(self): + have_sdo = self.has_channel(Pins.SDATA_OUT) + have_sdi = self.has_channel(Pins.SDATA_IN) + if not have_sdo and not have_sdi: + raise ChannelError('Either SDATA_OUT or SDATA_IN (or both) are required.') + have_reset = self.has_channel(Pins.RESET) + + # Data is sampled at falling CLK edges. Annotations need to span + # the period between rising edges. SYNC rises one cycle _before_ + # the start of a frame. Grab the earliest SYNC sample we can get + # and advance to the start of a bit time. Then keep getting the + # samples and the end of all subsequent bit times. + prev_sync = [None, None, None] + (sync, bit_clk, sdata_out, sdata_in, reset) = self.wait({Pins.BIT_CLK: 'e'}) + if bit_clk == 0: + prev_sync[-1] = sync + (sync, bit_clk, sdata_out, sdata_in, reset) = self.wait({Pins.BIT_CLK: 'r'}) + bit_ss = self.samplenum + while True: + (sync, bit_clk, sdata_out, sdata_in, reset) = self.wait({Pins.BIT_CLK: 'f'}) + prev_sync.pop(0) + prev_sync.append(sync) + self.wait({Pins.BIT_CLK: 'r'}) + if prev_sync[0] == 0 and prev_sync[1] == 1: + self.start_frame(bit_ss) + self.handle_bits(bit_ss, self.samplenum, + sdata_out if have_sdo else None, + sdata_in if have_sdi else None) + bit_ss = self.samplenum diff --git a/libsigrokdecode4DSL/decoders/ade77xx/__init__.py b/libsigrokdecode4DSL/decoders/ade77xx/__init__.py old mode 100644 new mode 100755 diff --git a/libsigrokdecode4DSL/decoders/ade77xx/lists.py b/libsigrokdecode4DSL/decoders/ade77xx/lists.py old mode 100644 new mode 100755 diff --git a/libsigrokdecode4DSL/decoders/ade77xx/pd.py b/libsigrokdecode4DSL/decoders/ade77xx/pd.py old mode 100644 new mode 100755 index 053575e8..5a24a25e --- a/libsigrokdecode4DSL/decoders/ade77xx/pd.py +++ b/libsigrokdecode4DSL/decoders/ade77xx/pd.py @@ -26,14 +26,15 @@ import sigrokdecode as srd from .lists import * class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'ade77xx' name = 'ADE77xx' longname = 'Analog Devices ADE77xx' desc = 'Poly phase multifunction energy metering IC protocol.' license = 'mit' inputs = ['spi'] - outputs = ['ade77xx'] + outputs = [] + tags = ['Analog/digital', 'IC', 'Sensor'] annotations = ( ('read', 'Register read commands'), ('write', 'Register write commands'), @@ -45,14 +46,17 @@ class Decoder(srd.Decoder): ('warnings', 'Warnings', (2,)), ) - def reset(self): + def reset_data(self): self.expected = 0 self.mosi_bytes, self.miso_bytes = [], [] def __init__(self): - self.ss_cmd, self.es_cmd = 0, 0 self.reset() + def reset(self): + self.ss_cmd, self.es_cmd = 0, 0 + self.reset_data() + def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) @@ -77,7 +81,7 @@ class Decoder(srd.Decoder): idx = 1 if write else 0 self.putx([idx, ['%s: %s' % (rblob[0], "SHORT")]]) self.put_warn([self.ss_cmd, es], "Short transfer!") - self.reset() + self.reset_data() return # Don't care about anything else. @@ -124,4 +128,4 @@ class Decoder(srd.Decoder): else: self.putx([0, ['%s: %#x' % (rblob[0], vali)]]) - self.reset() + self.reset_data() diff --git a/libsigrokdecode4DSL/decoders/adf435x/__init__.py b/libsigrokdecode4DSL/decoders/adf435x/__init__.py old mode 100644 new mode 100755 diff --git a/libsigrokdecode4DSL/decoders/adf435x/pd.py b/libsigrokdecode4DSL/decoders/adf435x/pd.py old mode 100644 new mode 100755 index 8f51ee24..f6c6e6e0 --- a/libsigrokdecode4DSL/decoders/adf435x/pd.py +++ b/libsigrokdecode4DSL/decoders/adf435x/pd.py @@ -88,14 +88,15 @@ regs = { ANN_REG = 0 class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'adf435x' name = 'ADF435x' longname = 'Analog Devices ADF4350/1' desc = 'Wideband synthesizer with integrated VCO.' license = 'gplv3+' inputs = ['spi'] - outputs = ['adf435x'] + outputs = [] + tags = ['Clock/timing', 'IC', 'Wireless/RF'] annotations = ( # Sent from the host to the chip. ('register', 'Register written to the device'), @@ -105,6 +106,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.bits = [] def start(self): diff --git a/libsigrokdecode4DSL/decoders/adns5020/__init__.py b/libsigrokdecode4DSL/decoders/adns5020/__init__.py index a0cc63c8..e519da44 100755 --- a/libsigrokdecode4DSL/decoders/adns5020/__init__.py +++ b/libsigrokdecode4DSL/decoders/adns5020/__init__.py @@ -14,13 +14,14 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' This decoder stacks on top of the 'spi' PD and decodes ADNS-5020 optical mouse -sensor commands and data. Use MOSI for the SDIO shared line. +sensor commands and data. + +Use MOSI for the SDIO shared line. ''' from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/adns5020/pd.py b/libsigrokdecode4DSL/decoders/adns5020/pd.py index 96521ec3..9ac778e0 100755 --- a/libsigrokdecode4DSL/decoders/adns5020/pd.py +++ b/libsigrokdecode4DSL/decoders/adns5020/pd.py @@ -14,44 +14,44 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd regs = { - 0: 'Product_ID', - 1: 'Revision_ID', - 2: 'Motion', - 3: 'Delta_X', - 4: 'Delta_Y', - 5: 'SQUAL', - 6: 'Shutter_Upper', - 7: 'Shutter_Lower', - 8: 'Maximum_Pixel', - 9: 'Pixel_Sum', - 0xa: 'Minimum_Pixel', - 0xb: 'Pixel_Grab', - 0xd: 'Mouse_Control', - 0x3a: 'Chip_Reset', - 0x3f: 'Inv_Rev_ID', - 0x63: 'Motion_Burst', + 0: 'Product_ID', + 1: 'Revision_ID', + 2: 'Motion', + 3: 'Delta_X', + 4: 'Delta_Y', + 5: 'SQUAL', + 6: 'Shutter_Upper', + 7: 'Shutter_Lower', + 8: 'Maximum_Pixel', + 9: 'Pixel_Sum', + 0xa: 'Minimum_Pixel', + 0xb: 'Pixel_Grab', + 0xd: 'Mouse_Control', + 0x3a: 'Chip_Reset', + 0x3f: 'Inv_Rev_ID', + 0x63: 'Motion_Burst', } class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'adns5020' name = 'ADNS-5020' - longname = 'Avago ADNS-5020 optical mouse sensor' - desc = 'Bidirectional command and data over an SPI-like protocol.' + longname = 'Avago ADNS-5020' + desc = 'Bidirectional optical mouse sensor protocol.' license = 'gplv2+' inputs = ['spi'] - outputs = ['adns5020'] + outputs = [] + tags = ['IC', 'PC', 'Sensor'] annotations = ( - ('106', 'read', 'Register read commands'), - ('108', 'write', 'Register write commands'), - ('0', 'warning', 'Warnings'), + ('read', 'Register read commands'), + ('write', 'Register write commands'), + ('warning', 'Warnings'), ) annotation_rows = ( ('read', 'Read', (0,)), @@ -60,6 +60,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.ss_cmd, self.es_cmd = 0, 0 self.mosi_bytes = [] diff --git a/libsigrokdecode4DSL/decoders/am230x/__init__.py b/libsigrokdecode4DSL/decoders/am230x/__init__.py index 51b84887..280c8856 100755 --- a/libsigrokdecode4DSL/decoders/am230x/__init__.py +++ b/libsigrokdecode4DSL/decoders/am230x/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/am230x/pd.py b/libsigrokdecode4DSL/decoders/am230x/pd.py index af8a2c4c..fbc68d39 100755 --- a/libsigrokdecode4DSL/decoders/am230x/pd.py +++ b/libsigrokdecode4DSL/decoders/am230x/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -35,14 +34,15 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'am230x' - name = 'AM230x/DHTxx/RHTxx' + name = 'AM230x' longname = 'Aosong AM230x/DHTxx/RHTxx' - desc = 'Aosong AM230x/DHTxx/RHTxx humidity/temperature sensor protocol.' + desc = 'Aosong AM230x/DHTxx/RHTxx humidity/temperature sensor.' license = 'gplv2+' inputs = ['logic'] - outputs = ['am230x'] + outputs = [] + tags = ['IC', 'Sensor'] channels = ( {'id': 'sda', 'name': 'SDA', 'desc': 'Single wire serial data line'}, ) @@ -51,14 +51,14 @@ class Decoder(srd.Decoder): 'default': 'am230x', 'values': ('am230x/rht', 'dht11')}, ) annotations = ( - ('7', 'start', 'Start'), - ('5', 'response', 'Response'), - ('107', 'bit', 'Bit'), - ('3', 'end', 'End'), - ('106', 'byte', 'Byte'), - ('108', 'humidity', 'Relative humidity in percent'), - ('109', 'temperature', 'Temperature in degrees Celsius'), - ('112', 'checksum', 'Checksum'), + ('start', 'Start'), + ('response', 'Response'), + ('bit', 'Bit'), + ('end', 'End'), + ('byte', 'Byte'), + ('humidity', 'Relative humidity in percent'), + ('temperature', 'Temperature in degrees Celsius'), + ('checksum', 'Checksum'), ) annotation_rows = ( ('bits', 'Bits', (0, 1, 2, 3)), @@ -75,9 +75,8 @@ class Decoder(srd.Decoder): def putv(self, data): self.put(self.bytepos[-2], self.samplenum, self.out_ann, data) - def reset(self): + def reset_variables(self): self.state = 'WAIT FOR START LOW' - self.samplenum = 0 self.fall = 0 self.rise = 0 self.bits = [] @@ -124,9 +123,12 @@ class Decoder(srd.Decoder): return checksum % 256 def __init__(self): - self.samplerate = None self.reset() + def reset(self): + self.samplerate = None + self.reset_variables() + def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) @@ -164,73 +166,64 @@ class Decoder(srd.Decoder): self.state = 'WAIT FOR END' self.bytepos.append(self.samplenum) - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - for (self.samplenum, (sda,)) in data: - data.itercnt += 1 + while True: # State machine. if self.state == 'WAIT FOR START LOW': - if sda != 0: - continue + self.wait({0: 'f'}) self.fall = self.samplenum self.state = 'WAIT FOR START HIGH' elif self.state == 'WAIT FOR START HIGH': - if sda != 1: - continue + self.wait({0: 'r'}) if self.is_valid('START LOW'): self.rise = self.samplenum self.state = 'WAIT FOR RESPONSE LOW' else: - self.reset() + self.reset_variables() elif self.state == 'WAIT FOR RESPONSE LOW': - if sda != 0: - continue + self.wait({0: 'f'}) if self.is_valid('START HIGH'): self.putfs([0, ['Start', 'S']]) self.fall = self.samplenum self.state = 'WAIT FOR RESPONSE HIGH' else: - self.reset() + self.reset_variables() elif self.state == 'WAIT FOR RESPONSE HIGH': - if sda != 1: - continue + self.wait({0: 'r'}) if self.is_valid('RESPONSE LOW'): self.rise = self.samplenum self.state = 'WAIT FOR FIRST BIT' else: - self.reset() + self.reset_variables() elif self.state == 'WAIT FOR FIRST BIT': - if sda != 0: - continue + self.wait({0: 'f'}) if self.is_valid('RESPONSE HIGH'): self.putfs([1, ['Response', 'R']]) self.fall = self.samplenum self.bytepos.append(self.samplenum) self.state = 'WAIT FOR BIT HIGH' else: - self.reset() + self.reset_variables() elif self.state == 'WAIT FOR BIT HIGH': - if sda != 1: - continue + self.wait({0: 'r'}) if self.is_valid('BIT LOW'): self.rise = self.samplenum self.state = 'WAIT FOR BIT LOW' else: - self.reset() + self.reset_variables() elif self.state == 'WAIT FOR BIT LOW': - if sda != 0: - continue + self.wait({0: 'f'}) if self.is_valid('BIT 0 HIGH'): bit = 0 elif self.is_valid('BIT 1 HIGH'): bit = 1 else: - self.reset() + self.reset_variables() continue self.handle_byte(bit) elif self.state == 'WAIT FOR END': - if sda != 1: - continue + self.wait({0: 'r'}) self.putfs([3, ['End', 'E']]) - self.reset() + self.reset_variables() diff --git a/libsigrokdecode4DSL/decoders/arm_etmv3/__init__.py b/libsigrokdecode4DSL/decoders/arm_etmv3/__init__.py index 7955139a..617063ca 100755 --- a/libsigrokdecode4DSL/decoders/arm_etmv3/__init__.py +++ b/libsigrokdecode4DSL/decoders/arm_etmv3/__init__.py @@ -14,12 +14,11 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' -This decoder stacks on top of the 'uart' decoder and decodes packets of +This decoder stacks on top of the 'uart' PD and decodes packets of the ARMv7m Embedded Trace Macroblock v3.x. ''' diff --git a/libsigrokdecode4DSL/decoders/arm_etmv3/pd.py b/libsigrokdecode4DSL/decoders/arm_etmv3/pd.py index 367ceb8a..6649b46e 100755 --- a/libsigrokdecode4DSL/decoders/arm_etmv3/pd.py +++ b/libsigrokdecode4DSL/decoders/arm_etmv3/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -32,30 +31,30 @@ exc_names = [ for i in range(8, 496): exc_names.append('IRQ%d' % i) -def parse_varint(bytes): +def parse_varint(bytes_): '''Parse an integer where the top bit is the continuation bit. Returns value and number of parsed bytes.''' v = 0 - for i, b in enumerate(bytes): + for i, b in enumerate(bytes_): v |= (b & 0x7F) << (i * 7) if b & 0x80 == 0: return v, i+1 - return v, len(bytes) + return v, len(bytes_) -def parse_uint(bytes): +def parse_uint(bytes_): '''Parse little-endian integer.''' v = 0 - for i, b in enumerate(bytes): + for i, b in enumerate(bytes_): v |= b << (i * 8) return v -def parse_exc_info(bytes): +def parse_exc_info(bytes_): '''Parse exception information bytes from a branch packet.''' - if len(bytes) < 1: + if len(bytes_) < 1: return None - excv, exclen = parse_varint(bytes) - if bytes[exclen - 1] & 0x80 != 0x00: + excv, exclen = parse_varint(bytes_) + if bytes_[exclen - 1] & 0x80 != 0x00: return None # Exception info not complete. if exclen == 2 and excv & (1 << 13): @@ -70,21 +69,21 @@ def parse_exc_info(bytes): resume = (excv >> 14) & 0x0F return (ns, exc, cancel, altisa, hyp, resume) -def parse_branch_addr(bytes, ref_addr, cpu_state, branch_enc): +def parse_branch_addr(bytes_, ref_addr, cpu_state, branch_enc): '''Parse encoded branch address. Returns addr, addrlen, cpu_state, exc_info. Returns None if packet is not yet complete''' - addr, addrlen = parse_varint(bytes) + addr, addrlen = parse_varint(bytes_) - if bytes[addrlen-1] & 0x80 != 0x00: + if bytes_[addrlen - 1] & 0x80 != 0x00: return None # Branch address not complete. addr_bits = 7 * addrlen have_exc_info = False if branch_enc == 'original': - if addrlen == 5 and bytes[4] & 0x40: + if addrlen == 5 and bytes_[4] & 0x40: have_exc_info = True elif branch_enc == 'alternative': addr_bits -= 1 # Top bit of address indicates exc_info. @@ -94,20 +93,20 @@ def parse_branch_addr(bytes, ref_addr, cpu_state, branch_enc): exc_info = None if have_exc_info: - exc_info = parse_exc_info(bytes[addrlen:]) + exc_info = parse_exc_info(bytes_[addrlen:]) if exc_info is None: return None # Exception info not complete. if addrlen == 5: # Possible change in CPU state. - if bytes[4] & 0xB8 == 0x08: + if bytes_[4] & 0xB8 == 0x08: cpu_state = 'arm' - elif bytes[4] & 0xB0 == 0x10: + elif bytes_[4] & 0xB0 == 0x10: cpu_state = 'thumb' - elif bytes[4] & 0xA0 == 0x20: + elif bytes_[4] & 0xA0 == 0x20: cpu_state = 'jazelle' else: - raise NotImplementedError('Unhandled branch byte 4: 0x%02x' % bytes[4]) + raise NotImplementedError('Unhandled branch byte 4: 0x%02x' % bytes_[4]) # Shift the address according to current CPU state. if cpu_state == 'arm': @@ -128,14 +127,15 @@ def parse_branch_addr(bytes, ref_addr, cpu_state, branch_enc): return addr, addrlen, cpu_state, exc_info class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'arm_etmv3' name = 'ARM ETMv3' - longname = 'ARM Embedded Trace Macroblock' - desc = 'Decode ETM instruction trace packets.' + longname = 'ARM Embedded Trace Macroblock v3' + desc = 'ARM ETM v3 instruction trace protocol.' license = 'gplv2+' inputs = ['uart'] - outputs = ['arm_etmv3'] + outputs = [] + tags = ['Debug/trace'] annotations = ( ('trace', 'Trace info'), ('branch', 'Branches'), @@ -171,6 +171,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.buf = [] self.syncbuf = [] self.prevsample = 0 diff --git a/libsigrokdecode4DSL/decoders/arm_itm/__init__.py b/libsigrokdecode4DSL/decoders/arm_itm/__init__.py index d9789a49..1733d36d 100755 --- a/libsigrokdecode4DSL/decoders/arm_itm/__init__.py +++ b/libsigrokdecode4DSL/decoders/arm_itm/__init__.py @@ -14,12 +14,11 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' -This decoder stacks on top of the 'uart' or 'arm_tpiu' PD and decodes the +This decoder stacks on top of the 'uart' or 'arm_tpiu' PD and decodes the ARM Cortex-M processor trace data from Instrumentation Trace Macroblock. ''' diff --git a/libsigrokdecode4DSL/decoders/arm_itm/pd.py b/libsigrokdecode4DSL/decoders/arm_itm/pd.py index 384999c3..64149787 100755 --- a/libsigrokdecode4DSL/decoders/arm_itm/pd.py +++ b/libsigrokdecode4DSL/decoders/arm_itm/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -38,14 +37,15 @@ ARM_EXCEPTIONS = { } class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'arm_itm' name = 'ARM ITM' longname = 'ARM Instrumentation Trace Macroblock' - desc = 'Trace data from Cortex-M / ARMv7m ITM module.' + desc = 'ARM Cortex-M / ARMv7m ITM trace protocol.' license = 'gplv2+' inputs = ['uart'] - outputs = ['arm_itm'] + outputs = [] + tags = ['Debug/trace'] options = ( {'id': 'objdump', 'desc': 'objdump path', 'default': 'arm-none-eabi-objdump'}, @@ -81,6 +81,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.buf = [] self.syncbuf = [] self.swpackets = {} diff --git a/libsigrokdecode4DSL/decoders/arm_tpiu/__init__.py b/libsigrokdecode4DSL/decoders/arm_tpiu/__init__.py index 4958df86..ce9c3744 100755 --- a/libsigrokdecode4DSL/decoders/arm_tpiu/__init__.py +++ b/libsigrokdecode4DSL/decoders/arm_tpiu/__init__.py @@ -14,15 +14,15 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' -This decoder stacks on top of the 'uart' decoder and decodes the frame format -of ARMv7m Trace Port Interface Unit. It filters the data coming from various -trace sources (such as ARMv7m ITM and ETM blocks) into separate streams that -can be further decoded by other PDs. +This decoder stacks on top of the 'uart' PD and decodes the frame format +of ARMv7m Trace Port Interface Unit. + +It filters the data coming from various trace sources (such as ARMv7m ITM +and ETM blocks) into separate streams that can be further decoded by other PDs. ''' from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/arm_tpiu/pd.py b/libsigrokdecode4DSL/decoders/arm_tpiu/pd.py index cf5e8750..29b4605f 100755 --- a/libsigrokdecode4DSL/decoders/arm_tpiu/pd.py +++ b/libsigrokdecode4DSL/decoders/arm_tpiu/pd.py @@ -14,14 +14,13 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'arm_tpiu' name = 'ARM TPIU' longname = 'ARM Trace Port Interface Unit' @@ -29,6 +28,7 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['uart'] outputs = ['uart'] # Emulate uart output so that arm_itm/arm_etm can stack. + tags = ['Debug/trace'] options = ( {'id': 'stream', 'desc': 'Stream index', 'default': 1}, {'id': 'sync_offset', 'desc': 'Initial sync offset', 'default': 0}, @@ -43,11 +43,14 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.buf = [] self.syncbuf = [] self.prevsample = 0 self.stream = 0 - self.stream_ss = None + self.ss_stream = None self.bytenum = 0 def start(self): @@ -57,10 +60,10 @@ class Decoder(srd.Decoder): def stream_changed(self, ss, stream): if self.stream != stream: if self.stream != 0: - self.put(self.stream_ss, ss, self.out_ann, + self.put(self.ss_stream, ss, self.out_ann, [0, ['Stream %d' % self.stream, 'S%d' % self.stream]]) self.stream = stream - self.stream_ss = ss + self.ss_stream = ss def emit_byte(self, ss, es, byte): if self.stream == self.options['stream']: diff --git a/libsigrokdecode4DSL/decoders/atsha204a/__init__.py b/libsigrokdecode4DSL/decoders/atsha204a/__init__.py new file mode 100755 index 00000000..fd0f4288 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/atsha204a/__init__.py @@ -0,0 +1,30 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Michalis Pappas +## +## 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 2 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 . +## + +''' +This decoder stacks on top of the 'i2c' PD and decodes the +Microchip ATSHA204A and ATECC508A crypto authentication protocol. + +The decoder might also support the following devices (untested): + * ATSHA204 + * ATECC108 + * ATECC108A +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/atsha204a/pd.py b/libsigrokdecode4DSL/decoders/atsha204a/pd.py new file mode 100755 index 00000000..c666332a --- /dev/null +++ b/libsigrokdecode4DSL/decoders/atsha204a/pd.py @@ -0,0 +1,323 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Michalis Pappas +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd + +WORD_ADDR_RESET = 0x00 +WORD_ADDR_SLEEP = 0x01 +WORD_ADDR_IDLE = 0x02 +WORD_ADDR_COMMAND = 0x03 + +WORD_ADDR = {0x00: 'RESET', 0x01: 'SLEEP', 0x02: 'IDLE', 0x03: 'COMMAND'} + +OPCODE_COUNTER = 0x24 +OPCODE_DERIVE_KEY = 0x1c +OPCODE_DEV_REV = 0x30 +OPCODE_ECDH = 0x43 +OPCODE_GEN_DIG = 0x15 +OPCODE_GEN_KEY = 0x40 +OPCODE_HMAC = 0x11 +OPCODE_CHECK_MAC = 0x28 +OPCODE_LOCK = 0x17 +OPCODE_MAC = 0x08 +OPCODE_NONCE = 0x16 +OPCODE_PAUSE = 0x01 +OPCODE_PRIVWRITE = 0x46 +OPCODE_RANDOM = 0x1b +OPCODE_READ = 0x02 +OPCODE_SHA = 0x47 +OPCODE_SIGN = 0x41 +OPCODE_UPDATE_EXTRA = 0x20 +OPCODE_VERIFY = 0x45 +OPCODE_WRITE = 0x12 + +OPCODES = { + 0x01: 'Pause', + 0x02: 'Read', + 0x08: 'MAC', + 0x11: 'HMAC', + 0x12: 'Write', + 0x15: 'GenDig', + 0x16: 'Nonce', + 0x17: 'Lock', + 0x1b: 'Random', + 0x1c: 'DeriveKey', + 0x20: 'UpdateExtra', + 0x24: 'Counter', + 0x28: 'CheckMac', + 0x30: 'DevRev', + 0x40: 'GenKey', + 0x41: 'Sign', + 0x43: 'ECDH', + 0x45: 'Verify', + 0x46: 'PrivWrite', + 0x47: 'SHA', +} + +ZONE_CONFIG = 0x00 +ZONE_OTP = 0x01 +ZONE_DATA = 0x02 + +ZONES = {0x00: 'CONFIG', 0x01: 'OTP', 0x02: 'DATA'} + +STATUS_SUCCESS = 0x00 +STATUS_CHECKMAC_FAIL = 0x01 +STATUS_PARSE_ERROR = 0x03 +STATUS_EXECUTION_ERROR = 0x0f +STATUS_READY = 0x11 +STATUS_CRC_COMM_ERROR = 0xff + +STATUS = { + 0x00: 'Command success', + 0x01: 'Checkmac failure', + 0x03: 'Parse error', + 0x0f: 'Execution error', + 0x11: 'Ready', + 0xff: 'CRC / communications error', +} + +class Decoder(srd.Decoder): + api_version = 3 + id = 'atsha204a' + name = 'ATSHA204A' + longname = 'Microchip ATSHA204A' + desc = 'Microchip ATSHA204A family crypto authentication protocol.' + license = 'gplv2+' + inputs = ['i2c'] + outputs = [] + tags = ['Security/crypto', 'IC', 'Memory'] + annotations = ( + ('waddr', 'Word address'), + ('count', 'Count'), + ('opcode', 'Opcode'), + ('param1', 'Param1'), + ('param2', 'Param2'), + ('data', 'Data'), + ('crc', 'CRC'), + ('status', 'Status'), + ('warning', 'Warning'), + ) + annotation_rows = ( + ('frame', 'Frame', (0, 1, 2, 3, 4, 5, 6)), + ('status', 'Status', (7,)), + ('warnings', 'Warnings', (8,)), + ) + + def __init__(self): + self.reset() + + def reset(self): + self.state = 'IDLE' + self.waddr = self.opcode = -1 + self.ss_block = self.es_block = 0 + self.bytes = [] + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + + def output_tx_bytes(self): + b = self.bytes + if len(b) < 1: # Ignore wakeup. + return + self.waddr = b[0][2] + self.put_waddr(b[0]) + if self.waddr == WORD_ADDR_COMMAND: + count = b[1][2] + self.put_count(b[1]) + if len(b) - 1 != count: + self.put_warning(b[0][0], b[-1][1], + 'Invalid frame length: Got {}, expecting {} '.format( + len(b) - 1, count)) + return + self.opcode = b[2][2] + self.put_opcode(b[2]) + self.put_param1(b[3]) + self.put_param2([b[4], b[5]]) + self.put_data(b[6:-2]) + self.put_crc([b[-2], b[-1]]) + + def output_rx_bytes(self): + b = self.bytes + count = b[0][2] + self.put_count(b[0]) + if self.waddr == WORD_ADDR_RESET: + self.put_data([b[1]]) + self.put_crc([b[2], b[3]]) + self.put_status(b[0][0], b[-1][1], b[1][2]) + elif self.waddr == WORD_ADDR_COMMAND: + if count == 4: # Status / Error. + self.put_data([b[1]]) + self.put_crc([b[2], b[3]]) + self.put_status(b[0][0], b[-1][1], b[1][2]) + else: + self.put_data(b[1:-2]) + self.put_crc([b[-2], b[-1]]) + + def putx(self, s, data): + self.put(s[0], s[1], self.out_ann, data) + + def puty(self, s, data): + self.put(s[0][0], s[1][1], self.out_ann, data) + + def putz(self, ss, es, data): + self.put(ss, es, self.out_ann, data) + + def put_waddr(self, s): + self.putx(s, [0, ['Word addr: %s' % WORD_ADDR[s[2]]]]) + + def put_count(self, s): + self.putx(s, [1, ['Count: %s' % s[2]]]) + + def put_opcode(self, s): + self.putx(s, [2, ['Opcode: %s' % OPCODES[s[2]]]]) + + def put_param1(self, s): + op = self.opcode + if op in (OPCODE_CHECK_MAC, OPCODE_COUNTER, OPCODE_DEV_REV, \ + OPCODE_ECDH, OPCODE_GEN_KEY, OPCODE_HMAC, OPCODE_MAC, \ + OPCODE_NONCE, OPCODE_RANDOM, OPCODE_SHA, OPCODE_SIGN, \ + OPCODE_VERIFY): + self.putx(s, [3, ['Mode: %02X' % s[2]]]) + elif op == OPCODE_DERIVE_KEY: + self.putx(s, [3, ['Random: %s' % s[2]]]) + elif op == OPCODE_PRIVWRITE: + self.putx(s, [3, ['Encrypted: {}'.format('Yes' if s[2] & 0x40 else 'No')]]) + elif op == OPCODE_GEN_DIG: + self.putx(s, [3, ['Zone: %s' % ZONES[s[2]]]]) + elif op == OPCODE_LOCK: + self.putx(s, [3, ['Zone: {}, Summary: {}'.format( + 'DATA/OTP' if s[2] else 'CONFIG', + 'Ignored' if s[2] & 0x80 else 'Used')]]) + elif op == OPCODE_PAUSE: + self.putx(s, [3, ['Selector: %02X' % s[2]]]) + elif op == OPCODE_READ: + self.putx(s, [3, ['Zone: {}, Length: {}'.format(ZONES[s[2] & 0x03], + '32 bytes' if s[2] & 0x90 else '4 bytes')]]) + elif op == OPCODE_WRITE: + self.putx(s, [3, ['Zone: {}, Encrypted: {}, Length: {}'.format(ZONES[s[2] & 0x03], + 'Yes' if s[2] & 0x40 else 'No', '32 bytes' if s[2] & 0x90 else '4 bytes')]]) + else: + self.putx(s, [3, ['Param1: %02X' % s[2]]]) + + def put_param2(self, s): + op = self.opcode + if op == OPCODE_DERIVE_KEY: + self.puty(s, [4, ['TargetKey: {:02x} {:02x}'.format(s[1][2], s[0][2])]]) + elif op in (OPCODE_COUNTER, OPCODE_ECDH, OPCODE_GEN_KEY, OPCODE_PRIVWRITE, \ + OPCODE_SIGN, OPCODE_VERIFY): + self.puty(s, [4, ['KeyID: {:02x} {:02x}'.format(s[1][2], s[0][2])]]) + elif op in (OPCODE_NONCE, OPCODE_PAUSE, OPCODE_RANDOM): + self.puty(s, [4, ['Zero: {:02x} {:02x}'.format(s[1][2], s[0][2])]]) + elif op in (OPCODE_HMAC, OPCODE_MAC, OPCODE_CHECK_MAC, OPCODE_GEN_DIG): + self.puty(s, [4, ['SlotID: {:02x} {:02x}'.format(s[1][2], s[0][2])]]) + elif op == OPCODE_LOCK: + self.puty(s, [4, ['Summary: {:02x} {:02x}'.format(s[1][2], s[0][2])]]) + elif op in (OPCODE_READ, OPCODE_WRITE): + self.puty(s, [4, ['Address: {:02x} {:02x}'.format(s[1][2], s[0][2])]]) + elif op == OPCODE_UPDATE_EXTRA: + self.puty(s, [4, ['NewValue: {:02x}'.format(s[0][2])]]) + else: + self.puty(s, [4, ['-']]) + + def put_data(self, s): + if len(s) == 0: + return + op = self.opcode + if op == OPCODE_CHECK_MAC: + self.putz(s[0][0], s[31][1], [5, ['ClientChal: %s' % ' '.join(format(i[2], '02x') for i in s[0:32])]]) + self.putz(s[32][0], s[63][1], [5, ['ClientResp: %s' % ' '.join(format(i[2], '02x') for i in s[32:64])]]) + self.putz(s[64][0], s[76][1], [5, ['OtherData: %s' % ' '.join(format(i[2], '02x') for i in s[64:77])]]) + elif op == OPCODE_DERIVE_KEY: + self.putz(s[0][0], s[31][1], [5, ['MAC: %s' % ' '.join(format(i[2], '02x') for i in s)]]) + elif op == OPCODE_ECDH: + self.putz(s[0][0], s[31][1], [5, ['Pub X: %s' % ' '.join(format(i[2], '02x') for i in s[0:32])]]) + self.putz(s[32][0], s[63][1], [5, ['Pub Y: %s' % ' '.join(format(i[2], '02x') for i in s[32:64])]]) + elif op in (OPCODE_GEN_DIG, OPCODE_GEN_KEY): + self.putz(s[0][0], s[3][1], [5, ['OtherData: %s' % ' '.join(format(i[2], '02x') for i in s)]]) + elif op == OPCODE_MAC: + self.putz(s[0][0], s[31][1], [5, ['Challenge: %s' % ' '.join(format(i[2], '02x') for i in s)]]) + elif op == OPCODE_PRIVWRITE: + if len(s) > 36: # Key + MAC. + self.putz(s[0][0], s[-35][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]]) + self.putz(s[-32][0], s[-1][1], [5, ['MAC: %s' % ' '.join(format(i[2], '02x') for i in s)]]) + else: # Just value. + self.putz(s[0][0], s[-1][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]]) + elif op == OPCODE_VERIFY: + if len(s) >= 64: # ECDSA components (always present) + self.putz(s[0][0], s[31][1], [5, ['ECDSA R: %s' % ' '.join(format(i[2], '02x') for i in s[0:32])]]) + self.putz(s[32][0], s[63][1], [5, ['ECDSA S: %s' % ' '.join(format(i[2], '02x') for i in s[32:64])]]) + if len(s) == 83: # OtherData (follow ECDSA components in validate / invalidate mode) + self.putz(s[64][0], s[82][1], [5, ['OtherData: %s' % ' '.join(format(i[2], '02x') for i in s[64:83])]]) + if len(s) == 128: # Public key components (follow ECDSA components in external mode) + self.putz(s[64][0], s[95][1], [5, ['Pub X: %s' % ' '.join(format(i[2], '02x') for i in s[64:96])]]) + self.putz(s[96][0], s[127][1], [5, ['Pub Y: %s' % ' '.join(format(i[2], '02x') for i in s[96:128])]]) + elif op == OPCODE_WRITE: + if len(s) > 32: # Value + MAC. + self.putz(s[0][0], s[-31][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]]) + self.putz(s[-32][0], s[-1][1], [5, ['MAC: %s' % ' '.join(format(i[2], '02x') for i in s)]]) + else: # Just value. + self.putz(s[0][0], s[-1][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]]) + else: + self.putz(s[0][0], s[-1][1], [5, ['Data: %s' % ' '.join(format(i[2], '02x') for i in s)]]) + + def put_crc(self, s): + self.puty(s, [6, ['CRC: {:02X} {:02X}'.format(s[0][2], s[1][2])]]) + + def put_status(self, ss, es, status): + self.putz(ss, es, [7, ['Status: %s' % STATUS[status]]]) + + def put_warning(self, ss, es, msg): + self.putz(ss, es, [8, ['Warning: %s' % msg]]) + + def decode(self, ss, es, data): + cmd, databyte = data + # State machine. + if self.state == 'IDLE': + # Wait for an I²C START condition. + if cmd != 'START': + return + self.state = 'GET SLAVE ADDR' + self.ss_block = ss + elif self.state == 'GET SLAVE ADDR': + # Wait for an address read/write operation. + if cmd == 'ADDRESS READ': + self.state = 'READ REGS' + elif cmd == 'ADDRESS WRITE': + self.state = 'WRITE REGS' + elif self.state == 'READ REGS': + if cmd == 'DATA READ': + self.bytes.append([ss, es, databyte]) + elif cmd == 'STOP': + self.es_block = es + # Reset the opcode before received data, as this causes + # responses to be displayed incorrectly. + self.opcode = -1 + if len(self.bytes) > 0: + self.output_rx_bytes() + self.waddr = -1 + self.bytes = [] + self.state = 'IDLE' + elif self.state == 'WRITE REGS': + if cmd == 'DATA WRITE': + self.bytes.append([ss, es, databyte]) + elif cmd == 'STOP': + self.es_block = es + self.output_tx_bytes() + self.bytes = [] + self.state = 'IDLE' diff --git a/libsigrokdecode4DSL/decoders/aud/__init__.py b/libsigrokdecode4DSL/decoders/aud/__init__.py index aaa31ff0..10b74234 100755 --- a/libsigrokdecode4DSL/decoders/aud/__init__.py +++ b/libsigrokdecode4DSL/decoders/aud/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/aud/pd.py b/libsigrokdecode4DSL/decoders/aud/pd.py index 98e9849d..ad93634f 100755 --- a/libsigrokdecode4DSL/decoders/aud/pd.py +++ b/libsigrokdecode4DSL/decoders/aud/pd.py @@ -2,6 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2016 fenugrec +## Copyright (C) 2019 DreamSourceLab ## ## 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 @@ -14,8 +15,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## # TODO: @@ -26,14 +26,15 @@ import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'aud' name = 'AUD' longname = 'Advanced User Debugger' desc = 'Renesas/Hitachi Advanced User Debugger (AUD) protocol.' license = 'gplv2+' inputs = ['logic'] - outputs = ['aud'] + outputs = [] + tags = ['Debug/trace'] channels = ( {'id': 'audck', 'name': 'AUDCK', 'desc': 'AUD clock'}, {'id': 'naudsync', 'name': 'nAUDSYNC', 'desc': 'AUD sync'}, @@ -47,12 +48,13 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.ncnt = 0 self.nmax = 0 self.addr = 0 self.lastaddr = 0 - self.samplenum = 0 - self.oldclk = 0 self.ss = 0 def start(self): @@ -61,15 +63,7 @@ class Decoder(srd.Decoder): def putx(self, data): self.put(self.ss, self.samplenum, self.out_ann, data) - def find_clk_edge(self, clk, sync, datapins): - # Ignore sample if there's no edge. - if clk == self.oldclk: - return - self.oldclk = clk - # Ignore falling edges. - if clk == 0: - return - + def handle_clk_edge(self, clk, sync, datapins): # Reconstruct nibble. nib = 0 for i in range(4): @@ -106,11 +100,8 @@ class Decoder(srd.Decoder): self.addr |= nib << (self.ncnt * 4) self.ncnt += 1 - def decode(self, ss, es, data): - for (self.samplenum, pins) in data: - print("1") - data.itercnt += 1 - clk = pins[0] - sync = pins[1] - d = pins[2:] - self.find_clk_edge(clk, sync, d) + def decode(self): + while True: + (clk, sync, d3, d2, d1, d0) = self.wait({0: 'r'}) + d = (d3, d2, d1, d0) + self.handle_clk_edge(clk, sync, d) diff --git a/libsigrokdecode4DSL/decoders/avr_isp/__init__.py b/libsigrokdecode4DSL/decoders/avr_isp/__init__.py index 75a9fe29..e3d90525 100755 --- a/libsigrokdecode4DSL/decoders/avr_isp/__init__.py +++ b/libsigrokdecode4DSL/decoders/avr_isp/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/avr_isp/parts.py b/libsigrokdecode4DSL/decoders/avr_isp/parts.py index 8e437b91..0767789a 100755 --- a/libsigrokdecode4DSL/decoders/avr_isp/parts.py +++ b/libsigrokdecode4DSL/decoders/avr_isp/parts.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## # Device code addresses: diff --git a/libsigrokdecode4DSL/decoders/avr_isp/pd.py b/libsigrokdecode4DSL/decoders/avr_isp/pd.py index 16e3d706..a0719b73 100755 --- a/libsigrokdecode4DSL/decoders/avr_isp/pd.py +++ b/libsigrokdecode4DSL/decoders/avr_isp/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -24,14 +23,15 @@ from .parts import * VENDOR_CODE_ATMEL = 0x1e class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'avr_isp' name = 'AVR ISP' longname = 'AVR In-System Programming' - desc = 'Protocol for in-system programming Atmel AVR MCUs.' + desc = 'Atmel AVR In-System Programming (ISP) protocol.' license = 'gplv2+' inputs = ['spi'] - outputs = ['avr_isp'] + outputs = [] + tags = ['Debug/trace'] annotations = ( ('pe', 'Programming enable'), ('rsb0', 'Read signature byte 0'), @@ -52,6 +52,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.mosi_bytes, self.miso_bytes = [], [] self.ss_cmd, self.es_cmd = 0, 0 diff --git a/libsigrokdecode4DSL/decoders/avr_pdi/__init__.py b/libsigrokdecode4DSL/decoders/avr_pdi/__init__.py new file mode 100755 index 00000000..1c61dea7 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/avr_pdi/__init__.py @@ -0,0 +1,42 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2016 Gerhard Sittig +## +## 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 2 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 . +## + +''' +PDI (Program and Debug Interface) is an Atmel proprietary interface for +external programming and on-chip debugging of the device. + +See the Atmel Application Note AVR1612 "PDI programming driver" and the +"Program and Debug Interface" section in the Xmega A manual for details. + +The protocol uses two pins: the RESET pin and one dedicated DATA pin. +The RESET pin provides a clock, the DATA pin communicates serial frames +with a start bit, eight data bits, an even parity bit, and two stop bits. +Data communication is bidirectional and half duplex, the device will +provide response data after reception of a respective request. + +Protocol frames communicate opcodes and their arguments, which provides +random and sequential access to the device's address space. By accessing +the registers of internal peripherals, especially the NVM controller, +it's possible to identify the device, read from and write to several +kinds of memory (signature rows, fuses and lock bits, internal flash and +EEPROM, memory mapped peripherals), and to control execution of software +on the device. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/avr_pdi/pd.py b/libsigrokdecode4DSL/decoders/avr_pdi/pd.py new file mode 100755 index 00000000..45705950 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/avr_pdi/pd.py @@ -0,0 +1,576 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2011-2014 Uwe Hermann +## Copyright (C) 2016 Gerhard Sittig +## Copyright (C) 2019 DreamSourceLab +## +## 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 2 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 . +## + +# Note the implementation details: +# +# Although the Atmel literature suggests (does not explicitly mandate, +# but shows in diagrams) that two stop bits are used in the protocol, +# the decoder loses synchronization with ATxmega generated responses +# when it expects more than one stop bit. Since the chip's hardware is +# fixed, this is not an implementation error in some programmer software. +# Since this is a protocol decoder which does not participate in the +# communication (does not actively send data), we can read the data +# stream with one stop bit, and transparently keep working when two +# are used. +# +# Annotations in the UART fields level differ from Atmel literature. +# Wrong parity bits are referred to as "parity error". Low stop bits are +# referred to as "frame error". +# +# The PDI component in the device starts disabled. Enabling PDI +# communication is done by raising DATA and clocking RESET with a +# minimum frequency. PDI communication automatically gets disabled when +# RESET "is inactive" for a certain period of time. The specific timing +# conditions are rather fuzzy in the literature (phrased weakly), and +# are device dependent (refer to the minumum RESET pulse width). This +# protocol decoder implementation internally prepares for but currently +# does not support these enable and disable phases. On the one hand it +# avoids excess external dependencies or wrong results for legal input +# data. On the other hand the decoder works when input streams start in +# the middle of an established connection. +# +# Communication peers detect physical collisions. The decoder can't. +# Upon collisions, a peer will cease any subsequent transmission, until +# a BREAK is seen. Synchronization can get enforced by sending two BREAK +# conditions. The first will cause a collision, the second will re-enable +# the peer. The decoder has no concept of physical collisions. It stops +# the interpretation of instructions when BREAK is seen, and assumes +# that a new instruction will start after BREAK. +# +# This protocol decoder only supports PDI communication over UART frames. +# It lacks support for PDI over JTAG. This would require separation into +# multiple protocol decoder layers (UART physical, JTAG physical, PDI +# instructions, optionally device support on top of PDI. There is some +# more potential for future extensions: +# - The JTAG physical has dedicated TX and RX directions. This decoder +# only picks up communicated bytes but does not check which "line" +# they are communicated on (not applicable to half duplex UART). +# - PDI over JTAG uses "special frame error" conditions to communicate +# additional symbols: BREAK (0xBB with parity 1), DELAY (0xDB with +# parity 1), and EMPTY (0xEB with parity 1). +# - Another "device support" layer might interpret device specific +# timings, and might map addresses used in memory access operations +# to component names, or even register names and bit fields(?). It's +# quite deep a rabbithole though... + +import sigrokdecode as srd +from collections import namedtuple + +class Ann: + '''Annotation and binary output classes.''' + ( + BIT, START, DATA, PARITY_OK, PARITY_ERR, + STOP_OK, STOP_ERR, BREAK, + OPCODE, DATA_PROG, DATA_DEV, PDI_BREAK, + ENABLE, DISABLE, COMMAND, + ) = range(15) + ( + BIN_BYTES, + ) = range(1) + +Bit = namedtuple('Bit', 'val ss es') + +class PDI: + '''PDI protocol instruction opcodes, and operand formats.''' + ( + OP_LDS, OP_LD, OP_STS, OP_ST, + OP_LDCS, OP_REPEAT, OP_STCS, OP_KEY, + ) = range(8) + pointer_format_nice = [ + '*(ptr)', + '*(ptr++)', + 'ptr', + 'ptr++ (rsv)', + ] + pointer_format_terse = [ + '*p', + '*p++', + 'p', + '(rsv)', + ] + ctrl_reg_name = { + 0: 'status', + 1: 'reset', + 2: 'ctrl', + } + +class Decoder(srd.Decoder): + api_version = 3 + id = 'avr_pdi' + name = 'AVR PDI' + longname = 'Atmel Program and Debug Interface' + desc = 'Atmel ATxmega Program and Debug Interface (PDI) protocol.' + license = 'gplv2+' + inputs = ['logic'] + outputs = [] + tags = ['Debug/trace'] + channels = ( + {'id': 'reset', 'name': 'RESET', 'desc': 'RESET / PDI_CLK'}, + {'id': 'data', 'name': 'DATA', 'desc': 'PDI_DATA'}, + ) + annotations = ( + ('uart-bit', 'UART bit'), + ('start-bit', 'Start bit'), + ('data-bit', 'Data bit'), + ('parity-ok', 'Parity OK bit'), + ('parity-err', 'Parity error bit'), + ('stop-ok', 'Stop OK bit'), + ('stop-err', 'Stop error bit'), + ('break', 'BREAK condition'), + ('opcode', 'Instruction opcode'), + ('data-prog', 'Programmer data'), + ('data-dev', 'Device data'), + ('pdi-break', 'BREAK at PDI level'), + ('enable', 'Enable PDI'), + ('disable', 'Disable PDI'), + ('cmd-data', 'PDI command with data'), + ) + annotation_rows = ( + ('uart_bits', 'UART bits', (Ann.BIT,)), + ('uart_fields', 'UART fields', (Ann.START, Ann.DATA, Ann.PARITY_OK, + Ann.PARITY_ERR, Ann.STOP_OK, Ann.STOP_ERR, Ann.BREAK)), + ('pdi_fields', 'PDI fields', (Ann.OPCODE, Ann.DATA_PROG, Ann.DATA_DEV, + Ann.PDI_BREAK)), + ('pdi_cmds', 'PDI Cmds', (Ann.ENABLE, Ann.DISABLE, Ann.COMMAND)), + ) + binary = ( + ('bytes', 'PDI protocol bytes'), + ) + + def __init__(self): + self.reset() + + def reset(self): + self.samplerate = None + self.clear_state() + + def clear_state(self): + # Track bit times and bit values. + self.ss_last_fall = None + self.data_sample = None + self.ss_curr_fall = None + # Collect UART frame bits into byte values. + self.bits = [] + self.zero_count = 0 + self.zero_ss = None + self.break_ss = None + self.break_es = None + self.clear_insn() + + def clear_insn(self): + # Collect instructions and their arguments, + # properties of the current instructions. + self.insn_rep_count = 0 + self.insn_opcode = None + self.insn_wr_counts = [] + self.insn_rd_counts = [] + # Accumulation of data items as bytes pass by. + self.insn_dat_bytes = [] + self.insn_dat_count = 0 + self.insn_ss_data = None + # Next layer "commands", instructions plus operands. + self.cmd_ss = None + self.cmd_insn_parts_nice = [] + self.cmd_insn_parts_terse = [] + + def metadata(self, key, value): + if key == srd.SRD_CONF_SAMPLERATE: + self.samplerate = value + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + self.out_binary = self.register(srd.OUTPUT_BINARY) + + def put_ann_bit(self, bit_nr, ann_idx): + b = self.bits[bit_nr] + self.put(b.ss, b.es, self.out_ann, [ann_idx, [str(b.val)]]) + + def put_ann_data(self, bit_nr, ann_data): + b = self.bits[bit_nr] + self.put(b.ss, b.es, self.out_ann, ann_data) + + def put_ann_row_val(self, ss, es, row, value): + self.put(ss, es, self.out_ann, [row, value]) + + def put_bin_bytes(self, ss, es, row, value): + self.put(ss, es, self.out_binary, [row, value]) + + def handle_byte(self, ss, es, byteval): + '''Handle a byte at the PDI protocol layer.''' + + # Handle BREAK conditions, which will abort any + # potentially currently executing instruction. + is_break = byteval is None + if is_break: + self.cmd_insn_parts_nice.append('BREAK') + self.cmd_insn_parts_terse.append('BRK') + self.insn_rep_count = 0 + # Will FALLTHROUGH to "end of instruction" below. + + # Decode instruction opcodes and argument sizes + # from the first byte of a transaction. + if self.insn_opcode is None and not is_break: + opcode = (byteval & 0xe0) >> 5 + arg30 = byteval & 0x0f + arg32 = (byteval & 0x0c) >> 2 + arg10 = byteval & 0x03 + self.insn_opcode = opcode + self.cmd_ss = ss + mnemonics = None + if opcode == PDI.OP_LDS: + # LDS: load data, direct addressing. + # Writes an address, reads a data item. + width_addr = arg32 + 1 + width_data = arg10 + 1 + self.insn_wr_counts = [width_addr] + self.insn_rd_counts = [width_data] + mnemonics = [ + 'Insn: LDS a{:d}, m{:d}'.format(width_addr, width_data), + 'LDS a{:d}, m{:d}'.format(width_addr, width_data), 'LDS', + ] + self.cmd_insn_parts_nice = ['LDS'] + self.cmd_insn_parts_terse = ['LDS'] + elif opcode == PDI.OP_LD: + # LD: load data, indirect addressing. + # Reads a data item, with optional repeat. + ptr_txt = PDI.pointer_format_nice[arg32] + ptr_txt_terse = PDI.pointer_format_terse[arg32] + width_data = arg10 + 1 + self.insn_wr_counts = [] + self.insn_rd_counts = [width_data] + if self.insn_rep_count: + self.insn_rd_counts.extend(self.insn_rep_count * [width_data]) + self.insn_rep_count = 0 + mnemonics = [ + 'Insn: LD {:s} m{:d}'.format(ptr_txt, width_data), + 'LD {:s} m{:d}'.format(ptr_txt, width_data), 'LD', + ] + self.cmd_insn_parts_nice = ['LD', ptr_txt] + self.cmd_insn_parts_terse = ['LD', ptr_txt_terse] + elif opcode == PDI.OP_STS: + # STS: store data, direct addressing. + # Writes an address, writes a data item. + width_addr = arg32 + 1 + width_data = arg10 + 1 + self.insn_wr_counts = [width_addr, width_data] + self.insn_rd_counts = [] + mnemonics = [ + 'Insn: STS a{:d}, i{:d}'.format(width_addr, width_data), + 'STS a{:d}, i{:d}'.format(width_addr, width_data), 'STS', + ] + self.cmd_insn_parts_nice = ['STS'] + self.cmd_insn_parts_terse = ['STS'] + elif opcode == PDI.OP_ST: + # ST: store data, indirect addressing. + # Writes a data item, with optional repeat. + ptr_txt = PDI.pointer_format_nice[arg32] + ptr_txt_terse = PDI.pointer_format_terse[arg32] + width_data = arg10 + 1 + self.insn_wr_counts = [width_data] + self.insn_rd_counts = [] + if self.insn_rep_count: + self.insn_wr_counts.extend(self.insn_rep_count * [width_data]) + self.insn_rep_count = 0 + mnemonics = [ + 'Insn: ST {:s} i{:d}'.format(ptr_txt, width_data), + 'ST {:s} i{:d}'.format(ptr_txt, width_data), 'ST', + ] + self.cmd_insn_parts_nice = ['ST', ptr_txt] + self.cmd_insn_parts_terse = ['ST', ptr_txt_terse] + elif opcode == PDI.OP_LDCS: + # LDCS: load control/status. + # Loads exactly one byte. + reg_num = arg30 + reg_txt = PDI.ctrl_reg_name.get(reg_num, 'r{:d}'.format(reg_num)) + reg_txt_terse = '{:d}'.format(reg_num) + self.insn_wr_counts = [] + self.insn_rd_counts = [1] + mnemonics = [ + 'Insn: LDCS {:s}, m1'.format(reg_txt), + 'LDCS {:s}, m1'.format(reg_txt), 'LDCS', + ] + self.cmd_insn_parts_nice = ['LDCS', reg_txt] + self.cmd_insn_parts_terse = ['LDCS', reg_txt_terse] + elif opcode == PDI.OP_STCS: + # STCS: store control/status. + # Writes exactly one byte. + reg_num = arg30 + reg_txt = PDI.ctrl_reg_name.get(reg_num, 'r{:d}'.format(reg_num)) + reg_txt_terse = '{:d}'.format(reg_num) + self.insn_wr_counts = [1] + self.insn_rd_counts = [] + mnemonics = [ + 'Insn: STCS {:s}, i1'.format(reg_txt), + 'STCS {:s}, i1'.format(reg_txt), 'STCS', + ] + self.cmd_insn_parts_nice = ['STCS', reg_txt] + self.cmd_insn_parts_terse = ['STCS', reg_txt_terse] + elif opcode == PDI.OP_REPEAT: + # REPEAT: sets repeat count for the next instruction. + # Reads repeat count from following bytes. + width_data = arg10 + 1 + self.insn_wr_counts = [width_data] + self.insn_rd_counts = [] + mnemonics = [ + 'Insn: REPEAT i{:d}'.format(width_data), + 'REPEAT i{:d}'.format(width_data), 'REP', + ] + self.cmd_insn_parts_nice = ['REPEAT'] + self.cmd_insn_parts_terse = ['REP'] + elif opcode == PDI.OP_KEY: + # KEY: set activation key (enables PDIBUS mmap access). + # Writes a sequence of 8 bytes, fixed length. + width_data = 8 + self.insn_wr_counts = [width_data] + self.insn_rd_counts = [] + mnemonics = [ + 'Insn: KEY i{:d}'.format(width_data), + 'KEY i{:d}'.format(width_data), 'KEY', + ] + self.cmd_insn_parts_nice = ['KEY'] + self.cmd_insn_parts_terse = ['KEY'] + + # Emit an annotation for the instruction opcode. + self.put_ann_row_val(ss, es, Ann.OPCODE, mnemonics) + + # Prepare to write/read operands/data bytes. + self.insn_dat_bytes = [] + if self.insn_wr_counts: + self.insn_dat_count = self.insn_wr_counts[0] + return + if self.insn_rd_counts: + self.insn_dat_count = self.insn_rd_counts[0] + return + # FALLTHROUGH. + # When there are no operands or data bytes to read, + # then fall through to the end of the instruction + # handling below (which emits annotations). + + # Read bytes which carry operands (addresses, immediates) + # or data values for memory access. + if self.insn_dat_count and not is_break: + + # Accumulate received bytes until another multi byte + # data item is complete. + if not self.insn_dat_bytes: + self.insn_ss_data = ss + self.insn_dat_bytes.append(byteval) + self.insn_dat_count -= 1 + if self.insn_dat_count: + return + + # Determine the data item's duration and direction, + # "consume" its length spec (to simplify later steps). + data_ss = self.insn_ss_data + data_es = es + if self.insn_wr_counts: + data_ann = Ann.DATA_PROG + data_width = self.insn_wr_counts.pop(0) + elif self.insn_rd_counts: + data_ann = Ann.DATA_DEV + data_width = self.insn_rd_counts.pop(0) + + # PDI communicates multi-byte data items in little endian + # order. Get a nice textual representation of the number, + # wide and narrow for several zoom levels. + self.insn_dat_bytes.reverse() + data_txt_digits = ''.join(['{:02x}'.format(b) for b in self.insn_dat_bytes]) + data_txt_hex = '0x' + data_txt_digits + data_txt_prefix = 'Data: ' + data_txt_hex + data_txts = [data_txt_prefix, data_txt_hex, data_txt_digits] + self.insn_dat_bytes = [] + + # Emit an annotation for the data value. + self.put_ann_row_val(data_ss, data_es, data_ann, data_txts) + + # Collect detailled information which describes the whole + # command when combined (for a next layer annotation, + # spanning the complete command). + self.cmd_insn_parts_nice.append(data_txt_hex) + self.cmd_insn_parts_terse.append(data_txt_digits) + + # Send out write data first until exhausted, + # then drain expected read data. + if self.insn_wr_counts: + self.insn_dat_count = self.insn_wr_counts[0] + return + if self.insn_rd_counts: + self.insn_dat_count = self.insn_rd_counts[0] + return + + # FALLTHROUGH. + # When all operands and data bytes were seen, + # terminate the inspection of the instruction. + + # Postprocess the instruction after its operands were seen. + cmd_es = es + cmd_txt_nice = ' '.join(self.cmd_insn_parts_nice) + cmd_txt_terse = ' '.join(self.cmd_insn_parts_terse) + cmd_txts = [cmd_txt_nice, cmd_txt_terse] + self.put_ann_row_val(self.cmd_ss, cmd_es, Ann.COMMAND, cmd_txts) + if self.insn_opcode == PDI.OP_REPEAT and not is_break: + # The last communicated data item is the repeat + # count for the next instruction (i.e. it will + # execute N+1 times when "REPEAT N" is specified). + count = int(self.cmd_insn_parts_nice[-1], 0) + self.insn_rep_count = count + + # Have the state for instruction decoding cleared, but make sure + # to carry over REPEAT count specs between instructions. They + # start out as zero, will be setup by REPEAT instructions, need + # to get passed to the instruction which follows REPEAT. The + # instruction which sees a non-zero repeat count which will + # consume the counter and drop it to zero, then the counter + # remains at zero until the next REPEAT instruction. + save_rep_count = self.insn_rep_count + self.clear_insn() + self.insn_rep_count = save_rep_count + + def handle_bits(self, ss, es, bitval): + '''Handle a bit at the UART layer.''' + + # Concentrate annotation literals here for easier maintenance. + ann_class_text = { + Ann.START: ['Start bit', 'Start', 'S'], + Ann.PARITY_OK: ['Parity OK', 'Par OK', 'P'], + Ann.PARITY_ERR: ['Parity error', 'Par ERR', 'PE'], + Ann.STOP_OK: ['Stop bit', 'Stop', 'T'], + Ann.STOP_ERR: ['Stop bit error', 'Stop ERR', 'TE'], + Ann.BREAK: ['Break condition', 'BREAK', 'BRK'], + } + def put_uart_field(bitpos, annclass): + self.put_ann_data(bitpos, [annclass, ann_class_text[annclass]]) + + # The number of bits which form one UART frame. Note that + # the decoder operates with only one stop bit. + frame_bitcount = 1 + 8 + 1 + 1 + + # Detect adjacent runs of all-zero bits. This is meant + # to cope when BREAK conditions appear at any arbitrary + # position, it need not be "aligned" to an UART frame. + if bitval == 1: + self.zero_count = 0 + elif bitval == 0: + if not self.zero_count: + self.zero_ss = ss + self.zero_count += 1 + if self.zero_count == frame_bitcount: + self.break_ss = self.zero_ss + + # BREAK conditions are _at_minimum_ the length of a UART frame, but + # can span an arbitrary number of bit times. Track the "end sample" + # value of the last low bit we have seen, and emit the annotation only + # after the line went idle (high) again. Pass BREAK to the upper layer + # as well. When the line is low, BREAK still is pending. When the line + # is high, the current bit cannot be START, thus return from here. + if self.break_ss is not None: + if bitval == '0': + self.break_es = es + return + self.put(self.break_ss, self.break_es, self.out_ann, + [Ann.BREAK, ann_class_text[Ann.BREAK]]) + self.handle_byte(self.break_ss, self.break_es, None) + self.break_ss = None + self.break_es = None + self.bits = [] + return + + # Ignore high bits when waiting for START. + if not self.bits and bitval == 1: + return + + # Store individual bits and their start/end sample numbers, + # until a complete frame was received. + self.bits.append(Bit(bitval, ss, es)) + if len(self.bits) < frame_bitcount: + return + + # Get individual fields of the UART frame. + bits_num = sum([b.val << pos for pos, b in enumerate(self.bits)]) + if False: + # This logic could detect BREAK conditions which are aligned to + # UART frames. Which was obsoleted by the above detection at + # arbitrary positions. The code still can be useful to detect + # "other kinds of frame errors" which carry valid symbols for + # upper layers (the Atmel literature suggests "break", "delay", + # and "empty" symbols when PDI is communicated over different + # physical layers). + if bits_num == 0: # BREAK + self.break_ss = self.bits[0].ss + self.break_es = es + self.bits = [] + return + start_bit = bits_num & 0x01; bits_num >>= 1 + data_val = bits_num & 0xff; bits_num >>= 8 + data_text = '{:02x}'.format(data_val) + parity_bit = bits_num & 0x01; bits_num >>= 1 + stop_bit = bits_num & 0x01; bits_num >>= 1 + + # Check for frame errors. START _must_ have been low + # according to the above accumulation logic. + parity_ok = (bin(data_val).count('1') + parity_bit) % 2 == 0 + stop_ok = stop_bit == 1 + valid_frame = parity_ok and stop_ok + + # Emit annotations. + for idx in range(frame_bitcount): + self.put_ann_bit(idx, Ann.BIT) + put_uart_field(0, Ann.START) + self.put(self.bits[1].ss, self.bits[8].es, self.out_ann, + [Ann.DATA, ['Data: ' + data_text, 'D: ' + data_text, data_text]]) + put_uart_field(9, Ann.PARITY_OK if parity_ok else Ann.PARITY_ERR) + put_uart_field(10, Ann.STOP_OK if stop_ok else Ann.STOP_ERR) + + # Emit binary data stream. Have bytes interpreted at higher layers. + if valid_frame: + byte_ss, byte_es = self.bits[0].ss, self.bits[-1].es + self.put_bin_bytes(byte_ss, byte_es, Ann.BIN_BYTES, bytes([data_val])) + self.handle_byte(byte_ss, byte_es, data_val) + + # Reset internal state for the next frame. + self.bits = [] + + def handle_clk_edge(self, clock_pin, data_pin): + # Sample the data line on rising clock edges. Always, for TX and for + # RX bytes alike. + if clock_pin == 1: + self.data_sample = data_pin + return + + # Falling clock edges are boundaries for bit slots. Inspect previously + # sampled bits on falling clock edges, when the start and end sample + # numbers were determined. Only inspect bit slots of known clock + # periods (avoid interpreting the DATA line when the "enabled" state + # has not yet been determined). + self.ss_last_fall = self.ss_curr_fall + self.ss_curr_fall = self.samplenum + if self.ss_last_fall is None: + return + + # Have the past bit slot processed. + bit_ss, bit_es = self.ss_last_fall, self.ss_curr_fall + bit_val = self.data_sample + self.handle_bits(bit_ss, bit_es, bit_val) + + def decode(self): + while True: + (clock_pin, data_pin) = self.wait({0: 'e'}) + self.handle_clk_edge(clock_pin, data_pin) diff --git a/libsigrokdecode4DSL/decoders/can/__init__.py b/libsigrokdecode4DSL/decoders/can/__init__.py index 60dc85f8..47f571d6 100755 --- a/libsigrokdecode4DSL/decoders/can/__init__.py +++ b/libsigrokdecode4DSL/decoders/can/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/can/pd.py b/libsigrokdecode4DSL/decoders/can/pd.py index 298bb57f..3e630ee1 100755 --- a/libsigrokdecode4DSL/decoders/can/pd.py +++ b/libsigrokdecode4DSL/decoders/can/pd.py @@ -2,6 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2012-2013 Uwe Hermann +## Copyright (C) 2019 DreamSourceLab ## ## 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 @@ -14,8 +15,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -24,14 +24,15 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'can' name = 'CAN' longname = 'Controller Area Network' desc = 'Field bus protocol for distributed realtime control.' license = 'gplv2+' inputs = ['logic'] - outputs = ['can'] + outputs = [] + tags = ['Automotive'] channels = ( {'id': 'can_rx', 'name': 'CAN RX', 'desc': 'CAN bus line'}, ) @@ -61,10 +62,14 @@ class Decoder(srd.Decoder): ) annotation_rows = ( ('bits', 'Bits', (15, 17)), - ('fields', 'Fields', tuple(range(15)) + (16,)), + ('fields', 'Fields', tuple(range(15))), + ('warnings', 'Warnings', (16,)), ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.reset_variables() @@ -75,11 +80,11 @@ class Decoder(srd.Decoder): if key == srd.SRD_CONF_SAMPLERATE: self.samplerate = value self.bit_width = float(self.samplerate) / float(self.options['bitrate']) - self.bitpos = (self.bit_width / 100.0) * self.options['sample_point'] + self.sample_point = (self.bit_width / 100.0) * self.options['sample_point'] # Generic helper for CAN bit annotations. def putg(self, ss, es, data): - left, right = int(self.bitpos), int(self.bit_width - self.bitpos) + left, right = int(self.sample_point), int(self.bit_width - self.sample_point) self.put(ss - left, es + right, self.out_ann, data) # Single-CAN-bit annotation using the current samplenum. @@ -105,16 +110,32 @@ class Decoder(srd.Decoder): self.ss_bit12 = None self.ss_databytebits = [] - # Return True if we reached the desired bit position, False otherwise. - def reached_bit(self, bitnum): - bitpos = int(self.sof + (self.bit_width * bitnum) + self.bitpos) - if self.samplenum >= bitpos: - return True - return False + # Poor man's clock synchronization. Use signal edges which change to + # dominant state in rather simple ways. This naive approach is neither + # aware of the SYNC phase's width nor the specific location of the edge, + # but improves the decoder's reliability when the input signal's bitrate + # does not exactly match the nominal rate. + def dom_edge_seen(self, force = False): + self.dom_edge_snum = self.samplenum + self.dom_edge_bcount = self.curbit + + def bit_sampled(self): + # EMPTY + pass + + # Determine the position of the next desired bit's sample point. + def get_sample_point(self, bitnum): + samplenum = self.dom_edge_snum + samplenum += int(self.bit_width * (bitnum - self.dom_edge_bcount)) + samplenum += int(self.sample_point) + return samplenum def is_stuff_bit(self): # CAN uses NRZ encoding and bit stuffing. # After 5 identical bits, a stuff bit of opposite value is added. + # But not in the CRC delimiter, ACK, and end of frame fields. + if len(self.bits) > self.last_databit + 17: + return False last_6_bits = self.rawbits[-6:] if last_6_bits not in ([0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 0]): return False @@ -155,6 +176,8 @@ class Decoder(srd.Decoder): elif bitnum == (self.last_databit + 16): self.putx([12, ['CRC delimiter: %d' % can_rx, 'CRC d: %d' % can_rx, 'CRC d']]) + if can_rx != 1: + self.putx([16, ['CRC delimiter must be a recessive bit']]) # ACK slot bit (dominant: ACK, recessive: NACK) elif bitnum == (self.last_databit + 17): @@ -165,6 +188,8 @@ class Decoder(srd.Decoder): elif bitnum == (self.last_databit + 18): self.putx([14, ['ACK delimiter: %d' % can_rx, 'ACK d: %d' % can_rx, 'ACK d']]) + if can_rx != 1: + self.putx([16, ['ACK delimiter must be a recessive bit']]) # Remember start of EOF (see below). elif bitnum == (self.last_databit + 19): @@ -173,6 +198,8 @@ class Decoder(srd.Decoder): # End of frame (EOF), 7 recessive bits elif bitnum == (self.last_databit + 25): self.putb([2, ['End of frame', 'EOF', 'E']]) + if self.rawbits[-7:] != [1, 1, 1, 1, 1, 1, 1]: + self.putb([16, ['End of frame (EOF) must be 7 recessive bits']]) self.reset_variables() return True @@ -204,6 +231,8 @@ class Decoder(srd.Decoder): self.putb([10, ['Data length code: %d' % self.dlc, 'DLC: %d' % self.dlc, 'DLC']]) self.last_databit = 18 + (self.dlc * 8) + if self.dlc > 8: + self.putb([16, ['Data length code (DLC) > 8 is not allowed']]) # Remember all databyte bits, except the very last one. elif bitnum in range(19, self.last_databit): @@ -318,9 +347,8 @@ class Decoder(srd.Decoder): # Bit 0: Start of frame (SOF) bit if bitnum == 0: - if can_rx == 0: - self.putx([1, ['Start of frame', 'SOF', 'S']]) - else: + self.putx([1, ['Start of frame', 'SOF', 'S']]) + if can_rx != 0: self.putx([16, ['Start of frame (SOF) must be a dominant bit']]) # Remember start of ID (see below). @@ -333,6 +361,8 @@ class Decoder(srd.Decoder): self.id = int(''.join(str(d) for d in self.bits[1:]), 2) s = '%d (0x%x)' % (self.id, self.id), self.putb([3, ['Identifier: %s' % s, 'ID: %s' % s, 'ID']]) + if (self.id & 0x7f0) == 0x7f0: + self.putb([16, ['Identifier bits 10..4 must not be all recessive']]) # RTR or SRR bit, depending on frame type (gets handled later). elif bitnum == 12: @@ -362,24 +392,24 @@ class Decoder(srd.Decoder): self.curbit += 1 - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - for (self.samplenum, pins) in data: - - (can_rx,) = pins - data.itercnt += 1 + while True: # State machine. if self.state == 'IDLE': # Wait for a dominant state (logic 0) on the bus. - if can_rx == 1: - continue + (can_rx,) = self.wait({0: 'l'}) self.sof = self.samplenum + self.dom_edge_seen(force = True) self.state = 'GET BITS' elif self.state == 'GET BITS': # Wait until we're in the correct bit/sampling position. - if not self.reached_bit(self.curbit): - continue - self.handle_bit(can_rx) - + pos = self.get_sample_point(self.curbit) + (can_rx,) = self.wait([{'skip': pos - self.samplenum}, {0: 'f'}]) + if (self.matched & (0b1 << 1)): + self.dom_edge_seen() + if (self.matched & (0b1 << 0)): + self.handle_bit(can_rx) + self.bit_sampled() diff --git a/libsigrokdecode4DSL/decoders/cec/__init__.py b/libsigrokdecode4DSL/decoders/cec/__init__.py new file mode 100755 index 00000000..4138b62b --- /dev/null +++ b/libsigrokdecode4DSL/decoders/cec/__init__.py @@ -0,0 +1,25 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Jorge Solla Rubiales +## +## 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 2 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 . +## + +''' +The Consumer Electronics Control (CEC) protocol allows users to command and +control devices connected through HDMI. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/cec/pd.py b/libsigrokdecode4DSL/decoders/cec/pd.py new file mode 100755 index 00000000..43b16a07 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/cec/pd.py @@ -0,0 +1,312 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Jorge Solla Rubiales +## Copyright (C) 2019 DreamSourceLab +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see . +## + +import sigrokdecode as srd +from .protocoldata import * + +# Pulse types +class Pulse: + INVALID, START, ZERO, ONE = range(4) + +# Protocol stats +class Stat: + WAIT_START, GET_BITS, WAIT_EOM, WAIT_ACK = range(4) + +# Pulse times in milliseconds +timing = { + Pulse.START: { + 'low': { 'min': 3.5, 'max': 3.9 }, + 'total': { 'min': 4.3, 'max': 4.7 } + }, + Pulse.ZERO: { + 'low': { 'min': 1.3, 'max': 1.7 }, + 'total': { 'min': 2.05, 'max': 2.75 } + }, + Pulse.ONE: { + 'low': { 'min': 0.4, 'max': 0.8 }, + 'total': { 'min': 2.05, 'max': 2.75 } + } +} + +class ChannelError(Exception): + pass + +class Decoder(srd.Decoder): + api_version = 3 + id = 'cec' + name = 'CEC' + longname = 'HDMI-CEC' + desc = 'HDMI Consumer Electronics Control (CEC) protocol.' + license = 'gplv2+' + inputs = ['logic'] + outputs = [] + tags = ['Display', 'PC'] + channels = ( + {'id': 'cec', 'name': 'CEC', 'desc': 'CEC bus data'}, + ) + annotations = ( + ('st', 'Start'), + ('eom-0', 'End of message'), + ('eom-1', 'Message continued'), + ('nack', 'ACK not set'), + ('ack', 'ACK set'), + ('bits', 'Bits'), + ('bytes', 'Bytes'), + ('frames', 'Frames'), + ('sections', 'Sections'), + ('warnings', 'Warnings') + ) + annotation_rows = ( + ('bits', 'Bits', (0, 1, 2, 3, 4, 5)), + ('bytes', 'Bytes', (6,)), + ('frames', 'Frames', (7,)), + ('sections', 'Sections', (8,)), + ('warnings', 'Warnings', (9,)) + ) + + def __init__(self): + self.reset() + + def precalculate(self): + # Restrict max length of ACK/NACK labels to 2 BIT pulses. + bit_time = timing[Pulse.ZERO]['total']['min'] * 2 + self.max_ack_len_samples = round((bit_time / 1000) * self.samplerate) + + def reset(self): + self.stat = Stat.WAIT_START + self.samplerate = None + self.fall_start = None + self.fall_end = None + self.rise = None + self.reset_frame_vars() + + def reset_frame_vars(self): + self.eom = None + self.bit_count = 0 + self.byte_count = 0 + self.byte = 0 + self.byte_start = None + self.frame_start = None + self.frame_end = None + self.is_nack = 0 + self.cmd_bytes = [] + + def metadata(self, key, value): + if key == srd.SRD_CONF_SAMPLERATE: + self.samplerate = value + self.precalculate() + + def handle_frame(self, is_nack): + if self.fall_start is None or self.fall_end is None: + return + + i = 0 + string = '' + while i < len(self.cmd_bytes): + string += '{:02x}'.format(self.cmd_bytes[i]['val']) + if i != (len(self.cmd_bytes) - 1): + string += ':' + i += 1 + + self.put(self.frame_start, self.frame_end, self.out_ann, [7, [string]]) + + i = 0 + operands = 0 + string = '' + while i < len(self.cmd_bytes): + if i == 0: # Parse header + (src, dst) = decode_header(self.cmd_bytes[i]['val']) + string = 'HDR: ' + src + ', ' + dst + elif i == 1: # Parse opcode + string += ' | OPC: ' + opcodes.get(self.cmd_bytes[i]['val'], 'Invalid') + else: # Parse operands + if operands == 0: + string += ' | OPS: ' + operands += 1 + string += '0x{:02x}'.format(self.cmd_bytes[i]['val']) + if i != len(self.cmd_bytes) - 1: + string += ', ' + i += 1 + + # Header only commands are PINGS + if i == 1: + string += ' | OPC: PING' if self.eom else ' | OPC: NONE. Aborted cmd' + + # Add extra information (ack of the command from the destination) + string += ' | R: NACK' if is_nack else ' | R: ACK' + + self.put(self.frame_start, self.frame_end, self.out_ann, [8, [string]]) + + def process(self): + zero_time = ((self.rise - self.fall_start) / self.samplerate) * 1000.0 + total_time = ((self.fall_end - self.fall_start) / self.samplerate) * 1000.0 + pulse = Pulse.INVALID + + # VALIDATION: Identify pulse based on length of the low period + for key in timing: + if zero_time >= timing[key]['low']['min'] and zero_time <= timing[key]['low']['max']: + pulse = key + break + + # VALIDATION: Invalid pulse + if pulse == Pulse.INVALID: + self.stat = Stat.WAIT_START + self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Invalid pulse: Wrong timing']]) + return + + # VALIDATION: If waiting for start, discard everything else + if self.stat == Stat.WAIT_START and pulse != Pulse.START: + self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Expected START: BIT found']]) + return + + # VALIDATION: If waiting for ACK or EOM, only BIT pulses (0/1) are expected + if (self.stat == Stat.WAIT_ACK or self.stat == Stat.WAIT_EOM) and pulse == Pulse.START: + self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Expected BIT: START received)']]) + self.stat = Stat.WAIT_START + + # VALIDATION: ACK bit pulse remains high till the next frame (if any): Validate only min time of the low period + if self.stat == Stat.WAIT_ACK and pulse != Pulse.START: + if total_time < timing[pulse]['total']['min']: + pulse = Pulse.INVALID + self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['ACK pulse below minimun time']]) + self.stat = Stat.WAIT_START + return + + # VALIDATION / PING FRAME DETECTION: Initiator doesn't sets the EOM = 1 but stops sending when ack doesn't arrive + if self.stat == Stat.GET_BITS and pulse == Pulse.START: + # Make sure we received a complete byte to consider it a valid ping + if self.bit_count == 0: + self.handle_frame(self.is_nack) + else: + self.put(self.frame_start, self.samplenum, self.out_ann, [9, ['ERROR: Incomplete byte received']]) + + # Set wait start so we receive next frame + self.stat = Stat.WAIT_START + + # VALIDATION: Check timing of the BIT (0/1) pulse in any other case (not waiting for ACK) + if self.stat != Stat.WAIT_ACK and pulse != Pulse.START: + if total_time < timing[pulse]['total']['min'] or total_time > timing[pulse]['total']['max']: + self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Bit pulse exceeds total pulse timespan']]) + pulse = Pulse.INVALID + self.stat = Stat.WAIT_START + return + + if pulse == Pulse.ZERO: + bit = 0 + elif pulse == Pulse.ONE: + bit = 1 + + # STATE: WAIT START + if self.stat == Stat.WAIT_START: + self.stat = Stat.GET_BITS + self.reset_frame_vars() + self.put(self.fall_start, self.fall_end, self.out_ann, [0, ['ST']]) + + # STATE: GET BITS + elif self.stat == Stat.GET_BITS: + # Reset stats on first bit + if self.bit_count == 0: + self.byte_start = self.fall_start + self.byte = 0 + + # If 1st byte of the datagram save its sample num + if len(self.cmd_bytes) == 0: + self.frame_start = self.fall_start + + self.byte += (bit << (7 - self.bit_count)) + self.bit_count += 1 + self.put(self.fall_start, self.fall_end, self.out_ann, [5, [str(bit)]]) + + if self.bit_count == 8: + self.bit_count = 0 + self.byte_count += 1 + self.stat = Stat.WAIT_EOM + self.put(self.byte_start, self.samplenum, self.out_ann, [6, ['0x{:02x}'.format(self.byte)]]) + self.cmd_bytes.append({'st': self.byte_start, 'ed': self.samplenum, 'val': self.byte}) + + # STATE: WAIT EOM + elif self.stat == Stat.WAIT_EOM: + self.eom = bit + self.frame_end = self.fall_end + + a = [2, ['EOM=Y']] if self.eom else [1, ['EOM=N']] + self.put(self.fall_start, self.fall_end, self.out_ann, a) + + self.stat = Stat.WAIT_ACK + + # STATE: WAIT ACK + elif self.stat == Stat.WAIT_ACK: + # If a frame with broadcast destination is being sent, the ACK is + # inverted: a 0 is considered a NACK, therefore we invert the value + # of the bit here, so we match the real meaning of it. + if (self.cmd_bytes[0]['val'] & 0x0F) == 0x0F: + bit = ~bit & 0x01 + + if (self.fall_end - self.fall_start) > self.max_ack_len_samples: + ann_end = self.fall_start + self.max_ack_len_samples + else: + ann_end = self.fall_end + + if bit: + # Any NACK detected in the frame is enough to consider the + # whole frame NACK'd. + self.is_nack = 1 + self.put(self.fall_start, ann_end, self.out_ann, [3, ['NACK']]) + else: + self.put(self.fall_start, ann_end, self.out_ann, [4, ['ACK']]) + + # After ACK bit, wait for new datagram or continue reading current + # one based on EOM value. + if self.eom or self.is_nack: + self.stat = Stat.WAIT_START + self.handle_frame(self.is_nack) + else: + self.stat = Stat.GET_BITS + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + + def decode(self): + if not self.samplerate: + raise SamplerateError('Cannot decode without samplerate.') + + # Wait for first falling edge. + self.wait({0: 'f'}) + self.fall_end = self.samplenum + + while True: + self.wait({0: 'r'}) + self.rise = self.samplenum + + if self.stat == Stat.WAIT_ACK: + self.wait([{0: 'f'}, {'skip': self.max_ack_len_samples}]) + else: + self.wait([{0: 'f'}]) + + self.fall_start = self.fall_end + self.fall_end = self.samplenum + self.process() + + # If there was a timeout while waiting for ACK: RESYNC. + # Note: This is an expected situation as no new falling edge will + # happen until next frame is transmitted. + if self.matched == 0b01: + self.wait({0: 'f'}) + self.fall_end = self.samplenum diff --git a/libsigrokdecode4DSL/decoders/cec/protocoldata.py b/libsigrokdecode4DSL/decoders/cec/protocoldata.py new file mode 100755 index 00000000..78c3b6f5 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/cec/protocoldata.py @@ -0,0 +1,117 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Jorge Solla Rubiales +## +## 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 2 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 . +## + +logical_adresses = [ + 'TV', + 'Recording_1', + 'Recording_2', + 'Tuner_1', + 'Playback_1', + 'AudioSystem', + 'Tuner2', + 'Tuner3', + 'Playback_2', + 'Recording_3', + 'Tuner_4', + 'Playback_3', + 'Backup_1', + 'Backup_2', + 'FreeUse', +] + +# List taken from LibCEC. +opcodes = { + 0x82: 'ACTIVE_SOURCE', + 0x04: 'IMAGE_VIEW_ON', + 0x0D: 'TEXT_VIEW_ON', + 0x9D: 'INACTIVE_SOURCE', + 0x85: 'REQUEST_ACTIVE_SOURCE', + 0x80: 'ROUTING_CHANGE', + 0x81: 'ROUTING_INFORMATION', + 0x86: 'SET_STREAM_PATH', + 0x36: 'STANDBY', + 0x0B: 'RECORD_OFF', + 0x09: 'RECORD_ON', + 0x0A: 'RECORD_STATUS', + 0x0F: 'RECORD_TV_SCREEN', + 0x33: 'CLEAR_ANALOGUE_TIMER', + 0x99: 'CLEAR_DIGITAL_TIMER', + 0xA1: 'CLEAR_EXTERNAL_TIMER', + 0x34: 'SET_ANALOGUE_TIMER', + 0x97: 'SET_DIGITAL_TIMER', + 0xA2: 'SET_EXTERNAL_TIMER', + 0x67: 'SET_TIMER_PROGRAM_TITLE', + 0x43: 'TIMER_CLEARED_STATUS', + 0x35: 'TIMER_STATUS', + 0x9E: 'CEC_VERSION', + 0x9F: 'GET_CEC_VERSION', + 0x83: 'GIVE_PHYSICAL_ADDRESS', + 0x91: 'GET_MENU_LANGUAGE', + 0x84: 'REPORT_PHYSICAL_ADDRESS', + 0x32: 'SET_MENU_LANGUAGE', + 0x42: 'DECK_CONTROL', + 0x1B: 'DECK_STATUS', + 0x1A: 'GIVE_DECK_STATUS', + 0x41: 'PLAY', + 0x08: 'GIVE_TUNER_DEVICE_STATUS', + 0x92: 'SELECT_ANALOGUE_SERVICE', + 0x93: 'SELECT_DIGITAL_SERVICE', + 0x07: 'TUNER_DEVICE_STATUS', + 0x06: 'TUNER_STEP_DECREMENT', + 0x05: 'TUNER_STEP_INCREMENT', + 0x87: 'DEVICE_VENDOR_ID', + 0x8C: 'GIVE_DEVICE_VENDOR_ID', + 0x89: 'VENDOR_COMMAND', + 0xA0: 'VENDOR_COMMAND_WITH_ID', + 0x8A: 'VENDOR_REMOTE_BUTTON_DOWN', + 0x8B: 'VENDOR_REMOTE_BUTTON_UP', + 0x64: 'SET_OSD_STRING', + 0x46: 'GIVE_OSD_NAME', + 0x47: 'SET_OSD_NAME', + 0x8D: 'MENU_REQUEST', + 0x8E: 'MENU_STATUS', + 0x44: 'USER_CONTROL_PRESSED', + 0x45: 'USER_CONTROL_RELEASE', + 0x8F: 'GIVE_DEVICE_POWER_STATUS', + 0x90: 'REPORT_POWER_STATUS', + 0x00: 'FEATURE_ABORT', + 0xFF: 'ABORT', + 0x71: 'GIVE_AUDIO_STATUS', + 0x7D: 'GIVE_SYSTEM_AUDIO_MODE_STATUS', + 0x7A: 'REPORT_AUDIO_STATUS', + 0x72: 'SET_SYSTEM_AUDIO_MODE', + 0x70: 'SYSTEM_AUDIO_MODE_REQUEST', + 0x7E: 'SYSTEM_AUDIO_MODE_STATUS', + 0x9A: 'SET_AUDIO_RATE', +} + +def resolve_logical_address(id_, is_initiator): + if id_ < 0 or id_ > 0x0F: + return 'Invalid' + + # Special handling of 0x0F. + if id_ == 0x0F: + return 'Unregistered' if is_initiator else 'Broadcast' + + return logical_adresses[id_] + +def decode_header(header): + src = (header & 0xF0) >> 4 + dst = (header & 0x0F) + return (resolve_logical_address(src, 1), resolve_logical_address(dst, 0)) diff --git a/libsigrokdecode4DSL/decoders/cfp/__init__.py b/libsigrokdecode4DSL/decoders/cfp/__init__.py new file mode 100755 index 00000000..351e893b --- /dev/null +++ b/libsigrokdecode4DSL/decoders/cfp/__init__.py @@ -0,0 +1,34 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Elias Oenal +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are met: +## +## 1. Redistributions of source code must retain the above copyright notice, +## this list of conditions and the following disclaimer. +## 2. Redistributions in binary form must reproduce the above copyright notice, +## this list of conditions and the following disclaimer in the documentation +## and/or other materials provided with the distribution. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +## POSSIBILITY OF SUCH DAMAGE. +## + +''' +This decoder stacks on top of the 'mdio' PD and decodes the CFP 100G +pluggable transceiver protocol. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/cfp/pd.py b/libsigrokdecode4DSL/decoders/cfp/pd.py new file mode 100755 index 00000000..9638ba19 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/cfp/pd.py @@ -0,0 +1,110 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Elias Oenal +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are met: +## +## 1. Redistributions of source code must retain the above copyright notice, +## this list of conditions and the following disclaimer. +## 2. Redistributions in binary form must reproduce the above copyright notice, +## this list of conditions and the following disclaimer in the documentation +## and/or other materials provided with the distribution. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +## POSSIBILITY OF SUCH DAMAGE. +## + +import sigrokdecode as srd + +MODULE_ID = { + 0x00: 'Unknown or unspecified', + 0x01: 'GBIC', + 0x02: 'Module/connector soldered to motherboard', + 0x03: 'SFP', + 0x04: '300 pin XSBI', + 0x05: 'XENPAK', + 0x06: 'XFP', + 0x07: 'XFF', + 0x08: 'XFP-E', + 0x09: 'XPAK', + 0x0a: 'X2', + 0x0B: 'DWDM-SFP', + 0x0C: 'QSFP', + 0x0D: 'QSFP+', + 0x0E: 'CFP', + 0x0F: 'CXP (TBD)', + 0x11: 'CFP2', + 0x12: 'CFP4', +} + +class Decoder(srd.Decoder): + api_version = 3 + id = 'cfp' + name = 'CFP' + longname = '100 Gigabit C form-factor pluggable' + desc = '100 Gigabit C form-factor pluggable (CFP) protocol.' + license = 'BSD' + inputs = ['mdio'] + outputs = [] + tags = ['Networking'] + annotations = ( + ('register', 'Register'), + ('decode', 'Decode'), + ) + annotation_rows = ( + ('registers', 'Registers', (0,)), + ('decodes', 'Decodes', (1,)), + ) + + def __init__(self): + self.reset() + + def reset(self): + pass + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + + def putx(self, data): + self.put(self.ss, self.es, self.out_ann, data) + + def decode(self, ss, es, data): + self.ss, self.es = ss, es + for (clause45, clause45_addr, is_read, portad, devad, reg) in data: + if not is_read: + continue + if clause45_addr in range(0x8000, 0x807F + 1): + self.putx([0, ['CFP NVR 1: Basic ID register', 'NVR1']]) + if clause45_addr == 0x8000: + self.putx([1, ['Module identifier: %s' % \ + MODULE_ID.get(reg, 'Reserved')]]) + elif clause45_addr in range(0x8080, 0x80FF + 1): + self.putx([0, ['CFP NVR 2: Extended ID register', 'NVR2']]) + elif clause45_addr in range(0x8100, 0x817F + 1): + self.putx([0, ['CFP NVR 3: Network lane specific register', 'NVR3']]) + elif clause45_addr in range(0x8180, 0x81FF + 1): + self.putx([0, ['CFP NVR 4', 'NVR4']]) + elif clause45_addr in range(0x8400, 0x847F + 1): + self.putx([0, ['Vendor NVR 1: Vendor data register', 'V-NVR1']]) + elif clause45_addr in range(0x8480, 0x84FF + 1): + self.putx([0, ['Vendor NVR 2: Vendor data register', 'V-NVR2']]) + elif clause45_addr in range(0x8800, 0x887F + 1): + self.putx([0, ['User NVR 1: User data register', 'U-NVR1']]) + elif clause45_addr in range(0x8880, 0x88FF + 1): + self.putx([0, ['User NVR 2: User data register', 'U-NVR2']]) + elif clause45_addr in range(0xA000, 0xA07F + 1): + self.putx([0, ['CFP Module VR 1: CFP Module level control and DDM register', 'Mod-VR1']]) + elif clause45_addr in range(0xA080, 0xA0FF + 1): + self.putx([0, ['MLG VR 1: MLG Management Interface register', 'MLG-VR1']]) diff --git a/libsigrokdecode4DSL/decoders/cjtag_oscan1/pd.py b/libsigrokdecode4DSL/decoders/cjtag_oscan1/pd.py deleted file mode 100755 index 251e6e3c..00000000 --- a/libsigrokdecode4DSL/decoders/cjtag_oscan1/pd.py +++ /dev/null @@ -1,298 +0,0 @@ -## -## Copyright (C) 2018 Sebastien Riou -## -## 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 2 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -## - -import sigrokdecode as srd - -jtag_states = [ - # Intro "tree" - 'TEST-LOGIC-RESET', 'RUN-TEST/IDLE', - # DR "tree" - 'SELECT-DR-SCAN', 'CAPTURE-DR', 'UPDATE-DR', 'PAUSE-DR', - 'SHIFT-DR', 'EXIT1-DR', 'EXIT2-DR', - # IR "tree" - 'SELECT-IR-SCAN', 'CAPTURE-IR', 'UPDATE-IR', 'PAUSE-IR', - 'SHIFT-IR', 'EXIT1-IR', 'EXIT2-IR', -] - -oscan1_phases = ['nTDI','TMS','TDO'] - -class Decoder(srd.Decoder): - api_version = 2 - id = 'cjtag_oscan1' - name = 'CJTAG OSCAN1' - longname = 'Joint Test Action Group (IEEE 1149.7 OSCAN1)' - desc = 'Protocol for testing, debugging, and flashing ICs.' - license = 'gplv2+' - inputs = ['logic'] - outputs = ['jtag'] - channels = ( - {'id': 'tck', 'name': 'TCK', 'desc': 'Test clock'}, - {'id': 'tms', 'name': 'TMS', 'desc': 'Test mode select'}, - ) - annotations = tuple([tuple([s.lower(), s]) for s in oscan1_phases]) + tuple([tuple([s.lower(), s]) for s in jtag_states]) - others = ( \ - ('bit-tdi', 'Bit (TDI)'), - ('bit-tdo', 'Bit (TDO)'), - ('bitstring-tdi', 'Bitstring (TDI)'), - ('bitstring-tdo', 'Bitstring (TDO)'), - ) - annotation_rows = ( - # ('bits-tdi', 'Bits (TDI)', (16,)), - # ('bits-tdo', 'Bits (TDO)', (17,)), - # ('bitstrings-tdi', 'Bitstring (TDI)', (18,)), - # ('bitstrings-tdo', 'Bitstring (TDO)', (19,)), - ('oscan1-phase', 'OSCAN1 phase', tuple(range(0,0+3)) ), - ('states', 'States', tuple(range(3,3+15+1))), - - ) - - def __init__(self): - self.state = 'RUN-TEST/IDLE' - self.phase = 'nTDI' - self.oldstate = None - self.oldpins = (-1, -1, -1, -1) - self.oldtck = -1 - self.bits_tdi = [] - self.bits_tdo = [] - self.bits_samplenums_tdi = [] - self.bits_samplenums_tdo = [] - self.samplenum = 0 - self.ss_item = self.es_item = None - self.ss_bitstring = self.es_bitstring = None - self.last_clock_samplenum = None - self.saved_item = None - self.first = True - self.first_bit = True - self.bits_cnt = 0 - self.data_ready = False - - def start(self): - self.out_python = self.register(srd.OUTPUT_PYTHON) - self.out_ann = self.register(srd.OUTPUT_ANN) - - def putx(self, data): - self.put(self.ss_item, self.es_item, self.out_ann, data) - - def putp(self, data): - self.put(self.ss_item, self.es_item, self.out_python, data) - - def putx_bs(self, data): - self.put(self.ss_bitstring, self.es_bitstring, self.out_ann, data) - - def putp_bs(self, data): - self.put(self.ss_bitstring, self.es_bitstring, self.out_python, data) - - def advance_state_machine(self, tms): - self.oldstate = self.state - - # Intro "tree" - if self.state == 'TEST-LOGIC-RESET': - # self.state = 'TEST-LOGIC-RESET' if (tms) else 'RUN-TEST/IDLE' - # if we reach this state we are not in OSCAN1 anymore. Since we don't handle anything else we stay in this state to show clearly the failure - self.state = 'TEST-LOGIC-RESET' - elif self.state == 'RUN-TEST/IDLE': - self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE' - - # DR "tree" - elif self.state == 'SELECT-DR-SCAN': - self.state = 'SELECT-IR-SCAN' if (tms) else 'CAPTURE-DR' - elif self.state == 'CAPTURE-DR': - self.state = 'EXIT1-DR' if (tms) else 'SHIFT-DR' - elif self.state == 'SHIFT-DR': - self.state = 'EXIT1-DR' if (tms) else 'SHIFT-DR' - elif self.state == 'EXIT1-DR': - self.state = 'UPDATE-DR' if (tms) else 'PAUSE-DR' - elif self.state == 'PAUSE-DR': - self.state = 'EXIT2-DR' if (tms) else 'PAUSE-DR' - elif self.state == 'EXIT2-DR': - self.state = 'UPDATE-DR' if (tms) else 'SHIFT-DR' - elif self.state == 'UPDATE-DR': - self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE' - - # IR "tree" - elif self.state == 'SELECT-IR-SCAN': - self.state = 'TEST-LOGIC-RESET' if (tms) else 'CAPTURE-IR' - elif self.state == 'CAPTURE-IR': - self.state = 'EXIT1-IR' if (tms) else 'SHIFT-IR' - elif self.state == 'SHIFT-IR': - self.state = 'EXIT1-IR' if (tms) else 'SHIFT-IR' - elif self.state == 'EXIT1-IR': - self.state = 'UPDATE-IR' if (tms) else 'PAUSE-IR' - elif self.state == 'PAUSE-IR': - self.state = 'EXIT2-IR' if (tms) else 'PAUSE-IR' - elif self.state == 'EXIT2-IR': - self.state = 'UPDATE-IR' if (tms) else 'SHIFT-IR' - elif self.state == 'UPDATE-IR': - self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE' - - def handle_rising_tck_edge(self, tck, tms): - - if self.phase == 'nTDI': - self.tdi = 1-tms - if self.first: - # Save the start sample and item for later (no output yet). - self.ss_item = self.samplenum - self.first = False - elif self.phase == 'TMS': - self.tms = tms - elif self.phase == 'TDO': - self.tdo = tms - self.advance_state_machine(self.tms) - - # Output the saved item (from the last CLK edge to the current). - self.es_item = self.samplenum - if self.ss_item is not None: - # Output the old state (from last rising TCK edge to current one). - self.putx([3+jtag_states.index(self.oldstate), [self.oldstate]]) - # self.putp(['NEW STATE', self.state]) - - self.ss_item = self.samplenum - - if 0: - # Upon SHIFT-IR/SHIFT-DR collect the current TDI/TDO values. - if self.state.startswith('SHIFT-'): - if self.bits_cnt > 0: - if self.bits_cnt == 1: - self.ss_bitstring = self.samplenum - - if self.bits_cnt > 1: - self.putx([16, [str(self.bits_tdi[0])]]) - self.putx([17, [str(self.bits_tdo[0])]]) - # Use self.samplenum as ES of the previous bit. - self.bits_samplenums_tdi[0][1] = self.samplenum - self.bits_samplenums_tdo[0][1] = self.samplenum - - self.bits_tdi.insert(0, tdi) - self.bits_tdo.insert(0, tdo) - - # Use self.samplenum as SS of the current bit. - self.bits_samplenums_tdi.insert(0, [self.samplenum, -1]) - self.bits_samplenums_tdo.insert(0, [self.samplenum, -1]) - - self.bits_cnt = self.bits_cnt + 1 - - # Output all TDI/TDO bits if we just switched from SHIFT-* to EXIT1-*. - if self.oldstate.startswith('SHIFT-') and \ - self.state.startswith('EXIT1-'): - - #self.es_bitstring = self.samplenum - if self.bits_cnt > 0: - if self.bits_cnt == 1: # Only shift one bit - self.ss_bitstring = self.samplenum - self.bits_tdi.insert(0, tdi) - self.bits_tdo.insert(0, tdo) - ## Use self.samplenum as SS of the current bit. - self.bits_samplenums_tdi.insert(0, [self.samplenum, -1]) - self.bits_samplenums_tdo.insert(0, [self.samplenum, -1]) - else: - ### ---------------------------------------------------------------- - self.putx([16, [str(self.bits_tdi[0])]]) - self.putx([17, [str(self.bits_tdo[0])]]) - ### Use self.samplenum as ES of the previous bit. - self.bits_samplenums_tdi[0][1] = self.samplenum - self.bits_samplenums_tdo[0][1] = self.samplenum - - self.bits_tdi.insert(0, tdi) - self.bits_tdo.insert(0, tdo) - - ## Use self.samplenum as SS of the current bit. - self.bits_samplenums_tdi.insert(0, [self.samplenum, -1]) - self.bits_samplenums_tdo.insert(0, [self.samplenum, -1]) - ## ---------------------------------------------------------------- - - self.data_ready = True - - self.first_bit = True - self.bits_cnt = 0 - if self.oldstate.startswith('EXIT'): - if self.data_ready: - self.data_ready = False - self.es_bitstring = self.samplenum - t = self.state[-2:] + ' TDI' - b = ''.join(map(str, self.bits_tdi)) - h = ' (0x%X' % int('0b' + b, 2) + ')' - s = t + ': ' + h + ', ' + str(len(self.bits_tdi)) + ' bits' #b + - self.putx_bs([18, [s]]) - self.bits_samplenums_tdi[0][1] = self.samplenum # ES of last bit. - self.putp_bs([t, [b, self.bits_samplenums_tdi]]) - self.putx([16, [str(self.bits_tdi[0])]]) # Last bit. - self.bits_tdi = [] - self.bits_samplenums_tdi = [] - - t = self.state[-2:] + ' TDO' - b = ''.join(map(str, self.bits_tdo)) - h = ' (0x%X' % int('0b' + b, 2) + ')' - s = t + ': ' + h + ', ' + str(len(self.bits_tdo)) + ' bits' #+ b - self.putx_bs([19, [s]]) - self.bits_samplenums_tdo[0][1] = self.samplenum # ES of last bit. - self.putp_bs([t, [b, self.bits_samplenums_tdo]]) - self.putx([17, [str(self.bits_tdo[0])]]) # Last bit. - self.bits_tdo = [] - self.bits_samplenums_tdo = [] - - - def handle_falling_tck_edge(self, tck, tms): - - if self.phase == 'nTDI': - next_phase = 'TMS' - elif self.phase == 'TMS': - next_phase = 'TDO' - elif self.phase == 'TDO': - next_phase = 'nTDI' - - if self.last_clock_samplenum is not None: - self.put(self.last_clock_samplenum, self.samplenum, self.out_ann, [0+oscan1_phases.index(self.phase), [self.phase]]) - - self.last_clock_samplenum = self.samplenum - self.phase = next_phase - - def decode(self, ss, es, logic): - for (self.samplenum, pins) in logic: - logic.logic_mask = 0b11 - logic.cur_pos = self.samplenum - logic.edge_index = -1 - - if self.last_clock_samplenum is None: - self.last_clock_samplenum = self.samplenum - elif self.last_clock_samplenum >= self.samplenum: - continue - - # If none of the pins changed, there's nothing to do. - if self.oldpins == pins: - continue - - # Store current pin values for the next round. - self.oldpins = pins - - # Get individual pin values into local variables. - # Unused channels will have a value of > 1. - ( tck, tms) = pins - - # We only care about TCK edges (either rising or falling). - if (self.oldtck == tck): - continue - - # Store start/end sample for later usage. - self.ss, self.es = ss, es - - if (self.oldtck == 0 and tck == 1): - self.handle_rising_tck_edge( tck, tms) - elif (self.oldtck == 1 and tck == 0): - self.handle_falling_tck_edge( tck, tms) - - self.oldtck = tck diff --git a/libsigrokdecode4DSL/decoders/common/__init__.py b/libsigrokdecode4DSL/decoders/common/__init__.py new file mode 100755 index 00000000..2a0beb50 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/common/__init__.py @@ -0,0 +1,19 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2016 Uwe Hermann +## +## 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 2 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 . +## + diff --git a/libsigrokdecode4DSL/decoders/common/plugtrx/__init__.py b/libsigrokdecode4DSL/decoders/common/plugtrx/__init__.py new file mode 100755 index 00000000..8dd0822b --- /dev/null +++ b/libsigrokdecode4DSL/decoders/common/plugtrx/__init__.py @@ -0,0 +1,20 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2016 Bert Vermeulen +## +## 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 . +## + +from .mod import * diff --git a/libsigrokdecode4DSL/decoders/common/plugtrx/mod.py b/libsigrokdecode4DSL/decoders/common/plugtrx/mod.py new file mode 100755 index 00000000..3d1b66dd --- /dev/null +++ b/libsigrokdecode4DSL/decoders/common/plugtrx/mod.py @@ -0,0 +1,192 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2016 Bert Vermeulen +## +## 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 . +## + +# This module contains definitions for use by pluggable network adapters, +# such as SFP, XFP etc. + +MODULE_ID = { + 0x01: 'GBIC', + 0x02: 'Integrated module/connector', + 0x03: 'SFP', + 0x04: '300-pin XBI', + 0x05: 'XENPAK', + 0x06: 'XFP', + 0x07: 'XFF', + 0x08: 'XFP-E', + 0x09: 'XPAK', + 0x0a: 'X2', +} + +ALARM_THRESHOLDS = { + 0: 'Temp high alarm', + 2: 'Temp low alarm', + 4: 'Temp high warning', + 6: 'Temp low warning', + 16: 'Bias high alarm', + 18: 'Bias low alarm', + 20: 'Bias high warning', + 22: 'Bias low warning', + 24: 'TX power high alarm', + 26: 'TX power low alarm', + 28: 'TX power high warning', + 30: 'TX power low warning', + 32: 'RX power high alarm', + 34: 'RX power low alarm', + 36: 'RX power high warning', + 38: 'RX power low warning', + 40: 'AUX 1 high alarm', + 42: 'AUX 1 low alarm', + 44: 'AUX 1 high warning', + 46: 'AUX 1 low warning', + 48: 'AUX 2 high alarm', + 50: 'AUX 2 low alarm', + 52: 'AUX 2 high warning', + 54: 'AUX 2 low warning', +} + +AD_READOUTS = { + 0: 'Module temperature', + 4: 'TX bias current', + 6: 'Measured TX output power', + 8: 'Measured RX input power', + 10: 'AUX 1 measurement', + 12: 'AUX 2 measurement', +} + +GCS_BITS = [ + 'TX disable', + 'Soft TX disable', + 'MOD_NR', + 'P_Down', + 'Soft P_Down', + 'Interrupt', + 'RX_LOS', + 'Data_Not_Ready', + 'TX_NR', + 'TX_Fault', + 'TX_CDR not locked', + 'RX_NR', + 'RX_CDR not locked', +] + +CONNECTOR = { + 0x01: 'SC', + 0x02: 'Fibre Channel style 1 copper', + 0x03: 'Fibre Channel style 2 copper', + 0x04: 'BNC/TNC', + 0x05: 'Fibre Channel coax', + 0x06: 'FiberJack', + 0x07: 'LC', + 0x08: 'MT-RJ', + 0x09: 'MU', + 0x0a: 'SG', + 0x0b: 'Optical pigtail', + 0x20: 'HSSDC II', + 0x21: 'Copper pigtail', +} + +TRANSCEIVER = [ + # 10GB Ethernet + ['10GBASE-SR', '10GBASE-LR', '10GBASE-ER', '10GBASE-LRM', '10GBASE-SW', + '10GBASE-LW', '10GBASE-EW'], + # 10GB Fibre Channel + ['1200-MX-SN-I', '1200-SM-LL-L', 'Extended Reach 1550 nm', + 'Intermediate reach 1300 nm FP'], + # 10GB Copper + [], + # 10GB low speed + ['1000BASE-SX / 1xFC MMF', '1000BASE-LX / 1xFC SMF', '2xFC MMF', + '2xFC SMF', 'OC48-SR', 'OC48-IR', 'OC48-LR'], + # 10GB SONET/SDH interconnect + ['I-64.1r', 'I-64.1', 'I-64.2r', 'I-64.2', 'I-64.3', 'I-64.5'], + # 10GB SONET/SDH short haul + ['S-64.1', 'S-64.2a', 'S-64.2b', 'S-64.3a', 'S-64.3b', 'S-64.5a', 'S-64.5b'], + # 10GB SONET/SDH long haul + ['L-64.1', 'L-64.2a', 'L-64.2b', 'L-64.2c', 'L-64.3', 'G.959.1 P1L1-2D2'], + # 10GB SONET/SDH very long haul + ['V-64.2a', 'V-64.2b', 'V-64.3'], +] + +SERIAL_ENCODING = [ + '64B/66B', + '8B/10B', + 'SONET scrambled', + 'NRZ', + 'RZ', +] + +XMIT_TECH = [ + '850 nm VCSEL', + '1310 nm VCSEL', + '1550 nm VCSEL', + '1310 nm FP', + '1310 nm DFB', + '1550 nm DFB', + '1310 nm EML' + '1550 nm EML' + 'copper', +] + +CDR = [ + '9.95Gb/s', + '10.3Gb/s', + '10.5Gb/s', + '10.7Gb/s', + '11.1Gb/s', + '(unknown)', + 'lineside loopback mode', + 'XFI loopback mode', +] + +DEVICE_TECH = [ + ['no wavelength control', 'sctive wavelength control'], + ['uncooled transmitter device', 'cooled transmitter'], + ['PIN detector', 'APD detector'], + ['transmitter not tunable', 'transmitter tunable'], +] + +ENHANCED_OPTS = [ + 'VPS', + 'soft TX_DISABLE', + 'soft P_Down', + 'VPS LV regulator mode', + 'VPS bypassed regulator mode', + 'active FEC control', + 'wavelength tunability', + 'CMU', +] + +AUX_TYPES = [ + 'not implemented', + 'APD bias voltage', + '(unknown)', + 'TEC current', + 'laser temperature', + 'laser wavelength', + '5V supply voltage', + '3.3V supply voltage', + '1.8V supply voltage', + '-5.2V supply voltage', + '5V supply current', + '(unknown)', + '(unknown)', + '3.3V supply current', + '1.8V supply current', + '-5.2V supply current', +] diff --git a/libsigrokdecode4DSL/decoders/common/sdcard/__init__.py b/libsigrokdecode4DSL/decoders/common/sdcard/__init__.py new file mode 100755 index 00000000..fb323856 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/common/sdcard/__init__.py @@ -0,0 +1,20 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2012-2014 Uwe Hermann +## +## 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 2 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 . +## + +from .mod import * diff --git a/libsigrokdecode4DSL/decoders/sdcard_sd/lists.py b/libsigrokdecode4DSL/decoders/common/sdcard/mod.py similarity index 92% rename from libsigrokdecode4DSL/decoders/sdcard_sd/lists.py rename to libsigrokdecode4DSL/decoders/common/sdcard/mod.py index 479c9f3f..cc9d70d8 100755 --- a/libsigrokdecode4DSL/decoders/sdcard_sd/lists.py +++ b/libsigrokdecode4DSL/decoders/common/sdcard/mod.py @@ -1,7 +1,7 @@ ## -## This file is part of the sigrok project. +## This file is part of the libsigrokdecode project. ## -## Copyright (C) 2015 Uwe Hermann +## Copyright (C) 2012-2014 Uwe Hermann ## ## 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 @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## # Normal commands (CMD) @@ -23,7 +22,7 @@ # seem to be mentioned in the spec, but aren't marked as reserved either. cmd_names = { 0: 'GO_IDLE_STATE', - # 1: Reserved + 1: 'SEND_OP_COND', # Reserved in SD mode 2: 'ALL_SEND_CID', 3: 'SEND_RELATIVE_ADDR', 4: 'SET_DSR', @@ -53,8 +52,8 @@ cmd_names = { 29: 'CLR_WRITE_PROT', 30: 'SEND_WRITE_PROT', # 31: Reserved - 32: 'ERASE_WR_BLK_START', - 33: 'ERASE_WR_BLK_END', + 32: 'ERASE_WR_BLK_START', # SPI mode: ERASE_WR_BLK_START_ADDR + 33: 'ERASE_WR_BLK_END', # SPI mode: ERASE_WR_BLK_END_ADDR 34: 'Reserved for CMD6', # New since spec 1.10 35: 'Reserved for CMD6', # New since spec 1.10 36: 'Reserved for CMD6', # New since spec 1.10 @@ -73,7 +72,8 @@ cmd_names = { 55: 'APP_CMD', 56: 'GEN_CMD', 57: 'Reserved for CMD6', # New since spec 1.10 - # 58-59: Reserved + 58: 'READ_OCR', # Reserved in SD mode + 59: 'CRC_ON_OFF', # Reserved in SD mode 60: 'Reserved for manufacturer', 61: 'Reserved for manufacturer', 62: 'Reserved for manufacturer', diff --git a/libsigrokdecode4DSL/decoders/common/srdhelper/__init__.py b/libsigrokdecode4DSL/decoders/common/srdhelper/__init__.py new file mode 100755 index 00000000..fb323856 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/common/srdhelper/__init__.py @@ -0,0 +1,20 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2012-2014 Uwe Hermann +## +## 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 2 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 . +## + +from .mod import * diff --git a/libsigrokdecode4DSL/decoders/common/srdhelper/mod.py b/libsigrokdecode4DSL/decoders/common/srdhelper/mod.py new file mode 100755 index 00000000..e37345a0 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/common/srdhelper/mod.py @@ -0,0 +1,36 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2012-2014 Uwe Hermann +## +## 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 2 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 . +## + +# Return the specified BCD number (max. 8 bits) as integer. +def bcd2int(b): + return (b & 0x0f) + ((b >> 4) * 10) + +def bin2int(s: str): + return int('0b' + s, 2) + +def bitpack(bits): + return sum([b << i for i, b in enumerate(bits)]) + +def bitunpack(num, minbits=0): + res = [] + while num or minbits > 0: + res.append(num & 1) + num >>= 1 + minbits -= 1 + return tuple(res) diff --git a/libsigrokdecode4DSL/decoders/counter/__init__.py b/libsigrokdecode4DSL/decoders/counter/__init__.py new file mode 100755 index 00000000..505148dd --- /dev/null +++ b/libsigrokdecode4DSL/decoders/counter/__init__.py @@ -0,0 +1,28 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Stefan Brüns +## +## 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 2 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 . +## + +''' +This decoder is a simple edge counter. + +It can count rising and/or falling edges, provides an optional reset +signal. It can also divide the count to e.g. count the number of +fixed-length words (where a word corresponds to e.g. 9 clock edges). +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/counter/pd.py b/libsigrokdecode4DSL/decoders/counter/pd.py new file mode 100755 index 00000000..b0b1af71 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/counter/pd.py @@ -0,0 +1,145 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Stefan Brüns +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd + +PIN_DATA, PIN_RESET = range(2) +ROW_EDGE, ROW_WORD, ROW_RESET = range(3) + +class Decoder(srd.Decoder): + api_version = 3 + id = 'counter' + name = 'Counter' + longname = 'Edge counter' + desc = 'Count the number of edges in a signal.' + license = 'gplv2+' + inputs = ['logic'] + outputs = [] + tags = ['Util'] + channels = ( + {'id': 'data', 'name': 'Data', 'desc': 'Data line'}, + ) + optional_channels = ( + {'id': 'reset', 'name': 'Reset', 'desc': 'Reset line'}, + ) + annotations = ( + ('edge_count', 'Edge count'), + ('word_count', 'Word count'), + ('word_reset', 'Word reset'), + ) + annotation_rows = ( + ('edge_counts', 'Edges', (ROW_EDGE,)), + ('word_counts', 'Words', (ROW_WORD,)), + ('word_resets', 'Word resets', (ROW_RESET,)), + ) + options = ( + {'id': 'data_edge', 'desc': 'Edges to count (data)', 'default': 'any', + 'values': ('any', 'rising', 'falling')}, + {'id': 'divider', 'desc': 'Count divider (word width)', 'default': 0}, + {'id': 'reset_edge', 'desc': 'Edge which clears counters (reset)', + 'default': 'falling', 'values': ('rising', 'falling')}, + {'id': 'edge_off', 'desc': 'Edge counter value after start/reset', 'default': 0}, + {'id': 'word_off', 'desc': 'Word counter value after start/reset', 'default': 0}, + {'id': 'dead_cycles', 'desc': 'Ignore this many edges after reset', 'default': 0}, + {'id': 'start_with_reset', 'desc': 'Assume decode starts with reset', + 'default': 'no', 'values': ('no', 'yes')}, + ) + + def __init__(self): + self.reset() + + def reset(self): + pass + + def metadata(self, key, value): + if key == srd.SRD_CONF_SAMPLERATE: + self.samplerate = value + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + + def putc(self, cls, ss, annlist): + self.put(ss, self.samplenum, self.out_ann, [cls, annlist]) + + def decode(self): + opt_edge_map = {'rising': 'r', 'falling': 'f', 'any': 'e'} + + data_edge = self.options['data_edge'] + divider = self.options['divider'] + if divider < 0: + divider = 0 + reset_edge = self.options['reset_edge'] + + condition = [{PIN_DATA: opt_edge_map[data_edge]}] + have_reset = self.has_channel(PIN_RESET) + if have_reset: + cond_reset = len(condition) + condition.append({PIN_RESET: opt_edge_map[reset_edge]}) + + edge_count = int(self.options['edge_off']) + edge_start = None + word_count = int(self.options['word_off']) + word_start = None + + if self.options['start_with_reset'] == 'yes': + dead_count = int(self.options['dead_cycles']) + else: + dead_count = 0 + + while True: + self.wait(condition) + now = self.samplenum + + if have_reset and (self.matched & (0b1 < +## +## 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 2 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 . +## + +''' +DALI is a biphase/manchester based lighting control protocol. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/dali/lists.py b/libsigrokdecode4DSL/decoders/dali/lists.py new file mode 100755 index 00000000..e9d3a4ba --- /dev/null +++ b/libsigrokdecode4DSL/decoders/dali/lists.py @@ -0,0 +1,98 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2015 Jeremy Swanson +## +## 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 2 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 . +## + +# DALI extended commands +extended_commands = { + 0xA1: ['Terminate special processes', 'Terminate'], + 0xA3: ['DTR = DATA', 'DTR'], + 0xA5: ['INITIALISE', 'INIT'], + 0xA7: ['RANDOMISE', 'RAND'], + 0xA9: ['COMPARE', 'COMP'], + 0xAB: ['WITHDRAW', 'WDRAW'], + 0xB1: ['SET SEARCH H', 'SAH'], + 0xB3: ['SET SEARCH M', 'SAM'], + 0xB5: ['SET SEARCH L', 'SAL'], + 0xB7: ['Program Short Address', 'ProgSA'], + 0xB9: ['Verify Short Address', 'VfySA'], + 0xBB: ['Query Short Address', 'QryShort'], + 0xBD: ['Physical Selection', 'PysSel'], + 0xC1: ['Enable Device Type X', 'EnTyp'], + 0xC3: ['DTR1 = DATA', 'DTR1'], + 0xC5: ['DTR2 = DATA', 'DTR2'], + 0xC7: ['Write Memory Location', 'WRI'], +} + +# List of commands +dali_commands = { + 0x00: ['Immediate Off', 'IOFF'], + 0x01: ['Up 200ms', 'Up'], + 0x02: ['Down 200ms', 'Down'], + 0x03: ['Step Up', 'Step+'], + 0x04: ['Step Down', 'Step-'], + 0x05: ['Recall Maximum Level', 'Recall Max'], + 0x06: ['Recall Minimum Level', 'Recall Min'], + 0x07: ['Step down and off', 'Down Off'], + 0x08: ['Step ON and UP', 'On Up'], + 0x20: ['Reset', 'Rst'], + 0x21: ['Store Dim Level in DTR', 'Level -> DTR'], + 0x2A: ['Store DTR as Max Level', 'DTR->Max'], + 0x2B: ['Store DTR as Min Level', 'DTR->Min'], + 0x2C: ['Store DTR as Fail Level', 'DTR->Fail'], + 0x2D: ['Store DTR as Power On Level', 'DTR->Poweron'], + 0x2E: ['Store DTR as Fade Time', 'DTR->Fade'], + 0x2F: ['Store DTR as Fade Rate', 'DTR->Rate'], + 0x80: ['Store DTR as Short Address', 'DTR->Add'], + 0x81: ['Enable Memory Write', 'WEn'], + 0x90: ['Query Status', 'Status'], + 0x91: ['Query Ballast', 'Ballast'], + 0x92: ['Query Lamp Failure', 'LmpFail'], + 0x93: ['Query Power On', 'Power On'], + 0x94: ['Query Limit Error', 'Limit Err'], + 0x95: ['Query Reset', 'Reset State'], + 0x96: ['Query Missing Short Address', 'NoSrt'], + 0x97: ['Query Version', 'Ver'], + 0x98: ['Query DTR', 'GetDTR'], + 0x99: ['Query Device Type', 'Type'], + 0x9A: ['Query Physical Minimum', 'PhysMin'], + 0x9B: ['Query Power Fail', 'PowerFailed'], + 0x9C: ['Query DTR1', 'GetDTR1'], + 0x9D: ['Query DTR2', 'GetDTR2'], + 0xA0: ['Query Level', 'GetLevel'], + 0xA1: ['Query Max Level', 'GetMax'], + 0xA2: ['Query Min Level', 'GetMin'], + 0xA3: ['Query Power On', 'GetPwrOn'], + 0xA4: ['Query Fail Level', 'GetFail'], + 0xA5: ['Query Fade Rate', 'GetRate'], + 0xA6: ['Query Power Fail', 'PwrFail'], + 0xC0: ['Query Groups 0-7', 'GetGrpsL'], + 0xC1: ['Query Groups 7-15', 'GetGrpsH'], + 0xC2: ['Query BRNH', 'BRNH'], + 0xC3: ['Query BRNM', 'BRNM'], + 0xC4: ['Query BRNL', 'BRNL'], + 0xC5: ['Query Memory', 'GetMem'], +} + +# DALI device type 8 +dali_device_type8 = { + 0xE0: ['Set Temp X-Y Coordinate', 'Set X-Y'], + 0xE2: ['Activate Colour Set point', 'Activate SetPoint'], + 0xE7: ['Set Colour Temperature Tc', 'DTRs->ColTemp'], + 0xF9: ['Query Features', 'QryFeats'], + 0xFA: ['Query Current Setpoint Colour', 'GetSetPoint'], +} diff --git a/libsigrokdecode4DSL/decoders/dali/pd.py b/libsigrokdecode4DSL/decoders/dali/pd.py new file mode 100755 index 00000000..53147463 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/dali/pd.py @@ -0,0 +1,245 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2015 Jeremy Swanson +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see . +## + +import sigrokdecode as srd +from .lists import * + +class SamplerateError(Exception): + pass + +class Decoder(srd.Decoder): + api_version = 3 + id = 'dali' + name = 'DALI' + longname = 'Digital Addressable Lighting Interface' + desc = 'Digital Addressable Lighting Interface (DALI) protocol.' + license = 'gplv2+' + inputs = ['logic'] + outputs = [] + tags = ['Embedded/industrial', 'Lighting'] + channels = ( + {'id': 'dali', 'name': 'DALI', 'desc': 'DALI data line'}, + ) + options = ( + {'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low', + 'values': ('active-low', 'active-high')}, + ) + annotations = ( + ('bit', 'Bit'), + ('startbit', 'Startbit'), + ('sbit', 'Select bit'), + ('ybit', 'Individual or group'), + ('address', 'Address'), + ('command', 'Command'), + ('reply', 'Reply data'), + ('raw', 'Raw data'), + ) + annotation_rows = ( + ('bits', 'Bits', (0,)), + ('raw', 'Raw data', (7,)), + ('fields', 'Fields', (1, 2, 3, 4, 5, 6)), + ) + + def __init__(self): + self.reset() + + def reset(self): + self.samplerate = None + self.samplenum = None + self.edges, self.bits, self.ss_es_bits = [], [], [] + self.state = 'IDLE' + self.dev_type = None + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + self.old_dali = 1 if self.options['polarity'] == 'active-low' else 0 + + def metadata(self, key, value): + if key == srd.SRD_CONF_SAMPLERATE: + self.samplerate = value + # One bit: 833.33us (one half low, one half high). + # This is how may samples are in 1TE. + self.halfbit = int((self.samplerate * 0.0008333) / 2.0) + + def putb(self, bit1, bit2, data): + ss, es = self.ss_es_bits[bit1][0], self.ss_es_bits[bit2][1] + self.put(ss, es, self.out_ann, data) + + def handle_bits(self, length): + a, c, f, g, b = 0, 0, 0, 0, self.bits + # Individual raw bits. + for i in range(length): + if i == 0: + ss = max(0, self.bits[0][0]) + else: + ss = self.ss_es_bits[i - 1][1] + es = self.bits[i][0] + (self.halfbit * 2) + self.ss_es_bits.append([ss, es]) + self.putb(i, i, [0, ['%d' % self.bits[i][1]]]) + # Bits[0:0]: Startbit + s = ['Startbit: %d' % b[0][1], 'ST: %d' % b[0][1], 'ST', 'S', 'S'] + self.putb(0, 0, [1, s]) + self.putb(0, 0, [7, s]) + # Bits[1:8] + for i in range(8): + f |= (b[1 + i][1] << (7 - i)) + if length == 9: # BACKWARD Frame + s = ['Reply: %02X' % f, 'Rply: %02X' % f, + 'Rep: %02X' % f, 'R: %02X' % f, 'R'] + self.putb(1, 8, [7, s]) + s = ['Reply: %d' % f, 'Rply: %d' % f, + 'Rep: %d' % f, 'R: %d' % f, 'R'] + self.putb(1, 8, [6, s]) + return + + # FORWARD FRAME + # Bits[9:16]: Command/data (MSB-first) + for i in range(8): + c |= (b[9 + i][1] << (7 - i)) + # Raw output + s = ['Raw data: %02X' % f, 'Raw: %02X' % f, + 'Raw: %02X' % f, 'R: %02X' % f, 'R'] + self.putb(1, 8, [7, s]) + s = ['Raw data: %02X' % c, 'Raw: %02X' % c, + 'Raw: %02X' % c, 'R: %02X' % c, 'R'] + self.putb(9, 16, [7, s]) + + # Bits[8:8]: Select bit + # s = ['Selectbit: %d' % b[8][1], 'SEL: %d' % b[8][1], 'SEL', 'SE', 'S'] + if b[8][1] == 1: + s = ['Command', 'Comd', 'COM', 'CO', 'C'] + else: + s = ['Arc Power Level', 'Arc Pwr', 'ARC', 'AC', 'A'] + self.putb(8, 8, [1, s]) + + # f &= 254 # Clear the select bit. + if f >= 254: # BROADCAST + s = ['BROADCAST', 'Brdcast', 'BC', 'B', 'B'] + self.putb(1, 7, [5, s]) + elif f >= 160: # Extended command 0b10100000 + if f == 0xC1: # DALI_ENABLE_DEVICE_TYPE_X + self.dev_type = -1 + x = extended_commands.get(f, ['Unknown', 'Unk']) + s = ['Extended Command: %02X (%s)' % (f, x[0]), + 'XC: %02X (%s)' % (f, x[1]), + 'XC: %02X' % f, 'X: %02X' % f, 'X'] + self.putb(1, 8, [5, s]) + elif f >= 128: # Group + # Bits[1:1]: Ybit + s = ['YBit: %d' % b[1][1], 'YB: %d' % b[1][1], 'YB', 'Y', 'Y'] + self.putb(1, 1, [3, s]) + g = (f & 127) >> 1 + s = ['Group address: %d' % g, 'Group: %d' % g, + 'GP: %d' % g, 'G: %d' % g, 'G'] + self.putb(2,7, [4, s]) + else: # Short address + # Bits[1:1]: Ybit + s = ['YBit: %d' % b[1][1], 'YB: %d' % b[1][1], 'YB', 'Y', 'Y'] + self.putb(1, 1, [3, s]) + a = f >> 1 + s = ['Short address: %d' % a, 'Addr: %d' % a, + 'Addr: %d' % a, 'A: %d' % a, 'A'] + self.putb(2, 7, [4, s]) + + # Bits[9:16]: Command/data (MSB-first) + if f >= 160 and f < 254: + if self.dev_type == -1: + self.dev_type = c + s = ['Type: %d' % c, 'Typ: %d' % c, + 'Typ: %d' % c, 'T: %d' % c, 'D'] + else: + self.dev_type = None + s = ['Data: %d' % c, 'Dat: %d' % c, + 'Dat: %d' % c, 'D: %d' % c, 'D'] + elif b[8][1] == 1: + un = c & 0xF0 + ln = c & 0x0F + if un == 0x10: # Set scene command + x = ['Recall Scene %d' % ln, 'SC %d' % ln] + elif un == 0x40: + x = ['Store DTR as Scene %d' % ln, 'SC %d = DTR' % ln] + elif un == 0x50: + x = ['Delete Scene %d' % ln, 'DEL SC %d' % ln] + elif un == 0x60: + x = ['Add to Group %d' % ln, 'Grp %d Add' % ln] + elif un == 0x70: + x = ['Remove from Group %d' % ln, 'Grp %d Del' % ln] + elif un == 0xB0: + x = ['Query Scene %d Level' % ln, 'Sc %d Level' % ln] + elif c >= 224: # Application specific commands + if self.dev_type == 8: + x = dali_device_type8.get(c, ['Unknown App', 'Unk']) + else: + x = ['Application Specific Command %d' % c, 'App Cmd %d' % c] + else: + x = dali_commands.get(c, ['Unknown', 'Unk']) + s = ['Command: %d (%s)' % (c, x[0]), 'Com: %d (%s)' % (c, x[1]), + 'Com: %d' % c, 'C: %d' % c, 'C'] + else: + s = ['Arc Power Level: %d' % c, 'Level: %d' % c, + 'Lev: %d' % c, 'L: %d' % c, 'L'] + self.putb(9, 16, [5, s]) + + def reset_decoder_state(self): + self.edges, self.bits, self.ss_es_bits = [], [], [] + self.state = 'IDLE' + + def decode(self): + if not self.samplerate: + raise SamplerateError('Cannot decode without samplerate.') + bit = 0 + while True: + # TODO: Come up with more appropriate self.wait() conditions. + (dali,) = self.wait() + if self.options['polarity'] == 'active-high': + dali ^= 1 # Invert. + + # State machine. + if self.state == 'IDLE': + # Wait for any edge (rising or falling). + if self.old_dali == dali: + continue + self.edges.append(self.samplenum) + self.state = 'PHASE0' + self.old_dali = dali + continue + + if self.old_dali != dali: + self.edges.append(self.samplenum) + elif self.samplenum == (self.edges[-1] + int(self.halfbit * 1.5)): + self.edges.append(self.samplenum - int(self.halfbit * 0.5)) + else: + continue + + bit = self.old_dali + if self.state == 'PHASE0': + self.phase0 = bit + self.state = 'PHASE1' + elif self.state == 'PHASE1': + if (bit == 1) and (self.phase0 == 1): # Stop bit. + if len(self.bits) == 17 or len(self.bits) == 9: + # Forward or Backward. + self.handle_bits(len(self.bits)) + self.reset_decoder_state() # Reset upon errors. + continue + else: + self.bits.append([self.edges[-3], bit]) + self.state = 'PHASE0' + + self.old_dali = dali diff --git a/libsigrokdecode4DSL/decoders/dcf77/__init__.py b/libsigrokdecode4DSL/decoders/dcf77/__init__.py index 8423028c..caadcff8 100755 --- a/libsigrokdecode4DSL/decoders/dcf77/__init__.py +++ b/libsigrokdecode4DSL/decoders/dcf77/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/dcf77/pd.py b/libsigrokdecode4DSL/decoders/dcf77/pd.py index 24318a46..7365134e 100755 --- a/libsigrokdecode4DSL/decoders/dcf77/pd.py +++ b/libsigrokdecode4DSL/decoders/dcf77/pd.py @@ -1,7 +1,7 @@ ## ## This file is part of the libsigrokdecode project. ## -## Copyright (C) 2012-2014 Uwe Hermann +## Copyright (C) 2012-2016 Uwe Hermann ## ## 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 @@ -14,29 +14,26 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd import calendar - -# Return the specified BCD number (max. 8 bits) as integer. -def bcd2int(b): - return (b & 0x0f) + ((b >> 4) * 10) +from common.srdhelper import bcd2int class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'dcf77' name = 'DCF77' longname = 'DCF77 time protocol' desc = 'European longwave time signal (77.5kHz carrier signal).' license = 'gplv2+' inputs = ['logic'] - outputs = ['dcf77'] + outputs = [] + tags = ['Clock/timing'] channels = ( {'id': 'data', 'name': 'DATA', 'desc': 'DATA line'}, ) @@ -68,12 +65,12 @@ class Decoder(srd.Decoder): ('warnings', 'Warnings', (19,)), ) - def __init__(self, **kwargs): + def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.state = 'WAIT FOR RISING EDGE' - self.oldpins = None - self.oldval = None - self.samplenum = 0 self.ss_bit = self.ss_bit_old = self.es_bit = self.ss_block = 0 self.datebits = [] self.bitcount = 0 # Counter for the DCF77 bits (0..58) @@ -131,7 +128,7 @@ class Decoder(srd.Decoder): else: self.tmp |= (bit << (c - 1)) if c == 14: - s = bin(self.tmp)[2:].zfill(14) + s = '{:014b}'.format(self.tmp) self.putb([1, ['Special bits: %s' % s, 'SB: %s' % s]]) elif c == 15: s = '' if (bit == 1) else 'not ' @@ -214,9 +211,13 @@ class Decoder(srd.Decoder): self.tmp |= (bit << (c - 42)) if c == 44: d = bcd2int(self.tmp) - dn = calendar.day_name[d - 1] # day_name[0] == Monday - self.putb([13, ['Day of week: %d (%s)' % (d, dn), - 'DoW: %d (%s)' % (d, dn)]]) + try: + dn = calendar.day_name[d - 1] # day_name[0] == Monday + self.putb([13, ['Day of week: %d (%s)' % (d, dn), + 'DoW: %d (%s)' % (d, dn)]]) + except IndexError: + self.putb([19, ['Day of week: %d (%s)' % (d, 'invalid'), + 'DoW: %d (%s)' % (d, 'inv')]]) elif c in range(45, 49 + 1): # Month (1-12): DCF77 bits 45-49 (BCD format). if c == 45: @@ -226,9 +227,13 @@ class Decoder(srd.Decoder): self.tmp |= (bit << (c - 45)) if c == 49: m = bcd2int(self.tmp) - mn = calendar.month_name[m] # month_name[1] == January - self.putb([14, ['Month: %d (%s)' % (m, mn), - 'Mon: %d (%s)' % (m, mn)]]) + try: + mn = calendar.month_name[m] # month_name[1] == January + self.putb([14, ['Month: %d (%s)' % (m, mn), + 'Mon: %d (%s)' % (m, mn)]]) + except IndexError: + self.putb([19, ['Month: %d (%s)' % (m, 'invalid'), + 'Mon: %d (%s)' % (m, 'inv')]]) elif c in range(50, 57 + 1): # Year (0-99): DCF77 bits 50-57 (BCD format). if c == 50: @@ -245,23 +250,16 @@ class Decoder(srd.Decoder): self.putx([16, ['Date parity: %s' % s, 'DP: %s' % s]]) self.datebits = [] else: - raise Exception('Invalid DCF77 bit: %d' % c) + self.putx([19, ['Invalid DCF77 bit: %d' % c, + 'Invalid bit: %d' % c, 'Inv: %d' % c]]) - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - for (self.samplenum, pins) in data: - data.itercnt += 1 - # Ignore identical samples early on (for performance reasons). - if self.oldpins == pins: - continue - self.oldpins, (val,) = pins, pins - + while True: if self.state == 'WAIT FOR RISING EDGE': # Wait until the next rising edge occurs. - if not (self.oldval == 0 and val == 1): - self.oldval = val - continue + self.wait({0: 'r'}) # Save the sample number where the DCF77 bit begins. self.ss_bit = self.samplenum @@ -286,9 +284,7 @@ class Decoder(srd.Decoder): elif self.state == 'GET BIT': # Wait until the next falling edge occurs. - if not (self.oldval == 1 and val == 0): - self.oldval = val - continue + self.wait({0: 'f'}) # Save the sample number where the DCF77 bit ends. self.es_bit = self.samplenum @@ -304,13 +300,12 @@ class Decoder(srd.Decoder): elif len_high_ms in range(161, 260 + 1): bit = 1 else: - bit = -1 # TODO: Error? + bit = -1 - # There's no bit 59, make sure none is decoded. - if bit in (0, 1) and self.bitcount in range(0, 58 + 1): + if bit in (0, 1): self.handle_dcf77_bit(bit) self.bitcount += 1 + else: + self.putx([19, ['Invalid bit timing', 'Inv timing', 'Inv']]) self.state = 'WAIT FOR RISING EDGE' - - self.oldval = val diff --git a/libsigrokdecode4DSL/decoders/dmx512/__init__.py b/libsigrokdecode4DSL/decoders/dmx512/__init__.py old mode 100644 new mode 100755 diff --git a/libsigrokdecode4DSL/decoders/dmx512/pd.py b/libsigrokdecode4DSL/decoders/dmx512/pd.py old mode 100644 new mode 100755 index 79270c75..49326224 --- a/libsigrokdecode4DSL/decoders/dmx512/pd.py +++ b/libsigrokdecode4DSL/decoders/dmx512/pd.py @@ -20,17 +20,22 @@ import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'dmx512' name = 'DMX512' longname = 'Digital MultipleX 512' - desc = 'Professional lighting control protocol.' + desc = 'Digital MultipleX 512 (DMX512) lighting protocol.' license = 'gplv2+' inputs = ['logic'] - outputs = ['dmx512'] + outputs = [] + tags = ['Embedded/industrial', 'Lighting'] channels = ( {'id': 'dmx', 'name': 'DMX data', 'desc': 'Any DMX data line'}, ) + options = ( + {'id': 'invert', 'desc': 'Invert Signal?', 'default': 'no', + 'values': ('yes', 'no')}, + ) annotations = ( ('bit', 'Bit'), ('break', 'Break'), @@ -52,11 +57,12 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.sample_usec = None - self.samplenum = -1 self.run_start = -1 - self.run_bit = 0 self.state = 'FIND BREAK' def start(self): @@ -71,97 +77,95 @@ class Decoder(srd.Decoder): def putr(self, data): self.put(self.run_start, self.samplenum, self.out_ann, data) - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - for (self.samplenum, pins) in data: - data.itercnt += 1 + + inv = self.options['invert'] == 'yes' + + while True: # Seek for an interval with no state change with a length between # 88 and 1000000 us (BREAK). if self.state == 'FIND BREAK': - if self.run_bit == pins[0]: - continue + (dmx,) = self.wait({0: 'h' if inv else 'l'}) + self.run_start = self.samplenum + (dmx,) = self.wait({0: 'f' if inv else 'r'}) runlen = (self.samplenum - self.run_start) * self.sample_usec if runlen > 88 and runlen < 1000000: self.putr([1, ['Break']]) - self.bit_break = self.run_bit self.state = 'MARK MAB' self.channel = 0 elif runlen >= 1000000: # Error condition. self.putr([10, ['Invalid break length']]) - self.run_bit = pins[0] - self.run_start = self.samplenum # Directly following the BREAK is the MARK AFTER BREAK. elif self.state == 'MARK MAB': - if self.run_bit == pins[0]: - continue + self.run_start = self.samplenum + (dmx,) = self.wait({0: 'r' if inv else 'f'}) self.putr([2, ['MAB']]) self.state = 'READ BYTE' self.channel = 0 self.bit = 0 - self.aggreg = pins[0] + self.aggreg = dmx self.run_start = self.samplenum # Mark and read a single transmitted byte # (start bit, 8 data bits, 2 stop bits). elif self.state == 'READ BYTE': - self.next_sample = self.run_start + (self.bit + 1) * self.skip_per_bit - self.aggreg += pins[0] - if self.samplenum != self.next_sample: - continue - bit_value = 0 if round(self.aggreg/self.skip_per_bit) == self.bit_break else 1 + bit_start = self.samplenum + bit_end = self.run_start + (self.bit + 1) * self.skip_per_bit + (dmx,) = self.wait({'skip': round(self.skip_per_bit/2)}) + bit_value = not dmx if inv else dmx if self.bit == 0: self.byte = 0 - self.putr([3, ['Start bit']]) + self.put(bit_start, bit_end, + self.out_ann, [3, ['Start bit']]) if bit_value != 0: # (Possibly) invalid start bit, mark but don't fail. - self.put(self.samplenum, self.samplenum, + self.put(bit_start, bit_end, self.out_ann, [10, ['Invalid start bit']]) elif self.bit >= 9: - self.put(self.samplenum - self.skip_per_bit, - self.samplenum, self.out_ann, [4, ['Stop bit']]) + self.put(bit_start, bit_end, + self.out_ann, [4, ['Stop bit']]) if bit_value != 1: # Invalid stop bit, mark. - self.put(self.samplenum, self.samplenum, + self.put(bit_start, bit_end, self.out_ann, [10, ['Invalid stop bit']]) if self.bit == 10: # On invalid 2nd stop bit, search for new break. - self.run_bit = pins[0] self.state = 'FIND BREAK' else: # Label and process one bit. - self.put(self.samplenum - self.skip_per_bit, - self.samplenum, self.out_ann, [0, [str(bit_value)]]) + self.put(bit_start, bit_end, + self.out_ann, [0, [str(bit_value)]]) self.byte |= bit_value << (self.bit - 1) # Label a complete byte. - if self.bit == 10: + if self.state == 'READ BYTE' and self.bit == 10: if self.channel == 0: d = [5, ['Start code']] else: d = [6, ['Channel ' + str(self.channel)]] - self.put(self.run_start, self.next_sample, self.out_ann, d) + self.put(self.run_start, bit_end, self.out_ann, d) self.put(self.run_start + self.skip_per_bit, - self.next_sample - 2 * self.skip_per_bit, + bit_end - 2 * self.skip_per_bit, self.out_ann, [9, [str(self.byte) + ' / ' + \ str(hex(self.byte))]]) # Continue by scanning the IFT. self.channel += 1 self.run_start = self.samplenum - self.run_bit = pins[0] self.state = 'MARK IFT' - self.aggreg = pins[0] self.bit += 1 + (dmx,) = self.wait({'skip': round(bit_end - self.samplenum)}) # Mark the INTERFRAME-TIME between bytes / INTERPACKET-TIME between packets. elif self.state == 'MARK IFT': - if self.run_bit == pins[0]: - continue + self.run_start = self.samplenum + (dmx,) = self.wait({0: 'l' if inv else 'h'}) + (dmx,) = self.wait({0: 'r' if inv else 'f'}) if self.channel > 512: self.putr([8, ['Interpacket']]) self.state = 'FIND BREAK' - self.run_bit = pins[0] self.run_start = self.samplenum else: self.putr([7, ['Interframe']]) diff --git a/libsigrokdecode4DSL/decoders/ds1307/__init__.py b/libsigrokdecode4DSL/decoders/ds1307/__init__.py index 71662298..faf4ce68 100755 --- a/libsigrokdecode4DSL/decoders/ds1307/__init__.py +++ b/libsigrokdecode4DSL/decoders/ds1307/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/ds1307/pd.py b/libsigrokdecode4DSL/decoders/ds1307/pd.py index b784b6dd..f8ebe195 100755 --- a/libsigrokdecode4DSL/decoders/ds1307/pd.py +++ b/libsigrokdecode4DSL/decoders/ds1307/pd.py @@ -15,12 +15,12 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import re import sigrokdecode as srd +from common.srdhelper import bcd2int days_of_week = ( 'Sunday', 'Monday', 'Tuesday', 'Wednesday', @@ -39,9 +39,9 @@ bits = ( rates = { 0b00: '1Hz', - 0b01: '4096kHz', - 0b10: '8192kHz', - 0b11: '32768kHz', + 0b01: '4096Hz', + 0b10: '8192Hz', + 0b11: '32768Hz', } DS1307_I2C_ADDRESS = 0x68 @@ -51,19 +51,16 @@ def regs_and_bits(): l += [('bit-' + re.sub('\/| ', '-', b).lower(), b + ' bit') for b in bits] return tuple(l) -# Return the specified BCD number (max. 8 bits) as integer. -def bcd2int(b): - return (b & 0x0f) + ((b >> 4) * 10) - class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'ds1307' name = 'DS1307' longname = 'Dallas DS1307' - desc = 'Realtime clock module protocol.' + desc = 'Dallas DS1307 realtime clock module protocol.' license = 'gplv2+' inputs = ['i2c'] - outputs = ['ds1307'] + outputs = [] + tags = ['Clock/timing', 'IC'] annotations = regs_and_bits() + ( ('read-datetime', 'Read date/time'), ('write-datetime', 'Write date/time'), @@ -78,7 +75,10 @@ class Decoder(srd.Decoder): ('warnings', 'Warnings', (28,)), ) - def __init__(self, **kwargs): + def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.hours = -1 self.minutes = -1 @@ -122,7 +122,7 @@ class Decoder(srd.Decoder): ampm_mode = True if (b & (1 << 6)) else False if ampm_mode: self.putd(6, 6, [13, ['12-hour mode', '12h mode', '12h']]) - a = 'AM' if (b & (1 << 6)) else 'PM' + a = 'PM' if (b & (1 << 5)) else 'AM' self.putd(5, 5, [14, [a, a[0]]]) h = self.hours = bcd2int(b & 0x1f) self.putd(4, 0, [15, ['Hour: %d' % h, 'H: %d' % h, 'H']]) diff --git a/libsigrokdecode4DSL/decoders/maxim_ds28ea00/__init__.py b/libsigrokdecode4DSL/decoders/ds243x/__init__.py similarity index 75% rename from libsigrokdecode4DSL/decoders/maxim_ds28ea00/__init__.py rename to libsigrokdecode4DSL/decoders/ds243x/__init__.py index 049f96ab..c460e045 100755 --- a/libsigrokdecode4DSL/decoders/maxim_ds28ea00/__init__.py +++ b/libsigrokdecode4DSL/decoders/ds243x/__init__.py @@ -1,7 +1,7 @@ ## ## This file is part of the libsigrokdecode project. ## -## Copyright (C) 2012 Uwe Hermann +## Copyright (C) 2017 Kevin Redon ## ## 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 @@ -14,13 +14,12 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' This decoder stacks on top of the 'onewire_network' PD and decodes the -Maxim DS28EA00 1-Wire digital thermometer protocol. +Maxim DS243x (1-Wire EEPROM) protocol. ''' from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/ds243x/pd.py b/libsigrokdecode4DSL/decoders/ds243x/pd.py new file mode 100755 index 00000000..7f9f6660 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/ds243x/pd.py @@ -0,0 +1,270 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Kevin Redon +## Copyright (C) 2017 Soeren Apel +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd + +# Dictionary of FUNCTION commands and their names. +commands_2432 = { + 0x0f: 'Write scratchpad', + 0xaa: 'Read scratchpad', + 0x55: 'Copy scratchpad', + 0xf0: 'Read memory', + 0x5a: 'Load first secret', + 0x33: 'Compute next secret', + 0xa5: 'Read authenticated page', +} + +commands_2433 = { + 0x0f: 'Write scratchpad', + 0xaa: 'Read scratchpad', + 0x55: 'Copy scratchpad', + 0xf0: 'Read memory', +} + +# Maxim DS243x family code, present at the end of the ROM code. +family_codes = { + 0x33: ('DS2432', commands_2432), + 0x23: ('DS2433', commands_2433), +} + +# Calculate the CRC-16 checksum. +# Initial value: 0x0000, xor-in: 0x0000, polynom 0x8005, xor-out: 0xffff. +def crc16(byte_array): + reverse = 0xa001 # Use the reverse polynom to make algo simpler. + crc = 0x0000 # Initial value. + # Reverse CRC calculation. + for byte in byte_array: + for bit in range(8): + if (byte ^ crc) & 1: + crc = (crc >> 1) ^ reverse + else: + crc >>= 1 + byte >>= 1 + crc ^= 0xffff # Invert CRC. + return crc + +class Decoder(srd.Decoder): + api_version = 3 + id = 'ds243x' + name = 'DS243x' + longname = 'Maxim DS2432/3' + desc = 'Maxim DS243x series 1-Wire EEPROM protocol.' + license = 'gplv2+' + inputs = ['onewire_network'] + outputs = [] + tags = ['IC', 'Memory'] + annotations = ( + ('text', 'Human-readable text'), + ) + binary = ( + ('mem_read', 'Data read from memory'), + ) + + def __init__(self): + self.reset() + + def reset(self): + # Bytes for function command. + self.bytes = [] + self.family_code = None + self.family = '' + self.commands = commands_2432 # Use max command set until we know better. + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + self.out_binary = self.register(srd.OUTPUT_BINARY) + + def putx(self, data): + self.put(self.ss, self.es, self.out_ann, data) + + def decode(self, ss, es, data): + code, val = data + + if code == 'RESET/PRESENCE': + self.ss, self.es = ss, es + self.putx([0, ['Reset/presence: %s' + % ('true' if val else 'false')]]) + self.bytes = [] + elif code == 'ROM': + self.ss, self.es = ss, es + self.family_code = val & 0xff + + s = None + if self.family_code in family_codes: + self.family, self.commands = family_codes[val & 0xff] + s = 'is 0x%02x, %s detected' % (self.family_code, self.family) + else: + s = '0x%02x unknown' % (self.family_code) + + self.putx([0, ['ROM: 0x%016x (%s)' % (val, 'family code ' + s), + 'ROM: 0x%016x (%s)' % (val, self.family)]]) + self.bytes = [] + elif code == 'DATA': + self.bytes.append(val) + if 1 == len(self.bytes): + self.ss, self.es = ss, es + if val not in self.commands: + self.putx([0, ['Unrecognized command: 0x%02x' % val]]) + else: + self.putx([0, ['Function command: %s (0x%02x)' + % (self.commands[val], val)]]) + elif 0x0f == self.bytes[0]: # Write scratchpad + if 2 == len(self.bytes): + self.ss = ss + elif 3 == len(self.bytes): + self.es = es + self.putx([0, ['Target address: 0x%04x' + % ((self.bytes[2] << 8) + self.bytes[1])]]) + elif 4 == len(self.bytes): + self.ss = ss + elif 11 == len(self.bytes): + self.es = es + self.putx([0, ['Data: ' + (','.join(format(n, '#04x') + for n in self.bytes[3:11]))]]) + elif 12 == len(self.bytes): + self.ss = ss + elif 13 == len(self.bytes): + self.es = es + self.putx([0, ['CRC: ' + + ('ok' if crc16(self.bytes[0:11]) == (self.bytes[11] + + (self.bytes[12] << 8)) else 'error')]]) + elif 0xaa == self.bytes[0]: # Read scratchpad + if 2 == len(self.bytes): + self.ss = ss + elif 3 == len(self.bytes): + self.es = es + self.putx([0, ['Target address: 0x%04x' + % ((self.bytes[2] << 8) + self.bytes[1])]]) + elif 4 == len(self.bytes): + self.ss, self.es = ss, es + self.putx([0, ['Data status (E/S): 0x%02x' + % (self.bytes[3])]]) + elif 5 == len(self.bytes): + self.ss = ss + elif 12 == len(self.bytes): + self.es = es + self.putx([0, ['Data: ' + (','.join(format(n, '#04x') + for n in self.bytes[4:12]))]]) + elif 13 == len(self.bytes): + self.ss = ss + elif 14 == len(self.bytes): + self.es = es + self.putx([0, ['CRC: ' + + ('ok' if crc16(self.bytes[0:12]) == (self.bytes[12] + + (self.bytes[13] << 8)) else 'error')]]) + elif 0x5a == self.bytes[0]: # Load first secret + if 2 == len(self.bytes): + self.ss = ss + elif 4 == len(self.bytes): + self.es = es + self.putx([0, ['Authorization pattern (TA1, TA2, E/S): ' + + (','.join(format(n, '#04x') + for n in self.bytes[1:4]))]]) + elif 4 < len(self.bytes): + self.ss, self.es = ss, es + if (0xaa == self.bytes[-1] or 0x55 == self.bytes[-1]): + self.putx([0, ['End of operation']]) + elif 0x33 == self.bytes[0]: # Compute next secret + if 2 == len(self.bytes): + self.ss = ss + elif 3 == len(self.bytes): + self.es = es + self.putx([0, ['Target address: 0x%04x' + % ((self.bytes[2] << 8) + self.bytes[1])]]) + elif 3 < len(self.bytes): + self.ss, self.es = ss, es + if (0xaa == self.bytes[-1] or 0x55 == self.bytes[-1]): + self.putx([0, ['End of operation']]) + elif 0x55 == self.bytes[0]: # Copy scratchpad + if 2 == len(self.bytes): + self.ss = ss + elif 4 == len(self.bytes): + self.es = es + self.putx([0, ['Authorization pattern (TA1, TA2, E/S): ' + + (','.join(format(n, '#04x') + for n in self.bytes[1:4]))]]) + elif 5 == len(self.bytes): + self.ss = ss + elif 24 == len(self.bytes): + self.es = es + mac = ','.join(format(n, '#04x') for n in self.bytes[4:24]) + self.putx([0, ['Message authentication code: ' + mac, + 'MAC: ' + mac]]) + elif 24 < len(self.bytes): + self.ss, self.es = ss, es + if (0xaa == self.bytes[-1] or 0x55 == self.bytes[-1]): + self.putx([0, ['Operation succeeded']]) + elif (0 == self.bytes[-1]): + self.putx([0, ['Operation failed']]) + elif 0xa5 == self.bytes[0]: # Read authenticated page + if 2 == len(self.bytes): + self.ss = ss + elif 3 == len(self.bytes): + self.es = es + self.putx([0, ['Target address: 0x%04x' + % ((self.bytes[2] << 8) + self.bytes[1])]]) + elif 4 == len(self.bytes): + self.ss = ss + elif 35 == len(self.bytes): + self.es = es + self.putx([0, ['Data: ' + (','.join(format(n, '#04x') + for n in self.bytes[3:35]))]]) + elif 36 == len(self.bytes): + self.ss, self.es = ss, es + self.putx([0, ['Padding: ' + + ('ok' if 0xff == self.bytes[-1] else 'error')]]) + elif 37 == len(self.bytes): + self.ss = ss + elif 38 == len(self.bytes): + self.es = es + self.putx([0, ['CRC: ' + + ('ok' if crc16(self.bytes[0:36]) == (self.bytes[36] + + (self.bytes[37] << 8)) else 'error')]]) + elif 39 == len(self.bytes): + self.ss = ss + elif 58 == len(self.bytes): + self.es = es + mac = ','.join(format(n, '#04x') for n in self.bytes[38:58]) + self.putx([0, ['Message authentication code: ' + mac, + 'MAC: ' + mac]]) + elif 59 == len(self.bytes): + self.ss = ss + elif 60 == len(self.bytes): + self.es = es + self.putx([0, ['MAC CRC: ' + + ('ok' if crc16(self.bytes[38:58]) == (self.bytes[58] + + (self.bytes[59] << 8)) else 'error')]]) + elif 60 < len(self.bytes): + self.ss, self.es = ss, es + if (0xaa == self.bytes[-1] or 0x55 == self.bytes[-1]): + self.putx([0, ['Operation completed']]) + elif 0xf0 == self.bytes[0]: # Read memory + if 2 == len(self.bytes): + self.ss = ss + elif 3 == len(self.bytes): + self.es = es + self.putx([0, ['Target address: 0x%04x' + % ((self.bytes[2] << 8) + self.bytes[1])]]) + elif 3 < len(self.bytes): + self.ss, self.es = ss, es + self.putx([0, ['Data: 0x%02x' % (self.bytes[-1])]]) + + bdata = self.bytes[-1].to_bytes(1, byteorder='big') + self.put(ss, es, self.out_binary, [0, bdata]) diff --git a/libsigrokdecode4DSL/decoders/ds28ea00/__init__.py b/libsigrokdecode4DSL/decoders/ds28ea00/__init__.py old mode 100644 new mode 100755 diff --git a/libsigrokdecode4DSL/decoders/ds28ea00/pd.py b/libsigrokdecode4DSL/decoders/ds28ea00/pd.py old mode 100644 new mode 100755 index 255f7c8f..9a578449 --- a/libsigrokdecode4DSL/decoders/ds28ea00/pd.py +++ b/libsigrokdecode4DSL/decoders/ds28ea00/pd.py @@ -35,19 +35,23 @@ command = { } class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'ds28ea00' name = 'DS28EA00' longname = 'Maxim DS28EA00 1-Wire digital thermometer' desc = '1-Wire digital thermometer with Sequence Detect and PIO.' license = 'gplv2+' inputs = ['onewire_network'] - outputs = ['ds28ea00'] + outputs = [] + tags = ['IC', 'Sensor'] annotations = ( ('text', 'Human-readable text'), ) def __init__(self): + self.reset() + + def reset(self): self.trn_beg = 0 self.trn_end = 0 self.state = 'ROM' diff --git a/libsigrokdecode4DSL/decoders/dsi/__init__.py b/libsigrokdecode4DSL/decoders/dsi/__init__.py old mode 100644 new mode 100755 diff --git a/libsigrokdecode4DSL/decoders/dsi/pd.py b/libsigrokdecode4DSL/decoders/dsi/pd.py old mode 100644 new mode 100755 index beba7f32..7ce95179 --- a/libsigrokdecode4DSL/decoders/dsi/pd.py +++ b/libsigrokdecode4DSL/decoders/dsi/pd.py @@ -23,14 +23,15 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'dsi' name = 'DSI' longname = 'Digital Serial Interface' - desc = 'DSI lighting control protocol.' + desc = 'Digital Serial Interface (DSI) lighting protocol.' license = 'gplv2+' inputs = ['logic'] - outputs = ['dsi'] + outputs = [] + tags = ['Embedded/industrial', 'Lighting'] channels = ( {'id': 'dsi', 'name': 'DSI', 'desc': 'DSI data line'}, ) @@ -40,23 +41,24 @@ class Decoder(srd.Decoder): ) annotations = ( ('bit', 'Bit'), - ('startbit', 'Startbit'), - ('Level', 'Dimmer level'), + ('startbit', 'Start bit'), + ('level', 'Dimmer level'), ('raw', 'Raw data'), ) annotation_rows = ( ('bits', 'Bits', (0,)), - ('raw', 'Raw Data',(3,)), - ('fields', 'Fields', (1, 2,)), + ('raw', 'Raw data', (3,)), + ('fields', 'Fields', (1, 2)), ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.samplenum = None self.edges, self.bits, self.ss_es_bits = [], [], [] self.state = 'IDLE' - self.nextSamplePoint = None - self.nextSample = None def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) @@ -105,13 +107,12 @@ class Decoder(srd.Decoder): self.edges, self.bits, self.ss_es_bits = [], [], [] self.state = 'IDLE' - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - bit = 0; - for (self.samplenum, pins) in data: - self.dsi = pins[0] - data.itercnt += 1 + bit = 0 + while True: + (self.dsi,) = self.wait() if self.options['polarity'] == 'active-high': self.dsi ^= 1 # Invert. @@ -128,15 +129,9 @@ class Decoder(srd.Decoder): self.state = 'PHASE1' self.old_dsi = self.dsi # Get the next sample point. - # self.nextSamplePoint = self.samplenum + int(self.halfbit / 2) self.old_dsi = self.dsi - # bit = self.dsi continue - # if(self.samplenum == self.nextSamplePoint): - # bit = self.dsi - # continue - if self.old_dsi != self.dsi: self.edges.append(self.samplenum) elif self.samplenum == (self.edges[-1] + int(self.halfbit * 1.5)): @@ -149,9 +144,9 @@ class Decoder(srd.Decoder): self.phase0 = bit self.state = 'PHASE1' elif self.state == 'PHASE1': - if (bit == 1) and (self.phase0 == 1): # Stop bit + if (bit == 1) and (self.phase0 == 1): # Stop bit. if len(self.bits) == 17 or len(self.bits) == 9: - # Forward or Backward + # Forward or Backward. self.handle_bits(len(self.bits)) self.reset_decoder_state() # Reset upon errors. continue @@ -159,6 +154,4 @@ class Decoder(srd.Decoder): self.bits.append([self.edges[-3], bit]) self.state = 'PHASE0' - # self.nextSamplePoint = self.edges[-1] + int(self.halfbit / 2) - self.old_dsi = self.dsi diff --git a/libsigrokdecode4DSL/decoders/edid/__init__.py b/libsigrokdecode4DSL/decoders/edid/__init__.py index de544d3c..256d839d 100755 --- a/libsigrokdecode4DSL/decoders/edid/__init__.py +++ b/libsigrokdecode4DSL/decoders/edid/__init__.py @@ -14,26 +14,22 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' -EDID 1.3 structure decoder. +Extended Display Identification Data (EDID) 1.3 structure decoder. The three-character vendor ID as specified in the EDID standard refers to a Plug and Play ID (PNPID). The list of PNPID assignments is done by Microsoft. -More information is available on this page: - - http://msdn.microsoft.com/en-us/windows/hardware/gg463195 The 'pnpids.txt' file included with this protocol decoder is derived from the list of assignments downloadable from that page. It was retrieved in January 2012. -More information on EDID is available here: - - https://en.wikipedia.org/wiki/Extended_display_identification_data +Details: +https://en.wikipedia.org/wiki/Extended_display_identification_data +http://msdn.microsoft.com/en-us/windows/hardware/gg463195 ''' from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/edid/config b/libsigrokdecode4DSL/decoders/edid/config index 44e1f353..ba74a8f7 100755 --- a/libsigrokdecode4DSL/decoders/edid/config +++ b/libsigrokdecode4DSL/decoders/edid/config @@ -1 +1 @@ -extra-install pnpids.txt +extra-install pnpids.txt diff --git a/libsigrokdecode4DSL/decoders/edid/pd.py b/libsigrokdecode4DSL/decoders/edid/pd.py index 389fbda9..2d7460ce 100755 --- a/libsigrokdecode4DSL/decoders/edid/pd.py +++ b/libsigrokdecode4DSL/decoders/edid/pd.py @@ -73,14 +73,15 @@ ANN_FIELDS = 0 ANN_SECTIONS = 1 class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'edid' name = 'EDID' longname = 'Extended Display Identification Data' desc = 'Data structure describing display device capabilities.' license = 'gplv3+' inputs = ['i2c'] - outputs = ['edid'] + outputs = [] + tags = ['Display', 'Memory', 'PC'] annotations = ( ('fields', 'EDID structure fields'), ('sections', 'EDID structure sections'), @@ -91,6 +92,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = None # Received data items, used as an index into samplenum/data self.cnt = 0 @@ -98,6 +102,12 @@ class Decoder(srd.Decoder): self.sn = [] # Received data self.cache = [] + # Random read offset + self.offset = 0 + # Extensions + self.extension = 0 + self.ext_sn = [[]] + self.ext_cache = [[]] def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) @@ -105,16 +115,55 @@ class Decoder(srd.Decoder): def decode(self, ss, es, data): cmd, data = data + if cmd == 'ADDRESS WRITE' and data == 0x50: + self.state = 'offset' + self.ss = ss + return + + if cmd == 'ADDRESS READ' and data == 0x50: + if self.extension > 0: + self.state = 'extensions' + s = str(self.extension) + t = ["Extension: " + s, "X: " + s, s] + else: + self.state = 'header' + t = ["EDID"] + self.put(ss, es, self.out_ann, [ANN_SECTIONS, t]) + return + + if cmd == 'DATA WRITE' and self.state == 'offset': + self.offset = data + self.extension = self.offset // 128 + self.cnt = self.offset % 128 + if self.extension > 0: + ext = self.extension - 1 + l = len(self.ext_sn[ext]) + # Truncate or extend to self.cnt. + self.sn = self.ext_sn[ext][0:self.cnt] + [0] * max(0, self.cnt - l) + self.cache = self.ext_cache[ext][0:self.cnt] + [0] * max(0, self.cnt - l) + else: + l = len(self.sn) + self.sn = self.sn[0:self.cnt] + [0] * max(0, self.cnt - l) + self.cache = self.cache[0:self.cnt] + [0] * max(0, self.cnt - l) + ss = self.ss if self.ss else ss + s = str(data) + t = ["Offset: " + s, "O: " + s, s] + self.put(ss, es, self.out_ann, [ANN_SECTIONS, t]) + return + # We only care about actual data bytes that are read (for now). if cmd != 'DATA READ': return self.cnt += 1 - self.sn.append([ss, es]) - self.cache.append(data) - # debug + if self.extension > 0: + self.ext_sn[self.extension - 1].append([ss, es]) + self.ext_cache[self.extension - 1].append(data) + else: + self.sn.append([ss, es]) + self.cache.append(data) - if self.state is None: + if self.state is None or self.state == 'header': # Wait for the EDID header if self.cnt >= OFF_VENDOR: if self.cache[-8:] == EDID_HEADER: @@ -176,12 +225,52 @@ class Decoder(srd.Decoder): self.put(ss, es, self.out_ann, [0, ['Checksum: %d (%s)' % ( self.cache[self.cnt-1], csstr)]]) self.state = 'extensions' + elif self.state == 'extensions': - pass + cache = self.ext_cache[self.extension - 1] + sn = self.ext_sn[self.extension - 1] + v = cache[self.cnt - 1] + if self.cnt == 1: + if v == 2: + self.put(ss, es, self.out_ann, [1, ['Extensions Tag', 'Tag']]) + else: + self.put(ss, es, self.out_ann, [1, ['Bad Tag']]) + elif self.cnt == 2: + self.put(ss, es, self.out_ann, [1, ['Version']]) + self.put(ss, es, self.out_ann, [0, [str(v)]]) + elif self.cnt == 3: + self.put(ss, es, self.out_ann, [1, ['DTD offset']]) + self.put(ss, es, self.out_ann, [0, [str(v)]]) + elif self.cnt == 4: + self.put(ss, es, self.out_ann, [1, ['Format support | DTD count']]) + support = "Underscan: {0}, {1} Audio, YCbCr: {2}".format( + "yes" if v & 0x80 else "no", + "Basic" if v & 0x40 else "No", + ["None", "422", "444", "422+444"][(v & 0x30) >> 4]) + self.put(ss, es, self.out_ann, [0, ['{0}, DTDs: {1}'.format(support, v & 0xf)]]) + elif self.cnt <= cache[2]: + if self.cnt == cache[2]: + self.put(sn[4][0], es, self.out_ann, [1, ['Data block collection']]) + self.decode_data_block_collection(cache[4:], sn[4:]) + elif (self.cnt - cache[2]) % 18 == 0: + n = (self.cnt - cache[2]) / 18 + if n <= cache[3] & 0xf: + self.put(sn[self.cnt - 18][0], es, self.out_ann, [1, ['DTD']]) + self.decode_descriptors(-18) + + elif self.cnt == 127: + dtd_last = cache[2] + (cache[3] & 0xf) * 18 + self.put(sn[dtd_last][0], es, self.out_ann, [1, ['Padding']]) + elif self.cnt == 128: + checksum = sum(cache) % 256 + self.put(ss, es, self.out_ann, [0, ['Checksum: %d (%s)' % ( + cache[self.cnt-1], 'Wrong' if checksum else 'OK')]]) def ann_field(self, start, end, annotation): - self.put(self.sn[start][0], self.sn[end][1], - self.out_ann, [ANN_FIELDS, [annotation]]) + annotation = annotation if isinstance(annotation, list) else [annotation] + sn = self.ext_sn[self.extension - 1] if self.extension else self.sn + self.put(sn[start][0], sn[end][1], + self.out_ann, [ANN_FIELDS, annotation]) def lookup_pnpid(self, pnpid): pnpid_file = os.path.join(os.path.dirname(__file__), 'pnpids.txt') @@ -226,7 +315,7 @@ class Decoder(srd.Decoder): datestr += 'week %d, ' % self.cache[offset] datestr += str(1990 + self.cache[offset+1]) if datestr: - self.ann_field(offset, offset+1, 'Manufactured ' + datestr) + self.ann_field(offset, offset+1, ['Manufactured ' + datestr, datestr]) def decode_basicdisplay(self, offset): # Video input definition @@ -351,60 +440,53 @@ class Decoder(srd.Decoder): self.ann_field(offset, offset + 15, 'Supported standard modes: %s' % modestr[:-2]) - def decode_detailed_timing(self, offset): - if offset == -72 and self.have_preferred_timing: + def decode_detailed_timing(self, cache, sn, offset, is_first): + if is_first and self.have_preferred_timing: # Only on first detailed timing descriptor section = 'Preferred' else: section = 'Detailed' section += ' timing descriptor' - self.put(self.sn[offset][0], self.sn[offset+17][1], + + self.put(sn[0][0], sn[17][1], self.out_ann, [ANN_SECTIONS, [section]]) - pixclock = float((self.cache[offset+1] << 8) + self.cache[offset]) / 100 + pixclock = float((cache[1] << 8) + cache[0]) / 100 self.ann_field(offset, offset+1, 'Pixel clock: %.2f MHz' % pixclock) - horiz_active = ((self.cache[offset+4] & 0xf0) << 4) + self.cache[offset+2] - self.ann_field(offset+2, offset+4, 'Horizontal active: %d' % horiz_active) + horiz_active = ((cache[4] & 0xf0) << 4) + cache[2] + horiz_blank = ((cache[4] & 0x0f) << 8) + cache[3] + self.ann_field(offset+2, offset+4, 'Horizontal active: %d, blanking: %d' % (horiz_active, horiz_blank)) - horiz_blank = ((self.cache[offset+4] & 0x0f) << 8) + self.cache[offset+3] - self.ann_field(offset+2, offset+4, 'Horizontal blanking: %d' % horiz_blank) + vert_active = ((cache[7] & 0xf0) << 4) + cache[5] + vert_blank = ((cache[7] & 0x0f) << 8) + cache[6] + self.ann_field(offset+5, offset+7, 'Vertical active: %d, blanking: %d' % (vert_active, vert_blank)) - vert_active = ((self.cache[offset+7] & 0xf0) << 4) + self.cache[offset+5] - self.ann_field(offset+5, offset+7, 'Vertical active: %d' % vert_active) + horiz_sync_off = ((cache[11] & 0xc0) << 2) + cache[8] + horiz_sync_pw = ((cache[11] & 0x30) << 4) + cache[9] + vert_sync_off = ((cache[11] & 0x0c) << 2) + ((cache[10] & 0xf0) >> 4) + vert_sync_pw = ((cache[11] & 0x03) << 4) + (cache[10] & 0x0f) - vert_blank = ((self.cache[offset+7] & 0x0f) << 8) + self.cache[offset+6] - self.ann_field(offset+5, offset+7, 'Vertical blanking: %d' % vert_blank) + syncs = (horiz_sync_off, horiz_sync_pw, vert_sync_off, vert_sync_pw) + self.ann_field(offset+8, offset+11, [ + 'Horizontal sync offset: %d, pulse width: %d, Vertical sync offset: %d, pulse width: %d' % syncs, + 'HSync off: %d, pw: %d, VSync off: %d, pw: %d' % syncs]) - horiz_sync_off = ((self.cache[offset+11] & 0xc0) << 2) + self.cache[offset+8] - self.ann_field(offset+8, offset+11, 'Horizontal sync offset: %d' % horiz_sync_off) - - horiz_sync_pw = ((self.cache[offset+11] & 0x30) << 4) + self.cache[offset+9] - self.ann_field(offset+8, offset+11, 'Horizontal sync pulse width: %d' % horiz_sync_pw) - - vert_sync_off = ((self.cache[offset+11] & 0x0c) << 2) \ - + ((self.cache[offset+10] & 0xf0) >> 4) - self.ann_field(offset+8, offset+11, 'Vertical sync offset: %d' % vert_sync_off) - - vert_sync_pw = ((self.cache[offset+11] & 0x03) << 4) \ - + (self.cache[offset+10] & 0x0f) - self.ann_field(offset+8, offset+11, 'Vertical sync pulse width: %d' % vert_sync_pw) - - horiz_size = ((self.cache[offset+14] & 0xf0) << 4) + self.cache[offset+12] - vert_size = ((self.cache[offset+14] & 0x0f) << 8) + self.cache[offset+13] + horiz_size = ((cache[14] & 0xf0) << 4) + cache[12] + vert_size = ((cache[14] & 0x0f) << 8) + cache[13] self.ann_field(offset+12, offset+14, 'Physical size: %dx%dmm' % (horiz_size, vert_size)) - horiz_border = self.cache[offset+15] + horiz_border = cache[15] self.ann_field(offset+15, offset+15, 'Horizontal border: %d pixels' % horiz_border) - vert_border = self.cache[offset+16] + vert_border = cache[16] self.ann_field(offset+16, offset+16, 'Vertical border: %d lines' % vert_border) features = 'Flags: ' - if self.cache[offset+17] & 0x80: + if cache[17] & 0x80: features += 'interlaced, ' - stereo = (self.cache[offset+17] & 0x60) >> 5 + stereo = (cache[17] & 0x60) >> 5 if stereo: - if self.cache[offset+17] & 0x01: + if cache[17] & 0x01: features += '2-way interleaved stereo (' features += ['right image on even lines', 'left image on even lines', @@ -415,8 +497,8 @@ class Decoder(srd.Decoder): features += ['right image on sync=1', 'left image on sync=1', '4-way interleaved'][stereo-1] features += '), ' - sync = (self.cache[offset+17] & 0x18) >> 3 - sync2 = (self.cache[offset+17] & 0x06) >> 1 + sync = (cache[17] & 0x18) >> 3 + sync2 = (cache[17] & 0x06) >> 1 posneg = ['negative', 'positive'] features += 'sync type ' if sync == 0x00: @@ -434,60 +516,153 @@ class Decoder(srd.Decoder): features += ', ' self.ann_field(offset+17, offset+17, features[:-2]) - def decode_descriptor(self, offset): - tag = self.cache[offset+3] + def decode_descriptor(self, cache, offset): + tag = cache[3] + self.ann_field(offset, offset+1, "Flag") + self.ann_field(offset+2, offset+2, "Flag (reserved)") + self.ann_field(offset+3, offset+3, "Tag: {0:X}".format(tag)) + self.ann_field(offset+4, offset+4, "Flag") + + sn = self.ext_sn[self.extension - 1] if self.extension else self.sn + if tag == 0xff: # Monitor serial number - self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann, + self.put(sn[offset][0], sn[offset+17][1], self.out_ann, [ANN_SECTIONS, ['Serial number']]) - text = bytes(self.cache[offset+5:][:13]).decode(encoding='cp437', errors='replace') - self.ann_field(offset, offset+17, text.strip()) + text = bytes(cache[5:][:13]).decode(encoding='cp437', errors='replace') + self.ann_field(offset+5, offset+17, text.strip()) elif tag == 0xfe: # Text - self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann, + self.put(sn[offset][0], sn[offset+17][1], self.out_ann, [ANN_SECTIONS, ['Text']]) - text = bytes(self.cache[offset+5:][:13]).decode(encoding='cp437', errors='replace') - self.ann_field(offset, offset+17, text.strip()) + text = bytes(cache[5:][:13]).decode(encoding='cp437', errors='replace') + self.ann_field(offset+5, offset+17, text.strip()) elif tag == 0xfc: # Monitor name - self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann, + self.put(sn[offset][0], sn[offset+17][1], self.out_ann, [ANN_SECTIONS, ['Monitor name']]) - text = bytes(self.cache[offset+5:][:13]).decode(encoding='cp437', errors='replace') - self.ann_field(offset, offset+17, text.strip()) + text = bytes(cache[5:][:13]).decode(encoding='cp437', errors='replace') + self.ann_field(offset+5, offset+17, text.strip()) elif tag == 0xfd: # Monitor range limits - self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann, + self.put(sn[offset][0], sn[offset+17][1], self.out_ann, [ANN_SECTIONS, ['Monitor range limits']]) - self.ann_field(offset+5, offset+5, 'Minimum vertical rate: %dHz' % - self.cache[offset+5]) - self.ann_field(offset+6, offset+6, 'Maximum vertical rate: %dHz' % - self.cache[offset+6]) - self.ann_field(offset+7, offset+7, 'Minimum horizontal rate: %dkHz' % - self.cache[offset+7]) - self.ann_field(offset+8, offset+8, 'Maximum horizontal rate: %dkHz' % - self.cache[offset+8]) - self.ann_field(offset+9, offset+9, 'Maximum pixel clock: %dMHz' % - (self.cache[offset+9] * 10)) - if self.cache[offset+10] == 0x02: - # Secondary GTF curve supported - self.ann_field(offset+10, offset+17, 'Secondary timing formula supported') + self.ann_field(offset+5, offset+5, [ + 'Minimum vertical rate: {0}Hz'.format(cache[5]), + 'VSync >= {0}Hz'.format(cache[5])]) + self.ann_field(offset+6, offset+6, [ + 'Maximum vertical rate: {0}Hz'.format(cache[6]), + 'VSync <= {0}Hz'.format(cache[6])]) + self.ann_field(offset+7, offset+7, [ + 'Minimum horizontal rate: {0}kHz'.format(cache[7]), + 'HSync >= {0}kHz'.format(cache[7])]) + self.ann_field(offset+8, offset+8, [ + 'Maximum horizontal rate: {0}kHz'.format(cache[8]), + 'HSync <= {0}kHz'.format(cache[8])]) + self.ann_field(offset+9, offset+9, [ + 'Maximum pixel clock: {0}MHz'.format(cache[9] * 10), + 'PixClk <= {0}MHz'.format(cache[9] * 10)]) + if cache[10] == 0x02: + self.ann_field(offset+10, offset+10, ['Secondary timing formula supported', '2nd GTF: yes']) + self.ann_field(offset+11, offset+17, ['GTF']) + else: + self.ann_field(offset+10, offset+10, ['Secondary timing formula unsupported', '2nd GTF: no']) + self.ann_field(offset+11, offset+17, ['Padding']) elif tag == 0xfb: # Additional color point data - self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann, + self.put(sn[offset][0], sn[offset+17][1], self.out_ann, [ANN_SECTIONS, ['Additional color point data']]) elif tag == 0xfa: # Additional standard timing definitions - self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann, + self.put(sn[offset][0], sn[offset+17][1], self.out_ann, [ANN_SECTIONS, ['Additional standard timing definitions']]) else: - self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann, + self.put(sn[offset][0], sn[offset+17][1], self.out_ann, [ANN_SECTIONS, ['Unknown descriptor']]) def decode_descriptors(self, offset): # 4 consecutive 18-byte descriptor blocks + cache = self.ext_cache[self.extension - 1] if self.extension else self.cache + sn = self.ext_sn[self.extension - 1] if self.extension else self.sn + for i in range(offset, 0, 18): - if self.cache[i] != 0 and self.cache[i+1] != 0: - self.decode_detailed_timing(i) + if cache[i] != 0 or cache[i+1] != 0: + self.decode_detailed_timing(cache[i:], sn[i:], i, i == offset) else: - if self.cache[i+2] == 0 or self.cache[i+4] == 0: - self.decode_descriptor(i) + if cache[i+2] == 0 or cache[i+4] == 0: + self.decode_descriptor(cache[i:], i) + + def decode_data_block(self, tag, cache, sn): + codes = { 0: ['0: Reserved'], + 1: ['1: Audio Data Block', 'Audio'], + 2: ['2: Video Data Block', 'Video'], + 3: ['3: Vendor Specific Data Block', 'VSDB'], + 4: ['4: Speacker Allocation Data Block', 'SADB'], + 5: ['5: VESA DTC Data Block', 'DTC'], + 6: ['6: Reserved'], + 7: ['7: Extended', 'Ext'] } + ext_codes = { 0: [ '0: Video Capability Data Block', 'VCDB'], + 1: [ '1: Vendor Specific Video Data Block', 'VSVDB'], + 17: ['17: Vendor Specific Audio Data Block', 'VSADB'], } + if tag < 7: + code = codes[tag] + ext_len = 0 + if tag == 1: + aformats = { 1: '1 (LPCM)' } + rates = [ '192', '176', '96', '88', '48', '44', '32' ] + + aformat = cache[1] >> 3 + sup_rates = [ i for i in range(0, 8) if (1 << i) & cache[2] ] + + data = "Format: {0} Channels: {1}".format( + aformats.get(aformat, aformat), (cache[1] & 0x7) + 1) + data += " Rates: " + " ".join(rates[6 - i] for i in sup_rates) + data += " Extra: [{0:02X}]".format(cache[3]) + + elif tag ==2: + data = "VIC: " + data += ", ".join("{0}{1}".format(v & 0x7f, + ['', ' (Native)'][v >> 7]) + for v in cache[1:]) + + elif tag ==3: + ouis = { b'\x00\x0c\x03': 'HDMI Licensing, LLC' } + oui = bytes(cache[3:0:-1]) + ouis = ouis.get(oui, None) + data = "OUI: " + " ".join('{0:02X}'.format(x) for x in oui) + data += " ({0})".format(ouis) if ouis else "" + data += ", PhyAddr: {0}.{1}.{2}.{3}".format( + cache[4] >> 4, cache[4] & 0xf, cache[5] >> 4, cache[5] & 0xf) + data += ", [" + " ".join('{0:02X}'.format(x) for x in cache[6:]) + "]" + + elif tag ==4: + speakers = [ 'FL/FR', 'LFE', 'FC', 'RL/RR', + 'RC', 'FLC/FRC', 'RLC/RRC', 'FLW/FRW', + 'FLH/FRH', 'TC', 'FCH' ] + sup_speakers = cache[1] + (cache[2] << 8) + sup_speakers = [ i for i in range(0, 8) if (1 << i) & sup_speakers ] + data = "Speakers: " + " ".join(speakers[i] for i in sup_speakers) + + else: + data = " ".join('{0:02X}'.format(x) for x in cache[1:]) + + else: + # Extended tags + ext_len = 1 + ext_code = ext_codes.get(cache[1], ['Unknown', '?']) + code = zip(codes[7], [", ", ": "], ext_code) + code = [ "".join(x) for x in code ] + data = " ".join('{0:02X}'.format(x) for x in cache[2:]) + + self.put(sn[0][0], sn[0 + ext_len][1], self.out_ann, + [ANN_FIELDS, code]) + self.put(sn[1 + ext_len][0], sn[len(cache) - 1][1], self.out_ann, + [ANN_FIELDS, [data]]) + + def decode_data_block_collection(self, cache, sn): + offset = 0 + while offset < len(cache): + length = 1 + cache[offset] & 0x1f + tag = cache[offset] >> 5 + self.decode_data_block(tag, cache[offset:offset + length], sn[offset:]) + offset += length diff --git a/libsigrokdecode4DSL/decoders/eeprom24xx/__init__.py b/libsigrokdecode4DSL/decoders/eeprom24xx/__init__.py index de19da45..7d496fce 100755 --- a/libsigrokdecode4DSL/decoders/eeprom24xx/__init__.py +++ b/libsigrokdecode4DSL/decoders/eeprom24xx/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/eeprom24xx/lists.py b/libsigrokdecode4DSL/decoders/eeprom24xx/lists.py index ff4b3ed7..c6ee63d5 100755 --- a/libsigrokdecode4DSL/decoders/eeprom24xx/lists.py +++ b/libsigrokdecode4DSL/decoders/eeprom24xx/lists.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## # @@ -125,6 +124,28 @@ chips = { 'max_speed': 400, }, + # ON Semiconductor + 'onsemi_cat24c256': { + 'vendor': 'ON Semiconductor', + 'model': 'CAT24C256', + 'size': 32 * 1024, + 'page_size': 64, + 'page_wraparound': True, + 'addr_bytes': 2, + 'addr_pins': 3, + 'max_speed': 1000, + }, + 'onsemi_cat24m01': { + 'vendor': 'ON Semiconductor', + 'model': 'CAT24M01', + 'size': 128 * 1024, + 'page_size': 256, + 'page_wraparound': True, + 'addr_bytes': 2, + 'addr_pins': 2, # Pin A0 not connected + 'max_speed': 1000, + }, + # Siemens 'siemens_slx_24c01': { 'vendor': 'Siemens', @@ -168,4 +189,16 @@ chips = { 'addr_pins': 3, # Called E0, E1, E2 on this chip. 'max_speed': 400, }, + + # Xicor + 'xicor_x24c02': { + 'vendor': 'Xicor', + 'model': 'X24C02', + 'size': 256, + 'page_size': 4, + 'page_wraparound': True, + 'addr_bytes': 1, + 'addr_pins': 3, + 'max_speed': 100, + }, } diff --git a/libsigrokdecode4DSL/decoders/eeprom24xx/pd.py b/libsigrokdecode4DSL/decoders/eeprom24xx/pd.py index 0738c06b..033a44b2 100755 --- a/libsigrokdecode4DSL/decoders/eeprom24xx/pd.py +++ b/libsigrokdecode4DSL/decoders/eeprom24xx/pd.py @@ -14,22 +14,22 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd from .lists import * class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'eeprom24xx' name = '24xx EEPROM' longname = '24xx I²C EEPROM' desc = '24xx series I²C EEPROM protocol.' license = 'gplv2+' inputs = ['i2c'] - outputs = ['eeprom24xx'] + outputs = [] + tags = ['IC', 'Memory'] options = ( {'id': 'chip', 'desc': 'Chip', 'default': 'generic', 'values': tuple(chips.keys())}, @@ -73,12 +73,15 @@ class Decoder(srd.Decoder): ('binary', 'Binary'), ) - def __init__(self, **kwargs): + def __init__(self): self.reset() + def reset(self): + self.reset_variables() + def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) - self.out_bin = self.register(srd.OUTPUT_BINARY) + self.out_binary = self.register(srd.OUTPUT_BINARY) self.chip = chips[self.options['chip']] self.addr_counter = self.options['addr_counter'] @@ -86,12 +89,12 @@ class Decoder(srd.Decoder): self.put(self.ss_block, self.es_block, self.out_ann, data) def putbin(self, data): - self.put(self.ss_block, self.es_block, self.out_bin, data) + self.put(self.ss_block, self.es_block, self.out_binary, data) def putbits(self, bit1, bit2, bits, data): self.put(bits[bit1][1], bits[bit2][2], self.out_ann, data) - def reset(self): + def reset_variables(self): self.state = 'WAIT FOR START' self.packets = [] self.bytebuf = [] @@ -165,7 +168,7 @@ class Decoder(srd.Decoder): self.putb([cls, ['%s (%s): %s' % (s, self.addr_and_len(), \ self.hexbytes(self.chip['addr_bytes'])), '%s (%s)' % (s, self.addr_and_len()), s, a, s[0]]]) - self.putbin((0, bytes(self.bytebuf[self.chip['addr_bytes']:]))) + self.putbin([0, bytes(self.bytebuf[self.chip['addr_bytes']:])]) def addr_and_len(self): if self.chip['addr_bytes'] == 1: @@ -180,7 +183,7 @@ class Decoder(srd.Decoder): def decide_on_seq_or_rnd_read(self): if len(self.bytebuf) < 2: - self.reset() + self.reset_variables() return if len(self.bytebuf) == 2: self.is_random_access_read = True @@ -215,7 +218,7 @@ class Decoder(srd.Decoder): [8, ['Data', 'D']]) self.putb([11, ['Current address read: %02X' % self.bytebuf[0], 'Current address read', 'Cur addr read', 'CAR', 'C']]) - self.putbin((0, bytes([self.bytebuf[0]]))) + self.putbin([0, bytes([self.bytebuf[0]])]) self.addr_counter += 1 elif self.is_random_access_read: # Random access read: word address, one data byte. @@ -238,7 +241,7 @@ class Decoder(srd.Decoder): def handle_get_control_word(self): # The packet after START must be an ADDRESS READ or ADDRESS WRITE. if self.cmd not in ('ADDRESS READ', 'ADDRESS WRITE'): - self.reset() + self.reset_variables() return self.packet_append() self.put_control_word(self.bits) @@ -250,18 +253,18 @@ class Decoder(srd.Decoder): elif self.cmd == 'NACK': self.es_block = self.es self.putb([0, ['Warning: No reply from slave!']]) - self.reset() + self.reset_variables() else: - self.reset() + self.reset_variables() def handle_r_get_word_addr_or_byte(self): if self.cmd == 'STOP': self.es_block = self.es self.putb([0, ['Warning: Slave replied, but master aborted!']]) - self.reset() + self.reset_variables() return elif self.cmd != 'DATA READ': - self.reset() + self.reset_variables() return self.packet_append() self.state = 'R GET ACK NACK AFTER WORD ADDR OR BYTE' @@ -273,20 +276,20 @@ class Decoder(srd.Decoder): self.is_cur_addr_read = True self.state = 'GET STOP AFTER LAST BYTE' else: - self.reset() + self.reset_variables() def handle_r_get_restart(self): if self.cmd == 'RESTART': self.state = 'R READ BYTE' else: - self.reset() + self.reset_variables() def handle_r_read_byte(self): if self.cmd == 'DATA READ': self.packet_append() self.state = 'R GET ACK NACK AFTER BYTE WAS READ' else: - self.reset() + self.reset_variables() def handle_r_get_ack_nack_after_byte_was_read(self): if self.cmd == 'ACK': @@ -295,7 +298,7 @@ class Decoder(srd.Decoder): # It's either a RANDOM READ or a SEQUENTIAL READ. self.state = 'GET STOP AFTER LAST BYTE' else: - self.reset() + self.reset_variables() def handle_w_get_ack_nack_after_control_word(self): if self.cmd == 'ACK': @@ -303,18 +306,18 @@ class Decoder(srd.Decoder): elif self.cmd == 'NACK': self.es_block = self.es self.putb([0, ['Warning: No reply from slave!']]) - self.reset() + self.reset_variables() else: - self.reset() + self.reset_variables() def handle_w_get_word_addr(self): if self.cmd == 'STOP': self.es_block = self.es self.putb([0, ['Warning: Slave replied, but master aborted!']]) - self.reset() + self.reset_variables() return elif self.cmd != 'DATA WRITE': - self.reset() + self.reset_variables() return self.packet_append() self.state = 'W GET ACK AFTER WORD ADDR' @@ -323,7 +326,7 @@ class Decoder(srd.Decoder): if self.cmd == 'ACK': self.state = 'W DETERMINE EEPROM READ OR WRITE' else: - self.reset() + self.reset_variables() def handle_w_determine_eeprom_read_or_write(self): if self.cmd == 'START REPEAT': @@ -333,7 +336,7 @@ class Decoder(srd.Decoder): self.packet_append() self.state = 'W GET ACK NACK AFTER BYTE WAS WRITTEN' else: - self.reset() + self.reset_variables() def handle_w_write_byte(self): if self.cmd == 'DATA WRITE': @@ -341,7 +344,7 @@ class Decoder(srd.Decoder): self.state = 'W GET ACK NACK AFTER BYTE WAS WRITTEN' elif self.cmd == 'STOP': if len(self.bytebuf) < 2: - self.reset() + self.reset_variables() return self.es_block = self.es if len(self.bytebuf) == 2: @@ -349,31 +352,31 @@ class Decoder(srd.Decoder): else: self.is_page_write = True self.put_operation() - self.reset() + self.reset_variables() elif self.cmd == 'START REPEAT': # It's either a RANDOM ACCESS READ or SEQUENTIAL RANDOM READ. self.state = 'R2 GET CONTROL WORD' else: - self.reset() + self.reset_variables() def handle_w_get_ack_nack_after_byte_was_written(self): if self.cmd == 'ACK': self.state = 'W WRITE BYTE' else: - self.reset() + self.reset_variables() def handle_r2_get_control_word(self): if self.cmd == 'ADDRESS READ': self.packet_append() self.state = 'R2 GET ACK AFTER ADDR READ' else: - self.reset() + self.reset_variables() def handle_r2_get_ack_after_addr_read(self): if self.cmd == 'ACK': self.state = 'R2 READ BYTE' else: - self.reset() + self.reset_variables() def handle_r2_read_byte(self): if self.cmd == 'DATA READ': @@ -384,9 +387,9 @@ class Decoder(srd.Decoder): self.es_block = self.es self.putb([0, ['Warning: STOP expected after a NACK (not ACK)']]) self.put_operation() - self.reset() + self.reset_variables() else: - self.reset() + self.reset_variables() def handle_r2_get_ack_nack_after_byte_was_read(self): if self.cmd == 'ACK': @@ -395,22 +398,22 @@ class Decoder(srd.Decoder): self.decide_on_seq_or_rnd_read() self.state = 'GET STOP AFTER LAST BYTE' else: - self.reset() + self.reset_variables() def handle_get_stop_after_last_byte(self): if self.cmd == 'STOP': self.es_block = self.es self.put_operation() - self.reset() + self.reset_variables() elif self.cmd == 'START REPEAT': self.es_block = self.es self.putb([0, ['Warning: STOP expected (not RESTART)']]) self.put_operation() - self.reset() + self.reset_variables() self.ss_block = self.ss self.state = 'GET CONTROL WORD' else: - self.reset() + self.reset_variables() def decode(self, ss, es, data): self.cmd, self.databyte = data diff --git a/libsigrokdecode4DSL/decoders/eeprom93cxx/__init__.py b/libsigrokdecode4DSL/decoders/eeprom93xx/__init__.py old mode 100644 new mode 100755 similarity index 98% rename from libsigrokdecode4DSL/decoders/eeprom93cxx/__init__.py rename to libsigrokdecode4DSL/decoders/eeprom93xx/__init__.py index 036f1f7a..c8eaf7a0 --- a/libsigrokdecode4DSL/decoders/eeprom93cxx/__init__.py +++ b/libsigrokdecode4DSL/decoders/eeprom93xx/__init__.py @@ -18,7 +18,7 @@ ## ''' -This decoder stacks on top of the 'microwire' PD and decodes the 93Cxx EEPROM +This decoder stacks on top of the 'microwire' PD and decodes the 93xx EEPROM specific instructions. The implemented instructions come from the STMicroelectronics M93Cx6 EEPROM diff --git a/libsigrokdecode4DSL/decoders/eeprom93cxx/pd.py b/libsigrokdecode4DSL/decoders/eeprom93xx/pd.py old mode 100644 new mode 100755 similarity index 95% rename from libsigrokdecode4DSL/decoders/eeprom93cxx/pd.py rename to libsigrokdecode4DSL/decoders/eeprom93xx/pd.py index cd54b479..7b64e59a --- a/libsigrokdecode4DSL/decoders/eeprom93cxx/pd.py +++ b/libsigrokdecode4DSL/decoders/eeprom93xx/pd.py @@ -20,14 +20,15 @@ import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 - id = 'eeprom93cxx' - name = '93Cxx EEPROM' - longname = '93Cxx Microwire EEPROM' - desc = '93Cxx series Microwire EEPROM protocol.' + api_version = 3 + id = 'eeprom93xx' + name = '93xx EEPROM' + longname = '93xx Microwire EEPROM' + desc = '93xx series Microwire EEPROM protocol.' license = 'gplv2+' inputs = ['microwire'] - outputs = ['eeprom93cxx'] + outputs = [] + tags = ['IC', 'Memory'] options = ( {'id': 'addresssize', 'desc': 'Address size', 'default': 8}, {'id': 'wordsize', 'desc': 'Word size', 'default': 16}, @@ -43,6 +44,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.frame = [] def start(self): diff --git a/libsigrokdecode4DSL/decoders/em4100/__init__.py b/libsigrokdecode4DSL/decoders/em4100/__init__.py index cff361fa..c3c95e28 100755 --- a/libsigrokdecode4DSL/decoders/em4100/__init__.py +++ b/libsigrokdecode4DSL/decoders/em4100/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/em4100/pd.py b/libsigrokdecode4DSL/decoders/em4100/pd.py index c686e520..7f42ad70 100755 --- a/libsigrokdecode4DSL/decoders/em4100/pd.py +++ b/libsigrokdecode4DSL/decoders/em4100/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -24,14 +23,15 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'em4100' name = 'EM4100' longname = 'RFID EM4100' desc = 'EM4100 100-150kHz RFID protocol.' license = 'gplv2+' inputs = ['logic'] - outputs = ['em4100'] + outputs = [] + tags = ['IC', 'RFID'] channels = ( {'id': 'data', 'name': 'Data', 'desc': 'Data line'}, ) @@ -63,6 +63,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.oldpin = None self.last_samplenum = None @@ -74,12 +77,12 @@ class Decoder(srd.Decoder): self.oldpl = 0 self.oldsamplenum = 0 self.last_bit_pos = 0 - self.first_ss = 0 + self.ss_first = 0 self.first_one = 0 self.state = 'HEADER' self.data = 0 self.data_bits = 0 - self.data_ss = 0 + self.ss_data = 0 self.data_parity = 0 self.payload_cnt = 0 self.data_col_parity = [0, 0, 0, 0, 0, 0] @@ -105,14 +108,14 @@ class Decoder(srd.Decoder): if self.first_one > 0: self.first_one += 1 if self.first_one == 9: - self.put(self.first_ss, es, self.out_ann, + self.put(self.ss_first, es, self.out_ann, [1, ['Header', 'Head', 'He', 'H']]) self.first_one = 0 self.state = 'PAYLOAD' return if self.first_one == 0: self.first_one = 1 - self.first_ss = ss + self.ss_first = ss if bit == 0: self.first_one = 0 @@ -121,14 +124,14 @@ class Decoder(srd.Decoder): if self.state == 'PAYLOAD': self.payload_cnt += 1 if self.data_bits == 0: - self.data_ss = ss + self.ss_data = ss self.data = 0 self.data_parity = 0 self.data_bits += 1 if self.data_bits == 5: s = 'Version/customer' if self.payload_cnt <= 10 else 'Data' c = 2 if self.payload_cnt <= 10 else 3 - self.put(self.data_ss, ss, self.out_ann, + self.put(self.ss_data, ss, self.out_ann, [c, [s + ': %X' % self.data, '%X' % self.data]]) s = 'OK' if self.data_parity == bit else 'ERROR' c = 4 if s == 'OK' else 5 @@ -150,7 +153,7 @@ class Decoder(srd.Decoder): if self.state == 'TRAILER': self.payload_cnt += 1 if self.data_bits == 0: - self.data_ss = ss + self.ss_data = ss self.data = 0 self.data_parity = 0 self.data_bits += 1 @@ -172,7 +175,7 @@ class Decoder(srd.Decoder): # Emit an annotation for valid-looking tags. all_col_parity_ok = (self.data_col_parity[1:5] == self.col_parity[1:5]) if all_col_parity_ok and self.all_row_parity_ok: - self.put(self.first_ss, es, self.out_ann, + self.put(self.ss_first, es, self.out_ann, [9, ['Tag: %010X' % self.tag, 'Tag', 'T']]) self.tag = 0 @@ -186,53 +189,50 @@ class Decoder(srd.Decoder): self.col_parity_pos = [] self.all_row_parity_ok = True - def manchester_decode(self, samplenum, pl, pp, pin): + def manchester_decode(self, pl, pp, pin): bit = self.oldpin ^ self.polarity if pl > self.halfbit_limit: - es = int(samplenum - pl/2) + es = int(self.samplenum - pl/2) if self.oldpl > self.halfbit_limit: ss = int(self.oldsamplenum - self.oldpl/2) else: ss = int(self.oldsamplenum - self.oldpl) self.putbit(bit, ss, es) - self.last_bit_pos = int(samplenum - pl/2) + self.last_bit_pos = int(self.samplenum - pl/2) else: - es = int(samplenum) + es = int(self.samplenum) if self.oldpl > self.halfbit_limit: ss = int(self.oldsamplenum - self.oldpl/2) self.putbit(bit, ss, es) - self.last_bit_pos = int(samplenum) + self.last_bit_pos = int(self.samplenum) else: if self.last_bit_pos <= self.oldsamplenum - self.oldpl: ss = int(self.oldsamplenum - self.oldpl) self.putbit(bit, ss, es) - self.last_bit_pos = int(samplenum) + self.last_bit_pos = int(self.samplenum) - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - for (samplenum, (pin,)) in data: - data.itercnt += 1 - # Ignore identical samples early on (for performance reasons). - if self.oldpin == pin: - continue - if self.oldpin is None: - self.oldpin = pin - self.last_samplenum = samplenum - self.lastlast_samplenum = samplenum - self.last_edge = samplenum - self.oldpl = 0 - self.oldpp = 0 - self.oldsamplenum = 0 - self.last_bit_pos = 0 - continue + # Initialize internal state from the very first sample. + (pin,) = self.wait() + self.oldpin = pin + self.last_samplenum = self.samplenum + self.lastlast_samplenum = self.samplenum + self.last_edge = self.samplenum + self.oldpl = 0 + self.oldpp = 0 + self.oldsamplenum = 0 + self.last_bit_pos = 0 - if self.oldpin != pin: - pl = samplenum - self.oldsamplenum - pp = pin - self.manchester_decode(samplenum, pl, pp, pin) - self.oldpl = pl - self.oldpp = pp - self.oldsamplenum = samplenum - self.oldpin = pin + while True: + # Ignore identical samples, only process edges. + (pin,) = self.wait({0: 'e'}) + pl = self.samplenum - self.oldsamplenum + pp = pin + self.manchester_decode(pl, pp, pin) + self.oldpl = pl + self.oldpp = pp + self.oldsamplenum = self.samplenum + self.oldpin = pin diff --git a/libsigrokdecode4DSL/decoders/em4305/__init__.py b/libsigrokdecode4DSL/decoders/em4305/__init__.py index 1c1896a3..df437787 100755 --- a/libsigrokdecode4DSL/decoders/em4305/__init__.py +++ b/libsigrokdecode4DSL/decoders/em4305/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/em4305/pd.py b/libsigrokdecode4DSL/decoders/em4305/pd.py index 9dd6c0e2..6297643c 100755 --- a/libsigrokdecode4DSL/decoders/em4305/pd.py +++ b/libsigrokdecode4DSL/decoders/em4305/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -24,14 +23,15 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'em4305' name = 'EM4305' longname = 'RFID EM4205/EM4305' desc = 'EM4205/EM4305 100-150kHz RFID protocol.' license = 'gplv2+' inputs = ['logic'] - outputs = ['em4305'] + outputs = [] + tags = ['IC', 'RFID'] channels = ( {'id': 'data', 'name': 'Data', 'desc': 'Data line'}, ) @@ -66,8 +66,10 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None - self.oldpin = None self.last_samplenum = None self.state = 'FFS_SEARCH' self.bits_pos = [[0 for col in range(3)] for row in range(70)] @@ -325,73 +327,68 @@ class Decoder(srd.Decoder): self.bits_pos[self.bit_nr][2] = es_bit self.bit_nr += 1 - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - for (self.samplenum, (pin,)) in data: - data.itercnt += 1 - # Ignore identical samples early on (for performance reasons). - if self.oldpin == pin: - continue - if self.oldpin is None: - self.oldpin = pin - self.last_samplenum = self.samplenum - self.oldsamplenum = 0 - self.old_gap_end = 0 - self.gap_detected = 0 - self.bit_nr = 0 - continue + # Initialize internal state. + self.last_samplenum = self.samplenum + self.oldsamplenum = 0 + self.old_gap_end = 0 + self.gap_detected = 0 + self.bit_nr = 0 - if self.oldpin != pin: - pl = self.samplenum - self.oldsamplenum - pp = pin - samples = self.samplenum - self.last_samplenum + while True: + # Ignore identical samples, only process edges. + (pin,) = self.wait({0: 'e'}) - if self.state == 'FFS_DETECTED': - if pl > self.writegap: - self.gap_detected = 1 - if (self.last_samplenum - self.old_gap_end) > self.nogap: - self.gap_detected = 0 - self.state = 'FFS_SEARCH' - self.put(self.old_gap_end, self.last_samplenum, - self.out_ann, [3, ['Write mode exit']]) - self.put_fields() + pl = self.samplenum - self.oldsamplenum + pp = pin + samples = self.samplenum - self.last_samplenum - if self.state == 'FFS_SEARCH': - if pl > self.ffs: - self.gap_detected = 1 - self.put(self.last_samplenum, self.samplenum, - self.out_ann, [1, ['First field stop', 'Field stop', 'FFS']]) - self.state = 'FFS_DETECTED' - - if self.gap_detected == 1: + if self.state == 'FFS_DETECTED': + if pl > self.writegap: + self.gap_detected = 1 + if (self.last_samplenum - self.old_gap_end) > self.nogap: self.gap_detected = 0 - if (self.last_samplenum - self.old_gap_end) > self.wzmin \ - and (self.last_samplenum - self.old_gap_end) < self.wzmax: - self.put(self.old_gap_end, self.samplenum, - self.out_ann, [0, ['0']]) - self.add_bits_pos(0, self.old_gap_end, self.samplenum) - if (self.last_samplenum - self.old_gap_end) > self.womax \ - and (self.last_samplenum-self.old_gap_end) < self.nogap: - # One or more 1 bits - one_bits = (int)((self.last_samplenum - self.old_gap_end) / self.womax) - for ox in range(0, one_bits): - bs = (int)(self.old_gap_end+ox*self.womax) - be = (int)(self.old_gap_end+ox*self.womax + self.womax) - self.put(bs, be, self.out_ann, [0, ['1']]) - self.add_bits_pos(1, bs, be) - if (self.samplenum - self.last_samplenum) > self.wzmin \ - and (self.samplenum - self.last_samplenum) < self.wzmax: - bs = (int)(self.old_gap_end+one_bits*self.womax) - self.put(bs, self.samplenum, self.out_ann, [0, ['0']]) - self.add_bits_pos(0, bs, self.samplenum) - - self.old_gap_end = self.samplenum - - if self.state == 'SKIP': self.state = 'FFS_SEARCH' + self.put(self.old_gap_end, self.last_samplenum, + self.out_ann, [3, ['Write mode exit']]) + self.put_fields() - self.oldsamplenum = self.samplenum - self.last_samplenum = self.samplenum - self.oldpin = pin + if self.state == 'FFS_SEARCH': + if pl > self.ffs: + self.gap_detected = 1 + self.put(self.last_samplenum, self.samplenum, + self.out_ann, [1, ['First field stop', 'Field stop', 'FFS']]) + self.state = 'FFS_DETECTED' + + if self.gap_detected == 1: + self.gap_detected = 0 + if (self.last_samplenum - self.old_gap_end) > self.wzmin \ + and (self.last_samplenum - self.old_gap_end) < self.wzmax: + self.put(self.old_gap_end, self.samplenum, + self.out_ann, [0, ['0']]) + self.add_bits_pos(0, self.old_gap_end, self.samplenum) + if (self.last_samplenum - self.old_gap_end) > self.womax \ + and (self.last_samplenum-self.old_gap_end) < self.nogap: + # One or more 1 bits + one_bits = (int)((self.last_samplenum - self.old_gap_end) / self.womax) + for ox in range(0, one_bits): + bs = (int)(self.old_gap_end+ox*self.womax) + be = (int)(self.old_gap_end+ox*self.womax + self.womax) + self.put(bs, be, self.out_ann, [0, ['1']]) + self.add_bits_pos(1, bs, be) + if (self.samplenum - self.last_samplenum) > self.wzmin \ + and (self.samplenum - self.last_samplenum) < self.wzmax: + bs = (int)(self.old_gap_end+one_bits*self.womax) + self.put(bs, self.samplenum, self.out_ann, [0, ['0']]) + self.add_bits_pos(0, bs, self.samplenum) + + self.old_gap_end = self.samplenum + + if self.state == 'SKIP': + self.state = 'FFS_SEARCH' + + self.oldsamplenum = self.samplenum + self.last_samplenum = self.samplenum diff --git a/libsigrokdecode4DSL/decoders/gpib/__init__.py b/libsigrokdecode4DSL/decoders/gpib/__init__.py old mode 100644 new mode 100755 diff --git a/libsigrokdecode4DSL/decoders/gpib/pd.py b/libsigrokdecode4DSL/decoders/gpib/pd.py old mode 100644 new mode 100755 index d3bb72f9..f0c963c2 --- a/libsigrokdecode4DSL/decoders/gpib/pd.py +++ b/libsigrokdecode4DSL/decoders/gpib/pd.py @@ -2,6 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2016 Rudolf Reuter +## Copyright (C) 2019 DreamSourceLab ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -20,14 +21,15 @@ import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'gpib' name = 'GPIB' longname = 'General Purpose Interface Bus' - desc = 'IEEE-488 GPIB / HPIB protocol.' + desc = 'IEEE-488 General Purpose Interface Bus (GPIB / HPIB).' license = 'gplv2+' inputs = ['logic'] - outputs = ['gpib'] + outputs = [] + tags = ['PC'] channels = ( {'id': 'dio1' , 'name': 'DIO1', 'desc': 'Data I/O bit 1'}, {'id': 'dio2' , 'name': 'DIO2', 'desc': 'Data I/O bit 2'}, @@ -61,14 +63,15 @@ class Decoder(srd.Decoder): ) def __init__(self): - self.olddav = None + self.reset() + + def reset(self): self.items = [] self.itemcount = 0 self.saved_item = None self.saved_ATN = False self.saved_EOI = False self.samplenum = 0 - self.oldpins = None self.ss_item = self.es_item = None self.first = True @@ -160,29 +163,20 @@ class Decoder(srd.Decoder): self.itemcount, self.items = 0, [] - def find_falling_dav_edge(self, dav, datapins): - # Ignore sample if the DAV pin hasn't changed. - if dav == self.olddav: - return - self.olddav = dav - # Sample on falling DAV edge. - if dav == 1: - return + def decode(self): - # Found the correct DAV edge, now get the bits. - self.handle_bits(datapins) - - def decode(self, ss, es, data): + # Inspect samples at falling edge of DAV. But make sure to also + # start inspection when the capture happens to start with low + # DAV level. Optionally enforce processing when a user specified + # sample number was reached. + waitcond = [{9: 'l'}] lsn = self.options['sample_total'] - data.itercnt += 1 - for (self.samplenum, pins) in data: - if lsn > 0: - if (lsn - self.samplenum) == 1: # Show the last data word. - self.handle_bits(pins) - - # Ignore identical samples early on (for performance reasons). - if self.oldpins == pins: - continue - self.oldpins = pins - - self.find_falling_dav_edge(pins[9], pins) + if lsn: + waitcond.append({'skip': lsn}) + while True: + if lsn: + waitcond[1]['skip'] = lsn - self.samplenum - 1 + (d1, d2, d3, d4, d5, d6, d7, d8, eoi, dav, nrfd, ndac, ifc, srq, atn, ren) = self.wait(waitcond) + pins = (d1, d2, d3, d4, d5, d6, d7, d8, eoi, dav, nrfd, ndac, ifc, srq, atn, ren) + self.handle_bits(pins) + waitcond[0][9] = 'f' diff --git a/libsigrokdecode4DSL/decoders/graycode/__init__.py b/libsigrokdecode4DSL/decoders/graycode/__init__.py new file mode 100755 index 00000000..90ef824c --- /dev/null +++ b/libsigrokdecode4DSL/decoders/graycode/__init__.py @@ -0,0 +1,24 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Christoph Rackwitz +## +## 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 2 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 . +## + +''' +Gray code and rotary encoder protocol. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/graycode/pd.py b/libsigrokdecode4DSL/decoders/graycode/pd.py new file mode 100755 index 00000000..9303c33a --- /dev/null +++ b/libsigrokdecode4DSL/decoders/graycode/pd.py @@ -0,0 +1,200 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Christoph Rackwitz +## Copyright (C) 2019 DreamSourceLab +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import math +import sigrokdecode as srd +from collections import deque +from common.srdhelper import bitpack, bitunpack + +def gray_encode(plain): + return plain & (plain >> 1) + +def gray_decode(gray): + temp = gray + temp ^= (temp >> 8) + temp ^= (temp >> 4) + temp ^= (temp >> 2) + temp ^= (temp >> 1) + return temp + +def prefix_fmt(value, emin=None): + sgn = (value > 0) - (value < 0) + value = abs(value) + p = math.log10(value) if value else 0 + value = sgn * math.floor(value * 10**int(3 - p)) * 10**-int(3 - p) + e = p // 3 * 3 + if emin is not None and e < emin: + e = emin + value *= 10**-e + p -= e + decimals = 2 - int(p) + prefixes = {-9: 'n', -6: 'µ', -3: 'm', 0: '', 3: 'k', 6: 'M', 9: 'G'} + return '{0:.{1}f} {2}'.format(value, decimals, prefixes[e]) + +class ChannelMapError(Exception): + pass + +class Value: + def __init__(self, onchange): + self.onchange = onchange + self.timestamp = None + self.value = None + + def get(self): + return self.value + + def set(self, timestamp, newval): + if newval != self.value: + if self.value is not None: + self.onchange(self.timestamp, self.value, timestamp, newval) + + self.value = newval + self.timestamp = timestamp + elif False: + if self.value is not None: + self.onchange(self.timestamp, self.value, timestamp, newval) + +MAX_CHANNELS = 8 # 10 channels causes some weird problems... + +class Decoder(srd.Decoder): + api_version = 3 + id = 'graycode' + name = 'Gray code' + longname = 'Gray code and rotary encoder' + desc = 'Accumulate rotary encoder increments, provide statistics.' + license = 'gplv2+' + inputs = ['logic'] + outputs = [] + tags = ['Encoding'] + optional_channels = tuple( + {'id': 'd{}'.format(i), 'name': 'D{}'.format(i), 'desc': 'Data line {}'.format(i)} + for i in range(MAX_CHANNELS) + ) + options = ( + {'id': 'edges', 'desc': 'Edges per rotation', 'default': 0}, + {'id': 'avg_period', 'desc': 'Averaging period', 'default': 10}, + ) + annotations = ( + ('phase', 'Phase'), + ('increment', 'Increment'), + ('count', 'Count'), + ('turns', 'Turns'), + ('interval', 'Interval'), + ('average', 'Average'), + ('rpm', 'Rate'), + ) + annotation_rows = tuple((u, v, (i,)) for i, (u, v) in enumerate(annotations)) + + def __init__(self): + self.reset() + + def reset(self): + self.num_channels = 0 + self.samplerate = None + self.last_n = deque() + + self.phase = Value(self.on_phase) + self.increment = Value(self.on_increment) + self.count = Value(self.on_count) + self.turns = Value(self.on_turns) + + def on_phase(self, told, vold, tnew, vnew): + self.put(told, tnew, self.out_ann, [0, ['{}'.format(vold)]]) + + def on_increment(self, told, vold, tnew, vnew): + if vold == 0: + message = '0' + elif abs(vold) == self.ENCODER_STEPS // 2: + message = '±π' + else: + message = '{:+d}'.format(vold) + self.put(told, tnew, self.out_ann, [1, [message]]) + + def on_count(self, told, vold, tnew, vnew): + self.put(told, tnew, self.out_ann, [2, ['{}'.format(vold)]]) + + def on_turns(self, told, vold, tnew, vnew): + self.put(told, tnew, self.out_ann, [3, ['{:+d}'.format(vold)]]) + + def metadata(self, key, value): + if key == srd.SRD_CONF_SAMPLERATE: + self.samplerate = value + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + + def decode(self): + chmask = [self.has_channel(i) for i in range(MAX_CHANNELS)] + self.num_channels = sum(chmask) + if chmask != [i < self.num_channels for i in range(MAX_CHANNELS)]: + raise ChannelMapError('Assigned channels need to be contiguous') + + self.ENCODER_STEPS = 1 << self.num_channels + + (d0, d1, d2, d3, d4, d5, d6, d7) = self.wait() + startbits = (d0, d1, d2, d3, d4, d5, d6, d7) + curtime = self.samplenum + + self.turns.set(self.samplenum, 0) + self.count.set(self.samplenum, 0) + self.phase.set(self.samplenum, gray_decode(bitpack(startbits[:self.num_channels]))) + + while True: + prevtime = curtime + (d0, d1, d2, d3, d4, d5, d6, d7) = self.wait([{i: 'e'} for i in range(self.num_channels)]) + bits = (d0, d1, d2, d3, d4, d5, d6, d7) + curtime = self.samplenum + + oldcount = self.count.get() + oldphase = self.phase.get() + + newphase = gray_decode(bitpack(bits[:self.num_channels])) + self.phase.set(self.samplenum, newphase) + + phasedelta_raw = (newphase - oldphase + (self.ENCODER_STEPS // 2 - 1)) % self.ENCODER_STEPS - (self.ENCODER_STEPS // 2 - 1) + phasedelta = phasedelta_raw + self.increment.set(self.samplenum, phasedelta) + if abs(phasedelta) == self.ENCODER_STEPS // 2: + phasedelta = 0 + + self.count.set(self.samplenum, self.count.get() + phasedelta) + + if self.options['edges']: + self.turns.set(self.samplenum, self.count.get() // self.options['edges']) + + if self.samplerate: + period = (curtime - prevtime) / self.samplerate + freq = abs(phasedelta_raw) / period + + self.put(prevtime, curtime, self.out_ann, [4, [ + '{}s, {}Hz'.format(prefix_fmt(period), prefix_fmt(freq))]]) + + if self.options['avg_period']: + self.last_n.append((abs(phasedelta_raw), period)) + if len(self.last_n) > self.options['avg_period']: + self.last_n.popleft() + + avg_period = sum(v for u, v in self.last_n) / (sum(u for u, v in self.last_n) or 1) + self.put(prevtime, curtime, self.out_ann, [5, [ + '{}s, {}Hz'.format(prefix_fmt(avg_period), + prefix_fmt(1 / avg_period))]]) + + if self.options['edges']: + self.put(prevtime, curtime, self.out_ann, [6, ['{}rpm'.format(prefix_fmt(60 * freq / self.options['edges'], emin=0))]]) diff --git a/libsigrokdecode4DSL/decoders/guess_bitrate/__init__.py b/libsigrokdecode4DSL/decoders/guess_bitrate/__init__.py index cd8f15e7..a02bf183 100755 --- a/libsigrokdecode4DSL/decoders/guess_bitrate/__init__.py +++ b/libsigrokdecode4DSL/decoders/guess_bitrate/__init__.py @@ -14,15 +14,16 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' This protocol decoder tries to guess the bitrate / baudrate of the -communication on the specified channel. Typically this will be used to -guess / detect the baudrate used in a UART communication snippet, but it -could also be used to guess bitrates of certain other protocols or buses. +communication on the specified channel. + +Typically this will be used to guess / detect the baudrate used in a UART +communication snippet, but it could also be used to guess bitrates of certain +other protocols or buses. It should be noted that this is nothing more than a simple guess / heuristic, and that there are various cases in practice where the detection of the @@ -32,6 +33,8 @@ The precision of the estimated bitrate / baudrate will also depend on the samplerate used to sample the respective channel. For good results it is recommended to use a logic analyzer samplerate that is much higher than the expected bitrate/baudrate that might be used on the channel. + +The last annotation emitted by the decoder will be the best bitrate guess. ''' from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/guess_bitrate/pd.py b/libsigrokdecode4DSL/decoders/guess_bitrate/pd.py index f8155030..462fa8aa 100755 --- a/libsigrokdecode4DSL/decoders/guess_bitrate/pd.py +++ b/libsigrokdecode4DSL/decoders/guess_bitrate/pd.py @@ -1,7 +1,7 @@ ## ## This file is part of the libsigrokdecode project. ## -## Copyright (C) 2013 Uwe Hermann +## Copyright (C) 2013-2016 Uwe Hermann ## ## 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 @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -24,14 +23,15 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'guess_bitrate' name = 'Guess bitrate' longname = 'Guess bitrate/baudrate' desc = 'Guess the bitrate/baudrate of a UART (or other) protocol.' license = 'gplv2+' inputs = ['logic'] - outputs = ['guess_bitrate'] + outputs = [] + tags = ['Clock/timing', 'Util'] channels = ( {'id': 'data', 'name': 'Data', 'desc': 'Data line'}, ) @@ -43,10 +43,10 @@ class Decoder(srd.Decoder): self.put(self.ss_edge, self.samplenum, self.out_ann, data) def __init__(self): - self.olddata = None + self.reset() + + def reset(self): self.ss_edge = None - self.first_transition = True - self.bitwidth = None def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) @@ -55,29 +55,25 @@ class Decoder(srd.Decoder): if key == srd.SRD_CONF_SAMPLERATE: self.samplerate = value - def decode(self, ss, es, logic): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - for (self.samplenum, pins) in logic: - (data,) = pins - logic.logic_mask = 1 - logic.cur_pos = self.samplenum - logic.edge_index = -1 - # Initialize first self.olddata with the first sample value. - if self.olddata is None: - self.olddata = data - continue + # Get the first edge on the data line. + self.wait({0: 'e'}) + self.ss_edge = self.samplenum - # Get the smallest distance between two transitions - # and use that to calculate the bitrate/baudrate. - if self.first_transition: - self.ss_edge = self.samplenum - self.first_transition = False - else: - b = self.samplenum - self.ss_edge - if self.bitwidth is None or b < self.bitwidth: - self.bitwidth = b - bitrate = int(float(self.samplerate) / float(b)) - self.putx([0, ['%d' % bitrate]]) - self.ss_edge = self.samplenum + # Get any subsequent edge on the data line. Get the smallest + # distance between any two transitions, assuming it corresponds + # to one bit time of the respective bitrate of the input stream. + # This heuristics keeps getting better for longer captures. + bitwidth = None + while True: + self.wait({0: 'e'}) + + b = self.samplenum - self.ss_edge + if bitwidth is None or b < bitwidth: + bitwidth = b + bitrate = int(float(self.samplerate) / float(b)) + self.putx([0, ['%d' % bitrate]]) + self.ss_edge = self.samplenum diff --git a/libsigrokdecode4DSL/decoders/i2cdemux/__init__.py b/libsigrokdecode4DSL/decoders/i2cdemux/__init__.py index 75fcd860..e3e9a913 100755 --- a/libsigrokdecode4DSL/decoders/i2cdemux/__init__.py +++ b/libsigrokdecode4DSL/decoders/i2cdemux/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/i2cdemux/pd.py b/libsigrokdecode4DSL/decoders/i2cdemux/pd.py index 5e83a214..d6841d32 100755 --- a/libsigrokdecode4DSL/decoders/i2cdemux/pd.py +++ b/libsigrokdecode4DSL/decoders/i2cdemux/pd.py @@ -14,14 +14,13 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'i2cdemux' name = 'I²C demux' longname = 'I²C demultiplexer' @@ -29,8 +28,12 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['i2c'] outputs = [] # TODO: Only known at run-time. + tags = ['Util'] def __init__(self): + self.reset() + + def reset(self): self.packets = [] # Local cache of I²C packets self.slaves = [] # List of known slave addresses self.stream = -1 # Current output stream diff --git a/libsigrokdecode4DSL/decoders/i2cfilter/pd.py b/libsigrokdecode4DSL/decoders/i2cfilter/pd.py index c3f148fc..7798e17a 100755 --- a/libsigrokdecode4DSL/decoders/i2cfilter/pd.py +++ b/libsigrokdecode4DSL/decoders/i2cfilter/pd.py @@ -23,7 +23,7 @@ import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'i2cfilter' name = 'I²C filter' longname = 'I²C filter' @@ -31,6 +31,7 @@ class Decoder(srd.Decoder): license = 'gplv3+' inputs = ['i2c'] outputs = ['i2c'] + tags = ['Util'] options = ( {'id': 'address', 'desc': 'Address to filter out of the I²C stream', 'default': 0}, @@ -39,6 +40,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.curslave = -1 self.curdirection = None self.packets = [] # Local cache of I²C packets diff --git a/libsigrokdecode4DSL/decoders/i2s/__init__.py b/libsigrokdecode4DSL/decoders/i2s/__init__.py index e114cd0b..a0b7097f 100755 --- a/libsigrokdecode4DSL/decoders/i2s/__init__.py +++ b/libsigrokdecode4DSL/decoders/i2s/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/i2s/pd.py b/libsigrokdecode4DSL/decoders/i2s/pd.py index f0477d10..32f873cc 100755 --- a/libsigrokdecode4DSL/decoders/i2s/pd.py +++ b/libsigrokdecode4DSL/decoders/i2s/pd.py @@ -2,6 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2012 Joel Holdsworth +## Copyright (C) 2019 DreamSourceLab ## ## 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 @@ -14,11 +15,11 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd +import struct ''' OUTPUT_PYTHON format: @@ -33,11 +34,8 @@ Packet: : integer ''' -class SamplerateError(Exception): - pass - class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'i2s' name = 'I²S' longname = 'Integrated Interchip Sound' @@ -45,11 +43,25 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['logic'] outputs = ['i2s'] + tags = ['Audio', 'PC'] channels = ( {'id': 'sck', 'name': 'SCK', 'desc': 'Bit clock line'}, {'id': 'ws', 'name': 'WS', 'desc': 'Word select line'}, {'id': 'sd', 'name': 'SD', 'desc': 'Serial data line'}, ) + options = ( + {'id': 'ws_polarity', 'desc': 'WS polarity', 'default': 'left-high', + 'values': ('left-low', 'left-high')}, + {'id': 'clk_edge', 'desc': 'SCK active edge', 'default': 'rising-edge', + 'values': ('rising-edge', 'falling-edge')}, + {'id': 'bit_shift', 'desc': 'Bit shift', 'default': 'none', + 'values': ('right-shifted by one', 'none')}, + {'id': 'bit_align', 'desc': 'Bit align', 'default': 'left-aligned', + 'values': ('left-aligned', 'right-aligned')}, + {'id': 'bitorder', 'desc': 'Bit order', + 'default': 'msb-first', 'values': ('msb-first', 'lsb-first')}, + {'id': 'wordsize', 'desc': 'Word size', 'default': 16}, + ) annotations = ( ('left', 'Left channel'), ('right', 'Right channel'), @@ -60,8 +72,10 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None - self.oldsck = 1 self.oldws = 1 self.bitcount = 0 self.data = 0 @@ -90,12 +104,12 @@ class Decoder(srd.Decoder): self.put(self.ss_block, self.samplenum, self.out_ann, data) def report(self): - # Calculate the sample rate. samplerate = '?' if self.ss_block is not None and \ self.first_sample is not None and \ - self.ss_block > self.first_sample: + self.ss_block > self.first_sample and \ + self.samplerate: samplerate = '%d' % (self.samplesreceived * self.samplerate / (self.ss_block - self.first_sample)) @@ -114,51 +128,68 @@ class Decoder(srd.Decoder): h += b'\x01\x00' # Audio format (0x0001 == PCM) h += b'\x02\x00' # Number of channels (2) h += b'\x80\x3e\x00\x00' # Samplerate (16000) - h += b'\x00\x7d\x00\x00' # Byterate (32000) + h += b'\x00\xfa\x00\x00' # Byterate (64000) h += b'\x04\x00' # Blockalign (4) - h += b'\x10\x00' # Bits per sample (16) + h += b'\x20\x00' # Bits per sample (32) # Data subchunk h += b'data' - h += b'\xff\xff\x00\x00' # Subchunk size (65535 bytes) TODO + h += b'\xff\xff\xff\xff' # Subchunk size (4G bytes) TODO return h def wav_sample(self, sample): - # TODO: This currently assumes U32 samples, and converts to S16. - s = sample >> 16 - if s >= 0x8000: - s -= 0x10000 - lo, hi = s & 0xff, (s >> 8) & 0xff - return bytes([lo, hi]) + return struct.pack(' self.bitcount: + self.putb([2, ['Received %d-bit word, expected %d-bit ' + 'word' % (self.bitcount, self.wordlength)]]) + else: + if (left_algined and msb) or (not left_algined and not msb): + self.data >>= self.bitcount - self.wordlength + else: + self.data &= int("1"*self.wordlength, 2) + self.oldws = self.oldws if left_high else not self.oldws idx = 0 if self.oldws else 1 c1 = 'Left channel' if self.oldws else 'Right channel' c2 = 'Left' if self.oldws else 'Right' @@ -167,18 +198,12 @@ class Decoder(srd.Decoder): self.putpb(['DATA', [c3, self.data]]) self.putb([idx, ['%s: %s' % (c1, v), '%s: %s' % (c2, v), '%s: %s' % (c3, v), c3]]) - #self.putbin([0, self.wav_sample(self.data)]) + self.putbin([0, self.wav_sample(self.data)]) - # Check that the data word was the correct length. - if self.wordlength != -1 and self.wordlength != self.bitcount: - self.putb([2, ['Received %d-bit word, expected %d-bit ' - 'word' % (self.bitcount, self.wordlength)]]) - - self.wordlength = self.bitcount # Reset decoder state. - self.data = 0 - self.bitcount = 0 + self.data = 0 if right_shifted else self.last + self.bitcount = 0 if right_shifted else 1 self.ss_block = self.samplenum # Save the first sample position. diff --git a/libsigrokdecode4DSL/decoders/iec/__init__.py b/libsigrokdecode4DSL/decoders/iec/__init__.py new file mode 100755 index 00000000..fa0d96f3 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/iec/__init__.py @@ -0,0 +1,24 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Marcus Comstedt +## +## 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 2 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 . +## + +''' +This protocol decoder can decode the Commodore serial IEEE-488 (IEC) protocol. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/iec/pd.py b/libsigrokdecode4DSL/decoders/iec/pd.py new file mode 100755 index 00000000..8acd1d1f --- /dev/null +++ b/libsigrokdecode4DSL/decoders/iec/pd.py @@ -0,0 +1,168 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Marcus Comstedt +## Copyright (C) 2019 DreamSourceLab +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd + +step_wait_conds = ( + [{2: 'f'}, {0: 'l', 1: 'h'}], + [{2: 'f'}, {0: 'h', 1: 'h'}, {1: 'l'}], + [{2: 'f'}, {0: 'f'}, {1: 'l'}], + [{2: 'f'}, {1: 'e'}], +) + +class Decoder(srd.Decoder): + api_version = 3 + id = 'iec' + name = 'IEC' + longname = 'Commodore IEC bus' + desc = 'Commodore serial IEEE-488 (IEC) bus protocol.' + license = 'gplv2+' + inputs = ['logic'] + outputs = [] + tags = ['PC', 'Retro computing'] + channels = ( + {'id': 'data', 'name': 'DATA', 'desc': 'Data I/O'}, + {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'}, + {'id': 'atn', 'name': 'ATN', 'desc': 'Attention'}, + ) + optional_channels = ( + {'id': 'srq', 'name': 'SRQ', 'desc': 'Service request'}, + ) + annotations = ( + ('items', 'Items'), + ('gpib', 'DAT/CMD'), + ('eoi', 'EOI'), + ) + annotation_rows = ( + ('bytes', 'Bytes', (0,)), + ('gpib', 'DAT/CMD', (1,)), + ('eoi', 'EOI', (2,)), + ) + + def __init__(self): + self.reset() + + def reset(self): + self.saved_ATN = False + self.saved_EOI = False + self.ss_item = self.es_item = None + self.step = 0 + self.bits = 0 + self.numbits = 0 + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + + def putb(self, data): + self.put(self.ss_item, self.es_item, self.out_ann, data) + + def handle_bits(self): + # Output the saved item. + dbyte = self.bits + dATN = self.saved_ATN + dEOI = self.saved_EOI + self.es_item = self.samplenum + self.putb([0, ['%02X' % dbyte]]) + + # Encode item byte to GPIB convention. + self.strgpib = ' ' + if dATN: # ATN, decode commands. + # Note: Commands < 0x20 are not used on IEC bus. + if dbyte == 0x01: self.strgpib = 'GTL' + if dbyte == 0x04: self.strgpib = 'SDC' + if dbyte == 0x05: self.strgpib = 'PPC' + if dbyte == 0x08: self.strgpib = 'GET' + if dbyte == 0x09: self.strgpib = 'TCT' + if dbyte == 0x11: self.strgpib = 'LLO' + if dbyte == 0x14: self.strgpib = 'DCL' + if dbyte == 0x15: self.strgpib = 'PPU' + if dbyte == 0x18: self.strgpib = 'SPE' + if dbyte == 0x19: self.strgpib = 'SPD' + + if dbyte == 0x3f: self.strgpib = 'UNL' + if dbyte == 0x5f: self.strgpib = 'UNT' + if dbyte > 0x1f and dbyte < 0x3f: # Address listener. + self.strgpib = 'L' + chr(dbyte + 0x10) + if dbyte > 0x3f and dbyte < 0x5f: # Address talker. + self.strgpib = 'T' + chr(dbyte - 0x10) + if dbyte > 0x5f and dbyte < 0x70: # Channel reopen. + self.strgpib = 'R' + chr(dbyte - 0x30) + if dbyte > 0xdf and dbyte < 0xf0: # Channel close. + self.strgpib = 'C' + chr(dbyte - 0xb0) + if dbyte > 0xef: # Channel open. + self.strgpib = 'O' + chr(dbyte - 0xc0) + else: + if dbyte > 0x1f and dbyte < 0x7f: + self.strgpib = chr(dbyte) + if dbyte == 0x0a: + self.strgpib = 'LF' + if dbyte == 0x0d: + self.strgpib = 'CR' + + self.putb([1, [self.strgpib]]) + self.strEOI = ' ' + if dEOI: + self.strEOI = 'EOI' + self.putb([2, [self.strEOI]]) + + def decode(self): + while True: + + (data, clk, atn, srq) = self.wait(step_wait_conds[self.step]) + + if (self.matched & (0b1 << 0)): + # Falling edge on ATN, reset step. + self.step = 0 + + if self.step == 0: + # Don't use self.matched[1] here since we might come from + # a step with different conds due to the code above. + if data == 0 and clk == 1: + # Rising edge on CLK while DATA is low: Ready to send. + self.step = 1 + elif self.step == 1: + if data == 1 and clk == 1: + # Rising edge on DATA while CLK is high: Ready for data. + self.ss_item = self.samplenum + self.saved_ATN = not atn + self.saved_EOI = False + self.bits = 0 + self.numbits = 0 + self.step = 2 + elif clk == 0: + # CLK low again, transfer aborted. + self.step = 0 + elif self.step == 2: + if data == 0 and clk == 1: + # DATA goes low while CLK is still high, EOI confirmed. + self.saved_EOI = True + elif clk == 0: + self.step = 3 + elif self.step == 3: + if (self.matched & (0b1 << 1)): + if clk == 1: + # Rising edge on CLK; latch DATA. + self.bits |= data << self.numbits + elif clk == 0: + # Falling edge on CLK; end of bit. + self.numbits += 1 + if self.numbits == 8: + self.handle_bits() + self.step = 0 diff --git a/libsigrokdecode4DSL/decoders/ir_nec/__init__.py b/libsigrokdecode4DSL/decoders/ir_nec/__init__.py index 6b15f194..c361c3dc 100755 --- a/libsigrokdecode4DSL/decoders/ir_nec/__init__.py +++ b/libsigrokdecode4DSL/decoders/ir_nec/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/ir_nec/lists.py b/libsigrokdecode4DSL/decoders/ir_nec/lists.py index 3f730d9c..7d47a46d 100755 --- a/libsigrokdecode4DSL/decoders/ir_nec/lists.py +++ b/libsigrokdecode4DSL/decoders/ir_nec/lists.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## # Addresses/devices. Items that are not listed are reserved/unknown. diff --git a/libsigrokdecode4DSL/decoders/ir_nec/pd.py b/libsigrokdecode4DSL/decoders/ir_nec/pd.py index b5d5f972..830892e8 100755 --- a/libsigrokdecode4DSL/decoders/ir_nec/pd.py +++ b/libsigrokdecode4DSL/decoders/ir_nec/pd.py @@ -2,6 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2014 Gump Yang +## Copyright (C) 2019 DreamSourceLab ## ## 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 @@ -14,8 +15,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -25,20 +25,22 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'ir_nec' name = 'IR NEC' longname = 'IR NEC' desc = 'NEC infrared remote control protocol.' license = 'gplv2+' inputs = ['logic'] - outputs = ['ir_nec'] + outputs = [] + tags = ['IR'] channels = ( {'id': 'ir', 'name': 'IR', 'desc': 'Data line'}, ) options = ( {'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low', 'values': ('active-low', 'active-high')}, + {'id': 'cd_freq', 'desc': 'Carrier Frequency', 'default': 0}, ) annotations = ( ('bit', 'Bit'), @@ -100,31 +102,37 @@ class Decoder(srd.Decoder): '%s' % btn[1]]]) def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.ss_bit = self.ss_start = self.ss_other_edge = self.ss_remote = 0 - self.data = self.count = self.active = self.old_ir = None + self.data = self.count = self.active = None self.addr = self.cmd = None def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) self.active = 0 if self.options['polarity'] == 'active-low' else 1 - self.old_ir = 1 if self.active == 0 else 0 def metadata(self, key, value): if key == srd.SRD_CONF_SAMPLERATE: self.samplerate = value - self.margin = int(self.samplerate * 0.0001) - 1 # 0.1ms + self.tolerance = 0.05 # +/-5% self.lc = int(self.samplerate * 0.0135) - 1 # 13.5ms self.rc = int(self.samplerate * 0.01125) - 1 # 11.25ms self.dazero = int(self.samplerate * 0.001125) - 1 # 1.125ms self.daone = int(self.samplerate * 0.00225) - 1 # 2.25ms self.stop = int(self.samplerate * 0.000652) - 1 # 0.652ms + def compare_with_tolerance(self, measured, base): + return (measured >= base * (1 - self.tolerance) + and measured <= base * (1 + self.tolerance)) + def handle_bit(self, tick): ret = None - if tick in range(self.dazero - self.margin, self.dazero + self.margin): + if self.compare_with_tolerance(tick, self.dazero): ret = 0 - elif tick in range(self.daone - self.margin, self.daone + self.margin): + elif self.compare_with_tolerance(tick, self.daone): ret = 1 if ret in (0, 1): self.putb([0, ['%d' % ret]]) @@ -150,32 +158,56 @@ class Decoder(srd.Decoder): self.ss_bit = self.ss_start = self.samplenum return ret == 0 - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - for (self.samplenum, pins) in data: - self.ir = pins[0] - data.itercnt += 1 - # Wait for an "interesting" edge, but also record the other ones. - if self.old_ir == self.ir: - continue + cd_count = None + if self.options['cd_freq']: + cd_count = int(self.samplerate / self.options['cd_freq']) + 1 + prev_ir = None + + while True: + # Detect changes in the presence of an active input signal. + # The decoder can either be fed an already filtered RX signal + # or optionally can detect the presence of a carrier. Periods + # of inactivity (signal changes slower than the carrier freq, + # if specified) pass on the most recently sampled level. This + # approach works for filtered and unfiltered input alike, and + # only slightly extends the active phase of input signals with + # carriers included by one period of the carrier frequency. + # IR based communication protocols can cope with this slight + # inaccuracy just fine by design. Enabling carrier detection + # on already filtered signals will keep the length of their + # active period, but will shift their signal changes by one + # carrier period before they get passed to decoding logic. + if cd_count: + (cur_ir,) = self.wait([{0: 'e'}, {'skip': cd_count}]) + if (self.matched & (0b1 << 0)): + cur_ir = self.active + if cur_ir == prev_ir: + continue + prev_ir = cur_ir + self.ir = cur_ir + else: + (self.ir,) = self.wait({0: 'e'}) + if self.ir != self.active: + # Save the non-active edge, then wait for the next edge. self.ss_other_edge = self.samplenum - self.old_ir = self.ir continue b = self.samplenum - self.ss_bit # State machine. if self.state == 'IDLE': - if b in range(self.lc - self.margin, self.lc + self.margin): + if self.compare_with_tolerance(b, self.lc): self.putpause('Long') self.putx([5, ['Leader code', 'Leader', 'LC', 'L']]) self.ss_remote = self.ss_start self.data = self.count = 0 self.state = 'ADDRESS' - elif b in range(self.rc - self.margin, self.rc + self.margin): + elif self.compare_with_tolerance(b, self.rc): self.putpause('Short') self.putstop(self.samplenum) self.samplenum += self.stop @@ -203,5 +235,3 @@ class Decoder(srd.Decoder): self.putremote() self.ss_bit = self.ss_start = self.samplenum self.state = 'IDLE' - - self.old_ir = self.ir diff --git a/libsigrokdecode4DSL/decoders/ir_rc5/__init__.py b/libsigrokdecode4DSL/decoders/ir_rc5/__init__.py index 8f5f5a37..7a209b19 100755 --- a/libsigrokdecode4DSL/decoders/ir_rc5/__init__.py +++ b/libsigrokdecode4DSL/decoders/ir_rc5/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/ir_rc5/lists.py b/libsigrokdecode4DSL/decoders/ir_rc5/lists.py index 2ac227eb..4a8c958d 100755 --- a/libsigrokdecode4DSL/decoders/ir_rc5/lists.py +++ b/libsigrokdecode4DSL/decoders/ir_rc5/lists.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## # Systems/addresses (0..31). Items that are not listed are reserved/unknown. @@ -70,7 +69,7 @@ command = { 12: ['Standby', 'StBy'], 13: ['Mute', 'M'], 14: ['Personal preferences', 'PP'], - 14: ['Display', 'Disp'], + 15: ['Display', 'Disp'], 16: ['Volume up', 'Vol+'], 17: ['Volume down', 'Vol-'], 18: ['Brightness up', 'Br+'], diff --git a/libsigrokdecode4DSL/decoders/ir_rc5/pd.py b/libsigrokdecode4DSL/decoders/ir_rc5/pd.py index 274c97af..e18a90bf 100755 --- a/libsigrokdecode4DSL/decoders/ir_rc5/pd.py +++ b/libsigrokdecode4DSL/decoders/ir_rc5/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -25,14 +24,15 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'ir_rc5' name = 'IR RC-5' longname = 'IR RC-5' desc = 'RC-5 infrared remote control protocol.' license = 'gplv2+' inputs = ['logic'] - outputs = ['ir_rc5'] + outputs = [] + tags = ['IR'] channels = ( {'id': 'ir', 'name': 'IR', 'desc': 'IR data line'}, ) @@ -57,6 +57,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.samplenum = None self.edges, self.bits, self.ss_es_bits = [], [], [] @@ -135,13 +138,12 @@ class Decoder(srd.Decoder): self.edges, self.bits, self.ss_es_bits = [], [], [] self.state = 'IDLE' - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - for (self.samplenum, pins) in data: + while True: - self.ir = pins[0] - data.itercnt += 1 + (self.ir,) = self.wait() # Wait for any edge (rising or falling). if self.old_ir == self.ir: @@ -149,8 +151,9 @@ class Decoder(srd.Decoder): # State machine. if self.state == 'IDLE': + bit = 1 self.edges.append(self.samplenum) - self.bits.append([self.samplenum, 1]) + self.bits.append([self.samplenum, bit]) self.state = 'MID1' self.old_ir = self.ir continue diff --git a/libsigrokdecode4DSL/decoders/jitter/__init__.py b/libsigrokdecode4DSL/decoders/jitter/__init__.py index e223e736..3394ad75 100755 --- a/libsigrokdecode4DSL/decoders/jitter/__init__.py +++ b/libsigrokdecode4DSL/decoders/jitter/__init__.py @@ -14,14 +14,14 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' This protocol decoder retrieves the timing jitter between two digital signals. It allows to define a clock source channel and a resulting signal channel. + Each time a significant edge is detected in the clock source, we calculate the elapsed time before the resulting signal answers and report the timing jitter. ''' diff --git a/libsigrokdecode4DSL/decoders/jitter/pd.py b/libsigrokdecode4DSL/decoders/jitter/pd.py index 7909c946..8ea1aa67 100755 --- a/libsigrokdecode4DSL/decoders/jitter/pd.py +++ b/libsigrokdecode4DSL/decoders/jitter/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -31,14 +30,15 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'jitter' name = 'Jitter' longname = 'Timing jitter calculation' desc = 'Retrieves the timing jitter between two digital signals.' license = 'gplv2+' inputs = ['logic'] - outputs = ['jitter'] + outputs = [] + tags = ['Clock/timing', 'Util'] channels = ( {'id': 'clk', 'name': 'Clock', 'desc': 'Clock reference channel'}, {'id': 'sig', 'name': 'Resulting signal', 'desc': 'Resulting signal controlled by the clock'}, @@ -64,10 +64,12 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'CLK' self.samplerate = None - self.oldpin = None - self.oldclk = self.oldsig = None + self.oldclk, self.oldsig = 0, 0 self.clk_start = None self.sig_start = None self.clk_missed = 0 @@ -111,8 +113,8 @@ class Decoder(srd.Decoder): return # Format the delta to an ASCII float value terminated by a newline. x = str(delta) + '\n' - #self.put(self.clk_start, self.sig_start, self.out_binary, - # [0, x.encode('UTF-8')]) + self.put(self.clk_start, self.sig_start, self.out_binary, + [0, x.encode('UTF-8')]) # Helper function for missed clock and signal annotations. def putm(self, data): @@ -174,20 +176,12 @@ class Decoder(srd.Decoder): # everything we can with this sample. return True - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - - for (self.samplenum, pins) in data: - data.itercnt += 1 - # We are only interested in transitions. - if self.oldpin == pins: - continue - - self.oldpin, (clk, sig) = pins, pins - - if self.oldclk is None and self.oldsig is None: - self.oldclk, self.oldsig = clk, sig + while True: + # Wait for a transition on CLK and/or SIG. + (clk, sig) = self.wait([{0: 'e'}, {1: 'e'}]) # State machine: # For each sample we can move 2 steps forward in the state machine. diff --git a/libsigrokdecode4DSL/decoders/jtag/__init__.py b/libsigrokdecode4DSL/decoders/jtag/__init__.py index 863ef09a..51bb6299 100755 --- a/libsigrokdecode4DSL/decoders/jtag/__init__.py +++ b/libsigrokdecode4DSL/decoders/jtag/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/jtag/pd.py b/libsigrokdecode4DSL/decoders/jtag/pd.py index b0bae30e..e9c629b6 100755 --- a/libsigrokdecode4DSL/decoders/jtag/pd.py +++ b/libsigrokdecode4DSL/decoders/jtag/pd.py @@ -2,6 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2012-2015 Uwe Hermann +## Copyright (C) 2019 DreamSourceLab ## ## Version: ## Modified by Shiqiu Nie(369614718@qq.com) @@ -22,16 +23,17 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd ''' OUTPUT_PYTHON format: + Packet: [, ] + : - 'NEW STATE': is the new state of the JTAG state machine. Valid values: 'TEST-LOGIC-RESET', 'RUN-TEST/IDLE', 'SELECT-DR-SCAN', @@ -42,6 +44,7 @@ Packet: - 'IR TDO': Bitstring that was clocked out of the IR register. - 'DR TDI': Bitstring that was clocked into the DR register. - 'DR TDO': Bitstring that was clocked out of the DR register. + All bitstrings are a list consisting of two items. The first is a sequence of '1' and '0' characters (the right-most character is the LSB. Example: '01110001', where 1 is the LSB). The second item is a list of ss/es values @@ -60,7 +63,7 @@ jtag_states = [ ] class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'jtag' name = 'JTAG' longname = 'Joint Test Action Group (IEEE 1149.1)' @@ -68,6 +71,7 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['logic'] outputs = ['jtag'] + tags = ['Debug/trace'] channels = ( {'id': 'tdi', 'name': 'TDI', 'desc': 'Test data input'}, {'id': 'tdo', 'name': 'TDO', 'desc': 'Test data output'}, @@ -94,16 +98,16 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): # self.state = 'TEST-LOGIC-RESET' self.state = 'RUN-TEST/IDLE' self.oldstate = None - self.oldpins = (-1, -1, -1, -1) - self.oldtck = -1 self.bits_tdi = [] self.bits_tdo = [] self.bits_samplenums_tdi = [] self.bits_samplenums_tdo = [] - self.samplenum = 0 self.ss_item = self.es_item = None self.ss_bitstring = self.es_bitstring = None self.saved_item = None @@ -169,7 +173,7 @@ class Decoder(srd.Decoder): elif self.state == 'UPDATE-IR': self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE' - def handle_rising_tck_edge(self, tdi, tdo, tck, tms): + def handle_rising_tck_edge(self, tdi, tdo, tck, tms, trst, srst, rtck): # Rising TCK edges always advance the state machine. self.advance_state_machine(tms) @@ -278,28 +282,8 @@ class Decoder(srd.Decoder): self.ss_item = self.samplenum - def decode(self, ss, es, data): - for (self.samplenum, pins) in data: - data.itercnt += 1 - # If none of the pins changed, there's nothing to do. - if self.oldpins == pins: - continue - - # Store current pin values for the next round. - self.oldpins = pins - - # Get individual pin values into local variables. - # Unused channels will have a value of > 1. - (tdi, tdo, tck, tms, trst, srst, rtck) = pins - - # We only care about TCK edges (either rising or falling). - if (self.oldtck == tck): - continue - - # Store start/end sample for later usage. - self.ss, self.es = ss, es - - if (self.oldtck == 0 and tck == 1): - self.handle_rising_tck_edge(tdi, tdo, tck, tms) - - self.oldtck = tck + def decode(self): + while True: + # Wait for a rising edge on TCK. + (tdi, tdo, tck, tms, trst, srst, rtck) = self.wait({2: 'r'}) + self.handle_rising_tck_edge(tdi, tdo, tck, tms, trst, srst, rtck) diff --git a/libsigrokdecode4DSL/decoders/jtag_ejtag/__init__.py b/libsigrokdecode4DSL/decoders/jtag_ejtag/__init__.py new file mode 100755 index 00000000..1c66dcd5 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/jtag_ejtag/__init__.py @@ -0,0 +1,25 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Vladislav Ivanov +## +## 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 2 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 . +## + +''' +This decoder stacks on top of the 'jtag' PD and decodes JTAG data specific +to the MIPS EJTAG protocol. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/jtag_ejtag/pd.py b/libsigrokdecode4DSL/decoders/jtag_ejtag/pd.py new file mode 100755 index 00000000..f16f0b4e --- /dev/null +++ b/libsigrokdecode4DSL/decoders/jtag_ejtag/pd.py @@ -0,0 +1,408 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Vladislav Ivanov +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd +from common.srdhelper import bin2int + +class Instruction(object): + IDCODE = 0x01 + IMPCODE = 0x03 + ADDRESS = 0x08 + DATA = 0x09 + CONTROL = 0x0A + ALL = 0x0B + EJTAGBOOT = 0x0C + NORMALBOOT = 0x0D + FASTDATA = 0x0E + TCBCONTROLA = 0x10 + TCBCONTROLB = 0x11 + TCBDATA = 0x12 + TCBCONTROLC = 0x13 + PCSAMPLE = 0x14 + TCBCONTROLD = 0x15 + TCBCONTROLE = 0x16 + +class State(object): + RESET = 0 + DEVICE_ID = 1 + IMPLEMENTATION = 2 + DATA = 3 + ADDRESS = 4 + CONTROL = 5 + FASTDATA = 6 + PC_SAMPLE = 7 + BYPASS = 8 + +class ControlReg(object): + PRACC = (1 << 18) + PRNW = (1 << 19) + +class Ann(object): + INSTRUCTION = 0 + REGISTER = 1 + CONTROL_FIELD_IN = 10 + CONTROL_FIELD_OUT = 11 + PRACC = 12 + +ejtag_insn = { + 0x00: ['Free', 'Boundary scan'], + 0x01: ['IDCODE', 'Select Device Identification (ID) register'], + 0x02: ['Free', 'Boundary scan'], + 0x03: ['IMPCODE', 'Select Implementation register'], + 0x08: ['ADDRESS', 'Select Address register'], + 0x09: ['DATA', 'Select Data register'], + 0x0A: ['CONTROL', 'Select EJTAG Control register'], + 0x0B: ['ALL', 'Select the Address, Data and EJTAG Control registers'], + 0x0C: ['EJTAGBOOT', 'Fetch code from the debug exception vector after reset'], + 0x0D: ['NORMALBOOT', 'Execute the reset handler after reset'], + 0x0E: ['FASTDATA', 'Select the Data and Fastdata registers'], + 0x0F: ['Reserved', 'Reserved'], + 0x10: ['TCBCONTROLA', 'Select the control register TCBTraceControl'], + 0x11: ['TCBCONTROLB', 'Selects trace control block register B'], + 0x12: ['TCBDATA', 'Access the registers specified by TCBCONTROLB'], + 0x13: ['TCBCONTROLC', 'Select trace control block register C'], + 0x14: ['PCSAMPLE', 'Select the PCsample register'], + 0x15: ['TCBCONTROLD', 'Select trace control block register D'], + 0x16: ['TCBCONTROLE', 'Select trace control block register E'], + 0x17: ['FDC', 'Select Fast Debug Channel'], + 0x1C: ['Free', 'Boundary scan'], +} + +ejtag_reg = { + 0x00: 'RESET', + 0x01: 'DEVICE_ID', + 0x02: 'IMPLEMENTATION', + 0x03: 'DATA', + 0x04: 'ADDRESS', + 0x05: 'CONTROL', + 0x06: 'FASTDATA', + 0x07: 'PC_SAMPLE', + 0x08: 'BYPASS', +} + +ejtag_control_reg = [ + [31, 31, 'Rocc', [ + # Read + ['No reset ocurred', 'Reset ocurred'], + # Write + ['Acknowledge reset', 'No effect'], + ]], + [30, 29, 'Psz', [ + ['Access: byte', 'Access: halfword', 'Access: word', 'Access: triple'], + ]], + [23, 23, 'VPED', [ + ['VPE disabled', 'VPE enabled'], + ]], + [22, 22, 'Doze', [ + ['Processor is not in low-power mode', 'Processor is in low-power mode'], + ]], + [21, 21, 'Halt', [ + ['Internal system bus clock is running', 'Internal system bus clock is stopped'], + ]], + [20, 20, 'Per Rst', [ + ['No peripheral reset applied', 'Peripheral reset applied'], + ['Deassert peripheral reset', 'Assert peripheral reset'], + ]], + [19, 19, 'PRn W', [ + ['Read processor access', 'Write processor access'], + ]], + [18, 18, 'Pr Acc', [ + ['No pending processor access', 'Pending processor access'], + ['Finish processor access', 'Don\'t finish processor access'], + ]], + [16, 16, 'Pr Rst', [ + ['No processor reset applied', 'Processor reset applied'], + ['Deassert processor reset', 'Assert system reset'], + ]], + [15, 15, 'Prob En', [ + ['Probe will not serve processor accesses', 'Probe will service processor accesses'], + ]], + [14, 14, 'Prob Trap', [ + ['Default location', 'DMSEG fetch'], + ['Set to default location', 'Set to DMSEG fetch'], + ]], + [13, 13, 'ISA On Debug', [ + ['MIPS32/MIPS64 ISA', 'microMIPS ISA'], + ['Set to MIPS32/MIPS64 ISA', 'Set to microMIPS ISA'], + ]], + [12, 12, 'EJTAG Brk', [ + ['No pending debug interrupt', 'Pending debug interrupt'], + ['No effect', 'Request debug interrupt'], + ]], + [3, 3, 'DM', [ + ['Not in debug mode', 'In debug mode'], + ]], +] + +ejtag_state_map = { + Instruction.IDCODE: State.DEVICE_ID, + Instruction.IMPCODE: State.IMPLEMENTATION, + Instruction.DATA: State.DATA, + Instruction.ADDRESS: State.ADDRESS, + Instruction.CONTROL: State.CONTROL, + Instruction.FASTDATA: State.FASTDATA, +} + +class RegData(object): + def __init__(self): + self.ss = None + self.es = None + self.data = None + +class LastData(object): + def __init__(self): + self.data_in = RegData() + self.data_out = RegData() + +class PraccState(object): + def reset(self): + self.address_in = None + self.address_out = None + self.data_in = None + self.data_out = None + self.write = False + self.ss = 0 + self.es = 0 + + def __init__(self): + self.reset() + +regs_items = { + 'ann': tuple([tuple([s.lower(), s]) for s in list(ejtag_reg.values())]), + 'rows_range': tuple(range(1, 1 + 9)), +} + +class Decoder(srd.Decoder): + api_version = 3 + id = 'jtag_ejtag' + name = 'JTAG / EJTAG' + longname = 'Joint Test Action Group / EJTAG (MIPS)' + desc = 'MIPS EJTAG protocol.' + license = 'gplv2+' + inputs = ['jtag'] + outputs = [] + tags = ['Debug/trace'] + annotations = ( + ('instruction', 'Instruction'), + ) + regs_items['ann'] + ( + ('control_field_in', 'Control field in'), + ('control_field_out', 'Control field out'), + ('pracc', 'PrAcc'), + ) + annotation_rows = ( + ('instructions', 'Instructions', (0,)), + ('regs', 'Registers', regs_items['rows_range']), + ('control_fields_in', 'Control fields in', (10,)), + ('control_fields_out', 'Control fields out', (11,)), + ('pracc', 'PrAcc', (12,)), + ) + + def __init__(self): + self.reset() + + def reset(self): + self.state = State.RESET + self.pracc_state = PraccState() + + def put_current(self, data): + self.put(self.ss, self.es, self.out_ann, data) + + def put_at(self, ss: int, es: int, data): + self.put(ss, es, self.out_ann, data) + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + + def select_reg(self, ir_value: int): + self.state = ejtag_state_map.get(ir_value, State.RESET) + + def parse_pracc(self): + control_in = bin2int(self.last_data['in']['data'][0]) + control_out = bin2int(self.last_data['out']['data'][0]) + + # Check if JTAG master acknowledges a pending PrAcc. + if not ((not (control_in & ControlReg.PRACC)) and \ + (control_out & ControlReg.PRACC)): + return + + ss, es = self.pracc_state.ss, self.pracc_state.es + pracc_write = (control_out & ControlReg.PRNW) != 0 + + s = 'PrAcc: ' + s += 'Store' if pracc_write else 'Load/Fetch' + + if pracc_write: + if self.pracc_state.address_out is not None: + s += ', A:' + ' 0x{:08X}'.format(self.pracc_state.address_out) + if self.pracc_state.data_out is not None: + s += ', D:' + ' 0x{:08X}'.format(self.pracc_state.data_out) + else: + if self.pracc_state.address_out is not None: + s += ', A:' + ' 0x{:08X}'.format(self.pracc_state.address_out) + if self.pracc_state.data_in is not None: + s += ', D:' + ' 0x{:08X}'.format(self.pracc_state.data_in) + + self.pracc_state.reset() + + self.put_at(ss, es, [Ann.PRACC, [s]]) + + def parse_control_reg(self, ann): + reg_write = ann == Ann.CONTROL_FIELD_IN + control_bit_positions = [] + data_select = 'in' if (reg_write) else 'out' + + control_bit_positions = self.last_data[data_select]['data'][1] + control_data = self.last_data[data_select]['data'][0] + + # Annotate control register fields. + for field in ejtag_control_reg: + start_bit = 31 - field[1] + end_bit = 31 - field[0] + comment = field[2] + value_descriptions = [] + + if reg_write: + if len(field[3]) < 2: + continue + value_descriptions = field[3][1] + else: + value_descriptions = field[3][0] + + ss = control_bit_positions[start_bit][0] + es = control_bit_positions[end_bit][1] + + value_str = control_data[end_bit : start_bit + 1] + value_index = bin2int(value_str) + + short_desc = comment + ': ' + value_str + long_desc = value_descriptions[value_index] if len(value_descriptions) > value_index else '?' + + self.put_at(ss, es, [ann, [long_desc, short_desc]]) + + def check_last_data(self): + if not hasattr(self, 'last_data'): + self.last_data = {'in': {}, 'out': {}} + + def handle_fastdata(self, val, ann): + spracc_write_desc = { + 0: ['0', 'SPrAcc: 0', 'Request completion of Fastdata access'], + 1: ['1', 'SPrAcc: 1', 'No effect'], + } + spracc_read_desc = { + 0: ['0', 'SPrAcc: 0', 'Fastdata access failure'], + 1: ['1', 'SPrAcc: 1', 'Successful completion of Fastdata access'], + } + + bitstring = val[0] + bit_sample_pos = val[1] + fastdata_state = bitstring[32] + data = bin2int(bitstring[0:32]) + + fastdata_bit_pos = bit_sample_pos[32] + data_pos = [bit_sample_pos[31][0], bit_sample_pos[0][1]] + + ss_fastdata, es_fastdata = fastdata_bit_pos + ss_data, es_data = data_pos + + display_data = [ann, ['0x{:08X}'.format(data)]] + spracc_display_data = [] + + if ann == Ann.CONTROL_FIELD_IN: + spracc_display_data = [ann, spracc_write_desc[int(fastdata_state)]] + elif ann == Ann.CONTROL_FIELD_OUT: + spracc_display_data = [ann, spracc_read_desc[int(fastdata_state)]] + + self.put_at(ss_fastdata, es_fastdata, spracc_display_data) + self.put_at(ss_data, es_data, display_data) + + def handle_dr_tdi(self, val): + value = bin2int(val[0]) + self.check_last_data() + self.last_data['in'] = {'ss': self.ss, 'es': self.es, 'data': val} + + self.pracc_state.ss, self.pracc_state.es = self.ss, self.es + + if self.state == State.ADDRESS: + self.pracc_state.address_in = value + elif self.state == State.DATA: + self.pracc_state.data_in = value + elif self.state == State.FASTDATA: + self.handle_fastdata(val, Ann.CONTROL_FIELD_IN) + + def handle_dr_tdo(self, val): + value = bin2int(val[0]) + self.check_last_data() + self.last_data['out'] = {'ss': self.ss, 'es': self.es, 'data': val} + if self.state == State.ADDRESS: + self.pracc_state.address_out = value + elif self.state == State.DATA: + self.pracc_state.data_out = value + elif self.state == State.FASTDATA: + self.handle_fastdata(val, Ann.CONTROL_FIELD_OUT) + + def handle_ir_tdi(self, val): + code = bin2int(val[0]) + hexval = '0x{:02X}'.format(code) + if code in ejtag_insn: + # Format instruction name. + insn = ejtag_insn[code] + s_short = insn[0] + s_long = insn[0] + ': ' + insn[1] + ' (' + hexval + ')' + # Display it and select data register. + self.put_current([Ann.INSTRUCTION, [s_long, s_short]]) + else: + self.put_current([Ann.INSTRUCTION, [hexval, 'IR TDI ({})'.format(hexval)]]) + self.select_reg(code) + + def handle_new_state(self, new_state): + if new_state != 'UPDATE-DR' or not hasattr(self, 'last_data'): + return + + if self.state == State.RESET: + return + + reg_name = ejtag_reg[self.state] + ann_index = Ann.REGISTER + self.state + display_data = [ann_index, [reg_name]] + self.put_at(self.last_data['in']['ss'], self.last_data['in']['es'], display_data) + + if self.state == State.CONTROL: + control_bit_positions = self.last_data['in']['data'][1] + bit_count = len(control_bit_positions) + # Check if control register data length is correct. + if bit_count != 32: + error_display = [Ann.REGISTER, ['Error: length != 32']] + self.put_at(self.last_data['in']['ss'], self.last_data['in']['es'], error_display) + return + self.parse_control_reg(Ann.CONTROL_FIELD_IN) + self.parse_control_reg(Ann.CONTROL_FIELD_OUT) + self.parse_pracc() + + def decode(self, ss: int, es: int, data): + cmd, val = data + self.ss, self.es = ss, es + + if cmd == 'IR TDI': + self.handle_ir_tdi(val) + elif cmd == 'DR TDI': + self.handle_dr_tdi(val) + elif cmd == 'DR TDO': + self.handle_dr_tdo(val) + elif cmd == 'NEW STATE': + self.handle_new_state(val) diff --git a/libsigrokdecode4DSL/decoders/jtag_stm32/__init__.py b/libsigrokdecode4DSL/decoders/jtag_stm32/__init__.py index 9d60c1cb..bf69e8e0 100755 --- a/libsigrokdecode4DSL/decoders/jtag_stm32/__init__.py +++ b/libsigrokdecode4DSL/decoders/jtag_stm32/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/jtag_stm32/pd.py b/libsigrokdecode4DSL/decoders/jtag_stm32/pd.py index ff5bb770..82558b82 100755 --- a/libsigrokdecode4DSL/decoders/jtag_stm32/pd.py +++ b/libsigrokdecode4DSL/decoders/jtag_stm32/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -32,6 +31,11 @@ ir = { '1000': ['ABORT', 35], # Abort register # TODO: 32 bits? Datasheet typo? } +# Boundary scan data registers (in IR[8:4]) and their sizes (in bits) +bs_ir = { + '11111': ['BYPASS', 1], # Bypass register +} + # ARM Cortex-M3 r1p1-01rel0 ID code cm3_idcode = 0x3ba00477 @@ -135,14 +139,15 @@ def data_out(bits): % (data_hex, ack_meaning) class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'jtag_stm32' name = 'JTAG / STM32' longname = 'Joint Test Action Group / ST STM32' desc = 'ST STM32-specific JTAG protocol.' license = 'gplv2+' inputs = ['jtag'] - outputs = ['jtag_stm32'] + outputs = [] + tags = ['Debug/trace'] annotations = ( ('item', 'Item'), ('field', 'Field'), @@ -157,6 +162,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.samplenums = None @@ -173,37 +181,35 @@ class Decoder(srd.Decoder): self.putx([0, ['BYPASS: ' + bits]]) def handle_reg_idcode(self, cmd, bits): - # IDCODE is a read-only register which is always accessible. - # IR == IDCODE: The 32bit device ID code is shifted out via DR next. + bits = bits[1:] - id_hex, manuf, ver, part = decode_device_id_code(bits[:-1]) - cc = '0x%x' % int('0b' + bits[:-1][-12:-8], 2) - ic = '0x%x' % int('0b' + bits[:-1][-7:-1], 2) + id_hex, manuf, ver, part = decode_device_id_code(bits) + cc = '0x%x' % int('0b' + bits[-12:-8], 2) + ic = '0x%x' % int('0b' + bits[-7:-1], 2) - self.putf(0, 0, [1, ['Reserved (BS TAP)', 'BS', 'B']]) - self.putf(1, 1, [1, ['Reserved', 'Res', 'R']]) - self.putf(9, 12, [0, ['Continuation code: %s' % cc, 'CC', 'C']]) - self.putf(2, 8, [0, ['Identity code: %s' % ic, 'IC', 'I']]) - self.putf(2, 12, [1, ['Manufacturer: %s' % manuf, 'Manuf', 'M']]) - self.putf(13, 28, [1, ['Part: %s' % part, 'Part', 'P']]) - self.putf(29, 32, [1, ['Version: %s' % ver, 'Version', 'V']]) + self.putf(0, 0, [1, ['Reserved', 'Res', 'R']]) + self.putf(8, 11, [0, ['Continuation code: %s' % cc, 'CC', 'C']]) + self.putf(1, 7, [0, ['Identity code: %s' % ic, 'IC', 'I']]) + self.putf(1, 11, [1, ['Manufacturer: %s' % manuf, 'Manuf', 'M']]) + self.putf(12, 27, [1, ['Part: %s' % part, 'Part', 'P']]) + self.putf(28, 31, [1, ['Version: %s' % ver, 'Version', 'V']]) + self.putf(32, 32, [1, ['BYPASS (BS TAP)', 'BS', 'B']]) - self.ss = self.samplenums[1][0] self.putx([2, ['IDCODE: %s (%s: %s/%s)' % \ - decode_device_id_code(bits[:-1])]]) + decode_device_id_code(bits)]]) def handle_reg_dpacc(self, cmd, bits): - bits = bits[:-1] + bits = bits[1:] s = data_in('DPACC', bits) if (cmd == 'DR TDI') else data_out(bits) self.putx([2, [s]]) def handle_reg_apacc(self, cmd, bits): - bits = bits[:-1] + bits = bits[1:] s = data_in('APACC', bits) if (cmd == 'DR TDI') else data_out(bits) self.putx([2, [s]]) def handle_reg_abort(self, cmd, bits): - bits = bits[:-1] + bits = bits[1:] # Bits[31:1]: reserved. Bit[0]: DAPABORT. a = '' if (bits[0] == '1') else 'No ' s = 'DAPABORT = %s: %sDAP abort generated' % (bits[0], a) @@ -214,7 +220,7 @@ class Decoder(srd.Decoder): self.putx([3, ['WARNING: DAPABORT[31:1] reserved!']]) def handle_reg_unknown(self, cmd, bits): - bits = bits[:-1] + bits = bits[1:] self.putx([2, ['Unknown instruction: %s' % bits]]) def decode(self, ss, es, data): @@ -227,22 +233,19 @@ class Decoder(srd.Decoder): val, self.samplenums = val self.samplenums.reverse() - # State machine - if self.state == 'IDLE': - # Wait until a new instruction is shifted into the IR register. - if cmd != 'IR TDI': - return + if cmd == 'IR TDI': # Switch to the state named after the instruction, or 'UNKNOWN'. # The STM32F10xxx has two serially connected JTAG TAPs, the # boundary scan tap (5 bits) and the Cortex-M3 TAP (4 bits). # See UM 31.5 "STM32F10xxx JTAG TAP connection" for details. - self.state = ir.get(val[:-1][-4:], ['UNKNOWN', 0])[0] - bstap_ir = ir.get(val[:-1][:4], ['UNKNOWN', 0])[0] - self.putf(5, 8, [1, ['IR (BS TAP): ' + bstap_ir]]) - self.putf(1, 4, [1, ['IR (M3 TAP): ' + self.state]]) - self.putf(0, 0, [1, ['Reserved (BS TAP)', 'BS', 'B']]) + self.state = ir.get(val[5:9], ['UNKNOWN', 0])[0] + bstap_ir = bs_ir.get(val[:5], ['UNKNOWN', 0])[0] + self.putf(4, 8, [1, ['IR (BS TAP): ' + bstap_ir]]) + self.putf(0, 3, [1, ['IR (M3 TAP): ' + self.state]]) self.putx([2, ['IR: %s' % self.state]]) - elif self.state == 'BYPASS': + + # State machine + if self.state == 'BYPASS': # Here we're interested in incoming bits (TDI). if cmd != 'DR TDI': return diff --git a/libsigrokdecode4DSL/decoders/lin/__init__.py b/libsigrokdecode4DSL/decoders/lin/__init__.py new file mode 100755 index 00000000..f5b2835f --- /dev/null +++ b/libsigrokdecode4DSL/decoders/lin/__init__.py @@ -0,0 +1,28 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Stephan Thiele +## +## 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 2 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 . +## + +''' +This decoder stacks on top of the 'uart' PD and decodes the LIN +(Local Interconnect Network) protocol. + +LIN is layered on top of the UART (async serial) protocol, with 8n1 settings. +Bytes are sent LSB-first. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/lin/pd.py b/libsigrokdecode4DSL/decoders/lin/pd.py new file mode 100755 index 00000000..c6db6787 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/lin/pd.py @@ -0,0 +1,235 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Stephan Thiele +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd + +class LinFsm: + class State: + WaitForBreak = 'WAIT_FOR_BREAK' + Sync = 'SYNC' + Pid = 'PID' + Data = 'DATA' + Checksum = 'CHECKSUM' + Error = 'ERROR' + + def transit(self, target_state): + if not self._transition_allowed(target_state): + return False + self.state = target_state + return True + + def _transition_allowed(self, target_state): + if target_state == LinFsm.State.Error: + return True + return target_state in self.allowed_state[self.state] + + def reset(self): + self.state = LinFsm.State.WaitForBreak + + def __init__(self): + a = dict() + a[LinFsm.State.WaitForBreak] = (LinFsm.State.Sync,) + a[LinFsm.State.Sync] = (LinFsm.State.Pid,) + a[LinFsm.State.Pid] = (LinFsm.State.Data,) + a[LinFsm.State.Data] = (LinFsm.State.Data, LinFsm.State.Checksum) + a[LinFsm.State.Checksum] = (LinFsm.State.WaitForBreak,) + a[LinFsm.State.Error] = (LinFsm.State.Sync,) + self.allowed_state = a + + self.state = None + self.reset() + +class Decoder(srd.Decoder): + api_version = 3 + id = 'lin' + name = 'LIN' + longname = 'Local Interconnect Network' + desc = 'Local Interconnect Network (LIN) protocol.' + license = 'gplv2+' + inputs = ['uart'] + outputs = [] + tags = ['Automotive'] + options = ( + {'id': 'version', 'desc': 'Protocol version', 'default': 2, 'values': (1, 2)}, + ) + annotations = ( + ('data', 'LIN data'), + ('control', 'Protocol info'), + ('error', 'Error descriptions'), + ('inline_error', 'Protocol violations and errors'), + ) + annotation_rows = ( + ('data', 'Data', (0, 1, 3)), + ('error', 'Error', (2,)), + ) + + def __init__(self): + self.reset() + + def reset(self): + self.fsm = LinFsm() + self.lin_header = [] + self.lin_rsp = [] + self.lin_version = None + self.out_ann = None + self.ss_block = None + self.es_block = None + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + self.lin_version = self.options['version'] + + def putx(self, data): + self.put(self.ss_block, self.es_block, self.out_ann, data) + + def wipe_break_null_byte(self, value): + # Upon a break condition a null byte is received which must be ignored. + if self.fsm.state not in (LinFsm.State.WaitForBreak, LinFsm.State.Error): + if len(self.lin_rsp): + value = self.lin_rsp.pop()[2] + else: + self.lin_header.pop() + + if value != 0: + self.fsm.transit(LinFsm.State.Error) + self.handle_error(None) + return False + + return True + + def handle_wait_for_break(self, value): + self.wipe_break_null_byte(value) + + def handle_break(self, value): + if self.fsm.state not in (LinFsm.State.WaitForBreak, LinFsm.State.Error): + if self.wipe_break_null_byte(value): + self.fsm.transit(LinFsm.State.Checksum) + self.handle_checksum() + + self.fsm.reset() + self.fsm.transit(LinFsm.State.Sync) + + self.putx([1, ['Break condition', 'Break', 'Brk', 'B']]) + + def handle_sync(self, value): + self.fsm.transit(LinFsm.State.Pid) + self.lin_header.append((self.ss_block, self.es_block, value)) + + def handle_pid(self, value): + self.fsm.transit(LinFsm.State.Data) + self.lin_header.append((self.ss_block, self.es_block, value)) + + def handle_data(self, value): + self.lin_rsp.append((self.ss_block, self.es_block, value)) + + def handle_checksum(self): + sync = self.lin_header.pop(0) if len(self.lin_header) else None + + self.put(sync[0], sync[1], self.out_ann, [0, ['Sync', 'S']]) + + if sync[2] != 0x55: + self.put(sync[0], sync[1], self.out_ann, + [2, ['Sync is not 0x55', 'Not 0x55', '!= 0x55']]) + + pid = self.lin_header.pop(0) if len(self.lin_header) else None + checksum = self.lin_rsp.pop() if len(self.lin_rsp) else None + + if pid: + id_ = pid[2] & 0x3F + parity = pid[2] >> 6 + + expected_parity = self.calc_parity(pid[2]) + parity_valid = parity == expected_parity + + if not parity_valid: + self.put(pid[0], pid[1], self.out_ann, [2, ['P != %d' % expected_parity]]) + + ann_class = 0 if parity_valid else 3 + self.put(pid[0], pid[1], self.out_ann, [ann_class, [ + 'ID: %02X Parity: %d (%s)' % (id_, parity, 'ok' if parity_valid else 'bad'), + 'ID: 0x%02X' % id_, 'I: %d' % id_ + ]]) + + if len(self.lin_rsp): + checksum_valid = self.checksum_is_valid(pid[2], self.lin_rsp, checksum[2]) + + for b in self.lin_rsp: + self.put(b[0], b[1], self.out_ann, [0, ['Data: 0x%02X' % b[2], 'D: 0x%02X' % b[2]]]) + + ann_class = 0 if checksum_valid else 3 + self.put(checksum[0], checksum[1], self.out_ann, + [ann_class, ['Checksum: 0x%02X' % checksum[2], 'Checksum', 'Chk', 'C']]) + + if not checksum_valid: + self.put(checksum[0], checksum[1], self.out_ann, [2, ['Checksum invalid']]) + else: + pass # No response. + + self.lin_header.clear() + self.lin_rsp.clear() + + def handle_error(self, dummy): + self.putx([3, ['Error', 'Err', 'E']]) + + def checksum_is_valid(self, pid, data, checksum): + if self.lin_version == 2: + id_ = pid & 0x3F + + if id_ != 60 and id_ != 61: + checksum += pid + + for d in data: + checksum += d[2] + + carry_bits = int(checksum / 256) + checksum += carry_bits + + return checksum & 0xFF == 0xFF + + @staticmethod + def calc_parity(pid): + id_ = [((pid & 0x3F) >> i) & 1 for i in range(8)] + + p0 = id_[0] ^ id_[1] ^ id_[2] ^ id_[4] + p1 = not (id_[1] ^ id_[3] ^ id_[4] ^ id_[5]) + + return (p0 << 0) | (p1 << 1) + + def decode(self, ss, es, data): + ptype, rxtx, pdata = data + + self.ss_block, self.es_block = ss, es + + # Ignore all UART packets except the actual data packets or BREAK. + if ptype == 'BREAK': + self.handle_break(pdata) + if ptype != 'DATA': + return + + # We're only interested in the byte value (not individual bits). + pdata = pdata[0] + + # Short LIN overview: + # - Message begins with a BREAK (0x00) for at least 13 bittimes. + # - Break is always followed by a SYNC byte (0x55). + # - Sync byte is followed by a PID byte (Protected Identifier). + # - PID byte is followed by 1 - 8 data bytes and a final checksum byte. + + handler = getattr(self, 'handle_%s' % self.fsm.state.lower()) + handler(pdata) diff --git a/libsigrokdecode4DSL/decoders/lm75/__init__.py b/libsigrokdecode4DSL/decoders/lm75/__init__.py index 1026df1b..83ce811b 100755 --- a/libsigrokdecode4DSL/decoders/lm75/__init__.py +++ b/libsigrokdecode4DSL/decoders/lm75/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/lm75/pd.py b/libsigrokdecode4DSL/decoders/lm75/pd.py index 50e3c913..14df1b52 100755 --- a/libsigrokdecode4DSL/decoders/lm75/pd.py +++ b/libsigrokdecode4DSL/decoders/lm75/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## # TODO: Better support for various LM75 compatible devices. @@ -40,14 +39,15 @@ ft = { } class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'lm75' name = 'LM75' longname = 'National LM75' desc = 'National LM75 (and compatibles) temperature sensor.' license = 'gplv2+' inputs = ['i2c'] - outputs = ['lm75'] + outputs = [] + tags = ['Sensor'] options = ( {'id': 'sensor', 'desc': 'Sensor type', 'default': 'lm75', 'values': ('lm75',)}, @@ -63,6 +63,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.reg = 0x00 # Currently selected register self.databytes = [] diff --git a/libsigrokdecode4DSL/decoders/lpc/__init__.py b/libsigrokdecode4DSL/decoders/lpc/__init__.py index 2c2c430c..52277587 100755 --- a/libsigrokdecode4DSL/decoders/lpc/__init__.py +++ b/libsigrokdecode4DSL/decoders/lpc/__init__.py @@ -14,12 +14,11 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' -LPC (Low-Pin Count) is a protocol for low-bandwidth devices used on +LPC (Low Pin Count) is a protocol for low-bandwidth devices used on some PC mainboards, such as the "BIOS chip" or the so-called "Super I/O". ''' diff --git a/libsigrokdecode4DSL/decoders/lpc/pd.py b/libsigrokdecode4DSL/decoders/lpc/pd.py index 7bb2fdd8..e436df34 100755 --- a/libsigrokdecode4DSL/decoders/lpc/pd.py +++ b/libsigrokdecode4DSL/decoders/lpc/pd.py @@ -2,6 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2012-2013 Uwe Hermann +## Copyright (C) 2019 DreamSourceLab ## ## 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 @@ -14,8 +15,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -104,14 +104,15 @@ fields = { } class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'lpc' name = 'LPC' - longname = 'Low-Pin-Count' + longname = 'Low Pin Count' desc = 'Protocol for low-bandwidth devices on PC mainboards.' license = 'gplv2+' inputs = ['logic'] - outputs = ['lpc'] + outputs = [] + tags = ['PC'] channels = ( {'id': 'lframe', 'name': 'LFRAME#', 'desc': 'Frame'}, {'id': 'lclk', 'name': 'LCLK', 'desc': 'Clock'}, @@ -146,22 +147,19 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.oldlclk = -1 - self.oldlframe = -1 - self.oldlad = -1 - self.oldlad_bits = -1 self.samplenum = 0 - self.clocknum = 0 self.lad = -1 self.addr = 0 self.cur_nibble = 0 - self.start_type = -1 self.cycle_type = -1 self.databyte = 0 self.tarcount = 0 self.synccount = 0 - self.timeoutcount = 0 self.oldpins = None self.ss_block = self.es_block = None @@ -174,19 +172,14 @@ class Decoder(srd.Decoder): def handle_get_start(self, lframe): # LAD[3:0]: START field (1 clock cycle). - self.start_type = fields['START'][self.oldlad] # The last value of LAD[3:0] before LFRAME# gets de-asserted is what # the peripherals must use. However, the host can keep LFRAME# asserted # multiple clocks, and we output all START fields that occur, even # though the peripherals are supposed to ignore all but the last one. self.es_block = self.samplenum - self.putb([1, [self.start_type, 'START', 'St', 'S']]) + self.putb([1, [fields['START'][self.oldlad], 'START', 'St', 'S']]) self.ss_block = self.samplenum - # Output a warning if LAD[3:0] changes while LFRAME# is low. - # TODO - #if (self.lad != -1 and self.lad != lad): - # self.putb([0, ['LAD[3:0] changed while LFRAME# was asserted']]) # LFRAME# is asserted (low). Wait until it gets de-asserted again # (the host is allowed to keep it asserted multiple clocks). @@ -250,6 +243,7 @@ class Decoder(srd.Decoder): self.es_block = self.samplenum self.putb([4, ['TAR, cycle %d: %s' % (self.tarcount, self.oldlad_bits)]]) + self.ss_block = self.samplenum # On the first TAR clock cycle LAD[3:0] is driven to 1111 by # either the host or peripheral. On the second clock cycle, @@ -258,7 +252,6 @@ class Decoder(srd.Decoder): if self.oldlad_bits != '1111': self.putb([0, ['TAR, cycle %d: %s (expected 1111)' % \ (self.tarcount, self.oldlad_bits)]]) - self.ss_block = self.samplenum if (self.tarcount != 1): self.tarcount += 1 @@ -279,6 +272,7 @@ class Decoder(srd.Decoder): self.putb([0, ['SYNC, cycle %d: %s (reserved value)' % \ (self.synccount, self.sync_val)]]) + self.es_block = self.samplenum self.putb([5, ['SYNC, cycle %d: %s' % (self.synccount, self.sync_val)]]) self.ss_block = self.samplenum @@ -309,7 +303,6 @@ class Decoder(srd.Decoder): self.timeoutcount = 0 self.state = 'IDLE' - def handle_get_data(self): # LAD[3:0]: DATA field (2 clock cycles). @@ -354,26 +347,17 @@ class Decoder(srd.Decoder): self.tarcount = 0 self.state = 'IDLE' - def decode(self, ss, es, logic): - for (self.samplenum, pins) in logic: - - # Get individual pin values into local variables. - (lframe, lclk, lad0, lad1, lad2, lad3) = pins[:6] - (lreset, ldrq, serirq, clkrun, lpme, lpcpd, lsmi) = pins[6:] + def decode(self): + while True: # Only look at the signals upon rising LCLK edges. The LPC clock # is the same as the PCI clock (which is sampled at rising edges). - logic.logic_mask = 0b0000000000010 - logic.exp_logic = 0b0000000000010 - logic.edge_index = 1 - logic.cur_pos = self.samplenum - #if not (self.oldlclk == 0 and lclk == 1): - # continue + (lframe, lclk, lad0, lad1, lad2, lad3, lreset, ldrq, serirq, clkrun, lpme, lpcpd, lsmi) = self.wait({1: 'r'}) # Store LAD[3:0] bit values (one nibble) in local variables. # Most (but not all) states need this. lad = (lad3 << 3) | (lad2 << 2) | (lad1 << 1) | lad0 - lad_bits = bin(lad)[2:].zfill(4) + lad_bits = '{:04b}'.format(lad) # self.putb([0, ['LAD: %s' % lad_bits]]) # TODO: Only memory read/write is currently supported/tested. @@ -381,18 +365,12 @@ class Decoder(srd.Decoder): # State machine if self.state == 'IDLE': # A valid LPC cycle starts with LFRAME# being asserted (low). - #if lframe != 0: - # continue - if (lframe == 0): - if (self.oldlclk == 0 and lclk == 1): - self.ss_block = self.samplenum - self.state = 'GET START' - self.lad = -1 - # self.clocknum = 0 + if lframe == 0: + self.ss_block = self.samplenum + self.state = 'GET START' + self.lad = -1 else: - logic.logic_mask = 0b0000000000001 - logic.exp_logic = 0b0000000000000 - logic.edge_index = 0 + self.wait({0: 'f'}) elif self.state == 'GET START': self.handle_get_start(lframe) elif self.state == 'GET CT/DR': @@ -410,16 +388,6 @@ class Decoder(srd.Decoder): elif self.state == 'GET TAR2': self.handle_get_tar2() - # Store current pin values for the next round. - self.oldpins = pins - if (logic.edge_index == 1): - self.oldlclk = 0 - else: - self.oldlclk = -1 - if (logic.edge_index == 0): - self.oldlframe = 1 - else: - self.oldlframe = lframe + self.oldlframe = lframe self.oldlad = lad self.oldlad_bits = lad_bits - diff --git a/libsigrokdecode4DSL/decoders/maple_bus/__init__.py b/libsigrokdecode4DSL/decoders/maple_bus/__init__.py new file mode 100755 index 00000000..33b90a5b --- /dev/null +++ b/libsigrokdecode4DSL/decoders/maple_bus/__init__.py @@ -0,0 +1,25 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Marcus Comstedt +## +## 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 2 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 . +## + +''' +Maple bus is serial communication protocol used by peripherals for the +SEGA Dreamcast game console. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/maple_bus/pd.py b/libsigrokdecode4DSL/decoders/maple_bus/pd.py new file mode 100755 index 00000000..0e4e6043 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/maple_bus/pd.py @@ -0,0 +1,219 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Marcus Comstedt +## Copyright (C) 2019 DreamSourceLab +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd + +ann = [ + ['Size', 'L'], + ['SrcAP', 'S'], + ['DstAP', 'D'], + ['Cmd', 'C'], + ['Data'], + ['Cksum', 'K'], +] + +class Decoder(srd.Decoder): + api_version = 3 + id = 'maple_bus' + name = 'Maple bus' + longname = 'SEGA Maple bus' + desc = 'Maple bus peripheral protocol for SEGA Dreamcast.' + license = 'gplv2+' + inputs = ['logic'] + outputs = [] + tags = ['Retro computing'] + channels = ( + {'id': 'sdcka', 'name': 'SDCKA', 'desc': 'Data/clock line A'}, + {'id': 'sdckb', 'name': 'SDCKB', 'desc': 'Data/clock line B'}, + ) + annotations = ( + ('start', 'Start pattern'), + ('end', 'End pattern'), + ('start-with-crc', 'Start pattern with CRC'), + ('occupancy', 'SDCKB occupancy pattern'), + ('reset', 'RESET pattern'), + ('bit', 'Bit'), + ('size', 'Data size'), + ('source', 'Source AP'), + ('dest', 'Destination AP'), + ('command', 'Command'), + ('data', 'Data'), + ('checksum', 'Checksum'), + ('frame-error', 'Frame error'), + ('checksum-error', 'Checksum error'), + ('size-error', 'Size error'), + ) + annotation_rows = ( + ('bits', 'Bits', (0, 1, 2, 3, 4, 5)), + ('fields', 'Fields', (6, 7, 8, 9, 10, 11)), + ('warnings', 'Warnings', (12, 13, 14)), + ) + binary = ( + ('size', 'Data size'), + ('source', 'Source AP'), + ('dest', 'Destination AP'), + ('command', 'Command code'), + ('data', 'Data'), + ('checksum', 'Checksum'), + ) + + def __init__(self): + self.reset() + + def reset(self): + pass + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + self.out_binary = self.register(srd.OUTPUT_BINARY) + self.pending_bit_pos = None + + def putx(self, data): + self.put(self.ss, self.es, self.out_ann, data) + + def putb(self, data): + self.put(self.ss, self.es, self.out_binary, data) + + def byte_annotation(self, bintype, d): + return [bintype + 6, + ['%s: %02X' % (name, d) for name in ann[bintype]] + ['%02X' % d]] + + def got_start(self): + self.putx([0, ['Start pattern', 'Start', 'S']]) + + def got_end(self): + self.putx([1, ['End pattern', 'End', 'E']]) + if self.length != self.expected_length + 1: + self.putx([14, ['Size error', 'L error', 'LE']]) + + def got_start_with_crc(self): + self.putx([2, ['Start pattern with CRC', 'Start CRC', 'SC']]) + + def got_occupancy(self): + self.putx([3, ['SDCKB occupancy pattern', 'Occupancy', 'O']]) + + def got_reset(self): + self.putx([4, ['RESET pattern', 'RESET', 'R']]) + + def output_pending_bit(self): + if self.pending_bit_pos: + self.put(self.pending_bit_pos, self.pending_bit_pos, self.out_ann, [5, ['Bit: %d' % self.pending_bit, '%d' % self.pending_bit]]) + + def got_bit(self, n): + self.output_pending_bit() + self.data = self.data * 2 + n + self.pending_bit = n + self.pending_bit_pos = self.samplenum + + def got_byte(self): + self.output_pending_bit() + bintype = 4 + if self.length < 4: + if self.length == 0: + self.expected_length = 4 * (self.data + 1) + bintype = self.length + elif self.length == self.expected_length: + bintype = 5 + if self.data != self.checksum: + self.putx([13, ['Cksum error', 'K error', 'KE']]) + self.length = self.length + 1 + self.checksum = self.checksum ^ self.data + self.putx(self.byte_annotation(bintype, self.data)) + self.putb([bintype, bytes([self.data])]) + self.pending_bit_pos = None + + def frame_error(self): + self.putx([7, ['Frame error', 'F error', 'FE']]) + + def handle_start(self): + self.wait({0: 'l', 1: 'h'}) + self.ss = self.samplenum + count = 0 + while True: + (sdcka, sdckb) = self.wait([{1: 'f'}, {0: 'r'}]) + if (self.matched & (0b1 << 0)): + count = count + 1 + if (self.matched & (0b1 << 1)): + self.es = self.samplenum + if sdckb == 1: + if count == 4: + self.got_start() + return True + elif count == 6: + self.got_start_with_crc() + return True + elif count == 8: + self.got_occupancy() + return False + elif count >= 14: + self.got_reset() + return False + self.frame_error() + return False + + def handle_byte_or_stop(self): + self.ss = self.samplenum + self.pending_bit_pos = None + initial = True + counta = 0 + countb = 0 + self.data = 0 + while countb < 4: + (sdcka, sdckb) = self.wait([{0: 'f'}, {1: 'f'}]) + self.es = self.samplenum + if (self.matched & (0b1 << 0)): + if counta == countb: + self.got_bit(sdckb) + counta = counta + 1 + elif counta == 1 and countb == 0 and self.data == 0 and sdckb == 0: + self.wait([{0: 'h', 1: 'h'}, {0: 'f'}, {1: 'f'}]) + self.es = self.samplenum + if (self.matched & (0b1 << 0)): + self.got_end() + else: + self.frame_error() + return False + else: + self.frame_error() + return False + elif (self.matched & (0b1 << 1)): + if counta == countb + 1: + self.got_bit(sdcka) + countb = countb + 1 + elif counta == 0 and countb == 0 and sdcka == 1 and initial: + self.ss = self.samplenum + initial = False + else: + self.frame_error() + return False + self.wait({0: 'h'}) + self.es = self.samplenum + self.got_byte() + return True + + def decode(self): + while True: + while not self.handle_start(): + pass + self.length = 0 + self.expected_length = 4 + self.checksum = 0 + while self.handle_byte_or_stop(): + pass diff --git a/libsigrokdecode4DSL/decoders/max7219/__init__.py b/libsigrokdecode4DSL/decoders/max7219/__init__.py index 249f09ca..673d3040 100755 --- a/libsigrokdecode4DSL/decoders/max7219/__init__.py +++ b/libsigrokdecode4DSL/decoders/max7219/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/max7219/pd.py b/libsigrokdecode4DSL/decoders/max7219/pd.py index c05ff849..53067a67 100755 --- a/libsigrokdecode4DSL/decoders/max7219/pd.py +++ b/libsigrokdecode4DSL/decoders/max7219/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import re @@ -42,14 +41,15 @@ registers = { ann_reg, ann_digit, ann_warning = range(3) class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'max7219' name = 'MAX7219' longname = 'Maxim MAX7219/MAX7221' - desc = '8-digit LED display driver.' + desc = 'Maxim MAX72xx series 8-digit LED display driver.' license = 'gplv2+' inputs = ['spi'] - outputs = ['max7219'] + outputs = [] + tags = ['Display'] annotations = ( ('register', 'Registers written to the device'), ('digit', 'Digits displayed on the device'), @@ -60,6 +60,12 @@ class Decoder(srd.Decoder): ('warnings', 'Warnings', (ann_warning,)), ) + def __init__(self): + self.reset() + + def reset(self): + pass + def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) self.pos = 0 diff --git a/libsigrokdecode4DSL/decoders/maxim_ds28ea00/pd.py b/libsigrokdecode4DSL/decoders/maxim_ds28ea00/pd.py deleted file mode 100755 index c7ff7dfa..00000000 --- a/libsigrokdecode4DSL/decoders/maxim_ds28ea00/pd.py +++ /dev/null @@ -1,90 +0,0 @@ -## -## This file is part of the libsigrokdecode project. -## -## Copyright (C) 2012 Iztok Jeras -## -## 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 2 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -## - -import sigrokdecode as srd - -# Dictionary of FUNCTION commands and their names. -command = { - # Scratchpad - 0x4e: 'Write scratchpad', - 0xbe: 'Read scratchpad', - 0x48: 'Copy scratchpad', - # Thermometer - 0x44: 'Convert temperature', - 0xb4: 'Read power mode', - 0xb8: 'Recall EEPROM', - 0xf5: 'PIO access read', - 0xA5: 'PIO access write', - 0x99: 'Chain', -} - -class Decoder(srd.Decoder): - api_version = 2 - id = 'maxim_ds28ea00' - name = 'DS28EA00' - longname = 'Maxim DS28EA00 1-Wire digital thermometer' - desc = '1-Wire digital thermometer with Sequence Detect and PIO.' - license = 'gplv2+' - inputs = ['onewire_network'] - outputs = ['maxim_ds28ea00'] - annotations = ( - ('text', 'Human-readable text'), - ) - - def __init__(self): - self.trn_beg = 0 - self.trn_end = 0 - self.state = 'ROM' - self.rom = 0x0000000000000000 - - def start(self): - self.out_ann = self.register(srd.OUTPUT_ANN) - - def putx(self, data): - self.put(self.ss, self.es, self.out_ann, data) - - def decode(self, ss, es, data): - code, val = data - - self.ss, self.es = ss, es - - # State machine. - if code == 'RESET/PRESENCE': - self.putx([0, ['Reset/presence: %s' - % ('true' if val else 'false')]]) - self.state = 'ROM' - elif code == 'ROM': - self.rom = val - self.putx([0, ['ROM: 0x%016x' % (val)]]) - self.state = 'COMMAND' - elif code == 'DATA': - if self.state == 'COMMAND': - if val not in command: - self.putx([0, ['Unrecognized command: 0x%02x' % val]]) - return - self.putx([0, ['Function command: 0x%02x \'%s\'' - % (val, command[val])]]) - self.state = command[val].upper() - elif self.state == 'READ SCRATCHPAD': - self.putx([0, ['Scratchpad data: 0x%02x' % val]]) - elif self.state == 'CONVERT TEMPERATURE': - self.putx([0, ['Temperature conversion status: 0x%02x' % val]]) - elif self.state in [s.upper() for s in command.values()]: - self.putx([0, ['TODO \'%s\': 0x%02x' % (self.state, val)]]) diff --git a/libsigrokdecode4DSL/decoders/mcs48/__init__.py b/libsigrokdecode4DSL/decoders/mcs48/__init__.py new file mode 100755 index 00000000..b989a2a8 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/mcs48/__init__.py @@ -0,0 +1,31 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 fenugrec +## +## 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 2 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 . +## + +''' +This protocol decoder de-multiplexes Intel MCS-48 (8039, 8048, etc.) external +program memory accesses. + +This requires 14 channels: 8 for D0-D7 (data and lower 8 bits of address), +4 for A8-A11 (output on port P2), ALE and PSEN. + +An optional A12 is supported, which may be an arbitrary I/O pin driven by +software (use case is dumping ROM of an HP 3478A). +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/mcs48/pd.py b/libsigrokdecode4DSL/decoders/mcs48/pd.py new file mode 100755 index 00000000..50216a41 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/mcs48/pd.py @@ -0,0 +1,119 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 fenugrec +## Copyright (C) 2019 DreamSourceLab +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd + +class ChannelError(Exception): + pass + +class Decoder(srd.Decoder): + api_version = 3 + id = 'mcs48' + name = 'MCS-48' + longname = 'Intel MCS-48' + desc = 'Intel MCS-48 external memory access protocol.' + license = 'gplv2+' + inputs = ['logic'] + outputs = [] + tags = ['Retro computing'] + channels = ( + {'id': 'ale', 'name': 'ALE', 'desc': 'Address latch enable'}, + {'id': 'psen', 'name': '/PSEN', 'desc': 'Program store enable'}, + ) + tuple({ + 'id': 'd%d' % i, + 'name': 'D%d' % i, + 'desc': 'CPU data line %d' % i + } for i in range(0, 8) + ) + tuple({ + 'id': 'a%d' % i, + 'name': 'A%d' % i, + 'desc': 'CPU address line %d' % i + } for i in range(8, 12) + ) + optional_channels = tuple({ + 'id': 'a%d' % i, + 'name': 'A%d' % i, + 'desc': 'CPU address line %d' % i + } for i in range(12, 13) + ) + annotations = ( + ('romdata', 'Address:Data'), + ) + binary = ( + ('romdata', 'AAAA:DD'), + ) + + def __init__(self): + self.reset() + + def reset(self): + self.addr = 0 + self.addr_s = 0 + self.data = 0 + self.data_s = 0 + + # Flag to make sure we get an ALE pulse first. + self.started = 0 + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + self.out_bin = self.register(srd.OUTPUT_BINARY) + + def newaddr(self, addr, data): + # Falling edge on ALE: reconstruct address. + self.started = 1 + addr = sum([bit << i for i, bit in enumerate(addr)]) + addr <<= len(data) + addr |= sum([bit << i for i, bit in enumerate(data)]) + self.addr = addr + self.addr_s = self.samplenum + + def newdata(self, data): + # Edge on PSEN: get data. + data = sum([bit << i for i, bit in enumerate(data)]) + self.data = data + self.data_s = self.samplenum + if self.started: + anntext = '{:04X}:{:02X}'.format(self.addr, self.data) + self.put(self.addr_s, self.data_s, self.out_ann, [0, [anntext]]) + bindata = self.addr.to_bytes(2, byteorder='big') + bindata += self.data.to_bytes(1, byteorder='big') + self.put(self.addr_s, self.data_s, self.out_bin, [0, bindata]) + + def decode(self): + # Address bits above A11 are optional, and are considered to be A12+. + # This logic needs more adjustment when more bank address pins are + # to get supported. For now, having just A12 is considered sufficient. + has_bank = self.has_channel(14) + bank_pin_count = 1 if has_bank else 0 + # Sample address on the falling ALE edge. + # Save data on falling edge of PSEN. + while True: + (ale, psen, d0, d1, d2, d3, d4, d5, d6, d7, a8, a9, a10, a11, a12) = self.wait([{0: 'f'}, {1: 'r'}]) + data = (d0, d1, d2, d3, d4, d5, d6, d7) + addr = (a8, a9, a10, a11) + bank = (a12, ) + if has_bank: + addr += bank[:bank_pin_count] + # Handle those conditions (one or more) that matched this time. + if (self.matched & (0b1 << 0)): + self.newaddr(addr, data) + if (self.matched & (0b1 << 1)): + self.newdata(data) diff --git a/libsigrokdecode4DSL/decoders/mdio/pd.py b/libsigrokdecode4DSL/decoders/mdio/pd.py index 2bc40319..9ea76c9e 100755 --- a/libsigrokdecode4DSL/decoders/mdio/pd.py +++ b/libsigrokdecode4DSL/decoders/mdio/pd.py @@ -2,6 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2016 Elias Oenal +## Copyright (C) 2019 DreamSourceLab ## All rights reserved. ## ## Redistribution and use in source and binary forms, with or without @@ -29,14 +30,15 @@ import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'mdio' name = 'MDIO' longname = 'Management Data Input/Output' - desc = 'Half-duplex sync serial bus for MII management between MAC and PHY.' + desc = 'MII management bus between MAC and PHY.' license = 'bsd' inputs = ['logic'] outputs = ['mdio'] + tags = ['Networking'] channels = ( {'id': 'mdc', 'name': 'MDC', 'desc': 'Clock'}, {'id': 'mdio', 'name': 'MDIO', 'desc': 'Data'}, @@ -62,7 +64,9 @@ class Decoder(srd.Decoder): ) def __init__(self): - self.last_mdc = 1 + self.reset() + + def reset(self): self.illegal_bus = 0 self.samplenum = -1 self.clause45_addr = -1 # Clause 45 is context sensitive. @@ -93,7 +97,7 @@ class Decoder(srd.Decoder): if self.clause45 and self.clause45_addr != -1: decoded_min += str.format('ADDR: %04X ' % self.clause45_addr) elif self.clause45: - decoded_min += str.format('ADDR: UKWN ' % self.clause45_addr) + decoded_min += str.format('ADDR: UKWN ') if self.clause45 and self.opcode > 1 \ or (not self.clause45 and self.opcode): @@ -273,10 +277,9 @@ class Decoder(srd.Decoder): def state_DATA(self, mdio): if self.data == -1: self.data = 0 - self.putff([2, ['TURNAROUND', 'TA', 'T']]) + self.putff([2, ['TA', 'T']]) if self.ta_invalid: - self.putff([4, ['TURNAROUND%s' % self.ta_invalid, - 'TA%s' % self.ta_invalid, 'TA', 'T']]) + self.putff([4, ['TA%s' % self.ta_invalid, 'TA', 'T']]) self.ss_frame_field = self.samplenum self.data_bits -= 1 self.data |= mdio << self.data_bits @@ -317,15 +320,8 @@ class Decoder(srd.Decoder): self.process_state(self.state, mdio) - def decode(self, ss, es, data): - for (self.samplenum, pins) in data: - data.itercnt += 1 - # Ignore identical samples early on (for performance reasons). - if self.last_mdc == pins[0]: - continue - self.last_mdc = pins[0] - if pins[0] == 0: # Check for rising edge. - continue - - # Found the correct clock edge, now get/handle the bit(s). - self.handle_bit(pins[1]) + def decode(self): + while True: + # Process pin state upon rising MDC edge. + (mdc, mdio) = self.wait({0: 'r'}) + self.handle_bit(mdio) diff --git a/libsigrokdecode4DSL/decoders/microwire/__init__.py b/libsigrokdecode4DSL/decoders/microwire/__init__.py new file mode 100755 index 00000000..53f52d09 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/microwire/__init__.py @@ -0,0 +1,40 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Kevin Redon +## +## 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 2 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 . +## + +''' +Microwire is a 3-wire half-duplex synchronous serial communication protocol. + +Originally from National Semiconductor, it is often used in EEPROM chips with +device specific commands on top of the bit stream. + +Channels: + + - CS: chip select, active high + - SK: clock line, for the synchronous communication (idle low) + - SI: slave data input, output by the master and parsed by the selected slave + on rising edge of clock line (idle low) + - SO: slave data output, output by the selected slave and changed on rising + edge of clock line, or goes from low to high when ready during status + check (idle high impedance) + +The channel names might vary from chip to chip but the underlying function is +the same. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/microwire/pd.py b/libsigrokdecode4DSL/decoders/microwire/pd.py new file mode 100755 index 00000000..47d87b85 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/microwire/pd.py @@ -0,0 +1,195 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Kevin Redon +## Copyright (C) 2019 DreamSourceLab +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd +from collections import namedtuple + +''' +OUTPUT_PYTHON format: + +Packet: +[namedtuple('ss': bit start sample number, + 'es': bit end sample number, + 'si': SI bit, + 'so': SO bit, + ), ...] + +Since address and word size are variable, a list of all bits in each packet +need to be output. Since Microwire is a synchronous protocol with separate +input and output lines (SI and SO) they are provided together, but because +Microwire is half-duplex only the SI or SO bits will be considered at once. +To be able to annotate correctly the instructions formed by the bit, the start +and end sample number of each bit (pair of SI/SO bit) are provided. +''' + +PyPacket = namedtuple('PyPacket', 'ss es si so') +Packet = namedtuple('Packet', 'samplenum matched cs sk si so') + +class Decoder(srd.Decoder): + api_version = 3 + id = 'microwire' + name = 'Microwire' + longname = 'Microwire' + desc = '3-wire, half-duplex, synchronous serial bus.' + license = 'gplv2+' + inputs = ['logic'] + outputs = ['microwire'] + tags = ['Embedded/industrial'] + channels = ( + {'id': 'cs', 'name': 'CS', 'desc': 'Chip select'}, + {'id': 'sk', 'name': 'SK', 'desc': 'Clock'}, + {'id': 'si', 'name': 'SI', 'desc': 'Slave in'}, + {'id': 'so', 'name': 'SO', 'desc': 'Slave out'}, + ) + annotations = ( + ('start-bit', 'Start bit'), + ('si-bit', 'SI bit'), + ('so-bit', 'SO bit'), + ('status-check-ready', 'Status check ready'), + ('status-check-busy', 'Status check busy'), + ('warning', 'Warning'), + ) + annotation_rows = ( + ('si-bits', 'SI bits', (0, 1)), + ('so-bits', 'SO bits', (2,)), + ('status', 'Status', (3, 4)), + ('warnings', 'Warnings', (5,)), + ) + + def __init__(self): + self.reset() + + def reset(self): + pass + + def start(self): + self.out_python = self.register(srd.OUTPUT_PYTHON) + self.out_ann = self.register(srd.OUTPUT_ANN) + + def decode(self): + while True: + # Wait for slave to be selected on rising CS. + (cs, sk, si, so) = self.wait({0: 'r'}) + if sk: + self.put(self.samplenum, self.samplenum, self.out_ann, + [5, ['Clock should be low on start', + 'Clock high on start', 'Clock high', 'SK high']]) + sk = 0 # Enforce correct state for correct clock handling. + # Because we don't know if this is bit communication or a + # status check we have to collect the SI and SO values on SK + # edges while the chip is selected and figure out afterwards. + packet = [] + while cs: + # Save change. + packet.append(Packet(self.samplenum, self.matched, cs, sk, si, so)) + edge = 'r' if sk == 0 else 'f' + (cs, sk, si, so) = self.wait([{0: 'l'}, {1: edge}, {3: 'e'}]) + # Save last change. + packet.append(Packet(self.samplenum, self.matched, cs, sk, si, so)) + + # Figure out if this is a status check. + # Either there is no clock or no start bit (on first rising edge). + status_check = True + for change in packet: + # Get first clock rising edge. + if (change.matched & (0b1 << 1)) and change.sk: + if change.si: + status_check = False + break + + # The packet is for a status check. + # SO low = busy, SO high = ready. + # The SO signal might be noisy in the beginning because it starts + # in high impedance. + if status_check: + start_samplenum = packet[0].samplenum + bit_so = packet[0].so + # Check for SO edges. + for change in packet: + if (change.matched & (0b1 << 2)): + if bit_so == 0 and change.so: + # Rising edge Busy -> Ready. + self.put(start_samplenum, change.samplenum, + self.out_ann, [4, ['Busy', 'B']]) + start_samplenum = change.samplenum + bit_so = change.so + # Put last state. + if bit_so == 0: + self.put(start_samplenum, packet[-1].samplenum, + self.out_ann, [4, ['Busy', 'B']]) + else: + self.put(start_samplenum, packet[-1].samplenum, + self.out_ann, [3, ['Ready', 'R']]) + else: + # Bit communication. + # Since the slave samples SI on clock rising edge we do the + # same. Because the slave changes SO on clock rising edge we + # sample on the falling edge. + bit_start = 0 # Rising clock sample of bit start. + bit_si = 0 # SI value at rising clock edge. + bit_so = 0 # SO value at falling clock edge. + start_bit = True # Start bit incoming (first bit). + pydata = [] # Python output data. + for change in packet: + if (change.matched & (0b1 << 1)): + # Clock edge. + if change.sk: # Rising clock edge. + if bit_start > 0: # Bit completed. + if start_bit: + if bit_si == 0: # Start bit missing. + self.put(bit_start, change.samplenum, + self.out_ann, + [5, ['Start bit not high', + 'Start bit low']]) + else: + self.put(bit_start, change.samplenum, + self.out_ann, + [0, ['Start bit', 'S']]) + start_bit = False + else: + self.put(bit_start, change.samplenum, + self.out_ann, + [1, ['SI bit: %d' % bit_si, + 'SI: %d' % bit_si, + '%d' % bit_si]]) + self.put(bit_start, change.samplenum, + self.out_ann, + [2, ['SO bit: %d' % bit_so, + 'SO: %d' % bit_so, + '%d' % bit_so]]) + pydata.append(PyPacket(bit_start, + change.samplenum, bit_si, bit_so)) + bit_start = change.samplenum + bit_si = change.si + else: # Falling clock edge. + bit_so = change.so + elif (change.matched & (0b1 << 0)) and \ + change.cs == 0 and change.sk == 0: + # End of packet. + self.put(bit_start, change.samplenum, self.out_ann, + [1, ['SI bit: %d' % bit_si, + 'SI: %d' % bit_si, '%d' % bit_si]]) + self.put(bit_start, change.samplenum, self.out_ann, + [2, ['SO bit: %d' % bit_so, + 'SO: %d' % bit_so, '%d' % bit_so]]) + pydata.append(PyPacket(bit_start, change.samplenum, + bit_si, bit_so)) + self.put(packet[0].samplenum, packet[len(packet) - 1].samplenum, + self.out_python, pydata) diff --git a/libsigrokdecode4DSL/decoders/midi/__init__.py b/libsigrokdecode4DSL/decoders/midi/__init__.py index 4dbcf96d..378b0167 100755 --- a/libsigrokdecode4DSL/decoders/midi/__init__.py +++ b/libsigrokdecode4DSL/decoders/midi/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/midi/lists.py b/libsigrokdecode4DSL/decoders/midi/lists.py index ce4c1aeb..1e628615 100755 --- a/libsigrokdecode4DSL/decoders/midi/lists.py +++ b/libsigrokdecode4DSL/decoders/midi/lists.py @@ -15,8 +15,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## # Each status byte has 3 string names, each shorter than the previous diff --git a/libsigrokdecode4DSL/decoders/midi/pd.py b/libsigrokdecode4DSL/decoders/midi/pd.py index 18c439f4..ae35e123 100755 --- a/libsigrokdecode4DSL/decoders/midi/pd.py +++ b/libsigrokdecode4DSL/decoders/midi/pd.py @@ -2,7 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2013-2016 Uwe Hermann -## Copyright (C) 2016 Chris Dreher +## Copyright (C) 2016 Chris Dreher ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -15,8 +15,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -26,14 +25,15 @@ RX = 0 TX = 1 class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'midi' name = 'MIDI' longname = 'Musical Instrument Digital Interface' desc = 'Musical Instrument Digital Interface (MIDI) protocol.' license = 'gplv2+' inputs = ['uart'] - outputs = ['midi'] + outputs = [] + tags = ['Audio', 'PC'] annotations = ( ('text-verbose', 'Human-readable text (verbose)'), ('text-sysreal-verbose', 'Human-readable SysReal text (verbose)'), @@ -45,6 +45,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.status_byte = 0 self.explicit_status_byte = False diff --git a/libsigrokdecode4DSL/decoders/miller/__init__.py b/libsigrokdecode4DSL/decoders/miller/__init__.py new file mode 100755 index 00000000..ce0d4941 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/miller/__init__.py @@ -0,0 +1,26 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Christoph Rackwitz +## +## 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 2 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 . +## + +''' +The Miller protocol decoder supports (modified) Miller encoded data. + +E.g. used in NFC communication at 106 kbaud. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/miller/pd.py b/libsigrokdecode4DSL/decoders/miller/pd.py new file mode 100755 index 00000000..f41711f1 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/miller/pd.py @@ -0,0 +1,190 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Christoph Rackwitz +## +## 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 2 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.gorferay.com/type-a-communications-interface/ +# https://resources.infosecinstitute.com/introduction-rfid-security/ +# https://www.radio-electronics.com/info/wireless/nfc/near-field-communications-modulation-rf-signal-interface.php +# https://www.researchgate.net/figure/Modified-Miller-Code_fig16_283498836 + +# Miller: either edge +# modified Miller: falling edge + +import sigrokdecode as srd + +def roundto(x, k=1.0): + return round(x / k) * k + +class Decoder(srd.Decoder): + api_version = 3 + id = 'miller' + name = 'Miller' + longname = 'Miller encoding' + desc = 'Miller encoding protocol.' + license = 'gplv2+' + inputs = ['logic'] + outputs = [] + tags = ['Encoding'] + channels = ( + {'id': 'data', 'name': 'Data', 'desc': 'Data signal'}, + ) + options = ( + {'id': 'baudrate', 'desc': 'Baud rate', 'default': 106000}, + {'id': 'edge', 'desc': 'Edge', 'default': 'falling', 'values': ('rising', 'falling', 'either')}, + ) + annotations = ( + ('bit', 'Bit'), + ('bitstring', 'Bitstring'), + ) + annotation_rows = tuple((u, v, (i,)) for i, (u, v) in enumerate(annotations)) + binary = ( + ('raw', 'Raw binary'), + ) + + def __init__(self): + self.reset() + + def reset(self): + self.samplerate = None + + def metadata(self, key, value): + if key == srd.SRD_CONF_SAMPLERATE: + self.samplerate = value + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + self.out_binary = self.register(srd.OUTPUT_BINARY) + + def decode_bits(self): + timeunit = self.samplerate / self.options['baudrate'] + edgetype = self.options['edge'][0] + + self.wait({0: edgetype}) # first symbol, beginning of unit + prevedge = self.samplenum + + # start of message: '0' + prevbit = 0 + yield (0, prevedge, prevedge + timeunit) + expectedstart = self.samplenum + timeunit + + # end of message: '0' followed by one idle symbol + + while True: + self.wait([{0: edgetype}, {'skip': int(3 * timeunit)}]) + got_timeout = (self.matched & (0b1 << 1)) + sampledelta = (self.samplenum - prevedge) + prevedge = self.samplenum + timedelta = roundto(sampledelta / timeunit, 0.5) + + # a mark stands for a 1 bit + # a mark has an edge in the middle + + # a space stands for a 0 bit + # a space either has an edge at the beginning or no edge at all + # after a mark, a space is edge-less + # after a space, a space has an edge + + # we get 1.0, 1.5, 2.0 times between edges + + # end of transmission is always a space, either edged or edge-less + + if prevbit == 0: # space -> ??? + if timedelta == 1.0: # 1.0 units -> space + yield (0, self.samplenum, self.samplenum + timeunit) + prevbit = 0 + expectedstart = self.samplenum + timeunit + elif timedelta == 1.5: # 1.5 units -> mark + yield (1, expectedstart, self.samplenum + 0.5*timeunit) + prevbit = 1 + expectedstart = self.samplenum + timeunit*0.5 + elif timedelta >= 2.0: + # idle symbol (end of message) + yield None + else: + # assert timedelta >= 2.0 + yield (False, self.samplenum - sampledelta, self.samplenum) + break + else: # mark -> ??? + if timedelta <= 0.5: + yield (False, self.samplenum - sampledelta, self.samplenum) + break + if timedelta == 1.0: # 1.0 units -> mark again (1.5 from start) + yield (1, expectedstart, self.samplenum + 0.5*timeunit) + prevbit = 1 + expectedstart = self.samplenum + 0.5*timeunit + elif timedelta == 1.5: # 1.5 units -> space (no pulse) and space (pulse) + yield (0, expectedstart, self.samplenum) + yield (0, self.samplenum, self.samplenum + timeunit) + prevbit = 0 + expectedstart = self.samplenum + timeunit + elif timedelta == 2.0: # 2.0 units -> space (no pulse) and mark (pulse) + yield (0, expectedstart, expectedstart + timeunit) + yield (1, self.samplenum - 0.5*timeunit, self.samplenum + 0.5*timeunit) + prevbit = 1 + expectedstart = self.samplenum + timeunit*0.5 + else: # longer -> space and end of message + yield (0, expectedstart, expectedstart + timeunit) + yield None + break + + def decode_run(self): + numbits = 0 + bitvalue = 0 + bitstring = '' + stringstart = None + stringend = None + + for bit in self.decode_bits(): + if bit is None: + break + + (value, ss, es) = bit + + if value is False: + self.put(int(ss), int(es), self.out_ann, [1, ['ERROR']]) + else: + self.put(int(ss), int(es), self.out_ann, [0, ['{}'.format(value)]]) + + if value is False: + numbits = 0 + break + + if stringstart is None: + stringstart = ss + + stringend = es + + bitvalue |= value << numbits + numbits += 1 + + bitstring += '{}'.format(value) + if numbits % 4 == 0: + bitstring += ' ' + + if not numbits: + return + + self.put(int(stringstart), int(stringend), self.out_ann, [1, ['{}'.format(bitstring)]]) + + numbytes = numbits // 8 + (numbits % 8 > 0) + bytestring = bitvalue.to_bytes(numbytes, 'little') + self.put(int(stringstart), int(stringend), self.out_binary, [0, bytestring]) + + def decode(self): + while True: + self.decode_run() diff --git a/libsigrokdecode4DSL/decoders/mlx90614/__init__.py b/libsigrokdecode4DSL/decoders/mlx90614/__init__.py index e985c910..2e20c4d9 100755 --- a/libsigrokdecode4DSL/decoders/mlx90614/__init__.py +++ b/libsigrokdecode4DSL/decoders/mlx90614/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/mlx90614/pd.py b/libsigrokdecode4DSL/decoders/mlx90614/pd.py index 0a3abbaf..f0dbe22a 100755 --- a/libsigrokdecode4DSL/decoders/mlx90614/pd.py +++ b/libsigrokdecode4DSL/decoders/mlx90614/pd.py @@ -14,27 +14,30 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'mlx90614' name = 'MLX90614' longname = 'Melexis MLX90614' - desc = 'Infrared Thermometer protocol.' + desc = 'Melexis MLX90614 infrared thermometer protocol.' license = 'gplv2+' inputs = ['i2c'] - outputs = ['mlx90614'] + outputs = [] + tags = ['IC', 'Sensor'] annotations = ( ('celsius', 'Temperature in degrees Celsius'), ('kelvin', 'Temperature in Kelvin'), ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IGNORE START REPEAT' self.data = [] diff --git a/libsigrokdecode4DSL/decoders/modbus/__init__.py b/libsigrokdecode4DSL/decoders/modbus/__init__.py index e9f0deb8..b60eea15 100755 --- a/libsigrokdecode4DSL/decoders/modbus/__init__.py +++ b/libsigrokdecode4DSL/decoders/modbus/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/modbus/pd.py b/libsigrokdecode4DSL/decoders/modbus/pd.py index 3420f99d..487acf1f 100755 --- a/libsigrokdecode4DSL/decoders/modbus/pd.py +++ b/libsigrokdecode4DSL/decoders/modbus/pd.py @@ -2,6 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2015 Bart de Waal +## Copyright (C) 2019 DreamSourceLab ## ## 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 @@ -811,7 +812,7 @@ class Modbus_ADU_CS(Modbus_ADU): self.check_crc(bytecount + 12) class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'modbus' name = 'Modbus' longname = 'Modbus RTU over RS232/RS485' @@ -819,6 +820,7 @@ class Decoder(srd.Decoder): license = 'gplv3+' inputs = ['uart'] outputs = ['modbus'] + tags = ['Embedded/industrial'] annotations = ( ('sc-server-id', ''), ('sc-function', ''), @@ -837,16 +839,19 @@ class Decoder(srd.Decoder): ('error-indication', ''), ) annotation_rows = ( - ('sc', 'Server->client', (0, 1, 2, 3, 4, 5, 6)), - ('cs', 'Client->server', (7, 8, 9, 10, 11, 12, 13)), + ('sc', 'Server->client', (7, 8, 9, 10, 11, 12, 13)), + ('cs', 'Client->server', (0, 1, 2, 3, 4, 5, 6)), ('error-indicator', 'Errors in frame', (14,)), ) options = ( - {'id': 'channel', 'desc': 'Server -> client channel', 'default': 'RX', - 'values': ('RX', 'TX')}, + {'id': 'channel', 'desc': 'Direction', 'default': 'TX', + 'values': ('TX', 'RX')}, ) def __init__(self): + self.reset() + + def reset(self): self.ADUSc = None # Start off with empty slave -> client ADU. self.ADUCs = None # Start off with empty client -> slave ADU. @@ -923,9 +928,7 @@ class Decoder(srd.Decoder): # Decide what ADU(s) we need this packet to go to. # Note that it's possible to go to both ADUs. - if rxtx == TX: + if self.options['channel'] == 'TX': + self.decode_adu(ss, es, data, 'Sc') + if self.options['channel'] == 'RX': self.decode_adu(ss, es, data, 'Cs') - if rxtx == TX and self.options['channel'] == 'TX': - self.decode_adu(ss, es, data, 'Sc') - if rxtx == RX and self.options['channel'] == 'RX': - self.decode_adu(ss, es, data, 'Sc') diff --git a/libsigrokdecode4DSL/decoders/cjtag_oscan1/__init__.py b/libsigrokdecode4DSL/decoders/morse/__init__.py similarity index 52% rename from libsigrokdecode4DSL/decoders/cjtag_oscan1/__init__.py rename to libsigrokdecode4DSL/decoders/morse/__init__.py index ca3fc839..5d916247 100755 --- a/libsigrokdecode4DSL/decoders/cjtag_oscan1/__init__.py +++ b/libsigrokdecode4DSL/decoders/morse/__init__.py @@ -1,7 +1,7 @@ ## ## This file is part of the libsigrokdecode project. ## -## Copyright (C) 2012 Uwe Hermann +## Copyright (C) 2017 Christoph Rackwitz ## ## 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 @@ -14,22 +14,15 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' -JTAG (Joint Test Action Group), a.k.a. "IEEE 1149.1: Standard Test Access Port -and Boundary-Scan Architecture", is a protocol used for testing, debugging, -and flashing various digital ICs. +Morse code is a method of transmitting text information as a series of +on-off tones. Details: -https://en.wikipedia.org/wiki/Joint_Test_Action_Group -http://focus.ti.com/lit/an/ssya002c/ssya002c.pdf - -This decoders handles a tiny part of IEEE 1149.7, the so called CJTAG OSCAN1 -format -http://developers-club.com/posts/237885/ +https://en.wikipedia.org/wiki/Morse_code ''' from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/morse/pd.py b/libsigrokdecode4DSL/decoders/morse/pd.py new file mode 100755 index 00000000..8b5cb829 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/morse/pd.py @@ -0,0 +1,250 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Christoph Rackwitz +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd + +def decode_ditdah(s): + return tuple({'-': 3, '.': 1}[c] for c in s) + +def encode_ditdah(tpl): + return ''.join({1: '.', 3: '-'}[c] for c in tpl) + +# https://www.itu.int/dms_pubrec/itu-r/rec/m/R-REC-M.1677-1-200910-I!!PDF-E.pdf +# Recommendation ITU-R M.1677-1 +# (10/2009) +# International Morse code +alphabet = { + # 1.1.1 Letters + '.-': 'a', + '-...': 'b', + '-.-.': 'c', + '-..': 'd', + '.': 'e', + '..-..': 'é', # "accented" + '..-.': 'f', + '--.': 'g', + '....': 'h', + '..': 'i', + '.---': 'j', + '-.-': 'k', + '.-..': 'l', + '--': 'm', + '-.': 'n', + '---': 'o', + '.--.': 'p', + '--.-': 'q', + '.-.': 'r', + '...': 's', + '-': 't', + '..-': 'u', + '...-': 'v', + '.--': 'w', + '-..-': 'x', + '-.--': 'y', + '--..': 'z', + + # 1.1.2 Figures + '.----': '1', + '..---': '2', + '...--': '3', + '....-': '4', + '.....': '5', + '-....': '6', + '--...': '7', + '---..': '8', + '----.': '9', + '-----': '0', + + # 1.1.3 Punctuation marks and miscellaneous signs + '.-.-.-': '.', # Full stop (period) + '--..--': ',', # Comma + '---...': ':', # Colon or division sign + '..--..': '?', # Question mark (note of interrogation or request for repetition of a transmission not understood) + '.----.': '’', # Apostrophe + '-....-': '-', # Hyphen or dash or subtraction sign + '-..-.': '/', # Fraction bar or division sign + '-.--.': '(', # Left-hand bracket (parenthesis) + '-.--.-': ')', # Right-hand bracket (parenthesis) + '.-..-.': '“ ”', # Inverted commas (quotation marks) (before and after the words) + '-...-': '=', # Double hyphen + '...-.': 'UNDERSTOOD', # Understood + '........': 'ERROR', # Error (eight dots) + '.-.-.': '+', # Cross or addition sign + '.-...': 'WAIT', # Wait + '...-.-': 'EOW', # End of work + '-.-.-': 'START', # Starting signal (to precede every transmission) + '.--.-.': '@', # Commercial at + + #'-.-': 'ITT', # K: Invitation to transmit + + # 3.2.1 For the multiplication sign, the signal corresponding to the letter X shall be transmitted. + #'-..-': '×', # Multiplication sign +} + +alphabet = {decode_ditdah(k): v for k, v in alphabet.items()} + +# 2 Spacing and length of the signals (right side is just for printing). +symbols = { # level, time units + # 2.1 A dash is equal to three dots. + (1, 1): '*', + (1, 3): '===', + # 2.2 The space between the signals forming the same letter is equal to one dot. + (0, 1): '_', + # 2.3 The space between two letters is equal to three dots. + (0, 3): '__', + # 2.4 The space between two words is equal to seven dots. + (0, 7): '___', +} + +class Decoder(srd.Decoder): + api_version = 3 + id = 'morse' + name = 'Morse' + longname = 'Morse code' + desc = 'Demodulated morse code protocol.' + license = 'gplv2+' + inputs = ['logic'] + outputs = [] + tags = ['Encoding'] + channels = ( + {'id': 'data', 'name': 'Data', 'desc': 'Data line'}, + ) + options = ( + {'id': 'timeunit', 'desc': 'Time unit (guess)', 'default': 0.1}, + ) + annotations = ( + ('time', 'Time'), + ('units', 'Units'), + ('symbol', 'Symbol'), + ('letter', 'Letter'), + ('word', 'Word'), + ) + annotation_rows = tuple((u, v, (i,)) for i, (u, v) in enumerate(annotations)) + + def __init__(self): + self.reset() + + def reset(self): + self.samplerate = None + + def metadata(self, key, value): + if key == srd.SRD_CONF_SAMPLERATE: + self.samplerate = value + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + self.out_binary = self.register(srd.OUTPUT_BINARY) + + def decode_symbols(self): + # Annotate symbols, emit symbols, handle timeout via token. + + timeunit = self.options['timeunit'] + + self.wait({0: 'r'}) + prevtime = self.samplenum # Time of an actual edge. + + while True: + (val,) = self.wait([{0: 'e'}, {'skip': int(5 * self.samplerate * timeunit)}]) + + pval = 1 - val + curtime = self.samplenum + dt = (curtime - prevtime) / self.samplerate + units = dt / timeunit + iunits = int(max(1, round(units))) + error = abs(units - iunits) + + symbol = (pval, iunits) + + if (self.matched & (0b1 << 1)): + yield None # Flush word. + continue + + self.put(prevtime, curtime, self.out_ann, [0, ['{:.3g}'.format(dt)]]) + + if symbol in symbols: + self.put(prevtime, curtime, self.out_ann, [1, ['{:.1f}*{:.3g}'.format(units, timeunit)]]) + yield (prevtime, curtime, symbol) + else: + self.put(prevtime, curtime, self.out_ann, [1, ['!! {:.1f}*{:.3g} !!'.format(units, timeunit)]]) + + prevtime = curtime + + thisunit = dt / iunits + timeunit += (thisunit - timeunit) * 0.2 * max(0, 1 - 2*error) # Adapt. + + def decode_morse(self): + # Group symbols into letters. + sequence = () + s0 = s1 = None + + for item in self.decode_symbols(): + do_yield = False + if item is not None: # Level + width. + (t0, t1, symbol) = item + (sval, sunits) = symbol + if sval == 1: + if s0 is None: + s0 = t0 + s1 = t1 + sequence += (sunits,) + else: + # Generate "flush" for end of letter, end of word. + if sunits >= 3: + do_yield = True + else: + do_yield = True + if do_yield: + if sequence: + yield (s0, s1, alphabet.get(sequence, encode_ditdah(sequence))) + sequence = () + s0 = s1 = None + if item is None: + yield None # Pass through flush of 5+ space. + + def decode(self): + + # Strictly speaking there is no point in running this decoder + # when the sample rate is unknown or zero. But the previous + # implementation already fell back to a rate of 1 in that case. + # We stick with this approach, to not introduce new constraints + # for existing use scenarios. + if not self.samplerate: + self.samplerate = 1.0 + + # Annotate letters, group into words. + s0 = s1 = None + word = '' + for item in self.decode_morse(): + do_yield = False + + if item is not None: # Append letter. + (t0, t1, letter) = item + self.put(t0, t1, self.out_ann, [3, [letter]]) + if s0 is None: + s0 = t0 + s1 = t1 + word += letter + else: + do_yield = True + + if do_yield: # Flush of word. + if word: + self.put(s0, s1, self.out_ann, [4, [word]]) + word = '' + s0 = s1 = None diff --git a/libsigrokdecode4DSL/decoders/mrf24j40/__init__.py b/libsigrokdecode4DSL/decoders/mrf24j40/__init__.py index f0820bd1..37b8b5c3 100755 --- a/libsigrokdecode4DSL/decoders/mrf24j40/__init__.py +++ b/libsigrokdecode4DSL/decoders/mrf24j40/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/mrf24j40/lists.py b/libsigrokdecode4DSL/decoders/mrf24j40/lists.py index e20b4b91..f5931c24 100755 --- a/libsigrokdecode4DSL/decoders/mrf24j40/lists.py +++ b/libsigrokdecode4DSL/decoders/mrf24j40/lists.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## sregs = { diff --git a/libsigrokdecode4DSL/decoders/mrf24j40/pd.py b/libsigrokdecode4DSL/decoders/mrf24j40/pd.py index 8ef7017e..b242ee66 100755 --- a/libsigrokdecode4DSL/decoders/mrf24j40/pd.py +++ b/libsigrokdecode4DSL/decoders/mrf24j40/pd.py @@ -14,22 +14,22 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd from .lists import * class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'mrf24j40' name = 'MRF24J40' longname = 'Microchip MRF24J40' desc = 'IEEE 802.15.4 2.4 GHz RF tranceiver chip.' license = 'gplv2+' inputs = ['spi'] - outputs = ['mrf24j40'] + outputs = [] + tags = ['IC', 'Wireless/RF'] annotations = ( ('sread', 'Short register read commands'), ('swrite', 'Short register write commands'), @@ -44,6 +44,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.ss_cmd, self.es_cmd = 0, 0 self.mosi_bytes = [] self.miso_bytes = [] @@ -57,7 +60,7 @@ class Decoder(srd.Decoder): def putw(self, pos, msg): self.put(pos[0], pos[1], self.out_ann, [4, [msg]]) - def reset(self): + def reset_data(self): self.mosi_bytes = [] self.miso_bytes = [] @@ -104,7 +107,7 @@ class Decoder(srd.Decoder): if cs_old is not None and cs_old == 0 and cs_new == 1: if len(self.mosi_bytes) not in (0, 2, 3): self.putw([self.ss_cmd, es], 'Misplaced CS!') - self.reset() + self.reset_data() return # Don't care about anything else. @@ -127,8 +130,8 @@ class Decoder(srd.Decoder): if len(self.mosi_bytes) == 3: self.es_cmd = es self.handle_long() - self.reset() + self.reset_data() else: self.es_cmd = es self.handle_short() - self.reset() + self.reset_data() diff --git a/libsigrokdecode4DSL/decoders/mxc6225xu/__init__.py b/libsigrokdecode4DSL/decoders/mxc6225xu/__init__.py index daeb89eb..2aaa726f 100755 --- a/libsigrokdecode4DSL/decoders/mxc6225xu/__init__.py +++ b/libsigrokdecode4DSL/decoders/mxc6225xu/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/mxc6225xu/pd.py b/libsigrokdecode4DSL/decoders/mxc6225xu/pd.py index 832eb06d..e9617782 100755 --- a/libsigrokdecode4DSL/decoders/mxc6225xu/pd.py +++ b/libsigrokdecode4DSL/decoders/mxc6225xu/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -60,19 +59,23 @@ status = { } class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'mxc6225xu' name = 'MXC6225XU' longname = 'MEMSIC MXC6225XU' desc = 'Digital Thermal Orientation Sensor (DTOS) protocol.' license = 'gplv2+' inputs = ['i2c'] - outputs = ['mxc6225xu'] + outputs = [] + tags = ['IC', 'Sensor'] annotations = ( ('text', 'Human-readable text'), ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' def start(self): diff --git a/libsigrokdecode4DSL/decoders/nrf24l01/__init__.py b/libsigrokdecode4DSL/decoders/nrf24l01/__init__.py index 7b4d7485..60124780 100755 --- a/libsigrokdecode4DSL/decoders/nrf24l01/__init__.py +++ b/libsigrokdecode4DSL/decoders/nrf24l01/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/nrf24l01/pd.py b/libsigrokdecode4DSL/decoders/nrf24l01/pd.py index 8356d4b9..bb7f71f7 100755 --- a/libsigrokdecode4DSL/decoders/nrf24l01/pd.py +++ b/libsigrokdecode4DSL/decoders/nrf24l01/pd.py @@ -2,6 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2014 Jens Steinhauser +## Copyright (C) 2019 DreamSourceLab ## ## 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 @@ -14,8 +15,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -60,14 +60,15 @@ xn297_regs = { } class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'nrf24l01' name = 'nRF24L01(+)' - longname = 'Nordic Semiconductor nRF24L01/nRF24L01+' - desc = '2.4GHz transceiver chip.' + longname = 'Nordic Semiconductor nRF24L01(+)' + desc = '2.4GHz RF transceiver chip.' license = 'gplv2+' inputs = ['spi'] - outputs = ['nrf24l01'] + outputs = [] + tags = ['IC', 'Wireless/RF'] options = ( {'id': 'chip', 'desc': 'Chip type', 'default': 'nrf24l01', 'values': ('nrf24l01', 'xn297')}, @@ -95,6 +96,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.next() self.requirements_met = True self.cs_was_released = False @@ -314,8 +318,8 @@ class Decoder(srd.Decoder): self.finish_command((self.mb_s, self.mb_e)) self.next() - self.cs_was_released = True - elif ptype == 'DATA': + self.cs_was_released = True + elif ptype == 'DATA' and self.cs_was_released: mosi, miso = data1, data2 pos = (ss, es) diff --git a/libsigrokdecode4DSL/decoders/nunchuk/__init__.py b/libsigrokdecode4DSL/decoders/nunchuk/__init__.py index c5791455..a6092c45 100755 --- a/libsigrokdecode4DSL/decoders/nunchuk/__init__.py +++ b/libsigrokdecode4DSL/decoders/nunchuk/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/nunchuk/pd.py b/libsigrokdecode4DSL/decoders/nunchuk/pd.py index 48a70991..59b10289 100755 --- a/libsigrokdecode4DSL/decoders/nunchuk/pd.py +++ b/libsigrokdecode4DSL/decoders/nunchuk/pd.py @@ -14,21 +14,21 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'nunchuk' name = 'Nunchuk' longname = 'Nintendo Wii Nunchuk' desc = 'Nintendo Wii Nunchuk controller protocol.' license = 'gplv2+' inputs = ['i2c'] - outputs = ['nunchuck'] + outputs = [] + tags = ['Sensor'] annotations = \ tuple(('reg-0x%02X' % i, 'Register 0x%02X' % i) for i in range(6)) + ( ('bit-bz', 'BZ bit'), @@ -48,6 +48,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.sx = self.sy = self.ax = self.ay = self.az = self.bz = self.bc = -1 self.databytecount = 0 diff --git a/libsigrokdecode4DSL/decoders/onewire_link/__init__.py b/libsigrokdecode4DSL/decoders/onewire_link/__init__.py index 12aad259..abd55671 100755 --- a/libsigrokdecode4DSL/decoders/onewire_link/__init__.py +++ b/libsigrokdecode4DSL/decoders/onewire_link/__init__.py @@ -2,6 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2012 Uwe Hermann +## Copyright (C) 2017 Kevin Redon ## ## 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 @@ -14,8 +15,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' @@ -40,29 +40,17 @@ overdrive communication speed. The following minimal values should be used: Channels: 1-Wire requires a single signal, but some master implementations might have a separate signal used to deliver power to the bus during temperature conversion -as an example. This power signal is currently not used. +as an example. - owr (1-Wire signal line) - - pwr (optional, dedicated power supply pin) Options: -1-Wire is an asynchronous protocol, so the decoder must know the samplerate. -The timing for sampling bits, presence, and reset is calculated by the decoder, -but in case the user wishes to use different values, it is possible to -configure the following timing values (number of samplerate periods): +1-Wire is an asynchronous protocol with fixed timing values, so the decoder +must know the samplerate. +Two speeds are available: normal and overdrive. The decoder detects when +switching speed, but the user can set which to start decoding with: - - overdrive (if active the decoder will be prepared for overdrive) - - cnt_normal_bit (time for normal mode sample bit) - - cnt_normal_slot (time for normal mode data slot) - - cnt_normal_presence (time for normal mode sample presence) - - cnt_normal_reset (time for normal mode reset) - - cnt_overdrive_bit (time for overdrive mode sample bit) - - cnt_overdrive_slot (time for overdrive mode data slot) - - cnt_overdrive_presence (time for overdrive mode sample presence) - - cnt_overdrive_reset (time for overdrive mode reset) - -These options should be configured only on very rare cases and the user should -read the decoder source code to understand them correctly. + - overdrive (to decode starting with overdrive speed) ''' from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/onewire_link/pd.py b/libsigrokdecode4DSL/decoders/onewire_link/pd.py index 0f4ed676..6592279e 100755 --- a/libsigrokdecode4DSL/decoders/onewire_link/pd.py +++ b/libsigrokdecode4DSL/decoders/onewire_link/pd.py @@ -1,7 +1,7 @@ ## ## This file is part of the libsigrokdecode project. ## -## Copyright (C) 2012 Iztok Jeras +## Copyright (C) 2017 Kevin Redon ## ## 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 @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -23,48 +22,95 @@ import sigrokdecode as srd class SamplerateError(Exception): pass +# Timing values in us for the signal at regular and overdrive speed. +timing = { + 'RSTL': { + 'min': { + False: 480.0, + True: 48.0, + }, + 'max': { + False: 960.0, + True: 80.0, + }, + }, + 'RSTH': { + 'min': { + False: 480.0, + True: 48.0, + }, + }, + 'PDH': { + 'min': { + False: 15.0, + True: 2.0, + }, + 'max': { + False: 60.0, + True: 6.0, + }, + }, + 'PDL': { + 'min': { + False: 60.0, + True: 8.0, + }, + 'max': { + False: 240.0, + True: 24.0, + }, + }, + 'SLOT': { + 'min': { + False: 60.0, + True: 6.0, + }, + 'max': { + False: 120.0, + True: 16.0, + }, + }, + 'REC': { + 'min': { + False: 1.0, + True: 1.0, + }, + }, + 'LOWR': { + 'min': { + False: 1.0, + True: 1.0, + }, + 'max': { + False: 15.0, + True: 2.0, + }, + }, +} + class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'onewire_link' - name = 'One-Wire link layer' - longname = 'One-Wire serial communication bus (link layer)' + name = 'OneWire link layer' + longname = '1-Wire serial communication bus (link layer)' desc = 'Bidirectional, half-duplex, asynchronous serial bus.' license = 'gplv2+' inputs = ['logic'] outputs = ['onewire_link'] + tags = ['Embedded/industrial'] channels = ( {'id': 'owr', 'name': 'OWR', 'desc': '1-Wire signal line'}, ) - optional_channels = ( - {'id': 'pwr', 'name': 'PWR', 'desc': '1-Wire power supply pin'}, - ) options = ( - {'id': 'overdrive', - 'desc': 'Overdrive mode', 'default': 'no', 'values': ('yes', 'no')}, - # Time options (specified in microseconds): - {'id': 'cnt_normal_bit', - 'desc': 'Normal mode sample bit time (μs)', 'default': 15}, - {'id': 'cnt_normal_slot', - 'desc': 'Normal mode data slot time (μs)', 'default': 60}, - {'id': 'cnt_normal_presence', - 'desc': 'Normal mode sample presence time (μs)', 'default': 75}, - {'id': 'cnt_normal_reset', - 'desc': 'Normal mode reset time (μs)', 'default': 480}, - {'id': 'cnt_overdrive_bit', - 'desc': 'Overdrive mode sample bit time (μs)', 'default': 2}, - {'id': 'cnt_overdrive_slot', - 'desc': 'Overdrive mode data slot time (μs)', 'default': 7.3}, - {'id': 'cnt_overdrive_presence', - 'desc': 'Overdrive mode sample presence time (μs)', 'default': 10}, - {'id': 'cnt_overdrive_reset', - 'desc': 'Overdrive mode reset time (μs)', 'default': 48}, + {'id': 'overdrive', 'desc': 'Start in overdrive speed', + 'default': 'no', 'values': ('yes', 'no')}, ) annotations = ( ('bit', 'Bit'), ('warnings', 'Warnings'), ('reset', 'Reset'), ('presence', 'Presence'), - ('overdrive', 'Overdrive mode notifications'), + ('overdrive', 'Overdrive speed notifications'), ) annotation_rows = ( ('bits', 'Bits', (0, 2, 3)), @@ -72,18 +118,37 @@ class Decoder(srd.Decoder): ('warnings', 'Warnings', (1,)), ) + def __init__(self): + self.reset() + + def reset(self): + self.samplerate = None + self.state = 'INITIAL' + self.present = 0 + self.bit = 0 + self.bit_count = -1 + self.command = 0 + self.overdrive = False + self.fall = 0 + self.rise = 0 + + def start(self): + self.out_python = self.register(srd.OUTPUT_PYTHON) + self.out_ann = self.register(srd.OUTPUT_ANN) + self.overdrive = (self.options['overdrive'] == 'yes') + self.fall = 0 + self.rise = 0 + self.bit_count = -1 + def putm(self, data): self.put(0, 0, self.out_ann, data) - def putpb(self, data): + def putpfs(self, data): self.put(self.fall, self.samplenum, self.out_python, data) - def putb(self, data): + def putfs(self, data): self.put(self.fall, self.samplenum, self.out_ann, data) - def putx(self, data): - self.put(self.fall, self.cnt_bit[self.overdrive], self.out_ann, data) - def putfr(self, data): self.put(self.fall, self.rise, self.out_ann, data) @@ -93,22 +158,6 @@ class Decoder(srd.Decoder): def putrs(self, data): self.put(self.rise, self.samplenum, self.out_ann, data) - def __init__(self): - self.samplerate = None - self.samplenum = 0 - self.state = 'WAIT FOR FALLING EDGE' - self.present = 0 - self.bit = 0 - self.bit_cnt = 0 - self.command = 0 - self.overdrive = 0 - self.fall = 0 - self.rise = 0 - - def start(self): - self.out_python = self.register(srd.OUTPUT_PYTHON) - self.out_ann = self.register(srd.OUTPUT_ANN) - def checks(self): # Check if samplerate is appropriate. if self.options['overdrive'] == 'yes': @@ -126,164 +175,173 @@ class Decoder(srd.Decoder): self.putm([1, ['Sampling rate is suggested to be above ' + '1MHz for proper normal mode decoding.']]) - # Check if sample times are in the allowed range. - - time_min = float(self.cnt_normal_bit) / self.samplerate - time_max = float(self.cnt_normal_bit + 1) / self.samplerate - if (time_min < 0.000005) or (time_max > 0.000015): - self.putm([1, ['The normal mode data sample time interval ' + - '(%2.1fus-%2.1fus) should be inside (5.0us, 15.0us).' - % (time_min * 1000000, time_max * 1000000)]]) - - time_min = float(self.cnt_normal_presence) / self.samplerate - time_max = float(self.cnt_normal_presence + 1) / self.samplerate - if (time_min < 0.0000681) or (time_max > 0.000075): - self.putm([1, ['The normal mode presence sample time interval ' + - '(%2.1fus-%2.1fus) should be inside (68.1us, 75.0us).' - % (time_min * 1000000, time_max * 1000000)]]) - - time_min = float(self.cnt_overdrive_bit) / self.samplerate - time_max = float(self.cnt_overdrive_bit + 1) / self.samplerate - if (time_min < 0.000001) or (time_max > 0.000002): - self.putm([1, ['The overdrive mode data sample time interval ' + - '(%2.1fus-%2.1fus) should be inside (1.0us, 2.0us).' - % (time_min * 1000000, time_max * 1000000)]]) - - time_min = float(self.cnt_overdrive_presence) / self.samplerate - time_max = float(self.cnt_overdrive_presence + 1) / self.samplerate - if (time_min < 0.0000073) or (time_max > 0.000010): - self.putm([1, ['The overdrive mode presence sample time interval ' + - '(%2.1fus-%2.1fus) should be inside (7.3us, 10.0us).' - % (time_min * 1000000, time_max * 1000000)]]) - - def metadata(self, key, value): if key != srd.SRD_CONF_SAMPLERATE: return self.samplerate = value - # The default 1-Wire time base is 30us. This is used to calculate - # sampling times. - samplerate = float(self.samplerate) + def wait_falling_timeout(self, start, t): + # Wait until either a falling edge is seen, and/or the specified + # number of samples have been skipped (i.e. time has passed). + cnt = int((t[self.overdrive] / 1000000.0) * self.samplerate) + samples_to_skip = (start + cnt) - self.samplenum + samples_to_skip = samples_to_skip if (samples_to_skip > 0) else 0 + return self.wait([{0: 'f'}, {'skip': samples_to_skip}]) - x = float(self.options['cnt_normal_bit']) / 1000000.0 - self.cnt_normal_bit = int(samplerate * x) - 1 - x = float(self.options['cnt_normal_slot']) / 1000000.0 - self.cnt_normal_slot = int(samplerate * x) - 1 - x = float(self.options['cnt_normal_presence']) / 1000000.0 - self.cnt_normal_presence = int(samplerate * x) - 1 - x = float(self.options['cnt_normal_reset']) / 1000000.0 - self.cnt_normal_reset = int(samplerate * x) - 1 - x = float(self.options['cnt_overdrive_bit']) / 1000000.0 - self.cnt_overdrive_bit = int(samplerate * x) - 1 - x = float(self.options['cnt_overdrive_slot']) / 1000000.0 - self.cnt_overdrive_slot = int(samplerate * x) - 1 - x = float(self.options['cnt_overdrive_presence']) / 1000000.0 - self.cnt_overdrive_presence = int(samplerate * x) - 1 - x = float(self.options['cnt_overdrive_reset']) / 1000000.0 - self.cnt_overdrive_reset = int(samplerate * x) - 1 - - # Organize values into lists. - self.cnt_bit = [self.cnt_normal_bit, self.cnt_overdrive_bit] - self.cnt_presence = [self.cnt_normal_presence, self.cnt_overdrive_presence] - self.cnt_reset = [self.cnt_normal_reset, self.cnt_overdrive_reset] - self.cnt_slot = [self.cnt_normal_slot, self.cnt_overdrive_slot] - - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - for (self.samplenum, (owr, pwr)) in data: - data.itercnt += 1 - if self.samplenum == 0: - self.checks() + self.checks() + while True: # State machine. - if self.state == 'WAIT FOR FALLING EDGE': - # The start of a cycle is a falling edge. - if owr != 0: - continue - # Save the sample number for the falling edge. + if self.state == 'INITIAL': # Unknown initial state. + # Wait until we reach the idle high state. + self.wait({0: 'h'}) + self.rise = self.samplenum + self.state = 'IDLE' + elif self.state == 'IDLE': # Idle high state. + # Wait for falling edge. + self.wait({0: 'f'}) self.fall = self.samplenum - # Go to waiting for sample time. - self.state = 'WAIT FOR DATA SAMPLE' - elif self.state == 'WAIT FOR DATA SAMPLE': - # Sample data bit. - t = self.samplenum - self.fall - if t == self.cnt_bit[self.overdrive]: - self.bit = owr - self.state = 'WAIT FOR DATA SLOT END' - elif self.state == 'WAIT FOR DATA SLOT END': - # A data slot ends in a recovery period, otherwise, this is - # probably a reset. - t = self.samplenum - self.fall - if t != self.cnt_slot[self.overdrive]: - continue - - if owr == 0: - # This seems to be a reset slot, wait for its end. - self.state = 'WAIT FOR RISING EDGE' - continue - - self.putb([0, ['Bit: %d' % self.bit, '%d' % self.bit]]) - self.putpb(['BIT', self.bit]) - - # Checking the first command to see if overdrive mode - # should be entered. - if self.bit_cnt <= 8: - self.command |= (self.bit << self.bit_cnt) - elif self.bit_cnt == 8 and self.command in [0x3c, 0x69]: - self.putx([4, ['Entering overdrive mode', 'Overdrive on']]) - # Increment the bit counter. - self.bit_cnt += 1 - # Wait for next slot. - self.state = 'WAIT FOR FALLING EDGE' - elif self.state == 'WAIT FOR RISING EDGE': - # The end of a cycle is a rising edge. - if owr != 1: - continue - - # Check if this was a reset cycle. - t = self.samplenum - self.fall - if t > self.cnt_normal_reset: - # Save the sample number for the rising edge. - self.rise = self.samplenum - self.putfr([2, ['Reset', 'Rst', 'R']]) - self.state = 'WAIT FOR PRESENCE DETECT' - # Exit overdrive mode. + # Get time since last rising edge. + time = ((self.fall - self.rise) / self.samplerate) * 1000000.0 + if self.rise > 0 and \ + time < timing['REC']['min'][self.overdrive]: + self.putfr([1, ['Recovery time not long enough' + 'Recovery too short', + 'REC < ' + str(timing['REC']['min'][self.overdrive])]]) + # A reset pulse or slot can start on a falling edge. + self.state = 'LOW' + # TODO: Check minimum recovery time. + elif self.state == 'LOW': # Reset pulse or slot. + # Wait for rising edge. + self.wait({0: 'r'}) + self.rise = self.samplenum + # Detect reset or slot base on timing. + time = ((self.rise - self.fall) / self.samplerate) * 1000000.0 + if time >= timing['RSTL']['min'][False]: # Normal reset pulse. + if time > timing['RSTL']['max'][False]: + self.putfr([1, ['Too long reset pulse might mask interrupt ' + + 'signalling by other devices', + 'Reset pulse too long', + 'RST > ' + str(timing['RSTL']['max'][False])]]) + # Regular reset pulse clears overdrive speed. if self.overdrive: - self.putx([4, ['Exiting overdrive mode', 'Overdrive off']]) - self.overdrive = 0 - # Clear command bit counter and data register. - self.bit_cnt = 0 - self.command = 0 - elif (t > self.cnt_overdrive_reset) and self.overdrive: - # Save the sample number for the rising edge. - self.rise = self.samplenum + self.putfr([4, ['Exiting overdrive mode', 'Overdrive off']]) + self.overdrive = False self.putfr([2, ['Reset', 'Rst', 'R']]) - self.state = 'WAIT FOR PRESENCE DETECT' - # Otherwise this is assumed to be a data bit. + self.state = 'PRESENCE DETECT HIGH' + elif self.overdrive == True and \ + time >= timing['RSTL']['min'][self.overdrive] and \ + time < timing['RSTL']['max'][self.overdrive]: + # Overdrive reset pulse. + self.putfr([2, ['Reset', 'Rst', 'R']]) + self.state = 'PRESENCE DETECT HIGH' + elif time < timing['SLOT']['max'][self.overdrive]: + # Read/write time slot. + if time < timing['LOWR']['min'][self.overdrive]: + self.putfr([1, ['Low signal not long enough', + 'Low too short', + 'LOW < ' + str(timing['LOWR']['min'][self.overdrive])]]) + if time < timing['LOWR']['max'][self.overdrive]: + self.bit = 1 # Short pulse is a 1 bit. + else: + self.bit = 0 # Long pulse is a 0 bit. + # Wait for end of slot. + self.state = 'SLOT' else: - self.state = 'WAIT FOR FALLING EDGE' - elif self.state == 'WAIT FOR PRESENCE DETECT': - # Sample presence status. - t = self.samplenum - self.rise - if t == self.cnt_presence[self.overdrive]: - self.present = owr - self.state = 'WAIT FOR RESET SLOT END' - elif self.state == 'WAIT FOR RESET SLOT END': - # A reset slot ends in a long recovery period. - t = self.samplenum - self.rise - if t != self.cnt_reset[self.overdrive]: - continue + # Timing outside of known states. + self.putfr([1, ['Erroneous signal', 'Error', 'Err', 'E']]) + self.state = 'IDLE' + elif self.state == 'PRESENCE DETECT HIGH': # Wait for slave presence signal. + # Wait for a falling edge and/or presence detect signal. + self.wait_falling_timeout(self.rise, timing['PDH']['max']) - if owr == 0: - # This seems to be a reset slot, wait for its end. - self.state = 'WAIT FOR RISING EDGE' - continue + # Calculate time since rising edge. + time = ((self.samplenum - self.rise) / self.samplerate) * 1000000.0 - p = 'false' if self.present else 'true' - self.putrs([3, ['Presence: %s' % p, 'Presence', 'Pres', 'P']]) - self.putprs(['RESET/PRESENCE', not self.present]) + if (self.matched & (0b1 << 0)) and not (self.matched & (0b1 << 1)): + # Presence detected. + if time < timing['PDH']['min'][self.overdrive]: + self.putrs([1, ['Presence detect signal is too early', + 'Presence detect too early', + 'PDH < ' + str(timing['PDH']['min'][self.overdrive])]]) + self.fall = self.samplenum + self.state = 'PRESENCE DETECT LOW' + else: # No presence detected. + self.putrs([3, ['Presence: false', 'Presence', 'Pres', 'P']]) + self.putprs(['RESET/PRESENCE', False]) + self.state = 'IDLE' + elif self.state == 'PRESENCE DETECT LOW': # Slave presence signalled. + # Wait for end of presence signal (on rising edge). + self.wait({0: 'r'}) + # Calculate time since start of presence signal. + time = ((self.samplenum - self.fall) / self.samplerate) * 1000000.0 + if time < timing['PDL']['min'][self.overdrive]: + self.putfs([1, ['Presence detect signal is too short', + 'Presence detect too short', + 'PDL < ' + str(timing['PDL']['min'][self.overdrive])]]) + elif time > timing['PDL']['max'][self.overdrive]: + self.putfs([1, ['Presence detect signal is too long', + 'Presence detect too long', + 'PDL > ' + str(timing['PDL']['max'][self.overdrive])]]) + if time > timing['RSTH']['min'][self.overdrive]: + self.rise = self.samplenum + # Wait for end of presence detect. + self.state = 'PRESENCE DETECT' - # Wait for next slot. - self.state = 'WAIT FOR FALLING EDGE' + # End states (for additional checks). + if self.state == 'SLOT': # Wait for end of time slot. + # Wait for a falling edge and/or end of timeslot. + self.wait_falling_timeout(self.fall, timing['SLOT']['min']) + if (self.matched & (0b1 << 0)) and not (self.matched & (0b1 << 1)): + # Low detected before end of slot. + self.putfs([1, ['Time slot not long enough', + 'Slot too short', + 'SLOT < ' + str(timing['SLOT']['min'][self.overdrive])]]) + # Don't output invalid bit. + self.fall = self.samplenum + self.state = 'LOW' + else: # End of time slot. + # Output bit. + self.putfs([0, ['Bit: %d' % self.bit, '%d' % self.bit]]) + self.putpfs(['BIT', self.bit]) + # Save command bits. + if self.bit_count >= 0: + self.command += (self.bit << self.bit_count) + self.bit_count += 1 + # Check for overdrive ROM command. + if self.bit_count >= 8: + if self.command == 0x3c or self.command == 0x69: + self.overdrive = True + self.put(self.samplenum, self.samplenum, + self.out_ann, + [4, ['Entering overdrive mode', 'Overdrive on']]) + self.bit_count = -1 + self.state = 'IDLE' + + if self.state == 'PRESENCE DETECT': + # Wait for a falling edge and/or end of presence detect. + self.wait_falling_timeout(self.rise, timing['RSTH']['min']) + + if (self.matched & (0b1 << 0)) and not (self.matched & (0b1 << 1)): + # Low detected before end of presence detect. + self.putfs([1, ['Presence detect not long enough', + 'Presence detect too short', + 'RTSH < ' + str(timing['RSTH']['min'][self.overdrive])]]) + # Inform about presence detected. + self.putrs([3, ['Slave presence detected', 'Slave present', + 'Present', 'P']]) + self.putprs(['RESET/PRESENCE', True]) + self.fall = self.samplenum + self.state = 'LOW' + else: # End of time slot. + # Inform about presence detected. + self.putrs([3, ['Presence: true', 'Presence', 'Pres', 'P']]) + self.putprs(['RESET/PRESENCE', True]) + self.rise = self.samplenum + # Start counting the first 8 bits to get the ROM command. + self.bit_count = 0 + self.command = 0 + self.state = 'IDLE' diff --git a/libsigrokdecode4DSL/decoders/onewire_network/__init__.py b/libsigrokdecode4DSL/decoders/onewire_network/__init__.py index 44b9bcc7..60907efa 100755 --- a/libsigrokdecode4DSL/decoders/onewire_network/__init__.py +++ b/libsigrokdecode4DSL/decoders/onewire_network/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/onewire_network/pd.py b/libsigrokdecode4DSL/decoders/onewire_network/pd.py index 1a7567a1..ef302aea 100755 --- a/libsigrokdecode4DSL/decoders/onewire_network/pd.py +++ b/libsigrokdecode4DSL/decoders/onewire_network/pd.py @@ -14,26 +14,27 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd # Dictionary of ROM commands and their names, next state. command = { - 0x33: ['Read ROM' , 'GET ROM' ], - 0x0f: ['Conditional read ROM' , 'GET ROM' ], - 0xcc: ['Skip ROM' , 'TRANSPORT' ], - 0x55: ['Match ROM' , 'GET ROM' ], - 0xf0: ['Search ROM' , 'SEARCH ROM'], - 0xec: ['Conditional search ROM', 'SEARCH ROM'], - 0x3c: ['Overdrive skip ROM' , 'TRANSPORT' ], - 0x69: ['Overdrive match ROM' , 'GET ROM' ], + 0x33: ['Read ROM' , 'GET ROM' ], + 0x0f: ['Conditional read ROM' , 'GET ROM' ], + 0xcc: ['Skip ROM' , 'TRANSPORT' ], + 0x55: ['Match ROM' , 'GET ROM' ], + 0xf0: ['Search ROM' , 'SEARCH ROM'], + 0xec: ['Conditional search ROM' , 'SEARCH ROM'], + 0x3c: ['Overdrive skip ROM' , 'TRANSPORT' ], + 0x69: ['Overdrive match ROM' , 'GET ROM' ], + 0xa5: ['Resume' , 'TRANSPORT' ], + 0x96: ['DS2408: Disable Test Mode' , 'GET ROM' ], } class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'onewire_network' name = '1-Wire network layer' longname = '1-Wire serial communication bus (network layer)' @@ -41,11 +42,15 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['onewire_link'] outputs = ['onewire_network'] + tags = ['Embedded/industrial'] annotations = ( ('text', 'Human-readable text'), ) def __init__(self): + self.reset() + + def reset(self): self.ss_block = 0 self.es_block = 0 self.state = 'COMMAND' @@ -130,7 +135,7 @@ class Decoder(srd.Decoder): # Data collector. def onewire_collect(self, length, val, ss, es): # Storing the sample this sequence begins with. - if self.bit_cnt == 1: + if self.bit_cnt == 0: self.ss_block = ss self.data = self.data & ~(1 << self.bit_cnt) | (val << self.bit_cnt) self.bit_cnt += 1 diff --git a/libsigrokdecode4DSL/decoders/ook/__init__.py b/libsigrokdecode4DSL/decoders/ook/__init__.py new file mode 100755 index 00000000..24e493bb --- /dev/null +++ b/libsigrokdecode4DSL/decoders/ook/__init__.py @@ -0,0 +1,36 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Steve R +## +## 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 2 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 . +## + +''' +OOK decodes On-off keying based remote control protocols. + +It is aimed at 433MHz but should also work with other common RC frequencies. +The input can be captured directly from a transmitter (before the modulation +stage) or demodulated by an RF receiver. + +Over the air captured traces will be a lot noisier and will probably need the +area of interest to be zoomed onto, then selected with the "Cursors" and the +"Save Selected Range As" feature to be used to extract it from the noise. + +There is a limited amount of pre-filtering and garbage removal built into the +decoder which can sometimes extract signals directly from a larger over the air +trace. It depends heavily on your environment. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/ook/pd.py b/libsigrokdecode4DSL/decoders/ook/pd.py new file mode 100755 index 00000000..559291c4 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/ook/pd.py @@ -0,0 +1,484 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Steve R +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd + +''' +OUTPUT_PYTHON format: +Samples: The Samples array is sent when a DECODE_TIMEOUT occurs. +[, , ] + is the sample number of the start of the decoded bit. This may not line +up with the pulses that were converted into the decoded bit particularly for +Manchester encoding. + is the sample number of the end of the decoded bit. + is a single character string which is the state of the decoded bit. +This can be +'0' zero or low +'1' one or high +'E' Error or invalid. This can be caused by missing transitions or the wrong +pulse lengths according to the rules for the particular encoding. In some cases +this is intentional (Oregon 1 preamble) and is part of the sync pattern. In +other cases the signal could simply be broken. + +If there are more than self.max_errors (default 5) in decoding then the +OUTPUT_PYTHON is not sent as the data is assumed to be worthless. +There also needs to be a low for five times the preamble period at the end of +each set of pulses to trigger a DECODE_TIMEOUT and get the OUTPUT_PYTHON sent. +''' + +class SamplerateError(Exception): + pass + +class Decoder(srd.Decoder): + api_version = 3 + id = 'ook' + name = 'OOK' + longname = 'On-off keying' + desc = 'On-off keying protocol.' + license = 'gplv2+' + inputs = ['logic'] + outputs = ['ook'] + tags = ['Encoding'] + channels = ( + {'id': 'data', 'name': 'Data', 'desc': 'Data line'}, + ) + annotations = ( + ('frame', 'Frame'), + ('info', 'Info'), + ('1111', '1111'), + ('1010', '1010'), + ('diffman', 'Diff Man'), + ('nrz', 'NRZ'), + ) + annotation_rows = ( + ('frame', 'Framing',(0,)), + ('info', 'Info', (1,)), + ('man1111', 'Man 1111', (2,)), + ('man1010', 'Man 1010', (3,)), + ('diffman', 'Diff Man', (4,)), + ('nrz', 'NRZ', (5,)), + ) + binary = ( + ('pulse-lengths', 'Pulse lengths'), + ) + options = ( + {'id': 'invert', 'desc': 'Invert data', 'default': 'no', + 'values': ('no', 'yes')}, + {'id': 'decodeas', 'desc': 'Decode type', 'default': 'Manchester', + 'values': ('NRZ', 'Manchester', 'Diff Manchester')}, + {'id': 'preamble', 'desc': 'Preamble', 'default': 'auto', + 'values': ('auto', '1010', '1111')}, + {'id': 'preamlen', 'desc': 'Filter length', 'default': '7', + 'values': ('0', '3', '4', '5', '6', '7', '8', '9', '10')}, + {'id': 'diffmanvar', 'desc': 'Transition at start', 'default': '1', + 'values': ('1', '0')}, + ) + + def __init__(self): + self.reset() + + def reset(self): + self.samplerate = None + self.ss = self.es = -1 + self.ss_1111 = self.ss_1010 = -1 + self.samplenumber_last = None + self.sample_first = None + self.sample_high = 0 + self.sample_low = 0 + self.edge_count = 0 + self.word_first = None + self.word_count = 0 + self.state = 'IDLE' + self.lstate = None + self.lstate_1010 = None + self.insync = 0 # Preamble in sync flag + self.man_errors = 0 + self.man_errors_1010 = 0 + self.preamble = [] # Preamble buffer + self.half_time = -1 # Half time for man 1111 + self.half_time_1010 = 0 # Half time for man 1010 + self.pulse_lengths = [] # Pulse lengths + self.decoded = [] # Decoded stream + self.decoded_1010 = [] # Decoded stream + self.diff_man_trans = '0' # Transition + self.diff_man_len = 1 # Length of pulse in half clock periods + self.max_errors = 5 # Max number of errors to output OOK + + def metadata(self, key, value): + if key == srd.SRD_CONF_SAMPLERATE: + self.samplerate = value + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + self.out_python = self.register(srd.OUTPUT_PYTHON) + self.out_binary = self.register(srd.OUTPUT_BINARY) + self.invert = self.options['invert'] + self.decodeas = self.options['decodeas'] + self.preamble_val = self.options['preamble'] + self.preamble_len = self.options['preamlen'] + self.diffmanvar = self.options['diffmanvar'] + + def putx(self, data): + self.put(self.ss, self.es, self.out_ann, data) + + def putp(self, data): + self.put(self.ss, self.es, self.out_python, data) + + def dump_pulse_lengths(self): + if self.samplerate: + self.pulse_lengths[-1] = self.sample_first # Fix final pulse length. + s = 'Pulses(us)=' + s += ','.join(str(int(int(x) * 1000000 / self.samplerate)) + for x in self.pulse_lengths) + s += '\n' + self.put(self.samplenum - 10, self.samplenum, self.out_binary, + [0, bytes([ord(c) for c in s])]) + + def decode_nrz(self, start, samples, state): + self.pulse_lengths.append(samples) + # Use different high and low widths to compensate skewed waveforms. + dsamples = self.sample_high if state == '1' else self.sample_low + self.ss, self.es = start, start + samples + while samples > dsamples * 0.5: + if samples >= dsamples * 1.5: # More than one bit. + self.es = self.ss + dsamples + self.putx([5, [state]]) + self.decoded.append([self.ss, self.es, state]) + self.edge_count += 1 + elif samples >= dsamples * 0.5 and samples < dsamples * 1.5: # Last bit. + self.putx([5, [state]]) + self.decoded.append([self.ss, self.es, state]) + self.edge_count += 1 + else: + self.edge_count += 1 + samples -= dsamples + self.ss += dsamples + self.es += dsamples + + # Ensure 2nd row doesn't go past end of 1st row. + if self.es > self.samplenum: + self.es = self.samplenum + + if self.state == 'DECODE_TIMEOUT': # Five bits - reset. + self.ss = self.decoded[0][0] + self.es = self.decoded[len(self.decoded) - 1][1] + self.dump_pulse_lengths() + self.putp(self.decoded) + self.decode_timeout() + break + + def lock_onto_preamble(self, samples, state): # Filters and recovers clock. + self.edge_count += 1 + l2s = 5 # Max ratio of long to short pulses. + + # Filter incoming pulses to remove random noise. + if self.state == 'DECODE_TIMEOUT': + self.preamble = [] + self.edge_count = 0 + self.word_first = self.samplenum + self.sample_first = self.samplenum - self.samplenumber_last + self.state = 'WAITING_FOR_PREAMBLE' + self.man_errors = 0 + + pre_detect = int(self.preamble_len) # Number of valid pulses to detect. + pre_samples = self.samplenum - self.samplenumber_last + if len(self.preamble) > 0: + if (pre_samples * l2s < self.preamble[-1][1] or + self.preamble[-1][1] * l2s < pre_samples): # Garbage in. + self.put(self.samplenum, self.samplenum, + self.out_ann, [0, ['R']]) # Display resets. + self.preamble = [] # Clear buffer. + self.preamble.append([self.samplenumber_last, + pre_samples, state]) + self.edge_count = 0 + self.samplenumber_last = self.samplenum + self.word_first = self.samplenum + else: + self.preamble.append([self.samplenumber_last, + pre_samples, state]) + else: + self.preamble.append([self.samplenumber_last, + pre_samples, state]) + + pre = self.preamble + if len(self.preamble) == pre_detect: # Have a valid series of pulses. + if self.preamble[0][2] == '1': + self.sample_high = self.preamble[0][1] # Allows skewed pulses. + self.sample_low = self.preamble[1][1] + else: + self.sample_high = self.preamble[1][1] + self.sample_low = self.preamble[0][1] + + self.edge_count = 0 + + for i in range(len(self.preamble)): + if i > 1: + if (pre[i][1] > pre[i - 2][1] * 1.25 or + pre[i][1] * 1.25 < pre[i - 2][1]): # Adjust ref width. + if pre[i][2] == '1': + self.sample_high = pre[i][1] + else: + self.sample_low = pre[i][1] + + # Display start of preamble. + if self.decodeas == 'NRZ': + self.decode_nrz(pre[i][0], pre[i][1], pre[i][2]) + if self.decodeas == 'Manchester': + self.decode_manchester(pre[i][0], pre[i][1], pre[i][2]) + if self.decodeas == 'Diff Manchester': + self.es = pre[i][0] + pre[i][1] + self.decode_diff_manchester(pre[i][0], pre[i][1], pre[i][2]) + + # Used to timeout signal. + self.sample_first = int((self.sample_high + self.sample_low)/2) + self.insync = 1 + self.state = 'DECODING' + self.lstate = state + self.lstate_1010 = state + + def decode_diff_manchester(self, start, samples, state): + self.pulse_lengths.append(samples) + + # Use different high and low widths to compensate skewed waveforms. + dsamples = self.sample_high if state == '1' else self.sample_low + + self.es = start + samples + p_length = round(samples / dsamples) # Find relative pulse length. + + if self.edge_count == 0: + self.diff_man_trans = '1' # Very first pulse must be a transition. + self.diff_man_len = 1 # Must also be a half pulse. + self.ss = start + elif self.edge_count % 2 == 1: # Time to make a decision. + if self.diffmanvar == '0': # Transition at self.ss is a zero. + self.diff_man_trans = '0' if self.diff_man_trans == '1' else '1' + if self.diff_man_len == 1 and p_length == 1: + self.putx([4, [self.diff_man_trans]]) + self.decoded.append([self.ss, self.es, self.diff_man_trans]) + self.diff_man_trans = '1' + elif self.diff_man_len == 1 and p_length == 2: + self.es -= int(samples / 2) + self.putx([4, [self.diff_man_trans]]) + self.decoded.append([self.ss, self.es, self.diff_man_trans]) + self.diff_man_trans = '0' + self.edge_count += 1 # Add a virt edge to keep in sync with clk. + elif self.diff_man_len == 2 and p_length == 1: + self.putx([4, [self.diff_man_trans]]) + self.decoded.append([self.ss, self.es, self.diff_man_trans]) + self.diff_man_trans = '1' + elif self.diff_man_len == 2 and p_length == 2: # Double illegal E E. + self.es -= samples + self.putx([4, ['E']]) + self.decoded.append([self.ss, self.es, 'E']) + self.ss = self.es + self.es += samples + self.putx([4, ['E']]) + self.decoded.append([self.ss, self.es, 'E']) + self.diff_man_trans = '1' + elif self.diff_man_len == 1 and p_length > 4: + if self.state == 'DECODE_TIMEOUT': + self.es = self.ss + 2 * self.sample_first + self.putx([4, [self.diff_man_trans]]) # Write error. + self.decoded.append([self.ss, self.es, self.diff_man_trans]) + self.ss = self.decoded[0][0] + self.es = self.decoded[len(self.decoded) - 1][1] + self.dump_pulse_lengths() + if self.man_errors < self.max_errors: + self.putp(self.decoded) + else: + error_message = 'Probably not Diff Manchester encoded' + self.ss = self.word_first + self.putx([1, [error_message]]) + self.decode_timeout() + self.diff_man_trans = '1' + self.ss = self.es + self.diff_man_len = p_length # Save the previous length. + self.edge_count += 1 + + def decode_manchester_sim(self, start, samples, state, + dsamples, half_time, lstate, ss, pream): + ook_bit = [] + errors = 0 + if self.edge_count == 0: + half_time += 1 + if samples > 0.75 * dsamples and samples <= 1.5 * dsamples: # Long p. + half_time += 2 + if half_time % 2 == 0: # Transition. + es = start + else: + es = start + int(samples / 2) + if ss == start: + lstate = 'E' + es = start + samples + if not (self.edge_count == 0 and pream == '1010'): # Skip first p. + ook_bit = [ss, es, lstate] + lstate = state + ss = es + elif samples > 0.25 * dsamples and samples <= 0.75 * dsamples: # Short p. + half_time += 1 + if (half_time % 2 == 0): # Transition. + es = start + samples + ook_bit = [ss, es, lstate] + lstate = state + ss = es + else: # 1st half. + ss = start + lstate = state + else: # Too long or too short - error. + errors = 1 + if self.state != 'DECODE_TIMEOUT': # Error condition. + lstate = 'E' + es = ss + samples + else: # Assume final half bit buried in timeout pulse. + es = ss + self.sample_first + ook_bit = [ss, es, lstate] + ss = es + + return (half_time, lstate, ss, ook_bit, errors) + + def decode_manchester(self, start, samples, state): + self.pulse_lengths.append(samples) + + # Use different high and low widths to compensate skewed waveforms. + dsamples = self.sample_high if state == '1' else self.sample_low + + if self.preamble_val != '1010': # 1111 preamble is half clock T. + (self.half_time, self.lstate, self.ss_1111, ook_bit, errors) = ( + self.decode_manchester_sim(start, samples, state, dsamples * 2, + self.half_time, self.lstate, + self.ss_1111, '1111')) + self.man_errors += errors + if ook_bit != []: + self.decoded.append([ook_bit[0], ook_bit[1], ook_bit[2]]) + + if self.preamble_val != '1111': # 1010 preamble is clock T. + (self.half_time_1010, self.lstate_1010, self.ss_1010, + ook_bit, errors) = ( + self.decode_manchester_sim(start, samples, state, dsamples, + self.half_time_1010, self.lstate_1010, + self.ss_1010, '1010')) + self.man_errors_1010 += errors + if ook_bit != []: + self.decoded_1010.append([ook_bit[0], ook_bit[1], ook_bit[2]]) + + self.edge_count += 1 + + # Stream display and save ook_bit. + if ook_bit != []: + self.ss, self.es = ook_bit[0], ook_bit[1] + if self.preamble_val == '1111': + self.putx([2, [ook_bit[2]]]) + if self.preamble_val == '1010': + self.putx([3, [ook_bit[2]]]) + + if self.state == 'DECODE_TIMEOUT': # End of packet. + self.dump_pulse_lengths() + + decoded = [] + # If 1010 preamble has less errors use it. + if (self.preamble_val == '1010' or + (self.man_errors_1010 < self.max_errors and + self.man_errors_1010 < self.man_errors and + len(self.decoded_1010) > 0)): + decoded = self.decoded_1010 + man_errors = self.man_errors_1010 + d_row = 3 + else: + decoded = self.decoded + man_errors = self.man_errors + d_row = 2 + + if self.preamble_val == 'auto': # Display OOK packet. + for i in range(len(decoded)): + self.ss, self.es = decoded[i][0], decoded[i][1] + self.putx([d_row, [decoded[i][2]]]) + + if (man_errors < self.max_errors and len(decoded) > 0): + self.ss, self.es = decoded[0][0], decoded[len(decoded) - 1][1] + self.putp(decoded) + else: + error_message = 'Not Manchester encoded or wrong preamble' + self.ss = self.word_first + self.putx([1, [error_message]]) + + self.put(self.es, self.es, self.out_ann, [0, ['T']]) # Mark timeout. + self.decode_timeout() + + def decode_timeout(self): + self.word_count = 0 + self.samplenumber_last = None + self.edge_count = 0 + self.man_errors = 0 # Clear the bit error counters. + self.man_errors_1010 = 0 + self.state = 'IDLE' + self.wait({0: 'e'}) # Get rid of long pulse. + self.samplenumber_last = self.samplenum + self.word_first = self.samplenum + self.insync = 0 # Preamble in sync flag + self.preamble = [] # Preamble buffer + self.half_time = -1 # Half time for man 1111 + self.half_time_1010 = 0 # Half time for man 1010 + self.decoded = [] # Decoded bits + self.decoded_1010 = [] # Decoded bits for man 1010 + self.pulse_lengths = [] + + def decode(self): + while True: + if self.edge_count == 0: # Waiting for a signal. + (ook,) = self.wait({0: 'e'}) + self.state = 'DECODING' + else: + (ook,) = self.wait([{0: 'e'}, {'skip': 5 * self.sample_first}]) + if (self.matched & (0b1 << 1)) and not (self.matched & (0b1 << 0)): # No edges for 5 p's. + self.state = 'DECODE_TIMEOUT' + + if not self.samplenumber_last: # Set counters to start of signal. + self.samplenumber_last = self.samplenum + self.word_first = self.samplenum + continue + samples = self.samplenum - self.samplenumber_last + if not self.sample_first: # Get number of samples for first pulse. + self.sample_first = samples + + pinstate = ook + if self.state == 'DECODE_TIMEOUT': # No edge so flip the state. + pinstate = int(not pinstate) + if self.invert == 'yes': # Invert signal. + pinstate = int(not pinstate) + state = '0' if pinstate else '1' + + # No preamble filtering or checking and no skew correction. + if self.preamble_len == '0': + self.sample_high = self.sample_first + self.sample_low = self.sample_first + self.insync = 0 + + if self.insync == 0: + self.lock_onto_preamble(samples, state) + else: + if self.decodeas == 'NRZ': + self.decode_nrz(self.samplenumber_last, samples, state) + if self.decodeas == 'Manchester': + self.decode_manchester(self.samplenumber_last, + samples, state) + if self.decodeas == 'Diff Manchester': + self.decode_diff_manchester(self.samplenumber_last, + samples, state) + + self.samplenumber_last = self.samplenum diff --git a/libsigrokdecode4DSL/decoders/ook_oregon/__init__.py b/libsigrokdecode4DSL/decoders/ook_oregon/__init__.py new file mode 100755 index 00000000..f1a1fdf4 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/ook_oregon/__init__.py @@ -0,0 +1,25 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Steve R +## +## 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 2 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 . +## + +''' +This decoder stacks on top of the 'ook' PD and decodes the Oregon Scientific +433MHz remote control protocol for weather sensors. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/ook_oregon/lists.py b/libsigrokdecode4DSL/decoders/ook_oregon/lists.py new file mode 100755 index 00000000..c46c4cc7 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/ook_oregon/lists.py @@ -0,0 +1,75 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Steve R +## +## 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 2 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 . +## + +# Most of the info here comes from "434MHz RF Protocol Descriptions for +# Wireless Weather Sensors - October 2015" Known Sensor ID Codes - p25. + +# Format is 4 hex digit ID code followed by a LIST of models that use that +# ID and the type of sensor. +# SensorID is used as the hash in a Python hash table, so it must be upper case. +# The type of sensor is used to decode and display readings in the L2 decode, +# it's case-sensitive. +# Be very careful with the formatting ' [] and commas. + +sensor = { +# 'SensorID': [['model1', 'model2'], 'type'], + '1984': [['WGR800'], 'Wind'], # The newer anemometer with no temperature/RH sensor. + '1994': [['WGR800'], 'Wind'], # The original anemometer which included a temperature/RH sensor. + '1A2D': [['THGR228N'], 'Temp_Hum1'], + '1A3D': [['THGR918'], ''], + '1D20': [['THGN123N', 'THGR122NX', 'THGN123N', 'THGR228N'], 'Temp_Hum'], + '1D30': [['THGN500', 'THGN132N'], ''], + '2914': [['PCR800'], 'Rain'], + '2A19': [['PCR800'], 'Rain1'], + '2A1D': [['RGR918'], 'Rain'], + '2D10': [['RGR968', 'PGR968 '], 'Rain1'], + '3A0D': [['STR918', 'WGR918'], 'Wind'], + '5A5D': [['BTHR918'], ''], + '5A6D': [['BTHR918N'], 'Temp_Hum_Baro'], + '5D53': [['BTHGN129'], 'Baro'], + '5D60': [['BTHR968'], 'Temp_Hum_Baro'], + 'C844': [['THWR800'], 'Temp'], + 'CC13': [['RTGR328N'], 'Temp_Hum'], + 'CC23': [['THGR328N'], 'Temp_Hum'], + 'CD39': [['RTHR328N'], 'Temp'], + 'D874': [['UVN800'], 'UV1'], + 'EA4C': [['THWR288A'], 'Temp'], + 'EC40': [['THN132N', 'THR238NF'], 'Temp'], + 'EC70': [['UVR128'], 'UV'], + 'F824': [['THGN800', 'THGN801', 'THGR810'], 'Temp_Hum'], + 'F8B4': [['THGR810'], 'Temp_Hum'], +# '': ['PSR01'], '', ''], +# '': ['RTGR328NA'], '', ''], +# '': ['THC268'], '', ''], +# '': ['THWR288A-JD'], '', ''], +# '': ['THGR268'], '', ''], +# '': ['THR268'], '', ''], +} + +# The sensor checksum exceptions are used to calculate the right checksum for +# sensors that don't follow the v1, v2.1 and v3 methods. For instance a v2.1 +# sensor that has a v3 checksum. +sensor_checksum = { +# 'SensorID': ['checksum_method', 'comment'], + '1D20': ['v3', 'THGR228N'], + '5D60': ['v3', 'BTHR918N'], + 'EC40': ['v3', 'THN132N'], +} + +dir_table = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW', 'N'] diff --git a/libsigrokdecode4DSL/decoders/ook_oregon/pd.py b/libsigrokdecode4DSL/decoders/ook_oregon/pd.py new file mode 100755 index 00000000..225f5983 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/ook_oregon/pd.py @@ -0,0 +1,389 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Steve R +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd +import math +from .lists import * + +class Decoder(srd.Decoder): + api_version = 3 + id = 'ook_oregon' + name = 'Oregon' + longname = 'Oregon Scientific' + desc = 'Oregon Scientific weather sensor protocol.' + license = 'gplv2+' + inputs = ['ook'] + outputs = [] + tags = ['Sensor'] + annotations = ( + ('bit', 'Bit'), + ('field', 'Field'), + ('l2', 'Level 2'), + ('pre', 'Preamble'), + ('syn', 'Sync'), + ('id', 'SensorID'), + ('ch', 'Channel'), + ('roll', 'Rolling code'), + ('f1', 'Flags1'), + ) + annotation_rows = ( + ('bits', 'Bits', (0,)), + ('fields', 'Fields', (1, 3, 4)), + ('l2', 'Level 2', (2,)), + ) + binary = ( + ('data-hex', 'Hex data'), + ) + options = ( + {'id': 'unknown', 'desc': 'Unknown type is', 'default': 'Unknown', + 'values': ('Unknown', 'Temp', 'Temp_Hum', 'Temp_Hum1', 'Temp_Hum_Baro', + 'Temp_Hum_Baro1', 'UV', 'UV1', 'Wind', 'Rain', 'Rain1')}, + ) + + def __init__(self): + self.reset() + + def reset(self): + self.decoded = [] # Local cache of decoded OOK. + self.skip = None + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + self.out_binary = self.register(srd.OUTPUT_BINARY) + self.unknown = self.options['unknown'] + + def putx(self, data): + self.put(self.ss, self.es, self.out_ann, data) + + def dump_oregon_hex(self, start, finish): + nib = self.decoded_nibbles + hexstring = '' + for x in nib: + hexstring += str(x[3]) if x[3] != '' else ' ' + s = 'Oregon ' + self.ver + ' \"' + hexstring.upper() + '\"\n' + self.put(start, finish, self.out_binary, + [0, bytes([ord(c) for c in s])]) + + def oregon_put_pre_and_sync(self, len_pream, len_sync, ver): + ook = self.decoded + self.decode_pos = len_pream + self.ss, self.es = ook[0][0], ook[self.decode_pos][0] + self.putx([1, ['Oregon ' + ver + ' Preamble', ver + ' Preamble', + ver + ' Pre', ver]]) + self.decode_pos += len_sync + self.ss, self.es = ook[len_pream][0], ook[self.decode_pos][0] + self.putx([1, ['Sync', 'Syn', 'S']]) + + # Strip off preamble and sync bits. + self.decoded = self.decoded[self.decode_pos:] + self.ookstring = self.ookstring[self.decode_pos:] + self.ver = ver + + def oregon(self): + self.ookstring = '' + self.decode_pos = 0 + ook = self.decoded + for i in range(len(ook)): + self.ookstring += ook[i][2] + if '10011001' in self.ookstring[:40]: + (preamble, data) = self.ookstring.split('10011001', 1) + if len(data) > 0 and len(preamble) > 16: + self.oregon_put_pre_and_sync(len(preamble), 8, 'v2.1') + self.oregon_v2() + elif 'E1100' in self.ookstring[:17]: + (preamble, data) = self.ookstring.split('E1100', 1) + if len(data) > 0 and len(preamble) <= 12: + self.oregon_put_pre_and_sync(len(preamble), 5, 'v1') + self.oregon_v1() + elif '0101' in self.ookstring[:28]: + (preamble, data) = self.ookstring.split('0101', 1) + if len(data) > 0 and len(preamble) > 12: + self.oregon_put_pre_and_sync(len(preamble), 4, 'v3') + self.oregon_v3() + elif len(self.ookstring) > 16: # Ignore short packets. + error_message = 'Not Oregon or wrong preamble' + self.ss, self.es = ook[0][0], ook[len(ook) - 1][1] + self.putx([1,[error_message]]) + + def oregon_v1(self): + ook = self.decoded + self.decode_pos = 0 + self.decoded_nibbles = [] + if len(self.decoded) >= 32: # Check there are at least 8 nibbles. + self.oregon_put_nib('RollingCode', ook[self.decode_pos][0], + ook[self.decode_pos + 3][1], 4) + self.oregon_put_nib('Ch', ook[self.decode_pos][0], + ook[self.decode_pos + 3][1], 4) + self.oregon_put_nib('Temp', ook[self.decode_pos][0], + ook[self.decode_pos + 15][1], 16) + self.oregon_put_nib('Checksum', ook[self.decode_pos][0], + ook[self.decode_pos + 7][1], 8) + + self.dump_oregon_hex(ook[0][0], ook[len(ook) - 1][1]) + + # L2 decode. + self.oregon_temp(2) + self.oregon_channel(1) + self.oregon_battery(2) + self.oregon_checksum_v1() + + def oregon_v2(self): # Convert to v3 format - discard odd bits. + self.decode_pos = 0 + self.ookstring = self.ookstring[1::2] + for i in range(len(self.decoded)): + if i % 2 == 1: + self.decoded[i][0] = self.decoded[i - 1][0] # Re-align start pos. + self.decoded = self.decoded[1::2] # Discard left hand bits. + self.oregon_v3() # Decode with v3 decoder. + + def oregon_nibbles(self, ookstring): + num_nibbles = int(len(ookstring) / 4) + nibbles = [] + for i in range(num_nibbles): + nibble = ookstring[4 * i : 4 * i + 4] + nibble = nibble[::-1] # Reversed from right. + nibbles.append(nibble) + return nibbles + + def oregon_put_nib(self, label, start, finish, numbits): + param = self.ookstring[self.decode_pos:self.decode_pos + numbits] + param = self.oregon_nibbles(param) + if 'E' in ''.join(param): # Blank out fields with errors. + result = '' + else: + result = hex(int(''.join(param), 2))[2:] + if len(result) < numbits / 4: # Reinstate leading zeros. + result = '0' * (int(numbits / 4) - len(result)) + result + if label != '': + label += ': ' + self.put(start, finish, self.out_ann, [1, [label + result, result]]) + if label == '': # No label - use nibble position. + label = int(self.decode_pos / 4) + for i in range(len(param)): + ss = self.decoded[self.decode_pos + (4 * i)][0] + es = self.decoded[self.decode_pos + (4 * i) + 3][1] + # Blank out nibbles with errors. + result = '' if ('E' in param[i]) else hex(int(param[i], 2))[2:] + # Save nibbles for L2 decoder. + self.decoded_nibbles.append([ss, es, label, result]) + self.decode_pos += numbits + + def oregon_v3(self): + self.decode_pos = 0 + self.decoded_nibbles = [] + ook = self.decoded + + if len(self.decoded) >= 32: # Check there are at least 8 nibbles. + self.oregon_put_nib('SensorID', ook[self.decode_pos][0], + ook[self.decode_pos + 16][0], 16) + self.oregon_put_nib('Ch', ook[self.decode_pos][0], + ook[self.decode_pos + 3][1], 4) + self.oregon_put_nib('RollingCode', ook[self.decode_pos][0], + ook[self.decode_pos + 7][1], 8) + self.oregon_put_nib('Flags1', ook[self.decode_pos][0], + ook[self.decode_pos + 3][1], 4) + + rem_nibbles = len(self.ookstring[self.decode_pos:]) // 4 + for i in range(rem_nibbles): # Display and save rest of nibbles. + self.oregon_put_nib('', ook[self.decode_pos][0], + ook[self.decode_pos + 3][1], 4) + self.dump_oregon_hex(ook[0][0], ook[len(ook) - 1][1]) + self.oregon_level2() # Level 2 decode. + else: + error_message = 'Too short to decode' + self.put(ook[0][0], ook[-1][1], self.out_ann, [1, [error_message]]) + + def oregon_put_l2_param(self, offset, digits, dec_point, pre_label, label): + nib = self.decoded_nibbles + result = 0 + out_string = ''.join(str(x[3]) for x in nib[offset:offset + digits]) + if len(out_string) == digits: + for i in range(dec_point, 0, -1): + result += int(nib[offset + dec_point - i][3], 16) / pow(10, i) + for i in range(dec_point, digits): + result += int(nib[offset + i][3], 16) * pow(10, i - dec_point) + result = '%g' % (result) + else: + result = '' + es = nib[offset + digits - 1][1] + if label == '\u2103': + es = nib[offset + digits][1] # Align temp to include +/- nibble. + self.put(nib[offset][0], es, self.out_ann, + [2, [pre_label + result + label, result]]) + + def oregon_temp(self, offset): + nib = self.decoded_nibbles + if nib[offset + 3][3] != '': + temp_sign = str(int(nib[offset + 3][3], 16)) + temp_sign = '-' if temp_sign != '0' else '+' + else: + temp_sign = '?' + self.oregon_put_l2_param(offset, 3, 1, temp_sign, '\u2103') + + def oregon_baro(self, offset): + nib = self.decoded_nibbles + baro = '' + if not (nib[offset + 2][3] == '' or nib[offset + 1][3] == '' + or nib[offset][3] == ''): + baro = str(int(nib[offset + 1][3] + nib[offset][3], 16) + 856) + self.put(nib[offset][0], nib[offset + 3][1], + self.out_ann, [2, [baro + ' mb', baro]]) + + def oregon_wind_dir(self, offset): + nib = self.decoded_nibbles + if nib[offset][3] != '': + w_dir = int(int(nib[offset][3], 16) * 22.5) + w_compass = dir_table[math.floor((w_dir + 11.25) / 22.5)] + self.put(nib[offset][0], nib[offset][1], self.out_ann, + [2, [w_compass + ' (' + str(w_dir) + '\u00b0)', w_compass]]) + + def oregon_channel(self, offset): + nib = self.decoded_nibbles + channel = '' + if nib[offset][3] != '': + ch = int(nib[offset][3], 16) + if self.ver != 'v3': # May not be true for all v2.1 sensors. + if ch != 0: + bit_pos = 0 + while ((ch & 1) == 0): + bit_pos += 1 + ch = ch >> 1 + if self.ver == 'v2.1': + bit_pos += 1 + channel = str(bit_pos) + elif self.ver == 'v3': # Not sure if this applies to all v3's. + channel = str(ch) + if channel != '': + self.put(nib[offset][0], nib[offset][1], + self.out_ann, [2, ['Ch ' + channel, channel]]) + + def oregon_battery(self, offset): + nib = self.decoded_nibbles + batt = 'OK' + if nib[offset][3] != '': + if (int(nib[offset][3], 16) >> 2) & 0x1 == 1: + batt = 'Low' + self.put(nib[offset][0], nib[offset][1], + self.out_ann, [2, ['Batt ' + batt, batt]]) + + def oregon_level2(self): # v2 and v3 level 2 decoder. + nib = self.decoded_nibbles + self.sensor_id = (nib[0][3] + nib[1][3] + nib[2][3] + nib[3][3]).upper() + nl, sensor_type = sensor.get(self.sensor_id, [['Unknown'], 'Unknown']) + names = ','.join(nl) + # Allow user to try decoding an unknown sensor. + if sensor_type == 'Unknown' and self.unknown != 'Unknown': + sensor_type = self.unknown + self.put(nib[0][0], nib[3][1], self.out_ann, + [2, [names + ' - ' + sensor_type, names, nl[0]]]) + self.oregon_channel(4) + self.oregon_battery(7) + if sensor_type == 'Rain': + self.oregon_put_l2_param(8, 4, 2, '', ' in/hr') # Rain rate + self.oregon_put_l2_param(12, 6, 3, 'Total ', ' in') # Rain total + self.oregon_checksum(18) + if sensor_type == 'Rain1': + self.oregon_put_l2_param(8, 3, 1, '', ' mm/hr') # Rain rate + self.oregon_put_l2_param(11, 5, 1, 'Total ', ' mm') # Rain total + self.oregon_checksum(18) + if sensor_type == 'Temp': + self.oregon_temp(8) + self.oregon_checksum(12) + if sensor_type == 'Temp_Hum_Baro': + self.oregon_temp(8) + self.oregon_put_l2_param(12, 2, 0, 'Hum ', '%') # Hum + self.oregon_baro(15) # Baro + self.oregon_checksum(19) + if sensor_type == 'Temp_Hum_Baro1': + self.oregon_temp(8) + self.oregon_put_l2_param(12, 2, 0, 'Hum ', '%') # Hum + self.oregon_baro(14) # Baro + if sensor_type == 'Temp_Hum': + self.oregon_temp(8) + self.oregon_put_l2_param(12, 2, 0, 'Hum ', '%') # Hum + self.oregon_checksum(15) + if sensor_type == 'Temp_Hum1': + self.oregon_temp(8) + self.oregon_put_l2_param(12, 2, 0, 'Hum ', '%') # Hum + self.oregon_checksum(14) + if sensor_type == 'UV': + self.oregon_put_l2_param(8, 2, 0, '', '') # UV + if sensor_type == 'UV1': + self.oregon_put_l2_param(11, 2, 0,'' ,'') # UV + if sensor_type == 'Wind': + self.oregon_wind_dir(8) + self.oregon_put_l2_param(11, 3, 1, 'Gust ', ' m/s') # Wind gust + self.oregon_put_l2_param(14, 3, 1, 'Speed ', ' m/s') # Wind speed + self.oregon_checksum(17) + + def oregon_put_checksum(self, nibbles, checksum): + nib = self.decoded_nibbles + result = 'BAD' + if (nibbles + 1) < len(nib): + if (nib[nibbles + 1][3] != '' and nib[nibbles][3] != '' + and checksum != -1): + if self.ver != 'v1': + if checksum == (int(nib[nibbles + 1][3], 16) * 16 + + int(nib[nibbles][3], 16)): + result = 'OK' + else: + if checksum == (int(nib[nibbles][3], 16) * 16 + + int(nib[nibbles + 1][3], 16)): + result = 'OK' + rx_check = (nib[nibbles + 1][3] + nib[nibbles][3]).upper() + details = '%s Calc %s Rx %s ' % (result, hex(checksum)[2:].upper(), + rx_check) + self.put(nib[nibbles][0], nib[nibbles + 1][1], + self.out_ann, [2, ['Checksum ' + details, result]]) + + def oregon_checksum(self, nibbles): + checksum = 0 + for i in range(nibbles): # Add reversed nibbles. + nibble = self.ookstring[i * 4 : i * 4 + 4] + nibble = nibble[::-1] # Reversed from right. + if 'E' in nibble: # Abort checksum if there are errors. + checksum = -1 + break + checksum += int(nibble, 2) + if checksum > 255: + checksum -= 255 # Make it roll over at 255. + chk_ver, comment = sensor_checksum.get(self.sensor_id, + ['Unknown', 'Unknown']) + if chk_ver != 'Unknown': + self.ver = chk_ver + if self.ver == 'v2.1': + checksum -= 10 # Subtract 10 from v2 checksums. + self.oregon_put_checksum(nibbles, checksum) + + def oregon_checksum_v1(self): + nib = self.decoded_nibbles + checksum = 0 + for i in range(3): # Add the first three bytes. + if nib[2 * i][3] == '' or nib[2 * i + 1][3] == '': # Abort if blank. + checksum = -1 + break + checksum += ((int(nib[2 * i][3], 16) & 0xF) << 4 | + (int(nib[2 * i + 1][3], 16) & 0xF)) + if checksum > 255: + checksum -= 255 # Make it roll over at 255. + self.oregon_put_checksum(6, checksum) + + def decode(self, ss, es, data): + self.decoded = data + self.oregon() diff --git a/libsigrokdecode4DSL/decoders/ook_vis/__init__.py b/libsigrokdecode4DSL/decoders/ook_vis/__init__.py new file mode 100755 index 00000000..f50f9ef8 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/ook_vis/__init__.py @@ -0,0 +1,25 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Steve R +## +## 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 2 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 . +## + +''' +This decoder stacks on top of the 'ook' PD and visualizes protocol details +in various ways. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/ook_vis/pd.py b/libsigrokdecode4DSL/decoders/ook_vis/pd.py new file mode 100755 index 00000000..f985b96f --- /dev/null +++ b/libsigrokdecode4DSL/decoders/ook_vis/pd.py @@ -0,0 +1,194 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Steve R +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd +from common.srdhelper import bcd2int + +class Decoder(srd.Decoder): + api_version = 3 + id = 'ook_vis' + name = 'OOK visualisation' + longname = 'On-off keying visualisation' + desc = 'OOK visualisation in various formats.' + license = 'gplv2+' + inputs = ['ook'] + outputs = ['ook'] + tags = ['Encoding'] + annotations = ( + ('bit', 'Bit'), + ('ref', 'Reference'), + ('field', 'Field'), + ('ref_field', 'Ref field'), + ('level2', 'L2'), + ('ref_level2', 'Ref L2'), + ) + annotation_rows = ( + ('bits', 'Bits', (0,)), + ('compare', 'Compare', (1,)), + ('fields', 'Fields', (2,)), + ('ref_fields', 'Ref fields', (3,)), + ('level2', 'L2', (4,)), + ('ref_level2', 'Ref L2', (5,)), + ) + options = ( + {'id': 'displayas', 'desc': 'Display as', 'default': 'Nibble - Hex', + 'values': ('Byte - Hex', 'Byte - Hex rev', 'Byte - BCD', + 'Byte - BCD rev', 'Nibble - Hex', 'Nibble - Hex rev', 'Nibble - BCD', + 'Nibble - BCD rev')}, + {'id': 'synclen', 'desc': 'Sync length', 'default': '4', + 'values': ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10')}, + {'id': 'syncoffset', 'desc': 'Sync offset', 'default': '0', + 'values': ('-4', '-3', '-2', '-1', '0', '1', '2', '3', '4')}, + {'id': 'refsample', 'desc': 'Compare', 'default': 'off', 'values': + ('off', 'show numbers', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', + '21', '22', '23', '24', '25', '26', '27', '28', '29', '30')}, + ) + + def __init__(self): + self.reset() + + def reset(self): + self.decoded = [] # Local cache of decoded OOK. + self.ookstring = '' + self.ookcache = [] + self.trace_num = 0 + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + self.out_python = self.register(srd.OUTPUT_PYTHON) + self.displayas = self.options['displayas'] + self.sync_length = self.options['synclen'] + self.sync_offset = self.options['syncoffset'] + self.ref = self.options['refsample'] + + def putx(self, data): + self.put(self.ss, self.es, self.out_ann, data) + + def putp(self, data): + self.put(self.ss, self.es, self.out_python, data) + + def display_level2(self, bits, line): + self.decode_pos = 0 + ook = self.decoded + # Find the end of the preamble which could be 1010 or 1111. + if len(ook) > 1: + preamble_end = len(ook) + 1 + char_first = ook[0][2] + char_second = ook[1][2] + if char_first == char_second: # 1111 + preamble = '1111' + char_last = char_first + else: + preamble = '1010' + char_last = char_second + for i in range(len(ook)): + if preamble == '1111': + if ook[i][2] != char_last: + preamble_end = i + break + else: + char_last = ook[i][2] + else: + if ook[i][2] != char_last: + char_last = ook[i][2] + else: + preamble_end = i + break + + if len(ook) >= preamble_end: + preamble_end += int(self.sync_offset) - 1 + self.ss, self.es = ook[0][0], ook[preamble_end][1] + self.putx([line, ['Preamble', 'Pre', 'P']]) + self.decode_pos += preamble_end + + if len(ook) > self.decode_pos + int(self.sync_length): + self.ss = self.es + self.es = ook[self.decode_pos + int(self.sync_length)][1] + self.putx([line, ['Sync', 'Syn', 'S']]) + self.decode_pos += int(self.sync_length) + 1 + + ookstring = self.ookstring[self.decode_pos:] + rem_nibbles = len(ookstring) // bits + for i in range(rem_nibbles): # Display the rest of nibbles. + self.ss = ook[self.decode_pos][0] + self.es = ook[self.decode_pos + bits - 1][1] + self.put_field(bits, line) + + def put_field(self, numbits, line): + param = self.ookstring[self.decode_pos:self.decode_pos + numbits] + if 'rev' in self.displayas: + param = param[::-1] # Reversed from right. + if not 'E' in param: # Format if no errors. + if 'Hex' in self.displayas: + param = hex(int(param, 2))[2:] + elif 'BCD' in self.displayas: + param = bcd2int(int(param, 2)) + self.putx([line, [str(param)]]) + self.decode_pos += numbits + + def display_all(self): + ookstring = '' + self.decode_pos = 0 + ook = self.decoded + for i in range(len(ook)): + self.ookstring += ook[i][2] + bits = 4 if 'Nibble' in self.displayas else 8 + rem_nibbles = len(self.ookstring) // bits + for i in range(rem_nibbles): # Display the rest of the nibbles. + self.ss = ook[self.decode_pos][0] + self.es = ook[self.decode_pos + bits - 1][1] + self.put_field(bits, 2) + + self.display_level2(bits, 4) # Display L2 decode. + + if (self.ref != 'off' and self.ref != 'show numbers' and + len(self.ookcache) >= int(self.ref)): # Compare traces. + ref = int(self.ref) - 1 + self.display_ref(self.trace_num, ref) + if len(self.ookcache) == int(self.ref): # Backfill. + for i in range(0, ref): + self.display_ref(i, ref) + elif self.ref == 'show numbers': # Display ref numbers. + self.ss = self.ookcache[self.trace_num][0][0] + end_sig = len(self.ookcache[self.trace_num]) - 1 + self.es = self.ookcache[self.trace_num][end_sig][1] + self.putx([1, [str(self.trace_num + 1)]]) + + def display_ref(self, t_num, ref): + display_len = len(self.ookcache[ref]) + if len(self.ookcache[t_num]) < len(self.ookcache[ref]): + display_len = len(self.ookcache[t_num]) + for i in range(display_len): + self.ss = self.ookcache[t_num][i][0] + self.es = self.ookcache[t_num][i][1] + self.putx([1, [self.ookcache[ref][i][2]]]) + + def add_to_cache(self): # Cache the OOK so it can be used as a reference. + self.ookcache.append(self.decoded) + + def decode(self, ss, es, data): + self.decoded = data + self.add_to_cache() + self.display_all() + self.ookstring = '' + self.trace_num += 1 + self.ss = ss + self.es = es + self.putp(data) # Send data up the stack. diff --git a/libsigrokdecode4DSL/decoders/pan1321/__init__.py b/libsigrokdecode4DSL/decoders/pan1321/__init__.py index c14236df..428fc91f 100755 --- a/libsigrokdecode4DSL/decoders/pan1321/__init__.py +++ b/libsigrokdecode4DSL/decoders/pan1321/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/pan1321/pd.py b/libsigrokdecode4DSL/decoders/pan1321/pd.py index a8938c60..6c931147 100755 --- a/libsigrokdecode4DSL/decoders/pan1321/pd.py +++ b/libsigrokdecode4DSL/decoders/pan1321/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -25,14 +24,15 @@ RX = 0 TX = 1 class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'pan1321' name = 'PAN1321' longname = 'Panasonic PAN1321' desc = 'Bluetooth RF module with Serial Port Profile (SPP).' license = 'gplv2+' inputs = ['uart'] - outputs = ['pan1321'] + outputs = [] + tags = ['Wireless/RF'] annotations = ( ('text-verbose', 'Human-readable text (verbose)'), ('text', 'Human-readable text'), @@ -40,6 +40,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.cmd = ['', ''] self.ss_block = None diff --git a/libsigrokdecode4DSL/decoders/parallel/__init__.py b/libsigrokdecode4DSL/decoders/parallel/__init__.py index a7077bb9..100523ec 100755 --- a/libsigrokdecode4DSL/decoders/parallel/__init__.py +++ b/libsigrokdecode4DSL/decoders/parallel/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/parallel/pd.py b/libsigrokdecode4DSL/decoders/parallel/pd.py index 55a4c36a..d7544c16 100755 --- a/libsigrokdecode4DSL/decoders/parallel/pd.py +++ b/libsigrokdecode4DSL/decoders/parallel/pd.py @@ -1,7 +1,7 @@ ## ## This file is part of the libsigrokdecode project. ## -## Copyright (C) 2013 Uwe Hermann +## Copyright (C) 2013-2016 Uwe Hermann ## ## 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 @@ -14,11 +14,11 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd +from common.srdhelper import bitpack ''' OUTPUT_PYTHON format: @@ -64,8 +64,10 @@ def channel_list(num_channels): class ChannelError(Exception): pass +NUM_CHANNELS = 8 + class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'parallel' name = 'Parallel' longname = 'Parallel sync bus' @@ -73,11 +75,13 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['logic'] outputs = ['parallel'] - optional_channels = channel_list(8) + tags = ['Util'] + optional_channels = channel_list(NUM_CHANNELS) options = ( {'id': 'clock_edge', 'desc': 'Clock edge to sample on', 'default': 'rising', 'values': ('rising', 'falling')}, - {'id': 'wordsize', 'desc': 'Data wordsize', 'default': 1}, + {'id': 'wordsize', 'desc': 'Data wordsize (# bus cycles)', + 'default': 0}, {'id': 'endianness', 'desc': 'Data endianness', 'default': 'little', 'values': ('little', 'big')}, ) @@ -85,15 +89,20 @@ class Decoder(srd.Decoder): ('items', 'Items'), ('words', 'Words'), ) + annotation_rows = ( + ('items', 'Items', (0,)), + ('words', 'Words', (1,)), + ) def __init__(self): - self.oldclk = None + self.reset() + + def reset(self): self.items = [] - self.itemcount = 0 self.saved_item = None - self.samplenum = 0 - self.oldpins = None self.ss_item = self.es_item = None + self.saved_word = None + self.ss_word = self.es_word = None self.first = True def start(self): @@ -112,19 +121,20 @@ class Decoder(srd.Decoder): def putw(self, data): self.put(self.ss_word, self.es_word, self.out_ann, data) - def handle_bits(self, datapins): - # If this is the first item in a word, save its sample number. - if self.itemcount == 0: - self.ss_word = self.samplenum + def handle_bits(self, item, used_pins): - # Get the bits for this item. - item, used_pins = 0, datapins.count(b'\x01') + datapins.count(b'\x00') - for i in range(used_pins): - item |= datapins[i] << i - - self.items.append(item) - self.itemcount += 1 + # If a word was previously accumulated, then emit its annotation + # now after its end samplenumber became available. + if self.saved_word is not None: + if self.options['wordsize'] > 0: + self.es_word = self.samplenum + self.putw([1, [self.fmt_word.format(self.saved_word)]]) + self.putpw(['WORD', self.saved_word]) + self.saved_word = None + # Defer annotations for individual items until the next sample + # is taken, and the previous sample's end samplenumber has + # become available. if self.first: # Save the start sample and item for later (no output yet). self.ss_item = self.samplenum @@ -134,59 +144,70 @@ class Decoder(srd.Decoder): # Output the saved item (from the last CLK edge to the current). self.es_item = self.samplenum self.putpb(['ITEM', self.saved_item]) - self.putb([0, ['%X' % self.saved_item]]) + self.putb([0, [self.fmt_item.format(self.saved_item)]]) self.ss_item = self.samplenum self.saved_item = item - endian, ws = self.options['endianness'], self.options['wordsize'] - - # Get as many items as the configured wordsize says. - if self.itemcount < ws: + # Get as many items as the configured wordsize specifies. + if not self.items: + self.ss_word = self.samplenum + self.items.append(item) + ws = self.options['wordsize'] + if len(self.items) < ws: return - # Output annotations/python for a word (a collection of items). - word = 0 - for i in range(ws): - if endian == 'little': - word |= self.items[i] << ((ws - 1 - i) * used_pins) - elif endian == 'big': - word |= self.items[i] << (i * used_pins) + # Collect words and prepare annotation details, but defer emission + # until the end samplenumber becomes available. + endian = self.options['endianness'] + if endian == 'big': + self.items.reverse() + word = sum([self.items[i] << (i * used_pins) for i in range(ws)]) + self.saved_word = word + self.items = [] - self.es_word = self.samplenum - # self.putpw(['WORD', word]) - # self.putw([1, ['%X' % word]]) - self.ss_word = self.samplenum + def decode(self): + # Determine which (optional) channels have input data. Insist in + # a non-empty input data set. Cope with sparse connection maps. + # Store enough state to later "compress" sampled input data. + max_possible = len(self.optional_channels) + idx_channels = [ + idx if self.has_channel(idx) else None + for idx in range(max_possible) + ] + has_channels = [idx for idx in idx_channels if idx is not None] + if not has_channels: + raise ChannelError('At least one channel has to be supplied.') + max_connected = max(has_channels) - self.itemcount, self.items = 0, [] + # Determine .wait() conditions, depending on the presence of a + # clock signal. Either inspect samples on the configured edge of + # the clock, or inspect samples upon ANY edge of ANY of the pins + # which provide input data. + if self.has_channel(0): + edge = self.options['clock_edge'][0] + conds = {0: edge} + else: + conds = [{idx: 'e'} for idx in has_channels] - def find_clk_edge(self, clk, datapins): - # Ignore sample if the clock pin hasn't changed. - if clk == self.oldclk: - return - self.oldclk = clk + # Pre-determine which input data to strip off, the width of + # individual items and multiplexed words, as well as format + # strings here. This simplifies call sites which run in tight + # loops later. + idx_strip = max_connected + 1 + num_item_bits = idx_strip - 1 + num_word_items = self.options['wordsize'] + num_word_bits = num_item_bits * num_word_items + num_digits = (num_item_bits + 3) // 4 + self.fmt_item = "{{:0{}x}}".format(num_digits) + num_digits = (num_word_bits + 3) // 4 + self.fmt_word = "{{:0{}x}}".format(num_digits) - # Sample data on rising/falling clock edge (depends on config). - c = self.options['clock_edge'] - if c == 'rising' and clk == 0: # Sample on rising clock edge. - return - elif c == 'falling' and clk == 1: # Sample on falling clock edge. - return - - # Found the correct clock edge, now get the bits. - self.handle_bits(datapins) - - def decode(self, ss, es, data): - for (self.samplenum, pins) in data: - data.itercnt += 1 - # Ignore identical samples early on (for performance reasons). - if self.oldpins == pins: - continue - self.oldpins = pins - - if sum(1 for p in pins if p in (0, 1)) == 0: - raise ChannelError('At least one channel has to be supplied.') - - if pins[0] not in (0, 1): - self.handle_bits(pins[1:]) - else: - self.find_clk_edge(pins[0], pins[1:]) + # Keep processing the input stream. Assume "always zero" for + # not-connected input lines. Pass data bits (all inputs except + # clock) to the handle_bits() method. + while True: + (clk, d0, d1, d2, d3, d4, d5, d6, d7) = self.wait(conds) + pins = (clk, d0, d1, d2, d3, d4, d5, d6, d7) + bits = [0 if idx is None else pins[idx] for idx in idx_channels] + item = bitpack(bits[1:idx_strip]) + self.handle_bits(item, num_item_bits) diff --git a/libsigrokdecode4DSL/decoders/ps2/pd.py b/libsigrokdecode4DSL/decoders/ps2/pd.py index 769352af..fae51ce6 100755 --- a/libsigrokdecode4DSL/decoders/ps2/pd.py +++ b/libsigrokdecode4DSL/decoders/ps2/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -27,14 +26,15 @@ class Ann: Bit = namedtuple('Bit', 'val ss es') class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'ps2' name = 'PS/2' longname = 'PS/2' desc = 'PS/2 keyboard/mouse interface.' license = 'gplv2+' inputs = ['logic'] - outputs = ['ps2'] + outputs = [] + tags = ['PC'] channels = ( {'id': 'clk', 'name': 'Clock', 'desc': 'Clock line'}, {'id': 'data', 'name': 'Data', 'desc': 'Data line'}, @@ -54,11 +54,11 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.bits = [] - self.prev_pins = None - self.prev_clock = None self.samplenum = 0 - self.clock_was_high = False self.bitcount = 0 def start(self): @@ -115,31 +115,8 @@ class Decoder(srd.Decoder): self.bits, self.bitcount = [], 0 - def find_clk_edge(self, clock_pin, data_pin): - # Ignore sample if the clock pin hasn't changed. - if clock_pin == self.prev_clock: - return - self.prev_clock = clock_pin - - # Sample on falling clock edge. - if clock_pin == 1: - return - - # Found the correct clock edge, now get the bits. - self.handle_bits(data_pin) - - def decode(self, ss, es, data): - for (self.samplenum, pins) in data: - data.itercnt += 1 - clock_pin, data_pin = pins[0], pins[1] - - # Ignore identical samples. - if self.prev_pins == pins: - continue - self.prev_pins = pins - - if clock_pin == 0 and not self.clock_was_high: - continue - self.clock_was_high = True - - self.find_clk_edge(clock_pin, data_pin) + def decode(self): + while True: + # Sample data bits on falling clock edge. + (clock_pin, data_pin) = self.wait({0: 'f'}) + self.handle_bits(data_pin) diff --git a/libsigrokdecode4DSL/decoders/pwm/__init__.py b/libsigrokdecode4DSL/decoders/pwm/__init__.py index 096e077d..8f039766 100755 --- a/libsigrokdecode4DSL/decoders/pwm/__init__.py +++ b/libsigrokdecode4DSL/decoders/pwm/__init__.py @@ -14,12 +14,11 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' -Pulse-width modulation (a.k.a pulse-duration modulation, PDM) decoder. +Pulse-width modulation (PWM) a.k.a pulse-duration modulation (PDM) decoder. ''' from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/pwm/pd.py b/libsigrokdecode4DSL/decoders/pwm/pd.py index f3fdf093..d8626ee0 100755 --- a/libsigrokdecode4DSL/decoders/pwm/pd.py +++ b/libsigrokdecode4DSL/decoders/pwm/pd.py @@ -15,21 +15,24 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd +class SamplerateError(Exception): + pass + class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'pwm' name = 'PWM' longname = 'Pulse-width modulation' desc = 'Analog level encoded in duty cycle percentage.' license = 'gplv2+' inputs = ['logic'] - outputs = ['pwm'] + outputs = [] + tags = ['Encoding'] channels = ( {'id': 'data', 'name': 'Data', 'desc': 'Data line'}, ) @@ -50,21 +53,17 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): + self.samplerate = None self.ss_block = self.es_block = None - self.first_transition = True - self.first_samplenum = None - self.start_samplenum = None - self.end_samplenum = None - self.oldpin = None - self.num_cycles = 0 - self.average = 0 def metadata(self, key, value): if key == srd.SRD_CONF_SAMPLERATE: self.samplerate = value def start(self): - self.startedge = 0 if self.options['polarity'] == 'active-low' else 1 self.out_ann = self.register(srd.OUTPUT_ANN) self.out_binary = self.register(srd.OUTPUT_BINARY) self.out_average = \ @@ -92,60 +91,51 @@ class Decoder(srd.Decoder): self.put(self.ss_block, self.es_block, self.out_ann, [1, [period_s]]) def putb(self, data): - self.put(self.num_cycles, self.num_cycles, self.out_binary, data) + self.put(self.ss_block, self.es_block, self.out_binary, data) - def decode(self, ss, es, data): + def decode(self): + if not self.samplerate: + raise SamplerateError('Cannot decode without samplerate.') - for (self.samplenum, pins) in data: - data.itercnt += 1 - # Ignore identical samples early on (for performance reasons). - if self.oldpin == pins[0]: - continue + num_cycles = 0 + average = 0 - # Initialize self.oldpins with the first sample value. - if self.oldpin is None: - self.oldpin = pins[0] - continue + # Wait for an "active" edge (depends on config). This starts + # the first full period of the inspected signal waveform. + self.wait({0: 'f' if self.options['polarity'] == 'active-low' else 'r'}) + self.first_samplenum = self.samplenum - if self.first_transition: - # First rising edge - if self.oldpin != self.startedge: - self.first_samplenum = self.samplenum - self.start_samplenum = self.samplenum - self.first_transition = False - else: - if self.oldpin != self.startedge: - # Rising edge - # We are on a full cycle we can calculate - # the period, the duty cycle and its ratio. - period = self.samplenum - self.start_samplenum - duty = self.end_samplenum - self.start_samplenum - ratio = float(duty / period) + # Keep getting samples for the period's middle and terminal edges. + # At the same time that last sample starts the next period. + while True: - # This interval starts at this edge. - self.ss_block = self.start_samplenum - # Store the new rising edge position and the ending - # edge interval. - self.start_samplenum = self.es_block = self.samplenum + # Get the next two edges. Setup some variables that get + # referenced in the calculation and in put() routines. + start_samplenum = self.samplenum + self.wait({0: 'e'}) + end_samplenum = self.samplenum + self.wait({0: 'e'}) + self.ss_block = start_samplenum + self.es_block = self.samplenum - # Report the duty cycle in percent. - percent = float(ratio * 100) - self.putx([0, ['%f%%' % percent]]) + # Calculate the period, the duty cycle, and its ratio. + period = self.samplenum - start_samplenum + duty = end_samplenum - start_samplenum + ratio = float(duty / period) - # Report the duty cycle in the binary output. - #self.putb([0, bytes([int(ratio * 256)])]) + # Report the duty cycle in percent. + percent = float(ratio * 100) + self.putx([0, ['%f%%' % percent]]) - # Report the period in units of time. - period_t = float(period / self.samplerate) - self.putp(period_t) + # Report the duty cycle in the binary output. + self.putb([0, bytes([int(ratio * 256)])]) - # Update and report the new duty cycle average. - self.num_cycles += 1 - self.average += percent - self.put(self.first_samplenum, self.es_block, self.out_average, - float(self.average / self.num_cycles)) - else: - # Falling edge - self.end_samplenum = self.ss_block = self.samplenum + # Report the period in units of time. + period_t = float(period / self.samplerate) + self.putp(period_t) - self.oldpin = pins[0] + # Update and report the new duty cycle average. + num_cycles += 1 + average += percent + self.put(self.first_samplenum, self.es_block, self.out_average, + float(average / num_cycles)) diff --git a/libsigrokdecode4DSL/decoders/qi/__init__.py b/libsigrokdecode4DSL/decoders/qi/__init__.py index 35ffe5ba..0d49d17b 100755 --- a/libsigrokdecode4DSL/decoders/qi/__init__.py +++ b/libsigrokdecode4DSL/decoders/qi/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/qi/pd.py b/libsigrokdecode4DSL/decoders/qi/pd.py index 6692ef35..b750d9ce 100755 --- a/libsigrokdecode4DSL/decoders/qi/pd.py +++ b/libsigrokdecode4DSL/decoders/qi/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -46,14 +45,15 @@ def bits_to_uint(bits): return reduce(lambda i, v: (i >> 1) | (v << (len(bits) - 1)), bits, 0) class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'qi' name = 'Qi' longname = 'Qi charger protocol' - desc = 'Protocol used by Qi receiver' + desc = 'Protocol used by Qi receiver.' license = 'gplv2+' inputs = ['logic'] - outputs = ['qi'] + outputs = [] + tags = ['Embedded/industrial', 'Wireless/RF'] channels = ( {'id': 'qi', 'name': 'Qi', 'desc': 'Demodulated Qi data line'}, ) @@ -74,6 +74,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.reset_variables() @@ -147,7 +150,7 @@ class Decoder(srd.Decoder): 'C: PC = %d MP = %d P = %d C = %d WS = %d WO = %d' % (powerclass, maxpower, prop, count, winsize, winoff), 'Configuration', 'C']) - elif self.packet[0] == 0x71: # Identification + elif self.packet[0] == 0x71: # Identification version = '%d.%d' % ((self.packet[1] & 0xf0) >> 4, self.packet[1] & 0x0f) mancode = '%02x%02x' % (self.packet[2], self.packet[3]) devid = '%02x%02x%02x%02x' % (self.packet[4] & ~0x80, @@ -229,17 +232,13 @@ class Decoder(srd.Decoder): self.bits.clear() self.bitsi.clear() - def next_sample(self, s): - if s == self.prev: - self.counter += 1 - else: - self.handle_transition(self.counter, s == 0) - self.prev = s - self.counter = 1 - - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - for (self.samplenum, (qi,)) in data: - data.itercnt += 1 - self.next_sample(qi) + + (qi,) = self.wait() + self.handle_transition(self.samplenum, qi == 0) + while True: + prev = self.samplenum + (qi,) = self.wait({0: 'e'}) + self.handle_transition(self.samplenum - prev, qi == 0) diff --git a/libsigrokdecode4DSL/decoders/rc_encode/__init__.py b/libsigrokdecode4DSL/decoders/rc_encode/__init__.py new file mode 100755 index 00000000..db78dc1e --- /dev/null +++ b/libsigrokdecode4DSL/decoders/rc_encode/__init__.py @@ -0,0 +1,36 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Steve R +## +## 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 2 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 . +## + +''' +This PD decodes the remote control protocol which is frequently used +within key fobs and power socket remotes. + +They contain encoding chips like the PT2262 which converts the button +pressed and address settings into a series of pulses which is then +transmitted over whatever frequency and modulation that the designer +chooses. These devices operate at a number of frequencies including 433MHz. + +This PD should also decode the HX2262 and SC5262 which are equivalents. + +The decoder also contains some additional decoding for a Maplin L95AR +remote control and will turn the received signal into which button was +pressed and what the address code DIP switches are set to. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/rc_encode/pd.py b/libsigrokdecode4DSL/decoders/rc_encode/pd.py new file mode 100755 index 00000000..daeca092 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/rc_encode/pd.py @@ -0,0 +1,167 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Steve R +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd + +bitvals = ('0', '1', 'f', 'U') + +def decode_bit(edges): + # Datasheet says long pulse is 3 times short pulse. + lmin = 2 # long min multiplier + lmax = 5 # long max multiplier + eqmin = 0.5 # equal min multiplier + eqmax = 1.5 # equal max multiplier + if ( # 0 -___-___ + (edges[1] >= edges[0] * lmin and edges[1] <= edges[0] * lmax) and + (edges[2] >= edges[0] * eqmin and edges[2] <= edges[0] * eqmax) and + (edges[3] >= edges[0] * lmin and edges[3] <= edges[0] * lmax)): + return '0' + elif ( # 1 ---_---_ + (edges[0] >= edges[1] * lmin and edges[0] <= edges[1] * lmax) and + (edges[0] >= edges[2] * eqmin and edges[0] <= edges[2] * eqmax) and + (edges[0] >= edges[3] * lmin and edges[0] <= edges[3] * lmax)): + return '1' + elif ( # float ---_-___ + (edges[1] >= edges[0] * lmin and edges[1] <= edges[0] * lmax) and + (edges[2] >= edges[0] * lmin and edges[2] <= edges[0]* lmax) and + (edges[3] >= edges[0] * eqmin and edges[3] <= edges[0] * eqmax)): + return 'f' + else: + return 'U' + +def pinlabels(bit_count): + if bit_count <= 6: + return 'A%i' % (bit_count - 1) + else: + return 'A%i/D%i' % (bit_count - 1, 12 - bit_count) + +def decode_model(model, bits): + if model == 'maplin_l95ar': + address = 'Addr' # Address pins A0 to A5 + for i in range(0, 6): + address += ' %i:' % (i + 1) + ('on' if bits[i][0] == '0' else 'off') + button = 'Button' + # Button pins A6/D5 to A11/D0 + if bits[6][0] == '0' and bits[11][0] == '0': + button += ' A ON/OFF' + elif bits[7][0] == '0' and bits[11][0] == '0': + button += ' B ON/OFF' + elif bits[9][0] == '0' and bits[11][0] == '0': + button += ' C ON/OFF' + elif bits[8][0] == '0' and bits[11][0] == '0': + button += ' D ON/OFF' + else: + button += ' Unknown' + return ['%s' % address, bits[0][1], bits[5][2], \ + '%s' % button, bits[6][1], bits[11][2]] + +class Decoder(srd.Decoder): + api_version = 3 + id = 'rc_encode' + name = 'RC encode' + longname = 'Remote control encoder' + desc = 'PT2262/HX2262/SC5262 remote control encoder protocol.' + license = 'gplv2+' + inputs = ['logic'] + outputs = [] + tags = ['IC', 'IR'] + channels = ( + {'id': 'data', 'name': 'Data', 'desc': 'Data line'}, + ) + annotations = ( + ('bit-0', 'Bit 0'), + ('bit-1', 'Bit 1'), + ('bit-f', 'Bit f'), + ('bit-U', 'Bit U'), + ('bit-sync', 'Bit sync'), + ('pin', 'Pin'), + ('code-word-addr', 'Code word address'), + ('code-word-data', 'Code word data'), + ) + annotation_rows = ( + ('bits', 'Bits', (0, 1, 2, 3, 4)), + ('pins', 'Pins', (5,)), + ('code-words', 'Code words', (6, 7)), + ) + options = ( + {'id': 'remote', 'desc': 'Remote', 'default': 'none', + 'values': ('none', 'maplin_l95ar')}, + ) + + def __init__(self): + self.reset() + + def reset(self): + self.samplenumber_last = None + self.pulses = [] + self.bits = [] + self.labels = [] + self.bit_count = 0 + self.ss = None + self.es = None + self.state = 'IDLE' + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + self.model = self.options['remote'] + + def putx(self, data): + self.put(self.ss, self.es, self.out_ann, data) + + def decode(self): + while True: + self.wait({0: 'e'}) + self.state = 'DECODING' + + if not self.samplenumber_last: # Set counters to start of signal. + self.samplenumber_last = self.samplenum + self.ss = self.samplenum + continue + + if self.bit_count < 12: # Decode A0 to A11. + self.bit_count += 1 + for i in range(0, 4): # Get four pulses for each bit. + if i > 0: + self.wait({0: 'e'}) # Get next 3 edges. + samples = self.samplenum - self.samplenumber_last + self.pulses.append(samples) # Save the pulse width. + self.samplenumber_last = self.samplenum + self.es = self.samplenum + self.bits.append([decode_bit(self.pulses), self.ss, + self.es]) # Save states and times. + idx = bitvals.index(decode_bit(self.pulses)) + self.putx([idx, [decode_bit(self.pulses)]]) # Write decoded bit. + self.putx([5, [pinlabels(self.bit_count)]]) # Write pin labels. + self.pulses = [] + self.ss = self.samplenum + else: + if self.model != 'none': + self.labels = decode_model(self.model, self.bits) + self.put(self.labels[1], self.labels[2], self.out_ann, + [6, [self.labels[0]]]) # Write model decode. + self.put(self.labels[4], self.labels[5], self.out_ann, + [7, [self.labels[3]]]) # Write model decode. + samples = self.samplenum - self.samplenumber_last + self.wait({'skip': 8 * samples}) # Wait for end of sync bit. + self.es = self.samplenum + self.putx([4, ['Sync']]) # Write sync label. + self.reset() # Reset and wait for next set of pulses. + self.state = 'DECODE_TIMEOUT' + if not self.state == 'DECODE_TIMEOUT': + self.samplenumber_last = self.samplenum diff --git a/libsigrokdecode4DSL/decoders/rfm12/__init__.py b/libsigrokdecode4DSL/decoders/rfm12/__init__.py index 725d4438..2fc6de7b 100755 --- a/libsigrokdecode4DSL/decoders/rfm12/__init__.py +++ b/libsigrokdecode4DSL/decoders/rfm12/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/rfm12/pd.py b/libsigrokdecode4DSL/decoders/rfm12/pd.py index 46e5b07b..d3df13a9 100755 --- a/libsigrokdecode4DSL/decoders/rfm12/pd.py +++ b/libsigrokdecode4DSL/decoders/rfm12/pd.py @@ -14,21 +14,21 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'rfm12' name = 'RFM12' - longname = 'RFM12 control protocol' + longname = 'HopeRF RFM12' desc = 'HopeRF RFM12 wireless transceiver control protocol.' license = 'gplv2+' inputs = ['spi'] - outputs = ['rfm12'] + outputs = [] + tags = ['Wireless/RF'] annotations = ( ('cmd', 'Command'), ('params', 'Command parameters'), @@ -44,6 +44,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.mosi_bytes, self.miso_bytes = [], [] self.mosi_bits, self.miso_bits = [], [] self.row_pos = [0, 0, 0] diff --git a/libsigrokdecode4DSL/decoders/rgb_led_spi/__init__.py b/libsigrokdecode4DSL/decoders/rgb_led_spi/__init__.py index c0e0ea19..1cf62eb8 100755 --- a/libsigrokdecode4DSL/decoders/rgb_led_spi/__init__.py +++ b/libsigrokdecode4DSL/decoders/rgb_led_spi/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/rgb_led_spi/pd.py b/libsigrokdecode4DSL/decoders/rgb_led_spi/pd.py index 9a465ad0..ee94c6bf 100755 --- a/libsigrokdecode4DSL/decoders/rgb_led_spi/pd.py +++ b/libsigrokdecode4DSL/decoders/rgb_led_spi/pd.py @@ -14,26 +14,29 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'rgb_led_spi' name = 'RGB LED (SPI)' longname = 'RGB LED string decoder (SPI)' desc = 'RGB LED string protocol (RGB values clocked over SPI).' license = 'gplv2+' inputs = ['spi'] - outputs = ['rgb_led_spi'] + outputs = [] + tags = ['Display'] annotations = ( ('rgb', 'RGB values'), ) def __init__(self): + self.reset() + + def reset(self): self.ss_cmd, self.es_cmd = 0, 0 self.mosi_bytes = [] diff --git a/libsigrokdecode4DSL/decoders/rgb_led_ws281x/__init__.py b/libsigrokdecode4DSL/decoders/rgb_led_ws281x/__init__.py index 63135a4d..20de109f 100755 --- a/libsigrokdecode4DSL/decoders/rgb_led_ws281x/__init__.py +++ b/libsigrokdecode4DSL/decoders/rgb_led_ws281x/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/rgb_led_ws281x/pd.py b/libsigrokdecode4DSL/decoders/rgb_led_ws281x/pd.py index cf0d3700..0d7e0f22 100755 --- a/libsigrokdecode4DSL/decoders/rgb_led_ws281x/pd.py +++ b/libsigrokdecode4DSL/decoders/rgb_led_ws281x/pd.py @@ -2,6 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2016 Vladimir Ermakov +## Copyright (C) 2019 DreamSourceLab ## ## 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 @@ -14,8 +15,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -25,14 +25,15 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'rgb_led_ws281x' name = 'RGB LED (WS281x)' longname = 'RGB LED string decoder (WS281x)' desc = 'RGB LED string protocol (WS281x).' license = 'gplv3+' inputs = ['logic'] - outputs = ['rgb_led_ws281x'] + outputs = [] + tags = ['Display', 'IC'] channels = ( {'id': 'din', 'name': 'DIN', 'desc': 'DIN data line'}, ) @@ -47,13 +48,15 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): + self.state = 'FIND RESET' self.samplerate = None - self.oldpin = None - self.packet_ss = None + self.ss_packet = None self.ss = None self.es = None self.bits = [] - self.inreset = False def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) @@ -66,64 +69,51 @@ class Decoder(srd.Decoder): if len(self.bits) == 24: grb = reduce(lambda a, b: (a << 1) | b, self.bits) rgb = (grb & 0xff0000) >> 8 | (grb & 0x00ff00) << 8 | (grb & 0x0000ff) - self.put(self.packet_ss, samplenum, self.out_ann, + self.put(self.ss_packet, samplenum, self.out_ann, [2, ['#%06x' % rgb]]) self.bits = [] - self.packet_ss = None + self.ss_packet = samplenum - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - for (samplenum, (pin, )) in data: - data.itercnt += 1 - if self.oldpin is None: - self.oldpin = pin - continue - - # Check RESET condition (manufacturer recommends 50 usec minimal, - # but real minimum is ~10 usec). - if not self.inreset and not pin and self.es is not None and \ - (samplenum - self.es) / self.samplerate > 50e-6: - - # Decode last bit value. - tH = (self.es - self.ss) / self.samplerate - bit_ = True if tH >= 625e-9 else False - - self.bits.append(bit_) - self.handle_bits(self.es) - - self.put(self.ss, self.es, self.out_ann, [0, ['%d' % bit_]]) - self.put(self.es, samplenum, self.out_ann, - [1, ['RESET', 'RST', 'R']]) - - self.inreset = True + while True: + if self.state == 'FIND RESET': + self.wait({0: 'f'}) + self.ss = self.samplenum + self.wait({0: 'r'}) + self.es = self.samplenum + if ((self.es - self.ss) / self.samplerate > 50e-6): + self.state = 'RESET' + elif self.state == 'RESET': + self.put(self.ss, self.es, self.out_ann, [1, ['RESET', 'RST', 'R']]) self.bits = [] - self.packet_ss = None - self.ss = None + self.ss = self.samplenum + self.ss_packet = self.samplenum + self.wait({0: 'f'}) + self.state = 'BIT FALLING' + elif self.state == 'BIT FALLING': + self.es = self.samplenum + self.wait({0: 'r'}) + if ((self.es - self.ss) / self.samplerate > 50e-6): + self.ss = self.es + self.es = self.samplenum + self.state = 'RESET' + else: + self.state = 'BIT RISING' + elif self.state == 'BIT RISING': + period = self.samplenum - self.ss + duty = self.es - self.ss + # Ideal duty for T0H: 33%, T1H: 66%. + bit_ = (duty / period) > 0.5 + + self.put(self.ss, self.samplenum, self.out_ann, + [0, ['%d' % bit_]]) + + self.bits.append(bit_) + self.handle_bits(self.samplenum) - if not self.oldpin and pin: - # Rising edge. - if self.ss and self.es: - period = samplenum - self.ss - duty = self.es - self.ss - # Ideal duty for T0H: 33%, T1H: 66%. - bit_ = (duty / period) > 0.5 - - self.put(self.ss, samplenum, self.out_ann, - [0, ['%d' % bit_]]) - - self.bits.append(bit_) - self.handle_bits(samplenum) - - if self.packet_ss is None: - self.packet_ss = samplenum - - self.ss = samplenum - - elif self.oldpin and not pin: - # Falling edge. - self.inreset = False - self.es = samplenum - - self.oldpin = pin + self.ss = self.samplenum + self.wait({0: 'f'}) + self.state = 'BIT FALLING' diff --git a/libsigrokdecode4DSL/decoders/rtc8564/__init__.py b/libsigrokdecode4DSL/decoders/rtc8564/__init__.py index e2776a6a..f17e7515 100755 --- a/libsigrokdecode4DSL/decoders/rtc8564/__init__.py +++ b/libsigrokdecode4DSL/decoders/rtc8564/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/rtc8564/pd.py b/libsigrokdecode4DSL/decoders/rtc8564/pd.py index 24a68fbe..b57fae64 100755 --- a/libsigrokdecode4DSL/decoders/rtc8564/pd.py +++ b/libsigrokdecode4DSL/decoders/rtc8564/pd.py @@ -14,15 +14,11 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd - -# Return the specified BCD number (max. 8 bits) as integer. -def bcd2int(b): - return (b & 0x0f) + ((b >> 4) * 10) +from common.srdhelper import bcd2int def reg_list(): l = [] @@ -32,14 +28,15 @@ def reg_list(): return tuple(l) class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'rtc8564' name = 'RTC-8564' longname = 'Epson RTC-8564 JE/NB' desc = 'Realtime clock module protocol.' license = 'gplv2+' inputs = ['i2c'] - outputs = ['rtc8564'] + outputs = [] + tags = ['Clock/timing'] annotations = reg_list() + ( ('read', 'Read date/time'), ('write', 'Write date/time'), @@ -55,7 +52,10 @@ class Decoder(srd.Decoder): ('date-time', 'Date/time', (9, 10)), ) - def __init__(self, **kwargs): + def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.hours = -1 self.minutes = -1 diff --git a/libsigrokdecode4DSL/decoders/sda2506/__init__.py b/libsigrokdecode4DSL/decoders/sda2506/__init__.py new file mode 100755 index 00000000..bf555109 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/sda2506/__init__.py @@ -0,0 +1,24 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Max Weller +## +## 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 2 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 . +## + +''' +Decoder for Siemens EEPROM SDA 2506-5. +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/sda2506/pd.py b/libsigrokdecode4DSL/decoders/sda2506/pd.py new file mode 100755 index 00000000..813bff6a --- /dev/null +++ b/libsigrokdecode4DSL/decoders/sda2506/pd.py @@ -0,0 +1,144 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Max Weller +## Copyright (C) 2019 DreamSourceLab +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import re +import sigrokdecode as srd + +ann_cmdbit, ann_databit, ann_cmd, ann_data, ann_warning = range(5) + +class Decoder(srd.Decoder): + api_version = 3 + id = 'sda2506' + name = 'SDA2506' + longname = 'Siemens SDA 2506-5' + desc = 'Serial nonvolatile 1-Kbit EEPROM.' + license = 'gplv2+' + inputs = ['logic'] + outputs = [] + tags = ['IC', 'Memory'] + channels = ( + {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'}, + {'id': 'd', 'name': 'DATA', 'desc': 'Data'}, + {'id': 'ce', 'name': 'CE#', 'desc': 'Chip-enable'}, + ) + annotations = ( + ('cmdbit', 'Command bit'), + ('databit', 'Data bit'), + ('cmd', 'Command'), + ('data', 'Data byte'), + ('warnings', 'Human-readable warnings'), + ) + annotation_rows = ( + ('bits', 'Bits', (ann_cmdbit, ann_databit)), + ('commands', 'Commands', (ann_cmd,)), + ('data', 'Data', (ann_data,)), + ('warnings', 'Warnings', (ann_warning,)), + ) + + def __init__(self): + self.samplerate = None + self.reset() + + def reset(self): + self.cmdbits = [] + self.databits = [] + + def metadata(self, key, value): + if key == srd.SRD_CONF_SAMPLERATE: + self.samplerate = value + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + + def putbit(self, ss, es, typ, value): + self.put(ss, es, self.out_ann, [typ, ['%s' % (value)]]) + + def putdata(self, ss, es): + value = 0 + for i in range(8): + value = (value << 1) | self.databits[i] + self.put(ss, es, self.out_ann, [ann_data, ['%02X' % (value)]]) + + def decode_bits(self, offset, width): + out = 0 + for i in range(width): + out = (out << 1) | self.cmdbits[offset + i][0] + return (out, self.cmdbits[offset + width - 1][1], self.cmdbits[offset][2]) + + def decode_field(self, name, offset, width): + val, ss, es = self.decode_bits(offset, width) + self.put(ss, es, self.out_ann, [ann_data, ['%s: %02X' % (name, val)]]) + return val + + def decode(self): + while True: + # Wait for CLK edge or CE edge. + (clk, d, ce) = self.wait([{0: 'e'}, {2: 'e'}]) + + if (self.matched & (0b1 << 0)) and ce == 1 and clk == 1: + # Rising clk edge and command mode. + bitstart = self.samplenum + self.wait({0: 'f'}) + self.cmdbits = [(d, bitstart, self.samplenum)] + self.cmdbits + if len(self.cmdbits) > 24: + self.cmdbits = self.cmdbits[0:24] + self.putbit(bitstart, self.samplenum, ann_cmdbit, d) + elif (self.matched & (0b1 << 0)) and ce == 0 and clk == 0: + # Falling clk edge and data mode. + bitstart = self.samplenum + (clk, d, ce) = self.wait([{'skip': int(2.5 * (1e6 / self.samplerate))}, {0: 'r'}, {2: 'e'}]) # Wait 25 us for data ready. + if (self.matched & (0b1 << 2)) and not (self.matched & 0b011): + self.wait([{0: 'r'}, {2: 'e'}]) + if len(self.databits) == 0: + self.datastart = bitstart + self.databits = [d] + self.databits + self.putbit(bitstart, self.samplenum, ann_databit, d) + if len(self.databits) == 8: + self.putdata(self.datastart, self.samplenum) + self.databits = [] + elif (self.matched & (0b1 << 1)) and ce == 0: + # Chip enable edge. + try: + self.decode_field('addr', 1, 7) + self.decode_field('CB', 0, 1) + if self.cmdbits[0][0] == 0: + # Beginning read command. + self.decode_field('read', 1, 7) + self.put(self.cmdbits[7][1], self.samplenum, + self.out_ann, [ann_cmd, ['read' ]]) + elif d == 0: + # Beginning write command. + self.decode_field('data', 8, 8) + addr, ss, es = self.decode_bits(1, 7) + data, ss, es = self.decode_bits(8, 8) + cmdstart = self.samplenum + self.wait({2: 'r'}) + self.put(cmdstart, self.samplenum, self.out_ann, + [ann_cmd, ['Write to %02X: %02X' % (addr, data)]]) + else: + # Beginning erase command. + val, ss, es = self.decode_bits(1, 7) + cmdstart = self.samplenum + self.wait({2: 'r'}) + self.put(cmdstart, self.samplenum, self.out_ann, + [ann_cmd, ['Erase: %02X' % (val)]]) + self.databits = [] + except Exception as ex: + self.reset() diff --git a/libsigrokdecode4DSL/decoders/sdcard_sd/__init__.py b/libsigrokdecode4DSL/decoders/sdcard_sd/__init__.py index a05f21aa..7c334222 100755 --- a/libsigrokdecode4DSL/decoders/sdcard_sd/__init__.py +++ b/libsigrokdecode4DSL/decoders/sdcard_sd/__init__.py @@ -1,5 +1,5 @@ ## -## This file is part of the sigrok project. +## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2015 Uwe Hermann ## @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/sdcard_sd/pd.py b/libsigrokdecode4DSL/decoders/sdcard_sd/pd.py index 27c157f4..66fa502f 100755 --- a/libsigrokdecode4DSL/decoders/sdcard_sd/pd.py +++ b/libsigrokdecode4DSL/decoders/sdcard_sd/pd.py @@ -1,5 +1,5 @@ ## -## This file is part of the sigrok project. +## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2015 Uwe Hermann ## @@ -14,22 +14,22 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd -from .lists import * +from common.sdcard import (cmd_names, acmd_names, accepted_voltages, card_status, sd_status) class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'sdcard_sd' name = 'SD card (SD mode)' longname = 'Secure Digital card (SD mode)' desc = 'Secure Digital card (SD mode) low-level protocol.' license = 'gplv2+' inputs = ['logic'] - outputs = ['sdcard_sd'] + outputs = [] + tags = ['Memory'] channels = ( {'id': 'cmd', 'name': 'CMD', 'desc': 'Command'}, {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'}, @@ -61,13 +61,15 @@ class Decoder(srd.Decoder): ('cmd', 'Commands', tuple(range(128))), ) - def __init__(self, **kwargs): + def __init__(self): + self.reset() + + def reset(self): self.state = 'GET COMMAND TOKEN' self.token = [] - self.oldpins = None - self.oldclk = 0 self.is_acmd = False # Indicates CMD vs. ACMD self.cmd = None + self.last_cmd = None self.arg = None def start(self): @@ -79,9 +81,6 @@ class Decoder(srd.Decoder): def putt(self, data): self.put(self.token[0][0], self.token[47][1], self.out_ann, data) - def putt2(self, data): - self.put(self.token[47][0], self.token[0][1], self.out_ann, data) - def putf(self, s, e, data): self.put(self.token[s][0], self.token[e][1], self.out_ann, data) @@ -90,18 +89,12 @@ class Decoder(srd.Decoder): self.out_ann, data) def putc(self, cmd, desc): + self.last_cmd = cmd self.putt([cmd, ['%s: %s' % (self.cmd_str, desc), self.cmd_str, self.cmd_str.split(' ')[0]]]) - def putr(self, cmd, desc): - self.putt([cmd, ['Reply: %s' % desc]]) - - def putr2(self, cmd, desc): - self.putt2([cmd, ['Reply: %s' % desc]]) - - def reset(self): - self.cmd, self.arg = None, None - self.token, self.state = [], 'GET COMMAND TOKEN' + def putr(self, desc): + self.putt([self.last_cmd, ['Reply: %s' % desc]]) def cmd_name(self, cmd): c = acmd_names if self.is_acmd else cmd_names @@ -138,7 +131,8 @@ class Decoder(srd.Decoder): 'CMD%d' % self.cmd, 'Cmd', 'C']]) # CMD[39:08]: Argument - self.putf(8, 39, [132, ['Argument', 'Arg', 'A']]) + self.arg = int('0b' + ''.join([str(s[i][2]) for i in range(8, 40)]), 2) + self.putf(8, 39, [132, ['Argument: 0x%08x' % self.arg, 'Arg', 'A']]) # CMD[07:01]: CRC7 self.crc = int('0b' + ''.join([str(s[i][2]) for i in range(40, 47)]), 2) @@ -206,7 +200,7 @@ class Decoder(srd.Decoder): self.puta(12, 31, [136, ['Reserved', 'Res', 'R']]) self.puta(8, 11, [136, ['Supply voltage', 'Voltage', 'VHS', 'V']]) self.puta(0, 7, [136, ['Check pattern', 'Check pat', 'Check', 'C']]) - self.putc(0, 'Send interface condition to card') + self.putc(8, 'Send interface condition to card') self.token, self.state = [], 'GET RESPONSE R7' # TODO: Handle case when card doesn't reply with R7 (no reply at all). @@ -233,9 +227,8 @@ class Decoder(srd.Decoder): def handle_cmd16(self): # CMD16 (SET_BLOCKLEN) -> R1 - self.blocklen = self.arg self.puta(0, 31, [136, ['Block length', 'Blocklen', 'BL', 'B']]) - self.putc(16, 'Set the block length to %d bytes' % self.blocklen) + self.putc(16, 'Set the block length to %d bytes' % self.arg) self.token, self.state = [], 'GET RESPONSE R1' def handle_cmd55(self): @@ -297,7 +290,7 @@ class Decoder(srd.Decoder): if not self.get_token_bits(cmd, 48): return self.handle_common_token_fields() - self.putr(55, 'R1') + self.putr('R1') self.puta(0, 31, [136, ['Card status', 'Status', 'S']]) for i in range(32): self.putbit(8 + i, [card_status[31 - i]]) @@ -309,7 +302,7 @@ class Decoder(srd.Decoder): return self.handle_common_token_fields() self.puta(0, 31, [136, ['Card status', 'Status', 'S']]) - self.putr(55, 'R1b') + self.putr('R1b') self.token, self.state = [], 'GET COMMAND TOKEN' def handle_response_r2(self, cmd): @@ -344,7 +337,7 @@ class Decoder(srd.Decoder): # - Bits[00:00]: End bit (always 1) if not self.get_token_bits(cmd, 48): return - self.putr(55, 'R3') + self.putr('R3') # Annotations for each individual bit. for bit in range(len(self.token)): self.putf(bit, bit, [128, ['%d' % self.token[bit][2]]]) @@ -372,7 +365,7 @@ class Decoder(srd.Decoder): self.handle_common_token_fields() self.puta(0, 15, [136, ['Card status bits', 'Status', 'S']]) self.puta(16, 31, [136, ['Relative card address', 'RCA', 'R']]) - self.putr(55, 'R6') + self.putr('R6') self.token, self.state = [], 'GET COMMAND TOKEN' def handle_response_r7(self, cmd): @@ -389,7 +382,7 @@ class Decoder(srd.Decoder): return self.handle_common_token_fields() - self.putr(55, 'R7') + self.putr('R7') # Arg[31:12]: Reserved bits (all-zero) self.puta(12, 31, [136, ['Reserved', 'Res', 'R']]) @@ -404,19 +397,10 @@ class Decoder(srd.Decoder): self.token, self.state = [], 'GET COMMAND TOKEN' - def decode(self, ss, es, data): - for (self.samplenum, pins) in data: - data.itercnt += 1 - # Ignore identical samples early on (for performance reasons). - if self.oldpins == pins: - continue - self.oldpins, (cmd, clk, dat0, dat1, dat2, dat3) = pins, pins - + def decode(self): + while True: # Wait for a rising CLK edge. - if not (self.oldclk == 0 and clk == 1): - self.oldclk = clk - continue - self.oldclk = clk + (cmd, clk, dat0, dat1, dat2, dat3) = self.wait({1: 'r'}) # State machine. if self.state == 'GET COMMAND TOKEN': diff --git a/libsigrokdecode4DSL/decoders/sdcard_spi/__init__.py b/libsigrokdecode4DSL/decoders/sdcard_spi/__init__.py index 7ce66bf9..a0945162 100755 --- a/libsigrokdecode4DSL/decoders/sdcard_spi/__init__.py +++ b/libsigrokdecode4DSL/decoders/sdcard_spi/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/sdcard_spi/pd.py b/libsigrokdecode4DSL/decoders/sdcard_spi/pd.py index bc761ee8..962438f1 100755 --- a/libsigrokdecode4DSL/decoders/sdcard_spi/pd.py +++ b/libsigrokdecode4DSL/decoders/sdcard_spi/pd.py @@ -14,72 +14,22 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd - -# Normal commands (CMD) -cmd_names = { - 0: 'GO_IDLE_STATE', - 1: 'SEND_OP_COND', - 6: 'SWITCH_FUNC', - 8: 'SEND_IF_COND', - 9: 'SEND_CSD', - 10: 'SEND_CID', - 12: 'STOP_TRANSMISSION', - 13: 'SEND_STATUS', - 16: 'SET_BLOCKLEN', - 17: 'READ_SINGLE_BLOCK', - 18: 'READ_MULTIPLE_BLOCK', - 24: 'WRITE_BLOCK', - 25: 'WRITE_MULTIPLE_BLOCK', - 27: 'PROGRAM_CSD', - 28: 'SET_WRITE_PROT', - 29: 'CLR_WRITE_PROT', - 30: 'SEND_WRITE_PROT', - 32: 'ERASE_WR_BLK_START_ADDR', - 33: 'ERASE_WR_BLK_END_ADDR', - 38: 'ERASE', - 42: 'LOCK_UNLOCK', - 55: 'APP_CMD', - 56: 'GEN_CMD', - 58: 'READ_OCR', - 59: 'CRC_ON_OFF', - # CMD60-63: Reserved for manufacturer -} - -# Application-specific commands (ACMD) -acmd_names = { - 13: 'SD_STATUS', - 18: 'Reserved for SD security applications', - 22: 'SEND_NUM_WR_BLOCKS', - 23: 'SET_WR_BLK_ERASE_COUNT', - 25: 'Reserved for SD security applications', - 26: 'Reserved for SD security applications', - 38: 'Reserved for SD security applications', - 41: 'SD_SEND_OP_COND', - 42: 'SET_CLR_CARD_DETECT', - 43: 'Reserved for SD security applications', - 44: 'Reserved for SD security applications', - 45: 'Reserved for SD security applications', - 46: 'Reserved for SD security applications', - 47: 'Reserved for SD security applications', - 48: 'Reserved for SD security applications', - 49: 'Reserved for SD security applications', - 51: 'SEND_SCR', -} +from common.sdcard import (cmd_names, acmd_names) class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'sdcard_spi' name = 'SD card (SPI mode)' longname = 'Secure Digital card (SPI mode)' desc = 'Secure Digital card (SPI mode) low-level protocol.' license = 'gplv2+' inputs = ['spi'] - outputs = ['sdcard_spi'] + outputs = [] + tags = ['Memory'] annotations = \ tuple(('cmd%d' % i, 'CMD%d' % i) for i in range(64)) + \ tuple(('acmd%d' % i, 'ACMD%d' % i) for i in range(64)) + ( \ @@ -92,11 +42,14 @@ class Decoder(srd.Decoder): ('bit-warnings', 'Bit warnings'), ) annotation_rows = ( - ('bits', 'Bits', (134, 135)), - ('cmd-reply', 'Commands/replies', tuple(range(134))), + ('bits', 'Bits', (133, 134)), + ('cmd-reply', 'Commands/replies', tuple(range(133))), ) - def __init__(self, **kwargs): + def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.ss, self.es = 0, 0 self.ss_bit, self.es_bit = 0, 0 @@ -107,6 +60,8 @@ class Decoder(srd.Decoder): self.blocklen = 0 self.read_buf = [] self.cmd_str = '' + self.is_cmd24 = False + self.cmd24_start_token_found = False def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) @@ -122,7 +77,12 @@ class Decoder(srd.Decoder): def cmd_name(self, cmd): c = acmd_names if self.is_acmd else cmd_names - return c.get(cmd, 'Unknown') + s = c.get(cmd, 'Unknown') + # SD mode names for CMD32/33: ERASE_WR_BLK_{START,END}. + # SPI mode names for CMD32/33: ERASE_WR_BLK_{START,END}_ADDR. + if cmd in (32, 33): + s += '_ADDR' + return s def handle_command_token(self, mosi, miso): # Command tokens (6 bytes) are sent (MSB-first) by the host. @@ -187,14 +147,13 @@ class Decoder(srd.Decoder): # Bits[0:0]: End bit (always 1) bit, self.ss_bit, self.es_bit = tb(0, 0)[0], tb(0, 0)[1], tb(0, 0)[2] - self.putb([134, ['End bit: %d' % bit]]) if bit == 1: self.putb([134, ['End bit: %d' % bit]]) else: self.putb([135, ['End bit: %d (Warning: Must be 1!)' % bit]]) # Handle command. - if cmd in (0, 1, 9, 16, 17, 41, 49, 55, 59): + if cmd in (0, 1, 9, 16, 17, 24, 41, 49, 55, 59): self.state = 'HANDLE CMD%d' % cmd self.cmd_str = '%s%d (%s)' % (s, cmd, self.cmd_name(cmd)) else: @@ -266,6 +225,12 @@ class Decoder(srd.Decoder): self.read_buf = [] self.state = 'GET RESPONSE R1' + def handle_cmd24(self): + # CMD24: WRITE_BLOCK + self.putc(24, 'Write a block to address 0x%04x' % self.arg) + self.is_cmd24 = True + self.state = 'GET RESPONSE R1' + def handle_cmd49(self): self.state = 'GET RESPONSE R1' @@ -370,7 +335,8 @@ class Decoder(srd.Decoder): # Bit 7: Always set to 0 putbit(7, ['Bit 7 (always 0)']) - self.state = 'IDLE' + if self.is_cmd24: + self.state = 'HANDLE DATA BLOCK CMD24' def handle_response_r1b(self, res): # TODO @@ -392,6 +358,61 @@ class Decoder(srd.Decoder): # TODO pass + def handle_data_cmd24(self, mosi): + if self.cmd24_start_token_found: + if len(self.read_buf) == 0: + self.ss_data = self.ss + if not self.blocklen: + # Assume a fixed block size when inspection of the + # previous traffic did not provide the respective + # parameter value. + # TODO Make the default block size a user adjustable option? + self.blocklen = 512 + self.read_buf.append(mosi) + # Wait until block transfer completed. + if len(self.read_buf) < self.blocklen: + return + self.es_data = self.es + self.put(self.ss_data, self.es_data, self.out_ann, [24, ['Block data: %s' % self.read_buf]]) + self.read_buf = [] + self.state = 'DATA RESPONSE' + elif mosi == 0xfe: + self.put(self.ss, self.es, self.out_ann, [24, ['Start Block']]) + self.cmd24_start_token_found = True + + def handle_data_response(self, miso): + # Data Response token (1 byte). + # + # Format: + # - Bits[7:5]: Don't care. + # - Bits[4:4]: Always 0. + # - Bits[3:1]: Status. + # - 010: Data accepted. + # - 101: Data rejected due to a CRC error. + # - 110: Data rejected due to a write error. + # - Bits[0:0]: Always 1. + miso &= 0x1f + if miso & 0x11 != 0x01: + # This is not the byte we are waiting for. + # Should we return to IDLE here? + return + m = self.miso_bits + self.put(m[7][1], m[5][2], self.out_ann, [134, ['Don\'t care']]) + self.put(m[4][1], m[4][2], self.out_ann, [134, ['Always 0']]) + if miso == 0x05: + self.put(m[3][1], m[1][2], self.out_ann, [134, ['Data accepted']]) + elif miso == 0x0b: + self.put(m[3][1], m[1][2], self.out_ann, [134, ['Data rejected (CRC error)']]) + elif miso == 0x0d: + self.put(m[3][1], m[1][2], self.out_ann, [134, ['Data rejected (write error)']]) + self.put(m[0][1], m[0][2], self.out_ann, [134, ['Always 1']]) + ann_class = None + if self.is_cmd24: + ann_class = 24 + if ann_class is not None: + self.put(self.ss, self.es, self.out_ann, [ann_class, ['Data Response']]) + self.state = 'IDLE' + def decode(self, ss, es, data): ptype, mosi, miso = data @@ -431,10 +452,14 @@ class Decoder(srd.Decoder): # Ignore stray 0xff bytes, some devices seem to send those!? if miso == 0xff: # TODO? return - # Call the respective handler method for the response. + # Assume return to IDLE state, but allow response handlers + # to advance to some other state when applicable. s = 'handle_response_%s' % self.state[13:].lower() handle_response = getattr(self, s) - handle_response(miso) - self.state = 'IDLE' + handle_response(miso) + elif self.state == 'HANDLE DATA BLOCK CMD24': + self.handle_data_cmd24(mosi) + elif self.state == 'DATA RESPONSE': + self.handle_data_response(miso) diff --git a/libsigrokdecode4DSL/decoders/spdif/__init__.py b/libsigrokdecode4DSL/decoders/spdif/__init__.py index 38363f0f..3f5109a7 100755 --- a/libsigrokdecode4DSL/decoders/spdif/__init__.py +++ b/libsigrokdecode4DSL/decoders/spdif/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/spdif/pd.py b/libsigrokdecode4DSL/decoders/spdif/pd.py index 1e0e6a93..532bf825 100755 --- a/libsigrokdecode4DSL/decoders/spdif/pd.py +++ b/libsigrokdecode4DSL/decoders/spdif/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -24,14 +23,15 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'spdif' name = 'S/PDIF' longname = 'Sony/Philips Digital Interface Format' desc = 'Serial bus for connecting digital audio devices.' license = 'gplv2+' inputs = ['logic'] - outputs = ['spdif'] + outputs = [] + tags = ['Audio', 'PC'] channels = ( {'id': 'data', 'name': 'Data', 'desc': 'Data line'}, ) @@ -59,10 +59,13 @@ class Decoder(srd.Decoder): self.put(self.ss_edge, self.samplenum, self.out_ann, data) def __init__(self): + self.reset() + + def reset(self): self.state = 'GET FIRST PULSE WIDTH' - self.olddata = None self.ss_edge = None self.first_edge = True + self.samplenum_prev_edge = 0 self.pulse_width = 0 self.clocks = [] @@ -218,41 +221,26 @@ class Decoder(srd.Decoder): self.last_preamble = self.samplenum - def decode(self, ss, es, logic): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - for (self.samplenum, pins) in logic: - data = pins[0] - logic.itercnt += 1 + # Throw away first detected edge as it might be mangled data. + self.wait({0: 'e'}) - # Initialize self.olddata with the first sample value. - if self.olddata is None: - self.olddata = data - continue + while True: + # Wait for any edge (rising or falling). + (data,) = self.wait({0: 'e'}) + self.pulse_width = self.samplenum - self.samplenum_prev_edge - 1 + self.samplenum_prev_edge = self.samplenum - # First we need to recover the clock. - if self.olddata == data: - self.pulse_width += 1 - continue - - # Found rising or falling edge. - if self.first_edge: - # Throw away first detected edge as it might be mangled data. - self.first_edge = False - self.pulse_width = 0 - else: - if self.state == 'GET FIRST PULSE WIDTH': - self.find_first_pulse_width() - elif self.state == 'GET SECOND PULSE WIDTH': - self.find_second_pulse_width() - elif self.state == 'GET THIRD PULSE WIDTH': - self.find_third_pulse_width() - elif self.state == 'DECODE STREAM': - self.decode_stream() - elif self.state == 'DECODE PREAMBLE': - self.decode_preamble() - - self.pulse_width = 0 - - self.olddata = data + if self.state == 'GET FIRST PULSE WIDTH': + self.find_first_pulse_width() + elif self.state == 'GET SECOND PULSE WIDTH': + self.find_second_pulse_width() + elif self.state == 'GET THIRD PULSE WIDTH': + self.find_third_pulse_width() + elif self.state == 'DECODE STREAM': + self.decode_stream() + elif self.state == 'DECODE PREAMBLE': + self.decode_preamble() diff --git a/libsigrokdecode4DSL/decoders/spiflash/__init__.py b/libsigrokdecode4DSL/decoders/spiflash/__init__.py index 6ffb4da8..151bd3e2 100755 --- a/libsigrokdecode4DSL/decoders/spiflash/__init__.py +++ b/libsigrokdecode4DSL/decoders/spiflash/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/spiflash/lists.py b/libsigrokdecode4DSL/decoders/spiflash/lists.py index ba9f2c2b..5c366bee 100755 --- a/libsigrokdecode4DSL/decoders/spiflash/lists.py +++ b/libsigrokdecode4DSL/decoders/spiflash/lists.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## from collections import OrderedDict @@ -38,6 +37,8 @@ cmds = OrderedDict([ (0x60, ('CE', 'Chip erase')), (0x70, ('ESRY', 'Enable SO to output RY/BY#')), (0x80, ('DSRY', 'Disable SO to output RY/BY#')), + (0x82, ('WRITE1', 'Main memory page program through buffer 1 with built-in erase')), + (0x85, ('WRITE2', 'Main memory page program through buffer 2 with built-in erase')), (0x90, ('REMS', 'Read electronic manufacturer & device ID')), (0x9f, ('RDID', 'Read identification')), (0xab, ('RDP/RES', 'Release from deep powerdown / Read electronic ID')), @@ -47,11 +48,15 @@ cmds = OrderedDict([ (0xbb, ('2READ', '2x I/O read')), # a.k.a. "Fast read dual I/O". (0xc1, ('EXSO', 'Exit secured OTP')), (0xc7, ('CE2', 'Chip erase')), # Alternative command ID + (0xd7, ('STATUS', 'Status register read')), (0xd8, ('BE', 'Block erase')), (0xef, ('REMS2', 'Read ID for 2x I/O mode')), ]) device_name = { + 'adesto': { + 0x00: 'AT45Dxxx family, standard series', + }, 'fidelix': { 0x15: 'FM25Q32', }, @@ -60,9 +65,24 @@ device_name = { 0x15: 'MX25L3205D', 0x16: 'MX25L6405D', }, + 'winbond': { + 0x13: 'W25Q80DV', + }, } chips = { + # Adesto + 'adesto_at45db161e': { + 'vendor': 'Adesto', + 'model': 'AT45DB161E', + 'res_id': 0xff, # The chip doesn't emit an ID here. + 'rems_id': 0xffff, # Not supported by the chip. + 'rems2_id': 0xffff, # Not supported by the chip. + 'rdid_id': 0x1f26000100, # RDID and 2 extra "EDI" bytes. + 'page_size': 528, # Configurable, could also be 512 bytes. + 'sector_size': 128 * 1024, + 'block_size': 4 * 1024, + }, # FIDELIX 'fidelix_fm25q32': { 'vendor': 'FIDELIX', @@ -109,4 +129,16 @@ chips = { 'sector_size': 4 * 1024, 'block_size': 64 * 1024, }, + # Winbond + 'winbond_w25q80dv': { + 'vendor': 'Winbond', + 'model': 'W25Q80DV', + 'res_id': 0x13, + 'rems_id': 0xef13, + 'rems2_id': 0xffff, # Not supported by the chip. + 'rdid_id': 0xef4014, + 'page_size': 256, + 'sector_size': 4 * 1024, + 'block_size': 64 * 1024, # Configurable, could also be 32 * 1024 bytes. + }, } diff --git a/libsigrokdecode4DSL/decoders/spiflash/pd.py b/libsigrokdecode4DSL/decoders/spiflash/pd.py index bc5f9ca8..5ee22740 100755 --- a/libsigrokdecode4DSL/decoders/spiflash/pd.py +++ b/libsigrokdecode4DSL/decoders/spiflash/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -26,8 +25,8 @@ L = len(cmds) # Don't forget to keep this in sync with 'cmds' is lists.py. class Ann: WRSR, PP, READ, WRDI, RDSR, WREN, FAST_READ, SE, RDSCUR, WRSCUR, \ - RDSR2, CE, ESRY, DSRY, REMS, RDID, RDP_RES, CP, ENSO, DP, READ2X, \ - EXSO, CE2, BE, REMS2, \ + RDSR2, CE, ESRY, DSRY, WRITE1, WRITE2, REMS, RDID, RDP_RES, CP, ENSO, DP, \ + READ2X, EXSO, CE2, STATUS, BE, REMS2, \ BIT, FIELD, WARN = range(L + 3) def cmd_annotation_classes(): @@ -72,14 +71,15 @@ def decode_status_reg(data): return ret class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'spiflash' name = 'SPI flash' longname = 'SPI flash chips' desc = 'xx25 series SPI (NOR) flash chip protocol.' license = 'gplv2+' inputs = ['spi'] - outputs = ['spiflash'] + outputs = [] + tags = ['IC', 'Memory'] annotations = cmd_annotation_classes() + ( ('bit', 'Bit'), ('field', 'Field'), @@ -99,9 +99,13 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.device_id = -1 self.on_end_transaction = None self.end_current_transaction() + self.writestate = 0 # Build dict mapping command keys to handler functions. Each # command in 'cmds' (defined in lists.py) has a matching @@ -172,10 +176,11 @@ class Decoder(srd.Decoder): def handle_wren(self, mosi, miso): self.putx([Ann.WREN, self.cmd_ann_list()]) - self.state = None + self.writestate = 1 def handle_wrdi(self, mosi, miso): - pass # TODO + self.putx([Ann.WRDI, self.cmd_ann_list()]) + self.writestate = 0 def handle_rdid(self, mosi, miso): if self.cmdstate == 1: @@ -213,6 +218,8 @@ class Decoder(srd.Decoder): self.putx([Ann.BIT, [decode_status_reg(miso)]]) self.putx([Ann.FIELD, ['Status register']]) self.putc([Ann.RDSR, self.cmd_ann_list()]) + # Set write latch state. + self.writestate = 1 if (miso & (1 << 1)) else 0 self.cmdstate += 1 def handle_rdsr2(self, mosi, miso): @@ -242,12 +249,14 @@ class Decoder(srd.Decoder): self.emit_cmd_byte() elif self.cmdstate == 2: # Byte 2: Master sends status register 1. - self.putx([Ann.BIT, [decode_status_reg(miso)]]) + self.putx([Ann.BIT, [decode_status_reg(mosi)]]) self.putx([Ann.FIELD, ['Status register 1']]) + # Set write latch state. + self.writestate = 1 if (miso & (1 << 1)) else 0 elif self.cmdstate == 3: # Byte 3: Master sends status register 2. # TODO: Decode status register 2 correctly. - self.putx([Ann.BIT, [decode_status_reg(miso)]]) + self.putx([Ann.BIT, [decode_status_reg(mosi)]]) self.putx([Ann.FIELD, ['Status register 2']]) self.es_cmd = self.es self.putc([Ann.WRSR, self.cmd_ann_list()]) @@ -271,6 +280,32 @@ class Decoder(srd.Decoder): self.data.append(miso) self.cmdstate += 1 + def handle_write_common(self, mosi, miso, ann): + # Write data bytes: Master asserts CS#, sends WRITE command, sends + # 3-byte address, writes >= 1 data bytes, de-asserts CS#. + if self.cmdstate == 1: + # Byte 1: Master sends command ID. + self.emit_cmd_byte() + if self.writestate == 0: + self.putc([Ann.WARN, ['Warning: WREN might be missing']]) + elif self.cmdstate in (2, 3, 4): + # Bytes 2/3/4: Master sends write address (24bits, MSB-first). + self.emit_addr_bytes(mosi) + elif self.cmdstate >= 5: + # Bytes 5-x: Master writes data bytes (until CS# de-asserted). + self.es_field = self.es # Will be overwritten for each byte. + if self.cmdstate == 5: + self.ss_field = self.ss + self.on_end_transaction = lambda: self.output_data_block('Data', ann) + self.data.append(mosi) + self.cmdstate += 1 + + def handle_write1(self, mosi, miso): + self.handle_write_common(mosi, miso, Ann.WRITE1) + + def handle_write2(self, mosi, miso): + self.handle_write_common(mosi, miso, Ann.WRITE2) + def handle_fast_read(self, mosi, miso): # Fast read: Master asserts CS#, sends FAST READ command, sends # 3-byte address + 1 dummy byte, reads >= 1 data bytes, de-asserts CS#. @@ -322,12 +357,27 @@ class Decoder(srd.Decoder): self.data.append(b2) self.cmdstate += 1 + def handle_status(self, mosi, miso): + if self.cmdstate == 1: + # Byte 1: Master sends command ID. + self.emit_cmd_byte() + self.on_end_transaction = lambda: self.putc([Ann.STATUS, [cmds[self.state][1]]]) + else: + # Will be overwritten for each byte. + self.es_cmd = self.es + self.es_field = self.es + if self.cmdstate == 2: + self.ss_field = self.ss + self.putx([Ann.BIT, ['Status register byte %d: 0x%02x' % ((self.cmdstate % 2) + 1, miso)]]) + self.cmdstate += 1 + # TODO: Warn/abort if we don't see the necessary amount of bytes. - # TODO: Warn if WREN was not seen before. def handle_se(self, mosi, miso): if self.cmdstate == 1: # Byte 1: Master sends command ID. self.emit_cmd_byte() + if self.writestate == 0: + self.putx([Ann.WARN, ['Warning: WREN might be missing']]) elif self.cmdstate in (2, 3, 4): # Bytes 2/3/4: Master sends sector address (24bits, MSB-first). self.emit_addr_bytes(mosi) @@ -348,10 +398,14 @@ class Decoder(srd.Decoder): pass # TODO def handle_ce(self, mosi, miso): - pass # TODO + self.putx([Ann.CE, self.cmd_ann_list()]) + if self.writestate == 0: + self.putx([Ann.WARN, ['Warning: WREN might be missing']]) def handle_ce2(self, mosi, miso): - pass # TODO + self.putx([Ann.CE2, self.cmd_ann_list()]) + if self.writestate == 0: + self.putx([Ann.WARN, ['Warning: WREN might be missing']]) def handle_pp(self, mosi, miso): # Page program: Master asserts CS#, sends PP command, sends 3-byte @@ -420,8 +474,8 @@ class Decoder(srd.Decoder): self.putx([Ann.FIELD, ['%s ID: 0x%02x' % (d, miso)]]) if self.cmdstate == 6: - id = self.ids[1] if self.manufacturer_id_first else self.ids[0] - self.device_id = id + id_ = self.ids[1] if self.manufacturer_id_first else self.ids[0] + self.device_id = id_ self.es_cmd = self.es self.putc([Ann.REMS, self.cmd_vendor_dev_list()]) self.state = None diff --git a/libsigrokdecode4DSL/decoders/ssi32/__init__.py b/libsigrokdecode4DSL/decoders/ssi32/__init__.py old mode 100644 new mode 100755 diff --git a/libsigrokdecode4DSL/decoders/ssi32/pd.py b/libsigrokdecode4DSL/decoders/ssi32/pd.py old mode 100644 new mode 100755 index f2e79b8a..51608039 --- a/libsigrokdecode4DSL/decoders/ssi32/pd.py +++ b/libsigrokdecode4DSL/decoders/ssi32/pd.py @@ -23,14 +23,15 @@ import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'ssi32' name = 'SSI32' longname = 'Synchronous Serial Interface (32bit)' desc = 'Synchronous Serial Interface (32bit) protocol.' license = 'gplv2+' inputs = ['spi'] - outputs = ['ssi32'] + outputs = [] + tags = ['Embedded/industrial'] options = ( {'id': 'msgsize', 'desc': 'Message size', 'default': 64}, ) @@ -46,6 +47,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.ss_cmd, self.es_cmd = 0, 0 self.mosi_bytes = [] self.miso_bytes = [] @@ -59,7 +63,7 @@ class Decoder(srd.Decoder): def putx(self, data): self.put(self.ss_cmd, self.es_cmd, self.out_ann, data) - def reset(self): + def reset_data(self): self.mosi_bytes = [] self.miso_bytes = [] self.es_array = [] @@ -93,7 +97,7 @@ class Decoder(srd.Decoder): def decode(self, ss, es, data): ptype = data[0] if ptype == 'CS-CHANGE': - self.reset() + self.reset_data() return # Don't care about anything else. @@ -114,10 +118,10 @@ class Decoder(srd.Decoder): return self.handle_ack() - self.reset() + self.reset_data() else: if len(self.mosi_bytes) < self.options['msgsize']: return self.handle_ctrl() - self.reset() + self.reset_data() diff --git a/libsigrokdecode4DSL/decoders/st7735/__init__.py b/libsigrokdecode4DSL/decoders/st7735/__init__.py new file mode 100755 index 00000000..771578c6 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/st7735/__init__.py @@ -0,0 +1,27 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Aleksander Alekseev +## +## 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 2 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 . +## + +''' +This decoder decodes the Sitronix ST7735 TFT controller protocol. + +Details: +http://www.displayfuture.com/Display/datasheet/controller/ST7735.pdf +''' + +from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/st7735/pd.py b/libsigrokdecode4DSL/decoders/st7735/pd.py new file mode 100755 index 00000000..252b1887 --- /dev/null +++ b/libsigrokdecode4DSL/decoders/st7735/pd.py @@ -0,0 +1,173 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Aleksander Alekseev +## +## 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 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . + +import sigrokdecode as srd + +MAX_DATA_LEN = 128 + +# Command ID -> name, short description +META = { + 0x00: {'name': 'NOP ', 'desc': 'No operation'}, + 0x01: {'name': 'SWRESET', 'desc': 'Software reset'}, + 0x04: {'name': 'RDDID ', 'desc': 'Read display ID'}, + 0x09: {'name': 'RDDST ', 'desc': 'Read display status'}, + 0x10: {'name': 'SLPIN ', 'desc': 'Sleep in & booster off'}, + 0x11: {'name': 'SLPOUT ', 'desc': 'Sleep out & booster on'}, + 0x12: {'name': 'PTLON ', 'desc': 'Partial mode on'}, + 0x13: {'name': 'NORON ', 'desc': 'Partial off (normal)'}, + 0x20: {'name': 'INVOFF ', 'desc': 'Display inversion off'}, + 0x21: {'name': 'INVON ', 'desc': 'Display inversion on'}, + 0x28: {'name': 'DISPOFF', 'desc': 'Display off'}, + 0x29: {'name': 'DISPON ', 'desc': 'Display on'}, + 0x2A: {'name': 'CASET ', 'desc': 'Column address set'}, + 0x2B: {'name': 'RASET ', 'desc': 'Row address set'}, + 0x2C: {'name': 'RAMWR ', 'desc': 'Memory write'}, + 0x2E: {'name': 'RAMRD ', 'desc': 'Memory read'}, + 0x30: {'name': 'PTLAR ', 'desc': 'Partial start/end address set'}, + 0x36: {'name': 'MADCTL ', 'desc': 'Memory data address control'}, + 0x3A: {'name': 'COLMOD ', 'desc': 'Interface pixel format'}, + 0xB1: {'name': 'FRMCTR1', 'desc': 'Frame rate control (in normal mode / full colors)'}, + 0xB2: {'name': 'FRMCTR2', 'desc': 'Frame rate control (in idle mode / 8-colors)'}, + 0xB3: {'name': 'FRMCTR3', 'desc': 'Frame rate control (in partial mode / full colors) '}, + 0xB4: {'name': 'INVCTR ', 'desc': 'Display inversion control'}, + 0xB6: {'name': 'DISSET5', 'desc': 'Display function set 5'}, + 0xC0: {'name': 'PWCTR1 ', 'desc': 'Power control 1'}, + 0xC1: {'name': 'PWCTR2 ', 'desc': 'Power control 2'}, + 0xC2: {'name': 'PWCTR3 ', 'desc': 'Power control 3'}, + 0xC3: {'name': 'PWCTR4 ', 'desc': 'Power control 4'}, + 0xC4: {'name': 'PWCTR5 ', 'desc': 'Power control 5'}, + 0xC5: {'name': 'VMCTR1 ', 'desc': 'VCOM control 1'}, + 0xDA: {'name': 'RDID1 ', 'desc': 'Read ID1'}, + 0xDB: {'name': 'RDID2 ', 'desc': 'Read ID2'}, + 0xDC: {'name': 'RDID3 ', 'desc': 'Read ID3'}, + 0xDD: {'name': 'RDID4 ', 'desc': 'Read ID4'}, + 0xFC: {'name': 'PWCTR6 ', 'desc': 'Power control 6'}, + 0xE0: {'name': 'GMCTRP1', 'desc': 'Gamma \'+\'polarity correction characteristics setting'}, + 0xE1: {'name': 'GMCTRN1', 'desc': 'Gamma \'-\'polarity correction characteristics setting'}, +} + +class Ann: + BITS, CMD, DATA, DESC = range(4) + +class Decoder(srd.Decoder): + api_version = 3 + id = 'st7735' + name = 'ST7735' + longname = 'Sitronix ST7735' + desc = 'Sitronix ST7735 TFT controller protocol.' + license = 'gplv2+' + inputs = ['logic'] + outputs = [] + tags = ['Display', 'IC'] + channels = ( + {'id': 'cs', 'name': 'CS#', 'desc': 'Chip-select'}, + {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'}, + {'id': 'mosi', 'name': 'MOSI', 'desc': 'Master out, slave in'}, + {'id': 'dc', 'name': 'DC', 'desc': 'Data or command'} + ) + annotations = ( + ('bit', 'Bit'), + ('command', 'Command'), + ('data', 'Data'), + ('description', 'Description'), + ) + annotation_rows = ( + ('bits', 'Bits', (Ann.BITS,)), + ('fields', 'Fields', (Ann.CMD, Ann.DATA)), + ('description', 'Description', (Ann.DESC,)), + ) + + def __init__(self): + self.reset() + + def reset(self): + self.accum_byte = 0 + self.accum_bits_num = 0 + self.bit_ss = -1 + self.byte_ss = -1 + self.current_bit = -1 + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + + def put_desc(self, ss, es, cmd, data): + if cmd == -1: + return + if META[cmd]: + self.put(ss, es, self.out_ann, [Ann.DESC, + ['%s: %s' % (META[cmd]['name'].strip(), META[cmd]['desc'])]]) + else: + # Default description: + dots = '' + if len(data) == MAX_DATA_LEN: + data = data[:-1] + dots = '...' + data_str = '(none)' + if len(data) > 0: + data_str = ' '.join(['%02X' % b for b in data]) + self.put(ss, es, self.out_ann, [Ann.DESC, + ['Unknown command: %02X. Data: %s%s' % (cmd, data_str, dots)]]) + + def decode(self): + current_cmd = -1 + current_data = [] + desc_ss = -1 + desc_es = -1 + self.reset() + while True: + # Check data on both CLK edges. + (cs, clk, mosi, dc) = self.wait({1: 'e'}) + + if cs == 1: # Wait for CS = low, ignore the rest. + self.reset() + continue + + if clk == 1: + # Read one bit. + self.bit_ss = self.samplenum + if self.accum_bits_num == 0: + self.byte_ss = self.samplenum + self.current_bit = mosi + + if (clk == 0) and (self.current_bit >= 0): + # Process one bit. + self.put(self.bit_ss, self.samplenum, self.out_ann, + [Ann.BITS, [str(self.current_bit)]]) + self.accum_byte = (self.accum_byte << 1) | self.current_bit # MSB-first. + self.accum_bits_num += 1 + if self.accum_bits_num == 8: + # Process one byte. + ann = Ann.DATA if dc else Ann.CMD # DC = low for commands. + self.put(self.byte_ss, self.samplenum, self.out_ann, + [ann, ['%02X' % self.accum_byte]]) + if ann == Ann.CMD: + self.put_desc(desc_ss, desc_es, current_cmd, current_data) + desc_ss = self.byte_ss + desc_es = self.samplenum # For cmds without data. + current_cmd = self.accum_byte + current_data = [] + else: + if len(current_data) < MAX_DATA_LEN: + current_data += [self.accum_byte] + desc_es = self.samplenum + + self.accum_bits_num = 0 + self.accum_byte = 0 + self.byte_ss = -1 + self.current_bit = -1 + self.bit_ss = -1 diff --git a/libsigrokdecode4DSL/decoders/stepper_motor/__init__.py b/libsigrokdecode4DSL/decoders/stepper_motor/__init__.py index 222d393a..9126104f 100755 --- a/libsigrokdecode4DSL/decoders/stepper_motor/__init__.py +++ b/libsigrokdecode4DSL/decoders/stepper_motor/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/stepper_motor/pd.py b/libsigrokdecode4DSL/decoders/stepper_motor/pd.py index 74416fa6..2a7009a0 100755 --- a/libsigrokdecode4DSL/decoders/stepper_motor/pd.py +++ b/libsigrokdecode4DSL/decoders/stepper_motor/pd.py @@ -14,24 +14,21 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd -class SamplerateError(Exception): - pass - class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'stepper_motor' name = 'Stepper motor' longname = 'Stepper motor position / speed' desc = 'Absolute position and movement speed from step/dir.' license = 'gplv2+' inputs = ['logic'] - outputs = ['stepper_motor'] + outputs = [] + tags = ['Embedded/industrial'] channels = ( {'id': 'step', 'name': 'Step', 'desc': 'Step pulse'}, {'id': 'dir', 'name': 'Direction', 'desc': 'Direction select'}, @@ -51,8 +48,12 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): + self.samplerate = None self.oldstep = None - self.prev_step_ss = None + self.ss_prev_step = None self.pos = 0 self.prev_speed = None self.prev_pos = None @@ -70,29 +71,25 @@ class Decoder(srd.Decoder): self.unit = 'mm' def step(self, ss, direction): - if self.prev_step_ss is not None: - delta = ss - self.prev_step_ss - speed = self.samplerate / delta / self.scale - speed_txt = self.format % speed + if self.ss_prev_step is not None: + if self.samplerate: + delta = ss - self.ss_prev_step + speed = self.samplerate / delta / self.scale + speed_txt = self.format % speed + self.put(self.ss_prev_step, ss, self.out_ann, + [0, [speed_txt + ' ' + self.unit + '/s', speed_txt]]) pos_txt = self.format % (self.pos / self.scale) - self.put(self.prev_step_ss, ss, self.out_ann, - [0, [speed_txt + ' ' + self.unit + '/s', speed_txt]]) - self.put(self.prev_step_ss, ss, self.out_ann, + self.put(self.ss_prev_step, ss, self.out_ann, [1, [pos_txt + ' ' + self.unit, pos_txt]]) self.pos += (1 if direction else -1) - self.prev_step_ss = ss + self.ss_prev_step = ss def metadata(self, key, value): if key == srd.SRD_CONF_SAMPLERATE: self.samplerate = value - def decode(self, ss, es, data): - if not self.samplerate: - raise SamplerateError('Cannot decode without samplerate.') - - for (self.samplenum, (step, direction)) in data: - data.itercnt += 1 - if step == 1 and self.oldstep == 0: - self.step(self.samplenum, direction) - self.oldstep = step + def decode(self): + while True: + (step, direction) = self.wait({0: 'r'}) + self.step(self.samplenum, direction) diff --git a/libsigrokdecode4DSL/decoders/swd/__init__.py b/libsigrokdecode4DSL/decoders/swd/__init__.py index 3a65143c..a141239b 100755 --- a/libsigrokdecode4DSL/decoders/swd/__init__.py +++ b/libsigrokdecode4DSL/decoders/swd/__init__.py @@ -14,22 +14,21 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' This PD decodes the ARM SWD (version 1) protocol, as described in the "ARM Debug Interface v5.2" Architecture Specification. -Details: -http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ihi0031c/index.html -(Registration required) - Not supported: * Turnaround periods other than the default 1, as set in DLCR.TURNROUND (should be trivial to add) * SWD protocol version 2 (multi-drop support, etc.) + +Details: +http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ihi0031c/index.html +(Registration required) ''' from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/swd/pd.py b/libsigrokdecode4DSL/decoders/swd/pd.py index f490c907..3f81e03d 100755 --- a/libsigrokdecode4DSL/decoders/swd/pd.py +++ b/libsigrokdecode4DSL/decoders/swd/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -65,7 +64,7 @@ BIT_CTRLSTAT_ORUNDETECT = 1 ANNOTATIONS = ['reset', 'enable', 'read', 'write', 'ack', 'data', 'parity'] class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'swd' name = 'SWD' longname = 'Serial Wire Debug' @@ -73,6 +72,7 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['logic'] outputs = ['swd'] + tags = ['Debug/trace'] channels = ( {'id': 'swclk', 'name': 'SWCLK', 'desc': 'Master clock'}, {'id': 'swdio', 'name': 'SWDIO', 'desc': 'Data input/output'}, @@ -93,9 +93,11 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): # SWD data/clock state self.state = 'UNKNOWN' - self.oldclk = -1 self.sample_edge = RISING self.ack = None # Ack state of the current phase self.ss_req = 0 # Start sample of current req @@ -142,12 +144,10 @@ class Decoder(srd.Decoder): }[(self.apdp, self.rw)] self.putp(ptype, (self.addr, self.data, self.ack)) - def decode(self, ss, es, data): - for (self.samplenum, (clk, dio)) in data: - data.itercnt += 1 - if clk == self.oldclk: - continue # Not a clock edge. - self.oldclk = clk + def decode(self): + while True: + # Wait for any clock edge. + (clk, dio) = self.wait({0: 'e'}) # Count rising edges with DIO held high, # as a line reset (50+ high edges) can happen from any state. @@ -201,15 +201,15 @@ class Decoder(srd.Decoder): elif self.state == 'DATA': self.state = 'DPARITY' elif self.state == 'DPARITY': - self.put_python_data() + #self.put_python_data() self.state = 'REQ' self.sample_edge = RISING self.turnaround = 1 if self.rw == 'R' else 0 def reset_state(self): '''Line reset (or equivalent), wait for a new pending SWD request.''' - if self.state != 'REQ': # Emit a Python data item. - self.put_python_data() + #if self.state != 'REQ': # Emit a Python data item. + # self.put_python_data() # Clear state. self.bits = '' self.samplenums = [] diff --git a/libsigrokdecode4DSL/decoders/swim/pd.py b/libsigrokdecode4DSL/decoders/swim/pd.py index 8e4fefab..2f0c1216 100755 --- a/libsigrokdecode4DSL/decoders/swim/pd.py +++ b/libsigrokdecode4DSL/decoders/swim/pd.py @@ -1,4 +1,4 @@ -# +## ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2018 Mike Jagdis @@ -21,20 +21,19 @@ import math import sigrokdecode as srd - class SamplerateError(Exception): pass - class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'swim' name = 'SWIM' longname = 'STM8 SWIM bus' - desc = 'STM8 Single Wire Interface Module (SWIM).' - license = 'gplv3+' + desc = 'STM8 Single Wire Interface Module (SWIM) protocol.' + license = 'gplv2+' inputs = ['logic'] outputs = [] + tags = ['Debug/trace'] options = ( {'id': 'debug', 'desc': 'Debug', 'default': 'no', 'values': ('yes', 'no') }, ) @@ -42,30 +41,27 @@ class Decoder(srd.Decoder): {'id': 'swim', 'name': 'SWIM', 'desc': 'SWIM data line'}, ) annotations = ( - ('108', 'bit', 'Bit'), - - ('7', 'enterseq', 'SWIM enter sequence'), - ('111', 'start-host', 'Start bit (host)'), - ('112', 'start-target', 'Start bit (target)'), - ('6', 'parity', 'Parity bit'), - ('6', 'ack', 'Acknowledgement'), - ('0', 'nack', 'Negative acknowledgement'), - ('111', 'byte-write', 'Byte write'), - ('112', 'byte-read', 'Byte read'), - - ('0', 'cmd-unknown', 'Unknown SWIM command'), - ('11', 'cmd', 'SWIM command'), - ('111', 'bytes', 'Byte count'), - ('111', 'address', 'Address'), - ('111', 'data-write', 'Data write'), - ('112', 'data-read', 'Data read'), - - ('208', 'debug', 'Debug'), + ('bit', 'Bit'), + ('enterseq', 'SWIM enter sequence'), + ('start-host', 'Start bit (host)'), + ('start-target', 'Start bit (target)'), + ('parity', 'Parity bit'), + ('ack', 'Acknowledgement'), + ('nack', 'Negative acknowledgement'), + ('byte-write', 'Byte write'), + ('byte-read', 'Byte read'), + ('cmd-unknown', 'Unknown SWIM command'), + ('cmd', 'SWIM command'), + ('bytes', 'Byte count'), + ('address', 'Address'), + ('data-write', 'Data write'), + ('data-read', 'Data read'), + ('debug', 'Debug'), ) annotation_rows = ( ('bits', 'Bits', (0,)), - ('framing', 'Framing', (2,3,4,5,6,7,8,)), - ('protocol', 'Protocol', (1,9,10,11,12,13,14,)), + ('framing', 'Framing', (2, 3, 4, 5, 6, 7, 8)), + ('protocol', 'Protocol', (1, 9, 10, 11, 12, 13, 14)), ('debug', 'Debug', (15,)), ) binary = ( @@ -73,13 +69,13 @@ class Decoder(srd.Decoder): ('rx', 'Dump of data read from target'), ) - def __init__(self): - # SWIM clock for the target is normally HSI/2 where HSI is 8MHz +- 5% although the - # divisor can be removed by setting the SWIMCLK bit in the CLK_SWIMCCR register. - # There is no standard for the host so we will be generous and assume it is using - # an 8MHz +- 10% oscillator. We do not need to be accurate. We just need to avoid - # treating enter sequence pulses as bits. A synchronization frame will cause this + # SWIM clock for the target is normally HSI/2 where HSI is 8MHz +- 5% + # although the divisor can be removed by setting the SWIMCLK bit in + # the CLK_SWIMCCR register. There is no standard for the host so we + # will be generous and assume it is using an 8MHz +- 10% oscillator. + # We do not need to be accurate. We just need to avoid treating enter + # sequence pulses as bits. A synchronization frame will cause this # to be adjusted. self.HSI = 8000000 self.HSI_min = self.HSI * 0.9 @@ -92,33 +88,28 @@ class Decoder(srd.Decoder): self.reset() - def reset(self): self.bit_edge = [[-1, None], [-1, None]] self.bit_maxlen = -1 - self.bitseq_len = 0 self.bitseq_end = None - self.proto_state = 'CMD' - def metadata(self, key, value): if key == srd.SRD_CONF_SAMPLERATE: self.samplerate = value - def adjust_timings(self): # A low-speed bit is 22 SWIM clocks long. - # There are options to shorten bits to 10 clocks or use HSI rather than HSI/2 as - # the SWIM clock but the longest valid bit should be no more than this many samples. - # This does not need to be accurate. It exists simply to prevent bits extending - # unecessarily far into trailing bus-idle periods. This will be adjusted every - # time we see a synchronization frame or start bit in order to show idle periods + # There are options to shorten bits to 10 clocks or use HSI rather + # than HSI/2 as the SWIM clock but the longest valid bit should be no + # more than this many samples. This does not need to be accurate. + # It exists simply to prevent bits extending unecessarily far into + # trailing bus-idle periods. This will be adjusted every time we see + # a synchronization frame or start bit in order to show idle periods # as accurately as possible. self.bit_reflen = math.ceil(self.samplerate * 22 / self.swim_clock) - def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) self.out_binary = self.register(srd.OUTPUT_BINARY) @@ -126,76 +117,62 @@ class Decoder(srd.Decoder): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - # A synchronization frame is a low that lasts for more than 64 but no more than - # 128 SWIM clock periods based on the standard SWIM clock. - # Note: we also allow for the possibility that the SWIM clock divisor has been - # disabled here. + # A synchronization frame is a low that lasts for more than 64 but no + # more than 128 SWIM clock periods based on the standard SWIM clock. + # Note: we also allow for the possibility that the SWIM clock divisor + # has been disabled here. self.sync_reflen_min = math.floor(self.samplerate * 64 / self.HSI_max) self.sync_reflen_max = math.ceil(self.samplerate * 128 / (self.HSI_min / 2)) - if self.options['debug'] == 'yes': - self.debug = True - else: - self.debug = False + self.debug = True if self.options['debug'] == 'yes' else False - # The SWIM entry sequence is four pulses at 2kHz followed by four at 1kHz. + # The SWIM entry sequence is 4 pulses at 2kHz followed by 4 at 1kHz. self.eseq_reflen = math.ceil(self.samplerate / 2048) self.adjust_timings() - def protocol(self): if self.proto_state == 'CMD': # Command if self.bitseq_value == 0x00: - self.put(self.bitseq_start, self.bitseq_end, self.out_ann, [ 10, [ 'system reset', 'SRST', '!' ]]) - + self.put(self.bitseq_start, self.bitseq_end, self.out_ann, [10, ['system reset', 'SRST', '!']]) elif self.bitseq_value == 0x01: self.proto_state = 'N' - self.put(self.bitseq_start, self.bitseq_end, self.out_ann, [ 10, [ 'read on-the-fly', 'ROTF', 'r' ]]) - + self.put(self.bitseq_start, self.bitseq_end, self.out_ann, [10, ['read on-the-fly', 'ROTF', 'r']]) elif self.bitseq_value == 0x02: self.proto_state = 'N' - self.put(self.bitseq_start, self.bitseq_end, self.out_ann, [ 10, [ 'write on-the-fly', 'WOTF', 'w' ]]) - + self.put(self.bitseq_start, self.bitseq_end, self.out_ann, [10, ['write on-the-fly', 'WOTF', 'w']]) else: - self.put(self.bitseq_start, self.bitseq_end, self.out_ann, [ 9, [ 'unknown', 'UNK' ]]) - + self.put(self.bitseq_start, self.bitseq_end, self.out_ann, [9, ['unknown', 'UNK']]) elif self.proto_state == 'N': # Number of bytes self.proto_byte_count = self.bitseq_value self.proto_state = '@E' - self.put(self.bitseq_start, self.bitseq_end, self.out_ann, [ 11, [ 'byte count 0x%02x' % self.bitseq_value, 'bytes 0x%02x' % self.bitseq_value, '0x%02x' % self.bitseq_value, '%02x' % self.bitseq_value, '%x' % self.bitseq_value ]]) - - + self.put(self.bitseq_start, self.bitseq_end, self.out_ann, [11, ['byte count 0x%02x' % self.bitseq_value, 'bytes 0x%02x' % self.bitseq_value, '0x%02x' % self.bitseq_value, '%02x' % self.bitseq_value, '%x' % self.bitseq_value]]) elif self.proto_state == '@E': # Address byte 1 self.proto_addr = self.bitseq_value self.proto_addr_start = self.bitseq_start self.proto_state = '@H' - elif self.proto_state == '@H': # Address byte 2 self.proto_addr = (self.proto_addr << 8) | self.bitseq_value self.proto_state = '@L' - elif self.proto_state == '@L': # Address byte 3 self.proto_addr = (self.proto_addr << 8) | self.bitseq_value self.proto_state = 'D' - self.put(self.proto_addr_start, self.bitseq_end, self.out_ann, [ 12, [ 'address 0x%06x' % self.proto_addr, 'addr 0x%06x' % self.proto_addr, '0x%06x' % self.proto_addr, '%06x' %self.proto_addr, '%x' % self.proto_addr ]]) - + self.put(self.proto_addr_start, self.bitseq_end, self.out_ann, [12, ['address 0x%06x' % self.proto_addr, 'addr 0x%06x' % self.proto_addr, '0x%06x' % self.proto_addr, '%06x' %self.proto_addr, '%x' % self.proto_addr]]) else: if self.proto_byte_count > 0: self.proto_byte_count -= 1 if self.proto_byte_count == 0: self.proto_state = 'CMD' - self.put(self.bitseq_start, self.bitseq_end, self.out_ann, [ 13 + self.bitseq_dir, [ '0x%02x' % self.bitseq_value, '%02x' % self.bitseq_value, '%x' % self.bitseq_value ]]) - self.put(self.bitseq_start, self.bitseq_end, self.out_binary, [ 0 + self.bitseq_dir, bytes([self.bitseq_value]) ]) + self.put(self.bitseq_start, self.bitseq_end, self.out_ann, [13 + self.bitseq_dir, ['0x%02x' % self.bitseq_value, '%02x' % self.bitseq_value, '%x' % self.bitseq_value]]) + self.put(self.bitseq_start, self.bitseq_end, self.out_binary, [0 + self.bitseq_dir, bytes([self.bitseq_value])]) if self.debug: - self.put(self.bitseq_start, self.bitseq_end, self.out_ann, [ 15, [ '%d more' % self.proto_byte_count, '%d' % self.proto_byte_count ]]) - + self.put(self.bitseq_start, self.bitseq_end, self.out_ann, [15, ['%d more' % self.proto_byte_count, '%d' % self.proto_byte_count]]) def bitseq(self, bitstart, bitend, bit): if self.bitseq_len == 0: @@ -204,83 +181,79 @@ class Decoder(srd.Decoder): self.bitseq_value = 0 self.bitseq_dir = bit self.bitseq_len = 1 - self.put(bitstart, bitend, self.out_ann, [ 2 + self.bitseq_dir, [ 'start', 's' ]]) - + self.put(bitstart, bitend, self.out_ann, [2 + self.bitseq_dir, ['start', 's']]) elif (self.proto_state == 'CMD' and self.bitseq_len == 4) or (self.proto_state != 'CMD' and self.bitseq_len == 9): # Parity bit self.bitseq_end = bitstart self.bitseq_len += 1 - self.put(bitstart, bitend, self.out_ann, [ 4, [ 'parity', 'par', 'p' ]]) + self.put(bitstart, bitend, self.out_ann, [4, ['parity', 'par', 'p']]) # The start bit is not data but was used for parity calculation. self.bitseq_value &= 0xff - self.put(self.bitseq_start, self.bitseq_end, self.out_ann, [ 7 + self.bitseq_dir, [ '0x%02x' % self.bitseq_value, '%02x' % self.bitseq_value, '%x' % self.bitseq_value ]]) - + self.put(self.bitseq_start, self.bitseq_end, self.out_ann, [7 + self.bitseq_dir, ['0x%02x' % self.bitseq_value, '%02x' % self.bitseq_value, '%x' % self.bitseq_value]]) elif (self.proto_state == 'CMD' and self.bitseq_len == 5) or (self.proto_state != 'CMD' and self.bitseq_len == 10): # ACK/NACK bit. if bit: - self.put(bitstart, bitend, self.out_ann, [ 5, [ 'ack', 'a' ]]) + self.put(bitstart, bitend, self.out_ann, [5, ['ack', 'a']]) else: - self.put(bitstart, bitend, self.out_ann, [ 6, [ 'nack', 'n' ]]) + self.put(bitstart, bitend, self.out_ann, [6, ['nack', 'n']]) # We only pass data that was ack'd up the stack. if bit: self.protocol() self.bitseq_len = 0 - else: if self.bitseq_len == 1: self.bitseq_start = bitstart - self.bitseq_value = (self.bitseq_value << 1) | bit self.bitseq_len += 1 - def bit(self, start, mid, end): if mid - start >= end - mid: - self.put(start, end, self.out_ann, [ 0, [ '0' ]]) + self.put(start, end, self.out_ann, [0, ['0']]) bit = 0 else: - self.put(start, end, self.out_ann, [ 0, [ '1' ]]) + self.put(start, end, self.out_ann, [0, ['1']]) bit = 1 self.bitseq(start, end, bit) - def detect_synchronize_frame(self, start, end): - # Strictly speaking, synchronization frames are only recognised when SWIM is - # active. A falling edge on reset disables SWIM and an enter sequence is needed - # to re-enable it. However we do not want to be reliant on seeing the NRST pin - # just for that and we also want to be able to decode SWIM even if we just sample - # parts of the dialogue. For this reason we limit ourselves to only recognizing - # synchronization frames that have believable lengths based on our knowledge - # of the range of possible SWIM clocks. + # Strictly speaking, synchronization frames are only recognised when + # SWIM is active. A falling edge on reset disables SWIM and an enter + # sequence is needed to re-enable it. However we do not want to be + # reliant on seeing the NRST pin just for that and we also want to be + # able to decode SWIM even if we just sample parts of the dialogue. + # For this reason we limit ourselves to only recognizing + # synchronization frames that have believable lengths based on our + # knowledge of the range of possible SWIM clocks. if self.samplenum - self.eseq_edge[1][1] >= self.sync_reflen_min and self.samplenum - self.eseq_edge[1][1] <= self.sync_reflen_max: - self.put(self.eseq_edge[1][1], self.samplenum, self.out_ann, [ 1, [ 'synchronization frame', 'synchronization', 'sync', 's', ]]) + self.put(self.eseq_edge[1][1], self.samplenum, self.out_ann, [1, ['synchronization frame', 'synchronization', 'sync', 's']]) - # A low that lasts for more than 64 SWIM clock periods causes a reset of the SWIM - # communication state machine and will switch the SWIM to low-speed mode (SWIM_CSR.HS - # is cleared) + # A low that lasts for more than 64 SWIM clock periods causes a + # reset of the SWIM communication state machine and will switch + # the SWIM to low-speed mode (SWIM_CSR.HS is cleared). self.reset() - # The low SHOULD last 128 SWIM clocks. This is used to resynchronize in order to - # allow for variation in the frequency of the internal RC oscillator. + # The low SHOULD last 128 SWIM clocks. This is used to + # resynchronize in order to allow for variation in the frequency + # of the internal RC oscillator. self.swim_clock = 128 * (self.samplerate / (self.samplenum - self.eseq_edge[1][1])) self.adjust_timings() - def eseq_potential_start(self, start, end): self.eseq_pairstart = start self.eseq_reflen = end - start self.eseq_pairnum = 1 def detect_enter_sequence(self, start, end): - # According to the spec the enter sequence is four pulses at 2kHz followed by - # four at 1kHz. We do not check the frequency but simply check the lengths - # of successive pulses against the first. This means we have no need to account - # for the accuracy (or lack of) of the host's oscillator. + # According to the spec the enter sequence is four pulses at 2kHz + # followed by four at 1kHz. We do not check the frequency but simply + # check the lengths of successive pulses against the first. This means + # we have no need to account for the accuracy (or lack of) of the + # host's oscillator. if self.eseq_pairnum == 0 or abs(self.eseq_reflen - (end - start)) > 2: self.eseq_potential_start(start, end) @@ -290,37 +263,33 @@ class Decoder(srd.Decoder): if self.eseq_pairnum == 4: self.eseq_reflen /= 2 - else: - # The final four pulses should each be half the length of the initial - # pair. Again, a mismatch causes us to reset and use the current pulse - # as a new potential enter sequence start. + # The final four pulses should each be half the length of the + # initial pair. Again, a mismatch causes us to reset and use the + # current pulse as a new potential enter sequence start. self.eseq_pairnum += 1 if self.eseq_pairnum == 8: - # Four matching pulses followed by four more that match each other - # but are half the length of the first four. SWIM is active! - self.put(self.eseq_pairstart, end, self.out_ann, [ 1, [ 'enter sequence', 'enter seq', 'enter', 'ent', 'e' ]]) + # Four matching pulses followed by four more that match each + # other but are half the length of the first 4. SWIM is active! + self.put(self.eseq_pairstart, end, self.out_ann, [1, ['enter sequence', 'enter seq', 'enter', 'ent', 'e']]) self.eseq_pairnum = 0 - - def decode(self, ss, es, data): - for (self.samplenum, pins) in data: - (swim,) = pins - + def decode(self): + while True: if self.bit_maxlen >= 0: + (swim,) = self.wait() self.bit_maxlen -= 1 + else: + (swim,) = self.wait({0: 'e'}) if swim != self.eseq_edge[1][0]: if swim == 1 and self.eseq_edge[1][1] is not None: self.detect_synchronize_frame(self.eseq_edge[1][1], self.samplenum) - if self.eseq_edge[0][1] is not None: self.detect_enter_sequence(self.eseq_edge[0][1], self.samplenum) - self.eseq_edge.pop(0) self.eseq_edge.append([swim, self.samplenum]) - if (swim != self.bit_edge[1][0] and (swim != 1 or self.bit_edge[1][0] != -1)) or self.bit_maxlen == 0: if self.bit_maxlen == 0 and self.bit_edge[1][0] == 1: swim = -1 @@ -333,14 +302,3 @@ class Decoder(srd.Decoder): self.bit_edge.pop(0) self.bit_edge.append([swim, self.samplenum]) - - - if self.bit_maxlen >= 0: - data.logic_mask = 0 - data.edge_index = 0 - data.itercnt += 1 - else: - data.exp_logic = 0b1 - data.logic_mask = 0b1 - data.edge_index = -1 - data.cur_pos = self.samplenum diff --git a/libsigrokdecode4DSL/decoders/t55xx/__init__.py b/libsigrokdecode4DSL/decoders/t55xx/__init__.py index 8f0f8a40..01184350 100755 --- a/libsigrokdecode4DSL/decoders/t55xx/__init__.py +++ b/libsigrokdecode4DSL/decoders/t55xx/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/t55xx/pd.py b/libsigrokdecode4DSL/decoders/t55xx/pd.py index 4423a045..d345d318 100755 --- a/libsigrokdecode4DSL/decoders/t55xx/pd.py +++ b/libsigrokdecode4DSL/decoders/t55xx/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -24,14 +23,15 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 't55xx' name = 'T55xx' longname = 'RFID T55xx' desc = 'T55xx 100-150kHz RFID protocol.' license = 'gplv2+' inputs = ['logic'] - outputs = ['t55xx'] + outputs = [] + tags = ['IC', 'RFID'] channels = ( {'id': 'data', 'name': 'Data', 'desc': 'Data line'}, ) @@ -67,8 +67,10 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None - self.oldpin = None self.last_samplenum = None self.lastlast_samplenum = None self.state = 'START_GAP' @@ -255,78 +257,70 @@ class Decoder(srd.Decoder): self.bits_pos[self.bit_nr][2] = bit_end self.bit_nr += 1 - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - for (self.samplenum, (pin,)) in data: - data.itercnt += 1 - # Ignore identical samples early on (for performance reasons). - if self.oldpin == pin: - continue - if self.oldpin is None: - self.oldpin = pin - self.last_samplenum = self.samplenum - self.lastlast_samplenum = self.samplenum - self.last_edge = self.samplenum - self.oldpl = 0 - self.oldpp = 0 - self.oldsamplenum = 0 - self.last_bit_pos = 0 + self.last_samplenum = 0 + self.lastlast_samplenum = 0 + self.last_edge = 0 + self.oldpl = 0 + self.oldpp = 0 + self.oldsamplenum = 0 + self.last_bit_pos = 0 + self.old_gap_start = 0 + self.old_gap_end = 0 + self.gap_detected = 0 + self.bit_nr = 0 - self.old_gap_start = 0 - self.old_gap_end = 0 - self.gap_detected = 0 - self.bit_nr = 0 - continue + while True: + (pin,) = self.wait({0: 'e'}) - if self.oldpin != pin: - pl = self.samplenum - self.oldsamplenum - pp = pin - samples = self.samplenum - self.last_samplenum + pl = self.samplenum - self.oldsamplenum + pp = pin + samples = self.samplenum - self.last_samplenum - if self.state == 'WRITE_GAP': - if pl > self.writegap: - self.gap_detected = 1 - self.put(self.last_samplenum, self.samplenum, - self.out_ann, [2, ['Write gap']]) - if (self.last_samplenum-self.old_gap_end) > self.nogap: - self.gap_detected = 0 - self.state = 'START_GAP' - self.put(self.old_gap_end, self.last_samplenum, - self.out_ann, [3, ['Write mode exit']]) - self.put_fields() - - if self.state == 'START_GAP': - if pl > self.startgap: - self.gap_detected = 1 - self.put(self.last_samplenum, self.samplenum, - self.out_ann, [1, ['Start gap']]) - self.state = 'WRITE_GAP' - - if self.gap_detected == 1: + if self.state == 'WRITE_GAP': + if pl > self.writegap: + self.gap_detected = 1 + self.put(self.last_samplenum, self.samplenum, + self.out_ann, [2, ['Write gap']]) + if (self.last_samplenum-self.old_gap_end) > self.nogap: self.gap_detected = 0 - if (self.last_samplenum - self.old_gap_end) > self.wzmin \ - and (self.last_samplenum - self.old_gap_end) < self.wzmax: - self.put(self.old_gap_end, self.last_samplenum, - self.out_ann, [0, ['0']]) - self.put(self.old_gap_end, self.last_samplenum, - self.out_ann, [4, ['Bit']]) - self.add_bits_pos(0, self.old_gap_end, - self.last_samplenum) - if (self.last_samplenum - self.old_gap_end) > self.womin \ - and (self.last_samplenum - self.old_gap_end) < self.womax: - self.put(self.old_gap_end, self.last_samplenum, - self.out_ann, [0, ['1']]) - self.put(self.old_gap_end, self.last_samplenum, - self.out_ann, [4, ['Bit']]) - self.add_bits_pos(1, self.old_gap_end, self.last_samplenum) + self.state = 'START_GAP' + self.put(self.old_gap_end, self.last_samplenum, + self.out_ann, [3, ['Write mode exit']]) + self.put_fields() - self.old_gap_start = self.last_samplenum - self.old_gap_end = self.samplenum + if self.state == 'START_GAP': + if pl > self.startgap: + self.gap_detected = 1 + self.put(self.last_samplenum, self.samplenum, + self.out_ann, [1, ['Start gap']]) + self.state = 'WRITE_GAP' - self.oldpl = pl - self.oldpp = pp - self.oldsamplenum = self.samplenum - self.last_samplenum = self.samplenum - self.oldpin = pin + if self.gap_detected == 1: + self.gap_detected = 0 + if (self.last_samplenum - self.old_gap_end) > self.wzmin \ + and (self.last_samplenum - self.old_gap_end) < self.wzmax: + self.put(self.old_gap_end, self.last_samplenum, + self.out_ann, [0, ['0']]) + self.put(self.old_gap_end, self.last_samplenum, + self.out_ann, [4, ['Bit']]) + self.add_bits_pos(0, self.old_gap_end, + self.last_samplenum) + if (self.last_samplenum - self.old_gap_end) > self.womin \ + and (self.last_samplenum - self.old_gap_end) < self.womax: + self.put(self.old_gap_end, self.last_samplenum, + self.out_ann, [0, ['1']]) + self.put(self.old_gap_end, self.last_samplenum, + self.out_ann, [4, ['Bit']]) + self.add_bits_pos(1, self.old_gap_end, self.last_samplenum) + + self.old_gap_start = self.last_samplenum + self.old_gap_end = self.samplenum + + self.oldpl = pl + self.oldpp = pp + self.oldsamplenum = self.samplenum + self.last_samplenum = self.samplenum diff --git a/libsigrokdecode4DSL/decoders/tca6408a/__init__.py b/libsigrokdecode4DSL/decoders/tca6408a/__init__.py index f679674e..5373c311 100755 --- a/libsigrokdecode4DSL/decoders/tca6408a/__init__.py +++ b/libsigrokdecode4DSL/decoders/tca6408a/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/tca6408a/pd.py b/libsigrokdecode4DSL/decoders/tca6408a/pd.py index a794547c..49245174 100755 --- a/libsigrokdecode4DSL/decoders/tca6408a/pd.py +++ b/libsigrokdecode4DSL/decoders/tca6408a/pd.py @@ -16,21 +16,21 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'tca6408a' name = 'TI TCA6408A' longname = 'Texas Instruments TCA6408A' desc = 'Texas Instruments TCA6408A 8-bit I²C I/O expander.' license = 'gplv2+' inputs = ['i2c'] - outputs = ['tca6408a'] + outputs = [] + tags = ['Embedded/industrial', 'IC'] annotations = ( ('register', 'Register type'), ('value', 'Register value'), @@ -42,6 +42,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.chip = -1 @@ -92,7 +95,7 @@ class Decoder(srd.Decoder): return self.state = 'GET SLAVE ADDR' elif self.state == 'GET SLAVE ADDR': - self.chip = databyte + self.chip = databyte self.state = 'GET REG ADDR' elif self.state == 'GET REG ADDR': # Wait for a data write (master selects the slave register). diff --git a/libsigrokdecode4DSL/decoders/timing/__init__.py b/libsigrokdecode4DSL/decoders/timing/__init__.py index ee315096..179487b3 100755 --- a/libsigrokdecode4DSL/decoders/timing/__init__.py +++ b/libsigrokdecode4DSL/decoders/timing/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/timing/pd.py b/libsigrokdecode4DSL/decoders/timing/pd.py index b558fa28..20ca2c4e 100755 --- a/libsigrokdecode4DSL/decoders/timing/pd.py +++ b/libsigrokdecode4DSL/decoders/timing/pd.py @@ -15,8 +15,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -26,19 +25,19 @@ class SamplerateError(Exception): pass def normalize_time(t): - if t >= 1.0: + if abs(t) >= 1.0: return '%.3f s (%.3f Hz)' % (t, (1/t)) - elif t >= 0.001: + elif abs(t) >= 0.001: if 1/t/1000 < 1: return '%.3f ms (%.3f Hz)' % (t * 1000.0, (1/t)) else: return '%.3f ms (%.3f kHz)' % (t * 1000.0, (1/t)/1000) - elif t >= 0.000001: + elif abs(t) >= 0.000001: if 1/t/1000/1000 < 1: return '%.3f μs (%.3f kHz)' % (t * 1000.0 * 1000.0, (1/t)/1000) else: return '%.3f μs (%.3f MHz)' % (t * 1000.0 * 1000.0, (1/t)/1000/1000) - elif t >= 0.000000001: + elif abs(t) >= 0.000000001: if 1/t/1000/1000/1000: return '%.3f ns (%.3f MHz)' % (t * 1000.0 * 1000.0 * 1000.0, (1/t)/1000/1000) else: @@ -47,35 +46,44 @@ def normalize_time(t): return '%f' % t class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'timing' name = 'Timing' longname = 'Timing calculation with frequency and averaging' desc = 'Calculate time between edges.' license = 'gplv2+' inputs = ['logic'] - outputs = ['timing'] + outputs = [] + tags = ['Clock/timing', 'Util'] channels = ( {'id': 'data', 'name': 'Data', 'desc': 'Data line'}, ) annotations = ( ('time', 'Time'), ('average', 'Average'), + ('delta', 'Delta'), ) annotation_rows = ( ('time', 'Time', (0,)), ('average', 'Average', (1,)), + ('delta', 'Delta', (2,)), ) options = ( { 'id': 'avg_period', 'desc': 'Averaging period', 'default': 100 }, + { 'id': 'edge', 'desc': 'Edges to check', 'default': 'any', 'values': ('any', 'rising', 'falling') }, + { 'id': 'delta', 'desc': 'Show delta from last', 'default': 'no', 'values': ('yes', 'no') }, ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None - self.oldpin = None self.last_samplenum = None self.last_n = deque() self.chunks = 0 + self.level_changed = False + self.last_t = None def metadata(self, key, value): if key == srd.SRD_CONF_SAMPLERATE: @@ -83,42 +91,38 @@ class Decoder(srd.Decoder): def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) + self.edge = self.options['edge'] - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') + while True: + if self.edge == 'rising': + self.wait({0: 'r'}) + elif self.edge == 'falling': + self.wait({0: 'f'}) + else: + self.wait({0: 'e'}) - for (self.samplenum, (pin,)) in data: - data.itercnt += 1 - if self.oldpin is None: - self.oldpin = pin + if not self.last_samplenum: self.last_samplenum = self.samplenum continue + samples = self.samplenum - self.last_samplenum + t = samples / self.samplerate - if self.oldpin != pin: - samples = self.samplenum - self.last_samplenum - t = samples / self.samplerate - self.chunks += 1 + if t > 0: + self.last_n.append(t) + if len(self.last_n) > self.options['avg_period']: + self.last_n.popleft() - # Don't insert the first chunk into the averaging as it is - # not complete probably. - if self.last_samplenum is None or self.chunks < 2: - # Report the timing normalized. - self.put(self.last_samplenum, self.samplenum, self.out_ann, - [0, [normalize_time(t)]]) - else: - if t > 0: - self.last_n.append(t) + self.put(self.last_samplenum, self.samplenum, self.out_ann, + [0, [normalize_time(t)]]) + if self.options['avg_period'] > 0: + self.put(self.last_samplenum, self.samplenum, self.out_ann, + [1, [normalize_time(sum(self.last_n) / len(self.last_n))]]) + if self.last_t and self.options['delta'] == 'yes': + self.put(self.last_samplenum, self.samplenum, self.out_ann, + [2, [normalize_time(t - self.last_t)]]) - if len(self.last_n) > self.options['avg_period']: - self.last_n.popleft() - - # Report the timing normalized. - self.put(self.last_samplenum, self.samplenum, self.out_ann, - [0, [normalize_time(t)]]) - self.put(self.last_samplenum, self.samplenum, self.out_ann, - [1, [normalize_time(sum(self.last_n) / len(self.last_n))]]) - - # Store data for next round. - self.last_samplenum = self.samplenum - self.oldpin = pin + self.last_t = t + self.last_samplenum = self.samplenum diff --git a/libsigrokdecode4DSL/decoders/tlc5620/__init__.py b/libsigrokdecode4DSL/decoders/tlc5620/__init__.py index cb953853..a642ef64 100755 --- a/libsigrokdecode4DSL/decoders/tlc5620/__init__.py +++ b/libsigrokdecode4DSL/decoders/tlc5620/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/tlc5620/pd.py b/libsigrokdecode4DSL/decoders/tlc5620/pd.py index 1ab83d17..eec040bc 100755 --- a/libsigrokdecode4DSL/decoders/tlc5620/pd.py +++ b/libsigrokdecode4DSL/decoders/tlc5620/pd.py @@ -2,6 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2012-2015 Uwe Hermann +## Copyright (C) 2019 DreamSourceLab ## ## 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 @@ -14,8 +15,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -28,14 +28,15 @@ dacs = { } class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'tlc5620' name = 'TI TLC5620' longname = 'Texas Instruments TLC5620' desc = 'Texas Instruments TLC5620 8-bit quad DAC.' license = 'gplv2+' inputs = ['logic'] - outputs = ['tlc5620'] + outputs = [] + tags = ['IC', 'Analog/digital'] channels = ( {'id': 'clk', 'name': 'CLK', 'desc': 'Serial interface clock'}, {'id': 'data', 'name': 'DATA', 'desc': 'Serial interface data'}, @@ -72,7 +73,9 @@ class Decoder(srd.Decoder): ) def __init__(self): - self.oldpins = self.oldclk = self.oldload = self.oldldac = None + self.reset() + + def reset(self): self.bits = [] self.ss_dac_first = None self.ss_dac = self.es_dac = 0 @@ -183,28 +186,25 @@ class Decoder(srd.Decoder): [8, ['Updating voltages: %s' % s, s, s.replace('DAC', '')]]) self.ss_dac_first = None - def handle_new_dac_bit(self): - self.bits.append([self.datapin, self.samplenum]) - - def decode(self, ss, es, data): - for (self.samplenum, pins) in data: - data.itercnt += 1 - # Ignore identical samples early on (for performance reasons). - if self.oldpins == pins: - continue - self.oldpins, (clk, self.datapin, load, ldac) = pins, pins - self.ldac = ldac + def handle_new_dac_bit(self, datapin): + self.bits.append([datapin, self.samplenum]) + def decode(self): + while True: # DATA is shifted in the DAC on the falling CLK edge (MSB-first). # A falling edge of LOAD will latch the data. - if self.oldload == 1 and load == 0: - self.handle_falling_edge_load() - if self.oldldac == 1 and ldac == 0: - self.handle_falling_edge_ldac() - if self.oldclk == 1 and clk == 0: - self.handle_new_dac_bit() + # Wait for one (or multiple) of the following conditions: + # a) Falling edge on CLK, and/or + # b) Falling edge on LOAD, and/or + # b) Falling edge on LDAC + (clk, data, load, ldac) = self.wait([{0: 'f'}, {2: 'f'}, {3: 'f'}]) + self.ldac = ldac - self.oldclk = clk - self.oldload = load - self.oldldac = ldac + # Handle those conditions (one or more) that matched this time. + if (self.matched & (0b1 << 0)): + self.handle_new_dac_bit(data) + if (self.matched & (0b1 << 1)): + self.handle_falling_edge_load() + if (self.matched & (0b1 << 2)): + self.handle_falling_edge_ldac() diff --git a/libsigrokdecode4DSL/decoders/usb_packet/__init__.py b/libsigrokdecode4DSL/decoders/usb_packet/__init__.py index f841d20b..5cd7c56b 100755 --- a/libsigrokdecode4DSL/decoders/usb_packet/__init__.py +++ b/libsigrokdecode4DSL/decoders/usb_packet/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/usb_packet/pd.py b/libsigrokdecode4DSL/decoders/usb_packet/pd.py index c1592025..e262074e 100755 --- a/libsigrokdecode4DSL/decoders/usb_packet/pd.py +++ b/libsigrokdecode4DSL/decoders/usb_packet/pd.py @@ -15,8 +15,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -148,7 +147,7 @@ def reverse_number(num, count): out = list(count * '0') for i in range(0, count): if num >> i & 1: - out[i] = '1'; + out[i] = '1' return int(''.join(out), 2) def calc_crc5(bitstr): @@ -174,7 +173,7 @@ def calc_crc16(bitstr): return reverse_number(crc16, 16) class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'usb_packet' name = 'USB packet' longname = 'Universal Serial Bus (LS/FS) packet' @@ -182,6 +181,7 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['usb_signalling'] outputs = ['usb_packet'] + tags = ['PC'] options = ( {'id': 'signalling', 'desc': 'Signalling', 'default': 'full-speed', 'values': ('full-speed', 'low-speed')}, @@ -223,6 +223,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.bits = [] self.packet = [] self.packet_summary = '' diff --git a/libsigrokdecode4DSL/decoders/usb_power_delivery/__init__.py b/libsigrokdecode4DSL/decoders/usb_power_delivery/__init__.py index 4ba626a8..43dfd5d6 100755 --- a/libsigrokdecode4DSL/decoders/usb_power_delivery/__init__.py +++ b/libsigrokdecode4DSL/decoders/usb_power_delivery/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/usb_power_delivery/pd.py b/libsigrokdecode4DSL/decoders/usb_power_delivery/pd.py index 77d8fbf8..45077f27 100755 --- a/libsigrokdecode4DSL/decoders/usb_power_delivery/pd.py +++ b/libsigrokdecode4DSL/decoders/usb_power_delivery/pd.py @@ -2,6 +2,8 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2015 Google, Inc +## Copyright (C) 2018 davidanger +## Copyright (C) 2018 Peter Hazenberg ## ## 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 @@ -14,8 +16,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -45,7 +46,13 @@ CTRL_TYPES = { 12: 'WAIT', 13: 'SOFT RESET', 14: 'reserved', - 15: 'reserved' + 15: 'reserved', + 16: 'Not Supported', + 17: 'Get_Source_Cap_Extended', + 18: 'Get_Status', + 19: 'FR_Swap', + 20: 'Get_PPS_Status', + 21: 'Get_Country_Codes', } # Data message type @@ -54,6 +61,9 @@ DATA_TYPES = { 2: 'REQUEST', 3: 'BIST', 4: 'SINK CAP', + 5: 'Battery_Status', + 6: 'Alert', + 7: 'Get_Country_Info', 15: 'VDM' } @@ -102,14 +112,23 @@ EOP = 0x16 SYNC_CODES = [SYNC1, SYNC2, SYNC3] HRST_CODES = [RST1, RST1, RST1, RST2] +SOP_SEQUENCES = [ + (SYNC1, SYNC1, SYNC1, SYNC2), + (SYNC1, SYNC1, SYNC3, SYNC3), + (SYNC1, SYNC3, SYNC1, SYNC3), + (SYNC1, RST2, RST2, SYNC3), + (SYNC1, RST2, SYNC3, SYNC2), + (RST1, SYNC1, RST1, SYNC3), + (RST1, RST1, RST1, RST2), +] START_OF_PACKETS = { - (SYNC1, SYNC1, SYNC1, SYNC2): 'SOP', - (SYNC1, SYNC1, SYNC3, SYNC3): "SOP'", - (SYNC1, SYNC3, SYNC1, SYNC3): 'SOP"', - (SYNC1, RST2, RST2, SYNC3): "SOP' Debug", - (SYNC1, RST2, SYNC3, SYNC2): 'SOP" Debug', - (RST1, SYNC1, RST1, SYNC3): 'Cable Reset', - (RST1, RST1, RST1, RST2): 'Hard Reset', + SOP_SEQUENCES[0]: 'SOP', + SOP_SEQUENCES[1]: "SOP'", + SOP_SEQUENCES[2]: 'SOP"', + SOP_SEQUENCES[3]: "SOP' Debug", + SOP_SEQUENCES[4]: 'SOP" Debug', + SOP_SEQUENCES[5]: 'Cable Reset', + SOP_SEQUENCES[6]: 'Hard Reset', } SYM_NAME = [ @@ -139,19 +158,12 @@ SYM_NAME = [ ] RDO_FLAGS = { + (1 << 23): 'unchunked', (1 << 24): 'no_suspend', (1 << 25): 'comm_cap', (1 << 26): 'cap_mismatch', (1 << 27): 'give_back' } -PDO_TYPE = ['', 'BATT:', 'VAR:', ''] -PDO_FLAGS = { - (1 << 29): 'dual_role_power', - (1 << 28): 'suspend', - (1 << 27): 'ext', - (1 << 26): 'comm_cap', - (1 << 25): 'dual_role_data' -} BIST_MODES = { 0: 'Receiver', @@ -178,11 +190,12 @@ VDM_CMDS = { } VDM_ACK = ['REQ', 'ACK', 'NAK', 'BSY'] + class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'usb_power_delivery' name = 'USB PD' longname = 'USB Power Delivery' @@ -190,22 +203,26 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['logic'] outputs = ['usb_pd'] + tags = ['PC'] channels = ( - {'id': 'cc', 'name': 'CC', 'desc': 'Control channel'}, + {'id': 'cc1', 'name': 'CC1', 'desc': 'Configuration Channel 1'}, + ) + optional_channels = ( + {'id': 'cc2', 'name': 'CC2', 'desc': 'Configuration Channel 2'}, ) options = ( - {'id': 'fulltext', 'desc': 'full text decoding of the packet', + {'id': 'fulltext', 'desc': 'Full text decoding of packets', 'default': 'no', 'values': ('yes', 'no')}, ) annotations = ( ('type', 'Packet Type'), - ('Preamble', 'Preamble'), - ('SOP', 'Start of Packet'), - ('Head', 'Header'), - ('Data', 'Data'), - ('CRC', 'Checksum'), - ('EOP', 'End Of Packet'), - ('Sym', '4b5b symbols'), + ('preamble', 'Preamble'), + ('sop', 'Start of Packet'), + ('header', 'Header'), + ('data', 'Data'), + ('crc', 'Checksum'), + ('eop', 'End Of Packet'), + ('sym', '4b5b symbols'), ('warnings', 'Warnings'), ('src', 'Source Message'), ('snk', 'Sink Message'), @@ -213,81 +230,128 @@ class Decoder(srd.Decoder): ('text', 'Plain text'), ) annotation_rows = ( - ('4B5B', 'symbols', (7, )), - ('Phase', 'parts', (1, 2, 3, 4, 5, 6, )), - ('payload', 'Payload', (11, )), - ('type', 'Type', (0, 9, 10, )), - ('warnings', 'Warnings', (8, )), - ('text', 'Full text', (12, )), + ('4b5b', 'Symbols', (7,)), + ('phase', 'Parts', (1, 2, 3, 4, 5, 6)), + ('payload', 'Payload', (11,)), + ('type', 'Type', (0, 9, 10)), + ('warnings', 'Warnings', (8,)), + ('text', 'Full text', (12,)), ) binary = ( ('raw-data', 'RAW binary data'), ) + stored_pdos = {} + def get_request(self, rdo): pos = (rdo >> 28) & 7 - op_ma = ((rdo >> 10) & 0x3ff) * 10 - max_ma = (rdo & 0x3ff) * 10 - flags = '' - for f in RDO_FLAGS.keys(): + + op_ma = ((rdo >> 10) & 0x3ff) * 0.01 + max_ma = (rdo & 0x3ff) * 0.01 + + mark = self.cap_mark[pos] + if mark == 3: + op_v = ((rdo >> 9) & 0x7ff) * 0.02 + op_a = (rdo & 0x3f) * 0.05 + t_settings = '%gV %gA' % (op_v, op_a) + elif mark == 2: + op_w = ((rdo >> 10) & 0x3ff) * 0.25 + mp_w = (rdo & 0x3ff) * 0.25 + t_settings = '%gW (operating)' % op_w + else: + op_a = ((rdo >> 10) & 0x3ff) * 0.01 + max_a = (rdo & 0x3ff) * 0.01 + t_settings = '%gA (operating) / %gA (max)' % (op_a, max_a) + + t_flags = '' + for f in sorted(RDO_FLAGS.keys(), reverse = True): if rdo & f: - flags += ' ' + RDO_FLAGS[f] - return '[%d]%d/%d mA%s' % (pos, op_ma, max_ma, flags) + t_flags += ' [' + RDO_FLAGS[f] + ']' - def get_source_cap(self, pdo): - t = (pdo >> 30) & 3 - if t == 0: - mv = ((pdo >> 10) & 0x3ff) * 50 - ma = ((pdo >> 0) & 0x3ff) * 10 - p = '%.1fV %.1fA' % (mv/1000.0, ma/1000.0) - elif t == 1: - minv = ((pdo >> 10) & 0x3ff) * 50 - maxv = ((pdo >> 20) & 0x3ff) * 50 - mw = ((pdo >> 0) & 0x3ff) * 250 - p = '%.1f/%.1fV %.1fW' % (minv/1000.0, maxv/1000.0, mw/1000.0) - elif t == 2: - minv = ((pdo >> 10) & 0x3ff) * 50 - maxv = ((pdo >> 20) & 0x3ff) * 50 - ma = ((pdo >> 0) & 0x3ff) * 10 - p = '%.1f/%.1fV %.1fA' % (minv/1000.0, maxv/1000.0, ma/1000.0) + if pos in self.stored_pdos.keys(): + t_pdo = '#%d: %s' % (pos, self.stored_pdos[pos]) else: - p = '' - flags = '' - for f in PDO_FLAGS.keys(): - if pdo & f: - flags += ' ' + PDO_FLAGS[f] - return '%s%s%s' % (PDO_TYPE[t], p, flags) + t_pdo = '#%d' % (pos) - def get_sink_cap(self, pdo): - t = (pdo >> 30) & 3 - if t == 0: - mv = ((pdo >> 10) & 0x3ff) * 50 - ma = ((pdo >> 0) & 0x3ff) * 10 - p = '%.1fV %.1fA' % (mv/1000.0, ma/1000.0) - elif t == 1: - minv = ((pdo >> 10) & 0x3ff) * 50 - maxv = ((pdo >> 20) & 0x3ff) * 50 - mw = ((pdo >> 0) & 0x3ff) * 250 - p = '%.1f/%.1fV %.1fW' % (minv/1000.0, maxv/1000.0, mw/1000.0) - elif t == 2: - minv = ((pdo >> 10) & 0x3ff) * 50 - maxv = ((pdo >> 20) & 0x3ff) * 50 - ma = ((pdo >> 0) & 0x3ff) * 10 - p = '%.1f/%.1fV %.1fA' % (minv/1000.0, maxv/1000.0, ma/1000.0) - else: - p = '' - flags = '' - for f in PDO_FLAGS.keys(): + return '(PDO %s) %s%s' % (t_pdo, t_settings, t_flags) + + def get_source_sink_cap(self, pdo, idx, source): + t1 = (pdo >> 30) & 3 + self.cap_mark[idx] = t1 + + flags = {} + if t1 == 0: + t_name = 'Fixed' + if source: + flags = { + (1 << 29): 'dual_role_power', + (1 << 28): 'suspend', + (1 << 27): 'unconstrained', + (1 << 26): 'comm_cap', + (1 << 25): 'dual_role_data', + (1 << 24): 'unchunked', + } + else: # Sink + flags = { + (1 << 29): 'dual_role_power', + (1 << 28): 'high_capability', + (1 << 27): 'unconstrained', + (1 << 26): 'comm_cap', + (1 << 25): 'dual_role_data', + (0b01 << 23): 'fr_swap default power', + (0b10 << 23): 'fr_swap 1.5 A', + (0b11 << 23): 'fr_swap 3.0 A', + } + mv = ((pdo >> 10) & 0x3ff) * 0.05 + ma = ((pdo >> 0) & 0x3ff) * 0.01 + p = '%gV %gA (%gW)' % (mv, ma, mv*ma) + self.stored_pdos[idx] = '%s %gV' % (t_name, mv) + elif t1 == 1: + t_name = 'Battery' + flags = {} # No flags defined for Battery PDO in PD 3.0 spec + minv = ((pdo >> 10) & 0x3ff) * 0.05 + maxv = ((pdo >> 20) & 0x3ff) * 0.05 + mw = ((pdo >> 0) & 0x3ff) * 0.25 + p = '%g/%gV %gW' % (minv, maxv, mw) + self.stored_pdos[idx] = '%s %g/%gV' % (t_name, minv, maxv) + elif t1 == 2: + t_name = 'Variable' + flags = {} # No flags defined for Variable PDO in PD 3.0 spec + minv = ((pdo >> 10) & 0x3ff) * 0.05 + maxv = ((pdo >> 20) & 0x3ff) * 0.05 + ma = ((pdo >> 0) & 0x3ff) * 0.01 + p = '%g/%gV %gA' % (minv, maxv, ma) + self.stored_pdos[idx] = '%s %g/%gV' % (t_name, minv, maxv) + elif t1 == 3: + t2 = (pdo >> 28) & 3 + if t2 == 0: + t_name = 'Programmable|PPS' + flags = { + (1 << 29): 'power_limited', + } + minv = ((pdo >> 8) & 0xff) * 0.1 + maxv = ((pdo >> 17) & 0xff) * 0.1 + ma = ((pdo >> 0) & 0xff) * 0.05 + p = '%g/%gV %gA' % (minv, maxv, ma) + if (pdo >> 27) & 0x1: + p += ' [limited]' + self.stored_pdos[idx] = '%s %g/%gV' % (t_name, minv, maxv) + else: + t_name = 'Reserved APDO: '+bin(t2) + p = '[raw: %s]' % (bin(pdo)) + self.stored_pdos[idx] = '%s %s' % (t_name, p) + t_flags = '' + for f in sorted(flags.keys(), reverse = True): if pdo & f: - flags += ' ' + PDO_FLAGS[f] - return '%s%s%s' % (PDO_TYPE[t], p, flags) + t_flags += ' [' + flags[f] + ']' + return '[%s] %s%s' % (t_name, p, t_flags) def get_vdm(self, idx, data): - if idx == 0: # VDM header + if idx == 0: # VDM header vid = data >> 16 struct = data & (1 << 15) txt = 'VDM' - if struct: # Structured VDM + if struct: # Structured VDM cmd = data & 0x1f src = data & (1 << 5) ack = (data >> 6) & 3 @@ -296,10 +360,10 @@ class Decoder(srd.Decoder): txt = VDM_ACK[ack] + ' ' txt += VDM_CMDS[cmd] if cmd in VDM_CMDS else 'cmd?' txt += ' pos %d' % (pos) if pos else ' ' - else: # Unstructured VDM + else: # Unstructured VDM txt = 'unstruct [%04x]' % (data & 0x7fff) txt += ' SVID:%04x' % (vid) - else: # VDM payload + else: # VDM payload txt = 'VDO:%08x' % (data) return txt @@ -309,22 +373,20 @@ class Decoder(srd.Decoder): mode_name = BIST_MODES[mode] if mode in BIST_MODES else 'INVALID' if mode == 2: mode_name = 'Counter[= %d]' % (counter) - # TODO check all 0 bits are 0 / emit warnings + # TODO: Check all 0 bits are 0 / emit warnings. return 'mode %s' % (mode_name) if idx == 0 else 'invalid BRO' def putpayload(self, s0, s1, idx): t = self.head_type() - txt = '???' + txt = '['+str(idx+1)+'] ' if t == 2: - txt = self.get_request(self.data[idx]) - elif t == 1: - txt = self.get_source_cap(self.data[idx]) - elif t == 4: - txt = self.get_sink_cap(self.data[idx]) + txt += self.get_request(self.data[idx]) + elif t == 1 or t == 4: + txt += self.get_source_sink_cap(self.data[idx], idx+1, t==1) elif t == 15: - txt = self.get_vdm(idx, self.data[idx]) + txt += self.get_vdm(idx, self.data[idx]) elif t == 3: - txt = self.get_bist(idx, self.data[idx]) + txt += self.get_bist(idx, self.data[idx]) self.putx(s0, s1, [11, [txt, txt]]) self.text += ' - ' + txt @@ -339,7 +401,7 @@ class Decoder(srd.Decoder): else: shortm = DATA_TYPES[t] if t in DATA_TYPES else 'DAT???' - longm = '{:s}[{:d}]:{:s}'.format(role, self.head_id(), shortm) + longm = '(r{:d}) {:s}[{:d}]: {:s}'.format(self.head_rev(), role, self.head_id(), shortm) self.putx(0, -1, [ann_type, [longm, shortm]]) self.text += longm @@ -385,13 +447,13 @@ class Decoder(srd.Decoder): def get_short(self): i = self.idx - # Check it's not a truncated packet + # Check it's not a truncated packet. if len(self.bits) - i <= 20: self.putwarn('Truncated', '!') return 0x0BAD k = [self.get_sym(i), self.get_sym(i+5), self.get_sym(i+10), self.get_sym(i+15)] - # TODO check bad symbols + # TODO: Check bad symbols. val = k[0] | (k[1] << 4) | (k[2] << 8) | (k[3] << 12) self.idx += 20 return val @@ -404,7 +466,7 @@ class Decoder(srd.Decoder): def find_corrupted_sop(self, k): # Start of packet are valid even if they have only 3 correct symbols # out of 4. - for seq in START_OF_PACKETS.keys(): + for seq in SOP_SEQUENCES: if [k[i] == seq[i] for i in range(len(k))].count(True) >= 3: return START_OF_PACKETS[seq] return None @@ -413,56 +475,58 @@ class Decoder(srd.Decoder): for i in range(len(self.bits) - 19): k = (self.get_sym(i, rec=False), self.get_sym(i+5, rec=False), self.get_sym(i+10, rec=False), self.get_sym(i+15, rec=False)) - sym = START_OF_PACKETS[k] if k in START_OF_PACKETS else None + sym = START_OF_PACKETS.get(k, None) if not sym: sym = self.find_corrupted_sop(k) - # We have an interesting symbol sequence + # We have an interesting symbol sequence. if sym: - # annotate the preamble + # Annotate the preamble. self.putx(0, i, [1, ['Preamble', '...']]) - # annotate each symbol + # Annotate each symbol. self.rec_sym(i, k[0]) self.rec_sym(i+5, k[1]) self.rec_sym(i+10, k[2]) self.rec_sym(i+15, k[3]) if sym == 'Hard Reset': self.text += 'HRST' - return -1 # Hard reset + return -1 # Hard reset elif sym == 'Cable Reset': self.text += 'CRST' - return -1 # Cable reset + return -1 # Cable reset else: self.putx(i, i+20, [2, [sym, 'S']]) return i+20 self.putx(0, len(self.bits), [1, ['Junk???', 'XXX']]) self.text += 'Junk???' self.putwarn('No start of packet found', 'XXX') - return -1 # No Start Of Packet + return -1 # No Start Of Packet def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.idx = 0 self.packet_seq = 0 - self.samplenum = 0 self.previous = 0 - self.oldpins = [0] self.startsample = None self.bits = [] self.edges = [] self.bad = [] self.half_one = False self.start_one = 0 + self.stored_pdos = {} + self.cap_mark = [0, 0, 0, 0, 0, 0, 0, 0] def metadata(self, key, value): if key == srd.SRD_CONF_SAMPLERATE: self.samplerate = value - # 0 is 2 UI, space larger than 1.5x 0 is definitely wrong + # 0 is 2 UI, space larger than 1.5x 0 is definitely wrong. self.maxbit = self.us2samples(3 * UI_US) - # duration threshold between half 1 and 0 + # Duration threshold between half 1 and 0. self.threshold = self.us2samples(THRESHOLD_US) def start(self): - self.out_python = self.register(srd.OUTPUT_PYTHON) self.out_ann = self.register(srd.OUTPUT_ANN) self.out_binary = self.register(srd.OUTPUT_BINARY) self.out_bitrate = self.register( @@ -471,8 +535,6 @@ class Decoder(srd.Decoder): ) def us2samples(self, us): - if not self.samplerate: - raise SamplerateError('Need the samplerate.') return int(us * self.samplerate / 1000000) def decode_packet(self): @@ -481,7 +543,7 @@ class Decoder(srd.Decoder): self.text = '' if len(self.edges) < 50: - return # Not a real PD packet + return # Not a real PD packet self.packet_seq += 1 tstamp = float(self.startsample) / self.samplerate @@ -489,9 +551,9 @@ class Decoder(srd.Decoder): self.idx = self.scan_eop() if self.idx < 0: - # Full text trace of the issue + # Full text trace of the issue. self.putx(0, self.idx, [12, [self.text, '...']]) - return # No real packet: ABORT + return # No real packet: ABORT. # Packet header self.head = self.get_short() @@ -527,20 +589,15 @@ class Decoder(srd.Decoder): bitrate = self.samplerate*len(self.bits) / float(es - ss) self.put(es, ss, self.out_bitrate, int(bitrate)) # Raw binary data (BMC decoded) - #self.put(es, ss, self.out_binary, [0, bytes(self.bits)]) + self.put(es, ss, self.out_binary, [0, bytes(self.bits)]) - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - for (self.samplenum, pins) in data: - data.itercnt += 1 - # find edges ... - if self.oldpins == pins: - continue + while True: + self.wait([{0: 'e'}, {1: 'e'}, {'skip': int(self.samplerate/1e3)}]) - self.oldpins, (cc, ) = pins, pins - - # First sample of the packet, just record the start date + # First sample of the packet, just record the start date. if not self.startsample: self.startsample = self.samplenum self.previous = self.samplenum @@ -548,20 +605,20 @@ class Decoder(srd.Decoder): diff = self.samplenum - self.previous - # Large idle: use it as the end of packet + # Large idle: use it as the end of packet. if diff > self.maxbit: - # the last edge of the packet + # The last edge of the packet. self.edges.append(self.previous) - # Export the packet + # Export the packet. self.decode_packet() - # Reset for next packet + # Reset for next packet. self.startsample = self.samplenum self.bits = [] self.edges = [] self.bad = [] self.half_one = False self.start_one = 0 - else: # add the bit to the packet + else: # Add the bit to the packet. is_zero = diff > self.threshold if is_zero and not self.half_one: self.bits.append(0) @@ -573,9 +630,9 @@ class Decoder(srd.Decoder): elif not is_zero and not self.half_one: self.half_one = True self.start_one = self.previous - else: # Invalid BMC sequence + else: # Invalid BMC sequence self.bad.append((self.start_one, self.previous)) - # TODO try to recover + # TODO: Try to recover. self.bits.append(0) self.edges.append(self.previous) self.half_one = False diff --git a/libsigrokdecode4DSL/decoders/usb_request/__init__.py b/libsigrokdecode4DSL/decoders/usb_request/__init__.py index 7a156ffe..66723dc2 100755 --- a/libsigrokdecode4DSL/decoders/usb_request/__init__.py +++ b/libsigrokdecode4DSL/decoders/usb_request/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/usb_request/pd.py b/libsigrokdecode4DSL/decoders/usb_request/pd.py index efd880d7..49b0b350 100755 --- a/libsigrokdecode4DSL/decoders/usb_request/pd.py +++ b/libsigrokdecode4DSL/decoders/usb_request/pd.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -113,14 +112,15 @@ class pcap_usb_pkt(): return 64 + len(self.data) class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'usb_request' name = 'USB request' longname = 'Universal Serial Bus (LS/FS) transaction/request' - desc = 'USB (low-speed and full-speed) transaction/request protocol.' + desc = 'USB (low-speed/full-speed) transaction/request protocol.' license = 'gplv2+' inputs = ['usb_packet'] outputs = ['usb_request'] + tags = ['PC'] annotations = ( ('request-setup-read', 'Setup: Device-to-host'), ('request-setup-write', 'Setup: Host-to-device'), @@ -137,12 +137,15 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.request = {} self.request_id = 0 self.transaction_state = 'IDLE' - self.transaction_ss = None - self.transaction_es = None + self.ss_transaction = None + self.es_transaction = None self.transaction_ep = None self.transaction_addr = None self.wrote_pcap_header = False @@ -169,7 +172,8 @@ class Decoder(srd.Decoder): def metadata(self, key, value): if key == srd.SRD_CONF_SAMPLERATE: self.samplerate = value - self.secs_per_sample = float(1) / float(self.samplerate) + if self.samplerate: + self.secs_per_sample = float(1) / float(self.samplerate) def start(self): self.out_binary = self.register(srd.OUTPUT_BINARY) @@ -180,28 +184,38 @@ class Decoder(srd.Decoder): request_end = self.handshake in ('ACK', 'STALL', 'timeout') ep = self.transaction_ep addr = self.transaction_addr + + # Handle protocol STALLs, condition lasts until next SETUP transfer (8.5.3.4) + if self.transaction_type == 'SETUP' and (addr, ep) in self.request: + request = self.request[(addr,ep)] + if request['type'] in ('SETUP IN', 'SETUP OUT'): + request['es'] = self.ss_transaction + self.handle_request(0, 1) + if not (addr, ep) in self.request: self.request[(addr, ep)] = {'setup_data': [], 'data': [], - 'type': None, 'ss': self.transaction_ss, 'es': None, + 'type': None, 'ss': self.ss_transaction, 'es': None, 'id': self.request_id, 'addr': addr, 'ep': ep} self.request_id += 1 request_started = 1 request = self.request[(addr,ep)] + if request_end: + request['es'] = self.es_transaction + request['handshake'] = self.handshake + # BULK or INTERRUPT transfer if request['type'] in (None, 'BULK IN') and self.transaction_type == 'IN': request['type'] = 'BULK IN' request['data'] += self.transaction_data - request['es'] = self.transaction_es self.handle_request(request_started, request_end) elif request['type'] in (None, 'BULK OUT') and self.transaction_type == 'OUT': request['type'] = 'BULK OUT' request['data'] += self.transaction_data - request['es'] = self.transaction_es self.handle_request(request_started, request_end) # CONTROL, SETUP stage - elif request['type'] == None and self.transaction_type == 'SETUP': + elif request['type'] is None and self.transaction_type == 'SETUP': request['setup_data'] = self.transaction_data request['wLength'] = struct.unpack(' transaction_timeout: - self.transaction_es = transaction_timeout + self.es_transaction = transaction_timeout self.handshake = 'timeout' self.handle_transfer() self.transaction_state = 'IDLE' @@ -323,8 +335,8 @@ class Decoder(srd.Decoder): sync, pid, addr, ep, crc5 = pinfo self.transaction_data = [] - self.transaction_ss = ss - self.transaction_es = es + self.ss_transaction = ss + self.es_transaction = es self.transaction_state = 'TOKEN RECEIVED' self.transaction_ep = ep self.transaction_addr = addr @@ -347,7 +359,7 @@ class Decoder(srd.Decoder): self.handshake = pname self.transaction_state = 'IDLE' - self.transaction_es = es + self.es_transaction = es self.handle_transfer() elif pname == 'PRE': diff --git a/libsigrokdecode4DSL/decoders/usb_signalling/__init__.py b/libsigrokdecode4DSL/decoders/usb_signalling/__init__.py index b2ceb4d6..eae18870 100755 --- a/libsigrokdecode4DSL/decoders/usb_signalling/__init__.py +++ b/libsigrokdecode4DSL/decoders/usb_signalling/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/usb_signalling/pd.py b/libsigrokdecode4DSL/decoders/usb_signalling/pd.py index 76b8485e..65a2b35c 100755 --- a/libsigrokdecode4DSL/decoders/usb_signalling/pd.py +++ b/libsigrokdecode4DSL/decoders/usb_signalling/pd.py @@ -3,6 +3,7 @@ ## ## Copyright (C) 2011 Gareth McMullin ## Copyright (C) 2012-2013 Uwe Hermann +## Copyright (C) 2019 DreamSourceLab ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -15,8 +16,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd @@ -101,14 +101,15 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'usb_signalling' name = 'USB signalling' longname = 'Universal Serial Bus (LS/FS) signalling' - desc = 'USB (low-speed and full-speed) signalling protocol.' + desc = 'USB (low-speed/full-speed) signalling protocol.' license = 'gplv2+' inputs = ['logic'] outputs = ['usb_signalling'] + tags = ['PC'] channels = ( {'id': 'dp', 'name': 'D+', 'desc': 'USB D+ signal'}, {'id': 'dm', 'name': 'D-', 'desc': 'USB D- signal'}, @@ -136,6 +137,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.oldsym = 'J' # The "idle" state is J. self.ss_block = None @@ -146,11 +150,10 @@ class Decoder(srd.Decoder): self.samplenum_target = None self.samplenum_edge = None self.samplenum_lastedge = 0 - self.oldpins = None self.edgepins = None self.consecutive_ones = 0 self.bits = None - self.state = 'INIT' + self.state = 'IDLE' def start(self): self.out_python = self.register(srd.OUTPUT_PYTHON) @@ -192,7 +195,7 @@ class Decoder(srd.Decoder): self.put(s, e, self.out_ann, data) def set_new_target_samplenum(self): - self.samplepos += self.bitwidth; + self.samplepos += self.bitwidth self.samplenum_target = int(self.samplepos) self.samplenum_lastedge = self.samplenum_edge self.samplenum_edge = int(self.samplepos - (self.bitwidth / 2)) @@ -290,57 +293,60 @@ class Decoder(srd.Decoder): self.putpb(['KEEP ALIVE', None]) self.putb([9, ['Keep-alive', 'KA', 'A']]) - if sym == 'FS_J': + if self.options['signalling'] == 'automatic' and sym == 'FS_J': self.signalling = 'full-speed' - self.update_bitrate() - elif sym == 'LS_J': + elif self.options['signalling'] == 'automatic' and sym == 'LS_J': self.signalling = 'low-speed' - self.update_bitrate() + else: + self.signalling = self.options['signalling'] + self.update_bitrate() + self.oldsym = 'J' self.state = 'IDLE' - def decode(self, ss, es, data): + def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - for (self.samplenum, pins) in data: - data.itercnt += 1 + + # Seed internal state from the very first sample. + (dp, dm) = self.wait() + sym = symbols[self.options['signalling']][(dp, dm)] + self.handle_idle(sym) + + while True: # State machine. if self.state == 'IDLE': - # Ignore identical samples early on (for performance reasons). - if self.oldpins == pins: - continue - self.oldpins = pins - sym = symbols[self.signalling][tuple(pins)] + # Wait for any edge on either DP and/or DM. + (dp, dm) = self.wait([{0: 'e'}, {1: 'e'}]) + sym = symbols[self.signalling][(dp, dm)] if sym == 'SE0': self.samplenum_lastedge = self.samplenum self.state = 'WAIT IDLE' else: self.wait_for_sop(sym) - self.edgepins = pins + self.edgepins = (dp, dm) elif self.state in ('GET BIT', 'GET EOP'): # Wait until we're in the middle of the desired bit. - if self.samplenum == self.samplenum_edge: - self.edgepins = pins - if self.samplenum < self.samplenum_target: - continue - sym = symbols[self.signalling][tuple(pins)] + if (self.samplenum_edge > self.samplenum): + (dp, dm) = self.wait([{'skip': self.samplenum_edge - self.samplenum}]) + self.edgepins = (dp, dm) + if (self.samplenum_target > self.samplenum): + (dp, dm) = self.wait([{'skip': self.samplenum_target - self.samplenum}]) + + sym = symbols[self.signalling][(dp, dm)] if self.state == 'GET BIT': self.get_bit(sym) elif self.state == 'GET EOP': self.get_eop(sym) - self.oldpins = pins elif self.state == 'WAIT IDLE': - if tuple(pins) == (0, 0): - continue + # Skip "all-low" input. Wait for high level on either DP or DM. + (dp, dm) = self.wait() + while not dp and not dm: + (dp, dm) = self.wait([{0: 'h'}, {1: 'h'}]) if self.samplenum - self.samplenum_lastedge > 1: - sym = symbols[self.options['signalling']][tuple(pins)] + sym = symbols[self.options['signalling']][(dp, dm)] self.handle_idle(sym) else: - sym = symbols[self.signalling][tuple(pins)] + sym = symbols[self.signalling][(dp, dm)] self.wait_for_sop(sym) - self.oldpins = pins - self.edgepins = pins - elif self.state == 'INIT': - sym = symbols[self.options['signalling']][tuple(pins)] - self.handle_idle(sym) - self.oldpins = pins + self.edgepins = (dp, dm) diff --git a/libsigrokdecode4DSL/decoders/wiegand/__init__.py b/libsigrokdecode4DSL/decoders/wiegand/__init__.py index 20f51f8d..d7d9a8c7 100755 --- a/libsigrokdecode4DSL/decoders/wiegand/__init__.py +++ b/libsigrokdecode4DSL/decoders/wiegand/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' diff --git a/libsigrokdecode4DSL/decoders/wiegand/pd.py b/libsigrokdecode4DSL/decoders/wiegand/pd.py index ba117df1..a93be109 100755 --- a/libsigrokdecode4DSL/decoders/wiegand/pd.py +++ b/libsigrokdecode4DSL/decoders/wiegand/pd.py @@ -14,21 +14,24 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## import sigrokdecode as srd +class SamplerateError(Exception): + pass + class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'wiegand' name = 'Wiegand' longname = 'Wiegand interface' desc = 'Wiegand interface for electronic entry systems.' license = 'gplv2+' inputs = ['logic'] - outputs = ['wiegand'] + outputs = [] + tags = ['Embedded/industrial', 'RFID'] channels = ( {'id': 'd0', 'name': 'D0', 'desc': 'Data 0 line'}, {'id': 'd1', 'name': 'D1', 'desc': 'Data 1 line'}, @@ -49,13 +52,17 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): + self.samplerate = None self._samples_per_bit = 10 self._d0_prev = None self._d1_prev = None self._state = None - self._ss_state = None + self.ss_state = None self.ss_bit = None self.es_bit = None @@ -65,15 +72,17 @@ class Decoder(srd.Decoder): def start(self): 'Register output types and verify user supplied decoder values.' self.out_ann = self.register(srd.OUTPUT_ANN) - self._active = self.options['active'] == 'high' and 1 or 0 + self._active = 1 if self.options['active'] == 'high' else 0 self._inactive = 1 - self._active def metadata(self, key, value): 'Receive decoder metadata about the data stream.' if key == srd.SRD_CONF_SAMPLERATE: - ms_per_sample = 1000 * (1.0 / value) - ms_per_bit = float(self.options['bitwidth_ms']) - self._samples_per_bit = int(max(1, int(ms_per_bit / ms_per_sample))) + self.samplerate = value + if self.samplerate: + ms_per_sample = 1000 * (1.0 / self.samplerate) + ms_per_bit = float(self.options['bitwidth_ms']) + self._samples_per_bit = int(max(1, int(ms_per_bit / ms_per_sample))) def _update_state(self, state, bit=None): 'Update state and bit values when they change.' @@ -98,14 +107,18 @@ class Decoder(srd.Decoder): elif self._state == 'invalid': ann = [1, [self._state]] if ann: - self.put(self._ss_state, self.samplenum, self.out_ann, ann) - self._ss_state = self.samplenum + self.put(self.ss_state, self.samplenum, self.out_ann, ann) + self.ss_state = self.samplenum self._state = state self._bits = [] - def decode(self, ss, es, data): - for self.samplenum, (d0, d1) in data: - data.itercnt += 1 + def decode(self): + if not self.samplerate: + raise SamplerateError('Cannot decode without samplerate.') + while True: + # TODO: Come up with more appropriate self.wait() conditions. + (d0, d1) = self.wait() + if d0 == self._d0_prev and d1 == self._d1_prev: if self.es_bit and self.samplenum >= self.es_bit: if (d0, d1) == (self._inactive, self._inactive): diff --git a/libsigrokdecode4DSL/decoders/xfp/__init__.py b/libsigrokdecode4DSL/decoders/xfp/__init__.py index 51936e55..72f35950 100755 --- a/libsigrokdecode4DSL/decoders/xfp/__init__.py +++ b/libsigrokdecode4DSL/decoders/xfp/__init__.py @@ -14,8 +14,7 @@ ## 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, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## along with this program; if not, see . ## ''' @@ -32,9 +31,8 @@ module startup. Other table are either reserved for future expansion, or available for vendor-specific extensions. This decoder supports both lower memory and table 0x01. -The XFP specification is available here: - - ftp://ftp.seagate.com/sff/INF-8077.PDF +Details: +ftp://ftp.seagate.com/sff/INF-8077.PDF (XFP specification) ''' from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/xfp/pd.py b/libsigrokdecode4DSL/decoders/xfp/pd.py index 41aca0db..ded76946 100755 --- a/libsigrokdecode4DSL/decoders/xfp/pd.py +++ b/libsigrokdecode4DSL/decoders/xfp/pd.py @@ -18,193 +18,29 @@ ## import sigrokdecode as srd - -MODULE_ID = { - 0x01: 'GBIC', - 0x02: 'Integrated module/connector', - 0x03: 'SFP', - 0x04: '300-pin XBI', - 0x05: 'XENPAK', - 0x06: 'XFP', - 0x07: 'XFF', - 0x08: 'XFP-E', - 0x09: 'XPAK', - 0x0a: 'X2', -} - -ALARM_THRESHOLDS = { - 0: "Temp high alarm", - 2: "Temp low alarm", - 4: "Temp high warning", - 6: "Temp low warning", - 16: "Bias high alarm", - 18: "Bias low alarm", - 20: "Bias high warning", - 22: "Bias low warning", - 24: "TX power high alarm", - 26: "TX power low alarm", - 28: "TX power high warning", - 30: "TX power low warning", - 32: "RX power high alarm", - 34: "RX power low alarm", - 36: "RX power high warning", - 38: "RX power low warning", - 40: "AUX 1 high alarm", - 42: "AUX 1 low alarm", - 44: "AUX 1 high warning", - 46: "AUX 1 low warning", - 48: "AUX 2 high alarm", - 50: "AUX 2 low alarm", - 52: "AUX 2 high warning", - 54: "AUX 2 low warning", -} - -AD_READOUTS = { - 0: "Module temperature", - 4: "TX bias current", - 6: "Measured TX output power", - 8: "Measured RX input power", - 10: "AUX 1 measurement", - 12: "AUX 2 measurement", -} - -GCS_BITS = [ - "TX disable", - "Soft TX disable", - "MOD_NR", - "P_Down", - "Soft P_Down", - "Interrupt", - "RX_LOS", - "Data_Not_Ready", - "TX_NR", - "TX_Fault", - "TX_CDR not locked", - "RX_NR", - "RX_CDR not locked", -] - -CONNECTOR = { - 0x01: "SC", - 0x02: "Fibre Channel style 1 copper", - 0x03: "Fibre Channel style 2 copper", - 0x04: "BNC/TNC", - 0x05: "Fibre Channel coax", - 0x06: "FiberJack", - 0x07: "LC", - 0x08: "MT-RJ", - 0x09: "MU", - 0x0a: "SG", - 0x0b: "Optical pigtail", - 0x20: "HSSDC II", - 0x21: "Copper pigtail", -} - -TRANSCEIVER = [ - # 10GB Ethernet - ["10GBASE-SR", "10GBASE-LR", "10GBASE-ER", "10GBASE-LRM", "10GBASE-SW", - "10GBASE-LW", "10GBASE-EW"], - # 10GB Fibre Channel - ["1200-MX-SN-I", "1200-SM-LL-L", "Extended Reach 1550 nm", - "Intermediate reach 1300 nm FP"], - # 10GB Copper - [], - # 10GB low speed - ["1000BASE-SX / 1xFC MMF", "1000BASE-LX / 1xFC SMF", "2xFC MMF", - "2xFC SMF", "OC48-SR", "OC48-IR", "OC48-LR"], - # 10GB SONET/SDH interconnect - ["I-64.1r", "I-64.1", "I-64.2r", "I-64.2", "I-64.3", "I-64.5"], - # 10GB SONET/SDH short haul - ["S-64.1", "S-64.2a", "S-64.2b", "S-64.3a", "S-64.3b", "S-64.5a", "S-64.5b"], - # 10GB SONET/SDH long haul - ["L-64.1", "L-64.2a", "L-64.2b", "L-64.2c", "L-64.3", "G.959.1 P1L1-2D2"], - # 10GB SONET/SDH very long haul - ["V-64.2a", "V-64.2b", "V-64.3"], -] - -SERIAL_ENCODING = [ - "64B/66B", - "8B/10B", - "SONET scrambled", - "NRZ", - "RZ", -] - -XMIT_TECH = [ - "850 nm VCSEL", - "1310 nm VCSEL", - "1550 nm VCSEL", - "1310 nm FP", - "1310 nm DFB", - "1550 nm DFB", - "1310 nm EML" - "1550 nm EML" - "copper", -] - -CDR = [ - "9.95Gb/s", - "10.3Gb/s", - "10.5Gb/s", - "10.7Gb/s", - "11.1Gb/s", - "(unknown)", - "lineside loopback mode", - "XFI loopback mode", -] - -DEVICE_TECH = [ - ["no wavelength control", "sctive wavelength control"], - ["uncooled transmitter device", "cooled transmitter"], - ["PIN detector", "APD detector"], - ["transmitter not tunable", "transmitter tunable"], -] - -ENHANCED_OPTS = [ - "VPS", - "soft TX_DISABLE", - "soft P_Down", - "VPS LV regulator mode", - "VPS bypassed regulator mode", - "active FEC control", - "wavelength tunability", - "CMU", -] - -AUX_TYPES = [ - "not implemented", - "APD bias voltage", - "(unknown)", - "TEC current", - "laser temperature", - "laser wavelength", - "5V supply voltage", - "3.3V supply voltage", - "1.8V supply voltage", - "-5.2V supply voltage", - "5V supply current", - "(unknown)", - "(unknown)", - "3.3V supply current", - "1.8V supply current", - "-5.2V supply current", -] +from common.plugtrx import (MODULE_ID, ALARM_THRESHOLDS, AD_READOUTS, GCS_BITS, + CONNECTOR, TRANSCEIVER, SERIAL_ENCODING, XMIT_TECH, CDR, DEVICE_TECH, + ENHANCED_OPTS, AUX_TYPES) class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'xfp' name = 'XFP' longname = '10 Gigabit Small Form Factor Pluggable Module (XFP)' - desc = 'Data structure describing display device capabilities.' + desc = 'XFP I²C management interface structures/protocol' license = 'gplv3+' inputs = ['i2c'] - outputs = ['xfp'] + outputs = [] + tags = ['Networking'] annotations = ( ('fieldnames-and-values', 'XFP structure field names and values'), ('fields', 'XFP structure fields'), ) - def __init__(self, **kwargs): + def __init__(self): + self.reset() + + def reset(self): # Received data items, used as an index into samplenum/data self.cnt = -1 # Start/end sample numbers per data item diff --git a/libsigrokdecode4DSL/decoders/z80/__init__.py b/libsigrokdecode4DSL/decoders/z80/__init__.py index aaf7c76e..52ff9bac 100755 --- a/libsigrokdecode4DSL/decoders/z80/__init__.py +++ b/libsigrokdecode4DSL/decoders/z80/__init__.py @@ -28,8 +28,9 @@ sampling clock, if applicable. Notes on the Z80 opcode format and descriptions of both documented and "undocumented" opcodes are available here: - http://www.z80.info/decoding.htm - http://clrhome.org/table/ +Details: +http://www.z80.info/decoding.htm +http://clrhome.org/table/ ''' from .pd import Decoder diff --git a/libsigrokdecode4DSL/decoders/z80/pd.py b/libsigrokdecode4DSL/decoders/z80/pd.py index 54655d0f..9af310e2 100755 --- a/libsigrokdecode4DSL/decoders/z80/pd.py +++ b/libsigrokdecode4DSL/decoders/z80/pd.py @@ -64,14 +64,15 @@ def signed_byte(byte): return byte if byte < 128 else byte - 256 class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'z80' name = 'Z80' longname = 'Zilog Z80 CPU' desc = 'Zilog Z80 microprocessor disassembly.' license = 'gplv3+' inputs = ['logic'] - outputs = ['z80'] + outputs = [] + tags = ['Retro computing'] channels = tuple({ 'id': 'd%d' % i, 'name': 'D%d' % i, @@ -111,6 +112,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.prev_cycle = Cycle.NONE self.op_state = self.state_IDLE @@ -129,9 +133,11 @@ class Decoder(srd.Decoder): self.op_state = self.state_IDLE self.instr_len = 0 - def decode(self, ss, es, data): - for (self.samplenum, pins) in data: - data.itercnt += 1 + def decode(self): + while True: + # TODO: Come up with more appropriate self.wait() conditions. + (d0, d1, d2, d3, d4, d5, d6, d7, m1, rd, wr, mreq, iorq, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) = self.wait() + pins = (d0, d1, d2, d3, d4, d5, d6, d7, m1, rd, wr, mreq, iorq, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) cycle = Cycle.NONE if pins[Pin.MREQ] != 1: # default to asserted if pins[Pin.RD] == 0: @@ -157,7 +163,6 @@ class Decoder(srd.Decoder): self.on_cycle_trans() self.prev_cycle = cycle - def on_cycle_begin(self, bus_addr): if self.pend_addr is not None: self.put_text(self.addr_start, Ann.ADDR, diff --git a/libsigrokdecode4DSL/error.c b/libsigrokdecode4DSL/error.c old mode 100644 new mode 100755 index 0820fbf2..81fb2f13 --- a/libsigrokdecode4DSL/error.c +++ b/libsigrokdecode4DSL/error.c @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program; if not, see . */ #include "config.h" diff --git a/libsigrokdecode4DSL/exception.c b/libsigrokdecode4DSL/exception.c old mode 100644 new mode 100755 index dd029d1b..e8c20633 --- a/libsigrokdecode4DSL/exception.c +++ b/libsigrokdecode4DSL/exception.c @@ -2,7 +2,6 @@ * This file is part of the libsigrokdecode project. * * Copyright (C) 2012 Bert Vermeulen - * Copyright (C) 2016 DreamSourceLab * * 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 @@ -23,13 +22,14 @@ #include "libsigrokdecode.h" #include #include -#include static char *py_stringify(PyObject *py_obj) { PyObject *py_str, *py_bytes; char *str = NULL; + /* Note: Caller already ran PyGILState_Ensure(). */ + if (!py_obj) return NULL; @@ -50,6 +50,7 @@ cleanup: PyErr_Clear(); srd_dbg("Failed to stringify object."); } + return str; } @@ -58,6 +59,8 @@ static char *py_get_string_attr(PyObject *py_obj, const char *attr) PyObject *py_str, *py_bytes; char *str = NULL; + /* Note: Caller already ran PyGILState_Ensure(). */ + if (!py_obj) return NULL; @@ -78,17 +81,21 @@ cleanup: PyErr_Clear(); srd_dbg("Failed to get object attribute %s.", attr); } + return str; } /** @private */ SRD_PRIV void srd_exception_catch(char **error, const char *format, ...) { + int i, ret; va_list args; PyObject *py_etype, *py_evalue, *py_etraceback; PyObject *py_mod, *py_func, *py_tracefmt; - char *msg, *etype_name, *evalue_str, *tracefmt_str; + char *msg, *etype_name, *evalue_str, *outstr; const char *etype_name_fallback; + PyGILState_STATE gstate; + GString *s; char *final_msg; py_etype = py_evalue = py_etraceback = py_mod = py_func = NULL; @@ -97,6 +104,8 @@ SRD_PRIV void srd_exception_catch(char **error, const char *format, ...) msg = g_strdup_vprintf(format, args); va_end(args); + gstate = PyGILState_Ensure(); + PyErr_Fetch(&py_etype, &py_evalue, &py_etraceback); if (!py_etype) { /* No current exception, so just print the message. */ @@ -135,17 +144,21 @@ SRD_PRIV void srd_exception_catch(char **error, const char *format, ...) /* Call into Python to format the stack trace. */ py_tracefmt = PyObject_CallFunctionObjArgs(py_func, py_etype, py_evalue, py_etraceback, NULL); - if (!py_tracefmt) + if (!py_tracefmt || !PyList_Check(py_tracefmt)) goto cleanup; - tracefmt_str = py_stringify(py_tracefmt); - Py_DECREF(py_tracefmt); - - /* Log the detailed stack trace. */ - if (tracefmt_str) { - srd_dbg("%s", tracefmt_str); - g_free(tracefmt_str); + s = g_string_sized_new(128); + for (i = 0; i < PyList_Size(py_tracefmt); i++) { + ret = py_listitem_as_str(py_tracefmt, i, &outstr); + if (ret == 0) { + s = g_string_append(s, outstr); + g_free(outstr); + } } + srd_err("%s", s->str); + g_string_free(s, TRUE); + + Py_DECREF(py_tracefmt); cleanup: if (error) @@ -159,6 +172,8 @@ cleanup: /* Just in case. */ PyErr_Clear(); + PyGILState_Release(gstate); + g_free(msg); g_free(final_msg); } diff --git a/libsigrokdecode4DSL/instance.c b/libsigrokdecode4DSL/instance.c old mode 100644 new mode 100755 index 1abcd0e3..73f2e181 --- a/libsigrokdecode4DSL/instance.c +++ b/libsigrokdecode4DSL/instance.c @@ -31,9 +31,6 @@ extern SRD_PRIV GSList *sessions; -/* module_sigrokdecode.c */ -extern SRD_PRIV PyObject *srd_logic_type; - /** @endcond */ /** @@ -50,6 +47,36 @@ extern SRD_PRIV PyObject *srd_logic_type; * @{ */ +static void oldpins_array_seed(struct srd_decoder_inst *di) +{ + size_t count; + GArray *arr; + + if (!di) + return; + if (di->old_pins_array) + return; + + count = di->dec_num_channels; + arr = g_array_sized_new(FALSE, TRUE, sizeof(uint8_t), count); + g_array_set_size(arr, count); + memset(arr->data, SRD_INITIAL_PIN_SAME_AS_SAMPLE0, count); + di->old_pins_array = arr; +} + +static void oldpins_array_free(struct srd_decoder_inst *di) +{ + if (!di) + return; + if (!di->old_pins_array) + return; + + srd_dbg("%s: Releasing initial pin state.", di->inst_id); + + g_array_free(di->old_pins_array, TRUE); + di->old_pins_array = NULL; +} + /** * Set one or more options in a decoder instance. * @@ -73,6 +100,7 @@ SRD_API int srd_inst_option_set(struct srd_decoder_inst *di, gint64 val_int; int ret; const char *val_str; + PyGILState_STATE gstate; if (!di) { srd_err("Invalid decoder instance."); @@ -84,8 +112,11 @@ SRD_API int srd_inst_option_set(struct srd_decoder_inst *di, return SRD_ERR_ARG; } + gstate = PyGILState_Ensure(); + if (!PyObject_HasAttrString(di->decoder->py_dec, "options")) { /* Decoder has no options. */ + PyGILState_Release(gstate); if (g_hash_table_size(options) == 0) { /* No options provided. */ return SRD_OK; @@ -156,7 +187,6 @@ SRD_API int srd_inst_option_set(struct srd_decoder_inst *di, /* Not harmful even if we used the default. */ g_hash_table_remove(options, sdo->id); } - Py_DECREF(py_di_options); if (g_hash_table_size(options) != 0) srd_warn("Unknown options specified for '%s'", di->inst_id); @@ -165,14 +195,15 @@ SRD_API int srd_inst_option_set(struct srd_decoder_inst *di, err_out: Py_XDECREF(py_optval); if (PyErr_Occurred()) { - srd_exception_catch(NULL, "Stray exception in srd_inst_option_set()"); + srd_exception_catch(NULL, "Stray exception in srd_inst_option_set()"); ret = SRD_ERR_PYTHON; } + PyGILState_Release(gstate); return ret; } -/* Helper GComparefunc for g_slist_find_custom() in srd_inst_channel_set_all() */ +/* Helper GComparefunc for g_slist_find_custom() in srd_inst_channel_set_all(). */ static gint compare_channel_id(const struct srd_channel *pdch, const char *channel_id) { @@ -218,7 +249,7 @@ SRD_API int srd_inst_channel_set_all(struct srd_decoder_inst *di, return SRD_ERR_ARG; } - new_channelmap = g_malloc(sizeof(int) * di->dec_num_channels); + new_channelmap = g_malloc0(sizeof(int) * di->dec_num_channels); /* * For now, map all indexes to channel -1 (can be overridden later). @@ -251,14 +282,20 @@ SRD_API int srd_inst_channel_set_all(struct srd_decoder_inst *di, } pdch = sl->data; new_channelmap[pdch->order] = new_channelnum; - srd_dbg("Setting channel mapping: %s (index %d) = channel %d.", + srd_dbg("Setting channel mapping: %s (PD ch idx %d) = input data ch idx %d.", pdch->id, pdch->order, new_channelnum); } srd_dbg("Final channel map:"); num_required_channels = g_slist_length(di->decoder->channels); for (i = 0; i < di->dec_num_channels; i++) { - srd_dbg(" - index %d = channel %d (%s)", i, new_channelmap[i], + GSList *ll = g_slist_nth(di->decoder->channels, i); + if (!ll) + ll = g_slist_nth(di->decoder->opt_channels, + i - num_required_channels); + pdch = ll->data; + srd_dbg(" - PD ch idx %d (%s) = input data ch idx %d (%s)", i, + pdch->id, new_channelmap[i], (i < num_required_channels) ? "required" : "optional"); } @@ -269,6 +306,7 @@ SRD_API int srd_inst_channel_set_all(struct srd_decoder_inst *di, pdch = g_slist_nth(di->decoder->channels, i)->data; srd_err("Required channel '%s' (index %d) was not specified.", pdch->id, i); + g_free(new_channelmap); return SRD_ERR; } @@ -282,6 +320,7 @@ SRD_API int srd_inst_channel_set_all(struct srd_decoder_inst *di, * Create a new protocol decoder instance. * * @param sess The session holding the protocol decoder instance. + * Must not be NULL. * @param decoder_id Decoder 'id' field. * @param options GHashtable of options which override the defaults set in * the decoder class. May be NULL. @@ -298,13 +337,12 @@ SRD_API struct srd_decoder_inst *srd_inst_new(struct srd_session *sess, struct srd_decoder *dec; struct srd_decoder_inst *di; char *inst_id; + PyGILState_STATE gstate; - srd_dbg("Creating new %s instance.", decoder_id); + i = 1; - if (session_is_valid(sess) != SRD_OK) { - srd_err("Invalid session."); + if (!sess) return NULL; - } if (!(dec = srd_decoder_get_by_id(decoder_id))) { srd_err("Protocol decoder %s not found.", decoder_id); @@ -315,17 +353,28 @@ SRD_API struct srd_decoder_inst *srd_inst_new(struct srd_session *sess, di->decoder = dec; di->sess = sess; + if (options) { inst_id = g_hash_table_lookup(options, "id"); - di->inst_id = g_strdup(inst_id ? inst_id : decoder_id); + if (inst_id) + di->inst_id = g_strdup(inst_id); g_hash_table_remove(options, "id"); - } else - di->inst_id = g_strdup(decoder_id); + } + + /* Create a unique instance ID (as none was provided). */ + if (!di->inst_id) { + di->inst_id = g_strdup_printf("%s-%d", decoder_id, i++); + while (srd_inst_find_by_id(sess, di->inst_id)) { + g_free(di->inst_id); + di->inst_id = g_strdup_printf("%s-%d", decoder_id, i++); + } + } /* * Prepare a default channel map, where samples come in the * order in which the decoder class defined them. */ + di->py_pinvalues = NULL; di->dec_num_channels = g_slist_length(di->decoder->channels) + g_slist_length(di->decoder->opt_channels); if (di->dec_num_channels) { @@ -333,39 +382,128 @@ SRD_API struct srd_decoder_inst *srd_inst_new(struct srd_session *sess, g_malloc(sizeof(int) * di->dec_num_channels); for (i = 0; i < di->dec_num_channels; i++) di->dec_channelmap[i] = i; - /* - * Will be used to prepare a sample at every iteration - * of the instance's decode() method. - */ - di->channel_samples = g_malloc(di->dec_num_channels); + + di->py_pinvalues = PyTuple_New(di->dec_num_channels); } + /* Default to the initial pins being the same as in sample 0. */ + oldpins_array_seed(di); + + gstate = PyGILState_Ensure(); + /* Create a new instance of this decoder class. */ if (!(di->py_inst = PyObject_CallObject(dec->py_dec, NULL))) { if (PyErr_Occurred()) - srd_exception_catch(NULL, "Failed to create %s instance", + srd_exception_catch(NULL, "Failed to create %s instance", decoder_id); - g_free(di->dec_channelmap); - g_free(di); - return NULL; + goto err; } - if (options && srd_inst_option_set(di, options) != SRD_OK) { - g_free(di->dec_channelmap); - g_free(di); - return NULL; + if (options && srd_inst_option_set(di, options) != SRD_OK) { + goto err; } + PyGILState_Release(gstate); + + di->condition_list = NULL; + di->match_array = 0; + di->abs_start_samplenum = 0; + di->abs_end_samplenum = 0; + di->inbuf = NULL; + di->inbuflen = 0; + di->abs_cur_samplenum = 0; + di->thread_handle = NULL; + di->got_new_samples = FALSE; + di->handled_all_samples = FALSE; + di->want_wait_terminate = FALSE; + di->decoder_state = SRD_OK; + + /* + * Strictly speaking initialization of statically allocated + * condition and mutex variables (or variables allocated on the + * stack) is not required, but won't harm either. Explicitly + * running init() will better match subsequent clear() calls. + */ + g_cond_init(&di->got_new_samples_cond); + g_cond_init(&di->handled_all_samples_cond); + g_mutex_init(&di->data_mutex); + /* Instance takes input from a frontend by default. */ sess->di_list = g_slist_append(sess->di_list, di); + srd_dbg("Creating new %s instance %s.", decoder_id, di->inst_id); return di; + +err: + PyGILState_Release(gstate); + g_free(di->dec_channelmap); + g_free(di); + return NULL; +} + +static void srd_inst_join_decode_thread(struct srd_decoder_inst *di) +{ + if (!di) + return; + if (!di->thread_handle) + return; + + srd_dbg("%s: Joining decoder thread.", di->inst_id); + + /* + * Terminate potentially running threads which still + * execute the decoder instance's decode() method. + */ + srd_dbg("%s: Raising want_term, sending got_new.", di->inst_id); + g_mutex_lock(&di->data_mutex); + di->want_wait_terminate = TRUE; + g_cond_signal(&di->got_new_samples_cond); + g_mutex_unlock(&di->data_mutex); + + srd_dbg("%s: Running join().", di->inst_id); + (void)g_thread_join(di->thread_handle); + srd_dbg("%s: Call to join() done.", di->inst_id); + di->thread_handle = NULL; + + /* + * Reset condition and mutex variables, such that next + * operations on them will find them in a clean state. + */ + g_cond_clear(&di->got_new_samples_cond); + g_cond_init(&di->got_new_samples_cond); + g_cond_clear(&di->handled_all_samples_cond); + g_cond_init(&di->handled_all_samples_cond); + g_mutex_clear(&di->data_mutex); + g_mutex_init(&di->data_mutex); +} + +static void srd_inst_reset_state(struct srd_decoder_inst *di) +{ + if (!di) + return; + + srd_dbg("%s: Resetting decoder state.", di->inst_id); + + /* Reset internal state of the decoder. */ + condition_list_free(di); + di->abs_start_samplenum = 0; + di->abs_end_samplenum = 0; + di->inbuf = NULL; + di->inbuflen = 0; + di->abs_cur_samplenum = 0; + oldpins_array_free(di); + di->got_new_samples = FALSE; + di->handled_all_samples = FALSE; + di->want_wait_terminate = FALSE; + di->decoder_state = SRD_OK; + /* Conditions and mutex got reset after joining the thread. */ } /** * Stack a decoder instance on top of another. * * @param sess The session holding the protocol decoder instances. + * Must not be NULL. * @param di_bottom The instance on top of which di_top will be stacked. * @param di_top The instance to go on top. * @@ -377,11 +515,8 @@ SRD_API int srd_inst_stack(struct srd_session *sess, struct srd_decoder_inst *di_bottom, struct srd_decoder_inst *di_top) { - - if (session_is_valid(sess) != SRD_OK) { - srd_err("Invalid session."); + if (!sess) return SRD_ERR_ARG; - } if (!di_bottom || !di_top) { srd_err("Invalid from/to instance pair."); @@ -393,21 +528,75 @@ SRD_API int srd_inst_stack(struct srd_session *sess, sess->di_list = g_slist_remove(sess->di_list, di_top); } + /* + * Check if there's at least one matching input/output pair + * for the stacked PDs. We warn if that's not the case, but it's + * not a hard error for the time being. + */ + gboolean at_least_one_match = FALSE; + for (GSList *out = di_bottom->decoder->outputs; out; out = out->next) { + const char *o = out->data; + for (GSList *in = di_top->decoder->inputs; in; in = in->next) { + const char *i = in->data; + if (!strcmp(o, i)) { + at_least_one_match = TRUE; + break; + } + } + } + + if (!at_least_one_match) + srd_warn("No matching in-/output when stacking %s onto %s.", + di_top->inst_id, di_bottom->inst_id); + /* Stack on top of source di. */ di_bottom->next_di = g_slist_append(di_bottom->next_di, di_top); - srd_dbg("Stacked %s onto %s.", di_top->inst_id, di_bottom->inst_id); + srd_dbg("Stacking %s onto %s.", di_top->inst_id, di_bottom->inst_id); return SRD_OK; } +/** + * Search a decoder instance and its stack for instance ID. + * + * @param[in] inst_id ID to search for. + * @param[in] stack A decoder instance, potentially with stacked instances. + * + * @return The matching instance, or NULL. + */ +static struct srd_decoder_inst *srd_inst_find_by_id_stack(const char *inst_id, + struct srd_decoder_inst *stack) +{ + const GSList *l; + struct srd_decoder_inst *tmp, *di; + + if (!strcmp(stack->inst_id, inst_id)) + return stack; + + /* Otherwise, look recursively in our stack. */ + di = NULL; + if (stack->next_di) { + for (l = stack->next_di; l; l = l->next) { + tmp = l->data; + if (!strcmp(tmp->inst_id, inst_id)) { + di = tmp; + break; + } + } + } + + return di; +} + /** * Find a decoder instance by its instance ID. * - * Only the bottom level of instances are searched -- instances already stacked - * on top of another one will not be found. + * This will recurse to find the instance anywhere in the stack tree of the + * given session. * * @param sess The session holding the protocol decoder instance. + * Must not be NULL. * @param inst_id The instance ID to be found. * * @return Pointer to struct srd_decoder_inst, or NULL if not found. @@ -420,212 +609,725 @@ SRD_API struct srd_decoder_inst *srd_inst_find_by_id(struct srd_session *sess, GSList *l; struct srd_decoder_inst *tmp, *di; - if (session_is_valid(sess) != SRD_OK) { - srd_err("Invalid session."); + if (!sess) return NULL; - } di = NULL; for (l = sess->di_list; l; l = l->next) { tmp = l->data; - if (!strcmp(tmp->inst_id, inst_id)) { - di = tmp; + if ((di = srd_inst_find_by_id_stack(inst_id, tmp)) != NULL) break; - } - } - - return di; -} - -static struct srd_decoder_inst *srd_sess_inst_find_by_obj( - struct srd_session *sess, const GSList *stack, - const PyObject *obj) -{ - const GSList *l; - struct srd_decoder_inst *tmp, *di; - - if (session_is_valid(sess) != SRD_OK) { - srd_err("Invalid session."); - return NULL; - } - - di = NULL; - for (l = stack ? stack : sess->di_list; di == NULL && l != NULL; l = l->next) { - tmp = l->data; - if (tmp->py_inst == obj) - di = tmp; - else if (tmp->next_di) - di = srd_sess_inst_find_by_obj(sess, tmp->next_di, obj); } return di; } /** - * Find a decoder instance by its Python object. + * Set the list of initial (assumed) pin values. * - * I.e. find that instance's instantiation of the sigrokdecode.Decoder class. - * This will recurse to find the instance anywhere in the stack tree of all - * sessions. + * @param di Decoder instance to use. Must not be NULL. + * @param initial_pins A GArray of uint8_t values. Must not be NULL. * - * @param stack Pointer to a GSList of struct srd_decoder_inst, indicating the - * stack to search. To start searching at the bottom level of - * decoder instances, pass NULL. - * @param obj The Python class instantiation. - * - * @return Pointer to struct srd_decoder_inst, or NULL if not found. - * - * @private - * - * @since 0.1.0 + * @since 0.5.0 */ -SRD_PRIV struct srd_decoder_inst *srd_inst_find_by_obj(const GSList *stack, - const PyObject *obj) +SRD_API int srd_inst_initial_pins_set_all(struct srd_decoder_inst *di, GArray *initial_pins) { - struct srd_decoder_inst *di; - struct srd_session *sess; - GSList *l; + int i; + GString *s; - di = NULL; - for (l = sessions; di == NULL && l != NULL; l = l->next) { - sess = l->data; - di = srd_sess_inst_find_by_obj(sess, stack, obj); + if (!di) { + srd_err("Invalid decoder instance."); + return SRD_ERR_ARG; } - return di; + if (!initial_pins) + return SRD_ERR_ARG; + + if (initial_pins->len != (guint)di->dec_num_channels) { + srd_err("Incorrect number of channels (need %d, got %d).", + di->dec_num_channels, initial_pins->len); + return SRD_ERR_ARG; + } + + /* Sanity-check initial pin state values. */ + for (i = 0; i < di->dec_num_channels; i++) { + if (initial_pins->data[i] <= 2) + continue; + srd_err("Invalid initial channel %d pin state: %d.", + i, initial_pins->data[i]); + return SRD_ERR_ARG; + } + + s = g_string_sized_new(100); + oldpins_array_seed(di); + for (i = 0; i < di->dec_num_channels; i++) { + di->old_pins_array->data[i] = initial_pins->data[i]; + g_string_append_printf(s, "%d, ", di->old_pins_array->data[i]); + } + s = g_string_truncate(s, s->len - 2); + srd_dbg("Initial pins: %s.", s->str); + g_string_free(s, TRUE); + + return SRD_OK; } /** @private */ SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di, char **error) { - srd_logic *logic; PyObject *py_res; GSList *l; struct srd_decoder_inst *next_di; int ret; + PyGILState_STATE gstate; - srd_dbg("Calling start() method on protocol decoder instance %s.", - di->inst_id); + srd_dbg("Calling start() of instance %s.", di->inst_id); + gstate = PyGILState_Ensure(); + + /* Run self.start(). */ if (!(py_res = PyObject_CallMethod(di->py_inst, "start", NULL))) { - srd_exception_catch(error, "Protocol decoder instance %s", + srd_exception_catch(error, "Protocol decoder instance %s", di->inst_id); + PyGILState_Release(gstate); return SRD_ERR_PYTHON; } Py_DecRef(py_res); - if ((di->decoder->channels || di->decoder->opt_channels) != 0 ) { - //logic = PyObject_New(srd_logic, &srd_logic_type); - logic = PyObject_New(srd_logic, (PyTypeObject *)srd_logic_type); - Py_INCREF(logic); - logic->di = (struct srd_decoder_inst *)di; - logic->sample = PyList_New(2); - //Py_INCREF(logic->sample); - di->py_logic = logic; - } + /* first pos */ + di->first_pos = TRUE; + + /* none matched */ + di->abs_cur_matched = FALSE; + + /* Set self.samplenum to 0. */ + PyObject_SetAttrString(di->py_inst, "samplenum", PyLong_FromLong(0)); + + /* Set self.matched to 0. */ + PyObject_SetAttrString(di->py_inst, "matched", PyLong_FromLong(0)); + + PyGILState_Release(gstate); /* Start all the PDs stacked on top of this one. */ for (l = di->next_di; l; l = l->next) { next_di = l->data; - if ((ret = srd_inst_start(next_di, error)) != SRD_OK) + if ((ret = srd_inst_start(next_di, error)) != SRD_OK) return ret; } return SRD_OK; } +/** + * Check whether the specified sample matches the specified term. + * + * In the case of SRD_TERM_SKIP, this function can modify + * term->num_samples_already_skipped. + * + * @param old_sample The value of the previous sample (0/1). + * @param sample The value of the current sample (0/1). + * @param term The term that should be checked for a match. Must not be NULL. + * + * @retval TRUE The current sample matches the specified term. + * @retval FALSE The current sample doesn't match the specified term, or an + * invalid term was provided. + * + * @private + */ +__attribute__((always_inline)) +static inline gboolean sample_matches(uint8_t old_sample, uint8_t sample, struct srd_term *term) +{ + /* Caller ensures term != NULL. */ + + switch (term->type) { + case SRD_TERM_HIGH: + if (sample == 1) + return TRUE; + break; + case SRD_TERM_LOW: + if (sample == 0) + return TRUE; + break; + case SRD_TERM_RISING_EDGE: + if (old_sample == 0 && sample == 1) + return TRUE; + break; + case SRD_TERM_FALLING_EDGE: + if (old_sample == 1 && sample == 0) + return TRUE; + break; + case SRD_TERM_EITHER_EDGE: + if ((old_sample == 1 && sample == 0) || (old_sample == 0 && sample == 1)) + return TRUE; + break; + case SRD_TERM_NO_EDGE: + if ((old_sample == 0 && sample == 0) || (old_sample == 1 && sample == 1)) + return TRUE; + break; + case SRD_TERM_SKIP: + if (term->num_samples_already_skipped == term->num_samples_to_skip) + return TRUE; + term->num_samples_already_skipped++; + break; + default: + srd_err("Unknown term type %d.", term->type); + break; + } + + return FALSE; +} + +/** @private */ +SRD_PRIV void condition_list_free(struct srd_decoder_inst *di) +{ + GSList *l, *ll; + + if (!di) + return; + + for (l = di->condition_list; l; l = l->next) { + ll = l->data; + if (ll) + g_slist_free_full(ll, g_free); + } + + g_slist_free(di->condition_list); + di->condition_list = NULL; +} + +static gboolean have_non_null_conds(const struct srd_decoder_inst *di) +{ + GSList *l, *cond; + + if (!di) + return FALSE; + + for (l = di->condition_list; l; l = l->next) { + cond = l->data; + if (cond) + return TRUE; + } + + return FALSE; +} + +static void update_old_pins_array(struct srd_decoder_inst *di) +{ + uint8_t sample; + int i, bit_offset; + const uint8_t *sample_pos; + + if (!di || !di->dec_channelmap) + return; + + oldpins_array_seed(di); + for (i = 0; i < di->dec_num_channels; i++) { + if (*(di->inbuf + i) == NULL) { + sample = *(di->inbuf_const + i) ? 1 : 0; + di->old_pins_array->data[i] = sample; + } else { + sample_pos = *(di->inbuf + i) + ((di->abs_cur_samplenum - di->abs_start_samplenum) / 8); + bit_offset = (di->abs_cur_samplenum - di->abs_start_samplenum) % 8; + sample = *sample_pos & (1 << bit_offset) ? 1 : 0; + di->old_pins_array->data[i] = sample; + } + } +} + +static void update_old_pins_array_initial_pins(struct srd_decoder_inst *di) +{ + uint8_t sample; + int i, bit_offset; + const uint8_t *sample_pos; + + if (!di || !di->dec_channelmap) + return; + + oldpins_array_seed(di); + for (i = 0; i < di->dec_num_channels; i++) { + if (di->old_pins_array->data[i] != SRD_INITIAL_PIN_SAME_AS_SAMPLE0) + continue; + + if (*(di->inbuf + i) == NULL) { + sample = *(di->inbuf_const + i) ? 1 : 0; + di->old_pins_array->data[i] = sample; + } else { + sample_pos = *(di->inbuf + i) + ((di->abs_cur_samplenum - di->abs_start_samplenum) / 8); + bit_offset = (di->abs_cur_samplenum - di->abs_start_samplenum) % 8; + sample = *sample_pos & (1 << bit_offset) ? 1 : 0; + di->old_pins_array->data[i] = sample; + } + } +} + +static gboolean term_matches(const struct srd_decoder_inst *di, + struct srd_term *term, gboolean *skip_allow) +{ + uint8_t old_sample, sample; + int bit_offset, ch; + const uint8_t *sample_pos; + + /* Caller ensures di, di->dec_channelmap, term, sample_pos != NULL. */ + + *skip_allow = FALSE; + if (term->type == SRD_TERM_SKIP) + return sample_matches(0, 0, term); + + ch = term->channel; + if (*(di->inbuf + ch) == NULL) { + sample = *(di->inbuf_const + ch) ? 1 : 0; + *skip_allow = TRUE; + } else { + sample_pos = *(di->inbuf + ch) + ((di->abs_cur_samplenum - di->abs_start_samplenum) / 8); + bit_offset = (di->abs_cur_samplenum - di->abs_start_samplenum) % 8; + sample = *sample_pos & (1 << bit_offset) ? 1 : 0; + } + old_sample = di->old_pins_array->data[ch]; + + return sample_matches(old_sample, sample, term); +} + +static gboolean all_terms_match(const struct srd_decoder_inst *di, + const GSList *cond, gboolean *skip_allow) +{ + const GSList *l; + struct srd_term *term; + + /* Caller ensures di, cond, sample_pos != NULL. */ + + for (l = cond; l; l = l->next) { + term = l->data; + if (!term_matches(di, term, skip_allow)) + return FALSE; + } + + return TRUE; +} + +static gboolean find_match(struct srd_decoder_inst *di) +{ + uint64_t j; + GSList *l, *cond; + gboolean skip_allow; + gboolean all_skip_allow = TRUE; + + /* Caller ensures di != NULL. */ + + /* Check whether the condition list is NULL/empty. */ + if (!di->condition_list) { + srd_dbg("NULL/empty condition list, automatic match."); + return TRUE; + } + + /* Check whether we have any non-NULL conditions. */ + if (!have_non_null_conds(di)) { + srd_dbg("Only NULL conditions in list, automatic match."); + return TRUE; + } + + /* di->match_array is 0 here. Create a new GArray. */ + di->match_array = 0; + + /* Sample 0: Set di->old_pins_array for SRD_INITIAL_PIN_SAME_AS_SAMPLE0 pins. */ + if (di->first_pos) { + di->first_pos = FALSE; + update_old_pins_array_initial_pins(di); + } + + if (di->abs_cur_matched) + di->abs_cur_samplenum++; + + while (di->abs_cur_samplenum < di->abs_end_samplenum) { + + /* Check whether the current sample matches at least one of the conditions (logical OR). */ + /* IMPORTANT: We need to check all conditions, even if there was a match already! */ + for (l = di->condition_list, j = 0; l; l = l->next, j++) { + cond = l->data; + if (!cond) + continue; + + /* All terms in 'cond' must match (logical AND). */ + if (all_terms_match(di, cond, &skip_allow)) { + all_skip_allow = FALSE; + di->match_array |= (1 << j); + } else { + all_skip_allow &= skip_allow; + } + } + + update_old_pins_array(di); + + /* If at least one condition matched we're done. */ + di->abs_cur_matched = (di->match_array != 0); + if (di->abs_cur_matched) + return TRUE; + + if (all_skip_allow) + di->abs_cur_samplenum = di->abs_end_samplenum; + else + di->abs_cur_samplenum++; + } + + return FALSE; +} + +/** + * Process available samples and check if they match the defined conditions. + * + * This function returns if there is an error, or when a match is found, or + * when all samples have been processed (whether a match was found or not). + * This function immediately terminates when the decoder's wait() method + * invocation shall get terminated. + * + * @param di The decoder instance to use. Must not be NULL. + * @param found_match Will be set to TRUE if at least one condition matched, + * FALSE otherwise. Must not be NULL. + * + * @retval SRD_OK No errors occured, see found_match for the result. + * @retval SRD_ERR_ARG Invalid arguments. + * + * @private + */ +SRD_PRIV int process_samples_until_condition_match(struct srd_decoder_inst *di, gboolean *found_match) +{ + if (!di || !found_match) + return SRD_ERR_ARG; + + *found_match = FALSE; + if (di->want_wait_terminate) + return SRD_OK; + + /* Check if any of the current condition(s) match. */ + while (TRUE) { + /* Feed the (next chunk of the) buffer to find_match(). */ + *found_match = find_match(di); + + /* Did we handle all samples yet? */ + if (di->abs_cur_samplenum >= di->abs_end_samplenum) { + srd_dbg("Done, handled all samples (abs cur %" PRIu64 + " / abs end %" PRIu64 ").", + di->abs_cur_samplenum, di->abs_end_samplenum); + return SRD_OK; + } + + /* If we didn't find a match, continue looking. */ + if (!(*found_match)) + continue; + + /* At least one condition matched, return. */ + return SRD_OK; + } + + return SRD_OK; +} + +/** + * Worker thread (per PD-stack). + * + * @param data Pointer to the lowest-level PD's device instance. + * Must not be NULL. + * + * @return NULL if there was an error. + */ +static gpointer di_thread(gpointer data) +{ + PyObject *py_res; + struct srd_decoder_inst *di; + int wanted_term; + PyGILState_STATE gstate; + + if (!data) + return NULL; + + di = data; + + srd_dbg("%s: Starting thread routine for decoder.", di->inst_id); + + gstate = PyGILState_Ensure(); + + /* + * Call self.decode(). Only returns if the PD throws an exception. + * "Regular" termination of the decode() method is not expected. + */ + //Py_IncRef(di->py_inst); + srd_dbg("%s: Calling decode().", di->inst_id); + py_res = PyObject_CallMethod(di->py_inst, "decode", NULL); + srd_dbg("%s: decode() terminated.", di->inst_id); + + if (!py_res) + di->decoder_state = SRD_ERR; + + /* + * Make sure to unblock potentially pending srd_inst_decode() + * calls in application threads after the decode() method might + * have terminated, while it neither has processed sample data + * nor has terminated upon request. This happens e.g. when "need + * a samplerate to decode" exception is thrown. + */ + g_mutex_lock(&di->data_mutex); + wanted_term = di->want_wait_terminate; + di->want_wait_terminate = TRUE; + di->handled_all_samples = TRUE; + g_cond_signal(&di->handled_all_samples_cond); + g_mutex_unlock(&di->data_mutex); + + /* + * Check for the termination cause of the decode() method. + * Though this is mostly for information. + */ + if (!py_res && wanted_term) { + /* + * Silently ignore errors upon return from decode() calls + * when termination was requested. Terminate the thread + * which executed this instance's decode() logic. + */ + srd_dbg("%s: Thread done (!res, want_term).", di->inst_id); + PyErr_Clear(); + PyGILState_Release(gstate); + return NULL; + } + if (!py_res) { + /* + * The decode() invocation terminated unexpectedly. Have + * the back trace printed, and terminate the thread which + * executed the decode() method. + */ + srd_dbg("%s: decode() terminated unrequested.", di->inst_id); + srd_exception_catch(NULL, "Protocol decoder instance %s: ", di->inst_id); + srd_dbg("%s: Thread done (!res, !want_term).", di->inst_id); + PyGILState_Release(gstate); + return NULL; + } + + /* + * TODO: By design the decode() method is not supposed to terminate. + * Nevertheless we have the thread joined, and srd backend calls to + * decode() will re-start another thread transparently. + */ + srd_dbg("%s: decode() terminated (req %d).", di->inst_id, wanted_term); + Py_DecRef(py_res); + PyErr_Clear(); + + PyGILState_Release(gstate); + + srd_dbg("%s: Thread done (with res).", di->inst_id); + + return NULL; +} + /** * Decode a chunk of samples. * + * The calls to this function must provide the samples that shall be + * used by the protocol decoder + * - in the correct order ([...]5, 6, 4, 7, 8[...] is a bug), + * - starting from sample zero (2, 3, 4, 5, 6[...] is a bug), + * - consecutively, with no gaps (0, 1, 2, 4, 5[...] is a bug). + * + * The start- and end-sample numbers are absolute sample numbers (relative + * to the start of the whole capture/file/stream), i.e. they are not relative + * sample numbers within the chunk specified by 'inbuf' and 'inbuflen'. + * + * Correct example (4096 samples total, 4 chunks @ 1024 samples each): + * srd_inst_decode(di, 0, 1024, inbuf, 1024, 1); + * srd_inst_decode(di, 1024, 2048, inbuf, 1024, 1); + * srd_inst_decode(di, 2048, 3072, inbuf, 1024, 1); + * srd_inst_decode(di, 3072, 4096, inbuf, 1024, 1); + * + * The chunk size ('inbuflen') can be arbitrary and can differ between calls. + * + * Correct example (4096 samples total, 7 chunks @ various samples each): + * srd_inst_decode(di, 0, 1024, inbuf, 1024, 1); + * srd_inst_decode(di, 1024, 1124, inbuf, 100, 1); + * srd_inst_decode(di, 1124, 1424, inbuf, 300, 1); + * srd_inst_decode(di, 1424, 1643, inbuf, 219, 1); + * srd_inst_decode(di, 1643, 2048, inbuf, 405, 1); + * srd_inst_decode(di, 2048, 3072, inbuf, 1024, 1); + * srd_inst_decode(di, 3072, 4096, inbuf, 1024, 1); + * + * INCORRECT example (4096 samples total, 4 chunks @ 1024 samples each, but + * the start- and end-samplenumbers are not absolute): + * srd_inst_decode(di, 0, 1024, inbuf, 1024, 1); + * srd_inst_decode(di, 0, 1024, inbuf, 1024, 1); + * srd_inst_decode(di, 0, 1024, inbuf, 1024, 1); + * srd_inst_decode(di, 0, 1024, inbuf, 1024, 1); + * * @param di The decoder instance to call. Must not be NULL. - * @param start_samplenum The starting sample number for the buffer's sample - * set, relative to the start of capture. - * @param end_samplenum The ending sample number for the buffer's sample - * set, relative to the start of capture. + * @param abs_start_samplenum The absolute starting sample number for the + * buffer's sample set, relative to the start of capture. + * @param abs_end_samplenum The absolute ending sample number for the + * buffer's sample set, relative to the start of capture. * @param inbuf The buffer to decode. Must not be NULL. * @param inbuflen Length of the buffer. Must be > 0. + * @param unitsize The number of bytes per sample. Must be > 0. * * @return SRD_OK upon success, a (negative) error code otherwise. * * @private */ -SRD_PRIV int srd_inst_decode(const struct srd_decoder_inst *di, uint8_t chunk_type, - uint64_t start_samplenum, uint64_t end_samplenum, - const uint8_t **inbuf, const uint8_t *inbuf_const, char **error) +SRD_PRIV int srd_inst_decode(struct srd_decoder_inst *di, + uint64_t abs_start_samplenum, uint64_t abs_end_samplenum, + const uint8_t **inbuf, const uint8_t *inbuf_const, uint64_t inbuflen, + char **error) { - PyObject *py_res; - srd_logic *logic; - /* Return an error upon unusable input. */ if (!di) { - *error = g_strdup("Empty decoder instance"); + *error = g_strdup("empty decoder instance"); return SRD_ERR_ARG; } - if (end_samplenum < start_samplenum) { - *error = g_strdup("Invalid start/end index couple"); + if (!inbuf) { + *error = g_strdup("NULL buffer pointer"); + return SRD_ERR_ARG; + } + if (inbuflen == 0) { + *error = g_strdup("empty buffer"); return SRD_ERR_ARG; } - srd_dbg("Calling decode(), start sample %" PRIu64 ", end sample %" - PRIu64 " (%" PRIu64 " samples, instance %s.", - start_samplenum, end_samplenum, - end_samplenum - start_samplenum + 1, di->inst_id); + if (di->first_pos) { + di->abs_cur_samplenum = abs_start_samplenum; + } - /* - * Create new srd_logic object. Each iteration around the PD's loop - * will fill one sample into this object. - */ - logic = di->py_logic; - logic->start_samplenum = start_samplenum; - if (chunk_type == 0) { - logic->itercnt = 0; // *inbuf is a byte pointer, 8bit align - logic->logic_mask = 0; + if (abs_start_samplenum != di->abs_cur_samplenum || + abs_end_samplenum < abs_start_samplenum) { + srd_dbg("Incorrect sample numbers: start=%" PRIu64 ", cur=%" + PRIu64 ", end=%" PRIu64 ".", abs_start_samplenum, + di->abs_cur_samplenum, abs_end_samplenum); + return SRD_ERR_ARG; } - logic->inbuf = (uint8_t **)inbuf; - logic->inbuf_const = inbuf_const; - logic->samplenum = end_samplenum - start_samplenum + 1; - Py_INCREF(logic); - //Py_IncRef(di->py_inst); - if (!(py_res = PyObject_CallMethod(di->py_inst, "decode", - "KKO", start_samplenum, end_samplenum, logic))) { - srd_exception_catch(error, "Protocol decoder instance %s", - di->inst_id); - return SRD_ERR_PYTHON; - } - Py_DecRef(py_res); + srd_dbg("Decoding: abs start sample %" PRIu64 ", abs end sample %" + PRIu64 " (%" PRIu64 " samples, %" PRIu64 " bytes), instance %s.", + abs_start_samplenum, abs_end_samplenum, + abs_end_samplenum - abs_start_samplenum, inbuflen, di->inst_id); - if (logic->logic_mask == 0) { - logic->itercnt -= logic->samplenum; + /* If this is the first call, start the worker thread. */ + if (!di->thread_handle) { + srd_dbg("No worker thread for this decoder stack " + "exists yet, creating one: %s.", di->inst_id); + di->thread_handle = g_thread_new(di->inst_id, + di_thread, di); } + /* Push the new sample chunk to the worker thread. */ + g_mutex_lock(&di->data_mutex); + di->abs_start_samplenum = abs_start_samplenum & ~7ULL; + di->abs_end_samplenum = abs_end_samplenum; + di->inbuf = inbuf; + di->inbuf_const = inbuf_const; + di->inbuflen = inbuflen; + di->got_new_samples = TRUE; + di->handled_all_samples = FALSE; + + /* Signal the thread that we have new data. */ + g_cond_signal(&di->got_new_samples_cond); + g_mutex_unlock(&di->data_mutex); + + /* When all samples in this chunk were handled, return. */ + g_mutex_lock(&di->data_mutex); + while (!di->handled_all_samples && !di->want_wait_terminate) + g_cond_wait(&di->handled_all_samples_cond, &di->data_mutex); + g_mutex_unlock(&di->data_mutex); + + if (di->want_wait_terminate) + return SRD_ERR_TERM_REQ; + return SRD_OK; } +/** + * Terminate current decoder work, prepare for re-use on new input data. + * + * Terminates all decoder operations in the specified decoder instance + * and the instances stacked on top of it. Resets internal state such + * that the previously constructed stack can process new input data that + * is not related to previously processed input data. This avoids the + * expensive and complex re-construction of decoder stacks. + * + * Callers are expected to follow up with start, metadata, and decode + * calls like they would for newly constructed decoder stacks. + * + * @param di The decoder instance to call. Must not be NULL. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * + * @private + */ +SRD_PRIV int srd_inst_terminate_reset(struct srd_decoder_inst *di) +{ + PyGILState_STATE gstate; + PyObject *py_ret; + GSList *l; + int ret; + + if (!di) + return SRD_ERR_ARG; + + /* + * Request termination and wait for previously initiated + * background operation to finish. Reset internal state, but + * do not start releasing resources yet. This shall result in + * decoders' state just like after creation. This block handles + * the C language library side. + */ + srd_dbg("Terminating instance %s", di->inst_id); + srd_inst_join_decode_thread(di); + srd_inst_reset_state(di); + + /* + * Have the Python side's .reset() method executed (if the PD + * implements it). It's assumed that .reset() assigns variables + * very much like __init__() used to do in the past. Thus memory + * that was allocated in previous calls gets released by Python + * as it's not referenced any longer. + */ + gstate = PyGILState_Ensure(); + if (PyObject_HasAttrString(di->py_inst, "reset")) { + srd_dbg("Calling reset() of instance %s", di->inst_id); + py_ret = PyObject_CallMethod(di->py_inst, "reset", NULL); + Py_XDECREF(py_ret); + } + PyGILState_Release(gstate); + + /* Pass the "restart" request to all stacked decoders. */ + for (l = di->next_di; l; l = l->next) { + ret = srd_inst_terminate_reset(l->data); + if (ret != SRD_OK) + return ret; + } + + return di->decoder_state; +} + /** @private */ SRD_PRIV void srd_inst_free(struct srd_decoder_inst *di) { GSList *l; struct srd_pd_output *pdo; - srd_logic *logic = di->py_logic; + PyGILState_STATE gstate; - srd_dbg("Freeing instance %s", di->inst_id); + srd_dbg("Freeing instance %s.", di->inst_id); - if ((di->decoder->channels || di->decoder->opt_channels) != 0 ) { - if (logic && logic->sample) - Py_XDECREF(logic->sample); - if (logic) - Py_XDECREF(logic); - } + srd_inst_join_decode_thread(di); + srd_inst_reset_state(di); + + gstate = PyGILState_Ensure(); Py_DecRef(di->py_inst); + if (di->py_pinvalues) { + Py_DecRef(di->py_pinvalues); + } + PyGILState_Release(gstate); + g_free(di->inst_id); g_free(di->dec_channelmap); - g_free(di->channel_samples); g_slist_free(di->next_di); for (l = di->pd_output; l; l = l->next) { pdo = l->data; g_free(pdo->proto_id); + if (pdo->meta_name) + g_free(pdo->meta_name); + if (pdo->meta_descr) + g_free(pdo->meta_descr); g_free(pdo); } g_slist_free(di->pd_output); @@ -633,27 +1335,12 @@ SRD_PRIV void srd_inst_free(struct srd_decoder_inst *di) } /** @private */ -SRD_PRIV void srd_inst_free_all(struct srd_session *sess, GSList *stack) +SRD_PRIV void srd_inst_free_all(struct srd_session *sess) { - GSList *l; - struct srd_decoder_inst *di; - - if (session_is_valid(sess) != SRD_OK) { - srd_err("Invalid session."); + if (!sess) return; - } - di = NULL; - for (l = stack ? stack : sess->di_list; di == NULL && l != NULL; l = l->next) { - di = l->data; - if (di->next_di) - srd_inst_free_all(sess, di->next_di); - srd_inst_free(di); - } - if (!stack) { - g_slist_free(sess->di_list); - sess->di_list = NULL; - } + g_slist_free_full(sess->di_list, (GDestroyNotify)srd_inst_free); } /** @} */ diff --git a/libsigrokdecode4DSL/libsigrokdecode-internal.h b/libsigrokdecode4DSL/libsigrokdecode-internal.h old mode 100644 new mode 100755 index 805d62a9..4a1efa66 --- a/libsigrokdecode4DSL/libsigrokdecode-internal.h +++ b/libsigrokdecode4DSL/libsigrokdecode-internal.h @@ -16,8 +16,7 @@ * 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program; if not, see . */ #ifndef LIBSIGROKDECODE_LIBSIGROKDECODE_INTERNAL_H @@ -30,41 +29,52 @@ #include "libsigrokdecode.h" #include +enum { + SRD_TERM_HIGH, + SRD_TERM_LOW, + SRD_TERM_RISING_EDGE, + SRD_TERM_FALLING_EDGE, + SRD_TERM_EITHER_EDGE, + SRD_TERM_NO_EDGE, + SRD_TERM_SKIP, +}; + +struct srd_term { + int type; + int channel; + uint64_t num_samples_to_skip; + uint64_t num_samples_already_skipped; +}; + /* Custom Python types: */ typedef struct { PyObject_HEAD struct srd_decoder_inst *di; - uint64_t start_samplenum; - float itercnt; - uint8_t **inbuf; - const uint8_t *inbuf_const; - uint64_t samplenum; + uint64_t abs_start_samplenum; + unsigned int itercnt; + uint8_t *inbuf; + uint64_t inbuflen; PyObject *sample; - - uint64_t exp_logic; - int edge_index; - uint64_t logic_mask; - uint64_t cur_pos; } srd_logic; /* srd.c */ SRD_PRIV int srd_decoder_searchpath_add(const char *path); /* session.c */ -SRD_PRIV int session_is_valid(struct srd_session *sess); SRD_PRIV struct srd_pd_callback *srd_pd_output_callback_find(struct srd_session *sess, int output_type); /* instance.c */ -SRD_PRIV struct srd_decoder_inst *srd_inst_find_by_obj( const GSList *stack, - const PyObject *obj); SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di, char **error); -SRD_PRIV int srd_inst_decode(const struct srd_decoder_inst *di, uint8_t chunk_type, - uint64_t start_samplenum, uint64_t end_samplenum, - const uint8_t **inbuf, const uint8_t *inbuf_const, char **error); +SRD_PRIV void condition_list_free(struct srd_decoder_inst *di); +SRD_PRIV int srd_inst_decode(struct srd_decoder_inst *di, + uint64_t abs_start_samplenum, uint64_t abs_end_samplenum, + const uint8_t **inbuf, const uint8_t *inbuf_const, uint64_t inbuflen, char **error); +SRD_PRIV int process_samples_until_condition_match(struct srd_decoder_inst *di, gboolean *found_match); +SRD_PRIV int srd_inst_terminate_reset(struct srd_decoder_inst *di); SRD_PRIV void srd_inst_free(struct srd_decoder_inst *di); -SRD_PRIV void srd_inst_free_all(struct srd_session *sess, GSList *stack); +SRD_PRIV void srd_inst_free_all(struct srd_session *sess); /* log.c */ #if defined(G_OS_WIN32) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)) @@ -89,6 +99,7 @@ SRD_PRIV long srd_decoder_apiver(const struct srd_decoder *d); /* type_decoder.c */ SRD_PRIV PyObject *srd_Decoder_type_new(void); +SRD_PRIV const char *output_type_name(unsigned int idx); /* type_logic.c */ SRD_PRIV PyObject *srd_logic_type_new(void); @@ -99,8 +110,12 @@ PyMODINIT_FUNC PyInit_sigrokdecode(void); /* util.c */ SRD_PRIV PyObject *py_import_by_name(const char *name); SRD_PRIV int py_attr_as_str(PyObject *py_obj, const char *attr, char **outstr); +SRD_PRIV int py_attr_as_strlist(PyObject *py_obj, const char *attr, GSList **outstrlist); SRD_PRIV int py_dictitem_as_str(PyObject *py_obj, const char *key, char **outstr); SRD_PRIV int py_dictitem_to_int(PyObject *py_obj, const char *key); +SRD_PRIV int py_listitem_as_str(PyObject *py_obj, int idx, char **outstr); +SRD_PRIV int py_pydictitem_as_str(PyObject *py_obj, PyObject *py_key, char **outstr); +SRD_PRIV int py_pydictitem_as_long(PyObject *py_obj, PyObject *py_key, uint64_t *out); SRD_PRIV int py_str_as_str(PyObject *py_str, char **outstr); SRD_PRIV int py_strseq_to_char(PyObject *py_strseq, char ***out_strv); SRD_PRIV GVariant *py_obj_to_variant(PyObject *py_obj); diff --git a/libsigrokdecode4DSL/libsigrokdecode.h b/libsigrokdecode4DSL/libsigrokdecode.h old mode 100644 new mode 100755 index 3d764f24..804b00f9 --- a/libsigrokdecode4DSL/libsigrokdecode.h +++ b/libsigrokdecode4DSL/libsigrokdecode.h @@ -16,8 +16,7 @@ * 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program; if not, see . */ #ifndef LIBSIGROKDECODE_LIBSIGROKDECODE_H @@ -30,7 +29,15 @@ extern "C" { #endif -//struct srd_session; +struct srd_session { + int session_id; + + /* List of decoder instances. */ + GSList *di_list; + + /* List of frontend callbacks to receive decoder output. */ + GSList *callbacks; +}; /** * @file @@ -44,7 +51,7 @@ extern "C" { * The correct way to get/use the libsigrokdecode API functions is: * * @code{.c} - * #include + * #include * @endcode */ @@ -73,6 +80,7 @@ enum srd_error_code { SRD_ERR_BUG = -4, /**< Errors hinting at internal bugs */ SRD_ERR_PYTHON = -5, /**< Python C API error */ SRD_ERR_DECODERS_DIR = -6, /**< Protocol decoder path invalid */ + SRD_ERR_TERM_REQ = -7, /**< Termination requested */ /* * Note: When adding entries here, don't forget to also update the @@ -120,16 +128,6 @@ enum srd_loglevel { #define SRD_PRIV #endif -struct srd_session { - int session_id; - - /* List of decoder instances. */ - GSList *di_list; - - /* List of frontend callbacks to receive decoder output. */ - GSList *callbacks; -}; - /* * When adding an output type, don't forget to... * - expose it to PDs in controller.c:PyInit_sigrokdecode() @@ -175,6 +173,15 @@ struct srd_decoder { */ char *license; + /** List of possible decoder input IDs. */ + GSList *inputs; + + /** List of possible decoder output IDs. */ + GSList *outputs; + + /** List of tags associated with this decoder. */ + GSList *tags; + /** List of channels required by this decoder. */ GSList *channels; @@ -186,7 +193,7 @@ struct srd_decoder { * supported annotation output. */ GSList *annotations; - GSList *ann_types; + GSList *ann_types; /** * List of annotation rows (row items: id, description, and a list @@ -210,6 +217,12 @@ struct srd_decoder { void *py_dec; }; +enum srd_initial_pin { + SRD_INITIAL_PIN_LOW, + SRD_INITIAL_PIN_HIGH, + SRD_INITIAL_PIN_SAME_AS_SAMPLE0, +}; + /** * Structure which contains information about one protocol decoder channel. * For example, I2C has two channels, SDA and SCL. @@ -223,8 +236,8 @@ struct srd_channel { char *desc; /** The index of the channel, i.e. its order in the list of channels. */ int order; - /** The type of the channel, such us: sclk/sdata/.../others */ - int type; + /** The type of the channel, such us: sclk/sdata/.../others */ + int type; }; struct srd_decoder_option { @@ -244,17 +257,64 @@ struct srd_decoder_inst { struct srd_decoder *decoder; struct srd_session *sess; void *py_inst; - void *py_logic; + void *py_pinvalues; char *inst_id; GSList *pd_output; int dec_num_channels; int *dec_channelmap; - uint8_t *channel_samples; GSList *next_di; - uint64_t cur_pos; - uint64_t logic_mask; - uint64_t exp_logic; - int edge_index; + + /** List of conditions a PD wants to wait for. */ + GSList *condition_list; + + /** Array of booleans denoting which conditions matched. */ + uint64_t match_array; + + /** Absolute start sample number. */ + uint64_t abs_start_samplenum; + + /** Absolute end sample number. */ + uint64_t abs_end_samplenum; + + /** Pointer to the buffer/chunk of input samples. */ + const uint8_t **inbuf; + + /** Pointer to the buffer/chunk of input const blocks. */ + const uint8_t *inbuf_const; + + /** Length (in bytes) of the input sample buffer. */ + uint64_t inbuflen; + + /** Absolute current samplenumber. */ + uint64_t abs_cur_samplenum; + + /** Absolute current sample matched conditions. */ + gboolean abs_cur_matched; + + /** Array of "old" (previous sample) pin values. */ + GArray *old_pins_array; + + /** Handle for this PD stack's worker thread. */ + GThread *thread_handle; + + /** Indicates whether new samples are available for processing. */ + gboolean got_new_samples; + + /** Indicates whether the worker thread has handled all samples. */ + gboolean handled_all_samples; + + /** Requests termination of wait() and decode(). */ + gboolean want_wait_terminate; + + /** First entry of wait(). */ + gboolean first_pos; + + /** Indicates the current state of the decoder stack. */ + int decoder_state; + + GCond got_new_samples_cond; + GCond handled_all_samples_cond; + GMutex data_mutex; }; struct srd_pd_output { @@ -276,13 +336,13 @@ struct srd_proto_data { }; struct srd_proto_data_annotation { int ann_class; - int ann_type; + int ann_type; char **ann_text; }; struct srd_proto_data_binary { int bin_class; uint64_t size; - unsigned char *data; + const unsigned char *data; }; typedef void (*srd_pd_output_callback)(struct srd_proto_data *pdata, @@ -297,15 +357,17 @@ struct srd_pd_callback { /* srd.c */ SRD_API int srd_init(const char *path); SRD_API int srd_exit(void); +SRD_API GSList *srd_searchpaths_get(void); /* session.c */ SRD_API int srd_session_new(struct srd_session **sess); SRD_API int srd_session_start(struct srd_session *sess, char **error); SRD_API int srd_session_metadata_set(struct srd_session *sess, int key, GVariant *data); -SRD_API int srd_session_send(struct srd_session *sess, uint8_t chunk_type, - uint64_t start_samplenum, uint64_t end_samplenum, - const uint8_t **inbuf, const uint8_t *inbuf_const, char **error); +SRD_API int srd_session_send(struct srd_session *sess, + uint64_t abs_start_samplenum, uint64_t abs_end_samplenum, + const uint8_t **inbuf, const uint8_t *inbuf_const, uint64_t inbuflen, char **error); +SRD_API int srd_session_terminate_reset(struct srd_session *sess); SRD_API int srd_session_destroy(struct srd_session *sess); SRD_API int srd_pd_output_callback_add(struct srd_session *sess, int output_type, srd_pd_output_callback cb, void *cb_data); @@ -330,12 +392,15 @@ SRD_API int srd_inst_stack(struct srd_session *sess, struct srd_decoder_inst *di_from, struct srd_decoder_inst *di_to); SRD_API struct srd_decoder_inst *srd_inst_find_by_id(struct srd_session *sess, const char *inst_id); +SRD_API int srd_inst_initial_pins_set_all(struct srd_decoder_inst *di, + GArray *initial_pins); /* log.c */ typedef int (*srd_log_callback)(void *cb_data, int loglevel, const char *format, va_list args); SRD_API int srd_log_loglevel_set(int loglevel); SRD_API int srd_log_loglevel_get(void); +SRD_API int srd_log_callback_get(srd_log_callback *cb, void **cb_data); SRD_API int srd_log_callback_set(srd_log_callback cb, void *cb_data); SRD_API int srd_log_callback_set_default(void); diff --git a/libsigrokdecode4DSL/libsigrokdecode4DSL.pc.in b/libsigrokdecode4DSL/libsigrokdecode4DSL.pc.in old mode 100644 new mode 100755 diff --git a/libsigrokdecode4DSL/log.c b/libsigrokdecode4DSL/log.c old mode 100644 new mode 100755 index 95f08e3e..c93a5f55 --- a/libsigrokdecode4DSL/log.c +++ b/libsigrokdecode4DSL/log.c @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program; if not, see . */ #include "config.h" @@ -130,6 +129,28 @@ SRD_API int srd_log_callback_set(srd_log_callback cb, void *cb_data) return SRD_OK; } +/** + * Get the libsigrokdecode log callback routine and callback data. + * + * @param[out] cb Pointer to a function pointer to receive the log callback + * function. Optional, can be NULL. + * @param[out] cb_data Pointer to a void pointer to receive the log callback's + * additional arguments. Optional, can be NULL. + * + * @return SRD_OK upon success. + * + * @since 0.5.2 + */ +SRD_API int srd_log_callback_get(srd_log_callback *cb, void **cb_data) +{ + if (cb) + *cb = srd_log_cb; + if (cb_data) + *cb_data = srd_log_cb_data; + + return SRD_OK; +} + /** * Set the libsigrokdecode log callback to the default built-in one. * @@ -157,15 +178,15 @@ static int srd_logv(void *cb_data, int loglevel, const char *format, /* This specific log callback doesn't need the void pointer data. */ (void)cb_data; - /* Only output messages of at least the selected loglevel(s). */ - if (loglevel > cur_loglevel) - return SRD_OK; + (void)loglevel; if (fputs("srd: ", stderr) < 0 - || g_vfprintf(stderr, format, args) < 0 + || vfprintf(stderr, format, args) < 0 || putc('\n', stderr) < 0) return SRD_ERR; + fflush(stderr); + return SRD_OK; } @@ -175,6 +196,10 @@ SRD_PRIV int srd_log(int loglevel, const char *format, ...) int ret; va_list args; + /* Only output messages of at least the selected loglevel(s). */ + if (loglevel > cur_loglevel) + return SRD_OK; + va_start(args, format); ret = srd_log_cb(srd_log_cb_data, loglevel, format, args); va_end(args); diff --git a/libsigrokdecode4DSL/m4/sigrok.m4 b/libsigrokdecode4DSL/m4/sigrok.m4 old mode 100644 new mode 100755 diff --git a/libsigrokdecode4DSL/module_sigrokdecode.c b/libsigrokdecode4DSL/module_sigrokdecode.c old mode 100644 new mode 100755 index 73243b2d..61268b3d --- a/libsigrokdecode4DSL/module_sigrokdecode.c +++ b/libsigrokdecode4DSL/module_sigrokdecode.c @@ -23,8 +23,6 @@ /** @cond PRIVATE */ -SRD_PRIV PyObject *srd_logic_type = NULL; - /* * When initialized, a reference to this module inside the Python interpreter * lives here. @@ -43,7 +41,10 @@ static struct PyModuleDef sigrokdecode_module = { /** @cond PRIVATE */ PyMODINIT_FUNC PyInit_sigrokdecode(void) { - PyObject *mod, *Decoder_type, *logic_type; + PyObject *mod, *Decoder_type; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); mod = PyModule_Create(&sigrokdecode_module); if (!mod) @@ -55,12 +56,6 @@ PyMODINIT_FUNC PyInit_sigrokdecode(void) if (PyModule_AddObject(mod, "Decoder", Decoder_type) < 0) goto err_out; - logic_type = srd_logic_type_new(); - if (!logic_type) - goto err_out; - if (PyModule_AddObject(mod, "srd_logic", logic_type) < 0) - goto err_out; - /* Expose output types as symbols in the sigrokdecode module */ if (PyModule_AddIntConstant(mod, "OUTPUT_ANN", SRD_OUTPUT_ANN) < 0) goto err_out; @@ -74,14 +69,18 @@ PyMODINIT_FUNC PyInit_sigrokdecode(void) if (PyModule_AddIntConstant(mod, "SRD_CONF_SAMPLERATE", SRD_CONF_SAMPLERATE) < 0) goto err_out; - srd_logic_type = logic_type; mod_sigrokdecode = mod; + PyGILState_Release(gstate); + return mod; + err_out: Py_XDECREF(mod); - srd_exception_catch(NULL, "Failed to initialize module"); + srd_exception_catch(NULL, "Failed to initialize module"); + PyGILState_Release(gstate); return NULL; } + /** @endcond */ diff --git a/libsigrokdecode4DSL/session.c b/libsigrokdecode4DSL/session.c old mode 100644 new mode 100755 index 24fde25b..e60a9da1 --- a/libsigrokdecode4DSL/session.c +++ b/libsigrokdecode4DSL/session.c @@ -3,7 +3,6 @@ * * Copyright (C) 2010 Uwe Hermann * Copyright (C) 2013 Bert Vermeulen - * Copyright (C) 2016 DreamSourceLab * * 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 @@ -46,16 +45,6 @@ SRD_PRIV int max_session_id = -1; /** @endcond */ -/** @private */ -SRD_PRIV int session_is_valid(struct srd_session *sess) -{ - - if (!sess || sess->session_id < 1) - return SRD_ERR; - - return SRD_OK; -} - /** * Create a decoding session. * @@ -63,7 +52,7 @@ SRD_PRIV int session_is_valid(struct srd_session *sess) * output callbacks. * * @param sess A pointer which will hold a pointer to a newly - * initialized session on return. + * initialized session on return. Must not be NULL. * * @return SRD_OK upon success, a (negative) error code otherwise. * @@ -71,11 +60,8 @@ SRD_PRIV int session_is_valid(struct srd_session *sess) */ SRD_API int srd_session_new(struct srd_session **sess) { - - if (!sess) { - srd_err("Invalid session pointer."); + if (!sess) return SRD_ERR_ARG; - } *sess = g_malloc(sizeof(struct srd_session)); (*sess)->session_id = ++max_session_id; @@ -84,7 +70,7 @@ SRD_API int srd_session_new(struct srd_session **sess) /* Keep a list of all sessions, so we can clean up as needed. */ sessions = g_slist_append(sessions, *sess); - srd_dbg("Created session %d.", (*sess)->session_id); + srd_dbg("Creating session %d.", (*sess)->session_id); return SRD_OK; } @@ -95,7 +81,7 @@ SRD_API int srd_session_new(struct srd_session **sess) * Decoders, instances and stack must have been prepared beforehand, * and all SRD_CONF parameters set. * - * @param sess The session to start. + * @param sess The session to start. Must not be NULL. * * @return SRD_OK upon success, a (negative) error code otherwise. * @@ -107,18 +93,16 @@ SRD_API int srd_session_start(struct srd_session *sess, char **error) struct srd_decoder_inst *di; int ret; - if (session_is_valid(sess) != SRD_OK) { - srd_err("Invalid session pointer."); - return SRD_ERR; - } + if (!sess) + return SRD_ERR_ARG; - srd_dbg("Calling start() on all instances in session %d.", sess->session_id); + srd_dbg("Calling start() of all instances in session %d.", sess->session_id); - /* Run the start() method on all decoders receiving frontend data. */ + /* Run the start() method of all decoders receiving frontend data. */ ret = SRD_OK; for (d = sess->di_list; d; d = d->next) { di = d->data; - if ((ret = srd_inst_start(di, error)) != SRD_OK) + if ((ret = srd_inst_start(di, error)) != SRD_OK) break; } @@ -132,11 +116,14 @@ static int srd_inst_send_meta(struct srd_decoder_inst *di, int key, GSList *l; struct srd_decoder_inst *next_di; int ret; + PyGILState_STATE gstate; if (key != SRD_CONF_SAMPLERATE) /* This is the only key we pass on to the decoder for now. */ return SRD_OK; + gstate = PyGILState_Ensure(); + if (PyObject_HasAttrString(di->py_inst, "metadata")) { py_ret = PyObject_CallMethod(di->py_inst, "metadata", "lK", (long)SRD_CONF_SAMPLERATE, @@ -144,6 +131,8 @@ static int srd_inst_send_meta(struct srd_decoder_inst *di, int key, Py_XDECREF(py_ret); } + PyGILState_Release(gstate); + /* Push metadata to all the PDs stacked on top of this one. */ for (l = di->next_di; l; l = l->next) { next_di = l->data; @@ -157,7 +146,7 @@ static int srd_inst_send_meta(struct srd_decoder_inst *di, int key, /** * Set a metadata configuration key in a session. * - * @param sess The session to configure. + * @param sess The session to configure. Must not be NULL. * @param key The configuration key (SRD_CONF_*). * @param data The new value for the key, as a GVariant with GVariantType * appropriate to that key. A floating reference can be passed @@ -173,10 +162,8 @@ SRD_API int srd_session_metadata_set(struct srd_session *sess, int key, GSList *l; int ret; - if (session_is_valid(sess) != SRD_OK) { - srd_err("Invalid session."); + if (!sess) return SRD_ERR_ARG; - } if (!key) { srd_err("Invalid key."); @@ -200,7 +187,7 @@ SRD_API int srd_session_metadata_set(struct srd_session *sess, int key, return SRD_ERR_ARG; } - srd_dbg("Setting session %d samplerate to %"PRIu64".", + srd_dbg("Setting session %d samplerate to %"G_GUINT64_FORMAT".", sess->session_id, g_variant_get_uint64(data)); ret = SRD_OK; @@ -221,31 +208,109 @@ SRD_API int srd_session_metadata_set(struct srd_session *sess, int key, * in channel order, in the least amount of space possible. The default * channel set consists of all required channels + all optional channels. * - * @param sess The session to use. - * @param start_samplenum The sample number of the first sample in this chunk. - * @param end_samplenum The sample number of the last sample in this chunk. - * @param inbuf Pointer to sample data. - * @param inbuflen Length in bytes of the buffer. + * The size of a sample in inbuf is 'unitsize' bytes. If no channel map + * has been configured, it is the minimum number of bytes needed to store + * the default channels. + * + * The calls to this function must provide the samples that shall be + * used by the protocol decoder + * - in the correct order ([...]5, 6, 4, 7, 8[...] is a bug), + * - starting from sample zero (2, 3, 4, 5, 6[...] is a bug), + * - consecutively, with no gaps (0, 1, 2, 4, 5[...] is a bug). + * + * The start- and end-sample numbers are absolute sample numbers (relative + * to the start of the whole capture/file/stream), i.e. they are not relative + * sample numbers within the chunk specified by 'inbuf' and 'inbuflen'. + * + * Correct example (4096 samples total, 4 chunks @ 1024 samples each): + * srd_session_send(s, 0, 1023, inbuf, 1024, 1); + * srd_session_send(s, 1024, 2047, inbuf, 1024, 1); + * srd_session_send(s, 2048, 3071, inbuf, 1024, 1); + * srd_session_send(s, 3072, 4095, inbuf, 1024, 1); + * + * The chunk size ('inbuflen') can be arbitrary and can differ between calls. + * + * Correct example (4096 samples total, 7 chunks @ various samples each): + * srd_session_send(s, 0, 1023, inbuf, 1024, 1); + * srd_session_send(s, 1024, 1123, inbuf, 100, 1); + * srd_session_send(s, 1124, 1423, inbuf, 300, 1); + * srd_session_send(s, 1424, 1642, inbuf, 219, 1); + * srd_session_send(s, 1643, 2047, inbuf, 405, 1); + * srd_session_send(s, 2048, 3071, inbuf, 1024, 1); + * srd_session_send(s, 3072, 4095, inbuf, 1024, 1); + * + * INCORRECT example (4096 samples total, 4 chunks @ 1024 samples each, but + * the start- and end-samplenumbers are not absolute): + * srd_session_send(s, 0, 1023, inbuf, 1024, 1); + * srd_session_send(s, 0, 1023, inbuf, 1024, 1); + * srd_session_send(s, 0, 1023, inbuf, 1024, 1); + * srd_session_send(s, 0, 1023, inbuf, 1024, 1); + * + * @param sess The session to use. Must not be NULL. + * @param abs_start_samplenum The absolute starting sample number for the + * buffer's sample set, relative to the start of capture. + * @param abs_end_samplenum The absolute ending sample number for the + * buffer's sample set, relative to the start of capture. + * @param inbuf Pointer to sample data. Must not be NULL. + * @param inbuflen Length in bytes of the buffer. Must be > 0. + * @param unitsize The number of bytes per sample. Must be > 0. * * @return SRD_OK upon success, a (negative) error code otherwise. * * @since 0.4.0 */ -SRD_API int srd_session_send(struct srd_session *sess, uint8_t chunk_type, - uint64_t start_samplenum, uint64_t end_samplenum, - const uint8_t **inbuf, const uint8_t *inbuf_const, char **error) +SRD_API int srd_session_send(struct srd_session *sess, + uint64_t abs_start_samplenum, uint64_t abs_end_samplenum, + const uint8_t **inbuf, const uint8_t *inbuf_const, uint64_t inbuflen, char **error) { GSList *d; int ret; - if (session_is_valid(sess) != SRD_OK) { - srd_err("Invalid session."); + if (!sess) return SRD_ERR_ARG; - } for (d = sess->di_list; d; d = d->next) { - if ((ret = srd_inst_decode(d->data, chunk_type, start_samplenum, - end_samplenum, inbuf, inbuf_const, error)) != SRD_OK) + if ((ret = srd_inst_decode(d->data, abs_start_samplenum, + abs_end_samplenum, inbuf, inbuf_const, inbuflen, error)) != SRD_OK) + return ret; + } + + return SRD_OK; +} + +/** + * Terminate currently executing decoders in a session, reset internal state. + * + * All decoder instances have their .wait() method terminated, which + * shall terminate .decode() as well. Afterwards the decoders' optional + * .reset() method gets executed. + * + * This routine allows callers to abort pending expensive operations, + * when they are no longer interested in the decoders' results. Note + * that the decoder state is lost and aborted work cannot resume. + * + * This routine also allows callers to re-use previously created decoder + * stacks to process new input data which is not related to previously + * processed input data. This avoids the necessity to re-construct the + * decoder stack. + * + * @param sess The session in which to terminate decoders. Must not be NULL. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * + * @since 0.5.1 + */ +SRD_API int srd_session_terminate_reset(struct srd_session *sess) +{ + GSList *d; + int ret; + + if (!sess) + return SRD_ERR_ARG; + + for (d = sess->di_list; d; d = d->next) { + ret = srd_inst_terminate_reset(d->data); + if (ret != SRD_OK) return ret; } @@ -257,7 +322,7 @@ SRD_API int srd_session_send(struct srd_session *sess, uint8_t chunk_type, * * All decoder instances and output callbacks are properly released. * - * @param sess The session to be destroyed. + * @param sess The session to be destroyed. Must not be NULL. * * @return SRD_OK upon success, a (negative) error code otherwise. * @@ -267,14 +332,12 @@ SRD_API int srd_session_destroy(struct srd_session *sess) { int session_id; - if (!sess) { - srd_err("Invalid session."); + if (!sess) return SRD_ERR_ARG; - } session_id = sess->session_id; if (sess->di_list) - srd_inst_free_all(sess, NULL); + srd_inst_free_all(sess); if (sess->callbacks) g_slist_free_full(sess->callbacks, g_free); sessions = g_slist_remove(sessions, sess); @@ -293,6 +356,7 @@ SRD_API int srd_session_destroy(struct srd_session *sess) * stack). * * @param sess The output session in which to register the callback. + * Must not be NULL. * @param output_type The output type this callback will receive. Only one * callback per output type can be registered. * @param cb The function to call. Must not be NULL. @@ -305,12 +369,11 @@ SRD_API int srd_pd_output_callback_add(struct srd_session *sess, { struct srd_pd_callback *pd_cb; - if (session_is_valid(sess) != SRD_OK) { - srd_err("Invalid session."); + if (!sess) return SRD_ERR_ARG; - } - srd_dbg("Registering new callback for output type %d.", output_type); + srd_dbg("Registering new callback for output type %s.", + output_type_name(output_type)); pd_cb = g_malloc(sizeof(struct srd_pd_callback)); pd_cb->output_type = output_type; @@ -328,10 +391,8 @@ SRD_PRIV struct srd_pd_callback *srd_pd_output_callback_find( GSList *l; struct srd_pd_callback *tmp, *pd_cb; - if (session_is_valid(sess) != SRD_OK) { - srd_err("Invalid session."); + if (!sess) return NULL; - } pd_cb = NULL; for (l = sess->callbacks; l; l = l->next) { diff --git a/libsigrokdecode4DSL/srd.c b/libsigrokdecode4DSL/srd.c old mode 100644 new mode 100755 index 5933f881..6b2569d2 --- a/libsigrokdecode4DSL/srd.c +++ b/libsigrokdecode4DSL/srd.c @@ -105,13 +105,58 @@ static int searchpath_add_xdg_dir(const char *datadir) if (g_file_test(decdir, G_FILE_TEST_IS_DIR)) ret = srd_decoder_searchpath_add(decdir); else - ret = SRD_OK; /* just ignore non-existing directory */ + ret = SRD_OK; /* Just ignore non-existing directory. */ g_free(decdir); return ret; } +static int print_searchpaths(void) +{ + PyObject *py_paths, *py_path, *py_bytes; + PyGILState_STATE gstate; + GString *s; + GSList *l; + int i; + + s = g_string_sized_new(500); + g_string_append(s, "Protocol decoder search paths:\n"); + for (l = searchpaths; l; l = l->next) + g_string_append_printf(s, " - %s\n", (const char *)l->data); + s->str[s->len - 1] = '\0'; + srd_dbg("%s", s->str); + g_string_free(s, TRUE); + + gstate = PyGILState_Ensure(); + + py_paths = PySys_GetObject("path"); + if (!py_paths) + goto err; + + s = g_string_sized_new(500); + g_string_append(s, "Python system search paths:\n"); + for (i = 0; i < PyList_Size(py_paths); i++) { + py_path = PyList_GetItem(py_paths, i); + py_bytes = PyUnicode_AsUTF8String(py_path); + g_string_append_printf(s, " - %s\n", PyBytes_AsString(py_bytes)); + Py_DECREF(py_bytes); + } + s->str[s->len - 1] = '\0'; + srd_dbg("%s", s->str); + g_string_free(s, TRUE); + + PyGILState_Release(gstate); + + return SRD_OK; + +err: + srd_err("Unable to query Python system search paths."); + PyGILState_Release(gstate); + + return SRD_ERR_PYTHON; +} + /** * Initialize libsigrokdecode. * @@ -142,9 +187,9 @@ static int searchpath_add_xdg_dir(const char *datadir) SRD_API int srd_init(const char *path) { const char *const *sys_datadirs; - const char *env_path; size_t i; int ret; + const char *env_path; if (max_session_id != -1) { srd_err("libsigrokdecode is already initialized."); @@ -198,11 +243,25 @@ SRD_API int srd_init(const char *path) } } + /* Initialize the Python GIL (this also happens to acquire it). */ + PyEval_InitThreads(); + + /* Release the GIL (ignore return value, we don't need it here). */ + (void)PyEval_SaveThread(); + max_session_id = 0; + print_searchpaths(); + return SRD_OK; } +static void srd_session_destroy_cb(void *arg, void *ignored) +{ + (void)ignored; // Prevent unused warning + srd_session_destroy((struct srd_session *)arg); +} + /** * Shutdown libsigrokdecode. * @@ -221,15 +280,26 @@ SRD_API int srd_exit(void) { srd_dbg("Exiting libsigrokdecode."); - g_slist_foreach(sessions, (GFunc)srd_session_destroy, NULL); + g_slist_foreach(sessions, srd_session_destroy_cb, NULL); + g_slist_free(sessions); + sessions = NULL; srd_decoder_unload_all(); g_slist_free_full(searchpaths, g_free); searchpaths = NULL; + /* + * Acquire the GIL, otherwise Py_Finalize() might have issues. + * Ignore the return value, we don't need it here. + */ + if (Py_IsInitialized()) + (void)PyGILState_Ensure(); + /* Py_Finalize() returns void, any finalization errors are ignored. */ Py_Finalize(); + /* Note: No need to release the GIL since Python is shut down now. */ + max_session_id = -1; return SRD_OK; @@ -256,29 +326,56 @@ SRD_API int srd_exit(void) */ SRD_PRIV int srd_decoder_searchpath_add(const char *path) { - PyObject *py_cur_path, *py_item; + PyGILState_STATE gstate; srd_dbg("Adding '%s' to module path.", path); + gstate = PyGILState_Ensure(); + + PyObject *py_cur_path, *py_item; py_cur_path = PySys_GetObject("path"); if (!py_cur_path) - return SRD_ERR_PYTHON; + goto err; py_item = PyUnicode_FromString(path); if (!py_item) { - srd_exception_catch(NULL, "Failed to create Unicode object"); - return SRD_ERR_PYTHON; + srd_exception_catch(NULL, "Failed to create Unicode object"); + goto err; } if (PyList_Insert(py_cur_path, 0, py_item) < 0) { - srd_exception_catch(NULL, "Failed to insert path element"); + srd_exception_catch(NULL, "Failed to insert path element"); Py_DECREF(py_item); - return SRD_ERR_PYTHON; + goto err; } Py_DECREF(py_item); + PyGILState_Release(gstate); + searchpaths = g_slist_prepend(searchpaths, g_strdup(path)); return SRD_OK; + +err: + PyGILState_Release(gstate); + + return SRD_ERR_PYTHON; +} + +/** + * Return the list of protocol decoder search paths. + * + * @return The list of search paths used when loading protocol decoders. + * + * @since 0.5.1 + */ +SRD_API GSList *srd_searchpaths_get(void) +{ + GSList *paths = NULL; + + for (GSList *l = searchpaths; l; l = l->next) + paths = g_slist_append(paths, g_strdup(l->data)); + + return paths; } /** @} */ diff --git a/libsigrokdecode4DSL/tests/core.c b/libsigrokdecode4DSL/tests/core.c old mode 100644 new mode 100755 index 670d9bd8..f09a6e87 --- a/libsigrokdecode4DSL/tests/core.c +++ b/libsigrokdecode4DSL/tests/core.c @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program; if not, see . */ #include diff --git a/libsigrokdecode4DSL/tests/decoder.c b/libsigrokdecode4DSL/tests/decoder.c old mode 100644 new mode 100755 index 2b887858..36784102 --- a/libsigrokdecode4DSL/tests/decoder.c +++ b/libsigrokdecode4DSL/tests/decoder.c @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program; if not, see . */ #include @@ -389,11 +388,14 @@ END_TEST START_TEST(test_doc_get) { struct srd_decoder *dec; + char *doc; srd_init(DECODERS_TESTDIR); srd_decoder_load("uart"); dec = srd_decoder_get_by_id("uart"); - fail_unless(srd_decoder_doc_get(dec) != NULL); + doc = srd_decoder_doc_get(dec); + fail_unless(doc != NULL); + g_free(doc); srd_exit(); } END_TEST diff --git a/libsigrokdecode4DSL/tests/inst.c b/libsigrokdecode4DSL/tests/inst.c old mode 100644 new mode 100755 index d52793aa..fd8f709a --- a/libsigrokdecode4DSL/tests/inst.c +++ b/libsigrokdecode4DSL/tests/inst.c @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program; if not, see . */ #include diff --git a/libsigrokdecode4DSL/tests/lib.h b/libsigrokdecode4DSL/tests/lib.h old mode 100644 new mode 100755 index 75380a1a..a4c1dd6b --- a/libsigrokdecode4DSL/tests/lib.h +++ b/libsigrokdecode4DSL/tests/lib.h @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program; if not, see . */ #ifndef LIBSIGROKDECODE_TESTS_LIB_H diff --git a/libsigrokdecode4DSL/tests/main.c b/libsigrokdecode4DSL/tests/main.c old mode 100644 new mode 100755 index b84b20e3..879440c9 --- a/libsigrokdecode4DSL/tests/main.c +++ b/libsigrokdecode4DSL/tests/main.c @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program; if not, see . */ #include diff --git a/libsigrokdecode4DSL/tests/session.c b/libsigrokdecode4DSL/tests/session.c old mode 100644 new mode 100755 index 0d352bd1..73b7669e --- a/libsigrokdecode4DSL/tests/session.c +++ b/libsigrokdecode4DSL/tests/session.c @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program; if not, see . */ #include @@ -149,10 +148,13 @@ static void conf_check_ok(struct srd_session *sess, int key, uint64_t x) static void conf_check_fail(struct srd_session *sess, int key, uint64_t x) { int ret; + GVariant *value = g_variant_new_uint64(x); - ret = srd_session_metadata_set(sess, key, g_variant_new_uint64(x)); + ret = srd_session_metadata_set(sess, key, value); fail_unless(ret != SRD_OK, "srd_session_metadata_set(%p, %d, %" PRIu64 ") worked.", sess, key, x); + if (ret != SRD_OK) + g_variant_unref(value); } static void conf_check_fail_null(struct srd_session *sess, int key) @@ -167,10 +169,13 @@ static void conf_check_fail_null(struct srd_session *sess, int key) static void conf_check_fail_str(struct srd_session *sess, int key, const char *s) { int ret; + GVariant *value = g_variant_new_string(s); - ret = srd_session_metadata_set(sess, key, g_variant_new_string(s)); + ret = srd_session_metadata_set(sess, key, value); fail_unless(ret != SRD_OK, "srd_session_metadata_set() for key %d " "failed: %d.", key, ret); + if (ret != SRD_OK) + g_variant_unref(value); } /* @@ -225,6 +230,36 @@ START_TEST(test_session_metadata_set_bogus) } END_TEST +/* + * Check whether srd_session_terminate_reset() succeeds on newly created + * sessions, as well as after calling start() and meta(). No data is fed + * to decoders here. + */ +START_TEST(test_session_reset_nodata) +{ + struct srd_session *sess; + int ret; + GVariant *data; + + srd_init(NULL); + srd_session_new(&sess); + ret = srd_session_terminate_reset(sess); + fail_unless(ret == SRD_OK, "srd_session_terminate_reset() failed: %d.", ret); + ret = srd_session_start(sess); + fail_unless(ret == SRD_OK, "srd_session_start() failed: %d.", ret); + ret = srd_session_terminate_reset(sess); + fail_unless(ret == SRD_OK, "srd_session_terminate_reset() failed: %d.", ret); + data = g_variant_new_uint64(1000000); + ret = srd_session_metadata_set(sess, SRD_CONF_SAMPLERATE, data); + fail_unless(ret == SRD_OK, "srd_session_metadata_set() failed: %d.", ret); + ret = srd_session_terminate_reset(sess); + fail_unless(ret == SRD_OK, "srd_session_terminate_reset() failed: %d.", ret); + ret = srd_session_destroy(sess); + fail_unless(ret == SRD_OK, "srd_session_destroy() failed: %d.", ret); + srd_exit(); +} +END_TEST + Suite *suite_session(void) { Suite *s; @@ -247,5 +282,9 @@ Suite *suite_session(void) tcase_add_test(tc, test_session_metadata_set_bogus); suite_add_tcase(s, tc); + tc = tcase_create("reset"); + tcase_add_test(tc, test_session_reset_nodata); + suite_add_tcase(s, tc); + return s; } diff --git a/libsigrokdecode4DSL/tools/install-decoders b/libsigrokdecode4DSL/tools/install-decoders index 118ae663..30360dbd 100755 --- a/libsigrokdecode4DSL/tools/install-decoders +++ b/libsigrokdecode4DSL/tools/install-decoders @@ -18,6 +18,7 @@ ## along with this program; if not, see . ## +import errno import os import sys from shutil import copy @@ -64,7 +65,7 @@ def install(srcdir, dstdir, s): try: os.mkdir(pd_dst) except OSError as e: - if e.errno != os.errno.EEXIST: + if e.errno != errno.EEXIST: raise else: pass @@ -119,5 +120,6 @@ if len(args) != 0 or dst is None: usage() install(src, dst, 'protocol decoders') +install(src + '/common', dst + '/common', 'common modules') diff --git a/libsigrokdecode4DSL/type_decoder.c b/libsigrokdecode4DSL/type_decoder.c old mode 100644 new mode 100755 index 08c5e775..abc55c5d --- a/libsigrokdecode4DSL/type_decoder.c +++ b/libsigrokdecode4DSL/type_decoder.c @@ -22,15 +22,17 @@ #include "libsigrokdecode-internal.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */ #include "libsigrokdecode.h" #include -#include + +/** @cond PRIVATE */ +extern SRD_PRIV GSList *sessions; +/** @endcond */ typedef struct { PyObject_HEAD } srd_Decoder; -/* This is only used for nicer srd_dbg() output. - */ -static const char *output_type_name(unsigned int idx) +/* This is only used for nicer srd_dbg() output. */ +SRD_PRIV const char *output_type_name(unsigned int idx) { static const char names[][16] = { "OUTPUT_ANN", @@ -39,23 +41,35 @@ static const char *output_type_name(unsigned int idx) "OUTPUT_META", "(invalid)" }; + return names[MIN(idx, G_N_ELEMENTS(names) - 1)]; } +static void release_annotation(struct srd_proto_data_annotation *pda) +{ + if (!pda) + return; + if (pda->ann_text) + g_strfreev(pda->ann_text); +} + static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj, struct srd_proto_data *pdata) { PyObject *py_tmp; struct srd_proto_data_annotation *pda; unsigned int ann_class; - char **ann_text; + char **ann_text; gpointer ann_type_ptr; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); /* Should be a list of [annotation class, [string, ...]]. */ if (!PyList_Check(obj)) { srd_err("Protocol decoder %s submitted an annotation that" " is not a list", di->decoder->name); - return SRD_ERR_PYTHON; + goto err; } /* Should have 2 elements. */ @@ -63,7 +77,7 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj, srd_err("Protocol decoder %s submitted annotation list with " "%zd elements instead of 2", di->decoder->name, PyList_Size(obj)); - return SRD_ERR_PYTHON; + goto err; } /* @@ -74,7 +88,7 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj, if (!PyLong_Check(py_tmp)) { srd_err("Protocol decoder %s submitted annotation list, but " "first element was not an integer.", di->decoder->name); - return SRD_ERR_PYTHON; + goto err; } ann_class = PyLong_AsLong(py_tmp); // if (!(pdo = g_slist_nth_data(di->decoder->annotations, ann_class))) { @@ -85,7 +99,7 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj, if (ann_class >= g_slist_length(di->decoder->ann_types)) { srd_err("Protocol decoder %s submitted data to unregistered " "annotation class %d.", di->decoder->name, ann_class); - return SRD_ERR_PYTHON; + goto err; } ann_type_ptr = g_slist_nth_data(di->decoder->ann_types, ann_class); @@ -94,21 +108,34 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj, if (!PyList_Check(py_tmp)) { srd_err("Protocol decoder %s submitted annotation list, but " "second element was not a list.", di->decoder->name); - return SRD_ERR_PYTHON; - } - if (py_strseq_to_char(py_tmp, &ann_text) != SRD_OK) { - srd_err("Protocol decoder %s submitted annotation list, but " - "second element was malformed.", di->decoder->name); - return SRD_ERR_PYTHON; + goto err; } + if (py_strseq_to_char(py_tmp, &ann_text) != SRD_OK) { + srd_err("Protocol decoder %s submitted annotation list, but " + "second element was malformed.", di->decoder->name); + goto err; + } - pda = g_malloc(sizeof(struct srd_proto_data_annotation)); + pda = pdata->data; pda->ann_class = ann_class; pda->ann_type = GPOINTER_TO_INT(ann_type_ptr); - pda->ann_text = ann_text; - pdata->data = pda; + pda->ann_text = ann_text; + + PyGILState_Release(gstate); return SRD_OK; + +err: + PyGILState_Release(gstate); + + return SRD_ERR_PYTHON; +} + +static void release_binary(struct srd_proto_data_binary *pdb) +{ + if (!pdb) + return; + g_free((void *)pdb->data); } static int convert_binary(struct srd_decoder_inst *di, PyObject *obj, @@ -119,12 +146,15 @@ static int convert_binary(struct srd_decoder_inst *di, PyObject *obj, Py_ssize_t size; int bin_class; char *class_name, *buf; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); /* Should be a list of [binary class, bytes]. */ if (!PyList_Check(obj)) { srd_err("Protocol decoder %s submitted non-list for SRD_OUTPUT_BINARY.", di->decoder->name); - return SRD_ERR_PYTHON; + goto err; } /* Should have 2 elements. */ @@ -132,7 +162,7 @@ static int convert_binary(struct srd_decoder_inst *di, PyObject *obj, srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list " "with %zd elements instead of 2", di->decoder->name, PyList_Size(obj)); - return SRD_ERR_PYTHON; + goto err; } /* The first element should be an integer. */ @@ -140,13 +170,13 @@ static int convert_binary(struct srd_decoder_inst *di, PyObject *obj, if (!PyLong_Check(py_tmp)) { srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list, " "but first element was not an integer.", di->decoder->name); - return SRD_ERR_PYTHON; + goto err; } bin_class = PyLong_AsLong(py_tmp); if (!(class_name = g_slist_nth_data(di->decoder->binary, bin_class))) { srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY with " "unregistered binary class %d.", di->decoder->name, bin_class); - return SRD_ERR_PYTHON; + goto err; } /* Second element should be bytes. */ @@ -154,57 +184,140 @@ static int convert_binary(struct srd_decoder_inst *di, PyObject *obj, if (!PyBytes_Check(py_tmp)) { srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list, " "but second element was not bytes.", di->decoder->name); - return SRD_ERR_PYTHON; + goto err; } /* Consider an empty set of bytes a bug. */ if (PyBytes_Size(py_tmp) == 0) { srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY " "with empty data set.", di->decoder->name); - return SRD_ERR_PYTHON; + goto err; } - pdb = g_malloc(sizeof(struct srd_proto_data_binary)); if (PyBytes_AsStringAndSize(py_tmp, &buf, &size) == -1) - return SRD_ERR_PYTHON; + goto err; + + PyGILState_Release(gstate); + + pdb = pdata->data; pdb->bin_class = bin_class; pdb->size = size; if (!(pdb->data = g_try_malloc(pdb->size))) return SRD_ERR_MALLOC; memcpy((void *)pdb->data, (const void *)buf, pdb->size); - pdata->data = pdb; return SRD_OK; + +err: + PyGILState_Release(gstate); + + return SRD_ERR_PYTHON; +} + +static inline struct srd_decoder_inst *srd_sess_inst_find_by_obj( + struct srd_session *sess, const GSList *stack, const PyObject *obj) +{ + const GSList *l; + struct srd_decoder_inst *tmp, *di; + + if (!sess) + return NULL; + + di = NULL; + for (l = stack ? stack : sess->di_list; di == NULL && l != NULL; l = l->next) { + tmp = l->data; + if (tmp->py_inst == obj) + di = tmp; + else if (tmp->next_di) + di = srd_sess_inst_find_by_obj(sess, tmp->next_di, obj); + } + + return di; +} + +/** + * Find a decoder instance by its Python object. + * + * I.e. find that instance's instantiation of the sigrokdecode.Decoder class. + * This will recurse to find the instance anywhere in the stack tree of all + * sessions. + * + * @param stack Pointer to a GSList of struct srd_decoder_inst, indicating the + * stack to search. To start searching at the bottom level of + * decoder instances, pass NULL. + * @param obj The Python class instantiation. + * + * @return Pointer to struct srd_decoder_inst, or NULL if not found. + * + * @since 0.1.0 + */ +static inline struct srd_decoder_inst *srd_inst_find_by_obj( + const GSList *stack, const PyObject *obj) +{ + struct srd_decoder_inst *di; + struct srd_session *sess; + GSList *l; + + /* Performance shortcut: Handle the most common case first. */ + sess = sessions->data; + di = sess->di_list->data; + if (di->py_inst == obj) + return di; + + di = NULL; + for (l = sessions; di == NULL && l != NULL; l = l->next) { + sess = l->data; + di = srd_sess_inst_find_by_obj(sess, stack, obj); + } + + return di; } static int convert_meta(struct srd_proto_data *pdata, PyObject *obj) { long long intvalue; double dvalue; + PyGILState_STATE gstate; - if (pdata->pdo->meta_type == G_VARIANT_TYPE_INT64) { + gstate = PyGILState_Ensure(); + + if (g_variant_type_equal(pdata->pdo->meta_type, G_VARIANT_TYPE_INT64)) { if (!PyLong_Check(obj)) { PyErr_Format(PyExc_TypeError, "This output was registered " "as 'int', but something else was passed."); - return SRD_ERR_PYTHON; + goto err; } intvalue = PyLong_AsLongLong(obj); if (PyErr_Occurred()) - return SRD_ERR_PYTHON; + goto err; pdata->data = g_variant_new_int64(intvalue); - } else if (pdata->pdo->meta_type == G_VARIANT_TYPE_DOUBLE) { + } else if (g_variant_type_equal(pdata->pdo->meta_type, G_VARIANT_TYPE_DOUBLE)) { if (!PyFloat_Check(obj)) { PyErr_Format(PyExc_TypeError, "This output was registered " "as 'float', but something else was passed."); - return SRD_ERR_PYTHON; + goto err; } dvalue = PyFloat_AsDouble(obj); if (PyErr_Occurred()) - return SRD_ERR_PYTHON; + goto err; pdata->data = g_variant_new_double(dvalue); } + PyGILState_Release(gstate); + return SRD_OK; + +err: + PyGILState_Release(gstate); + + return SRD_ERR_PYTHON; +} + +static void release_meta(GVariant *gvar) +{ + if (!gvar) + return; + g_variant_unref(gvar); } static PyObject *Decoder_put(PyObject *self, PyObject *args) @@ -213,18 +326,22 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args) PyObject *py_data, *py_res; struct srd_decoder_inst *di, *next_di; struct srd_pd_output *pdo; - struct srd_proto_data *pdata; + struct srd_proto_data pdata; + struct srd_proto_data_annotation pda; + struct srd_proto_data_binary pdb; uint64_t start_sample, end_sample; int output_id; struct srd_pd_callback *cb; - struct srd_proto_data_binary *pdb; - struct srd_proto_data_annotation *pda; - char **annotations; + PyGILState_STATE gstate; + + py_data = NULL; + + gstate = PyGILState_Ensure(); if (!(di = srd_inst_find_by_obj(NULL, self))) { /* Shouldn't happen. */ srd_dbg("put(): self instance not found."); - return NULL; + goto err; } if (!PyArg_ParseTuple(args, "KKiO", &start_sample, &end_sample, @@ -234,97 +351,112 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args) * Python raise it. This results in a much better trace in * controller.c on the decode() method call. */ - return NULL; + goto err; } if (!(l = g_slist_nth(di->pd_output, output_id))) { srd_err("Protocol decoder %s submitted invalid output ID %d.", di->decoder->name, output_id); - return NULL; + goto err; } pdo = l->data; - srd_spew("Instance %s put %" PRIu64 "-%" PRIu64 " %s on oid %d.", - di->inst_id, start_sample, end_sample, - output_type_name(pdo->output_type), output_id); + /* Upon SRD_OUTPUT_PYTHON for stacked PDs, we have a nicer log message later. */ + if (pdo->output_type != SRD_OUTPUT_PYTHON && di->next_di != NULL) { + srd_spew("Instance %s put %"PRIu64 "-%" PRIu64 " %s on " + "oid %d (%s).", di->inst_id, start_sample, end_sample, + output_type_name(pdo->output_type), output_id, + pdo->proto_id); + } - pdata = g_malloc0(sizeof(struct srd_proto_data)); - pdata->start_sample = start_sample; - pdata->end_sample = end_sample; - pdata->pdo = pdo; + pdata.start_sample = start_sample; + pdata.end_sample = end_sample; + pdata.pdo = pdo; + pdata.data = NULL; switch (pdo->output_type) { case SRD_OUTPUT_ANN: /* Annotations are only fed to callbacks. */ if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) { + pdata.data = &pda; /* Convert from PyDict to srd_proto_data_annotation. */ - if (convert_annotation(di, py_data, pdata) != SRD_OK) { + if (convert_annotation(di, py_data, &pdata) != SRD_OK) { /* An error was already logged. */ break; } - cb->cb(pdata, cb->cb_data); - pda = pdata->data; - annotations = (char**)pda->ann_text; - while(*annotations) { - g_free(*annotations); - annotations++; - } - g_free(pda->ann_text); - g_free(pda); + Py_BEGIN_ALLOW_THREADS + cb->cb(&pdata, cb->cb_data); + Py_END_ALLOW_THREADS + release_annotation(pdata.data); } break; - case SRD_OUTPUT_PYTHON: - for (l = di->next_di; l; l = l->next) { - next_di = l->data; - srd_spew("Sending %" PRIu64 "-%" PRIu64 " to instance %s", - start_sample, end_sample, next_di->inst_id); - if (!(py_res = PyObject_CallMethod( - next_di->py_inst, "decode", "KKO", start_sample, - end_sample, py_data))) { - srd_exception_catch(NULL, "Calling %s decode() failed", - next_di->inst_id); - } - Py_XDECREF(py_res); - } - if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) { - /* Frontends aren't really supposed to get Python - * callbacks, but it's useful for testing. */ - pdata->data = py_data; - cb->cb(pdata, cb->cb_data); - } - break; - case SRD_OUTPUT_BINARY: - if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) { - /* Convert from PyDict to srd_proto_data_binary. */ - if (convert_binary(di, py_data, pdata) != SRD_OK) { - /* An error was already logged. */ - break; - } - cb->cb(pdata, cb->cb_data); - pdb = pdata->data; - g_free(pdb->data); - g_free(pdb); - } - break; - case SRD_OUTPUT_META: - if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) { - /* Annotations need converting from PyObject. */ - if (convert_meta(pdata, py_data) != SRD_OK) { - /* An exception was already set up. */ - break; - } - cb->cb(pdata, cb->cb_data); - } - break; - default: - srd_err("Protocol decoder %s submitted invalid output type %d.", - di->decoder->name, pdo->output_type); - break; - } - g_free(pdata); + case SRD_OUTPUT_PYTHON: + for (l = di->next_di; l; l = l->next) { + next_di = l->data; + srd_spew("Instance %s put %" PRIu64 "-%" PRIu64 " %s " + "on oid %d (%s) to instance %s.", di->inst_id, + start_sample, + end_sample, output_type_name(pdo->output_type), + output_id, pdo->proto_id, next_di->inst_id); + if (!(py_res = PyObject_CallMethod( + next_di->py_inst, "decode", "KKO", start_sample, + end_sample, py_data))) { + srd_exception_catch(NULL, "Calling %s decode() failed", + next_di->inst_id); + } + Py_XDECREF(py_res); + } + if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) { + /* + * Frontends aren't really supposed to get Python + * callbacks, but it's useful for testing. + */ + pdata.data = py_data; + cb->cb(&pdata, cb->cb_data); + } + break; + case SRD_OUTPUT_BINARY: + if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) { + pdata.data = &pdb; + /* Convert from PyDict to srd_proto_data_binary. */ + if (convert_binary(di, py_data, &pdata) != SRD_OK) { + /* An error was already logged. */ + break; + } + Py_BEGIN_ALLOW_THREADS + cb->cb(&pdata, cb->cb_data); + Py_END_ALLOW_THREADS + release_binary(pdata.data); + } + break; + case SRD_OUTPUT_META: + if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) { + /* Annotations need converting from PyObject. */ + if (convert_meta(&pdata, py_data) != SRD_OK) { + /* An exception was already set up. */ + break; + } + Py_BEGIN_ALLOW_THREADS + cb->cb(&pdata, cb->cb_data); + Py_END_ALLOW_THREADS + release_meta(pdata.data); + } + break; + default: + srd_err("Protocol decoder %s submitted invalid output type %d.", + di->decoder->name, pdo->output_type); + break; + } + + PyGILState_Release(gstate); Py_RETURN_NONE; + +err: + PyGILState_Release(gstate); + + return NULL; } static PyObject *Decoder_register(PyObject *self, PyObject *args, @@ -337,7 +469,13 @@ static PyObject *Decoder_register(PyObject *self, PyObject *args, const GVariantType *meta_type_gv; int output_type; char *proto_id, *meta_name, *meta_descr; - char *keywords[] = {"output_type", "proto_id", "meta", NULL}; + char *keywords[] = { "output_type", "proto_id", "meta", NULL }; + PyGILState_STATE gstate; + gboolean is_meta; + GSList *l; + struct srd_pd_output *cmp; + + gstate = PyGILState_Ensure(); meta_type_py = NULL; meta_type_gv = NULL; @@ -345,32 +483,52 @@ static PyObject *Decoder_register(PyObject *self, PyObject *args, if (!(di = srd_inst_find_by_obj(NULL, self))) { PyErr_SetString(PyExc_Exception, "decoder instance not found"); - return NULL; + goto err; } - /* Default to instance id, which defaults to class id. */ + /* Default to instance ID, which defaults to class ID. */ proto_id = di->inst_id; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|s(Oss)", keywords, &output_type, &proto_id, &meta_type_py, &meta_name, &meta_descr)) { /* Let Python raise this exception. */ - return NULL; + goto err; } /* Check if the meta value's type is supported. */ - if (output_type == SRD_OUTPUT_META) { + is_meta = output_type == SRD_OUTPUT_META; + if (is_meta) { if (meta_type_py == &PyLong_Type) meta_type_gv = G_VARIANT_TYPE_INT64; else if (meta_type_py == &PyFloat_Type) meta_type_gv = G_VARIANT_TYPE_DOUBLE; else { PyErr_Format(PyExc_TypeError, "Unsupported type."); - return NULL; + goto err; } } - srd_dbg("Instance %s creating new output type %d for %s.", - di->inst_id, output_type, proto_id); + pdo = NULL; + for (l = di->pd_output; l; l = l->next) { + cmp = l->data; + if (cmp->output_type != output_type) + continue; + if (strcmp(cmp->proto_id, proto_id) != 0) + continue; + if (is_meta && cmp->meta_type != meta_type_gv) + continue; + if (is_meta && strcmp(cmp->meta_name, meta_name) != 0) + continue; + if (is_meta && strcmp(cmp->meta_descr, meta_descr) != 0) + continue; + pdo = cmp; + break; + } + if (pdo) { + py_new_output_id = Py_BuildValue("i", pdo->pdo_id); + PyGILState_Release(gstate); + return py_new_output_id; + } pdo = g_malloc(sizeof(struct srd_pd_output)); @@ -379,6 +537,8 @@ static PyObject *Decoder_register(PyObject *self, PyObject *args, pdo->output_type = output_type; pdo->di = di; pdo->proto_id = g_strdup(proto_id); + pdo->meta_name = NULL; + pdo->meta_descr = NULL; if (output_type == SRD_OUTPUT_META) { pdo->meta_type = meta_type_gv; @@ -389,19 +549,503 @@ static PyObject *Decoder_register(PyObject *self, PyObject *args, di->pd_output = g_slist_append(di->pd_output, pdo); py_new_output_id = Py_BuildValue("i", pdo->pdo_id); + PyGILState_Release(gstate); + + srd_dbg("Instance %s creating new output type %s as oid %d (%s).", + di->inst_id, output_type_name(output_type), pdo->pdo_id, + proto_id); + return py_new_output_id; + +err: + PyGILState_Release(gstate); + + return NULL; +} + +static int get_term_type(const char *v) +{ + switch (v[0]) { + case 'h': + return SRD_TERM_HIGH; + case 'l': + return SRD_TERM_LOW; + case 'r': + return SRD_TERM_RISING_EDGE; + case 'f': + return SRD_TERM_FALLING_EDGE; + case 'e': + return SRD_TERM_EITHER_EDGE; + case 'n': + return SRD_TERM_NO_EDGE; + default: + return -1; + } + + return -1; +} + +/** + * Get the pin values at the current sample number. + * + * @param di The decoder instance to use. Must not be NULL. + * The number of channels must be >= 1. + * + * @return A newly allocated PyTuple containing the pin values at the + * current sample number. + */ +static int get_current_pinvalues(const struct srd_decoder_inst *di) +{ + int i; + uint8_t sample; + const uint8_t *sample_pos; + int bit_offset; + PyGILState_STATE gstate; + + if (!di) { + srd_err("Invalid decoder instance."); + return SRD_ERR_ARG; + } + + gstate = PyGILState_Ensure(); + + for (i = 0; i < di->dec_num_channels; i++) { + /* A channelmap value of -1 means "unused optional channel". */ + if (di->dec_channelmap[i] == -1) { + /* Value of unused channel is 0xff, instead of 0 or 1. */ + PyTuple_SetItem(di->py_pinvalues, i, PyLong_FromLong(0xff)); + } else { + if (*(di->inbuf + i) == NULL) { + sample = *(di->inbuf_const + i) ? 1 : 0; + PyTuple_SetItem(di->py_pinvalues, i, PyLong_FromLong(sample)); + } else { + sample_pos = *(di->inbuf + i) + ((di->abs_cur_samplenum - di->abs_start_samplenum) / 8); + bit_offset = (di->abs_cur_samplenum - di->abs_start_samplenum) % 8; + sample = *sample_pos & (1 << bit_offset) ? 1 : 0; + PyTuple_SetItem(di->py_pinvalues, i, PyLong_FromLong(sample)); + } + } + } + + PyGILState_Release(gstate); + + return SRD_OK; +} + +/** + * Create a list of terms in the specified condition. + * + * If there are no terms in the condition, 'term_list' will be NULL. + * + * @param py_dict A Python dict containing terms. Must not be NULL. + * @param term_list Pointer to a GSList which will be set to the newly + * created list of terms. Must not be NULL. + * + * @return SRD_OK upon success, a negative error code otherwise. + */ +static int create_term_list(PyObject *py_dict, GSList **term_list, gboolean cur_matched) +{ + Py_ssize_t pos = 0; + PyObject *py_key, *py_value; + struct srd_term *term; + uint64_t num_samples_to_skip; + char *term_str; + PyGILState_STATE gstate; + + if (!py_dict || !term_list) + return SRD_ERR_ARG; + + /* "Create" an empty GSList of terms. */ + *term_list = NULL; + + gstate = PyGILState_Ensure(); + + /* Iterate over all items in the current dict. */ + while (PyDict_Next(py_dict, &pos, &py_key, &py_value)) { + /* Check whether the current key is a string or a number. */ + if (PyLong_Check(py_key)) { + /* The key is a number. */ + /* TODO: Check if the number is a valid channel. */ + /* Get the value string. */ + if ((py_pydictitem_as_str(py_dict, py_key, &term_str)) != SRD_OK) { + srd_err("Failed to get the value."); + goto err; + } + term = g_malloc(sizeof(struct srd_term)); + term->type = get_term_type(term_str); + term->channel = PyLong_AsLong(py_key); + g_free(term_str); + } else if (PyUnicode_Check(py_key)) { + /* The key is a string. */ + /* TODO: Check if it's "skip". */ + if ((py_pydictitem_as_long(py_dict, py_key, &num_samples_to_skip)) != SRD_OK) { + srd_err("Failed to get number of samples to skip."); + goto err; + } + term = g_malloc(sizeof(struct srd_term)); + term->type = SRD_TERM_SKIP; + term->num_samples_to_skip = num_samples_to_skip; + term->num_samples_already_skipped = cur_matched ? 1 : 0; + } else { + srd_err("Term key is neither a string nor a number."); + goto err; + } + + /* Add the term to the list of terms. */ + *term_list = g_slist_append(*term_list, term); + } + + PyGILState_Release(gstate); + + return SRD_OK; + +err: + PyGILState_Release(gstate); + + return SRD_ERR; +} + +/** + * Replace the current condition list with the new one. + * + * @param self TODO. Must not be NULL. + * @param args TODO. Must not be NULL. + * + * @retval SRD_OK The new condition list was set successfully. + * @retval SRD_ERR There was an error setting the new condition list. + * The contents of di->condition_list are undefined. + * @retval 9999 TODO. + */ +static int set_new_condition_list(struct srd_decoder_inst *di, PyObject *args) +{ + GSList *term_list; + PyObject *py_conditionlist, *py_conds, *py_dict; + int i, num_conditions, ret; + PyGILState_STATE gstate; + + if (!args) + return SRD_ERR_ARG; + + gstate = PyGILState_Ensure(); + + /* + * Return an error condition from .wait() when termination is + * requested, such that decode() will terminate. + */ + if (di->want_wait_terminate) { + srd_dbg("%s: %s: Skip (want_term).", di->inst_id, __func__); + goto err; + } + + /* + * Parse the argument of self.wait() into 'py_conds', and check + * the data type. The argument is optional, None is assumed in + * its absence. None or an empty dict or an empty list mean that + * there is no condition, and the next available sample shall + * get returned to the caller. + */ + py_conds = Py_None; + if (!PyArg_ParseTuple(args, "|O", &py_conds)) { + /* Let Python raise this exception. */ + goto err; + } + if (py_conds == Py_None) { + /* 'py_conds' is None. */ + goto ret_9999; + } else if (PyList_Check(py_conds)) { + /* 'py_conds' is a list. */ + py_conditionlist = py_conds; + num_conditions = PyList_Size(py_conditionlist); + if (num_conditions == 0) + goto ret_9999; /* The PD invoked self.wait([]). */ + Py_IncRef(py_conditionlist); + } else if (PyDict_Check(py_conds)) { + /* 'py_conds' is a dict. */ + if (PyDict_Size(py_conds) == 0) + goto ret_9999; /* The PD invoked self.wait({}). */ + /* Make a list and put the dict in there for convenience. */ + py_conditionlist = PyList_New(1); + Py_IncRef(py_conds); + PyList_SetItem(py_conditionlist, 0, py_conds); + num_conditions = 1; + } else { + srd_err("Condition list is neither a list nor a dict."); + goto err; + } + + /* Free the old condition list. */ + condition_list_free(di); + + ret = SRD_OK; + + /* Iterate over the conditions, set di->condition_list accordingly. */ + for (i = 0; i < num_conditions; i++) { + /* Get a condition (dict) from the condition list. */ + py_dict = PyList_GetItem(py_conditionlist, i); + if (!PyDict_Check(py_dict)) { + srd_err("Condition is not a dict."); + ret = SRD_ERR; + break; + } + + /* Create the list of terms in this condition. */ + if ((ret = create_term_list(py_dict, &term_list, di->abs_cur_matched)) < 0) + break; + + /* Add the new condition to the PD instance's condition list. */ + di->condition_list = g_slist_append(di->condition_list, term_list); + } + + Py_DecRef(py_conditionlist); + + PyGILState_Release(gstate); + + return ret; + +err: + PyGILState_Release(gstate); + + return SRD_ERR; + +ret_9999: + PyGILState_Release(gstate); + + return 9999; +} + +/** + * Create a SKIP condition list for condition-less .wait() calls. + * + * @param di Decoder instance. + * @param count Number of samples to skip. + * + * @retval SRD_OK The new condition list was set successfully. + * @retval SRD_ERR There was an error setting the new condition list. + * The contents of di->condition_list are undefined. + * + * This routine is a reduced and specialized version of the @ref + * set_new_condition_list() and @ref create_term_list() routines which + * gets invoked when .wait() was called without specifications for + * conditions. This minor duplication of the SKIP term list creation + * simplifies the logic and avoids the creation of expensive Python + * objects with "constant" values which the caller did not pass in the + * first place. It results in maximum sharing of match handling code + * paths. + */ +static int set_skip_condition(struct srd_decoder_inst *di, uint64_t count) +{ + struct srd_term *term; + GSList *term_list; + + condition_list_free(di); + term = g_malloc(sizeof(*term)); + term->type = SRD_TERM_SKIP; + term->num_samples_to_skip = count; + term->num_samples_already_skipped = di->abs_cur_matched ? 1 : 0; + term_list = g_slist_append(NULL, term); + di->condition_list = g_slist_append(di->condition_list, term_list); + + return SRD_OK; +} + +static PyObject *Decoder_wait(PyObject *self, PyObject *args) +{ + int ret; + uint64_t skip_count; + gboolean found_match; + struct srd_decoder_inst *di; + PyGILState_STATE gstate; + + if (!self || !args) + return NULL; + + gstate = PyGILState_Ensure(); + + if (!(di = srd_inst_find_by_obj(NULL, self))) { + PyErr_SetString(PyExc_Exception, "decoder instance not found"); + PyGILState_Release(gstate); + Py_RETURN_NONE; + } + + ret = set_new_condition_list(di, args); + if (ret < 0) { + srd_dbg("%s: %s: Aborting wait().", di->inst_id, __func__); + goto err; + } + + if (ret == 9999) { + /* + * Empty condition list, automatic match. Arrange for the + * execution of regular match handling code paths such that + * the next available sample is returned to the caller. + * Make sure to skip one sample when "anywhere within the + * stream", yet make sure to not skip sample number 0. + */ + if (!di->first_pos && di->abs_cur_samplenum) + skip_count = 1; + else if (!di->condition_list) + skip_count = 0; + else + skip_count = 1; + ret = set_skip_condition(di, skip_count); + if (ret < 0) { + srd_dbg("%s: %s: Cannot setup condition-less wait().", + di->inst_id, __func__); + goto err; + } + } + + + while (1) { + + Py_BEGIN_ALLOW_THREADS + + /* Wait for new samples to process, or termination request. */ + g_mutex_lock(&di->data_mutex); + while (!di->got_new_samples && !di->want_wait_terminate) + g_cond_wait(&di->got_new_samples_cond, &di->data_mutex); + + /* + * Check whether any of the current condition(s) match. + * Arrange for termination requests to take a code path which + * won't find new samples to process, pretends to have processed + * previously stored samples, and returns to the main thread, + * while the termination request still gets signalled. + */ + found_match = FALSE; + + /* Ignore return value for now, should never be negative. */ + (void)process_samples_until_condition_match(di, &found_match); + + Py_END_ALLOW_THREADS + + /* If there's a match, set self.samplenum etc. and return. */ + if (found_match) { + /* Set self.samplenum to the (absolute) sample number that matched. */ + PyObject *py_cur_samplenum = PyLong_FromUnsignedLongLong(di->abs_cur_samplenum); + PyObject_SetAttrString(di->py_inst, "samplenum", py_cur_samplenum); + Py_DECREF(py_cur_samplenum); + + /* Set self.matched to math_array. */ + PyObject *py_matched = PyLong_FromUnsignedLongLong(di->match_array); + PyObject_SetAttrString(di->py_inst, "matched", py_matched); + Py_DECREF(py_matched); + + get_current_pinvalues(di); + + g_mutex_unlock(&di->data_mutex); + + PyGILState_Release(gstate); + + Py_INCREF(di->py_pinvalues); + return (PyObject *)di->py_pinvalues; + } + + /* No match, reset state for the next chunk. */ + di->got_new_samples = FALSE; + di->handled_all_samples = TRUE; + di->abs_start_samplenum = 0; + di->abs_end_samplenum = 0; + di->inbuf = NULL; + di->inbuflen = 0; + + /* Signal the main thread that we handled all samples. */ + g_cond_signal(&di->handled_all_samples_cond); + + /* + * When termination of wait() and decode() was requested, + * then exit the loop after releasing the mutex. + */ + if (di->want_wait_terminate) { + srd_dbg("%s: %s: Will return from wait().", + di->inst_id, __func__); + g_mutex_unlock(&di->data_mutex); + goto err; + } + + g_mutex_unlock(&di->data_mutex); + } + + PyGILState_Release(gstate); + + Py_RETURN_NONE; + +err: + PyGILState_Release(gstate); + + return NULL; +} + +/** + * Return whether the specified channel was supplied to the decoder. + * + * @param self TODO. Must not be NULL. + * @param args TODO. Must not be NULL. + * + * @retval Py_True The channel has been supplied by the frontend. + * @retval Py_False The channel has been supplied by the frontend. + * @retval NULL An error occurred. + */ +static PyObject *Decoder_has_channel(PyObject *self, PyObject *args) +{ + int idx, count; + struct srd_decoder_inst *di; + PyGILState_STATE gstate; + + if (!self || !args) + return NULL; + + gstate = PyGILState_Ensure(); + + if (!(di = srd_inst_find_by_obj(NULL, self))) { + PyErr_SetString(PyExc_Exception, "decoder instance not found"); + goto err; + } + + /* + * Get the integer argument of self.has_channel(). Check for + * the range of supported PD input channel numbers. + */ + if (!PyArg_ParseTuple(args, "i", &idx)) { + /* Let Python raise this exception. */ + goto err; + } + + count = g_slist_length(di->decoder->channels) + + g_slist_length(di->decoder->opt_channels); + if (idx < 0 || idx >= count) { + srd_err("Invalid index %d, PD channel count %d.", idx, count); + PyErr_SetString(PyExc_IndexError, "invalid channel index"); + goto err; + } + + PyGILState_Release(gstate); + + return (di->dec_channelmap[idx] == -1) ? Py_False : Py_True; + +err: + PyGILState_Release(gstate); + + return NULL; } static PyMethodDef Decoder_methods[] = { - {"put", Decoder_put, METH_VARARGS, - "Accepts a dictionary with the following keys: startsample, endsample, data"}, - {"register", (PyCFunction)Decoder_register, METH_VARARGS|METH_KEYWORDS, - "Register a new output stream"}, + { "put", Decoder_put, METH_VARARGS, + "Accepts a dictionary with the following keys: startsample, endsample, data" }, + { "register", (PyCFunction)Decoder_register, METH_VARARGS|METH_KEYWORDS, + "Register a new output stream" }, + { "wait", Decoder_wait, METH_VARARGS, + "Wait for one or more conditions to occur" }, + { "has_channel", Decoder_has_channel, METH_VARARGS, + "Report whether a channel was supplied" }, {NULL, NULL, 0, NULL} }; -/** Create the sigrokdecode.Decoder type. +/** + * Create the sigrokdecode.Decoder type. + * * @return The new type object. + * * @private */ SRD_PRIV PyObject *srd_Decoder_type_new(void) @@ -413,11 +1057,20 @@ SRD_PRIV PyObject *srd_Decoder_type_new(void) { Py_tp_new, (void *)&PyType_GenericNew }, { 0, NULL } }; + PyObject *py_obj; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); + spec.name = "sigrokdecode.Decoder"; spec.basicsize = sizeof(srd_Decoder); spec.itemsize = 0; spec.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; spec.slots = slots; - return PyType_FromSpec(&spec); + py_obj = PyType_FromSpec(&spec); + + PyGILState_Release(gstate); + + return py_obj; } diff --git a/libsigrokdecode4DSL/type_logic.c b/libsigrokdecode4DSL/type_logic.c deleted file mode 100644 index 75143933..00000000 --- a/libsigrokdecode4DSL/type_logic.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * This file is part of the libsigrokdecode project. - * - * Copyright (C) 2012 Bert Vermeulen - * Copyright (C) 2016 DreamSourceLab - * - * 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 . - */ - -#include "config.h" -#include "libsigrokdecode-internal.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */ -#include "libsigrokdecode.h" -#include -#include - -static PyObject *srd_logic_iter(PyObject *self) -{ - return self; -} - -static PyObject *srd_logic_iternext(PyObject *self) -{ - srd_logic *logic; - PyObject *py_samplenum, *py_samples; - int i; - - logic = (srd_logic *)self; - uint64_t offset = floor(logic->itercnt); - logic->di->cur_pos = logic->cur_pos; - logic->di->logic_mask = logic->logic_mask; - logic->di->exp_logic = logic->exp_logic; - logic->di->edge_index = -1; - if (logic->logic_mask != 0 && logic->edge_index != -1) - logic->di->edge_index = logic->di->dec_channelmap[logic->edge_index]; - - if (offset > logic->samplenum || logic->logic_mask != 0) { - /* End iteration loop. */ - return NULL; - } - - /* - * Convert the bit-packed sample to an array of bytes, with only 0x01 - * and 0x00 values, so the PD doesn't need to do any bitshifting. - */ - for (i = 0; i < logic->di->dec_num_channels; i++) { - /* A channelmap value of -1 means "unused optional channel". */ - if (logic->di->dec_channelmap[i] == -1) { - /* Value of unused channel is 0xff, instead of 0 or 1. */ - logic->di->channel_samples[i] = 0xff; - } else { - if (*(logic->inbuf + i) == NULL) { - logic->di->channel_samples[i] = *(logic->inbuf_const + i) ? 1 : 0; - } else { - uint64_t inbuf_offset = (offset + (logic->start_samplenum % 8)); - uint8_t *ptr = *(logic->inbuf + i) + (inbuf_offset / 8); - logic->di->channel_samples[i] = *ptr & (1 << (inbuf_offset % 8)) ? 1 : 0; - } - } - } - - /* Prepare the next samplenum/sample list in this iteration. */ - py_samplenum = PyLong_FromUnsignedLongLong(logic->start_samplenum + offset); - PyList_SetItem(logic->sample, 0, py_samplenum); - py_samples = PyBytes_FromStringAndSize((const char *)logic->di->channel_samples, - logic->di->dec_num_channels); - PyList_SetItem(logic->sample, 1, py_samples); - Py_INCREF(logic->sample); - - return logic->sample; -} - -static PyMemberDef srd_logic_members[] = { - {"itercnt", T_FLOAT, offsetof(srd_logic, itercnt), 0, - "next expacted samples offset"}, - {"logic_mask", T_ULONGLONG, offsetof(srd_logic, logic_mask), 0, - "next expacted logic value mask"}, - {"exp_logic", T_ULONGLONG, offsetof(srd_logic, exp_logic), 0, - "next expacted logic value"}, - {"edge_index", T_INT, offsetof(srd_logic, edge_index), 0, - "channel index of next expacted edge"}, - {"cur_pos", T_ULONGLONG, offsetof(srd_logic, cur_pos), 0, - "current sample position"}, - {NULL} /* Sentinel */ -}; - -//static PyMemberDef srd_logic_members[] = { -//// {"itercnt", T_FLOAT, offsetof(srd_logic, itercnt), 0, -//// "next expacted samples offset"}, -// {"logic_mask", T_ULONGLONG, offsetof(srd_logic, logic_mask), 0, -// "next expacted logic value mask"}, -//// {"exp_logic", T_ULONGLONG, offsetof(srd_logic, exp_logic), 0, -//// "next expacted logic value"}, -//// {"edge_index", T_INT, offsetof(srd_logic, edge_index), 0, -//// "channel index of next expacted edge"}, -//// {"cur_pos", T_ULONGLONG, offsetof(srd_logic, cur_pos), 0, -//// "current sample position"}, -// {NULL} /* Sentinel */ -//}; - - -/** Create the srd_logic type. - * @return The new type object. - * @private - */ -SRD_PRIV PyObject *srd_logic_type_new(void) -{ - PyType_Spec spec; - PyType_Slot slots[] = { - { Py_tp_doc, "sigrokdecode logic sample object" }, - { Py_tp_iter, (void *)&srd_logic_iter }, - { Py_tp_iternext, (void *)&srd_logic_iternext }, - { Py_tp_new, (void *)&PyType_GenericNew }, - { Py_tp_members, srd_logic_members }, - { 0, NULL } - }; - spec.name = "srd_logic"; - spec.basicsize = sizeof(srd_logic); - spec.itemsize = 0; - spec.flags = Py_TPFLAGS_DEFAULT; - spec.slots = slots; - - return PyType_FromSpec(&spec); -} diff --git a/libsigrokdecode4DSL/util.c b/libsigrokdecode4DSL/util.c old mode 100644 new mode 100755 index d071256f..45fb2d1c --- a/libsigrokdecode4DSL/util.c +++ b/libsigrokdecode4DSL/util.c @@ -3,7 +3,6 @@ * * Copyright (C) 2010 Uwe Hermann * Copyright (C) 2012 Bert Vermeulen - * Copyright (C) 2016 DreamSourceLab * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +20,7 @@ #include "config.h" #include "libsigrokdecode-internal.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */ -#include "libsigrokdecode.h" + /** * Import a Python module by name. * @@ -37,14 +36,21 @@ SRD_PRIV PyObject *py_import_by_name(const char *name) { PyObject *py_mod, *py_modname; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); py_modname = PyUnicode_FromString(name); - if (!py_modname) + if (!py_modname) { + PyGILState_Release(gstate); return NULL; + } py_mod = PyImport_Import(py_modname); Py_DECREF(py_modname); + PyGILState_Release(gstate); + return py_mod; } @@ -65,21 +71,93 @@ SRD_PRIV int py_attr_as_str(PyObject *py_obj, const char *attr, char **outstr) { PyObject *py_str; int ret; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); if (!PyObject_HasAttrString(py_obj, attr)) { srd_dbg("Object has no attribute '%s'.", attr); - return SRD_ERR_PYTHON; + goto err; } if (!(py_str = PyObject_GetAttrString(py_obj, attr))) { - srd_exception_catch(NULL, "Failed to get attribute '%s'", attr); - return SRD_ERR_PYTHON; + srd_exception_catch(NULL, "Failed to get attribute '%s'", attr); + goto err; } ret = py_str_as_str(py_str, outstr); Py_DECREF(py_str); + PyGILState_Release(gstate); + return ret; + +err: + PyGILState_Release(gstate); + + return SRD_ERR_PYTHON; +} + +/** + * Get the value of a Python object's attribute, returned as a newly + * allocated GSList of char *. + * + * @param[in] py_obj The object to probe. + * @param[in] attr Name of the attribute to retrieve. + * @param[out] outstrlist ptr to GSList of char * storage to be filled in. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * The 'outstrlist' argument points to a GSList of g_malloc()ed strings + * upon success. + * + * @private + */ +SRD_PRIV int py_attr_as_strlist(PyObject *py_obj, const char *attr, GSList **outstrlist) +{ + PyObject *py_list; + int i; + int ret; + char *outstr; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); + + if (!PyObject_HasAttrString(py_obj, attr)) { + srd_dbg("Object has no attribute '%s'.", attr); + goto err; + } + + if (!(py_list = PyObject_GetAttrString(py_obj, attr))) { + srd_exception_catch(NULL, "Failed to get attribute '%s'", attr); + goto err; + } + + if (!PyList_Check(py_list)) { + srd_dbg("Object is not a list."); + goto err; + } + + *outstrlist = NULL; + + for (i = 0; i < PyList_Size(py_list); i++) { + ret = py_listitem_as_str(py_list, i, &outstr); + if (ret < 0) { + srd_dbg("Couldn't get item %d.", i); + goto err; + } + *outstrlist = g_slist_append(*outstrlist, outstr); + } + + Py_DECREF(py_list); + + PyGILState_Release(gstate); + + return SRD_OK; + +err: + PyGILState_Release(gstate); + + return SRD_ERR_PYTHON; } /** @@ -99,18 +177,28 @@ SRD_PRIV int py_dictitem_as_str(PyObject *py_obj, const char *key, char **outstr) { PyObject *py_value; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); if (!PyDict_Check(py_obj)) { srd_dbg("Object is not a dictionary."); - return SRD_ERR_PYTHON; + goto err; } if (!(py_value = PyDict_GetItemString(py_obj, key))) { srd_dbg("Dictionary has no attribute '%s'.", key); - return SRD_ERR_PYTHON; + goto err; } + PyGILState_Release(gstate); + return py_str_as_str(py_value, outstr); + +err: + PyGILState_Release(gstate); + + return SRD_ERR_PYTHON; } /** @@ -124,23 +212,168 @@ SRD_PRIV int py_dictitem_as_str(PyObject *py_obj, const char *key, * @private */ SRD_PRIV int py_dictitem_to_int(PyObject *py_obj, const char *key) +{ + PyObject *py_value; + long type; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); + + if (!PyDict_Check(py_obj)) { + srd_dbg("Object is not a dictionary."); + goto err; + } + + if (!(py_value = PyDict_GetItemString(py_obj, key))) { + srd_dbg("Dictionary has no attribute '%s'.", key); + goto err; + } + + type = PyLong_Check(py_value) ? PyLong_AsLong(py_value) : SRD_ERR; + return type; + +err: + PyGILState_Release(gstate); + return SRD_ERR; +} + +/** + * Get the value of a Python list item, returned as a newly + * allocated char *. + * + * @param[in] py_obj The list to probe. + * @param[in] idx Index of the list item to retrieve. + * @param[out] outstr Pointer to char * storage to be filled in. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * The 'outstr' argument points to a g_malloc()ed string upon success. + * + * @private + */ +SRD_PRIV int py_listitem_as_str(PyObject *py_obj, int idx, + char **outstr) { PyObject *py_value; - long type; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); + + if (!PyList_Check(py_obj)) { + srd_dbg("Object is not a list."); + goto err; + } + + if (!(py_value = PyList_GetItem(py_obj, idx))) { + srd_dbg("Couldn't get list item %d.", idx); + goto err; + } + + PyGILState_Release(gstate); + + return py_str_as_str(py_value, outstr); + +err: + PyGILState_Release(gstate); + + return SRD_ERR_PYTHON; +} + +/** + * Get the value of a Python dictionary item, returned as a newly + * allocated char *. + * + * @param py_obj The dictionary to probe. + * @param py_key Key of the item to retrieve. + * @param outstr Pointer to char * storage to be filled in. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * The 'outstr' argument points to a malloc()ed string upon success. + * + * @private + */ +SRD_PRIV int py_pydictitem_as_str(PyObject *py_obj, PyObject *py_key, + char **outstr) +{ + PyObject *py_value; + PyGILState_STATE gstate; + + if (!py_obj || !py_key || !outstr) + return SRD_ERR_ARG; + + gstate = PyGILState_Ensure(); if (!PyDict_Check(py_obj)) { srd_dbg("Object is not a dictionary."); - return -1; + goto err; } - if (!(py_value = PyDict_GetItemString(py_obj, key))) { - srd_dbg("Dictionary has no attribute '%s'.", key); - return -1; + if (!(py_value = PyDict_GetItem(py_obj, py_key))) { + srd_dbg("Dictionary has no such key."); + goto err; } - type = PyLong_Check(py_value) ? PyLong_AsLong(py_value) : -1; + if (!PyUnicode_Check(py_value)) { + srd_dbg("Dictionary value should be a string."); + goto err; + } - return type; + PyGILState_Release(gstate); + + return py_str_as_str(py_value, outstr); + +err: + PyGILState_Release(gstate); + + return SRD_ERR_PYTHON; +} + +/** + * Get the value of a Python dictionary item, returned as a newly + * allocated char *. + * + * @param py_obj The dictionary to probe. + * @param py_key Key of the item to retrieve. + * @param out TODO. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * + * @private + */ +SRD_PRIV int py_pydictitem_as_long(PyObject *py_obj, PyObject *py_key, uint64_t *out) +{ + PyObject *py_value; + PyGILState_STATE gstate; + + if (!py_obj || !py_key || !out) + return SRD_ERR_ARG; + + gstate = PyGILState_Ensure(); + + if (!PyDict_Check(py_obj)) { + srd_dbg("Object is not a dictionary."); + goto err; + } + + if (!(py_value = PyDict_GetItem(py_obj, py_key))) { + srd_dbg("Dictionary has no such key."); + goto err; + } + + if (!PyLong_Check(py_value)) { + srd_dbg("Dictionary value should be a long."); + goto err; + } + + *out = PyLong_AsUnsignedLongLong(py_value); + + PyGILState_Release(gstate); + + return SRD_OK; + +err: + PyGILState_Release(gstate); + + return SRD_ERR_PYTHON; } /** @@ -159,9 +392,13 @@ SRD_PRIV int py_str_as_str(PyObject *py_str, char **outstr) { PyObject *py_bytes; char *str; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); if (!PyUnicode_Check(py_str)) { srd_dbg("Object is not a string object."); + PyGILState_Release(gstate); return SRD_ERR_PYTHON; } @@ -171,10 +408,13 @@ SRD_PRIV int py_str_as_str(PyObject *py_str, char **outstr) Py_DECREF(py_bytes); if (str) { *outstr = str; + PyGILState_Release(gstate); return SRD_OK; } } - srd_exception_catch(NULL, "Failed to extract string"); + srd_exception_catch(NULL, "Failed to extract string"); + + PyGILState_Release(gstate); return SRD_ERR_PYTHON; } @@ -197,54 +437,64 @@ SRD_PRIV int py_strseq_to_char(PyObject *py_strseq, char ***out_strv) PyObject *py_item, *py_bytes; char **strv, *str; ssize_t seq_len, i; + PyGILState_STATE gstate; + int ret = SRD_ERR_PYTHON; + gstate = PyGILState_Ensure(); + + str = NULL; + strv = NULL; if (!PySequence_Check(py_strseq)) { srd_err("Object does not provide sequence protocol."); - return SRD_ERR_PYTHON; + goto err; } seq_len = PySequence_Size(py_strseq); if (seq_len < 0) { - srd_exception_catch(NULL, "Failed to obtain sequence size"); - return SRD_ERR_PYTHON; + srd_exception_catch(NULL, "Failed to obtain sequence size"); + goto err; } strv = g_try_new0(char *, seq_len + 1); if (!strv) { srd_err("Failed to allocate result string vector."); - return SRD_ERR_MALLOC; + ret = SRD_ERR_MALLOC; + goto err; } for (i = 0; i < seq_len; i++) { py_item = PySequence_GetItem(py_strseq, i); if (!py_item) - goto err_out; + goto err; if (!PyUnicode_Check(py_item)) { Py_DECREF(py_item); - goto err_out; + goto err; } py_bytes = PyUnicode_AsUTF8String(py_item); Py_DECREF(py_item); if (!py_bytes) - goto err_out; + goto err; str = g_strdup(PyBytes_AsString(py_bytes)); Py_DECREF(py_bytes); if (!str) - goto err_out; + goto err; strv[i] = str; } *out_strv = strv; + PyGILState_Release(gstate); + return SRD_OK; -err_out: - g_strfreev(strv); - srd_exception_catch(NULL, "Failed to obtain string item"); - - return SRD_ERR_PYTHON; +err: + if (strv) + g_strfreev(strv); + srd_exception_catch(NULL, "Failed to obtain string item"); + PyGILState_Release(gstate); + return ret; } /** @@ -259,6 +509,9 @@ err_out: SRD_PRIV GVariant *py_obj_to_variant(PyObject *py_obj) { GVariant *var = NULL; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); if (PyUnicode_Check(py_obj)) { /* string */ PyObject *py_bytes; @@ -272,8 +525,7 @@ SRD_PRIV GVariant *py_obj_to_variant(PyObject *py_obj) Py_DECREF(py_bytes); } if (!var) - srd_exception_catch(NULL, "Failed to extract string value"); - + srd_exception_catch(NULL, "Failed to extract string value"); } else if (PyLong_Check(py_obj)) { /* integer */ int64_t val; @@ -281,8 +533,7 @@ SRD_PRIV GVariant *py_obj_to_variant(PyObject *py_obj) if (!PyErr_Occurred()) var = g_variant_new_int64(val); else - srd_exception_catch(NULL, "Failed to extract integer value"); - + srd_exception_catch(NULL, "Failed to extract integer value"); } else if (PyFloat_Check(py_obj)) { /* float */ double val; @@ -290,11 +541,12 @@ SRD_PRIV GVariant *py_obj_to_variant(PyObject *py_obj) if (!PyErr_Occurred()) var = g_variant_new_double(val); else - srd_exception_catch(NULL, "Failed to extract float value"); - + srd_exception_catch(NULL, "Failed to extract float value"); } else { srd_err("Failed to extract value of unsupported type."); } + PyGILState_Release(gstate); + return var; } diff --git a/libsigrokdecode4DSL/version.c b/libsigrokdecode4DSL/version.c old mode 100644 new mode 100755 index e708fdaa..cf4a80a0 --- a/libsigrokdecode4DSL/version.c +++ b/libsigrokdecode4DSL/version.c @@ -14,11 +14,11 @@ * 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program; if not, see . */ #include "config.h" +#include "libsigrokdecode-internal.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */ #include "libsigrokdecode.h" /** diff --git a/libsigrokdecode4DSL/version.h.in b/libsigrokdecode4DSL/version.h.in old mode 100644 new mode 100755 diff --git a/ug.pdf b/ug.pdf deleted file mode 100755 index b3a199cd..00000000 Binary files a/ug.pdf and /dev/null differ diff --git a/ug25.pdf b/ug25.pdf new file mode 100755 index 00000000..d14e0077 Binary files /dev/null and b/ug25.pdf differ diff --git a/ug31.pdf b/ug31.pdf new file mode 100755 index 00000000..5198792f Binary files /dev/null and b/ug31.pdf differ