Commit: 95ecd3e8f70327cc99af48d025582ff4987329e1 Author: Vi Grey Date: 2022-11-02 05:11 UTC Summary: Initial Commit .gitignore | 1 + Makefile | 41 ++ bin/sattrak | Bin 0 -> 6294800 bytes docs/draft-sattrak-v1-00.html | 1728 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ docs/draft-sattrak-v1-00.pdf | 2006 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ docs/draft-sattrak-v1-00.txt | 425 +++++++++++++++++ go.mod | 15 + go.sum | 23 + lua/sattrak.lua | 71 +++ nes/sattrak.nes | Bin 0 -> 40976 bytes src/docs/draft-sattrak-v1-00.xml | 327 +++++++++++++ src/nes/controller.asm | 508 ++++++++++++++++++++ src/nes/decompress.asm | 95 ++++ src/nes/defs.asm | 53 +++ src/nes/draw.asm | 385 +++++++++++++++ src/nes/graphics/crew-name.go | 34 ++ src/nes/graphics/crew.nam | Bin 0 -> 960 bytes src/nes/graphics/east.atr | Bin 0 -> 64 bytes src/nes/graphics/east.nam | Bin 0 -> 960 bytes src/nes/graphics/tilemap.chr | Bin 0 -> 8192 bytes src/nes/graphics/west.atr | Bin 0 -> 64 bytes src/nes/graphics/west.nam | Bin 0 -> 960 bytes src/nes/include.asm | 124 +++++ src/nes/map.asm | 518 +++++++++++++++++++++ src/nes/prg.asm | 149 ++++++ src/nes/ram.asm | 94 ++++ src/nes/sattrak.asm | 46 ++ src/nes/screen.asm | 32 ++ src/sattrak/config.go | 141 ++++++ src/sattrak/conversion.go | 91 ++++ src/sattrak/device.go | 256 ++++++++++ src/sattrak/http.go | 83 ++++ src/sattrak/http/fonts/MaterialIcons-Regular.ttf | Bin 0 -> 327360 bytes src/sattrak/http/images/map.png | Bin 0 -> 793184 bytes src/sattrak/http/index.html | 164 +++++++ src/sattrak/http/scripts/index2.js | 37 ++ src/sattrak/http/styles/index2.css | 202 ++++++++ src/sattrak/info.go | 39 ++ src/sattrak/io.go | 11 + src/sattrak/modify.go | 67 +++ src/sattrak/moon.go | 438 ++++++++++++++++++ src/sattrak/omm.go | 111 +++++ src/sattrak/orbits.go | 98 ++++ src/sattrak/sat.go | 60 +++ src/sattrak/sattrak.go | 402 ++++++++++++++++ src/sattrak/search.go | 43 ++ src/sattrak/sun.go | 54 +++ src/sattrak/syzygy.go | 40 ++ src/sattrak/tcp.go | 65 +++ src/sattrak/test.txt | 99 ++++ src/sattrak/timer.go | 59 +++ src/sattrak/ws.go | 108 +++++ 52 files changed, 9343 insertions(+) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..58fef41 --- /dev/null +++ b/Makefile @@ -0,0 +1,41 @@ +# Copyright (C) 2022, Vi Grey +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +PKG_NAME := sattrak +CURRENTDIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST)))) +DOC_NAME := draft-sattrak-v1-00 + +all: + mkdir -p $(CURRENTDIR)build/bin; \ + mkdir -p $(CURRENTDIR)build/docs; \ + mkdir -p $(CURRENTDIR)build/nes; \ + go build -ldflags="-s -w" -o $(CURRENTDIR)build/bin/$(PKG_NAME) $(CURRENTDIR)src/$(PKG_NAME)/; \ + xml2rfc $(CURRENTDIR)src/docs/$(DOC_NAME).xml -p $(CURRENTDIR)build/docs --text --html --pdf --no-external-js --no-external-css --v3 --id-is-work-in-progress --no-pagination; \ + cd $(CURRENTDIR)src/nes/; \ + asm6 $(PKG_NAME).asm ../../build/nes/$(PKG_NAME).nes; \ + cd $(CURRENTDIR); \ + +clean: + rm -rf -- $(CURRENTDIR)build; \ diff --git a/bin/sattrak b/bin/sattrak new file mode 100755 index 0000000..6e6718d Binary files /dev/null and b/bin/sattrak differ diff --git a/docs/draft-sattrak-v1-00.html b/docs/draft-sattrak-v1-00.html new file mode 100644 index 0000000..0e3a79d --- /dev/null +++ b/docs/draft-sattrak-v1-00.html @@ -0,0 +1,1728 @@ + + + + + + +SatTrack Controller Input Specification Version 1 + + + + + + + + + + + + + + + + + + + + + + + +
SatTrak Controller Input SpecificationNovember 2022
GreyInformational[Page]
+
+
+
+
Workgroup:
+
Independent
+
Published:
+
+ +
+
Author:
+
+
+
V. Grey
+
VG Interactive
+
+
+
+
+

SatTrack Controller Input Specification Version 1

+
+

Abstract

+

+
+
+
+

+Table of Contents +

+ +
+
+
+

+1. Introduction +

+
+

+1.1. Overview and Preliminaries +

+

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

+
+

+1.1.1. Notation and Vocabulary +

+

Data Types:

+
    +
  • (S) = String +
  • +
  • (A) = Binary Data +
  • +
  • (I) = Signed Integer +
  • +
  • (N) = Unsigned Integer +
  • +
  • (D) = Binary Coded Decimal Number +
  • +
  • (B) = Boolean +
  • +
+

We use the terms byte and octet interchangeably in this document.

+

Unless specified otherwise, all multi-octet binary coded decimal numbers, unsigned integers, and signed integers are big-endian.

+

String values MUST use UTF-8 encoding. If an example of a string value is given, the example will be between quotation marks. The quotation marks are not included in the string. Not all content between quotations will be string value examples, so be aware of that when continuing through this document.

+

Binary coded decimal numbers, Unsigned integers, and signed integers can have different bit widths. For instance, an 8-bit signed integer can be used in one part of the specification and a 64-bit signed integer can be used in another part of the specification.

+

Boolean values MUST be a single octet with the value of 0 for FALSE or 1 for TRUE.

+
+
[N octets]
+
A sequence of octets with a length of N +
+
+
[00 7F FF]
+
A sequence of octets 00, 7F, and FF in that order. +
+
+
[80 FF (01 02)]
+
A sequence of octets 80, FF, 01, and 02 in that order with visual grouping of 01 and 02. The visual grouping are only used as a visual indicator to imply that the bytes are related to each other in some way. +
+
+
0-indexed
+
When counting sequentially, start counting at 0. +
+
+
1-indexed
+
When counting sequentially, start counting at 1. +
+
+
+

In packet diagrams, a single octet is represented with a box like this:

+
+
+      +-----+
+      | Var | <-- Vertical bars MAY be missing
+      +-----+
+
+
+
    +
  • + Var is a variable name. +
  • +
+

In packet diagrams, an arbitrary number of octets are represented with a box like this:

+
+
+      +=====+
+      | Var |
+      +=====+
+
+
+
    +
  • + Var is a variable name. +
  • +
+

In packet diagrams, boxes can be connected like these examples:

+
+
+      +-----------+-----------+
+      | Var 1 (B) | Var 2 (I) |
+      +-----------+-----------+
+
+
+
    +
  • In this example, Var 1 is a 1 octet long boolean value and Var 2 is a 1 octet long signed integer. +
  • +
+
+
+      +-----+-----+===========+
+      | Var 1 (N) | Var 2 (S) |
+      +-----+-----+===========+
+
+
+
    +
  • In this example, Var 1 is a 2 octet long unsigned integer and Var 2 is an arbitrary octet length string. +
  • +
+

In packet diagrams, boxes MAY have relative offset values above them like in the following example:

+
+
+         0     1       2...
+      +-----+-----+===========+
+      | Var 1 (N) | Var 2 (S) |
+      +-----+-----+===========+
+
+
+
    +
  • In this example, Var 1 is a 2 octet long unsigned integer and Var 2 is an arbitrary octet length string. +
  • +
+

Packet diagrams MAY be split into multiple lined sections like in the following example:

+
+
+            0           1           2           3
+      +-----------+-----------+-----------+-----------+
+      | Var 1 (N) | Var 2 (I) | Var 3 (B) | Var 4 (I) | ...
+      +-----------+-----------+-----------+-----------+
+            4           5
+      +-----------+===========+
+      | Var 5 (N) | Var 6 (S) |
+      +-----------+===========+
+
+
+
    +
  • In this example, enough variable boxes are in the packet diagram to require being split into 2 sections to take up less horizontal space in this documentation. The packet diagram is seperated by an ellipsis (...) on the same text row as the variable names to signify that the packet diagram is continuing. +
  • +
+
+
+
+
+

+2. Packets +

+
+

+2.1. Location Packet +

+

Location packets MUST be in the following format:

+
+
+           0          1          2      4  5  6  7    8     9
+      +----------+---------+-----------+--+--+--+--+-----+-----+
+      | Sync (A) | Day (D) | Month (N) | Year (D)  | Hour (D)  | ...
+      +----------+---------+-----------+--+--+--+--+-----+-----+
+         10     11     12     13   14 15 16 17 18 19 20 21
+      +------+------+------+------+--+--+--+--+--+--+--+--+
+      | Minute (D)  | Second (D)  |     Altitude (D)      | ...
+      +------+------+------+------+--+--+--+--+--+--+--+--+
+       22 23 24 25 26 27 28 29 30 31  32  33  34  35 36 37 38 39
+      +--+--+--+--+--+--+--+--+--+---+---+---+---+--+--+--+--+--+
+      |       NORAD ID (D)       |  Sat Lat (D)  | Sat Lon (D)  | ...
+      +--+--+--+--+--+--+--+--+--+---+---+---+---+--+--+--+--+--+
+              40
+      +----------------+
+      | Sat Status (A) |
+      +----------------+
+
+      Sync (Binary Data)                      [1 Octet]
+      Day (Binary-Coded Decimal Number)       [2 Octets]
+      Month (Unsigned Integer)                [1 Octet]
+      Year (Binary-Coded Decimal Number)      [2 Octets]
+      Hour (Binary-Coded Decimal Number)      [2 Octets]
+      Minute (Binary-Coded Decimal Number)    [2 Octets]
+      Second (Binary-Coded Decimal Number)    [2 Octets]
+      Altitude (Binary Coded Decimal)         [8 octets]
+      NORAD ID (Binary Coded Decimal)         [9 octets]
+      Sat Lat (Binary-Coded Decimal Number)   [4 Octets]
+      Sat Lon (Binary-Coded Decimal Number)   [5 Octets]
+      Sat Status (Binary Data)                [1 Octet]
+         7654 3210
+         ---- ----
+         ||    |||
+         ||    ||+- Time Is UTC (1 if true otherwise 0)
+         ||    |+-- Time is 12 Hour (1 if true otherwise 0)
+         ||    +--- Time is PM (1 if true otherwise 0)
+         |+-------- Sat Longitude East (1 if lon East otherwise 0)
+         +--------- Sat Latitude North (1 if lat North otherwise 0)
+
+
+
+
Sync
+
Binary data that MUST be the value of [F0]. +
+
+
UTC
+
Boolean value that is TRUE if the time is in UTC (timezone offset of 0000) otherwise is FALSE. +
+
+
Day
+
Day value for the satellite location data. Day MUST be a binary-coded decimal value at or between [00 01] (1) and [03 01] (31). +
+
+
Month
+
Month number for the satellite location data. Month MUST be at or between the values of 1 and 12. +
+
+
Year
+
Year value for the satellite location data. Year MUST be a binary-coded decimal value at or between [00 00 00 01] (1) and [09 09 09 09] (9999). +
+
+
Hour
+
24-hour system hour number for the satellite location data. Hour MUST be a binary-coded decimal value at or between [00 00] (0) and [02 03] (23). +
+
+
Minute
+
Minute number for the satellite location data. Minute MUST be a binary-coded decimal value at or between [00 00] (0) and [05 09] (59). +
+
+
Second
+
Second number for the satellite location data. Second MUST be a binary-coded decimal value at or between [00 00] (0) and [05 09] (59). +
+
+
South
+
Boolean value that is TRUE if the latitude for the satellite location data is negative (latitude is in the Western Hemisphere) otherwise is FALSE. +
+
+
Latitude
+
Absolute value of the latitude for the satellite location data multiplied by 100. No decimal values are provided after the multiplication by 100. Latitude MUST be a binary-coded decimal value at or between [00 00 00 00] (0) and [09 00 00 00] (9000). +
+
+
West
+
Boolean value that is TRUE if the longitude for the satellite location data is negative (longitude is in the Southern Hemisphere) otherwise is FALSE. +
+
+
Longitude
+
Absolute value of the longitude for the satellite location data multiplied by 100. No decimal values are provided after the multiplication by 100. Longitude MUST be a binary-coded decimal value at or between [00 00 00 00 00] (0) and [01 08 00 00 00] (18000). +
+
+
+
+

+2.1.1. Example +

+

The information of February 27, 2022 02:46:18 UTC, Latitude: 21.56 N, Longitude: 18.70 S will result in a Location packet of [(F0) (01) (02 07) (02) (02 00 02 02) (00 02) (04 06) (01 08) (00) (02 01 05 06) (01) (00 01 08 07 00)].

+
+
+
+

+2.2. Home/Sun/Moon Packet +

+

Home/Sun/Moon packets MUST be in the following format:

+
+
+           0            1              2              3
+      +----------+--------------+--------------+-------------+
+      | Sync (A) | Home Lat (N) | Home Lon (N) | Sun Lat (N) | ...
+      +----------+--------------+--------------+-------------+
+             4             5              6
+      +-------------+--------------+--------------+
+      | Sun Lon (N) | Moon Lat (N) | Moon Lon (I) | ...
+      +-------------+--------------+--------------+
+              7                     8
+      +----------------+--------------------------+
+      | Moon Phase (U) | Sun/Moon Lon Pos/Neg (A) |
+      +----------------+--------------------------+
+
+      Sync (Binary Data)                      [1 octet]
+      Home Lat (Unsigned Integer)             [1 octet]
+      Home Lon (Signed Integer)               [1 octet]
+      Sun Lat (Unsigned Integer)              [1 octet]
+      Sun Lon (Signed Integer)                [1 octet]
+      Moon Lat (Unsigned Integer)             [1 octet]
+      Moon Lon (Signed Integer)               [1 octet]
+      Moon Phase (Unsigned Integer)           [1 octet]
+      Home/Sun/Moon Status                    [1 octet]
+         7654 3210
+         ---- ----
+         |||| | ||
+         |||| | |+- Enable Home (1 if true otherwise 0)
+         |||| | |+- Enable Sun (1 if true otherwise 0)
+         |||| | +-- Enable Moon (1 if true otherwise 0)
+         |||| +---- Home Longitude East (1 if lon East otherwise 0)
+         |||+------ Sun Longitude East (1 if lon East otherwise 0)
+         ||+------- Moon Longitude East (1 if lon East otherwise 0)
+         |+-------- Sat In Daylight (1 if true otherwise 0)
+         +--------- Sat In View (1 if true otherwise 0)
+
+
+
+
+
+
Sync
+
Binary data that MUST be the value of [F5]. +
+
+
Sun Lat
+
Latitude of the Sun + 90. +
+
+
Sun Lon Diff
+
Longitude of the Sun minus the Longitude of the Satellite. If difference is below -100 or above 100, the value for Sun Lat MUST be between or at [64] (100) and [9C] (156, which is -100). +
+
+
Moon Lat
+
Latitude of the Moon + 90. +
+
+
Moon Lon Diff
+
Longitude of the Moon minus the Longitude of the Satellite. If difference is below -100 or above 100, the value for Sun Lat MUST be between or at [64] (100) and [9C] (156, which is -100). +
+
+
Moon Phase
+
Value between and including 0 and 15 for current moon phase. 0 is a New Moon, 1 is Waxing Crescent with 12.5% illumination, 2 is Waxing Crescent with 25% illumination, 3 is Waxing Crescent with 37.5% illumination, 4 is a First Quarter Moon, 5 is Waxing Gibbous with 62.5% illumination, 6 is Waxing Gibbous with 75% illumination, 7 is Waxing Gibbous with 87.5% illumination, 8 is a Full Moon, 9 is Waning Gibbous with 87.5% illumination, 10 is Waning Gibbous with 75% illumination, 11 is Waning Gibbous with 62.5% illumination, 12 is a Third Quarter Moon, 13 is Waning Crescent with 37.5% illumination, 14 is Waning Crescent with 25% illumination, and 15 is Waning Crescent with 12.5% illumination. +
+
+
+
+

+2.2.1. Example +

+

