first commit

This commit is contained in:
magnum88 2024-06-04 19:03:30 +02:00
commit 37d7fa7261
64 changed files with 13353 additions and 0 deletions

39
README.md Normal file
View File

@ -0,0 +1,39 @@
DatvRx
===============
Digital Tv HamRadio under Linux
V2.0
============================
## Version en production 2.0
## 2.0
Récupérer le ZIP,<br>
Ou télécharger le repo<br>
```git clone http://fra1od.freeboxos.fr:3000/magnum88/DatvRx ```<br>
Il vous reste à installer les dépendenses<br>
```pip install -r requierement.txt```<br>
puis compiler longmynd sil ne marchera pas<br>
dans le répertoire longmynd<br>
``
sudo apt-get install libusb-1.0-0-dev libasound2-dev tstools
make
``<br>
revenir dans le répertoire DatvRx<br>
copier le fichier<br>
dans /etc/udev/rules.d/<br>
donc<br>
```
sudo cp minitiouner.rules /etc/udev/rules.d/
```<br>
et enfin lancer le programme<br>
puis faire ./main.py <br>
73<br>
Xavier

7
config.json Executable file
View File

@ -0,0 +1,7 @@
{"IP": "127.0.0.1",
"PORT": "4321",
"DEV": "/dev/ttyUSB0",
"FREQ": "1049",
"F1": "9",
"F2": "250",
"SR": "333"}

BIN
icon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
img/aprs_ico2.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
img/btnjaune.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
img/btnjaunesmall.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 B

BIN
img/btnrouge.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
img/btnrougesmall.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 B

BIN
img/btnvert.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
img/btnvertsmall.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 B

BIN
img/datv_ico.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
img/icone.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
img/nosignal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
img/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

5
longmynd/.gitignore vendored Executable file
View File

@ -0,0 +1,5 @@
*~
*.swp
*.o
longmynd
fake_read

31
longmynd/.travis.yml Executable file
View File

@ -0,0 +1,31 @@
## Travis CI Configuration file
matrix:
include:
# Ubuntu 16.04
- os: linux
dist: xenial
# Ubuntu 18.04
- os: linux
dist: bionic
language: c
# Install dependencies
before_install:
- sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
- sudo apt-get update -q
# Build dependencies
- sudo apt-get install libusb-1.0-0-dev libasound2-dev -y
# Test Matrix dependencies
- sudo apt-get install gcc-5 gcc-6 gcc-7 gcc-8 gcc-9 -y
# Only grab latest git commit (no need for history)
git:
depth: 1
script:
- make clean && make werror GCC=gcc-5
- make clean && make werror GCC=gcc-6
- make clean && make werror GCC=gcc-7
- make clean && make werror GCC=gcc-8
- make clean && make werror GCC=gcc-9

674
longmynd/LICENSE Executable file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

40
longmynd/Makefile Executable file
View File

@ -0,0 +1,40 @@
BIN = longmynd
SRC = main.c nim.c ftdi.c stv0910.c stv0910_utils.c stvvglna.c stvvglna_utils.c stv6120.c stv6120_utils.c ftdi_usb.c fifo.c udp.c beep.c ts.c
OBJ = ${SRC:.c=.o}
ifndef CC
CC = gcc
endif
COPT = -O3 -march=native -mtune=native
CFLAGS += -Wall -Wextra -Wpedantic -Wunused -DVERSION=\"${VER}\" -pthread -D_GNU_SOURCE
LDFLAGS += -lusb-1.0 -lm -lasound
all: ${BIN} fake_read
debug: COPT = -Og
debug: CFLAGS += -ggdb -fno-omit-frame-pointer
debug: all
werror: CFLAGS += -Werror
werror: all
fake_read:
@echo " CC "$@
@${CC} fake_read.c -o $@
$(BIN): ${OBJ}
@echo " LD "$@
@${CC} ${COPT} ${CFLAGS} -o $@ ${OBJ} ${LDFLAGS}
%.o: %.c
@echo " CC "$<
@${CC} ${COPT} ${CFLAGS} -c -fPIC -o $@ $<
clean:
@rm -rf ${BIN} fake_read ${OBJ}
tags:
@ctags *
.PHONY: all clean

154
longmynd/README.md Executable file
View File

@ -0,0 +1,154 @@
# Longmynd [![Build Status](https://travis-ci.org/myorangedragon/longmynd.svg?branch=master)](https://travis-ci.org/myorangedragon/longmynd)
An Open Source Linux ATV Receiver.
Copyright 2019 Heather Lomond
## Dependencies
sudo apt-get install libusb-1.0-0-dev libasound2-dev tstools
To run longmynd without requiring root, unplug the minitiouner and then install the udev rules file with:
sudo cp minitiouner.rules /etc/udev/rules.d/
## Compile
make
## Run
Please refer to the longmynd manual page via:
```
man -l longmynd.1
```
## Standalone
If running longmynd standalone (i.e. not integrated with the Portsdown software), you must create the status FIFO and (if you plan to use it) the TS FIFO:
```
mkfifo longmynd_main_status
mkfifo longmynd_main_ts
```
The test harness `fake_read` or a similar process must be running to consume the output of the status FIFO:
```
./fake_read &
```
A video player (e.g. VLC) must be running to consume the output of the TS FIFO.
## Output
The status fifo is filled with status information as and when it becomes available.
The format of the status information is:
$n,m<cr>
Where:
n = identifier integer of Status message
m = integer value associated with this status message
And the values of n and m are defined as:
ID Meaning Value and Units
==============================================================================================
1 State 0: initialising
1: searching
2: found headers
3: locked on a DVB-S signal
4: locked on a DVB-S2 signal
2 LNA Gain On devices that have LNA Amplifiers this represents the two gain
sent as N, where n = (lna_gain<<5) | lna_vgo
Though not actually linear, n can be usefully treated as a single
byte representing the gain of the amplifier
3 Puncture Rate During a search this is the pucture rate that is being trialled
When locked this is the pucture rate detected in the stream
Sent as a single value, n, where the pucture rate is n/(n+1)
4 I Symbol Power Measure of the current power being seen in the I symbols
5 Q Symbol Power Measure of the current power being seen in the Q symbols
6 Carrier Frequency During a search this is the carrier frequency being trialled
When locked this is the Carrier Frequency detected in the stream
Sent in KHz
7 I Constellation Single signed byte representing the voltage of a sampled I point
8 Q Constellation Single signed byte representing the voltage of a sampled Q point
9 Symbol Rate During a search this is the symbol rate being trialled
When locked this is the symbol rate detected in the stream
10 Viterbi Error Rate Viterbi correction rate as a percentage * 100
11 BER Bit Error Rate as a Percentage * 100
12 MER Modulation Error Ratio in dB * 10
13 Service Provider TS Service Provider Name
14 Service TS Service Name
15 Null Ratio Ratio of Nulls in TS as percentage
16 ES PID Elementary Stream PID (repeated as pair with 17 for each ES)
17 ES Type Elementary Stream Type (repeated as pair with 16 for each ES)
18 MODCOD Received Modulation & Coding Rate. See MODCOD Lookup Table below
19 Short Frames 1 if received signal is using Short Frames, 0 otherwise (DVB-S2 only)
20 Pilot Symbols 1 if received signal is using Pilot Symbols, 0 otherwise (DVB-S2 only)
21 LDPC Error Count LDPC Corrected Errors in last frame (DVB-S2 only)
22 BCH Error Count BCH Corrected Errors in last frame (DVB-S2 only)
23 BCH Uncorrected 1 if some BCH-detected errors were not able to be corrected, 0 otherwise (DVB-S2 only)
24 LNB Voltage Enabled 1 if LNB Voltage Supply is enabled, 0 otherwise (LNB Voltage Supply requires add-on board)
25 LNB H Polarisation 1 if LNB Voltage Supply is configured for Horizontal Polarisation (18V), 0 otherwise (LNB Voltage Supply requires add-on board)
### MODCOD Lookup
### DVB-S
```
0: QPSK 1/2
1: QPSK 2/3
2: QPSK 3/4
3: QPSK 5/6
4: QPSK 6/7
5: QPSK 7/8
```
#### DVB-S2
```
0: DummyPL
1: QPSK 1/4
2: QPSK 1/3
3: QPSK 2/5
4: QPSK 1/2
5: QPSK 3/5
6: QPSK 2/3
7: QPSK 3/4
8: QPSK 4/5
9: QPSK 5/6
10: QPSK 8/9
11: QPSK 9/10
12: 8PSK 3/5
13: 8PSK 2/3
14: 8PSK 3/4
15: 8PSK 5/6
16: 8PSK 8/9
17: 8PSK 9/10
18: 16APSK 2/3
19: 16APSK 3/4
20: 16APSK 4/5
21: 16APSK 5/6
22: 16APSK 8/9
23: 16APSK 9/10
24: 32APSK 3/4
25: 32APSK 4/5
26: 32APSK 5/6
27: 32APSK 8/9
28: 32APSK 9/10
```
## License
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.

328
longmynd/beep.c Executable file
View File

@ -0,0 +1,328 @@
/*
* Copyright (C) 2000-2004 James Courtier-Dutton
* Copyright (C) 2005 Nathan Hurst
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
#include <ctype.h>
#include <stdbool.h>
#include <pthread.h>
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
#include <alsa/asoundlib.h>
#include <sys/time.h>
#include <math.h>
#include "main.h"
#include "errors.h"
static void generate_sine(uint8_t *frames, int count, double *_phase, double _freq, unsigned int _rate, unsigned int _channels, bool _enable) {
double phase = *_phase;
double max_phase = 1.0 / _freq;
double step = 1.0 / (double)_rate;
double res = 0.0;
unsigned int chn;
int32_t ires;
int16_t *samp16 = (int16_t*) frames;
while (count-- > 0) {
for(chn = 0; chn < _channels; chn++) {
// SND_PCM_FORMAT_S16_LE:
if(_enable)
{
res = (sin((phase * 2 * M_PI) / max_phase - M_PI)) * 0x03fffffff; /* Don't use MAX volume */
}
ires = res;
*samp16++ = ires >> 16;
}
phase += step;
if (phase >= max_phase)
phase -= max_phase;
}
*_phase = phase;
}
static int set_hwparams(
snd_pcm_t *handle,
snd_pcm_hw_params_t *params,
snd_pcm_access_t access,
snd_pcm_format_t format,
unsigned int rate,
unsigned int channels,
snd_pcm_uframes_t *buffer_size,
snd_pcm_uframes_t *period_size) {
unsigned int rrate;
int err;
snd_pcm_uframes_t period_size_min;
snd_pcm_uframes_t period_size_max;
snd_pcm_uframes_t buffer_size_min;
snd_pcm_uframes_t buffer_size_max;
unsigned int period_time = 0; /* period time in us */
unsigned int buffer_time = 0; /* ring buffer length in us */
unsigned int nperiods = 4; /* number of periods */
/* choose all parameters */
err = snd_pcm_hw_params_any(handle, params);
if (err < 0) {
fprintf(stderr, "Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
return err;
}
/* set the interleaved read/write format */
err = snd_pcm_hw_params_set_access(handle, params, access);
if (err < 0) {
fprintf(stderr, "Access type not available for playback: %s\n", snd_strerror(err));
return err;
}
/* set the sample format */
err = snd_pcm_hw_params_set_format(handle, params, format);
if (err < 0) {
fprintf(stderr, "Sample format not available for playback: %s\n", snd_strerror(err));
return err;
}
/* set the count of channels */
err = snd_pcm_hw_params_set_channels(handle, params, channels);
if (err < 0) {
fprintf(stderr, "Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
return err;
}
/* set the stream rate */
rrate = rate;
err = snd_pcm_hw_params_set_rate(handle, params, rate, 0);
if (err < 0) {
fprintf(stderr, "Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
return err;
}
if (rrate != rate) {
fprintf(stderr, "Rate doesn't match (requested %iHz, get %iHz, err %d)\n", rate, rrate, err);
return -EINVAL;
}
//printf(_("Rate set to %iHz (requested %iHz)\n"), rrate, rate);
/* set the buffer time */
err = snd_pcm_hw_params_get_buffer_size_min(params, &buffer_size_min);
err = snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size_max);
err = snd_pcm_hw_params_get_period_size_min(params, &period_size_min, NULL);
err = snd_pcm_hw_params_get_period_size_max(params, &period_size_max, NULL);
//printf(_("Buffer size range from %lu to %lu\n"),buffer_size_min, buffer_size_max);
//printf(_("Period size range from %lu to %lu\n"),period_size_min, period_size_max);
if (period_time > 0) {
//printf(_("Requested period time %u us\n"), period_time);
err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, NULL);
if (err < 0) {
fprintf(stderr, "Unable to set period time %u us for playback: %s\n",
period_time, snd_strerror(err));
return err;
}
}
if (buffer_time > 0) {
//printf(_("Requested buffer time %u us\n"), buffer_time);
err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, NULL);
if (err < 0) {
fprintf(stderr, "Unable to set buffer time %u us for playback: %s\n",
buffer_time, snd_strerror(err));
return err;
}
}
if (! buffer_time && ! period_time) {
//buffer_size = buffer_size_max;
//buffer_size = buffer_size_min;
*buffer_size = 2048;
if (! period_time)
*buffer_size = (*buffer_size / nperiods) * nperiods;
//printf(_("Using max buffer size %lu\n"), buffer_size);
//printf(_("Using min buffer size %lu\n"), *buffer_size);
err = snd_pcm_hw_params_set_buffer_size_near(handle, params, buffer_size);
if (err < 0) {
fprintf(stderr, "Unable to set buffer size %lu for playback: %s\n",
*buffer_size, snd_strerror(err));
return err;
}
}
if (! buffer_time || ! period_time) {
//printf(_("Periods = %u\n"), nperiods);
err = snd_pcm_hw_params_set_periods_near(handle, params, &nperiods, NULL);
if (err < 0) {
fprintf(stderr, "Unable to set nperiods %u for playback: %s\n",
nperiods, snd_strerror(err));
return err;
}
}
/* write the parameters to device */
err = snd_pcm_hw_params(handle, params);
if (err < 0) {
fprintf(stderr, "Unable to set hw params for playback: %s\n", snd_strerror(err));
return err;
}
snd_pcm_hw_params_get_buffer_size(params, buffer_size);
snd_pcm_hw_params_get_period_size(params, period_size, NULL);
//printf(_("was set period_size = %lu\n"),*period_size);
//printf(_("was set buffer_size = %lu\n"),*buffer_size);
if (2*(*period_size) > *buffer_size) {
fprintf(stderr, "buffer to small, could not use\n");
return -EINVAL;
}
return 0;
}
static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, snd_pcm_uframes_t *buffer_size, snd_pcm_uframes_t *period_size) {
int err;
/* get the current swparams */
err = snd_pcm_sw_params_current(handle, swparams);
if (err < 0) {
fprintf(stderr, "Unable to determine current swparams for playback: %s\n", snd_strerror(err));
return err;
}
/* start the transfer when a buffer is full */
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, *buffer_size - *period_size);
if (err < 0) {
fprintf(stderr, "Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
return err;
}
/* allow the transfer when at least period_size frames can be processed */
err = snd_pcm_sw_params_set_avail_min(handle, swparams, *period_size);
if (err < 0) {
fprintf(stderr, "Unable to set avail min for playback: %s\n", snd_strerror(err));
return err;
}
/* write the parameters to the playback device */
err = snd_pcm_sw_params(handle, swparams);
if (err < 0) {
fprintf(stderr, "Unable to set sw params for playback: %s\n", snd_strerror(err));
return err;
}
return 0;
}
void *loop_beep(void *arg) {
thread_vars_t *thread_vars = (thread_vars_t *)arg;
uint8_t *err = &thread_vars->thread_err;
while(*err == ERROR_NONE && *thread_vars->main_err_ptr == ERROR_NONE){
if(thread_vars->config->beep_enabled){
snd_pcm_t *handle;
int error;
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_sw_params_alloca(&swparams);
char *device = "default"; /* playback device */
snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */
unsigned int rate = 48000; /* stream rate */
unsigned int channels = 2; /* count of channels */
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t period_size;
if ((error = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
printf("Playback open error: %d,%s\n", error,snd_strerror(error));
*err = ERROR_THREAD_ERROR;
return NULL;
}
if ((error = set_hwparams(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED, format, rate, channels, &buffer_size, &period_size)) < 0) {
printf("Setting of hwparams failed: %s\n", snd_strerror(error));
snd_pcm_close(handle);
*err = ERROR_THREAD_ERROR;
return NULL;
}
if ((error = set_swparams(handle, swparams, &buffer_size, &period_size)) < 0) {
printf("Setting of swparams failed: %s\n", snd_strerror(error));
snd_pcm_close(handle);
*err = ERROR_THREAD_ERROR;
return NULL;
}
uint8_t *frames;
frames = malloc(snd_pcm_frames_to_bytes(handle, period_size));
if (frames == NULL) {
fprintf(stderr, "No enough memory\n");
*err = ERROR_THREAD_ERROR;
return NULL;
}
snd_pcm_prepare(handle);
double freq = 400.0;
double phase = 0.0;
if(thread_vars->status->modulation_error_rate > 0 && thread_vars->status->modulation_error_rate <= 310)
{
freq = 700.0 * (exp((200+(10*thread_vars->status->modulation_error_rate))/1127.0)-1.0);
}
generate_sine(frames, period_size, &phase, freq, rate, channels, (thread_vars->status->state == STATE_DEMOD_S2));
/* Start playback */
snd_pcm_writei(handle, frames, period_size);
snd_pcm_start(handle);
snd_pcm_sframes_t avail;
while(*err == ERROR_NONE && *thread_vars->main_err_ptr == ERROR_NONE && thread_vars->config->beep_enabled){
/* Wait until device is ready for more data */
if(snd_pcm_wait(handle, 100))
{
avail = snd_pcm_avail_update(handle);
if(avail < 0 && avail == -32)
{
/* Handle underrun */
snd_pcm_prepare(handle);
}
while (avail >= (snd_pcm_sframes_t)period_size)
{
if(thread_vars->status->modulation_error_rate > 0 && thread_vars->status->modulation_error_rate <= 310)
{
freq = 700.0 * (exp((200+(10*thread_vars->status->modulation_error_rate))/1127.0)-1.0);
}
generate_sine(frames, period_size, &phase, freq, rate, channels, (thread_vars->status->state == STATE_DEMOD_S2));
while (snd_pcm_writei(handle, frames, period_size) < 0)
{
/* Handle underrun */
snd_pcm_prepare(handle);
}
avail = snd_pcm_avail_update(handle);
}
}
}
/* Stop Playback */
snd_pcm_drop(handle);
/* Empty buffer */
snd_pcm_drain(handle);
free(frames);
snd_pcm_close(handle);
}
usleep(10*1000); // 10ms
}
return NULL;
}

28
longmynd/beep.h Executable file
View File

@ -0,0 +1,28 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: beep.h */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BEEP_H
#define BEEP_H
void *loop_beep(void *arg);
#endif

78
longmynd/errors.h Executable file
View File

@ -0,0 +1,78 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: errors.h */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef ERRORS_H
#define ERRORS_H
/* possible error states */
#define ERROR_NONE 0
#define ERROR_ARGS 1
#define ERROR_ARGS_INPUT 2
#define ERROR_STATE 3
#define ERROR_DEMOD_STATE 4
#define ERROR_FTDI_READ_HIGH 5
#define ERROR_READ_DEMOD 6
#define ERROR_WRITE_DEMOD 7
#define ERROR_READ_OTHER 8
#define ERROR_WRITE_OTHER 9
#define ERROR_FTDI_USB_INIT_LIBUSB 101
#define ERROR_FTDI_USB_VID_PID 102
#define ERROR_FTDI_USB_CLAIM 103
#define ERROR_FTDI_USB_I2C_WRITE 104
#define ERROR_FTDI_USB_I2C_READ 105
#define ERROR_FTDI_USB_CMD 106
#define ERROR_FTDI_USB_INIT 107
#define ERROR_FTDI_SYNC_AA 10
#define ERROR_FTDI_SYNC_AB 11
#define ERROR_NIM_INIT 12
#define ERROR_LNA_ID 13
#define ERROR_TUNER_ID 14
#define ERROR_TUNER_LOCK_TIMEOUT 15
#define ERROR_TUNER_CAL_TIMEOUT 16
#define ERROR_TUNER_CAL_LOWPASS_TIMEOUT 17
#define ERROR_I2C_NO_ACK 18
#define ERROR_FTDI_I2C_WRITE_LEN 19
#define ERROR_FTDI_I2C_READ_LEN 20
#define ERROR_MPSSE 21
#define ERROR_DEMOD_INIT 22
#define ERROR_BAD_DEMOD_HUNT_STATE 23
#define ERROR_TS_FIFO_WRITE 24
#define ERROR_OPEN_TS_FIFO 25
#define ERROR_TS_FIFO_CREATE 26
#define ERROR_STATUS_FIFO_CREATE 27
#define ERROR_OPEN_STATUS_FIFO 28
#define ERROR_TS_FIFO_CLOSE 29
#define ERROR_STATUS_FIFO_CLOSE 30
#define ERROR_USB_TS_READ 31
#define ERROR_LNA_AGC_TIMEOUT 32
#define ERROR_DEMOD_PLL_TIMEOUT 33
#define ERROR_FTDI_USB_DEVICE_LIST 34
#define ERROR_FTDI_USB_BAD_DEVICE_NUM 35
#define ERROR_FTDI_USB_DEVICE_NUM_OPEN 36
#define ERROR_UDP_WRITE 37
#define ERROR_UDP_SOCKET_OPEN 38
#define ERROR_UDP_CLOSE 39
#define ERROR_VITERBI_PUNCTURE_RATE 40
#define ERROR_TS_BUFFER_MALLOC 41
#define ERROR_THREAD_ERROR 41
#endif

32
longmynd/fake_read.c Executable file
View File

@ -0,0 +1,32 @@
/* pretends to be a portsdown */
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int num;
int ret;
int fd_status_fifo;
char status_message[14];
/* Open status FIFO for read only */
ret=mkfifo("longmynd_main_status", 0666);
fd_status_fifo = open("longmynd_main_status", O_RDONLY);
if (fd_status_fifo<0) printf("Failed to open status fifo\n");
printf("Listening\n");
while (1) {
num=read(fd_status_fifo, status_message, 1);
status_message[num]='\0';
if (num>0) printf("%s",status_message);
}
close(fd_status_fifo);
return 0;
}

203
longmynd/fifo.c Executable file
View File

