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