The Sun and Moon information of Sun Latitude: -7, Sun Longitude: Earth Station - 16, Moon Latitude: 12, Moon Longitude: Earth Station - 22, Moon Phase: New Moon will result in a Sun/Moon packet of [(FC) (44) (F0) (66) (EA) (00) (44) (F0).

+

The Sun and Moon information of Moon Latitude: 0, Moon Longitude: Earth Station - 140, Moon Phase: First Quarter Moon, Sun Latitude: -4, Sun Longitude: Earth Station - 64, Moon Latitude: 0, Moon Longitude: Earth Station - 140, and Moon Phase: First Quarter Moon will result in a Sun/Moon packet of [(FC) (56) (C0) (5A) (80) (04).

+
+
+
+
+

+3. Controller Input +

+
+
+      7654 3210
+      ---- ----
+      |||| ||||
+      |||| |||+- Right on D-Pad (1 if Pressed otherwise 0)
+      |||| ||+-- Left on D-Pad (1 if Pressed otherwise 0)
+      |||| |+--- Down on D-Pad (1 if Pressed otherwise 0)
+      |||| +---- Up on D-Pad (1 if Pressed otherwise 0)
+      |||+------ Start Button (1 if Pressed otherwise 0)
+      ||+------- Select Button (1 if Pressed otherwise 0)
+      |+-------- B Button (1 if Pressed otherwise 0)
+      +--------- A Button (1 if Pressed otherwise 0)
+
+
+
+

+3.1. Example +

+

To send the byte value [F5], the following buttons MUST to be pressed:

+ +

and the following buttons MUST NOT to be pressed:

+ +
+
+
+

+4. References +

+
+

+4.1. Normative References +

+
+
[RFC2119]
+
+Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/info/rfc2119>.
+
+
[RFC8174]
+
+Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/info/rfc8174>.
+
+
+
+
+
+ +

Copyright (c) 2022, Vi Grey

+

All rights reserved.

+

Redistribution and use of this documentation in source (XML format) and/or "compiled" forms (TXT, PDF, HTML, etc), with or without modification, are permitted provided that the following conditions are met:

+
    +
  1. Redistributions of source code (XML format) of this documentation must retain the above copyright notice, this list of conditions, and the following disclaimer in the documentation. +
  2. +
  3. Redistributions in compiled form (Converted to TXT, PDF, HTML, and other formats) of this documentation must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation. +
  4. +
+

THIS DOCUMENTATION IS PROVIDED BY THE AUTHOR(S) "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+
+
+
+

+Author's Address +

+
+
Vi Grey
+
VG Interactive
+ + +
+
+
+ + + diff --git a/docs/draft-sattrak-v1-00.pdf b/docs/draft-sattrak-v1-00.pdf new file mode 100644 index 0000000..5725bcb --- /dev/null +++ b/docs/draft-sattrak-v1-00.pdf @@ -0,0 +1,2006 @@ +%PDF-1.7 +%🖤 +1 0 obj +<< +/Type /Pages +/Kids [ 6 0 R 38 0 R 48 0 R 50 0 R 56 0 R 58 0 R 64 0 R 66 0 R 74 0 R 85 0 R ] +/Count 10 +>> +endobj +2 0 obj +<< +/Producer (WeasyPrint 56.1) +/Title (SatTrack Controller Input Specification Version 1) +/Author (Vi Grey) +/Subject ( + + ) +/Creator (xml2rfc 3.15.0) +>> +endobj +3 0 obj +<< +/Type /Catalog +/Pages 1 0 R +/Outlines 106 0 R +/Names << +/Dests << +/Names [ (RFC2119) [ 74 0 R /XYZ 65.905512 641.411609 0 ] (RFC8174) [ 74 0 R /XYZ 65.905512 592.553455 0 ] (abstract) [ 6 0 R /XYZ 65.905512 663.848514 0 ] (appendix-A) [ 74 0 R /XYZ 65.905512 555.195301 0 ] (appendix-A-1) [ 74 0 R /XYZ 65.905512 507.675301 0 ] (appendix-A-2) [ 74 0 R /XYZ 65.905512 484.055916 0 ] (appendix-A-3) [ 74 0 R /XYZ 65.905512 460.436531 0 ] (appendix-A-4) [ 74 0 R /XYZ 85.905512 409.578377 0 ] (appendix-A-4.1) [ 74 0 R /XYZ 85.905512 409.578377 0 ] (appendix-A-4.2) [ 74 0 R /XYZ 85.905512 379.839608 0 ] (appendix-A-5) [ 74 0 R /XYZ 65.905512 328.981453 0 ] (appendix-B) [ 85 0 R /XYZ 65.905512 771.389764 0 ] (authors-addresses) [ 85 0 R /XYZ 65.905512 771.389764 0 ] (identifiers) [ 6 0 R /XYZ 70.905512 764.889764 0 ] (internal-metadata) [ 6 0 R /XYZ 65.905512 769.889764 0 ] (name-authors-address-3) [ 85 0 R /XYZ 65.905512 771.389764 0 ] (name-controller-input-3) [ 66 0 R /XYZ 65.905512 517.479608 0 ] (name-copyright-notice-3) [ 74 0 R /XYZ 65.905512 555.195301 0 ] (name-example-7) [ 58 0 R /XYZ 65.905512 364.785760 0 ] (name-example-8) [ 66 0 R /XYZ 65.905512 652.315301 0 ] (name-example-9) [ 66 0 R /XYZ 65.905512 343.119608 0 ] (name-home-sun-moon-packet-3) [ 58 0 R /XYZ 65.905512 294.427606 0 ] (name-introduction-3) [ 38 0 R /XYZ 65.905512 771.389764 0 ] (name-location-packet-3) [ 50 0 R /XYZ 65.905512 575.772225 0 ] (name-normative-references-3) [ 74 0 R /XYZ 65.905512 685.011609 0 ] (name-notation-and-vocabulary-3) [ 38 0 R /XYZ 65.905512 646.792225 0 ] (name-overview-and-preliminaries-3) [ 38 0 R /XYZ 65.905512 737.369764 0 ] (name-packets-3) [ 50 0 R /XYZ 65.905512 609.792225 0 ] (name-references-3) [ 74 0 R /XYZ 65.905512 719.031609 0 ] (name-table-of-contents-3) [ 6 0 R /XYZ 65.905512 626.278514 0 ] (section-1) [ 38 0 R /XYZ 65.905512 771.389764 0 ] (section-1.1) [ 38 0 R /XYZ 65.905512 737.369764 0 ] (section-1.1-1) [ 38 0 R /XYZ 65.905512 697.769764 0 ] (section-1.1.1) [ 38 0 R /XYZ 65.905512 646.792225 0 ] (section-1.1.1-1) [ 38 0 R /XYZ 65.905512 613.792225 0 ] (section-1.1.1-10) [ 48 0 R /XYZ 65.905512 724.150994 0 ] (section-1.1.1-11.1) [ 48 0 R /XYZ 85.905512 666.730994 0 ] (section-1.1.1-12) [ 48 0 R /XYZ 65.905512 643.111609 0 ] (section-1.1.1-13) [ 48 0 R /XYZ 65.905512 619.492225 0 ] (section-1.1.1-14.1) [ 48 0 R /XYZ 85.905512 562.072225 0 ] (section-1.1.1-15) [ 48 0 R /XYZ 65.905512 538.452840 0 ] (section-1.1.1-16) [ 48 0 R /XYZ 65.905512 514.833455 0 ] (section-1.1.1-17.1) [ 48 0 R /XYZ 85.905512 457.413455 0 ] (section-1.1.1-18) [ 48 0 R /XYZ 65.905512 420.174686 0 ] (section-1.1.1-19.1) [ 48 0 R /XYZ 85.905512 362.754686 0 ] (section-1.1.1-2.1) [ 38 0 R /XYZ 85.905512 590.172840 0 ] (section-1.1.1-2.2) [ 38 0 R /XYZ 85.905512 571.553455 0 ] (section-1.1.1-2.3) [ 38 0 R /XYZ 85.905512 552.934070 0 ] (section-1.1.1-2.4) [ 38 0 R /XYZ 85.905512 534.314686 0 ] (section-1.1.1-2.5) [ 38 0 R /XYZ 85.905512 515.695301 0 ] (section-1.1.1-2.6) [ 38 0 R /XYZ 85.905512 497.075916 0 ] (section-1.1.1-20) [ 48 0 R /XYZ 65.905512 325.515916 0 ] (section-1.1.1-21) [ 48 0 R /XYZ 65.905512 288.277147 0 ] (section-1.1.1-22.1) [ 48 0 R /XYZ 85.905512 220.217147 0 ] (section-1.1.1-23) [ 48 0 R /XYZ 65.905512 182.978377 0 ] (section-1.1.1-24) [ 50 0 R /XYZ 65.905512 771.389764 0 ] (section-1.1.1-25.1) [ 50 0 R /XYZ 85.905512 660.769764 0 ] (section-1.1.1-3) [ 38 0 R /XYZ 65.905512 473.456531 0 ] (section-1.1.1-4) [ 38 0 R /XYZ 65.905512 449.837147 0 ] (section-1.1.1-5) [ 38 0 R /XYZ 65.905512 412.598377 0 ] (section-1.1.1-6) [ 38 0 R /XYZ 65.905512 348.120838 0 ] (section-1.1.1-7) [ 38 0 R /XYZ 65.905512 297.262684 0 ] (section-1.1.1-8) [ 38 0 R /XYZ 65.905512 263.643299 0 ] (section-1.1.1-8.1) [ 38 0 R /XYZ 65.905512 263.643299 0 ] (section-1.1.1-8.10) [ 48 0 R /XYZ 80.905512 771.389764 0 ] (section-1.1.1-8.2) [ 38 0 R /XYZ 80.905512 263.643299 0 ] (section-1.1.1-8.3) [ 38 0 R /XYZ 65.905512 242.023914 0 ] (section-1.1.1-8.4) [ 38 0 R /XYZ 80.905512 242.023914 0 ] (section-1.1.1-8.5) [ 38 0 R /XYZ 65.905512 220.404529 0 ] (section-1.1.1-8.6) [ 38 0 R /XYZ 80.905512 220.404529 0 ] (section-1.1.1-8.7) [ 38 0 R /XYZ 65.905512 171.546375 0 ] (section-1.1.1-8.8) [ 38 0 R /XYZ 80.905512 171.546375 0 ] (section-1.1.1-8.9) [ 48 0 R /XYZ 65.905512 771.389764 0 ] (section-1.1.1-9) [ 48 0 R /XYZ 65.905512 747.770379 0 ] (section-2) [ 50 0 R /XYZ 65.905512 609.792225 0 ] (section-2.1) [ 50 0 R /XYZ 65.905512 575.772225 0 ] (section-2.1-1) [ 50 0 R /XYZ 65.905512 536.172225 0 ] (section-2.1-2) [ 56 0 R /XYZ 65.905512 771.389764 0 ] (section-2.1-3) [ 56 0 R /XYZ 65.905512 347.209764 0 ] (section-2.1-3.1) [ 56 0 R /XYZ 65.905512 347.209764 0 ] (section-2.1-3.10) [ 58 0 R /XYZ 80.905512 757.770379 0 ] (section-2.1-3.11) [ 58 0 R /XYZ 65.905512 722.531609 0 ] (section-2.1-3.12) [ 58 0 R /XYZ 80.905512 708.912225 0 ] (section-2.1-3.13) [ 58 0 R /XYZ 65.905512 673.673455 0 ] (section-2.1-3.14) [ 58 0 R /XYZ 80.905512 660.054070 0 ] (section-2.1-3.15) [ 58 0 R /XYZ 65.905512 624.815301 0 ] (section-2.1-3.16) [ 58 0 R /XYZ 80.905512 611.195916 0 ] (section-2.1-3.17) [ 58 0 R /XYZ 65.905512 575.957147 0 ] (section-2.1-3.18) [ 58 0 R /XYZ 80.905512 562.337762 0 ] (section-2.1-3.19) [ 58 0 R /XYZ 65.905512 527.098992 0 ] (section-2.1-3.2) [ 56 0 R /XYZ 80.905512 333.590379 0 ] (section-2.1-3.20) [ 58 0 R /XYZ 80.905512 513.479608 0 ] (section-2.1-3.21) [ 58 0 R /XYZ 65.905512 464.621453 0 ] (section-2.1-3.22) [ 58 0 R /XYZ 80.905512 451.002068 0 ] (section-2.1-3.23) [ 58 0 R /XYZ 65.905512 415.763299 0 ] (section-2.1-3.24) [ 58 0 R /XYZ 80.905512 402.143914 0 ] (section-2.1-3.3) [ 56 0 R /XYZ 65.905512 311.970994 0 ] (section-2.1-3.4) [ 56 0 R /XYZ 80.905512 298.351609 0 ] (section-2.1-3.5) [ 56 0 R /XYZ 65.905512 276.732225 0 ] (section-2.1-3.6) [ 56 0 R /XYZ 80.905512 263.112840 0 ] (section-2.1-3.7) [ 56 0 R /XYZ 65.905512 227.874070 0 ] (section-2.1-3.8) [ 56 0 R /XYZ 80.905512 214.254686 0 ] (section-2.1-3.9) [ 58 0 R /XYZ 65.905512 771.389764 0 ] (section-2.1.1) [ 58 0 R /XYZ 65.905512 364.785760 0 ] (section-2.1.1-1) [ 58 0 R /XYZ 65.905512 331.785760 0 ] (section-2.2) [ 58 0 R /XYZ 65.905512 294.427606 0 ] (section-2.2-1) [ 58 0 R /XYZ 65.905512 254.827606 0 ] (section-2.2-2) [ 64 0 R /XYZ 65.905512 771.389764 0 ] (section-2.2-3) [ 64 0 R /XYZ 65.905512 368.489764 0 ] (section-2.2-3.1) [ 64 0 R /XYZ 65.905512 368.489764 0 ] (section-2.2-3.10) [ 64 0 R /XYZ 80.905512 200.295916 0 ] (section-2.2-3.11) [ 66 0 R /XYZ 65.905512 771.389764 0 ] (section-2.2-3.12) [ 66 0 R /XYZ 80.905512 757.770379 0 ] (section-2.2-3.2) [ 64 0 R /XYZ 80.905512 354.870379 0 ] (section-2.2-3.3) [ 64 0 R /XYZ 65.905512 333.250994 0 ] (section-2.2-3.4) [ 64 0 R /XYZ 80.905512 319.631609 0 ] (section-2.2-3.5) [ 64 0 R /XYZ 65.905512 298.012225 0 ] (section-2.2-3.6) [ 64 0 R /XYZ 80.905512 284.392840 0 ] (section-2.2-3.7) [ 64 0 R /XYZ 65.905512 249.154070 0 ] (section-2.2-3.8) [ 64 0 R /XYZ 80.905512 235.534686 0 ] (section-2.2-3.9) [ 64 0 R /XYZ 65.905512 213.915301 0 ] (section-2.2.1) [ 66 0 R /XYZ 65.905512 652.315301 0 ] (section-2.2.1-1) [ 66 0 R /XYZ 65.905512 619.315301 0 ] (section-2.2.1-2) [ 66 0 R /XYZ 65.905512 568.457147 0 ] (section-3) [ 66 0 R /XYZ 65.905512 517.479608 0 ] (section-3-1) [ 66 0 R /XYZ 65.905512 467.159608 0 ] (section-3.1) [ 66 0 R /XYZ 65.905512 343.119608 0 ] (section-3.1-1) [ 66 0 R /XYZ 65.905512 303.519608 0 ] (section-3.1-2.1) [ 66 0 R /XYZ 85.905512 279.900223 0 ] (section-3.1-2.2) [ 66 0 R /XYZ 85.905512 261.280838 0 ] (section-3.1-2.3) [ 66 0 R /XYZ 85.905512 242.661453 0 ] (section-3.1-2.4) [ 66 0 R /XYZ 85.905512 224.042068 0 ] (section-3.1-2.5) [ 66 0 R /XYZ 85.905512 205.422684 0 ] (section-3.1-2.6) [ 66 0 R /XYZ 85.905512 186.803299 0 ] (section-3.1-3) [ 74 0 R /XYZ 65.905512 771.389764 0 ] (section-3.1-4.1) [ 74 0 R /XYZ 85.905512 747.770379 0 ] (section-3.1-4.2) [ 74 0 R /XYZ 85.905512 729.150994 0 ] (section-4) [ 74 0 R /XYZ 65.905512 719.031609 0 ] (section-4.1) [ 74 0 R /XYZ 65.905512 685.011609 0 ] (section-abstract) [ 6 0 R /XYZ 65.905512 663.848514 0 ] (section-abstract-1) [ 6 0 R /XYZ 65.905512 616.328514 0 ] (section-toc.1) [ 6 0 R /XYZ 65.905512 626.278514 0 ] (section-toc.1-1.1) [ 6 0 R /XYZ 65.905512 578.458514 0 ] (section-toc.1-1.1.1) [ 6 0 R /XYZ 77.905512 578.458514 0 ] (section-toc.1-1.1.2.1) [ 6 0 R /XYZ 77.905512 557.958514 0 ] (section-toc.1-1.1.2.1.1) [ 6 0 R /XYZ 89.905512 557.958514 0 ] (section-toc.1-1.1.2.1.2.1) [ 6 0 R /XYZ 89.905512 537.458514 0 ] (section-toc.1-1.1.2.1.2.1.1) [ 6 0 R /XYZ 101.905512 537.458514 0 ] (section-toc.1-1.2) [ 6 0 R /XYZ 65.905512 514.458514 0 ] (section-toc.1-1.2.1) [ 6 0 R /XYZ 77.905512 514.458514 0 ] (section-toc.1-1.2.2.1) [ 6 0 R /XYZ 77.905512 493.958514 0 ] (section-toc.1-1.2.2.1.1) [ 6 0 R /XYZ 89.905512 493.958514 0 ] (section-toc.1-1.2.2.1.2.1) [ 6 0 R /XYZ 89.905512 473.458514 0 ] (section-toc.1-1.2.2.1.2.1.1) [ 6 0 R /XYZ 101.905512 473.458514 0 ] (section-toc.1-1.2.2.2) [ 6 0 R /XYZ 77.905512 450.458514 0 ] (section-toc.1-1.2.2.2.1) [ 6 0 R /XYZ 89.905512 450.458514 0 ] (section-toc.1-1.2.2.2.2.1) [ 6 0 R /XYZ 89.905512 429.958514 0 ] (section-toc.1-1.2.2.2.2.1.1) [ 6 0 R /XYZ 101.905512 429.958514 0 ] (section-toc.1-1.3) [ 6 0 R /XYZ 65.905512 406.958514 0 ] (section-toc.1-1.3.1) [ 6 0 R /XYZ 77.905512 406.958514 0 ] (section-toc.1-1.3.2.1) [ 6 0 R /XYZ 77.905512 386.458514 0 ] (section-toc.1-1.3.2.1.1) [ 6 0 R /XYZ 89.905512 386.458514 0 ] (section-toc.1-1.4) [ 6 0 R /XYZ 65.905512 363.458514 0 ] (section-toc.1-1.4.1) [ 6 0 R /XYZ 77.905512 363.458514 0 ] (section-toc.1-1.4.2.1) [ 6 0 R /XYZ 77.905512 342.958514 0 ] (section-toc.1-1.4.2.1.1) [ 6 0 R /XYZ 89.905512 342.958514 0 ] (section-toc.1-1.5) [ 6 0 R /XYZ 65.905512 319.958514 0 ] (section-toc.1-1.5.1) [ 6 0 R /XYZ 77.905512 319.958514 0 ] (section-toc.1-1.6) [ 6 0 R /XYZ 65.905512 299.458514 0 ] (section-toc.1-1.6.1) [ 6 0 R /XYZ 77.905512 299.458514 0 ] (title) [ 6 0 R /XYZ 65.905512 707.858514 0 ] (toc) [ 6 0 R /XYZ 65.905512 613.528514 0 ] ] +>> +>> +>> +endobj +4 0 obj +<< +/ExtGState << +/a1.0 << +/ca 1 +>> +>> +/XObject << +>> +/Pattern << +>> +/Shading << +>> +/Font 127 0 R +>> +endobj +5 0 obj +<< +/Filter /FlateDecode +/Length 1258 +>> +stream +xYKo8W\`ٙcH 0C[E\oK@!EIlŪm5lA>i/͏[DQݤOѰmٵhl1m~k>76 "O=*hudwDKe,z?axTx CgEᛩR&)Z?wXܗ݀`'L- @!gjKA{u됕xm4X(V\$\tzqb2nO/J]}^R #KwqiZ!xL|MH-;Zk{s_߭%Ck;eŵHx;YswƆu4h6_^N!z0Pzf ;6 +/X$lw +KIʅx=m?}zlw;s67{?4, )'(tR1fNCv2^A'z9(nzN0~q#Rʨ(lVȜ *&DNRD<# 5()P^$%uB]!֒1ؘrR$/RVZΆjmUQ=)CJ!uIv +B% r;/7u48l^74Ծιf̬Xe9bA1A9`]ҸAκ«V}g41\,E<{+lBulRq%ڇVϹ?hOA> 2YƊeɋ\嶈g 1Ik+Md]*NECϕ$:m27]1ۂ4 Pr-%y-~C9mPRRqCQa4 }sDjP3\,u!}ϔ"&hٿPk4r>-E}zV/G/_/!q:9Is51P;"p7ylJinšs8) V2qq8vP3\3,w۱|;)}:Gy,^( +9SMt?Sp&IH;!>&a{B9q}_G1L7C)Pl[']YN0͓v.2՜L踰ҍSX)ɟ;a\;^8zN9r~^8{HsE]G ~DX_| +endstream +endobj +6 0 obj +<< +/Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 595.275591 841.889764 ] +/Contents 5 0 R +/Resources 4 0 R +/Annots [ 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R 24 0 R 25 0 R 26 0 R 27 0 R 28 0 R 29 0 R 30 0 R 31 0 R 32 0 R 33 0 R 34 0 R 35 0 R 36 0 R ] +/TrimBox [ 0 0 595.275591 841.889764 ] +/BleedBox [ 0 0 595.275591 841.889764 ] +>> +endobj +7 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 642.248514 128.244867 623.528514 ] +/BS << +/W 0 +>> +/Dest (abstract) +>> +endobj +8 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 604.678514 192.886957 585.958514 ] +/BS << +/W 0 +>> +/Dest (name-table-of-contents-3) +>> +endobj +9 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 578.458514 71.495356 565.458514 ] +/BS << +/W 0 +>> +/Dest (section-1) +>> +endobj +10 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 79.195307 578.458514 139.664789 565.458514 ] +/BS << +/W 0 +>> +/Dest (name-introduction-3) +>> +endobj +11 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 77.905512 557.958514 91.584955 544.958514 ] +/BS << +/W 0 +>> +/Dest (section-1.1) +>> +endobj +12 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 99.284906 557.958514 233.832270 544.958514 ] +/BS << +/W 0 +>> +/Dest (name-overview-and-preliminaries-3) +>> +endobj +13 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 89.905512 537.458514 111.674555 524.458514 ] +/BS << +/W 0 +>> +/Dest (section-1.1.1) +>> +endobj +14 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 119.374506 537.458514 238.333734 524.458514 ] +/BS << +/W 0 +>> +/Dest (name-notation-and-vocabulary-3) +>> +endobj +15 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 514.458514 71.495356 501.458514 ] +/BS << +/W 0 +>> +/Dest (section-2) +>> +endobj +16 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 79.195307 514.458514 114.814447 501.458514 ] +/BS << +/W 0 +>> +/Dest (name-packets-3) +>> +endobj +17 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 77.905512 493.958514 91.584955 480.958514 ] +/BS << +/W 0 +>> +/Dest (section-2.1) +>> +endobj +18 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 99.284906 493.958514 174.483393 480.958514 ] +/BS << +/W 0 +>> +/Dest (name-location-packet-3) +>> +endobj +19 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 89.905512 473.458514 111.674555 460.458514 ] +/BS << +/W 0 +>> +/Dest (section-2.1.1) +>> +endobj +20 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 119.374506 473.458514 161.052973 460.458514 ] +/BS << +/W 0 +>> +/Dest (name-example-7) +>> +endobj +21 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 77.905512 450.458514 91.584955 437.458514 ] +/BS << +/W 0 +>> +/Dest (section-2.2) +>> +endobj +22 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 99.284906 450.458514 212.862299 437.458514 ] +/BS << +/W 0 +>> +/Dest (name-home-sun-moon-packet-3) +>> +endobj +23 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 89.905512 429.958514 111.674555 416.958514 ] +/BS << +/W 0 +>> +/Dest (section-2.2.1) +>> +endobj +24 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 119.374506 429.958514 161.052973 416.958514 ] +/BS << +/W 0 +>> +/Dest (name-example-8) +>> +endobj +25 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 406.958514 71.495356 393.958514 ] +/BS << +/W 0 +>> +/Dest (section-3) +>> +endobj +26 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 79.195307 406.958514 156.544184 393.958514 ] +/BS << +/W 0 +>> +/Dest (name-controller-input-3) +>> +endobj +27 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 77.905512 386.458514 91.584955 373.458514 ] +/BS << +/W 0 +>> +/Dest (section-3.1) +>> +endobj +28 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 99.284906 386.458514 140.963373 373.458514 ] +/BS << +/W 0 +>> +/Dest (name-example-9) +>> +endobj +29 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 363.458514 71.495356 350.458514 ] +/BS << +/W 0 +>> +/Dest (section-4) +>> +endobj +30 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 79.195307 363.458514 131.433100 350.458514 ] +/BS << +/W 0 +>> +/Dest (name-references-3) +>> +endobj +31 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 77.905512 342.958514 91.584955 329.958514 ] +/BS << +/W 0 +>> +/Dest (section-4.1) +>> +endobj +32 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 99.284906 342.958514 205.171869 329.958514 ] +/BS << +/W 0 +>> +/Dest (name-normative-references-3) +>> +endobj +33 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 319.958514 65.905512 306.958514 ] +/BS << +/W 0 +>> +/Dest (appendix-A) +>> +endobj +34 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 319.958514 145.554193 306.958514 ] +/BS << +/W 0 +>> +/Dest (name-copyright-notice-3) +>> +endobj +35 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 299.458514 65.905512 286.458514 ] +/BS << +/W 0 +>> +/Dest (appendix-B) +>> +endobj +36 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 299.458514 146.975824 286.458514 ] +/BS << +/W 0 +>> +/Dest (name-authors-address-3) +>> +endobj +37 0 obj +<< +/Filter /FlateDecode +/Length 2862 +>> +stream +x[K$7ׯaeEH 2˲ C容, 2%2+ff̪;B߀8 9o>_>>$'>į'KgkF+aΞ%_^Ns +#@޸ˇӏO OW:Ln1ʧ?%?6߇nIs4_k)LT..?=a&?{qߜ%s^^~~ڭRʳq>~#f> ip99 Jg_\y8! {1‚ `d~;1bzb;4C;uf(wDPYtYxsQH6bkP͘5 :cIY_ʺox~=Jӄ9Yb.g%0+g PQ/gz։+z/Wf˘N.;)ڼYtM^@2뜓 A9gmP{Seqz"JlYO +JƈbG`(\{˚79dS+ +{uBD0Y`bîw+z5M}KnKzar-,]X?᪈ &܋r +l?z8t߻?ڧ9G6~_ fJ6CpyI%5ӄ-kԹ'b]#4*$p?"O 鳝(qI%844B:TzA]Rj)Hr6RVmN[֣~TOA3lN4c:6N̢&FZ .d]; 9/DMl}ςF:(oԵytՎUkO;]:s9օ^ȽB~=4>b YB\(V(nբ-bT !zVuCH9MxFz5SOZ ^ 84n0{<z&1:Ȳ'+=r6Z> +endobj +39 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 749.789764 89.132367 731.069764 ] +/BS << +/W 0 +>> +/Dest (section-1) +>> +endobj +40 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 89.132367 749.789764 182.575483 731.069764 ] +/BS << +/W 0 +>> +/Dest (name-introduction-3) +>> +endobj +41 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 719.369764 95.498285 703.769764 ] +/BS << +/W 0 +>> +/Dest (section-1.1) +>> +endobj +42 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 95.498285 719.369764 266.869623 703.769764 ] +/BS << +/W 0 +>> +/Dest (name-overview-and-preliminaries-3) +>> +endobj +43 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 249.782221 670.530994 290.740717 656.911609 ] +/BS << +/W 0 +>> +/Dest (RFC2119) +>> +endobj +44 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 300.540522 670.530994 341.499018 656.911609 ] +/BS << +/W 0 +>> +/Dest (RFC8174) +>> +endobj +45 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 631.792225 99.094965 618.792225 ] +/BS << +/W 0 +>> +/Dest (section-1.1.1) +>> +endobj +46 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 99.094965 631.792225 225.322748 618.792225 ] +/BS << +/W 0 +>> +/Dest (name-notation-and-vocabulary-3) +>> +endobj +47 0 obj +<< +/Filter /FlateDecode +/Length 1745 +>> +stream +xZ݋6߿ρ*F#A8HʕB !m^B:%[zm%ڣH4Z~-(;lnb(_wr}05gbs㙔:Xj=m>l:DEOo6/wtAXG )_(j>g/D؂>-ckuk`ዱIV?ߡ^ϫ?V`Os1ޠ#:ypn 3 c1`geMlî +O s)HVJ3dZ`W,<2$dI$HBPNX7e%BOv\ B䩕ǭ[BK{wXe6#$$S㔥8{cTYSF "-)!ٕfD@'RUb*: JGl]nM55G kG7/k*f[vɋ s3ebĖ~Y- 5ʙ[v߻Oc<{'4K\J҅jkhrb>$4=VbM:ȝ5zk*$Yև^;]tP±s\E[iܸ ݬj5^}Wx{>@+kvszw4ʊ,gdJUOhJёVuj^54K!.5mr}êDNOe! 8] `|X-;t6ǃor/^$"ɕȳY79ASCI(#k/?o?xS~0}qW"cq#/'#$8m/t(ؿ|s Ǟf>:~D4OoDtűK˝q*05pu*rM*ޯ9{K9B;9 c2zyNp'n t?G)J3 dVq唻e/qwƣMYoZO]ӟFeqE &mxW~F Q@/׷<#';|ͳSgL/{)V(z<#Q&87 ?UkO]sy4* 㭿I˳]gdrn*.o;HNvر؝>xb4IK,>8xt?$_9.I&$cE/,E,59^v/Nybk^Ӆ+;4Yz8HFD?r:nK_^Wz "QcSIO;Iz +QP6^dymt^}j7rX {T7KK[J5wLnkrq6 9\ԀE>3O:S} +]Ьu]2& 6Bбe9})lzʹnB@zQe뛐_ +endstream +endobj +48 0 obj +<< +/Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 595.275591 841.889764 ] +/Contents 47 0 R +/Resources 4 0 R +/TrimBox [ 0 0 595.275591 841.889764 ] +/BleedBox [ 0 0 595.275591 841.889764 ] +>> +endobj +49 0 obj +<< +/Filter /FlateDecode +/Length 1237 +>> +stream +xX݋6_@t3 \)-6Cm^ +I˖{45^h7z J9#ݷ$Y_| +r- < OVX{ +=Rbp>Q%w'f:&QV/ '}3HY˳)`Ue6/v\+/O71w`A4p^ e$bQ:؝/`qh~2ts;g}ec)éaVm|P7mx +*tsX7[ 5w +n /*c|0zUGj,]1Eikltl,O}me`&-z9ItEci@Egx2V?cSy&V˯y3(*2:b3%lR+˕*Qc8̝/Ӎ8+(┯Z(IS=[{J0d}e y)o]-ʑo~ef]P$T.<pj&Vk؝rZ.ԯm%5G";Py?! +endstream +endobj +50 0 obj +<< +/Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 595.275591 841.889764 ] +/Contents 49 0 R +/Resources 4 0 R +/Annots [ 51 0 R 52 0 R 53 0 R 54 0 R ] +/TrimBox [ 0 0 595.275591 841.889764 ] +/BleedBox [ 0 0 595.275591 841.889764 ] +>> +endobj +51 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 588.192225 89.132367 569.472225 ] +/BS << +/W 0 +>> +/Dest (section-2) +>> +endobj +52 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 89.132367 588.192225 144.486615 569.472225 ] +/BS << +/W 0 +>> +/Dest (name-packets-3) +>> +endobj +53 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 557.772225 95.498285 542.172225 ] +/BS << +/W 0 +>> +/Dest (section-2.1) +>> +endobj +54 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 95.498285 557.772225 192.049066 542.172225 ] +/BS << +/W 0 +>> +/Dest (name-location-packet-3) +>> +endobj +55 0 obj +<< +/Filter /FlateDecode +/Length 2130 +>> +stream +x[K6W oIEȡk[$×EQEHB!E ߼82:?11V+?(ђ1jӲӪSM63Z!z}sOh{`#s_?^߻Y;Lzg짒ݧ8} +'['mJ # _Z/hڐg_@DMPP͉a)?^}aR< +ugut`iX#,߽ٿ{;eEb'Sc[Ǿ<0S^%BR0湺M-m,zMϐ3q }@ V.~FiZ!]3p-[`FA堯[W,;FF*JQvU2[` +'ʍơf6 3SJWLFpVJ8f JСoX`+!ч)y@_?FR\8.x]0IVM0{\ ahghCYA@ZЬUkΠ4Vqu| zNJ3Iʺ3vYs*`N*&:WU3>}T}>8\t+F \.*1D"147H0`M s=e eܒF1~QgUIlBPZ$(I4%jrD w71#Dɺ +]1l֔0+RFP=WF)CL2Έcg:]=`$MYFs`T!o5XEǴe_$E4#9 /P6SX +/cWwmg!2Ic 9>Y[)XX,JPn[/)qeL,YB #ͅa.&rOQIeUk(UU,Z@A'+馱zXSw臌>fi~bdJc؂TgǁMW-<[yt9 % kCzpIK.c13Oog<OOy<;-{1 6VAJ+"X7qg)N{ q[C;dCF3w{ +h񺚞˨0>]ʺGFE޷0?}C)/ +S?oz@Ki;2v0n}臌>f)bb*6EC~cF2ѕlsҷծ\u/Zm1F$qxpXQ˯c=4nמmUZͷK,k̵>2w/cGy" f3fB̻ϻb]R3"cDQbjZT#,d Xs˥Ր|yأ2pS\ڥs1&>w.8#Q0(8k"qO:TOc#?KU>=ʉh|#6q=D5I=$8T V/HIlSZۑ MMs^˦W I=19"MKsmH7 a;I zGpwHʈn1j×F$@Tm8y}ˮJret{tN=̦SbbMuV~Ӵ2D?zsNՒRs*-3Yޓr߽*Y14"oo'hÑX=$,>wSH + +^B!I%ٻօs0߆-RhA8 yYS%W5Œ3=B?wgY8=hqJD[W"Z}\iXb9}/+s xyN>co8BfUHt.F|\u)w;tp+ҹlmôAgeR3jjD? xQ~7x@q}ur\S(6!ꢸ?Қ0628iF\* +牪ϔypq+2m,<<%uIr +endstream +endobj +56 0 obj +<< +/Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 595.275591 841.889764 ] +/Contents 55 0 R +/Resources 4 0 R +/TrimBox [ 0 0 595.275591 841.889764 ] +/BleedBox [ 0 0 595.275591 841.889764 ] +>> +endobj +57 0 obj +<< +/Filter /FlateDecode +/Length 2257 +>> +stream +x[]۶}sː  )(`<}uyI.JDQ-9dcɒ3gi?>A+&-Iw?b3)Do;'8 7;1}~x`>pQ8-;UtT~\oeeL.sqY+{/g  YLghq?7y\Ww:g~?,g +WGc%ܟ=ݱOcZMtQѣHp + :/|r{ $8^'pyN]'Sm=\\O}_lS[3 YtakgVuJ#`*ݢX`ݩ?yt;ƩW+1)Y=[sS\{B*!*b~ [%C#gjۿp~ҌrOu,fTQ,Xj&< q F$f2 4cŴ>Srt~u1pBT _HgSwcgi莍DܮStPlàv?~+@s e~_S.8L|̵V@C-z}Ra5:IЎ^և6XEoz=N9F. )6SX=B zE4Ep#~Jķg aTқ@ RQ&F0P#L[=jo5J߱?֟R2һO_. 7QV;Ҏ="fQw +5:^PEP2<2:#ȳJK؀sATMPPP$h~W׍<ˈ6J"!CݢɇiB6h䚚ꩈtgE$PWxУi00vޗ@!N#!xdQOLM~urד 5)@THpG!-.M&4>~ңWdG9TOwz ^i:ܹHW,1>bW6P֪2Mm㚣IcҷaA& RTBⷯI&b3YB25[ѧCy7\;3#UގW +.W^*NJ<{۫SQQv$#lll,I}Q2s[x0͌ܳd0#M9&mO$)f!wU6}O^?Ƀ:K.zJch{(C]lbCܮ6Hf.3۹(2qs>Nᢍ&Ea9b- Բq6 fAx):M!?]!yR{AEc=;nOߑMR|W-(#6Е|c^e d43ҁ728^(uʕZ\WKH7>Q!nLiP']KTht`@SJy8tRkU>5Q^{$KYh /{lf)lnA KUK07 nk6Pʍ8ԏAޥz}t H! GԠogT D:?>bjidX1z$|JDغՁMa\݁a}oXƵuV> +endobj +59 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 349.785760 99.094965 336.785760 ] +/BS << +/W 0 +>> +/Dest (section-2.1.1) +>> +endobj +60 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 99.094965 349.785760 143.613764 336.785760 ] +/BS << +/W 0 +>> +/Dest (name-example-7) +>> +endobj +61 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 276.427606 95.498285 260.827606 ] +/BS << +/W 0 +>> +/Dest (section-2.2) +>> +endobj +62 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 95.498285 276.427606 238.849359 260.827606 ] +/BS << +/W 0 +>> +/Dest (name-home-sun-moon-packet-3) +>> +endobj +63 0 obj +<< +/Filter /FlateDecode +/Length 1791 +>> +stream +x[ݏ6 _SE}Qpð +dð\җv@X[]zClIDRB'o@xЙneu#ZI&>@ ](%zy_yX=4ʼn +(hu9Cu_ڟ|+htz̻x*hml^MZDa{ڽ@g4"Ȟ"ɛoߍI*GS:D^jܖov"(h*"U}H́}ծ:LpčlN_%L!"y)4F0!U4Xcnaa|-CA#Ha |qZw$C}:jRW`Izo3X+pH!mv +;bEM{,V۠#vIO?qjܻt@238Τm,yf4 /qBSlkI[֔7^hk7R }W8:1#qd@@RHfz_vDZL>="ˈގwEAˌ6x +kP9^mBSaR`.bw.A_߻MY$.>c3gpV +aB;NA:!U@{(7`:((EA +§83kkvRp64e)ȋ=/xIDK3d?9O$gi0 G8=I& #ѼHy$PARPt= B ^hB>٬5RnC,˘>qjP=ma~lQZyڧc12~B(EmY]8dnVA +-r9ϥg(;(eYlf9[ve6{מvO_ҽޠ85Qe*gن>cK}NBR3K{\kM>qz[yR8eqo%``Uч +5 +UhR(k8#5$%YU|w9RBA+a|kYgrq6[4K7t-sQ=A6i >Ֆ5//i.R'pMp%Ƌq-'_7ëA`S.W}zPL\$zzha$8nH;4>|} G4B7k8H;ϖMd%N4lGa3(x/lSr s `%Wg[z0ᓞp"4efK(L4ޭU:{a8TJ8&>je[&w|B^> +endobj +65 0 obj +<< +/Filter /FlateDecode +/Length 2103 +>> +stream +x[Y7~_φRI,aCH`;RcNK{{ZU_ǪQ9Ot67RJO.ǟ!vhAR +c7x| +( Py ?lnݗ6{&re sYa5:<\~I4FSN&)''#oZ@ZU ~P:tӓz͇*H82RNI'k}ۼwB' +wԌ!J=?%6`Qln?aA.4{&`:GVnyp" >7hQuSz~kUx,:kk8.}jV~;1~Ye_11I +V[IiF[:%){ b2j EUzd&ӸO0}~6vf{%XVVw>+(c>?0tYsW,dHָb5H,}"cW`Wrp5v~7]ZR?KKZeVE*ly-L"ZȼK5bB[f{nN/%ӹ35{38TƷ.$-4L٥d.?b:ٮӂ6*wo_ax"'}XKX ɏ/ywD9R~_lVbQ0MMm:{`s=!4ٙ<0Ov8;okJ 'xB{K#'M\F&LVx缆1=W( Iþr\6 +2iz;Yxk9xC7?!}/RbI{[^㯘aMΧL wqs5ϒk#w3)̤L~yΖ4zl`ٍED *{ +vT(M,ƤIv@I#)YZ3VQ(6rE9+rW Q'vN ~ʚKNöZ[tpZ~2,ӏ|yYHI@q&V賑%*cXC^iVw1ǚd‰x͵WWTmB;dф^hp2QM=LV=K$ʭ`&;I8w%WPp㦽S֬hïf͖-sP8~RGJ93ёsmHJT݁.ϱ<~` 9zYlؚ hRN!+XԒ; i*ĜU]aiK艣 &–!T1*{ϥ G o|Gɖnz҇d6'+eOU< !s+Q=iF3PhBrY!nv8kf!N޳ C[›z.J8>VX6+j 2&p? MaXZJL's+v úH{L#?<7]58cYW o%^Xk&=ڰJZ[ZMi? 9GZ0 ]4JQPDk5uWy'5-4!SQ?mp'4 + +$,1;{eI3zy#=wC3k|J9i iDE'Ѓ0΄y]]ècrd7.ce49Щq7O>駺_ gr*Pz `$9!a??FyȒUj2NU;wJ9y.0QY +endstream +endobj +66 0 obj +<< +/Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 595.275591 841.889764 ] +/Contents 65 0 R +/Resources 4 0 R +/Annots [ 67 0 R 68 0 R 69 0 R 70 0 R 71 0 R 72 0 R ] +/TrimBox [ 0 0 595.275591 841.889764 ] +/BleedBox [ 0 0 595.275591 841.889764 ] +>> +endobj +67 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 637.315301 99.094965 624.315301 ] +/BS << +/W 0 +>> +/Dest (section-2.2.1) +>> +endobj +68 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 99.094965 637.315301 143.613764 624.315301 ] +/BS << +/W 0 +>> +/Dest (name-example-8) +>> +endobj +69 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 495.879608 89.132367 477.159608 ] +/BS << +/W 0 +>> +/Dest (section-3) +>> +endobj +70 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 89.132367 495.879608 209.115522 477.159608 ] +/BS << +/W 0 +>> +/Dest (name-controller-input-3) +>> +endobj +71 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 325.119608 95.498285 309.519608 ] +/BS << +/W 0 +>> +/Dest (section-3.1) +>> +endobj +72 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 95.498285 325.119608 148.922602 309.519608 ] +/BS << +/W 0 +>> +/Dest (name-example-9) +>> +endobj +73 0 obj +<< +/Filter /FlateDecode +/Length 2689 +>> +stream +x[K$7ׯzi/6 m|XP]e` =B +)ScTVgJ_VO0J3ӟ')'1|ɏϓgh=Sjc{A)aUt!O\saשsQ>c<}+ yJcKdrq3H,`@dAby;]#yy}rRPV3%J˧7|oBAI^z]4N/+6ͤQZerCEF*wV2"=O/EBJ( AQ-\ޢZ]{ }<.=/j5)(T=Nb&HLtt$mE)#.U,,Z\"`͖MrIz!JX2Ct1D' f(PPԪnA]3]B#?N=8amWzĠw|M[SoPhk.?#|-2;\7.F}g0Y8{Gh6d)m6Sm}1s액>F#D32ehP܈9/ D7AP0,hmH8;F+e߳I`_Qoz]L5 얡zX!2Ɠ];25h8"{ `aY0ZiZwȌV:O7yOIww5' Y)(M"96S: 1 Sr{n$Bpx~'Mq &*=נ-%hg$m1=>\Klz\9q(a|0 6Emk1MiaЂ|{uiMi+l +h/$>>\.kouV C¾P $_ު}8(MY?,!ϦcLH0ejkeY"WzSDYʰ9SZFȮP]z[] +P:@Sa<(y(x/p+\auN=D<'i>߼옫b䛲fҋi~ 6 +endstream +endobj +74 0 obj +<< +/Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 595.275591 841.889764 ] +/Contents 73 0 R +/Resources 4 0 R +/Annots [ 75 0 R 76 0 R 77 0 R 78 0 R 79 0 R 80 0 R 81 0 R 82 0 R 83 0 R ] +/TrimBox [ 0 0 595.275591 841.889764 ] +/BleedBox [ 0 0 595.275591 841.889764 ] +>> +endobj +75 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 697.431609 89.132367 678.711609 ] +/BS << +/W 0 +>> +/Dest (section-4) +>> +endobj +76 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 89.132367 697.431609 169.815229 678.711609 ] +/BS << +/W 0 +>> +/Dest (name-references-3) +>> +endobj +77 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 667.011609 95.498285 651.411609 ] +/BS << +/W 0 +>> +/Dest (section-4.1) +>> +endobj +78 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 95.498285 667.011609 231.169184 651.411609 ] +/BS << +/W 0 +>> +/Dest (name-normative-references-3) +>> +endobj +79 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 372.730707 627.792225 520.088129 614.172840 ] +/BS << +/W 0 +>> +/A << +/Type /Action +/S /URI +/URI (https://www.rfc-editor.org/info/rfc2119) +>> +>> +endobj +80 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 145.905512 614.172840 181.584711 600.553455 ] +/BS << +/W 0 +>> +/A << +/Type /Action +/S /URI +/URI (https://www.rfc-editor.org/info/rfc2119) +>> +>> +endobj +81 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 378.480219 578.934070 525.837641 565.314686 ] +/BS << +/W 0 +>> +/A << +/Type /Action +/S /URI +/URI (https://www.rfc-editor.org/info/rfc8174) +>> +>> +endobj +82 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 145.905512 565.314686 181.584711 551.695301 ] +/BS << +/W 0 +>> +/A << +/Type /Action +/S /URI +/URI (https://www.rfc-editor.org/info/rfc8174) +>> +>> +endobj +83 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 533.595301 188.422846 514.875301 ] +/BS << +/W 0 +>> +/Dest (name-copyright-notice-3) +>> +endobj +84 0 obj +<< +/Filter /FlateDecode +/Length 674 +>> +stream +xVKk19Pef4@lɡ:q.I!άd{vm'MBH7-: }%Ev$&aFsĥ"RN.'PX}wlG*kD"/Xb$Yyy=z0(%A>Nm|{`G3&i >Cr3axMƏuxh+)1v;}W}Ā!OT$?u."g ,ڊe?Uk 9N:.lXτq!Bҳ7%#0Q8POh +MHҏL@+e}VQ)E'5y1u7PVwy.#Dڒryr^YM`cTisTRýԈ4H ,'’i -K]YЬQ#>"4 T!/VC;xjHQO`VV@$oȔBP9s nLkXVoleA6jn8ͥdcs&kTgHϣ8ZQsnD:.6hg(SP[\J*_ $ȵu %"i'X5^ٔ9%:%1zvfNFT +endstream +endobj +85 0 obj +<< +/Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 595.275591 841.889764 ] +/Contents 84 0 R +/Resources 4 0 R +/Annots [ 86 0 R 87 0 R 88 0 R ] +/TrimBox [ 0 0 595.275591 841.889764 ] +/BleedBox [ 0 0 595.275591 841.889764 ] +>> +endobj +86 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 65.905512 749.789764 191.865961 731.069764 ] +/BS << +/W 0 +>> +/Dest (name-authors-address-3) +>> +endobj +87 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 98.975092 693.830994 168.992426 680.211609 ] +/BS << +/W 0 +>> +/A << +/Type /Action +/S /URI +/URI (mailto:vi@vigrey.com) +>> +>> +endobj +88 0 obj +<< +/Type /Annot +/Subtype /Link +/Rect [ 88.765131 680.211609 173.243402 666.592225 ] +/BS << +/W 0 +>> +/A << +/Type /Action +/S /URI +/URI (https://vigrey.com) +>> +>> +endobj +89 0 obj +<< +/Title (SatTrack Controller Input Specification Version 1) +/Dest [ 6 0 R /XYZ 65.905512 707.858514 0 ] +/Count 16 +/First 90 0 R +/Last 105 0 R +/Parent 106 0 R +>> +endobj +90 0 obj +<< +/Title (Abstract) +/Dest [ 6 0 R /XYZ 65.905512 663.848514 0 ] +/Count 0 +/Parent 89 0 R +/Next 91 0 R +>> +endobj +91 0 obj +<< +/Title (Table of Contents) +/Dest [ 6 0 R /XYZ 65.905512 626.278514 0 ] +/Count 0 +/Prev 90 0 R +/Parent 89 0 R +/Next 92 0 R +>> +endobj +92 0 obj +<< +/Title (1. Introduction) +/Dest [ 38 0 R /XYZ 65.905512 771.389764 0 ] +/Count 2 +/Prev 91 0 R +/First 93 0 R +/Last 93 0 R +/Parent 89 0 R +/Next 95 0 R +>> +endobj +93 0 obj +<< +/Title (1.1. Overview and Preliminaries) +/Dest [ 38 0 R /XYZ 65.905512 737.369764 0 ] +/Count 1 +/First 94 0 R +/Last 94 0 R +/Parent 92 0 R +>> +endobj +94 0 obj +<< +/Title (1.1.1. Notation and Vocabulary) +/Dest [ 38 0 R /XYZ 65.905512 646.792225 0 ] +/Count 0 +/Parent 93 0 R +>> +endobj +95 0 obj +<< +/Title (2. Packets) +/Dest [ 50 0 R /XYZ 65.905512 609.792225 0 ] +/Count 4 +/Prev 92 0 R +/First 96 0 R +/Last 98 0 R +/Parent 89 0 R +/Next 100 0 R +>> +endobj +96 0 obj +<< +/Title (2.1. Location Packet) +/Dest [ 50 0 R /XYZ 65.905512 575.772225 0 ] +/Count 1 +/First 97 0 R +/Last 97 0 R +/Parent 95 0 R +/Next 98 0 R +>> +endobj +97 0 obj +<< +/Title (2.1.1. Example) +/Dest [ 58 0 R /XYZ 65.905512 364.785760 0 ] +/Count 0 +/Parent 96 0 R +>> +endobj +98 0 obj +<< +/Title (2.2. Home/Sun/Moon Packet) +/Dest [ 58 0 R /XYZ 65.905512 294.427606 0 ] +/Count 1 +/Prev 96 0 R +/First 99 0 R +/Last 99 0 R +/Parent 95 0 R +>> +endobj +99 0 obj +<< +/Title (2.2.1. Example) +/Dest [ 66 0 R /XYZ 65.905512 652.315301 0 ] +/Count 0 +/Parent 98 0 R +>> +endobj +100 0 obj +<< +/Title (3. Controller Input) +/Dest [ 66 0 R /XYZ 65.905512 517.479608 0 ] +/Count 1 +/Prev 95 0 R +/First 101 0 R +/Last 101 0 R +/Parent 89 0 R +/Next 102 0 R +>> +endobj +101 0 obj +<< +/Title (3.1. Example) +/Dest [ 66 0 R /XYZ 65.905512 343.119608 0 ] +/Count 0 +/Parent 100 0 R +>> +endobj +102 0 obj +<< +/Title (4. References) +/Dest [ 74 0 R /XYZ 65.905512 719.031609 0 ] +/Count 1 +/Prev 100 0 R +/First 103 0 R +/Last 103 0 R +/Parent 89 0 R +/Next 104 0 R +>> +endobj +103 0 obj +<< +/Title (4.1. Normative References) +/Dest [ 74 0 R /XYZ 65.905512 685.011609 0 ] +/Count 0 +/Parent 102 0 R +>> +endobj +104 0 obj +<< +/Title (Copyright Notice) +/Dest [ 74 0 R /XYZ 65.905512 555.195301 0 ] +/Count 0 +/Prev 102 0 R +/Parent 89 0 R +/Next 105 0 R +>> +endobj +105 0 obj +<< +/Title (Author's Address) +/Dest [ 85 0 R /XYZ 65.905512 771.389764 0 ] +/Count 0 +/Prev 104 0 R +/Parent 89 0 R +>> +endobj +106 0 obj +<< +/Count 17 +/First 89 0 R +/Last 89 0 R +>> +endobj +107 0 obj +<< +/Length1 21100 +/Filter /FlateDecode +/Length 5635 +>> +stream +x2}<{)P O튩ţ,ʿr?8R2c>?7rtA1{w^327ξhChkK('/% Fr~sgH폨MASHNIB_COJU8Hz =3ց:F}I{%އbz)%J)HWMܼ:bvÐKUF]$4ڞ!hc> +jd;k£O &6]"EL%g$?biHz@z2[v)9>Hnˊˊ)zҒҮ<|\SUnPgxR!R!R!R!R!R!R!4""wЫmEO!ϼ B5t?wj>QExdP0h+eӔv7*'Oelow1f,yyz[*za#*PV8mI`qW_.D!v/T,IYAD0T2o xp1qٞpxvxL,6e5Gڽ[pK_pABY7dB(N&d23qfdKy^{c|N,=`y#0 +8d d9bs2 %cWݛj-W+k;] יzЬWK'k:0wUWܻ@h] +cՂ@U$+ZNIW ;ГjmI++T̉ߖP%tx|͵ݩT⻷1ʽu[cFmN V9r]< jsǁ?ie#wΩ4HQƛÍgXkcDU/)9}vj92W2q^-+=} +&R vxb*'>.UHJ]mr, +c5tcOJ^w-5|WV432ėBbвrk-EkKxD+KOn4bwBKnj[UVK}Zfhn,jܤoq녙FGwܬ1ֺ͍y`Cwi>Lh@z%hs\%] +_Md RKѥI)vVܚ qo$=+9-hO&zoL2ֆĎC3]ݽ]L2Xc7v2otqCuᭉ ig`_i !jK͑Dmp:m|]h(ˎ*VL#;,5# +ްYfΨ؎k] -Ύ9;@XJXvi(Yo8n#۷'6Lyrׁ_UEK|:27ES=\d= +k=d=rrAGaafb[;~Z.+|[bܟlU7_fEQy1o QKy)6)vbqA,l%藭 ;ޕ/C袳kcbEg4L%p!]ӱ3;W:lhmIt~Wkm/vu=mho;Tow:{Ւ`Ek]L$?/.~3MGu:rهE1W/^1%=Ijd7/h3~xirzz;ksK=|¹'sձ7pT:bJpV?cT@Jl}yM~=`/2—s{mc+N`)0{}nLoǗ#>._/Qo+7t^ɬw1?zo=zZ\w*85S;uGjb/В{|wXr&#N,zjbٳ%z?`XjgxW'(d>u@P(36Fr.Lq)m`!/+"6v`oXaOq"m<-*^2t4GV;-YBgNǕy9Y~=7w>GMfJVi̮kn^# Rh g 9gtVKOPfBVß}?;wl,gO>tI^Nb)9~mgfY'Ӹ)ͱxaAX-o@_{H^+p_*{ + A +aW~-YAn^Hˢ4E1ޛӶ:]/ՏA* +o(${1M]R UVagnhuDM[un]wG4z3%난e",&xraU~hZ+vM [_C}m-\|S՗ojj; 8Cx5EwmDU5t7N *>A伟σmBD&mr \q}6,A\Z׀-Hw%`{[?YE297(o'K>sr[Kg/:>+/`M4MpÝ' 9mWKLJcRZ/( LEzcԸM==sGTvN󖪥'T%[M5)h\\OmE.d6VMɵEtY +C1O.m.?v č(7͍Fg ?9[^1E1(w3Էh>תŴp pYgɺʢSZMX7[M_s|iN|Vfڦ3أF"p[w*[>&jG; pkX:uRΎ)M$Oq}rߨEɫiY8/DB\wuckkW3=]Jbޘ.5T)Kd1lwLJ;at]考 Qpk˻yį\_|Xd\N;;kG'@@JKBU%/shlb|l!0ܬp료v-4w35b04%zuŕەAk*vYYc kz#$mUg0^{|0zhzf#H".&Qإc w_t˒ )]"=Y "7bc~eJGn{|˙n\;RM:GEϜpp-ap:@tud)"̬zc˴k衖M̜=kb5aj{'!hz,d{+Vkq?']J$jCD{]6'\$fypyŻHƳJ`?nTż.e,+*e`Ry4r]76"_墡ޝ|T7md F{2KV;ix +T]ӞN&3QYt u`Ӱ:g*w@4.ܷs[Q'~0 MņpKSDRd'}C.~"hEe!R.{آzY O*c%g.^9%:e#tkyuI$]>!Li'KzU7᥺ +9P#8b6hɵN2 :}vU˺Ԕ5icm3.%0_ZdmF +6)T d+Fxξ'2UJd;M5P7HW5>3綎T<>H̳ٓxڟjdWʒ"O [&uk)Auuɪ1RX!+> q/X> +stream +x +O%G +rcHFYqMcYн +x*C] ƵC)A07^Iב69 ՄDVF$bHe)))))))))))))))VJ۷qm#4b+6|Q !:Ԏ!4fEBS=CSUIlT}MCBߡOq?Z"(&m"hXtegbxz⿊*KK~&YG +eee@>!)7ʻ˿#?w{scE"LҢܡSkHUU=י7ٜٜٜٜٜٜٜ/IΗ,-J^DN$GIm(1G6:7;T?_7[Vmr8::[o~̨y 0T-}? eI}rw{, F +T'vCc;bzUi ޶2CCeiYyd6[j;YgK:!Zw"RÊRV/5Y5*Npp-}` ~c\)S"J7({=T*R bIc:Di`&Vr]ۺ^RSI8Qmw"ZkyAWOLT-A쫲^Im&])qnp59UIuk?@F׷:G^Izh47w:EDʕ(Z'lbSNrSe綖xPCwu5\cvTj[u}~&|_$vJ,@[㽡 gm7\QGg{lB-*nƁ} M_S5:pU~yo%륞^M?Pبq.S{B55ov.lTԈDgΜF]]~LW{M!w 4g +a/>*-m 9[Y83mDGmZn8PتĊ/k%SKցD˦M-qBvHy|cCۺxԉƞԯc* ζON疎_p<3~Ms3;&"=3no+喍ʇ!dq]'nryEQN_dGɡ)Gڏd`@o=[^2 j3@5g =q.j:0x8*~AXz?~>k8q8;.AÐJ(~/4H{nirymՑHwttߵ0UTln5r]*"J$nړ _6u5>> +:!$vq3I:,W_fN G!!٨P/cJ@~Pg=b(KvW,La)[_c*7Yn"#OP)sH5"(T"._/_7{hh~ -uqk`?u*idg/[uC-iWO7<7nܐz#Ի4Rc +&[CTꑸ vmc,/m b2kdY +$&nsX6b% `N⯅Nd5ϰF+[@ JێO_En`P1g ȉŀ$kI8+O/wčߴFv<&R7:# { D*:>i>-@R^**hGU;W@k$fwx^1Pd,~ +-rz$)\T +kuL|G|I%|%zJv&(qmcd*3C&1ޥU"Qe5FXB|TmVjr^g7H5z># j/^~Sͤ^|w4dD(Q+$EA.1M+-~p`*Uܫ5μ6+@L 3MX:OvzxG 9A0^$_m~[i1| h2V&' p{GjwU1n!^jسЧp{n l2;mkؔwtҸmvk45y zPJ@K9[~c9<^ծh.{SXĴU,4VV6uUUî.v!gԱpnY7]0?spv dHK'=рqV[{jC^q.fL'gg\fU7ro;!:A痥 i`c Pۼ[7('܄]E^,q㷁?mVpC:ؼ⽳coPmvTrkb˯aZ,(枱~y`4Cg"MsL++58}ٜ F%FS::i6|JEQJ˺\YFǟdWT~UզOֿ3=.m֭ *Uj2LZeHak kϖCݞp3u톈E_ꚜ/E NNgeJߺ4%71I5dCcSCMj9n/3` Q5 +ZˈY~x5Q 24Slg[V2 L[m~^yA`c.э5gk,B" QlͰx%&jWjPGu;#Vh**6Tg 5+[R>ROZ6Z嫂hgb*RTJwJ.ASL"53!ؔ|Hr0/wV?3" ڕ֠VnQL Vy:c͇v^OOl@Q|@VFZm$u.{Q>XhWż' $~ccp?NwcM(G҄״+Oyy#yXiS+V"x)4zyjH/)btnßl7kM?x G s6+V'|ܩҲ+4|eT܏yjMyqeq6$9d@3'B⋈^܊fZ'_X߀qaZmK˰R@f"Pa#l.\ r͖+!Ob-Qh|eL96zJGPj#yv95QdsXke$5O&J +3 FE9SH_SyzSja1u.KQXɪ{F>Om_Mm-ꖣyW%cZr4F%p|A`3#?v=??hvu $Nce*uX.m;O*FF.RFޔHJKšƜ[*SѮ_xDm3-/p0[@H +[uX8b]\Ri i[t'.ba*DX"=:wefj죌`,QȦ2mLqcX?eR*x {ֈ g+tgO[+䘆p!{k |\"@~?H0澻 mr0-)Bmk B[E y]G3h +`7x0V +aNM Z 虃wyw7 $@0 fbA}\ B_cQ!qT غ!B+=oeV c [2sO\c0[wFI{9]\U{?1 }=K;&ZP9~hK@[jUĀoa٨lQ0o|THfs_qPwނ`*ܙ؅ +endstream +endobj +109 0 obj +<< +/Length1 3052 +/Filter /FlateDecode +/Length 1781 +>> +stream +xV{lW?>~ʼnqJl~ů8v٩[7m']fiִj$[dmV%ۀhLL BC]!i 1m⏱4lgI=s={ιbDE$TUkD]ϝέLf@_RmJRH~zvc=p6z΃ogDκ\:hٷ1*ۺ!O^om a/o\y,B^ޤTdRZ|xclnj>C9?E`,TL^+FOyw-7 + 2 *ҍ?-/5PEN[sͿ,>vr8F tFA{znn-4`s|Nh;!RCig7kv7[SX*yEM=d@˴^"dH&DcN" +-]E7MOk~oG&1@{‡!Km@_QQ@(Z\!+Q%-8}Z,J1WB^//r[^ϧϯ+}A)7(MC[OԾ;3˳o]]D=C>,Ml31%=6^:!<p&~]ij!ϰ ']GGF&ϊBQR.u"w'ߠ[Pբ"˱\E(":uf.8RI 't:9t)DsaSJmnau7~aN=fgOAɅ䋽-+*<Qٳ@vva=qbv:jo-9i\v:^jr|`|&#-O?@ ? +6_Sy0p£/,(,dB/:z.=l06;wCCx?4 .jI/0`^' &)&}K{aQ/6cP'y!})AU;V;WbE(Δ^TjG>NJ`6d+^\!4tTZtYi,Y:ݝ̩&v{[sǧ&ROue]~G~Eqױ]QCN=UI'ⱔS;o Ue(Tr/C|%LF;SI-MgZ5͂e}V=QІn/A4A}OQި&ofJJ0X8 +`4`Z} +,FKH&CtgYX +endstream +endobj +110 0 obj +<< +/Length1 18940 +/Filter /FlateDecode +/Length 14612 +>> +stream +x|\2 @!@؄6=Ľy{+D;njkGZ+Ž7 +(27I h}s8ys} -|8%znD"c'KA?'EOj66^NeUɝK2'U7p=ea柁oegn BuBGXKQ-Z> +Y]֡4kksk-[9/1`LXݿcTitRrZ |{l>O #I8d +2B#u?\yf3BJzÐh$IE&#ː5?{aA_UD8O]DZp AـdqQ)]oߠy ,[ .L==%>NEԋ.y==|fFc&cӚ|Ylsx[eE X{_ k65 'B#}*CkN6y|})%o`=g #`\' FVժ*R\i-R{)/i\*JI95o`VnL8T%fiR.2s`i)TTznnBLFB7Z4[3WyZ@bvp}}ѡ~L.u>W*U(rD/˔$=6S#HG~Ϙ1 Zm`/A61D8`9#wbOc_ $W9#->\b)yao|2 oσyD'~Wv(y;!V |_VkЏ_(-y{ +mHi!ڣ%5h!R֘c< xgqr {8o9cJ%"b`5xK@:A:H +IW"H*kJ|":t1[nHf8NqGlؾ2 hg3GlX؆=i9YjuP/+9y{ڞmagoAJJ z;`oj][Z[ '%}-"Kf~{8#W3F0n*oo7eQX"cAA! +ӡn +~\BIކݸ vCP]¾A].ar11G3MOvƬvwQ{&j; +viZr 1T.//] M}$4q_4 tw_]co.z0.nx}~+}`4U 4J6!66oΔ11'ꣷѬQUaSE +|q-x??3ҿJ\\FLsqqqW(F ~/-8]*u# Il^:J۶mbGj@=@9ZU\uǔrŲr +%04>"E##)yC||t>>EC#11Ù Ĩǰ RґT ߁޾j:.\e@DlIiؙcѫ+4> ?[Z" ѐ|bj843`x S>_a9 VZ|E;>8d[ BSR {PT~k)qNvH$x|=NmPHE7<Af5Bo4g73 +`N]'r)tՇ('?-<< E9q/> ƟMNZ͠K?amy`gK92WIzdrwP6wk.Tz)y6[hV_~ 4ph7㍶ϴv#@LqFi×ay"_BDHV(]*ꂖyd ˰tZ G ͗\Ivvj5K+^ +l7Y +% v_IBB,%d8]FqY\MO0x diX[0-'@s\3g]5~~_L^v[n'pPLe[=0/*ɔF^8h2}n~?Hvstte!;';,[a5NaYjyBʯס";sr(sr灻!#(d9n?G$pߓGI]ٶIkF ևb"OyxTdlldً͇;oFeј5cBS᝔|c76((::(۱qЍ.D$挀 p9\Gϥ۵ZNY%؇Ucc%N^{sg*\f +# zs-IINxc\xv.Ugoύ} xp) !3y'_N)WjҵBDoa;IVc,2x_2Kl8?b7/~ĵHraVn:yzhLD̟N2+5S,vDŎ>b!-8n/ĘL2ǖpq(qt%|F@ڳ|N/8;%mH6Q&SCZzl)S (sJ8r4.($'' 4extW^t:gc (W0!{=Dd2Dw)<#B3ɉT'/٩%Bd@mzt}̔9#6~BxSm̨؀c?,Y޷@(Ux$e47`phEryDTTXxaQQy+W 2Z{鄧kFd\Wb/%' _> nƝ\]rWy +l-ԽE.) 򭐺EJ&| ,QƳPʹB,7|]got0D^Ut9enJ@Q3O"eޜPr_ۀNn؉cN,v0.nո`oH AF%~ lNdR qH49U mI )I6qHL9?nwHqofXe_G] +u:x<|c]T ҮWW*b | +-XW76e; +>l^SF\ȗh0~?cګhyM&>WiWR \PSxWi-81t/^~0!.ihL@t1O>y]QNq-tR׏<łD7S?#6 0J+mZE< >d*]7-AGĭ}'Md.'Rh ƭ 6(ʕ&;N'&-Đ@`!+Н؜_9Q"(dz;4|;6-2!O$wG->j?5!*SGT7ҧ"rtpEHEhHC\G8D DS& |0ҧ#hHбQ&GV SYpͨأַ&Gb`GF# EZDC4Л# {@e@[ M8Y0,3uFXޣ+}.}HuZ,Y+eZV_ 3|-wȮ 0w! IyRC3_oBM+-%?c*\7I^M} pkχu@ ep&8{1p,ߊ %wAlF#wqiK~-+Y੖wػ %M_<((uxmЄ֨ȯ0μF^m-0ІW8#={7Y!I0|sN0IȽ_C!0R +m,P:e12!ߺn".Ϩ ACYj&Brۄ-F,'>].JM͡YѦzkKXxΜ+ZjzU`e]`efmǮ]\7Qw9u^לksph= Xxag<v{14 !ѐ{QHrl}p;DC_)u b+Os%u{)S,]6gR޽{'Lu#Z Y<`533h4 38+J@s_lriѢE'%߿q!CBC椥E݃b=5\.򤢢ĆNCsϟ?,hB^IsO r.++m2Y 'T64:i88ydE[ᾬޖ׏ݑco-VޡIoWT>}kbyN.R94v7=5{oٗ{~:GN0mnzƄ8Q?gvs[*g$b82G3ݷ{m۶u!hXhoZhc/\jF(#0"9%e 6pz# 峝EY?J`DHE`꒟WXYP <єSAke!IDF]AY(+O_B +eԖ#Yh+O<,a1BhL9GiH^5Y̯> /R7J޽ǝj͚ (A.!qyam׮ql%))GVV(~ټАej6D +eޔԤ}0k,0 x vbӛ?s {V Tjw}Xav4r7657y2̘}쵂A;~? aD"-. :^8mR,XO{#8uuMwo×عsWw4kGWR:z$Ni.9~`8߭ilɖڛU>zp:if8Y,:mCgdG`t\yC{-V " `H}}e2"gVtG*ooouw555;Wh=% +ĄxJ$֦!0۠ +6,GuR&fv=ss-n4fCeOil%T_YDङY#v5gXksݿ +'-ݭ/<1dM;y ΌRl'(lݬ ҧN6gfX0)|k^C!x&z{Q\K P]dQf$3fovH{Ka b69>sp3e , ¸Uc HXR"%R64vP8a*yήk"yT̕W gpŲj]-+/9l)Qz3)01Qqyc'CWCt5d+9.b.ɄpWˮ}hcf,\p\ґ +Ph`q]=}#kjuAZSB0y\4Da +\C]h8P߹z񌁎6R# 'iX:PsGŏ s9ysUw@[g2͗Le@O*[1;\pq'j?1Prrސe I1c|ͶfҲ,BjdC@;E^##j o\8Gc[hnt͝=F*Ĵ#o=|ipHL YS^;qĹ@@)!2^D 3<*q:ON#FBaXKݿ?iDw gwV&MJ3?8o  spgB=t?[P_0׀D`|`*YU哊Wo;9;w~ʧj-'T0z_K.S(f|I6 (  J_0:@W|̟KE֦w}OdWU0qv[6=eWRc/۶]%PL'`ӲZ:9l +Ș%2*>u|r28GF묩ރS:+ݿ}vRb&&&O?64@ow/$@ +8qiͅϟ9U*N%?q=0v6}͍ C8V1dӦeRO?")HTz;Pk=y )h! In."myԖuuȥKYOպzedPV^BDW(>7GZ*l5 .ҩ!)0DսohyΝ;-CT붖/:\G7 75ֿ|Q ;"H- zˊݹSXhc^˦qN Hg{p+`dd;!=m䈼^ W䯿Ts^%]<~9s**@X>:@MYhpgNao"Ё' ⰰOt^JGnAAN)w~Z]aXm7O>uaU]QY7Cr=c%`/A8Yw@!{(G-8,//n9zrB6xx8X65UXbuuEEG64mCU`'Esz}]G 3޼j^GD"mPew8y%²#1T8DQd qi?o}q n*("RCHPKKhhH=S13Y".ţSTaH\<|Bt!m+ρЀ+}0\KˎcE'u(^UO~bPw+qgKPBwYpzHu_kGW9uixNbo|YVT8,Onn*o/OO_m),TRp rAucm4gSPd27ALE;[Zۻz(`rG.5W^j(!po ^Nf3htkeu^#m{HNNΑ f(mkg)649999"t@}2},u#%ʤlɼKJ!N6bא VEܹ3g8W!-vbβwJk`3dߥǗnz kn|\Jސ3"]I+ ϵӷK_dKګ45?)閘>!WfƬHgg.͕zz8B#ajwVoݺzʳ^vݾ]PXTj`G{eH:@v?ySHg{o޼ɸSpaqQὂ[-ӊm'T +L F+H8R-O3ݻwAYsww?,$=-M/jUV>}҉sӃO9mʤcE+{B'KJ Kuڴݧ*^vewT<շߓwOQ} Ee&ac/V]Į*J,VPb\*b3cؘظx(q. [/sSQ1gn/P٣wѴ(lt0+GOǯ<|VEu3Q/SPYE rFFFBSS[Eك07:$@XAZd&5Ç) v{QdggJΆ,= ءOe!"6]wnknr=!ѐe^nga|OGE%Oوۆl%1tD{S]mg213XlϾ~ +HJNIJL!l$^v$ .jLpHݳ0, ND%iEüshn%3%e[a/rwH?@Y.n1K~{IYK('@~6ë(\~+b>$Q/G0{L6fJ[5_?k rG~G>7$MF8fs4d2# KI^\?sv:vDsVM@1T* +qpqhppgD_sƹnh||RRR1(6(JP/^<Q|UgUVWT=[F,ϫȣuvgя?э $֐ֳo|Xѳp*N59Շ7tlnC<A9JOOO//O %C\qQQQqiyMsg'x㊧ aݍuu/;{vKGn:86 3y睌bݟ k-ۆ:qlggӿqܭba[چv.b'ti˖-K)p MO{n?d7 u5фSW[ǡLl`$LLqHH_iC5 +F +I2aڜ9ƲwtCR0v䤤'30Tȅk޼YSNL|ҧO6# .*/b!q#d3LfQz L|Tp ԅkU=I*z  #Nva [ar) -c2^(Fh HC`g-} #klh9j /pXs4h6O8g{?V]W{|YnKdΰ)ݙh1;ZMLEk^p+6͆#RO+<<տw7=({Pp;uq7ԑ=ii%KO{sGa8fu>򨤸yC'UU0L:Fc(7F}?g ;ǎ +VC---u5Ϟ7T?/yZ 5v֊+MK7ػ/<%T]f%{Pۗ͵PV }᧲z!p̜#eχzRj73)o_yш6\GB* Ey u޼~FSid@/uQAj+" ȿ-Ͷr;jߺuVF$$$ʨ=́-[8:.I:g@e6R,Z&.ė>Ȃ6F㺸G(E<T  _h[mf?$=s|-yz?Jzz' +1P[sm̶ac3a`{]}ȡ#L\7]}щ=` ~=xaQ&L5BdM +`ZZ Gbd:Q0Ddb n-#_T\;wTçϜ9Eskk;t{۟K4S=So3{ڿI53F f3JK/qr5AjFcuYYu#C$l?qFa0_ySR(jȰzt&ښ/tٹYLtU:ڲ+Ie]n44maT-+ojmxl)C,Q₩W@v[(}QEe_#: =*C}_R)tW?)sÒҲOٷgώw>O~dvOo;o߾ +6Mppww7C, |Lk_޽zO +ڪn]9}Y;bE@#5KG--2Ty٢7/&+^.WWt`w0aBP-A,INmJF6 Q3Ypw#Otܽzf݃#Ŧ_v]:,{\o S +m)nQe{^G#ϴr +endstream +endobj +111 0 obj +<< +/Length 1256 +>> +stream +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (Adobe) +/Ordering (UCS) +/Supplement 0 +>> def +/CMapName /Adobe-Identity-UCS def +/CMapType 2 def +1 begincodespacerange +<0000> +endcodespacerange +66 beginbfchar +<0036> <0053> +<0044> <0061> +<0057> <0074> +<0037> <0054> +<0055> <0072> +<0046> <0063> +<004e> <006b> +<0003> <0020> +<0026> <0043> +<0052> <006f> +<0051> <006e> +<004f> <006c> +<0048> <0065> +<002c> <0049> +<0053> <0070> +<0058> <0075> +<004c> <0069> +<0862> <00660069> +<0039> <0056> +<0056> <0073> +<0014> <0031> +<0024> <0041> +<0045> <0062> +<0049> <0066> +<0013> <0030> +<0010> <002d> +<0047> <0064> +<005b> <0078> +<0011> <002e> +<0032> <004f> +<0059> <0076> +<005a> <0077> +<0033> <0050> +<0050> <006d> +<0031> <004e> +<005c> <0079> +<0029> <0046> +<002f> <004c> +<0028> <0045> +<0035> <0052> +<0038> <0055> +<0030> <004d> +<0034> <0051> +<0027> <0044> +<002b> <0048> +<003c> <0059> +<0015> <0032> +<004b> <0068> +<003a> <0057> +<004a> <0067> +<001a> <0037> +<000f> <002c> +<001d> <003a> +<0017> <0034> +<0019> <0036> +<001b> <0038> +<0018> <0035> +<0012> <002f> +<085f> <00660066> +<0016> <0033> +<0025> <0042> +<003e> <005b> +<001c> <0039> +<0040> <005d> +<000a> <0027> +<002a> <0047> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +endstream +endobj +112 0 obj +<< +/Type /FontDescriptor +/FontName /UAKETE+Noto-Serif-Bold +/FontFamily (Noto Serif) +/Flags 6 +/FontBBox [ -2 -9 740 723 ] +/ItalicAngle 0 +/Ascent 1068 +/Descent -293 +/CapHeight 723 +/StemV 80 +/StemH 80 +/FontFile2 107 0 R +>> +endobj +113 0 obj +<< +/Type /Font +/Subtype /CIDFontType2 +/BaseFont /UAKETE+Noto-Serif-Bold +/CIDSystemInfo << +/Registry (Adobe) +/Ordering (Identity) +/Supplement 0 +>> +/CIDToGIDMap /Identity +/W [ 3 [ 260 ] 10 [ 290 ] 15 [ 294 310 294 288 559 559 559 559 559 559 559 559 559 559 304 ] 36 [ 753 672 668 767 653 621 769 819 401 ] 47 [ 654 952 788 787 638 787 707 586 653 747 698 1067 ] 60 [ 693 ] 62 [ 414 ] 64 [ 414 ] 68 [ 599 649 527 649 571 407 560 667 352 ] 78 [ 636 352 986 667 613 645 ] 85 [ 523 488 405 667 606 856 646 579 ] 2143 [ 762 ] 2146 [ 719 ] ] +/FontDescriptor 112 0 R +>> +endobj +114 0 obj +<< +/Type /Font +/Subtype /Type0 +/BaseFont /UAKETE+Noto-Serif-Bold +/ToUnicode 111 0 R +/Encoding /Identity-H +/DescendantFonts [ 113 0 R ] +>> +endobj +115 0 obj +<< +/Length 1466 +>> +stream +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (Adobe) +/Ordering (UCS) +/Supplement 0 +>> def +/CMapName /Adobe-Identity-UCS def +/CMapType 2 def +1 begincodespacerange +<0000> +endcodespacerange +81 beginbfchar +<003a> <0057> +<0052> <006f> +<0055> <0072> +<004e> <006b> +<004a> <0067> +<0058> <0075> +<0053> <0070> +<001d> <003a> +<0033> <0050> +<0045> <0062> +<004f> <006c> +<004c> <0069> +<0056> <0073> +<004b> <0068> +<0048> <0065> +<0047> <0064> +<0024> <0041> +<0057> <0074> +<002c> <0049> +<0051> <006e> +<0014> <0031> +<0003> <0020> +<0031> <004e> +<0059> <0076> +<0050> <006d> +<0015> <0032> +<0013> <0030> +<0039> <0056> +<0011> <002e> +<002a> <0047> +<005c> <0079> +<0062> <00a0> +<0046> <0063> +<0032> <004f> +<005a> <0077> +<0044> <0061> +<002f> <004c> +<0028> <0045> +<005b> <0078> +<002b> <0048> +<0012> <002f> +<0036> <0053> +<0030> <004d> +<0016> <0033> +<0026> <0043> +<0017> <0034> +<0035> <0052> +<0049> <0066> +<000a> <0027> +<0037> <0054> +<0005> <0022> +<000f> <002c> +<0025> <0042> +<0027> <0044> +<000b> <0028> +<000c> <0029> +<0020> <003d> +<0038> <0055> +<0862> <00660069> +<0010> <002d> +<0029> <0046> +<001b> <0038> +<0054> <0071> +<085f> <00660066> +<0019> <0036> +<003e> <005b> +<001c> <0039> +<0040> <005d> +<001a> <0037> +<005d> <007a> +<0018> <0035> +<003c> <0059> +<000e> <002b> +<0008> <0025> +<0034> <0051> +<003b> <0058> +<001e> <003b> +<002e> <004b> +<001f> <003c> +<0021> <003e> +<0023> <0040> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +endstream +endobj +116 0 obj +<< +/Type /FontDescriptor +/FontName /ENATNB+Noto-Serif +/FontFamily (Noto Serif) +/Flags 6 +/FontBBox [ -4 -142 867 713 ] +/ItalicAngle 0 +/Ascent 1069 +/Descent -292 +/CapHeight 713 +/StemV 80 +/StemH 80 +/FontFile2 108 0 R +>> +endobj +117 0 obj +<< +/Type /Font +/Subtype /CIDFontType2 +/BaseFont /ENATNB+Noto-Serif +/CIDSystemInfo << +/Registry (Adobe) +/Ordering (Identity) +/Supplement 0 +>> +/CIDToGIDMap /Identity +/W [ 3 [ 260 ] 5 [ 408 ] 8 [ 896 ] 10 [ 220 346 346 ] 14 [ 559 250 310 250 288 559 559 559 559 559 559 559 559 559 559 286 286 559 559 559 ] 35 [ 921 705 654 614 727 623 590 714 793 367 ] 46 [ 700 623 938 763 742 604 742 656 544 613 717 675 1047 660 625 ] 62 [ 360 ] 64 [ 360 ] 68 [ 563 614 492 614 535 369 538 635 320 ] 78 [ 585 310 945 645 577 614 614 471 451 352 635 579 862 578 565 511 ] 98 [ 260 ] 2143 [ 708 ] 2146 [ 663 ] ] +/FontDescriptor 116 0 R +>> +endobj +118 0 obj +<< +/Type /Font +/Subtype /Type0 +/BaseFont /ENATNB+Noto-Serif +/ToUnicode 115 0 R +/Encoding /Identity-H +/DescendantFonts [ 117 0 R ] +>> +endobj +119 0 obj +<< +/Length 492 +>> +stream +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (Adobe) +/Ordering (UCS) +/Supplement 0 +>> def +/CMapName /Adobe-Identity-UCS def +/CMapType 2 def +1 begincodespacerange +<0000> +endcodespacerange +12 beginbfchar +<0039> <0056> +<002a> <0047> +<0003> <0020> +<002c> <0049> +<0051> <006e> +<0057> <0074> +<0048> <0065> +<0055> <0072> +<0044> <0061> +<0046> <0063> +<004c> <0069> +<0059> <0076> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +endstream +endobj +120 0 obj +<< +/Type /FontDescriptor +/FontName /LETBFK+Noto-Serif-Italic +/FontFamily (Noto Serif) +/Flags 70 +/FontBBox [ -20 -8 505 541 ] +/ItalicAngle 0 +/Ascent 1069 +/Descent -292 +/CapHeight 541 +/StemV 80 +/StemH 80 +/FontFile2 109 0 R +>> +endobj +121 0 obj +<< +/Type /Font +/Subtype /CIDFontType2 +/BaseFont /LETBFK+Noto-Serif-Italic +/CIDSystemInfo << +/Registry (Adobe) +/Ordering (Identity) +/Supplement 0 +>> +/CIDToGIDMap /Identity +/W [ 3 [ 260 ] 42 [ 714 ] 44 [ 367 ] 57 [ 675 ] 68 [ 579 ] 70 [ 487 ] 72 [ 493 ] 76 [ 304 ] 81 [ 599 ] 85 [ 468 ] 87 [ 368 ] 89 [ 538 ] ] +/FontDescriptor 120 0 R +>> +endobj +122 0 obj +<< +/Type /Font +/Subtype /Type0 +/BaseFont /LETBFK+Noto-Serif-Italic +/ToUnicode 119 0 R +/Encoding /Identity-H +/DescendantFonts [ 121 0 R ] +>> +endobj +123 0 obj +<< +/Length 1178 +>> +stream +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (Adobe) +/Ordering (UCS) +/Supplement 0 +>> def +/CMapName /Adobe-Identity-UCS def +/CMapType 2 def +1 begincodespacerange +<0000> +endcodespacerange +61 beginbfchar +<0076> <005b> +<000f> <004e> +<0001> <0020> +<002a> <006f> +<001e> <0063> +<002f> <0074> +<0020> <0065> +<002e> <0073> +<0077> <005d> +<0036> <0030> +<003d> <0037> +<0007> <0046> +<003e> <0038> +<0074> <0028> +<0037> <0031> +<0038> <0032> +<0075> <0029> +<007c> <002b> +<0067> <002d> +<008e> <007c> +<0017> <0056> +<001c> <0061> +<002d> <0072> +<0084> <003c> +<0024> <0069> +<0027> <006c> +<001d> <0062> +<000e> <004d> +<0002> <0041> +<001a> <0059> +<0028> <006d> +<0029> <006e> +<0022> <0067> +<0081> <003d> +<0003> <0042> +<000a> <0049> +<0014> <0053> +<0060> <002e> +<0039> <0033> +<003a> <0034> +<003b> <0035> +<003c> <0036> +<003f> <0039> +<0034> <0079> +<0005> <0044> +<0023> <0068> +<0009> <0048> +<0030> <0075> +<001f> <0064> +<0010> <004f> +<0013> <0052> +<000d> <004c> +<0004> <0043> +<0016> <0055> +<0015> <0054> +<0021> <0066> +<0032> <0077> +<0011> <0050> +<0006> <0045> +<0089> <002f> +<002b> <0070> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +endstream +endobj +124 0 obj +<< +/Type /FontDescriptor +/FontName /FCTWHU+Roboto-Mono +/FontFamily (Roboto Mono) +/Flags 4 +/FontBBox [ 0 -203 530 538 ] +/ItalicAngle 0 +/Ascent 1047 +/Descent -270 +/CapHeight 538 +/StemV 80 +/StemH 80 +/FontFile2 110 0 R +>> +endobj +125 0 obj +<< +/Type /Font +/Subtype /CIDFontType2 +/BaseFont /FCTWHU+Roboto-Mono +/CIDSystemInfo << +/Registry (Adobe) +/Ordering (Identity) +/Supplement 0 +>> +/CIDToGIDMap /Identity +/W [ 1 [ 600 600 600 600 600 600 600 ] 9 [ 600 600 ] 13 [ 600 600 600 600 600 ] 19 [ 600 600 600 600 600 ] 26 [ 600 ] 28 [ 600 600 600 600 600 600 600 600 600 ] 39 [ 600 600 600 600 600 ] 45 [ 600 600 600 600 ] 50 [ 600 ] 52 [ 600 ] 54 [ 600 600 600 600 600 600 600 600 600 600 ] 96 [ 600 ] 103 [ 600 ] 116 [ 600 600 600 600 ] 124 [ 600 ] 129 [ 600 ] 132 [ 600 ] 137 [ 600 ] 142 [ 600 ] ] +/FontDescriptor 124 0 R +>> +endobj +126 0 obj +<< +/Type /Font +/Subtype /Type0 +/BaseFont /FCTWHU+Roboto-Mono +/ToUnicode 123 0 R +/Encoding /Identity-H +/DescendantFonts [ 125 0 R ] +>> +endobj +127 0 obj +<< +/UAKETE 114 0 R +/ENATNB 118 0 R +/LETBFK 122 0 R +/FCTWHU 126 0 R +>> +endobj +xref +0 128 +0000000000 65535 f +0000000015 00000 n +0000000138 00000 n +0000000315 00000 n +0000010396 00000 n +0000010511 00000 n +0000011842 00000 n +0000012260 00000 n +0000012394 00000 n +0000012544 00000 n +0000012678 00000 n +0000012824 00000 n +0000012961 00000 n +0000013121 00000 n +0000013261 00000 n +0000013419 00000 n +0000013554 00000 n +0000013695 00000 n +0000013832 00000 n +0000013981 00000 n +0000014121 00000 n +0000014263 00000 n +0000014400 00000 n +0000014554 00000 n +0000014694 00000 n +0000014836 00000 n +0000014971 00000 n +0000015121 00000 n +0000015258 00000 n +0000015399 00000 n +0000015534 00000 n +0000015678 00000 n +0000015815 00000 n +0000015969 00000 n +0000016105 00000 n +0000016255 00000 n +0000016391 00000 n +0000016540 00000 n +0000019476 00000 n +0000019745 00000 n +0000019880 00000 n +0000020026 00000 n +0000020163 00000 n +0000020323 00000 n +0000020458 00000 n +0000020593 00000 n +0000020732 00000 n +0000020889 00000 n +0000022708 00000 n +0000022909 00000 n +0000024220 00000 n +0000024461 00000 n +0000024596 00000 n +0000024737 00000 n +0000024874 00000 n +0000025023 00000 n +0000027227 00000 n +0000027428 00000 n +0000029759 00000 n +0000030000 00000 n +0000030139 00000 n +0000030280 00000 n +0000030417 00000 n +0000030571 00000 n +0000032436 00000 n +0000032637 00000 n +0000034814 00000 n +0000035069 00000 n +0000035208 00000 n +0000035349 00000 n +0000035484 00000 n +0000035634 00000 n +0000035771 00000 n +0000035912 00000 n +0000038675 00000 n +0000038951 00000 n +0000039086 00000 n +0000039230 00000 n +0000039367 00000 n +0000039521 00000 n +0000039718 00000 n +0000039915 00000 n +0000040112 00000 n +0000040309 00000 n +0000040459 00000 n +0000041206 00000 n +0000041440 00000 n +0000041589 00000 n +0000041766 00000 n +0000041941 00000 n +0000042120 00000 n +0000042241 00000 n +0000042384 00000 n +0000042553 00000 n +0000042712 00000 n +0000042843 00000 n +0000043008 00000 n +0000043169 00000 n +0000043284 00000 n +0000043450 00000 n +0000043565 00000 n +0000043742 00000 n +0000043857 00000 n +0000044029 00000 n +0000044157 00000 n +0000044303 00000 n +0000044435 00000 n +0000044495 00000 n +0000050220 00000 n +0000056633 00000 n +0000058503 00000 n +0000073206 00000 n +0000074516 00000 n +0000074753 00000 n +0000075332 00000 n +0000075487 00000 n +0000077007 00000 n +0000077241 00000 n +0000077880 00000 n +0000078030 00000 n +0000078575 00000 n +0000078816 00000 n +0000079169 00000 n +0000079326 00000 n +0000080558 00000 n +0000080793 00000 n +0000081391 00000 n +0000081542 00000 n +trailer +<< +/Size 128 +/Root 3 0 R +/Info 2 0 R +>> +startxref +81629 +%%EOF diff --git a/docs/draft-sattrak-v1-00.txt b/docs/draft-sattrak-v1-00.txt new file mode 100644 index 0000000..77958c0 --- /dev/null +++ b/docs/draft-sattrak-v1-00.txt @@ -0,0 +1,425 @@ + + + + +Independent V. Grey + VG Interactive + 1 November 2022 + + + SatTrack Controller Input Specification Version 1 + draft-sattrak-v1-00 + +Abstract + + +Table of Contents + + 1. Introduction + 1.1. Overview and Preliminaries + 1.1.1. Notation and Vocabulary + 2. Packets + 2.1. Location Packet + 2.1.1. Example + 2.2. Home/Sun/Moon Packet + 2.2.1. Example + 3. Controller Input + 3.1. Example + 4. References + 4.1. Normative References + Copyright Notice + Author's Address + +1. Introduction + +1.1. Overview and Preliminaries + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and + "OPTIONAL" in this document are to be interpreted as described in BCP + 14 [RFC2119] [RFC8174] when, and only when, they appear in all + capitals, as shown here. + +1.1.1. Notation and Vocabulary + + Data Types: + + (S) = String + (A) = Binary Data + (I) = Signed Integer + (N) = Unsigned Integer + (D) = Binary Coded Decimal Number + (B) = Boolean + + We use the terms *byte* and *octet* interchangeably in this document. + + Unless specified otherwise, all multi-octet binary coded decimal + numbers, unsigned integers, and signed integers are big-endian. + + String values MUST use UTF-8 encoding. If an example of a string + value is given, the example will be between quotation marks. The + quotation marks are not included in the string. Not all content + between quotations will be string value examples, so be aware of that + when continuing through this document. + + Binary coded decimal numbers, Unsigned integers, and signed integers + can have different bit widths. For instance, an 8-bit signed integer + can be used in one part of the specification and a 64-bit signed + integer can be used in another part of the specification. + + Boolean values MUST be a single octet with the value of 0 for *FALSE* + or 1 for *TRUE*. + + [N octets] A sequence of octets with a length of N + + [00 7F FF] A sequence of octets 00, 7F, and FF in that order. + + [80 FF (01 02)] A sequence of octets 80, FF, 01, and 02 in that + order with visual grouping of 01 and 02. The visual grouping are + only used as a visual indicator to imply that the bytes are + related to each other in some way. + + *0-indexed* When counting sequentially, start counting at 0. + + *1-indexed* When counting sequentially, start counting at 1. + + In packet diagrams, a single octet is represented with a box like + this: + + +-----+ + | Var | <-- Vertical bars MAY be missing + +-----+ + + *Var* is a variable name. + + In packet diagrams, an arbitrary number of octets are represented + with a box like this: + + +=====+ + | Var | + +=====+ + + *Var* is a variable name. + + In packet diagrams, boxes can be connected like these examples: + + +-----------+-----------+ + | Var 1 (B) | Var 2 (I) | + +-----------+-----------+ + + In this example, *Var 1* is a 1 octet long boolean value and *Var + 2* is a 1 octet long signed integer. + + +-----+-----+===========+ + | Var 1 (N) | Var 2 (S) | + +-----+-----+===========+ + + In this example, *Var 1* is a 2 octet long unsigned integer and + *Var 2* is an arbitrary octet length string. + + In packet diagrams, boxes MAY have relative offset values above them + like in the following example: + + 0 1 2... + +-----+-----+===========+ + | Var 1 (N) | Var 2 (S) | + +-----+-----+===========+ + + In this example, *Var 1* is a 2 octet long unsigned integer and + *Var 2* is an arbitrary octet length string. + + Packet diagrams MAY be split into multiple lined sections like in the + following example: + + 0 1 2 3 + +-----------+-----------+-----------+-----------+ + | Var 1 (N) | Var 2 (I) | Var 3 (B) | Var 4 (I) | ... + +-----------+-----------+-----------+-----------+ + 4 5 + +-----------+===========+ + | Var 5 (N) | Var 6 (S) | + +-----------+===========+ + + In this example, enough variable boxes are in the packet diagram + to require being split into 2 sections to take up less horizontal + space in this documentation. The packet diagram is seperated by + an ellipsis (...) on the same text row as the variable names to + signify that the packet diagram is continuing. + +2. Packets + +2.1. Location Packet + + Location packets MUST be in the following format: + + 0 1 2 4 5 6 7 8 9 + +----------+---------+-----------+--+--+--+--+-----+-----+ + | Sync (A) | Day (D) | Month (N) | Year (D) | Hour (D) | ... + +----------+---------+-----------+--+--+--+--+-----+-----+ + 10 11 12 13 14 15 16 17 18 19 20 21 + +------+------+------+------+--+--+--+--+--+--+--+--+ + | Minute (D) | Second (D) | Altitude (D) | ... + +------+------+------+------+--+--+--+--+--+--+--+--+ + 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 + +--+--+--+--+--+--+--+--+--+---+---+---+---+--+--+--+--+--+ + | NORAD ID (D) | Sat Lat (D) | Sat Lon (D) | ... + +--+--+--+--+--+--+--+--+--+---+---+---+---+--+--+--+--+--+ + 40 + +----------------+ + | Sat Status (A) | + +----------------+ + + Sync (Binary Data) [1 Octet] + Day (Binary-Coded Decimal Number) [2 Octets] + Month (Unsigned Integer) [1 Octet] + Year (Binary-Coded Decimal Number) [2 Octets] + Hour (Binary-Coded Decimal Number) [2 Octets] + Minute (Binary-Coded Decimal Number) [2 Octets] + Second (Binary-Coded Decimal Number) [2 Octets] + Altitude (Binary Coded Decimal) [8 octets] + NORAD ID (Binary Coded Decimal) [9 octets] + Sat Lat (Binary-Coded Decimal Number) [4 Octets] + Sat Lon (Binary-Coded Decimal Number) [5 Octets] + Sat Status (Binary Data) [1 Octet] + 7654 3210 + ---- ---- + || ||| + || ||+- Time Is UTC (1 if true otherwise 0) + || |+-- Time is 12 Hour (1 if true otherwise 0) + || +--- Time is PM (1 if true otherwise 0) + |+-------- Sat Longitude East (1 if lon East otherwise 0) + +--------- Sat Latitude North (1 if lat North otherwise 0) + + *Sync* + Binary data that MUST be the value of [F0]. + + *UTC* + Boolean value that is *TRUE* if the time is in UTC (timezone + offset of 0000) otherwise is *FALSE*. + + *Day* + Day value for the satellite location data. *Day* MUST be a + binary-coded decimal value at or between [00 01] (1) and [03 01] + (31). + + *Month* + Month number for the satellite location data. *Month* MUST be at + or between the values of 1 and 12. + + *Year* + Year value for the satellite location data. *Year* MUST be a + binary-coded decimal value at or between [00 00 00 01] (1) and [09 + 09 09 09] (9999). + + *Hour* + 24-hour system hour number for the satellite location data. + *Hour* MUST be a binary-coded decimal value at or between [00 00] + (0) and [02 03] (23). + + *Minute* + Minute number for the satellite location data. *Minute* MUST be a + binary-coded decimal value at or between [00 00] (0) and [05 09] + (59). + + *Second* + Second number for the satellite location data. *Second* MUST be a + binary-coded decimal value at or between [00 00] (0) and [05 09] + (59). + + *South* + Boolean value that is *TRUE* if the latitude for the satellite + location data is negative (latitude is in the Western Hemisphere) + otherwise is *FALSE*. + + *Latitude* + Absolute value of the latitude for the satellite location data + multiplied by 100. No decimal values are provided after the + multiplication by 100. *Latitude* MUST be a binary-coded decimal + value at or between [00 00 00 00] (0) and [09 00 00 00] (9000). + + *West* + Boolean value that is *TRUE* if the longitude for the satellite + location data is negative (longitude is in the Southern + Hemisphere) otherwise is *FALSE*. + + *Longitude* + Absolute value of the longitude for the satellite location data + multiplied by 100. No decimal values are provided after the + multiplication by 100. *Longitude* MUST be a binary-coded decimal + value at or between [00 00 00 00 00] (0) and [01 08 00 00 00] + (18000). + +2.1.1. Example + + The information of *February 27, 2022 02:46:18 UTC, Latitude: 21.56 + N, Longitude: 18.70 S* will result in a Location packet of [(F0) (01) + (02 07) (02) (02 00 02 02) (00 02) (04 06) (01 08) (00) (02 01 05 06) + (01) (00 01 08 07 00)]. + +2.2. Home/Sun/Moon Packet + + Home/Sun/Moon packets MUST be in the following format: + + 0 1 2 3 + +----------+--------------+--------------+-------------+ + | Sync (A) | Home Lat (N) | Home Lon (N) | Sun Lat (N) | ... + +----------+--------------+--------------+-------------+ + 4 5 6 + +-------------+--------------+--------------+ + | Sun Lon (N) | Moon Lat (N) | Moon Lon (I) | ... + +-------------+--------------+--------------+ + 7 8 + +----------------+--------------------------+ + | Moon Phase (U) | Sun/Moon Lon Pos/Neg (A) | + +----------------+--------------------------+ + + Sync (Binary Data) [1 octet] + Home Lat (Unsigned Integer) [1 octet] + Home Lon (Signed Integer) [1 octet] + Sun Lat (Unsigned Integer) [1 octet] + Sun Lon (Signed Integer) [1 octet] + Moon Lat (Unsigned Integer) [1 octet] + Moon Lon (Signed Integer) [1 octet] + Moon Phase (Unsigned Integer) [1 octet] + Home/Sun/Moon Status [1 octet] + 7654 3210 + ---- ---- + |||| | || + |||| | |+- Enable Home (1 if true otherwise 0) + |||| | |+- Enable Sun (1 if true otherwise 0) + |||| | +-- Enable Moon (1 if true otherwise 0) + |||| +---- Home Longitude East (1 if lon East otherwise 0) + |||+------ Sun Longitude East (1 if lon East otherwise 0) + ||+------- Moon Longitude East (1 if lon East otherwise 0) + |+-------- Sat In Daylight (1 if true otherwise 0) + +--------- Sat In View (1 if true otherwise 0) + + + + *Sync* + Binary data that MUST be the value of [F5]. + + *Sun Lat* + Latitude of the Sun + 90. + + *Sun Lon Diff* + Longitude of the Sun minus the Longitude of the Satellite. If + difference is below -100 or above 100, the value for Sun Lat MUST + be between or at [64] (100) and [9C] (156, which is -100). + + *Moon Lat* + Latitude of the Moon + 90. + + *Moon Lon Diff* + Longitude of the Moon minus the Longitude of the Satellite. If + difference is below -100 or above 100, the value for Sun Lat MUST + be between or at [64] (100) and [9C] (156, which is -100). + + *Moon Phase* + Value between and including 0 and 15 for current moon phase. 0 is + a New Moon, 1 is Waxing Crescent with 12.5% illumination, 2 is + Waxing Crescent with 25% illumination, 3 is Waxing Crescent with + 37.5% illumination, 4 is a First Quarter Moon, 5 is Waxing Gibbous + with 62.5% illumination, 6 is Waxing Gibbous with 75% + illumination, 7 is Waxing Gibbous with 87.5% illumination, 8 is a + Full Moon, 9 is Waning Gibbous with 87.5% illumination, 10 is + Waning Gibbous with 75% illumination, 11 is Waning Gibbous with + 62.5% illumination, 12 is a Third Quarter Moon, 13 is Waning + Crescent with 37.5% illumination, 14 is Waning Crescent with 25% + illumination, and 15 is Waning Crescent with 12.5% illumination. + +2.2.1. Example + + The Sun and Moon information of *Sun Latitude: -7, Sun Longitude: + Earth Station - 16, Moon Latitude: 12, Moon Longitude: Earth Station + - 22, Moon Phase: New Moon* will result in a Sun/Moon packet of [(FC) + (44) (F0) (66) (EA) (00) (44) (F0). + + The Sun and Moon information of *Moon Latitude: 0, Moon Longitude: + Earth Station - 140, Moon Phase: First Quarter Moon, Sun Latitude: + -4, Sun Longitude: Earth Station - 64, Moon Latitude: 0, Moon + Longitude: Earth Station - 140, and Moon Phase: First Quarter Moon* + will result in a Sun/Moon packet of [(FC) (56) (C0) (5A) (80) (04). + +3. Controller Input + + 7654 3210 + ---- ---- + |||| |||| + |||| |||+- Right on D-Pad (1 if Pressed otherwise 0) + |||| ||+-- Left on D-Pad (1 if Pressed otherwise 0) + |||| |+--- Down on D-Pad (1 if Pressed otherwise 0) + |||| +---- Up on D-Pad (1 if Pressed otherwise 0) + |||+------ Start Button (1 if Pressed otherwise 0) + ||+------- Select Button (1 if Pressed otherwise 0) + |+-------- B Button (1 if Pressed otherwise 0) + +--------- A Button (1 if Pressed otherwise 0) + +3.1. Example + + To send the byte value [F5], the following buttons MUST to be + pressed: + + *A* + *B* + *Start* + *Select* + *Down* + *Right* + + and the following buttons MUST NOT to be pressed: + + *Up* + *Left* + +4. References + +4.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, + DOI 10.17487/RFC2119, March 1997, + . + + [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC + 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, + May 2017, . + +Copyright Notice + + Copyright (c) 2022, Vi Grey + + All rights reserved. + + Redistribution and use of this documentation in source (XML format) + and/or "compiled" forms (TXT, PDF, HTML, etc), with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code (XML format) of this documentation + must retain the above copyright notice, this list of conditions, + and the following disclaimer in the documentation. + + 2. Redistributions in compiled form (Converted to TXT, PDF, HTML, + and other formats) of this documentation must reproduce the above + copyright notice, this list of conditions, and the following + disclaimer in the documentation. + + THIS DOCUMENTATION IS PROVIDED BY THE AUTHOR(S) "AS IS" AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Author's Address + + Vi Grey + VG Interactive + Email: vi@vigrey.com + URI: https://vigrey.com diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..01edeb2 --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module sattrak + +go 1.19 + +require ( + github.com/go-yaml/yaml v2.1.0+incompatible + github.com/gorilla/websocket v1.5.0 + github.com/joshuaferrara/go-satellite v0.0.0-20220611180459-512638c64e5b + github.com/mikepb/go-serial v0.0.0-20201030162908-19fa9bf168fc +) + +require ( + github.com/pkg/errors v0.9.1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..cae1eb9 --- /dev/null +++ b/go.sum @@ -0,0 +1,23 @@ +github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= +github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/joshuaferrara/go-satellite v0.0.0-20220611180459-512638c64e5b h1:JlltDRgni6FuoFwluvoZCrE6cmpojccO4WsqeYlFJLE= +github.com/joshuaferrara/go-satellite v0.0.0-20220611180459-512638c64e5b/go.mod h1:msW2QeN9IsnRyvuK8OBAzBwn6DHwXpiAiqBk8dbLfrU= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mikepb/go-serial v0.0.0-20201030162908-19fa9bf168fc h1:1lTV3ULswqs4Id7ufAHgKRqm+khmExlw9ivNNXUFxHw= +github.com/mikepb/go-serial v0.0.0-20201030162908-19fa9bf168fc/go.mod h1:/GJho3VaF1qyrexGicbl96+cYZXep0Xr2hxYYwY/cpE= +github.com/onsi/ginkgo v1.2.1-0.20160509182050-5437a97bf824 h1:MbMqwlWoESqhGm4Sslfdyeq7Ww8R9ppeKS5DcO3xDI0= +github.com/onsi/ginkgo v1.2.1-0.20160509182050-5437a97bf824/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20160516222431-c73e51675ad2 h1:38zSYUaJJkzreBjLz7tx4AUTVjnFI7EQBnlRoWt4QFA= +github.com/onsi/gomega v0.0.0-20160516222431-c73e51675ad2/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.0.0-20160301204022-a83829b6f129 h1:RBgb9aPUbZ9nu66ecQNIBNsA7j3mB5h8PNDIfhPjaJg= +gopkg.in/yaml.v2 v2.0.0-20160301204022-a83829b6f129/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= diff --git a/lua/sattrak.lua b/lua/sattrak.lua new file mode 100644 index 0000000..9eebaf6 --- /dev/null +++ b/lua/sattrak.lua @@ -0,0 +1,71 @@ +local socket = require("socket.core") + +local injectionContent = "" + +local i = 0; +local controller1 = 0x501; +local next = next; + +local HOST, PORT = "localhost", 25544 + +local sock = nil + +function handleConnRead() + if sock ~= nil then + local _, err, part = sock:receive("*all") + if err == "closed" then + createSock() + elseif part and #part > 0 then + injectionContent = injectionContent .. part + end + else + createSock() + end +end + +function connect(address, port, laddress, lport) + local sock, err = socket.tcp() + if not sock then + return nil, err + end + if laddress then + local res, err = sock:bind(laddress, lport, -1) + if not res then + return nil, err + end + end + local res, err = sock:connect(address, port) + if not res then + return nil, err + end + return sock, nil +end + +function createSock() + sock, err = connect(HOST, PORT) + if sock ~= nil then + sock:settimeout(0) + end +end + +function handleControllerRead() + if injectionContent == "" then + memory.writebyte(controller1, 0); + else + local injectionByte = string.byte(string.sub(injectionContent, 1, 1)) + memory.writebyte(controller1, injectionByte) + injectionContent = string.sub(injectionContent, 2); + end +end + +function main() + while true do + handleConnRead() + emu.frameadvance() + end +end + +memory.registerexec(0x801B, handleControllerRead); +createSock() + +main(); diff --git a/nes/sattrak.nes b/nes/sattrak.nes new file mode 100644 index 0000000..e1ba957 Binary files /dev/null and b/nes/sattrak.nes differ diff --git a/src/docs/draft-sattrak-v1-00.xml b/src/docs/draft-sattrak-v1-00.xml new file mode 100644 index 0000000..1c5c260 --- /dev/null +++ b/src/docs/draft-sattrak-v1-00.xml @@ -0,0 +1,327 @@ + + + + + + SatTrack Controller Input Specification Version 1 + + + VG Interactive +
+ vi@vigrey.com + https://vigrey.com +
+
+ + General + Independent + + + +
+ +
+ Introduction +
+ Overview and Preliminaries + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 when, and only when, they appear in all capitals, as shown here. + +
+ Notation and Vocabulary + Data Types: +
    +
  • (S) = String
  • +
  • (A) = Binary Data
  • +
  • (I) = Signed Integer
  • +
  • (N) = Unsigned Integer
  • +
  • (D) = Binary Coded Decimal Number
  • +
  • (B) = Boolean
  • +
+ We use the terms byte and octet interchangeably in this document. + Unless specified otherwise, all multi-octet binary coded decimal numbers, unsigned integers, and signed integers are big-endian. + String values MUST use UTF-8 encoding. If an example of a string value is given, the example will be between quotation marks. The quotation marks are not included in the string. Not all content between quotations will be string value examples, so be aware of that when continuing through this document. + Binary coded decimal numbers, Unsigned integers, and signed integers can have different bit widths. For instance, an 8-bit signed integer can be used in one part of the specification and a 64-bit signed integer can be used in another part of the specification. + Boolean values MUST be a single octet with the value of 0 for FALSE or 1 for TRUE. +
+
[N octets]
A sequence of octets with a length of N
+
[00 7F FF]
A sequence of octets 00, 7F, and FF in that order.
+
[80 FF (01 02)]
A sequence of octets 80, FF, 01, and 02 in that order with visual grouping of 01 and 02. The visual grouping are only used as a visual indicator to imply that the bytes are related to each other in some way.
+
0-indexed
When counting sequentially, start counting at 0.
+
1-indexed
When counting sequentially, start counting at 1.
+
+ In packet diagrams, a single octet is represented with a box like this: + + +
    +
  • Var is a variable name.
  • +
+ In packet diagrams, an arbitrary number of octets are represented with a box like this: + + +
    +
  • Var is a variable name.
  • +
+ In packet diagrams, boxes can be connected like these examples: + + +
    +
  • In this example, Var 1 is a 1 octet long boolean value and Var 2 is a 1 octet long signed integer.
  • +
+ + +
    +
  • In this example, Var 1 is a 2 octet long unsigned integer and Var 2 is an arbitrary octet length string.
  • +
+ In packet diagrams, boxes MAY have relative offset values above them like in the following example: + + +
    +
  • In this example, Var 1 is a 2 octet long unsigned integer and Var 2 is an arbitrary octet length string.
  • +
+ Packet diagrams MAY be split into multiple lined sections like in the following example: + + +
    +
  • In this example, enough variable boxes are in the packet diagram to require being split into 2 sections to take up less horizontal space in this documentation. The packet diagram is seperated by an ellipsis (...) on the same text row as the variable names to signify that the packet diagram is continuing.
  • +
+
+
+ +
+
+ Packets +
+ Location Packet + Location packets MUST be in the following format: + + +
+
Sync
+
Binary data that MUST be the value of [F0].
+
UTC
+
Boolean value that is TRUE if the time is in UTC (timezone offset of 0000) otherwise is FALSE.
+
Day
+
Day value for the satellite location data. Day MUST be a binary-coded decimal value at or between [00 01] (1) and [03 01] (31).
+
Month
+
Month number for the satellite location data. Month MUST be at or between the values of 1 and 12.
+
Year
+
Year value for the satellite location data. Year MUST be a binary-coded decimal value at or between [00 00 00 01] (1) and [09 09 09 09] (9999).
+
Hour
+
24-hour system hour number for the satellite location data. Hour MUST be a binary-coded decimal value at or between [00 00] (0) and [02 03] (23).
+
Minute
+
Minute number for the satellite location data. Minute MUST be a binary-coded decimal value at or between [00 00] (0) and [05 09] (59).
+
Second
+
Second number for the satellite location data. Second MUST be a binary-coded decimal value at or between [00 00] (0) and [05 09] (59).
+
South
+
Boolean value that is TRUE if the latitude for the satellite location data is negative (latitude is in the Western Hemisphere) otherwise is FALSE.
+
Latitude
+
Absolute value of the latitude for the satellite location data multiplied by 100. No decimal values are provided after the multiplication by 100. Latitude MUST be a binary-coded decimal value at or between [00 00 00 00] (0) and [09 00 00 00] (9000).
+
West
+
Boolean value that is TRUE if the longitude for the satellite location data is negative (longitude is in the Southern Hemisphere) otherwise is FALSE.
+
Longitude
+
Absolute value of the longitude for the satellite location data multiplied by 100. No decimal values are provided after the multiplication by 100. Longitude MUST be a binary-coded decimal value at or between [00 00 00 00 00] (0) and [01 08 00 00 00] (18000).
+
+
+ Example + The information of February 27, 2022 02:46:18 UTC, Latitude: 21.56 N, Longitude: 18.70 S will result in a Location packet of [(F0) (01) (02 07) (02) (02 00 02 02) (00 02) (04 06) (01 08) (00) (02 01 05 06) (01) (00 01 08 07 00)]. +
+
+
+ Home/Sun/Moon Packet + Home/Sun/Moon packets MUST be in the following format: + + + + +
+
Sync
+
Binary data that MUST be the value of [F5].
+
Sun Lat
+
Latitude of the Sun + 90.
+
Sun Lon Diff
+
Longitude of the Sun minus the Longitude of the Satellite. If difference is below -100 or above 100, the value for Sun Lat MUST be between or at [64] (100) and [9C] (156, which is -100).
+
Moon Lat
+
Latitude of the Moon + 90.
+
Moon Lon Diff
+
Longitude of the Moon minus the Longitude of the Satellite. If difference is below -100 or above 100, the value for Sun Lat MUST be between or at [64] (100) and [9C] (156, which is -100).
+
Moon Phase
+
Value between and including 0 and 15 for current moon phase. 0 is a New Moon, 1 is Waxing Crescent with 12.5% illumination, 2 is Waxing Crescent with 25% illumination, 3 is Waxing Crescent with 37.5% illumination, 4 is a First Quarter Moon, 5 is Waxing Gibbous with 62.5% illumination, 6 is Waxing Gibbous with 75% illumination, 7 is Waxing Gibbous with 87.5% illumination, 8 is a Full Moon, 9 is Waning Gibbous with 87.5% illumination, 10 is Waning Gibbous with 75% illumination, 11 is Waning Gibbous with 62.5% illumination, 12 is a Third Quarter Moon, 13 is Waning Crescent with 37.5% illumination, 14 is Waning Crescent with 25% illumination, and 15 is Waning Crescent with 12.5% illumination.
+
+
+ Example + The Sun and Moon information of Sun Latitude: -7, Sun Longitude: Earth Station - 16, Moon Latitude: 12, Moon Longitude: Earth Station - 22, Moon Phase: New Moon will result in a Sun/Moon packet of [(FC) (44) (F0) (66) (EA) (00) (44) (F0). + The Sun and Moon information of Moon Latitude: 0, Moon Longitude: Earth Station - 140, Moon Phase: First Quarter Moon, Sun Latitude: -4, Sun Longitude: Earth Station - 64, Moon Latitude: 0, Moon Longitude: Earth Station - 140, and Moon Phase: First Quarter Moon will result in a Sun/Moon packet of [(FC) (56) (C0) (5A) (80) (04). +
+
+
+
+ Controller Input + + +
+ Example + To send the byte value [F5], the following buttons MUST to be pressed: +
    +
  • A
  • +
  • B
  • +
  • Start
  • +
  • Select
  • +
  • Down
  • +
  • Right
  • +
+ and the following buttons MUST NOT to be pressed: +
    +
  • Up
  • +
  • Left
  • +
+
+
+
+ + + + References + + Normative References + + + + +
+ Copyright Notice + Copyright (c) 2022, Vi Grey + All rights reserved. + Redistribution and use of this documentation in source (XML format) and/or "compiled" forms (TXT, PDF, HTML, etc), with or without modification, are permitted provided that the following conditions are met: +
    +
  1. Redistributions of source code (XML format) of this documentation must retain the above copyright notice, this list of conditions, and the following disclaimer in the documentation.
  2. +
  3. Redistributions in compiled form (Converted to TXT, PDF, HTML, and other formats) of this documentation must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation.
  4. +
+ THIS DOCUMENTATION IS PROVIDED BY THE AUTHOR(S) "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +
+ +
+
+ + diff --git a/src/nes/controller.asm b/src/nes/controller.asm new file mode 100644 index 0000000..1e36e23 --- /dev/null +++ b/src/nes/controller.asm @@ -0,0 +1,508 @@ +PollController: + lda allowPolling + beq PollControllerDone + lda #$01 + sta CONTROLLER1 + lda #$00 + sta CONTROLLER1 + ldx #$08 +PollControllerLoop: + lda CONTROLLER1 + lsr + rol controller1 + dex + bne PollControllerLoop + jsr HandleController1 +PollControllerDone: + jmp PollController + +HandleController1: + lda readingData + beq HandleControllerCheckSyncByte + jmp HandleController1IOByte +HandleControllerCheckSyncByte: + lda controller1 + cmp #(SYNC_SATELLITE) + beq HandleController1SetSyncByte + cmp #(SYNC_HOME_SUN_MOON) + beq HandleController1SetSyncByte + rts +HandleController1SetSyncByte: + sta dataType + lda #$01 + sta readingData + lda #$00 + sta readingDataOffset + rts +HandleController1IOByte: + ldx readingDataOffset + lda dataType + cmp #(SYNC_SATELLITE) + beq HandleController1IOSatellite + jmp HandleController1IONotSatellite +HandleController1IOSatellite: + lda SatelliteIO, X + tay + lda controller1 + sta ControllerRAMData, y + inx + stx readingDataOffset + cpx #40 ; SatelliteIO length + bcc HandleController1IOSatelliteDone + lda #$00 + sta readingData + jsr DrawTopBarData +HandleController1IOSatelliteDone: + rts +HandleController1IONotSatellite: + cmp #(SYNC_HOME_SUN_MOON) + beq HandleController1IOHomeSunMoon + jmp HandleController1IONotHomeSunMoon +HandleController1IOHomeSunMoon: + lda HomeSunMoonIO, X + tay + lda controller1 + sta ControllerRAMData, y + inx + stx readingDataOffset + cpx #8 ; HomeSunMoonIO length + bcc HandleController1IOHomeSunMoonDone + lda #$00 + sta readingData +HandleController1IOHomeSunMoonDone: + rts +HandleController1IONotHomeSunMoon: + rts + +DrawTopBarData: + lda #$01 + sta disableDraw + jsr WaitFrame + + ; TODO + ldy #$00 + sty drawBufferOffset + ; TODO + + jsr DrawDate + jsr DrawTime + jsr DrawLatitude + jsr DrawLongitude + ldy drawBufferOffset + lda #$8F + sta (drawBuffer), Y + iny + sty drawBufferOffset + + jsr LatitudeToTmp + jsr LatitudeToYPosition + jsr LongitudeToTmp + jsr LongitudeToXScroll + + ; Because sun and moon are relational to the satellite, + ; we execute this immediately after dealing with satellite data + jsr SetHome + jsr SetSunMoon + jsr DrawInViewSprite + + lda #$01 + sta forceDraw + jsr WaitFrame + + ldy #$00 + sty drawBufferOffset + + jsr DrawShading + jsr DrawAltitude + jsr DrawNoradID + + ldy drawBufferOffset + lda #$8F + sta (drawBuffer), Y + iny + sty drawBufferOffset + + lda #$01 + sta forceDraw + jsr WaitFrame + + lda #$00 + sta disableDraw +DrawTopBarDataDone: + rts + + +DrawShading: + lda homeSunMoonStatus + and #%00000010 + bne DrawShadingSunEnabled + ldx #$00 + jmp DrawShadingContinue +DrawShadingSunEnabled: + lda homeSunMoonStatus + and #%01000000 + eor #%01000000 + lsr + ror + ror + ror + tax +DrawShadingContinue: + ldy drawBufferOffset + lda #$3F + sta (drawBuffer), Y + iny + lda #$10 + sta (drawBuffer), Y + iny +DrawShadingLoop: + lda ShadowPalette, X + sta (drawBuffer), Y + iny + inx + txa + and #%00000011 + bne DrawShadingLoop + lda #$8E + sta (drawBuffer), Y + iny + sty drawBufferOffset + rts + +DrawAltitude: + lda #$00 + sta altitudeBlank + ldy drawBufferOffset + lda #$20 + sta (drawBuffer), Y + iny + lda #$76 + sta (drawBuffer), Y + iny + ldx #$00 +DrawAltitudeLoop: + lda altitude, X + bne DrawAltitudeLoopNotBlank + dec altitudeBlank + inc altitudeBlank + bne DrawAltitudeLoopNotBlank + dec altitudeBlank + lda #$23 +DrawAltitudeLoopNotBlank: + inc altitudeBlank + clc + adc #$E1 + sta (drawBuffer), Y + iny + inx + cpx #$08 + bne DrawAltitudeLoop + lda #$8E + sta (drawBuffer), Y + iny + sty drawBufferOffset + rts + +DrawNoradID: + lda #$00 + sta noradIDBlank + ldy drawBufferOffset + lda #$20 + sta (drawBuffer), Y + iny + lda #$B5 + sta (drawBuffer), Y + iny + ldx #$00 +DrawNoradIDLoop: + lda noradID, X + bne DrawNoradIDLoopNotBlank + dec noradIDBlank + inc noradIDBlank + bne DrawNoradIDLoopNotBlank + dec noradIDBlank + lda #$23 +DrawNoradIDLoopNotBlank: + inc noradIDBlank + clc + adc #$E1 + sta (drawBuffer), Y + iny + inx + cpx #$09 + bne DrawNoradIDLoop + lda #$8E + sta (drawBuffer), Y + iny + sty drawBufferOffset + rts + +DrawDate: + ldy drawBufferOffset + lda #$20 + sta (drawBuffer), Y + iny + lda #$49 + sta (drawBuffer), Y + iny + ldx month +DrawDateCheckMonth: + beq DrawDateInvalidMonth + cpx #13 + bcs DrawDateInvalidMonth + dex + stx tmp + txa + asl + clc + adc tmp + tax + jmp DrawDateContinue +DrawDateInvalidMonth: + ldx #$00 +DrawDateContinue: + lda Months, X + sta (drawBuffer), Y + iny + inx + lda Months, X + sta (drawBuffer), Y + iny + inx + lda Months, X + sta (drawBuffer), Y + iny + lda #$04 + sta (drawBuffer), Y + iny + lda day + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda (day + 1) + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda #$04 + sta (drawBuffer), Y + iny + lda year + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda (year + 1) + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda (year + 2) + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda (year + 3) + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda #$8E + sta (drawBuffer), Y + iny + sty drawBufferOffset + rts + +DrawTime: + ldy drawBufferOffset + lda #$20 + sta (drawBuffer), Y + iny + lda #$69 + sta (drawBuffer), Y + iny + lda hour + adc #$E1 + sta (drawBuffer), Y + iny + lda (hour + 1) + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda #$E0 + sta (drawBuffer), Y + iny + lda minute + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda (minute + 1) + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda #$E0 + sta (drawBuffer), Y + iny + lda second + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda (second + 1) + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda satStatus + and #%00000111 + asl + asl + tax +DrawUTCWordLoop: + lda TimeLabel, X + sta (drawBuffer), Y + iny + inx + txa + and #$03 + cmp #$00 + bne DrawUTCWordLoop + lda #$8E + sta (drawBuffer), Y + iny + sty drawBufferOffset + rts + +DrawLatitude: + ldy drawBufferOffset + lda #$20 + sta (drawBuffer), Y + iny + lda #$89 + sta (drawBuffer), Y + iny + lda satLat + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda (satLat + 1) + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda #$DF + sta (drawBuffer), Y + iny + lda (satLat + 2) + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda (satLat + 3) + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda #$04 + sta (drawBuffer), Y + iny + lda satStatus + and #%10000000 + asl + rol + tax + lda LatitudeLetter, X + sta (drawBuffer), Y + iny + lda #$8E + sta (drawBuffer), Y + iny + sty drawBufferOffset + rts + +DrawLongitude: + ldy drawBufferOffset + lda #$20 + sta (drawBuffer), Y + iny + lda #$A8 + sta (drawBuffer), Y + iny + lda satLon + beq DrawLongitudeNotHundred + clc + adc #$E1 + jmp DrawLongitudeContinue +DrawLongitudeNotHundred: + lda #$04 +DrawLongitudeContinue: + sta (drawBuffer), Y + iny + lda (satLon + 1) + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda (satLon + 2) + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda #$DF + sta (drawBuffer), Y + iny + lda (satLon + 3) + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda (satLon + 4) + clc + adc #$E1 + sta (drawBuffer), Y + iny + lda #$04 + sta (drawBuffer), Y + iny + lda satStatus + and #%01000000 + asl + rol + rol + tax + lda LongitudeLetter, X + sta (drawBuffer), Y + iny + lda #$8E + sta (drawBuffer), Y + iny + sty drawBufferOffset + rts + +SatelliteIO: + .byte #<(day), #<(day + 1) + .byte #<(month) + .byte #<(year), #<(year + 1), #<(year + 2), #<(year + 3) + .byte #<(hour), #<(hour + 1) + .byte #<(minute), #<(minute + 1) + .byte #<(second), #<(second + 1) + .byte #<(altitude), #<(altitude + 1), #<(altitude + 2), #<(altitude + 3), #<(altitude + 4), #<(altitude + 5), #<(altitude + 6), #<(altitude + 7) + .byte #<(noradID), #<(noradID + 1), #<(noradID + 2), #<(noradID + 3), #<(noradID + 4), #<(noradID + 5), #<(noradID + 6), #<(noradID + 7), #<(noradID + 8) + .byte #<(satLat), #<(satLat + 1), #<(satLat + 2), #<(satLat + 3) + .byte #<(satLon), #<(satLon + 1), #<(satLon + 2), #<(satLon + 3), #<(satLon + 4) + .byte #<(satStatus) +SatelliteIODone: + + +HomeSunMoonIO: + .byte #<(homeLat), #<(homeLon) + .byte #<(sunLat), #<(sunLon) + .byte #<(moonLat), #<(moonLon) + .byte #<(moonPhase) + .byte #<(homeSunMoonStatus) +HomeSunMoonIODone: diff --git a/src/nes/decompress.asm b/src/nes/decompress.asm new file mode 100644 index 0000000..667e9a6 --- /dev/null +++ b/src/nes/decompress.asm @@ -0,0 +1,95 @@ +; Copyright (C) 2020, Vi Grey +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; +; 1. Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; 2. Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +; SUCH DAMAGE. + +DecompressAddr: + lda PPU_STATUS + ldy #$00 + lda (addr), Y + sta PPU_ADDR + sta ppuAddr + jsr IncAddr + lda (addr), Y + sta PPU_ADDR + sta (ppuAddr + 1) + jsr IncAddr +DecompressAddrGetRowByteLoop: + jsr CmpAddrAddrEnd + bne DecompressAddrDone + ; addr not addrEnd + lda (addr), Y + sta tmp + jsr IncAddr + lda tmp + cmp #$01 + beq DecompressAddrGetRowByteLoopIs01 + cmp #$02 + beq DecompressAddr + sta PPU_DATA + jmp DecompressAddrGetRowByteLoop +DecompressAddrGetRowByteLoopIs01: + lda PPU_STATUS + jsr IncPPUAddrOneLineDecompress + lda ppuAddr + sta PPU_ADDR + lda (ppuAddr + 1) + sta PPU_ADDR + jmp DecompressAddrGetRowByteLoop +DecompressAddrDone: + rts + +IncPPUAddrOneLineDecompress: + lda (ppuAddr + 1) + clc + adc #$20 + sta (ppuAddr + 1) + lda ppuAddr + adc #$00 + sta ppuAddr + rts + +CmpAddrAddrEnd: + lda (addr + 1) + cmp (addrEnd + 1) + bcc CmpAddrAddrEndLTUpper + bne CmpAddrAddrEndPositive: + lda addr + cmp addrEnd + bcs CmpAddrAddrEndPositive +CmpAddrAddrEndLTUpper: + lda #$00 + rts +CmpAddrAddrEndPositive: + lda #$01 + rts + +IncAddr: + lda addr + clc + adc #$01 + sta addr + lda (addr + 1) + adc #$00 + sta (addr + 1) + rts diff --git a/src/nes/defs.asm b/src/nes/defs.asm new file mode 100644 index 0000000..ea57b1f --- /dev/null +++ b/src/nes/defs.asm @@ -0,0 +1,53 @@ +; Copyright (C) 2020, Vi Grey +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; +; 1. Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; 2. Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +; SUCH DAMAGE. + +CONTROLLER1 = $4016 +CONTROLLER2 = $4017 + +BUTTON_A = 1 << 7 +BUTTON_B = 1 << 6 +BUTTON_SELECT = 1 << 5 +BUTTON_START = 1 << 4 +BUTTON_UP = 1 << 3 +BUTTON_DOWN = 1 << 2 +BUTTON_LEFT = 1 << 1 +BUTTON_RIGHT = 1 << 0 + +PPU_CTRL = $2000 +PPU_MASK = $2001 +PPU_STATUS = $2002 +PPU_OAM_ADDR = $2003 +PPU_OAM_DATA = $2004 +PPU_SCROLL = $2005 +PPU_ADDR = $2006 +PPU_DATA = $2007 + +OAM_DMA = $4014 +APU_FRAME_COUNTER = $4017 + +CALLBACK = $FFFA + +SYNC_SATELLITE = $F0 +SYNC_HOME_SUN_MOON = $F5 diff --git a/src/nes/draw.asm b/src/nes/draw.asm new file mode 100644 index 0000000..ca560dc --- /dev/null +++ b/src/nes/draw.asm @@ -0,0 +1,385 @@ +; Copyright (C) 2020, Vi Grey +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; +; 1. Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; 2. Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +; SUCH DAMAGE. + +ResetScroll: + lda #$00 + sta PPU_SCROLL + sta PPU_SCROLL + jsr EnableNMI + rts + +Draw: + lda #%00011000 + sta PPU_MASK + rts + +DisableNMI: + lda #$00 + sta PPU_CTRL + rts + +EnableNMI: + lda #%10100000 + ora patterns + sta PPU_CTRL + rts + +Blank: + lda #%00000000 + sta PPU_MASK + jsr DisableNMI + rts + +ClearPPURAM: + lda PPU_STATUS + lda #$20 + sta PPU_ADDR + lda #$00 + sta PPU_ADDR + ldy #$10 + ldx #$00 + txa +ClearPPURAMLoop: + sta PPU_DATA + dex + bne ClearPPURAMLoop + ldx #$00 + dey + bne ClearPPURAMLoop + rts + +DrawPreviousFrame: + ldy #$00 +DrawPreviousFrameLoop: + lda PPU_STATUS + lda (drawBuffer), Y + iny + cmp #$8F + beq DrawPreviousFrameDone + cmp #$8E + beq DrawPreviousFrameLoop + sta PPU_ADDR + lda (drawBuffer), Y + iny + sta PPU_ADDR +DrawPreviousFrameLoopContentLoop: + lda (drawBuffer), Y + iny + cmp #$8E + beq DrawPreviousFrameLoop + ; Not #$8E + cmp #$8F + beq DrawPreviousFrameDone + ; Not #$8F + sta PPU_DATA + jmp DrawPreviousFrameLoopContentLoop +DrawPreviousFrameDone: + jsr InitializeDrawBuffer + rts + +InitializeDrawBuffer: + ldy #$00 + sty drawBufferOffset + lda #$8F + sta (drawBuffer), Y + rts + +EndDrawBuffer: + lda disableDraw + bne EndDrawBufferDone + ldy drawBufferOffset + lda #$8F + sta (drawBuffer), Y + iny + sty drawBufferOffset +EndDrawBufferDone: + rts + +ClearSprites: + ldy #$00 + lda #$FE +ClearSpritesLoop: + sta $200, Y + iny + bne ClearSpritesLoop + rts + +DrawSpriteZero: + lda #$27 + sta $200 + lda #$D1 + sta $201 + lda #$01 + sta #$202 + lda #08 + sta $203 + rts + +DrawSatSprites: + lda #$82 + sta $204 + lda #$81 + sta $205 + lda #$7F + sta $208 + lda #$83 + sta $209 + lda #$82 + sta $20C + lda #$85 + sta $20D + lda #$78 + sta $207 + lda #$80 + sta $20B + lda #$88 + sta $20F + lda #$00 + sta $206 + sta $20A + sta $20E + rts + +DrawISSSprites: + lda #$6F + sta $204 + lda #$B0 + sta $205 + lda #$00 + sta $206 + lda #$78 + sta $207 + lda #$6F + sta $208 + lda #$B1 + sta $209 + lda #$00 + sta $20A + lda #$80 + sta $20B + lda #$6F + sta $20C + lda #$B2 + sta $20D + lda #$00 + sta $20E + lda #$88 + sta $20F + lda #$77 + sta $210 + lda #$C0 + sta $211 + lda #$00 + sta $212 + lda #$78 + sta $213 + lda #$77 + sta $214 + lda #$C1 + sta $215 + lda #$00 + sta $216 + lda #$80 + sta $217 + lda #$77 + sta $218 + lda #$C2 + sta $219 + lda #$00 + sta $21A + lda #$88 + sta $21B + rts + +DrawTopBar: + lda #<(TopBarTextDone) + sta addrEnd + lda #>(TopBarTextDone) + sta (addrEnd + 1) + lda #<(TopBarText) + sta addr + lda #>(TopBarText) + sta (addr + 1) + jsr DecompressAddr + rts + +DrawStatusIcons: + jsr DrawTAStm32StatusIcon + jsr DrawInternetStatusIcon + rts + +;UpdateStatusIcons: +; lda screen +; bne UpdateStatusIconsMap +; lda statusTAStm32 +; beq UpdateStatusIconsTAStm32Crewmate +; jsr ResetYTAStm32StatusIcon +; jmp UpdateStatusIconsCrewmateContinue +;UpdateStatusIconsTAStm32Crewmate: +; jsr HandleTAStm32StatusIconCrewmate +;UpdateStatusIconsCrewmateContinue: +; lda statusInternet +; beq UpdateStatusIconsInternetCrewmate +; jsr ResetYInternetStatusIcon +; rts +;UpdateStatusIconsInternetCrewmate: +; jsr HandleInternetStatusIconCrewmate +; rts +;UpdateStatusIconsMap: +; lda statusTAStm32 +; beq UpdateStatusIconsTAStm32Map +; jsr ResetYTAStm32StatusIcon +; jmp UpdateStatusIconsMapContinue +;UpdateStatusIconsTAStm32Map: +; jsr HandleTAStm32StatusIconMap +;UpdateStatusIconsMapContinue: +; lda statusInternet +; beq UpdateStatusIconsInternetMap +; jsr ResetYInternetStatusIcon +; rts +;UpdateStatusIconsInternetMap: +; jsr HandleInternetStatusIconMap +; rts + +DrawTAStm32StatusIcon: + lda #$83 + sta $231 + lda #$84 + sta $235 + lda #$93 + sta $239 + lda #$94 + sta $23D + lda #$02 + sta $236 + sta $232 + sta $23A + sta $23E + rts + +DrawInternetStatusIcon: + lda #$85 + sta $241 + lda #$86 + sta $245 + lda #$95 + sta $249 + lda #$96 + sta $24D + lda #$02 + sta $246 + sta $242 + sta $24A + sta $24E + rts + +HandleTAStm32StatusIconMap: + lda #$0E + sta $230 + sta $234 + lda #$16 + sta $238 + sta $23C + lda #$C8 + sta $233 + sta $23B + lda #$D0 + sta $237 + sta $23F + rts + +HandleInternetStatusIconMap: + lda #$0E + sta $240 + sta $244 + lda #$16 + sta $248 + sta $24C + lda #$E0 + sta $243 + sta $24B + lda #$E8 + sta $247 + sta $24F + rts + +HandleTAStm32StatusIconCrewmate: + lda #$23 + sta $230 + sta $234 + lda #$2B + sta $238 + sta $23C + lda #$1D + sta $233 + sta $23B + lda #$24 + sta $237 + sta $23F + rts + +HandleInternetStatusIconCrewmate: + lda #$23 + sta $240 + sta $244 + lda #$2B + sta $248 + sta $24C + lda #$D4 + sta $243 + sta $24B + lda #$DC + sta $247 + sta $24F + rts + +ResetYTAStm32StatusIcon: + lda #$FE + sta $230 + sta $234 + sta $238 + sta $23C + rts + +ResetYInternetStatusIcon: + lda #$FE + sta $240 + sta $244 + sta $248 + sta $24C + rts + + +BlankScreen: + lda #$01 + sta disableDraw + lda nmi +BlankScreenWaitFrame: + cmp nmi + beq BlankScreenWaitFrame + jsr SetBlankPalette + jsr Blank + rts + diff --git a/src/nes/graphics/crew-name.go b/src/nes/graphics/crew-name.go new file mode 100644 index 0000000..6a10699 --- /dev/null +++ b/src/nes/graphics/crew-name.go @@ -0,0 +1,34 @@ +package main + +import ( + //"io/ioutil" + "fmt" +) + +func main() { + dat := []byte{} + for i := 0; i < 3*32; i++ { + dat = append(dat, 0) + } + dat = append(dat, []byte{0, 0, 0x80}...) + for i := 0; i < 26; i++ { + dat = append(dat, 0x81) + } + dat = append(dat, []byte{0x82, 0, 0}...) + for i := 0; i < 22; i++ { + dat = append(dat, []byte{0, 0, 0x90}...) + for j := 0; j < 26; j++ { + dat = append(dat, 0) + } + dat = append(dat, []byte{0x92, 0, 0}...) + } + dat = append(dat, []byte{0, 0, 0xA0}...) + for i := 0; i < 26; i++ { + dat = append(dat, 0xA1) + } + dat = append(dat, []byte{0xA2, 0, 0}...) + for i := 0; i < 3*32; i++ { + dat = append(dat, 0) + } + fmt.Printf(string(dat)) +} diff --git a/src/nes/graphics/crew.nam b/src/nes/graphics/crew.nam new file mode 100644 index 0000000..a17dc5d Binary files /dev/null and b/src/nes/graphics/crew.nam differ diff --git a/src/nes/graphics/east.atr b/src/nes/graphics/east.atr new file mode 100644 index 0000000..c9006dc Binary files /dev/null and b/src/nes/graphics/east.atr differ diff --git a/src/nes/graphics/east.nam b/src/nes/graphics/east.nam new file mode 100644 index 0000000..8155645 Binary files /dev/null and b/src/nes/graphics/east.nam differ diff --git a/src/nes/graphics/tilemap.chr b/src/nes/graphics/tilemap.chr new file mode 100644 index 0000000..7d2659f Binary files /dev/null and b/src/nes/graphics/tilemap.chr differ diff --git a/src/nes/graphics/west.atr b/src/nes/graphics/west.atr new file mode 100644 index 0000000..fce1ad3 Binary files /dev/null and b/src/nes/graphics/west.atr differ diff --git a/src/nes/graphics/west.nam b/src/nes/graphics/west.nam new file mode 100644 index 0000000..64bb831 Binary files /dev/null and b/src/nes/graphics/west.nam differ diff --git a/src/nes/include.asm b/src/nes/include.asm new file mode 100644 index 0000000..e365828 --- /dev/null +++ b/src/nes/include.asm @@ -0,0 +1,124 @@ +; Copyright (C) 2020, Vi Grey +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; +; 1. Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; 2. Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +; SUCH DAMAGE. + +Palettes: + .byte $0F, $11, $2A, $30 + .byte $0F, $11, $30, $30 + .byte $0F, $0F, $0F, $30 + .byte $0F, $0F, $0F, $30 + .byte $0F, $2D, $2C, $30 + .byte $0F, $0F, $0F, $30 + ;.byte $0F, $2D, $0F, $30 + .byte $0F, $27, $16, $30 + .byte $0F, $0F, $0F, $0F + +ShadowPalette: + .byte $0F, $2D, $2C, $30 + .byte $0F, $0F, $1C, $10 + +CrewPageTiles: + .incbin "graphics/crew.nam" + +West: + .incbin "graphics/west.nam" + .incbin "graphics/west.atr" +East: + .incbin "graphics/east.nam" + .incbin "graphics/east.atr" + +Months: + .byte $F3, $EB, $F6 + .byte $F0, $EF, $EC + .byte $F5, $EB, $F9 + .byte $EB, $F8, $F9 + .byte $F5, $EB, $FF + .byte $F3, $FC, $F6 + .byte $F3, $FC, $F4 + .byte $EB, $FC, $F1 + .byte $FA, $EF, $F8 + .byte $F7, $ED, $FB + .byte $F6, $F7, $FD + .byte $EE, $EF, $ED + +LatitudeLetter: + .byte $FA, $F6 + +LongitudeLetter: + .byte $FE, $EF + +Latitudes: + .byte $28, $29, $2a, $2b, $2c, $2d, $2e, $2f, $30, $32, $33, $34, $35, $36, $37, $38, $39, $3a, $3b, $3c, $3d, $3e, $3f, $40, $41, $43, $44, $45, $46, $47, $48, $49, $4a, $4b, $4c, $4d, $4e, $4f, $50, $51, $52, $53, $55, $56, $57, $58, $59, $5a, $5b, $5c, $5d, $5e, $5f, $60, $61, $62, $63, $64, $66, $67, $68, $69, $6a, $6b, $6c, $6d, $6e, $6f, $70, $71, $72, $73, $74, $75, $76, $78, $79, $7a, $7b, $7c, $7d, $7e, $7f, $80, $81, $82, $83, $84, $85, $86, $87, $89, $8a, $8b, $8c, $8d, $8e, $8f, $90, $91, $92, $93, $94, $95, $96, $97, $98, $9a, $9b, $9c, $9d, $9e, $9f, $a0, $a1, $a2, $a3, $a4, $a5, $a6, $a7, $a8, $a9, $aa, $ac, $ad, $ae, $af, $b0, $b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8, $b9, $ba, $bb, $bd, $be, $bf, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7, $c8, $c9, $ca, $cb, $cc, $cd, $cf, $d0, $d1, $d2, $d3, $d4, $d5, $d6, $d7, $d8, $d9, $da, $db, $dc, $dd, $de, $e0, $e1, $e2, $e3, $e4, $e5, $e6, $e7 +.byte $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe +.byte $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe +.byte $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe +.byte $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe +.byte $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe +.byte $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe, $fe + +Longitudes: + .byte $00, $01, $03, $04, $06, $07, $09, $0a, $0b, $0d, $0e, $10, $11, $12, $14, $15, $17, $18, $1a, $1b, $1c, $1e, $1f, $21, $22, $24, $25, $26, $28, $29, $2b, $2c, $2e, $2f, $30, $32, $33, $35, $36, $37, $39, $3a, $3c, $3d, $3f, $40, $41, $43, $44, $46, $47, $49, $4a, $4b, $4d, $4e, $50, $51, $52, $54, $55, $57, $58, $5a, $5b, $5c, $5e, $5f, $61, $62, $64, $65, $66, $68, $69, $6b, $6c, $6e, $6f, $70, $72, $73, $75, $76, $77, $79, $7a, $7c, $7d, $7f, $80, $81, $83, $84, $86, $87, $89, $8a, $8b, $8d, $8e, $90, $91, $92, $94, $95, $97, $98, $9a, $9b, $9c, $9e, $9f, $a1, $a2, $a4, $a5, $a6, $a8, $a9, $ab, $ac, $ae, $af, $b0, $b2, $b3, $b5, $b6, $b7, $b9, $ba, $bc, $bd, $bf, $c0, $c1, $c3, $c4, $c6, $c7, $c9, $ca, $cb, $cd, $ce, $d0, $d1, $d2, $d4, $d5, $d7, $d8, $da, $db, $dc, $de, $df, $e1, $e2, $e4, $e5, $e6, $e8, $e9, $eb, $ec, $ee, $ef, $f0, $f2, $f3, $f5, $f6, $f7, $f9, $fa, $fc, $fd, $ff, $ff +.byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff +.byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff +.byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff +.byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff +.byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff +.byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff + + +TopBarText: + .byte $20, $43 + .byte $EE, $EB, $FB, $EF, $E0, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $EB, $F4, $FB, $F2, $FB, $FC, $EE, $EF, $01 + .byte $FB, $F2, $F5, $EF, $E0, $01 + .byte $F4, $EB, $FB, $E0, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $F6, $F7, $F9, $EB, $EE, $04, $F2, $EE, $01 + .byte $F4, $F7, $F6, $E0 +TopBarTextDone: + +TimeLabel: + .byte $04, $04, $04, $04 + .byte $04, $FC, $FB, $ED + .byte $04, $EB, $F5, $04 + .byte $04, $EB, $F5, $04 + .byte $04, $04, $04, $04 + .byte $04, $FC, $FB, $ED + .byte $04, $F8, $F5, $04 + .byte $04, $F8, $F5, $04 + +MoonPhases: + .byte $9E, $9F + .byte $9E, $91 + .byte $9E, $93 + .byte $9E, $95 + .byte $9E, $97 + .byte $9C, $97 + .byte $9A, $97 + .byte $98, $97 + .byte $96, $97 + .byte $96, $99 + .byte $96, $9B + .byte $96, $9D + .byte $96, $9F + .byte $94, $9F + .byte $92, $9F + .byte $90, $9F diff --git a/src/nes/map.asm b/src/nes/map.asm new file mode 100644 index 0000000..e1b8bf4 --- /dev/null +++ b/src/nes/map.asm @@ -0,0 +1,518 @@ +LatitudeToTmp: + lda #$00 + ldx satLat +LatitudeToTmpTensLoop: + cpx #$00 + beq LatitudeToTmpTensLoopDone + clc + adc #$0A + dex + jmp LatitudeToTmpTensLoop +LatitudeToTmpTensLoopDone: + clc + adc (satLat + 1) + cmp #91 + bcc LatitudeToTmpDone + lda #$00 +LatitudeToTmpDone: + sta (tmp + 1) + rts + +LatitudeToYPosition: + lda satStatus + and #%10000000 + beq LatitudeToYPositionSouth + lda #90 + sec + sbc (tmp + 1) + jmp LatitudeToYPositionContinue +LatitudeToYPositionSouth: + lda (tmp + 1) + clc + adc #90 +LatitudeToYPositionContinue: + tay + lda (Latitudes), Y + sta $208 + clc + adc #$03 + sta $204 + sta $20C + rts + +LongitudeToTmp: + lda #$00 + ldx satLon +LongitudeToTmpHundredsLoop: + beq LongitudeToTmpHundredsLoopDone + clc + adc #100 +LongitudeToTmpHundredsLoopDone: + ldx (satLon + 1) +LongitudeToTmpTensLoop: + cpx #$00 + beq LongitudeToTmpTensLoopDone + clc + adc #$0A + dex + jmp LongitudeToTmpTensLoop +LongitudeToTmpTensLoopDone: + clc + adc (satLon + 2) + cmp #180 + bcc LongitudeToTmpDone + lda #180 +LongitudeToTmpDone: + sta (tmp + 1) + rts + +LongitudeToXScroll: + lda satStatus + and #%01000000 + bne LongitudeToXScrollEast + lda #$00 + sta nametable + lda #180 + sec + sbc (tmp + 1) + jmp LongitudeToXScrollContinue +LongitudeToXScrollEast: + lda #$01 + sta nametable + lda (tmp + 1) +LongitudeToXScrollContinue: + tay + lda (Longitudes), Y + sec + sbc #$80 + sta xscroll + bcs LongitudeToXScrollDone + lda nametable + eor #%00000001 + sta nametable +LongitudeToXScrollDone: + lda (tmp + 1) + sta satLon + rts + + + + + + + + + + + + +;;;;;;;;;; +;; +;; MAP Screen Draw Code +;; +;;;;;;;;;; + +DrawMap: + lda #$00 + sta allowPolling + jsr BlankScreen + lda #$01 + sta screen + lda #%00001000 + sta patterns + jsr ClearPPURAM + jsr SetMapScreenPalettes + jsr DrawMapScreen + jsr ClearSprites + jsr DrawSpriteZero + jsr DrawSatSprites + jsr DrawSunSprite + jsr DrawMoonSprite + jsr DrawHomeSprite + jsr DrawTopBar + jsr InitializeDrawBuffer + jsr LatitudeToTmp + jsr LatitudeToYPosition + jsr LongitudeToTmp + jsr LongitudeToXScroll + lda #$00 + sta disableDraw + jsr ResetScroll + lda #$01 + sta forceDraw + jsr WaitFrame + jsr DrawTopBarData + rts + +SetMapScreenPalettes: + lda PPU_STATUS + lda #$3F + sta PPU_ADDR + lda #$00 + sta PPU_ADDR + lda #<(Palettes) + sta addr + lda #>(Palettes) + sta (addr + 1) + ldy #$00 +SetMapScreenPalettesLoop: + lda (addr), Y + sta PPU_DATA + iny + cpy #$20 + bne SetMapScreenPalettesLoop + rts + +DrawMapScreen: + lda PPU_STATUS + lda #$20 + sta PPU_ADDR + lda #$00 + sta PPU_ADDR + lda #<(West) + sta addr + lda #>(West) + sta (addr + 1) + ldy #$00 + ldx #$04 +DrawMapScreenWest: + lda (addr), Y + sta PPU_DATA + iny + bne DrawMapScreenWest + inc (addr + 1) + dex + bne DrawMapScreenWest + lda PPU_STATUS + lda #$24 + sta PPU_ADDR + lda #$00 + sta PPU_ADDR + lda #<(East) + sta addr + lda #>(East) + sta (addr + 1) + ldx #$04 +DrawMapScreenEast: + lda (addr), Y + sta PPU_DATA + iny + bne DrawMapScreenEast + inc (addr + 1) + dex + bne DrawMapScreenEast + rts + +DrawSunSprite: + lda #$FE + sta $21C + sta $220 + + lda #$8F + sta $21D + sta $221 + + lda #$02 + sta $21E + lda #$42 + sta $222 + + rts + +DrawMoonSprite: + lda #$FE + sta $214 + sta $218 + + lda #$97 + sta $215 + sta $219 + + lda #$01 + sta $216 + lda #$41 + sta $21A + + rts + +DrawHomeSprite: + lda #$FE + sta $224 + lda #$A1 + sta $225 + lda #$01 + sta $226 + + rts + + +;;;;;;;;;; +;; +;; SET HOME SPRITE +;; +;;;;;;;;;; + +SetHome: + lda homeSunMoonStatus + and #%00000001 + bne SetHomeHomeEnabled + lda #$FE + sta $224 + rts +SetHomeHomeEnabled: + lda #180 + sec + sbc homeLat + tax + lda Latitudes, X + sta $224 +HomeLonHandling: + lda #$01 + sta (tmp + 1) + ldy homeLon + lda satStatus + and #%00001000 + bne HomeLonEast + dec (tmp + 1) + lda #180 + sec + sbc homeLon + tay +HomeLonEast: + + lda (Longitudes), Y + sec + sbc xscroll + sta $227 + + bcs HomeLonGTEXScroll + lda (tmp + 1) + eor nametable + and #$01 + bne HomeLonContinue + lda #$FE + sta $224 + jmp HomeLonContinue + +HomeLonGTEXScroll: + lda (tmp + 1) + eor nametable + and #$01 + beq HomeLonContinue + lda #$FE + sta $224 +HomeLonContinue: + lda $227 + sec + sbc #$04 + sta $227 + bcs HomeLonGTE8 + lda $224 + cmp #$FE + beq HomeLonSet + jmp HomeLonClear +HomeLonGTE8: + lda $224 + cmp #$FE + bne HomeLonSet +HomeLonClear: + lda #$FE + sta $224 +HomeLonSet: + rts + + + +SetSunMoon: +SetSunMoonSunCheck: + lda homeSunMoonStatus + and #%00000010 + bne SetSunMoonSunEnabled + lda #$FE + sta $21C + sta $220 + jmp SetSunMoonMoonCheck +SetSunMoonSunEnabled: + lda #180 + sec + sbc sunLat + tax + lda Latitudes, X + sta $21C + sta $220 +SetSunMoonMoonCheck: + lda homeSunMoonStatus + and #%00000100 + bne SetSunMoonMoonEnabled + lda #$FE + sta $214 + sta $218 + jmp SunLonHandling +SetSunMoonMoonEnabled: + lda #180 + sec + sbc moonLat + tax + lda Latitudes, X + sta $214 + sta $218 + + lda moonPhase + and #%00001111 + asl + tax + lda MoonPhases, X + ora #%00000001 + sta $215 + lda MoonPhases, X + lsr + lda #$04 + ror + lsr + sta $216 + inx + lda MoonPhases, X + ora #%00000001 + sta $219 + lda MoonPhases, X + lsr + lda #$04 + ror + lsr + sta $21A + +SunLonHandling: + lda #$01 + sta (tmp + 1) + ldy sunLon + lda homeSunMoonStatus + and #%00010000 + bne LonSignsSunEast + dec (tmp + 1) + lda #180 + sec + sbc sunLon + tay +LonSignsSunEast: + lda (Longitudes), Y + sec + sbc xscroll + sta $223 + bcs SunLonGTEXScroll + lda (tmp + 1) + eor nametable + and #$01 + bne SunLonContinue + lda #$FE + sta $220 + jmp SunLonContinue +SunLonGTEXScroll: + lda (tmp + 1) + eor nametable + and #$01 + beq SunLonContinue + lda #$FE + sta $220 +SunLonContinue: + + lda $223 + sec + sbc #$08 + sta $21F + bcs SunLonGTE8 + ;; Underflow happening + lda $220 + cmp #$FE + beq SunLonLeftContinue + lda #$FE + sta $21C + jmp SunLonLeftContinue +SunLonGTE8: + lda $220 + cmp #$FE + bne SunLonLeftContinue + lda #$FE + sta $21C +SunLonLeftContinue: + + + + + +MoonLonHandling: + lda #$01 + sta (tmp + 1) + ldy moonLon + lda homeSunMoonStatus + and #%00100000 + bne LonSignsMoonEast + dec (tmp + 1) + lda #180 + sec + sbc moonLon + tay +LonSignsMoonEast: + lda (Longitudes), Y + sec + sbc xscroll + sta $21B + bcs MoonLonGTEXScroll + lda (tmp + 1) + eor nametable + and #$01 + bne MoonLonContinue + lda #$FE + sta $218 + jmp MoonLonContinue +MoonLonGTEXScroll: + lda (tmp + 1) + eor nametable + and #$01 + beq MoonLonContinue + lda #$FE + sta $218 +MoonLonContinue: + + lda $21B + sec + sbc #$08 + sta $217 + bcs MoonLonGTE8 + ;; Underflow happening + lda $218 + cmp #$FE + beq MoonLonLeftContinue + lda #$FE + sta $214 + jmp MoonLonLeftContinue +MoonLonGTE8: + lda $218 + cmp #$FE + bne MoonLonLeftContinue + lda #$FE + sta $214 +MoonLonLeftContinue: + rts + +DrawInViewSprite: + lda homeSunMoonStatus + and #%00000001 + beq DrawInViewSpriteNotInView + lda homeSunMoonStatus + and #%10000000 + beq DrawInViewSpriteNotInView + lda $20C + sec + sbc #$09 + jmp DrawInViewSpriteContinue +DrawInViewSpriteNotInView: + lda #$FE +DrawInViewSpriteContinue: + sta $210 + lda #$87 + sta $211 + lda #$01 + sta $212 + lda $20F + sta $213 + rts diff --git a/src/nes/prg.asm b/src/nes/prg.asm new file mode 100644 index 0000000..4cbd828 --- /dev/null +++ b/src/nes/prg.asm @@ -0,0 +1,149 @@ +; Copyright (C) 2020, Vi Grey +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; +; 1. Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; 2. Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +; SUCH DAMAGE. + +.include "controller.asm" + +RESET: + sei + cld + ldx #$40 + stx APU_FRAME_COUNTER + ldx #$FF + txs + inx + lda #%00000110 + sta PPU_MASK + lda #$00 + sta PPU_CTRL + stx $4010 + ldy #$00 + +InitialVWait: + lda PPU_STATUS + bpl InitialVWait +InitialVWait2: + lda PPU_STATUS + bpl InitialVWait2 + +InitializeRAM: + ldx #$00 +InitializeRAMLoop: + lda #$00 + sta $0000, x + sta $0100, x + sta $0300, x + sta $0400, x + sta $0500, x + sta $0600, x + sta $0700, x + lda #$FE + sta $0200, x + inx + bne InitializeRAMLoop + jsr ResetScroll + jsr DrawMap + +jmp PollController + +NMI: + php + pha + txa + pha + tya + pha + inc nmi + lda #$00 + sta PPU_OAM_ADDR + lda #$02 + sta OAM_DMA + +; lda skipFrame +; beq NMINotSkipFrame +; lda #$00 +; sta skipFrame +; jmp NMIDone +NMINotSkipFrame: + + jsr HandleDraw + lda screen + beq NMIHandleScreenNotMap + +NMISprite0ClearWait: + bit PPU_STATUS + bvs NMISprite0ClearWait +NMISprite0HitWait: + bit PPU_STATUS + bvc NMISprite0HitWait + lda #%10100000 + ora nametable + ora patterns + sta PPU_CTRL + lda xscroll + sta PPU_SCROLL + +NMIHandleScreenNotMap: + jsr Update +NMIDone: + jsr EndDrawBuffer + pla + tay + pla + tax + pla + plp + rti + +Update: + rts + + +HandleDraw: + lda forceDraw + bne HandleDrawContinue + lda disableDraw + bne HandleDrawDone +HandleDrawContinue: + lda PPU_STATUS + jsr Draw + jsr DrawPreviousFrame + lda #$01 + sta allowPolling + jsr ResetScroll +HandleDrawDone: + rts + +WaitFrame: + lda nmi +WaitFrameLoop: + cmp nmi + beq WaitFrameLoop + rts + +.include "decompress.asm" +.include "draw.asm" +.include "include.asm" +.include "screen.asm" +.include "map.asm" diff --git a/src/nes/ram.asm b/src/nes/ram.asm new file mode 100644 index 0000000..7279d36 --- /dev/null +++ b/src/nes/ram.asm @@ -0,0 +1,94 @@ +; Copyright (C) 2020, Vi Grey +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; +; 1. Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; 2. Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +; SUCH DAMAGE. + +.enum $0000 +Variables: + nmi dsb 1 + screen dsb 1 + addr dsb 2 + addrEnd dsb 2 + ppuAddr dsb 2 + xscroll dsb 1 + nametable dsb 1 + patterns dsb 1 + tmp dsb 2 + + skipFrame dsb 1 + disableDraw dsb 1 +.ende + +.enum $0300 +DrawBufferVariables: + drawBufferOffset dsb 1 + drawBufferOffsetTmp dsb 1 + drawBuffer dsb 200 + forceDraw dsb 1 +.ende + +.enum $0400 +TopBarVariables: + satelliteDataWriting dsb 1 + satelliteDataOffset dsb 2 +.ende + +.enum $0500 +ControllerRAMData: + allowPolling dsb 1 + controller1 dsb 1 + readingData dsb 1 + readingDataOffset dsb 1 + dataType dsb 1 + + satStatus dsb 1 + day dsb 2 + month dsb 1 + year dsb 4 + hour dsb 2 + minute dsb 2 + second dsb 2 + altitude dsb 8 + noradID dsb 9 + satLat dsb 4 + satLon dsb 5 + + + homeLat dsb 1 + homeLon dsb 1 + sunLat dsb 1 + sunLon dsb 1 + moonLat dsb 1 + moonLon dsb 1 + moonPhase dsb 1 + homeSunMoonStatus dsb 1 + + altitudeBlank dsb 1 + noradIDBlank dsb 1 +.ende + +.enum $0600 + crewmateDataWriting dsb 1 + crewmateDataOffset dsb 2 + crewmateBuffer dsb 508 +.ende diff --git a/src/nes/sattrak.asm b/src/nes/sattrak.asm new file mode 100644 index 0000000..3fd437f --- /dev/null +++ b/src/nes/sattrak.asm @@ -0,0 +1,46 @@ +; Copyright (C) 2020, Vi Grey +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; +; 1. Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; 2. Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +; SUCH DAMAGE. + + .db "NES", $1A + .db $02 + .db $01 + .db $01 + .db $00 + .db 0, 0, 0, 0, 0, 0, 0, 0 + +.include "ram.asm" +.include "defs.asm" + +.base $8000 + +.include "prg.asm" + + .pad CALLBACK, #$FF + .dw NMI + .dw RESET + .dw 0 + +.base $0000 + .incbin "graphics/tilemap.chr" diff --git a/src/nes/screen.asm b/src/nes/screen.asm new file mode 100644 index 0000000..b306db5 --- /dev/null +++ b/src/nes/screen.asm @@ -0,0 +1,32 @@ +SetPalette: + lda #<(Palettes) + sta addr + lda #>(Palettes) + sta (addr + 1) + lda PPU_STATUS + lda #$3F + sta PPU_ADDR + lda #$00 + sta PPU_ADDR + ldy #$00 +SetPaletteLoop: + lda (addr), Y + sta PPU_DATA + iny + cpy #$20 + bne SetPaletteLoop + rts + +SetBlankPalette: + lda PPU_STATUS + lda #$3F + sta PPU_ADDR + lda #$00 + sta PPU_ADDR + ldx #$20 + lda #$0F +SetBlankPaletteLoop: + sta PPU_DATA + dex + bne SetBlankPaletteLoop + rts diff --git a/src/sattrak/config.go b/src/sattrak/config.go new file mode 100644 index 0000000..485bdb9 --- /dev/null +++ b/src/sattrak/config.go @@ -0,0 +1,141 @@ +package main + +import ( + "github.com/go-yaml/yaml" + + "os" + "strings" + "time" +) + +var ( + config = Config{} + basePath = os.Getenv("HOME") + "/.sattrak" + configFile = basePath + "/config.yaml" +) + +type Config struct { + EditKey string `yaml:"edit-key,omitempty"` + DefaultSatellite int64 `yaml:"default-satellite"` + SatelliteSwapRate int `yaml:"satellite-swap-rate"` + EnableSatelliteSwap bool `yaml:"enable-satellite-swap"` + OrbitRefreshRate int `yaml:"orbit-refresh-rate"` + OrbitSourceListPath string `yaml:"orbit-src-list-path"` + OrbitCachePath string `yaml:"orbit-cache-path"` + HomeLatitude float64 `yaml:"home-latitude"` + HomeLongitude float64 `yaml:"home-longitude"` + EnableHome bool `yaml:"enable-home"` + EnableSun bool `yaml:"enable-sun"` + EnableMoon bool `yaml:"enable-moon"` + AccurateMoonPhase bool `yaml:"accurate-moon-phase"` + TimeUTC bool `yaml:"time-utc"` + Time12Hr bool `yaml:"time-12-hr"` + StartTAStm32Immediately bool `yaml:"start-tastm32-immediately"` +} + +type ConfigPacket struct { + PacketType string `yaml:"packetType"` + Packet Config `yaml:"packet"` +} + +func readConfigFile() { + os.Mkdir(basePath, 0755) + content, _ := os.ReadFile(configFile) + yaml.Unmarshal(content, &config) + if config.OrbitRefreshRate == 0 { + config.OrbitRefreshRate = 86400 + } + if config.OrbitSourceListPath == "" { + config.OrbitSourceListPath = "$BASE/cache/orbits-list.txt" + } + if config.OrbitCachePath == "" { + config.OrbitCachePath = "$BASE/cache/orbits.xml" + } + config.OrbitSourceListPath = strings.ReplaceAll(config.OrbitSourceListPath, "$BASE", basePath) + config.OrbitCachePath = strings.ReplaceAll(config.OrbitCachePath, "$BASE", basePath) + isUTC = config.TimeUTC + is12Hr = config.Time12Hr + homeEnabled = config.EnableHome + sunEnabled = config.EnableSun + moonEnabled = config.EnableMoon + accurateMoonPhase = config.AccurateMoonPhase + autoStartDevices = config.StartTAStm32Immediately +} + +func editConfig(content Config) { + validConfigKey := true + if content.EditKey == "default-satellite" { + if content.DefaultSatellite > 0 { + config.DefaultSatellite = content.DefaultSatellite + setChosenOMM(content.DefaultSatellite) + } else { + setChosenOMM(0) + } + if config.SatelliteSwapRate > 0 && config.EnableSatelliteSwap { + satSwapTicker.Reset(time.Duration(config.SatelliteSwapRate) * time.Second) + } else { + satSwapTicker.Stop() + } + } else if content.EditKey == "enable-satellite-swap" { + config.EnableSatelliteSwap = content.EnableSatelliteSwap + if config.SatelliteSwapRate > 0 && config.EnableSatelliteSwap { + satSwapTicker.Reset(time.Duration(config.SatelliteSwapRate) * time.Second) + } else { + satSwapTicker.Stop() + } + } else if content.EditKey == "enable-home" { + config.EnableHome = content.EnableHome + homeEnabled = content.EnableHome + } else if content.EditKey == "enable-sun" { + config.EnableSun = content.EnableSun + sunEnabled = content.EnableSun + } else if content.EditKey == "enable-moon" { + config.EnableMoon = content.EnableMoon + moonEnabled = content.EnableMoon + } else if content.EditKey == "accurate-moon-phase" { + config.AccurateMoonPhase = content.AccurateMoonPhase + accurateMoonPhase = content.AccurateMoonPhase + } else if content.EditKey == "time-utc" { + config.TimeUTC = content.TimeUTC + isUTC = content.TimeUTC + } else if content.EditKey == "time-12-hr" { + config.Time12Hr = content.Time12Hr + is12Hr = content.Time12Hr + } else if content.EditKey == "start-tastm32-immediately" { + config.StartTAStm32Immediately = content.StartTAStm32Immediately + autoStartDevices = content.StartTAStm32Immediately + } else if content.EditKey == "satellite-swap-rate" { + if content.SatelliteSwapRate > 0 { + config.SatelliteSwapRate = content.SatelliteSwapRate + if config.SatelliteSwapRate > 0 && config.EnableSatelliteSwap { + satSwapTicker.Reset(time.Duration(config.SatelliteSwapRate) * time.Second) + } + } else { + validConfigKey = false + } + } else if content.EditKey == "home-latitude" { + if content.HomeLatitude >= -90 || content.HomeLatitude <= 90 { + config.HomeLatitude = content.HomeLatitude + } else { + validConfigKey = false + } + } else if content.EditKey == "home-longitude" { + if content.HomeLongitude >= -180 || content.HomeLongitude <= 180 { + config.HomeLongitude = content.HomeLongitude + } else { + validConfigKey = false + } + } else { + validConfigKey = false + } + if validConfigKey { + writeConfig() + } +} + +func writeConfig() { + config.OrbitSourceListPath = strings.ReplaceAll(config.OrbitSourceListPath, basePath, "$BASE") + config.OrbitCachePath = strings.ReplaceAll(config.OrbitCachePath, basePath, "$BASE") + content, _ := yaml.Marshal(config) + os.WriteFile(configFile, content, 0655) +} diff --git a/src/sattrak/conversion.go b/src/sattrak/conversion.go new file mode 100644 index 0000000..31acbb8 --- /dev/null +++ b/src/sattrak/conversion.go @@ -0,0 +1,91 @@ +package main + +import ( + "math" + "strconv" +) + +func setBitFlag(flag bool, statusByte byte, position int) byte { + if flag { + return statusByte | (1 << position) + } + return statusByte & (0xff ^ (1 << position)) +} + +func intToBCD(num interface{}) (bcd []byte) { + var num64 int64 + switch num.(type) { + case int: + num64 = int64(num.(int)) + case int64: + num64 = num.(int64) + default: + } + numStr := strconv.FormatInt(num64, 10) + for _, x := range numStr { + bcd = append(bcd, byte(x-'0')) + } + return +} + +func padByteSlice(i []byte, size int) (o []byte) { + o = i[:] + for x := len(i); x < size; x++ { + o = append([]byte{0}, o...) + } + return +} + +func float64ToDecimalPointAssumed(f float64) (s string) { + if f < 0 { + s += "-" + } else { + s += " " + } + if f == 0 { + s += "00000+0" + } else { + l10 := int(math.Log10(math.Abs(f))) + s += float64ToFixedString(f/math.Pow(10, float64(l10)), 5) + if l10 < 0 { + s += "-" + } else { + s += "+" + } + s += intToFixedString(l10, 1) + } + return +} + +func float64ToFixedString(f float64, places int) string { + _, dec := math.Modf(math.Abs(f)) + decInt := int64(math.Round(math.Abs(dec*math.Pow(10, float64(places))))) + decIntStr := strconv.FormatInt(decInt, 10) + for len(decIntStr) < places { + decIntStr = "0"+decIntStr + } + return decIntStr[:places] +} + +func intToFixedString(i int, places int) string { + intStr := strconv.Itoa(int(math.Abs(float64(i)))) + for len(intStr) < places { + intStr = "0"+intStr + } + return intStr[len(intStr)-places:] +} + +func int64ToFixedString(i int64, places int) string { + int64Str := strconv.FormatInt(int64(math.Abs(float64(i))), 10) + for len(int64Str) < places { + int64Str = "0"+int64Str + } + return int64Str[len(int64Str)-places:] +} + +func stringToFixedString(s string, places int) string { + for len(s) < places { + s += " " + } + return s[:places] +} diff --git a/src/sattrak/device.go b/src/sattrak/device.go new file mode 100644 index 0000000..5cecc2b --- /dev/null +++ b/src/sattrak/device.go @@ -0,0 +1,256 @@ +package main + +import ( + "github.com/mikepb/go-serial" + + "bytes" + "encoding/json" + "sync" + "time" +) + +const ( + DEVICE_READY = 0 + DEVICE_IN_USE = 1 + DEVICE_DFU_MODE = 2 +) + +var ( + deviceVIDPID = map[string][]int{ + "TAStm32": []int{0xb07, 0x7a5}, + } + readBuffer []byte + readPause bool + readWait chan bool + indexBase int + devices = map[*Device]*sync.RWMutex{} + devicesMutex = sync.RWMutex{} + deviceData DeviceData + deviceDataMutex = sync.RWMutex{} +) + +type DevicesPacket struct { + PacketType string `json:"packetType"` + Packet DeviceData `json:"packet"` +} + +type Device struct { + device *serial.Port `json:"-"` + Name string `json:"name,omitempty"` + Path string `json:"path,omitempty"` + Status int `json:"status"` + Index int `json:"index"` + Alive bool `json:"alive"` + resetWaiting bool `json:"-"` + setupWaiting bool `json:"-"` + devR chan bool `json:"-"` + devS chan bool `json:"-"` +} + +type DeviceData struct { + Devices []*Device `json:"devices"` +} + +func getTAStm32COMPaths() { + for { + infoList, err := serial.ListPorts() + if err == nil { + for _, list := range infoList { + vid, pid, _ := list.USBVIDPID() + for trd, y := range deviceVIDPID { + if y[0] == vid && y[1] == pid { + go addDevice(trd, list.Name()) + } + } + } + } + go func() { + packet := WSPacket{"devices", deviceData} + wsContent, _ := json.Marshal(packet) + sendAllWS(wsContent) + }() + time.Sleep(1 * time.Second) + } +} + +func addDevice(trd, devPath string) { + newDev := true + for _, d := range deviceData.Devices { + if d.Path == devPath { + newDev = false + break + } + } + if newDev { + d := new(Device) + options := serial.RawOptions + options.Mode = serial.MODE_READ_WRITE + options.BitRate = 115200 + device, err := options.Open(devPath) + if err != nil { + return + } + d.device = device + d.Name = trd + d.Path = devPath + d.Status = DEVICE_READY + d.Alive = false + if config.StartTAStm32Immediately { + d.Status = DEVICE_IN_USE + } + d.Index = indexBase + devices[d] = &sync.RWMutex{} + + devicesMutex.Lock() + deviceData.Devices = append(deviceData.Devices, d) + devicesMutex.Unlock() + + go deviceRead(d) + + deviceReset(d) + deviceSetup(d) + + indexBase++ + writeToDevice(d, satInputs) + writeToDevice(d, homeSunMoonInputs) + } +} + +func writeToDevice(d *Device, content []byte) { + if d.Status != DEVICE_IN_USE || d.Alive == false { + return + } + var final []byte + for _, x := range content { + final = append(final, []byte{'A', x}...) + } + if len(final) > 0 { + devices[d].Lock() + d.device.Write(final) + devices[d].Unlock() + } +} + +func deviceRead(d *Device) { + for { + d.device.SetDeadline(time.Now().Add(100 * time.Millisecond)) + buf := make([]byte, 4096) + bufN, err := d.device.Read(buf) + if bufN > 0 { + if bytes.ContainsRune(buf[:bufN], 'A') { + if d.Status == DEVICE_IN_USE && !d.Alive { + d.Alive = true + } + } + if bytes.ContainsRune(buf[:bufN], 'R') { + if d.resetWaiting { + d.resetWaiting = false + d.devR <- true + } + } + if bytes.ContainsRune(buf[:bufN], 'S') { + if d.setupWaiting { + d.setupWaiting = false + d.devS <- true + } + } + } else { + if err == serial.ErrTimeout { + if d.Alive == true && d.Status == DEVICE_IN_USE { + go func() { + d.Alive = false + deviceReset(d) + deviceSetup(d) + }() + } + } else { + d.device.Close() + break + } + } + } + for x := range deviceData.Devices { + if deviceData.Devices[x].Index == d.Index { + deviceData.Devices = append(deviceData.Devices[:x], deviceData.Devices[x+1:]...) + break + } + } +} + +func writeToAllDevices(content []byte) { + for _, d := range deviceData.Devices { + writeToDevice(d, content) + } +} + +func modifyDevicesStatus(index, status int) { + for _, d := range deviceData.Devices { + if d.Index == index { + d.Status = status + if d.Status == DEVICE_READY { + deviceReset(d) + } else if d.Status == DEVICE_IN_USE { + deviceReset(d) + deviceSetup(d) + } + break + } + } +} + +func deviceResetConsole(d *Device) { + devices[d].Lock() + d.device.Write([]byte("P0")) + time.Sleep(1000 * time.Millisecond) + d.device.Write([]byte("P1")) + devices[d].Unlock() +} + +func deviceReset(d *Device) { + ticker := time.NewTicker(1000 * time.Millisecond) + d.devR = make(chan bool) + for { + devices[d].Lock() + d.resetWaiting = true + d.device.Write([]byte("R")) + select { + case <-ticker.C: + case <-d.devR: + devices[d].Unlock() + return + } + devices[d].Unlock() + } +} + +func deviceSetup(d *Device) { + ticker := time.NewTicker(1000 * time.Millisecond) + d.devS = make(chan bool) + for { + devices[d].Lock() + d.setupWaiting = true + d.device.Write([]byte("SAN\x80\x44A\x00")) + select { + case <-ticker.C: + devices[d].Unlock() + deviceReset(d) + case <-d.devS: + devices[d].Unlock() + return + } + } +} + +func devicesRestartTimer() { + ticker := time.NewTicker(240000 * time.Millisecond) + for { + select { + case <-ticker.C: + for _, d := range deviceData.Devices { + if d.Status == DEVICE_IN_USE { + deviceResetConsole(d) + } + } + } + } +} diff --git a/src/sattrak/http.go b/src/sattrak/http.go new file mode 100644 index 0000000..3593d78 --- /dev/null +++ b/src/sattrak/http.go @@ -0,0 +1,83 @@ +package main + +import ( + "io/ioutil" + "mime" + + //"net" + "net/http" + //"os" + "strings" + "time" +) + +var ( + httpPath = basePath + "/http/" +) + +func getMIMEType(path string) (mimeType string) { + pathSplit := strings.Split(path, ".") + extension := pathSplit[len(pathSplit)-1] + if len(path) > 0 { + mimeType = mime.TypeByExtension("." + extension) + } + return +} + +func catchAll(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + url := r.URL.Path + url = url[1:] + urlModded := strings.ToLower(url) + if len(urlModded) > 0 { + if urlModded[len(urlModded)-1] == '/' { + urlModded = urlModded[:len(urlModded)-1] + } + } + if urlModded == "" { + urlModded = "index" + } + if urlModded == "index" { + handlePath(w, r, "index.html", httpPath) + } + if urlModded == "display" { + handlePath(w, r, "display.html", httpPath) + } + if urlModded == "control" { + handlePath(w, r, "control.html", httpPath) + } + if strings.Index(urlModded, "fonts/") == 0 { + handlePath(w, r, url, httpPath) + } + if strings.Index(urlModded, "scripts/") == 0 { + handlePath(w, r, url, httpPath) + } + if strings.Index(urlModded, "styles/") == 0 { + handlePath(w, r, url, httpPath) + } + if strings.Index(urlModded, "images/") == 0 { + handlePath(w, r, url, httpPath) + } +} + +func handlePath(w http.ResponseWriter, r *http.Request, path, pathBase string) { + if content, err := ioutil.ReadFile(pathBase + path); err == nil { + w.Header().Set("content-type", getMIMEType(path)) + w.Write(content) + } else { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte("404 page not found")) + } +} + +func httpListen() { + mux := http.NewServeMux() + mux.HandleFunc("/ws", wsHandler) + mux.HandleFunc("/", catchAll) + srv := &http.Server{ + Addr: "0.0.0.0:1234", + ReadTimeout: 250 * time.Millisecond, + Handler: mux, + } + srv.ListenAndServe() +} diff --git a/src/sattrak/http/fonts/MaterialIcons-Regular.ttf b/src/sattrak/http/fonts/MaterialIcons-Regular.ttf new file mode 100644 index 0000000..22db32c Binary files /dev/null and b/src/sattrak/http/fonts/MaterialIcons-Regular.ttf differ diff --git a/src/sattrak/http/images/map.png b/src/sattrak/http/images/map.png new file mode 100644 index 0000000..6586e89 Binary files /dev/null and b/src/sattrak/http/images/map.png differ diff --git a/src/sattrak/http/index.html b/src/sattrak/http/index.html new file mode 100644 index 0000000..1090885 --- /dev/null +++ b/src/sattrak/http/index.html @@ -0,0 +1,164 @@ + + + + + + + + + + +
+
+ + lock +
+
+
+
+ +
+ satellite_alt +
+
+ satellite_alt +
+
+ satellite_alt +
+ +
+ person_pin_circle +
+
+ person_pin_circle +
+
+ person_pin_circle +
+ + + +
+ light_mode +
+
+ light_mode +
+
+ light_mode +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+

+
+
+
+
+
+
+
+
+
+

Sun

+

+
+
+
+
+

Moon

+

+
+
+
+
+
+
+ +
+
+
+
+ +

Satellite

+

+ +
+
SAVE
+
UNDO
+
+
+

Home Location

+

+ Latitude: +
+ Longitude: +
+
SAVE
+
UNDO
+
+
+

Initial Date/Time

+

+ +
+ Set To Current Time +
+ Time is in UTC +
+
SAVE
+
UNDO
+
+
+

Device Settings

+

+ Automatically Start Devices +
+
SAVE
+
UNDO
+
+
+
+
  • + satellite_alt +
    + Info +
  • +
  • + cable +
    + Devices +
  • +
  • + edit +
    + Modify +
  • +
    + + + + + + + + diff --git a/src/sattrak/http/scripts/index2.js b/src/sattrak/http/scripts/index2.js new file mode 100644 index 0000000..f72add6 --- /dev/null +++ b/src/sattrak/http/scripts/index2.js @@ -0,0 +1,37 @@ +var homeLocation = document.getElementById("location"); +var homeLatitude = document.getElementById("home_latitude"); +var homeLongitude = document.getElementById("home_longitude"); +var startTime = document.getElementById("start_time"); +var startTimeNow = document.getElementById("start_time_now"); + +function setStartTimeToNow() { + var timeNow = new Date(); + + console.log(timeNow.getUTCDate()); + console.log(timeNow.getUTCMonth()); + startTime.value = timeNow.toISOString().slice(0,16); +} + +function getLocation() { + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition(handleCurrentPosition) + } +} + +function handleCurrentPosition(position) { + home_latitude.value = position.coords.latitude; + home_longitude.value = position.coords.longitude; +} + +startTimeNow.addEventListener("change", () => { + if (startTimeNow.checked) { + startTime.setAttribute("disabled", ""); + setStartTimeToNow(); + } else { + startTime.removeAttribute("disabled"); + } +}); + +homeLocation.addEventListener("click", () => { + getLocation(); +}); diff --git a/src/sattrak/http/styles/index2.css b/src/sattrak/http/styles/index2.css new file mode 100644 index 0000000..23b451e --- /dev/null +++ b/src/sattrak/http/styles/index2.css @@ -0,0 +1,202 @@ +@font-face { + font-family: 'Material Icons'; + font-style: normal; + font-weight: 400; + src: url(../fonts/MaterialIcons-Regular.ttf); + font-display: block; +} + +.material-icons { + vertical-align: bottom; + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 24px; /* Preferred icon size */ + display: inline-block; + line-height: 1; + text-transform: none; + letter-spacing: normal; + word-wrap: normal; + white-space: nowrap; + direction: ltr; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + -moz-osx-font-smoothing: grayscale; + font-feature-settings: 'liga'; +} + +.material-icons.md-dark { + color: rgba(0, 0, 0, 0.54); +} + +.material-icons.md-light { + color: rgba(255, 255, 255, 1); +} + +#location { + color: rgba(0, 0, 255, 0.7); + cursor: pointer; +} + +#aurora { + position: fixed; + width: 200%; + height: 200%; + left: -50%; + right: -50%; + top: -50%; + bottom: -50%; + z-index: -1000; +} + +#aurora > div { + position: absolute; + border-radius: 50%; + filter: brightness(15%) blur(180px) saturate(500%); + opacity: 0.6; +} + +.aurora-1 { + height: 90%; + width: 80%; + background-color: #ff057c; + left: -20%; + top: -10%; +} + +.aurora-2 { + height: 80%; + width: 70%; + background-color: #800f86; + top: -12%; + right: -12%; +} +.aurora-3 { + height: 65%; + width: 80%; + background-color: #1619b9; + bottom: -15%; + right: -20%; +} + + +.aurora-4 { + height: 50%; + width: 75%; + background-color: #1ba8e9; + bottom: -10%; + left: -18%; +} + +#aurora:after { + position: absolute; + content: ""; + height: 100%; + width: 100%; +} + +footer li { + list-style-type: none; + cursor: pointer; + border-right: 1px solid #888; +} +footer li:last-of-type { + border-right: 0px; +} + +html, +body { + margin: 0; + padding: 0; +} + +body { + text-align: center; + font-family: "Montserrat"; +} + +li { + list-style-type: none; + margin-bottom: 24px; +} + +#info { + background-color: #333388; + color: white; +} + +#info .key { + font-weight: bold; + text-align: right; + white-space: nowrap; +} + +#info .value { + width: 100%; + text-align: left; + padding-left: 8px; + font-weight: 500; +} + +#info, +#map, +#edit { + border-radius: 32px; + box-shadow: 4px 4px 16px rgba(0, 0, 0, 0.2); + margin: 16px auto; + max-width: 480px; +} + +#info { + padding: 16px; +} + +#edit { + padding: 16px; +} + +select, +option, +#start_time { + font-family: "Montserrat"; +} + +select { + font-size: 22px; + font-weight: 500; + padding: 6px 18px; + appearance: none; + border: 2px solid #aaa; + background-color: #ddd; +} + +#start_time { + font-size: 24px; + margin-bottom: 8px; +} + +.home-location { + padding: 6px 8px; + font-size: 22px; + max-width: 128px; +} + +#map { + background-image: url("../images/map.png"); + aspect-ratio: 2 / 1; + background-size: contain; + background-repeat: no-repeat; + width: calc(100% - 32px); +} + +#info, +#edit { + width: calc(100% - 64px); + max-width: 448px; +} + +.toggle-switch { + height: 1.5rem; + width: 4.5rem; + border-radius: 0.75rem; +} diff --git a/src/sattrak/info.go b/src/sattrak/info.go new file mode 100644 index 0000000..ed7bf68 --- /dev/null +++ b/src/sattrak/info.go @@ -0,0 +1,39 @@ +package main + +import ( + "sync" + "time" +) + +var ( + infoData InfoData + infoDataMutex = sync.RWMutex{} +) + +type InfoPacket struct { + PacketType string `json:"packetType"` + Packet InfoData `json:"packet"` +} + +type InfoData struct { + SatelliteName string `json:"satelliteName"` + Time time.Time `json:"time"` + Altitude float64 `json:"altitude"` + NoradID int64 `json:"noradID"` + SatelliteLatitude float64 `json:"satelliteLatitude"` + SatelliteLongitude float64 `json:"satelliteLongitude"` + SatelliteDaylight bool `json:"satelliteDaylight"` + HomeLatitude float64 `json:"homeLatitude"` + HomeLongitude float64 `json:"homeLongitude"` + SunLatitude float64 `json:"sunLatitude"` + SunLongitude float64 `json:"sunLongitude"` + MoonLatitude float64 `json:"moonLatitude"` + MoonLongitude float64 `json:"moonLongitude"` + MoonPhase float64 `json:"moonPhase"` + MoonIllumination float64 `json:"moonIllumination"` + IsUTC bool `json:"isUTC"` + Is12Hr bool `json:"is12Hr"` + HomeEnabled bool `json:"homeEnabled"` + SunEnabled bool `json:"sunEnabled"` + MoonEnabled bool `json:"moonEnabled"` +} diff --git a/src/sattrak/io.go b/src/sattrak/io.go new file mode 100644 index 0000000..27661b8 --- /dev/null +++ b/src/sattrak/io.go @@ -0,0 +1,11 @@ +package main + +import ( + "io/ioutil" + "strings" +) + +func getOrbitSourceList() { + content, _ := ioutil.ReadFile(config.OrbitSourceListPath) + orbitURLList = strings.Split(string(content), "\n") +} diff --git a/src/sattrak/modify.go b/src/sattrak/modify.go new file mode 100644 index 0000000..dac627a --- /dev/null +++ b/src/sattrak/modify.go @@ -0,0 +1,67 @@ +package main + +import ( + "encoding/json" +) + +type ModifyPacket struct { + PacketType string `json:"packetType"` + Packet ModifyData `json:"packet"` +} + +type ModifyData struct { + DefaultSatellite int64 `json:"default-satellite"` + DefaultSatelliteName string `json:"default-satellite-name"` + SatelliteSwapRate int `json:"satellite-swap-rate"` + EnableSatelliteSwap bool `json:"enable-satellite-swap"` + OrbitRefreshRate int `json:"orbit-refresh-rate"` + OrbitSourceListPath string `json:"orbit-src-list-path"` + OrbitCachePath string `json:"orbit-cache-path"` + HomeLatitude float64 `json:"home-latitude"` + HomeLongitude float64 `json:"home-longitude"` + EnableHome bool `json:"enable-home"` + EnableSun bool `json:"enable-sun"` + EnableMoon bool `json:"enable-moon"` + AccurateMoonPhase bool `json:"accurate-moon-phase"` + TimeUTC bool `json:"time-utc"` + Time12Hr bool `json:"time-12-hr"` + StartTAStm32Immediately bool `json:"start-tastm32-immediately"` +} + +func getModifyDataValues() (wsContent []byte) { + ommsMutex.Lock() + ommsListTmp := omms.OMMs[:] + ommsMutex.Unlock() + + modifyData := ModifyData{} + modifyData.DefaultSatellite = config.DefaultSatellite + satOMMIndex, _ := searchSortedOMMList(config.DefaultSatellite, 0, len(ommsListTmp), ommsListTmp) + modifyData.DefaultSatellite = config.DefaultSatellite + + modifyData.DefaultSatelliteName = ommsListTmp[satOMMIndex].OBJECT_NAME + + modifyData.SatelliteSwapRate = config.SatelliteSwapRate + modifyData.EnableSatelliteSwap = config.EnableSatelliteSwap + modifyData.OrbitRefreshRate = config.OrbitRefreshRate + modifyData.OrbitSourceListPath = config.OrbitSourceListPath + modifyData.OrbitCachePath = config.OrbitCachePath + modifyData.HomeLatitude = config.HomeLatitude + modifyData.HomeLongitude = config.HomeLongitude + modifyData.EnableHome = config.EnableHome + modifyData.EnableSun = config.EnableSun + modifyData.EnableMoon = config.EnableMoon + modifyData.AccurateMoonPhase = config.AccurateMoonPhase + //modifyData.TimeCurrent = config.TimeCurrent + modifyData.TimeUTC = config.TimeUTC + modifyData.Time12Hr = config.Time12Hr + //modifyData.StartTime = config.StartTime + modifyData.StartTAStm32Immediately = config.StartTAStm32Immediately + packet := WSPacket{"modify", modifyData} + wsContent, _ = json.Marshal(packet) + return +} + +func sendModifyDataValues() { + wsContent := getModifyDataValues() + sendAllWS(wsContent) +} diff --git a/src/sattrak/moon.go b/src/sattrak/moon.go new file mode 100644 index 0000000..8e9cf2d --- /dev/null +++ b/src/sattrak/moon.go @@ -0,0 +1,438 @@ +package main + +import ( + // "math" +) +/* + +import ( + "github.com/joshuaferrara/go-satellite" + + "math" + "time" +) + +var ( + //TABLE 47.A + //MEEUS PAGES 339 & 340 + sumLRArray = [][]float64{ + []float64{0, 0, 1, 0, 6288774, -20905355}, //1 + []float64{2, 0, -1, 0, 1274027, -3699111}, //2 + []float64{2, 0, 0, 0, 658314, -2955968}, //3 + []float64{0, 0, 2, 0, 213618, -569925}, //4 + []float64{0, 1, 0, 0, -185116, 48888}, //5 + []float64{0, 0, 0, 2, -114332, -3149}, //6 + []float64{2, 0, -2, 0, 58793, 246158}, //7 + []float64{2, -1, -1, 0, 57066, -152138}, //8 + []float64{2, 0, 1, 0, 53322, -170733}, //9 + []float64{2, -1, 0, 0, 45758, -204586}, //10 + []float64{0, 1, -1, 0, -40923, -129620}, //11 + []float64{1, 0, 0, 0, -34720, 108743}, //12 + []float64{0, 1, 1, 0, -30383, 104755}, //13 + []float64{2, 0, 0, -2, 15327, 10321}, //14 + []float64{0, 0, 1, 2, -12528, 0}, //15 + []float64{0, 0, 1, -2, 10980, 79661}, //16 + []float64{4, 0, -1, 0, 10675, -34782}, //17 + []float64{0, 0, 3, 0, 10034, -23210}, //18 + []float64{4, 0, -2, 0, 8548, -21636}, //19 + []float64{2, 1, -1, 0, -7888, 24208}, //20 + []float64{2, 1, 0, 0, -6766, 30824}, //21 + []float64{1, 0, -1, 0, -5163, -8379}, //22 + []float64{1, 1, 0, 0, 4987, -16675}, //23 + []float64{2, -1, 1, 0, 4036, -12831}, //24 + []float64{2, 0, 2, 0, 3994, -10445}, //25 + []float64{4, 0, 0, 0, 3861, -11650}, //26 + []float64{2, 0, -3, 0, 3665, 14403}, //27 + []float64{0, 1, -2, 0, -2689, -7003}, //28 + []float64{2, 0, -1, 2, -2602, 0}, //29 + []float64{2, -1, -2, 0, 2390, 10056}, //30 + []float64{1, 0, 1, 0, -2348, 6322}, //31 + []float64{2, -2, 0, 0, 2236, -9884}, //32 + []float64{0, 1, 2, 0, -2120, 5751}, //33 + []float64{0, 2, 0, 0, -2069, 0}, //34 + []float64{2, -2, -1, 0, 2048, -4950}, //35 + []float64{2, 0, 1, -2, -1773, 4130}, //36 + []float64{2, 0, 0, 2, -1595, 0}, //37 + []float64{4, -1, -1, 0, 1215, -3958}, //38 + []float64{0, 0, 2, 2, -1110, 0}, //39 + []float64{3, 0, -1, 0, -892, 3258}, //40 + []float64{2, 1, 1, 0, -810, 2616}, //41 + []float64{4, -1, -2, 0, 759, -1897}, //42 + []float64{0, 2, -1, 0, -713, -2117}, //43 + []float64{2, 2, -1, 0, -700, 2354}, //44 + []float64{2, 1, -2, 0, 691, 0}, //45 + []float64{2, -1, 0, -2, 596, 0}, //46 + []float64{4, 0, 1, 0, 549, -1423}, //47 + []float64{0, 0, 4, 0, 537, -1117}, //48 + []float64{4, -1, 0, 0, 520, -1571}, //49 + []float64{1, 0, -2, 0, -487, -1739}, //50 + []float64{2, 1, 0, -2, -399, 0}, //51 + []float64{0, 0, 2, -2, -381, -4421}, //52 + []float64{1, 1, 1, 0, 351, 0}, //53 + []float64{3, 0, -2, 0, -340, 0}, //54 + []float64{4, 0, -3, 0, 330, 0}, //55 + []float64{2, -1, 2, 0, 327, 0}, //56 + []float64{0, 2, 1, 0, -323, 1165}, //57 + []float64{1, 1, -1, 0, 299, 0}, //58 + []float64{2, 0, 3, 0, 294, 0}, //59 + []float64{2, 0, -1, -2, 0, 8752}} //60 + + //TABLE 47.B + //MEEUS PAGES 341 + //Eb Periodic terms for the latitude of the Moon + sumBArray = [][]float64{ + []float64{0, 0, 0, 1, 5128122}, //1 + []float64{0, 0, 1, 1, 280602}, //2 + []float64{0, 0, 1, -1, 277693}, //3 + []float64{2, 0, 0, -1, 173237}, //4 + []float64{2, 0, -1, 1, 55413}, //5 + []float64{2, 0, -1, -1, 46271}, //6 + []float64{2, 0, 0, 1, 32573}, //7 + []float64{0, 0, 2, 1, 17198}, //8 + []float64{2, 0, 1, -1, 9266}, //9 + []float64{0, 0, 2, -1, 8822}, //10 + []float64{2, -1, 0, -1, 8216}, //11 + []float64{2, 0, -2, -1, 4324}, //12 + []float64{2, 0, 1, 1, 4200}, //13 + []float64{2, 1, 0, -1, -3359}, //14 + []float64{2, -1, -1, 1, 2463}, //15 + []float64{2, -1, 0, 1, 2211}, //16 + []float64{2, -1, -1, -1, 2065}, //17 + []float64{0, 1, -1, -1, -1870}, //18 + []float64{4, 0, -1, -1, 1828}, //19 + []float64{0, 1, 0, 1, -1794}, //20 + []float64{0, 0, 0, 3, -1749}, //21 + []float64{0, 1, -1, 1, -1565}, //22 + []float64{1, 0, 0, 1, -1491}, //23 + []float64{0, 1, 1, 1, -1475}, //24 + []float64{0, 1, 1, -1, -1410}, //25 + []float64{0, 1, 0, -1, -1344}, //26 + []float64{1, 0, 0, -1, -1335}, //27 + []float64{0, 0, 3, 1, 1107}, //28 + []float64{4, 0, 0, -1, 1021}, //29 + []float64{4, 0, -1, 1, 833}, //30 + []float64{0, 0, 1, -3, 777}, //31 + []float64{4, 0, -2, 1, 671}, //32 + []float64{2, 0, 0, -3, 607}, //33 + []float64{2, 0, 2, -1, 596}, //34 + []float64{2, -1, 1, -1, 491}, //35 + []float64{2, 0, -2, 1, -451}, //36 + []float64{0, 0, 3, -1, 439}, //37 + []float64{2, 0, 2, 1, 422}, //38 + []float64{2, 0, -3, -1, 421}, //39 + []float64{2, 1, -1, 1, -366}, //40 + []float64{2, 1, 0, 1, -351}, //41 + []float64{4, 0, 0, 1, 331}, //42 + []float64{2, -1, 1, 1, 315}, //43 + []float64{2, -2, 0, -1, 302}, //44 + []float64{0, 0, 1, 3, -283}, //45 + []float64{2, 1, 1, -1, -229}, //46 + []float64{1, 1, 0, -1, 223}, //47 + []float64{1, 1, 0, 1, 223}, //48 + []float64{0, 1, -2, -1, -220}, //49 + []float64{2, 1, -1, -1, -220}, //50 + []float64{1, 0, 1, 1, -185}, //51 + []float64{2, -1, -2, -1, 181}, //52 + []float64{0, 1, 2, 1, -177}, //53 + []float64{4, 0, -2, -1, 176}, //54 + []float64{4, -1, -1, -1, 166}, //55 + []float64{1, 0, 1, -1, -164}, //56 + []float64{4, 0, 1, -1, 132}, //57 + []float64{1, 0, -1, -1, -119}, //58 + []float64{4, -1, 0, -1, 115}, //59 + []float64{2, -2, 0, 1, 107}} //60 + + //TABLE 22.A + //MEEUS PAGES 144 + //Periodic terms for the nutation in longitude + sumNutationLongitudeArray = [][]float64{ + []float64{0, 0, 0, 0, 1, -171996, -174.2, 92025, 8.9}, //1 + []float64{-2, 0, 0, 2, 2, -13187, -1.6, 5736, -3.1}, //2 + []float64{0, 0, 0, 2, 2, -2274, -0.2, 977, -0.5}, //3 + []float64{0, 0, 0, 0, 2, 2062, 0.2, -895, 0.5}, //4 + []float64{0, 1, 0, 0, 0, 1426, -3.4, 54, -0.1}, //5 + []float64{0, 0, 1, 0, 0, 712, 0.1, -7, 0}, //6 + []float64{-2, 1, 0, 2, 2, -517, 1.2, 224, -0.6}, //7 + []float64{0, 0, 0, 2, 1, -386, -0.4, 200, 0}, //8 + []float64{0, 0, 1, 2, 2, -301, 0, 129, -0.1}, //9 + []float64{-2, -1, 0, 2, 2, 217, -0.5, -95, 0.3}, //10 + []float64{-2, 0, 1, 0, 0, -158, 0, 0, 0}, //11 + []float64{-2, 0, 0, 2, 1, 129, 0.1, -70, 0}, //12 + []float64{0, 0, -1, 2, 2, 123, 0, -53, 0}, //13 + []float64{2, 0, 0, 0, 0, 63, 0, 0, 0}, //14 + []float64{0, 0, 1, 0, 1, 63, 0.1, -33, 0}, //15 + []float64{2, 0, -1, 2, 2, -59, 0, 26, 0}, //16 + []float64{0, 0, -1, 0, 1, -58, -0.1, 32, 0}, //17 + []float64{0, 0, 1, 2, 1, -51, 0, 27, 0}, //18 + []float64{-2, 0, 2, 0, 0, 48, 0, 0, 0}, //19 + []float64{0, 0, -2, 2, 1, 46, 0, -24, 0}, //20 + []float64{2, 0, 0, 2, 2, -38, 0, 16, 0}, //21 + []float64{0, 0, 2, 2, 2, -31, 0, 13, 0}, //22 + []float64{0, 0, 2, 0, 0, 29, 0, 0, 0}, //23 + []float64{-2, 0, 1, 2, 2, 29, 0, -12, 0}, //24 + []float64{0, 0, 0, 2, 0, 26, 0, 0, 0}, //25 + []float64{-2, 0, 0, 2, 0, -22, 0, 0, 0}, //26 + []float64{0, 0, -1, 2, 1, 21, 0, -10, 0}, //27 + []float64{0, 2, 0, 0, 0, 17, -0.1, 0, 0}, //28 + []float64{2, 0, -1, 0, 1, 16, 0, -8, 0}, //29 + []float64{-2, 2, 0, 2, 2, -16, 0.1, 7, 0}, //30 + []float64{0, 1, 0, 0, 1, -15, 0, 9, 0}, //31 + []float64{-2, 0, 1, 0, 1, -13, 0, 7, 0}, //32 + []float64{0, -1, 0, 0, 1, -12, 0, 6, 0}, //33 + []float64{0, 0, 2, -2, 0, 11, 0, 0, 0}, //34 + []float64{2, 0, -1, 2, 1, -10, 0, 5, 0}, //35 + []float64{2, 0, 1, 2, 2, -8, 0, 3, 0}, //36 + []float64{0, 1, 0, 2, 2, 7, 0, -3, 0}, //37 + []float64{-2, 1, 1, 0, 0, -7, 0, 0, 0}, //38 + []float64{0, -1, 0, 2, 2, -7, 0, 3, 0}, //39 + []float64{2, 0, 0, 2, 1, -7, 0, 3, 0}, //40 + []float64{2, 0, 1, 0, 0, 6, 0, 0, 0}, //41 + []float64{-2, 0, 2, 2, 2, 6, 0, -3, 0}, //42 + []float64{-2, 0, 1, 2, 1, 6, 0, -3, 0}, //43 + []float64{2, 0, -2, 0, 1, -6, 0, 3, 0}, //44 + []float64{2, 0, 0, 0, 1, -6, 0, 3, 0}, //45 + []float64{0, -1, 1, 0, 0, 5, 0, 0, 0}, //46 + []float64{-2, -1, 0, 2, 1, -5, 0, 3, 0}, //47 + []float64{-2, 0, 0, 0, 1, -5, 0, 3, 0}, //48 + []float64{0, 0, 2, 2, 1, -5, 0, 3, 0}, //49 + []float64{-2, 0, 2, 0, 1, 4, 0, 0, 0}, //50 + []float64{-2, 1, 0, 2, 1, 4, 0, 0, 0}, //51 + []float64{0, 0, 1, -2, 0, 4, 0, 0, 0}, //52 + []float64{-1, 0, 1, 0, 0, -4, 0, 0, 0}, //53 + []float64{-2, 1, 0, 0, 0, -4, 0, 0, 0}, //54 + []float64{1, 0, 0, 0, 0, -4, 0, 0, 0}, //55 + []float64{0, 0, 1, 2, 0, 3, 0, 0, 0}, //56 + []float64{0, 0, -2, 2, 2, -3, 0, 0, 0}, //57 + []float64{-1, -1, 1, 0, 0, -3, 0, 0, 0}, //58 + []float64{0, 1, 1, 0, 0, -3, 0, 0, 0}, //59 + []float64{0, -1, 1, 2, 2, -3, 0, 0, 0}, //60 + []float64{2, -1, -1, 2, 2, -3, 0, 0, 0}, //61 + []float64{0, 0, 3, 2, 2, -3, 0, 0, 0}, //62 + []float64{2, -1, 0, 2, 2, -3, 0, 0, 0}} //63 +) + +// (22.1) Astronomical Algorithms p 143 +// Get Julian Day Century from 2000 +func getJ2000Century(j2000 float64) float64 { + return (j2000 - 2451545) / 36525 +} + +// (47.1) Astronomical Algorithms p 338 +// Moon Mean Longitude +func getMoonMeanLongitude(t float64) float64 { + return (218.3164477 + t*481267.88123421 - t*t*0.0015786 + + t*t*t/538841 - t*t*t*t/6519400) +} + +// (47.2) Astronomical Algorithms p 338 +// Moon Mean Elongation +func getMoonMeanElongation(t float64) float64 { + return (297.8501921 + t*445267.1114034 - t*t*0.0018819 + + t*t*t/545868 - t*t*t*t/113065000) +} + +// (47.4) Astronomical Algorithms p 338 +// Moon Mean Anomaly +func getMoonMeanAnomaly(t float64) float64 { + return (134.9633964 + t*477198.8675055 + t*t*0.0087414 + + t*t*t/69699 - t*t*t*t/14712000) +} + +// (47.5) Astronomical Algorithms p 338 +// Moon Argument of Latitude (mean distance of moon from ascending node) +func getMoonArgumentOfLatitude(t float64) float64 { + return (93.2720950 + t*483202.0175233 - t*t*0.0036539 - + t*t*t/3526000 + t*t*t*t/863310000) +} + +// Astronomical Algorithms p 338 +// Moon Correction Term A1 (due to action of Venus) +func getMoonA1(t float64) float64 { + return 119.75 + t*131.849 +} + +// Astronomical Algorithms p 338 +// Moon Correction Term A2 (due to action of Jupiter) +func getMoonA2(t float64) float64 { + return 53.09 + t*479264.29 +} + +// Astronomical Algorithms p 338 +// Moon Correction Term A3 (due to flattening of Earth) +func getMoonA3(t float64) float64 { + return 313.45 + t*481266.484 +} + +// (47.6) Astronomical Algorithms p 338 +// Earth Eccentricity for Moon Calculations +func getEarthEccentricityForMoon(t float64) float64 { + return 1 - t*0.002516 - t*t*0.0000074 +} + +// (47.7) Astronomical Algorithms p 338 +// Moon Longitude of Ascending Node +func getMoonLongitudeOfAscendingNode(t float64) float64 { + return (125.04452 - t*1934.136261 + t*t*0.0020708 + + t*t*t/450000) +} + +func calculateSumsLAndR(lp, d, m, mp, f, t float64) (sumL, sumR float64) { + a1 := getMoonA1(t) + a1Rad := degreesToRadians(a1) + a2 := getMoonA2(t) + a2Rad := degreesToRadians(a2) + moonEarthEccentricity := getEarthEccentricityForMoon(t) + for _, x := range sumLRArray { + termD := x[0] * d + termM := x[1] * m + termMP := x[2] * mp + termF := x[3] * f + term := termD + termM + termMP + termF + termRad := degreesToRadians(term) + termSin := math.Sin(termRad) + termCos := math.Cos(termRad) + if x[1] != 0 { + if x[4] != 0 { + termSin *= x[4] * moonEarthEccentricity + } else { + termSin *= moonEarthEccentricity + } + if x[5] != 0 { + termCos *= x[5] * moonEarthEccentricity + } else { + termCos *= moonEarthEccentricity + } + } else { + if x[4] != 0 { + termSin *= x[4] + } + if x[5] != 0 { + termCos *= x[5] + } + } + sumL += termSin + sumR += termCos + } + sumL += 3958 * math.Sin(a1Rad) + sumL += 1962 * math.Sin(degreesToRadians(lp-f)) + sumL += 318 * math.Sin(a2Rad) + return +} + +func calculateSumB(lp, d, m, mp, f, t float64) (sumB float64) { + a1 := getMoonA1(t) + a3 := getMoonA1(t) + a3Rad := degreesToRadians(a3) + moonEarthEccentricity := getEarthEccentricityForMoon(t) + for _, x := range sumBArray { + termD := x[0] * d + termM := x[1] * m + termMP := x[2] * mp + termF := x[3] * f + term := termD + termM + termMP + termF + term = math.Sin(degreesToRadians(term)) + if x[1] != 0 { + if x[4] != 0 { + term *= x[4] * moonEarthEccentricity + } else { + term *= moonEarthEccentricity + } + } else { + if x[4] != 0 { + term *= x[4] + } + } + sumB += term + } + sumB += 2235 * math.Sin(degreesToRadians(lp)) + sumB += 382 * math.Sin(a3Rad) + sumB += 175 * math.Sin(degreesToRadians(a1-f)) + sumB += 175 * math.Sin(degreesToRadians(a1+f)) + sumB += 127 * math.Sin(degreesToRadians(lp-mp)) + sumB += 115 * math.Sin(degreesToRadians(lp+mp)) + return +} + +func calculateSumsDeltaYAndE(lp, d, m, mp, f, o, t float64) (sumY, sumE float64) { + for _, x := range sumNutationLongitudeArray { + termD := x[0] * d + termM := x[1] * m + termMP := x[2] * mp + termF := x[3] * f + termOmega := x[4] * o + term := termD + termM + termMP + termF + termOmega + termRad := degreesToRadians(term) + termSin := math.Sin(termRad) + termCos := math.Cos(termRad) + if x[6] != 0 { + termSin *= (x[5] + x[6]*t) + } else { + termSin *= x[5] + } + if x[7] != 0 { + if x[8] != 0 { + termCos *= (x[7] + x[8]*t) + } else { + termCos *= x[7] + } + } + sumY += termSin + sumE += termCos + } + sumY *= 0.0001 / 3600 + sumE *= 0.0001 / 3600 + return +} + +func degreesToArctime(d float64) (degree, arcMinute, arcSecond float64) { + modD := modDegrees(d) + degree, arcTime := math.Modf(modD) + arcTime *= 3600 + arcMinute, _ = math.Modf(arcTime / 60) + arcSecond = math.Mod(arcTime, 60) + return +} + +// (48.4) Astronomical Algorithms p 346 +func getMoonIlluminationFraction(d, m, mp float64) float64 { + dDeg := radiansToDegrees(d) + a := (180 - dDeg - 6.289*math.Sin(mp) + 2.1*math.Sin(m) - + 1.274*math.Sin(2*d-mp) - 0.658*math.Sin(2*d) - + 0.214*math.Sin(2*mp) - 0.11*math.Sin(d)) + return a +} + +// (48.1) Astronomical Algorithms p 345 +func getIllumination(i float64) float64 { + return (1 + math.Cos(degreesToRadians(i))) / 2 +} +func getMoonLocationIllumination(t time.Time) (eciCoords satellite.Vector3, i float64) { + year, month, day := t.UTC().Date() + hour, minute, second := t.UTC().Clock() + jday := satellite.JDay(year, int(month), day, hour, minute, second) + jC := getJ2000Century(jday) + + lp := getMoonMeanLongitude(jC) + d := getMoonMeanElongation(jC) + dRad := degreesToRadians(d) + m := getSunMeanAnomaly(jC) + mRad := degreesToRadians(m) + mp := getMoonMeanAnomaly(jC) + mpRad := degreesToRadians(mp) + f := getMoonArgumentOfLatitude(jC) + o := getMoonLongitudeOfAscendingNode(jC) + sumL, sumR := calculateSumsLAndR(lp, d, m, mp, f, jC) + l := lp + sumL/1000000 + delta := 385000.56 + sumR/1000 + sumY, sumE := calculateSumsDeltaYAndE(lp, d, m, mp, f, o, jC) + epsilon := 23.43929 - jC*0.01300417 - jC*jC*0.0000001638889*jC*jC*jC*0.0000005036111 + sumE + epsilonRad := degreesToRadians(epsilon) + apparentLongitude := l + sumY + apparentLongitudeRad := degreesToRadians(apparentLongitude) + eciCoords.X = math.Cos(apparentLongitudeRad) * delta + eciCoords.Y = math.Cos(epsilonRad) * math.Sin(apparentLongitudeRad) * delta + eciCoords.Z = math.Sin(epsilonRad) * math.Sin(apparentLongitudeRad) * delta + i = getMoonIlluminationFraction(dRad, mRad, mpRad) + return +} +*/ diff --git a/src/sattrak/omm.go b/src/sattrak/omm.go new file mode 100644 index 0000000..39c02d9 --- /dev/null +++ b/src/sattrak/omm.go @@ -0,0 +1,111 @@ +package main + +import ( + "github.com/joshuaferrara/go-satellite" + + "crypto/rand" + "encoding/xml" + "math/big" + "strings" + //"time" +) + +type OMM struct { + OBJECT_NAME string `xml:"body>segment>metadata>OBJECT_NAME"` + OBJECT_ID string `xml:"body>segment>metadata>OBJECT_ID"` + CENTER_NAME string `xml:"body>segment>metadata>CENTER_NAME"` + EPOCH string `xml:"body>segment>data>meanElements>EPOCH"` + MEAN_MOTION float64 `xml:"body>segment>data>meanElements>MEAN_MOTION"` + ECCENTRICITY float64 `xml:"body>segment>data>meanElements>ECCENTRICITY"` + INCLINATION float64 `xml:"body>segment>data>meanElements>INCLINATION"` + RA_OF_ASC_NODE float64 `xml:"body>segment>data>meanElements>RA_OF_ASC_NODE"` + ARG_OF_PERICENTER float64 `xml:"body>segment>data>meanElements>ARG_OF_PERICENTER"` + MEAN_ANOMALY float64 `xml:"body>segment>data>meanElements>MEAN_ANOMALY"` + EPHEMERIS_TYPE int64 `xml:"body>segment>data>tleParameters>EPHEMERIS_TYPE"` + CLASSIFICATION_TYPE string `xml:"body>segment>data>tleParameters>CLASSIFICATION_TYPE"` + NORAD_CAT_ID int64 `xml:"body>segment>data>tleParameters>NORAD_CAT_ID"` + ELEMENT_SET_NO int64 `xml:"body>segment>data>tleParameters>ELEMENT_SET_NO"` + REV_AT_EPOCH int64 `xml:"body>segment>data>tleParameters>REV_AT_EPOCH"` + BSTAR float64 `xml:"body>segment>data>tleParameters>BSTAR"` + MEAN_MOTION_DOT float64 `xml:"body>segment>data>tleParameters>MEAN_MOTION_DOT"` + MEAN_MOTION_DDOT float64 `xml:"body>segment>data>tleParameters>MEAN_MOTION_DDOT"` +} + +type OMMs struct { + XMLName xml.Name `xml:"ndm"` + OMMs []OMM `xml:"omm"` +} + +func ommToSatelliteOMM(omm OMM) (sat satellite.Satellite) { + /* + satOMM.ObjectName = omm.OBJECT_NAME + satOMM.ObjectID = omm.OBJECT_ID + satOMM.CenterName = omm.CENTER_NAME + satOMM.Epoch, _ = time.Parse(getSubsecondFormat(omm.EPOCH), omm.EPOCH) + satOMM.MeanMotion = omm.MEAN_MOTION + satOMM.Eccentricity = omm.ECCENTRICITY + satOMM.Inclination = omm.INCLINATION + satOMM.RAOfAscNode = omm.RA_OF_ASC_NODE + satOMM.ArgOfPericenter = omm.ARG_OF_PERICENTER + satOMM.MeanAnomaly = omm.MEAN_ANOMALY + satOMM.EphemerisType = omm.EPHEMERIS_TYPE + satOMM.ClassificationType = omm.CLASSIFICATION_TYPE + satOMM.NORADCatID = omm.NORAD_CAT_ID + satOMM.ElementSetNo = omm.ELEMENT_SET_NO + satOMM.RevAtEpoch = omm.REV_AT_EPOCH + satOMM.BStar = omm.BSTAR + satOMM.MeanMotionDot = omm.MEAN_MOTION_DOT + satOMM.MeanMotionDdot = omm.MEAN_MOTION_DDOT + */ + return +} + +func getSubsecondFormat(timeString string) (timeFormat string) { + timeFormat = "2006-01-02T15:04:05" + x := strings.LastIndex(timeString, ".") + if x > -1 { + timeFormat += "." + for x < len(timeString)-1 { + timeFormat += "0" + x++ + } + } + return +} + +func setChosenOMM(noradIDVal int64) { + + ommsMutex.Lock() + ommsListTmp := omms.OMMs[:] + ommsMutex.Unlock() + + ommLen := len(ommsListTmp) + + if noradIDVal > 0 { + ommIndex, noradID := searchSortedOMMList(noradIDVal, 0, ommLen, ommsListTmp) + if noradID == noradIDVal { + chosenOMM = ommsListTmp[ommIndex] + return + } + } + if ommLen > 0 { + offset, _ := rand.Int(rand.Reader, big.NewInt(int64(ommLen-1))) + chosenOMM = ommsListTmp[int(offset.Int64())] + } +} + +func searchSortedOMMList(noradIDVal int64, start, length int, ommsList []OMM) (ommIndex int, noradID int64) { + if length > 0 { + indexPoint := (start + (start + length - 1)) / 2 + noradIDAtIndexPoint := ommsList[indexPoint].NORAD_CAT_ID + if noradIDVal == noradIDAtIndexPoint { + ommIndex = indexPoint + noradID = noradIDAtIndexPoint + } else if noradIDVal < noradIDAtIndexPoint { + ommIndex, noradID = searchSortedOMMList(noradIDVal, start, indexPoint-start, ommsList) + } else { + ommIndex, noradID = searchSortedOMMList(noradIDVal, indexPoint+1, length-(indexPoint+1-start), ommsList) + } + } + return +} diff --git a/src/sattrak/orbits.go b/src/sattrak/orbits.go new file mode 100644 index 0000000..5065388 --- /dev/null +++ b/src/sattrak/orbits.go @@ -0,0 +1,98 @@ +package main + +import ( + "encoding/xml" + "io" + "net/http" + "os" + "sort" + "time" +) + +var ( + orbitURLList []string + ommFileDownloadTicker *time.Ticker +) + +func ommFileDownloadTimer() { + ommFileDownloadTicker = time.NewTicker(time.Duration(config.OrbitRefreshRate) * time.Second) + for { + select { + case <-ommFileDownloadTicker.C: + handleOMMFile(true) + case <-newOrbitRefreshRate: + ommFileDownloadTicker = time.NewTicker(time.Duration(config.OrbitRefreshRate) * time.Second) + } + } +} + +func handleOMMFile(force bool) { + f, err := os.Stat(config.OrbitCachePath) + if err == nil { + if force || time.Now().Sub(f.ModTime()) >= time.Duration(config.OrbitRefreshRate)*time.Second { + downloadOMMFile() + } + xmlDat, _ := os.ReadFile(config.OrbitCachePath) + + ommsNew := new(OMMs) + xml.Unmarshal(xmlDat, ommsNew) + sortOMMs(*ommsNew) + } else { + downloadOMMFile() + } +} + +func downloadOMMFile() { + ommsNew := new(OMMs) + sats := make(map[int64]bool) + getOrbitSourceList() + writeCacheFile := true + for _, url := range orbitURLList { + for attempt := 0; attempt < 3 && len(url) > 0; attempt++ { + client := http.Client{ + Timeout: 5 * time.Second, + } + resp, err := client.Get(url) + if err == nil { + defer resp.Body.Close() + content, err := io.ReadAll(resp.Body) + // SUCCESSFUL CONTENT READ + if err == nil { + ommsTmp := new(OMMs) + xml.Unmarshal(content, &ommsTmp) + for _, omm := range ommsTmp.OMMs { + if sats[omm.NORAD_CAT_ID] == false { + sats[omm.NORAD_CAT_ID] = true + ommsNew.OMMs = append(ommsNew.OMMs, omm) + } + } + break + } + } + if attempt == 2 { + writeCacheFile = false + break + } + } + if !writeCacheFile { + break + } + } + if writeCacheFile { + sortOMMs(*ommsNew) + ommsCacheContent, _ := xml.Marshal(ommsNew) + err := os.WriteFile(config.OrbitCachePath+"-bak", ommsCacheContent, 0644) + if err == nil { + os.Rename(config.OrbitCachePath+"-bak", config.OrbitCachePath) + } + } +} + +func sortOMMs(ommsList OMMs) { + sort.Slice(ommsList.OMMs, func(i, j int) bool { + return ommsList.OMMs[i].NORAD_CAT_ID < ommsList.OMMs[j].NORAD_CAT_ID + }) + ommsMutex.Lock() + omms.OMMs = ommsList.OMMs[:] + ommsMutex.Unlock() +} diff --git a/src/sattrak/sat.go b/src/sattrak/sat.go new file mode 100644 index 0000000..91cf755 --- /dev/null +++ b/src/sattrak/sat.go @@ -0,0 +1,60 @@ +package main + +import ( + "github.com/joshuaferrara/go-satellite" + + "math" +) + +const ( + EARTH_SEMI_MAJOR_AXIS float64 = 6378.137 + EARTH_SEMI_MINOR_AXIS float64 = 6356.7523142 +) +/* + +func eciToLLA(eciCoords satellite.Vector3, gmst float64) (altitude float64, latLon satellite.LatLong) { + f := (EARTH_SEMI_MAJOR_AXIS - EARTH_SEMI_MINOR_AXIS) / EARTH_SEMI_MAJOR_AXIS + e2 := (2*f - f*f) + xyLen := math.Sqrt(eciCoords.X*eciCoords.X + eciCoords.Y*eciCoords.Y) + + latLon.Longitude = math.Atan2(eciCoords.Y, eciCoords.X) - gmst + latitude := math.Atan2(eciCoords.Z, xyLen) + c := 0.0 + for i := 0; i < 20; i++ { + c = 1 / math.Sqrt(1-e2*(math.Sin(latitude)*math.Sin(latitude))) + latitude = math.Atan2(eciCoords.Z+(EARTH_SEMI_MAJOR_AXIS*c*e2*math.Sin(latitude)), xyLen) + } + altitude = (xyLen / math.Cos(latitude)) + latLon.Latitude = latitude + return +} + +func llaToECI(latLonRad satellite.LatLong, altitude, gmst float64) (eciCoords satellite.Vector3) { + theta := gmst + latLonRad.Longitude + pa := (EARTH_SEMI_MINOR_AXIS + altitude) * math.Cos(latLonRad.Latitude) + eciCoords.X = pa * math.Cos(theta) + eciCoords.Y = pa * math.Sin(theta) + eciCoords.Z = (EARTH_SEMI_MAJOR_AXIS + altitude) * math.Sin(latLonRad.Latitude) + return +} +*/ + +func latLongRadians(latLon satellite.LatLong) (latLonRad satellite.LatLong) { + latLonRad.Latitude = latLon.Latitude * degreesToRadiansMul + latLonRad.Longitude = latLon.Longitude * degreesToRadiansMul + return +} + +func diffBetweenECIs(eciA, eciB satellite.Vector3) (eciAB satellite.Vector3) { + eciAB.X = eciA.X - eciB.X + eciAB.Y = eciA.Y - eciB.Y + eciAB.Z = eciA.Z - eciB.Z + return +} + +func ecefToECI(ecef satellite.Vector3, gmst float64) (eci satellite.Vector3) { + eci.X = (ecef.X * math.Cos(-gmst)) + (ecef.Y*math.Sin(-gmst)) + eci.Y = (ecef.X * -math.Sin(-gmst)) + (ecef.Y*math.Cos(-gmst)) + eci.Z = ecef.Z + return +} diff --git a/src/sattrak/sattrak.go b/src/sattrak/sattrak.go new file mode 100644 index 0000000..68f0768 --- /dev/null +++ b/src/sattrak/sattrak.go @@ -0,0 +1,402 @@ +package main + +import ( + "github.com/joshuaferrara/go-satellite" + + "fmt" + + "encoding/json" + "unicode" + "strings" + "strconv" + "math" + "sync" + "time" +) + +const ( + STATUS_SAT_IN_VIEW = 1 << 0 + STATUS_SAT_IN_SHADOW = 1 << 7 + STATUS_HOME_LOCATION_IN_NIGHT = 1 << 1 + STATUS_HOME_LONGITUDE_EAST = 1 << 2 +) + +var ( + locationTime time.Time + + chosenOMM OMM + chosenOMMOffset int + ommNames = []string{} + omms OMMs + + newOrbitRefreshRate chan bool + newOrbitRefreshRateMutex = sync.RWMutex{} + degreesToRadiansMul = math.Pi / 180 + twoPi = math.Pi * 2 + + isUTC, is12Hr bool + homeEnabled, sunEnabled, moonEnabled bool + accurateMoonPhase bool + isCurrentTime bool + autoStartDevices bool + + satInputs = []byte{} + homeSunMoonInputs = []byte{} + + satSwapTicker *time.Ticker + + ommsMutex = sync.RWMutex{} +) + +type WSPacket struct { + PacketType string `json:"packetType"` + Packet interface{} `json:"packet"` +} + +func getSatAltitudeSlice(altitude float64) (altitudeBCDFinal []byte) { + altitudeBCD := intToBCD(int64(math.Round(altitude * 100))) + altitudeBCDLen := len(altitudeBCD) + if altitudeBCDLen > 7 { + exponent := intToBCD(altitudeBCDLen - 3) + exponentLen := len(exponent) + altitudeBCD = intToBCD(int64(math.Round(altitude / + math.Pow(10, float64(altitudeBCDLen-8+exponentLen))))) + altitudeBCDFinal = append(altitudeBCDFinal, altitudeBCD[0]) + altitudeBCDFinal = append(altitudeBCDFinal, 0xfe) + altitudeBCDFinal = append(altitudeBCDFinal, altitudeBCD[1:6-exponentLen]...) + altitudeBCDFinal = append(altitudeBCDFinal, 0x0e) + altitudeBCDFinal = append(altitudeBCDFinal, exponent...) + } else { + altitudeBCD = padByteSlice(altitudeBCD, 7) + altitudeBCDFinal = append(altitudeBCDFinal, altitudeBCD[:5]...) + altitudeBCDFinal = append(altitudeBCDFinal, 0xfe) + altitudeBCDFinal = append(altitudeBCDFinal, altitudeBCD[5:]...) + } + return +} + +func setSatelliteInputs(t time.Time, timeIs12Hr bool, satLatLon satellite.LatLong, altitude float64, noradID int64) (controllerInputs []byte) { + sec := padByteSlice(intToBCD(t.Second()), 2) + min := padByteSlice(intToBCD(t.Minute()), 2) + hour := t.Hour() + var hr []byte + if timeIs12Hr { + hr = padByteSlice(intToBCD(hour%12), 2) + if hr[0] == 0 && hr[1] == 0 { + hr[0] = 1 + hr[1] = 2 + } + } else { + hr = padByteSlice(intToBCD(hour), 2) + } + day := padByteSlice(intToBCD(t.Day()), 2) + yr := padByteSlice(intToBCD(t.Year()), 4) + nID := padByteSlice(intToBCD(noradID), 9) + altitudeSlice := getSatAltitudeSlice(altitude) + satLat := padByteSlice(intToBCD(int(math.Abs(math.Round(satLatLon.Latitude*100)))), 4) + satLon := padByteSlice(intToBCD(int(math.Abs(math.Round(satLatLon.Longitude*100)))), 5) + var satStatus byte + satStatus = setBitFlag(satLatLon.Latitude >= 0, satStatus, 7) + satStatus = setBitFlag(satLatLon.Longitude >= 0, satStatus, 6) + satStatus = setBitFlag(hour >= 12, satStatus, 2) + satStatus = setBitFlag(is12Hr, satStatus, 1) + _, offset := t.Zone() + satStatus = setBitFlag(offset == 0, satStatus, 0) + controllerInputs = []byte{0xf0} + controllerInputs = append(controllerInputs, day...) + controllerInputs = append(controllerInputs, uint8(t.Month())) + controllerInputs = append(controllerInputs, yr...) + controllerInputs = append(controllerInputs, hr...) + controllerInputs = append(controllerInputs, min...) + controllerInputs = append(controllerInputs, sec...) + controllerInputs = append(controllerInputs, altitudeSlice...) + controllerInputs = append(controllerInputs, nID...) + controllerInputs = append(controllerInputs, satLat...) + controllerInputs = append(controllerInputs, satLon...) + controllerInputs = append(controllerInputs, satStatus) + return +} + +func homeSunMoonToInputs(homeLatLon, sunLatLon, moonLatLon satellite.LatLong, illumination float64, satInDaylight, satInView bool) (controllerInputs []byte) { + homeLat := uint8(math.Abs(math.Round(homeLatLon.Latitude))+90) % 181 + homeLon := uint8(math.Abs(math.Round(homeLatLon.Longitude))) % 181 + + sunLat := uint8(math.Round(sunLatLon.Latitude)+90) % 181 + sunLon := uint8(math.Abs(math.Round(sunLatLon.Longitude))) % 181 + + moonLat := uint8(math.Round(moonLatLon.Latitude)+90) % 181 + moonLon := uint8(math.Abs(math.Round(moonLatLon.Longitude))) % 181 + if !accurateMoonPhase { + illumination = 8 + } + moonPhase := moonPhaseToSpriteOffset(illumination) + var homeSunMoonStatus byte + + homeSunMoonStatus = setBitFlag(homeEnabled, homeSunMoonStatus, 0) + homeSunMoonStatus = setBitFlag(sunEnabled, homeSunMoonStatus, 1) + homeSunMoonStatus = setBitFlag(moonEnabled, homeSunMoonStatus, 2) + + homeSunMoonStatus = setBitFlag(homeLatLon.Longitude >= 0, homeSunMoonStatus, 3) + homeSunMoonStatus = setBitFlag(sunLatLon.Longitude >= 0, homeSunMoonStatus, 4) + homeSunMoonStatus = setBitFlag(moonLatLon.Longitude >= 0, homeSunMoonStatus, 5) + + homeSunMoonStatus = setBitFlag(satInDaylight, homeSunMoonStatus, 6) + homeSunMoonStatus = setBitFlag(satInView, homeSunMoonStatus, 7) + + controllerInputs = []byte{0xf5} + controllerInputs = append(controllerInputs, homeLat) + controllerInputs = append(controllerInputs, homeLon) + controllerInputs = append(controllerInputs, sunLat) + controllerInputs = append(controllerInputs, sunLon) + controllerInputs = append(controllerInputs, moonLat) + controllerInputs = append(controllerInputs, moonLon) + controllerInputs = append(controllerInputs, moonPhase) + controllerInputs = append(controllerInputs, homeSunMoonStatus) + return +} + +func getLineChecksum(s string) (checksum int) { + for _, letter := range s { + if letter == '-' { + checksum++ + } else if unicode.IsNumber(letter) { + checksum += int(letter-'0') + } + } + return +} + +func handleNewSecondData(t time.Time, omm OMM) { + date, _ := time.Parse("2006-01-02T15:04:05.999999", omm.EPOCH) + f := float64(date.Hour()*3600e9+date.Minute()*60e9+date.Second()*1e9+date.Nanosecond())/float64(24*3600e9) + line1 := "1 " + line1 += int64ToFixedString(omm.NORAD_CAT_ID, 5) + line1 += stringToFixedString(omm.CLASSIFICATION_TYPE, 1) + line1 += " " + s := stringToFixedString(strings.ReplaceAll(omm.OBJECT_ID, "-", ""), 10) + line1 += s[2:] + line1 += " " + line1 += intToFixedString(date.Year(), 2) + line1 += intToFixedString(date.YearDay(), 3) + line1 += "." + float64ToFixedString(f, 8) + line1 += " " + if omm.MEAN_MOTION_DOT < 0 { + line1 += "-" + } else { + line1 += " " + } + line1 += "." + float64ToFixedString(omm.MEAN_MOTION_DOT, 8) + line1 += " " + line1 += float64ToDecimalPointAssumed(omm.MEAN_MOTION_DDOT) + line1 += " " + line1 += float64ToDecimalPointAssumed(omm.BSTAR) + line1 += " 0 " + s = strconv.Itoa(int(omm.ELEMENT_SET_NO)) + for len(s) < 4 { + s = " " + s + } + line1 += s + line1 += intToFixedString(getLineChecksum(line1), 1) + + line2 := "2 " + line2 += int64ToFixedString(omm.NORAD_CAT_ID, 5) + line2 += " " + i, d := math.Modf(omm.INCLINATION) + if i < 10 { + line2 += " " + intToFixedString(int(i), 1) + } else if i < 100 { + line2 += " " + intToFixedString(int(i), 2) + } else { + line2 += intToFixedString(int(i), 3) + } + line2 += "." + float64ToFixedString(d, 4) + line2 += " " + i, d = math.Modf(omm.RA_OF_ASC_NODE) + if i < 10 { + line2 += " " + intToFixedString(int(i), 1) + } else if i < 100 { + line2 += " " + intToFixedString(int(i), 2) + } else { + line2 += intToFixedString(int(i), 3) + } + line2 += "." + float64ToFixedString(d, 4) + line2 += " " + line2 += float64ToFixedString(omm.ECCENTRICITY, 7) + line2 += " " + i, d = math.Modf(omm.ARG_OF_PERICENTER) + if i < 10 { + line2 += " " + intToFixedString(int(i), 1) + } else if i < 100 { + line2 += " " + intToFixedString(int(i), 2) + } else { + line2 += intToFixedString(int(i), 3) + } + line2 += "." + float64ToFixedString(d, 4) + line2 += " " + i, d = math.Modf(omm.MEAN_ANOMALY) + if i < 10 { + line2 += " " + intToFixedString(int(i), 1) + } else if i < 100 { + line2 += " " + intToFixedString(int(i), 2) + } else { + line2 += intToFixedString(int(i), 3) + } + line2 += "." + float64ToFixedString(d, 4) + line2 += " " + i, d = math.Modf(omm.MEAN_MOTION) + if i < 10 { + line2 += " " + intToFixedString(int(i), 1) + } else { + line2 += intToFixedString(int(i), 2) + } + line2 += "." + float64ToFixedString(d, 8) + s = strconv.Itoa(int(omm.REV_AT_EPOCH)) + for len(s) < 5 { + s = " " + s + } + line2 += s + line2 += intToFixedString(getLineChecksum(line2), 1) + + sat := satellite.TLEToSat(line1, line2, "wgs84") + timeNow := t + if isUTC { + timeNow = timeNow.UTC() + } + yr, mon, day := timeNow.UTC().Date() + hr, min, sec := timeNow.UTC().Clock() + + gmst := satellite.ThetaG_JD(satellite.JDay(yr, int(mon), day, hr, min, sec)) + satECI, _ := satellite.Propagate(sat, yr, int(mon), day, hr, min, sec) + satAltitude, _, satLatLon := satellite.ECIToLLA(satECI, gmst) + satLatLon = satellite.LatLongDeg(satLatLon) + + homeLatLon := satellite.LatLong{config.HomeLatitude, config.HomeLongitude} + + homeECI := satellite.LLAToECI(latLongRadians(homeLatLon), 0, gmst) + sunECI, moonECI, illumination, phase := getSunMoonECIMoonIlluminationPhase(strconv.FormatInt(t.Unix(), 10), gmst) + + + _, _, sunLatLon := satellite.ECIToLLA(sunECI, gmst) + sunLatLon = satellite.LatLongDeg(sunLatLon) + _, _, moonLatLon := satellite.ECIToLLA(moonECI, gmst) + moonLatLon = satellite.LatLongDeg(moonLatLon) + + homeToSatLine := diffBetweenECIs(homeECI, satECI) + satToSunLine := diffBetweenECIs(satECI, sunECI) + + distanceECIToHome := math.Sqrt(homeECI.X*homeECI.X + homeECI.Y*homeECI.Y + + homeECI.Z*homeECI.Z) + distanceECIToSat := math.Sqrt(satECI.X*satECI.X + satECI.Y*satECI.Y + + satECI.Z*satECI.Z) + distanceECIToSun := math.Sqrt(sunECI.X*sunECI.X + sunECI.Y*sunECI.Y + + sunECI.Z*sunECI.Z) + distanceHomeToSat := math.Sqrt(homeToSatLine.X*homeToSatLine.X + + homeToSatLine.Y*homeToSatLine.Y + homeToSatLine.Z*homeToSatLine.Z) + distanceSatToSun := math.Sqrt(satToSunLine.X*satToSunLine.X + + satToSunLine.Y*satToSunLine.Y + satToSunLine.Z*satToSunLine.Z) + + angleECIHomeSat := radiansToDegrees(math.Acos( + (distanceHomeToSat*distanceHomeToSat + + distanceECIToHome*distanceECIToHome - + distanceECIToSat*distanceECIToSat) / + (2 * distanceHomeToSat * distanceECIToHome))) + angleSatECISun := radiansToDegrees(math.Acos( + (distanceECIToSat*distanceECIToSat + + distanceECIToSun*distanceECIToSun - + distanceSatToSun*distanceSatToSun) / + (2 * distanceECIToSat * distanceECIToSun))) + angleSatSunECI := radiansToDegrees(math.Acos( + (distanceSatToSun*distanceSatToSun + + distanceECIToSun*distanceECIToSun - + distanceECIToSat*distanceECIToSat) / + (2 * distanceSatToSun * distanceECIToSun))) + + angleEarthSunECI := radiansToDegrees( + math.Atan(EARTH_SEMI_MAJOR_AXIS / distanceECIToSun)) + + satInDaylight := angleSatECISun < 90 || (angleSatECISun >= 90 && angleSatSunECI > angleEarthSunECI) + satInView := angleECIHomeSat >= 90 + + utc := isUTC + moonSprite := illumination / 2 + if phase < 0.5 { + moonSprite = 1 - moonSprite + } + satInputs = setSatelliteInputs(timeNow, is12Hr, satLatLon, satAltitude, + omm.NORAD_CAT_ID) + homeSunMoonInputs = homeSunMoonToInputs(homeLatLon, sunLatLon, moonLatLon, + moonSprite, satInDaylight, satInView) + + infoDataMutex.Lock() + infoData.SatelliteName = omm.OBJECT_NAME + infoData.NoradID = omm.NORAD_CAT_ID + infoData.Time = timeNow + infoData.Altitude = satAltitude + infoData.SatelliteLatitude = satLatLon.Latitude + infoData.SatelliteLongitude = satLatLon.Longitude + infoData.SatelliteDaylight = satInDaylight + infoData.HomeLatitude = homeLatLon.Latitude + infoData.HomeLongitude = homeLatLon.Longitude + infoData.SunLatitude = sunLatLon.Latitude + infoData.SunLongitude = sunLatLon.Longitude + infoData.MoonLatitude = moonLatLon.Latitude + infoData.MoonLongitude = moonLatLon.Longitude + infoData.MoonIllumination = illumination + infoData.IsUTC = utc + infoData.Is12Hr = is12Hr + infoData.HomeEnabled = config.EnableHome + infoData.SunEnabled = config.EnableSun + infoData.MoonEnabled = config.EnableMoon + if config.AccurateMoonPhase { + infoData.MoonPhase = modDegrees(phase) / 360 + } else { + infoData.MoonPhase = 0 + } + packet := WSPacket{"info", infoData} + wsContent, _ := json.Marshal(packet) + sendAllWS(wsContent) + infoDataMutex.Unlock() +} + +func degreesToRadians(deg float64) float64 { + return math.Mod(deg*degreesToRadiansMul, twoPi) +} + +func radiansToDegrees(rad float64) float64 { + return math.Mod(rad/degreesToRadiansMul, 360) +} + +func modDegrees(i float64) float64 { + return math.Mod(math.Mod(i, 360)+360, 360) + +} + +func mod2Pi(i float64) float64 { + return math.Mod(math.Mod(i, twoPi)+twoPi, twoPi) +} + +func main() { + wsConns = make(map[int]*wsConn) + fmt.Println("READING CONFIG") + readConfigFile() + fmt.Println("READING CONFIG DONE") + newOrbitRefreshRate = make(chan bool) + go ommFileDownloadTimer() + fmt.Println("HANDLE OMM FILE") + handleOMMFile(false) + fmt.Println("HANDLE OMM FILE DONE") + fmt.Println("SELECT OMM") + setChosenOMM(config.DefaultSatellite) + fmt.Println("SELECT OMM DONE") + + go devicesRestartTimer() + go getTAStm32COMPaths() + + go setHomeSatSunMoonInputsLoop() + + //setTimeDif(time.Unix(1663251565, 0)) + + go switchOMM() + go httpListen() + startServer() +} diff --git a/src/sattrak/search.go b/src/sattrak/search.go new file mode 100644 index 0000000..3fa21c5 --- /dev/null +++ b/src/sattrak/search.go @@ -0,0 +1,43 @@ +package main + +import ( + "strconv" + "strings" +) + +type SatellitePacket struct { + PacketType string `json:"packetType"` + Packet DeviceData `json:"packet"` +} + +func searchSatName(name string, maxLen int) (satList []string) { + ommsMutex.Lock() + ommsListTmp := omms.OMMs[:] + ommsMutex.Unlock() + + var satListPerfectMatch []string + var satListStartMatch []string + var satListInnerMatch []string + name = strings.ToLower(name) + + for _, omm := range ommsListTmp { + satName := strings.ToLower(omm.OBJECT_NAME) + ind := strings.Index(satName, name) + if ind >= 0 { + if satName == name { + satListPerfectMatch = append(satListPerfectMatch, omm.OBJECT_NAME+" (NORAD ID: "+strconv.FormatInt(omm.NORAD_CAT_ID, 10)+")") + } else if ind == 0 { + satListStartMatch = append(satListStartMatch, omm.OBJECT_NAME+" (NORAD ID: "+strconv.FormatInt(omm.NORAD_CAT_ID, 10)+")") + } else { + satListInnerMatch = append(satListInnerMatch, omm.OBJECT_NAME+" (NORAD ID: "+strconv.FormatInt(omm.NORAD_CAT_ID, 10)+")") + } + } + } + satList = append(satList, satListPerfectMatch...) + satList = append(satList, satListStartMatch...) + satList = append(satList, satListInnerMatch...) + if maxLen > 0 { + satList = satList[:maxLen] + } + return +} diff --git a/src/sattrak/sun.go b/src/sattrak/sun.go new file mode 100644 index 0000000..f1d0be9 --- /dev/null +++ b/src/sattrak/sun.go @@ -0,0 +1,54 @@ +package main +/* + +import ( + "github.com/joshuaferrara/go-satellite" + + "math" + "time" +) + +// (28.2) Astronomical Algorithms p 183 +// Sun Mean Longitude +func getSunMeanLongitude(t float64) float64 { + //a := 280.4606184 + t*36000.77005361 + //return a + return (280.466567 + t*36000.76982779 + t*t*0.0003032028 + + t*t*t/49.931 - t*t*t*t/1.53 - t*t*t*t*t/20) +} + +// (47.3) Astronomical Algorithms p 338 +// Sun Mean Anomaly +func getSunMeanAnomaly(t float64) float64 { + return (357.5291092 + t*35999.0502909 - t*t*0.0001536 + + t*t*t/24490000) +} + +func getSunEccentricity(t float64) float64 { + //(46.8093/3600) + return 23.43929 - t*0.01300258333 +} + +func getSunLocation(t time.Time) (eciCoords satellite.Vector3) { + year, month, day := t.UTC().Date() + hour, minute, second := t.UTC().Clock() + jday := satellite.JDay(year, int(month), day, hour, minute, second) + jC := getJ2000Century(jday) + + meanLongitude := getSunMeanLongitude(jC) + meanAnomaly := getSunMeanAnomaly(jC) + meanAnomalyRad := degreesToRadians(meanAnomaly) + + eclipticLongitude := (meanLongitude + 1.914666471*(math.Sin(meanAnomalyRad)) + + (0.918994643 * math.Sin(2*meanAnomalyRad))) + eclipticLongitudeRad := degreesToRadians(eclipticLongitude) + + eccentricity := getSunEccentricity(jC) + eccentricityRad := degreesToRadians(eccentricity) + var auInKM float64 = 149597870.700 + eciCoords.X = math.Cos(eclipticLongitudeRad) * auInKM + eciCoords.Y = math.Cos(eccentricityRad) * math.Sin(eclipticLongitudeRad) * auInKM + eciCoords.Z = math.Sin(eccentricityRad) * math.Sin(eclipticLongitudeRad) * auInKM + return +} +*/ diff --git a/src/sattrak/syzygy.go b/src/sattrak/syzygy.go new file mode 100644 index 0000000..76aa368 --- /dev/null +++ b/src/sattrak/syzygy.go @@ -0,0 +1,40 @@ +package main + +import ( + "github.com/joshuaferrara/go-satellite" + + "math" + "encoding/json" + "os/exec" +) + +type Syzygy struct { + MoonECEF satellite.Vector3 `json:"moonECEF"` + SunECEF satellite.Vector3 `json:"sunECEF"` + MoonIllumination float64 `json:"moonIllumination"` + MoonPhase float64 `json:"moonPhase"` +} + +type Vector struct { + X float64 `json:"x"` + Y float64 `json:"y"` + Z float64 `json:"z"` +} + +func getSunMoonECIMoonIlluminationPhase(unixTimestamp string, gmst float64) (sunECI, moonECI satellite.Vector3, moonIllumination, moonPhase float64) { + out, err := exec.Command("syzygy", unixTimestamp).Output() + if err != nil { + return + } + s := new(Syzygy) + json.Unmarshal(out, &s) + moonIllumination = s.MoonIllumination + moonPhase = s.MoonPhase + sunECI = ecefToECI(s.SunECEF, gmst) + moonECI = ecefToECI(s.MoonECEF, gmst) + return +} + +func moonPhaseToSpriteOffset(i float64) uint8 { + return uint8((int(math.Round(i*16))%16+16)%16) +} diff --git a/src/sattrak/tcp.go b/src/sattrak/tcp.go new file mode 100644 index 0000000..92379a3 --- /dev/null +++ b/src/sattrak/tcp.go @@ -0,0 +1,65 @@ +package main + +import ( + "net" + "sync" + //"time" +) + +var ( + connections = map[net.Conn]*sync.RWMutex{} + connectionsTmp = map[net.Conn]*sync.RWMutex{} + connectionsCopyMutex = sync.RWMutex{} +) + +func startServer() { + l, _ := net.Listen("tcp", "127.0.0.1:25544") + defer l.Close() + for { + conn, _ := l.Accept() + if conn != nil { + connections[conn] = &sync.RWMutex{} + connectionsCopyMutex.Lock() + connectionsTmp = map[net.Conn]*sync.RWMutex{} + for conn := range connections { + connectionsTmp[conn] = connections[conn] + } + writeToConn(conn, satInputs) + writeToConn(conn, homeSunMoonInputs) + connectionsCopyMutex.Unlock() + } + } +} + +func writeToConn(conn net.Conn, content []byte) { + if connectionsTmp[conn] != nil { + if len(content) > 0 { + connectionsTmp[conn].Lock() + _, err := conn.Write(content) + connectionsTmp[conn].Unlock() + if err != nil { + delete(connections, conn) + } + } + } else { + delete(connections, conn) + } +} + +func writeToAllConns(content []byte) { + connectionsCopyMutex.Lock() + connectionsTmp = map[net.Conn]*sync.RWMutex{} + for conn := range connections { + connectionsTmp[conn] = connections[conn] + } + var wg sync.WaitGroup + for conn := range connectionsTmp { + wg.Add(1) + go func(wg *sync.WaitGroup) { + defer wg.Done() + writeToConn(conn, content) + }(&wg) + } + wg.Wait() + connectionsCopyMutex.Unlock() +} diff --git a/src/sattrak/test.txt b/src/sattrak/test.txt new file mode 100644 index 0000000..273cf96 --- /dev/null +++ b/src/sattrak/test.txt @@ -0,0 +1,99 @@ +48027 +46490 +47912 +40536 +47781 +47833 +32951 +38354 +48599 +48840 +45101 +41774 +47776 +45257 +47460 +47126 +49728 +48435 +41170 +48361 +46038 +32393 +45419 +49731 +47383 +45246 +40080 +42865 +37234 +47967 +48225 +45045 +43157 +40730 +25981 +48643 +41891 +37602 +49773 +43752 +28220 +51740 +37192 +47367 +48983 +43928 +49315 +47601 +51626 +48901 +45570 +43152 +45378 +50842 +47359 +40030 +40340 +46563 +44767 +44204 +46833 +44109 +45531 +48386 +33053 +45559 +50186 +45060 +45258 +44103 +45752 +47477 +49150 +38741 +32299 +43850 +40889 +43857 +47766 +46303 +45209 +33436 +42842 +51510 +43158 +40001 +48307 +47753 +46298 +32384 +42870 +25652 +45256 +39086 +51739 +45090 +44785 +47875 +48799 diff --git a/src/sattrak/timer.go b/src/sattrak/timer.go new file mode 100644 index 0000000..6118eca --- /dev/null +++ b/src/sattrak/timer.go @@ -0,0 +1,59 @@ +package main + +import ( + "time" +) + +var ( + lastCheckedTimeUnix int64 + timeDif time.Duration +) + +func checkNewSecond(t time.Time) bool { + timeUnix := t.Unix() + if timeUnix > lastCheckedTimeUnix { + lastCheckedTimeUnix = timeUnix + return true + } + return false +} + +func setHomeSatSunMoonInputsLoop() { + ticker := time.NewTicker(100 * time.Millisecond) + for { + select { + case <-ticker.C: + t := time.Now().Add(timeDif) + if checkNewSecond(t) { + handleNewSecondData(t, chosenOMM) + + writeToAllConns(homeSunMoonInputs) + writeToAllConns(satInputs) + + writeToAllDevices(homeSunMoonInputs) + writeToAllDevices(satInputs) + } + } + } +} + +func setTimeDif(t time.Time) { + timeDif = t.UTC().Sub(time.Now().UTC()) + lastCheckedTimeUnix = 0 +} + +func switchOMM() { + satSwapTicker = time.NewTicker(1 * time.Second) + satSwapRate := config.SatelliteSwapRate + if satSwapRate > 0 && config.EnableSatelliteSwap { + satSwapTicker.Reset(time.Duration(config.SatelliteSwapRate) * time.Second) + } else { + satSwapTicker.Stop() + } + for { + select { + case <-satSwapTicker.C: + setChosenOMM(0) + } + } +} diff --git a/src/sattrak/ws.go b/src/sattrak/ws.go new file mode 100644 index 0000000..34eefb2 --- /dev/null +++ b/src/sattrak/ws.go @@ -0,0 +1,108 @@ +package main + +import ( + "github.com/go-yaml/yaml" + "github.com/gorilla/websocket" + + "encoding/json" + "net/http" + "sync" + "time" +) + +var ( + upgrader = websocket.Upgrader{} + wsConns map[int]*wsConn + newWSConnMutex sync.RWMutex + wsMutex sync.RWMutex + wsConnNum = 0 +) + +type wsConn struct { + conn *websocket.Conn + mutex sync.RWMutex + authenticated bool + display bool + connNum int +} + +func wsHandler(w http.ResponseWriter, r *http.Request) { + upgrader.CheckOrigin = func(r *http.Request) bool { + return true + } + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + panic(err) + return + } + wc := new(wsConn) + wc.conn = conn + wc.authenticated = true + newWSConnMutex.Lock() + wc.connNum = wsConnNum + wsConns[wc.connNum] = wc + wsConnNum++ + packet := WSPacket{"info", infoData} + wsContent, _ := json.Marshal(packet) + sendWS(wc, wsContent) + wsContent = getModifyDataValues() + sendWS(wc, wsContent) + newWSConnMutex.Unlock() + go connRead(wc) +} + +func connRead(wc *wsConn) { + defer wc.conn.Close() + defer delete(wsConns, wc.connNum) + //wc.conn.SetReadDeadline(time.Now().Add(5 * time.Second)) + for { + if wc.authenticated { + wc.conn.SetReadDeadline(time.Time{}) + } + //_, _, err := wc.conn.ReadMessage() + _, content, err := wc.conn.ReadMessage() + if err != nil { + break + } + go func(content []byte) { + contentVals := new(WSPacket) + json.Unmarshal(content, &contentVals) + if contentVals.PacketType == "devices" { + d := new(DevicesPacket) + json.Unmarshal(content, &d) + if len(d.Packet.Devices) > 0 { + modifyDevicesStatus(d.Packet.Devices[0].Index, d.Packet.Devices[0].Status) + packet := WSPacket{"devices", deviceData} + wsContent, _ := json.Marshal(packet) + sendAllWS(wsContent) + } + } else if contentVals.PacketType == "config" { + c := new(ConfigPacket) + yaml.Unmarshal(content, &c) + editConfig(c.Packet) + sendModifyDataValues() + } + }(content) + } +} + +func sendAllWS(content []byte) { + wsMutex.Lock() + for _, wc := range wsConns { + sendWS(wc, content) + } + wsMutex.Unlock() +} + +func sendWS(wc *wsConn, content []byte) { + if !wc.authenticated { + return + } + wc.mutex.Lock() + err := wc.conn.WriteMessage(websocket.TextMessage, content) + if err != nil { + wc.conn.Close() + delete(wsConns, wc.connNum) + } + wc.mutex.Unlock() +}