mirror of
https://github.com/Ysurac/openmptcprouter-feeds.git
synced 2025-03-09 15:40:03 +00:00
fix
This commit is contained in:
parent
a7cabff2d1
commit
d25235c692
47 changed files with 0 additions and 8227 deletions
Binary file not shown.
Before Width: | Height: | Size: 26 KiB |
Binary file not shown.
Before Width: | Height: | Size: 156 KiB |
Binary file not shown.
Before Width: | Height: | Size: 135 KiB |
Binary file not shown.
Before Width: | Height: | Size: 47 KiB |
|
@ -1,674 +0,0 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://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 <https://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
|
||||
<https://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
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
|
@ -1,14 +0,0 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=GNSS Information dashboard for 3G/LTE dongle
|
||||
LUCI_DEPENDS:=+lua +curl +lua-rs232 +luasocket +iwinfo +libiwinfo-lua +lua-bit32
|
||||
PKG_LICENSE:=GPLv3
|
||||
PKG_VERSION:=1.6.0
|
||||
|
||||
define Package/luci-app-gpoint/conffiles
|
||||
/etc/config/gpoint
|
||||
endef
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
|
@ -1,45 +0,0 @@
|
|||
<h2 align="center">
|
||||
<img src="https://github.com/Kodo-kakaku/luci-app-gpoint/blob/main/Images/logo.png" alt="Gpoint" height="200" width="370">
|
||||
<br>Global Navigation Satellite System for OpenWrt LuCi<br>
|
||||
</h2>
|
||||
<p align="center">Gpoint was created in order to use full set of functions of mobile modules installed in OpenWRT router.
|
||||
Manufacturers of GSM/3G/LTE modems often lay down GNSS function, so why not use it?
|
||||
It doesn't matter if you use a router in transport or it is installed in your terminal, you can always find out its location!</p>
|
||||
<details>
|
||||
<summary>Screenshots</summary>
|
||||
<img src="https://github.com/Kodo-kakaku/luci-app-gpoint/blob/main/Images/overview_wait.png" alt="overview_wait">
|
||||
<img src="https://github.com/Kodo-kakaku/luci-app-gpoint/blob/main/Images/overview.png" alt="overview">
|
||||
<img src="https://github.com/Kodo-kakaku/luci-app-gpoint/blob/main/Images/settings.png" alt="overview">
|
||||
</details>
|
||||
|
||||
## Features
|
||||
- Support: GPS, GLONASS (works with "NMEA 0183" standard protocol)
|
||||
- GeoHash (reduces drift of GPS\GLONASS coordinate readings in parking)
|
||||
- [Kalman filter](https://github.com/lacker/ikalman) (Implementation of Kalman filter for geo (gps) tracks. This is a Lua port of original C code)
|
||||
- Yandex Locator [API](https://yandex.ru/dev/locator/) (Determines location by nearest Wi-Fi access points)
|
||||
- Server side (sends GNSS data to a remote server)
|
||||
- Support [OpenLayers](https://openlayers.org/) maps in UI, and much more!
|
||||
|
||||
## Supported devices
|
||||
- Dell DW5821e/DW5829e
|
||||
- Quectel EC25/EP06/EM12/RM500Q
|
||||
- Sierra EM7455/EM7565
|
||||
- Simcom SIM7600E-H
|
||||
- U-Blox VK-172 GPS/GLONASS module (u-blox 7 GNSS modules)
|
||||
|
||||
|
||||
## Supported GNSS protocols
|
||||
- [OsmAnd](https://www.traccar.org/osmand/)
|
||||
- [Wialon IPS](https://gurtam.com/ru/gps-hardware/soft/wialon-ips)
|
||||
|
||||
## Install
|
||||
- Upload ipk file to tmp folder
|
||||
- cd /tmp
|
||||
- opkg update
|
||||
- opkg install luci-app-gpoint_1.7.0_all.ipk
|
||||
|
||||
## Uninstall
|
||||
- opkg remove luci-app-gpoint
|
||||
|
||||
## License
|
||||
Gpoint like OpenWRT is released under the GPL v3.0 License - see detailed [LICENSE](https://github.com/Kodo-kakaku/luci-app-gpoint/blob/main/LICENSE).
|
Binary file not shown.
Before Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 31 KiB |
|
@ -1,86 +0,0 @@
|
|||
-------------------------------------------------------------
|
||||
-- luci-app-gpoint. Gnss information dashboard for 3G/LTE dongle.
|
||||
-------------------------------------------------------------
|
||||
-- Copyright 2021-2022 Vladislav Kadulin <spanky@yandex.ru>
|
||||
-- Licensed to the GNU General Public License v3.0
|
||||
|
||||
local ubus = require("ubus")
|
||||
local uci = require("luci.model.uci")
|
||||
local json = require("luci.jsonc")
|
||||
|
||||
module("luci.controller.gpoint.gpoint", package.seeall)
|
||||
|
||||
function index()
|
||||
entry({"admin", "services", "gpoint"}, alias ("admin","services", "gpoint", "map"), translate("GPoint"), 10).acl_depends={"unauthenticated"}
|
||||
entry({"admin", "services", "gpoint", "map"}, template("gpoint/overview"), translate("Overview"), 51).acl_depends={"unauthenticated"}
|
||||
entry({"admin", "services", "gpoint", "settings"}, cbi("gpoint/gpoint"), translate("Settings"), 52).acl_depends={"unauthenticated"}
|
||||
entry({"admin", "services", "gpoint", "action"}, call("gpoint_action"), nil).leaf = true
|
||||
entry({"admin", "services", "gpoint", "geopoint"}, call("get_geopoint"), nil).leaf = true
|
||||
entry({"admin", "services", "gpoint", "blackbox"}, call("get_blackbox"), nil).leaf = true
|
||||
end
|
||||
|
||||
local serviceIsStop = {
|
||||
warning={
|
||||
app={true,"Service stop"},
|
||||
server={true,"Service stop"},
|
||||
filter={true,"Service stop"},
|
||||
locator={true,"Service stop"},
|
||||
kalman={true,"Service stop"}
|
||||
}
|
||||
}
|
||||
|
||||
local serviceUbusFailed = {
|
||||
warning={
|
||||
app={true,"Ubus Failed"},
|
||||
server={true,"Loading..."},
|
||||
filter={true,"Loading..."},
|
||||
locator={true,"Loading..."},
|
||||
kalman ={true, "Loading..."}
|
||||
}
|
||||
}
|
||||
|
||||
-- Overview JSON request
|
||||
function get_geopoint()
|
||||
local sessionId = uci:get("gpoint", "service_settings", "sessionid")
|
||||
luci.http.prepare_content("application/json")
|
||||
local data
|
||||
if sessionId == "stop" then
|
||||
data = json.stringify(serviceIsStop)
|
||||
else
|
||||
local conn = ubus.connect()
|
||||
if conn then
|
||||
local resp = conn:call("session", "list", {ubus_rpc_session = sessionId})
|
||||
data = json.stringify(resp.data)
|
||||
conn:close()
|
||||
else
|
||||
data = json.stringify(serviceUbusFailed)
|
||||
end
|
||||
|
||||
end
|
||||
luci.http.write(data)
|
||||
end
|
||||
|
||||
-- BlackBox JSON request
|
||||
function get_blackbox()
|
||||
local data = luci.sys.exec("cat /usr/share/gpoint/tmp/blackbox.json")
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write(data)
|
||||
end
|
||||
|
||||
-- Settings init.d service
|
||||
function gpoint_action(name)
|
||||
local packageName = "gpoint"
|
||||
if name == "start" then
|
||||
luci.sys.init.start(packageName)
|
||||
elseif name == "action" then
|
||||
luci.util.exec("/etc/init.d/" .. packageName .. " reload")
|
||||
elseif name == "stop" then
|
||||
luci.sys.init.stop(packageName)
|
||||
elseif name == "enable" then
|
||||
luci.sys.init.enable(packageName)
|
||||
elseif name == "disable" then
|
||||
luci.sys.init.disable(packageName)
|
||||
end
|
||||
luci.http.prepare_content("text/plain")
|
||||
luci.http.write("0")
|
||||
end
|
|
@ -1,252 +0,0 @@
|
|||
-------------------------------------------------------------
|
||||
-- luci-app-gpoint. Gnss information dashboard for 3G/LTE dongle.
|
||||
-------------------------------------------------------------
|
||||
-- Copyright 2021-2022 Vladislav Kadulin <spanky@yandex.ru>
|
||||
-- Licensed to the GNU General Public License v3.0
|
||||
|
||||
local fs = require("nixio.fs")
|
||||
local sys = require("luci.sys")
|
||||
local util = require("luci.util")
|
||||
local json = require("luci.jsonc")
|
||||
|
||||
|
||||
local packageName = "gpoint"
|
||||
local helperText = ""
|
||||
local tmpfsStatus, tmpfsStatusCode
|
||||
local ubusStatus = util.ubus("service", "list", { name = packageName })
|
||||
local lsusb = sys.exec("lsusb")
|
||||
local device_port = fs.glob("/dev/tty[A-Z][A-Z]*")
|
||||
|
||||
local timezone = {
|
||||
{'Autodetect (experimental)','auto'},{'Etc/GMT', '0' },{ 'Etc/GMT+1', '1' },{ 'Etc/GMT+10', '10'},
|
||||
{ 'Etc/GMT+11', '11'},{ 'Etc/GMT+12', '12'},{ 'Etc/GMT+2', '2' },{ 'Etc/GMT+3', '3' },
|
||||
{ 'Etc/GMT+4', '4' },{ 'Etc/GMT+5', '5' },{ 'Etc/GMT+6', '6' },{ 'Etc/GMT+7', '7' },
|
||||
{ 'Etc/GMT+8', '8' },{ 'Etc/GMT+9', '9' },{ 'Etc/GMT-1', '-1' },{ 'Etc/GMT-10', '-10'},
|
||||
{ 'Etc/GMT-11', '-11'},{ 'Etc/GMT-12', '-12'},{ 'Etc/GMT-13', '-13'},{ 'Etc/GMT-14', '-14'},
|
||||
{ 'Etc/GMT-2', '-2' },{ 'Etc/GMT-3', '-3' },{ 'Etc/GMT-4', '-4' },{ 'Etc/GMT-5', '-5' },
|
||||
{ 'Etc/GMT-6', '-6' },{ 'Etc/GMT-7', '-7' },{ 'Etc/GMT-8', '-8' },{ 'Etc/GMT-9', '-9' }
|
||||
}
|
||||
|
||||
local modems = {
|
||||
["Quectel"] = {
|
||||
["2c7c:0306"] = "EP06",
|
||||
["2c7c:0512"] = "EM12",
|
||||
["2c7c:0125"] = "EC25",
|
||||
["2c7c:0800"] = "RM500Q"
|
||||
},
|
||||
["Sierra"] = {
|
||||
["1199:9071"] = "EM7455",
|
||||
["1199:9091"] = "EM7565"
|
||||
},
|
||||
["U-Blox"] = {
|
||||
["1546:01a7"] = "VK-172"
|
||||
},
|
||||
["Simcom"] = {
|
||||
["1e0e:9001"] = "SIM7600E-H"
|
||||
},
|
||||
["Dell"] = {
|
||||
["413c:81d7"] = "DW5821e",
|
||||
["413c:81e6"] = "DW5829e"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
local m = Map("gpoint", translate(""))
|
||||
|
||||
-- Service
|
||||
local s = m:section(TypedSection, "modem_settings", translate("Service"))
|
||||
s.anonymous = true
|
||||
s.addremove = false
|
||||
|
||||
local o = s:option(DummyValue, "_dummy")
|
||||
o.template = packageName .. "/buttons"
|
||||
|
||||
o = s:option(DummyValue, "_dummy", translate("Service Status:"))
|
||||
o.template = packageName .. "/service_status"
|
||||
|
||||
|
||||
-- Modem
|
||||
s = m:section(TypedSection, "modem_settings", translate("Modem"), translate("Select the modem(s) to find the location"))
|
||||
s.anonymous = true
|
||||
s.addremove = false
|
||||
|
||||
local no_device = true
|
||||
o = s:option(ListValue, "modem", translate("Modem(s):"))
|
||||
if lsusb then
|
||||
for modem_name, modem_data in pairs(modems) do
|
||||
for id, modem in pairs(modem_data) do
|
||||
if string.find(lsusb, id) then
|
||||
no_device = false
|
||||
o:value(modem_name .. '_' .. modem, modem_name .. ' ' .. modem)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if no_device then
|
||||
o:value('mnf', translate("-- Modems not found --"))
|
||||
end
|
||||
|
||||
o = s:option(ListValue, "port", translate("Modem port:"), translate("Select the NMEA port of the device."))
|
||||
if no_device then
|
||||
o:value('pnf', translate("-- disable --"))
|
||||
o = s:option( DummyValue, "nfound")
|
||||
function o.cfgvalue(self, section)
|
||||
local nfound = "<div style=\"color: red;\"><b>No modem(s) found! Check the modem connections.</b><br> \
|
||||
<div style=\"color: lime;\">Supported modems: "
|
||||
for modem_name, modem_data in pairs(modems) do
|
||||
nfound = nfound .. "<br>" .. modem_name .. ' '
|
||||
for _, modem in pairs(modem_data) do
|
||||
nfound = nfound .. modem .. ", "
|
||||
end
|
||||
nfound = nfound:sub(1, -3)
|
||||
end
|
||||
nfound = nfound .. "</div></div>"
|
||||
return translate(nfound)
|
||||
end
|
||||
o.rawhtml = true
|
||||
else
|
||||
if device_port then
|
||||
for node in device_port do
|
||||
o:value(node, node)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Add TimeZone
|
||||
o = s:option(ListValue, "timezone", translate("Timezone:"))
|
||||
for _, zone in pairs(timezone) do
|
||||
o:value(zone[2], zone[1])
|
||||
end
|
||||
|
||||
|
||||
-- Remote Server
|
||||
s = m:section(TypedSection, "server_settings", translate("Remote Server"), translate("Configuration of the remote navigation server"))
|
||||
s.addremove = false
|
||||
s.anonymous = true
|
||||
|
||||
o = s:option(Flag, "server_enable", translate("Enable server:"), translate("Enabling Remote Server service"))
|
||||
|
||||
o = s:option(ListValue, "proto", translate(" "), translate("Navigation data transmission protocol"))
|
||||
o.widget = "radio"
|
||||
o:value("traccar", " Traccar Client") -- Key and value pairs
|
||||
o:value("wialon", " Wialon IPS")
|
||||
o.default = "trackcar"
|
||||
|
||||
o = s:option(Value, "server_frequency", translate("Frequency:"), translate("Frequency of sending data to the Remote Server"))
|
||||
o.placeholder = "In seconds"
|
||||
o.datatype = "range(5, 600)"
|
||||
|
||||
o = s:option(Value, "server_ip", translate("Address:"))
|
||||
o.datatype = "host"
|
||||
o.placeholder = '172.0.0.1'
|
||||
|
||||
o = s:option(Value, "server_port", translate("Port:"))
|
||||
o.datatype = "port"
|
||||
o.placeholder = '80'
|
||||
|
||||
o = s:option(Value, "server_login", translate("Login:"))
|
||||
o.placeholder = "Device login (ID)"
|
||||
|
||||
o = s:option(Value, "server_password", translate("Password:"), translate("If you don't use Password, leave the field empty"))
|
||||
o.password = true
|
||||
o.placeholder = "Device password"
|
||||
|
||||
o = s:option(Flag, "blackbox_enable", translate("BlackBox enable:"),
|
||||
translate("Blackbox makes it possible to record and store data even in the absence of a cellular signal"))
|
||||
o:depends("proto","wialon")
|
||||
|
||||
o = s:option(Flag, "blackbox_cycle", translate("BlackBox cycle:"), translate("Cyclic overwriting of data stored in the BlackBox"))
|
||||
o:depends("proto", "wialon")
|
||||
|
||||
o = s:option(Value, "blackbox_max_size", translate("BlackBox size:"), translate("Number of sentences in the BlackBox"))
|
||||
o.placeholder = "default: 1000 sentence"
|
||||
o.datatype = "range(1000, 5000)"
|
||||
o:depends("proto","wialon")
|
||||
|
||||
o = s:option(DummyValue, "_dummy", translate(" "))
|
||||
o.template = packageName .. "/blackbox"
|
||||
o:depends("proto","wialon")
|
||||
|
||||
o = s:option(Button, "clear", translate("Clear BlackBox"), translate("Warning! After clearing the BlackBox, GNSS data will be destroyed!"))
|
||||
o.inputstyle = "remove"
|
||||
o:depends("proto", "wialon")
|
||||
function o.write(self, section)
|
||||
local file = io.open("/usr/share/gpoint/tmp/blackbox.json", 'w')
|
||||
file:write(json.stringify({["size"]=0,["max"]=1000,["data"]={}}))
|
||||
file:close()
|
||||
end
|
||||
|
||||
|
||||
-- Tab menu settings
|
||||
s = m:section(TypedSection, "service_settings")
|
||||
s.addremove = false
|
||||
s.anonymous = true
|
||||
|
||||
|
||||
----------------------------------------------------------------------------------------------------------------
|
||||
s:tab("ya", translate("Yandex Locator"), translate("Determines the location of the mobile \
|
||||
device by the nearest Wi-Fi access points and \
|
||||
cellular base stations — without using satellite navigation systems."))
|
||||
s:tab("gpoint_filter", translate("GeoHash Filter"), translate("Filters \"DRIFT\" and \"JUMPS\" of navigation 3G/LTE dongles"))
|
||||
s:tab("kalman", translate("Kalman Filter"), translate("Designed to make the route smoother. Removes \"jumps\" of navigation 3G/LTE dongles"))
|
||||
|
||||
----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- API Yandex locator
|
||||
o = s:taboption("ya", Flag, "ya_enable", translate("Enable:"), translate("Enabling the Yandex locator"))
|
||||
o.optional = true
|
||||
|
||||
o = s:taboption("ya", ListValue, "ya_wifi", translate("Interface:"), translate("Select the Wi-Fi interface for Yandex locator"))
|
||||
local iwinfo = sys.exec("iwinfo")
|
||||
no_device = true
|
||||
for device in string.gmatch(iwinfo, "(%S+)(%s%s%s%s%s)(%S+)") do
|
||||
o:value(device, device)
|
||||
no_device = false
|
||||
end
|
||||
|
||||
if no_device then
|
||||
o:value('wnf', translate("-- Wifi not found --"))
|
||||
end
|
||||
|
||||
o = s:taboption("ya", Value, "ya_key", translate("API Key:"), translate("To work with the Yandex locator must use an API key"))
|
||||
o.password = true
|
||||
o.placeholder = "Yandex API key"
|
||||
|
||||
o = s:taboption("ya", DummyValue, "ya_href")
|
||||
function o.cfgvalue(self, section)
|
||||
local h = "<a href=\"https://yandex.ru/dev/locator/keys/get/\">Get Yandex API key</a>"
|
||||
return translate(h)
|
||||
end
|
||||
o.rawhtml = true
|
||||
|
||||
-- GeoHash
|
||||
o = s:taboption("gpoint_filter", Flag, "filter_enable", translate("Enable:"), translate("Enabling GpointFilter"))
|
||||
o.optional = true
|
||||
|
||||
o = s:taboption("gpoint_filter", Value, "filter_changes", translate("Jump:"), translate("Registration of the \"jump\" coordinates. \
|
||||
The coordinate is recognized as valid after the modem has received it more than the specified number of times."))
|
||||
o.placeholder = ""
|
||||
o.datatype = "range(2, 6)"
|
||||
|
||||
o = s:taboption("gpoint_filter", ListValue, "filter_hash", translate("Area:"), translate("The longer the hash length,\
|
||||
the smaller the area and the greater the accuracy of the coordinates in one area."))
|
||||
o.optional = true
|
||||
o.default = 7
|
||||
for i = 1, 12 do
|
||||
o:value(i, i)
|
||||
end
|
||||
o = s:taboption("gpoint_filter", Value, "filter_speed", translate("Speed:"), translate("Above the specified speed, the filter will be disabled"))
|
||||
o.placeholder = "default 2 km/h"
|
||||
o.datatype = "range(0, 150)"
|
||||
|
||||
-- Kalman
|
||||
o = s:taboption("kalman", Flag, "kalman_enable", translate("Enable:"), translate("Enabling KalmanFilter"))
|
||||
o.optional = true
|
||||
o = s:taboption("kalman", Value, "kalman_noise", translate("Noise:"), translate("Noise is a parameter you can use to alter the expected noise.\
|
||||
1.0 is the original, and the higher it is, the more a path will be \"smoothed\""))
|
||||
o.placeholder = ""
|
||||
o.datatype = "range(1.0, 30.0)"
|
||||
|
||||
|
||||
return m
|
|
@ -1,33 +0,0 @@
|
|||
<%#
|
||||
Module for providing data from a mobile satellite navigation system ( mobile modems, etc.)
|
||||
-= Design and Development 2021-2022 =-
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%+cbi/valueheader%>
|
||||
|
||||
<div class="boof" id="boof"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
XHR.poll(2, '<%=luci.dispatcher.build_url("admin", "services", "gpoint", "blackbox")%>', null,
|
||||
// x - luci json header, geopoint - payload;
|
||||
function (x, blackbox) {
|
||||
var e = document.getElementById("boof");
|
||||
if (blackbox && e) {
|
||||
var s = (blackbox.size / blackbox.max) * 100;
|
||||
var c = "green";
|
||||
if(s === blackbox.max) { c = "red"; }
|
||||
e.innerHTML = String.format('<div style="width:21em; position:relative; border:1px solid #999999">'+
|
||||
'<div style="animation-direction: reverse; background-color:#90c0e0; width:%d%%; height:20px">'+
|
||||
'<div style="position:absolute; left:0; top:0; text-align:center; width:100%%; color:%s">'+
|
||||
'<small>%s</small>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>', s, c, "<b>BlackBox status: "+blackbox.size+" / "+blackbox.max+"</b>"
|
||||
);
|
||||
}});
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<%+cbi/valuefooter%>
|
|
@ -1,81 +0,0 @@
|
|||
<%#
|
||||
Module for providing data from a mobile satellite navigation system ( mobile modems, etc.)
|
||||
-= Design and Development 2021-2022 =-
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%+gpoint/css/style%>
|
||||
<%+gpoint/service%>
|
||||
|
||||
<%-
|
||||
local packageName = "gpoint"
|
||||
local serviceRunning, serviceEnabled = false, false;
|
||||
|
||||
serviceEnabled = luci.sys.init.enabled(packageName)
|
||||
local ubusStatus = luci.util.ubus("service", "list", { name = packageName })
|
||||
if ubusStatus and ubusStatus[packageName] then
|
||||
serviceRunning = true
|
||||
end
|
||||
|
||||
if serviceEnabled then
|
||||
btn_start_status = true
|
||||
btn_action_status = true
|
||||
btn_stop_status = true
|
||||
btn_enable_status = false
|
||||
btn_disable_status = true
|
||||
else
|
||||
btn_start_status = false
|
||||
btn_action_status = false
|
||||
btn_stop_status = false
|
||||
btn_enable_status = true
|
||||
btn_disable_status = false
|
||||
end
|
||||
if serviceRunning then
|
||||
btn_start_status = false
|
||||
btn_action_status = true
|
||||
btn_stop_status = true
|
||||
else
|
||||
btn_action_status = false
|
||||
btn_stop_status = false
|
||||
end
|
||||
-%>
|
||||
|
||||
<div class="cbi-value"><label class="cbi-value-title">Service Control:</label>
|
||||
<div class="cbi-value-field">
|
||||
<input type="button" class="cbi-button cbi-button-apply" id="btn_start" name="start" value="<%:Start%>"
|
||||
onclick="button_action(this)" />
|
||||
<span id="btn_start_spinner" class="btn_spinner"></span>
|
||||
<input type="button" class="cbi-button cbi-button-apply" id="btn_action" name="action" value="<%:Reload%>"
|
||||
onclick="button_action(this)" />
|
||||
<span id="btn_action_spinner" class="btn_spinner"></span>
|
||||
<input type="button" class="cbi-button cbi-button-reset" id="btn_stop" name="stop" value="<%:Stop%>"
|
||||
onclick="button_action(this)" />
|
||||
<span id="btn_stop_spinner" class="btn_spinner"></span>
|
||||
|
||||
|
||||
|
||||
|
||||
<input type="button" class="cbi-button cbi-button-apply" id="btn_enable" name="enable" value="<%:Enable%>"
|
||||
onclick="button_action(this)" />
|
||||
<span id="btn_enable_spinner" class="btn_spinner"></span>
|
||||
<input type="button" class="cbi-button cbi-button-reset" id="btn_disable" name="disable" value="<%:Disable%>"
|
||||
onclick="button_action(this)" />
|
||||
<span id="btn_disable_spinner" class="btn_spinner"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%-if not btn_start_status then%>
|
||||
<script type="text/javascript">document.getElementById("btn_start").disabled = true;</script>
|
||||
<%-end%>
|
||||
<%-if not btn_action_status then%>
|
||||
<script type="text/javascript">document.getElementById("btn_action").disabled = true;</script>
|
||||
<%-end%>
|
||||
<%-if not btn_stop_status then%>
|
||||
<script type="text/javascript">document.getElementById("btn_stop").disabled = true;</script>
|
||||
<%-end%>
|
||||
<%-if not btn_enable_status then%>
|
||||
<script type="text/javascript">document.getElementById("btn_enable").disabled = true;</script>
|
||||
<%-end%>
|
||||
<%-if not btn_disable_status then%>
|
||||
<script type="text/javascript">document.getElementById("btn_disable").disabled = true;</script>
|
||||
<%-end%>
|
|
@ -1,26 +0,0 @@
|
|||
<style type="text/css">
|
||||
.btn_spinner {
|
||||
display: inline-block;
|
||||
width: 0px;
|
||||
height: 16px;
|
||||
margin: 0 0px;
|
||||
}
|
||||
.map {
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
.ol-viewport {
|
||||
border-radius: 0.5%;
|
||||
}
|
||||
.fly-to-burn {
|
||||
text-align: right;
|
||||
}
|
||||
.locate {
|
||||
top: 6em;
|
||||
left: .5em;
|
||||
}
|
||||
.status {
|
||||
color: white;
|
||||
float: right;
|
||||
}
|
||||
</style>
|
|
@ -1,183 +0,0 @@
|
|||
<%#
|
||||
Module for providing data from a mobile satellite navigation system ( mobile modems, etc.)
|
||||
-= Design and Development 2021-2022 =-
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
|
||||
const icon = "<%=resource%>/icons/gpoint_icons/marker.png";
|
||||
|
||||
function onClick(id, callback) {
|
||||
document.getElementById(id).addEventListener('click', callback);
|
||||
}
|
||||
|
||||
function flyTo(location, z, done) {
|
||||
var duration = 5000;
|
||||
var zoom = z;
|
||||
var parts = 2;
|
||||
var called = false;
|
||||
|
||||
function callback(complete) {
|
||||
--parts;
|
||||
if (called) {
|
||||
return;
|
||||
}
|
||||
if (parts === 0 || !complete) {
|
||||
called = true;
|
||||
done(complete);
|
||||
}
|
||||
}
|
||||
|
||||
view.animate({center: location, duration: duration,}, callback);
|
||||
view.animate({zoom: zoom - 1,duration: duration / 2,},
|
||||
{zoom: zoom,duration: duration / 2,}, callback);
|
||||
}
|
||||
|
||||
function getAddress(lat, lon) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "https://nominatim.openstreetmap.org/reverse?lat="
|
||||
+lat+"&lon="+lon+"&zoom=18&format=json", true);
|
||||
xhr.onload = function () {
|
||||
if (xhr.status == 200) {
|
||||
var data = JSON.parse(xhr.responseText);
|
||||
var e = document.getElementById('address');
|
||||
const gMap = "https://www.google.com/maps/place/"+lat+','+lon;
|
||||
e.innerHTML = String.format("<a style=\"color:gray; font-size: 18px; text-decoration: none;\"href=\"" + gMap + "\"><br>"
|
||||
+ (data.address.state !== undefined ? data.address.state : "")
|
||||
+ (data.address.road !== undefined ? (", " + data.address.road ) : "")
|
||||
+ (data.address.house_number !== undefined ? (", " + data.address.house_number) : "") + "</a>");
|
||||
}
|
||||
}
|
||||
xhr.send(null);
|
||||
}
|
||||
|
||||
function addMarker(longitude, latitude, icon) {
|
||||
|
||||
if(spinerStatus) {
|
||||
map.removeOverlay(marker);
|
||||
zoom = 18;
|
||||
spinerStatus = false;
|
||||
}
|
||||
rasterLayer.setVisible(false);
|
||||
|
||||
lon = longitude;
|
||||
lat = latitude;
|
||||
|
||||
coordinate = ol.proj.fromLonLat([lat, lon]);
|
||||
|
||||
iconFeatures.length = 0; // deleting the layer
|
||||
vectorSource.clear(); // deleting array of features
|
||||
|
||||
var iconFeature = new ol.Feature({
|
||||
geometry: new ol.geom.Point(ol.proj.transform([lat, lon], 'EPSG:4326', 'EPSG:3857')),
|
||||
name: 'COOR',
|
||||
population: 4000,
|
||||
rainfall: 500
|
||||
});
|
||||
|
||||
iconFeatures.push(iconFeature);
|
||||
vectorSource.addFeatures(iconFeatures);
|
||||
}
|
||||
|
||||
function addSpiner() {
|
||||
rasterLayer.setVisible(true);
|
||||
if(!spinerStatus) {
|
||||
map.addOverlay(marker);
|
||||
zoom = 3;
|
||||
spinerStatus = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function putData(data) {
|
||||
var id = ["latitude", "hdop", "longitude", "altitude", "cog", "spkm", "nsat", "date"];
|
||||
id.forEach(function(item, i, id) {
|
||||
var e = document.getElementById(item);
|
||||
if(e) {
|
||||
e.innerHTML = String.format(data.item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var lon = 55.7522;
|
||||
var lat = 37.6156;
|
||||
var zoom = 3;
|
||||
var spinerStatus = true;
|
||||
|
||||
var iconFeatures=[];
|
||||
var coordinate = ol.proj.fromLonLat([lat, lon]);
|
||||
|
||||
var vectorSource = new ol.source.Vector({
|
||||
features: iconFeatures //add an array of features
|
||||
});
|
||||
|
||||
var rasterLayer = new ol.layer.Tile({
|
||||
source: new ol.source.Stamen({
|
||||
layer: 'toner'
|
||||
})
|
||||
});
|
||||
|
||||
var mapLayer = new ol.layer.Tile({
|
||||
preload: 4,
|
||||
source: new ol.source.OSM()
|
||||
});
|
||||
|
||||
var iconStyle = new ol.style.Style({
|
||||
image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
|
||||
anchor: [0.5, 46],
|
||||
anchorXUnits: 'fraction',
|
||||
anchorYUnits: 'pixels',
|
||||
opacity: 0.75,
|
||||
src: icon
|
||||
}))
|
||||
});
|
||||
|
||||
var vectorLayer = new ol.layer.Vector({
|
||||
source: vectorSource,
|
||||
style: iconStyle
|
||||
});
|
||||
|
||||
var view = new ol.View({
|
||||
center: coordinate,
|
||||
zoom: 3,
|
||||
});
|
||||
|
||||
var map = new ol.Map({
|
||||
controls: ol.control.defaults({zoom: true, attribution: false, rotate: false}),
|
||||
target: 'map',
|
||||
layers: [mapLayer
|
||||
|
||||
],
|
||||
|
||||
view: view
|
||||
});
|
||||
|
||||
map.addLayer(vectorLayer);
|
||||
map.addLayer(rasterLayer);
|
||||
|
||||
// To display a widget that shows the location of an object
|
||||
const locate = document.createElement('div');
|
||||
locate.className = 'ol-control ol-unselectable locate';
|
||||
locate.innerHTML = '<button id="fmr" title="Find my Router">◎</button>';
|
||||
locate.addEventListener('click', function(){ flyTo(coordinate, zoom, function () {}); });
|
||||
map.addControl(new ol.control.Control({
|
||||
element: locate
|
||||
}));
|
||||
|
||||
var marker = new ol.Overlay({
|
||||
position: coordinate,
|
||||
positioning: 'center-center',
|
||||
element: document.getElementById('marker'),
|
||||
stopEvent: false
|
||||
});
|
||||
|
||||
map.addOverlay(marker);
|
||||
|
||||
//]]>
|
||||
</script>
|
|
@ -1,140 +0,0 @@
|
|||
<%#
|
||||
Module for providing data from a mobile satellite navigation system ( mobile modems, etc.)
|
||||
-= Design and Development 2021-2022 =-
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%+gpoint/css/style%>
|
||||
|
||||
<%+header%>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@main/dist/en/v6.8.1/css/ol.css" type="text/css">
|
||||
<style type="text/css">
|
||||
#marker {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background: url("<%=resource%>/icons/gpoint_icons/spiner.gif") no-repeat scroll 0% 0% transparent;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h2>
|
||||
<div>GPoint: Router Location
|
||||
<span class="status" id="stat">◉</span>
|
||||
</div>
|
||||
</h2>
|
||||
<body>
|
||||
<div id="map" class="map"></div>
|
||||
<div style="display: none;">
|
||||
<div id="marker" title="Marker"></div>
|
||||
</div>
|
||||
<div><a id="address" href=""></a></div>
|
||||
|
||||
<div class="cbi-section fade-in" style="">
|
||||
<h3><%:Details:%></h3>
|
||||
<div>
|
||||
<table class="table">
|
||||
<tr class="tr">
|
||||
<td class="td left" width="33.3%"><%:Longitude:%></td>
|
||||
<td class="td left" id="longitude">-</td>
|
||||
<td class="td left" width="33.3%"><%:Latitude:%></td>
|
||||
<td class="td left" id="latitude">-</td>
|
||||
<td class="td left" width="33.3%"><%:Altitude:%></td>
|
||||
<td class="td left" id="altitude">-</td>
|
||||
</tr>
|
||||
<tr class="tr">
|
||||
<td class="td left" width="33.3%"><%:Time (UTC):%></td>
|
||||
<td class="td left" id="utc">-</td>
|
||||
<td class="td left" width="33.3%"><%:Date:%></td>
|
||||
<td class="td left" id="date">-</td>
|
||||
<td class="td left" width="33.3%"><%:Satellites:%></td>
|
||||
<td class="td left" id="nsat">-</td>
|
||||
</tr>
|
||||
<tr class="tr">
|
||||
<td class="td left" width="33.3%"><%:Hdop:%></td>
|
||||
<td class="td left" id="hdop">-</td>
|
||||
<td class="td left" width="33.3%"><%:Course:%></td>
|
||||
<td class="td left" id="cog">-</td>
|
||||
<td class="td left" width="33.3%"><%:Speed:%></td>
|
||||
<td class="td left" id="spkm">-</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@main/dist/en/v6.8.1/build/ol.js"></script>
|
||||
<%+gpoint/js/js%>
|
||||
<script type="text/javascript">
|
||||
var prev = true; // show geolocation (auto zoom) <%=resource%>/gpoint
|
||||
const keys = ["longitude", "latitude", "altitude", "utc", "date", "nsat", "hdop", "cog", "spkm"];
|
||||
|
||||
function cleanTable() {
|
||||
for(var key of keys) {
|
||||
document.getElementById(key).innerHTML = "-";
|
||||
}
|
||||
}
|
||||
|
||||
function preview(prev) {
|
||||
if(prev) {
|
||||
document.getElementById('fmr').click();
|
||||
prev = false;
|
||||
} else {
|
||||
document.getElementById('fmr').click();
|
||||
document.getElementById('address').innerHTML = "";
|
||||
prev = true;
|
||||
}
|
||||
return prev
|
||||
}
|
||||
|
||||
|
||||
|
||||
XHR.poll(4, '<%=build_url("admin/services/gpoint/geopoint")%>', null,
|
||||
// x - luci json header, geopoint - payload;
|
||||
function(x, geopoint) {
|
||||
// GP Data
|
||||
if(!geopoint.warning.app[0]) {
|
||||
if(geopoint.gp.latitude != '-' && geopoint.gp.longitude != '-') {
|
||||
getAddress(geopoint.gp.latitude, geopoint.gp.longitude);
|
||||
addMarker(geopoint.gp.latitude, geopoint.gp.longitude, icon);
|
||||
}
|
||||
for(const key of keys) {document.getElementById(key).innerHTML = String.format(geopoint.gp[key]);}
|
||||
if (prev) {
|
||||
document.getElementById('fmr').click();
|
||||
prev = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
addSpiner();
|
||||
if (!prev) {
|
||||
cleanTable();
|
||||
document.getElementById('fmr').click();
|
||||
document.getElementById('address').innerHTML = "";
|
||||
prev = true;
|
||||
}
|
||||
}
|
||||
|
||||
var color = "orange";
|
||||
var error_status = "";
|
||||
if(geopoint.warning.locator[1] == "Data received from locator...") {
|
||||
error_status = geopoint.warning.locator[1];
|
||||
if(geopoint.warning.app[0]) {
|
||||
error_status = error_status + " (Service: "+geopoint.warning.app[1]+')';
|
||||
}
|
||||
}
|
||||
else if(geopoint.warning.app[0]) {
|
||||
color = "red";
|
||||
error_status = geopoint.warning.app[1];
|
||||
}
|
||||
else if(geopoint.warning.gp[0]) {
|
||||
color = "orange"
|
||||
error_status = geopoint.warning.gp[1]
|
||||
}
|
||||
else if(!geopoint.warning.app[0] && !geopoint.warning.gp[0]) {
|
||||
color = "greenyellow";
|
||||
error_status = geopoint.warning.gp[1];
|
||||
}
|
||||
else{}
|
||||
|
||||
document.querySelector("#stat").style.color = color;
|
||||
document.getElementById("stat").title = error_status;
|
||||
});
|
||||
</script>
|
||||
<%+footer%>
|
|
@ -1,65 +0,0 @@
|
|||
<%#
|
||||
Module for providing data from a mobile satellite navigation system ( mobile modems, etc.)
|
||||
-= Design and Development 2021-2022 =-
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
function button_action(action) {
|
||||
var xhr = new XHR(false);
|
||||
var btn_start = document.getElementById("btn_start");
|
||||
var btn_action = document.getElementById("btn_action");
|
||||
var btn_stop = document.getElementById("btn_stop");
|
||||
var btn_enable = document.getElementById("btn_enable");
|
||||
var btn_disable = document.getElementById("btn_disable");
|
||||
var btn_spinner;
|
||||
switch (action.name) {
|
||||
case "start":
|
||||
btn_spinner = document.getElementById("btn_start_spinner");
|
||||
break;
|
||||
case "action":
|
||||
btn_spinner = document.getElementById("btn_action_spinner");
|
||||
break;
|
||||
case "stop":
|
||||
btn_spinner = document.getElementById("btn_stop_spinner");
|
||||
break;
|
||||
case "enable":
|
||||
btn_spinner = document.getElementById("btn_enable_spinner");
|
||||
break;
|
||||
case "disable":
|
||||
btn_spinner = document.getElementById("btn_disable_spinner");
|
||||
break;
|
||||
}
|
||||
btn_start.disabled = true;
|
||||
btn_action.disabled = true;
|
||||
btn_stop.disabled = true;
|
||||
btn_enable.disabled = true;
|
||||
btn_disable.disabled = true;
|
||||
spinner(btn_spinner, 1);
|
||||
xhr.get('<%=luci.dispatcher.build_url("admin", "services", "gpoint", "action")%>/' + action.name, null,
|
||||
function (x) {
|
||||
if (!x) {
|
||||
return;
|
||||
}
|
||||
btn_start.disabled = false;
|
||||
btn_action.disabled = false;
|
||||
btn_stop.disabled = false;
|
||||
btn_enable.disabled = false;
|
||||
btn_disable.disabled = false;
|
||||
spinner(btn_spinner, 0);
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
function spinner(element, state) {
|
||||
if (state === 1) {
|
||||
element.style.width = "16px";
|
||||
element.innerHTML = '<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" width="16" height="16" style="vertical-align:middle" />';
|
||||
}
|
||||
else {
|
||||
element.style.width = "0px";
|
||||
element.innerHTML = '';
|
||||
}
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
<%#
|
||||
Module for providing data from a mobile satellite navigation system ( mobile modems, etc.)
|
||||
-= Design and Development 2021-2022 =-
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%+cbi/valueheader%>
|
||||
<table style="outline:none;border:none;box-shadow:none;background:transparent;font-weight:bold;line-height:30px;height:30px;width:60em;">
|
||||
<tr class="tr">
|
||||
<td class="td left" id="application">-</td>
|
||||
</tr>
|
||||
<tr class="tr"></tr>
|
||||
<tr class="tr">
|
||||
<td class="td left" id="app">Application: -</td>
|
||||
</tr>
|
||||
<tr class="tr"></tr>
|
||||
<tr class="tr">
|
||||
<td class="td left" id="server">Remote Server: -</td>
|
||||
</tr>
|
||||
<tr class="tr"></tr>
|
||||
<tr class="tr">
|
||||
<td class="td left" id="locat">Yandex Locator: -</td>
|
||||
</tr>
|
||||
<tr class="tr"></tr>
|
||||
<tr class="tr">
|
||||
<td class="td left" id="filter">GpointFilter: -</td>
|
||||
</tr>
|
||||
<tr class="tr"></tr>
|
||||
<tr class="tr">
|
||||
<td class="td left" id="kalmanf">KalmanFilter: -</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
function createForm(f_input, f_name, f_id) {
|
||||
var f = document.getElementById(f_id);
|
||||
var color = "orange";
|
||||
if(f_input) {
|
||||
f.innerHTML = f_name + f_input[1];
|
||||
if (f_input[1] == "OK") {
|
||||
color = "green";
|
||||
}
|
||||
else if(f_input[1] == "OFF") {
|
||||
color = "red";
|
||||
}
|
||||
}
|
||||
document.querySelector("#" + f_id).style.color = color;
|
||||
}
|
||||
|
||||
var pointer = ""
|
||||
XHR.poll(3, '<%=luci.dispatcher.build_url("admin", "services", "gpoint", "geopoint")%>', null,
|
||||
function (x, data) {
|
||||
var app = document.getElementById("application");
|
||||
var color = "green";
|
||||
if(document.getElementById("btn_disable").disabled) {
|
||||
app.innerHTML = "Stop (Disabled)";
|
||||
color = "red";
|
||||
}
|
||||
else if (document.getElementById("btn_stop").disabled) {
|
||||
app.innerHTML = "Stop";
|
||||
color = "orange";
|
||||
}
|
||||
else if(document.getElementById("btn_start").disabled) {
|
||||
app.innerHTML = "Running" + pointer;
|
||||
pointer += '.';
|
||||
if(pointer.length > 3) {
|
||||
pointer = ''
|
||||
}
|
||||
}
|
||||
else {}
|
||||
document.querySelector("#application").style.color = color
|
||||
|
||||
createForm(data.warning.app, "Application: ", "app");
|
||||
createForm(data.warning.server, "Remote Server: ", "server");
|
||||
createForm(data.warning.locator, "Yandex Locator: ", "locat");
|
||||
createForm(data.warning.filter, "GeoHash Filter: ", "filter");
|
||||
createForm(data.warning.kalman, "Kalman filter: ", "kalmanf");
|
||||
});
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<%+cbi/valuefooter%>
|
|
@ -1,5 +0,0 @@
|
|||
config modem_settings 'modem_settings'
|
||||
|
||||
config server_settings 'server_settings'
|
||||
|
||||
config service_settings 'service_settings'
|
|
@ -1,32 +0,0 @@
|
|||
#!/bin/sh /etc/rc.common
|
||||
# Copyright 2021-2022 Vladislav Kadulin {spanky@yandex.ru}
|
||||
|
||||
USE_PROCD=1
|
||||
START=95
|
||||
STOP=01
|
||||
|
||||
CONFIGURATION=gpoint
|
||||
|
||||
start_service() {
|
||||
config_load "${CONFIGURATION}"
|
||||
procd_open_instance
|
||||
procd_append_param command /usr/share/gpoint/gpoint
|
||||
procd_set_param respawn
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_set_param pidfile /var/run/gpoint.pid
|
||||
procd_close_instance
|
||||
echo "gpoint service start"
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
if [ -f /var/run/gpoint.pid ]; then
|
||||
sessionId=$(uci get gpoint.service_settings.sessionid)
|
||||
uci set gpoint.service_settings.sessionid=stop
|
||||
uci commit gpoint
|
||||
ubus call session destroy "{\"ubus_rpc_session\":\"${sessionId}\"}"
|
||||
echo "gpoint service stop"
|
||||
else
|
||||
echo "gpoint not running"
|
||||
fi
|
||||
}
|
|
@ -1,229 +0,0 @@
|
|||
#!/usr/bin/env lua
|
||||
-------------------------------------------------------------
|
||||
-- luci-app-gpoint. Gnss information dashboard for 3G/LTE dongle.
|
||||
-------------------------------------------------------------
|
||||
-- Copyright 2021-2022 Vladislav Kadulin <spanky@yandex.ru>
|
||||
-- Licensed to the GNU General Public License v3.0
|
||||
|
||||
common_path = "/usr/share/gpoint/modems/?.lua;/usr/share/gpoint/lib/?.lua;/usr/share/gpoint/proto/?.lua;/usr/share/gpoint/lib/kalman_filter/?.lua;"
|
||||
package.path = common_path .. package.path
|
||||
|
||||
local config = require("config")
|
||||
local socket = require("socket")
|
||||
local nixio = require("nixio.fs")
|
||||
local ubus = require("ubus")
|
||||
|
||||
|
||||
function portActive(port)
|
||||
local fport = nixio.glob("/dev/tty[A-Z][A-Z]*")
|
||||
for p in fport do
|
||||
if string.find(p, port) then
|
||||
return true, {warning = {app = {false, "OK"}}}
|
||||
end
|
||||
end
|
||||
return false, {warning = {
|
||||
app = {true, "Port is unavailable. Check the modem connections!"},
|
||||
locator = {},
|
||||
server = {}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- all warnings: [1]-true/false, [2]-what err
|
||||
-- initializing modem
|
||||
--------------------------------------------------------------------------------
|
||||
local modemVendor = ""
|
||||
local modemStatus, modemConfig = config.getModemData()
|
||||
if not modemStatus[1] then
|
||||
if string.find(modemConfig.name, "Quectel") then
|
||||
modemVendor = require("que")
|
||||
elseif string.find(modemConfig.name, "Sierra") then
|
||||
modemVendor = require("sierra")
|
||||
elseif string.find(modemConfig.name, "U-Blox") then
|
||||
modemVendor = require("ublox")
|
||||
elseif string.find(modemConfig.name, "Simcom") then
|
||||
modemVendor = require("simcom")
|
||||
elseif string.find(modemConfig.name, "Dell") then
|
||||
modemVendor = require("dell")
|
||||
end
|
||||
end
|
||||
--------------------------------------------------------------------------------
|
||||
-- initializing Remote Server, Remote Server frequency update time
|
||||
--------------------------------------------------------------------------------
|
||||
local gnssProtocol, frequencyDataSend = "", nil
|
||||
local serverStatus, serverConfig = config.getServerData()
|
||||
if not serverStatus[1] then
|
||||
if serverConfig.protocol == "wialon" then
|
||||
gnssProtocol = require("wialon_ips")
|
||||
elseif serverConfig.protocol == "traccar" then
|
||||
gnssProtocol = require("traccar")
|
||||
end
|
||||
frequencyDataSend = os.time() + serverConfig.frequency
|
||||
end
|
||||
--------------------------------------------------------------------------------
|
||||
-- initializing Yandex Locator
|
||||
--------------------------------------------------------------------------------
|
||||
local locator = ""
|
||||
local locatorGPSmiss = 0
|
||||
local locatorStatus, locatorConfig = config.getLoctorData()
|
||||
if not locatorStatus[1] then
|
||||
locator = require("locator")
|
||||
end
|
||||
--------------------------------------------------------------------------------
|
||||
-- Filter coorinate GeoHash Filter
|
||||
--------------------------------------------------------------------------------
|
||||
local FilterGNSS = ""
|
||||
local FilterGNSSdata = {
|
||||
gp = { longitude = "", latitude = "" },
|
||||
gga = { longitude = "", latitude = "" },
|
||||
gns = { longitude = "", latitude = "" },
|
||||
coordHash = 0,
|
||||
changesSize = 0,
|
||||
empty = true
|
||||
}
|
||||
|
||||
local filterStatus, filterConfig = config.getFilterData()
|
||||
if not filterStatus[1] then
|
||||
FilterGNSS = require("geohash")
|
||||
else
|
||||
FilterGNSS, FilterGNSSdata = nil, nil
|
||||
end
|
||||
--------------------------------------------------------------------------------
|
||||
-- Filter coorinate KalmanFilter
|
||||
--------------------------------------------------------------------------------
|
||||
local kalman = {}
|
||||
local kalmanFilter = ""
|
||||
local kalmanIsStop = true;
|
||||
local secondsSinceLastUpdate = os.time()
|
||||
|
||||
local kalmanStatus, kalmanConfig = config.getKalmanData()
|
||||
if not kalmanStatus[1] then
|
||||
kalmanFilter = require("gps_lib")
|
||||
else
|
||||
kalman,kalmanFilter,kalmanIsStop,secondsSinceLastUpdate = nil, nil, nil, nil
|
||||
end
|
||||
--------------------------------------------------------------------------------
|
||||
-- Config Ubus
|
||||
--------------------------------------------------------------------------------
|
||||
local serviceIsStart = {warning={app={true,"Service start"},locator={true,"Loading..."},server={true,"Loading..."}}}
|
||||
local conn = ubus.connect()
|
||||
if not conn then
|
||||
error("Failed to connect to ubus")
|
||||
end
|
||||
|
||||
local createUbusSession = conn:call("session", "create", {timeout = 0})
|
||||
local ubusSessionId = createUbusSession.ubus_rpc_session
|
||||
config.setUbusSessionId(ubusSessionId)
|
||||
conn:call("session", "set", {ubus_rpc_session = ubusSessionId, values = serviceIsStart})
|
||||
serviceIsStart = nil
|
||||
conn:close()
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local timeToUpdateGNSSdata = 3
|
||||
|
||||
while true do
|
||||
-- modem GNSS data
|
||||
local portStatus, GnssData = portActive(modemConfig.port)
|
||||
if portStatus then
|
||||
GnssData = modemVendor.getGNSSdata(modemConfig.port)
|
||||
if GnssData.warning.app[1] then
|
||||
modemVendor.start(modemConfig.port)
|
||||
end
|
||||
end
|
||||
|
||||
-- yandex locator
|
||||
GnssData.warning.locator = locatorStatus
|
||||
if portStatus and not locatorStatus[1] then
|
||||
if GnssData.gp.longitude == "-" and GnssData.gp.latitude == "-" then
|
||||
if locatorGPSmiss >= 3 then
|
||||
local err, latitude, longitude = locator.getLocation(locatorConfig.iface, locatorConfig.key)
|
||||
GnssData.warning.locator = err[1] and err or {false, "Data received from locator..."}
|
||||
if not GnssData.warning.locator[1] then
|
||||
GnssData.gp.latitude = latitude
|
||||
GnssData.gp.longitude = longitude
|
||||
GnssData.gp.date = os.date("%d.%m.%Y")
|
||||
GnssData.gp.utc = os.date("%H:%M", os.time(os.date("!*t"))) -- TODO time + UTF(+3)
|
||||
GnssData.gga.latitude = locator.degreesToNmea(latitude)
|
||||
GnssData.gga.longitude = locator.degreesToNmea(longitude)
|
||||
end
|
||||
else
|
||||
GnssData.warning.locator = {true, "getting navigation data..."}
|
||||
locatorGPSmiss = 1 + locatorGPSmiss
|
||||
end
|
||||
else
|
||||
locatorGPSmiss = 0
|
||||
end
|
||||
end
|
||||
|
||||
-- drift GpointFilter
|
||||
GnssData.warning.filter = filterStatus
|
||||
if portStatus and not GnssData.warning.filter[1] then
|
||||
if not GnssData.warning.gga[1] or not GnssData.warning.gns[1] and GnssData.warning.locator[2] == "OK" then
|
||||
if GnssData.gp.spkm ~= '-' and tonumber(GnssData.gp.spkm) < filterConfig.speed then
|
||||
|
||||
local tmpHash = FilterGNSS.encode(GnssData.gp.latitude, GnssData.gp.longitude, filterConfig.hash)
|
||||
|
||||
if not FilterGNSSdata.empty and tmpHash == FilterGNSSdata.coordHash and FilterGNSSdata.changesSize < filterConfig.changes then
|
||||
FilterGNSSdata.changesSize = 0
|
||||
GnssData.gp.latitude, GnssData.gp.longitude = FilterGNSSdata.gp.latitude, FilterGNSSdata.gp.longitude
|
||||
GnssData.gga.latitude, GnssData.gga.longitude = FilterGNSSdata.gga.latitude, FilterGNSSdata.gga.longitude
|
||||
GnssData.gns.latitude, GnssData.gns.longitude = FilterGNSSdata.gns.latitude, FilterGNSSdata.gns.longitude
|
||||
|
||||
elseif FilterGNSSdata.changesSize >= filterConfig.changes or FilterGNSSdata.empty then
|
||||
FilterGNSSdata.gp.latitude, FilterGNSSdata.gp.longitude = GnssData.gp.latitude, GnssData.gp.longitude
|
||||
if not GnssData.warning.gga[1] then
|
||||
FilterGNSSdata.gga.latitude, FilterGNSSdata.gga.longitude = GnssData.gga.latitude, GnssData.gga.longitude
|
||||
end
|
||||
if not GnssData.warning.gns[1] then
|
||||
FilterGNSSdata.gns.latitude, FilterGNSSdata.gns.longitude = GnssData.gns.latitude, GnssData.gns.longitude
|
||||
end
|
||||
FilterGNSSdata.changesSize, FilterGNSSdata.empty, FilterGNSSdata.coordHash = 0, false, tmpHash
|
||||
else
|
||||
FilterGNSSdata.changesSize = FilterGNSSdata.changesSize + 1
|
||||
end
|
||||
else
|
||||
FilterGNSSdata.changesSize, FilterGNSSdata.empty = 0, true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- KalmanFilter
|
||||
-- TODO enabling and disabling in parking lot
|
||||
GnssData.warning.kalman = kalmanStatus
|
||||
if portStatus and not GnssData.warning.kalman[1] then
|
||||
if not GnssData.warning.gga[1] or not GnssData.warning.gns[1] and GnssData.warning.locator[2] == "OK" then
|
||||
if kalmanIsStop then
|
||||
kalman = kalmanFilter.create_velocity2d(kalmanConfig.noise)
|
||||
kalmanIsStop = false
|
||||
end
|
||||
kalman = kalmanFilter.update_velocity2d(kalman, GnssData.gp.latitude, GnssData.gp.longitude, os.time() - secondsSinceLastUpdate)
|
||||
GnssData.gp.latitude, GnssData.gp.longitude = kalmanFilter.get_lat_lon(kalman)
|
||||
GnssData.gga.latitude, GnssData.gga.longitude = locator.degreesToNmea(GnssData.gp.latitude), ('0' .. locator.degreesToNmea(GnssData.gp.longitude))
|
||||
GnssData.gns.latitude, GnssData.gns.longitude = GnssData.gga.latitude, GnssData.gga.longitude
|
||||
else
|
||||
kalmanIsStop = true
|
||||
end
|
||||
end
|
||||
|
||||
-- remote server
|
||||
-- Filter locator to access send data to server
|
||||
GnssData.warning.server = serverStatus
|
||||
if locatorGPSmiss == 0 or locatorGPSmiss >= 3 then
|
||||
if portStatus and not serverStatus[1] and os.time() >= frequencyDataSend then
|
||||
frequencyDataSend = os.time() + (serverConfig.frequency - timeToUpdateGNSSdata)
|
||||
GnssData.warning.server = gnssProtocol.sendData(GnssData, serverConfig)
|
||||
end
|
||||
end
|
||||
|
||||
-- ubus send json data in session
|
||||
conn = ubus.connect()
|
||||
if conn then
|
||||
conn:call("session", "set", {ubus_rpc_session = ubusSessionId, values = GnssData})
|
||||
conn:close()
|
||||
end
|
||||
|
||||
-- update NMEA coordinate time
|
||||
socket.sleep((locatorGPSmiss == 0 or locatorGPSmiss >= 3) and timeToUpdateGNSSdata or 1)
|
||||
end
|
|
@ -1,95 +0,0 @@
|
|||
-------------------------------------------------------------------
|
||||
-- This module is designed to receive a checksum of GNSS messages,
|
||||
-- Such as crc8 and crc16 checksum.
|
||||
-------------------------------------------------------------------
|
||||
-- Copyright 2021-2022 Vladislav Kadulin <spanky@yandex.ru>
|
||||
-- Licensed to the GNU General Public License v3.0
|
||||
|
||||
checksum = {}
|
||||
|
||||
local function decimalToHex(num)
|
||||
if num == 0 then
|
||||
return '0'
|
||||
end
|
||||
local neg = false
|
||||
if num < 0 then
|
||||
neg = true
|
||||
num = num * -1
|
||||
end
|
||||
local hexstr = "0123456789ABCDEF"
|
||||
local result = ""
|
||||
while num > 0 do
|
||||
local n = math.mod(num, 16)
|
||||
result = string.sub(hexstr, n + 1, n + 1) .. result
|
||||
num = math.floor(num / 16)
|
||||
end
|
||||
if neg then
|
||||
result = '-' .. result
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function BitXOR(a, b)
|
||||
local p, c = 1, 0
|
||||
while a > 0 and b > 0 do
|
||||
local ra, rb = a % 2, b % 2
|
||||
if ra ~= rb then c = c + p end
|
||||
a, b, p = (a - ra) / 2, (b - rb) / 2, p * 2
|
||||
end
|
||||
|
||||
if a < b then a = b end
|
||||
while a > 0 do
|
||||
local ra = a % 2
|
||||
if ra > 0 then c = c + p end
|
||||
a, p = (a - ra) / 2, p * 2
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
local function BitAND(a, b)
|
||||
local p, c = 1,0
|
||||
while a > 0 and b > 0 do
|
||||
local ra, rb = a%2, b%2
|
||||
if ra + rb > 1 then c = c + p end
|
||||
a, b, p = (a - ra) / 2, (b - rb) / 2, p*2
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
local function rshift(x, by)
|
||||
return math.floor(x / 2 ^ by)
|
||||
end
|
||||
|
||||
|
||||
-- Checksum for NMEA data (CRC8)
|
||||
function checksum.crc8(data)
|
||||
local crc8 = string.sub(data, #data - 1)
|
||||
data = string.sub(data, 2, #data - 3)
|
||||
|
||||
local b_sum = string.byte(data, 1)
|
||||
for i = 2, #data do
|
||||
b_sum = BitXOR(b_sum, string.byte(data, i))
|
||||
end
|
||||
|
||||
return decimalToHex(b_sum) == crc8 and true or false
|
||||
end
|
||||
|
||||
-- Checksum for Wialone IPS (CRC16)
|
||||
function checksum.crc16(s)
|
||||
assert(type(s) == 'string')
|
||||
local crc16 = 0x0000
|
||||
for i = 1, #s do
|
||||
local c = s:byte(i)
|
||||
crc16 = BitXOR(crc16, c)
|
||||
for j = 1, 8 do
|
||||
local k = BitAND(crc16, 1)
|
||||
crc16 = rshift(crc16, 1)
|
||||
if k ~= 0 then
|
||||
crc16 = BitXOR(crc16, 0xA001)
|
||||
end
|
||||
end
|
||||
end
|
||||
return decimalToHex(crc16)
|
||||
end
|
||||
|
||||
return checksum
|
|
@ -1,225 +0,0 @@
|
|||
-------------------------------------------------------------------
|
||||
-- Module is used for point configuration and interaction with the UI
|
||||
-------------------------------------------------------------------
|
||||
-- Copyright 2021-2022 Vladislav Kadulin <spanky@yandex.ru>
|
||||
-- Licensed to the GNU General Public License v3.0
|
||||
|
||||
local uci = require("luci.model.uci")
|
||||
local sys = require("luci.sys")
|
||||
|
||||
config = {}
|
||||
|
||||
local CFG = uci:get_all("gpoint")
|
||||
|
||||
-- Status table
|
||||
local STATUS = {
|
||||
APP = {
|
||||
MODEM_OK = {false, "OK"},
|
||||
MODEM_ERROR = {true, "Modem error. Select modem in the settings!"},
|
||||
PORT_ERROR = {true, "Modem Port error. Select port in the settings!"}
|
||||
},
|
||||
SERVER = {
|
||||
SERVICE_ON = {false, "OK"},
|
||||
SERVICE_OFF = {true, "OFF"},
|
||||
IP_ERROR = {true, "Server address error. Enter the server address!"},
|
||||
PORT_ERROR = {true, "Server port error. Set the server port!"},
|
||||
LOGIN_ERROR = {true, "Login (ID) error. Specify the device login!"}
|
||||
},
|
||||
LOCATOR = {
|
||||
SERVICE_ON = {false, "OK"},
|
||||
SERVICE_OFF = {true, "OFF"},
|
||||
API_KEY_ERROR = {true, "Yandex Locator: API key not found!"},
|
||||
WIFI_IFACE_ERROR = {true, "Yandex Locator: Wi-Fi interface not found!"}
|
||||
},
|
||||
FILTER = {
|
||||
SERVICE_ON = {false, "OK"},
|
||||
SERVICE_OFF = {true, "OFF"}
|
||||
}
|
||||
}
|
||||
|
||||
-----------------------------------------------------------------------------------
|
||||
-- APP (Modem Settings)
|
||||
-- 1.Checking the configuration for the presence of the modem name
|
||||
-- 2.Checking the presence of the port in the configuration and whether
|
||||
-- it is in the list of devices, if the device is unavailable, we return a warning
|
||||
-- 3. return err + modem data (name modem and NMEA port modem)
|
||||
-----------------------------------------------------------------------------------
|
||||
function config.getModemData()
|
||||
|
||||
local err = {}
|
||||
local modem = {
|
||||
name = "-",
|
||||
port = "-"
|
||||
}
|
||||
|
||||
if not CFG.modem_settings.modem and CFG.modem_settings.modem == "mnf" then
|
||||
err = STATUS.APP.MODEM_ERROR
|
||||
elseif CFG.modem_settings.port and CFG.modem_settings.port == "pnf" then
|
||||
err = STATUS.APP.PORT_ERROR
|
||||
else
|
||||
err = STATUS.APP.MODEM_OK
|
||||
end
|
||||
|
||||
if not err[1] then
|
||||
modem.name = CFG.modem_settings.modem
|
||||
modem.port = CFG.modem_settings.port
|
||||
end
|
||||
return err, modem
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------------
|
||||
-- Remote Server
|
||||
-- 1.We check whether the server service is enabled or not.
|
||||
-- 2.The correctness of the completed forms is checked such as address, login, port, etc ...
|
||||
-- 3.We return the absence of an error and the server configuration data otherwise an error, nil ...
|
||||
-----------------------------------------------------------------------------------
|
||||
function config.getServerData()
|
||||
|
||||
local err = {}
|
||||
local server = {
|
||||
address = "",
|
||||
port = "",
|
||||
protocol = "",
|
||||
login = "",
|
||||
password = "",
|
||||
frequency = "",
|
||||
blackbox = {
|
||||
enable = "",
|
||||
cycle = "",
|
||||
size = 0
|
||||
}
|
||||
}
|
||||
|
||||
if not CFG.server_settings.server_enable then
|
||||
err = STATUS.SERVER.SERVICE_OFF
|
||||
elseif not CFG.server_settings.server_ip then
|
||||
err = STATUS.SERVER.IP_ERROR
|
||||
elseif not CFG.server_settings.server_port then
|
||||
err = STATUS.SERVER.PORT_ERROR
|
||||
elseif not CFG.server_settings.server_login then
|
||||
err = STATUS.SERVER.LOGIN_ERROR
|
||||
else
|
||||
err = STATUS.SERVER.SERVICE_ON
|
||||
end
|
||||
|
||||
if not err[1] then
|
||||
server.address = CFG.server_settings.server_ip
|
||||
server.port = CFG.server_settings.server_port
|
||||
server.protocol = CFG.server_settings.proto
|
||||
server.login = CFG.server_settings.server_login
|
||||
|
||||
if server.protocol == "wialon" then
|
||||
server.password = CFG.server_settings.server_password or "NA"
|
||||
server.frequency = CFG.server_settings.server_frequency or 5
|
||||
server.blackbox.enable = CFG.server_settings.blackbox_enable and true or false
|
||||
server.blackbox.cycle = CFG.server_settings.blackbox_cycle and true or false
|
||||
server.blackbox.size = CFG.server_settings.blackbox_max_size or 1000
|
||||
elseif server.protocol == "traccar" then
|
||||
server.frequency = CFG.server_settings.server_frequency or 5
|
||||
end
|
||||
|
||||
return err, server
|
||||
else
|
||||
return err, nil
|
||||
end
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------------
|
||||
-- Yandex Locator
|
||||
-- 1.Check Yandex Locator service enable/disable
|
||||
-- 2.Check Yandex API key status enable/disable
|
||||
-- 3.Check Yandex Locator interface status enable/disable
|
||||
-----------------------------------------------------------------------------------
|
||||
function config.getLoctorData()
|
||||
|
||||
local err = {}
|
||||
local locator = {
|
||||
enable = false,
|
||||
iface = "",
|
||||
key = ""
|
||||
}
|
||||
|
||||
if not CFG.service_settings.ya_enable then
|
||||
err = STATUS.LOCATOR.SERVICE_OFF
|
||||
elseif not CFG.service_settings.ya_key then
|
||||
err = STATUS.LOCATOR.API_KEY_ERROR
|
||||
elseif not CFG.service_settings.ya_wifi and CFG.service_settings.ya_wifi == "wnf" then
|
||||
err = STATUS.LOCATOR.WIFI_IFACE_ERROR
|
||||
else
|
||||
err = STATUS.LOCATOR.SERVICE_ON
|
||||
end
|
||||
|
||||
if not err[1] then
|
||||
locator.iface = CFG.service_settings.ya_wifi
|
||||
locator.key = CFG.service_settings.ya_key
|
||||
return err, locator
|
||||
else
|
||||
return err, nil
|
||||
end
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------------
|
||||
-- GpointFilter
|
||||
-- 1. Checking for the filter library
|
||||
-- 2. Check GpointFilter service enable/disable
|
||||
-- 3. Make the settings, if there are none, then we apply the default settings
|
||||
-----------------------------------------------------------------------------------
|
||||
function config.getFilterData()
|
||||
|
||||
local err = {}
|
||||
local filter = {
|
||||
enable = false,
|
||||
changes = 0,
|
||||
hash ='0',
|
||||
speed = 0
|
||||
}
|
||||
|
||||
if not CFG.service_settings.filter_enable then
|
||||
err = STATUS.FILTER.SERVICE_OFF
|
||||
else
|
||||
err = STATUS.FILTER.SERVICE_ON
|
||||
filter.enable = true
|
||||
filter.changes = tonumber(CFG.service_settings.filter_changes or 3)
|
||||
filter.hash = tostring(CFG.service_settings.filter_hash or'7')
|
||||
filter.speed = tonumber(CFG.service_settings.filter_speed or 2)
|
||||
end
|
||||
|
||||
return err, filter
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------------
|
||||
-- KalmanFilter
|
||||
-- 1. Checking for the kalman filter library
|
||||
-- 2. Check KalmanFilter service enable/disable
|
||||
-- 3. Make the settings, if there are none, then we apply the default settings
|
||||
-----------------------------------------------------------------------------------
|
||||
function config.getKalmanData()
|
||||
|
||||
local err = {}
|
||||
local filter = {
|
||||
enable = false,
|
||||
noise = 0
|
||||
}
|
||||
|
||||
if not CFG.service_settings.kalman_enable then
|
||||
err = STATUS.FILTER.SERVICE_OFF
|
||||
else
|
||||
err = STATUS.FILTER.SERVICE_ON
|
||||
filter.enable = true
|
||||
filter.noise = tonumber(CFG.service_settings.kalman_noise or 1.0)
|
||||
end
|
||||
|
||||
return err, filter
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------------
|
||||
-- Session ID
|
||||
-- 1.When initializing the ubus, we write the session id to the uci to work with the UI
|
||||
-----------------------------------------------------------------------------------
|
||||
function config.setUbusSessionId(id)
|
||||
uci:set("gpoint", "service_settings", "sessionid", id)
|
||||
uci:save("gpoint")
|
||||
uci:commit("gpoint")
|
||||
end
|
||||
|
||||
return config
|
|
@ -1,147 +0,0 @@
|
|||
-- Geohash
|
||||
-- (c) 2015 Ivan Ribeiro Rocha (ivan.ribeiro@gmail.com)
|
||||
-- (c) 2022 modified by Vladislav Kadulin (spanky@yandex.ru)
|
||||
|
||||
local bit = require("bit32")
|
||||
|
||||
geohash = {}
|
||||
|
||||
local BITS = { 16, 8, 4, 2, 1 }
|
||||
local BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz"
|
||||
|
||||
local NEIGHBORS = { right = { even = "bc01fg45238967deuvhjyznpkmstqrwx" },
|
||||
left = { even = "238967debc01fg45kmstqrwxuvhjyznp" },
|
||||
top = { even = "p0r21436x8zb9dcf5h7kjnmqesgutwvy" },
|
||||
bottom = { even = "14365h7k9dcfesgujnmqp0r2twvyx8zb" } }
|
||||
|
||||
local BORDERS = { right = { even = "bcfguvyz" },
|
||||
left = { even = "0145hjnp" },
|
||||
top = { even = "prxz" },
|
||||
bottom = { even = "028b" } }
|
||||
|
||||
NEIGHBORS.bottom.odd = NEIGHBORS.left.even
|
||||
NEIGHBORS.top.odd = NEIGHBORS.right.even
|
||||
NEIGHBORS.left.odd = NEIGHBORS.bottom.even
|
||||
NEIGHBORS.right.odd = NEIGHBORS.top.even
|
||||
|
||||
BORDERS.bottom.odd = BORDERS.left.even
|
||||
BORDERS.top.odd = BORDERS.right.even
|
||||
BORDERS.left.odd = BORDERS.bottom.even
|
||||
BORDERS.right.odd = BORDERS.top.even
|
||||
|
||||
function geohash.decode(hash)
|
||||
local flip = true;
|
||||
local coords = { latitude = { -90.0, 90.0 },
|
||||
longitude = { -180.0, 180.0 } }
|
||||
|
||||
for i = 1, #hash do
|
||||
local c = hash:sub(i, i)
|
||||
local cd = BASE32:find(c) - 1
|
||||
for j = 1, 5 do
|
||||
mask = BITS[j]
|
||||
local tab = (flip and coords.longitude) or coords.latitude
|
||||
local idx = (bit.band(cd, mask) > 0) and 1 or 2
|
||||
tab[idx] = (tab[1] + tab[2]) / 2
|
||||
flip = not flip
|
||||
end
|
||||
end
|
||||
|
||||
for k, _ in pairs(coords) do
|
||||
coords[k][3] = (coords[k][1] + coords[k][2]) / 2
|
||||
end
|
||||
|
||||
return { lat = coords.latitude, lon = coords.longitude }
|
||||
|
||||
end
|
||||
|
||||
function geohash.encode(latitude, longitude, precision)
|
||||
local lat = { -90.0, 90.0 }
|
||||
local lon = { -180.0, 180.0 }
|
||||
local b, ch, flip = 0, 0, true
|
||||
local res = "";
|
||||
|
||||
latitude = tonumber(latitude)
|
||||
longitude = tonumber(longitude)
|
||||
precision = tonumber(precision)
|
||||
local precision = precision or 12
|
||||
|
||||
while #res < precision do
|
||||
local tab = flip and lon or lat
|
||||
local grd = flip and longitude or latitude
|
||||
|
||||
mid = (tab[1] + tab[2]) / 2
|
||||
|
||||
if grd > mid then
|
||||
ch = bit.bor(ch, BITS[b + 1])
|
||||
tab[1] = mid
|
||||
else
|
||||
tab[2] = mid
|
||||
end
|
||||
|
||||
flip = not flip;
|
||||
|
||||
if b < 4 then
|
||||
b = b + 1
|
||||
else
|
||||
res = res..BASE32:sub(ch + 1, ch + 1);
|
||||
b, ch = 0, 0
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
function geohash.calculate_distance(lat1, lon1, lat2, lon2)
|
||||
local R = 6371000
|
||||
local r1, r2 = math.rad(lat1), math.rad(lat2)
|
||||
local dlat, dlon = math.rad((lat2-lat1)), math.rad((lon2-lon1))
|
||||
local a = math.sin(dlat/2) * math.sin(dlat/2) +
|
||||
math.cos(r1) * math.cos(r2) *
|
||||
math.sin(dlon/2) * math.sin(dlon/2)
|
||||
local c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
|
||||
return R * c
|
||||
end
|
||||
|
||||
function geohash.distance(hash1, hash2)
|
||||
local t1, t2 = decode(hash1), decode(hash2)
|
||||
return calculate_distance(coord(t1).lat, coord(t1).lon,
|
||||
coord(t2).lat, coord(t2).lon)
|
||||
end
|
||||
|
||||
function geohash.neighbor(hash, dir)
|
||||
hash = hash:lower()
|
||||
local len = #hash
|
||||
local last = hash:sub(len, len);
|
||||
local flip = ((math.mod(len,2) == 0) and 'even') or 'odd'
|
||||
local base = hash:sub(1, len - 1)
|
||||
if BORDERS[dir][flip]:find(last) then
|
||||
base = neighbor(base, dir)
|
||||
end
|
||||
local n = NEIGHBORS[dir][flip]:find(last)
|
||||
return base..BASE32:sub(n, n)
|
||||
end
|
||||
|
||||
function geohash.neighbors(hash)
|
||||
local neighbors = { top = neighbor(hash, 'top'),
|
||||
bottom = neighbor(hash, 'bottom'),
|
||||
right = neighbor(hash, 'right'),
|
||||
left = neighbor(hash, 'left') }
|
||||
neighbors.topleft = neighbor(neighbors.left, 'top');
|
||||
neighbors.topright = neighbor(neighbors.right, 'top');
|
||||
neighbors.bottomleft = neighbor(neighbors.left, 'bottom');
|
||||
neighbors.bottomright = neighbor(neighbors.right, 'bottom');
|
||||
return neighbors
|
||||
end
|
||||
|
||||
function geohash.coord(t)
|
||||
if type(t) == 'table' then
|
||||
return { lat = t.lat[3], lon = t.lon[3] }
|
||||
end
|
||||
return coord(decode(t))
|
||||
end
|
||||
|
||||
function geohash.coord_str(t)
|
||||
local t = coord(t)
|
||||
return string.format("lat: %s and lon: %s", tostring(t.lat), tostring(t.lon))
|
||||
end
|
||||
|
||||
return geohash
|
|
@ -1,111 +0,0 @@
|
|||
local kalman = require("kalman_lib")
|
||||
local matrix = require("matrix_lib")
|
||||
|
||||
gps_lib = {}
|
||||
|
||||
local PI = 3.14159265
|
||||
local EARTH_RADIUS_IN_MILES = 3963.1676
|
||||
|
||||
function gps_lib.set_seconds_per_timestep(kalman_filter, seconds_per_timestep)
|
||||
local unit_scaler = 0.001
|
||||
kalman_filter.state_transition[1][3] = unit_scaler * seconds_per_timestep
|
||||
kalman_filter.state_transition[2][4] = unit_scaler * seconds_per_timestep
|
||||
return kalman_filter
|
||||
end
|
||||
|
||||
function gps_lib.create_velocity2d(noise)
|
||||
local kalman_filter = kalman.create(4, 2)
|
||||
local v2p = 0.001
|
||||
|
||||
kalman_filter.state_transition = matrix.set_identity(kalman_filter.state_transition)
|
||||
kalman_filter = gps_lib.set_seconds_per_timestep(kalman_filter, 1.0)
|
||||
kalman_filter.observation_model = matrix.set(kalman_filter.observation_model,
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0)
|
||||
|
||||
local pos = 0.000001
|
||||
kalman_filter.process_noise_covariance = matrix.set(kalman_filter.process_noise_covariance,
|
||||
pos, 0.0, 0.0, 0.0,
|
||||
0.0, pos, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0)
|
||||
|
||||
kalman_filter.observation_noise_covariance = matrix.set(kalman_filter.observation_noise_covariance,
|
||||
pos * noise, 0.0,
|
||||
0.0, pos * noise)
|
||||
|
||||
kalman_filter.state_estimate = matrix.set(kalman_filter.state_estimate, 0.0, 0.0, 0.0, 0.0)
|
||||
kalman_filter.estimate_covariance = matrix.set_identity(kalman_filter.estimate_covariance)
|
||||
local trillion = 1000.0 * 1000.0 * 1000.0 * 1000.0
|
||||
kalman_filter.estimate_covariance = matrix.scale(kalman_filter.estimate_covariance, trillion)
|
||||
return kalman_filter
|
||||
end
|
||||
|
||||
function gps_lib.update_velocity2d(kalman_filter, lat, lon, seconds_since_last_timestep)
|
||||
kalman_filter = gps_lib.set_seconds_per_timestep(kalman_filter, seconds_since_last_timestep)
|
||||
kalman_filter.observation = matrix.set(kalman_filter.observation, lat * 1000.0, lon * 1000.0)
|
||||
kalman_filter = kalman.update(kalman_filter)
|
||||
return kalman_filter
|
||||
end
|
||||
|
||||
function gps_lib.get_lat_lon(kalman_filter)
|
||||
return string.format("%0.6f", kalman_filter.state_estimate[1][1] / 1000.0),
|
||||
string.format("%0.6f", kalman_filter.state_estimate[2][1] / 1000.0)
|
||||
end
|
||||
|
||||
function gps_lib.get_velocity(kalman_filter)
|
||||
return kalman_filter.state_estimate[3][1] / (1000.0 * 1000.0),
|
||||
kalman_filter.state_estimate[4][1] / (1000.0 * 1000.0)
|
||||
end
|
||||
|
||||
function gps_lib.get_bearing(kalman_filter)
|
||||
local lat, lon = gps_lib.get_lat_lon(kalman_filter)
|
||||
local delta_lat, delta_lon = gps_lib.get_velocity(kalman_filter)
|
||||
|
||||
local to_radians = PI / 180.0
|
||||
lat = lat * to_radians
|
||||
lon = lon * to_radians
|
||||
delta_lat = delta_lat * to_radians
|
||||
delta_lon = delta_lon * to_radians
|
||||
|
||||
local lat1 = lat - delta_lat
|
||||
local y = math.sin(delta_lon) * math.cos(lat)
|
||||
local x = math.cos(lat1) * math.sin(lat) - math.sin(lat1) * math.cos(lat) * math.cos(delta_lon)
|
||||
local bearing = math.atan2(y, x)
|
||||
|
||||
bearing = bearing / to_radians
|
||||
while bearing >= 360 do
|
||||
bearing = bearing - 360
|
||||
end
|
||||
while bearing < 0 do
|
||||
bearing = bearing + 360
|
||||
end
|
||||
return bearing
|
||||
end
|
||||
|
||||
function gps_lib.calculate_mph(lat, lon, delta_lat, delta_lon)
|
||||
local to_radians = PI / 180
|
||||
lat = lat * to_radians
|
||||
lon = lon * to_radians
|
||||
delta_lat = delta_lat * to_radians
|
||||
delta_lon = delta_lon * to_radians
|
||||
|
||||
local lat1 = lat - delta_lat
|
||||
local sin_half_dlat = math.sin(delta_lat / 2)
|
||||
local sin_half_dlon = math.sin(delta_lon / 2)
|
||||
|
||||
local a = sin_half_dlat * sin_half_dlat + math.cos(lat1) * math.cos(lat) * sin_half_dlon * sin_half_dlon
|
||||
local radians_per_second = 2 * math.atan2(1000 * math.sqrt(a), 1000 * math.sqrt(1.0 - a))
|
||||
|
||||
local miles_per_second = radians_per_second * EARTH_RADIUS_IN_MILES
|
||||
local miles_per_hour = miles_per_second * 60 * 60
|
||||
return miles_per_hour
|
||||
end
|
||||
|
||||
function gps_lib.get_mph(kalman_filter)
|
||||
local lat, lon = gps_lib.get_lat_lon(kalman_filter)
|
||||
local delta_lat, delta_lon = gps_lib.get_velocity(kalman_filter)
|
||||
return gps_lib.calculate_mph(lat, lon, delta_lat, delta_lon)
|
||||
end
|
||||
|
||||
return gps_lib
|
|
@ -1,82 +0,0 @@
|
|||
local matrix = require("matrix_lib")
|
||||
|
||||
kalman_lib = {}
|
||||
|
||||
function kalman_lib.create(state, observation)
|
||||
local kalman = {
|
||||
timestep = 0, -- K
|
||||
-- These parameters define the size of the matrices.
|
||||
state_dimension = state,
|
||||
observation_dimension = observation,
|
||||
|
||||
-- This group of matrices must be specified by the user.
|
||||
state_transition = matrix.create(state, state), -- F_k
|
||||
observation_model = matrix.create(observation, state), -- H_k
|
||||
process_noise_covariance = matrix.create(state, state), -- Q_k
|
||||
observation_noise_covariance = matrix.create(observation, observation), -- R_k
|
||||
|
||||
-- The observation is modified by the user before every time step.
|
||||
observation = matrix.create(observation, 1), -- z_k
|
||||
|
||||
-- This group of matrices are updated every time step by the filter.
|
||||
predicted_state = matrix.create(state, 1), -- x-hat_k|k-1
|
||||
predicted_estimate_covariance = matrix.create(state, state), -- P_k|k-1
|
||||
innovation = matrix.create(observation, 1), -- y-tilde_k
|
||||
innovation_covariance = matrix.create(observation, observation), -- S_k
|
||||
inverse_innovation_covariance = matrix.create(observation, observation), -- S_k^-1
|
||||
optimal_gain = matrix.create(state, observation), -- K_k
|
||||
state_estimate = matrix.create(state, 1), -- x-hat_k|k
|
||||
estimate_covariance = matrix.create(state, state), -- P_k|k
|
||||
|
||||
-- This group is used for meaningless intermediate calculations.
|
||||
vertical_scratch = matrix.create(state, observation),
|
||||
mall_square_scratch = matrix.create(observation, observation),
|
||||
big_square_scratch = matrix.create(state, state)
|
||||
}
|
||||
return kalman
|
||||
end
|
||||
|
||||
function kalman_lib.predict(kalman)
|
||||
kalman.timestep = kalman.timestep + 1
|
||||
-- Predict the state
|
||||
kalman.predicted_state = matrix.multiply(kalman.state_transition, kalman.state_estimate, kalman.predicted_state)
|
||||
-- Predict the state estimate covariance
|
||||
kalman.big_square_scratch = matrix.multiply(kalman.state_transition, kalman.estimate_covariance, kalman.big_square_scratch)
|
||||
kalman.predicted_estimate_covariance = matrix.multiply_by_transpose(kalman.big_square_scratch, kalman.state_transition, kalman.predicted_estimate_covariance)
|
||||
kalman.predicted_estimate_covariance = matrix.add(kalman.predicted_estimate_covariance, kalman.process_noise_covariance, kalman.predicted_estimate_covariance)
|
||||
return kalman
|
||||
end
|
||||
|
||||
function kalman_lib.estimate(kalman)
|
||||
-- Calculate innovation
|
||||
kalman.innovation = matrix.multiply(kalman.observation_model, kalman.predicted_state, kalman.innovation)
|
||||
kalman.innovation = matrix.subtract(kalman.observation, kalman.innovation, kalman.innovation)
|
||||
-- Calculate innovation covariance
|
||||
kalman.vertical_scratch = matrix.multiply_by_transpose(kalman.predicted_estimate_covariance, kalman.observation_model, kalman.vertical_scratch)
|
||||
kalman.innovation_covariance = matrix.multiply(kalman.observation_model, kalman.vertical_scratch, kalman.innovation_covariance)
|
||||
kalman.innovation_covariance = matrix.add(kalman.innovation_covariance, kalman.observation_noise_covariance, kalman.innovation_covariance)
|
||||
-- Invert the innovation covariance.
|
||||
-- Note: this destroys the innovation covariance.
|
||||
-- TODO: handle inversion failure intelligently.
|
||||
matrix.destructive_invert(kalman.innovation_covariance, kalman.inverse_innovation_covariance)
|
||||
-- Calculate the optimal Kalman gain.
|
||||
-- Note we still have a useful partial product in vertical scratch
|
||||
-- from the innovation covariance.
|
||||
kalman.optimal_gain = matrix.multiply(kalman.vertical_scratch, kalman.inverse_innovation_covariance, kalman.optimal_gain)
|
||||
-- Estimate the state
|
||||
kalman.state_estimate = matrix.multiply(kalman.optimal_gain, kalman.innovation, kalman.state_estimate)
|
||||
kalman.state_estimate = matrix.add(kalman.state_estimate, kalman.predicted_state, kalman.state_estimate)
|
||||
-- Estimate the state covariance
|
||||
kalman.big_square_scratch = matrix.multiply(kalman.optimal_gain, kalman.observation_model, kalman.big_square_scratch)
|
||||
kalman.big_square_scratch = matrix.subtract_from_identity(kalman.big_square_scratch)
|
||||
kalman.estimate_covariance = matrix.multiply(kalman.big_square_scratch, kalman.predicted_estimate_covariance, kalman.estimate_covariance)
|
||||
return kalman
|
||||
end
|
||||
|
||||
function kalman_lib.update(kalman)
|
||||
kalman = kalman_lib.predict(kalman)
|
||||
kalman = kalman_lib.estimate(kalman)
|
||||
return kalman
|
||||
end
|
||||
|
||||
return kalman_lib
|
|
@ -1,191 +0,0 @@
|
|||
matrix_lib = {}
|
||||
|
||||
function matrix_lib.create(rows, cols)
|
||||
local matrix = {}
|
||||
for i = 1,rows do
|
||||
matrix[i] = {}
|
||||
for j = 1,cols do
|
||||
matrix[i][j] = 0.0
|
||||
end
|
||||
end
|
||||
return matrix
|
||||
end
|
||||
|
||||
function matrix_lib.print(matrix)
|
||||
for i = 1, #matrix do
|
||||
for j = 1, #matrix[i] do
|
||||
io.write(matrix[i][j] .. " ")
|
||||
end
|
||||
io.write('\n')
|
||||
end
|
||||
end
|
||||
|
||||
function matrix_lib.set(matrix, ...)
|
||||
local k = 1
|
||||
for i = 1, #matrix do
|
||||
for j = 1, #matrix[i] do
|
||||
if arg[k] ~= nil then
|
||||
matrix[i][j] = arg[k]
|
||||
end
|
||||
k = k + 1
|
||||
end
|
||||
end
|
||||
return matrix
|
||||
end
|
||||
|
||||
function matrix_lib.set_identity(matrix)
|
||||
for i = 1, #matrix do
|
||||
for j = 1, #matrix[i] do
|
||||
matrix[i][j] = i == j and 1.0 or 0.0
|
||||
end
|
||||
end
|
||||
return matrix
|
||||
end
|
||||
|
||||
function matrix_lib.copy(matrix)
|
||||
local copy = {}
|
||||
for i = 1, #matrix do
|
||||
copy[i] = {}
|
||||
for j = 1, #matrix[i] do
|
||||
copy[i][j] = matrix[i][j]
|
||||
end
|
||||
end
|
||||
return copy
|
||||
end
|
||||
|
||||
function matrix_lib.add(matrix_a, matrix_b, matrix_c)
|
||||
for i = 1, #matrix_a do
|
||||
for j = 1, #matrix_a[i] do
|
||||
matrix_c[i][j] = matrix_a[i][j] + matrix_b[i][j]
|
||||
end
|
||||
end
|
||||
return matrix_c
|
||||
end
|
||||
|
||||
function matrix_lib.subtract(matrix_a, matrix_b, matrix_c)
|
||||
for i = 1, #matrix_a do
|
||||
for j = 1, #matrix_a[i] do
|
||||
matrix_c[i][j] = matrix_a[i][j] - matrix_b[i][j]
|
||||
end
|
||||
end
|
||||
return matrix_c
|
||||
end
|
||||
|
||||
function matrix_lib.subtract_from_identity(matrix)
|
||||
for i = 1, #matrix do
|
||||
for j = 1, #matrix[i] do
|
||||
matrix[i][j] = i == j and (1.0 - matrix[i][j]) or (0.0 - matrix[i][j])
|
||||
end
|
||||
end
|
||||
return matrix
|
||||
end
|
||||
|
||||
function matrix_lib.multiply(matrix_a, matrix_b, matrix_c)
|
||||
for i = 1, #matrix_c do
|
||||
for j = 1, #matrix_c[i] do
|
||||
matrix_c[i][j] = 0.0
|
||||
for k = 1, #matrix_a[i] do
|
||||
matrix_c[i][j] = matrix_c[i][j] + (matrix_a[i][k] * matrix_b[k][j])
|
||||
end
|
||||
end
|
||||
end
|
||||
return matrix_c
|
||||
end
|
||||
|
||||
function matrix_lib.multiply_by_transpose(matrix_a, matrix_b, matrix_c)
|
||||
for i = 1, #matrix_c do
|
||||
for j = 1, #matrix_c[i] do
|
||||
matrix_c[i][j] = 0.0
|
||||
for k = 1, #matrix_a[1] do
|
||||
matrix_c[i][j] = matrix_c[i][j] + (matrix_a[i][k] * matrix_b[j][k])
|
||||
end
|
||||
end
|
||||
end
|
||||
return matrix_c
|
||||
end
|
||||
|
||||
function matrix_lib.transpose(matrix_input, matrix_output)
|
||||
for i = 1, #matrix_input do
|
||||
for j = 1, #matrix_input[i] do
|
||||
matrix_output[j][i] = matrix_input[i][j]
|
||||
end
|
||||
end
|
||||
return matrix_output
|
||||
end
|
||||
|
||||
function matrix_lib.equal(matrix_a, matrix_b, tolerance)
|
||||
for i = 1, #matrix_a do
|
||||
for j = 1, #matrix_a[i] do
|
||||
if math.abs(matrix_a[i][j] - matrix_b[i][j]) > tolerance then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function matrix_lib.scale(matrix, scalar)
|
||||
for i = 1, #matrix do
|
||||
for j = 1, #matrix[i] do
|
||||
matrix[i][j] = matrix[i][j] * scalar
|
||||
end
|
||||
end
|
||||
return matrix
|
||||
end
|
||||
|
||||
function matrix_lib.swap_rows(matrix, r1, r2)
|
||||
local tmp = matrix[r1]
|
||||
matrix[r1] = matrix[r2]
|
||||
matrix[r2] = tmp
|
||||
return matrix
|
||||
end
|
||||
|
||||
function matrix_lib.scale_row(matrix, r, scalar)
|
||||
for i = 1, #matrix do
|
||||
matrix[r][i] = matrix[r][i] * scalar
|
||||
end
|
||||
return matrix
|
||||
end
|
||||
|
||||
function matrix_lib.shear_row(matrix, r1, r2, scalar)
|
||||
for i = 1, #matrix do
|
||||
matrix[r1][i] = matrix[r1][i] + (scalar * matrix[r2][i])
|
||||
end
|
||||
return matrix
|
||||
end
|
||||
|
||||
function matrix_lib.destructive_invert(matrix_input, matrix_output)
|
||||
matrix_output = matrix_lib.set_identity(matrix_output)
|
||||
for i = 1, #matrix_input do
|
||||
if matrix_input[i][i] == 0.0 then
|
||||
local j
|
||||
for j = i + 1, #matrix_input do
|
||||
if matrix_input[r][i] ~= 0.0 then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if j == #matrix_input then
|
||||
return
|
||||
end
|
||||
|
||||
matrix_input = matrix_lib.swap_rows(matrix_input, i, j)
|
||||
matrix_output = matrix_lib.swap_rows(matrix_output, i, j)
|
||||
end
|
||||
|
||||
local scalar = 1.0 / matrix_input[i][i]
|
||||
matrix_input = matrix_lib.scale_row(matrix_input, i, scalar)
|
||||
matrix_output = matrix_lib.scale_row(matrix_output, i, scalar)
|
||||
|
||||
for r = 1, #matrix_input do
|
||||
if i ~= r then
|
||||
local shear_needed = -matrix_input[r][i]
|
||||
matrix_input = matrix_lib.shear_row(matrix_input, r, i, shear_needed)
|
||||
matrix_output = matrix_lib.shear_row(matrix_output, r, i, shear_needed)
|
||||
end
|
||||
end
|
||||
end
|
||||
return matrix_input, matrix_output
|
||||
end
|
||||
|
||||
return matrix_lib
|
|
@ -1,78 +0,0 @@
|
|||
-------------------------------------------------------------------
|
||||
-- Module is designed to work with the Yandex Locator API
|
||||
-- (WiFi is required!)
|
||||
-------------------------------------------------------------------
|
||||
-- Copyright 2021-2022 Vladislav Kadulin <spanky@yandex.ru>
|
||||
-- Licensed to the GNU General Public License v3.0
|
||||
|
||||
local json = require("luci.jsonc")
|
||||
local sys = require("luci.sys")
|
||||
local iwinfo = require("iwinfo")
|
||||
|
||||
locator = {}
|
||||
|
||||
local function configJSON(jsonData, iface, key)
|
||||
jsonData.common.api_key = key
|
||||
local inter = iwinfo.type(iface)
|
||||
local scanlist = iwinfo[inter].scanlist(iface)
|
||||
for _, v in pairs(scanlist) do
|
||||
v.bssid = string.gsub(v.bssid, ':', '')
|
||||
table.insert(jsonData.wifi_networks, {["mac"] = v.bssid, ["signal_strength"] = v.signal})
|
||||
end
|
||||
end
|
||||
|
||||
local function request(curl, jsonData)
|
||||
curl = curl .. json.stringify(jsonData) .. '\''
|
||||
local res = sys.exec(curl)
|
||||
if res == "" then
|
||||
res = "{\"error\": {\"message\":\"No internet connection\"}}"
|
||||
end
|
||||
return json.parse(res)
|
||||
end
|
||||
|
||||
-- Converter from degrees to NMEA data.
|
||||
function locator.degreesToNmea(coord)
|
||||
local degrees = math.floor(coord)
|
||||
coord = math.abs(coord) - degrees
|
||||
local sign = coord < 0 and "-" or ""
|
||||
return sign .. string.format("%02i%02.5f", degrees, coord * 60.00)
|
||||
end
|
||||
|
||||
-- Getting data coordinates via Yandex API
|
||||
function locator.getLocation(iface_name, api_key)
|
||||
local curl = "curl -X POST 'http://api.lbs.yandex.net/geolocation' -d 'json="
|
||||
local jsonData = {
|
||||
wifi_networks = {},
|
||||
common = {
|
||||
version = "1.0",
|
||||
api_key = ""
|
||||
}
|
||||
}
|
||||
|
||||
configJSON(jsonData, iface_name, api_key)
|
||||
local location = request(curl, jsonData)
|
||||
local err = {false, "OK"}
|
||||
local latitude = ""
|
||||
local longitude = ""
|
||||
local altitude = ""
|
||||
|
||||
if location.error then
|
||||
err = {true, location.error.message}
|
||||
end
|
||||
|
||||
if location.position then
|
||||
if tonumber(location.position.precision) >= 100000 then
|
||||
err = {true, "Bad precision"}
|
||||
else
|
||||
latitude = string.format("%0.8f", location.position.latitude)
|
||||
longitude = string.format("%0.8f", location.position.longitude)
|
||||
if latitude == "" or longitude == "" then
|
||||
err = {true, "Bad data..."}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return err, latitude, longitude
|
||||
end
|
||||
|
||||
return locator
|
|
@ -1,385 +0,0 @@
|
|||
-------------------------------------------------------------
|
||||
-- This module is designed to extract data from NMEA messages.
|
||||
-- All data is combined into a table "GnssData".
|
||||
-------------------------------------------------------------
|
||||
-- Copyright 2021-2022 Vladislav Kadulin <spanky@yandex.ru>
|
||||
-- Licensed to the GNU General Public License v3.0
|
||||
|
||||
local uci = require("luci.model.uci")
|
||||
local serial = require("serial")
|
||||
local checksum = require("checksum")
|
||||
|
||||
|
||||
local nmea = {}
|
||||
|
||||
-- Table for navigation data
|
||||
local function createGnssForm()
|
||||
local GnssForm = {
|
||||
warning = {
|
||||
app = {true, ""},
|
||||
gga = {true, ""},
|
||||
rmc = {true, ""},
|
||||
vtg = {true, ""},
|
||||
gsa = {true, ""},
|
||||
gp = {true, ""},
|
||||
gns = {true, ""},
|
||||
server = {true, ""},
|
||||
locator = {true, ""}
|
||||
},
|
||||
gp = { longitude = "-", latitude = "-"},
|
||||
gga = { longitude = "-", latitude = "-"}
|
||||
}
|
||||
return GnssForm
|
||||
end
|
||||
|
||||
--Converting coordinates from the NMEA protocol to degrees
|
||||
local function nmeaCoordinatesToDouble(coord)
|
||||
local deg = math.floor(coord / 100)
|
||||
return deg + (coord - 100 * deg) / 60
|
||||
end
|
||||
|
||||
--We are looking for the desired data line in the line received from the device
|
||||
local function findInResp(data, begin)
|
||||
local err = true
|
||||
local b = string.find(data, begin)
|
||||
local e = string.find(data, "\r\n", b)
|
||||
|
||||
if b and e then
|
||||
err = false
|
||||
else
|
||||
b, e = nil, nil
|
||||
end
|
||||
return err, b, e
|
||||
end
|
||||
|
||||
-- message parsing, checksum checking
|
||||
local function getCropData(data, msg)
|
||||
local err, b, e = findInResp(data, msg)
|
||||
if not err then
|
||||
data = string.gsub(string.sub(data, b, e), '%c', "")
|
||||
if checksum.crc8(data) then
|
||||
data = string.gsub(data, msg, '', 1)
|
||||
data = string.gsub(data, "*%d+%w+", '', 1)
|
||||
err = {false, "OK"}
|
||||
else
|
||||
err = {true, "Checksum error"}
|
||||
data = nil
|
||||
end
|
||||
else
|
||||
err = {true, "No data found"}
|
||||
data = nil
|
||||
end
|
||||
return err, data
|
||||
end
|
||||
|
||||
-- Creating a table with data before adding data to a single space
|
||||
function doTable(data, keys)
|
||||
local parseData = {}
|
||||
|
||||
while string.find(data, ',,') do
|
||||
data = string.gsub(data, ',,', ",-,")
|
||||
end
|
||||
|
||||
if string.sub(data, 1, 1) == ',' then
|
||||
data = '-' .. data
|
||||
end
|
||||
|
||||
local i = 1
|
||||
for val in string.gmatch(data, "[^,]+") do
|
||||
parseData[keys[i]] = val
|
||||
i = i + 1
|
||||
end
|
||||
return parseData
|
||||
end
|
||||
|
||||
-- The function of searching the time zone by the received coordinates
|
||||
local function findTimeZone(time, date, lon)
|
||||
local datetime = { year,month,day,hour,min,sec }
|
||||
local timeZone = uci:get("gpoint", "modem_settings", "timezone")
|
||||
|
||||
-- calculate the time zone by coordinates
|
||||
if timeZone == nil or timeZone == "auto" then
|
||||
timeZone = math.floor((tonumber(lon) + (7.5 * (tonumber(lon) > 0 and 1.0 or -1.0))) / 15.0)
|
||||
end
|
||||
|
||||
datetime.hour, datetime.min, datetime.sec = string.match(time, "(%d%d)(%d%d)(%d%d)")
|
||||
datetime.day, datetime.month, datetime.year = string.match(date,"(%d%d)(%d%d)(%d%d)")
|
||||
datetime.year = "20" .. datetime.year -- Someone change this to 21 in the 2100 year
|
||||
|
||||
--we request the unix time and then add the time zone
|
||||
local unix = os.time(datetime)
|
||||
unix = unix + ((math.floor(tonumber(timeZone) * 100)) % 100) * 36
|
||||
return unix + math.floor(tonumber(timeZone)) * 3600
|
||||
end
|
||||
|
||||
-- Add 0 for the time and date values if < 10
|
||||
local function addZero(val)
|
||||
return tonumber(val) > 9 and tostring(val) or '0' .. tostring(val)
|
||||
end
|
||||
|
||||
-- If there is no data, the default values of the table are dashed
|
||||
local function addDash(data)
|
||||
local dashData = {}
|
||||
for i=1, #data do
|
||||
dashData[data[i]] = '-'
|
||||
end
|
||||
return dashData
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------
|
||||
-- GGA - Global Positioning System Fix Data
|
||||
local function getGGA(GnssData, resp)
|
||||
GnssData.gga = {
|
||||
"utc", -- UTC of this position report, hh is hours, mm is minutes, ss.ss is seconds.
|
||||
"latitude", -- Latitude, dd is degrees, mm.mm is minutes
|
||||
"ne", -- N or S (North or South)
|
||||
"longitude", -- Longitude, dd is degrees, mm.mm is minutes
|
||||
"ew", -- E or W (East or West)
|
||||
"qual", -- GPS Quality Indicator (non null)
|
||||
"sat", -- Number of satellites in use, 00 - 12
|
||||
"hdp", -- Horizontal Dilution of precision (meters)
|
||||
"alt", -- Antenna Altitude above/below mean-sea-level (geoid) (in meters)
|
||||
"ualt", -- Units of antenna altitude, meters
|
||||
"gsep", -- Geoidal separation, the difference between the WGS-84 earth ellipsoid and mean-sea-level
|
||||
"ugsep", -- Units of geoidal separation, meters
|
||||
"age", -- Age of differential GPS data, time in seconds since last SC104 type 1 or 9 update, null field when DGPS is not used
|
||||
"drs" -- Differential reference station ID, 0000-1023
|
||||
}
|
||||
|
||||
local err, gga = getCropData(resp, "$GPGGA,")
|
||||
if not err[1] and string.gsub(gga, ',', '') ~= '0'
|
||||
and string.sub(gga, string.find(gga, ',') + 1, string.find(gga, ',') + 1) ~= ',' then
|
||||
GnssData.gga = doTable(gga, GnssData.gga)
|
||||
GnssData.warning.gga = {false, "OK"}
|
||||
else
|
||||
GnssData.gga = addDash(GnssData.gga)
|
||||
GnssData.warning.gga = err[1] and err or {true, "Bad GGA data"}
|
||||
end
|
||||
end
|
||||
|
||||
-- RMC - Recommended Minimum Navigation Information
|
||||
local function getRMC(GnssData, resp)
|
||||
GnssData.rmc = {
|
||||
"utc", -- UTC of position fix, hh is hours, mm is minutes, ss.ss is seconds.
|
||||
"valid", -- Status, A = Valid, V = Warning
|
||||
"latitude", -- Latitude, dd is degrees. mm.mm is minutes.
|
||||
"ns", -- N or S
|
||||
"longitude", -- Longitude, ddd is degrees. mm.mm is minutes.
|
||||
"ew", -- E or W
|
||||
"knots", -- Speed over ground, knots
|
||||
"tmgdt", -- Track made good, degrees true
|
||||
"date", -- Date, ddmmyy
|
||||
"mv", -- Magnetic Variation, degrees
|
||||
"ewm", -- E or W
|
||||
"nstat", -- Nav Status (NMEA 4.1 and later) A=autonomous, D=differential, E=Estimated, ->
|
||||
-- M=Manual input mode N=not valid, S=Simulator, V = Valid
|
||||
"sc" --checksum
|
||||
}
|
||||
|
||||
local err, rmc = getCropData(resp, "$GPRMC,")
|
||||
if not err[1] and string.find(rmc, ",A,") then
|
||||
GnssData.rmc = doTable(rmc, GnssData.rmc)
|
||||
GnssData.warning.rmc = {false, "OK"}
|
||||
else
|
||||
GnssData.rmc = addDash(GnssData.rmc)
|
||||
GnssData.warning.rmc = err[1] and err or {true, "Bad RMC data"}
|
||||
end
|
||||
end
|
||||
|
||||
-- VTG - Track made good and Ground speed
|
||||
local function getVTG(GnssData, resp)
|
||||
GnssData.vtg = {
|
||||
"course_t", -- Course over ground, degrees True
|
||||
't', -- T = True
|
||||
"course_m", -- Course over ground, degrees Magnetic
|
||||
'm', -- M = Magnetic
|
||||
"knots", -- Speed over ground, knots
|
||||
'n', -- N = Knots
|
||||
"speed", -- Speed over ground, km/hr
|
||||
'k', -- K = Kilometers Per Hour
|
||||
"faa" -- FAA mode indicator (NMEA 2.3 and later)
|
||||
}
|
||||
|
||||
local err, vtg = getCropData(resp, "$GPVTG,")
|
||||
if not err[1] and (string.find(vtg, 'A') or string.find(vtg, 'D')) then
|
||||
GnssData.vtg = doTable(vtg, GnssData.vtg)
|
||||
GnssData.warning.vtg = {false, "OK"}
|
||||
else
|
||||
GnssData.vtg = addDash(GnssData.vtg)
|
||||
GnssData.warning.vtg = err[1] and err or {true, "Bad VTG data"}
|
||||
end
|
||||
end
|
||||
|
||||
--GSA - GPS DOP and active satellites
|
||||
local function getGSA(GnssData, resp)
|
||||
GnssData.gsa = {
|
||||
"smode", -- Selection mode: M=Manual, forced to operate in 2D or 3D, A=Automatic, 2D/3D
|
||||
"mode", -- Mode (1 = no fix, 2 = 2D fix, 3 = 3D fix)
|
||||
"id1", -- ID of 1st satellite used for fix
|
||||
"id2", -- ID of 2nd satellite used for fix
|
||||
"id3", -- ID of 3rd satellite used for fix
|
||||
"id4", -- ID of 4th satellite used for fix
|
||||
"id5", -- ID of 5th satellite used for fix
|
||||
"id6", -- ID of 6th satellite used for fix
|
||||
"id7", -- ID of 7th satellite used for fix
|
||||
"id8", -- ID of 8th satellite used for fix
|
||||
"id9", -- ID of 9th satellite used for fix
|
||||
"id10", -- ID of 10th satellite used for fix
|
||||
"id11", -- ID of 11th satellite used for fix
|
||||
"id12", -- ID of 12th satellite used for fix
|
||||
"pdop", -- PDOP
|
||||
"hdop", -- HDOP
|
||||
"vdop", -- VDOP
|
||||
"sc" -- checksum
|
||||
}
|
||||
|
||||
local err, gsa = getCropData(resp, "$GPGSA,")
|
||||
if not err[1] and string.find(gsa, '2') then
|
||||
GnssData.gsa = doTable(gsa, GnssData.gsa)
|
||||
GnssData.warning.gsa = {false, "OK"}
|
||||
else
|
||||
GnssData.gsa = addDash(GnssData.gsa)
|
||||
GnssData.warning.gsa = err[1] and err or {true, "Bad GSA data"}
|
||||
end
|
||||
end
|
||||
|
||||
--GNS - GLONAS Fix data
|
||||
local function getGNS(GnssData, resp)
|
||||
GnssData.gns = {
|
||||
"utc", -- UTC of this position report, hh is hours, mm is minutes, ss.ss is seconds.
|
||||
"latitude", -- Latitude, dd is degrees, mm.mm is minutes
|
||||
"ne", -- N or S (North or South)
|
||||
"longitude", -- Longitude, dd is degrees, mm.mm is minutes
|
||||
"ew", -- E or W (East or West)
|
||||
"mi", -- Mode indicator (non-null)
|
||||
"sat", -- Total number of satellites in use, 00-99
|
||||
"hdp", -- Horizontal Dilution of Precision, HDOP
|
||||
"alt", -- Antenna Altitude above/below mean-sea-level (geoid) (in meters)
|
||||
"gsep", -- Goeidal separation meters
|
||||
"age", -- Age of differential data
|
||||
"drs", -- Differential reference station ID
|
||||
"nstat" --Navigational status (optional) S = Safe C = Caution U = Unsafe V = Not valid for navigation
|
||||
}
|
||||
|
||||
local err, gns = getCropData(resp, "$GNGNS,")
|
||||
if not err[1] and string.gsub(gns, ',', '') ~= '0' and not string.find(gns, "NNN") then
|
||||
GnssData.gns = doTable(gns, GnssData.gns)
|
||||
GnssData.warning.gns = {false, "OK"}
|
||||
else
|
||||
GnssData.gns = addDash(GnssData.gns)
|
||||
GnssData.warning.gns = err[1] and err or {true, "Bad GNS data"}
|
||||
end
|
||||
end
|
||||
|
||||
-- Prepares data for the web application (Some custom data)
|
||||
local function getGPoint(GnssData, resp)
|
||||
GnssData.gp = {
|
||||
longitude = '-',
|
||||
latitude = '-',
|
||||
altitude = '-',
|
||||
utc = '-',
|
||||
date = '-',
|
||||
nsat = '-',
|
||||
hdop = '-',
|
||||
cog = '-',
|
||||
spkm = '-',
|
||||
unix = '-'
|
||||
}
|
||||
|
||||
local err = {true, ""}
|
||||
local GpsOrGlonas = false
|
||||
|
||||
if not GnssData.warning.gga[1] then
|
||||
GpsOrGlonas = GnssData.gga
|
||||
elseif not GnssData.warning.gns[1] then
|
||||
GpsOrGlonas = GnssData.gns
|
||||
else
|
||||
err[2] = "GGA: " .. GnssData.warning.gga[2] .. ' ' .. "GNS: " .. GnssData.warning.gns[2] .. ' '
|
||||
end
|
||||
|
||||
if GpsOrGlonas then
|
||||
GnssData.gp.latitude = string.format("%0.6f", nmeaCoordinatesToDouble(GpsOrGlonas.latitude))
|
||||
GnssData.gp.longitude = string.format("%0.6f", nmeaCoordinatesToDouble(GpsOrGlonas.longitude))
|
||||
GnssData.gp.altitude = GpsOrGlonas.alt
|
||||
GnssData.gp.nsat = GpsOrGlonas.sat
|
||||
GnssData.gp.hdop = GpsOrGlonas.hdp
|
||||
end
|
||||
|
||||
if not GnssData.warning.vtg[1] then
|
||||
GnssData.gp.cog = GnssData.vtg.course_t
|
||||
GnssData.gp.spkm = GnssData.vtg.speed
|
||||
else
|
||||
err[2] = err[2] .. "VTG: " .. GnssData.warning.vtg[2] .. ' '
|
||||
end
|
||||
|
||||
if not GnssData.warning.rmc[1] then
|
||||
local unixTime = findTimeZone(GnssData.rmc.utc, GnssData.rmc.date, nmeaCoordinatesToDouble(GnssData.rmc.longitude))
|
||||
local dateTime = os.date("*t", unixTime)
|
||||
|
||||
GnssData.gp.utc = string.format("%s:%s", addZero(dateTime.hour), addZero(dateTime.min))
|
||||
GnssData.gp.date = string.format("%s.%s.%d", addZero(dateTime.day), addZero(dateTime.month), dateTime.year)
|
||||
GnssData.gp.unix = unixTime
|
||||
else
|
||||
err[2] = err[2] .. "RMC: " .. GnssData.warning.rmc[2]
|
||||
end
|
||||
|
||||
if GnssData.warning.gga[1] and GnssData.warning.gns[1] and GnssData.warning.vtg[1] and GnssData.warning.rmc[1] then
|
||||
err = {false, "Updating data..."}
|
||||
end
|
||||
|
||||
if err[2] == "" then
|
||||
err = {false, "OK"}
|
||||
end
|
||||
|
||||
GnssData.warning.gp = err
|
||||
end
|
||||
|
||||
------------------------------------------------------
|
||||
-- Get a certain kind of NMEA data (data parsing)
|
||||
------------------------------------------------------
|
||||
|
||||
function nmea.getData(line, port)
|
||||
GnssData = createGnssForm()
|
||||
GnssData.warning.app, resp = serial.read(port)
|
||||
|
||||
if line == "GP" then
|
||||
getGGA(GnssData, resp)
|
||||
getGNS(GnssData, resp)
|
||||
getRMC(GnssData, resp)
|
||||
getVTG(GnssData, resp)
|
||||
getGPoint(GnssData, resp)
|
||||
elseif line == "GGA" then
|
||||
getGGA(GnssData, resp)
|
||||
elseif line == "GNS" then
|
||||
getGNS(GnssData, resp)
|
||||
elseif line == "RMC" then
|
||||
getRMC(GnssData, resp)
|
||||
elseif line == "VTG" then
|
||||
getVTG(GnssData, resp)
|
||||
elseif line == "GSA" then
|
||||
getGSA(GnssData, resp)
|
||||
else
|
||||
GnssData.warning.app = {true, "Bad argument..."}
|
||||
end
|
||||
return GnssData
|
||||
end
|
||||
|
||||
------------------------------------------------------
|
||||
-- parsing all NMEA data
|
||||
------------------------------------------------------
|
||||
|
||||
function nmea.getAllData(port)
|
||||
GnssData = createGnssForm()
|
||||
GnssData.warning.app, resp = serial.read(port)
|
||||
|
||||
getGGA(GnssData, resp)
|
||||
getGNS(GnssData, resp)
|
||||
getRMC(GnssData, resp)
|
||||
getVTG(GnssData, resp)
|
||||
getGSA(GnssData, resp) -- rarely used
|
||||
getGPoint(GnssData, resp)
|
||||
|
||||
return GnssData
|
||||
end
|
||||
|
||||
return nmea
|
|
@ -1,73 +0,0 @@
|
|||
-------------------------------------------------------------------
|
||||
-- Wrapper for working with a modem via serial port
|
||||
-------------------------------------------------------------------
|
||||
-- Copyright 2021-2022 Vladislav Kadulin <spanky@yandex.ru>
|
||||
-- Licensed to the GNU General Public License v3.0
|
||||
|
||||
local rs232 = require("luars232")
|
||||
|
||||
local serial = {}
|
||||
|
||||
local function configSerial(port)
|
||||
assert(port:set_baud_rate(rs232.RS232_BAUD_115200) == rs232.RS232_ERR_NOERROR)
|
||||
assert(port:set_parity(rs232.RS232_PARITY_NONE) == rs232.RS232_ERR_NOERROR)
|
||||
assert(port:set_data_bits(rs232.RS232_DATA_8) == rs232.RS232_ERR_NOERROR)
|
||||
assert(port:set_stop_bits(rs232.RS232_STOP_1) == rs232.RS232_ERR_NOERROR)
|
||||
assert(port:set_flow_control(rs232.RS232_FLOW_OFF) == rs232.RS232_ERR_NOERROR)
|
||||
end
|
||||
|
||||
-- write data from modem (AT PORT)
|
||||
function serial.write(serial_port, command)
|
||||
local err, port = rs232.open(serial_port)
|
||||
if err ~= rs232.RS232_ERR_NOERROR then
|
||||
err = {true, "Error opening AT port"}
|
||||
assert(port:close() == rs232.RS232_ERR_NOERROR)
|
||||
return err
|
||||
end
|
||||
configSerial(port)
|
||||
|
||||
local err, len_written = port:write(command .. "\r\n")
|
||||
if err ~= rs232.RS232_ERR_NOERROR then
|
||||
err = {true, "Error writing AT port"}
|
||||
assert(port:close() == rs232.RS232_ERR_NOERROR)
|
||||
return err
|
||||
end
|
||||
|
||||
err = {false, "OK"}
|
||||
assert(port:close() == rs232.RS232_ERR_NOERROR)
|
||||
return err
|
||||
end
|
||||
|
||||
-- read data from modem (GNSS PORT)
|
||||
function serial.read(serial_port)
|
||||
local err, port = rs232.open(serial_port)
|
||||
if err ~= rs232.RS232_ERR_NOERROR then
|
||||
err = {true, "Error opening GNSS port"}
|
||||
assert(port:close() == rs232.RS232_ERR_NOERROR)
|
||||
return err, ''
|
||||
end
|
||||
configSerial(port)
|
||||
|
||||
local READ_LEN = 1024 -- Read byte form GNSS port
|
||||
local TIMEOUT = 1000 -- Timeout reading in miliseconds
|
||||
|
||||
local serialData, err, read_data = {}, "", ""
|
||||
while READ_LEN > 0 do
|
||||
err, read_data = port:read(1, TIMEOUT)
|
||||
if err ~= rs232.RS232_ERR_NOERROR then
|
||||
err = {true, "Error reading GNSS port. Updating data or searching for satellites..."}
|
||||
assert(port:close() == rs232.RS232_ERR_NOERROR)
|
||||
return err, ""
|
||||
end
|
||||
if read_data ~= nil then
|
||||
table.insert(serialData, read_data)
|
||||
READ_LEN = READ_LEN - 1
|
||||
end
|
||||
end
|
||||
assert(port:close() == rs232.RS232_ERR_NOERROR)
|
||||
|
||||
err = {false, "OK"}
|
||||
return err, table.concat(serialData)
|
||||
end
|
||||
|
||||
return serial
|
|
@ -1,44 +0,0 @@
|
|||
common_path = '/usr/share/gpoint/lib/?.lua;'
|
||||
package.path = common_path .. package.path
|
||||
|
||||
|
||||
local nmea = require("nmea")
|
||||
local serial = require("serial")
|
||||
local nixio = require("nixio.fs")
|
||||
|
||||
local dell = {}
|
||||
|
||||
local DELL_BEGIN_GPS = "AT+GPS=1"
|
||||
local DELL_END_GPS = "AT+GPS=0"
|
||||
|
||||
-- automatic activation of the NMEA port for data transmission
|
||||
function dell.start(port)
|
||||
local p = tonumber(string.sub(port, #port)) + 1
|
||||
p = string.gsub(port, '%d', tostring(p))
|
||||
local error, resp = true, {
|
||||
warning = {
|
||||
app = {true, "Port is unavailable. Check the modem connections!"},
|
||||
locator = {},
|
||||
server = {}
|
||||
}
|
||||
}
|
||||
-- DELL DW5821 series default NMEA /dev/ttyUSB2
|
||||
local fport = nixio.glob("/dev/tty[A-Z][A-Z]*")
|
||||
for name in fport do
|
||||
if string.find(name, p) then
|
||||
error, resp = serial.write(p, DELL_BEGIN_GPS)
|
||||
end
|
||||
end
|
||||
return error, resp
|
||||
end
|
||||
-- stop send data to NMEA port
|
||||
function dell.stop(port)
|
||||
error, resp = serial.write(port, DELL_END_GPS)
|
||||
return error, resp
|
||||
end
|
||||
-- get GNSS data for application
|
||||
function dell.getGNSSdata(port)
|
||||
return nmea.getAllData(port)
|
||||
end
|
||||
|
||||
return dell
|
|
@ -1,41 +0,0 @@
|
|||
common_path = '/usr/share/gpoint/lib/?.lua;'
|
||||
package.path = common_path .. package.path
|
||||
|
||||
local nmea = require("nmea")
|
||||
local serial = require("serial")
|
||||
local nixio = require("nixio.fs")
|
||||
|
||||
local que = {}
|
||||
|
||||
local QUECTEL_BEGIN_GPS = "AT+QGPS=1"
|
||||
local QUECTEL_END_GPS = "AT+QGPSEND"
|
||||
|
||||
-- automatic activation of the NMEA port for data transmission
|
||||
function que.start(port)
|
||||
local p = tonumber(string.sub(port, #port)) + 1
|
||||
p = string.gsub(port, '%d', tostring(p))
|
||||
local error, resp = true, {warning = {
|
||||
app = {true, "Port is unavailable. Check the modem connections!"},
|
||||
locator = {},
|
||||
server = {}
|
||||
}
|
||||
}
|
||||
local fport = nixio.glob("/dev/tty[A-Z][A-Z]*")
|
||||
for name in fport do
|
||||
if string.find(name, p) then
|
||||
error, resp = serial.write(p, QUECTEL_BEGIN_GPS)
|
||||
end
|
||||
end
|
||||
return error, resp
|
||||
end
|
||||
-- stop send data to NMEA port
|
||||
function que.stop(port)
|
||||
error, resp = serial.write(port, QUECTEL_END_GPS)
|
||||
return error, resp
|
||||
end
|
||||
-- get GNSS data for application
|
||||
function que.getGNSSdata(port)
|
||||
return nmea.getAllData(port)
|
||||
end
|
||||
|
||||
return que
|
|
@ -1,40 +0,0 @@
|
|||
common_path = '/usr/share/gpoint/lib/?.lua;'
|
||||
package.path = common_path .. package.path
|
||||
|
||||
|
||||
local nmea = require("nmea")
|
||||
local serial = require("serial")
|
||||
local nixio = require("nixio.fs")
|
||||
|
||||
local sierra = {}
|
||||
|
||||
local SIERRA_BEGIN_GPS = "$GPS_START"
|
||||
local SIERRA_END_GPS = "$GPS_STOP"
|
||||
|
||||
-- automatic activation of the NMEA port for data transmission
|
||||
function sierra.start(port)
|
||||
local error, resp = true, {warning = {
|
||||
app = {true, "Port is unavailable. Check the modem connections!"},
|
||||
locator = {},
|
||||
server = {}
|
||||
}
|
||||
}
|
||||
local fport = nixio.glob("/dev/tty[A-Z][A-Z]*")
|
||||
for name in fport do
|
||||
if string.find(name, port) then
|
||||
error, resp = serial.write(port, SIERRA_BEGIN_GPS)
|
||||
end
|
||||
end
|
||||
return error, resp
|
||||
end
|
||||
-- stop send data to NMEA port
|
||||
function sierra.stop(port)
|
||||
error, resp = serial.write(port, SIERRA_END_GPS)
|
||||
return error, resp
|
||||
end
|
||||
-- get GNSS data for application
|
||||
function sierra.getGNSSdata(port)
|
||||
return nmea.getAllData(port)
|
||||
end
|
||||
|
||||
return sierra
|
|
@ -1,44 +0,0 @@
|
|||
common_path = '/usr/share/gpoint/lib/?.lua;'
|
||||
package.path = common_path .. package.path
|
||||
|
||||
|
||||
local nmea = require("nmea")
|
||||
local serial = require("serial")
|
||||
local nixio = require("nixio.fs")
|
||||
|
||||
local simcom = {}
|
||||
|
||||
local SIMCOM_BEGIN_GPS = "AT+CGPS=1,1"
|
||||
local SIMCOM_END_GPS = "AT+CGPS=0,1"
|
||||
|
||||
-- automatic activation of the NMEA port for data transmission
|
||||
function simcom.start(port)
|
||||
local p = tonumber(string.sub(port, #port)) + 1
|
||||
p = string.gsub(port, '%d', tostring(p))
|
||||
local error, resp = true, {
|
||||
warning = {
|
||||
app = {true, "Port is unavailable. Check the modem connections!"},
|
||||
locator = {},
|
||||
server = {}
|
||||
}
|
||||
}
|
||||
-- SIM7600 series default NMEA /dev/ttyUSB1
|
||||
local fport = nixio.glob("/dev/tty[A-Z][A-Z]*")
|
||||
for name in fport do
|
||||
if string.find(name, p) then
|
||||
error, resp = serial.write(p, SIMCOM_BEGIN_GPS)
|
||||
end
|
||||
end
|
||||
return error, resp
|
||||
end
|
||||
-- stop send data to NMEA port
|
||||
function simcom.stop(port)
|
||||
error, resp = serial.write(port, SIMCOM_END_GPS)
|
||||
return error, resp
|
||||
end
|
||||
-- get GNSS data for application
|
||||
function simcom.getGNSSdata(port)
|
||||
return nmea.getAllData(port)
|
||||
end
|
||||
|
||||
return simcom
|
|
@ -1,22 +0,0 @@
|
|||
common_path = '/usr/share/gpoint/lib/?.lua;'
|
||||
package.path = common_path .. package.path
|
||||
|
||||
|
||||
local nmea = require("nmea")
|
||||
|
||||
local ublox = {}
|
||||
|
||||
-- Wrapper over the interface, the module does not need an implementation
|
||||
function ublox.start(port)
|
||||
return false, {warning = {app = {false, "GOOD!"}, locator = {}, server = {}}}
|
||||
end
|
||||
|
||||
function ublox.stop(port)
|
||||
return {false, "OK"}
|
||||
end
|
||||
-- get GNSS data for application
|
||||
function ublox.getGNSSdata(port)
|
||||
return nmea.getAllData(port)
|
||||
end
|
||||
|
||||
return ublox
|
|
@ -1,31 +0,0 @@
|
|||
-------------------------------------------------------------
|
||||
-- Traccar Client use this protocol to report GPS data to the server side.
|
||||
-- OsmAnd Live Tracking web address format:
|
||||
-- http://demo.traccar.org:5055/?id=123456&lat={0}&lon={1}×tamp={2}&hdop={3}&altitude={4}&speed={5}
|
||||
-------------------------------------------------------------
|
||||
-- Copyright 2021-2022 Vladislav Kadulin <spanky@yandex.ru>
|
||||
-- Licensed to the GNU General Public License v3.0
|
||||
|
||||
local http = require("socket.http")
|
||||
|
||||
local trackcar = {}
|
||||
|
||||
local function OsmAnd(GnssData, serverConfig)
|
||||
local unix = GnssData.warning.rmc[1] and os.time() or GnssData.gp.unix
|
||||
return string.format("http://%s:%s/?id=%s&lat=%s&lon=%s×tamp=%s&hdop=%s&altitude=%s&speed=%s&satellites=%s",
|
||||
serverConfig.address, serverConfig.port, serverConfig.login,
|
||||
GnssData.gp.latitude or '-', GnssData.gp.longitude or '-',
|
||||
unix or '-', GnssData.gp.hdop or '-',
|
||||
GnssData.gp.altitude or '-', GnssData.gp.spkm or '-',
|
||||
GnssData.gp.nsat or '-')
|
||||
end
|
||||
|
||||
-- Send data to server side
|
||||
function trackcar.sendData(GnssData, serverConfig)
|
||||
local data = OsmAnd(GnssData, serverConfig)
|
||||
http.TIMEOUT = 0.5
|
||||
http.request{ method = "POST", url = data}
|
||||
return {false, "OK"}
|
||||
end
|
||||
|
||||
return trackcar
|
|
@ -1,160 +0,0 @@
|
|||
-------------------------------------------------------------
|
||||
-- A module for working with the "WIALON IPS" navigation protocol.
|
||||
-- This module prepares and sends data to a remote server.
|
||||
-------------------------------------------------------------
|
||||
-- Copyright 2021-2022 Vladislav Kadulin <spanky@yandex.ru>
|
||||
-- Licensed to the GNU General Public License v3.0
|
||||
|
||||
local json = require("luci.jsonc")
|
||||
local checksum = require("checksum")
|
||||
local blackbox = require("wialon_ips_boof")
|
||||
local socket = require("socket")
|
||||
local tcp = assert(socket.tcp())
|
||||
|
||||
local wialon_ips = {}
|
||||
|
||||
-- Abbreviated data package
|
||||
local function shortData(GnssData)
|
||||
local SD = {"NA","NA","NA","NA","NA","NA","NA","NA","NA","NA"}
|
||||
if not GnssData.warning.rmc[1] then
|
||||
SD[1], SD[2] = GnssData.rmc.date, GnssData.rmc.utc
|
||||
end
|
||||
if not GnssData.warning.gga[1] then
|
||||
-- Lat2[4], Lon2[6] - NA
|
||||
SD[3], SD[5] = GnssData.gga.latitude, GnssData.gga.longitude
|
||||
SD[9], SD[10] = GnssData.gga.alt, GnssData.gga.sat
|
||||
elseif not GnssData.warning.gns[1] then
|
||||
SD[3], SD[5] = GnssData.gns.latitude, GnssData.gns.longitude
|
||||
SD[9], SD[10] = GnssData.gns.alt, GnssData.gns.sat
|
||||
elseif not GnssData.warning.locator[1] then
|
||||
SD[1], SD[2] = os.date("%d%m%y"), os.date("%H%M%S", os.time(os.date("!*t"))) .. ".00"
|
||||
SD[3], SD[5] = GnssData.gga.latitude, '0' .. GnssData.gga.longitude
|
||||
end
|
||||
if not GnssData.warning.vtg[1] then
|
||||
SD[7], SD[8] = GnssData.vtg.speed, GnssData.vtg.course_t
|
||||
end
|
||||
SD[11] = checksum.crc16(table.concat(SD, ";") .. ';')
|
||||
return "#SD#" .. table.concat(SD, ";") .. "\r\n"
|
||||
end
|
||||
|
||||
-- Extended Data package with CRC16
|
||||
local function bigData(GnssData, params)
|
||||
local D = {"NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","","NA","NA"}
|
||||
if not GnssData.warning.rmc[1] then
|
||||
D[1], D[2] = GnssData.rmc.date, GnssData.rmc.utc
|
||||
end
|
||||
if not GnssData.warning.gga[1] then
|
||||
-- Lat2[4], Lon2[6] - NA
|
||||
D[3], D[5] = GnssData.gga.latitude, GnssData.gga.longitude
|
||||
D[9], D[10] = GnssData.gga.alt, GnssData.gga.sat
|
||||
D[11] = GnssData.gga.hdp
|
||||
elseif not GnssData.warning.gns[1] then
|
||||
D[3], D[5] = GnssData.gns.latitude, GnssData.gns.longitude
|
||||
D[9], D[10] = GnssData.gns.alt, GnssData.gns.sat
|
||||
D[11] = GnssData.gns.hdp
|
||||
elseif not GnssData.warning.locator[1] then
|
||||
D[1], D[2] = os.date("%d%m%y"), os.date("%H%M%S", os.time(os.date("!*t"))) .. ".00"
|
||||
D[3], D[5] = GnssData.gga.latitude, '0' .. GnssData.gga.longitude
|
||||
D[16] = string.format("%s:%s:%s", "yandex", '3', "Data from wi-fi scan")
|
||||
end
|
||||
if not GnssData.warning.vtg[1] then
|
||||
D[7], D[8] = GnssData.vtg.speed, GnssData.vtg.course_t
|
||||
end
|
||||
if params then
|
||||
-- TODO PARAMS WITH YA LOCATOR
|
||||
D[16] = string.format("%s:%s:%s", params[1], params[2], params[3])
|
||||
end
|
||||
D[17] = checksum.crc16(table.concat(D, ";") .. ';')
|
||||
return "#D#" .. table.concat(D, ";") .. "\r\n"
|
||||
end
|
||||
|
||||
-- Response from the server to the message
|
||||
local function handlErr(resp)
|
||||
local ERROR_CODE = {
|
||||
["#AL#1"] = "OK",
|
||||
["#ASD#1"] = "OK",
|
||||
["#AD#1"] = "OK",
|
||||
["#AL#0"] = "Rejected connection",
|
||||
["#AL#01"] = "Password verification error",
|
||||
["#AL#10"] = "Checksum verification error",
|
||||
["#ASD#-1"] = "Package structure error",
|
||||
["#ASD#0"] = "Incorrect time",
|
||||
["#ASD#10"] = "Error getting coordinates",
|
||||
["#ASD#11"] = "Error getting speed, course, or altitude",
|
||||
["#ASD#12"] = "Error getting the number of satellites",
|
||||
["#ASD#13"] = "Checksum verification error",
|
||||
["#AD#-1"] = "Package structure error",
|
||||
["#AD#0"] = "Incorrect time",
|
||||
["#AD#10"] = "Error getting coordinates",
|
||||
["#AD#11"] = "Error getting speed, course, or altitude",
|
||||
["#AD#12"] = "Error in getting the number of satellites or HDOP",
|
||||
["#AD#13"] = "Error getting Inputs or Outputs",
|
||||
["#AD#14"] = "Error receiving ADC",
|
||||
["#AD#15"] = "Error getting additional parameters",
|
||||
["#AD#16"] = "Checksum verification error"
|
||||
}
|
||||
|
||||
for k, v in pairs(ERROR_CODE) do
|
||||
if k == resp then
|
||||
return v
|
||||
end
|
||||
end
|
||||
return "Unknown error"
|
||||
end
|
||||
|
||||
-- Login Package
|
||||
local function login(imei, pass)
|
||||
local L = {}
|
||||
L[1], L[2], L[3] = "2.0", imei, pass
|
||||
L[4] = checksum.crc16(table.concat(L, ";") .. ';')
|
||||
return "#L#" .. table.concat(L, ";") .. "\r\n"
|
||||
end
|
||||
|
||||
-- Send data to server side
|
||||
function wialon_ips.sendData(GnssData, serverConfig)
|
||||
local r, s, e
|
||||
local DATA_OK = "OK"
|
||||
local err = {false, DATA_OK}
|
||||
local wialonData = bigData(GnssData)
|
||||
|
||||
-- Data is missing, there is nothing to send
|
||||
if string.find(wialonData, "DB2D") then
|
||||
return {true, "No data to send"}
|
||||
end
|
||||
|
||||
s, e = tcp:connect(serverConfig.address, serverConfig.port)
|
||||
if not s then
|
||||
blackbox.set(GnssData, serverConfig)
|
||||
tcp:close()
|
||||
return {true, e}
|
||||
end
|
||||
tcp:settimeout(2)
|
||||
tcp:send(login(serverConfig.login, serverConfig.password) .. '\n')
|
||||
r, e = tcp:receive()
|
||||
|
||||
if handlErr(r) == DATA_OK then
|
||||
tcp:send(wialonData)
|
||||
r, e = tcp:receive()
|
||||
if handlErr(r) == DATA_OK then
|
||||
local booferSize, booferData = blackbox.get(serverConfig)
|
||||
if booferSize > 0 then
|
||||
tcp:send(booferData .. '\n')
|
||||
r, e = tcp:receive()
|
||||
local sentSize = string.gsub(r, "%D", "")
|
||||
if tonumber(sentSize) and tonumber(sentSize) >= booferSize then
|
||||
blackbox.clean(serverConfig)
|
||||
end
|
||||
end
|
||||
elseif handlErr(r) ~= DATA_OK then
|
||||
blackbox.set(GnssData, serverConfig)
|
||||
end
|
||||
else
|
||||
err = {true, handlErr(r)}
|
||||
blackbox.set(GnssData, serverConfig)
|
||||
end
|
||||
|
||||
tcp:close()
|
||||
return err
|
||||
end
|
||||
|
||||
return wialon_ips
|
|
@ -1,119 +0,0 @@
|
|||
-------------------------------------------------------------
|
||||
-- A module for working with the "WIALON IPS" navigation protocol.
|
||||
-- This module saves navigation data in case of signal loss
|
||||
-- (if it is not possible to transfer data to the server)
|
||||
-------------------------------------------------------------
|
||||
-- Copyright 2021-2022 Vladislav Kadulin <spanky@yandex.ru>
|
||||
-- Licensed to the GNU General Public License v3.0
|
||||
|
||||
local json = require("luci.jsonc")
|
||||
local checksum = require("checksum")
|
||||
|
||||
local wialon_ips_boof = {}
|
||||
|
||||
-- Extended Data package
|
||||
local function transformBooferData(GnssData, params)
|
||||
local D = {"NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","","NA","NA"}
|
||||
if not GnssData.warning.rmc[1] then
|
||||
D[1], D[2] = GnssData.rmc.date, GnssData.rmc.utc
|
||||
else
|
||||
D[1], D[2] = os.date("%d%m%y"), os.date("%H%M%S",os.time(os.date("!*t"))) .. ".00"
|
||||
end
|
||||
if not GnssData.warning.gga[1] then
|
||||
-- Lat2[4], Lon2[6] - NA
|
||||
D[3], D[5] = GnssData.gga.latitude, GnssData.gga.longitude
|
||||
D[9], D[10] = GnssData.gga.alt, GnssData.gga.sat
|
||||
D[11] = GnssData.gga.hdp
|
||||
elseif not GnssData.warning.gns[1] then
|
||||
D[3], D[5] = GnssData.gns.latitude, GnssData.gns.longitude
|
||||
D[9], D[10] = GnssData.gns.alt, GnssData.gns.sat
|
||||
D[11] = GnssData.gns.hdp
|
||||
elseif not GnssData.warning.locator[1] then
|
||||
D[3], D[5] = GnssData.gga.latitude, '0' .. GnssData.gga.longitude
|
||||
end
|
||||
if not GnssData.warning.vtg[1] then
|
||||
D[7], D[8] = GnssData.vtg.speed, GnssData.vtg.course_t
|
||||
end
|
||||
if params then
|
||||
D[16] = string.format("%s:%s:%s", params[1], params[2], params[3])
|
||||
else
|
||||
D[16] = string.format("%s:%s:%s", "boof", '3', "Data from boofer")
|
||||
end
|
||||
|
||||
return table.concat(D, ";")
|
||||
end
|
||||
|
||||
-- read GNSS data from file
|
||||
local function readBoof()
|
||||
local file = io.open("/usr/share/gpoint/tmp/blackbox.json", 'r')
|
||||
if not file then return nil end
|
||||
local bb_data = json.parse(file:read("*a"))
|
||||
file:close()
|
||||
return bb_data
|
||||
end
|
||||
|
||||
-- write GNSS data to file
|
||||
local function writeBoof(boof)
|
||||
local file = io.open("/usr/share/gpoint/tmp/blackbox.json", 'w')
|
||||
file:write(json.stringify(boof))
|
||||
file:close()
|
||||
end
|
||||
|
||||
-- create boofer with data if
|
||||
local function createBoof(size)
|
||||
local BLACKBOX = { ["size"]=0,["max"]=tonumber(size),["data"]={} }
|
||||
writeBoof(BLACKBOX)
|
||||
return BLACKBOX
|
||||
end
|
||||
|
||||
-- Package from the black box
|
||||
local function parseBlackBoxData(data)
|
||||
local B = ""
|
||||
for _, gnss_msg in pairs(data) do
|
||||
B = B .. gnss_msg .. '|'
|
||||
end
|
||||
return "#B#" .. B .. checksum.crc16(B) .. "\r\n"
|
||||
end
|
||||
|
||||
-- get data from the black box
|
||||
function wialon_ips_boof.get(serverConfig)
|
||||
if not serverConfig.blackbox.enable then
|
||||
return -1, nil
|
||||
end
|
||||
|
||||
local blackbox = readBoof()
|
||||
if blackbox == nil or tonumber(blackbox.max) ~= tonumber(serverConfig.blackbox.size) then
|
||||
blackbox = createBoof(serverConfig.blackbox.size)
|
||||
end
|
||||
|
||||
return blackbox.size, parseBlackBoxData(blackbox.data)
|
||||
end
|
||||
|
||||
-- send data to the black box
|
||||
function wialon_ips_boof.set(GnssData, serverConfig)
|
||||
|
||||
if not serverConfig.blackbox.enable then
|
||||
return -1
|
||||
end
|
||||
|
||||
local blackbox = readBoof()
|
||||
if blackbox == nil or tonumber(blackbox.max) ~= tonumber(serverConfig.blackbox.size) then
|
||||
blackbox = createBoof(serverConfig.blackbox.size)
|
||||
end
|
||||
|
||||
if blackbox.size < blackbox.max then
|
||||
blackbox.size = blackbox.size + 1
|
||||
elseif serverConfig.blackbox.cycle then
|
||||
blackbox.size = 1
|
||||
end
|
||||
|
||||
blackbox.data[blackbox.size] = transformBooferData(GnssData)
|
||||
writeBoof(blackbox)
|
||||
end
|
||||
|
||||
-- clear the black box
|
||||
function wialon_ips_boof.clean(serverConfig)
|
||||
createBoof(serverConfig.blackbox.size)
|
||||
end
|
||||
|
||||
return wialon_ips_boof
|
|
@ -1 +0,0 @@
|
|||
{"max":1000,"data":[],"size":0}
|
|
@ -1,143 +0,0 @@
|
|||
common_path = '/usr/share/gpoint/lib/?.lua;'
|
||||
package.path = common_path .. package.path
|
||||
|
||||
local checksum = require("checksum")
|
||||
local lu = require('luaunit')
|
||||
|
||||
local goodGNSSSdata = {
|
||||
"$GPRMC,113702.568,V,4154.931,N,08002.497,W,95.5,0.02,220721,,E*4E",
|
||||
"$GPGGA,113703.568,4154.931,N,08002.497,W,0,00,,,M,,M,,*52",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.1,0.8,0.6*3F",
|
||||
"$GPRMC,113705.568,V,4154.933,N,08002.497,W,86.0,-0.05,220721,,E*66",
|
||||
"$GPGGA,113706.568,4154.933,N,08002.497,W,0,00,0.8,,M,,M,,*73",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.0,0.4*36",
|
||||
"$GPRMC,113708.568,V,4154.935,N,08002.498,W,55.1,-0.10,220721,,E*69",
|
||||
"$GPGGA,113709.568,4154.935,N,08002.498,W,0,00,0.0,,M,,M,,*7D",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.2,0.8*3C",
|
||||
"$GPRMC,113711.568,V,4154.937,N,08002.498,W,95.0,-0.10,220721,,E*6E",
|
||||
"$GPGGA,113712.568,4154.937,N,08002.498,W,0,00,0.2,,M,,M,,*77",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.7,0.6,0.3*32",
|
||||
"$GPRMC,113714.568,V,4154.939,N,08002.498,W,28.0,-0.07,220721,,E*65",
|
||||
"$GPGGA,113715.568,4154.939,N,08002.498,W,0,00,0.6,,M,,M,,*7A",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,1.0,1.0,0.5*34",
|
||||
"$GPRMC,113717.568,V,4154.940,N,08002.498,W,30.1,0.03,220721,,E*49",
|
||||
"$GPGGA,113718.568,4154.940,N,08002.498,W,0,00,1.0,,M,,M,,*7E",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.5,0.1,0.2*36",
|
||||
"$GPRMC,113720.568,V,4154.942,N,08002.498,W,0.2,-0.02,220721,,E*53",
|
||||
"$GPGGA,113721.568,4154.942,N,08002.498,W,0,00,0.1,,M,,M,,*76",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.5,0.7*30",
|
||||
"$GPRMC,113723.568,V,4154.944,N,08002.498,W,53.6,0.05,220721,,E*4E",
|
||||
"$GPGGA,113724.568,4154.944,N,08002.498,W,0,00,0.5,,M,,M,,*71",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,1.0,0.6,0.1*37",
|
||||
"$GPRMC,113726.568,V,4154.946,N,08002.498,W,76.6,0.04,220721,,E*4F",
|
||||
"$GPGGA,113727.568,4154.946,N,08002.498,W,0,00,0.6,,M,,M,,*73",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.2,0.3,0.7*37",
|
||||
"$GPRMC,113729.568,V,4154.948,N,08002.497,W,30.9,0.12,220721,,E*4B",
|
||||
"$GPGGA,113730.568,4154.948,N,08002.497,W,0,00,0.3,,M,,M,,*71",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.2,0.2*34",
|
||||
"$GPRMC,113732.568,V,4154.949,N,08002.497,W,47.2,0.20,220721,,E*4A",
|
||||
"$GPGGA,113733.568,4154.949,N,08002.497,W,0,00,0.2,,M,,M,,*72",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.3,0.8,0.2*39",
|
||||
"$GPRMC,113735.568,V,4154.951,N,08002.496,W,31.1,0.13,220721,,E*47",
|
||||
"$GPGGA,113736.568,4154.951,N,08002.496,W,0,00,0.8,,M,,M,,*75",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.6,0.1*35",
|
||||
"$GPRMC,113738.568,V,4154.953,N,08002.496,W,58.2,0.10,220721,,E*47",
|
||||
"$GPGGA,113739.568,4154.953,N,08002.496,W,0,00,0.6,,M,,M,,*76",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.6,0.9,0.7*39",
|
||||
"$GPRMC,113741.568,V,4154.955,N,08002.496,W,88.3,0.03,220721,,E*41",
|
||||
"$GPGGA,113742.568,4154.955,N,08002.496,W,0,00,0.9,,M,,M,,*73",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.8,0.8,0.1*30",
|
||||
"$GPRMC,113744.568,V,4154.956,N,08002.496,W,89.3,0.10,220721,,E*44",
|
||||
"$GPGGA,113745.568,4154.956,N,08002.496,W,0,00,0.8,,M,,M,,*76",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.9,0.2,1.0*3B",
|
||||
"$GPRMC,113747.568,V,4154.958,N,08002.495,W,99.1,0.14,220721,,E*4D",
|
||||
"$GPGGA,113748.568,4154.958,N,08002.495,W,0,00,0.2,,M,,M,,*7C",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.1,0.2*35",
|
||||
"$GPRMC,113750.568,V,4154.960,N,08002.495,W,84.0,0.19,220721,,E*40",
|
||||
"$GPGGA,113751.568,4154.960,N,08002.495,W,0,00,0.1,,M,,M,,*7C",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.3,1.0,0.5*36",
|
||||
"$GPRMC,113753.568,V,4154.962,N,08002.495,W,24.0,0.13,220721,,E*41",
|
||||
"$GPGGA,113754.568,4154.962,N,08002.495,W,0,00,1.0,,M,,M,,*7B",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.8,0.9*37",
|
||||
"$GPRMC,113756.568,V,4154.963,N,08002.494,W,27.8,0.03,220721,,E*4E",
|
||||
"$GPGGA,113757.568,4154.963,N,08002.494,W,0,00,0.8,,M,,M,,*71",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.4,0.7*37"
|
||||
}
|
||||
|
||||
local badGNSSSdata = {
|
||||
"$GPRMC,113702.568,V,4154.931,N,08002.497,W,95.5,0.02,220721,,E*48",
|
||||
"$GPGGA,113703.568,4154.931,N,08002.497,W,0,00,,,M,,M,,*54",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.1,0.8,0.6*33",
|
||||
"$GPRMC,113720.568,V,4154.942,N,08002.498,W,0.2,-0.02,220721,,E*5F",
|
||||
"$GPGGA,113721.568,4154.942,N,08002.498,W,0,00,0.1,,M,,M,,*82",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.5,0.7*35",
|
||||
"$GPRMC,113723.568,V,4154.944,N,08002.498,W,53.6,0.05,220721,,E*5A",
|
||||
"$GPGGA,113724.568,4154.944,N,08002.498,W,0,00,0.5,,M,,M,,*12",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,1.0,0.6,0.1*35",
|
||||
"$GPRMC,113726.568,V,4154.946,N,08002.498,W,76.6,0.04,220721,,E*9E",
|
||||
"$GPGGA,113727.568,4154.946,N,08002.498,W,0,00,0.6,,M,,M,,*94",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.2,0.3,0.7*32",
|
||||
"$GPRMC,113729.568,V,4154.948,N,08002.497,W,30.9,0.12,220721,,E*9C",
|
||||
"$GPGGA,113730.568,4154.948,N,08002.497,W,0,00,0.3,,M,,M,,*79",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.2,0.2*39",
|
||||
"$GPRMC,113705.568,V,4154.933,N,08002.497,W,86.0,-0.05,220721,,E*90",
|
||||
"$GPGGA,113706.568,4154.933,N,08002.497,W,0,00,0.8,,M,,M,,*42",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.0,0.4*43",
|
||||
"$GPRMC,113708.568,V,4154.935,N,08002.498,W,55.1,-0.10,220721,,E*44",
|
||||
"$GPGGA,113709.568,4154.935,N,08002.498,W,0,00,0.0,,M,,M,,*4A",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.2,0.8*4D",
|
||||
"$GPRMC,113711.568,V,4154.937,N,08002.498,W,95.0,-0.10,220721,,E*44",
|
||||
"$GPGGA,113712.568,4154.937,N,08002.498,W,0,00,0.2,,M,,M,,*44",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.7,0.6,0.3*4D",
|
||||
"$GPRMC,113714.568,V,4154.939,N,08002.498,W,28.0,-0.07,220721,,E*4D",
|
||||
"$GPGGA,113715.568,4154.939,N,08002.498,W,0,00,0.6,,M,,M,,*4D",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,1.0,1.0,0.5*24",
|
||||
"$GPRMC,113717.568,V,4154.940,N,08002.498,W,30.1,0.03,220721,,E*59",
|
||||
"$GPGGA,113718.568,4154.940,N,08002.498,W,0,00,1.0,,M,,M,,*4D",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.5,0.1,0.2*39",
|
||||
"$GPGGA,113736.568,4154.951,N,08002.496,W,0,00,0.8,,M,,M,,*79",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.6,0.1*39",
|
||||
"$GPRMC,113738.568,V,4154.953,N,08002.496,W,58.2,0.10,220721,,E*67",
|
||||
"$GPGGA,113739.568,4154.953,N,08002.496,W,0,00,0.6,,M,,M,,*79",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.6,0.9,0.7*35",
|
||||
"$GPRMC,113741.568,V,4154.955,N,08002.496,W,88.3,0.03,220721,,E*31",
|
||||
"$GPGGA,113742.568,4154.955,N,08002.496,W,0,00,0.9,,M,,M,,*33",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.8,0.8,0.1*34",
|
||||
"$GPRMC,113744.568,V,4154.956,N,08002.496,W,89.3,0.10,220721,,E*34",
|
||||
"$GPGGA,113745.568,4154.956,N,08002.496,W,0,00,0.8,,M,,M,,*75",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.9,0.2,1.0*4B",
|
||||
"$GPRMC,113747.568,V,4154.958,N,08002.495,W,99.1,0.14,220721,,E*5D",
|
||||
"$GPGGA,113748.568,4154.958,N,08002.495,W,0,00,0.2,,M,,M,,*5C",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.1,0.2*55",
|
||||
"$GPRMC,113750.568,V,4154.960,N,08002.495,W,84.0,0.19,220721,,E*50",
|
||||
"$GPGGA,113751.568,4154.960,N,08002.495,W,0,00,0.1,,M,,M,,*5C",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.3,1.0,0.5*35",
|
||||
"$GPRMC,113753.568,V,4154.962,N,08002.495,W,24.0,0.13,220721,,E*51",
|
||||
"$GPGGA,113754.568,4154.962,N,08002.495,W,0,00,1.0,,M,,M,,*5B",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.8,0.9*35",
|
||||
"$GPRMC,113756.568,V,4154.963,N,08002.494,W,27.8,0.03,220721,,E*5E",
|
||||
"$GPGGA,113757.568,4154.963,N,08002.494,W,0,00,0.8,,M,,M,,*51",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.4,0.7*57"
|
||||
}
|
||||
|
||||
function testCRC8()
|
||||
for i = 1, #goodGNSSSdata do
|
||||
lu.assertEquals(checksum.crc8(goodGNSSSdata[i]), true)
|
||||
end
|
||||
for i = 1, #badGNSSSdata do
|
||||
lu.assertEquals(checksum.crc8(badGNSSSdata[i]), false)
|
||||
end
|
||||
end
|
||||
local a = checksum.crc16("$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.4,0.7*57")
|
||||
|
||||
function testCRC16()
|
||||
lu.assertEquals(checksum.crc16(goodGNSSSdata[1]), "E50C")
|
||||
lu.assertEquals(checksum.crc16(goodGNSSSdata[2]), "CCD5")
|
||||
lu.assertEquals(checksum.crc16(goodGNSSSdata[3]), "852C")
|
||||
lu.assertEquals(checksum.crc16(goodGNSSSdata[4]), "EDB5")
|
||||
lu.assertEquals(checksum.crc16(goodGNSSSdata[5]), "5DF0")
|
||||
lu.assertEquals(checksum.crc16(goodGNSSSdata[6]), "7B29")
|
||||
lu.assertEquals(checksum.crc16(goodGNSSSdata[7]), "7CC")
|
||||
end
|
||||
|
||||
os.exit(lu.LuaUnit.run())
|
|
@ -1,96 +0,0 @@
|
|||
local gps = require("gps_lib")
|
||||
--local kalman = require("kalman_lib")
|
||||
|
||||
function read_lat_lon()
|
||||
local file = io.open("/www/kalman_lua/matrix_c_test/testdata/gps_example_1", 'r')
|
||||
|
||||
while file:read() do
|
||||
local lat, lon = file:read("*n", "*n")
|
||||
if lat and lon then
|
||||
return lat, lon
|
||||
end
|
||||
end
|
||||
|
||||
file:close()
|
||||
print("read_lat_lon - OK")
|
||||
end
|
||||
|
||||
function test_read_lat_long()
|
||||
local lat, lon = read_lat_lon()
|
||||
assert(math.abs(lat - 39.315828) < 0.000001)
|
||||
assert(math.abs(lon - -120.167838) < 0.000001)
|
||||
for i = 1, 131 do
|
||||
local lat, lon = read_lat_lon()
|
||||
end
|
||||
print("test_read_lat_long - OK")
|
||||
end
|
||||
|
||||
function test_bearing_north()
|
||||
local kalman = gps.create_velocity2d(1.0)
|
||||
for i = 1, 100 do
|
||||
kalman = gps.update_velocity2d(kalman, i * 0.0001, 0.0, 1.0)
|
||||
end
|
||||
|
||||
local bearing = gps.get_bearing(kalman)
|
||||
assert(math.abs(bearing - 0.0) < 0.01)
|
||||
|
||||
local dlat, dlon = gps.get_velocity(kalman)
|
||||
assert(math.abs(dlat - 0.0001) < 0.00001)
|
||||
assert(math.abs(dlon) < 0.00001)
|
||||
print("test_bearing_north - OK")
|
||||
end
|
||||
|
||||
function test_bearing_east()
|
||||
local kalman = gps.create_velocity2d(1.0)
|
||||
for i = 1, 100 do
|
||||
kalman = gps.update_velocity2d(kalman, 0.0, i * 0.0001, 1.0)
|
||||
end
|
||||
|
||||
local bearing = gps.get_bearing(kalman)
|
||||
assert(math.abs(bearing - 90.0) < 0.01)
|
||||
--At this rate, it takes 10,000 timesteps to travel one longitude
|
||||
--unit, and thus 3,600,000 timesteps to travel the circumference of
|
||||
--the earth. Let's say one timestep is a second, so it takes
|
||||
--3,600,000 seconds, which is 60,000 minutes, which is 1,000
|
||||
--hours. Since the earth is about 25000 miles around, this means we
|
||||
--are traveling at about 25 miles per hour.
|
||||
local mph = gps.get_mph(kalman)
|
||||
assert(math.abs(mph - 25.0) < 2.0)
|
||||
print("test_bearing_east - OK")
|
||||
end
|
||||
|
||||
function test_bearing_south()
|
||||
local kalman = gps.create_velocity2d(1.0)
|
||||
for i = 1, 100 do
|
||||
kalman = gps.update_velocity2d(kalman, i * -0.0001, 0.0, 1.0)
|
||||
end
|
||||
|
||||
local bearing = gps.get_bearing(kalman)
|
||||
assert(math.abs(bearing - 180.0) < 0.01)
|
||||
print("test_bearing_south - OK")
|
||||
end
|
||||
|
||||
function test_bearing_west()
|
||||
local kalman = gps.create_velocity2d(1.0)
|
||||
for i = 1, 100 do
|
||||
kalman = gps.update_velocity2d(kalman, 0.0, i * -0.0001, 1.0)
|
||||
end
|
||||
|
||||
local bearing = gps.get_bearing(kalman)
|
||||
assert(math.abs(bearing - 270.0) < 0.01)
|
||||
print("test_bearing_west - OK")
|
||||
end
|
||||
|
||||
function test_calculate_mph()
|
||||
local mph = gps.calculate_mph(39.315842, -120.167107, -0.000031, 0.000003);
|
||||
assert(math.abs(mph - 7.74) < 0.01);
|
||||
print("test_calculate_mph - OK")
|
||||
end
|
||||
|
||||
-- test start
|
||||
test_read_lat_long()
|
||||
test_bearing_north()
|
||||
test_bearing_east()
|
||||
test_bearing_south()
|
||||
test_bearing_west()
|
||||
test_calculate_mph()
|
|
@ -1,34 +0,0 @@
|
|||
local kalman = require("kalman_lib")
|
||||
local matrix = require("matrix_lib")
|
||||
|
||||
-- Test the example of a train moving along a 1-d track
|
||||
function test_train()
|
||||
local k = kalman.create(2, 1)
|
||||
-- The train state is a 2d vector containing position and velocity.
|
||||
-- Velocity is measured in position units per timestep units.
|
||||
k.state_transition = matrix.set(k.state_transition, 1.0, 1.0,
|
||||
0.0, 1.0)
|
||||
-- We only observe position
|
||||
k.observation_model = matrix.set(k.observation_model, 1.0, 0.0)
|
||||
-- The covariance matrices are blind guesses
|
||||
k.process_noise_covariance = matrix.set_identity(k.process_noise_covariance)
|
||||
k.observation_noise_covariance = matrix.set_identity(k.observation_noise_covariance)
|
||||
-- Our knowledge of the start position is incorrect and unconfident
|
||||
local deviation = 1000.0
|
||||
k.state_estimate = matrix.set(k.state_estimate, 10 * deviation)
|
||||
k.estimate_covariance = matrix.set_identity(k.estimate_covariance)
|
||||
k.estimate_covariance = matrix.scale(k.estimate_covariance, deviation * deviation)
|
||||
|
||||
for i = 1, 10 do
|
||||
k.observation = matrix.set(k.observation, i)
|
||||
k = kalman.update(k)
|
||||
end
|
||||
|
||||
|
||||
print("estimated position: " .. tostring(k.state_estimate[1][1]))
|
||||
print("estimated position: " .. tostring(k.state_estimate[2][1]))
|
||||
|
||||
end
|
||||
|
||||
test_train()
|
||||
print("OK")
|
File diff suppressed because it is too large
Load diff
|
@ -1,46 +0,0 @@
|
|||
|
||||
local matrix = require("matrix_lib")
|
||||
|
||||
|
||||
function test_copy()
|
||||
local foo = matrix.create(3, 3)
|
||||
local bar = matrix.create(3, 3)
|
||||
foo[1][1] = 1337.0
|
||||
bar = matrix.copy(foo, bar)
|
||||
|
||||
assert(bar[1][1] == 1337.0)
|
||||
end
|
||||
|
||||
function test_inverse()
|
||||
local foo = matrix.create(4, 4)
|
||||
foo = matrix.set(foo, 1.0, 2.0, 3.0, 4.0,
|
||||
4.0, 1.0, 7.0, 9.0,
|
||||
0.0, 0.0, -4.0, -4.0,
|
||||
2.3, 3.4, 3.1, 0.0)
|
||||
|
||||
local foo_copy = matrix.copy(foo)
|
||||
local bar = matrix.create(4, 4)
|
||||
local identity = matrix.create(4, 4)
|
||||
|
||||
identity = matrix.set_identity(identity)
|
||||
|
||||
matrix.print(foo)
|
||||
print("--------------")
|
||||
matrix.print(bar)
|
||||
print("--------------")
|
||||
assert(matrix.destructive_invert(foo, bar))
|
||||
matrix.print(foo)
|
||||
print("--------------")
|
||||
matrix.print(bar)
|
||||
print("--------------")
|
||||
|
||||
assert(matrix.equal(foo, identity, 0.0001))
|
||||
foo = matrix.multiply(foo_copy, bar, foo)
|
||||
assert(matrix.equal(foo, identity, 0.0001))
|
||||
foo = matrix.multiply(bar, foo_copy, foo)
|
||||
assert(matrix.equal(foo, identity, 0.0001))
|
||||
end
|
||||
|
||||
test_copy()
|
||||
test_inverse()
|
||||
print("OK")
|
|
@ -1,547 +0,0 @@
|
|||
common_path = '/usr/share/gpoint/tests/luaunitlib/?.lua;'
|
||||
package.path = common_path .. package.path
|
||||
|
||||
lu = require('luaunit')
|
||||
----------------------------------------------------------------------------
|
||||
-- nmea test
|
||||
|
||||
-- Bitxor
|
||||
-- checkcrc
|
||||
-- getcropdata
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
function createGnssForm()
|
||||
local GnssData = {
|
||||
warning = {app, gga, rmc, vtg, gsa, gp}
|
||||
}
|
||||
return GnssData
|
||||
end
|
||||
-- To calculate the checksum
|
||||
-- Bitwise xor
|
||||
local function BitXOR(a, b)
|
||||
local p, c = 1, 0
|
||||
while a > 0 and b > 0 do
|
||||
local ra, rb = a % 2, b % 2
|
||||
if ra ~= rb then c = c + p end
|
||||
a, b, p = (a - ra) / 2, (b - rb) / 2, p * 2
|
||||
end
|
||||
|
||||
if a < b then a = b end
|
||||
while a > 0 do
|
||||
local ra = a % 2
|
||||
if ra > 0 then c = c + p end
|
||||
a, p = (a - ra) / 2, p * 2
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
-- To calculate the checksum
|
||||
function decimalToHex(num)
|
||||
if num == 0 then
|
||||
return '0'
|
||||
end
|
||||
|
||||
local neg = false
|
||||
if num < 0 then
|
||||
neg = true
|
||||
num = num * -1
|
||||
end
|
||||
|
||||
local hexstr = "0123456789ABCDEF"
|
||||
local result = ""
|
||||
while num > 0 do
|
||||
local n = math.mod(num, 16)
|
||||
result = string.sub(hexstr, n + 1, n + 1) .. result
|
||||
num = math.floor(num / 16)
|
||||
end
|
||||
|
||||
if neg then
|
||||
result = '-' .. result
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- Сalculate the checksum (CRC-8)
|
||||
function checkCRC(data)
|
||||
local crc8 = string.sub(data, #data - 1)
|
||||
data = string.sub(data, 2, #data - 3)
|
||||
|
||||
local b_sum = string.byte(data, 1)
|
||||
for i = 2, #data do
|
||||
b_sum = BitXOR(b_sum, string.byte(data, i))
|
||||
end
|
||||
|
||||
return decimalToHex(b_sum) == crc8 and true or false
|
||||
end
|
||||
|
||||
--Converting coordinates from the NMEA protocol to degrees
|
||||
function nmeaCoordinatesToDouble(coord)
|
||||
local deg = math.floor(coord / 100)
|
||||
return deg + (coord - 100 * deg) / 60
|
||||
end
|
||||
|
||||
--We are looking for the desired data line in the line received from the device
|
||||
function findInResp(data, begin)
|
||||
local err = true
|
||||
local b = string.find(data, begin)
|
||||
local e = string.find(data, "\r\n", b)
|
||||
|
||||
if b and e then
|
||||
err = false
|
||||
else
|
||||
b = nil
|
||||
e = nil
|
||||
end
|
||||
return err, b, e
|
||||
end
|
||||
|
||||
function getCropData(data, msg)
|
||||
local err, b, e = findInResp(data, msg)
|
||||
if not err then
|
||||
data = string.gsub(string.sub(data, b, e), '%c', "")
|
||||
if checkCRC(data) then
|
||||
data = string.gsub(data, msg, '', 1)
|
||||
data = string.gsub(data, "*%d+%w+", '', 1)
|
||||
err = {false, "OK"}
|
||||
else
|
||||
err = {true, "Checksum error"}
|
||||
data = nil
|
||||
end
|
||||
else
|
||||
err = {true, "No data found"}
|
||||
data = nil
|
||||
end
|
||||
return err, data
|
||||
end
|
||||
|
||||
-- Creating a table with data before adding data to a single space
|
||||
function doTable(data, keys)
|
||||
local tabl = {}
|
||||
|
||||
while string.find(data, ',,') do
|
||||
data = string.gsub(data, ',,', ",-,")
|
||||
end
|
||||
|
||||
local i = 1
|
||||
for val in string.gmatch(data, "[^,]+") do
|
||||
tabl[keys[i]] = val
|
||||
i = i + 1
|
||||
end
|
||||
return tabl
|
||||
end
|
||||
|
||||
-- The function of searching the time zone by the received coordinates
|
||||
function findTimeZone(time, date, lon)
|
||||
local datetime = { year,month,day,hour,min,sec }
|
||||
-- calculate the time zone by coordinates
|
||||
local timeZone = math.floor((tonumber(lon) + (7.5 * (tonumber(lon) > 0 and 1.0 or -1.0))) / 15.0)
|
||||
|
||||
datetime.hour, datetime.min, datetime.sec = string.match(time, "(%d%d)(%d%d)(%d%d)")
|
||||
datetime.day, datetime.month, datetime.year = string.match(date,"(%d%d)(%d%d)(%d%d)")
|
||||
datetime.year = "20" .. datetime.year -- Someone change this to 21 in the 2100 year
|
||||
|
||||
--we request the unix time and then add the time zone
|
||||
local unix = os.time(datetime)
|
||||
unix = unix + ((math.floor(timeZone* 100)) % 100) * 36
|
||||
unix = unix + math.floor(timeZone) * 3600
|
||||
|
||||
return os.date("*t", unix)
|
||||
end
|
||||
-- Add 0 for the time and date values if < 10
|
||||
function addZero(val)
|
||||
return tonumber(val) > 9 and tostring(val) or '0' .. tostring(val)
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------
|
||||
-- GGA - Global Positioning System Fix Data
|
||||
function getGGA(resp)
|
||||
local err, gga = getCropData(resp, "$GPGGA,")
|
||||
if not err[1] then
|
||||
local ggakeys = {
|
||||
"utc", -- UTC of this position report, hh is hours, mm is minutes, ss.ss is seconds.
|
||||
"latitude", -- Latitude, dd is degrees, mm.mm is minutes
|
||||
"ne", -- N or S (North or South)
|
||||
"longitude", -- Longitude, dd is degrees, mm.mm is minutes
|
||||
"ew", -- E or W (East or West)
|
||||
"qual", -- GPS Quality Indicator (non null)
|
||||
"sat", -- Number of satellites in use, 00 - 12
|
||||
"hdp", -- Horizontal Dilution of precision (meters)
|
||||
"alt", -- Antenna Altitude above/below mean-sea-level (geoid) (in meters)
|
||||
"ualt", -- Units of antenna altitude, meters
|
||||
"gsep", -- Geoidal separation, the difference between the WGS-84 earth ellipsoid and mean-sea-level
|
||||
"ugsep", -- Units of geoidal separation, meters
|
||||
"age", -- Age of differential GPS data, time in seconds since last SC104 type 1 or 9 update, null field when DGPS is not used
|
||||
"drs" -- Differential reference station ID, 0000-1023
|
||||
}
|
||||
|
||||
if string.gsub(gga, ',', '') ~= '0' then
|
||||
gga = doTable(gga, ggakeys)
|
||||
else
|
||||
err = {true, "Bad GGA data"}
|
||||
gga = nil
|
||||
end
|
||||
end
|
||||
return err, gga
|
||||
end
|
||||
|
||||
-- RMC - Recommended Minimum Navigation Information
|
||||
function getRMC(resp)
|
||||
local err, rmc = getCropData(resp, "$GPRMC,")
|
||||
if not err[1] then
|
||||
local rmckeys = {
|
||||
"utc", -- UTC of position fix, hh is hours, mm is minutes, ss.ss is seconds.
|
||||
"valid", -- Status, A = Valid, V = Warning
|
||||
"latitude", -- Latitude, dd is degrees. mm.mm is minutes.
|
||||
"ns", -- N or S
|
||||
"longitude", -- Longitude, ddd is degrees. mm.mm is minutes.
|
||||
"ew", -- E or W
|
||||
"knots", -- Speed over ground, knots
|
||||
"tmgdt", -- Track made good, degrees true
|
||||
"date", -- Date, ddmmyy
|
||||
"mv", -- Magnetic Variation, degrees
|
||||
"ewm", -- E or W
|
||||
"nstat" -- Nav Status (NMEA 4.1 and later) A=autonomous, D=differential, E=Estimated, ->
|
||||
-- -> M=Manual input mode N=not valid, S=Simulator, V = Valid
|
||||
}
|
||||
|
||||
if not string.find(rmc, 'V') then
|
||||
rmc = doTable(rmc, rmckeys)
|
||||
else
|
||||
err = {true, "Bad RMC data"}
|
||||
rmc = nil
|
||||
end
|
||||
end
|
||||
return err, rmc
|
||||
end
|
||||
|
||||
-- VTG - Track made good and Ground speed
|
||||
function getVTG(resp)
|
||||
local err, vtg = getCropData(resp, "$GPVTG,")
|
||||
if not err[1] then
|
||||
local vtgkeys = {
|
||||
"course_t", -- Course over ground, degrees True
|
||||
't', -- T = True
|
||||
"course_m", -- Course over ground, degrees Magnetic
|
||||
'm', -- M = Magnetic
|
||||
"knots", -- Speed over ground, knots
|
||||
'n', -- N = Knots
|
||||
"speed", -- Speed over ground, km/hr
|
||||
'k', -- K = Kilometers Per Hour
|
||||
"faa" -- FAA mode indicator (NMEA 2.3 and later)
|
||||
}
|
||||
|
||||
if string.find(vtg, 'A') or string.find(vtg, 'D') then
|
||||
vtg = doTable(vtg, vtgkeys)
|
||||
else
|
||||
err = {true, "Bad VTG data"}
|
||||
vtg = nil
|
||||
end
|
||||
end
|
||||
return err, vtg
|
||||
end
|
||||
|
||||
--GSA - GPS DOP and active satellites
|
||||
function getGSA(resp)
|
||||
local err, gsa = getCropData(resp, "$GPGSA,")
|
||||
if not err[1] then
|
||||
local gsakeys = {
|
||||
"smode", -- Selection mode: M=Manual, forced to operate in 2D or 3D, A=Automatic, 2D/3D
|
||||
"mode", -- Mode (1 = no fix, 2 = 2D fix, 3 = 3D fix)
|
||||
"id1", -- ID of 1st satellite used for fix
|
||||
"id2", -- ID of 2nd satellite used for fix
|
||||
"id3", -- ID of 3rd satellite used for fix
|
||||
"id4", -- ID of 4th satellite used for fix
|
||||
"id5", -- ID of 5th satellite used for fix
|
||||
"id6", -- ID of 6th satellite used for fix
|
||||
"id7", -- ID of 7th satellite used for fix
|
||||
"id8", -- ID of 8th satellite used for fix
|
||||
"id9", -- ID of 9th satellite used for fix
|
||||
"id10", -- ID of 10th satellite used for fix
|
||||
"id11", -- ID of 11th satellite used for fix
|
||||
"id12", -- ID of 12th satellite used for fix
|
||||
"pdop", -- PDOP
|
||||
"hdop", -- HDOP
|
||||
"vdop" -- VDOP
|
||||
}
|
||||
|
||||
if string.find(gsa, '2') then
|
||||
gsa = doTable(gsa, gsakeys)
|
||||
else
|
||||
err = {true, "Bad GSA data"}
|
||||
gsa = nil
|
||||
end
|
||||
end
|
||||
return err, gsa
|
||||
end
|
||||
|
||||
function parseAllData(resp)
|
||||
local GnssData = createGnssForm()
|
||||
GnssData.warning.gga, GnssData["GGA"] = getGGA(resp)
|
||||
GnssData.warning.rmc, GnssData["RMC"] = getRMC(resp)
|
||||
GnssData.warning.vtg, GnssData["VTG"] = getVTG(resp)
|
||||
GnssData.warning.gsa, GnssData["GSA"] = getGSA(resp)
|
||||
return GnssData
|
||||
end
|
||||
|
||||
-- This function prepares data for the web application (Some custom data)
|
||||
function getGPoint(resp)
|
||||
|
||||
local web = {
|
||||
longitude = '-',
|
||||
latitude = '-',
|
||||
altitude = '-',
|
||||
utc = '-',
|
||||
date = '-',
|
||||
nsat = '-',
|
||||
hdop = '-',
|
||||
cog = '-',
|
||||
spkm = '-'
|
||||
}
|
||||
|
||||
local GnssData = parseAllData(resp)
|
||||
local err = {true, ""}
|
||||
|
||||
if not GnssData.warning.gga[1] then
|
||||
web.latitude = string.format("%0.6f", nmeaCoordinatesToDouble(GnssData.GGA.latitude))
|
||||
web.longitude = string.format("%0.6f",nmeaCoordinatesToDouble(GnssData.GGA.longitude))
|
||||
web.altitude = GnssData.GGA.alt
|
||||
web.nsat = GnssData.GGA.sat
|
||||
web.hdop = GnssData.GGA.hdp
|
||||
else
|
||||
err[2] = "GGA: " .. GnssData.warning.gga[2] .. ' '
|
||||
end
|
||||
|
||||
if not GnssData.warning.vtg[1] then
|
||||
web.cog = GnssData.VTG.course_t
|
||||
web.spkm = GnssData.VTG.speed
|
||||
else
|
||||
err[2] = err[2] .. "VTG: " .. GnssData.warning.vtg[2] .. ' '
|
||||
end
|
||||
|
||||
if not GnssData.warning.rmc[1] then
|
||||
local dateTime = findTimeZone(GnssData.RMC.utc, GnssData.RMC.date, nmeaCoordinatesToDouble(GnssData.RMC.longitude))
|
||||
|
||||
web.utc = string.format("%s:%s", addZero(dateTime.hour), addZero(dateTime.min))
|
||||
web.date = string.format("%s.%s.%d", addZero(dateTime.day), addZero(dateTime.month), dateTime.year)
|
||||
else
|
||||
err[2] = err[2] .. "RMC: " .. GnssData.warning.vtg[2]
|
||||
end
|
||||
|
||||
if GnssData.warning.gga[1] and GnssData.warning.vtg[1] and GnssData.warning.rmc[1] then
|
||||
err[2] = "Updating data..."
|
||||
else
|
||||
err = {false, "OK"}
|
||||
end
|
||||
|
||||
return err, web
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------------
|
||||
-- public
|
||||
-----------------------------------------------------------------------------------
|
||||
|
||||
function getData(line, port)
|
||||
local err, resp = serial.read(port)
|
||||
local GnssData = createGnssForm()
|
||||
if err[1] then
|
||||
GnssData.warning.app = {true, err[2]}
|
||||
return GnssData
|
||||
end
|
||||
|
||||
GnssData.warning.app = {false, "OK"}
|
||||
|
||||
if line == "GP" then
|
||||
GnssData.warning.gp, GnssData["gp"] = getGPoint(resp)
|
||||
elseif line == "GGA" then
|
||||
GnssData.warning.gga, GnssData["gga"] = getGGA(resp)
|
||||
elseif line == "RMC" then
|
||||
GnssData.warning.rmc, GnssData["rmc"] = getRMC(resp)
|
||||
elseif line == "VTG" then
|
||||
GnssData.warning.vtg, GnssData["vtg"] = getVTG(resp)
|
||||
elseif line == "GSA" then
|
||||
GnssData.warning.gsa, GnssData["gsa"] = getGSA(resp)
|
||||
else
|
||||
GnssData.warning.app = {true, "Bad argument..."}
|
||||
end
|
||||
return GnssData
|
||||
end
|
||||
|
||||
function getAllData(port)
|
||||
local err, resp = serial.read(port)
|
||||
local GnssData = createGnssForm()
|
||||
if err[1] then
|
||||
GnssData.warning.app = {true, err[2]}
|
||||
return GnssData
|
||||
end
|
||||
|
||||
GnssData = parseAllData(resp)
|
||||
GnssData.warning.gp, GnssData["gp"] = getGPoint(resp)
|
||||
GnssData.warning.app = {false, "OK"}
|
||||
return GnssData
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local goodGNSSSdata = {
|
||||
"$GPRMC,113702.568,V,4154.931,N,08002.497,W,95.5,0.02,220721,,E*4E",
|
||||
"$GPGGA,113703.568,4154.931,N,08002.497,W,0,00,,,M,,M,,*52",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.1,0.8,0.6*3F",
|
||||
"$GPRMC,113705.568,V,4154.933,N,08002.497,W,86.0,-0.05,220721,,E*66",
|
||||
"$GPGGA,113706.568,4154.933,N,08002.497,W,0,00,0.8,,M,,M,,*73",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.0,0.4*36",
|
||||
"$GPRMC,113708.568,V,4154.935,N,08002.498,W,55.1,-0.10,220721,,E*69",
|
||||
"$GPGGA,113709.568,4154.935,N,08002.498,W,0,00,0.0,,M,,M,,*7D",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.2,0.8*3C",
|
||||
"$GPRMC,113711.568,V,4154.937,N,08002.498,W,95.0,-0.10,220721,,E*6E",
|
||||
"$GPGGA,113712.568,4154.937,N,08002.498,W,0,00,0.2,,M,,M,,*77",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.7,0.6,0.3*32",
|
||||
"$GPRMC,113714.568,V,4154.939,N,08002.498,W,28.0,-0.07,220721,,E*65",
|
||||
"$GPGGA,113715.568,4154.939,N,08002.498,W,0,00,0.6,,M,,M,,*7A",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,1.0,1.0,0.5*34",
|
||||
"$GPRMC,113717.568,V,4154.940,N,08002.498,W,30.1,0.03,220721,,E*49",
|
||||
"$GPGGA,113718.568,4154.940,N,08002.498,W,0,00,1.0,,M,,M,,*7E",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.5,0.1,0.2*36",
|
||||
"$GPRMC,113720.568,V,4154.942,N,08002.498,W,0.2,-0.02,220721,,E*53",
|
||||
"$GPGGA,113721.568,4154.942,N,08002.498,W,0,00,0.1,,M,,M,,*76",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.5,0.7*30",
|
||||
"$GPRMC,113723.568,V,4154.944,N,08002.498,W,53.6,0.05,220721,,E*4E",
|
||||
"$GPGGA,113724.568,4154.944,N,08002.498,W,0,00,0.5,,M,,M,,*71",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,1.0,0.6,0.1*37",
|
||||
"$GPRMC,113726.568,V,4154.946,N,08002.498,W,76.6,0.04,220721,,E*4F",
|
||||
"$GPGGA,113727.568,4154.946,N,08002.498,W,0,00,0.6,,M,,M,,*73",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.2,0.3,0.7*37",
|
||||
"$GPRMC,113729.568,V,4154.948,N,08002.497,W,30.9,0.12,220721,,E*4B",
|
||||
"$GPGGA,113730.568,4154.948,N,08002.497,W,0,00,0.3,,M,,M,,*71",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.2,0.2*34",
|
||||
"$GPRMC,113732.568,V,4154.949,N,08002.497,W,47.2,0.20,220721,,E*4A",
|
||||
"$GPGGA,113733.568,4154.949,N,08002.497,W,0,00,0.2,,M,,M,,*72",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.3,0.8,0.2*39",
|
||||
"$GPRMC,113735.568,V,4154.951,N,08002.496,W,31.1,0.13,220721,,E*47",
|
||||
"$GPGGA,113736.568,4154.951,N,08002.496,W,0,00,0.8,,M,,M,,*75",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.6,0.1*35",
|
||||
"$GPRMC,113738.568,V,4154.953,N,08002.496,W,58.2,0.10,220721,,E*47",
|
||||
"$GPGGA,113739.568,4154.953,N,08002.496,W,0,00,0.6,,M,,M,,*76",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.6,0.9,0.7*39",
|
||||
"$GPRMC,113741.568,V,4154.955,N,08002.496,W,88.3,0.03,220721,,E*41",
|
||||
"$GPGGA,113742.568,4154.955,N,08002.496,W,0,00,0.9,,M,,M,,*73",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.8,0.8,0.1*30",
|
||||
"$GPRMC,113744.568,V,4154.956,N,08002.496,W,89.3,0.10,220721,,E*44",
|
||||
"$GPGGA,113745.568,4154.956,N,08002.496,W,0,00,0.8,,M,,M,,*76",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.9,0.2,1.0*3B",
|
||||
"$GPRMC,113747.568,V,4154.958,N,08002.495,W,99.1,0.14,220721,,E*4D",
|
||||
"$GPGGA,113748.568,4154.958,N,08002.495,W,0,00,0.2,,M,,M,,*7C",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.1,0.2*35",
|
||||
"$GPRMC,113750.568,V,4154.960,N,08002.495,W,84.0,0.19,220721,,E*40",
|
||||
"$GPGGA,113751.568,4154.960,N,08002.495,W,0,00,0.1,,M,,M,,*7C",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.3,1.0,0.5*36",
|
||||
"$GPRMC,113753.568,V,4154.962,N,08002.495,W,24.0,0.13,220721,,E*41",
|
||||
"$GPGGA,113754.568,4154.962,N,08002.495,W,0,00,1.0,,M,,M,,*7B",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.8,0.9*37",
|
||||
"$GPRMC,113756.568,V,4154.963,N,08002.494,W,27.8,0.03,220721,,E*4E",
|
||||
"$GPGGA,113757.568,4154.963,N,08002.494,W,0,00,0.8,,M,,M,,*71",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.4,0.7*37"
|
||||
}
|
||||
|
||||
local badGNSSSdata = {
|
||||
"$GPRMC,113702.568,V,4154.931,N,08002.497,W,95.5,0.02,220721,,E*48",
|
||||
"$GPGGA,113703.568,4154.931,N,08002.497,W,0,00,,,M,,M,,*54",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.1,0.8,0.6*33",
|
||||
"$GPRMC,113720.568,V,4154.942,N,08002.498,W,0.2,-0.02,220721,,E*5F",
|
||||
"$GPGGA,113721.568,4154.942,N,08002.498,W,0,00,0.1,,M,,M,,*82",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.5,0.7*35",
|
||||
"$GPRMC,113723.568,V,4154.944,N,08002.498,W,53.6,0.05,220721,,E*5A",
|
||||
"$GPGGA,113724.568,4154.944,N,08002.498,W,0,00,0.5,,M,,M,,*12",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,1.0,0.6,0.1*35",
|
||||
"$GPRMC,113726.568,V,4154.946,N,08002.498,W,76.6,0.04,220721,,E*9E",
|
||||
"$GPGGA,113727.568,4154.946,N,08002.498,W,0,00,0.6,,M,,M,,*94",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.2,0.3,0.7*32",
|
||||
"$GPRMC,113729.568,V,4154.948,N,08002.497,W,30.9,0.12,220721,,E*9C",
|
||||
"$GPGGA,113730.568,4154.948,N,08002.497,W,0,00,0.3,,M,,M,,*79",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.2,0.2*39",
|
||||
"$GPRMC,113705.568,V,4154.933,N,08002.497,W,86.0,-0.05,220721,,E*90",
|
||||
"$GPGGA,113706.568,4154.933,N,08002.497,W,0,00,0.8,,M,,M,,*42",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.0,0.4*43",
|
||||
"$GPRMC,113708.568,V,4154.935,N,08002.498,W,55.1,-0.10,220721,,E*44",
|
||||
"$GPGGA,113709.568,4154.935,N,08002.498,W,0,00,0.0,,M,,M,,*4A",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.2,0.8*4D",
|
||||
"$GPRMC,113711.568,V,4154.937,N,08002.498,W,95.0,-0.10,220721,,E*44",
|
||||
"$GPGGA,113712.568,4154.937,N,08002.498,W,0,00,0.2,,M,,M,,*44",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.7,0.6,0.3*4D",
|
||||
"$GPRMC,113714.568,V,4154.939,N,08002.498,W,28.0,-0.07,220721,,E*4D",
|
||||
"$GPGGA,113715.568,4154.939,N,08002.498,W,0,00,0.6,,M,,M,,*4D",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,1.0,1.0,0.5*24",
|
||||
"$GPRMC,113717.568,V,4154.940,N,08002.498,W,30.1,0.03,220721,,E*59",
|
||||
"$GPGGA,113718.568,4154.940,N,08002.498,W,0,00,1.0,,M,,M,,*4D",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.5,0.1,0.2*39",
|
||||
"$GPGGA,113736.568,4154.951,N,08002.496,W,0,00,0.8,,M,,M,,*79",
|
||||
"$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.6,0.1*39",
|
||||
"$GPRMC,113738.568,V,4154.953,N,08002.496,W,58.2,0.10,220721,,E*67",
|
||||
"$GPGGA,113739.568,4154.953,N,08002.496,W,0,00,0.6,,M,,M,,*79",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.6,0.9,0.7*35",
|
||||
"$GPRMC,113741.568,V,4154.955,N,08002.496,W,88.3,0.03,220721,,E*31",
|
||||
"$GPGGA,113742.568,4154.955,N,08002.496,W,0,00,0.9,,M,,M,,*33",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.8,0.8,0.1*34",
|
||||
"$GPRMC,113744.568,V,4154.956,N,08002.496,W,89.3,0.10,220721,,E*34",
|
||||
"$GPGGA,113745.568,4154.956,N,08002.496,W,0,00,0.8,,M,,M,,*75",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.9,0.2,1.0*4B",
|
||||
"$GPRMC,113747.568,V,4154.958,N,08002.495,W,99.1,0.14,220721,,E*5D",
|
||||
"$GPGGA,113748.568,4154.958,N,08002.495,W,0,00,0.2,,M,,M,,*5C",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.1,0.2*55",
|
||||
"$GPRMC,113750.568,V,4154.960,N,08002.495,W,84.0,0.19,220721,,E*50",
|
||||
"$GPGGA,113751.568,4154.960,N,08002.495,W,0,00,0.1,,M,,M,,*5C",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.3,1.0,0.5*35",
|
||||
"$GPRMC,113753.568,V,4154.962,N,08002.495,W,24.0,0.13,220721,,E*51",
|
||||
"$GPGGA,113754.568,4154.962,N,08002.495,W,0,00,1.0,,M,,M,,*5B",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.8,0.9*35",
|
||||
"$GPRMC,113756.568,V,4154.963,N,08002.494,W,27.8,0.03,220721,,E*5E",
|
||||
"$GPGGA,113757.568,4154.963,N,08002.494,W,0,00,0.8,,M,,M,,*51",
|
||||
"$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.4,0.7*57"
|
||||
}
|
||||
|
||||
local GNSStr = "$GPRMC,143753.498,V,3854.930,N,07902.496,W,91.3,0.75,220721,,E*4B\r\n\
|
||||
$GPGGA,143754.498,3854.930,N,07902.496,W,0,00,,,M,,M,,*53\r\n\
|
||||
$GPGSA,A,2,13,09,13,09,18,16,,,,,,,0.1,0.0,0.2*3E\r\n\
|
||||
$GPRMC,143756.498,V,3854.931,N,07902.494,W,92.7,0.76,220721,,E*49\r\n\
|
||||
$GPGGA,143757.498,3854.931,N,07902.494,W,0,00,0.0,,M,,M,,*7D\r\n\
|
||||
$GPGSA,A,2,13,09,13,09,18,16,,,,,,,0.4,0.4,0.5*38\r\n\
|
||||
$GPRMC,143759.498,V,3854.932,N,07902.492,W,15.5,0.75,220721,,E*4D\r\n"
|
||||
|
||||
local badCRC = "$GPRMC,143753.498,V,3854.930,N,07902.496,W,91.3,0.75,220721,,E*6B\r\n\
|
||||
$GPGGA,143754.498,3854.930,N,07902.496,W,0,00,,,M,,M,,*43\r\n\
|
||||
$GPGSA,A,2,13,09,13,09,18,16,,,,,,,0.1,0.0,0.2*5E\r\n\
|
||||
$GPRMC,143756.498,V,3854.931,N,07902.494,W,92.7,0.76,220721,,E*48\r\n\
|
||||
$GPGGA,143757.498,3854.931,N,07902.494,W,0,00,0.0,,M,,M,,*4D\r\n\
|
||||
$GPGSA,A,2,13,09,13,09,18,16,,,,,,,0.4,0.4,0.5*39\r\n\
|
||||
$GPRMC,143759.498,V,3854.932,N,07902.492,W,15.5,0.75,220721,,E*3D\r\n"
|
||||
|
||||
local nmeaDataType = {"$GPGSA,", "$GPRMC,", "$GPGGA,"}
|
||||
local badString = "oskdsajdij232391i*&^^&7^&^(*&*YDUDHJSBDNBNVyywfdywf"
|
||||
|
||||
function testBitXOR()
|
||||
lu.assertEquals(BitXOR(string.byte('q'), string.byte('r')), 3)
|
||||
lu.assertEquals(BitXOR(string.byte('a'), string.byte('b')), 3)
|
||||
lu.assertEquals(BitXOR(string.byte('1'), string.byte('5')), 4)
|
||||
lu.assertEquals(BitXOR(string.byte('0'), string.byte('0')), 0)
|
||||
lu.assertEquals(BitXOR(string.byte('9'), string.byte('1')), 8)
|
||||
lu.assertEquals(BitXOR(string.byte('f'), string.byte('w')), 17)
|
||||
end
|
||||
|
||||
function testCRC()
|
||||
for i = 1, #goodGNSSSdata do
|
||||
lu.assertEquals(checkCRC(goodGNSSSdata[i]), true)
|
||||
end
|
||||
for i = 1, #badGNSSSdata do
|
||||
lu.assertEquals(checkCRC(badGNSSSdata[i]), false)
|
||||
end
|
||||
end
|
||||
|
||||
function testCropData()
|
||||
for i = 1, #nmeaDataType do
|
||||
lu.assertEquals(getCropData("", nmeaDataType[i]), {true, "No data found"})
|
||||
lu.assertEquals(getCropData(badString, nmeaDataType[i]), {true, "No data found"})
|
||||
lu.assertEquals(getCropData(badCRC, nmeaDataType[i]), {true, "Checksum error"})
|
||||
lu.assertEquals(getCropData(GNSStr, nmeaDataType[i]), {false, "OK"})
|
||||
end
|
||||
end
|
||||
|
||||
os.exit(lu.LuaUnit.run())
|
|
@ -1,56 +0,0 @@
|
|||
common_path = '/usr/share/gpoint/lib/?.lua;'
|
||||
package.path = common_path .. package.path
|
||||
|
||||
|
||||
function printData(data, key)
|
||||
for i, j in pairs(data) do
|
||||
if i == key then
|
||||
print("----->",i)
|
||||
for k, v in pairs(j) do
|
||||
print(k, v)
|
||||
print("*********************")
|
||||
end
|
||||
end
|
||||
end
|
||||
print("----------------------------------------------")
|
||||
end
|
||||
|
||||
function printError(data)
|
||||
for i, j in pairs(data) do
|
||||
print(i)
|
||||
for k, v in pairs(j) do
|
||||
if k == "app" then
|
||||
for m, n in pairs(v) do
|
||||
print(m, n)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local nmea = require("nmea")
|
||||
local port = "/dev/ttyUSB1"
|
||||
|
||||
-- test get NMEA data (GGA)
|
||||
--local GGA = nmea.getData("GGA", port)
|
||||
--printData(GGA, "gga")
|
||||
|
||||
-- test get NMEA data (GNS)
|
||||
--local GNS = nmea.getData("GNS", port)
|
||||
--printData(GNS, "gns")
|
||||
|
||||
-- test get RMC data (RMC)
|
||||
--local RMC = nmea.getData("RMC", port)
|
||||
--printData(RMC, "rmc")
|
||||
|
||||
-- test get NMEA data (VTG)
|
||||
--local VTG = nmea.getData("VTG", port)
|
||||
--printData(VTG, "vtg")
|
||||
|
||||
-- test get NMEA data (GSA)
|
||||
--local GSA = nmea.getData("GSA", port)
|
||||
--printData(GSA, "gsa")
|
||||
|
||||
-- test get NMEA data (BAD STRING)
|
||||
--local AAA = nmea.getData("AAA", port)
|
||||
--printError(AAA)
|
|
@ -1,16 +0,0 @@
|
|||
common_path = '/usr/share/gpoint/lib/?.lua;'
|
||||
package.path = common_path .. package.path
|
||||
|
||||
serial = require("serial")
|
||||
|
||||
function serial_read(port)
|
||||
local err, data = serial.read(port)
|
||||
assert(err, data)
|
||||
print("Error data: OK")
|
||||
assert(data)
|
||||
print("Data from serial: OK")
|
||||
end
|
||||
local port = "/dev/ttyUSB1"
|
||||
serial_read(port)
|
||||
|
||||
print("OK")
|
Loading…
Add table
Add a link
Reference in a new issue