@ -0,0 +1,203 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: fifo.c */
/* - an implementation of the Serit NIM controlling software for the MiniTiouner Hardware */
/* - linux fifo handlers for the transport stream (TS) and the status stream (status) */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- INCLUDES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdint.h>
#include <unistd.h>
#include <stdbool.h>
#include "errors.h"
#include "fifo.h"
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- GLOBALS ------------------------------------------------------------------------ */
/* -------------------------------------------------------------------------------------------------- */
int fd_ts_fifo;
int fd_status_fifo;
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- ROUTINES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
uint8_t fifo_ts_write(uint8_t *buffer, uint32_t len) {
/* -------------------------------------------------------------------------------------------------- */
/* takes a buffer and writes out the contents to the ts fifo but removes the unwanted bytes */
/* *buffer: the buffer that contains the data to be sent */
/* len: the length (number of bytes) of data to be sent */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
int ret;
int32_t remaining_len; /* note it is signed so can go negative */
uint32_t write_size;
remaining_len=len;
/* we need to loop round sending 510 byte chunks so that we can skip the 2 extra bytes put in by */
/* the FTDI chip every 512 bytes of USB message */
while (remaining_len>0) {
if (remaining_len>510) {
/* calculate where to start in the buffer and how many bytes to send */
write_size=510;
ret=write(fd_ts_fifo, &buffer[len-remaining_len], write_size);
/* note we skip over the 2 bytes inserted by the FTDI */
remaining_len-=512;
} else {
write_size=remaining_len;
ret=write(fd_ts_fifo, &buffer[len-remaining_len], write_size);
remaining_len-=write_size; /* should be 0 if all went well */
}
if (ret!=(int)write_size) {
printf("ERROR: ts fifo write\n");
err=ERROR_TS_FIFO_WRITE;
break;
}
}
/* if someting went bad with our calcs, remaining will not be 0 */
if ((err==ERROR_NONE) && (remaining_len!=0)) {
printf("ERROR: ts fifo write incorrect number of bytes\n");
err=ERROR_TS_FIFO_WRITE;
}
if (err!=ERROR_NONE) printf("ERROR: fifo ts write\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t fifo_status_write(uint8_t message, uint32_t data) {
/* -------------------------------------------------------------------------------------------------- */
/* *message: the string to write out that identifies the status message */
/* data: an integer to be sent out (as a decimal number string) */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
int ret;
char status_message[30];
sprintf(status_message, "$%i,%i\n", message, data);
ret=write(fd_status_fifo, status_message, strlen(status_message));
if (ret!=(int)strlen(status_message)) {
printf("ERROR: status fifo write\n");
err=ERROR_TS_FIFO_WRITE;
}
if (err!=ERROR_NONE) printf("ERROR: fifo status write\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t fifo_status_string_write(uint8_t message, char *data) {
/* -------------------------------------------------------------------------------------------------- */
/* *message: the string to write out that identifies the status message */
/* data: an integer to be sent out (as a decimal number string) */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
int ret;
char status_message[5+128];
sprintf(status_message, "$%i,%s\n", message, data);
ret=write(fd_status_fifo, status_message, strlen(status_message));
if (ret!=(int)strlen(status_message)) {
printf("ERROR: status fifo write\n");
err=ERROR_TS_FIFO_WRITE;
}
if (err!=ERROR_NONE) printf("ERROR: fifo status write\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
static uint8_t fifo_init(int *fd_ptr, char *fifo_path) {
/* -------------------------------------------------------------------------------------------------- */
/* initialises the ts and status fifos */
/* ts_fifo: the name of the fifo to use for the TS */
/* status_fifo: the name of the fifo to use for the status output */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
printf("Flow: Fifo Init\n");
/* if we are using the TS FIFO then set it up first */
*fd_ptr = open(fifo_path, O_WRONLY);
if (err==ERROR_NONE) {
if (*fd_ptr<0) {
printf("ERROR: Failed to open fifo %s\n",fifo_path);
err=ERROR_OPEN_TS_FIFO;
} else printf(" Status: opened fifo ok\n");
}
if (err!=ERROR_NONE) printf("ERROR: fifo init\n");
return err;
}
uint8_t fifo_ts_init(char *fifo_path) {
return fifo_init(&fd_ts_fifo, fifo_path);
}
uint8_t fifo_status_init(char *fifo_path) {
return fifo_init(&fd_status_fifo, fifo_path);
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t fifo_close(bool ignore_ts_fifo) {
/* ------------------------------------------------------------------------------------------------- */
/* closes the fifo's */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
int ret;
if (!ignore_ts_fifo) {
ret=close(fd_ts_fifo);
if (ret!=0) {
printf("ERROR: ts fifo close\n");
err=ERROR_TS_FIFO_CLOSE;
}
}
ret=close(fd_status_fifo);
if (ret!=0) {
printf("ERROR: status fifo close\n");
err=ERROR_STATUS_FIFO_CLOSE;
}
if (err!=ERROR_NONE) printf("ERROR: fifo close\n");
return err;
}

35
longmynd/fifo.h Executable file
View File

@ -0,0 +1,35 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: fifo.h */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef FIFO_H
#define FIFO_H
#include <stdint.h>
uint8_t fifo_ts_write(uint8_t*, uint32_t);
uint8_t fifo_status_write(uint8_t, uint32_t);
uint8_t fifo_status_string_write(uint8_t, char*);
uint8_t fifo_ts_init(char *fifo_path);
uint8_t fifo_status_init(char *fifo_path);
uint8_t fifo_close(bool);
#endif

528
longmynd/ftdi.c Executable file
View File

@ -0,0 +1,528 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: ftdi.c */
/* - an implementation of the Serit NIM controlling software for the MiniTiouner Hardware */
/* - implements all the ftdi i2c accessing routines apart from the usb stuff */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- INCLUDES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include "ftdi.h"
#include "ftdi_usb.h"
#include "nim.h"
#include "errors.h"
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- DEFINES ------------------------------------------------------------------------ */
/* -------------------------------------------------------------------------------------------------- */
#define FTDI_VID 0x0403
#define FTDI_PID 0x6010
#define FTDI_NUM_TRIES 10
#define FTDI_STOP_START_REPEATS 4
#define FTDI_RDWR_TIMEOUT 100
#define MSB_FALLING_EDGE_CLOCK_BYTE_IN 0x24
#define MSB_FALLING_EDGE_CLOCK_BYTE_OUT 0x11
#define MSB_RISING_EDGE_CLOCK_BIT_IN 0x22
#define MSB_FAILING_EDGE_CLOCK_BIT_IN 0x26
/*
FTDI GPIO Pins
LSB
- AC0: NIM Reset
- AC1: TS2SYNC
- AC2: <unused>
- AC3: <unused>
- AC4: LNB Bias Enable
- AC5: <unused>
- AC6: <unused>
- AC7: LNB Bias Voltage Select
MSB
*/
#define FTDI_GPIO_PINID_NIM_RESET 0
#define FTDI_GPIO_PINID_TS2SYNC 1
#define FTDI_GPIO_PINID_LNB_BIAS_ENABLE 4
#define FTDI_GPIO_PINID_LNB_BIAS_VSEL 7
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- GLOBALS ------------------------------------------------------------------------ */
/* -------------------------------------------------------------------------------------------------- */
static int num_bytes_to_send = 0;
static uint8_t out_buffer[256];
/* Default GPIO value 0x6f = 0b01101111 = LNB Bias Off, LNB Voltage 12V, NIM not reset */
static uint8_t ftdi_gpio_value = 0x6f;
/* Default GPIO direction 0xf1 = 0b11110001 = LNB pins, NIM Reset are outputs, TS2SYNC is input (0 for in and 1 for out) */
static uint8_t ftdi_gpio_direction = 0xf1;
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- ROUTINES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
uint8_t ftdi_setup_ftdi_io(void){
/* -------------------------------------------------------------------------------------------------- */
/* sets up the IO stages of the FTDI and syncs the comms */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
uint8_t *in_buffer;
uint8_t old_in_buffer=0;
int i;
int clock_divisor = 0x0095;
printf("Flow: FTDI setup io\n");
/* note we don't accumulate errors for this bit as we are trying to sync */
/* first we send out a byte to act as our sync */
num_bytes_to_send = 0;
out_buffer[num_bytes_to_send++] = 0xAA;
if (err==ERROR_NONE) ftdi_usb_i2c_write(out_buffer, num_bytes_to_send);
num_bytes_to_send = 0;
/* next we keep reading it until we get it back, and the reply before it */
for (i=0; i<FTDI_NUM_TRIES; i++) {
if (err==ERROR_NONE) ftdi_usb_i2c_read(&in_buffer);
if ((old_in_buffer==0xFA) && (*in_buffer==0xAA)) {
printf(" Status: MPSSE Synched ");
printf("%.2X %.2X\n",*in_buffer,old_in_buffer);
break;
}
old_in_buffer = *in_buffer;
}
out_buffer[num_bytes_to_send++]=0x8A;
out_buffer[num_bytes_to_send++]=0x97;
out_buffer[num_bytes_to_send++]=0x8D;
if (err==ERROR_NONE) err=ftdi_usb_i2c_write(out_buffer, num_bytes_to_send);
num_bytes_to_send = 0;
out_buffer[num_bytes_to_send++]=0x80;
out_buffer[num_bytes_to_send++]=0x13;
out_buffer[num_bytes_to_send++]=0x13;
out_buffer[num_bytes_to_send++]=0x82;
out_buffer[num_bytes_to_send++]=0x6f;
out_buffer[num_bytes_to_send++]=0xf1;
out_buffer[num_bytes_to_send++] = 0x86;
out_buffer[num_bytes_to_send++] = clock_divisor & 0xFF;
out_buffer[num_bytes_to_send++] = (clock_divisor >> 8) & 0xFF;
if (err==ERROR_NONE) err=ftdi_usb_i2c_write(out_buffer, num_bytes_to_send);
num_bytes_to_send = 0;
usleep(30000);
out_buffer[num_bytes_to_send++] = 0x85;
if (err==ERROR_NONE) err=ftdi_usb_i2c_write(out_buffer, num_bytes_to_send);
num_bytes_to_send = 0;
/* need to wait a while for it to work */
usleep(30000);
if (err!=ERROR_NONE) printf("ERROR: set mpsse mode\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t ftdi_i2c_set_start(void) {
/* -------------------------------------------------------------------------------------------------- */
/* sets the i2c start condition on the i2c bus */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
int count;
for (count=0; count<FTDI_STOP_START_REPEATS; count++) {
out_buffer[num_bytes_to_send++] = 0x80;
out_buffer[num_bytes_to_send++] = 0x03;
out_buffer[num_bytes_to_send++] = 0x13;
}
for (count=0; count<FTDI_STOP_START_REPEATS; count++) {
out_buffer[num_bytes_to_send++] = 0x80;
out_buffer[num_bytes_to_send++] = 0x01;
out_buffer[num_bytes_to_send++] = 0x13;
}
/* note we don't send this out yet as there will be more */
return ERROR_NONE;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t ftdi_i2c_set_stop(void) {
/* -------------------------------------------------------------------------------------------------- */
/* sets the i2c stop condition on the i2c bus */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
int count;
for (count=0; count<FTDI_STOP_START_REPEATS; count++) {
out_buffer[num_bytes_to_send++] = 0x80;
out_buffer[num_bytes_to_send++] = 0x01;
out_buffer[num_bytes_to_send++] = 0x13;
}
for (count=0; count<FTDI_STOP_START_REPEATS; count++) {
out_buffer[num_bytes_to_send++] = 0x80;
out_buffer[num_bytes_to_send++] = 0x03;
out_buffer[num_bytes_to_send++] = 0x13;
}
out_buffer[num_bytes_to_send++] = 0x80;
out_buffer[num_bytes_to_send++] = 0x03;
out_buffer[num_bytes_to_send++] = 0x10;
/* note we don't send this out yet as there will be more */
return ERROR_NONE;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t ftdi_i2c_send_byte_check_ack(uint8_t b)
/* -------------------------------------------------------------------------------------------------- */
/* writes a byte to the i2c bus and checks we get an ack */
/* b: the byte to write out */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
{
uint8_t *in_buffer;
uint8_t err;
out_buffer[num_bytes_to_send++] = 0x80;
out_buffer[num_bytes_to_send++] = 0x00;
out_buffer[num_bytes_to_send++] = 0x13;
out_buffer[num_bytes_to_send++] = 0x11;
out_buffer[num_bytes_to_send++] = 0x00;
out_buffer[num_bytes_to_send++] = 0x00; /* Data length of 0x0000 means clock out 1 byte */
out_buffer[num_bytes_to_send++] = b; /* Actual byte to clock out */
out_buffer[num_bytes_to_send++] = 0x80;
out_buffer[num_bytes_to_send++] = 0x00;
out_buffer[num_bytes_to_send++] = 0x11;
out_buffer[num_bytes_to_send++] = 0x27;
out_buffer[num_bytes_to_send++] = 0x00;
out_buffer[num_bytes_to_send++] = 0x87;
err=ftdi_usb_i2c_write(out_buffer, num_bytes_to_send);
num_bytes_to_send = 0;
if (err==ERROR_NONE) err=ftdi_usb_i2c_read(&in_buffer);
if ((err==ERROR_NONE) && ((*in_buffer&0x01)!=0)) {
err=ERROR_I2C_NO_ACK;
}
return err;
}
/* -------------------------------------------------------------------------------------------------- */
int ftdi_i2c_read_byte_send_nak(uint8_t *b ) {
/* -------------------------------------------------------------------------------------------------- */
/* reads a byte from the i2c bus */
/* *b: a byte to return the read value in */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
uint8_t *in_buffer;
/* first send off the commands to do the read */
out_buffer[num_bytes_to_send++] = 0x80;
out_buffer[num_bytes_to_send++] = 0x00;
out_buffer[num_bytes_to_send++] = 0x13;
out_buffer[num_bytes_to_send++] = 0x80;
out_buffer[num_bytes_to_send++] = 0x00;
out_buffer[num_bytes_to_send++] = 0x11;
out_buffer[num_bytes_to_send++] = 0x25;
out_buffer[num_bytes_to_send++] = 0x00;
out_buffer[num_bytes_to_send++] = 0x00;
out_buffer[num_bytes_to_send++] = 0x87;
err=ftdi_usb_i2c_write(out_buffer, num_bytes_to_send);
num_bytes_to_send = 0;
/* now read the value back */
if (err==ERROR_NONE) err=ftdi_usb_i2c_read(&in_buffer);
*b = *in_buffer;
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t ftdi_i2c_output(void) {
/* -------------------------------------------------------------------------------------------------- */
/* once other routines have set up a sequence of bytes to write, this actually sends them */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
err=ftdi_usb_i2c_write(out_buffer, num_bytes_to_send);
num_bytes_to_send = 0;
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t ftdi_i2c_read_reg16(uint8_t addr, uint16_t reg, uint8_t *val) {
/* -------------------------------------------------------------------------------------------------- */
/* read an i2c 16 bit register from the nim */
/* addr: the i2c buser address to access */
/* reg: the i2c register to read */
/* *val: the return value for the register we have read */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
int err;
int i;
int timeout=0;
do {
/* Send the register that needs to be read */
for(i=0; i<FTDI_NUM_TRIES; i++) {
err =ftdi_i2c_set_start();
err|=ftdi_i2c_send_byte_check_ack(addr);
err|=ftdi_i2c_send_byte_check_ack(reg>>8);
err|=ftdi_i2c_send_byte_check_ack(reg&0xff);
if (err==ERROR_NONE) break;
}
if (err==ERROR_NONE) {
/* Read back the contents of that register */
for(i=0; i<FTDI_NUM_TRIES; i++) {
err =ftdi_i2c_set_start();
err|=ftdi_i2c_send_byte_check_ack(addr|0x01);
err|=ftdi_i2c_read_byte_send_nak(val);
err|=ftdi_i2c_set_stop();
err|=ftdi_i2c_output();
if (err==ERROR_NONE) break;
}
}
timeout++;
} while ((err!=ERROR_NONE) && (timeout!=FTDI_RDWR_TIMEOUT));
if (err!=ERROR_NONE) printf("ERROR: i2c read reg16 0x%.2x, 0x%.4x\n",addr,reg);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t ftdi_i2c_write_reg16(uint8_t addr, uint16_t reg, uint8_t val) {
/* -------------------------------------------------------------------------------------------------- */
/* write an 8 bit value into a 16 bit i2c register */
/* addr: the i2c bus address to access */
/* reg: the i2c register to write to */
/* val: the value to write into the register */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
int err;
int i;
int timeout=0;
do {
for (i=0; i<FTDI_NUM_TRIES; i++) {
err =ftdi_i2c_set_start();
err|=ftdi_i2c_send_byte_check_ack(addr);
err|=ftdi_i2c_send_byte_check_ack(reg>>8);
err|=ftdi_i2c_send_byte_check_ack(reg&0xff);
err|=ftdi_i2c_send_byte_check_ack(val);
err|=ftdi_i2c_set_stop();
err|=ftdi_i2c_output();
if (err==ERROR_NONE) break;
}
timeout++;
} while ((err!=ERROR_NONE) && (timeout!=FTDI_RDWR_TIMEOUT));
if (err!=ERROR_NONE) printf("ERROR: i2c write reg16 0x%.2x, 0x%.4x, 0x%.2x\n",addr,reg, val);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t ftdi_i2c_read_reg8(uint8_t addr, uint8_t reg, uint8_t *val) {
/* -------------------------------------------------------------------------------------------------- */
/* read an i2c 8 bit register from the nim */
/* addr: the i2c bus address to access */
/* reg: the i2c register to read */
/* *val: the return value for the register we have read */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
int i;
int timeout=0;
do {
for (i=0; i<FTDI_NUM_TRIES; i++) {
err =ftdi_i2c_set_start();
err|=ftdi_i2c_send_byte_check_ack(addr);
err|=ftdi_i2c_send_byte_check_ack(reg);
err|=ftdi_i2c_set_stop();
err|=ftdi_i2c_output();
if (err==ERROR_NONE) break;
}
if (err==ERROR_NONE) {
for (i=0; i<FTDI_NUM_TRIES; i++) {
err =ftdi_i2c_set_start();
err|=ftdi_i2c_send_byte_check_ack(addr|0x01);
err|=ftdi_i2c_read_byte_send_nak(val);
err|=ftdi_i2c_set_stop();
err|=ftdi_i2c_output();
if (err==ERROR_NONE) break;
}
}
timeout++;
} while ((err!=ERROR_NONE) && (timeout!=FTDI_RDWR_TIMEOUT));
if (err!=ERROR_NONE) printf("ERROR: i2c read reg8 0x%.2x, 0x%.2x\n",addr,reg);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t ftdi_i2c_write_reg8(uint8_t addr, uint8_t reg, uint8_t val) {
/* -------------------------------------------------------------------------------------------------- */
/* write an 8 bit value to an i2c 8 bit register in the nim */
/* addr: the i2c bus address to access */
/* reg: the i2c register to write to */
/* val: the value to write into the register */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
int err;
int i;
int timeout=0;
do {
for (i=0; i<FTDI_NUM_TRIES; i++) {
err =ftdi_i2c_set_start();
err|=ftdi_i2c_send_byte_check_ack(addr);
err|=ftdi_i2c_send_byte_check_ack(reg);
err|=ftdi_i2c_send_byte_check_ack(val);
err|=ftdi_i2c_set_stop();
err|=ftdi_i2c_output();
if (err==ERROR_NONE) break;
}
timeout++;
} while ((err!=ERROR_NONE) && (timeout!=FTDI_RDWR_TIMEOUT));
if (err!=ERROR_NONE) printf("ERROR: i2c_write reg8 0x%.2x, 0x%.2x, 0x%.2x\n",addr,reg,val);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t ftdi_gpio_write(uint8_t pin_id, bool pin_value)
/* -------------------------------------------------------------------------------------------------- */
/* write pin_value to the FTDI GPIO pin AC<pin_id> */
/* -------------------------------------------------------------------------------------------------- */
{
printf("Flow: FTDI GPIO Write: pin %d -> value %d\n", pin_id, (int)pin_value);
if(pin_value)
{
ftdi_gpio_value |= (1 << pin_id);
}
else
{
ftdi_gpio_value &= ~(1 << pin_id);
}
num_bytes_to_send = 0;
out_buffer[num_bytes_to_send++] = 0x82; /* aka. MPSSE_CMD_SET_DATA_BITS_HIGHBYTE */
out_buffer[num_bytes_to_send++] = ftdi_gpio_value;
out_buffer[num_bytes_to_send++] = ftdi_gpio_direction;
ftdi_usb_i2c_write(out_buffer, num_bytes_to_send);
num_bytes_to_send = 0;
return ERROR_NONE;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t ftdi_nim_reset(void)
/* -------------------------------------------------------------------------------------------------- */
/* toggle the reset line on the nim */
/* -------------------------------------------------------------------------------------------------- */
{
printf("Flow: FTDI nim reset\n");
ftdi_gpio_write(FTDI_GPIO_PINID_NIM_RESET, 0);
usleep(10000);
ftdi_gpio_write(FTDI_GPIO_PINID_NIM_RESET, 1);
usleep(10000);
return ERROR_NONE;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t ftdi_set_polarisation_supply(bool supply_enable, bool supply_horizontal)
/* -------------------------------------------------------------------------------------------------- */
/* Controls RT5047A LNB Power Supply IC, fitted to an additional board. */
/* -------------------------------------------------------------------------------------------------- */
{
if(supply_enable) {
/* Set Voltage */
if(supply_horizontal) {
ftdi_gpio_write(FTDI_GPIO_PINID_LNB_BIAS_VSEL, 1);
}
else {
ftdi_gpio_write(FTDI_GPIO_PINID_LNB_BIAS_VSEL, 0);
}
/* Then enable output */
ftdi_gpio_write(FTDI_GPIO_PINID_LNB_BIAS_ENABLE, 1);
}
else {
/* Disable output */
ftdi_gpio_write(FTDI_GPIO_PINID_LNB_BIAS_ENABLE, 0);
}
return ERROR_NONE;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t ftdi_init(uint8_t usb_bus, uint8_t usb_addr) {
/* -------------------------------------------------------------------------------------------------- */
/* initialises the ftdi module on the minitiouner */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
printf("Flow: FTDI init\n");
err=ftdi_usb_init_i2c(usb_bus, usb_addr, FTDI_VID, FTDI_PID);
if (err==ERROR_NONE) err=ftdi_usb_set_mpsse_mode_i2c();
if (err==ERROR_NONE) err=ftdi_usb_init_ts(usb_bus, usb_addr, FTDI_VID, FTDI_PID);
if (err==ERROR_NONE) err=ftdi_usb_set_mpsse_mode_ts();
if (err==ERROR_NONE) err=ftdi_setup_ftdi_io();
if (err==ERROR_NONE) err=ftdi_nim_reset();
if (err!=ERROR_NONE) printf("ERROR: FTDI init\n");
return err;
}

40
longmynd/ftdi.h Executable file
View File

@ -0,0 +1,40 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: ftdi.h */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef FTDI_H
#define FTDI_H
#include <stdint.h>
#include <stdbool.h>
uint8_t ftdi_init(uint8_t, uint8_t);
uint8_t ftdi_set_polarisation_supply(bool, bool);
uint8_t ftdi_send_byte(uint8_t);
uint8_t ftdi_read(uint8_t*,uint8_t*);
uint8_t ftdi_read_highbyte(uint8_t*);
uint8_t ftdi_write_highbyte(uint8_t, uint8_t);
uint8_t ftdi_i2c_read_reg16 (uint8_t, uint16_t, uint8_t*);
uint8_t ftdi_i2c_read_reg8 (uint8_t, uint8_t, uint8_t*);
uint8_t ftdi_i2c_write_reg16(uint8_t, uint16_t, uint8_t );
uint8_t ftdi_i2c_write_reg8 (uint8_t, uint8_t, uint8_t );
#endif

376
longmynd/ftdi_usb.c Executable file
View File

@ -0,0 +1,376 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: ftdi_usb.c */
/* - an implementation of the Serit NIM controlling software for the MiniTiouner Hardware */
/* - here we have all the usb interactions with the ftdi module */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- INCLUDES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <libusb-1.0/libusb.h>
#include <memory.h>
#include <unistd.h>
#include "errors.h"
#include "ftdi_usb.h"
#include "ftdi.h"
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- DEFINES ------------------------------------------------------------------------ */
/* -------------------------------------------------------------------------------------------------- */
#define FTDI_USB_READ_RETRIES 2
/* Requests */
#define SIO_RESET_REQUEST SIO_RESET
#define SIO_SET_BAUDRATE_REQUEST SIO_SET_BAUD_RATE
#define SIO_SET_DATA_REQUEST SIO_SET_DATA
#define SIO_SET_FLOW_CTRL_REQUEST SIO_SET_FLOW_CTRL
#define SIO_SET_MODEM_CTRL_REQUEST SIO_MODEM_CTRL
#define SIO_POLL_MODEM_STATUS_REQUEST 0x05
#define SIO_SET_EVENT_CHAR_REQUEST 0x06
#define SIO_SET_ERROR_CHAR_REQUEST 0x07
#define SIO_SET_LATENCY_TIMER_REQUEST 0x09
#define SIO_GET_LATENCY_TIMER_REQUEST 0x0A
#define SIO_SET_BITMODE_REQUEST 0x0B
#define SIO_READ_PINS_REQUEST 0x0C
#define SIO_READ_EEPROM_REQUEST 0x90
#define SIO_WRITE_EEPROM_REQUEST 0x91
#define SIO_ERASE_EEPROM_REQUEST 0x92
#define LATENCY_MS 16
#define SIO_RESET 0 /* Reset the port */
#define SIO_MODEM_CTRL 1 /* Set the modem control register */
#define SIO_SET_FLOW_CTRL 2 /* Set flow control register */
#define SIO_SET_BAUD_RATE 3 /* Set baud rate */
#define SIO_SET_DATA 4 /* Set the data characteristics of the port */
#define SIO_RESET_SIO 0
#define SIO_RESET_PURGE_RX 1
#define SIO_RESET_PURGE_TX 2
#define FTDI_DEVICE_OUT_REQTYPE (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT)
#define FTDI_DEVICE_IN_REQTYPE (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN)
#define FTDI_RX_CHUNK_SIZE 4096
#define FTDI_TX_CHUNK_SIZE 4096
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- GLOBALS ------------------------------------------------------------------------ */
/* -------------------------------------------------------------------------------------------------- */
uint8_t rx_chunk[FTDI_RX_CHUNK_SIZE];
/* MPSSE bitbang modes */
enum ftdi_mpsse_mode
{
BITMODE_RESET = 0x00, /* switch off bitbang mode, back to regular serial/FIFO */
BITMODE_BITBANG= 0x01, /* classical asynchronous bitbang mode, introduced with B-type chips */
BITMODE_MPSSE = 0x02, /* MPSSE mode, available on 2232x chips */
BITMODE_SYNCBB = 0x04, /* synchronous bitbang mode, available on 2232x and R-type chips */
BITMODE_MCU = 0x08, /* MCU Host Bus Emulation mode, available on 2232x chips */
/* CPU-style fifo mode gets set via EEPROM */
BITMODE_OPTO = 0x10, /* Fast Opto-Isolated Serial Interface Mode, available on 2232x chips */
BITMODE_CBUS = 0x20, /* Bitbang on CBUS pins of R-type chips, configure in EEPROM before */
BITMODE_SYNCFF = 0x40, /* Single Channel Synchronous FIFO mode, available on 2232H chips */
};
static libusb_device_handle *usb_device_handle_i2c; // interface 0, endpoints: 0x81, 0x02
static libusb_device_handle *usb_device_handle_ts; // interface 1, endpoints: 0x83, 0x04
static libusb_context *usb_context_i2c;
static libusb_context *usb_context_ts;
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- ROUTINES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
uint8_t ftdi_usb_i2c_write( uint8_t *buffer, uint8_t len ){
/* -------------------------------------------------------------------------------------------------- */
/* writes data out to the usb */
/* *buffer: the buffer containing the data to be written out */
/* len: the number of bytes to send */
/* return : error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
int sent=0;
int res;
res=libusb_bulk_transfer(usb_device_handle_i2c, 0x02, buffer, len, &sent, USB_TIMEOUT);
if (res<0) {
printf("ERROR: USB Cmd Write failure %d\n",res);
err=ERROR_FTDI_USB_CMD;
}
if ((err==ERROR_NONE) && (sent!=len)) {
printf("ERROR: i2c write incorrect num bytes sent=%i, len=%i\n",sent,len);
err=ERROR_FTDI_I2C_WRITE_LEN;
}
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t ftdi_usb_i2c_read( uint8_t **buffer) {
/* -------------------------------------------------------------------------------------------------- */
/* reads one byte from the usb and returns it. Keeping any other data bytes for later */
/* Note: we only ever need to read one byte of actual data so we can avoid data copying by using the */
/* internal buffers of the usb reads to keep the data */
/* *buffer: iretruned as a pointer the the actual data read into the usb */
/* len: the number of bytes to read */
/* return : error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
static int rxed=0;
static int posn=0;
int res;
int n;
/* if we have unused characters in the buffer then use them up first */
if (posn!=rxed) {
*buffer=&rx_chunk[posn++];
} else {
/* if we couldn't do it with data we already have then get a new buffer */
/* the data may not be available immediatly so try a few times until it appears (or we error) */
for (n=0; n<FTDI_USB_READ_RETRIES; n++) {
/* we use endpoint 0x81 for the i2c traffic */
if ((res=libusb_bulk_transfer(usb_device_handle_i2c, 0x81, rx_chunk, FTDI_RX_CHUNK_SIZE, &rxed, USB_TIMEOUT))<0) {
printf("ERROR: USB Cmd Read failure %d\n",res);
err=ERROR_FTDI_USB_CMD;
break;
}
/* we always get 2 bytes header from the FTDI, so we only have valid data with more than this */
if (rxed>2) break;
}
/* check we didn't timeout */
if (n==FTDI_USB_READ_RETRIES) err=ERROR_FTDI_I2C_READ_LEN;
else if (err==ERROR_NONE) {
/* once we have good data, use it to fulfil the request */
posn=2;
*buffer=&rx_chunk[posn++];
}
}
return err;
}
/* -------------------------------------------------------------------------------------------------- */
static uint8_t ftdi_usb_set_mpsse_mode(libusb_device_handle *_device_handle){
/* -------------------------------------------------------------------------------------------------- */
/* setup the FTDI USB interface and MPSEE mode */
/* return : error code */
/* -------------------------------------------------------------------------------------------------- */
uint16_t val;
int res;
uint8_t err=ERROR_NONE;
printf("Flow: FTDI set mpsse mode\n");
/* clear out the receive buffers */
if ((res=libusb_control_transfer(_device_handle, FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
SIO_RESET_PURGE_RX, 1, NULL, 0, USB_TIMEOUT))<0) {
printf("ERROR: USB RX Purge failed %d",res);
err=ERROR_MPSSE;
}
/* clear out the transmit buffers */
if ((res=libusb_control_transfer(_device_handle, FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
SIO_RESET_PURGE_TX, 1, NULL, 0, USB_TIMEOUT))<0) {
printf("ERROR: USB TX Purge failed %d",res);
err=ERROR_MPSSE;
}
if ((res=libusb_control_transfer(_device_handle, FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
SIO_RESET_SIO, 1, NULL, 0, USB_TIMEOUT))<0) {
printf("ERROR: USB Reset failed %d",res);
err=ERROR_MPSSE;
}
/* set the latence of the bus */
if ((res=libusb_control_transfer(_device_handle, FTDI_DEVICE_OUT_REQTYPE, SIO_SET_LATENCY_TIMER_REQUEST,
LATENCY_MS, 1, NULL, 0, USB_TIMEOUT))<0) {
printf("ERROR: USB Set Latency failed %d",res);
err=ERROR_MPSSE;
}
/* set the bit modes */
val = (BITMODE_RESET<<8);
if ((res=libusb_control_transfer(_device_handle, FTDI_DEVICE_OUT_REQTYPE, SIO_SET_BITMODE_REQUEST,
val, 1, NULL, 0, USB_TIMEOUT))<0) {
printf("USB Reset Bitmode failed %d\n",res);
err=ERROR_MPSSE;
}
val = (BITMODE_MPSSE<<8);
if ((res=libusb_control_transfer(_device_handle, FTDI_DEVICE_OUT_REQTYPE, SIO_SET_BITMODE_REQUEST,
val, 1, NULL, 0, USB_TIMEOUT))<0) {
printf("USB Set MPSSE failed %d\n",res);
err=ERROR_MPSSE;
}
usleep(1000);
return err;
}
uint8_t ftdi_usb_set_mpsse_mode_i2c(void){
return ftdi_usb_set_mpsse_mode(usb_device_handle_i2c);
}
uint8_t ftdi_usb_set_mpsse_mode_ts(void){
return ftdi_usb_set_mpsse_mode(usb_device_handle_ts);
}
/* -------------------------------------------------------------------------------------------------- */
static uint8_t ftdi_usb_init(libusb_context **usb_context_ptr, libusb_device_handle **usb_device_handle_ptr, int interface_num, uint8_t usb_bus, uint8_t usb_addr, uint16_t vid, uint16_t pid) {
/* -------------------------------------------------------------------------------------------------- */
/* initialise the usb device of choice (or via vid/pid if no USB selected) */
/* return : error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
ssize_t count;
libusb_device **usb_device_list;
int error_code;
struct libusb_device_descriptor usb_descriptor;
libusb_device *usb_candidate_device;
uint8_t usb_device_count;
printf("Flow: FTDI USB init\n");
if (libusb_init(usb_context_ptr)<0) {
printf("ERROR: Unable to initialise LIBUSB\n");
err=ERROR_FTDI_USB_INIT_LIBUSB;
}
/* turn on debug */
#if LIBUSB_API_VERSION >= 0x01000106
libusb_set_option(*usb_context_ptr, LIBUSB_LOG_LEVEL_INFO);
#else
libusb_set_debug(*usb_context_ptr, LIBUSB_LOG_LEVEL_INFO);
#endif
/* now we need to decide if we are opening by VID and PID or by device number */
if ((err==ERROR_NONE) && (usb_bus==0) && (usb_addr==0)) {
/* if we are using vid and pid it is easy */
if ((*usb_device_handle_ptr = libusb_open_device_with_vid_pid(*usb_context_ptr, vid, pid))==NULL) {
printf("ERROR: Unable to open device with VID and PID\n");
printf(" Is the USB cable plugged in?\n");
err=ERROR_FTDI_USB_VID_PID;
}
/* if we are finding by usb device number then we have to take a look at the IDs to check we are */
/* being asked to open the right one. sTto do this we get a list of all the USB devices on the system */
} else if (err==ERROR_NONE) {
printf("Flow: Searching for bus/device=%i,%i\n",usb_bus,usb_addr);
count=libusb_get_device_list(*usb_context_ptr, &usb_device_list);
if (count<=0) {
printf("ERROR: failed to get the list of devices\n");
err=ERROR_FTDI_USB_DEVICE_LIST;
}
if (err==ERROR_NONE) {
/* we need to find the device we have been told to use */
for (usb_device_count=0; usb_device_count<count; usb_device_count++) {
if ((libusb_get_bus_number(usb_device_list[usb_device_count])==usb_bus) &&
(libusb_get_device_address(usb_device_list[usb_device_count])==usb_addr)) break;
}
}
if ((err==ERROR_NONE) && (usb_device_count==count)) {
printf("ERROR: invalid USB bus/device number\n");
err=ERROR_FTDI_USB_BAD_DEVICE_NUM;
} else if(err==ERROR_NONE) {
/* now we check our one has the right VID and PID */
usb_candidate_device=usb_device_list[usb_device_count];
/* some of it we have to do looking in the device descriptor */
error_code=libusb_get_device_descriptor(usb_candidate_device, &usb_descriptor);
/* if we have found our one then we can start using it */
if ((usb_descriptor.idVendor==vid) && (usb_descriptor.idProduct==pid)) {
/* first we open it */
error_code=libusb_open(usb_candidate_device, usb_device_handle_ptr);
if (error_code==0) printf(" Status: successfully opened USB Device %i,%i\n",
usb_bus,usb_addr);
else {
printf("ERROR: Unable to open Minitiouner\n");
err=ERROR_FTDI_USB_DEVICE_NUM_OPEN;
}
} else {
printf("ERROR: This bus/device is not a Minitiouner\n");
err=ERROR_FTDI_USB_DEVICE_NUM_OPEN;
}
}
/* importantly, we now free up the list */
libusb_free_device_list(usb_device_list, 1);
}
if (err==ERROR_NONE) {
/* now we should have the device handle of the device we are going to us */
/* we have two interfaces on the ftdi device (0 and 1) */
/* so next we make sure we are the only people using this device and this */
if (libusb_kernel_driver_active(*usb_device_handle_ptr, interface_num)) libusb_detach_kernel_driver(*usb_device_handle_ptr, interface_num);
/* finally we claim both interfaces as ours */
if (libusb_claim_interface(*usb_device_handle_ptr, interface_num)<0) {
libusb_close(*usb_device_handle_ptr);
libusb_exit(*usb_context_ptr);
printf("ERROR: Unable to claim interface\n");
err=ERROR_FTDI_USB_CLAIM;
}
}
return err;
}
uint8_t ftdi_usb_init_i2c(uint8_t usb_bus, uint8_t usb_addr, uint16_t vid, uint16_t pid) {
return ftdi_usb_init(&usb_context_i2c, &usb_device_handle_i2c, 0, usb_bus, usb_addr, vid, pid);
}
uint8_t ftdi_usb_init_ts(uint8_t usb_bus, uint8_t usb_addr, uint16_t vid, uint16_t pid) {
return ftdi_usb_init(&usb_context_ts, &usb_device_handle_ts, 1, usb_bus, usb_addr, vid, pid);
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t ftdi_usb_ts_read(uint8_t *buffer, uint16_t *len, uint32_t frame_size) {
/* -------------------------------------------------------------------------------------------------- */
/* every now and again we check to see if there is any transport stream available */
/* *buffer: the buffer to collect the ts data into */
/* *len: how many bytes we put into the buffer */
/* return : error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
int rxed=0;
int res=0;
/* the TS traffic is on endpoint 0x83 */
res=libusb_bulk_transfer(usb_device_handle_ts, 0x83, buffer, frame_size, &rxed, USB_FAST_TIMEOUT);
if (res<0) {
printf("ERROR: USB TS Data Read %i (%s), received %i\n",res,libusb_error_name(res),rxed);
err=ERROR_USB_TS_READ;
} else *len=rxed; /* just type converting */
if (err!=ERROR_NONE) printf("ERROR: FTDI USB ts read\n");
return err;
}

42
longmynd/ftdi_usb.h Executable file
View File

@ -0,0 +1,42 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: ftdi_usb.h */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef FTDI_USB_H
#define FTDI_USB_H
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
/* Definitions for flow control */
#define USB_TIMEOUT 5000
#define USB_FAST_TIMEOUT 500
uint8_t ftdi_usb_i2c_write( uint8_t *, uint8_t);
uint8_t ftdi_usb_i2c_read( uint8_t **);
uint8_t ftdi_usb_set_mpsse_mode_i2c(void);
uint8_t ftdi_usb_set_mpsse_mode_ts(void);
uint8_t ftdi_usb_ts_read(uint8_t *, uint16_t *, uint32_t);
uint8_t ftdi_usb_init_i2c(uint8_t, uint8_t, uint16_t, uint16_t);
uint8_t ftdi_usb_init_ts(uint8_t, uint8_t, uint16_t, uint16_t);
#endif

48
longmynd/gosrv.sh Executable file
View File

@ -0,0 +1,48 @@
#!/bin/bash
# 2023 F4IYT
PATHBIN="longmynd"
PATHOSBIN="/usr/bin/"
#echo "1=$1 2=$2 3=$3 4=$4"
#exit 0
if [ -z "$1" ]
then
ip="127.0.0.1"
else
ip=$1
fi
if [ -z "$2" ]
then
port="1234"
else
port=$2
fi
if [ -z "$3" ]
then
freq=$((10491500-9750000))
else
freq=$(($3-9750000))
fi
if [ -z "$4" ]
then
symb=1500
else
symb=$4
fi
pkexec $PATHBIN/stopsrv.sh
mkfifo fifo.264
mkfifo longmynd_main_ts
mkfifo longmynd_main_status
$PATHBIN/fake_read &
# Version to display via UDP on a remote machine
$PATHBIN/longmynd -i $ip $port $freq $symb &

69
longmynd/longmynd.1 Executable file
View File

@ -0,0 +1,69 @@
.TH LONGMYND 1
.SH NAME
longmynd \- Outputs transport streams from the Minitiouner DVB-S/S2 demodulator
.SH SYNOPSIS
.B longmynd \fR[\fB\-u\fR \fIUSB_BUS USB_DEVICE\fR]
[\fB\-i\fR \fIMAIN_IP_ADDR\fR \fIMAIN_PORT\fR | \fB\-t\fR \fIMAIN_TS_FIFO\fR]
[\fB\-I\fR \fISTATUS_IP_ADDR\fR \fISTATUS_PORT\fR | \fB\-s\fR \fIMAIN_STATUS_FIFO\fR]
[\fB\-w\fR] [\fB\-b\fR] [\fB\-p\fR \fIh\fR | \fB\-p\fR \fIv\fR]
\fIMAIN_FREQ\fR \fIMAIN_SR\fR
.IR
.SH DESCRIPTION
.B longmynd
Interfaces to the Minitiouner hardware to search for and demodulate a DVB-S or DVB-S2 stream. This stream can be output either to a local FIFO (using the default or -t option) or to an IP address/port via UDP.
The Main TS stream is the one coming out of the Primary FTDI Board.
.SH OPTIONS
.TP
.BR \-u " " \fIUSB_BUS\fR " " \fIUSB_DEVICE\fR
Sets the USB Bus and USB Device Number of the required Minitiouner in a multi device system.
Default uses the first detected Minitiouner.
.TP
.BR \-i " " \fIIP_ADDR\fR " " \fIPORT\fR
If UDP output is required (instead of the default FIFO output), this option sets the IP Address and Port to send the Main TS Stream to.
Default is to use a FIFO for Main TS Stream.
.TP
.BR \-I " " \fIIP_ADDR\fR " " \fIPORT\fR
If UDP output is required (instead of the default FIFO output), this option sets the IP Address and Port to send the Main Status Stream to.
Default is to use a FIFO for Main Status Stream.
.TP
.BR \-t " " \fITS_FIFO\fR
Sets the name of the Main TS Stream output FIFO.
Default is "./longmynd_main_ts".
.TP
.BR \-s " " \fISTATUS_FIFO\fR
Sets the name of the Status output FIFO.
Default is "./longmynd_main_status".
.TP
.BR \-w
If selected, this option swaps over the RF input so that the Main TS Stream is fed from the BOTTOM F-Type of the NIM.
Default uses the TOP RF input for the Main TS stream.
.TP
.BR \-b
If selected, this option enables a tone audio output that will be present when DVB-S2 is being demodulated, and will increase in pitch for an increase in MER, to aid pointing.
By default this option is disabled.
.TP
.BR \-p " " \fIh\fR " "| " "\-p " " \fIv\fR
Controls and enables the LNB supply voltage output when an RT5047A LNB Voltage Regulator is fitted.
"-p v" will set 13V output (Vertical Polarisation), "-p h" will set 18V output (Horizontal Polarisation).
By default the RT5047A output is disabled.
.TP
.BR \fIMAIN_FREQ\fR
specifies the starting frequency (in KHz) of the Main TS Stream search algorithm".
.TP
.BR \fIMAIN_SR\fR
specifies the starting Symbol Rate (in KSPS) of the Main TS Stream search algorithm".
.SH EXAMPLES
.TP
longmynd 2000 2000
will find the first available Minitiouner, search for a 2MHz TS Stream at 2MSPS on the TOP RF input, output the TS to a FIFO called "longmynd_main_ts" and the status to a FIFO called "longmynd_main_status".
.TP
longmynd -w 2000 2000
As above but uses the BOTTOM RF input.
.TP
longmynd -u 1 4 2000 2000
As above but will attempt to find a minitiouner at usb device 4 on usb bus 1.
.TP
longmynd -i 192.168.1.1 87 2000 2000
As above but any TS output will be to IP address 192.168.1.1 on port 87

775
longmynd/main.c Executable file
View File

@ -0,0 +1,775 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: main.c */
/* - an implementation of the Serit NIM controlling software for the MiniTiouner Hardware */
/* - the top level (main) and command line procesing */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- INCLUDES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <pthread.h>
#include "main.h"
#include "ftdi.h"
#include "stv0910.h"
#include "stv0910_regs.h"
#include "stv0910_utils.h"
#include "stv6120.h"
#include "stvvglna.h"
#include "nim.h"
#include "errors.h"
#include "fifo.h"
#include "ftdi_usb.h"
#include "udp.h"
#include "beep.h"
#include "ts.h"
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- DEFINES ------------------------------------------------------------------------ */
/* -------------------------------------------------------------------------------------------------- */
/* Milliseconds between each i2c control loop */
#define I2C_LOOP_MS 100
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- GLOBALS ------------------------------------------------------------------------ */
/* -------------------------------------------------------------------------------------------------- */
static longmynd_config_t longmynd_config = {
.new = false,
.mutex = PTHREAD_MUTEX_INITIALIZER
};
static longmynd_status_t longmynd_status = {
.service_name = "\0",
.service_provider_name = "\0",
.last_updated_monotonic = 0,
.mutex = PTHREAD_MUTEX_INITIALIZER,
.signal = PTHREAD_COND_INITIALIZER
};
static pthread_t thread_ts_parse;
static pthread_t thread_ts;
static pthread_t thread_i2c;
static pthread_t thread_beep;
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- ROUTINES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
uint64_t timestamp_ms(void) {
/* -------------------------------------------------------------------------------------------------- */
/* Returns the current unix timestamp in milliseconds */
/* return: unix timestamp in milliseconds */
/* -------------------------------------------------------------------------------------------------- */
struct timespec tp;
if(clock_gettime(CLOCK_REALTIME, &tp) != 0)
{
return 0;
}
return (uint64_t) tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
}
void config_set_frequency(uint32_t frequency)
{
if (frequency <= 2450000 && frequency >= 144000)
{
pthread_mutex_lock(&longmynd_config.mutex);
longmynd_config.freq_requested = frequency;
longmynd_config.new = true;
pthread_mutex_unlock(&longmynd_config.mutex);
}
}
void config_set_symbolrate(uint32_t symbolrate)
{
if (symbolrate <= 27500 && symbolrate >= 33)
{
pthread_mutex_lock(&longmynd_config.mutex);
longmynd_config.sr_requested = symbolrate;
longmynd_config.new = true;
pthread_mutex_unlock(&longmynd_config.mutex);
}
}
void config_set_frequency_and_symbolrate(uint32_t frequency, uint32_t symbolrate)
{
if (frequency <= 2450000 && frequency >= 144000
&& symbolrate <= 27500 && symbolrate >= 33)
{
pthread_mutex_lock(&longmynd_config.mutex);
longmynd_config.freq_requested = frequency;
longmynd_config.sr_requested = symbolrate;
longmynd_config.new = true;
pthread_mutex_unlock(&longmynd_config.mutex);
}
}
void config_set_lnbv(bool enabled, bool horizontal)
{
pthread_mutex_lock(&longmynd_config.mutex);
longmynd_config.polarisation_supply = enabled;
longmynd_config.polarisation_horizontal = horizontal;
longmynd_config.new = true;
pthread_mutex_unlock(&longmynd_config.mutex);
}
/* -------------------------------------------------------------------------------------------------- */
uint64_t monotonic_ms(void) {
/* -------------------------------------------------------------------------------------------------- */
/* Returns current value of a monotonic timer in milliseconds */
/* return: monotonic timer in milliseconds */
/* -------------------------------------------------------------------------------------------------- */
struct timespec tp;
if(clock_gettime(CLOCK_MONOTONIC, &tp) != 0)
{
return 0;
}
return (uint64_t) tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t process_command_line(int argc, char *argv[], longmynd_config_t *config) {
/* -------------------------------------------------------------------------------------------------- */
/* processes the command line arguments, sets up the parameters in main from them and error checks */
/* All the required parameters are passed in */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
uint8_t param;
bool main_usb_set=false;
bool ts_ip_set=false;
bool ts_fifo_set=false;
bool status_ip_set=false;
bool status_fifo_set=false;
/* Defaults */
config->port_swap = false;
config->beep_enabled = false;
config->device_usb_addr = 0;
config->device_usb_bus = 0;
config->ts_use_ip = false;
strcpy(config->ts_fifo_path, "longmynd_main_ts");
config->status_use_ip = false;
strcpy(config->status_fifo_path, "longmynd_main_status");
config->polarisation_supply=false;
char polarisation_str[8];
param=1;
while (param<argc-2) {
if (argv[param][0]=='-') {
switch (argv[param++][1]) {
case 'u':
config->device_usb_bus =(uint8_t)strtol(argv[param++],NULL,10);
config->device_usb_addr=(uint8_t)strtol(argv[param ],NULL,10);
main_usb_set=true;
break;
case 'i':
strncpy(config->ts_ip_addr,argv[param++], 16);
config->ts_ip_port=(uint16_t)strtol(argv[param],NULL,10);
config->ts_use_ip=true;
ts_ip_set = true;
break;
case 't':
strncpy(config->status_fifo_path, argv[param], 128);
ts_fifo_set=true;
break;
case 'I':
strncpy(config->status_ip_addr,argv[param++], 16);
config->status_ip_port=(uint16_t)strtol(argv[param],NULL,10);
config->status_use_ip=true;
status_ip_set = true;
break;
case 's':
strncpy(config->status_fifo_path, argv[param], 128);
status_fifo_set=true;
break;
case 'p':
strncpy(polarisation_str, argv[param], 8);
config->polarisation_supply=true;
break;
case 'w':
config->port_swap=true;
param--; /* there is no data for this so go back */
break;
case 'b':
config->beep_enabled=true;
param--; /* there is no data for this so go back */
break;
}
}
param++;
}
if ((argc-param)<2) {
err=ERROR_ARGS_INPUT;
printf("ERROR: Main Frequency and Main Symbol Rate not found.\n");
}
if (err==ERROR_NONE) {
config->freq_requested =(uint32_t)strtol(argv[param++],NULL,10);
if(config->freq_requested==0) {
err=ERROR_ARGS_INPUT;
printf("ERROR: Main Frequency not in a valid format.\n");
}
config->sr_requested =(uint32_t)strtol(argv[param ],NULL,10);
if(config->sr_requested==0) {
err=ERROR_ARGS_INPUT;
printf("ERROR: Main Symbol Rate not in a valid format.\n");
}
}
/* Process LNB Voltage Supply parameter */
if (err==ERROR_NONE && config->polarisation_supply) {
if(0 == strcasecmp("h", polarisation_str)) {
config->polarisation_horizontal=true;
}
else if(0 == strcasecmp("v", polarisation_str)) {
config->polarisation_horizontal=false;
}
else {
config->polarisation_supply = false;
err=ERROR_ARGS_INPUT;
printf("ERROR: Polarisation voltage supply parameter not recognised\n");
}
}
if (err==ERROR_NONE) {
if (config->freq_requested>2450000) {
err=ERROR_ARGS_INPUT;
printf("ERROR: Freq must be <= 2450 MHz\n");
} else if (config->freq_requested<144) {
err=ERROR_ARGS_INPUT;
printf("ERROR: Freq_must be >= 144 MHz\n");
} else if (config->sr_requested>27500) {
err=ERROR_ARGS_INPUT;
printf("ERROR: SR must be <= 27 Msymbols/s\n");
} else if (config->sr_requested<33) {
err=ERROR_ARGS_INPUT;
printf("ERROR: SR must be >= 33 Ksymbols/s\n");
} else if (ts_ip_set && ts_fifo_set) {
err=ERROR_ARGS_INPUT;
printf("ERROR: Cannot set TS FIFO and TS IP address\n");
} else if (status_ip_set && status_fifo_set) {
err=ERROR_ARGS_INPUT;
printf("ERROR: Cannot set Status FIFO and Status IP address\n");
} else if (config->ts_use_ip && config->status_use_ip && (config->ts_ip_port == config->status_ip_port) && (0==strcmp(config->ts_ip_addr, config->status_ip_addr))) {
err=ERROR_ARGS_INPUT;
printf("ERROR: Cannot set Status IP & Port identical to TS IP & Port\n");
} else { /* err==ERROR_NONE */
printf(" Status: Main Frequency=%i KHz\n",config->freq_requested);
printf(" Main Symbol Rate=%i KSymbols/s\n",config->sr_requested);
if (!main_usb_set) printf(" Using First Minitiouner detected on USB\n");
else printf(" USB bus/device=%i,%i\n",config->device_usb_bus,config->device_usb_addr);
if (!config->ts_use_ip) printf(" Main TS output to FIFO=%s\n",config->ts_fifo_path);
else printf(" Main TS output to IP=%s:%i\n",config->ts_ip_addr,config->ts_ip_port);
if (!config->status_use_ip) printf(" Main Status output to FIFO=%s\n",config->status_fifo_path);
else printf(" Main Status output to IP=%s:%i\n",config->status_ip_addr,config->status_ip_port);
if (config->port_swap) printf(" NIM inputs are swapped (Main now refers to BOTTOM F-Type\n");
else printf(" Main refers to TOP F-Type\n");
if (config->beep_enabled) printf(" MER Beep enabled\n");
if (config->polarisation_supply) printf(" Polarisation Voltage Supply enabled: %s\n", (config->polarisation_horizontal ? "H, 18V" : "V, 13V"));
}
}
if (err!=ERROR_NONE) {
printf("Please refer to the longmynd manual page via:\n");
printf(" man -l longmynd.1\n");
}
config->new = true;
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t do_report(longmynd_status_t *status) {
/* -------------------------------------------------------------------------------------------------- */
/* interrogates the demodulator to find the interesting info to report */
/* status: the state struct */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
/* LNAs if present */
if (status->lna_ok) {
uint8_t lna_gain, lna_vgo;
if (err==ERROR_NONE) stvvglna_read_agc(NIM_INPUT_TOP, &lna_gain, &lna_vgo);
status->lna_gain = (lna_gain<<5) | lna_vgo;
}
/* I,Q powers */
if (err==ERROR_NONE) err=stv0910_read_power(STV0910_DEMOD_TOP, &status->power_i, &status->power_q);
/* constellations */
if (err==ERROR_NONE) {
for (uint8_t count=0; count<NUM_CONSTELLATIONS; count++) {
stv0910_read_constellation(STV0910_DEMOD_TOP, &status->constellation[count][0], &status->constellation[count][1]);
}
}
/* puncture rate */
if (err==ERROR_NONE) err=stv0910_read_puncture_rate(STV0910_DEMOD_TOP, &status->puncture_rate);
/* carrier frequency offset we are trying */
if (err==ERROR_NONE) err=stv0910_read_car_freq(STV0910_DEMOD_TOP, &status->frequency_offset);
/* symbol rate we are trying */
if (err==ERROR_NONE) err=stv0910_read_sr(STV0910_DEMOD_TOP, &status->symbolrate);
/* viterbi error rate */
if (err==ERROR_NONE) err=stv0910_read_err_rate(STV0910_DEMOD_TOP, &status->viterbi_error_rate);
/* BER */
if (err==ERROR_NONE) err=stv0910_read_ber(STV0910_DEMOD_TOP, &status->bit_error_rate);
/* BCH Uncorrected Flag */
if (err==ERROR_NONE) err=stv0910_read_errors_bch_uncorrected(STV0910_DEMOD_TOP, &status->errors_bch_uncorrected);
/* BCH Error Count */
if (err==ERROR_NONE) err=stv0910_read_errors_bch_count(STV0910_DEMOD_TOP, &status->errors_bch_count);
/* LDPC Error Count */
if (err==ERROR_NONE) err=stv0910_read_errors_ldpc_count(STV0910_DEMOD_TOP, &status->errors_ldpc_count);
/* MER */
if(status->state==STATE_DEMOD_S || status->state==STATE_DEMOD_S2) {
if (err==ERROR_NONE) err=stv0910_read_mer(STV0910_DEMOD_TOP, &status->modulation_error_rate);
} else {
status->modulation_error_rate = 0;
}
/* MODCOD, Short Frames, Pilots */
if (err==ERROR_NONE) err=stv0910_read_modcod_and_type(STV0910_DEMOD_TOP, &status->modcod, &status->short_frame, &status->pilots);
if(status->state!=STATE_DEMOD_S2) {
/* short frames & pilots only valid for S2 DEMOD state */
status->short_frame = 0;
status->pilots = 0;
}
return err;
}
/* -------------------------------------------------------------------------------------------------- */
void *loop_i2c(void *arg) {
/* -------------------------------------------------------------------------------------------------- */
/* Runs a loop to configure and monitor the Minitiouner Receiver */
/* Configuration is read from the configuration struct */
/* Status is written to the status struct */
/* -------------------------------------------------------------------------------------------------- */
thread_vars_t *thread_vars=(thread_vars_t *)arg;
longmynd_status_t *status=(longmynd_status_t *)thread_vars->status;
uint8_t *err = &thread_vars->thread_err;
*err=ERROR_NONE;
longmynd_config_t config_cpy;
longmynd_status_t status_cpy;
uint64_t last_i2c_loop = timestamp_ms();
while (*err==ERROR_NONE && *thread_vars->main_err_ptr==ERROR_NONE) {
/* Receiver State Machine Loop Timer */
do {
/* Sleep for at least 10ms */
usleep(10*1000);
} while (timestamp_ms() < (last_i2c_loop + I2C_LOOP_MS));
/* Check if there's a new config */
if(thread_vars->config->new)
{
/* Lock config struct */
pthread_mutex_lock(&thread_vars->config->mutex);
/* Clone status struct locally */
memcpy(&config_cpy, thread_vars->config, sizeof(longmynd_config_t));
/* Clear new config flag */
thread_vars->config->new = false;
/* Set flag to clear ts buffer */
thread_vars->config->ts_reset = true;
pthread_mutex_unlock(&thread_vars->config->mutex);
status_cpy.frequency_requested = config_cpy.freq_requested;
/* init all the modules */
if (*err==ERROR_NONE) *err=nim_init();
/* we are only using the one demodulator so set the other to 0 to turn it off */
if (*err==ERROR_NONE) *err=stv0910_init(config_cpy.sr_requested,0);
/* we only use one of the tuners in STV6120 so freq for tuner 2=0 to turn it off */
if (*err==ERROR_NONE) *err=stv6120_init(config_cpy.freq_requested,0,config_cpy.port_swap);
/* we turn on the LNA we want and turn the other off (if they exist) */
if (*err==ERROR_NONE) *err=stvvglna_init(NIM_INPUT_TOP, (config_cpy.port_swap) ? STVVGLNA_OFF : STVVGLNA_ON, &status_cpy.lna_ok);
if (*err==ERROR_NONE) *err=stvvglna_init(NIM_INPUT_BOTTOM, (config_cpy.port_swap) ? STVVGLNA_ON : STVVGLNA_OFF, &status_cpy.lna_ok);
if (*err!=ERROR_NONE) printf("ERROR: failed to init a device - is the NIM powered on?\n");
/* Enable/Disable polarisation voltage supply */
if (*err==ERROR_NONE) *err=ftdi_set_polarisation_supply(config_cpy.polarisation_supply, config_cpy.polarisation_horizontal);
if (*err==ERROR_NONE) {
status_cpy.polarisation_supply = config_cpy.polarisation_supply;
status_cpy.polarisation_horizontal = config_cpy.polarisation_horizontal;
}
/* now start the whole thing scanning for the signal */
if (*err==ERROR_NONE) {
*err=stv0910_start_scan(STV0910_DEMOD_TOP);
status_cpy.state=STATE_DEMOD_HUNTING;
}
}
/* Main receiver state machine */
switch(status_cpy.state) {
case STATE_DEMOD_HUNTING:
if (*err==ERROR_NONE) *err=do_report(&status_cpy);
/* process state changes */
if (*err==ERROR_NONE) *err=stv0910_read_scan_state(STV0910_DEMOD_TOP, &status_cpy.demod_state);
if (status_cpy.demod_state==DEMOD_FOUND_HEADER) {
status_cpy.state=STATE_DEMOD_FOUND_HEADER;
}
else if (status_cpy.demod_state==DEMOD_S2) {
status_cpy.state=STATE_DEMOD_S2;
}
else if (status_cpy.demod_state==DEMOD_S) {
status_cpy.state=STATE_DEMOD_S;
}
else if ((status_cpy.demod_state!=DEMOD_HUNTING) && (*err==ERROR_NONE)) {
printf("ERROR: demodulator returned a bad scan state\n");
*err=ERROR_BAD_DEMOD_HUNT_STATE; /* not allowed to have any other states */
} /* no need for another else, all states covered */
break;
case STATE_DEMOD_FOUND_HEADER:
if (*err==ERROR_NONE) *err=do_report(&status_cpy);
/* process state changes */
*err=stv0910_read_scan_state(STV0910_DEMOD_TOP, &status_cpy.demod_state);
if (status_cpy.demod_state==DEMOD_HUNTING) {
status_cpy.state=STATE_DEMOD_HUNTING;
}
else if (status_cpy.demod_state==DEMOD_S2) {
status_cpy.state=STATE_DEMOD_S2;
}
else if (status_cpy.demod_state==DEMOD_S) {
status_cpy.state=STATE_DEMOD_S;
}
else if ((status_cpy.demod_state!=DEMOD_FOUND_HEADER) && (*err==ERROR_NONE)) {
printf("ERROR: demodulator returned a bad scan state\n");
*err=ERROR_BAD_DEMOD_HUNT_STATE; /* not allowed to have any other states */
} /* no need for another else, all states covered */
break;
case STATE_DEMOD_S2:
if (*err==ERROR_NONE) *err=do_report(&status_cpy);
/* process state changes */
*err=stv0910_read_scan_state(STV0910_DEMOD_TOP, &status_cpy.demod_state);
if (status_cpy.demod_state==DEMOD_HUNTING) {
status_cpy.state=STATE_DEMOD_HUNTING;
}
else if (status_cpy.demod_state==DEMOD_FOUND_HEADER) {
status_cpy.state=STATE_DEMOD_FOUND_HEADER;
}
else if (status_cpy.demod_state==DEMOD_S) {
status_cpy.state=STATE_DEMOD_S;
}
else if ((status_cpy.demod_state!=DEMOD_S2) && (*err==ERROR_NONE)) {
printf("ERROR: demodulator returned a bad scan state\n");
*err=ERROR_BAD_DEMOD_HUNT_STATE; /* not allowed to have any other states */
} /* no need for another else, all states covered */
break;
case STATE_DEMOD_S:
if (*err==ERROR_NONE) *err=do_report(&status_cpy);
/* process state changes */
*err=stv0910_read_scan_state(STV0910_DEMOD_TOP, &status_cpy.demod_state);
if (status_cpy.demod_state==DEMOD_HUNTING) {
status_cpy.state=STATE_DEMOD_HUNTING;
}
else if (status_cpy.demod_state==DEMOD_FOUND_HEADER) {
status_cpy.state=STATE_DEMOD_FOUND_HEADER;
}
else if (status_cpy.demod_state==DEMOD_S2) {
status_cpy.state=STATE_DEMOD_S2;
}
else if ((status_cpy.demod_state!=DEMOD_S) && (*err==ERROR_NONE)) {
printf("ERROR: demodulator returned a bad scan state\n");
*err=ERROR_BAD_DEMOD_HUNT_STATE; /* not allowed to have any other states */
} /* no need for another else, all states covered */
break;
default:
*err=ERROR_STATE; /* we should never get here so panic if we do */
break;
}
/* Copy local status data over global object */
pthread_mutex_lock(&status->mutex);
/* Copy out other vars */
status->state = status_cpy.state;
status->demod_state = status_cpy.demod_state;
status->lna_ok = status_cpy.lna_ok;
status->lna_gain = status_cpy.lna_gain;
status->power_i = status_cpy.power_i;
status->power_q = status_cpy.power_q;
status->frequency_requested = status_cpy.frequency_requested;
status->frequency_offset = status_cpy.frequency_offset;
status->polarisation_supply = status_cpy.polarisation_supply;
status->polarisation_horizontal = status_cpy.polarisation_horizontal;
status->symbolrate = status_cpy.symbolrate;
status->viterbi_error_rate = status_cpy.viterbi_error_rate;
status->bit_error_rate = status_cpy.bit_error_rate;
status->modulation_error_rate = status_cpy.modulation_error_rate;
status->errors_bch_uncorrected = status_cpy.errors_bch_uncorrected;
status->errors_bch_count = status_cpy.errors_bch_count;
status->errors_ldpc_count = status_cpy.errors_ldpc_count;
memcpy(status->constellation, status_cpy.constellation, (sizeof(uint8_t) * NUM_CONSTELLATIONS * 2));
status->puncture_rate = status_cpy.puncture_rate;
status->modcod = status_cpy.modcod;
status->short_frame = status_cpy.short_frame;
status->pilots = status_cpy.pilots;
/* Set monotonic value to signal new data */
status->last_updated_monotonic = monotonic_ms();
/* Trigger pthread signal */
pthread_cond_signal(&status->signal);
pthread_mutex_unlock(&status->mutex);
last_i2c_loop = timestamp_ms();
}
return NULL;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t status_all_write(longmynd_status_t *status, uint8_t (*status_write)(uint8_t, uint32_t), uint8_t (*status_string_write)(uint8_t, char*)) {
/* -------------------------------------------------------------------------------------------------- */
/* Reads the past status struct out to the passed write function */
/* Returns: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
/* Main status */
if (err==ERROR_NONE) err=status_write(STATUS_STATE,status->state);
/* LNAs if present */
if (status->lna_ok) {
if (err==ERROR_NONE) err=status_write(STATUS_LNA_GAIN,status->lna_gain);
}
/* I,Q powers */
if (err==ERROR_NONE) err=status_write(STATUS_POWER_I, status->power_i);
if (err==ERROR_NONE) err=status_write(STATUS_POWER_Q, status->power_q);
/* constellations */
for (uint8_t count=0; count<NUM_CONSTELLATIONS; count++) {
if (err==ERROR_NONE) err=status_write(STATUS_CONSTELLATION_I, status->constellation[count][0]);
if (err==ERROR_NONE) err=status_write(STATUS_CONSTELLATION_Q, status->constellation[count][1]);
}
/* puncture rate */
if (err==ERROR_NONE) err=status_write(STATUS_PUNCTURE_RATE, status->puncture_rate);
/* carrier frequency offset we are trying */
/* note we now have the offset, so we need to add in the freq we tried to set it to */
if (err==ERROR_NONE) err=status_write(STATUS_CARRIER_FREQUENCY, (uint32_t)(status->frequency_requested+(status->frequency_offset/1000)));
/* LNB Voltage Supply Enabled: true / false */
if (err==ERROR_NONE) err=status_write(STATUS_LNB_SUPPLY, status->polarisation_supply);
/* LNB Voltage Supply is Horizontal Polarisation: true / false */
if (err==ERROR_NONE) err=status_write(STATUS_LNB_POLARISATION_H, status->polarisation_horizontal);
/* symbol rate we are trying */
if (err==ERROR_NONE) err=status_write(STATUS_SYMBOL_RATE, status->symbolrate);
/* viterbi error rate */
if (err==ERROR_NONE) err=status_write(STATUS_VITERBI_ERROR_RATE, status->viterbi_error_rate);
/* BER */
if (err==ERROR_NONE) err=status_write(STATUS_BER, status->bit_error_rate);
/* MER */
if (err==ERROR_NONE) err=status_write(STATUS_MER, status->modulation_error_rate);
/* BCH Uncorrected Errors Flag */
if (err==ERROR_NONE) err=status_write(STATUS_ERRORS_BCH_UNCORRECTED, status->errors_bch_uncorrected);
/* BCH Corrected Errors Count */
if (err==ERROR_NONE) err=status_write(STATUS_ERRORS_BCH_COUNT, status->errors_bch_count);
/* LDPC Corrected Errors Count */
if (err==ERROR_NONE) err=status_write(STATUS_ERRORS_LDPC_COUNT, status->errors_ldpc_count);
/* Service Name */
if (err==ERROR_NONE) err=status_string_write(STATUS_SERVICE_NAME, status->service_name);
/* Service Provider Name */
if (err==ERROR_NONE) err=status_string_write(STATUS_SERVICE_PROVIDER_NAME, status->service_provider_name);
/* TS Null Percentage */
if (err==ERROR_NONE) err=status_write(STATUS_TS_NULL_PERCENTAGE, status->ts_null_percentage);
/* TS Elementary Stream PIDs */
for (uint8_t count=0; count<NUM_ELEMENT_STREAMS; count++) {
if(status->ts_elementary_streams[count][0] > 0)
{
if (err==ERROR_NONE) err=status_write(STATUS_ES_PID, status->ts_elementary_streams[count][0]);
if (err==ERROR_NONE) err=status_write(STATUS_ES_TYPE, status->ts_elementary_streams[count][1]);
}
}
/* MODCOD */
if (err==ERROR_NONE) err=status_write(STATUS_MODCOD, status->modcod);
/* Short Frames */
if (err==ERROR_NONE) err=status_write(STATUS_SHORT_FRAME, status->short_frame);
/* Pilots */
if (err==ERROR_NONE) err=status_write(STATUS_PILOTS, status->pilots);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
int main(int argc, char *argv[]) {
/* -------------------------------------------------------------------------------------------------- */
/* command line processing */
/* module initialisation */
/* Print out of status information to requested interface, triggered by pthread condition variable */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
uint8_t (*status_write)(uint8_t,uint32_t);
uint8_t (*status_string_write)(uint8_t,char*);
printf("Flow: main\n");
err=process_command_line(argc, argv, &longmynd_config);
/* first setup the fifos, udp socket, ftdi and usb */
if(longmynd_config.status_use_ip) {
if (err==ERROR_NONE) err=udp_status_init(longmynd_config.status_ip_addr, longmynd_config.status_ip_port);
status_write = udp_status_write;
status_string_write = udp_status_string_write;
} else {
if (err==ERROR_NONE) err=fifo_status_init(longmynd_config.status_fifo_path);
status_write = fifo_status_write;
status_string_write = fifo_status_string_write;
}
if (err==ERROR_NONE) err=ftdi_init(longmynd_config.device_usb_bus, longmynd_config.device_usb_addr);
thread_vars_t thread_vars_ts = {
.main_err_ptr = &err,
.thread_err = ERROR_NONE,
.config = &longmynd_config,
.status = &longmynd_status
};
if(0 != pthread_create(&thread_ts, NULL, loop_ts, (void *)&thread_vars_ts))
{
fprintf(stderr, "Error creating loop_ts pthread\n");
}
else
{
pthread_setname_np(thread_ts, "TS Transport");
}
thread_vars_t thread_vars_ts_parse = {
.main_err_ptr = &err,
.thread_err = ERROR_NONE,
.config = &longmynd_config,
.status = &longmynd_status
};
if(0 != pthread_create(&thread_ts_parse, NULL, loop_ts_parse, (void *)&thread_vars_ts_parse))
{
fprintf(stderr, "Error creating loop_ts_parse pthread\n");
}
else
{
pthread_setname_np(thread_ts_parse, "TS Parse");
}
thread_vars_t thread_vars_i2c = {
.main_err_ptr = &err,
.thread_err = ERROR_NONE,
.config = &longmynd_config,
.status = &longmynd_status
};
if(0 != pthread_create(&thread_i2c, NULL, loop_i2c, (void *)&thread_vars_i2c))
{
fprintf(stderr, "Error creating loop_i2c pthread\n");
}
else
{
pthread_setname_np(thread_i2c, "Receiver");
}
thread_vars_t thread_vars_beep = {
.main_err_ptr = &err,
.thread_err = ERROR_NONE,
.config = &longmynd_config,
.status = &longmynd_status
};
if(0 != pthread_create(&thread_beep, NULL, loop_beep, (void *)&thread_vars_beep))
{
fprintf(stderr, "Error creating loop_beep pthread\n");
}
else
{
pthread_setname_np(thread_beep, "Beep Audio");
}
uint64_t last_status_sent_monotonic = 0;
longmynd_status_t longmynd_status_cpy;
while (err==ERROR_NONE) {
/* Test if new status data is available */
if(longmynd_status.last_updated_monotonic != last_status_sent_monotonic) {
/* Acquire lock on global status struct */
pthread_mutex_lock(&longmynd_status.mutex);
/* Clone status struct locally */
memcpy(&longmynd_status_cpy, &longmynd_status, sizeof(longmynd_status_t));
/* Release lock on global status struct */
pthread_mutex_unlock(&longmynd_status.mutex);
/* Send all status via configured output interface from local copy */
err=status_all_write(&longmynd_status_cpy, status_write, status_string_write);
/* Update monotonic timestamp last sent */
last_status_sent_monotonic = longmynd_status_cpy.last_updated_monotonic;
} else {
/* Sleep 10ms */
usleep(10*1000);
}
/* Check for errors on threads */
if(err==ERROR_NONE &&
(thread_vars_ts.thread_err!=ERROR_NONE
|| thread_vars_ts_parse.thread_err!=ERROR_NONE
|| thread_vars_beep.thread_err!=ERROR_NONE
|| thread_vars_i2c.thread_err!=ERROR_NONE)) {
err=ERROR_THREAD_ERROR;
}
}
/* Exited, wait for child threads to exit */
pthread_join(thread_ts_parse, NULL);
pthread_join(thread_ts, NULL);
pthread_join(thread_i2c, NULL);
pthread_join(thread_beep, NULL);
return err;
}

146
longmynd/main.h Executable file
View File

@ -0,0 +1,146 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: main.h */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef MAIN_H
#define MAIN_H
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>
#include <pthread.h>
/* states of the main loop state machine */
#define STATE_INIT 0
#define STATE_DEMOD_HUNTING 1
#define STATE_DEMOD_FOUND_HEADER 2
#define STATE_DEMOD_S 3
#define STATE_DEMOD_S2 4
/* define the various status reports */
#define STATUS_STATE 1
#define STATUS_LNA_GAIN 2
#define STATUS_PUNCTURE_RATE 3
#define STATUS_POWER_I 4
#define STATUS_POWER_Q 5
#define STATUS_CARRIER_FREQUENCY 6
#define STATUS_CONSTELLATION_I 7
#define STATUS_CONSTELLATION_Q 8
#define STATUS_SYMBOL_RATE 9
#define STATUS_VITERBI_ERROR_RATE 10
#define STATUS_BER 11
#define STATUS_MER 12
#define STATUS_SERVICE_NAME 13
#define STATUS_SERVICE_PROVIDER_NAME 14
#define STATUS_TS_NULL_PERCENTAGE 15
#define STATUS_ES_PID 16
#define STATUS_ES_TYPE 17
#define STATUS_MODCOD 18
#define STATUS_SHORT_FRAME 19
#define STATUS_PILOTS 20
#define STATUS_ERRORS_LDPC_COUNT 21
#define STATUS_ERRORS_BCH_COUNT 22
#define STATUS_ERRORS_BCH_UNCORRECTED 23
#define STATUS_LNB_SUPPLY 24
#define STATUS_LNB_POLARISATION_H 25
/* The number of constellation peeks we do for each background loop */
#define NUM_CONSTELLATIONS 16
#define NUM_ELEMENT_STREAMS 16
typedef struct {
bool port_swap;
uint8_t port;
uint32_t freq_requested;
uint32_t sr_requested;
bool beep_enabled;
uint8_t device_usb_bus;
uint8_t device_usb_addr;
bool ts_use_ip;
bool ts_reset;
char ts_fifo_path[128];
char ts_ip_addr[16];
int ts_ip_port;
bool status_use_ip;
char status_fifo_path[128];
char status_ip_addr[16];
int status_ip_port;
bool polarisation_supply;
bool polarisation_horizontal; // false -> 13V, true -> 18V
bool new;
pthread_mutex_t mutex;
} longmynd_config_t;
typedef struct {
uint8_t state;
uint8_t demod_state;
bool lna_ok;
uint16_t lna_gain;
uint8_t power_i;
uint8_t power_q;
uint32_t frequency_requested;
int32_t frequency_offset;
bool polarisation_supply;
bool polarisation_horizontal; // false -> 13V, true -> 18V
uint32_t symbolrate;
uint32_t viterbi_error_rate; // DVB-S1
uint32_t bit_error_rate; // DVB-S2
uint32_t modulation_error_rate; // DVB-S2
bool errors_bch_uncorrected;
uint32_t errors_bch_count;
uint32_t errors_ldpc_count;
uint8_t constellation[NUM_CONSTELLATIONS][2]; // { i, q }
uint8_t puncture_rate;
char service_name[255];
char service_provider_name[255];
uint8_t ts_null_percentage;
uint16_t ts_elementary_streams[NUM_ELEMENT_STREAMS][2]; // { pid, type }
uint32_t modcod;
bool short_frame;
bool pilots;
uint64_t last_updated_monotonic;
pthread_mutex_t mutex;
pthread_cond_t signal;
} longmynd_status_t;
typedef struct {
uint8_t *main_state_ptr;
uint8_t *main_err_ptr;
uint8_t thread_err;
longmynd_config_t *config;
longmynd_status_t *status;
} thread_vars_t;
uint64_t timestamp_ms(void);
void config_set_frequency(uint32_t frequency);
void config_set_symbolrate(uint32_t symbolrate);
void config_set_frequency_and_symbolrate(uint32_t frequency, uint32_t symbolrate);
void config_set_lnbv(bool enabled, bool horizontal);
#endif

7
longmynd/minitiouner.rules Executable file
View File

@ -0,0 +1,7 @@
# Install with `sudo cp minitiouner.rules /etc/udev/rules.d/`
# then unplug and replug the minitiouner
# Minitiouner uses FT2232H/D default VID/PID, but it's own product string.
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", ATTRS{product}=="USB <-> NIM tuner", MODE:="0666"
# Minitiouner Express uses different product string
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", ATTRS{product}=="MiniTiouner-Express", MODE:="0666"

207
longmynd/nim.c Executable file
View File

@ -0,0 +1,207 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: nim.c */
/* - an implementation of the Serit NIM controlling software for the MiniTiouner Hardware */
/* - handlers for the nim module itself */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- INCLUDES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "nim.h"
#include "ftdi.h"
#include "errors.h"
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- GLOBALS ------------------------------------------------------------------------ */
/* -------------------------------------------------------------------------------------------------- */
/* The tuner and LNAs are accessed by turning on the I2C bus repeater in the demodulator
this reduces the noise on the tuner I2C lines as they are inactive when the repeater
is turned off. We need to keep track of this when we access the NIM */
bool repeater_on;
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- ROUTINES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
uint8_t nim_read_demod(uint16_t reg, uint8_t *val) {
/* -------------------------------------------------------------------------------------------------- */
/* reads a demodulator register and takes care of the i2c bus repeater */
/* reg: which demod register to read */
/* val: where to put the result */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
/* if we are not using the tuner or lna any more then we can turn off
the repeater to reduce noise
this is bit 7 of the Px_I2CRPT register. Other bits define I2C speed etc. */
if (repeater_on) {
repeater_on=false;
err=nim_write_demod(0xf12a,0x38);
}
if (err==ERROR_NONE) err=ftdi_i2c_read_reg16(NIM_DEMOD_ADDR,reg,val);
if (err!=ERROR_NONE) printf("ERROR: demod read 0x%.4x\n",reg);
/* note we don't turn the repeater off as there might be other r/w to tuner/LNAs */
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t nim_write_demod(uint16_t reg, uint8_t val) {
/* -------------------------------------------------------------------------------------------------- */
/* writes to a demodulator register and takes care of the i2c bus repeater */
/* reg: which demod register to write to */
/* val: what to write to it */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
if (repeater_on) {
repeater_on=false;
err=nim_write_demod(0xf12a,0x38);
}
if (err==ERROR_NONE) err=ftdi_i2c_write_reg16(NIM_DEMOD_ADDR,reg,val);
if (err!=ERROR_NONE) printf("ERROR: demod write 0x%.4x, 0x%.2x\n",reg,val);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t nim_read_lna(uint8_t lna_addr, uint8_t reg, uint8_t *val) {
/* -------------------------------------------------------------------------------------------------- */
/* reads from the specified lna taking care of the i2c bus repeater */
/* lna_addr: i2c address of the lna to access */
/* reg: which lna register to read */
/* val: where to put the result */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
if (!repeater_on) {
err=nim_write_demod(0xf12a,0xb8);
repeater_on=true;
}
if (err==ERROR_NONE) err=ftdi_i2c_read_reg8(lna_addr,reg,val);
if (err!=ERROR_NONE) printf("ERROR: lna read 0x%.2x, 0x%.2x\n",lna_addr,reg);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t nim_write_lna(uint8_t lna_addr, uint8_t reg, uint8_t val) {
/* -------------------------------------------------------------------------------------------------- */
/* writes to the specified lna taking care of the i2c bus repeater */
/* lna_addr: i2c address of the lna to access */
/* reg: which lna register to write to */
/* val: what to write to it */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
if (!repeater_on) {
err=nim_write_demod(0xf12a,0xb8);
repeater_on=true;
}
if (err==ERROR_NONE) err=ftdi_i2c_write_reg8(lna_addr,reg,val);
if (err!=ERROR_NONE) printf("ERROR: lna write 0x%.2x, 0x%.2x,0x%.2x\n",lna_addr,reg,val);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t nim_read_tuner(uint8_t reg, uint8_t *val) {
/* -------------------------------------------------------------------------------------------------- */
/* reads from the stv0910 (tuner) taking care of the i2c bus repeater */
/* reg: which tuner register to read from */
/* val: where to put the result */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
if (!repeater_on) {
err=nim_write_demod(0xf12a,0xb8);
repeater_on=true;
}
if (err==ERROR_NONE) err=ftdi_i2c_read_reg8(NIM_TUNER_ADDR,reg,val);
if (err!=ERROR_NONE) printf("ERROR: tuner read 0x%.2x\n",reg);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t nim_write_tuner(uint8_t reg, uint8_t val) {
/* -------------------------------------------------------------------------------------------------- */
/* writes to the stv0910 (tuner) taking care of the i2c bus repeater */
/* reg: which tuner register to write to */
/* val: what to write to it */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
if (!repeater_on) {
err=nim_write_demod(0xf12a,0xb8);
repeater_on=true;
}
if (err==ERROR_NONE) err=ftdi_i2c_write_reg8(NIM_TUNER_ADDR,reg,val);
if (err!=ERROR_NONE) printf("ERROR: tuner write %i,%i\n",reg,val);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t nim_init() {
/* -------------------------------------------------------------------------------------------------- */
/* initialises the nim (at the highest level) */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
uint8_t val;
printf("Flow: NIM init\n");
repeater_on = false;
/* check we can read and write a register */
/* check to see if we can write and readback to a demod register */
if (err==ERROR_NONE) err=nim_write_demod(0xf536,0xaa); /* random reg with alternating bits */
if (err==ERROR_NONE) err=nim_read_demod(0xf536,&val);
if (err==ERROR_NONE) {
if (val!=0xaa) { /* check alternating bits ok */
printf("ERROR: nim_init didn't r/w to the modulator\n");
err=ERROR_NIM_INIT;
}
}
/* we always want to start with the i2c repeater turned off */
if (err!=ERROR_NONE) err=nim_write_demod(0xf12a,0x38);
if (err!=ERROR_NONE) printf("ERROR: nim_init\n");
return err;
}

48
longmynd/nim.h Executable file
View File

@ -0,0 +1,48 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: nim.h */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef NIM_H
#define NIM_H
#include "stvvglna.h"
#include <stdint.h>
#define NIM_DEMOD_ADDR 0xd2
#define NIM_TUNER_ADDR 0xc0
#define NIM_LNA_0_ADDR STVVGLNA_I2C_ADDR3
#define NIM_LNA_1_ADDR STVVGLNA_I2C_ADDR0
#define NIM_TUNER_XTAL 30000 /* in KHz */
#define NIM_DEMOD_MCLK 135000000 /* in Hz */
#define NIM_INPUT_TOP 1
#define NIM_INPUT_BOTTOM 2
uint8_t nim_init();
uint8_t nim_send_d0();
uint8_t nim_read_tuner (uint8_t, uint8_t*);
uint8_t nim_write_tuner(uint8_t, uint8_t );
uint8_t nim_read_demod (uint16_t, uint8_t*);
uint8_t nim_write_demod(uint16_t, uint8_t );
uint8_t nim_read_lna (uint8_t, uint8_t, uint8_t*);
uint8_t nim_write_lna (uint8_t, uint8_t, uint8_t );
#endif

17
longmynd/stopsrv.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
# 2023 F4IYT
if [ -e fifo.264 ]
then
rm fifo.264
fi
if [ -e longmynd_main_status ]
then
rm longmynd_main_*
rm flux.datv
killall fake_read
killall longmynd
fi
exit 0

684
longmynd/stv0910.c Executable file
View File

@ -0,0 +1,684 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: stv0910.c */
/* - an implementation of the Serit NIM controlling software for the MiniTiouner Hardware */
/* - the demodulator support routines (STV0910) */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- INCLUDES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include "stv0910.h"
#include "stv0910_regs.h"
#include "stv0910_utils.h"
#include "nim.h"
#include "errors.h"
#include "stv0910_regs_init.h"
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- ROUTINES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_read_car_freq(uint8_t demod, int32_t *cf) {
/* -------------------------------------------------------------------------------------------------- */
/* reads the current carrier frequency and return it (in Hz) */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* car_freq: signed place to store the answer */
/* return: error state */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
uint8_t val_h, val_m, val_l;
double car_offset_freq;
/* first off we read in the carrier offset as a signed number */
err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ?
RSTV0910_P2_CFR2 : RSTV0910_P1_CFR2, &val_h); /* high byte*/
if (err==ERROR_NONE) err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ?
RSTV0910_P2_CFR1 : RSTV0910_P1_CFR1, &val_m); /* mid */
if (err==ERROR_NONE) err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ?
RSTV0910_P2_CFR0 : RSTV0910_P1_CFR0, &val_l); /* low */
/* since this is a 24 bit signed value, we need to build it as a 24 bit value, shift it up to the top
to get a 32 bit signed value, then convert it to a double */
car_offset_freq=(double)(int32_t)((((uint32_t)val_h<<16) + ((uint32_t)val_m<< 8) + ((uint32_t)val_l )) << 8);
/* carrier offset freq (MHz)= mclk (MHz) * CFR/2^24. But we have the extra 256 in there from the sign shift */
/* so in Hz we need: */
car_offset_freq=135000000*car_offset_freq/256.0/256.0/256.0/256.0;
*cf=(int32_t)car_offset_freq;
if (err!=ERROR_NONE) printf("ERROR: STV0910 read carrier frequency\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_read_constellation(uint8_t demod, uint8_t *i, uint8_t *q) {
/* -------------------------------------------------------------------------------------------------- */
/* reads an I,Q pair from the constellation monitor registers */
/* i,q: places to store the results */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* return: error state */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_ISYMB : RSTV0910_P1_ISYMB, i);
if (err==ERROR_NONE) err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_QSYMB : RSTV0910_P1_QSYMB, q);
if (err!=ERROR_NONE) printf("ERROR: STV0910 read constellation\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_read_sr(uint8_t demod, uint32_t *found_sr) {
/* -------------------------------------------------------------------------------------------------- */
/* reads the currently detected symbol rate */
/* found_sr: place to store the result */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* return: error state */
/* -------------------------------------------------------------------------------------------------- */
double sr;
uint8_t val_h, val_mu, val_ml, val_l;
uint8_t err;
err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_SFR3 : RSTV0910_P1_SFR3, &val_h); /* high byte */
if (err==ERROR_NONE) err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_SFR2 : RSTV0910_P1_SFR2, &val_mu); /* mid upper */
if (err==ERROR_NONE) err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_SFR1 : RSTV0910_P1_SFR1, &val_ml); /* mid lower */
if (err==ERROR_NONE) err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_SFR0 : RSTV0910_P1_SFR0, &val_l); /* low byte */
sr=((uint32_t)val_h << 24) +
((uint32_t)val_mu << 16) +
((uint32_t)val_ml << 8) +
((uint32_t)val_l );
/* sr (MHz) = ckadc (MHz) * SFR/2^32. So in Symbols per Second we need */
sr=135000000*sr/256.0/256.0/256.0/256.0;
*found_sr=(uint32_t)sr;
if (err!=ERROR_NONE) printf("ERROR: STV0910 read symbol rate\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_read_puncture_rate(uint8_t demod, uint8_t *rate) {
/* -------------------------------------------------------------------------------------------------- */
/* reads teh detected viterbi punctuation rate */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* rate: place to store the result */
/* The single byta, n, represents a rate=n/n+1 */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
uint8_t val;
err=stv0910_read_reg_field(demod==STV0910_DEMOD_TOP ? FSTV0910_P2_VIT_CURPUN : FSTV0910_P1_VIT_CURPUN, &val);
switch (val) {
case STV0910_PUNCTURE_1_2: *rate=1; break;
case STV0910_PUNCTURE_2_3: *rate=2; break;
case STV0910_PUNCTURE_3_4: *rate=3; break;
case STV0910_PUNCTURE_5_6: *rate=5; break;
case STV0910_PUNCTURE_6_7: *rate=6; break;
case STV0910_PUNCTURE_7_8: *rate=7; break;
default: err=ERROR_VITERBI_PUNCTURE_RATE; break;
}
if (err!=ERROR_NONE) printf("ERROR: STV0910 read puncture rate\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_read_power(uint8_t demod, uint8_t *power_i, uint8_t *power_q) {
/* -------------------------------------------------------------------------------------------------- */
/* reads the power registers in the Demodulator and returns the results */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* power_i, power_q: places to store the results */
/* return: error state */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
/*power=1/4.ADC */
err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_POWERI : RSTV0910_P1_POWERI, power_i);
if (err==ERROR_NONE) err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_POWERQ : RSTV0910_P1_POWERQ, power_q);
if (err!=ERROR_NONE) printf("ERROR: STV0910 read power\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_read_err_rate(uint8_t demod, uint32_t *vit_errs) {
/* -------------------------------------------------------------------------------------------------- */
/* reads the viterbi error rate registers */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* vit_errs: place to store the result */
/* return: error state */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
uint8_t val;
err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_VERROR : RSTV0910_P1_VERROR, &val);
/* 0=perfect, 0xff=6.23 %errors (errs/4096) */
/* note there is a problem in the datasheet here as it says 255/2048=6.23% */
/* to report an integer we will report in 100 * the percentage, so 623=6.23% */
/* also want to round up to the nearest integer just to be pedantic */
*vit_errs=((((uint32_t)val)*100000/4096)+5)/10;
if (err!=ERROR_NONE) printf("ERROR: STV0910 read viterbi error rate\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_read_ber(uint8_t demod, uint32_t *ber) {
/* -------------------------------------------------------------------------------------------------- */
/* reads the number of bytes processed by the FEC, the number of error bits and then calculates BER */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* ber: place to store the result */
/* return: error state */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
uint8_t high, mid_u, mid_m, mid_l, low;
double cpt;
double errs;
/* first we trigger a buffer transfer and read the byte counter 40 bits */
err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_FBERCPT4 : RSTV0910_P1_FBERCPT4, &high);
if (err==ERROR_NONE) err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_FBERCPT3 : RSTV0910_P1_FBERCPT3, &mid_u);
if (err==ERROR_NONE) err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_FBERCPT2 : RSTV0910_P1_FBERCPT2, &mid_m);
if (err==ERROR_NONE) err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_FBERCPT1 : RSTV0910_P1_FBERCPT1, &mid_l);
if (err==ERROR_NONE) err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_FBERCPT0 : RSTV0910_P1_FBERCPT0, &low);
cpt=(double)high*256.0*256.0*256.0*256.0 + (double)mid_u*256.0*256.0*256.0 + (double)mid_m*256.0*256.0 +
(double)mid_l*256.0 + (double)low;
/* we have already triggered the register buffer transfer, so now we we read the bit error from them */
err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_FBERERR2 : RSTV0910_P1_FBERERR2, &high);
if (err==ERROR_NONE) err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_FBERERR1 : RSTV0910_P1_FBERERR1, &mid_m);
if (err==ERROR_NONE) err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_FBERERR0 : RSTV0910_P1_FBERERR0, &low);
errs=(double)high*256.0*256.0 + (double)mid_m*256.0 + (double)low;
*ber=(uint32_t)(10000.0*errs/(cpt*8.0));
if (err!=ERROR_NONE) printf("ERROR: STV0910 read BER\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_read_mer(uint8_t demod, uint32_t *mer) {
/* -------------------------------------------------------------------------------------------------- */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* mer: place to store the result */
/* return: error state */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
uint8_t high, low;
err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_NOSRAMPOS : RSTV0910_P1_NOSRAMPOS, &high);
if (err==ERROR_NONE) err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_NOSRAMVAL : RSTV0910_P1_NOSRAMVAL, &low);
if(((high >> 2) & 0x01) == 1)
{
/* Px_NOSRAM_CNRVAL is valid */
*mer = ((high & 0x03) << 8) | low;
}
else
{
*mer = 0;
if (err==ERROR_NONE) err=stv0910_write_reg_field(demod==STV0910_DEMOD_TOP ? FSTV0910_P2_NOSRAM_ACTIVATION : FSTV0910_P1_NOSRAM_ACTIVATION, 0x02);
}
if (err!=ERROR_NONE) printf("ERROR: STV0910 read DVBS2 MER\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_read_errors_bch_uncorrected(uint8_t demod, bool *errors_bch_uncorrected) {
/* -------------------------------------------------------------------------------------------------- */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read*/
/* errors_bch_uncorrected: place to store the result */
/* return: error state */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
uint8_t result;
/* This parameter appears to be total, not for an individual demodulator */
(void)demod;
err=stv0910_read_reg_field(FSTV0910_ERRORFLAG, &result);
if(result == 0) {
*errors_bch_uncorrected = true;
}
else {
*errors_bch_uncorrected = false;
}
if (err!=ERROR_NONE) printf("ERROR: STV0910 read BCH Errors Uncorrected\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_read_errors_bch_count(uint8_t demod, uint32_t *errors_bch_count) {
/* -------------------------------------------------------------------------------------------------- */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* errors_bch_count: place to store the result */
/* return: error state */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
uint8_t result;
/* This parameter appears to be total, not for an individual demodulator */
(void)demod;
err=stv0910_read_reg_field(FSTV0910_BCH_ERRORS_COUNTER, &result);
*errors_bch_count = (uint32_t)result;
if (err!=ERROR_NONE) printf("ERROR: STV0910 read BCH Errors Count\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_read_errors_ldpc_count(uint8_t demod, uint32_t *errors_ldpc_count) {
/* -------------------------------------------------------------------------------------------------- */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* errors_ldpc_count: place to store the result */
/* return: error state */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
uint8_t high, low;
/* This parameter appears to be total, not for an individual demodulator */
(void)demod;
err=stv0910_read_reg_field(FSTV0910_LDPC_ERRORS1, &high);
if (err==ERROR_NONE) err=stv0910_read_reg_field(FSTV0910_LDPC_ERRORS0, &low);
*errors_ldpc_count = (uint32_t)high << 8 | (uint32_t)low;
if (err!=ERROR_NONE) printf("ERROR: STV0910 read LDPC Errors Count\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_read_modcod_and_type(uint8_t demod, uint32_t *modcod, bool *short_frame, bool *pilots) {
/* -------------------------------------------------------------------------------------------------- */
/* Note that MODCODs are different in DVBS and DVBS2. Also short_frame and pilots only valid for S2 */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* modcod: place to store the result */
/* return: error state */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
uint8_t regval;
err=stv0910_read_reg(demod==STV0910_DEMOD_TOP ? RSTV0910_P2_DMDMODCOD : RSTV0910_P1_DMDMODCOD, &regval);
*modcod = (regval & 0x7c) >> 2;
*short_frame = (regval & 0x02) >> 1;
*pilots = regval & 0x01;
if (err!=ERROR_NONE) printf("ERROR: STV0910 read MODCOD\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_setup_clocks() {
/* -------------------------------------------------------------------------------------------------- */
/* sequence is: */
/* DIRCLK=0 (the hw clock selection pin) */
/* RESETB (the hw reset pin) transits from low to high at least 3ms after power stabilises */
/* disable demodulators (done in register initialisation) */
/* standby=1 (done in register initialisation) */
/* set NCOURSE etc. (PLL regs, also done in reg init) */
/* STANDBY=0 (turn on PLL) */
/* SYNCTRL:BYPASSPLLCORE=0 (turn on clocks) */
/* wait for lock bit to go high */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
uint32_t ndiv;
uint32_t odf;
uint32_t idf;
uint32_t f_phi;
uint32_t f_xtal;
uint8_t cp;
uint8_t lock=0;
uint16_t timeout=0;
printf("Flow: STV0910 set MCLK\n");
/* 800MHz < Fvco < 1800MHz */
/* Fvco = (ExtClk * 2 * NDIV) / IDF */
/* (400 * IDF) / ExtClk < NDIV < (900 * IDF) / ExtClk */
/* ODF forced to 4 otherwise desynchronization of digital and analog clock which result */
/* in a bad calculated symbolrate */
odf=4;
/* IDF forced to 1 : Optimal value */
idf=1;
if (err==ERROR_NONE) err=stv0910_write_reg_field(FSTV0910_ODF, odf);
if (err==ERROR_NONE) err=stv0910_write_reg_field(FSTV0910_IDF, idf);
f_xtal=NIM_TUNER_XTAL/1000; /* in MHz */
f_phi=135000000/1000000;
ndiv=(f_phi * odf * idf) / f_xtal;
if (err==ERROR_NONE) err=stv0910_write_reg_field(FSTV0910_N_DIV, ndiv);
/* Set CP according to NDIV */
cp=7;
if (err==ERROR_NONE) err=stv0910_write_reg_field(FSTV0910_CP, cp);
/* turn on all the clocks */
if (err==ERROR_NONE) err=stv0910_write_reg_field(FSTV0910_STANDBY, 0);
/* derive clocks from PLL */
if (err==ERROR_NONE) err=stv0910_write_reg_field(FSTV0910_BYPASSPLLCORE, 0);
/* wait for PLL to lock */
do {
timeout++;
if (timeout==STV0910_PLL_LOCK_TIMEOUT) {
err=ERROR_DEMOD_PLL_TIMEOUT;
printf("ERROR: STV0910 pll lock timeout\n");
}
if (err==ERROR_NONE) stv0910_read_reg_field(FSTV0910_PLLLOCK, &lock);
} while ((err==ERROR_NONE) && (lock==0));
if (err!=ERROR_NONE) printf("ERROR: STV0910 set MCLK\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_setup_equalisers(uint8_t demod) {
/* -------------------------------------------------------------------------------------------------- */
/* 2 parts DFE, FFE */
/* DFE: update speed is in EQUALCFG.PX_MU_EQUALDFE. */
/* turn it on with EQUAL_ON and freeze with PX_MU_EQUALDFE=0 */
/* FFE: update speed is in FFECFGPX_MU_EQUALFFE. */
/* turn it on with EQUALFFE_ON and freeze with PX_MU_EQUALFFE=0 */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
printf("Flow: Setup equlaizers %i\n", demod);
return ERROR_NONE;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_setup_carrier_loop(uint8_t demod) {
/* -------------------------------------------------------------------------------------------------- */
/* 3 stages: */
/* course: */
/* CARFREQ sets speed and precision */
/* limitsd are in CFRUP and CFRLOW */
/* step size in CFRINC */
/* fine: */
/* CARFREQ_BETA_FREQ defines time constant */
/* once course is done, phase tracking loop is used, ACLC and BCLC define loop parameters */
/* if DVBS not resolved, attempts to reolve DVB-S2 headers as defined in CARHDR.K_FREQ_HDR */
/* tracking: */
/* seperate alpha a beta for DVBS (ACLC and BCLC) and DVB-S2 (Alpha in CLC2S2Q and */
/* beta in ACLC2S28) */
/* lock detect: */
/* DVBS LDI has accumulator. compared to threshold (LDT, LDT2) and the results are */
/* in DSTATUS.CAR_LOCK */
/* when lock bit is set, freq detector is disabled amd starts phase tracking. */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
printf("Flow: Setup carrier loop %i\n", demod);
/* start at 0 offset */
err=stv0910_write_reg((demod==STV0910_DEMOD_TOP ? RSTV0910_P2_CFRINIT0 : RSTV0910_P1_CFRINIT0), 0);
if (err==ERROR_NONE) err=stv0910_write_reg((demod==STV0910_DEMOD_TOP ? RSTV0910_P2_CFRINIT1 : RSTV0910_P1_CFRINIT1), 0);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_setup_timing_loop(uint8_t demod, uint32_t sr) {
/* -------------------------------------------------------------------------------------------------- */
/* coarse aquisition */
/* put in coarse mode in TMGCFG2 (regs init) */
/* put in auto mode TMGCFG3 (regs init) */
/* set SFRUPRATIO, SFTLOWRATIO (regs init) */
/* set so that when boundary reached scan is inverted (regs init) */
/* set to keep looking indefinitely (regs init) */
/* DVBS alpha and beta are used */
/* observe where it is at with KREFTMG2 */
/* fine aquisition */
/* coarse search defines the fine scanning range automtically */
/* go to fine mode TMGCFG2 */
/* SFRSTEP.SFR_SCANSTEP defines fineness of scan */
/* tracking */
/* it now loads loop parameters */
/* seperate alphas and betas for DVBS and SVB-S2: */
/* DVBS TMGALPHA_EXP TIMGBETA_EXP in RTC */
/* iDVB-S2 TMGALPHAS2_EXP and TMGBETA2_EXP in RTCS2 */
/* when lock achieved timing offset is in TMGREG */
/* timing offset can be cancelled out (ie TMGREG set to zero and SFR adjusted accordingly */
/* */
/* lock indicator is DSTATUS maximised when locked. */
/* need to optimise lock thresholds to optimise lock stability */
/* lock indicator is filtered with time constant: TMGCFG.TMGLOCK_BETA */
/* this is compared to 2 thresholds TMGTHRISE_TMGLOCK_THRISE and TMGTHFALL.TMGLOCK_THFALL in order */
/* to issue TMGCLOCK_QUALITY[1:0] which is a 2 bit lock indicator with hysterisis in DSTATUS. */
/* lock is when TMGLOCK>TMGLOCK_THRISE (in TMGLOCK_QUALITY) */
/* loss of lock is when TMGLOCK < TTMGLOCK_THFALL in TMGLOCK_QUALITY */
/* */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* return: error state */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
uint16_t sr_reg;
printf("Flow: Setup timing loop %i\n", demod);
/* SR (MHz) = ckadc (135MHz) * SFRINIT / 2^16 */
/* we have sr in KHz, ckadc in MHz) */
sr_reg=(uint16_t)((((uint32_t)sr) << 16) / 135 / 1000);
if (err==ERROR_NONE) err=stv0910_write_reg((demod==STV0910_DEMOD_TOP ? RSTV0910_P2_SFRINIT1 : RSTV0910_P1_SFRINIT0),
(uint8_t)(sr_reg >> 8) );
if (err==ERROR_NONE) err=stv0910_write_reg((demod==STV0910_DEMOD_TOP ? RSTV0910_P2_SFRINIT0 : RSTV0910_P1_SFRINIT1),
(uint8_t)(sr_reg & 0xFF) );
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_setup_ts(uint8_t demod) {
/* -------------------------------------------------------------------------------------------------- */
/* format with or without sync and header bytes TSINSDELH */
/* output rate manual or auto adjust */
/* control with TSCFX */
/* serial or paralled TSCFGH.PxTSFIFO_SERIAL (serial is on D7) 2 control bits */
/* configure bus to low impedance (high Z on reset) OUTCFG */
/* DPN (data valid/parity negated) is high when FEC is outputting data */
/* low when redundant data is out[ut eq parity data or rate regulation stuffing bits) */
/* Data is regulated by CLKOUT and DPN: either data valid or envelope. */
/* data valid uses continuous clock and select valid data using DPN */
/* envelope: DPN still indicates valid data and then punctured clock for rate regulation */
/* TSCFGH.TSFIFO_DVBCI=1 for data and 0 for envelope. */
/* CLKOUT polarity bit XOR, OUTCFG2.TS/2_CLKOUT_XOR=0 valid rising (=1 for falling). */
/* TSFIFOMANSPEED controlls data rate (padding). 0x11 manual, 0b00 fully auto. speed is TSSPEE */
/* if need square clock, TSCFGH.TSFIFO_DUTY50. */
/* parallel mode is ST back end. CLKOUT held (TSCFGH.TSINFO_DBCI) for unknown data section */
/* or DVB-CI: DRN is help (CLKOUTnCFG.CLKOUT_XOR) for unknown data section */
/* in both STRUT is high for first byte of packet */
/* rate compensation is TSCFGH.TSFIFO_DVBCI */
/* */
/* All of this is set in the register init. */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* return: error state */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
printf("Flow: Setup ts %i\n", demod);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_start_scan(uint8_t demod) {
/* -------------------------------------------------------------------------------------------------- */
/* demodulator search sequence is: */
/* setup the timing loop */
/* setup the carrier loop */
/* set initial carrier offset CFRINIT for best guess carrier search */
/* set initial symbol ratea SFRINIT for best guess blind search */
/* set manual mode for CFRINC to be used - make it small */
/* write DMDISTATE to AER = 1 - blind search with best guess */
/* auto mode for symbol rate will be +/25% SFRUPRATIO and SFRLOWRATIO define this number. */
/* SFRUP1:AUTO_GUP=1 for auto (and SFRLOW1:AUTO_GLOW=1) */
/* cold start carrier and sr unknown but use best guess (0x01) */
/* */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* return: error state */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
printf("Flow: STV0910 start scan\n");
if (err==ERROR_NONE) err=stv0910_write_reg((demod==STV0910_DEMOD_TOP ? RSTV0910_P2_DMDISTATE : RSTV0910_P1_DMDISTATE),
STV0910_SCAN_BLIND_BEST_GUESS);
if (err!=ERROR_NONE) printf("ERROR: STV0910 start scan\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_read_scan_state(uint8_t demod, uint8_t *state) {
/* -------------------------------------------------------------------------------------------------- */
/* simply reads out the demodulator states for the given demodulator */
/* demod: STV0910_DEMOD_TOP | STV0910_DEMOD_BOTTOM: which demodulator is being read */
/* return: error state */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
if (err==ERROR_NONE) err=stv0910_read_reg_field((demod==STV0910_DEMOD_TOP ?
FSTV0910_P2_HEADER_MODE : FSTV0910_P1_HEADER_MODE), state);
if (err!=ERROR_NONE) printf("ERROR: STV0910 read scan state\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_init_regs() {
/* -------------------------------------------------------------------------------------------------- */
/* reads all the initial values for all the demodulator registers and sets them up */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t val1;
uint8_t val2;
uint8_t err;
uint16_t i=0;
printf("Flow: stv0910 init regs\n");
/* first we check on the IDs */
err=nim_read_demod(0xf100, &val1);
if (err==ERROR_NONE) err=nim_read_demod(0xf101, &val2);
printf(" Status: STV0910 MID = 0x%.2x, DID = 0x%.2x\n", val1, val2);
if ((val1!=0x51) || (val2!=0x20)) {
printf("ERROR: read the wrong stv0910 MID/DID");
return ERROR_DEMOD_INIT;
}
/* next we initialise all the registers in the list */
do {
if (err==ERROR_NONE) err=stv0910_write_reg(STV0910DefVal[i].reg, STV0910DefVal[i].val);
}
while (STV0910DefVal[i++].reg!=RSTV0910_TSTTSRS);
/* finally (from ST example code) reset the LDPC decoder */
if (err==ERROR_NONE) err=stv0910_write_reg(RSTV0910_TSTRES0, 0x80);
if (err==ERROR_NONE) err=stv0910_write_reg(RSTV0910_TSTRES0, 0x00);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_init(uint32_t sr1, uint32_t sr2) {
/* -------------------------------------------------------------------------------------------------- */
/* demodulator search sequence is: */
/* setup the carrier loop */
/* set initial carrier offset CFRINIT for best guess carrier search */
/* set manual mode for CFRINC to be used - make it small */
/* setup the timing loop */
/* set initial symbol rate SFRINIT */
/* auto mode for symbol rate will be +/25% SFRUPRATIO and SFRLOWRATIO define this number. */
/* SFRUP1:AUTO_GUP=1 for auto (and SFRLOW1:AUTO_GLOW=1) */
/* write DMDISTATE to AER = 1 - blind search with best guess (SR and carrier unknown) */
/* FLYWHEEL_CPT: when 0xf DVB-S2 is locked in DMDFLYW (also int stus bits */
/* sr_top : the symbol rate to initialise the top demodulator to (0=disable) */
/* sr_bottom: the symbol rate to initialise the bottom demodulator to (0=disable) */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
printf("Flow: STV0910 init\n");
/* first we stop the demodulators in case they are already running */
if (err==ERROR_NONE) err=stv0910_write_reg(RSTV0910_P1_DMDISTATE, 0x1c);
if (err==ERROR_NONE) err=stv0910_write_reg(RSTV0910_P2_DMDISTATE, 0x1c);
/* do the non demodulator specific stuff */
if (err==ERROR_NONE) err=stv0910_init_regs();
if (err==ERROR_NONE) err=stv0910_setup_clocks();
/* now we do the inits for each specific demodulator */
if (sr1!=0) {
if (err==ERROR_NONE) err=stv0910_setup_equalisers(STV0910_DEMOD_TOP);
if (err==ERROR_NONE) err=stv0910_setup_carrier_loop(STV0910_DEMOD_TOP);
if (err==ERROR_NONE) err=stv0910_setup_timing_loop(STV0910_DEMOD_TOP, sr1);
}
if (sr2!=0) {
if (err==ERROR_NONE) err=stv0910_setup_equalisers(STV0910_DEMOD_BOTTOM);
if (err==ERROR_NONE) err=stv0910_setup_carrier_loop(STV0910_DEMOD_BOTTOM);
if (err==ERROR_NONE) err=stv0910_setup_timing_loop(STV0910_DEMOD_BOTTOM, sr2);
}
if (err!=ERROR_NONE) printf("ERROR: STV0910 init\n");
return err;
}

69
longmynd/stv0910.h Executable file
View File

@ -0,0 +1,69 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: stv0910.h */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef STV0910_H
#define STV0910_H
#include <stdbool.h>
#define DEMOD_HUNTING 0
#define DEMOD_FOUND_HEADER 1
#define DEMOD_S2 2
#define DEMOD_S 3
#define STV0910_PLL_LOCK_TIMEOUT 100
#define STV0910_SCAN_BLIND_BEST_GUESS 0x15
#define STV0910_DEMOD_TOP 1
#define STV0910_DEMOD_BOTTOM 2
#define STV0910_PUNCTURE_1_2 0x0d
#define STV0910_PUNCTURE_2_3 0x12
#define STV0910_PUNCTURE_3_4 0x15
#define STV0910_PUNCTURE_5_6 0x18
#define STV0910_PUNCTURE_6_7 0x19
#define STV0910_PUNCTURE_7_8 0x1a
uint8_t stv0910_read_car_freq(uint8_t, int32_t*);
uint8_t stv0910_read_constellation(uint8_t, uint8_t*, uint8_t*);
uint8_t stv0910_read_sr(uint8_t demod, uint32_t*);
uint8_t stv0910_read_puncture_rate(uint8_t, uint8_t*);
uint8_t stv0910_read_power(uint8_t, uint8_t*, uint8_t*);
uint8_t stv0910_read_err_rate(uint8_t, uint32_t*);
uint8_t stv0910_read_ber(uint8_t, uint32_t*);
uint8_t stv0910_read_dvbs2_mer(uint8_t, uint32_t*);
uint8_t stv0910_read_errors_bch_uncorrected(uint8_t demod, bool *errors_bch_uncorrected);
uint8_t stv0910_read_errors_bch_count(uint8_t demod, uint32_t *errors_bch_count);
uint8_t stv0910_read_errors_ldpc_count(uint8_t demod, uint32_t *errors_ldpc_count);
uint8_t stv0910_read_mer(uint8_t, uint32_t*);
uint8_t stv0910_read_modcod_and_type(uint8_t, uint32_t*, bool*, bool*);
uint8_t stv0910_init(uint32_t, uint32_t);
uint8_t stv0910_init_regs(void);
uint8_t stv0910_setup_timing_loop(uint8_t, uint32_t);
uint8_t stv0910_setup_carrier_loop(uint8_t);
uint8_t stv0910_read_scan_state(uint8_t, uint8_t *);
uint8_t stv0910_start_scan(uint8_t);
uint8_t stv0910_setup_search_params(uint8_t);
uint8_t stv0910_setup_clocks();
#endif

4790
longmynd/stv0910_regs.h Executable file

File diff suppressed because it is too large Load Diff

1082
longmynd/stv0910_regs_init.h Executable file

File diff suppressed because it is too large Load Diff

111
longmynd/stv0910_utils.c Executable file
View File

@ -0,0 +1,111 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: stv0910_utils.c */
/* - an implementation of the Serit NIM controlling software for the MiniTiouner Hardware */
/* - These routines abstract the register read and writes for the demodulator (STV0910) */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- INCLUDES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdint.h>
#include "stv0910_regs.h"
#include "stv0910_utils.h"
#include "errors.h"
#include "nim.h"
/* in order to do bitfields efficiently, we need to keep a shadow register set */
uint8_t stv0910_shadow_regs[STV0910_END_ADDR - STV0910_START_ADDR + 1];
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- ROUTINES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_write_reg_field(uint32_t field, uint8_t field_val) {
/* -------------------------------------------------------------------------------------------------- */
/* changes a bitfield of a register using a shadow register array to do the read/modify/write */
/* field: the #define of the bitfield as given in stv0910_regs.h (from the datasheet) */
/* field_val: what to set the field to (in the LSBs of the value */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
uint16_t reg;
uint8_t val;
/* firsr we need to work out which register to use */
reg=field >> 16;
/* now we calculate the new value for this reg by reading the shadow array, */
/* masking out the field we want, and putting the new value in */
val=((stv0910_shadow_regs[reg-STV0910_START_ADDR] & ~(field & 0xff)) |
(field_val << ((field >> 12) & 0x0f)) );
/* now we can write the new value back to the demodulator and the shadow registers */
if (err==ERROR_NONE) err=nim_write_demod(reg, val);
if (err==ERROR_NONE) stv0910_shadow_regs[reg-STV0910_START_ADDR]=val;
if (err!=ERROR_NONE) printf("ERROR: STV0910 write field\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_read_reg_field(uint32_t field, uint8_t *field_val) {
/* -------------------------------------------------------------------------------------------------- */
/* reads a bitfield of a register. This cannot go via the shadows as the regs are volatile */
/* field: the #define of the bitfield as given in stv0910_regs.h (from the datasheet) */
/* *field_val: the contents of the field once read */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
uint8_t val;
/* we can read the register value first */
if (err==ERROR_NONE) err=nim_read_demod((uint16_t)(field >> 16), &val);
/* and then do the masks and shifts to get at the specific bits */
*field_val = ((val) & (field & 0xff)) >> ((field >> 12) & 0x0f);
if (err!=ERROR_NONE) printf("ERROR: STV0910 read field\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_write_reg(uint16_t reg, uint8_t val) {
/* -------------------------------------------------------------------------------------------------- */
/* abstracts a hardware register write to the stv0910 */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
stv0910_shadow_regs[reg-STV0910_START_ADDR]=val;
return nim_write_demod(reg, val);
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv0910_read_reg(uint16_t reg, uint8_t *val) {
/* -------------------------------------------------------------------------------------------------- */
/* abstracts a hardware register read from the stv0910 */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
return nim_read_demod(reg, val);
}

34
longmynd/stv0910_utils.h Executable file
View File

@ -0,0 +1,34 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: stv0910_utils.h */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef STV0910_UTILS_H
#define STV0910_UTILS_H
#define STV0910_START_ADDR RSTV0910_MID
#define STV0910_END_ADDR RSTV0910_TSTTSRS
uint8_t stv0910_write_reg_field(uint32_t, uint8_t);
uint8_t stv0910_read_reg_field(uint32_t, uint8_t *);
uint8_t stv0910_write_reg(uint16_t, uint8_t);
uint8_t stv0910_read_reg(uint16_t, uint8_t *);
#endif

438
longmynd/stv6120.c Executable file
View File

@ -0,0 +1,438 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: stv6120.c */
/* - an implementation of the Serit NIM controlling software for the MiniTiouner Hardware */
/* - the stv6120 (tuner) specific routines */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- INCLUDES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
#include "math.h"
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "nim.h"
#include "stv6120.h"
#include "stv6120_regs.h"
#include "stv6120_utils.h"
#include "errors.h"
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- GLOBALS ------------------------------------------------------------------------ */
/* -------------------------------------------------------------------------------------------------- */
/* some globals to make it easier to keep track of register contents across function calls */
uint8_t rdiv;
uint8_t ctrl7;
uint8_t ctrl8;
uint8_t ctrl16;
uint8_t ctrl17;
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- CONSTANTS ---------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
/* from the datasheet: charge pump data */
const uint32_t stv6120_icp_lookup[7][3]={
/* low high icp */
{2380000, 2472000, 0},
{2473000, 2700000, 1},
{2701000, 3021000, 2},
{3022000, 3387000, 3},
{3388000, 3845000, 5},
{3846000, 4394000, 6},
{4395000, 4760000, 7}
};
/* a lookup table for the cutuff freq of the HF filter in MHz */
const uint16_t stv6120_cfhf[32]={6796, 5828, 4778, 4118, 3513, 3136, 2794, 2562,
2331, 2169, 2006, 1890, 1771, 1680, 1586, 1514,
1433, 1374, 1310, 1262, 1208, 1167, 1122, 1087,
1049, 1018, 983, 956, 926, 902, 875, 854};
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- ROUTINES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv6120_cal_lowpass(uint8_t tuner) {
/* -------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
uint8_t val;
uint16_t timeout;
printf("Flow: Tuner cal lowpass\n");
/* turn on the clock for the low pass filter. This is in ctrl7/16 so we have a shadow for it */
if (tuner==TUNER_1) err=stv6120_write_reg(STV6120_CTRL7 , ctrl7 & ~(1 << STV6120_CTRL7_RCCLKOFF_SHIFT));
else err=stv6120_write_reg(STV6120_CTRL16, ctrl16 & ~(1 << STV6120_CTRL7_RCCLKOFF_SHIFT));
/* now we can do a low pass filter calibration, by setting the CALRCSTRT bit. NOte it is safe to just write to it */
if (err==ERROR_NONE) err=stv6120_write_reg(tuner==TUNER_1 ? STV6120_STAT1 : STV6120_STAT2,
(STV6120_STAT1_CALRCSTRT_START << STV6120_STAT1_CALRCSTRT_SHIFT));
/* wait for the bit to be cleared to say cal has finished*/
if (err==ERROR_NONE) {
timeout=0;
do {
err=stv6120_read_reg(STV6120_STAT1, &val);
timeout++;
if (timeout==STV6120_CAL_TIMEOUT) {
err=ERROR_TUNER_CAL_LOWPASS_TIMEOUT;
printf("ERROR: tuner wait on CAL_lowpass timed out\n");
}
} while ((err==ERROR_NONE) && ((val & (1<<STV6120_STAT1_CALRCSTRT_SHIFT)) == (1<<STV6120_STAT1_CALRCSTRT_SHIFT)));
}
/* turn off the low pass filter clock (=1) */
if (err==ERROR_NONE) err=stv6120_write_reg(tuner==TUNER_1 ? STV6120_CTRL7 : STV6120_CTRL16,
(tuner==TUNER_1 ? ctrl7 : ctrl16));
if (err!=ERROR_NONE) printf("ERROR: Failed to cal lowpass filter\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv6120_set_freq(uint8_t tuner, uint32_t freq) {
/* -------------------------------------------------------------------------------------------------- */
/* Sets one of the tuners to the given frequency */
/* tuner: TUNER_1 | TUNER_2 : which tuner we are going to work on */
/* freq: the frequency to set the tuner to in KHz */
/* return: error code */
/* */
/* when locked, F.lo = F.vco/P = (F.xtal/R) * (N+F/2^18)/P */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
uint8_t val;
uint8_t pos;
uint32_t f;
uint16_t n;
uint8_t p;
uint8_t icp;
uint32_t f_vco;
uint16_t timeout;
uint8_t cfhf;
printf("Flow: Tuner set freq\n");
/* the global rdiv has already been set up in the init routines */
/* p is defined from the datasheet (note, this is reg value, not P) */
if (freq<=STV6120_P_THRESHOLD_1) p=3; /* P=16 */
else if (freq<=STV6120_P_THRESHOLD_2) p=2; /* P= 8 */
else if (freq<=STV6120_P_THRESHOLD_3) p=1; /* P= 4 */
else p=0; /* P= 2 */
/* we have to be careful of the size of the typesi in the following */
/* F.vco=F.rf*P where F.rf=F.lo all in KHz */
/* f_vco is uint32_t, so p_max is 3 (i.e P_max is 16), freq_max is 2500000KHz, results is 0x02625a00 ... OK */
f_vco = freq<<(p+1);
/* n=integer(f_vco/f_xtal*R) note: f_xtal and f_vco both in KHz */
/* we do the *R first (a shift by rdiv), and max is 0x04c4b400, then the divide and we are OK */
n = (uint16_t)((f_vco << rdiv) / NIM_TUNER_XTAL);
/* f = fraction(f_vco/f_xtal*R).2^18 */
/* as for n, we do the shift first (which we know is safe), then modulus to get the fraction */
/* then we have to go to 64 bits to do the shift and divide, and then back to uint32_t for the result */
f = (uint32_t)(( ((uint64_t)((f_vco << rdiv) % NIM_TUNER_XTAL)) << 18) / NIM_TUNER_XTAL);
/* lookup the ICP value in the lookup table as per datasheet */
pos=0;
while (f_vco > stv6120_icp_lookup[pos++][1]);
icp=stv6120_icp_lookup[pos-1][2];
/* lookup the high freq filter cutoff setting as per datasheet */
cfhf=0;
while ((3*freq/1000) <= stv6120_cfhf[cfhf]) {
cfhf++;
}
cfhf--; /* we are sure it isn't greater then the first array element so this is safe */
printf(" Status: tuner:%i, f_vco=0x%x, icp=0x%x, f=0x%x, n=0x%x,\n",tuner,f_vco,icp,f,n);
printf(" rdiv=0x%x, p=0x%x, freq=%i, cfhf=%i\n",rdiv,p,freq,stv6120_cfhf[cfhf]);
/* now we fill in the PLL and ICP values */
if (err==ERROR_NONE) err=stv6120_write_reg(tuner==TUNER_1 ? STV6120_CTRL3 : STV6120_CTRL12,
(n & 0x00ff) ); /* set N[7:0] */
if (err==ERROR_NONE) err=stv6120_write_reg(tuner==TUNER_1 ? STV6120_CTRL4 : STV6120_CTRL13,
((f & 0x0000007f) << 1) | /* set F[6:0] */
((n & 0x0100) >> 8) ); /* N[8] */
if (err==ERROR_NONE) err=stv6120_write_reg(tuner==TUNER_1 ? STV6120_CTRL5 : STV6120_CTRL14,
((f & 0x00007f80) >> 7) ); /* set F[14:7] */
if (err==ERROR_NONE) err=stv6120_write_reg(tuner==TUNER_1 ? STV6120_CTRL6 : STV6120_CTRL15,
((f & 0x00038000) >> 15) | /* set f[17:15] */
(icp << STV6120_CTRL6_ICP_SHIFT) | /* ICP[2:0] */
STV6120_CTRL6_RESERVED ); /* reserved bit */
if (tuner==TUNER_1) {
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL7,
(p<<STV6120_CTRL7_PDIV_SHIFT) |
ctrl7 ); /* put back in RCCLKOFF_1 as well */
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL8,
(cfhf << STV6120_CTRL8_CFHF_SHIFT) |
ctrl8 );
} else { /* tuner=TUNER_2 */
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL16,
(p<<STV6120_CTRL7_PDIV_SHIFT) |
ctrl16 ); /* put back in RCCLKOFF_2 as well */
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL17,
(cfhf << STV6120_CTRL8_CFHF_SHIFT) |
ctrl17 );
}
/* if we change the filter re-cal it, and if we change VCO we have to re-cal it, so here goes */
if (err==ERROR_NONE) err=stv6120_write_reg(tuner==TUNER_1 ? STV6120_STAT1 : STV6120_STAT2,
(STV6120_STAT1_CALVCOSTRT_START << STV6120_STAT1_CALVCOSTRT_SHIFT) | /* start CALVCOSTRT */
STV6120_STAT1_RESERVED );
/* wait for CALVCOSTRT bit to go low to say VCO cal is finished */
if (err==ERROR_NONE) {
timeout=0;
do {
err=stv6120_read_reg(tuner==TUNER_1 ? STV6120_STAT1 : STV6120_STAT2, &val);
timeout++;
} while ((err==ERROR_NONE) &&
(timeout<STV6120_CAL_TIMEOUT) &&
((val & (1<<STV6120_STAT1_CALVCOSTRT_SHIFT))!=(STV6120_STAT1_CALVCOSTRT_FINISHED << STV6120_STAT1_CALVCOSTRT_SHIFT)));
if ((err==ERROR_NONE) && (timeout==STV6120_CAL_TIMEOUT)) {
printf("ERROR: tuner wait on CAL timed out\n");
err=ERROR_TUNER_CAL_TIMEOUT;
}
}
/* wait for LOCK bit to go high to say PLL is locked */
if (err==ERROR_NONE) {
timeout=0;
do {
err=stv6120_read_reg(tuner==TUNER_1 ? STV6120_STAT1 : STV6120_STAT2, &val);
timeout++;
} while ((err==ERROR_NONE) &&
(timeout<STV6120_CAL_TIMEOUT) &&
((val & (1<<STV6120_STAT1_LOCK_SHIFT)) != (STV6120_STAT1_LOCK_LOCKED << STV6120_STAT1_LOCK_SHIFT)));
if ((err==ERROR_NONE) && (timeout==STV6120_CAL_TIMEOUT)) {
printf("ERROR: tuner wait on lock timed out\n");
err=ERROR_TUNER_LOCK_TIMEOUT;
}
}
if (err!=ERROR_NONE) printf("ERROR: Tuner set freq %i\n",freq);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv6120_init(uint32_t freq_tuner_1, uint32_t freq_tuner_2, bool swap) {
/* -------------------------------------------------------------------------------------------------- */
/* Initialises the tuner. Both tuners can be set up. */
/* freq_tuner_1: 0: disable tuner 1 */
/* >0: the frequency to set tuner 1 to */
/* freq_tuner_2: 0: disable tuner 2 */
/* >0: the frequency to set tuner 2 to */
/* swap: false: use TOP input for tuner_1 */
/* true : use bottom input for tuner_1 */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
uint8_t k;
printf("Flow: Tuner init\n");
/* note, we always init the tuner from scratch so no need to check if we have already inited it before */
/* also, the tuner doesn't have much of an ID so no point in checking it */
/* we calculate K from F_xtal/(K+16)=1MHz as specified in the datasheet */
k=NIM_TUNER_XTAL/1000-16;
/* setup the clocks for both tuners (note rdiv is a global) */
if (NIM_TUNER_XTAL>=STV6120_RDIV_THRESHOLD) rdiv=1; /* from the data sheet */
else rdiv=0;
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL1,
(k << STV6120_CTRL1_K_SHIFT) |
(rdiv << STV6120_CTRL1_RDIV_SHIFT) |
(STV6120_CTRL1_OSHAPE_SINE << STV6120_CTRL1_OSHAPE_SHIFT) |
(STV6120_CTRL1_MCLKDIV_4 << STV6120_CTRL1_MCLKDIV_SHIFT) );
/* Configure path 1 */
if (freq_tuner_1>0) { /* we are go on tuner 1 so turn it on */
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL2,
(STV6120_CTRL2_DCLOOPOFF_ENABLE << STV6120_CTRL2_DCLOOPOFF_SHIFT) |
(STV6120_CTRL2_SDOFF_OFF << STV6120_CTRL2_SDOFF_SHIFT) |
(STV6120_CTRL2_SYN_ON << STV6120_CTRL2_SYN_SHIFT) |
(STV6120_CTRL2_REFOUTSEL_1_25V << STV6120_CTRL2_REFOUTSEL_SHIFT) |
(STV6120_CTRL2_BBGAIN_0DB << STV6120_CTRL2_BBGAIN_SHIFT) );
/* CTRL3,4,5,6 are all tuner 1 PLL regs we will set them later */
/* turn off rcclk for now */
if (err==ERROR_NONE) {
ctrl7 = (STV6120_CTRL7_RCCLKOFF_DISABLE << STV6120_CTRL7_RCCLKOFF_SHIFT) |
(STV6120_CTRL7_CF_5MHZ << STV6120_CTRL7_CF_SHIFT) ;
err=stv6120_write_reg(STV6120_CTRL7, ctrl7);
}
} else { /* we are not going to use path 1 so shut it down */
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL2,
(STV6120_CTRL2_DCLOOPOFF_DISABLE << STV6120_CTRL2_DCLOOPOFF_SHIFT) |
(STV6120_CTRL2_SDOFF_ON << STV6120_CTRL2_SDOFF_SHIFT) |
(STV6120_CTRL2_SYN_OFF << STV6120_CTRL2_SYN_SHIFT) |
(STV6120_CTRL2_REFOUTSEL_1_25V << STV6120_CTRL2_REFOUTSEL_SHIFT) |
(STV6120_CTRL2_BBGAIN_0DB << STV6120_CTRL2_BBGAIN_SHIFT) );
/* CTRL3,4,5,6 are all tuner 1 PLL regs we will set them later */
if (err==ERROR_NONE) {
ctrl7 = (STV6120_CTRL7_RCCLKOFF_DISABLE << STV6120_CTRL7_RCCLKOFF_SHIFT) |
(STV6120_CTRL7_CF_5MHZ << STV6120_CTRL7_CF_SHIFT) ;
err=stv6120_write_reg(STV6120_CTRL7, ctrl7);
}
}
/* we need to set tcal for both tuners, but we need to remember the state in case we are using tuner 1 later */
if (err==ERROR_NONE) {
ctrl8 = (STV6120_CTRL8_TCAL_DIV_2 << STV6120_CTRL8_TCAL_SHIFT) |
(STV6120_CTRL8_CALTIME_500US << STV6120_CTRL8_TCAL_SHIFT) ;
err=stv6120_write_reg(STV6120_CTRL8, ctrl8);
}
/* no need to touch the STAT1 status register for now */
/* setup the RF path registers. RFA and RFD are not used. RFB is fed from the TOP NIM input, RFC from the BOTTOM */
/* if we are swapping these inputs over we need to enable the correct LNAs */
if (swap) {
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL9,
(STV6120_CTRL9_RFSEL_RFC_IN << STV6120_CTRL9_RFSEL_1_SHIFT) |
(STV6120_CTRL9_RFSEL_RFB_IN << STV6120_CTRL9_RFSEL_2_SHIFT) |
STV6120_CTRL9_RESERVED );
/* decide on which LNAs are we going to enable */
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL10,
(( STV6120_CTRL10_LNA_OFF) << STV6120_CTRL10_LNADON_SHIFT) |
((freq_tuner_2 > 0 ? STV6120_CTRL10_LNA_ON : STV6120_CTRL10_LNA_OFF) << STV6120_CTRL10_LNABON_SHIFT) |
((freq_tuner_1 > 0 ? STV6120_CTRL10_LNA_ON : STV6120_CTRL10_LNA_OFF) << STV6120_CTRL10_LNACON_SHIFT) |
(( STV6120_CTRL10_LNA_OFF) << STV6120_CTRL10_LNAAON_SHIFT) |
((freq_tuner_2 > 0 ? STV6120_CTRL10_PATH_ON : STV6120_CTRL10_PATH_OFF) << STV6120_CTRL10_PATHON_2_SHIFT) |
((freq_tuner_1 > 0 ? STV6120_CTRL10_PATH_ON : STV6120_CTRL10_PATH_OFF) << STV6120_CTRL10_PATHON_1_SHIFT) );
} else {
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL9,
(STV6120_CTRL9_RFSEL_RFB_IN << STV6120_CTRL9_RFSEL_1_SHIFT) |
(STV6120_CTRL9_RFSEL_RFC_IN << STV6120_CTRL9_RFSEL_2_SHIFT) |
STV6120_CTRL9_RESERVED );
/* decide on which LNAs are we going to enable */
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL10,
(( STV6120_CTRL10_LNA_OFF) << STV6120_CTRL10_LNADON_SHIFT) |
((freq_tuner_2 > 0 ? STV6120_CTRL10_LNA_ON : STV6120_CTRL10_LNA_OFF) << STV6120_CTRL10_LNACON_SHIFT) |
((freq_tuner_1 > 0 ? STV6120_CTRL10_LNA_ON : STV6120_CTRL10_LNA_OFF) << STV6120_CTRL10_LNABON_SHIFT) |
(( STV6120_CTRL10_LNA_OFF) << STV6120_CTRL10_LNAAON_SHIFT) |
((freq_tuner_2 > 0 ? STV6120_CTRL10_PATH_ON : STV6120_CTRL10_PATH_OFF) << STV6120_CTRL10_PATHON_2_SHIFT) |
((freq_tuner_1 > 0 ? STV6120_CTRL10_PATH_ON : STV6120_CTRL10_PATH_OFF) << STV6120_CTRL10_PATHON_1_SHIFT) );
}
/* Configure path 2 */
if (freq_tuner_2>0) { /* we are go on tuner 2 so turn it on */
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL11,
(STV6120_CTRL2_DCLOOPOFF_ENABLE << STV6120_CTRL2_DCLOOPOFF_SHIFT) |
(STV6120_CTRL2_SDOFF_OFF << STV6120_CTRL2_SDOFF_SHIFT) |
(STV6120_CTRL2_SYN_ON << STV6120_CTRL2_SYN_SHIFT) |
(STV6120_CTRL2_REFOUTSEL_1_25V << STV6120_CTRL2_REFOUTSEL_SHIFT) |
(STV6120_CTRL2_BBGAIN_6DB << STV6120_CTRL2_BBGAIN_SHIFT) );
/* CTRL12, 13, 14, 15 are PLL for tuner 2 */
if (err==ERROR_NONE) {
ctrl16 = (STV6120_CTRL7_RCCLKOFF_ENABLE << STV6120_CTRL7_RCCLKOFF_SHIFT) |
(STV6120_CTRL7_CF_5MHZ << STV6120_CTRL7_CF_SHIFT) ;
err=stv6120_write_reg(STV6120_CTRL16, ctrl16);
}
} else { /* we are not going to use path 1 so shut it down */
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL11,
(STV6120_CTRL2_DCLOOPOFF_DISABLE << STV6120_CTRL2_DCLOOPOFF_SHIFT) |
(STV6120_CTRL2_SDOFF_ON << STV6120_CTRL2_SDOFF_SHIFT) |
(STV6120_CTRL2_SYN_OFF << STV6120_CTRL2_SYN_SHIFT) |
(STV6120_CTRL2_REFOUTSEL_1_25V << STV6120_CTRL2_REFOUTSEL_SHIFT) |
(STV6120_CTRL2_BBGAIN_0DB << STV6120_CTRL2_BBGAIN_SHIFT) );
/* CTRL12, 13, 14, 15 are PLL for tuner 2 */
if (err==ERROR_NONE) {
ctrl16 = (STV6120_CTRL7_RCCLKOFF_DISABLE << STV6120_CTRL7_RCCLKOFF_SHIFT) |
(STV6120_CTRL7_CF_5MHZ << STV6120_CTRL7_CF_SHIFT) ;
err=stv6120_write_reg(STV6120_CTRL16, ctrl16);
}
}
/* there is no tcal field in CTRL17 but we still need to remember the state in case we are using tuner 2 later */
if (err==ERROR_NONE) {
ctrl17 = (STV6120_CTRL8_CALTIME_500US << STV6120_CTRL8_TCAL_SHIFT);
err=stv6120_write_reg(STV6120_CTRL17, ctrl8);
}
/* no need to touch the STAT2 status register for now */
/* CTRL18, CTRL19 are test regs so just write in the default */
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL18,STV6120_CTRL18_DEFAULT);
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL19,STV6120_CTRL19_DEFAULT);
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL20,
(STV6120_CTRL20_VCOAMP_NORMAL << STV6120_CTRL20_VCOAMP_SHIFT) |
STV6120_CTRL20_RESERVED );
/* CTRL21, CTRL22 are test regs so leave alone */
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL21,STV6120_CTRL21_DEFAULT);
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL22,STV6120_CTRL22_DEFAULT);
if (err==ERROR_NONE) err=stv6120_write_reg(STV6120_CTRL23,
(STV6120_CTRL20_VCOAMP_NORMAL << STV6120_CTRL20_VCOAMP_SHIFT) |
STV6120_CTRL20_RESERVED );
/* now we can calibrate the lowpass filters and setup the PLLs for each tuner required */
if ((err==ERROR_NONE) && (freq_tuner_1>0)) {
err=stv6120_cal_lowpass(TUNER_1);
if (err==ERROR_NONE) err=stv6120_set_freq(TUNER_1, freq_tuner_1);
}
if ((err==ERROR_NONE) && (freq_tuner_2>0)) {
err=stv6120_cal_lowpass(TUNER_2);
if (err==ERROR_NONE) err=stv6120_set_freq(TUNER_2, freq_tuner_2);
}
if (err!=ERROR_NONE) printf("ERROR: Failed to init Tuner %i, %i\n",freq_tuner_1, freq_tuner_2);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
void stv6120_print_settings() {
/* -------------------------------------------------------------------------------------------------- */
/* debug routine to print out all the regsiter values in the tuner */
/* -------------------------------------------------------------------------------------------------- */
uint8_t val;
printf("Tuner regs are:\n");
for(int i=0;i<0x19;i++) {
stv6120_read_reg(i,&val);
printf(" 0x%.2x = 0x%.2x\n",i,val);
}
}

46
longmynd/stv6120.h Executable file
View File

@ -0,0 +1,46 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: stv6120.h */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef STV6120_H
#define STV6120_H
#include <stdint.h>
#define TUNER_LOCKED 0
#define TUNER_NOT_LOCKED 1
#define TUNER_1 0
#define TUNER_2 1
#define STV6120_RDIV_THRESHOLD 27000
#define STV6120_P_THRESHOLD_1 299000
#define STV6120_P_THRESHOLD_2 596000
#define STV6120_P_THRESHOLD_3 1191000
#define STV6120_CAL_TIMEOUT 200
uint8_t stv6120_init(uint32_t, uint32_t, bool);
uint8_t stv6120_set_freq(uint8_t, uint32_t);
uint8_t stv6120_cal_lowpass(uint8_t);
void stv6120_print_settings();
#endif

185
longmynd/stv6120_regs.h Executable file
View File

@ -0,0 +1,185 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: stv6120_regs.h */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef STV6120_REGS_H
#define STV6120_REGS_H
/* common registers toboth tuners */
#define STV6120_CTRL1 0x00
#define STV6120_CTRL1_K_SHIFT 3
#define STV6120_CTRL1_K_MASK 0xf8
#define STV6120_CTRL1_RDIV_SHIFT 2
#define STV6120_CTRL1_OSHAPE_SHIFT 1
#define STV6120_CTRL1_OSHAPE_SINE 0
#define STV6120_CTRL1_OSHAPE_SQUARE 1
#define STV6120_CTRL1_MCLKDIV_SHIFT 0
#define STV6120_CTRL1_MCLKDIV_2 0
#define STV6120_CTRL1_MCLKDIV_4 1
#define STV6120_CTRL2 0x01
#define STV6120_CTRL2_DCLOOPOFF_SHIFT 7
#define STV6120_CTRL2_DCLOOPOFF_ENABLE 0
#define STV6120_CTRL2_DCLOOPOFF_DISABLE 1
#define STV6120_CTRL2_SDOFF_SHIFT 6
#define STV6120_CTRL2_SDOFF_OFF 0
#define STV6120_CTRL2_SDOFF_ON 1
#define STV6120_CTRL2_SYN_SHIFT 5
#define STV6120_CTRL2_SYN_OFF 0
#define STV6120_CTRL2_SYN_ON 1
#define STV6120_CTRL2_REFOUTSEL_SHIFT 4
#define STV6120_CTRL2_REFOUTSEL_VCC_DIV_2 0
#define STV6120_CTRL2_REFOUTSEL_1_25V 1
#define STV6120_CTRL2_BBGAIN_SHIFT 0
#define STV6120_CTRL2_BBGAIN_0DB 0x0
#define STV6120_CTRL2_BBGAIN_2DB 0x1
#define STV6120_CTRL2_BBGAIN_4DB 0x2
#define STV6120_CTRL2_BBGAIN_6DB 0x3
#define STV6120_CTRL2_BBGAIN_8DB 0x4
#define STV6120_CTRL2_BBGAIN_10DB 0x5
#define STV6120_CTRL2_BBGAIN_12DB 0x6
#define STV6120_CTRL2_BBGAIN_14DB 0x7
#define STV6120_CTRL2_BBGAIN_16DB 0x8
/* registers for tuner 1 */
#define STV6120_CTRL3 0x02
#define STV6120_CTRL4 0x03
#define STV6120_CTRL4_F_6_0_SHIFT 1
#define STV6120_CTRL4_F_6_0_MASK 0xfe
#define STV6120_CTRL4_N_9_SHIFT 0
#define STV6120_CTRL5 0x04
#define STV6120_CTRL6 0x05
#define STV6120_CTRL6_ICP_SHIFT 4
#define STV6120_CTRL6_ICP_MASK 0x70
#define STV6120_CTRL6_ICP_300UA 0
#define STV6120_CTRL6_ICP_325UA 1
#define STV6120_CTRL6_ICP_360UA 2
#define STV6120_CTRL6_ICP_400UA 3
/* note 400uA has 2 definitions */
#define STV6120_CTRL6_ICP_400UA_2 4
#define STV6120_CTRL6_ICP_450UA 5
#define STV6120_CTRL6_ICP_525UA 6
#define STV6120_CTRL6_ICP_600UA 7
#define STV6120_CTRL6_F_17_15_SHIFT 0
#define STV6120_CTRL6_F_17_15_MASK 0x07
#define STV6120_CTRL6_RESERVED 0x08
#define STV6120_CTRL7 0x06
#define STV6120_CTRL7_RCCLKOFF_SHIFT 7
#define STV6120_CTRL7_RCCLKOFF_ENABLE 0
#define STV6120_CTRL7_RCCLKOFF_DISABLE 1
#define STV6120_CTRL7_PDIV_SHIFT 5
#define STV6120_CTRL7_CF_SHIFT 0
#define STV6120_CTRL7_CF_5MHZ 0x00
#define STV6120_CTRL8 0x07
#define STV6120_CTRL8_TCAL_SHIFT 6
#define STV6120_CTRL8_TCAL_MASK 0xc0
#define STV6120_CTRL8_TCAL_DIV_1 0
#define STV6120_CTRL8_TCAL_DIV_2 1
#define STV6120_CTRL8_TCAL_DIV_4 2
#define STV6120_CTRL8_TCAL_DIV_8 3
#define STV6120_CTRL8_CALTIME_SHIFT 5
#define STV6120_CTRL8_CALTIME_500US 0
#define STV6120_CTRL8_CALTIME_1MS 1
#define STV6120_CTRL8_CFHF_SHIFT 0
#define STV6120_CTRL8_CFHF_MASK 0x1f
#define STV6120_STAT1 0x08
#define STV6120_STAT1_CALVCOSTRT_SHIFT 2
#define STV6120_STAT1_CALVCOSTRT_FINISHED 0
#define STV6120_STAT1_CALVCOSTRT_START 1
#define STV6120_STAT1_CALRCSTRT_SHIFT 1
#define STV6120_STAT1_CALRCSTRT_FINISHED 0
#define STV6120_STAT1_CALRCSTRT_START 1
#define STV6120_STAT1_LOCK_SHIFT 0
#define STV6120_STAT1_LOCK_NOT_IN_LOCK 0
#define STV6120_STAT1_LOCK_LOCKED 1
#define STV6120_STAT1_RESERVED 0x08
/* refsel fields for both tuners and path select */
#define STV6120_CTRL9 0x09
#define STV6120_CTRL9_RFSEL_2_SHIFT 2
#define STV6120_CTRL9_RFSEL_2_MASK 0x0c
#define STV6120_CTRL9_RFSEL_RFA_IN 0
#define STV6120_CTRL9_RFSEL_RFB_IN 1
#define STV6120_CTRL9_RFSEL_RFC_IN 2
#define STV6120_CTRL9_RFSEL_RFD_IN 3
#define STV6120_CTRL9_RFSEL_1_SHIFT 0
#define STV6120_CTRL9_RFSEL_1_MASK 0x03
#define STV6120_CTRL9 0x09
#define STV6120_CTRL9_RESERVED 0xf0
/* path select for both tuners */
#define STV6120_CTRL10 0x0a
#define STV6120_CTRL10_DEFAULT 0xbf
#define STV6120_CTRL10_ID_SHIFT 6
#define STV6120_CTRL10_ID_MASK 0xc0
#define STV6120_CTRL10_LNADON_SHIFT 5
#define STV6120_CTRL10_LNACON_SHIFT 4
#define STV6120_CTRL10_LNA_OFF 0
#define STV6120_CTRL10_LNA_ON 1
#define STV6120_CTRL10_LNABON_SHIFT 3
#define STV6120_CTRL10_LNAAON_SHIFT 2
#define STV6120_CTRL10_PATHON_2_SHIFT 1
#define STV6120_CTRL10_PATH_OFF 0
#define STV6120_CTRL10_PATH_ON 1
#define STV6120_CTRL10_PATHON_1_SHIFT 0
/* Regs CTRL11 to 18 and STAT2 are all Tuner2 regs */
#define STV6120_CTRL11 0x0b
#define STV6120_CTRL12 0x0c
#define STV6120_CTRL13 0x0d
#define STV6120_CTRL14 0x0e
#define STV6120_CTRL15 0x0f
#define STV6120_CTRL16 0x10
#define STV6120_CTRL17 0x11
#define STV6120_STAT2 0x12
/* test registers */
#define STV6120_CTRL18 0x13
#define STV6120_CTRL18_DEFAULT 0x00
#define STV6120_CTRL19 0x14
#define STV6120_CTRL19_DEFAULT 0x00
/* vco 1 amplfier and test */
#define STV6120_CTRL20 0x15
#define STV6120_CTRL20_VCOAMP_SHIFT 6
#define STV6120_CTRL20_VCOAMP_MASK 0xc0
#define STV6120_CTRL20_VCOAMP_AUTO 0
#define STV6120_CTRL20_VCOAMP_LOW 1
#define STV6120_CTRL20_VCOAMP_NORMAL 2
#define STV6120_CTRL20_VCOAMP_VERY_LOW 3
#define STV6120_CTRL20_RESERVED 0x0c
/* test registers */
#define STV6120_CTRL21 0x16
#define STV6120_CTRL21_DEFAULT 0x00
#define STV6120_CTRL22 0x17
#define STV6120_CTRL22_DEFAULT 0x00
/* vco 2 amplfier and test */
#define STV6120_CTRL23 0x18
#endif

32
longmynd/stv6120_utils.c Executable file
View File

@ -0,0 +1,32 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: stv6120_utils.c */
/* - an implementation of the Serit NIM controlling software for the MiniTiouner Hardware */
/* - abstracting out the tuner (STV6120) register read and write routines */
/* H.L. Lomond 2018,2019 */
/* -------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- INCLUDES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
#include "nim.h"
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- ROUTINES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv6120_read_reg(uint8_t reg, uint8_t *val) {
/* -------------------------------------------------------------------------------------------------- */
/* passes the register read through to the underlying register reading routines */
/* -------------------------------------------------------------------------------------------------- */
return nim_read_tuner(reg, val);
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stv6120_write_reg(uint8_t reg, uint8_t val) {
/* -------------------------------------------------------------------------------------------------- */
/* passes the register write through to the underlying register writing routines */
/* -------------------------------------------------------------------------------------------------- */
return nim_write_tuner(reg, val);
}

31
longmynd/stv6120_utils.h Executable file
View File

@ -0,0 +1,31 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: stv6120_utils.h */
/* - an implementation of the Serit NIM controlling software for the MiniTiouner Hardware */
/* - top level (main) and command line procesing */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef STV6120_UTILS_H
#define STV6120_UTILS_H
uint8_t stv6120_read_reg(uint8_t, uint8_t *);
uint8_t stv6120_write_reg(uint8_t, uint8_t);
#endif

165
longmynd/stvvglna.c Executable file
View File

@ -0,0 +1,165 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: stvvglna.c */
/* - an implementation of the Serit NIM controlling software for the MiniTiouner Hardware */
/* - the LNA support routines (STVVGLNA). The Serit NIM has 2 LNAs */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- INCLUDES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "stvvglna.h"
#include "nim.h"
#include "stvvglna_regs.h"
#include "stvvglna_utils.h"
#include "errors.h"
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- ROUTINES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
uint8_t stvvglna_read_agc(uint8_t input, uint8_t *gain, uint8_t *vgo) {
/* -------------------------------------------------------------------------------------------------- */
/* once we are running, this routine will read the gain and AGC for the LNA specified */
/* input: NIM_INPUT_TOP | NIM_INPUT_BOTTOM: which LNA is being worked on */
/* return: error code */
/* *gain: the gain read from the specified LNA */
/* *vgo: the vgo read from the specified LNA */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
uint8_t lna_addr;
uint16_t timeout=0;
uint8_t status;
/* first we decide which LNA to use */
if (input==NIM_INPUT_TOP) lna_addr=NIM_LNA_0_ADDR;
else lna_addr=NIM_LNA_1_ADDR;
/* in fully auto, we can read the gain sn SWLNAGAIN and VGO[4:0]. First we get the LNA to measure the */
/* variable part of the gain for us. Note, it is ok to write 0 to the other bitfields */
if (err==ERROR_NONE) err=stvvglna_write_reg(lna_addr, STVVGLNA_REG1,
STVVGLNA_REG1_GETAGC_START << STVVGLNA_REG1_GETAGC_SHIFT);
do {
err=stvvglna_read_reg(lna_addr, STVVGLNA_REG1, &status); /* read out the status */
timeout++;
if ((err==ERROR_NONE) && (timeout==STVVGLNA_AGC_TIMEOUT)) {
err=ERROR_LNA_AGC_TIMEOUT;
printf("Error: read AGC timeout\n");
}
}
while ((err==ERROR_NONE) && (((status >> STVVGLNA_REG1_GETAGC_SHIFT) & 1)!=STVVGLNA_REG1_GETAGC_FORCED));
stvvglna_read_reg(lna_addr, STVVGLNA_REG0, &status); /* read out the RFAGC high and low bits */
if (err==ERROR_NONE) err=stvvglna_read_reg(lna_addr, STVVGLNA_REG3, gain); /* read out the gain curves */
*gain=((*gain & STVVGLNA_REG3_SWLNAGAIN_MASK) >> STVVGLNA_REG3_SWLNAGAIN_SHIFT);
if (err==ERROR_NONE) err=stvvglna_read_reg(lna_addr, STVVGLNA_REG1, vgo); /* read out the Vagc value */
*vgo=((*vgo & STVVGLNA_REG1_VGO_MASK) >> STVVGLNA_REG1_VGO_SHIFT);
if (err!=ERROR_NONE) printf("ERROR: Failed LNA aquire AGC %i\n", input);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stvvglna_init(uint8_t input, uint8_t state, bool *lna_ok) {
/* -------------------------------------------------------------------------------------------------- */
/* this is the init routine for the LNA */
/* input: NIM_INPUT_TOP | NIM_INPUT_BOTTOM : specifies which LNA we are processing */
/* lna_ok: if we find a NIM with an LNA then we set this to true otherwise false */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
uint8_t val;
uint8_t lna_addr;
printf("Flow: LNA init %i\n",input);
/* first we decide which LNA to use */
if (input==NIM_INPUT_TOP) lna_addr=NIM_LNA_0_ADDR;
else lna_addr=NIM_LNA_1_ADDR;
/* now check to see if there is an LNA present */
err=stvvglna_read_reg(lna_addr, STVVGLNA_REG0, &val);
if (err!=ERROR_NONE) {
printf(" Status: found an older NIM with no LNA\n");
*lna_ok=false; /* tell caller that there is no LNA */
err=ERROR_NONE; /* we do not throw an error, just exit init */
} else {
/* otherwise, lna is there so we go on to us it */
printf(" Status: found new NIM with LNAs\n");
*lna_ok=true;
/* now check it has a good ID */
if ((val & STVVGLNA_REG0_IDENT_MASK) != STVVGLNA_REG0_IDENT_DEFAULT) {
printf("ERROR: failed to recognise LNA ID %i %i\n",input,val);
err=ERROR_LNA_ID;
}
if (state==STVVGLNA_ON) {
/* set up the defaults. We are going to use fully automatic mode */
if (err==ERROR_NONE) err=stvvglna_write_reg(lna_addr,STVVGLNA_REG0,
(STVVGLNA_REG0_AGC_TUPD_FAST << STVVGLNA_REG0_AGC_TUPD_SHIFT) |
(STVVGLNA_REG0_AGC_TLOCK_SLOW << STVVGLNA_REG0_AGC_TLOCK_SHIFT) );
if (err==ERROR_NONE) err=stvvglna_write_reg(lna_addr,STVVGLNA_REG1,
(STVVGLNA_REG1_LNAGC_PWD_POWER_ON << STVVGLNA_REG1_LNAGC_PWD_SHIFT) |
(STVVGLNA_REG1_GETOFF_ACQUISITION_MODE << STVVGLNA_REG1_GETOFF_SHIFT) |
(STVVGLNA_REG1_GETAGC_FORCED << STVVGLNA_REG1_GETAGC_SHIFT) );
if (err==ERROR_NONE) err=stvvglna_write_reg(lna_addr,STVVGLNA_REG2,
(STVVGLNA_REG2_PATH_ACTIVE << STVVGLNA_REG2_PATH2OFF_SHIFT) |
(STVVGLNA_REG2_RFAGC_PREF_N20DBM << STVVGLNA_REG2_RFAGC_PREF_SHIFT) |
(STVVGLNA_REG2_PATH_ACTIVE << STVVGLNA_REG2_PATH1OFF_SHIFT) |
(STVVGLNA_REG2_RFAGC_MODE_AUTO_TRACK << STVVGLNA_REG2_RFAGC_MODE_SHIFT) );
/* note that in REG3 the gain curves (SWLNAGAIN) are read only in fully automatic mod, so no need to write to them */
if (err==ERROR_NONE) err=stvvglna_write_reg(lna_addr,STVVGLNA_REG3,
(STVVGLNA_REG3_LCAL_17KHZ << STVVGLNA_REG3_LCAL_SHIFT) |
(STVVGLNA_REG3_RFAGC_UPDATE_FORCED << STVVGLNA_REG3_RFAGC_UPDATE_SHIFT) |
(STVVGLNA_REG3_RFAGC_CALSTART_FORCED << STVVGLNA_REG3_RFAGC_CALSTART_SHIFT) );
} else { /* state==STVVGLAN_OFF so disable it as we are not turning it on */
if (err==ERROR_NONE) err=stvvglna_write_reg(lna_addr,STVVGLNA_REG2,
(STVVGLNA_REG2_PATH_OFF << STVVGLNA_REG2_PATH2OFF_SHIFT) |
(STVVGLNA_REG2_PATH_OFF << STVVGLNA_REG2_PATH1OFF_SHIFT) );
}
}
if (err!=ERROR_NONE) printf("ERROR: LNA init %i\n",input);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
void stvvglna_read_regs(uint8_t addr) {
/* -------------------------------------------------------------------------------------------------- */
/* support routine for debug */
/* addr: the i2c address of the LNA to be dumped */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t val;
printf("LNA 0x%.2x regs:\n",addr);
for(int i=0; i<4; i++) {
stvvglna_read_reg(addr,i,&val);
printf(" 0x%.2x = 0x%.2x\n",i,val);
}
}

37
longmynd/stvvglna.h Executable file
View File

@ -0,0 +1,37 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: stvvglna.h */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef STVVGLNA_H
#define STVVGLNA_H
#include "stvvglna_regs.h"
#include <stdint.h>
#include <stdbool.h>
#define STVVGLNA_AGC_TIMEOUT 20
#define STVVGLNA_OFF 0
#define STVVGLNA_ON 1
uint8_t stvvglna_read_agc(uint8_t, uint8_t *, uint8_t*);
uint8_t stvvglna_init(uint8_t, uint8_t, bool*);
void stvvglna_read_regs (uint8_t);
#endif

112
longmynd/stvvglna_regs.h Executable file
View File

@ -0,0 +1,112 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: stvvglna_regs.h */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef STVVGLNA_REGS_H
#define STVVGLNA_REGS_H
#define STVVGLNA_I2C_ADDR0 0xc8
#define STVVGLNA_I2C_ADDR1 0xca
#define STVVGLNA_I2C_ADDR2 0xcc
#define STVVGLNA_I2C_ADDR3 0xce
#define STVVGLNA_REG0 0x00
#define STVVGLNA_REG0_IDENT_SHIFT 4
#define STVVGLNA_REG0_IDENT_MASK 0xf0
#define STVVGLNA_REG0_IDENT_DEFAULT 0x20
#define STVVGLNA_REG0_AGC_TUPD_SHIFT 3
#define STVVGLNA_REG0_AGC_TUPD_FAST 0
#define STVVGLNA_REG0_AGC_TUPD_SLOW 1
#define STVVGLNA_REG0_AGC_TLOCK_SHIFT 2
#define STVVGLNA_REG0_AGC_TLOCK_SLOW 0
#define STVVGLNA_REG0_AGC_TLOCK_FAST 1
#define STVVGLNA_REG0_RFAGC_HIGH_SHIFT 1
#define STVVGLNA_REG0_RFAGC_HIGH_NOT_HIGH 0
#define STVVGLNA_REG0_RFAGC_HIGH_IS_HIGH 1
#define STVVGLNA_REG0_RFAGC_LOW_SHIFT 0
#define STVVGLNA_REG0_RFAGC_LOW_NOT_LOW 0
#define STVVGLNA_REG0_RFAGC_LOW_IS_LOW 1
#define STVVGLNA_REG1 0x01
#define STVVGLNA_REG1_LNAGC_PWD_SHIFT 7
#define STVVGLNA_REG1_LNAGC_PWD_POWER_ON 0
#define STVVGLNA_REG1_LNAGC_PWD_POWER_OFF 1
#define STVVGLNA_REG1_GETOFF_SHIFT 6
#define STVVGLNA_REG1_GETOFF_ACQUISITION_MODE 0
#define STVVGLNA_REG1_GETOFF_VGO_4_0 1
#define STVVGLNA_REG1_GETAGC_SHIFT 5
#define STVVGLNA_REG1_GETAGC_FORCED 0
#define STVVGLNA_REG1_GETAGC_START 1
#define STVVGLNA_REG1_VGO_SHIFT 0
#define STVVGLNA_REG1_VGO_MASK 0x1f
#define STVVGLNA_REG2 0x02
#define STVVGLNA_REG2_PATH2OFF_SHIFT 7
#define STVVGLNA_REG2_PATH_ACTIVE 0
#define STVVGLNA_REG2_PATH_OFF 1
#define STVVGLNA_REG2_RFAGC_PREF_SHIFT 4
#define STVVGLNA_REG2_RFAGC_PREF_MASK 0x70
#define STVVGLNA_REG2_RFAGC_PREF_N25DBM 0x0
#define STVVGLNA_REG2_RFAGC_PREF_N24DBM 0x1
#define STVVGLNA_REG2_RFAGC_PREF_N23DBM 0x2
#define STVVGLNA_REG2_RFAGC_PREF_N22DBM 0x3
#define STVVGLNA_REG2_RFAGC_PREF_N21DBM 0x4
#define STVVGLNA_REG2_RFAGC_PREF_N20DBM 0x5
#define STVVGLNA_REG2_RFAGC_PREF_N19DBM 0x6
#define STVVGLNA_REG2_RFAGC_PREF_N18DBM 0x7
#define STVVGLNA_REG2_PATH1OFF_SHIFT 3
#define STVVGLNA_REG2_RFAGC_MODE_SHIFT 0
#define STVVGLNA_REG2_RFAGC_MODE_MASK 0x07
#define STVVGLNA_REG2_RFAGC_MODE_AUTO_TRACK 0x0
#define STVVGLNA_REG2_RFAGC_MODE_AUTO_REQUEST 0x1
#define STVVGLNA_REG2_RFAGC_MODE_MINIMAL_GAIN_INTERNAL 0x2
#define STVVGLNA_REG2_RFAGC_MODE_MAXIMAL_GAIN_INTERNAL 0x3
#define STVVGLNA_REG2_RFAGC_MODE_EXTERNAL_AGC_EXTERNAL 0x4
#define STVVGLNA_REG2_RFAGC_MODE_AGC_LOOP_EXTERNAL 0x5
#define STVVGLNA_REG2_RFAGC_MODE_MINIMAL_AGC_EXTERNAL 0x6
#define STVVGLNA_REG2_RFAGC_MODE_MAXIMAL_AGC_EXTERNAL 0x7
#define STVVGLNA_REG3 0x03
#define STVVGLNA_REG3_LCAL_SHIFT 4
#define STVVGLNA_REG3_LCAL_MASK 0x70
#define STVVGLNA_REG3_LCAL_68KHZ 0x0
#define STVVGLNA_REG3_LCAL_34KHZ 0x1
#define STVVGLNA_REG3_LCAL_17KHZ 0x2
#define STVVGLNA_REG3_LCAL_8_5KHZ 0x3
#define STVVGLNA_REG3_LCAL_4_2KHZ 0x4
#define STVVGLNA_REG3_LCAL_2_1KHZ 0x5
#define STVVGLNA_REG3_LCAL_1_0KHZ 0x6
#define STVVGLNA_REG3_LCAL_0_5KHZ 0x7
#define STVVGLNA_REG3_RFAGC_UPDATE_SHIFT 3
#define STVVGLNA_REG3_RFAGC_UPDATE_FORCED 0
#define STVVGLNA_REG3_RFAGC_UPDATE_START 1
#define STVVGLNA_REG3_RFAGC_CALSTART_SHIFT 2
#define STVVGLNA_REG3_RFAGC_CALSTART_FORCED 0
#define STVVGLNA_REG3_RFAGC_CALSTART_START 1
#define STVVGLNA_REG3_SWLNAGAIN_SHIFT 0
#define STVVGLNA_REG3_SWLNAGAIN_MASK 0x03
#define STVVGLNA_REG3_SWLNAGAIN_LOWEST 0x0
#define STVVGLNA_REG3_SWLNAGAIN_INTERMEDIATE_LOW 0x1
#define STVVGLNA_REG3_SWLNAGAIN_INTERMEDIATE_HIGH 0x2
#define STVVGLNA_REG3_SWLNAGAIN_HIGHEST 0x3
#endif

50
longmynd/stvvglna_utils.c Executable file
View File

@ -0,0 +1,50 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: stvvglna_utils.c */
/* - an implementation of the Serit NIM controlling software for the MiniTiouner Hardware */
/* - abstracting out the lna register read and write routines */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- INCLUDES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
#include <stdint.h>
#include "nim.h"
#include "stvvglna_regs.h"
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- ROUTINES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
uint8_t stvvglna_read_reg(uint8_t lna_addr, uint8_t reg, uint8_t *val) {
/* -------------------------------------------------------------------------------------------------- */
/* passes the register read through to the underlying register reading routines */
/* -------------------------------------------------------------------------------------------------- */
return nim_read_lna(lna_addr, reg, val);
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t stvvglna_write_reg(uint8_t lna_addr, uint8_t reg, uint8_t val) {
/* -------------------------------------------------------------------------------------------------- */
/* passes the register write through to the underlying register writing routines */
/* -------------------------------------------------------------------------------------------------- */
return nim_write_lna(lna_addr, reg, val);
}

30
longmynd/stvvglna_utils.h Executable file
View File

@ -0,0 +1,30 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: stvvglna_utils.h */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef STVVGLNA_UTILS_H
#define STVVGLNA_UTILS_H
#include <stdint.h>
uint8_t stvvglna_read_reg(uint8_t, uint8_t, uint8_t *);
uint8_t stvvglna_write_reg(uint8_t, uint8_t, uint8_t);
#endif

548
longmynd/ts.c Executable file
View File

@ -0,0 +1,548 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: ts.c */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "main.h"
#include "errors.h"
#include "udp.h"
#include "fifo.h"
#include "ftdi.h"
#include "ftdi_usb.h"
#include "ts.h"
#define TS_FRAME_SIZE 20*512 // 512 is base USB FTDI frame
#define MAX_PID 8192
#define TS_PACKET_SIZE 188
#define TS_HEADER_SYNC 0x47
#define TS_PID_PAT 0x0000
#define TS_PID_SDT 0x0011
#define TS_PID_NULL 0x1FFF
#define TS_TABLE_PAT 0x00
#define TS_TABLE_PMT 0x02
#define TS_TABLE_SDT 0x42
uint8_t *ts_buffer_ptr = NULL;
bool ts_buffer_waiting;
typedef struct {
uint8_t *buffer;
uint32_t length;
bool waiting;
pthread_mutex_t mutex;
pthread_cond_t signal;
} longmynd_ts_parse_buffer_t;
static longmynd_ts_parse_buffer_t longmynd_ts_parse_buffer = {
.buffer = NULL,
.length = 0,
.waiting = false,
.mutex = PTHREAD_MUTEX_INITIALIZER,
.signal = PTHREAD_COND_INITIALIZER
};
/* -------------------------------------------------------------------------------------------------- */
void *loop_ts(void *arg) {
/* -------------------------------------------------------------------------------------------------- */
/* Runs a loop to query the Minitiouner TS endpoint, and output it to the requested interface */
/* -------------------------------------------------------------------------------------------------- */
thread_vars_t *thread_vars=(thread_vars_t *)arg;
uint8_t *err = &thread_vars->thread_err;
longmynd_config_t *config = thread_vars->config;
uint8_t *buffer;
uint16_t len=0;
uint8_t (*ts_write)(uint8_t*,uint32_t);
*err=ERROR_NONE;
buffer = malloc(TS_FRAME_SIZE);
if(buffer == NULL)
{
*err=ERROR_TS_BUFFER_MALLOC;
}
if(thread_vars->config->ts_use_ip) {
*err=udp_ts_init(thread_vars->config->ts_ip_addr, thread_vars->config->ts_ip_port);
ts_write = udp_ts_write;
} else {
*err=fifo_ts_init(thread_vars->config->ts_fifo_path);
ts_write = fifo_ts_write;
}
while(*err == ERROR_NONE && *thread_vars->main_err_ptr == ERROR_NONE){
/* If reset flag is active (eg. just started or changed station), then clear out the ts buffer */
if(config->ts_reset) {
do {
if (*err==ERROR_NONE) *err=ftdi_usb_ts_read(buffer, &len, TS_FRAME_SIZE);
} while (*err==ERROR_NONE && len>2);
config->ts_reset = false;
}
*err=ftdi_usb_ts_read(buffer, &len, TS_FRAME_SIZE);
/* if there is ts data then we send it out to the required output. But, we have to lose the first 2 bytes */
/* that are the usual FTDI 2 byte response and not part of the TS */
if ((*err==ERROR_NONE) && (len>2)) {
ts_write(&buffer[2],len-2);
if(longmynd_ts_parse_buffer.waiting && longmynd_ts_parse_buffer.buffer != NULL)
{
pthread_mutex_lock(&longmynd_ts_parse_buffer.mutex);
memcpy(longmynd_ts_parse_buffer.buffer, &buffer[2],len-2);
longmynd_ts_parse_buffer.length = len-2;
pthread_cond_signal(&longmynd_ts_parse_buffer.signal);
longmynd_ts_parse_buffer.waiting = false;
pthread_mutex_unlock(&longmynd_ts_parse_buffer.mutex);
}
}
}
free(buffer);
return NULL;
}
static const uint32_t crc32_mpeg2_table[256];
static uint32_t crc32_mpeg2(uint8_t *data_ptr, size_t length)
{
uint32_t crc;
crc = 0xFFFFFFFF;
while (length--)
{
crc = (crc << 8) ^ crc32_mpeg2_table[((crc >> 24) ^ *data_ptr++) & 0xFF];
}
return crc;
}
/* -------------------------------------------------------------------------------------------------- */
void *loop_ts_parse(void *arg) {
/* -------------------------------------------------------------------------------------------------- */
/* Runs a loop to parse the MPEG-TS */
/* -------------------------------------------------------------------------------------------------- */
thread_vars_t *thread_vars=(thread_vars_t *)arg;
uint8_t *err = &thread_vars->thread_err;
*err=ERROR_NONE;
//longmynd_config_t *config = thread_vars->config;
longmynd_status_t *status = thread_vars->status;
/* TS Processing Vars */
uint8_t *ts_buffer;
uint32_t ts_buffer_length;
uint8_t *ts_packet_ptr;
uint32_t ts_buffer_length_remaining;
/* TS Stats Vars */
uint32_t ts_packet_total_count;
uint32_t ts_packet_null_count;
/* Generic TS */
uint32_t ts_pid;
uint32_t ts_adaption_field_flag;
uint32_t ts_adaption_field_length;
uint32_t ts_payload_content_offset;
uint32_t ts_payload_content_length;
uint8_t *ts_payload_ptr;
uint32_t ts_payload_section_length;
uint32_t ts_payload_crc;
uint32_t ts_payload_crc_c;
/* PAT */
//uint32_t ts_pat_programs_count;
//uint32_t ts_pat_program_id;
//uint32_t ts_pat_program_pid;
/* PMT */
//uint32_t ts_pmt_pcr_pid;
uint32_t ts_pmt_program_info_length;
uint8_t *ts_pmt_es_ptr;
uint32_t ts_pmt_es_type;
uint32_t ts_pmt_es_pid;
uint32_t ts_pmt_es_info_length;
uint32_t ts_pmt_offset;
uint32_t ts_pmt_index;
/* SDT */
uint8_t *ts_packet_sdt_table_ptr;
//uint32_t service_id;
uint8_t *ts_packet_sdt_descriptor_ptr;
//uint32_t descriptor_tag;
//uint32_t descriptor_length;
uint32_t service_provider_name_length;
uint32_t service_name_length;
ts_buffer = malloc(TS_FRAME_SIZE);
if(ts_buffer == NULL)
{
*err=ERROR_TS_BUFFER_MALLOC;
}
longmynd_ts_parse_buffer.buffer = ts_buffer;
struct timespec ts;
/* Set pthread timer on .signal to use monotonic clock */
pthread_condattr_t attr;
pthread_condattr_init(&attr);
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
pthread_cond_init (&longmynd_ts_parse_buffer.signal, &attr);
pthread_condattr_destroy(&attr);
while(*err == ERROR_NONE && *thread_vars->main_err_ptr == ERROR_NONE)
{
//ts_pat_program_pid = 0x00; // Updated by PAT parse
/* Reset Stats */
ts_packet_total_count = 0;
ts_packet_null_count = 0;
pthread_mutex_lock(&longmynd_ts_parse_buffer.mutex);
longmynd_ts_parse_buffer.waiting = true;
while(longmynd_ts_parse_buffer.waiting && *thread_vars->main_err_ptr == ERROR_NONE)
{
/* Set timer for 100ms */
clock_gettime(CLOCK_MONOTONIC, &ts);
ts.tv_nsec += 100 * 1000000;
pthread_cond_timedwait(&longmynd_ts_parse_buffer.signal, &longmynd_ts_parse_buffer.mutex, &ts);
}
pthread_mutex_unlock(&longmynd_ts_parse_buffer.mutex);
ts_packet_ptr = &ts_buffer[0];
ts_buffer_length = longmynd_ts_parse_buffer.length;
ts_buffer_length_remaining = ts_buffer_length;
while(ts_packet_ptr != NULL)
{
if(ts_packet_ptr[0] != TS_HEADER_SYNC)
{
/* Align input to the TS sync byte */
ts_buffer_length_remaining = ts_buffer_length - (&ts_packet_ptr[0] - &ts_buffer[0]);
if(ts_buffer_length_remaining <= TS_PACKET_SIZE)
{
/* Nothing more in buffer, force exit */
ts_packet_ptr = NULL;
continue;
}
ts_packet_ptr = memchr(ts_packet_ptr, TS_HEADER_SYNC, ts_buffer_length_remaining - TS_PACKET_SIZE);
if(ts_packet_ptr == NULL)
{
continue;
}
}
ts_pid = (uint32_t)((ts_packet_ptr[1] & 0x1F) << 8) | (uint32_t)ts_packet_ptr[2];
ts_packet_total_count++;
ts_payload_content_offset = 4;
ts_adaption_field_flag = (uint32_t)(ts_packet_ptr[3] & 0x20) >> 5;
if(ts_adaption_field_flag > 0)
{
ts_adaption_field_length = ts_packet_ptr[4];
if(ts_adaption_field_length == 0
|| ts_adaption_field_length > 183)
{
/* Length invalid, packet is likely invalid */
ts_packet_ptr++;
continue;
}
ts_payload_content_offset += ts_adaption_field_length;
}
/* NULL/padding packets */
if(ts_pid == TS_PID_NULL)
{
ts_packet_null_count++;
ts_packet_ptr++;
continue;
}
#if 0
if(ts_pid == TS_PID_PAT)
{
ts_payload_ptr = (uint8_t *)&ts_packet_ptr[ts_payload_content_offset + 1 + ts_packet_ptr[ts_payload_content_offset]];
if(ts_payload_ptr[0] != TS_TABLE_PAT)
{
ts_packet_ptr++;
continue;
}
ts_payload_section_length = ((uint32_t)(ts_payload_ptr[1] & 0x0F) << 8) | (uint32_t)ts_payload_ptr[2];
if(ts_payload_section_length < 1)
{
ts_packet_ptr++;
continue;
}
ts_payload_crc = ((uint32_t)ts_payload_ptr[ts_payload_section_length-1] << 24) | ((uint32_t)ts_payload_ptr[ts_payload_section_length] << 16)
| ((uint32_t)ts_payload_ptr[ts_payload_section_length+1] << 8) | (uint32_t)ts_payload_ptr[ts_payload_section_length+2];
ts_payload_crc_c = crc32_mpeg2(ts_payload_ptr, (ts_payload_section_length-1));
if(ts_payload_crc != ts_payload_crc_c)
{
/* CRC Fail */
ts_packet_ptr++;
continue;
}
ts_pat_programs_count = (ts_payload_section_length - 9) / 4;
/* For now, only read the first programme */
/* TODO: Read all programs here to enable PID parsing of PMT */
if(ts_pat_programs_count > 0)
{
//ts_pat_program_id = ((uint32_t)ts_payload_ptr[8] << 8) | (uint32_t)ts_payload_ptr[9];
ts_pat_program_pid = ((uint32_t)(ts_payload_ptr[10] & 0x1F) << 8) | (uint32_t)ts_payload_ptr[11];
//printf(" - PAT Program PID: %"PRIu32"\n", ts_pat_program_pid);
}
ts_packet_ptr++;
continue;
}
#endif
if(ts_pid == TS_PID_SDT)
{
ts_payload_content_length = 0;
ts_payload_ptr = (uint8_t *)&ts_packet_ptr[ts_payload_content_offset + 1 + ts_packet_ptr[ts_payload_content_offset]];
if(ts_payload_ptr[0] != TS_TABLE_SDT)
{
ts_packet_ptr++;
continue;
}
ts_payload_section_length = ((uint32_t)(ts_payload_ptr[1] & 0x0F) << 8) | (uint32_t)ts_payload_ptr[2];
//printf(" - SDT Section Length: %"PRIu32"\n", ts_payload_section_length);
if(ts_payload_section_length < 1)
{
ts_packet_ptr++;
continue;
}
ts_payload_crc = ((uint32_t)ts_payload_ptr[ts_payload_section_length-1] << 24) | ((uint32_t)ts_payload_ptr[ts_payload_section_length] << 16)
| ((uint32_t)ts_payload_ptr[ts_payload_section_length+1] << 8) | (uint32_t)ts_payload_ptr[ts_payload_section_length+2];
ts_payload_crc_c = crc32_mpeg2(ts_payload_ptr, (ts_payload_section_length-1));
if(ts_payload_crc != ts_payload_crc_c)
{
/* CRC Fail */
ts_packet_ptr++;
continue;
}
/* Per service */
ts_packet_sdt_table_ptr = &ts_payload_ptr[11];
ts_payload_content_length += 11;
//service_id = ((uint32_t)ts_packet_sdt_table_ptr[0] << 8) | (uint32_t)ts_packet_sdt_table_ptr[1];
//printf(" - - Service ID: %"PRIu32"\n", service_id);
/* Per descriptor */
ts_packet_sdt_descriptor_ptr = &ts_packet_sdt_table_ptr[5];
ts_payload_content_length += 5;
//descriptor_tag = (uint32_t)ts_packet_sdt_descriptor_ptr[0];
//printf(" - - - Descriptor Tag: %"PRIu32"\n", descriptor_tag);
//descriptor_length = (uint32_t)ts_packet_sdt_descriptor_ptr[1];
//printf(" - - - Descriptor Length: %"PRIu32"\n", descriptor_length);
//uint32_t service_type = (uint32_t)ts_packet_sdt_descriptor_ptr[2];
//printf(" - - - Service Type %"PRIu32"\n", service_type);
ts_payload_content_length += 3;
service_provider_name_length = (uint32_t)ts_packet_sdt_descriptor_ptr[3];
//printf(" - - - Service Provider Name Length %"PRIu32"\n", service_provider_name_length);
//printf(" - - - Service Provider Name: %.*s\n", service_provider_name_length, &ts_packet_sdt_descriptor_ptr[4]);
service_name_length = (uint32_t)ts_packet_sdt_descriptor_ptr[3+1+service_provider_name_length];
//printf(" - - - Service Name Length %"PRIu32"\n", service_name_length);
//printf(" - - - Service Name: %.*s\n", service_name_length, &ts_packet_sdt_descriptor_ptr[4+1+service_provider_name_length]);
pthread_mutex_lock(&status->mutex);
memcpy(status->service_name, &ts_packet_sdt_descriptor_ptr[4+1+service_provider_name_length], service_name_length);
status->service_name[service_name_length] = '\0';
memcpy(status->service_provider_name, &ts_packet_sdt_descriptor_ptr[4], service_provider_name_length);
status->service_provider_name[service_provider_name_length] = '\0';
pthread_mutex_unlock(&status->mutex);
ts_payload_content_length += 1;
ts_payload_content_length += service_provider_name_length;
ts_payload_content_length += 1;
ts_payload_content_length += service_name_length;
ts_packet_ptr++;
continue;
}
else // if(ts_pat_program_pid !=0x00 && ts_pid == ts_pat_program_pid) /* PMT, once found in PAT */
{
ts_payload_ptr = (uint8_t *)&ts_packet_ptr[ts_payload_content_offset + 1 + ts_packet_ptr[ts_payload_content_offset]];
/* We're not filtering by PID here yet, so we rely on filtering by table ID */
if(ts_payload_ptr[0] != TS_TABLE_PMT)
{
ts_packet_ptr++;
continue;
}
ts_payload_section_length = ((uint32_t)(ts_payload_ptr[1] & 0x0F) << 8) | (uint32_t)ts_payload_ptr[2];
if(ts_payload_section_length < 1)
{
ts_packet_ptr++;
continue;
}
ts_payload_crc = ((uint32_t)ts_payload_ptr[ts_payload_section_length-1] << 24) | ((uint32_t)ts_payload_ptr[ts_payload_section_length] << 16)
| ((uint32_t)ts_payload_ptr[ts_payload_section_length+1] << 8) | (uint32_t)ts_payload_ptr[ts_payload_section_length+2];
ts_payload_crc_c = crc32_mpeg2(ts_payload_ptr, (ts_payload_section_length-1));
if(ts_payload_crc != ts_payload_crc_c)
{
/* CRC Fail */
ts_packet_ptr++;
continue;
}
//ts_pmt_pcr_pid = ((uint32_t)(ts_payload_ptr[8] & 0x1F) << 8) | (uint32_t)ts_payload_ptr[9];
//printf(" - PMT: PCR PID: %"PRIu32"\n", ts_pmt_pcr_pid);
ts_pmt_program_info_length = ((uint32_t)(ts_payload_ptr[10] & 0x0F) << 8) | (uint32_t)ts_payload_ptr[11];
//if(ts_pmt_program_info_length > 0)
//{
// printf(" - PMT Program Info: %.*s\n", ts_pmt_program_info_length, &ts_payload_ptr[12]);
//}
ts_pmt_offset = 0;
ts_pmt_index = 0;
while((12+1+ts_pmt_program_info_length+ts_pmt_offset) < ts_payload_section_length)
{
ts_pmt_es_ptr = &ts_payload_ptr[12 + ts_pmt_program_info_length + ts_pmt_offset];
/* For each elementary PID */
ts_pmt_es_type = (uint32_t)ts_pmt_es_ptr[0];
ts_pmt_es_pid = ((uint32_t)(ts_pmt_es_ptr[1] & 0x1F) << 8) | (uint32_t)ts_pmt_es_ptr[2];
ts_pmt_es_info_length = ((uint32_t)(ts_pmt_es_ptr[3] & 0x0F) << 8) | (uint32_t)ts_pmt_es_ptr[4];
//if(ts_pmt_es_info_length > 0)
//{
//printf(" - - PMT ES Info: %.*s\n", ts_pmt_es_info_length, &ts_pmt_es_ptr[5]);
//}
pthread_mutex_lock(&status->mutex);
status->ts_elementary_streams[ts_pmt_index][0] = ts_pmt_es_pid;
status->ts_elementary_streams[ts_pmt_index][1] = ts_pmt_es_type;
pthread_mutex_unlock(&status->mutex);
ts_pmt_offset += (5 + ts_pmt_es_info_length);
ts_pmt_index++;
}
ts_packet_ptr++;
continue;
}
ts_packet_ptr++;
}
pthread_mutex_lock(&status->mutex);
if(ts_packet_total_count > 0)
{
status->ts_null_percentage = (100 * ts_packet_null_count) / ts_packet_total_count;
}
/* Trigger pthread signal */
pthread_cond_signal(&status->signal);
pthread_mutex_unlock(&status->mutex);
}
free(ts_buffer);
return NULL;
}
static const uint32_t crc32_mpeg2_table[256] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};

29
longmynd/ts.h Executable file
View File

@ -0,0 +1,29 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: ts.h */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef TS_H
#define TS_H
void *loop_ts(void *arg);
void *loop_ts_parse(void *arg);
#endif

198
longmynd/udp.c Executable file
View File

@ -0,0 +1,198 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: udp.c */
/* - an implementation of the Serit NIM controlling software for the MiniTiouner Hardware */
/* - linux udp handler to send the TS data to a remote display */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- INCLUDES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "errors.h"
#include "udp.h"
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- GLOBALS ------------------------------------------------------------------------ */
/* -------------------------------------------------------------------------------------------------- */
struct sockaddr_in servaddr_status;
struct sockaddr_in servaddr_ts;
int sockfd_status;
int sockfd_ts;
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- DEFINES ------------------------------------------------------------------------ */
/* -------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- ROUTINES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
uint8_t udp_ts_write(uint8_t *buffer, uint32_t len) {
/* -------------------------------------------------------------------------------------------------- */
/* takes a buffer and writes out the contents to udp socket */
/* *buffer: the buffer that contains the data to be sent */
/* len: the length (number of bytes) of data to be sent */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
int32_t remaining_len; /* note it is signed so can go negative */
uint32_t write_size;
remaining_len=len;
/* we need to loop round sending 510 byte chunks so that we can skip the 2 extra bytes put in by */
/* the FTDI chip every 512 bytes of USB message */
while (remaining_len>0) {
if (remaining_len>510) {
/* calculate where to start in the buffer and how many bytes to send */
write_size=510;
sendto(sockfd_ts, &buffer[len-remaining_len], write_size, 0,
(const struct sockaddr *) &servaddr_ts, sizeof(struct sockaddr));
/* note we skip over the 2 bytes inserted by the FTDI */
remaining_len-=512;
} else {
write_size=remaining_len;
sendto(sockfd_ts, &buffer[len-remaining_len], write_size, 0,
(const struct sockaddr *) &servaddr_ts, sizeof(struct sockaddr));
remaining_len-=write_size; /* should be 0 if all went well */
}
}
/* if someting went bad with our calcs, remaining will not be 0 */
if ((err==ERROR_NONE) && (remaining_len!=0)) {
printf("ERROR: UDP socket write incorrect number of bytes\n");
err=ERROR_UDP_WRITE;
}
if (err!=ERROR_NONE) printf("ERROR: UDP socket ts write\n");
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t udp_status_write(uint8_t message, uint32_t data) {
/* -------------------------------------------------------------------------------------------------- */
/* takes a buffer and writes out the contents to udp socket */
/* *buffer: the buffer that contains the data to be sent */
/* len: the length (number of bytes) of data to be sent */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
char status_message[30];
sprintf(status_message, "$%i,%i\n", message, data);
sendto(sockfd_status, status_message, strlen(status_message), 0, (const struct sockaddr *)&servaddr_status, sizeof(struct sockaddr));
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t udp_status_string_write(uint8_t message, char *data) {
/* -------------------------------------------------------------------------------------------------- */
/* takes a buffer and writes out the contents to udp socket */
/* *buffer: the buffer that contains the data to be sent */
/* len: the length (number of bytes) of data to be sent */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
char status_message[5+128];
sprintf(status_message, "$%i,%s\n", message, data);
sendto(sockfd_status, status_message, strlen(status_message), 0, (const struct sockaddr *)&servaddr_status, sizeof(struct sockaddr));
return err;
}
/* -------------------------------------------------------------------------------------------------- */
static uint8_t udp_init(struct sockaddr_in *servaddr_ptr, int *sockfd_ptr, char *udp_ip, int udp_port) {
/* -------------------------------------------------------------------------------------------------- */
/* initialises the udp socket */
/* udp_ip: the ip address (as a string) of the socket to open */
/* udp_port: the UDP port to be opened at the given IP address */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
printf("Flow: UDP Init\n");
/* Creat the socket for IPv4 and UDP */
if ((*sockfd_ptr = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
printf("ERROR: socket creation failed\n");
err=ERROR_UDP_SOCKET_OPEN;
} else {
/* setup all the destination fields */
memset(servaddr_ptr, 0, sizeof(struct sockaddr_in));
servaddr_ptr->sin_family = AF_INET;
servaddr_ptr->sin_port = htons(udp_port);
servaddr_ptr->sin_addr.s_addr = inet_addr(udp_ip); // INADDR_ANY;
}
if (err!=ERROR_NONE) printf("ERROR: UDP init\n");
return err;
}
uint8_t udp_status_init(char *udp_ip, int udp_port) {
return udp_init(&servaddr_status, &sockfd_status, udp_ip, udp_port);
}
uint8_t udp_ts_init(char *udp_ip, int udp_port) {
return udp_init(&servaddr_ts, &sockfd_ts, udp_ip, udp_port);
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t udp_close(void) {
/* -------------------------------------------------------------------------------------------------- */
/* closes the udp socket */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
int ret;
printf("Flow: UDP Close\n");
ret=close(sockfd_ts);
if (ret!=0) {
err=ERROR_UDP_CLOSE;
printf("ERROR: TS UDP close\n");
}
ret=close(sockfd_status);
if (ret!=0) {
err=ERROR_UDP_CLOSE;
printf("ERROR: Status UDP close\n");
}
return err;
}

37
longmynd/udp.h Executable file
View File

@ -0,0 +1,37 @@
/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: udp.h */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef UDP_H
#define UDP_H
#include <stdint.h>
uint8_t udp_status_init(char *udp_ip, int udp_port);
uint8_t udp_ts_init(char *udp_ip, int udp_port);
uint8_t udp_status_write(uint8_t message, uint32_t data);
uint8_t udp_status_string_write(uint8_t message, char *data);
uint8_t udp_ts_write(uint8_t *buffer, uint32_t len);
uint8_t udp_close(void);
#endif

359
main.py Executable file
View File

@ -0,0 +1,359 @@
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
# (c) Xavier 2022
# pour débugger… en ligne de commande !-)
# import pdb; pdb.set_trace()
# DATV
# for Python3
from tkinter import *
import tkinter.messagebox
import tkinter.filedialog
import tkinter as tk
import tkinter.ttk as ttk
import os
import time
import json
import shutil
import vlc
import sys
import usb.core
import usb.util
from PIL import Image, ImageTk
from ffpyplayer.player import MediaPlayer
import threading
import subprocess
import socket
import cv2
try:
import configparser as configparser
except ImportError:
import ConfigParser as configparser
# Var
fileconfig = "config.json"
fileflux = "flux.datv"
DEVS = ['/dev/ttyUSB0', '/dev/ttyUSB1', '/dev/ttyUSB2']
F1 = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
F2 = ['0', '250', '500', '750']
SR = ['66', '125', '250', '333', '500', '1000', '1500']
class DATV(tk.Frame):
def __init__(self, root):
super().__init__(root)
self.champs = {
'IP': tk.StringVar(),
'PORT': tk.StringVar(),
'DEV': tk.StringVar(),
'FREQ': tk.StringVar(),
'F1': tk.StringVar(),
'F2': tk.StringVar(),
'SR': tk.StringVar(),
}
self.root = root
self._create_bar()
self.pack()
self.RunVIDEO()
def lireparam(self):
with open(fileconfig) as mon_fichier:
config = json.load(mon_fichier)
return config
def ecrireparam(self):
self.StopStream()
jsonString = '{'
cpt = 0
for v, k in self.champs.items():
jsonString += '"' + v + '": "' + k.get() + '"'
if cpt < 6:
jsonString += ",\n"
cpt = cpt + 1
jsonString += '}'
file = open(fileconfig, "w")
file.write(jsonString)
file.close()
self.Clean()
self.RunVIDEO()
@staticmethod
def firstparam(fileconfig):
jsonString = '{"IP": "127.0.0.1", "PORT": "1234", "DEV": "/dev/ttyUSB0", "FREQ": "10491500", "SR": "1500"}'
file = open(fileconfig, "w")
file.write(jsonString)
file.close()
def StopStream(self):
#self.Clean()
#self.photo = PhotoImage(file='img/btnjaune.gif')
#espace_image = Canvas(self, width=120, height=120, bg='red')
#espace_image.grid(row=3, columnspan=2, column=0, padx=10, pady=10)
#espace_image.create_image(60, 60, image=self.photo)
os.system('bash longmynd/stopsrv.sh')
def RunStream(self):
self.Clean()
dev = usb.core.find(idVendor=0x0403, idProduct=0x6010)
if dev is not None:
data = self.lireparam()
freq = data['FREQ'] + data['F1'] + data['F2']
print('IP=' + data['IP'] + ':' + data['PORT'] + ' freq=' + freq + ' SR=' + data['SR'])
os.system('bash ./longmynd/gosrv.sh ' + data['IP'] + ' ' + data['PORT'] + ' ' + freq + ' ' + data['SR'] + '> flux.datv')
time.sleep(2)
return
else:
self.photo = PhotoImage(file='img/btnrouge.gif')
espace_image = Canvas(self, width=120, height=120, bg='red')
espace_image.grid(row=3, columnspan=2, column=0, padx=10, pady=10)
espace_image.create_image(60, 60, image=self.photo)
return
def RunVIDEO(self):
#Stream
self.RunStream()
# Créer un Canvas pour afficher la vidéo
self.Canvas_video = tk.Canvas(self, bg='black', width=800, height=600)
self.Canvas_video.grid(row=0, column=0, padx=1, pady=1)
#self.photo = PhotoImage(file='nosignal.png')
#self.Canvas_video.create_image(450, 300, image=self.photo)
# Afficher la vidéo UDP
self.RunVLC()
def RunVLC(self):
# Créer une instance VLC pour la vidéo
data = self.lireparam()
# Assurez-vous de gérer les erreurs de création de la fenêtre
try:
self.Instance = vlc.Instance('--no-xlib')
self.player = self.Instance.media_player_new()
m = self.Instance.media_new(str('udp://@'+data['IP']+':'+data['PORT'])) # Path, unicode
self.player.set_media(m)
# set the window id where to render VLC's video output
h = self.Canvas_video.winfo_id() # .winfo_visualid()?
self.player.set_xwindow(h) # fails on Windows
self.player.play() # == -1
except Exception as e:
print(f"Erreur de création de la sortie vidéo: {e}")
tk.messagebox.showerror("Erreur VLC", f"Erreur de création de la sortie vidéo: {e}")
def TestBeacon(self):
self.Clean
self.StopStream()
dev = usb.core.find(idVendor=0x0403, idProduct=0x6010)
if dev is not None:
self.photo = PhotoImage(file='img/btnvert.gif')
espace_image = Canvas(self, width=120, height=120, bg='green')
espace_image.grid(row=3, columnspan=2, column=0, padx=10, pady=10)
espace_image.create_image(60, 60, image=self.photo)
data = self.lireparam()
freq = "10491500"
symb = "1500"
os.system('bash ./longmynd/gosrv.sh ' + data['IP'] + ' ' + data['PORT'] + ' ' + freq + ' ' + symb + '> flux.datv')
time.sleep(5)
self.RunVIDEO()
return
else:
self.photo = PhotoImage(file='img/btnrouge.gif')
espace_image = Canvas(self, width=120, height=120, bg='red')
espace_image.grid(row=3, columnspan=2, column=0, padx=10, pady=10)
espace_image.create_image(60, 60, image=self.photo)
return
def Testconnexion(self):
self.Clean()
connected = False
dev = usb.core.find(idVendor=0x0403, idProduct=0x6010)
if dev is not None:
connected = True
if connected:
self.photo = PhotoImage(file='img/btnvert.gif')
espace_image = Canvas(self, width=120, height=120, bg='green')
espace_image.grid(row=3, columnspan=2, column=0, padx=10, pady=10)
espace_image.create_image(60, 60, image=self.photo)
else:
self.photo = PhotoImage(file='img/btnrouge.gif')
espace_image = Canvas(self, width=120, height=120, bg='red')
espace_image.grid(row=3, columnspan=2, column=0, padx=10, pady=10)
espace_image.create_image(60, 60, image=self.photo)
def Apropos(self):
tk.messagebox.showinfo("A propos", "Reception DATV via Linux...\n Dev F4IYT Xavier\n")
def Version(self):
tk.messagebox.showinfo("Version", "DATVLin \n(C) 2023 F4IYT Xavier\n2.00")
def Clean(self):
for c in self.winfo_children():
c.destroy()
self._create_bar()
def _create_bar(self):
logo = PhotoImage(file='img/datv_ico.png')
menubar = Menu(self)
self.icon = PhotoImage(file='./img/datv_ico.png')
menubar.add_cascade(label="Command", image=self.icon)
menufichier = Menu(menubar, tearoff=0)
menufichier.add_command(label="Ouvrir memoire", command=app.quit)
menufichier.add_command(label="Enregistrer memoire", command=app.quit)
menufichier.add_separator()
menufichier.add_command(label="Config", command=self._create_config)
menufichier.add_separator()
menufichier.add_command(label="Nettoyer Frame", command=self.Clean)
menufichier.add_command(label="Quitter", command=app.quit)
menubar.add_cascade(label="Fichier", menu=menufichier)
menudatv = Menu(menubar, tearoff=0)
menudatv.add_command(label="Test Connection", command=self.Testconnexion)
menudatv.add_command(label="Test Beacon", command=self.TestBeacon)
menudatv.add_command(label="Run Srv Stream", command=self.RunStream)
menudatv.add_command(label="Stop Srv Stream", command=self.StopStream)
menudatv.add_command(label="Config", command=self._create_config)
menudatv.add_separator()
menudatv.add_command(label="Run Video Stream", command=self.RunVIDEO)
menudatv.add_command(label="Status DATV Rx", command=run_troisieme_program)
menudatv.add_separator()
menubar.add_cascade(label="DATV", menu=menudatv)
menuaide = Menu(menubar, tearoff=0)
menuaide.add_command(label="A propos", command=self.Apropos)
menuaide.add_command(label="Version", command=self.Version)
menubar.add_cascade(label="Aide", menu=menuaide)
app.config(menu=menubar)
def _create_config(self):
self.Clean()
self.champs = {
'IP': tk.StringVar(),
'PORT': tk.StringVar(),
'DEV': tk.StringVar(),
'FREQ': tk.StringVar(),
'F1': tk.StringVar(),
'F2': tk.StringVar(),
'SR': tk.StringVar(),
}
data = self.lireparam()
print(data)
Label0 = Label(self, text='CONFIG SRV STREAM: ')
Label0.grid(column=0, row=0, sticky='w', pady=2)
Label2 = Label(self, text='IP : ')
Label2.grid(column=0, row=2, sticky='w')
Champ2 = Entry(self, textvariable=self.champs['IP'], width=16)
Champ2.focus_set()
Champ2.grid(column=1, row=2, sticky='sw', columnspan=3, padx=10)
Champ2.insert(0, data['IP'])
Label3 = Label(self, text='PORT : ')
Label3.grid(column=9, row=2, sticky='w')
Champ3 = Entry(self, textvariable=self.champs['PORT'], width=7)
Champ3.focus_set()
Champ3.grid(column=10, row=2, sticky='sw', columnspan=3, padx=10)
Champ3.insert(0, data['PORT'])
Label4 = Label(self, text='Dev : ')
Label4.grid(column=0, row=3, sticky='w')
combo = ttk.Combobox(self, values=DEVS,
textvariable=self.champs['DEV'], width=12)
combo['state'] = 'readonly'
combo.set(data['DEV'])
combo.grid(column=1, row=3, columnspan=3)
Label6 = Label(self, text='Fréquence : ')
Label6.grid(column=0, row=4, sticky='w')
Champ6 = Entry(self, textvariable=self.champs['FREQ'], width=4)
Champ6.focus_set()
Champ6.grid(column=1, row=4, sticky='sw', columnspan=3, padx=10)
Champ6.insert(0, data['FREQ'])
combo5 = ttk.Combobox(self, values=F1,
textvariable=self.champs['F1'], width=2)
combo5['state'] = 'readonly'
combo5.set(data['F1'])
combo5.grid(column=3, row=4)
combo6 = ttk.Combobox(self, values=F2,
textvariable=self.champs['F2'], width=3)
combo6['state'] = 'readonly'
combo6.set(data['F2'])
combo6.grid(column=4, row=4)
Label7 = Label(self, text='MHz')
Label7.grid(column=6, row=4, sticky='w')
Label4 = Label(self, text='Symbol : ')
Label4.grid(column=9, row=4, sticky='w')
combo4 = ttk.Combobox(self, values=SR,
textvariable=self.champs['SR'], width=7)
combo4['state'] = 'readonly'
combo4.set(data['SR'])
combo4.grid(column=10, row=4, columnspan=2)
button = tk.Button(self, text="Valider", command=self.ecrireparam)
button.grid(column=0, row=5)
button = tk.Button(self, text="Fermer", command=self.Clean)
button.grid(column=2, row=5)
# Fonction exécutant le second programme
def run_second_program():
# Commande pour exécuter le second programme, remplacez-la par la commande appropriée
command = ["python3", "waterfall.py"]
proc = subprocess.run(command)
# Fonction exécutant le troisieme programme
def run_troisieme_program():
# Commande pour exécuter le second programme, remplacez-la par la commande appropriée
title = "DATV Status"
command = ["gnome-terminal", "--title", title, "--", "./status", ""]
proc = subprocess.run(command)
if __name__ == '__main__':
if not os.path.exists(fileconfig):
firstparam(fileconfig)
print("START INIT")
app = Tk()
app.title("DATV Srv STREAM")
app.geometry("1024x800")
DATV(app)
# Création du thread pour exécuter le second programme
thread = threading.Thread(target=run_second_program)
# Démarrage du thread
thread.start()
app.mainloop()
os.system('bash longmynd/stopsrv.sh')
#kill(proc.pid)

BIN
manuel.pdf Normal file

Binary file not shown.

7
minitiouner.rules Normal file
View File

@ -0,0 +1,7 @@
# Install with `sudo cp minitiouner.rules /etc/udev/rules.d/`
# then unplug and replug the minitiouner
# Minitiouner uses FT2232H/D default VID/PID, but it's own product string.
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", ATTRS{product}=="USB <-> NIM tuner", MODE:="0666"
# Minitiouner Express uses different product string
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", ATTRS{product}=="MiniTiouner-Express", MODE:="0666"

7
requierement.txt Normal file
View File

@ -0,0 +1,7 @@
tk
Pillow
python-vlc
pyusb
ffpyplayer
opencv-python

BIN
status Executable file

Binary file not shown.

126
status.py Normal file
View File

@ -0,0 +1,126 @@
import os
import curses
import time
MERThreshold = 0
def display_percent(stdscr, id, meaning, value):
stdscr.addstr(2+id, 0, f"{meaning}: {float(value)/100:.2f} %\n")
def display_mer(stdscr, id, meaning, value):
stdscr.addstr(2+id, 0, f"{meaning}: {float(value)/10:.1f} dB (need {MERThreshold:.1f} dB)\n")
def display_yes_no(stdscr, id, meaning, value):
stdscr.addstr(2+id, 0, f"{meaning}: {'yes' if value != 0 else 'no'}\n")
def display_state(stdscr, id, meaning, value):
states = [
"initialising",
"searching",
"found headers",
"DVB-S lock",
"DVB-S2 lock"
]
if value < len(states):
stdscr.addstr(2+id, 0, f"{meaning}: {states[value]}\n")
else:
stdscr.addstr(2+id, 0, f"{meaning}: {value}\n")
def display_modcod_s(stdscr, id, meaning, value):
modcods = [
"QPSK 1/2",
"QPSK 2/3",
"QPSK 3/4",
"QPSK 5/6",
"QPSK 6/7",
"QPSK 7/8"
]
if value < len(modcods):
stdscr.addstr(2+id, 0, f"{meaning}: {modcods[value]}\n")
else:
stdscr.addstr(2+id, 0, f"{meaning}: {value}\n")
global MERThreshold
MERThreshold = 0
def display_modcod_s2(stdscr, id, meaning, value):
modcods = [
"DummyPL", "QPSK 1/4", "QPSK 1/3", "QPSK 2/5", "QPSK 1/2", "QPSK 3/5", "QPSK 2/3",
"QPSK 3/4", "QPSK 4/5", "QPSK 5/6", "QPSK 8/9", "QPSK 9/10", "8PSK 3/5", "8PSK 2/3",
"8PSK 3/4", "8PSK 5/6", "8PSK 8/9", "8PSK 9/10", "16APSK 2/3", "16APSK 3/4", "16APSK 4/5",
"16APSK 5/6", "16APSK 8/9", "16APSK 9/10", "32APSK 3/4", "32APSK 4/5", "32APSK 5/6",
"32APSK 8/9", "32APSK 9/10"
]
MER_thresholds = [
0, -2.3, -1.2, -0.3, 1.0, 2.3, 3.1, 4.1, 4.7, 5.2, 6.2, 6.5,
5.5, 6.6, 7.9, 9.4, 10.7, 11.0, 9.0, 10.2, 11.0, 11.6, 12.9,
13.2, 12.8, 13.7, 14.3, 15.7, 16.1
]
if value < len(modcods):
stdscr.addstr(2+id, 0, f"{meaning}: {modcods[value]}\n")
else:
stdscr.addstr(2+id, 0, f"{meaning}: {value}\n")
global MERThreshold
if value < len(MER_thresholds):
MERThreshold = MER_thresholds[value]
else:
MERThreshold = 0
def display_status(stdscr, id, value):
meanings = [
"", "State", "LNA Gain", "Puncture Rate", "I Symbol Power", "Q Symbol Power",
"Carrier Frequency", "I Constellation", "Q Constellation", "Symbol Rate",
"Viterbi Error Rate", "BER", "MER", "Service Provider", "Service", "Null Ratio",
"ES PID", "ES Type", "MODCOD", "Short Frames", "Pilot Symbols", "LDPC Error Count",
"BCH Error Count", "BCH Uncorrected", "LNB Voltage Enabled", "LNB H Polarisation",
"AGC1 Gain", "AGC2 Gain"
]
if id < len(meanings):
if id in [19, 20, 23, 24, 25]:
display_yes_no(stdscr, id, meanings[id], int(value))
elif id == 1:
state = int(value)
display_state(stdscr, id, meanings[id], state)
elif id in [10, 11]:
display_percent(stdscr, id, meanings[id], int(value))
elif id == 12:
display_mer(stdscr, id, meanings[id], int(value))
elif id == 18:
state = int(value)
if state == 3:
display_modcod_s(stdscr, id, meanings[id], int(value))
elif state == 4:
display_modcod_s2(stdscr, id, meanings[id], int(value))
else:
stdscr.addstr(2+id, 0, f"{meanings[id]}: {value}\n")
else:
stdscr.addstr(2+id, 0, f"{id}: {value}\n")
def main(stdscr):
os.system('clear')
stdscr.addstr(1, 0, "LongMynd DATV Receiver Status\n")
try:
status_fifo = open("longmynd_main_status", "r")
except Exception as e:
stdscr.addstr(2, 0, f"Failed to open status fifo: {e}\n")
stdscr.refresh()
time.sleep(2)
return
stdscr.scrollok(True)
stdscr.addstr(2, 0, "Listening\n")
stdscr.refresh()
while True:
status_message = status_fifo.readline().strip()
if status_message.startswith('$'):
parts = status_message[1:].split(',')
if parts:
n = int(parts[0])
m_str = parts[1] if len(parts) > 1 else ''
display_status(stdscr, n, m_str)
stdscr.refresh()
if __name__ == "__main__":
curses.wrapper(main)

189
waterfall.py Normal file
View File

@ -0,0 +1,189 @@
#!/usr/bin/python3
# ZR6TG - Tom - 2022/06/23
# basic signals detection code ported from https://github.com/m0dts/QO-100-WB-Live-Tune
import asyncio
import pygame
import websockets
import os
import pygame.gfxdraw
# CONFIGURATION
FFT_URL = "wss://eshail.batc.org.uk/wb/fft" #official batc fft
COL_BG = (20, 29, 43)
COL_SPECTRUM = (217, 127, 43)
COL_TEXT = (224,224,224)
WIDTH = 800
HEIGHT = 480
class Graphics:
def __init__(self, width, height):
self.start_freq = 10490.5
self.width = width
self.height = height
self.x_tab = (self.width-50) /922
self.font = pygame.font.SysFont('freesans', 20)
self.bigfont = pygame.font.SysFont('freesans', 180)
self.mediumfont = pygame.font.SysFont('freesans', 50)
def align_symbolrate(self, width):
if width < 0.002: return 0
if width < 0.065: return 0.035
if width < 0.086: return 0.066
if width < 0.195: return 0.125
if width < 0.277: return 0.250
if width < 0.388: return 0.333
if width < 0.700: return 0.500
if width < 1.2: return 1.0
if width < 1.6: return 1.5
if width < 2.2: return 2
return int(width)
async def find_signals(self, fft_data):
signals = []
i = 0
j = 0
noise_level = 11000
signal_threshold = 18000
in_signal = False
start_signal = 0
end_signal = 0
mid_signal = 0
signal_strength = 0
signal_bw = 0
signal_freq = 0
acc = 0
acc_i = 0
i = 2
while i < len(fft_data):
if in_signal == False:
if (fft_data[i] + fft_data[i-1] + fft_data[i-2]) / 3 > signal_threshold:
in_signal = True
start_signal = i
else:
if (fft_data[i] + fft_data[i-1] + fft_data[i-2]) / 3 < signal_threshold:
in_signal = False
end_signal = i
acc = 0
acc_i = 0
j = int(start_signal + (0.3 * ( end_signal - start_signal )))
while ( j < start_signal + (0.8 * (end_signal - start_signal))):
acc = acc + fft_data[j]
acc_i = acc_i + 1
j+=1
if acc_i == 0:
in_signal = False
continue
signal_strength = acc / acc_i
# find real start
j = start_signal
while (fft_data[j] - noise_level) < 0.75 * (signal_strength - noise_level):
start_signal = j
j+=1
# find real end
j = end_signal
end_signal_orig = j
while (fft_data[j] - noise_level) < 0.75 * (signal_strength - noise_level):
end_signal = j
if j <= 0:
end_signal = end_signal_orig
fake_end = True
break
j -= 1
mid_signal = start_signal + ((end_signal - start_signal)/2)
signal_freq = self.start_freq + (((mid_signal + 1) / (len(fft_data)) * 9))
signal_bw = self.align_symbolrate((end_signal - start_signal) * (9 / len(fft_data)))
if signal_bw >= 0.033:
signals.append({'start': start_signal, 'end' : end_signal, 'mid' : mid_signal, 'freq' : signal_freq, 'signal_strength' : signal_strength/255, 'signal_bw' : signal_bw})
i += 1
return signals
async def update(self, window, fft):
polygon_data = []
fft_data = []
x = 0
while ( x < len(fft)-1 ):
db = [fft[x],fft[x+1]]
#y = int.from_bytes(db, 'little')/255
fft_data.append(int.from_bytes(db, 'little'))
polygon_data.append((25 + (x/2 * self.x_tab),480-int.from_bytes(db, 'little')/255))
x += 2
signals = await self.find_signals(fft_data)
polygon_data.append((25,480))
pygame.gfxdraw.filled_polygon(window, polygon_data,COL_SPECTRUM)
pygame.gfxdraw.aapolygon(window, polygon_data,(197,244,103))
for sig in signals:
text_width, text_height = self.font.size(str(round(sig['freq'],2)))
text = self.font.render(str(round(sig['freq'],2)) + "", True, COL_TEXT)
window.blit(text, (25 + (int(sig['mid']) * self.x_tab) - text_width/2, 480 - int(sig['signal_strength']) - 60))
text_width, text_height = self.font.size(str(int(sig['signal_bw'] * 1000)) + "Ks")
text = self.font.render(str(int(sig['signal_bw'] * 1000)) + "Ks", True, COL_TEXT)
window.blit(text, (25 + (int(sig['mid']) * self.x_tab) - text_width/2, 480 - int(sig['signal_strength']) - 40))
class Socket:
def __init__(self):
self.websocket = None
async def startup(self):
self.websocket = await websockets.connect(FFT_URL)
async def updateFFT(self):
return await self.websocket.recv()
async def main():
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.mouse.set_visible(False)
graphics = Graphics(WIDTH, HEIGHT)
socket = Socket()
await socket.startup()
while True:
fft = await socket.updateFFT()
window.fill(COL_BG)
await graphics.update(window, fft)
pygame.display.flip()
if __name__ == "__main__":
pygame.init()
asyncio.run(main())
pygame.quit()