├── .gitignore
├── AuthorsSignature.txt
├── Drivers
├── Accelerometer
│ ├── Accelerometer.py
│ ├── README.md
│ └── __init__.py
├── Driver.py
├── Magnetometer
│ ├── Magnetometer.py
│ ├── README.md
│ └── __init__.py
├── README.md
├── UV
│ ├── README.md
│ ├── UVDriver.py
│ └── __init__.py
├── __init__.py
├── adc
│ ├── ADC_Driver.py
│ ├── ADC_Driver.py.save
│ ├── README.md
│ └── __init__.py
├── antennaDoor
│ ├── AntennaDoor.py
│ ├── README.md
│ └── __init__.py
├── backupAntennaDeployer
│ ├── BackupAntennaDeployer.py
│ ├── README.md
│ └── __init__.py
├── boomDeployer
│ ├── BoomDeployer.py
│ ├── README.md
│ └── __init__.py
├── camera
│ ├── .Camera.py.swo
│ ├── Camera.py
│ ├── README.md
│ └── __init__.py
├── cpuTemperature
│ ├── CpuTemperature.py
│ ├── README.md
│ └── __init__.py
├── eps
│ ├── EPS.py
│ ├── EPS.py.save
│ ├── README.md
│ ├── __init__.py
│ └── documentation.txt
├── rtc
│ ├── README.md
│ ├── __init__.py
│ └── rtc_driver.py
├── solarPanelTemp
│ ├── README.md
│ ├── __init__.py
│ └── solarDriver.py
├── sunSensors
│ ├── README.md
│ ├── __init__.py
│ └── sunSensorDriver.py
└── transceiverConfig
│ ├── README.md
│ ├── TransceiverConfig.py
│ └── __init__.py
├── DummyDrivers
├── Accelerometer
│ ├── Accelerometer.py
│ ├── README.md
│ └── __init__.py
├── Driver.py
├── Magnetometer
│ ├── Magnetometer.py
│ ├── README.md
│ └── __init__.py
├── README.md
├── UV
│ ├── README.md
│ ├── UVDriver.py
│ └── __init__.py
├── __init__.py
├── adc
│ ├── ADC_Driver.py
│ ├── README.md
│ └── __init__.py
├── antennaDoor
│ ├── AntennaDoor.py
│ ├── README.md
│ └── __init__.py
├── backupAntennaDeployer
│ ├── BackupAntennaDeployer.py
│ ├── README.md
│ └── __init__.py
├── boomDeployer
│ ├── BoomDeployer.py
│ ├── README.md
│ └── __init__.py
├── camera
│ ├── .Camera.py.swo
│ ├── Camera.py
│ ├── README.md
│ └── __init__.py
├── cpuTemperature
│ ├── CpuTemperature.py
│ ├── README.md
│ └── __init__.py
├── eps
│ ├── EPS.py
│ ├── README.md
│ ├── __init__.py
│ └── documentation.txt
├── rtc
│ ├── README.md
│ ├── __init__.py
│ └── rtc_driver.py
├── solarPanelTemp
│ ├── README.md
│ ├── __init__.py
│ └── solarDriver.py
├── sunSensors
│ ├── README.md
│ ├── __init__.py
│ └── sunSensorDriver.py
└── transceiverConfig
│ ├── README.md
│ ├── TransceiverConfig.py
│ └── __init__.py
├── README.md
├── TXISR
├── InformationDocs
│ ├── TXServiceCode User Guide.docx
│ ├── TXServiceCodeTest
│ └── TXServiceCode_User_Guide.txt
├── README.md
├── TXServiceCode
│ ├── TXService.run
│ ├── TXServiceCode.c
│ ├── TXServiceCodeV1.map
│ ├── debug.h
│ └── readMe
├── __init__.py
├── exampleFiles
│ ├── exampleTXFileData.txt
│ └── exampleTXFilePicture.txt
├── packetProcessing.py
├── prepareFiles.py
├── pythonInterrupt.py
└── transmitionQueue.py
├── __init__.py
├── changelog
├── docs
├── CubeWorks_UML.png
├── DataBase schema Proposal.docx
├── Electrical_notes.md
├── README.md
└── testing.md
├── flightConfig.c
├── flightConfigWifi.c
├── flightLogic
├── DummygetDriverData.py
├── DummymainFlightLogic.py
├── DummymissionModes
│ ├── README.md
│ ├── antennaDeploy.py
│ ├── boomDeploy.py
│ ├── postBoomDeploy.py
│ ├── preBoomDeploy.py
│ └── safe.py
├── README.md
├── flightLogicPsuedo.md
├── getDriverData.py
├── mainFlightLogic.py
├── missionModes
│ ├── README.md
│ ├── antennaDeploy.py
│ ├── boomDeploy.py
│ ├── heartBeat.py
│ ├── postBoomDeploy.py
│ ├── preBoomDeploy.py
│ └── transmitting.py
├── postBoomTime.txt
└── saveTofiles.py
├── install.c
├── lastBase.txt
├── log.txt
├── mycron
├── protectionProticol
└── fileProtection.py
├── requirements.txt
├── setNewTXWindow.c
├── startup.c
├── tests
├── README.md
├── __init__.py
├── misc
│ └── boomDeployTest.py
├── postBoomTime.txt
├── quickTransmitTest.c
├── radio
│ ├── 126bytefile.txt
│ ├── 127bytefile.txt
│ ├── 128bytefile.txt
│ └── pipeMode.sh
├── resetFlightLogic.sh
├── testADC.py
├── testAccel.py
├── testAllDrivers.py
├── testAntennaDoor.py
├── testBackupAntennaDeployer.py
├── testBoomDeployer.py
├── testCamera.py
├── testCpuTemperature.py
├── testDummyFlightLogic.py
├── testEPSRaw.py
├── testEPSRead.py
├── testFlightLogic.py
├── testHeartbeat.py
├── testInterrupt.py
├── testMag.py
├── testMainFlightLogic.py
├── testMainFlightLogicNOTSub.py
├── testPrimaryAntennaDeploy.py
├── testRTC.py
├── testReceiving.py
├── testSPI.py
├── testSolarPanelTemp.py
├── testSolarPower.py
├── testSunSensor.py
├── testTransceiverConfig.py
├── testTransmissions.py
├── testUV.py
├── testWatchdogCommands.py
└── unit_testing_example.py
├── upDateCode.c
└── watchdog
├── Low-Power-master.zip
├── README.md
├── arduino_upload_notes.md
├── arduino_watchdog_v5
├── README.md
└── arduino_watchdog_v7.2.ino
└── watchdog_v8.1
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 | .pytest_cache/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 | db.sqlite3
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 |
106 | # pycharm
107 | .idea/
108 |
109 | # vim
110 | *.swp
111 |
112 | # images
113 | *.jpg
114 |
115 | # Doxygen
116 | doxygen
117 |
118 | # Executables
119 | a.out
120 |
121 |
122 | ### GASPACS FILES ###
123 |
124 | # Flight logic data files
125 | flightLogic/data/*
126 | flightLogic/bootRecords
127 | flightLogic/backupBootRecords
128 | ~/flightLogicData/*
129 | ~/TXISRData/*
130 | flightLogic/TXISR/TXServiceCode/TXService.run
131 | install.exe
132 | upDataCode.exe
133 | flightConfig.exe
134 | flightConfigWifi.exe
135 |
136 | # TXISR Data Files
137 | TXISR/data/*
138 | # Picture Files
139 | Pictures/*
140 |
--------------------------------------------------------------------------------
/AuthorsSignature.txt:
--------------------------------------------------------------------------------
1 | Team Lead/Desinger: Shawn Jones
2 | Project Coordinator: Jack Danos
3 | Contributors: Alex Allgrunn
4 | Carter Page
5 | Logan Freeman
6 | Josh Hessing
7 | Tom Christensen
8 | Evan Anderson
9 | Daniel Combs Jr.
10 | Ben Lewis
11 | Donna Metcalf
12 |
--------------------------------------------------------------------------------
/Drivers/Accelerometer/Accelerometer.py:
--------------------------------------------------------------------------------
1 | #Make sure the following libraries are installed:
2 | # sudo pip3 install RPI.GPIO
3 | # sudo pip3 install adafruit-blinka
4 | # sudo pip3 install adafruit-circuitpython-lsm303-accel
5 | # For LSM303AGR:
6 | # sudo pip3 install adafruit-circuitpython-lis2mdl
7 | # For LSM303DLH:
8 | # sudo pip3 install adafruit-circuitpython-lsm303dlh-mag
9 |
10 | from Drivers.Driver import Driver
11 | import board
12 | import busio
13 | import adafruit_lsm303_accel
14 |
15 | """
16 | Pulls data from the Accelerometer
17 | """
18 |
19 | class Accelerometer(Driver):
20 | #Set up I2C link
21 | i2c = busio.I2C(board.SCL, board.SDA)
22 |
23 | def __init__(self):
24 | super().__init__("Accelerometer")
25 |
26 | def read(self):
27 | accel = adafruit_lsm303_accel.LSM303_Accel(self.i2c)
28 | return accel.acceleration
29 |
--------------------------------------------------------------------------------
/Drivers/Accelerometer/README.md:
--------------------------------------------------------------------------------
1 | Accelerometer Driver
2 | --
3 | Location: ../Drivers/Accelerometer/Accelerometer.py
4 |
5 | Functionality:
6 | The Accelerometer Driver sets up an I2C communication route to the Accelerometer. After this connection is made it can gather the acceleration data by calling: Accelerometer.Accelerometer.read()
7 |
8 |
--------------------------------------------------------------------------------
/Drivers/Accelerometer/__init__.py:
--------------------------------------------------------------------------------
1 | from .Accelerometer import *
2 |
--------------------------------------------------------------------------------
/Drivers/Driver.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 |
3 | class Driver:
4 | """
5 | Abstract class that defines a device driver.
6 | """
7 | def __init__(self, name, delay=1, initial_delay=0):
8 | """
9 | Initializes a Driver object.
10 | 1. self.name is a string identifying the object.
11 | 2. self.delay is an integer defining the refresh frequency of the driver.
12 | 3. self.initial_delay defines how long the driver should wait before beginning to refresh
13 | """
14 | self.name = name
15 | self.delay = delay
16 | self.initial_delay = initial_delay
17 |
18 | def read(self):
19 | """
20 | Abstract method that defines the behavior of the device driver.
21 | The child class implements this for communicating with specific hardware components.
22 | """
23 | pass
24 |
25 | async def run(self, context, lock):
26 | """
27 | Asynchronous method that controls how often driver reads values from the hardware component.
28 | Waits self.initial_delay time before beginning to read from the hardware.
29 | 1. context is the mission mode defined in the mission_modes module and passed to the driver by main.
30 | 2. lock controls asynchronous execution to avoid race conditions.
31 | """
32 | if self.initial_delay > 0:
33 | await asyncio.sleep(self.initial_delay)
34 | self.initial_delay = 0
35 |
36 | while True:
37 | reading = self.read()
38 | async with lock:
39 | context[self.name] = reading
40 | await asyncio.sleep(self.delay)
41 |
--------------------------------------------------------------------------------
/Drivers/Magnetometer/Magnetometer.py:
--------------------------------------------------------------------------------
1 | #Make sure the following libraries are installed:
2 | # sudo pip3 install RPI.GPIO
3 | # sudo pip3 install adafruit-blinka
4 | # sudo pip3 install adafruit-circuitpython-lsm303-accel
5 | # For LSM303AGR:
6 | # sudo pip3 install adafruit-circuitpython-lis2mdl
7 | # For LSM303DLH:
8 | # sudo pip3 install adafruit-circuitpython-lsm303dlh-mag
9 | """
10 | Pulls data from the magnetometer
11 | """
12 |
13 | from Drivers.Driver import Driver
14 | import board
15 | import busio
16 | #for LSM303AGR
17 | import adafruit_lis2mdl
18 | #for LSM303DLH
19 | #import adafruit_lsm303dlh_mag
20 |
21 | class Magnetometer(Driver):
22 | #Set up I2C link
23 | i2c = busio.I2C(board.SCL, board.SDA)
24 |
25 | def __init__(self):
26 | super().__init__("Magnetometer")
27 |
28 | def read(self) :
29 | #Set up link to magnetometer
30 | #for LSM303AGR
31 | mag = adafruit_lis2mdl.LIS2MDL(self.i2c)
32 | #for LSM303DLH
33 | #mag = adafruit_lsm303dlh_mag.LSM303DLH_Mag(self.i2c)
34 | return mag.magnetic
35 |
--------------------------------------------------------------------------------
/Drivers/Magnetometer/README.md:
--------------------------------------------------------------------------------
1 | Magnetometer Driver:
2 | --
3 | Location: ../Drivers/Magnetometer/Magnetometer.py
4 |
5 | Functionality:
6 | The Magnetometer Driver establishes an I2C connection with the Magnetometer. Once that connection is made the driver will then collect information concerning the direction, strength, or relative change of magnetic fields in relation to the satellite that have been gathered by the Magnetometer. To gain access to this information call Magnetometer.Magnetometer.read()
7 |
--------------------------------------------------------------------------------
/Drivers/Magnetometer/__init__.py:
--------------------------------------------------------------------------------
1 | from .Magnetometer import *
2 |
--------------------------------------------------------------------------------
/Drivers/README.md:
--------------------------------------------------------------------------------
1 | GASPACS Software: The Drivers
2 | ===
3 | Opening Note:
4 | --
5 | The purpose of the Drivers is to communicate between the Pi and the various sensors and components of the GASPACS mission.
6 |
7 | The following drivers return sensor data:
8 | - Accelerometer
9 | - Magnetometer
10 | - UV
11 | - adc
12 | - antennaDoor
13 | - cpuTemperature
14 | - rtc
15 | - solarPanelTemp
16 | - sunSensors
17 |
18 | The following drivers are designed to configure, command, or control components:
19 | - backupAntennaDeployer
20 | - boomDeployer
21 |
22 | The following drivers both configure components and return their values.
23 | - camera
24 | - transceiverConfig
25 | - eps
26 |
27 | All these drivers inherit from a single file called Driver.py. Their relationship with this file gives them the ability to run asynchronously as well as making the code more organized and easier to handle. For more documentation on each driver, there is a readme located in each Driver folder.
28 |
29 |
--------------------------------------------------------------------------------
/Drivers/UV/README.md:
--------------------------------------------------------------------------------
1 | UV Driver:
2 | --
3 | Location: ../Drivers/UV/UVDriver.py
4 |
5 | Functionality:
6 | The UV Driver inherits functionality from the ADC Driver, much like the Sun Sensor Driver. It reads from a specific channel through the SPi connection created in the ADC Driver, and returns a single value. To get the data returned by this driver call UVDriver.UVDriver.read()
7 |
8 |
9 | # UV Sensor Documentation
10 | ## Requirements
11 | The the value of uv power hitting the uv sensor and return the power in mW/cm^2
12 | ## Design
13 | > MCP3008 ADC
14 | >> * The adc driver for the MCP3008 ADC returns a list with values in volts
15 | >> * The uv sensor driver calls a method from the adc class, and gets the value in the uv sensor spot
16 | >> * Using the formula given by the graphs on the GUVA-S12D uv sensor datasheet( uvPower = voltage * 1000 / 485.9), plug in the value returned by the adc and return uvPower as a float
17 |
18 | > AD7998 ADC
19 | >> As of right now, the AD7998 ADC driver has not been written.
20 | Reading the datasheet tells us that the ADC will return a 12bit binary number over the I2C interface.
21 | As of now, assuming that the adc driver method call for the AD7998 adc returns a 12bit binary number, the design is as follows.
22 | >> * Using the VOLTAGE_STEP constant, which is defined as Vref_in / 4096
23 | >> * 4096 come from the maximum decimal value able to be represented from 12bit binary
24 | >> * The 12 bit binary number returned from the adc will be multiplied with the VOLTAGE_STEP constant to get the voltage
25 | >> * This voltage is then plugged into the previous equation stated, uvPower = voltage * 1000 / 485.9
26 | ## Implementation
27 | * import the adc class from the adc driver
28 | * create an adc object
29 | * call the read method specifying which register to return
30 | * take the return value and depending on which adc it is dealing with, follow the steps outlined above
31 | ## Testing
32 | > Testing with the MCP3008 ADC
33 | >> * Running the ADC code seperatley gives a list of register values
34 | >> * Running the uv sensor code returns the same value as the register in the 4th position
35 |
36 | > Testing with the AD7998
--------------------------------------------------------------------------------
/Drivers/UV/UVDriver.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | from Drivers.adc import ADC_Driver
3 |
4 | class UVDriver(Driver):
5 | """
6 | This class calls the ADC driver and asks for data from the UV channel
7 | """
8 | adc = ADC_Driver.ADC()
9 | uv_channel = 1 #The channel on the ADC that th UV sensor is connected to
10 |
11 | def __init__(self):
12 | super().__init__("UVDriver")
13 |
14 | def read(self):
15 | """
16 | This function calls the read function of the ADC with the channel for the uv sensor
17 | """
18 | return self.adc.read(self.uv_channel)
19 |
--------------------------------------------------------------------------------
/Drivers/UV/__init__.py:
--------------------------------------------------------------------------------
1 | from .UVDriver import *
2 |
--------------------------------------------------------------------------------
/Drivers/__init__.py:
--------------------------------------------------------------------------------
1 | from .Driver import *
2 | from .Magnetometer import *
3 | from .Accelerometer import *
4 | from .camera import *
5 | from .cpuTemperature import *
6 | from .adc import *
7 | from .UV import *
8 | from .rtc import *
9 | from .eps import *
10 | from .antennaDoor import *
11 | from .backupAntennaDeployer import *
12 | from .boomDeployer import *
13 | from .solarPanelTemp import *
14 | from .sunSensors import *
15 | from .transceiverConfig import *
16 |
17 |
--------------------------------------------------------------------------------
/Drivers/adc/ADC_Driver.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | import spidev
3 | import RPi.GPIO as GPIO
4 | from time import sleep
5 |
6 | class ADC(Driver):
7 | """
8 | This class interfaces with the ADC to read the voltage on a specified channel
9 | """
10 | # Chip Select Pin. This is BOARD Pin 22, which is GPIO 25
11 | csPin = 25
12 |
13 | spi_ch = 0
14 |
15 | spi = spidev.SpiDev()
16 | # spi.open(bus, device)
17 | spi.open(0, spi_ch)
18 | # disable spidev's chip select. we need to manage this manually
19 | spi.no_cs = True
20 | spi.max_speed_hz = 1000
21 | # cs = chip select
22 |
23 | # Sleep after chip select delay
24 | csDelay = 0.01
25 |
26 | def __init__ (self):
27 | # these are the pins for miso, mosi, cs, clk.
28 | # these are where the board is hooked up
29 | super().__init__("ADC")
30 |
31 |
32 | GPIO.setmode(GPIO.BCM)
33 | GPIO.setup(self.csPin, GPIO.OUT, initial=GPIO.HIGH)
34 |
35 | print("Initializing ADC")
36 |
37 | def read(self, channel):
38 | """
39 | Sends a read command with a specified channel and then returns the reply from the ADC
40 | """
41 | # Start the read with both clock and chip select low
42 | GPIO.output(self.csPin, GPIO.LOW)
43 | sleep(self.csDelay)
44 | msg = (channel << 3)
45 | msg = [msg, 0b00000000]
46 |
47 | # print("Channel: ", channel)
48 | # print("Sent message: ", bin(msg[0]), bin(msg[1]))
49 | # print("Spi channel: ", self.spi)
50 |
51 | reply = self.spi.xfer2(msg)
52 | value = (reply[1] + (reply[0] * 256))*(3.3/4096)
53 | # set the clock and chip select to high to end message
54 | GPIO.output(self.csPin, GPIO.HIGH)
55 | sleep(self.csDelay)
56 | return value
57 |
58 | # convert the reply from 12 bits stored in two bytes to a voltage
59 | # 12 bit data: byte 0 [0 0 0 0 MSB d11 d10 d9] byte 1 [d8 d7 d6 d5 d4 d3 d2 LSB]
60 | # Each LSB represents 3.3/4096 volts
61 |
62 | def close(self):
63 | self.spi.close()
64 |
--------------------------------------------------------------------------------
/Drivers/adc/ADC_Driver.py.save:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | import spidev
3 | import RPi.GPIO as GPIO
4 |
5 |
6 | class ADC(Driver):
7 | """
8 | This class interfaces with the ADC to read the voltage on a specified channel
9 | """
10 | csPin = 22
11 |
12 | spi_ch = 0
13 | spi = spidev.SpiDev()
14 | # spi.open(bus, device)
15 | spi.open(0, spi_ch)
16 | # disable spidev's chip select. we need to manage this manually
17 | spi.no_cs = True
18 | spi.max_speed_hz = 1000
19 | # cs = chip select
20 |
21 | def __init__ (self):
22 | # these are the pins for miso, mosi, cs, clk.
23 | # these are were the board is hooked up
24 | super().__init__("ADC")
25 |
26 |
27 | GPIO.setmode(GPIO.BOARD)
28 | GPIO.setup(self.csPin, GPIO.OUT, initial=GPIO.HIGH)
29 |
30 | def read(self, channel):
31 | """
32 | Sends a read command with a specified channel and then returns the reply from the ADC
33 | """
34 | # Start the read with both clock and chip select low
35 | GPIO.output(self.csPin, GPIO.LOW)
36 |
37 | # the following creates a message to send to the slave
38 | msg = (channel << 3)
39 | msg = [msg, 0b00000000]
40 | # the followin
41 | replyList = list()
42 | replySet = list()
43 | for i in range(0, 6):
44 | replyList[i] = self.spi.xfer2(msg)
45 |
46 | # set the clock and chip select to high to end message
47 | GPIO.output(self.csPin, GPIO.HIGH)
48 |
49 | # convert the reply from 12 bits stored in two bytes to a voltage
50 | # 12 bit data: byte 0 [0 0 0 0 MSB d11 d10 d9] byte 1 [d8 d7 d6 d5 d4 d3 d2 LSB]
51 | # Each LSB represents 3.3/4096 volts
52 | return(reply[1] + (reply[0] * 256))*(3.3/4096)
53 |
--------------------------------------------------------------------------------
/Drivers/adc/README.md:
--------------------------------------------------------------------------------
1 | ADC Driver:
2 | --
3 | Location: ../Drivers/adc/ADC_Driver.py
4 |
5 | Functionality:
6 | The ADC Driver sets up a SPi communication route with the ADC. Through the ADC it can then get data from our five Sun Sensors and one UV sensor. After this connection is made one can gather data from these sensors by calling: ADC_Driver.ADC.read(channel).
7 | (see Sun Sensor Driver and UV Driver)
8 |
--------------------------------------------------------------------------------
/Drivers/adc/__init__.py:
--------------------------------------------------------------------------------
1 | from .ADC_Driver import *
2 |
--------------------------------------------------------------------------------
/Drivers/antennaDoor/AntennaDoor.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | import smbus
3 | from time import sleep
4 |
5 | class AntennaDoor(Driver):
6 |
7 | def __init__(self):
8 | super().__init__("AntennaDoor") #Calls parent constructor
9 | """
10 | Sets up I2C bus for communication
11 | """
12 | self.DEVICE_BUS = 1
13 | self.DEVICE_ADDR = 0x33
14 | self.bus = smbus.SMBus(self.DEVICE_BUS)
15 | self.doorStatus = -1
16 | sleep(1)
17 |
18 | def readDoorStatus(self):
19 | """
20 | Reads the I2C response from the antenna.
21 | The first 4 bits of the first byte represent the 4 antenna doors. 0 is not deployed, 1 is deployed.
22 | Uses bitwise "or" to check if all 4 antenna doors are deployed. If yes, set deployed True. If any
23 | doors are undeployed, set deployed False.
24 | """
25 | # This command returns one byte from the antenna. Check the antenna manual for an explanation of the bytes.
26 | self.doorStatus = self.bus.read_byte(self.DEVICE_ADDR)
27 | doorBits = self.doorStatus
28 | print("Decimal value from antenna: ", doorBits)
29 | deployed = False
30 | # decimal representation of 00001111
31 | bitmask = 15
32 | bitwise = (doorBits | bitmask)
33 | print("Bitwise result: ", bitwise)
34 | if(bitwise == 255):
35 | deployed = True
36 | else :
37 | deployed = False
38 | print("deployed", deployed)
39 | return deployed
40 |
41 | #this is the command to deploy the anntenna
42 | def deployAntennaMain(self):
43 | try :
44 | deployed = self.readDoorStatus()
45 | except :
46 | self.doorStatus = -1
47 |
48 | print("\t____Deploying the Antenna____")
49 | if(self.doorStatus == 0):
50 | try :
51 | self.bus.write_byte(self.DEVICE_ADDR,0x1F)
52 | except :
53 | print("Failed to run 1")
54 | elif (not deployed):
55 | try :
56 | self.bus.write_byte(self.DEVICE_ADDR,0x2F)
57 | except :
58 | print("Failed to run 2")
59 | print("\t____Deployed the Antenna____")
60 |
--------------------------------------------------------------------------------
/Drivers/antennaDoor/README.md:
--------------------------------------------------------------------------------
1 | Antenna Door Driver:
2 | --
3 | Location: ../Drivers/antennaDoor/AntennaDoor.py
4 |
5 | Functionality:
6 | The Antenna Door Driver sets up an I2C communication route with the Antenna Doors. After this connection is established the Driver is then able to determine whether the doors have or have not opened in order to confirm antenna deployment. The data collected may be acquired by calling: AntennaDoor.AntennaDoor.readDoorStatus()
7 |
--------------------------------------------------------------------------------
/Drivers/antennaDoor/__init__.py:
--------------------------------------------------------------------------------
1 | from .AntennaDoor import *
2 |
--------------------------------------------------------------------------------
/Drivers/backupAntennaDeployer/BackupAntennaDeployer.py:
--------------------------------------------------------------------------------
1 | from asyncio.tasks import wait
2 | from Drivers.Driver import Driver
3 | import asyncio
4 | import time
5 | import RPi.GPIO as GPIO
6 |
7 | class BackupAntennaDeployer(Driver):
8 | def __init__(self):
9 | """
10 | Calls parent constructor, sets burn time, sets GPIO pins
11 | """
12 | super().__init__("BackupAntennaDeployer")
13 | # Initial values
14 | self.burnTime = 10
15 |
16 | # Set up the GPIO pins for use
17 | GPIO.setmode(GPIO.BCM)
18 |
19 | # Setup GPIO pins
20 | # Primary Backup Pin: BOARD 33 which is GPIO 13
21 | self.primaryPin = 13
22 | # Secondary Backup Pin: BOARD 32 which is GPIO 12
23 | self.secondaryPin = 12
24 | GPIO.setup(self.primaryPin,GPIO.OUT, initial=GPIO.LOW)
25 | GPIO.setup(self.secondaryPin,GPIO.OUT, initial=GPIO.LOW)
26 |
27 | async def deployPrimary(self):
28 | """
29 | Set primary deploy pin to high for a specified time, triggering the
30 | backup antenna burn.
31 | """
32 | #Burn primary backup, then turn off and wait
33 | self.PWMPRIMARY = GPIO.PWM(self.primaryPin, 500)
34 | self.PWMPRIMARY.start(0)
35 | try:
36 | while True:
37 |
38 | for dc in range(0, 101, 5):
39 | self.PWMPRIMARY.ChangeDutyCycle(dc)
40 | time.sleep(0.05)
41 | await asyncio.sleep(self.burnTime)
42 |
43 | GPIO.output(self.primaryPin, GPIO.LOW)
44 | self.PWMPRIMARY.stop()
45 |
46 | break
47 | except:
48 | print("Failed to use primary antenna deploy")
49 |
50 | # GPIO.output(self.primaryPin, GPIO.HIGH)
51 | # time.sleep()
52 | # await asyncio.sleep(self.burnTime)
53 | # GPIO.output(self.primaryPin, GPIO.LOW)
54 |
55 | async def deploySecondary(self):
56 | """
57 | Set secondary deploy pin to high for a specified time, triggering the
58 | backup antenna burn.
59 | """
60 |
61 | self.PWMSECONDARY = GPIO.PWM(self.secondaryPin, 500)
62 | self.PWMSECONDARY.start(0)
63 | try:
64 | while True:
65 |
66 | for dc in range(0, 101, 5):
67 | self.PWMSECONDARY.ChangeDutyCycle(dc)
68 | time.sleep(0.05)
69 | await asyncio.sleep(self.burnTime)
70 |
71 | GPIO.output(self.secondaryPin, GPIO.LOW)
72 | self.PWMSECONDARY.stop()
73 |
74 | break
75 | except:
76 | print("Failed to use secondary antenna deploy")
77 |
78 |
79 | # GPIO.output(self.secondaryPin, GPIO.HIGH)
80 | # await asyncio.sleep(self.burnTime)
81 | # GPIO.output(self.secondaryPin, GPIO.LOW)
82 |
83 | def read(self):
84 | """
85 | Left undefined as no data is collected by this component
86 | """
87 | pass
88 |
--------------------------------------------------------------------------------
/Drivers/backupAntennaDeployer/README.md:
--------------------------------------------------------------------------------
1 | Backup Antenna Deployment Driver:
2 | --
3 | Location: ../Drivers/backupAntennaDeployerBackupAntennaDeployer.py
4 |
5 | Functionality:
6 | The Backup Antenna Deployment Driver sets up a connection to various pins. These pins are connected to capacitors that power the Antenna Doors. Through this connection the software will set the capacitors to turn on and open the Antenna Doors. This code will be used as a fail safe if the premade software to open the Antenna Doors is unsuccessful. The Antenna can be manually deployed by calling: BackupAntennaDeployer.BackupAntennaDeployer.deploy().
7 |
--------------------------------------------------------------------------------
/Drivers/backupAntennaDeployer/__init__.py:
--------------------------------------------------------------------------------
1 | from .BackupAntennaDeployer import *
2 |
--------------------------------------------------------------------------------
/Drivers/boomDeployer/BoomDeployer.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | from Drivers import EPS
3 | import asyncio
4 | import smbus
5 | import RPi.GPIO as GPIO
6 | import time
7 |
8 | class BoomDeployer(Driver):
9 | def __init__(self):
10 | """
11 | Calls parent constructor, Defines initial burn time, time to wait in between burns,
12 | and how many times to burn before giving up. Sets up the GPIO pin for use by the actuate method.
13 | """
14 | #super().__init__("BoomDeployer")
15 | # Initial values
16 | self.burnTimeWC1 = 10
17 | self.burnTimeWC2 = 10
18 | self.waitTime = 10
19 | self.numTimes = 3
20 | #The duty cycles will specify the maximum duty cycle for the PWM for each wire burn
21 | self.dutyCycle1 = 15
22 | self.dutyCycle2 = 15
23 |
24 | # Set up the GPIO pins for use
25 | GPIO.setmode(GPIO.BCM)
26 |
27 | # First Wirecutter
28 | # BOARD 38 is GPIO 20
29 | self.wireCutter1_high1 = 20
30 | # BOARD 36 is GPIO 16
31 | self.wireCutter1_high2 = 16
32 | # BOARD 7 is GPIO 4
33 | self.wireCutter1_low1 = 4
34 |
35 | # Set up Wirecutter 1 pins
36 | GPIO.setup(self.wireCutter1_high1, GPIO.OUT, initial=GPIO.LOW)
37 | GPIO.setup(self.wireCutter1_high2,GPIO.OUT, initial=GPIO.LOW)
38 | GPIO.setup(self.wireCutter1_low1,GPIO.OUT, initial=GPIO.HIGH)
39 | self.PWM1 = GPIO.PWM(self.wireCutter1_high1, 500)
40 |
41 | #Second Wirecutter
42 | # BOARD 37 is GPIO 26
43 | self.wireCutter2_high1 = 26
44 | # BOARD 35 is GPIO 19
45 | self.wireCutter2_high2 = 19
46 | # BOARD 29 is GPIO 5
47 | self.wireCutter2_low1 = 5
48 |
49 | # Set up Wirecutter 2 pins
50 | GPIO.setup(self.wireCutter2_high1, GPIO.OUT, initial=GPIO.LOW)
51 | GPIO.setup(self.wireCutter2_high2,GPIO.OUT, initial=GPIO.LOW)
52 | GPIO.setup(self.wireCutter2_low1,GPIO.OUT, initial=GPIO.HIGH)
53 | self.PWM2 = GPIO.PWM(self.wireCutter2_high1, 500)
54 |
55 | #used to turn on the eps bus
56 | self.Bus = EPS()
57 |
58 | async def deploy(self):
59 | """
60 | Loop a specified number of times, setting the correct GPIO pins to HIGH/LOW to start/stop
61 | the burn. Wait and then repeat with the other wirecutter mechanism.
62 | Note: PWM is used on only one channel.
63 | """
64 | #this is turn onthe raw out put on the bus, this is incase it turns off
65 | try:
66 | self.Bus.enableRaw()
67 | except:
68 | pass
69 |
70 | for num in range(0, self.numTimes):
71 | #Turn on Wire Cutter 1
72 | #GPIO.output(self.wireCutter1_high1, GPIO.HIGH)
73 | GPIO.output(self.wireCutter1_high2, GPIO.HIGH)
74 | GPIO.output(self.wireCutter1_low1, GPIO.LOW)
75 | self.PWM1.start(0)
76 |
77 | for dc in range(0, self.dutyCycle1, 5):
78 | self.PWM1.ChangeDutyCycle(dc)
79 | time.sleep(0.05)
80 |
81 | #Burn for set number of seconds
82 | await asyncio.sleep(self.burnTimeWC1)
83 | self.PWM1.stop()
84 |
85 | #Turn off Wire Cutter 1
86 | GPIO.output(self.wireCutter1_high1, GPIO.LOW)
87 | GPIO.output(self.wireCutter1_high2, GPIO.LOW)
88 | GPIO.output(self.wireCutter1_low1, GPIO.HIGH)
89 | #Wait
90 | await asyncio.sleep(self.waitTime)
91 |
92 | #Turn on Wire Cutter 2
93 | #GPIO.output(self.wireCutter2_high1, GPIO.HIGH)
94 | GPIO.output(self.wireCutter2_high2, GPIO.HIGH)
95 | GPIO.output(self.wireCutter2_low1, GPIO.LOW)
96 | self.PWM2.start(0)
97 |
98 | for dc in range(0, self.dutyCycle2, 5):
99 | self.PWM2.ChangeDutyCycle(dc)
100 | time.sleep(0.05)
101 |
102 | #Burn for set number of seconds
103 | await asyncio.sleep(self.burnTimeWC2)
104 | self.PWM2.stop()
105 |
106 | #Turn off Wire Cutter 2
107 | GPIO.output(self.wireCutter2_high1, GPIO.LOW)
108 | GPIO.output(self.wireCutter2_high2, GPIO.LOW)
109 | GPIO.output(self.wireCutter2_low1, GPIO.HIGH)
110 | #Wait
111 | await asyncio.sleep(self.waitTime)
112 |
113 | print('Loop executed once')
114 |
115 | def read(self):
116 | """
117 | Left undefined as no data is collected by this component
118 | """
119 | pass
120 |
--------------------------------------------------------------------------------
/Drivers/boomDeployer/README.md:
--------------------------------------------------------------------------------
1 | Boom Deployer Driver:
2 | --
3 | Location: ../Drivers/boomDeployer/BoomDeployer.py
4 |
5 | Functionality:
6 | Our AeroBoom is stored in a container held shut by wire. The Boom Deployer Driver will set up a connection to various pins. These pins will be connected to both the wire cutters. After this connection is made the Driver will tell the first wire cutter to turn on for three seconds and then turn off. It will do this twice. Since there is no way for us to tell if the first wire cutter was successful we will then turn on the second wire cutter and run it in the same manner. This ensures that the AeroBoom gets deployed. The AeroBoom gets deployed by calling: boomDeployer.BoomDeployer.deploy()
7 |
--------------------------------------------------------------------------------
/Drivers/boomDeployer/__init__.py:
--------------------------------------------------------------------------------
1 | from .BoomDeployer import *
2 |
--------------------------------------------------------------------------------
/Drivers/camera/.Camera.py.swo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmallSatGasTeam/CubeWorks/d615cb27b897289bb3a5305b91c62256718685a1/Drivers/camera/.Camera.py.swo
--------------------------------------------------------------------------------
/Drivers/camera/Camera.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | from time import sleep
3 | from picamera import PiCamera
4 | from os import listdir
5 | #from os.path import expanduser
6 | from os import makedirs
7 | from os import system
8 | from pathlib import Path
9 | import subprocess
10 |
11 | class Camera(Driver):
12 | def __init__(self):
13 | """
14 | Takes a picture
15 | """
16 | #super().__init__("Camera")
17 |
18 | self.highRes = (3280, 2464)
19 | self.lowRes = (640, 480)
20 | #self.pictureDirectoryPath = expanduser('~/Pictures')
21 | #self.pictureDirectoryPath = str(Path(__file__).parent / "../../Pictures")
22 | self.pictureDirectoryPath = "/home/pi/flightLogicData/Pictures"
23 | self.pictureNumber = 0
24 | try:
25 | self.__cam = PiCamera()
26 | print("made cam object")
27 | except:
28 | print("failed to make cam object")
29 | self.__cam = None
30 |
31 | def read(self):
32 | pass
33 |
34 | def takePicture(self):
35 | """
36 | Takes the picture
37 | """
38 | try :
39 | #the way to count folders in directory is len(os.listdir(path of directory to count in))
40 | #you have to import OS
41 | #This also counts files in the total, but with the file structure we came up with this shouldn't be a problem
42 | makedirs(self.pictureDirectoryPath, exist_ok=True)
43 | self.pictureNumber = len(listdir(self.pictureDirectoryPath))
44 | print("Picture number:", self.pictureNumber)
45 | #count number of folders in directory, add 1 for current pic
46 |
47 | self.__cam.resolution = self.lowRes
48 | print("Resolution:", self.__cam.resolution)
49 | sleep(2)
50 | makedirs(self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/LowRes", exist_ok=True)
51 | print("Made LowDir")
52 | self.__cam.capture(self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/LowRes/LowResOriginal"+str(self.pictureNumber)+".jpg")
53 | print("Captured Low")
54 | self.__cam.resolution = self.highRes
55 | print("Res:", self.__cam.resolution)
56 | makedirs(self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/HighRes", exist_ok=True)
57 | print("Made HighDir")
58 | self.__cam.capture(self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/HighRes/HighResOriginal"+str(self.pictureNumber)+".jpg")
59 | print("Took pictures")
60 | return self.pictureNumber
61 | except:
62 | #return neg 1 if no picture was taken
63 | print("Failed to take a picture")
64 | return -1
65 |
66 | def compressLowResToFiles(self, pictureNumber):
67 | """
68 | Compresses the Low Res to files. Compresses with SSDV, converts from Hex to ASCII with xxd, splits into 128 byte files.
69 | """
70 | self.pictureNumber = pictureNumber
71 | #Set up paths for low res picture and creates the packets directory
72 | lowResOriginalPath = self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/LowRes/LowResOriginal"+str(self.pictureNumber)+".jpg"
73 | lowResSSDVPath = self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/LowRes/LowResOriginal"+str(self.pictureNumber)+".bin"
74 | print("before compress")
75 | ssdv_lowRes_picture = system('sudo /home/pi/ssdv/ssdv -e -c N7GAS -i ' + str(pictureNumber) + " " + str(lowResOriginalPath) + ' ' + str(lowResSSDVPath))
76 | print("After compress")
77 | def compressHighResToFiles(self,pictureNumber):
78 | """
79 | Compresses the Low Res to files. Compresses with SSDV, converts from Hex to ASCII with xxd, splits into 128 byte files.
80 | """
81 | self.pictureNumber = int(pictureNumber)
82 |
83 | #Set up paths for high res picture and creates the packets directory
84 | highResOriginalPath = self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/HighRes/HighResOriginal"+str(self.pictureNumber)+".jpg"
85 | highResSSDVPath = self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/HighRes/HighResOriginal"+str(self.pictureNumber)+".bin"
86 |
87 | ssdv_highRes_picture = system('sudo /home/pi/ssdv/ssdv -e -c N7GAS -i ' + str(pictureNumber + 100) + " " + str(highResOriginalPath) + ' ' + str(highResSSDVPath))
88 |
--------------------------------------------------------------------------------
/Drivers/camera/README.md:
--------------------------------------------------------------------------------
1 | Camera Driver:
2 | --
3 | Location: ../Drivers/camera/Camera.py
4 |
5 | Functionality:
6 | The Camera Driver will take a photo and store the photo in a specific file with a very specific file path. This Driver will also give us the ability to compress the file into two different resolutions. In order to take a picture call Camera.Camera.takePicture(). In order to store the photo in either of the two resolutions call Camera.Camera.compressLowResToFiles(pictureNumber)
7 | Camera.Camera.compressHighResToFiles(pictureNumber)
8 |
--------------------------------------------------------------------------------
/Drivers/camera/__init__.py:
--------------------------------------------------------------------------------
1 | from .Camera import *
2 |
--------------------------------------------------------------------------------
/Drivers/cpuTemperature/CpuTemperature.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | from os import popen
3 |
4 | class CpuTemperature(Driver):
5 | def __init__(self):
6 | super().__init__(CpuTemperature)
7 |
8 | def read(self):
9 | temp = popen("vcgencmd measure_temp").readline()
10 | temp = float(temp.replace("temp="," ").replace("\'C"," ").replace("\n"," "))
11 | return temp
12 |
13 |
--------------------------------------------------------------------------------
/Drivers/cpuTemperature/README.md:
--------------------------------------------------------------------------------
1 | CPU Temperature Driver:
2 | --
3 | Location: ../Drivers/cpuTemperature/CpuTemperature.py
4 |
5 | Functionality:
6 | The CPU Temperature Driver reads in the temperature of the CPU from the temperature sensor connected to the CPU. To collect the data gathered call CpuTemperature.CpuTemperature.read().
7 |
--------------------------------------------------------------------------------
/Drivers/cpuTemperature/__init__.py:
--------------------------------------------------------------------------------
1 | from .CpuTemperature import *
2 |
--------------------------------------------------------------------------------
/Drivers/eps/EPS.py:
--------------------------------------------------------------------------------
1 | #EPS Driver, not tested against hardware yet
2 | from Drivers.Driver import Driver
3 | import smbus
4 | import RPi.GPIO as GPIO
5 | """
6 | Pulls battery data and solar panel data from the EPS
7 | """
8 |
9 | class EPS(Driver):
10 | #this sets up i2c commincation.
11 | def __init__(self):
12 | super().__init__("EPS") #Calls parent constructor
13 |
14 | #Setup I2C bus for communication
15 | self.DEVICE_BUS = 1
16 | self.DEVICE_ADDR = 0x18
17 | self.RegisterADR = 0x00
18 | self.bus = smbus.SMBus(self.DEVICE_BUS)
19 |
20 | def enableRaw(self):
21 | #This method enables RAW battery output from the EPS
22 | #The command is 3 bytes device address left-shifted by one bit, the command, and the state
23 | self.bus.write_byte_data(self.DEVICE_ADDR, 0x01, 0x03)
24 |
25 | def disableRaw(self):
26 | #This method disables RAW battery output from the EPS
27 | #The command is 3 bytes device address left-shifted by one bit, the command, and the state
28 | self.bus.write_byte_data(self.DEVICE_ADDR, 0x01, 0x02)
29 |
30 | def enableUHF(self):
31 | #This method sends the command to the EPS to enable UHF Transmission, and sets the corresponding GPIO Pin high on the Pi
32 | self.bus.write_byte_data(self.DEVICE_ADDR, 0x0E, 0x03)
33 | GPIO.setmode(GPIO.BOARD)
34 | GPIO.setup(18, GPIO.OUT, initial = GPIO.HIGH)
35 | GPIO.output(18, GPIO.HIGH)
36 |
37 | def read(self):
38 | #returns nothing since there are so many different things we could read, use other methods instead
39 | pass
40 |
41 | def startRead(self, command):
42 | value = self.bus.read_i2c_block_data(self.DEVICE_ADDR, command, 2)
43 | return (value[0] * 256) + value [1]
44 |
45 | #Getter calls read method, returns converted data - all values are converted 12 bits, even though 2 bytes returned
46 | def getMCUTemp(self):
47 | #super().__init__("ESP") <-- Shawn had this in here, I don't understand why it's here so I'm commenting it out and leaving it out of other ones
48 | temp = self.startRead(18)
49 | temp = ((temp *0.0006103516) - 0.986)/0.00355
50 | return temp #done with multiple lines because of complicated conversion
51 | def getCell1Temp(self):
52 | return self.startRead(19)*0.00390625 #Reads data of specified type, sets up conversion factor to 'C
53 |
54 | def getCell2Temp(self):
55 | return self.startRead(20)*0.00390625 #Reads data of specified type, sets up conversion factor to 'C
56 |
57 | def getBusVoltage(self):
58 | return self.startRead(1) * 0.0023394775 #Reads data of specified type, sets up conversion factor to V
59 |
60 | def getBusCurrent(self):
61 | return self.startRead(2) * 0.0030517578 #Reads data of specified type, sets up conversion factor to A
62 |
63 | def getBCRVoltage(self):
64 | return self.startRead(3) * 0.0023394775 #Reads data of specified type, sets up conversion factor to V
65 |
66 | def getBCRCurrent(self):
67 | return self.startRead(4) * 0.0015258789 #Reads data of specified type, sets up conversion factor to A
68 |
69 | def get3V3Current(self):
70 | return self.startRead(14) * 0.0020345052 #Reads data of specified type, sets up conversion factor to A
71 |
72 | def get5VCurrent(self):
73 | return self.startRead(15) * 0.0020345052 #Reads data of specified type, sets up conversion factor to A
74 |
75 | def getSPXVoltage(self):
76 | return self.startRead(5) * 0.0024414063 #Reads data of specified type, sets up conversion factor to V
77 |
78 | def getSPXMinusCurrent(self):
79 | return self.startRead(6) * 0.0006103516 #Reads data of specified type, sets up conversion factor to A
80 |
81 | def getSPXPlusCurrent(self):
82 | return self.startRead(7) * 0.0006103516 #Reads data of specified type, sets up conversion factor to A
83 |
84 | def getSPYVoltage(self):
85 | return self.startRead(8) * 0.0024414063 #Reads data of specified type, sets up conversion factor to V
86 |
87 | def getSPYMinusCurrent(self):
88 | return self.startRead(9) * 0.0006103516 #Reads data of specified type, sets up conversion factor to A
89 |
90 | def getSPYPlusCurrent(self):
91 | return self.startRead(10) * 0.0006103516 #Reads data of specified type, sets up conversion factor to A
92 |
93 | def getSPZVoltage(self):
94 | return self.startRead(11) * 0.0024414063 #Reads data of specified type, sets up conversion factor to V
95 |
96 | def getSPZPlusCurrent(self):
97 | return self.startRead(13) * 0.0006103516
98 |
--------------------------------------------------------------------------------
/Drivers/eps/EPS.py.save:
--------------------------------------------------------------------------------
1 | #EPS Driver, not tested against hardware yet
2 | from Drivers.Driver import Driver
3 | import smbus
4 | import RPi.GPIO as GPIO
5 |
6 | class EPS(Driver):
7 | #this sets up i2c commincation.
8 | def __init__(self):
9 | super().__init__("EPS") #Calls parent constructor
10 |
11 | #Setup I2C bus for communication
12 | self.DEVICE_BUS = 1
13 | self.DEVICE_ADDR = 0x18
14 | self.RegisterADR = 0x00
15 | self.bus = smbus.SMBus(self.DEVICE_BUS)
16 |
17 | def enableRaw(self):
18 | #This method enables RAW battery output from the EPS
19 | #The command is 3 bytes device address left-shifted by one bit, the command, and the state
20 | self.bus.write_byte_data(self.DEVICE_ADDR, 0x01, 0x03)
21 |
22 | def disableRaw(self):
23 | #This method disables RAW battery output from the EPS
24 | #The command is 3 bytes device address left-shifted by one bit, the command, and the state
25 | self.bus.write_byte_data(self.DEVICE_ADDR, 0x01, 0x02)
26 |
27 | def enableUHF(self):
28 | #This method sends the command to the EPS to enable UHF Transmission, and sets the corresponding GPIO Pin high on the Pi
29 | self.bus.write_byte_data(self.DEVICE_ADDR, 0x0E, 0x03)
30 | GPIO.setmode(GPIO.BOARD)
31 | GPIO.setup(18, GPIO.OUT, initial = GPIO.HIGH)
32 | GPIO.output(18, GPIO.HIGH)
33 |
34 | def read(self):
35 | #returns nothing since there are so many different things we could read, use other methods instead
36 | pass
37 |
38 | def startRead(self, command):
39 | value = self.bus.read_i2c_block_data(self.DEVICE_ADDR, command, 2)
40 | return (value[0] * 256) + value [1]
41 |
42 | #Getter calls read method, returns converted data - all values are converted 12 bits, even though 2 bytes returned
43 | def getMCUTemp(self):
44 | #super().__init__("ESP") <-- Shawn had this in here, I don't understand why it's here so I'm commenting it out and leaving it out of other ones
45 | temp = self.startRead(18)
46 | temp = ((temp *0.0006103516) - 0.986)/0.00355
47 | return temp #done with multiple lines because of complicated conversion
48 | def getCell1Temp(self):
49 | return self.startRead(19)*0.00390625 #Reads data of specified type, sets up conversion factor to 'C
50 |
51 | def getCell2Temp(self):
52 | return self.startRead(20)*0.00390625 #Reads data of specified type, sets up conversion factor to 'C
53 |
54 | def getBusVoltage(self):
55 | return self.startRead(1) * 0.0023394775 #Reads data of specified type, sets up conversion factor to V
56 |
57 | def getBusCurrent(self):
58 | return self.startRead(2) * 0.0030517578 #Reads data of specified type, sets up conversion factor to A
59 |
60 | def getBCRVoltage(self):
61 | return self.startRead(3) * 0.0023394775 #Reads data of specified type, sets up conversion factor to V
62 |
63 | def getBCRCurrent(self):
64 | return self.startRead(4) * 0.0015258789 #Reads data of specified type, sets up conversion factor to A
65 |
66 | def get3V3Current(self):
67 | return self.startRead(14) * 0.0020345052 #Reads data of specified type, sets up conversion factor to A
68 |
69 | def get5VCurrent(self):
70 | return self.startRead(15) * 0.0020345052 #Reads data of specified type, sets up conversion factor to A
71 |
72 | def getSPXVoltage(self):
73 | return self.startRead(5) * 0.0024414063 #Reads data of specified type, sets up conversion factor to V
74 |
75 | def getSPXMinusCurrent(self):
76 | return self.startRead(6) * 0.0006103516 #Reads data of specified type, sets up conversion factor to A
77 |
78 | def getSPXPlusCurrent(self):
79 | return self.startRead(7) * 0.0006103516 #Reads data of specified type, sets up conversion factor to A
80 |
81 | def getSPYVoltage(self):
82 | return self.startRead(8) * 0.0024414063 #Reads data of specified type, sets up conversion factor to V
83 |
84 | def getSPYMinusCurrent(self):
85 | return self.startRead(9) * 0.0006103516 #Reads data of specified type, sets up conversion factor to A
86 |
87 | def getSPYPlusCurrent(self):
88 | return self.startRead(10) * 0.0006103516 #Reads data of specified type, sets up conversion factor to A
89 |
90 | def getSPZVoltage(self):
91 | return self.startRead(11) * 0.0024414063 #Reads data of specified type, sets up conversion factor to V
92 |
93 | def getSPZPlusCurrent(self):
94 | return self.startRead(13) * 0.0006103516
95 |
--------------------------------------------------------------------------------
/Drivers/eps/README.md:
--------------------------------------------------------------------------------
1 | EPS Driver:
2 | --
3 | Location: ../Drivers/eps/EPS.py
4 |
5 | Functionality:
6 | The EPS Driver sets up an I2C communication line with the EPS. Once this connection is established the Driver gains access to many different forms of data collected by the EPS. These data forms are:
7 |
8 | Cell temperature
9 |
10 | MCU temperature
11 |
12 | Bus voltage
13 |
14 | Bus current
15 |
16 | BCR Voltage
17 |
18 | BCR current
19 |
20 | 3V3 current
21 |
22 | 5V current
23 |
24 | SPX voltage
25 |
26 | SPX minus current
27 |
28 | SPX plus current
29 |
30 | SPY voltage
31 |
32 | SPY minus current
33 |
34 | SPY plus current
35 |
36 | SPZ current
37 |
--------------------------------------------------------------------------------
/Drivers/eps/__init__.py:
--------------------------------------------------------------------------------
1 | from .EPS import *
2 |
--------------------------------------------------------------------------------
/Drivers/eps/documentation.txt:
--------------------------------------------------------------------------------
1 | #This is the code that handle the i2c communication with the esp
2 | #it ment to be abstract and it handles the sending and reciving commands for you.
3 | #It will just return the desired information
4 |
5 |
--------------------------------------------------------------------------------
/Drivers/rtc/README.md:
--------------------------------------------------------------------------------
1 | RTC Driver:
2 | --
3 | Location: ../Drivers/rtc/rtc_driver.py
4 |
5 | Functionality:
6 | The RTC Driver is perhaps the most simple driver. All it does is read the system clock and return the value found there.This value will be the time in milliseconds since the Unix Epoch To get that value call: rtc_driver.RTC.read().
7 |
--------------------------------------------------------------------------------
/Drivers/rtc/__init__.py:
--------------------------------------------------------------------------------
1 | from .rtc_driver import RTC
2 |
--------------------------------------------------------------------------------
/Drivers/rtc/rtc_driver.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | from datetime import datetime
3 |
4 | class RTC(Driver):
5 | def __init__(self):
6 | """
7 | Initializes a simple driver that only reads the system clock.
8 | """
9 | super().__init__('rtc')
10 |
11 |
12 | def readSeconds(self):
13 | """
14 | Returns the UTC time in miliseconds since the Unix epoch.
15 | The integer returned should be 64 bits in size and be on the order of 1500000000000.
16 | """
17 | return int((datetime.utcnow() - datetime.utcfromtimestamp(0)).total_seconds())
18 |
19 | def readMilliseconds(self):
20 | return int((datetime.utcnow() - datetime.utcfromtimestamp(0)).total_seconds() * 1000)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Drivers/solarPanelTemp/README.md:
--------------------------------------------------------------------------------
1 | Solar Panel Temperature Driver:
2 | --
3 | Location: ../Drivers/solarPanelTemp/solarDriver.py
4 |
5 | Functionality:
6 | The Solar Panel Temperature Driver establishes an SPi connection through the ADC to the two Temperature Sensors connected to the solar panels. After the connection is established this driver can return the temperature of the solar panels. In order to gain access to this data call: solarDriver.TempSensor.read().
7 |
--------------------------------------------------------------------------------
/Drivers/solarPanelTemp/__init__.py:
--------------------------------------------------------------------------------
1 | from .solarDriver import *
2 |
--------------------------------------------------------------------------------
/Drivers/solarPanelTemp/solarDriver.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | import spidev
3 | import RPi.GPIO as GPIO
4 | from time import sleep
5 |
6 | class TempSensor(Driver):
7 | """
8 | This class returns the temperature sensor values for both Endurosat solar panels.
9 | """
10 |
11 | GPIO.setmode(GPIO.BCM)
12 |
13 | spi0 = spidev.SpiDev()
14 | # open SPI to Temp sensor0
15 | spi0.open(0, 1)
16 | spi0.max_speed_hz = 10
17 | spi0.no_cs = True
18 | # BOARD 26 is GPIO 7
19 | spi0_cs = 7
20 | GPIO.setup(spi0_cs, GPIO.OUT, initial=GPIO.HIGH)
21 |
22 | spi1 = spidev.SpiDev()
23 | # open SPI to Temp sensor1
24 | spi1.open(0, 1)
25 | spi1.max_speed_hz = 10
26 | spi1.no_cs = True
27 | # BOARD 24 is GPIO 8
28 | spi1_cs = 8
29 | GPIO.setup(spi1_cs, GPIO.OUT, initial=GPIO.HIGH)
30 |
31 | def __init__ (self):
32 | #super().__init__("ADC")
33 | pass
34 |
35 | def read(self):
36 | """
37 | Sends a read command with a specified channel and then returns the reply from the ADC
38 | """
39 | GPIO.output(self.spi0_cs, GPIO.LOW)
40 | temp0_raw = self.spi0.readbytes(2) #We need just the first 13 bits, as the last three bits of the 16 bit(2 byte) word are not used
41 | GPIO.output(self.spi0_cs, GPIO.HIGH)
42 | #This function converts the two bytes to the temperature value. The data is stored as two bytes where T13 is the most significant bit
43 | #| T13 T12 T11 T10 T9 T8 T7 T6 | | T5 T4 T3 T2 T1 1 1 1 |
44 | temp0 = ((temp0_raw[0] * 32) + (temp0_raw[1] >> 3))* 0.0625
45 |
46 | GPIO.output(self.spi1_cs, GPIO.LOW)
47 | temp1_raw = self.spi1.readbytes(2) #We need just the first 13 bits, as the last three bits of the 16 bit(2 byte) word are not used
48 | GPIO.output(self.spi1_cs, GPIO.HIGH)
49 | #This function converts the two bytes to the temperature value. The data is stored as two bytes where T13 is the most significant bit
50 | #| T13 T12 T11 T10 T9 T8 T7 T6 | | T5 T4 T3 T2 T1 1 1 1 |
51 | temp1 = ((temp1_raw[0] *32) + (temp1_raw[1] >> 3))* 0.0625
52 |
53 | return [temp0, temp1]
54 |
--------------------------------------------------------------------------------
/Drivers/sunSensors/README.md:
--------------------------------------------------------------------------------
1 | Sun Sensor Driver:
2 | --
3 | Location: ../Drivers/sunSensors/sunSensorDriver.py
4 |
5 | Functionality:
6 | The Sun Sensor Driver uses the ADC Drivers already established SPi connection. (See ADC Driver) Using polymorphism this driver reads in from multiple channels of communication via the ADC’s read() function. These channels are connected to the five Sun Sensors. The data collected from these sensors can be accessed by calling: sunSensorDriver.SunSensor.read()
7 |
--------------------------------------------------------------------------------
/Drivers/sunSensors/__init__.py:
--------------------------------------------------------------------------------
1 | from .sunSensorDriver import *
2 |
--------------------------------------------------------------------------------
/Drivers/sunSensors/sunSensorDriver.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | from Drivers.adc import ADC_Driver
3 | from time import sleep
4 |
5 |
6 | class sunSensor(Driver):
7 | """
8 | This class calls the ADC driver and asks for data from the UV channel
9 | """
10 | adc = ADC_Driver.ADC()
11 | adcChannel = [5, 4, 0, 2, 3]
12 | voltageList = []
13 |
14 | def __init__(self):
15 | super().__init__("Sun Sensor")
16 | print("Initializing sun sensor")
17 |
18 | def read(self):
19 | """
20 | This function calls the read function of the ADC for each channel a sun sensor has and return a list of the voltages
21 | """
22 | self.voltageList = []
23 | for i in range(0, 5):
24 | #self.voltageList.append(self.adc.read(self.adcChannel[i]))
25 |
26 | value = self.adc.read(self.adcChannel[i])
27 | #print(value)
28 | self.voltageList.append(value)
29 | #sleep(.1)
30 | return self.voltageList
31 |
32 | def close(self):
33 | self.adc.close()
34 |
--------------------------------------------------------------------------------
/Drivers/transceiverConfig/README.md:
--------------------------------------------------------------------------------
1 | Transceiver Configuration Driver:
2 | --
3 | Location: ../Drivers/tranceiverConfig/TranceiverConfig.py
4 |
5 | Functionality:
6 | The Transceiver Configuration Driver does not handle the transmissions. It does, however, turn on and off the beacon, turn on low power mode, and read the internal temperature sensor. To access these different capabilities see the following commands:
7 | Turn off the beacon: TranceiverConfig.TranceiverConfig.setBeaconOff()
8 | Turn on the beacon: TranceiverConfig.TranceiverConfig.setBeaconOn()
9 | Set the Transceiver to low power mode: TranceiverConfig.TranceiverConfig.setLowPowerMode()
10 | Read in the internal temperature of the transceiver: TranceiverConfig.TranceiverConfig.read()
11 |
--------------------------------------------------------------------------------
/Drivers/transceiverConfig/TransceiverConfig.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | import serial
3 |
4 | class TransceiverConfig(Driver):
5 | def __init__(self):
6 | """
7 | The purpose of this driver is to modify the configuration of the Endurosat UHF Transceiver.
8 | It does not have anything to do with sending packets over the radio.
9 | We need the ability to turn on and off the beacon, turn on low power mode, and read the internal temp sensor.
10 | See Page 25 of the Endurosat UHF Transceiver Type II Manual Rev 1.8 document for the SCW bit description.
11 |
12 | ***Note: ALL ES+ commands need to be changed to reflect the address of the transceiver - currently set to 22***
13 | ***Potentially need to add CRC32 checksum functionality***
14 | """
15 | super().__init__("TransceiverConfig")
16 |
17 | def writeData(self, input):
18 | """
19 | Writes the input to the transceiver over UART
20 | """
21 | ser = serial.Serial('/dev/serial0', 115200)
22 | data = input #Set data to the character 'a', 0x61 or 01100001
23 | ser.write(data) #Send the data
24 | #response = ser.read(128)
25 | ser.close()
26 | #return response
27 |
28 | def setBeaconOn(self):
29 | """
30 | Turns on the morse beacon. Leaves all values at default except beacon.
31 | Binary SCW: 11001101000001
32 | Hex SCW: 3341
33 | """
34 | self.writeData(b'ES+W22003341\r')
35 |
36 | def setBeaconOff(self):
37 | """
38 | Turns off the morse beacon. All values are back to default.
39 | Binary SCW: 11001100000001
40 | Hex SCW: 3301
41 | """
42 | self.writeData(b'ES+W22003301\r')
43 |
44 | def setLowPowerMode(self):
45 | """
46 | Turns on Low Power Mode. Note: Any ESTTC command can be used to bring the transceiver out of low power mode
47 | """
48 | self.writeData(b'ES+W22F4\r')
49 |
50 | def read(self):
51 | """
52 | Returns the temperature from the transceiver internal temp sensor
53 | """
54 | temp = self.writeData(b'ES+R220A\r')
55 | return temp
56 |
--------------------------------------------------------------------------------
/Drivers/transceiverConfig/__init__.py:
--------------------------------------------------------------------------------
1 | from .TransceiverConfig import *
2 |
--------------------------------------------------------------------------------
/DummyDrivers/Accelerometer/Accelerometer.py:
--------------------------------------------------------------------------------
1 | #Make sure the following libraries are installed:
2 | # sudo pip3 install RPI.GPIO
3 | # sudo pip3 install adafruit-blinka
4 | # sudo pip3 install adafruit-circuitpython-lsm303-accel
5 | # For LSM303AGR:
6 | # sudo pip3 install adafruit-circuitpython-lis2mdl
7 | # For LSM303DLH:
8 | # sudo pip3 install adafruit-circuitpython-lsm303dlh-mag
9 |
10 | from Drivers.Driver import Driver
11 | import board
12 | import busio
13 | import adafruit_lsm303_accel
14 |
15 | class Accelerometer(Driver):
16 | # Set up I2C link
17 | # i2c = busio.I2C(board.SCL, board.SDA)
18 |
19 | def __init__(self):
20 | super().__init__("Accelerometer")
21 |
22 | def read(self):
23 | return 9.6,0.3,0.3
24 |
--------------------------------------------------------------------------------
/DummyDrivers/Accelerometer/README.md:
--------------------------------------------------------------------------------
1 | Accelerometer Driver
2 | --
3 | Location: ../Drivers/Accelerometer/Accelerometer.py
4 |
5 | Functionality:
6 | The Accelerometer Driver sets up an I2C communication route to the Accelerometer. After this connection is made it can gather the acceleration data by calling: Accelerometer.Accelerometer.read()
7 |
8 |
--------------------------------------------------------------------------------
/DummyDrivers/Accelerometer/__init__.py:
--------------------------------------------------------------------------------
1 | from .Accelerometer import *
2 |
--------------------------------------------------------------------------------
/DummyDrivers/Driver.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 |
3 | class Driver:
4 | """
5 | Abstract class that defines a device driver.
6 | """
7 | def __init__(self, name, delay=1, initial_delay=0):
8 | """
9 | Initializes a Driver object.
10 | 1. self.name is a string identifying the object.
11 | 2. self.delay is an integer defining the refresh frequency of the driver.
12 | 3. self.initial_delay defines how long the driver should wait before beginning to refresh
13 | """
14 | self.name = name
15 | self.delay = delay
16 | self.initial_delay = initial_delay
17 |
18 | def read(self):
19 | """
20 | Abstract method that defines the behavior of the device driver.
21 | The child class implements this for communicating with specific hardware components.
22 | """
23 | pass
24 |
25 | async def run(self, context, lock):
26 | """
27 | Asynchronous method that controls how often driver reads values from the hardware component.
28 | Waits self.initial_delay time before beginning to read from the hardware.
29 | 1. context is the mission mode defined in the mission_modes module and passed to the driver by main.
30 | 2. lock controls asynchronous execution to avoid race conditions.
31 | """
32 | if self.initial_delay > 0:
33 | await asyncio.sleep(self.initial_delay)
34 | self.initial_delay = 0
35 |
36 | while True:
37 | reading = self.read()
38 | async with lock:
39 | context[self.name] = reading
40 | await asyncio.sleep(self.delay)
41 |
--------------------------------------------------------------------------------
/DummyDrivers/Magnetometer/Magnetometer.py:
--------------------------------------------------------------------------------
1 | #Make sure the following libraries are installed:
2 | # sudo pip3 install RPI.GPIO
3 | # sudo pip3 install adafruit-blinka
4 | # sudo pip3 install adafruit-circuitpython-lsm303-accel
5 | # For LSM303AGR:
6 | # sudo pip3 install adafruit-circuitpython-lis2mdl
7 | # For LSM303DLH:
8 | # sudo pip3 install adafruit-circuitpython-lsm303dlh-mag
9 |
10 | from Drivers.Driver import Driver
11 | import board
12 | import busio
13 | #for LSM303AGR
14 | import adafruit_lis2mdl
15 | #for LSM303DLH
16 | #import adafruit_lsm303dlh_mag
17 |
18 | class Magnetometer(Driver):
19 | #Set up I2C link
20 | #i2c = busio.I2C(board.SCL, board.SDA)
21 | def __init__(self):
22 | super().__init__("Magnetometer")
23 |
24 | def read(self) :
25 | #Set up link to magnetometer
26 | #for LSM303AGR
27 | #mag = adafruit_lis2mdl.LIS2MDL(self.i2c)
28 | #for LSM303DLH
29 | #mag = adafruit_lsm303dlh_mag.LSM303DLH_Mag(self.i2c)
30 | return 40.3,40.5,46.312
31 |
--------------------------------------------------------------------------------
/DummyDrivers/Magnetometer/README.md:
--------------------------------------------------------------------------------
1 | Magnetometer Driver:
2 | --
3 | Location: ../Drivers/Magnetometer/Magnetometer.py
4 |
5 | Functionality:
6 | The Magnetometer Driver establishes an I2C connection with the Magnetometer. Once that connection is made the driver will then collect information concerning the direction, strength, or relative change of magnetic fields in relation to the satellite that have been gathered by the Magnetometer. To gain access to this information call Magnetometer.Magnetometer.read()
7 |
--------------------------------------------------------------------------------
/DummyDrivers/Magnetometer/__init__.py:
--------------------------------------------------------------------------------
1 | from .Magnetometer import *
2 |
--------------------------------------------------------------------------------
/DummyDrivers/README.md:
--------------------------------------------------------------------------------
1 | GASPACS Software: The Drivers
2 | ===
3 | Opening Note:
4 | --
5 | The purpose of the Drivers is to communicate between the Pi and the various sensors and components of the GASPACS mission.
6 |
7 | The following drivers return sensor data:
8 | - Accelerometer
9 | - Magnetometer
10 | - UV
11 | - adc
12 | - antennaDoor
13 | - cpuTemperature
14 | - rtc
15 | - solarPanelTemp
16 | - sunSensors
17 |
18 | The following drivers are designed to configure, command, or control components:
19 | - backupAntennaDeployer
20 | - boomDeployer
21 |
22 | The following drivers both configure components and return their values.
23 | - camera
24 | - transceiverConfig
25 | - eps
26 |
27 | All these drivers inherit from a single file called Driver.py. Their relationship with this file gives them the ability to run asynchronously as well as making the code more organized and easier to handle. For more documentation on each driver, there is a readme located in each Driver folder.
28 |
29 |
--------------------------------------------------------------------------------
/DummyDrivers/UV/README.md:
--------------------------------------------------------------------------------
1 | UV Driver:
2 | --
3 | Location: ../Drivers/UV/UVDriver.py
4 |
5 | Functionality:
6 | The UV Driver inherits functionality from the ADC Driver, much like the Sun Sensor Driver. It reads from a specific channel through the SPi connection created in the ADC Driver, and returns a single value. To get the data returned by this driver call UVDriver.UVDriver.read()
7 |
8 |
9 | # UV Sensor Documentation
10 | ## Requirements
11 | The the value of uv power hitting the uv sensor and return the power in mW/cm^2
12 | ## Design
13 | > MCP3008 ADC
14 | >> * The adc driver for the MCP3008 ADC returns a list with values in volts
15 | >> * The uv sensor driver calls a method from the adc class, and gets the value in the uv sensor spot
16 | >> * Using the formula given by the graphs on the GUVA-S12D uv sensor datasheet( uvPower = voltage * 1000 / 485.9), plug in the value returned by the adc and return uvPower as a float
17 |
18 | > AD7998 ADC
19 | >> As of right now, the AD7998 ADC driver has not been written.
20 | Reading the datasheet tells us that the ADC will return a 12bit binary number over the I2C interface.
21 | As of now, assuming that the adc driver method call for the AD7998 adc returns a 12bit binary number, the design is as follows.
22 | >> * Using the VOLTAGE_STEP constant, which is defined as Vref_in / 4096
23 | >> * 4096 come from the maximum decimal value able to be represented from 12bit binary
24 | >> * The 12 bit binary number returned from the adc will be multiplied with the VOLTAGE_STEP constant to get the voltage
25 | >> * This voltage is then plugged into the previous equation stated, uvPower = voltage * 1000 / 485.9
26 | ## Implementation
27 | * import the adc class from the adc driver
28 | * create an adc object
29 | * call the read method specifying which register to return
30 | * take the return value and depending on which adc it is dealing with, follow the steps outlined above
31 | ## Testing
32 | > Testing with the MCP3008 ADC
33 | >> * Running the ADC code seperatley gives a list of register values
34 | >> * Running the uv sensor code returns the same value as the register in the 4th position
35 |
36 | > Testing with the AD7998
--------------------------------------------------------------------------------
/DummyDrivers/UV/UVDriver.py:
--------------------------------------------------------------------------------
1 | from DummyDrivers.Driver import Driver
2 | from DummyDrivers.adc import ADC_Driver
3 |
4 | class UVDriver(Driver):
5 | """
6 | This class calls the ADC driver and asks for data from the UV channel
7 | """
8 | #adc = ADC_Driver.ADC()
9 | #uv_channel = 1 #The channel on the ADC that th UV sensor is connected to
10 |
11 | def __init__(self):
12 | super().__init__("UVDriver")
13 |
14 | def read(self):
15 | """
16 | This function calls the read function of the ADC with the channel for the uv sensor
17 | """
18 | return 16.234
19 |
--------------------------------------------------------------------------------
/DummyDrivers/UV/__init__.py:
--------------------------------------------------------------------------------
1 | from .UVDriver import *
2 |
--------------------------------------------------------------------------------
/DummyDrivers/__init__.py:
--------------------------------------------------------------------------------
1 | from .Driver import *
2 | from .Magnetometer import *
3 | from .Accelerometer import *
4 | from .camera import *
5 | from .cpuTemperature import *
6 | from .adc import *
7 | from .UV import *
8 | from .rtc import *
9 | from .eps import *
10 | from .antennaDoor import *
11 | from .backupAntennaDeployer import *
12 | from .boomDeployer import *
13 | from .solarPanelTemp import *
14 | from .sunSensors import *
15 | from .transceiverConfig import *
16 |
17 |
--------------------------------------------------------------------------------
/DummyDrivers/adc/ADC_Driver.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | import spidev
3 | import RPi.GPIO as GPIO
4 |
5 |
6 | class ADC(Driver):
7 | """
8 | This class interfaces with the ADC to read the voltage on a specified channel
9 | """
10 | #csPin = 22
11 |
12 | #spi_ch = 0
13 | #spi = spidev.SpiDev()
14 | # spi.open(bus, device)
15 | #spi.open(0, spi_ch)
16 | # disable spidev's chip select. we need to manage this manually
17 | #spi.no_cs = True
18 | # cs = chip select
19 |
20 | def __init__ (self):
21 | # these are the pins for miso, mosi, cs, clk.
22 | # these are were the board is hooked up
23 | super().__init__("ADC")
24 | #GPIO.setmode(GPIO.BOARD)
25 | #GPIO.setup(self.csPin, GPIO.OUT, initial=GPIO.HIGH)
26 |
27 | def read(self, channel):
28 | """
29 | Sends a read command with a specified channel and then returns the reply from the ADC
30 | """
31 | # Start the read with both clock and chip select low
32 | #GPIO.output(self.csPin, GPIO.LOW)
33 |
34 | # the following creates a message to send to the slave
35 | #msg = (channel << 3)
36 | #msg = [msg, 0b00000000]
37 | # the followin
38 | #reply = self.spi.xfer2(msg)
39 |
40 | # set the clock and chip select to high to end message
41 | #GPIO.output(self.csPin, GPIO.HIGH)
42 |
43 | # convert the reply from 12 bits stored in two bytes to a voltage
44 | # 12 bit data: byte 0 [0 0 0 0 MSB d11 d10 d9] byte 1 [d8 d7 d6 d5 d4 d3 d2 LSB]
45 | # Each LSB represents 3.3/4096 volts
46 | return 'Dummy ADC used'
47 |
--------------------------------------------------------------------------------
/DummyDrivers/adc/README.md:
--------------------------------------------------------------------------------
1 | ADC Driver:
2 | --
3 | Location: ../Drivers/adc/ADC_Driver.py
4 |
5 | Functionality:
6 | The ADC Driver sets up a SPi communication route with the ADC. Through the ADC it can then get data from our five Sun Sensors and one UV sensor. After this connection is made one can gather data from these sensors by calling: ADC_Driver.ADC.read(channel).
7 | (see Sun Sensor Driver and UV Driver)
8 |
--------------------------------------------------------------------------------
/DummyDrivers/adc/__init__.py:
--------------------------------------------------------------------------------
1 | from .ADC_Driver import *
2 |
--------------------------------------------------------------------------------
/DummyDrivers/antennaDoor/AntennaDoor.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | import smbus
3 |
4 | class AntennaDoor(Driver):
5 |
6 | def __init__(self):
7 | super().__init__("AntennaDoor") #Calls parent constructor
8 |
9 | #Setup I2C bus for communication
10 | #self.DEVICE_BUS = 1
11 | #self.DEVICE_ADDR = 0x33
12 | #self.RegisterADR = 0x00 # I do not know what this means or what it should be
13 | #self.bus = smbus.SMBus(self.DEVICE_BUS)
14 |
15 | def readDoorStatus(self):
16 | #returns the status of all 4 antenna doors
17 | #doorStatus = self.bus.read_i2c_block_data(self.DEVICE_ADDR, self.RegisterADR, 1)
18 | return (0,0,0,0)
19 |
20 |
--------------------------------------------------------------------------------
/DummyDrivers/antennaDoor/README.md:
--------------------------------------------------------------------------------
1 | Antenna Door Driver:
2 | --
3 | Location: ../Drivers/antennaDoor/AntennaDoor.py
4 |
5 | Functionality:
6 | The Antenna Door Driver sets up an I2C communication route with the Antenna Doors. After this connection is established the Driver is then able to determine whether the doors have or have not opened in order to confirm antenna deployment. The data collected may be acquired by calling: AntennaDoor.AntennaDoor.readDoorStatus()
7 |
--------------------------------------------------------------------------------
/DummyDrivers/antennaDoor/__init__.py:
--------------------------------------------------------------------------------
1 | from .AntennaDoor import *
2 |
--------------------------------------------------------------------------------
/DummyDrivers/backupAntennaDeployer/BackupAntennaDeployer.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | import asyncio
3 | import RPi.GPIO as GPIO
4 |
5 | class BackupAntennaDeployer(Driver):
6 | def __init__(self):
7 | """
8 | Calls parent constructor, sets burn time, sets GPIO pins
9 | """
10 | super().__init__("BackupAntennaDeployer")
11 | # Initial values
12 | self.burnTime = 10
13 |
14 | # Set up the GPIO pins for use
15 | #GPIO.setmode(GPIO.BOARD)
16 |
17 | #Setup GPIO pins
18 | #self.primaryPin = 33
19 | #self.secondaryPin = 32
20 | #GPIO.setup(self.primaryPin,GPIO.OUT, initial=GPIO.LOW)
21 | #GPIO.setup(self.secondaryPin,GPIO.OUT, initial=GPIO.LOW)
22 |
23 | async def deployPrimary(self):
24 | """
25 | Set primary deploy pin to high for a specified time, triggering the
26 | backup antenna burn.
27 | """
28 | #Burn primary backup, then turn off and wait
29 | #GPIO.output(self.primaryPin, GPIO.HIGH)
30 | #await asyncio.sleep(self.burnTime)
31 | #GPIO.output(self.primaryPin, GPIO.LOW)
32 | print("Deploy Primary")
33 |
34 | async def deploySecondary(self):
35 | """
36 | Set secondary deploy pin to high for a specified time, triggering the
37 | backup antenna burn.
38 | """
39 | #GPIO.output(self.secondaryPin, GPIO.HIGH)
40 | #await asyncio.sleep(self.burnTime)
41 | #GPIO.output(self.secondaryPin, GPIO.LOW)
42 | print("Deploy secondary")
43 |
44 | def read(self):
45 | """
46 | Left undefined as no data is collected by this component
47 | """
48 | pass
49 |
--------------------------------------------------------------------------------
/DummyDrivers/backupAntennaDeployer/README.md:
--------------------------------------------------------------------------------
1 | Backup Antenna Deployment Driver:
2 | --
3 | Location: ../Drivers/backupAntennaDeployerBackupAntennaDeployer.py
4 |
5 | Functionality:
6 | The Backup Antenna Deployment Driver sets up a connection to various pins. These pins are connected to capacitors that power the Antenna Doors. Through this connection the software will set the capacitors to turn on and open the Antenna Doors. This code will be used as a fail safe if the premade software to open the Antenna Doors is unsuccessful. The Antenna can be manually deployed by calling: BackupAntennaDeployer.BackupAntennaDeployer.deploy().
7 |
--------------------------------------------------------------------------------
/DummyDrivers/backupAntennaDeployer/__init__.py:
--------------------------------------------------------------------------------
1 | from .BackupAntennaDeployer import *
2 |
--------------------------------------------------------------------------------
/DummyDrivers/boomDeployer/BoomDeployer.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | from time import sleep
3 | import RPi.GPIO as GPIO
4 |
5 | class BoomDeployer(Driver):
6 | def __init__(self):
7 | """
8 | Calls parent constructor, Defines initial burn time, time to wait in between burns,
9 | and how many times to burn before giving up. Sets up the GPIO pin for use by the actuate method.
10 | """
11 | super().__init__("BoomDeployer")
12 | """
13 | # Initial values
14 | self.burnTime = 1
15 | self.waitTime = 3
16 | self.numTimes = 3
17 |
18 | # Set up the GPIO pins for use
19 | #GPIO.setmode(GPIO.BOARD)
20 |
21 | #First Wirecutter
22 | self.wireCutter1_high1 = 36
23 | self.wireCutter1_high2 = 38
24 | self.wireCutter1_low1 = 7
25 | GPIO.setup(self.wireCutter1_high1, GPIO.OUT, initial=GPIO.LOW)
26 | GPIO.setup(self.wireCutter1_high2,GPIO.OUT, initial=GPIO.LOW)
27 | GPIO.setup(self.wireCutter1_low1,GPIO.OUT, initial=GPIO.HIGH)
28 |
29 | #Second Wirecutter
30 | self.wireCutter2_high1 = 35
31 | self.wireCutter2_high2 = 37
32 | self.wireCutter2_low1 = 29
33 | GPIO.setup(self.wireCutter2_high1, GPIO.OUT, initial=GPIO.LOW)
34 | GPIO.setup(self.wireCutter2_high2,GPIO.OUT, initial=GPIO.LOW)
35 | GPIO.setup(self.wireCutter2_low1,GPIO.OUT, initial=GPIO.HIGH)
36 | """
37 |
38 | async def deploy(self):
39 | """
40 | Loop a specified number of times, setting the correct GPIO pins to HIGH/LOW to start/stop
41 | the burn. Wait and then repeat with the other wirecutter mechanism
42 | """
43 | """
44 | for num in range(0, self.numTimes):
45 | #Turn on Wire Cutter 1
46 | GPIO.output(self.wireCutter1_high1, GPIO.HIGH)
47 | GPIO.output(self.wireCutter1_high2, GPIO.HIGH)
48 | GPIO.output(self.wireCutter1_low1, GPIO.LOW)
49 | #Burn for set number of seconds
50 | sleep(self.burnTime)
51 | #Turn off Wire Cutter 1
52 | GPIO.output(self.wireCutter1_high1, GPIO.LOW)
53 | GPIO.output(self.wireCutter1_high2, GPIO.LOW)
54 | GPIO.output(self.wireCutter1_low1, GPIO.HIGH)
55 | #Wait
56 | sleep(self.waitTime)
57 |
58 | #Turn on Wire Cutter 2
59 | GPIO.output(self.wireCutter2_high1, GPIO.HIGH)
60 | GPIO.output(self.wireCutter2_high2, GPIO.HIGH)
61 | GPIO.output(self.wireCutter2_low1, GPIO.LOW)
62 | #Burn for set number of seconds
63 | sleep(self.burnTime)
64 | #Turn off Wire Cutter 2
65 | GPIO.output(self.wireCutter2_high1, GPIO.LOW)
66 | GPIO.output(self.wireCutter2_high2, GPIO.LOW)
67 | GPIO.output(self.wireCutter2_low1, GPIO.HIGH)
68 | #Wait
69 | sleep(self.waitTime)
70 | GPIO.cleanup()
71 | """
72 | print("Triggering dummy deploy")
73 | def read(self):
74 | """
75 | Left undefined as no data is collected by this component
76 | """
77 | pass
78 |
79 |
--------------------------------------------------------------------------------
/DummyDrivers/boomDeployer/README.md:
--------------------------------------------------------------------------------
1 | Boom Deployer Driver:
2 | --
3 | Location: ../Drivers/boomDeployer/BoomDeployer.py
4 |
5 | Functionality:
6 | Our AeroBoom is stored in a container held shut by wire. The Boom Deployer Driver will set up a connection to various pins. These pins will be connected to both the wire cutters. After this connection is made the Driver will tell the first wire cutter to turn on for three seconds and then turn off. It will do this twice. Since there is no way for us to tell if the first wire cutter was successful we will then turn on the second wire cutter and run it in the same manner. This ensures that the AeroBoom gets deployed. The AeroBoom gets deployed by calling: boomDeployer.BoomDeployer.deploy()
7 |
--------------------------------------------------------------------------------
/DummyDrivers/boomDeployer/__init__.py:
--------------------------------------------------------------------------------
1 | from .BoomDeployer import *
2 |
--------------------------------------------------------------------------------
/DummyDrivers/camera/.Camera.py.swo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmallSatGasTeam/CubeWorks/d615cb27b897289bb3a5305b91c62256718685a1/DummyDrivers/camera/.Camera.py.swo
--------------------------------------------------------------------------------
/DummyDrivers/camera/Camera.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | from time import sleep
3 | from picamera import PiCamera
4 | from os import listdir
5 | from os.path import expanduser
6 | from os import makedirs
7 | import subprocess
8 |
9 | class Camera(Driver):
10 | def __init__(self):
11 | """
12 | Takes a picture
13 | """
14 | super().__init__("Camera")
15 | """
16 | self.highRes = (3280, 2464)
17 | self.lowRes = (640, 480)
18 | self.pictureDirectoryPath = expanduser('~/Pictures')
19 | self.cam = None
20 | self.pictureNumber = 0
21 | """
22 |
23 | def read(self):
24 | pass
25 |
26 | def takePicture(self):
27 | """
28 | Takes the picture
29 |
30 | #the way to count folders in directory is len(os.listdir(path of directory to count in))
31 | #you have to import OS
32 | #This also counts files in the total, but with the file structure we came up with this shouldn't be a problem
33 | try:
34 | self.pictureNumber = len(listdir(self.pictureDirectoryPath))
35 | except FileNotFoundError:
36 | self.pictureNumber = 0
37 | #count number of folders in directory, add 1 for current pic
38 |
39 | self.cam = PiCamera()
40 | self.cam.resolution = self.lowRes
41 | sleep(2)
42 | makedirs(self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/LowRes", exist_ok=True)
43 | self.cam.capture(self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/LowRes/LowResOriginal"+str(self.pictureNumber)+".jpg")
44 | self.cam.resolution = self.highRes
45 | makedirs(self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/HighRes", exist_ok=True)
46 | self.cam.capture(self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/HighRes/HighResOriginal"+str(self.pictureNumber)+".jpg")
47 | """
48 | print("Dummy taking picture")
49 |
50 | def compressLowResToFiles(self, pictureNumber):
51 | """
52 | Compresses the Low Res to files. Compresses with SSDV, converts from Hex to ASCII with xxd, splits into 128 byte files.
53 |
54 | self.pictureNumber = pictureNumber
55 | #Set up paths for low res picture and creates the packets directory
56 | lowResOriginalPath = self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/LowRes/LowResOriginal"+str(self.pictureNumber)+".jpg"
57 | lowResSSDVPath = self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/LowRes/LowResOriginal"+str(self.pictureNumber)+".bin"
58 | lowResASCIIPath = self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/LowRes/LowResOriginal"+str(self.pictureNumber)+".txt"
59 | makedirs(self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/LowRes/Packets", exist_ok=True)
60 | lowResPacketPath = self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/LowRes/Packets/"
61 |
62 |
63 | ssdv_lowRes_picture = subprocess.run(["ssdv", "-e", lowResOriginalPath, lowResSSDVPath])
64 | xxd_lowRes_picture = subprocess.run(["xxd", lowResSSDVPath, lowResASCIIPath])
65 | split_lowRes_picture = subprocess.run(["split", "-b", "128", lowResASCIIPath, lowResPacketPath])
66 | """
67 | print("Dummy compress low res")
68 |
69 | def compressHighResToFiles(self,pictureNumber):
70 | """
71 | Compresses the Low Res to files. Compresses with SSDV, converts from Hex to ASCII with xxd, splits into 128 byte files.
72 |
73 | self.pictureNumber = pictureNumber
74 |
75 | #Set up paths for high res picture and creates the packets directory
76 | highResOriginalPath = self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/HighRes/HighResOriginal"+str(self.pictureNumber)+".jpg"
77 | highResSSDVPath = self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/HighRes/HighResOriginal"+str(self.pictureNumber)+".bin"
78 | highResASCIIPath = self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/HighRes/HighResOriginal"+str(self.pictureNumber)+".txt"
79 | makedirs(self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/HighRes/Packets", exist_ok=True)
80 | highResPacketPath = self.pictureDirectoryPath+"/"+str(self.pictureNumber)+"/HighRes/Packets/"
81 |
82 | ssdv_highRes_picture = subprocess.run(["ssdv", "-e", highResOriginalPath, highResSSDVPath])
83 | xxd_highRes_picture = subprocess.run(["xxd", highResSSDVPath, highResASCIIPath])
84 | split_highRes_picture = subprocess.run(["split", "-b", "128", highResASCIIPath, highResPacketPath])
85 | """
86 | print("dummy compress high res")
87 |
--------------------------------------------------------------------------------
/DummyDrivers/camera/README.md:
--------------------------------------------------------------------------------
1 | Camera Driver:
2 | --
3 | Location: ../Drivers/camera/Camera.py
4 |
5 | Functionality:
6 | The Camera Driver will take a photo and store the photo in a specific file with a very specific file path. This Driver will also give us the ability to compress the file into two different resolutions. In order to take a picture call Camera.Camera.takePicture(). In order to store the photo in either of the two resolutions call Camera.Camera.compressLowResToFiles(pictureNumber)
7 | Camera.Camera.compressHighResToFiles(pictureNumber)
8 |
--------------------------------------------------------------------------------
/DummyDrivers/camera/__init__.py:
--------------------------------------------------------------------------------
1 | from .Camera import *
2 |
--------------------------------------------------------------------------------
/DummyDrivers/cpuTemperature/CpuTemperature.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | from os import popen
3 |
4 | class CpuTemperature(Driver):
5 | def __init__(self):
6 | super().__init__(CpuTemperature)
7 |
8 | def read(self):
9 | temp = popen("vcgencmd measure_temp").readline()
10 | temp = float(temp.replace("temp="," ").replace("\'C"," ").replace("\n"," "))
11 | return temp
12 |
13 |
--------------------------------------------------------------------------------
/DummyDrivers/cpuTemperature/README.md:
--------------------------------------------------------------------------------
1 | CPU Temperature Driver:
2 | --
3 | Location: ../Drivers/cpuTemperature/CpuTemperature.py
4 |
5 | Functionality:
6 | The CPU Temperature Driver reads in the temperature of the CPU from the temperature sensor connected to the CPU. To collect the data gathered call CpuTemperature.CpuTemperature.read().
7 |
--------------------------------------------------------------------------------
/DummyDrivers/cpuTemperature/__init__.py:
--------------------------------------------------------------------------------
1 | from .CpuTemperature import *
2 |
--------------------------------------------------------------------------------
/DummyDrivers/eps/EPS.py:
--------------------------------------------------------------------------------
1 | #EPS Driver, not tested against hardware yet
2 | from Drivers.Driver import Driver
3 | import smbus
4 | #circurt bus??? maybe use that
5 |
6 | class EPS(Driver):
7 | #this sets up i2c commincation.
8 | def __init__(self):
9 | super().__init__("EPS") #Calls parent constructor
10 |
11 | #Setup I2C bus for communication
12 | self.DEVICE_BUS = 1
13 | self.DEVICE_ADDR = 0x18
14 | self.RegisterADR = 0x00
15 | #self.bus = smbus.SMBus(self.DEVICE_BUS)
16 |
17 | def enableRaw(self):
18 | #This method enables RAW battery output from the EPS
19 | #The command is 3 bytes device address left-shifted by one bit, the command, and the state
20 | tempAddress = self.DEVICE_ADDR << 1
21 | #self.bus.write_byte_data(self.DEVICE_ADDR, self.RegisterADR, tempAddress)
22 | #self.bus.write_byte_data(self.DEVICE_ADDR, self.RegisterADR, 0x01) #Battery Raw BUS
23 | #self.bus.write_byte_data(self.DEVICE_ADDR, self.RegisterADR, 0x03) #Forced ON state
24 |
25 | def disableRaw(self):
26 | #This method disables RAW battery output from the EPS
27 | #The command is 3 bytes device address left-shifted by one bit, the command, and the state
28 | tempAddress = self.DEVICE_ADDR << 1
29 | #self.bus.write_byte_data(self.DEVICE_ADDR, self.RegisterADR, tempAddress)
30 | #self.bus.write_byte_data(self.DEVICE_ADDR, self.RegisterADR, 0x01) #Battery Raw BUS
31 | #self.bus.write_byte_data(self.DEVICE_ADDR, self.RegisterADR, 0x02) #Forced OFF state <-- Do we want to use Forced OFF/ON or Auto OFF/ON?
32 |
33 | def read(self):
34 | #returns nothing since there are so many different things we could read, use other methods instead
35 | pass
36 |
37 | def __startRead(command):
38 | #This method sends read commands to the EPS. Shawn wrote it, I assume it's correct --Logan
39 | #this code starts the command process, for read commands it is address bit shifted left by one, then the command, and then address bit
40 | #shifted right by one.
41 | #Note: you may have to wait for an acknowledge bit, but this is stander i2c proticol so I'm not sure if python does it automatically.
42 | tempAddress = self.DEVICE_ADDR << 1
43 | #self.bus.write_byte_data(self.DEVICE_ADDR, self.RegisterADR, tempAddress)
44 | #self.bus.write_byte_data(self.DEVICE_ADDR, self.RegisterADR, command) # do you need do convert the int to hex somehow?
45 | #tempAddress = self.DEVICE_ADDR >> 1
46 | #self.bus.write_byte_data(self.DEVICE_ADDR, self.RegisterADR, tempAddress)
47 | #the read command always returns two bytes.
48 | #return self.bus.read_i2c_block_data(self.DEVICE_ADDR, self.RegisterADR, 2)
49 |
50 | #Getter calls read method, returns converted data - all values are converted 12 bits, even though 2 bytes returned
51 | def getMCUTemp(self):
52 | #super().__init__("ESP") <-- Shawn had this in here, I don't understand why it's here so I'm commenting it out and leaving it out of other ones
53 | temp = 26
54 | temp = ((temp *0.0006103516) - 0.986)/0.00355
55 | return temp #done with multiple lines because of complicated conversion
56 | def getCell1Temp(self):
57 | return 11*0.00390625 #Reads data of specified type, sets up conversion factor to 'C
58 |
59 | def getCell2Temp(self):
60 | return 12*0.00390625 #Reads data of specified type, sets up conversion factor to 'C
61 |
62 | def getBusVoltage(self):
63 | return 13000 * 0.0023394775 #Reads data of specified type, sets up conversion factor to V
64 |
65 | def getBusCurrent(self):
66 | return 14 * 0.0030517578 #Reads data of specified type, sets up conversion factor to A
67 |
68 | def getBCRVoltage(self):
69 | return 15000 * 0.0023394775 #Reads data of specified type, sets up conversion factor to V
70 |
71 | def getBCRCurrent(self):
72 | return 16 * 0.0015258789 #Reads data of specified type, sets up conversion factor to A
73 |
74 | def get3V3Current(self):
75 | return 17 * 0.0020345052 #Reads data of specified type, sets up conversion factor to A
76 |
77 | def get5VCurrent(self):
78 | return 18 * 0.0020345052 #Reads data of specified type, sets up conversion factor to A
79 |
80 | def getSPXVoltage(self):
81 | return 21 * 0.0024414063 #Reads data of specified type, sets up conversion factor to V
82 |
83 | def getSPXMinusCurrent(self):
84 | return 22 * 0.0006103516 #Reads data of specified type, sets up conversion factor to A
85 |
86 | def getSPXPlusCurrent(self):
87 | return 23 * 0.0006103516 #Reads data of specified type, sets up conversion factor to A
88 |
89 | def getSPYVoltage(self):
90 | return 24 * 0.0024414063 #Reads data of specified type, sets up conversion factor to V
91 |
92 | def getSPYMinusCurrent(self):
93 | return 25 * 0.0006103516 #Reads data of specified type, sets up conversion factor to A
94 |
95 | def getSPYPlusCurrent(self):
96 | return 26 * 0.0006103516 #Reads data of specified type, sets up conversion factor to A
97 |
98 | def getSPZVoltage(self):
99 | return 27 * 0.0024414063 #Reads data of specified type, sets up conversion factor to V
100 |
101 | def getSPZPlusVoltage(self):
102 | return 28 * 0.0006103516
103 |
--------------------------------------------------------------------------------
/DummyDrivers/eps/README.md:
--------------------------------------------------------------------------------
1 | EPS Driver:
2 | --
3 | Location: ../Drivers/eps/EPS.py
4 |
5 | Functionality:
6 | The EPS Driver sets up an I2C communication line with the EPS. Once this connection is established the Driver gains access to many different forms of data collected by the EPS. These data forms are:
7 |
8 | Cell temperature
9 |
10 | MCU temperature
11 |
12 | Bus voltage
13 |
14 | Bus current
15 |
16 | BCR Voltage
17 |
18 | BCR current
19 |
20 | 3V3 current
21 |
22 | 5V current
23 |
24 | SPX voltage
25 |
26 | SPX minus current
27 |
28 | SPX plus current
29 |
30 | SPY voltage
31 |
32 | SPY minus current
33 |
34 | SPY plus current
35 |
36 | SPZ current
37 |
--------------------------------------------------------------------------------
/DummyDrivers/eps/__init__.py:
--------------------------------------------------------------------------------
1 | from .EPS import *
2 |
--------------------------------------------------------------------------------
/DummyDrivers/eps/documentation.txt:
--------------------------------------------------------------------------------
1 | #This is the code that handle the i2c communication with the esp
2 | #it ment to be abstract and it handles the sending and reciving commands for you.
3 | #It will just return the desired information
4 |
5 |
--------------------------------------------------------------------------------
/DummyDrivers/rtc/README.md:
--------------------------------------------------------------------------------
1 | RTC Driver:
2 | --
3 | Location: ../Drivers/rtc/rtc_driver.py
4 |
5 | Functionality:
6 | The RTC Driver is perhaps the most simple driver. All it does is read the system clock and return the value found there.This value will be the time in milliseconds since the Unix Epoch To get that value call: rtc_driver.RTC.read().
7 |
--------------------------------------------------------------------------------
/DummyDrivers/rtc/__init__.py:
--------------------------------------------------------------------------------
1 | from .rtc_driver import RTC
2 |
--------------------------------------------------------------------------------
/DummyDrivers/rtc/rtc_driver.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | from datetime import datetime
3 |
4 | class RTC(Driver):
5 | def __init__(self):
6 | """
7 | Initializes a simple driver that only reads the system clock.
8 | """
9 | super().__init__('rtc')
10 |
11 |
12 | def readSeconds(self):
13 | """
14 | Returns the UTC time in miliseconds since the Unix epoch.
15 | The integer returned should be 64 bits in size and be on the order of 1500000000000.
16 | """
17 | return int((datetime.utcnow() - datetime.utcfromtimestamp(0)).total_seconds())
18 |
19 | def readMilliseconds(self):
20 | return int((datetime.utcnow() - datetime.utcfromtimestamp(0)).total_seconds() * 1000)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/DummyDrivers/solarPanelTemp/README.md:
--------------------------------------------------------------------------------
1 | Solar Panel Temperature Driver:
2 | --
3 | Location: ../Drivers/solarPanelTemp/solarDriver.py
4 |
5 | Functionality:
6 | The Solar Panel Temperature Driver establishes an SPi connection through the ADC to the two Temperature Sensors connected to the solar panels. After the connection is established this driver can return the temperature of the solar panels. In order to gain access to this data call: solarDriver.TempSensor.read().
7 |
--------------------------------------------------------------------------------
/DummyDrivers/solarPanelTemp/__init__.py:
--------------------------------------------------------------------------------
1 | from .solarDriver import *
2 |
--------------------------------------------------------------------------------
/DummyDrivers/solarPanelTemp/solarDriver.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | import spidev
3 | import RPi.GPIO as GPIO
4 |
5 |
6 | class TempSensor(Driver):
7 | """
8 | This class interfaces with the ADC to read a specified channel
9 | """
10 | #spi0 = spidev.SpiDev()
11 | # open SPI to Temp sensor0
12 | #spi0.open(0, 0)
13 | #spi1 = spidev.SpiDev()
14 | # open SPI to Temp sensor1
15 | #spi1.open(0, 1)
16 |
17 | def __init__ (self):
18 | super().__init__("ADC")
19 |
20 | def read(self):
21 | """
22 | Sends a read command with a specified channel and then returns the reply from the ADC
23 | """
24 | #temp0_raw = self.spi0.readbytes(2) #We need just the first 13 bits, as the last three bits of the 16 bit(2 byte) word are not used
25 |
26 | #This function converts the two bytes to the temperature value. The data is stored as two bytes where T13 is the most significant bit
27 | #| T13 T12 T11 T10 T9 T8 T7 T6 | | T5 T4 T3 T2 T1 1 1 1 |
28 | #temp0 = ((temp0_raw[0] * 64) + (temp0_raw[1] >> 3))* 0.0625
29 |
30 | #temp1_raw = self.spi1.readbytes(2) #We need just the first 13 bits, as the last three bits of the 16 bit(2 byte) word are not used
31 |
32 | #This function converts the two bytes to the temperature value. The data is stored as two bytes where T13 is the most significant bit
33 | #| T13 T12 T11 T10 T9 T8 T7 T6 | | T5 T4 T3 T2 T1 1 1 1 |
34 | #temp1 = ((temp1_raw[0] * 64) + (temp1_raw[1] >> 3))* 0.0625
35 |
36 | return [61.2, 62.1]
37 |
--------------------------------------------------------------------------------
/DummyDrivers/sunSensors/README.md:
--------------------------------------------------------------------------------
1 | Sun Sensor Driver:
2 | --
3 | Location: ../Drivers/sunSensors/sunSensorDriver.py
4 |
5 | Functionality:
6 | The Sun Sensor Driver uses the ADC Drivers already established SPi connection. (See ADC Driver) Using polymorphism this driver reads in from multiple channels of communication via the ADC’s read() function. These channels are connected to the five Sun Sensors. The data collected from these sensors can be accessed by calling: sunSensorDriver.SunSensor.read()
7 |
--------------------------------------------------------------------------------
/DummyDrivers/sunSensors/__init__.py:
--------------------------------------------------------------------------------
1 | from .sunSensorDriver import *
2 |
--------------------------------------------------------------------------------
/DummyDrivers/sunSensors/sunSensorDriver.py:
--------------------------------------------------------------------------------
1 | from DummyDrivers.Driver import Driver
2 | from DummyDrivers.adc import ADC_Driver
3 | from datetime import datetime
4 |
5 | class sunSensor(Driver):
6 | """
7 | This class calls the ADC driver and asks for data from the UV channel
8 | """
9 | #adc = ADC_Driver.ADC()
10 | #adcChannel = [5, 4, 2, 3, 0]
11 | #voltageList = []
12 |
13 | def __init__(self):
14 | # super().__init__("Sun Sensor")
15 | self.voltageList=[] * 5
16 |
17 | def read(self):
18 | # """
19 | # This function calls the read function of the ADC for each channel a sun sensor has and return a list of the voltages
20 | # """
21 | # for i in range(0, 5):
22 | # self.voltageList.append(51)
23 |
24 | # return self.voltageList
25 |
26 | v = [0, 0, 0, 0, 0]
27 | self.voltageList = [0, 0, 0, 0, 0]
28 | getTime = int((datetime.utcnow() - datetime.utcfromtimestamp(0)).total_seconds())
29 | #Sun length is the amount of time the satellite will be in the sun
30 | sunLength = 1.75
31 |
32 | interval = sunLength*60
33 | switch = 20
34 |
35 | if ((getTime % interval) == 0) | ((getTime % interval) == switch):
36 | v[0] = 1.5
37 | v[4] = v[3] = v[2] = v[1] = v[0]
38 | elif (getTime % interval) > switch:
39 | v[4] = v[3] = v[2] = v[1] = v[0] = 3.3
40 | elif (getTime % interval) < switch:
41 | v[4] = v[3] = v[2] = v[1] = v[0] = 0.0
42 |
43 | self.voltageList = v
44 |
45 | print("Dummy sun sensor driver voltage list: ", self.voltageList, "Time, interval, switch: ", getTime%interval, interval, switch)
46 |
47 | return self.voltageList
--------------------------------------------------------------------------------
/DummyDrivers/transceiverConfig/README.md:
--------------------------------------------------------------------------------
1 | Transceiver Configuration Driver:
2 | --
3 | Location: ../Drivers/tranceiverConfig/TranceiverConfig.py
4 |
5 | Functionality:
6 | The Transceiver Configuration Driver does not handle the transmissions. It does, however, turn on and off the beacon, turn on low power mode, and read the internal temperature sensor. To access these different capabilities see the following commands:
7 | Turn off the beacon: TranceiverConfig.TranceiverConfig.setBeaconOff()
8 | Turn on the beacon: TranceiverConfig.TranceiverConfig.setBeaconOn()
9 | Set the Transceiver to low power mode: TranceiverConfig.TranceiverConfig.setLowPowerMode()
10 | Read in the internal temperature of the transceiver: TranceiverConfig.TranceiverConfig.read()
11 |
--------------------------------------------------------------------------------
/DummyDrivers/transceiverConfig/TransceiverConfig.py:
--------------------------------------------------------------------------------
1 | from Drivers.Driver import Driver
2 | import serial
3 |
4 | class TransceiverConfig(Driver):
5 | def __init__(self):
6 | """
7 | The purpose of this driver is to modify the configuration of the Endurosat UHF Transceiver.
8 | It does not have anything to do with sending packets over the radio.
9 | We need the ability to turn on and off the beacon, turn on low power mode, and read the internal temp sensor.
10 | See Page 25 of the Endurosat UHF Transceiver Type II Manual Rev 1.8 document for the SCW bit description.
11 |
12 | ***Note: ALL ES+ commands need to be changed to reflect the address of the transceiver - currently set to 23***
13 | ***Potentially need to add CRC32 checksum functionality***
14 | """
15 | super().__init__("TransceiverConfig")
16 |
17 | def writeData(self, input):
18 | """
19 | Writes the input to the transceiver over UART
20 | """
21 | #ser = serial.Serial("/dev/ttyAMA0") #Open named port
22 | #ser.baudrate = 115200 #Set baud rate to 9600
23 | #ser.timeout = 1
24 | #data = input #Set data to the character 'a', 0x61 or 01100001
25 | #ser.write(data.encode()) #Send the data
26 | #response = ser.read(128)
27 | #ser.close()
28 | #return response
29 | pass
30 |
31 | def setBeaconOn(self):
32 | """
33 | Turns on the morse beacon. Leaves all values at default except beacon.
34 | Binary SCW: 11001101000001
35 | Hex SCW: 3341
36 | """
37 | #self.writeData("ES+W23003341")
38 | pass
39 |
40 | def setBeaconOff(self):
41 | """
42 | Turns off the morse beacon. All values are back to default.
43 | Binary SCW: 11001100000001
44 | Hex SCW: 3301
45 | """
46 | #self.writeData("ES+W23003301")
47 | pass
48 |
49 | def setLowPowerMode(self):
50 | """
51 | Turns on Low Power Mode. Note: Any ESTTC command can be used to bring the transceiver out of low power mode
52 | """
53 | #self.writeData("ES+W23F4")
54 | pass
55 |
56 | def read(self):
57 | """
58 | Returns the temperature from the transceiver internal temp sensor
59 | """
60 | #temp = self.writeData("ES+R230A")
61 | #return temp
62 | pass
63 |
64 |
--------------------------------------------------------------------------------
/DummyDrivers/transceiverConfig/__init__.py:
--------------------------------------------------------------------------------
1 | from .TransceiverConfig import *
2 |
--------------------------------------------------------------------------------
/TXISR/InformationDocs/TXServiceCode User Guide.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmallSatGasTeam/CubeWorks/d615cb27b897289bb3a5305b91c62256718685a1/TXISR/InformationDocs/TXServiceCode User Guide.docx
--------------------------------------------------------------------------------
/TXISR/InformationDocs/TXServiceCodeTest:
--------------------------------------------------------------------------------
1 | Test Procuder for the txisr (c code)
2 |
3 |
4 | 1: run the program and make sure the the program runs with no erros
5 | 2: run program and check the out put files make sure the files have been up dated with the correct time.
6 | 3: connect the program to the radio and make sure it sends the correct data
7 | 4: let program finish the trigger the isr again to see if it reconfigures the pins correctly
8 |
--------------------------------------------------------------------------------
/TXISR/InformationDocs/TXServiceCode_User_Guide.txt:
--------------------------------------------------------------------------------
1 | TXServiceCode User Guide
2 |
3 | How to use: The TXServiceCode is designed to be able to simplify the transmission process to the radio receiver. It takes a single file (txFile.txt), this file contains the information need to transmit, and the information that the user desires to transmit. The file follows the following format.
4 | First line: The transition window in milliseconds. EX 900000 (15 minutes)
5 | All subsequent lines: 000000001 (time stamp, 10 chars) : (divider flag) Data. EX 000000001: 123,123,123
6 |
7 | Notes:
8 | 1. DO NOT put an extra new line at the end of the file.
9 | 2. The divider is not transmitted
10 | 3. If the divider happens to be contained in the data it will still be transmitted.
11 | 4. There are certain data types that do not transmit the time stamp and the divider.
12 |
13 | To call the TXService code it is required that you navigate to the piTXISR folder, the full path is CubeWorks/TXISR/piTXISR. Once in the folder the code is called by calling the they binary executable, these files most commonly end in .out or .run, although this is not strictly required. Usually lunix will highlight this files in a different color. The data type also need to follow the excitable name. The format is as follows ./ EX ./TXSerice.run 1
14 |
15 | Notes:
16 | 1. There are 5 data types. (0 - 4)
17 | 2. If the code is passed data type 3 it does not transmit the time stamp. This is meant to be used for pictures. If multiply data types need to have this behavior this may easy be changed in the TXSerciveCode.c If the number would like to be changed that can also be done easaly by changing the corresponding #define in the first lines of the code. I
18 |
19 | Workings of the TXSeriveCode.c: As a forestated the object of this code is to simplify the transmission process. Therefore the process is as follows,
20 | Step one: The serial port is opened, and the files are verified as being good files, if either of these steps fail the code terminates with an abnormal termination flag.
21 | Step two, the code waits exactly 5 seconds for the transmission window.
22 | • Note: This means that the code should be called exactly 5 seconds before the transmission window.
23 | • Note: The code uses delta t this means that the code does not need to know what the system time is. It also is not important that the system time is correct so long as delta t is still correct.
24 | Step three: the radio is put into pip mode
25 | Step four the transmission
26 | • The code takes in one line of the txFile.txt at a times. It reads the time stamp and then transmit the data.
27 | • The code then saves the time stamp to the flagsFile.txt.
28 | • The code waits 120 milliseconds.
29 | • This process is repeated until all the data is sent or the transmission window expires.
30 | Notes:
31 | 1. The code will NOT watch the packet size, if one line of data is more then 128, it will still transmit the whole line.
32 | 2. The code saves the time stand after every transmission. If the code should fail the last send transmission will always be saved.
33 |
34 | Compiling the code: GCC is used to compile the TXServiceCode. There are two files that should be created each time the code is compiled, 1 a.out and 2 TXService.run. To create this files the process is as follows.
35 | i) Navigate to the piTXISR folder.
36 | ii) Enter gcc TXServiceCode.c (This code is for the testUART.py)
37 | iii) Enter gcc TXServiceCode.c -o TXService.run
38 | iv) Enter cd ..
39 | v) Enter cp piTXISR/TXService.run TXServiceCode
40 | Camera files: Camera transmission files have only one difference from normal transmission files, this is that the time stamp is replaced with a line number. EX 1: (camera data). The line number is important because should a transmission fail we will know where to resume transmissions on the next window.
41 |
42 | Shawn Jones
43 | GASPACS Software Team Lead
44 |
45 |
46 |
--------------------------------------------------------------------------------
/TXISR/README.md:
--------------------------------------------------------------------------------
1 | GASPACS Software: The TXISR
2 | ==
3 | Interrupt:
4 | --
5 | Location: ../TXISR/interrupt.py
6 |
7 | Functionality: The code that comprises the Interrupt does one thing. When a transmission comes from a ground station it will interrupt what is happening on the Raspberry Pi so that we can handle the transmissions to and from the ground. After the interrupt, the software will then go to the TX/RX stage.
8 |
9 | TX/RX (Transmit and Receive):
10 | --
11 | Location: ../TXISR/rxHandling.py
12 |
13 | Functionality: The TX/RX stage does the main bulk of the work needed to be done in order to transmit a message back to the ground. During this stage the software will:
14 | Decode the message received from the ground.
15 | Determine the type of packets or data requested.
16 | Get the queried data from the database, packetize data and write the data to a file.
17 | Determine from the received message when the transmission window will start.
18 | Determine how long the transmission window will last.
19 | Then wait till five seconds before the scheduled window for transmission.
20 |
21 | NOTES:
22 | --
23 | The data will be data collected by the Drivers (see GASPACS Software I: The Drivers).
24 | For more information on transmissions from the ground see GASPACS Flight Logic Plan Outline Appendix C Tables 5, 6 and 7.
25 | For information on transmissions to the ground see GASPACS Flight Logic Plan Outline Appendix B Tables 1, 2, 3 and 4.
26 |
27 |
--------------------------------------------------------------------------------
/TXISR/TXServiceCode/TXService.run:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmallSatGasTeam/CubeWorks/d615cb27b897289bb3a5305b91c62256718685a1/TXISR/TXServiceCode/TXService.run
--------------------------------------------------------------------------------
/TXISR/TXServiceCode/debug.h:
--------------------------------------------------------------------------------
1 | #include
2 | #ifdef DEBUG
3 | #define PRINT_DEBUG(n) printf("Value of " #n ": %d\n", n);
4 | #define PRINT_DEBUG_f(n) printf("Value of " #n ": %f\n", n);
5 | #define PRINT_DEBUG_c(n) printf("Value of " #n ": %c\n", n);
6 | #define DEBUG_REST(n) printf(#n" Has been rest\n");
7 | #define DEBUG_P(n) printf(#n"\n");
8 | #define PRINT_DEBUG_CHAR(n) putchar(n);
9 | #define PRINT_TIME(n) printf("%jd milliseconds\n", (intmax_t)n);
10 | #define PRINT_LONG(n) printf(#n" %ld\n", n);
11 | #define PRINT_HEX(n) printf("hex value: %X\n", n);
12 | #else
13 | #define PRINT_DEBUG(n)
14 | #define PRINT_DEBUG_f(n)
15 | #define PRINT_DEBUG_c(n)
16 | #define DEBUG_REST(n)
17 | #define DEBUG_P(n)
18 | #define PRINT_DEBUG_CHAR(n)
19 | #define PRINT_TIME(n)
20 | #define PRINT_LONG(n)
21 | #define PRINT_HEX(n)
22 | #endif
--------------------------------------------------------------------------------
/TXISR/TXServiceCode/readMe:
--------------------------------------------------------------------------------
1 | #All the code that is not in a folder, are source code! They are files that are used to compile and run the system code
2 | #Hurrah! I found the file that had literally nothing important in it so that I can push and not worry about killing everything!
--------------------------------------------------------------------------------
/TXISR/__init__.py:
--------------------------------------------------------------------------------
1 | #Nothing
2 |
3 |
--------------------------------------------------------------------------------
/TXISR/pythonInterrupt.py:
--------------------------------------------------------------------------------
1 | import serial
2 | import asyncio
3 | import sys
4 | sys.path.append('../')
5 | from TXISR.packetProcessing import packetProcessing
6 | from protectionProticol.fileProtection import FileReset
7 | from time import sleep
8 |
9 |
10 | fileChecker = FileReset()
11 |
12 | """
13 | This file sets up the interrupt process. Every five seconds, the buffer of the serial port at /dev/serial0 is read.
14 | Content is split up into AX.25 Packets, and Command Packets. The data is passed to Jack's packetProcessing.py methods.
15 | TODO: Implement AX.25 digipeating, probably in packetProcessing.py
16 | To defray the possibility of half a packet being in the buffer, any half-packets are stored and evaluated the next time around
17 | """
18 |
19 | async def interrupt(transmitObject, packetObj):
20 | packet = packetObj
21 | fileChecker.fullReset()
22 | try:
23 | serialport = serial.Serial('/dev/serial0', 115200) #Open serial port. Currently /dev/serial0, might change to the PL011 port for flight article
24 | except Exception as e:
25 | print("Failed to open serialport. Exception:", repr(e))
26 | serialport = None
27 | leftovers = '' #Stores any half-packets for evaluation the next loop
28 | # leftoversEmpty = True
29 | gaspacsHex = str(b'GASPACS'.hex())
30 | while True:
31 | if transmitObject.isRunning():
32 | await asyncio.sleep(5)
33 | continue
34 | try:
35 | if serialport == None:
36 | print("Reopening serial port")
37 | serialport = serial.Serial('/dev/serial0', 115200) #Open serial port. Currently /dev/serial0, might change to the PL011 port for flight article
38 | print("Python interrupt.", serialport.in_waiting)
39 | if serialport.in_waiting: #If there is content in the serial buffer, read it and act on it
40 | #if True: #This is a testing line
41 | print('Data in waiting')
42 | data = str(serialport.read(serialport.in_waiting).hex()) #This produces a list of nibbles (half bytes)
43 | data = leftovers + data #Append any leftover data for evaluation
44 |
45 | commands = []
46 | commands, leftovers = parseData(data, gaspacsHex)
47 | print("Commands:" + str(commands))
48 |
49 | for command in commands:
50 | # print(command)
51 | await packet.processPacket(command) #Process Command Packets
52 | print("Made it all the way. Leftovers: ", leftovers)
53 | # serialport.reset_input_buffer()
54 | await asyncio.sleep(5)
55 | else: #No contents in serial buffer
56 | print('buffer empty')
57 | await asyncio.sleep(5)
58 |
59 | # serialport.close()
60 | except Exception as e:
61 | print("Failure to run interrupt. Exception:", repr(e))
62 | await asyncio.sleep(5)
63 |
64 | def parseData(data, bracket): #Takes data string, in the form of hex, from async read serial function. Spits out all AX.25 packets and GASPACS packets contained inside, as well as remaining data to be put into the leftovers
65 | # fileChecker.fullReset()
66 | try:
67 | searching = True
68 | gaspacsPackets = []
69 | modifiedString = data
70 | while searching: #Searching for packets bracketed by Hex-bytes of 'GASPACS'
71 | content = None
72 | content, modifiedString, searching = searchGASPACS(modifiedString, bracket)
73 | if searching:
74 | gaspacsPackets.append(content)
75 |
76 | return gaspacsPackets, modifiedString
77 | except Exception as e:
78 | print("Failed in parse Data. Error:", e)
79 | return 0, 0, 0
80 |
81 |
82 | def searchGASPACS(data, str): #Must be passed as a string of hex, for both parameters
83 | #Finds command or window packets, bracketed by in . Removes the brackets and the contents in between from . Returns the command contents
84 | # fileChecker.fullReset()
85 | try:
86 | content=''
87 | occurences = findOccurences(data, str)
88 | modifiedString = data
89 | changed = False
90 | i=0
91 | if (0= 0):
51 | min = line
52 | minLine = i
53 | contents.remove(minLine)
54 | self.__fileChecher.checkFile(self.__filepath)
55 | file = open(self.__filepath, "w")
56 | if(not delete):
57 | file.write(minLine + "\n")
58 | for j in contents:
59 | line = j.split(',')
60 | if line[0] != '':
61 | if(int(line[0]) >= 0):
62 | file.write(j + "\n")
63 | file.close()
64 | if(not delete):
65 | return int (min[0])
66 | else :
67 | return minLine
68 |
69 | def clearQueue(self):
70 | self.__fileChecher.checkFile(self.__filepath)
71 | self.__fileChecher.windowProtection()
72 | file = open(self.__filepath, "w")
73 | file.close()
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
1 | from .Drivers import *
2 |
--------------------------------------------------------------------------------
/docs/CubeWorks_UML.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmallSatGasTeam/CubeWorks/d615cb27b897289bb3a5305b91c62256718685a1/docs/CubeWorks_UML.png
--------------------------------------------------------------------------------
/docs/DataBase schema Proposal.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmallSatGasTeam/CubeWorks/d615cb27b897289bb3a5305b91c62256718685a1/docs/DataBase schema Proposal.docx
--------------------------------------------------------------------------------
/docs/Electrical_notes.md:
--------------------------------------------------------------------------------
1 | # Electrical notes on devices
2 |
3 | ## Magnetometer/Accelerometer
4 | Pins:
5 | | Name | GPIO number | Pin Number|
6 | | --- | --- | --- |
7 | | SDA | 2 | 3 |
8 | | SCL | 3 | 5 |
9 | | INT1 | 16 | 36 |
10 | | DRDY | 18 | 12 |
11 |
12 | The values for this will vary greatly depending on the orientation of the computer board.
13 | Expected readings are:
14 | ```
15 | Acceleration (m/s^2): X=-1.606 Y=-0.650 Z=9.523
16 | Magnetometer (micro-Teslas): X=19.182 Y=4.909 Z=-39.490
17 | ```
18 | If lying flat, the Z values should be similar to those above.
19 |
20 | ## ADC
21 | Pins:
22 | | Name | GPIO number | Pin Number|
23 | | --- | --- | --- |
24 | | MISO | 9 | 21 |
25 | | MOSI | 10 | 19 |
26 | | SCLK | 11 | 23 |
27 | | CS | 25 | 22 |
28 |
29 | *CS is active low. Setting the pin low allows you to communicate with the ADC
30 |
31 | Channels:
32 | | Sensor | ADC channel |
33 | | --- | --- |
34 | | UV sensor | 1 |
35 | | Sun sensor 1 | 5 |
36 | | Sun sensor 2 | 4 |
37 | | Sun sensor 3 | 2 |
38 | | Sun sensor 4 | 3 |
39 | | Sun sensor 5 | 0 |
40 |
41 | *The channels are weird, but it made the traces on the board work out nicely.
42 |
43 | ### UV sensor
44 | The UV sensor circuit is tuned to that any UV light will cause the circuit to max out at around 3.3V (3.29V in testing)
45 | This is converted by the ADC into a 12 bit binary number, with 0 as 0V and 4096 as 3.3V.
46 | When the sensor is exposed to UV light, it should read around 3.3V.
47 |
48 | ### Sun sensors
49 | The Sun sensors have almost the exact same circuit as the UV sensor, except that they are tuned so that the max amount of sunlight the sensor can read corresponds to just below 3.3V in the circuit.
50 |
51 | ## EPS
52 | The EPS module is connected on the I2C bus, with a seperate pin for an EPS reset.
53 |
54 | Pins:
55 | | Name | GPIO number | Pin Number|
56 | | --- | --- | --- |
57 | | SDA | 2 | 3 |
58 | | SCL | 3 | 5 |
59 | | EPS_RST | 6 | 31 |
60 |
61 | *EPS_RST is active low, setting the pin low will reset the EPS
62 |
63 | Command 1 will give the battery voltage from the EPS as a 12 bit number (0-4096).
64 | Multiply this by 0.0023394775 to get the voltage.
65 | The raw battery bus should read between 3.5 - 4.12 V
66 |
67 | ## Solar Panel Temperature Sensors
68 | The solar panels are connected to the Pi on the SPI bus.
69 |
70 | Pins:
71 | | Name | GPIO number | Pin Number|
72 | | --- | --- | --- |
73 | | MISO | 9 | 21 |
74 | | MOSI | 10 | 19 |
75 | | SCLK | 11 | 23 |
76 | | Panel_1_Temp_CS | 7 | 26 |
77 | | Panel_2_Temp_CS | 8 | 24 |
78 |
79 | The accuracy of the Temperature sensors are:
80 | | Condition | Error margin |
81 | | --- | --- |
82 | | -25°C to 85°C | ±1.5°C |
83 | | -55°C to 125°C | ±2.0°C |
84 |
85 | ## Chronodot
86 | The chronodot is connected to the Pi on the I2C bus, along with a reset pin, an interrupt, and a 32kHz square wave
87 |
88 | Pins:
89 | | Name | GPIO number | Pin Number|
90 | | --- | --- | --- |
91 | | SDA | 2 | 3 |
92 | | SCL | 3 | 5 |
93 | | RST | 22 | 15 |
94 | | INT | 23 | 16 |
95 | | 32kHz | 27 | 13 |
96 |
97 | *The RST pin is active low, setting the RST pin low will cause the chronodot to reset
98 |
99 | *The INT pin is also active low.
100 |
101 | The RST pin acts as both an external reset button, but also an internal reset indicator. The RST pin will go low if the suply voltage for the chronodot goes too low and the chronodot resets. This can indicate a comprimised clock.
102 |
103 | The INT pin acts like an alarm. A specific time can be set, and at that time, the chronodot will set the INT pin low. Otherwise this pin is high.
104 |
105 | The accuracy of the clock is ±2 minutes/year of operation
106 |
107 | ## Wire cutters
108 | The wire cutters have an on switch as two inhibits.
109 |
110 | Pins:
111 | | Name | GPIO number | Pin Number|
112 | | --- | --- | --- |
113 | | WC1_on | 4 | 7 |
114 | | WC1_safety1 | 20 | 38 |
115 | | WC1_safety2 | 16 | 36 |
116 | | WC2_on | 5 | 29 |
117 | | WC2_safety1 | 26 | 37 |
118 | | WC2_safety2 | 19 | 35 |
119 |
120 | The inhibits must be set High and the on pin must be set Low to turn the wire cutters on.
121 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # CubeWorks Documentation
2 |
3 | CubeWorks is documented using Doxygen, a free and simple automatic documentation generator.
4 |
5 | ## Building the Docs
6 |
7 | The output directory is set to be ignored by git so the documentation is not committed to source control. To build the CubeWorks documentation, do the following:
8 |
9 | ### Install Doxygen
10 |
11 | To install Doxygen, simply run the following command from a linux shell:
12 |
13 | `$ sudo apt install doxygen`
14 |
15 | If you are using a Windows or MacOS system, you can either install the windows version as outlined in the Doxygen setup instructions (http://www.doxygen.nl/manual/install.html) or install the Windows Subsystem for Linux and run the command there. There is also Homebrew entry for Doxygen for Mac users.
16 |
17 | ### Run Doxygen
18 |
19 | Doxygen is already configured to build the documentation using the `Doxyfile` file included in the root directory of the project. Simply use the following command:
20 |
21 | `$ doxygen Doxyfile`
22 |
23 | Doxygen will run and output the documentation in `GASPACS/docs/doxygen`. To view the documentation as a web page, `cd` into `GASPACS/docs/doxygen/html` and run `index.html` or double click on `index.html` in your file browser. Doxygen also generates LaTeX documents for the project.
24 |
--------------------------------------------------------------------------------
/docs/testing.md:
--------------------------------------------------------------------------------
1 | #Testing Procedures
2 |
--------------------------------------------------------------------------------
/flightConfig.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | char cammand0 [] = "sudo crontab -l >> crontab_new ; echo @reboot sudo /usr/bin/tvservice -o >> crontab_new ; sudo crontab crontab_new ; rm crontab_new";
5 | char cammand1 [] = "sudo cat /boot/config.txt >> config_temp ; sudo echo dtparam=act_led_trigger=none >> config_temp";
6 | char cammand2 [] = "sudo echo dtparam=act_led_activelow=on >> config_temp ";
7 | char cammand3 [] = "sudo echo dtoverlay=disable-wifi >> config_temp";
8 | char cammand4 [] = "sudo echo dtoverlay=disable-bt >> config_temp";
9 | char cammand5 [] = "sudo cp config_temp /boot/config.txt ; rm config_temp";
10 |
11 | /*************************************************************************
12 | * This is the code that will set the pi into the power saving options we
13 | * want to use.
14 | * NOTE: This dispable wifi as well, this is the finial flight
15 | * configuration
16 | ************************************************************************/
17 | void main()
18 | {
19 | system(cammand0);
20 | system(cammand1);
21 | system(cammand2);
22 | system(cammand3);
23 | system(cammand4);
24 | system(cammand5);
25 | system("sudo reboot");
26 | }
27 |
28 | //this code will update all the code bases
29 | //Written by Shawn
--------------------------------------------------------------------------------
/flightConfigWifi.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | char cammand0 [] = "sudo crontab -l >> crontab_new ; echo @reboot sudo /usr/bin/tvservice -o >> crontab_new ; sudo crontab crontab_new ; rm crontab_new";
5 | char cammand1 [] = "sudo cat /boot/config.txt >> config_temp ; sudo echo dtparam=act_led_trigger=none >> config_temp";
6 | char cammand2 [] = "sudo echo dtparam=act_led_activelow=on >> config_temp ";
7 | char cammand5 [] = "sudo cp config_temp /boot/config.txt ; rm config_temp";
8 |
9 | /*************************************************************************
10 | * This is the code that will set the pi into the power saving options we
11 | * want to use.
12 | * NOTE: This does not dispable wifi as well, this is for testing finial
13 | * configuration
14 | ************************************************************************/
15 | void main()
16 | {
17 | system(cammand0);
18 | system(cammand1);
19 | system(cammand2);
20 | system(cammand5);
21 | system("sudo reboot");
22 | }
23 |
24 | //this code will update all the code bases
25 | //Written by Shawn
--------------------------------------------------------------------------------
/flightLogic/DummymissionModes/README.md:
--------------------------------------------------------------------------------
1 | Mission Modes
2 | ==
3 | Antenna Deploy Mode:
4 | --
5 | Location: ../flightLogic/missionModes/antennaDeploy.py
6 |
7 | Functionality:
8 | In the file flightLogic.py there is a section of code that will check if the antennas have been deployed using our antenna door driver (see GASPACS Software I: the Drivers). If this check returns false the antenna deploy mode will run. This mode will check our battery to see if we have enough power to open the doors. If that check returns true we will then call our back up antenna deployment driver (see GASPACA Software I: the Drivers). If we do not have enough power we will go into sage mode.
9 |
10 | Pre Boom Deployment mode:
11 | --
12 | Location: ../flightLogic/missionModes/preBoomDeploy.py
13 |
14 | Functionality:
15 | When we go into Pre Boom Deployment mode we perform multiple checks. These checks are to insure the satellite is exposed to the sun where our Aero Boom can cure and stiffen as well as to make sure we have enough power. To get the data needed (TTNC, Attitude and Boom Deployment data) to perform these checks the Pre Boom Deployment Mode code will call the getDriverData class.
16 |
17 | Boom Deployment mode:
18 | ---
19 | Location: ../flightLogic/missionModes/boomDeploy.py
20 |
21 | Functionality:
22 | The Boom Deployment Mode will run after all the checks performed by the Pre-Boom Deployment mode. The Boom Deployment mode will run the Boom Deployer Driver and then the Camera Driver. (see GASPACA Software I: the Drivers)
23 |
24 | Post-Boom Deploy mode:
25 | --
26 | Location: ../flightLogic/missionModes/postBoomDeploy.py
27 |
28 | Functionality:
29 | This is the last mission mode the Raspberry Pi enters. Once all modes have been run this mode will run a Reboot loop. This loop will cause a reboot every twenty-four hours. This mode will monitor the battery power and other basic functions to keep our satellite from dying. This is also the mode that transmission will take place in.
30 |
31 |
32 | SAFE:
33 | --
34 | Location: ../flightLogic/missionModes/safe.py
35 |
36 | Functionality:
37 | This mode will turn the Raspberry Pi off. It does this by sending a signal to the Arduino Beetle to turn it off. This is to protect against damage to the Pi and lose of data.
38 |
39 |
40 |
--------------------------------------------------------------------------------
/flightLogic/DummymissionModes/antennaDeploy.py:
--------------------------------------------------------------------------------
1 | import sys
2 | sys.path.append('../../')
3 | import time
4 | from DummyDrivers.backupAntennaDeployer import BackupAntennaDeployer
5 | from DummyDrivers.antennaDoor import AntennaDoor
6 | import DummyDrivers.eps.EPS as EPS
7 | import asyncio
8 | from flightLogic.DummymissionModes import safe
9 | import flightLogic.DummygetDriverData as getDriverData
10 | from TXISR import pythonInterrupt
11 |
12 |
13 | class antennaMode:
14 |
15 | def __init__(self, saveobject):
16 | self.deployVoltage = 3 #Threshold voltage to deploy
17 | self.maximumWaitTime = 30 #Maximum time to wait for deployment before going to SAFE
18 | self.timeWaited = 0 #Time already waited - zero
19 | self.__getDataTTNC = getDriverData.TTNCData(saveobject)
20 | self.__getDataAttitude = getDriverData.AttitudeData(saveobject)
21 | self.__tasks = [] #List will be populated with background tasks to cancel them
22 | self.__safeMode = safe.safe(saveobject)
23 | self.__antennaDeployer = BackupAntennaDeployer()
24 | self.__antennaDoor = AntennaDoor()
25 |
26 |
27 | async def run(self):
28 | print('Antenna Deploy Running!')
29 | ttncData = self.__getDataTTNC
30 | attitudeData = self.__getDataAttitude
31 | self.__tasks.append(asyncio.create_task(pythonInterrupt.interrupt()))
32 | self.__tasks.append(asyncio.create_task(ttncData.collectTTNCData(1))) #Antenna deploy is mission mode 1
33 | self.__tasks.append(asyncio.create_task(attitudeData.collectAttitudeData()))
34 | self.__tasks.append(asyncio.create_task(self.__safeMode.thresholdCheck())) #Check battery conditions, run safe mode if battery drops below safe level
35 | self.__tasks.append(asyncio.create_task(self.__safeMode.heartBeat()))
36 | eps=EPS()
37 | while True: #Runs antenna deploy loop
38 | if (eps.getBusVoltage()>self.deployVoltage):
39 | await asyncio.gather(self.__antennaDeployer.deployPrimary()) #Fire Primary Backup Resistor
40 | doorStatus = self.__antennaDoor.readDoorStatus() #Check Door status
41 | if doorStatus == (1,1,1,1): #probably need to change this to actually work
42 | self.cancelAllTasks(self.__tasks)
43 | print('Doors are open, returning true')
44 | return True
45 | else:
46 | print('Firing secondary, primary did not work. Returning True')
47 | await asyncio.gather(self.__antennaDeployer.deploySecondary())
48 | self.cancelAllTasks(self.__tasks)
49 | return True
50 | else:
51 | if(self.timeWaited > self.maximumWaitTime):
52 | self.__safeMode.run(10) #1 hour
53 | await asyncio.sleep(5) #This is an artifact of testing, and will not matter for the actual flight software
54 | else:
55 | #Wait 1 minute
56 | print('Waiting 1 minute until battery status resolves')
57 | self.timeWaited = self.timeWaited+1
58 | await asyncio.sleep(60)
59 |
60 |
61 |
62 | def cancelAllTasks(self, taskList):
63 | try:
64 | for t in taskList:
65 | t.cancel()
66 | except asyncio.exceptions.CancelledException:
67 | print("Caught thrown exception in cancelling background task")
68 |
--------------------------------------------------------------------------------
/flightLogic/DummymissionModes/boomDeploy.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import time
3 | sys.path.append('../../')
4 | import asyncio
5 | from flightLogic.DummymissionModes import safe
6 | from flightLogic.DummygetDriverData import *
7 | import DummyDrivers.boomDeployer as boomDeployer
8 | import DummyDrivers.camera.Camera as camera
9 | from TXISR import pythonInterrupt
10 |
11 |
12 |
13 | class boomMode:
14 | def __init__(self, saveobject):
15 | self.__getDataTTNC = TTNCData(saveobject)
16 | self.__getDataAttitude = AttitudeData(saveobject)
17 | self.__getDataDeployData = DeployData(saveobject)
18 | self.__tasks = [] # Empty list will be populated with all background tasks
19 | self.safeMode = safe.safe(saveobject)
20 | self.saveobject = saveobject
21 |
22 | async def run(self):
23 | # Setting up background processes
24 | ttncData = self.__getDataTTNC
25 | attitudeData = self.__getDataAttitude
26 | deployData = DeployData(self.saveobject)
27 | self.__tasks.append(asyncio.create_task(pythonInterrupt.interrupt()))
28 | self.__tasks.append(asyncio.create_task(ttncData.collectTTNCData(3))) # Boom deploy is mode 3
29 | self.__tasks.append(asyncio.create_task(attitudeData.collectAttitudeData()))
30 | self.__tasks.append(asyncio.create_task(deployData.collectDeployData()))
31 | self.__tasks.append(asyncio.create_task(self.safeMode.thresholdCheck()))
32 | self.__tasks.append(asyncio.create_task(self.safeMode.heartBeat()))
33 |
34 | # Deploy boom, take picture
35 | await asyncio.sleep(5)
36 | deployer = boomDeployer.BoomDeployer()
37 | cam = camera.Camera()
38 | await deployer.deploy() #From LOGAN: Deployer.deploy is now an asyncio method, run it like the others
39 | #cam.takePicture()
40 | #cam.compressLowResToFiles() No longer necessary
41 | #cam.compressHighResToFiles() No longer necessary
42 | await asyncio.sleep(5)
43 | self.cancelAllTasks(self.__tasks) # Cancel all background tasks
44 | return True # Go to post-boom deploy
45 |
46 | def cancelAllTasks(self, taskList):
47 | try:
48 | for t in taskList:
49 | t.cancel()
50 | except asyncio.exceptions.CancelledException:
51 | print("Caught thrown exception in cancelling background task")
52 |
--------------------------------------------------------------------------------
/flightLogic/DummymissionModes/postBoomDeploy.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import subprocess
3 | import os
4 | import time
5 | sys.path.append('../../')
6 | import asyncio
7 | from flightLogic.DummymissionModes import safe
8 | from flightLogic.DummygetDriverData import *
9 | import DummyDrivers.eps.EPS as EPS
10 | from TXISR import prepareFiles
11 | from TXISR import pythonInterrupt
12 | from protectionProticol.fileProtection import FileReset
13 |
14 | fileChecker = FileReset()
15 |
16 | TRANSFER_WINDOW_BUFFER_TIME = 30 #30 seconds
17 | REBOOT_WAIT_TIME = 900 #15 minutes, 900 seconds
18 |
19 | class postBoomMode:
20 |
21 | def __init__(self, saveobject):
22 | self.postBoomTimeFile = open("postBoomTime.txt", "w+")
23 | self.__getDataTTNC = TTNCData(saveobject)
24 | self.__getDataAttitude = AttitudeData(saveobject)
25 | self.__tasks = [] # List will be populated with all background tasks
26 | self.__safeMode = safe.safe(saveobject)
27 | self.__timeToNextWindow = -1
28 | self.___nextWindowTime = -1
29 | self.__duration = -1
30 | self.__datatype = -1
31 | self.__pictureNumber = -1
32 | self.__startFromBeginning = -1
33 | fileChecker.checkFile("../TXISR/data/transmissionFlag.txt")
34 | fileChecker.checkFile("../TXSIR/data/txWindows.txt")
35 | self.__transmissionFlagFile = open("../TXISR/data/transmissionFlag.txt")
36 | self.__txWindowsPath = ("../TXISR/data/txWindows.txt")
37 |
38 | async def run(self):
39 | #Set up background processes
40 | ttncData = self.__getDataTTNC
41 | attitudeData = self.__getDataAttitude
42 | self.__tasks.append(asyncio.create_task(pythonInterrupt.interrupt()))
43 | self.__tasks.append(asyncio.create_task(ttncData.collectTTNCData(4))) #Post-boom is mode 4
44 | self.__tasks.append(asyncio.create_task(attitudeData.collectAttitudeData()))
45 | self.__tasks.append(asyncio.create_task(self.__safeMode.thresholdCheck()))
46 | fileChecker.checkFile("../TXISR/data/txWindows.txt")
47 | self.__tasks.append(asyncio.create_task(self.__safeMode.heartBeat()))
48 | self.__tasks.append(asyncio.create_task(self.readNextTransferWindow()))
49 | self.__tasks.append(asyncio.create_task(self.rebootLoop()))
50 | while True:
51 | #if close enough, prep files
52 | #wait until 5 seconds before, return True
53 | if(self.__timeToNextWindow is not -1 and self.__timeToNextWindow<60): #If next window is in 2 minutes or less
54 | if(self.__datatype < 3): #Attitude, TTNC, or Deployment data
55 | prepareFiles.prepareData(self.__duration, self.__datatype)
56 | else:
57 | prepareFiles.preparePicture(self.__duration, self.__datatype, self.pictureNumber)
58 | break
59 | await asyncio.sleep(5)
60 | windowTime = self.__nextWindowTime
61 | while True:
62 | if((windowTime-time.time()) <= 5):
63 | # Check the files before adding them as objects
64 | fileChecker.checkFile("../TXISR/data/transmissionFlag.txt")
65 | txisrCodePath = '../TXISR/TXServiceCode/TXService.run'
66 | os.system(txisrCodePath + ' ' + str(self.__datatype)) #Call TXISR Code
67 | return True
68 | await asyncio.sleep(0.1) #Check at 10Hz until the window time gap is less than 5 seconds
69 |
70 | async def rebootLoop(self):
71 | upTime = 0
72 | while True:
73 | if upTime>86400: #Live for more than 24 hours
74 | if (self.__timeToNextWindow == -1) or (self.__timeToNextWindow > REBOOT_WAIT_TIME):
75 | self.__safeMode.run(1) #restart, powering off Pi for 1 minute
76 | print('Rebooting raspberry pi')
77 | upTime=0
78 | else:
79 | print('Uptime: '+str(upTime))
80 | await asyncio.sleep(60)
81 | upTime += 60
82 |
83 | async def readNextTransferWindow(self, transferWindowFilename):
84 | while True:
85 | #read the given transfer window file and extract the data for the soonest transfer window
86 | fileChecker.checkFile(transferWindowFilename)
87 | transferWindowFile = open(transferWindowFilename)
88 | sendData = 0
89 | soonestWindowTime = 0
90 | for line in transferWindowFile:
91 | #print("reading line: ")
92 | #print(line)
93 | data = line.split(",")
94 | #data[0] = time of next window, data[1] = duration of window, data[2] = datatype, data[3] = picture number
95 | if(float(data[0]) - time.time() > TRANSFER_WINDOW_BUFFER_TIME): #if the transfer window is at BUFFER_TIME milliseconds in the future
96 | if(soonestWindowTime == 0 or float(data[0]) - time.time() < soonestWindowTime):
97 | soonestWindowTime = float(data[0]) - time.time()
98 | sendData = data
99 | if not(sendData == 0):
100 | #print("Found next transfer window: ")
101 | #print(sendData)
102 | self.__timeToNextWindow = float(sendData[0]) - time.time()
103 | self.__duration = int(sendData[1])
104 | self.__datatype = int(sendData[2])
105 | self.__pictureNumber = int(sendData[3])
106 | self.__nextWindowTime = float(sendData[0])
107 | #print(self.__timeToNextWindow)
108 | #print(self.__duration)
109 | #print(self.__datatype)
110 | #print(self.__pictureNumber)
111 | await asyncio.sleep(10) #Checks transmission windows every 10 seconds
112 |
113 | def cancellAllTasks(self, taskList): #Isn't used in this class, but here anyways
114 | try:
115 | for t in taskList:
116 | t.cancel()
117 | except asyncio.exceptions.CancelledException:
118 | print("Caught thrown exception in cancelling background task")
119 |
--------------------------------------------------------------------------------
/flightLogic/DummymissionModes/preBoomDeploy.py:
--------------------------------------------------------------------------------
1 | import sys
2 | sys.path.append('../../')
3 | import asyncio
4 | from flightLogic.DummymissionModes import safe
5 | from flightLogic.DummygetDriverData import *
6 | from DummyDrivers.eps import EPS as EPS
7 | from DummyDrivers.sunSensors import sunSensorDriver as sunSensorDriver
8 | from TXISR import pythonInterrupt
9 |
10 |
11 | class preBoomMode:
12 | def __init__(self, saveobject):
13 | self.thresholdVoltage = 3.5 #Threshold voltage to deploy AeroBoom.
14 | self.criticalVoltage = 3.1 #Critical voltage, below this go to SAFE
15 | self.darkVoltage = 1 #Average voltage from sunsors that, if below this, indicates GASPACS is in darkness
16 | self.darkMinutes = 1 #How many minutes GASPACS must be on the dark side for before moving forward
17 | self.lightMinimumMinutes = 1 #Minimum amount of time GASPACS must be on light side of orbit before deploying
18 | self.lightMaximumMinutes = 60 #Maximum amount of time GASPACS may be on light side of orbit before deploying, must be less than 90 by a fair margin since less than half of orbit can be sun
19 | self.batteryStatusOk = False
20 | self.maximumWaitTime = 240 #Max time GASPACS can wait, charging batteries, before SAFEing
21 | self.timeWaited = 0
22 | self.sunlightData = []
23 | self.__getDataTTNC = TTNCData(saveobject)
24 | self.__getDataAttitude = AttitudeData(saveobject)
25 | self.__tasks = [] #Will be populated with tasks
26 | self.saveobject = saveobject
27 | self.safeMode = safe.safe(saveobject)
28 |
29 | async def run(self):
30 | ttncData = self.__getDataTTNC
31 | attitudeData = self.__getDataAttitude
32 | self.__tasks.append(asyncio.create_task(pythonInterrupt.interrupt()))
33 | self.__tasks.append(asyncio.create_task(ttncData.collectTTNCData(2))) #Pre-Boom is mode 2
34 | self.__tasks.append(asyncio.create_task(attitudeData.collectAttitudeData()))
35 | self.__tasks.append(asyncio.create_task(self.safeMode.thresholdCheck()))
36 | self.__tasks.append(asyncio.create_task(self.safeMode.heartBeat()))
37 | self.__tasks.append(asyncio.create_task(self.sunCheck()))
38 | self.__tasks.append(asyncio.create_task(self.batteryCheck()))
39 | while True: #iterate through array, checking for set amount of dark minutes, then set amount of light minutes no greater than the maximum. When light minutes are greater than the maximum, empties array
40 | i=0
41 | darkLength = 0
42 | lastDark = 0
43 | while i < len(self.sunlightData): #Loop through sunlightData, checking for X minutes of darkness
44 | if(self.sunlightData[i]self.darkMinutes*6): #If GASPACS has been in dark longer than the preset amount
49 | lastDark = i
50 | break
51 | i+=1
52 | print('Last Dark ' + str(lastDark))
53 |
54 | if lastDark != 0: #Condition from previous while loop has been met
55 | q=lastDark
56 | lightLength = 0
57 | print("In Sunlight, looking for min minutes")
58 | while q < len(self.sunlightData):
59 | if(self.sunlightData[q]>=self.darkVoltage):
60 | lightLength+=1
61 | else:
62 | lightLength = 0 #Maybe lightLength -=1 to avoid 1 bad measurement resetting everything
63 |
64 | if(lightLength>self.lightMaximumMinutes*6): #Has been in the light for too long
65 | self.sunlightData.clear() #Reset array of data
66 | break
67 | if(lightLength>self.lightMinimumMinutes*6 and self.batteryStatusOk==True):
68 | self.cancelAllTasks(self.__tasks) #Cancel all background processes
69 | print('Returning and exiting')
70 | return True #Go on to Boom Deploy Mode if the battery is Ok
71 | q += 1
72 | await asyncio.sleep(15) #Run this whole while loop every 15 seconds
73 |
74 | async def sunCheck(self):
75 | sunSensor = sunSensorDriver.sunSensor()
76 | while True: #Monitor the sunlight, record it in list NOTE: could be improved to halve calls
77 | #print('Checking sunlight: '+str(self.sunlightData))
78 | vList = sunSensor.read()
79 | averageVoltage = sum(vList)/len(vList)
80 | await asyncio.sleep(5)
81 | averageVoltage += sum(vList)/len(vList)
82 | self.sunlightData.append(averageVoltage/2)
83 | await asyncio.sleep(5) #Every 10 seconds, record average solar panel voltage. Rough running average with two pieces to avoid jumps in avg. voltage
84 |
85 | async def batteryCheck(self):
86 | eps = EPS()
87 | while True: #Checking the battery voltage to see if it's ready for deployment, if it is too low for too long --> SAFE
88 | if (eps.getBusVoltage()>self.thresholdVoltage):
89 | print('Battery above threshold voltage for deployment')
90 | self.batteryStatusOk=True
91 | self.timeWaited = 0
92 | await asyncio.sleep(5)
93 | else:
94 | self.batteryStatusOk=False
95 |
96 | if(self.timeWaited*12 > self.maximumWaitTime): #5 seconds every wait
97 | self.safeMode.run(10) #1 hour
98 | print('Battery too low for too long. Rebooting')
99 | self.timeWaited = 0
100 | await asyncio.sleep(5)
101 | else:
102 | #Wait 5 more seconds
103 | self.timeWaited = self.timeWaited+1
104 | await asyncio.sleep(5) #Check battery every 5 seconds
105 |
106 | def cancelAllTasks(self, taskList): #Isn't used in this class, but here anyways
107 | try:
108 | for t in taskList:
109 | t.cancel()
110 | except asyncio.exceptions.CancelledException:
111 | print("Caught thrown exception in cancelling background task")
112 |
--------------------------------------------------------------------------------
/flightLogic/DummymissionModes/safe.py:
--------------------------------------------------------------------------------
1 | import sys
2 | sys.path.append('../../')
3 | import DummyDrivers.eps.EPS as EPS
4 | import asyncio
5 | import RPi.GPIO as GPIO
6 | from os import system
7 | from time import sleep
8 | import smbus
9 | #####################################################################################
10 | #All this class does is tell the arduino to shut off the pi for the specified amount
11 | #of time.
12 | #times: to pass to the run func
13 | #pass 1 for 1 min
14 | #pass 2 for 2 min
15 | #pass 3 for 3 min
16 | #pass 10 for an hour
17 | #pass 60 for 6 hours
18 | #pass 120 for 12 hours
19 | #pass 24 for a day
20 | #####################################################################################
21 | class safe:
22 | def __init__(self, saveObject):
23 | #Setup I2C bus for communication
24 | self.DEVICE_ADDR = 0x08
25 | self.bus = smbus.SMBus(1)
26 | self.__eps = EPS()
27 | self.thresholdVoltage = 3.33 #Threshold Voltage
28 | self.__saveObject = saveObject
29 | #GPIO.setwarnings(False)
30 | #GPIO.setmode(GPIO.BOARD) #Physical Pin numbering NOTE: 8/14/20, this threw an error
31 | #GPIO.setup(40, GPIO.OUT, initial=GPIO.LOW) #Sets pin 40 to be an output pin and sets the initial value to low (off)
32 |
33 |
34 |
35 | def run(self, time):
36 | #send message to the arduino to power off the pi
37 | #make sure we are not about to tx
38 | if(self.__saveObject is not None and self.__saveObject.checkTxWindow()):
39 | #self.bus.write_byte(self.DEVICE_ADDR, time)
40 | print('Sent power-off command')
41 | else:
42 | #self.bus.write_byte(self.DEVICE_ADDR, time)
43 | print('Send power-off command')
44 |
45 | sleep(15) #If Pi hasn't turned off by now, must take drastic measures. Kill heartbeat code!
46 | #system('pkill -9 python')
47 | print("killing heartbeat code")
48 |
49 | async def thresholdCheck(self):
50 | while True:
51 | epsVoltage = self.__eps.getBusVoltage()
52 | if epsVoltage < self.thresholdVoltage:
53 | print("Going into SAFE. eps voltage was "+ str(epsVoltage))
54 | #self.run(10) #1 hour
55 | else:
56 | print('Threshold is good')
57 | await asyncio.sleep(1) #check voltage every second
58 |
59 | async def heartBeat(self): #Sets up up-and-down voltage on pin 40 for heartbeat with Arduino
60 | waitTime = 4
61 | while True:
62 | #GPIO.output(40, GPIO.HIGH)
63 | print("Heartbeat wave high")
64 | await asyncio.sleep(waitTime/2)
65 | #GPIO.output(40, GPIO.LOW)
66 | print("Heartbeat wave low")
67 | await asyncio.sleep(waitTime/2)
68 |
--------------------------------------------------------------------------------
/flightLogic/README.md:
--------------------------------------------------------------------------------
1 | GASPACS Software: The Flight Software
2 | ==
3 | Opening Note:
4 | The Flight Logic Plan Outline document goes into greater details on the individual mission modes. This document will explain things more simply. Please read Flight Logic Plan Outline.
5 |
6 | Flight Logic/the Main File:
7 | --
8 | Location: ../flightLogic/flightLogic.py
9 |
10 | Functionality:
11 | This file will manage all our mission modes outlines in the Flight Logic Plan Outline. All these modes will be run in other files except for the BOOT mission mode. This will be run inside the file flightLogic.py. This file will run all the other mission modes and make sure we are going through them in the right order and completing them successfully. This file also gets Attitude, Boom Deployment and TTNC data gathered by the getDriverData.py file.
12 |
13 | Getting the Data From the Drivers:
14 | --
15 | Location: ../flightLogic/flightLogic.py
16 |
17 | Functionality:
18 | This file calls on the drivers to gather and organize the TTNC, Boom Deployment and Attitude data. This data is then used in the flightLogic.py file to determine if we are able to execute certain modes.
19 |
20 | Boot Records and Backup Boot Records:
21 | --
22 | Location: ../flightLogic/bootRecords
23 | ../flightLogic/backupBootRecords
24 |
25 | Purpose:
26 | These are simple text files. They will store in the first line how many times we have booted, in the second line whether we are on the first boot, in the third line whether the antenna have been deployed and in the fourth line what our last mission mode was. We have multiple files with this same data to prevent losing the data.
27 |
28 | Mission Modes
29 | ==
30 | Antenna Deploy Mode:
31 | --
32 | Location: ../flightLogic/missionModes/antennaDeploy.py
33 |
34 | Functionality:
35 | In the file flightLogic.py there is a section of code that will check if the antennas have been deployed using our antenna door driver (see GASPACS Software I: the Drivers). If this check returns false the antenna deploy mode will run. This mode will check our battery to see if we have enough power to open the doors. If that check returns true we will then call our back up antenna deployment driver (see GASPACA Software I: the Drivers). If we do not have enough power we will go into sage mode.
36 |
37 | Pre Boom Deployment mode:
38 | --
39 | Location: ../flightLogic/missionModes/preBoomDeploy.py
40 |
41 | Functionality:
42 | When we go into Pre Boom Deployment mode we perform multiple checks. These checks are to insure the satellite is exposed to the sun where our Aero Boom can cure and stiffen as well as to make sure we have enough power. To get the data needed (TTNC, Attitude and Boom Deployment data) to perform these checks the Pre Boom Deployment Mode code will call the getDriverData class.
43 |
44 | Boom Deployment mode:
45 | --
46 | Location: ../flightLogic/missionModes/boomDeploy.py
47 |
48 | Functionality:
49 | The Boom Deployment Mode will run after all the checks performed by the Pre-Boom Deployment mode. The Boom Deployment mode will run the Boom Deployer Driver and then the Camera Driver. (see GASPACA Software I: the Drivers)
50 |
51 | Post-Boom Deploy mode:
52 | --
53 | Location: ../flightLogic/missionModes/postBoomDeploy.py
54 |
55 | Functionality:
56 | This is the last mission mode the Raspberry Pi enters. Once all modes have been run this mode will run a Reboot loop. This loop will cause a reboot every twenty-four hours. This mode will monitor the battery power and other basic functions to keep our satellite from dying. This is also the mode that transmission will take place in.
57 |
58 |
59 | SAFE:
60 | --
61 | Location: ../flightLogic/missionModes/safe.py
62 |
63 | Functionality:
64 | This mode will turn the Raspberry Pi off. It does this by sending a signal to the Arduino Beetle to turn it off. This is to protect against damage to the Pi and lose of data.
65 |
66 |
67 |
--------------------------------------------------------------------------------
/flightLogic/flightLogicPsuedo.md:
--------------------------------------------------------------------------------
1 | Psuedocode for Flight Logic
2 | --
3 | STARTING NOTES:
4 |
5 | NOTE: drivers running at different hz must be done inside drivers depending on mission mode.
6 |
7 | MAIN
8 | --
9 | This file will control running all the mission mode files.
10 | kinda like a parent or a guardian.
11 |
12 | runLogic(drivers, context, lock #Note:entry conditions):
13 | CheckSafe():
14 | If power < CritPower
15 | Go into safe mode
16 |
17 | checkBootMode():
18 | If antenna deployed == True:
19 | Resume = Get most recent datapoint from db
20 | Mission mode = resume
21 | Else:
22 | Wait for 30 + x minutes
23 | If antenna deployed == false:
24 | Deploy antenna
25 | Mission mode = ANTENNA DEPLOY
26 | Else:
27 | Mission mode = PRE-BOOM DEPLOY
28 |
29 |
30 |
31 | SAFE
32 | --
33 | This mission mode will put the satellite into what is basically
34 | low power mode.
35 |
36 | Send flag to watchdog telling Pi to power off
37 |
38 | NOTE: Watchdog needs to handle exit conditions for SAFE
39 |
40 | Antenna Deploy
41 | --
42 | This mission mode will be run as many times as it takes to
43 | deploy the antenna. If needs be it will go to safe. this will work
44 | very closely with the antennaDoor Driver.
45 |
46 | antennaDeploy:
47 | NOTE: steps 2,3 and 4 from flight logic doc are in the antenna deploy driver.
48 | checkPower():
49 | While time elapsed < TimeOut:
50 | If eps value in context > CritPower
51 | deployment():
52 | Antenna.deployed = true;
53 | Break out of loop
54 | Else:
55 | wait(1 min)
56 | If time elapsed > TimeOut:
57 | Go to SAFE
58 |
59 | deployment():
60 | Call antenna deploy driver
61 |
62 |
63 | boomDeploy():
64 | If boomDeploy conditions met:
65 | deployment():
66 | Call boom deploy driver
67 | takePicture()
68 | Boom.deployed = true;
69 | Mission mode = POST-BOOM DEPLOY
70 |
71 | NOTE: post boom deploy step 2?
72 |
73 | Transmission mode
74 | --
75 | This file will set up a communication line with the ground
76 | and will work call the code under the TXISR file.
77 |
78 | transmission:
79 | evalPower():
80 | If tx window imminent:
81 | If enough power:
82 | Wait for tx window + offset
83 | transmit()
84 |
85 |
86 | NOTE: Trying to conserve power for the TX
87 |
--------------------------------------------------------------------------------
/flightLogic/missionModes/README.md:
--------------------------------------------------------------------------------
1 | Mission Modes
2 | ==
3 | Antenna Deploy Mode:
4 | --
5 | Location: ../flightLogic/missionModes/antennaDeploy.py
6 |
7 | Functionality:
8 | In the file flightLogic.py there is a section of code that will check if the antennas have been deployed using our antenna door driver (see GASPACS Software I: the Drivers). If this check returns false the antenna deploy mode will run. This mode will check our battery to see if we have enough power to open the doors. If that check returns true we will then call our back up antenna deployment driver (see GASPACA Software I: the Drivers). If we do not have enough power we will go into sage mode.
9 |
10 | Pre Boom Deployment mode:
11 | --
12 | Location: ../flightLogic/missionModes/preBoomDeploy.py
13 |
14 | Functionality:
15 | When we go into Pre Boom Deployment mode we perform multiple checks. These checks are to insure the satellite is exposed to the sun where our Aero Boom can cure and stiffen as well as to make sure we have enough power. To get the data needed (TTNC, Attitude and Boom Deployment data) to perform these checks the Pre Boom Deployment Mode code will call the getDriverData class.
16 |
17 | Boom Deployment mode:
18 | ---
19 | Location: ../flightLogic/missionModes/boomDeploy.py
20 |
21 | Functionality:
22 | The Boom Deployment Mode will run after all the checks performed by the Pre-Boom Deployment mode. The Boom Deployment mode will run the Boom Deployer Driver and then the Camera Driver. (see GASPACA Software I: the Drivers)
23 |
24 | Post-Boom Deploy mode:
25 | ---
26 | Location: ../flightLogic/missionModes/postBoomDeploy.py
27 |
28 | Functionality:
29 | This is the last mission mode the Raspberry Pi enters. Once all modes have been run this mode will run a Reboot loop. This loop will cause a reboot every twenty-four hours. This mode will monitor the battery power and other basic functions to keep our satellite from dying. This is also the mode that transmission will take place in.
30 | Heartbeat:
31 | ---
32 |
33 | Functionality:
34 | The code in Heartbeat.py is the ‘Heartbeat’ of the Raspberry Pi. It sends a five macro-second pulse every four seconds over a pin that connects theRaspberry Pi to the Arduino Beetle where the Watchdog ‘listens’ for the pulse.
35 |
36 | Transmitting:
37 | ---
38 | This code handles watching the tx windows, preparing the data 20 seconds before it is time to transmit, and calling the transmission runtine when it is time to transmit.
--------------------------------------------------------------------------------
/flightLogic/missionModes/antennaDeploy.py:
--------------------------------------------------------------------------------
1 | """
2 | Contains the antennaMode class and the functions: run, cancelAllTasks, and
3 | skipToPostBoom
4 | """
5 | import sys
6 | #import subprocess
7 | import os
8 | sys.path.append('../../')
9 | import time
10 | from Drivers.backupAntennaDeployer import BackupAntennaDeployer
11 | from Drivers.antennaDoor import AntennaDoor
12 | import Drivers.eps.EPS as EPS
13 | import asyncio
14 | import flightLogic.getDriverData as getDriverData
15 | from TXISR import pythonInterrupt
16 | from TXISR.packetProcessing import packetProcessing as packet
17 | from flightLogic.missionModes.heartBeat import heart_beat
18 | from inspect import currentframe, getframeinfo
19 |
20 | BattVoltageMin = 3.5
21 | BattVoltageMax = 5.1
22 |
23 | class antennaMode:
24 | """
25 | Deploys the antenna when the battery voltage is high enough.
26 | Instantiated in mainFlightLogic.
27 | """
28 | "safeModeObject was deleted below in the init parameters after saveObject"
29 | def __init__(self, saveObject, transmitObject, packetObj):
30 | self.deployVoltage = 3.85 #Threshold voltage to deploy
31 | self.maximumWaitTime = 22 * 60 #Maximum time to wait for deployment before going to SAFE
32 | self.timeWaited = 0 #Time already waited - zero
33 | self.__getTTNCData = getDriverData.TTNCData(saveObject)
34 | self.__getAttitudeData = getDriverData.AttitudeData(saveObject)
35 | self.__tasks = [] #List will be populated with background tasks to cancel them
36 | # self.__safeMode = safeModeObject
37 | self.__antennaDeployer = BackupAntennaDeployer()
38 | self.__antennaDoor = AntennaDoor()
39 | self.__transmit = transmitObject
40 | self.__packetProcessing = packetObj
41 | self.__heartBeatObj = heart_beat()
42 |
43 |
44 | async def run(self):
45 | """
46 | Deploys the antenna when the battery voltage is high enough.
47 | Runs async tasks pythonInterrupt, collectTTNCData, collectAttitudeData,
48 | thresholdcheck, skipToPostBoom, readNextTransferWindow, trasmit
49 | """
50 | print('Antenna Deploy Running!')
51 | self.__tasks.append(asyncio.create_task(self.__heartBeatObj.heartBeatRun()))
52 | self.__tasks.append(asyncio.create_task(pythonInterrupt.interrupt(self.__transmit, self.__packetProcessing)))
53 | self.__tasks.append(asyncio.create_task(self.__getTTNCData.collectTTNCData(1))) #Antenna deploy is mission mode 1
54 | self.__tasks.append(asyncio.create_task(self.__getAttitudeData.collectAttitudeData()))
55 |
56 |
57 | eps=EPS()
58 | #If ground station has sent command to skip to post boom
59 | # if await self.skipToPostBoom():
60 | # return True #Finish this mode and move on
61 | while True: #Runs antenna deploy loop
62 | try:
63 | BattVoltage = eps.getBusVoltage()
64 | if ((BattVoltage < BattVoltageMin) or (BattVoltage > BattVoltageMax)):
65 | print("BattVoltageInt: ", BattVoltage, "BattVoltage: ", BattVoltage)
66 | raise unexpectedValue
67 | except Exception as e:
68 | BattVoltage = 4.18
69 | print("failed to retrieve BattVoltage. Exception: ", repr(e),
70 | getframeinfo(currentframe()).filename, getframeinfo(currentframe()).lineno,
71 | "Received: ", BattVoltage)
72 |
73 | if (BattVoltage > self.deployVoltage): #If the bus voltage is high enough
74 | await asyncio.gather(self.__antennaDeployer.deployPrimary()) #Fire Primary Backup Resistor
75 | try:
76 | doorStatus = self.__antennaDoor.readDoorStatus() #Returns True if all doors are deployed
77 | except:
78 | doorStatus = False
79 | print("Failed to check door status")
80 | if doorStatus == True:
81 | self.cancelAllTasks(self.__tasks)
82 | print('Doors are open, returning true')
83 | return True
84 | else:
85 | print('Firing secondary, primary did not work. Returning True')
86 | await asyncio.gather(self.__antennaDeployer.deploySecondary())
87 | self.cancelAllTasks(self.__tasks)
88 | return True
89 | else:
90 | if(self.timeWaited > self.maximumWaitTime):
91 | await asyncio.gather(self.__antennaDeployer.deployPrimary()) #Fire Primary Backup Resistor
92 | try:
93 | doorStatus = self.__antennaDoor.readDoorStatus() #Returns True if all doors are deployed
94 | except:
95 | print("Failed to check door status")
96 | if doorStatus == True:
97 | self.cancelAllTasks(self.__tasks)
98 | print('Doors are open, returning true')
99 | return True
100 | else:
101 | print('Firing secondary, primary did not work. Returning True')
102 | await asyncio.gather(self.__antennaDeployer.deploySecondary())
103 | self.cancelAllTasks(self.__tasks)
104 | return True
105 | else:
106 | #Wait 1 minute
107 | print('Waiting 1 minute until battery status resolves')
108 | self.timeWaited = self.timeWaited+1
109 | await asyncio.sleep(60)
110 |
111 | def cancelAllTasks(self, taskList):
112 | """
113 | Cancels all async tasks created at the beginning of run.
114 | """
115 | try:
116 | for t in taskList:
117 | t.cancel()
118 | except asyncio.exceptions.CancelledException:
119 | print("Caught thrown exception in cancelling background task")
120 |
--------------------------------------------------------------------------------
/flightLogic/missionModes/boomDeploy.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import time
3 | sys.path.append('../../')
4 | import asyncio
5 | from flightLogic.getDriverData import *
6 | import Drivers.boomDeployer as boomDeployer
7 | from TXISR import pythonInterrupt
8 | from TXISR.packetProcessing import packetProcessing as packet
9 | from flightLogic.missionModes.heartBeat import heart_beat
10 |
11 | #safeModeObject was deleted below in the init parameters after saveObject
12 | class boomMode:
13 | def __init__(self, saveObject, transmitObject, cam, packetObj):
14 | self.__getTTNCData = TTNCData(saveObject)
15 | self.__getAttitudeData = AttitudeData(saveObject)
16 | self.__getDeployData = DeployData(saveObject)
17 | self.__tasks = [] # Empty list will be populated with all background tasks
18 | # self.__safeMode = safeModeObject
19 | self.__saveObject = saveObject
20 | self.__transmit = transmitObject
21 | self.__packetProcessing = packet(transmitObject, cam)
22 | self.__heartBeatObj = heart_beat()
23 | self.__cam = cam
24 | self.__packetProcessing = packetObj
25 |
26 | async def run(self):
27 | # Setting up background processes
28 | self.__tasks.append(asyncio.create_task(self.__heartBeatObj.heartBeatRun()))
29 | self.__tasks.append(asyncio.create_task(pythonInterrupt.interrupt(self.__transmit, self.__packetProcessing)))
30 | self.__tasks.append(asyncio.create_task(self.__getTTNCData.collectTTNCData(3))) # Boom deploy is mode 3
31 | self.__tasks.append(asyncio.create_task(self.__getAttitudeData.collectAttitudeData()))
32 | self.__tasks.append(asyncio.create_task(self.__getDeployData.collectDeployData()))
33 |
34 | print("Starting boom deploy")
35 | # Deploy boom, take picture
36 | await asyncio.sleep(5)
37 |
38 | deployer = boomDeployer.BoomDeployer()
39 | await deployer.deploy()
40 |
41 |
42 | try:
43 | print("Taking picture")
44 | self.__cam.takePicture()
45 | except Exception as e:
46 | print("Failed to take a picture because we received excpetion:", repr(e))
47 | await asyncio.sleep(5)
48 | # await asyncio.sleep(60) #sleep if a transmission is running
49 | self.cancelAllTasks(self.__tasks) # Cancel all background tasks
50 | return True # Go to post-boom deploy
51 |
52 | def cancelAllTasks(self, taskList):
53 | try:
54 | for t in taskList:
55 | t.cancel()
56 | except asyncio.exceptions.CancelledException:
57 | print("Caught thrown exception in cancelling background task")
58 |
--------------------------------------------------------------------------------
/flightLogic/missionModes/heartBeat.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import RPi.GPIO as GPIO
3 |
4 | class heart_beat:
5 | def __init__(self) -> None:
6 | GPIO.setwarnings(False)
7 | GPIO.setmode(GPIO.BCM) #Physical Pin numbering
8 | GPIO.setup(21, GPIO.OUT, initial=GPIO.LOW) #Sets pin 40 (GPIO 21) to be an output pin and sets the initial value to low (off)
9 |
10 |
11 | async def heartBeatRun(self): #Sets up up-and-down voltage on pin 40 (GPIO 21) for heartbeat with Arduino
12 | waitTime = 4
13 | while True:
14 | GPIO.output(21, GPIO.HIGH)
15 | print("Heartbeat wave high")
16 | await asyncio.sleep(waitTime/2)
17 | GPIO.output(21, GPIO.LOW)
18 | print("Heartbeat wave low")
19 | await asyncio.sleep(waitTime/2)
--------------------------------------------------------------------------------
/flightLogic/missionModes/postBoomDeploy.py:
--------------------------------------------------------------------------------
1 | import sys
2 | sys.path.append('../../')
3 | import asyncio
4 | from flightLogic.getDriverData import *
5 | from TXISR import pythonInterrupt
6 | from flightLogic.missionModes.heartBeat import heart_beat
7 |
8 | # safeModeObject was deleted below in the init parameters after saveObject
9 | class postBoomMode:
10 | def __init__(self, saveObject, transmitObject, packetObj):
11 | self.__transmit = transmitObject
12 | self.__getTTNCData = TTNCData(saveObject)
13 | self.__getAttitudeData = AttitudeData(saveObject)
14 | self.__heartBeatObj = heart_beat()
15 | self.__tasks = [] # List will be populated with all background tasks
16 | # self.__safeMode = safeModeObject
17 | print("Initialized postBoomDeploy")
18 | self.__packet = packetObj
19 |
20 | async def run(self):
21 | #Set up background processes
22 | print("Inside of run in postBoomDeploy")
23 | self.__tasks.append(asyncio.create_task(self.__heartBeatObj.heartBeatRun()))
24 | self.__tasks.append(asyncio.create_task(pythonInterrupt.interrupt(self.__transmit, self.__packet)))
25 | self.__tasks.append(asyncio.create_task(self.__getTTNCData.collectTTNCData(4))) #Post-boom is mode 4
26 | self.__tasks.append(asyncio.create_task(self.__getAttitudeData.collectAttitudeData()))
27 |
28 | print("Initalized all tasks.")
29 | while True:
30 | # Don't remove this print statement, the while loop is an integral part
31 | # of making sure that postBoomDeploy doesn't crash and it needs to
32 | # have something in there so it doesn't crash and suffer
33 | print('This is post Boom Deploy')
34 | await asyncio.sleep(10)
35 |
36 | def cancellAllTasks(self, taskList): #Isn't used in this class, but here anyways
37 | try:
38 | for t in taskList:
39 | t.cancel()
40 | except asyncio.exceptions.CancelledException:
41 | print("Caught thrown exception in cancelling background task")
42 |
--------------------------------------------------------------------------------
/flightLogic/missionModes/preBoomDeploy.py:
--------------------------------------------------------------------------------
1 | import sys
2 | sys.path.append('../../')
3 | import asyncio
4 | from flightLogic.getDriverData import *
5 | from Drivers.eps import EPS as EPS
6 | from Drivers.sunSensors import sunSensorDriver
7 | from TXISR import pythonInterrupt
8 |
9 | from inspect import currentframe, getframeinfo
10 | from TXISR.packetProcessing import packetProcessing as packet
11 | from flightLogic.missionModes.heartBeat import heart_beat
12 |
13 |
14 | sunSensorMin = 0.0
15 | sunSensorMax = 3.3
16 | getBusVoltageMin = 3.5
17 | getBusVoltageMax = 5.1
18 |
19 |
20 | DEBUG = False
21 |
22 | #safeModeObject was deleted below in the init parameters after saveObject
23 | class preBoomMode:
24 | def __init__(self, saveObject, transmitObject, packetObj):
25 | self.thresholdVoltage = 3.85 #Threshold voltage to deploy AeroBoom.
26 | self.darkVoltage = 0.9 #Average voltage from sunsors that, if below this, indicates GASPACS is in darkness
27 | self.lightMinimumMinutes = 1 #Minimum amount of time GASPACS must be on light side of orbit before deploying
28 | self.batteryStatusOk = False
29 | self.sunlightData = 0
30 | self.__getTTNCData = TTNCData(saveObject)
31 | self.__getAttitudeData = AttitudeData(saveObject)
32 | self.__tasks = [] #Will be populated with tasks
33 | self.__packetProcessing = packetObj
34 | self.__transmit = transmitObject
35 | self.__heartBeatObj = heart_beat()
36 |
37 |
38 | async def run(self):
39 | self.__tasks.append(asyncio.create_task(self.__heartBeatObj.heartBeatRun()))
40 | self.__tasks.append(asyncio.create_task(pythonInterrupt.interrupt(self.__transmit, self.__packetProcessing)))
41 | self.__tasks.append(asyncio.create_task(self.__getTTNCData.collectTTNCData(2))) #Pre-Boom is mode 2
42 | self.__tasks.append(asyncio.create_task(self.__getAttitudeData.collectAttitudeData()))
43 | self.__tasks.append(asyncio.create_task(self.sunCheck()))
44 | self.__tasks.append(asyncio.create_task(self.batteryCheck()))
45 |
46 | """This code was intended to find out when to deploy the boom based on how long GASPACS was in the light.
47 | This was commented out because there is no need for this functionality if there is no resin in the boom"""
48 | while True:
49 | print("checking for sunlight")
50 |
51 | if ((self.sunlightData > self.darkVoltage) and self.batteryStatusOk == True):
52 | self.cancelAllTasks(self.__tasks) #Cancel all background processes, this depolys the boom basically
53 | print('Returning and exiting')
54 | return True #Go on to Boom Deploy Mode if the battery is Ok
55 | await asyncio.sleep(5) #Run this whole while loop every 15 seconds
56 |
57 | async def sunCheck(self):
58 | sunSensor = sunSensorDriver.sunSensor()
59 | while True: #Monitor the sunlight, record it in list NOTE: could be improved to halve calls
60 | vList = [0.0, 0.0, 0.0, 0.0, 0.0]
61 | try:
62 | vList = sunSensor.read()
63 | if DEBUG:
64 | print("Pre boom deploy sun sensor values: ", vList)
65 | size = 0
66 | while size < 5:
67 | if (vList[size] < sunSensorMin) or (vList[size] > sunSensorMax):
68 | raise unexpectedValue
69 | size += 1
70 | except Exception as e:
71 | print("Failure to pull sunSensor data. Received error:", repr(e),
72 | getframeinfo(currentframe()).filename, getframeinfo(currentframe()).lineno)
73 | vList[0] = 1
74 |
75 | self.sunlightData = max(vList)
76 | await asyncio.sleep(5)
77 |
78 | async def batteryCheck(self):
79 | eps = EPS()
80 | while True: #Checking the battery voltage to see if it's ready for deployment, if it is too low for too long --> SAFE
81 | try:
82 | BusVoltage = eps.getBusVoltage()
83 | #NOTE: This line is for testing! DO NOT
84 | if(BusVoltage < getBusVoltageMin) or (BusVoltage > getBusVoltageMax):
85 | raise unexpectedValue
86 | except Exception as e:
87 | BusVoltage = 4.18
88 | print("Failed to retrieve BusVoltage, got", BusVoltage, "instead. Received error: ",
89 | repr(e), getframeinfo(currentframe()).filename, getframeinfo(currentframe()).lineno)
90 |
91 | if (BusVoltage > self.thresholdVoltage):
92 | print('Battery above threshold voltage for deployment')
93 | self.batteryStatusOk=True
94 | await asyncio.sleep(5)
95 | else:
96 | self.batteryStatusOk=False
97 |
98 | #Wait 5 more seconds
99 | await asyncio.sleep(5) #Check battery every 5 seconds
100 |
101 | def cancelAllTasks(self, taskList): #Isn't used in this class, but here anyways
102 | try:
103 | for t in taskList:
104 | t.cancel()
105 | except asyncio.exceptions.CancelledException:
106 | print("Caught thrown exception in cancelling background task")
107 |
108 | class unexpectedValue(Exception):
109 | print("Received unexpected value.")
110 | pass
111 |
--------------------------------------------------------------------------------
/flightLogic/postBoomTime.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmallSatGasTeam/CubeWorks/d615cb27b897289bb3a5305b91c62256718685a1/flightLogic/postBoomTime.txt
--------------------------------------------------------------------------------
/flightLogic/saveTofiles.py:
--------------------------------------------------------------------------------
1 | """
2 | These are the classes that handle saving the data to the files
3 |
4 | Each class has one func that will write all the data in a list that
5 | is passed to it.
6 |
7 | They are written as different classes so that each code my be quicker
8 |
9 | NOTE: try to only instantiate the classes once. if you have to do
10 | more this will not cause a problem it will only slow the code
11 | down.
12 | """
13 | import os.path
14 | import asyncio
15 | import time
16 | from protectionProticol.fileProtection import FileReset
17 |
18 |
19 | fileChecker = FileReset()
20 |
21 | class save:
22 | def __init__(self):
23 | pass
24 |
25 | #write the data to the file,
26 | #NOTE: it is important that you put a : after the time stamp, this will
27 | #effect the txisr
28 | async def writeTTNC(self, data):
29 | fileChecker.checkFile("/home/pi/flightLogicData/TTNC_Data.txt")
30 | TTNC_File = open('/home/pi/flightLogicData/TTNC_Data.txt', "a+")
31 | TTNC_File.write(str(data)+'\n')
32 | TTNC_File.close()
33 |
34 |
35 | #this is data collection for Deploy
36 | #write the data to the file,
37 | #NOTE: it is important that you put a : after the time stamp, this will
38 | #effect the txisr
39 | async def writeDeploy(self, data):
40 | fileChecker.checkFile("/home/pi/flightLogicData/Deploy_Data.txt")
41 | Deploy_File = open("/home/pi/flightLogicData/Deploy_Data.txt", "a+")
42 | Deploy_File.write(str(data)+'\n')
43 | Deploy_File.close()
44 |
45 |
46 | #this part of the code is for data collection of attitude data
47 | #write the data to the file,
48 | #NOTE: it is important that you put a : after the time stamp, this will
49 | #effect the txisr
50 | async def writeAttitude(self, data):
51 | fileChecker.checkFile("/home/pi/flightLogicData/Attitude_Data.txt")
52 | Attitude_File = open("/home/pi/flightLogicData/Attitude_Data.txt", "a+")
53 | Attitude_File.write(str(data)+'\n')
54 | Attitude_File.close()
55 |
56 |
--------------------------------------------------------------------------------
/install.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 |
5 | //this will handle the install of all gas software
6 | char GIT_CODE_BASE [] ="https://github.com/SmallSatGasTeam/CubeWorks.git";
7 | char crontabComand [] ="@reboot sudo runuser pi -c ./startup.exe";
8 |
9 |
10 | //to change the branch take out the -b codeBase for master or change it to a different branch.
11 | char cammand0 [] = "git clone https://github.com/SmallSatGasTeam/CubeWorks.git CubeWorks0/";
12 | char cammand1 [] = "git clone https://github.com/SmallSatGasTeam/CubeWorks.git CubeWorks1/";
13 | char cammand2 [] = "git clone https://github.com/SmallSatGasTeam/CubeWorks.git CubeWorks2/";
14 | char cammand3 [] = "git clone https://github.com/SmallSatGasTeam/CubeWorks.git CubeWorks3/";
15 | char cammand4 [] = "git clone https://github.com/SmallSatGasTeam/CubeWorks.git CubeWorks4/";
16 |
17 | //this code compiles the c code that is need to run transmission.
18 | char branchCommand0 [] = "cd ; cd CubeWorks0/TXISR/TXServiceCode ; gcc TXServiceCode.c -o TXService.run; cd ;";
19 | char branchCommand1 [] = "cd ; cd CubeWorks1/TXISR/TXServiceCode ; gcc TXServiceCode.c -o TXService.run; cd ;";
20 | char branchCommand2 [] = "cd ; cd CubeWorks2/TXISR/TXServiceCode ; gcc TXServiceCode.c -o TXService.run; cd ;";
21 | char branchCommand3 [] = "cd ; cd CubeWorks3/TXISR/TXServiceCode ; gcc TXServiceCode.c -o TXService.run; cd ;";
22 | char branchCommand4 [] = "cd ; cd CubeWorks4/TXISR/TXServiceCode ; gcc TXServiceCode.c -o TXService.run; cd ;";
23 |
24 | char upDateCommand [] = "cd ; cd CubeWorks0/ ; gcc upDateCode.c -o upDateCode.exe ; cp upDateCode.exe ~/ ; rm upDateCode.exe";
25 |
26 | char testingCommand [] = "cd ; cd CubeWorks0/ ; gcc setNewTXWindow.c -o setNewTXWindow.exe ; mkdir ../TXISRData; sudo cp setNewTXWindow.exe ~/TXISRData/setNewTXWindow.exe ; rm setNewTXWindow.exe";
27 |
28 |
29 |
30 |
31 | // #update and install python
32 | // #NO long in use cause the version are lock for FLIGHT!
33 | // # sudo apt full-upgrade
34 | // # sudo apt-get update
35 | // # sudo apt install python3
36 | // # sudo apt install python3-pip
37 | // # sudo apt install python3-numpy
38 | // # sudo apt install git
39 |
40 | void main()
41 | {
42 | printf("\n>>>Creating a CubeWorks0<<<\n");
43 | //install the first code base
44 | system(cammand0);
45 |
46 | // //install the code bases
47 | printf("\n>>>Creating a CubeWorks1<<<\n");
48 | system(cammand1);
49 |
50 | printf("\n>>>Creating a CubeWorks2<<<\n");
51 | system(cammand2);
52 |
53 | printf("\n>>>Creating a CubeWorks3<<<\n");
54 | system(cammand3);
55 |
56 | printf("\n>>>Creating a CubeWorks4<<<\n");
57 | system(cammand4);
58 |
59 |
60 | //complie the code
61 | printf("\n>>>Creating tx routine for CubeWorks0<<<\n");
62 | system(branchCommand0);
63 | printf("\n>>>Creating tx routine for CubeWorks1<<<\n");
64 | system(branchCommand1);
65 | printf("\n>>>Creating tx routine for CubeWorks2<<<\n");
66 | system(branchCommand2);
67 | printf("\n>>>Creating tx routine for CubeWorks3<<<\n");
68 | system(branchCommand3);
69 | printf("\n>>>Creating tx routine for CubeWorks4<<<\n");
70 | system(branchCommand4);
71 |
72 | //create the upDate code
73 | printf("\n>>>Creating update code<<<\n");
74 | system(upDateCommand);
75 |
76 | //Create the set new txWindows code
77 | printf("\n>>>Creating setTXWindows routine<<<\n");
78 | system(testingCommand);
79 |
80 | //create the start up code, and then move it to the root
81 | printf("\n>>>creating multi-code base protocol\n");
82 | system("cd CubeWorks0\ngcc startup.c -o startup.exe\ncp startup.exe ~/");
83 |
84 |
85 | //up date the crontab to run the startup.exe
86 | // printf("\n>>>creating start up proticol<<<\n");
87 | // system("sudo crontab -l > mycron");
88 | // system("echo @reboot sudo runuser pi -c cd ; ./startup.exe >> mycron");
89 | // system("sudo crontab mycron");
90 | // system("rm mycron");
91 |
92 |
93 | printf(">>rebooting to finish installation<<<\n");
94 | system("sudo reboot");
95 | }
--------------------------------------------------------------------------------
/lastBase.txt:
--------------------------------------------------------------------------------
1 | 1
--------------------------------------------------------------------------------
/log.txt:
--------------------------------------------------------------------------------
1 | Booted
2 |
--------------------------------------------------------------------------------
/mycron:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmallSatGasTeam/CubeWorks/d615cb27b897289bb3a5305b91c62256718685a1/mycron
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | board
2 | RPi.GPIO
3 | adafruit-blinka
4 | adafruit-circuitpython-lsm303_accel
5 | adafruit-circuitpython-lis2mdl
6 | spidev
7 | picamera
8 | smbus
9 |
--------------------------------------------------------------------------------
/setNewTXWindow.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | /* This code provides a simple way to feed a transmission window into txWindows.txt while running the main flight logic. */
6 |
7 | char command0[] = "date +%s";
8 |
9 | int main(int argc, char * argv[])
10 | {
11 | FILE *fptr;
12 | fptr = fopen("/home/pi/TXISRData/txWindows.txt","a+");
13 | int flag = 0;
14 | int input, dataType, windowsNumber, windowLength, n, i, line, pic, length;
15 | long int Time;
16 | time_t txTime;
17 |
18 | if(fptr == NULL)
19 | {
20 | printf("ERROR WITH FILEPATH\n");
21 | exit(1);
22 | }
23 |
24 | if(argc == 1){
25 | printf("You are creating custom txWindows. To create multiple at equal"
26 | " intervals, the usage is: sudo ./setNewTXWindow.c